diff options
Diffstat (limited to 'engines/glk/archetype/saveload.cpp')
-rw-r--r-- | engines/glk/archetype/saveload.cpp | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/engines/glk/archetype/saveload.cpp b/engines/glk/archetype/saveload.cpp new file mode 100644 index 0000000000..2a965e3039 --- /dev/null +++ b/engines/glk/archetype/saveload.cpp @@ -0,0 +1,672 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "glk/archetype/saveload.h" +#include "glk/archetype/id_table.h" +#include "glk/archetype/semantic.h" + +namespace Glk { +namespace Archetype { + +StatementKind vEndSeq, vContSeq; // to make BlockWrite happy +int Dynamic; +bool Translating; + +void saveload_init() { + vEndSeq = END_SEQ; + vContSeq = CONT_SEQ; + + Dynamic = 0; + Translating = true; +} + +// ===================== Forward Declarations ======================= + +static void walk_item_list(MissionType mission, Common::Stream *bfile, + ListType elements, ContentType content); +static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_expr); +static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &the_stmt); + +// ========================== Item Lists ============================ + +// Wrappers + +void load_item_list(Common::ReadStream *f_in, ListType elements, ContentType content) { + walk_item_list(LOAD, f_in, elements, content); +} + +void dump_item_list(Common::WriteStream *f_out, ListType elements, ContentType content) { + walk_item_list(DUMP, f_out, elements, content); +} + +void dispose_item_list(ListType &elements, ContentType content) { + walk_item_list(FREE, nullptr, elements, content); +} + +/** + * Used for operating on general linked lists which are homogenous, all containing the same type + * of data, signified by the "content" variable. + * @param mission action to perform while walking through + * @param bfile binary file to read or write from + * @param elements list of items + * @param content contents of each of the items + */ +void walk_item_list(MissionType mission, Common::Stream *bfile, ListType elements, ContentType content) { + StatementKind sentinel; + StatementPtr this_stmt; + ExprTree this_expr; + CasePairPtr this_case; + NodePtr np = nullptr; + bool yet_more = false; + + Common::ReadStream *readStream = dynamic_cast<Common::ReadStream *>(bfile); + Common::WriteStream *writeStream = dynamic_cast<Common::WriteStream *>(bfile); + + // Prelude + switch (mission) { + case LOAD: + assert(readStream); + sentinel = (StatementKind)readStream->readUint32LE(); + new_list(elements); + yet_more = sentinel == CONT_SEQ; + break; + + case DUMP: + case FREE: + np = nullptr; + yet_more = iterate_list(elements, np); + break; + + default: + break; + } + + while (yet_more) { + // Main walk + switch (mission) { + case LOAD: + assert(readStream); + np = (NodePtr)malloc(sizeof(NodeType)); + add_bytes(sizeof(NodeType)); + np->key = readStream->readSint32LE(); + break; + + case DUMP: + assert(writeStream); + writeStream->writeUint32LE(vContSeq); + writeStream->writeSint32LE(np->key); + break; + + default: + break; + } + + switch (content) { + case EXPR_LIST: + switch (mission) { + case LOAD: + walk_expr(mission, bfile, this_expr); + np->data = this_expr; + break; + case DUMP: + case FREE: + this_expr = (ExprTree)np->data; + walk_expr(mission, bfile, this_expr); + break; + default: + break; + } + break; + + case STMT_LIST: + switch (mission) { + case LOAD: + walk_stmt(mission, bfile, this_stmt); + np->data = this_stmt; + break; + case DUMP: + case FREE: + this_stmt = (StatementPtr)np->data; + walk_stmt(mission, bfile, this_stmt); + break; + default: + break; + } + break; + + case CASE_LIST: + switch (mission) { + case LOAD: + this_case = (CasePairPtr)malloc(sizeof(CasePairType)); + add_bytes(sizeof(CasePairType)); + walk_expr(mission, bfile, this_case->value); + walk_stmt(mission, bfile, this_case->action); + + np->data = this_case; + break; + + case DUMP: + case FREE: + this_case = (CasePairPtr)np->data; + walk_expr(mission, bfile, this_case->value); + walk_stmt(mission, bfile, this_case->action); + + if (mission == FREE) { + add_bytes(sizeof(CasePairType)); + free(this_case); + } + break; + + default: + break; + } + break; + + default: + break; + } + + switch (mission) { + case LOAD: + assert(readStream); + append_to_list(elements, np); + sentinel = (StatementKind)readStream->readUint32LE(); + yet_more = sentinel == CONT_SEQ; + break; + + case DUMP: + case FREE: + yet_more = iterate_list(elements, np); + break; + + default: + break; + } + } + + // Postlude + switch (mission) { + case DUMP: + writeStream->writeUint32LE(vEndSeq); + break; + case FREE: + dispose_list(elements); + elements = nullptr; + break; + default: + break; + } +} + +// ============================ Expressions =========================== + +// Wrappers +void load_expr(Common::ReadStream *f_in, ExprTree &the_expr) { + walk_expr(LOAD, f_in, the_expr); +} + +void dump_expr(Common::WriteStream *f_out, ExprTree &the_expr) { + walk_expr(DUMP, f_out, the_expr); +} + +void dispose_expr(ExprTree &the_expr) { + walk_expr(FREE, nullptr, the_expr); +} + +/** + * Separated from walk_expr so as not to consume too much stack space with its large temporary string.. + */ +static StringPtr LoadDynStr(Common::ReadStream *f_in) { + String s; + load_string(f_in, s); + return NewDynStr(s); +} + +/** + * Walks through an expression tree. + * @param mission action to take on each visited element + * @param bfile binary file to read or write from as necessary + * @param the_expr expression tree to walk + */ +static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_expr) { + int temp = 0; + ClassifyType ID_kind = UNDEFINED_ID; + + Common::ReadStream *readStream = dynamic_cast<Common::ReadStream *>(bfile); + Common::WriteStream *writeStream = dynamic_cast<Common::WriteStream *>(bfile); + + // Prelude + switch (mission) { + case LOAD: + assert(readStream); + the_expr = (ExprTree)malloc(sizeof(ExprNode)); + add_bytes(sizeof(ExprNode)); + the_expr->_kind = (AclType)readStream->readUint32LE(); + break; + + case DUMP: + if (the_expr == nullptr) + return; + + assert(writeStream); + while (the_expr->_kind == OPER && the_expr->_oper.op_name == OP_LPAREN) + the_expr = the_expr->_oper.right; + + writeStream->writeUint32LE(the_expr->_kind); + break; + + case FREE: + if (the_expr == nullptr) + return; + break; + + default: + break; + } + + // Main walk + switch (the_expr->_kind) { + case OPER: + switch (mission) { + case LOAD: + the_expr->_oper.op_name = readStream->readSByte(); + the_expr->_oper.left = nullptr; + break; + case DUMP: + writeStream->writeSByte(the_expr->_oper.op_name); + break; + default: + break; + } + + if (Binary[the_expr->_oper.op_name]) + walk_expr(mission, bfile, the_expr->_oper.left); + walk_expr(mission, bfile, the_expr->_oper.right); + break; + + case NUMERIC: + switch (mission) { + case LOAD: + the_expr->_numeric.acl_int = readStream->readSint32LE(); + break; + case DUMP: + writeStream->writeSint32LE(the_expr->_numeric.acl_int); + break; + default: + break; + } + break; + + case MESSAGE: + case TEXT_LIT: + case QUOTE_LIT: + switch (mission) { + case LOAD: + the_expr->_msgTextQuote.index = readStream->readSint32LE(); + break; + case DUMP: + writeStream->writeSint32LE(the_expr->_msgTextQuote.index); + break; + default: + break; + } + break; + + case IDENT: + switch (mission) { + case LOAD: + the_expr->_ident.ident_kind = (ClassifyType)readStream->readUint32LE(); + the_expr->_ident.ident_int = readStream->readSint32LE(); + break; + case DUMP: + if (Translating && the_expr->_ident.ident_kind == DefaultClassification) { + // may have changed meaning + get_meaning(the_expr->_ident.ident_int, ID_kind, temp); + if (ID_kind == UNDEFINED_ID) + add_undefined(the_expr->_ident.ident_int); + } else { + the_expr->_ident.ident_kind = ID_kind; + the_expr->_ident.ident_int = temp; + } + + writeStream->writeUint32LE(the_expr->_ident.ident_kind); + writeStream->writeSint32LE(the_expr->_ident.ident_int); + break; + default: + break; + } + break; + + case RESERVED: + switch (mission) { + case LOAD: + the_expr->_reserved.keyword = readStream->readSByte(); + break; + case DUMP: + writeStream->writeSByte(the_expr->_reserved.keyword); + break; + default: + break; + } + break; + + case STR_PTR: + switch (mission) { + case LOAD: + the_expr->_str.acl_str = LoadDynStr(readStream); + break; + case DUMP: + dump_string(writeStream, *the_expr->_str.acl_str); + break; + case FREE: + FreeDynStr(the_expr->_str.acl_str); + break; + default: + break; + } + break; + + default: + break; + } + + // Postlude + switch (mission) { + case FREE: + free(the_expr); + the_expr = nullptr; + break; + default: + break; + } +} + + +// =========================== Statements ========================= + +// Wrappers + +void load_stmt(Common::ReadStream *f_in, StatementPtr &the_stmt) { + walk_stmt(LOAD, f_in, the_stmt); +} + +void dump_stmt(Common::WriteStream *f_out, StatementPtr &the_stmt) { + walk_stmt(DUMP, f_out, the_stmt); +} + +void dispose_stmt(StatementPtr &the_stmt) { + walk_stmt(FREE, nullptr, the_stmt); +} + +/** + * Handles the control involved in walking through a statement. + * @param mission action to take for each statement + * @param bfile binary file to read or write as necessary + * @param the_stmt pointer to a statement record + */ +static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &the_stmt) { + NodePtr np; // for appending to lists + StatementKind sentinel; + StatementPtr this_stmt = nullptr; + ExprTree this_expr; + + Common::ReadStream *readStream = dynamic_cast<Common::ReadStream *>(bfile); + Common::WriteStream *writeStream = dynamic_cast<Common::WriteStream *>(bfile); + + // Prelude + switch (mission) { + case LOAD: + the_stmt = nullptr; + if (readStream->eos()) + return; + + sentinel = (StatementKind)readStream->readUint32LE(); + if (sentinel == END_SEQ) + return; + + the_stmt = (StatementPtr)malloc(sizeof(StatementType)); + add_bytes(sizeof(StatementType)); + the_stmt->_kind = sentinel; + break; + + case DUMP: + if (the_stmt == nullptr) { + writeStream->writeUint32LE(vEndSeq); + return; + } else { + writeStream->writeUint32LE(this_stmt->_kind); + } + break; + + case FREE: + if (the_stmt == nullptr) + return; + break; + + default: + break; + } + + // Main walk + assert(this_stmt); + switch (this_stmt->_kind) { + case COMPOUND: + walk_item_list(mission, bfile, this_stmt->_compound.statements, STMT_LIST); + break; + + case ST_EXPR: + walk_expr(mission, bfile, this_stmt->_expr.expression); + break; + + case ST_IF: + walk_expr(mission, bfile, this_stmt->_if.condition); + walk_stmt(mission, bfile, this_stmt->_if.then_branch); + walk_stmt(mission, bfile, this_stmt->_if.else_branch); + break; + + case ST_CASE: + walk_expr(mission, bfile, this_stmt->_case.test_expr); + walk_item_list(mission, bfile, this_stmt->_case.cases, CASE_LIST); + break; + + case ST_CREATE: + switch (mission) { + case LOAD: + this_stmt->_create.archetype = readStream->readSint32LE(); + break; + case DUMP: + writeStream->writeSint32LE(this_stmt->_create.archetype); + break; + default: + break; + } + + walk_expr(mission, bfile, this_stmt->_create.new_name); + break; + + case ST_DESTROY: + walk_expr(mission, bfile, this_stmt->_destroy.victim); + break; + + case ST_FOR: + case ST_WHILE: + walk_expr(mission, bfile, this_stmt->_loop.selection); + walk_stmt(mission, bfile, this_stmt->_loop.action); + break; + + case ST_WRITE: + case ST_WRITES: + case ST_STOP: + switch (mission) { + case LOAD: + new_list(this_stmt->_write.print_list); + sentinel = (StatementKind)readStream->readUint32LE(); + + while (sentinel != END_SEQ) { + walk_expr(mission, bfile, this_expr); + np = (NodePtr)malloc(sizeof(NodeType)); + add_bytes(sizeof(NodeType)); + + np->data = this_expr; + append_to_list(this_stmt->_write.print_list, np); + + sentinel = (StatementKind)readStream->readUint32LE(); + } + break; + + case DUMP: + case FREE: + np = nullptr; + while (iterate_list(this_stmt->_write.print_list, np)) { + if (mission == DUMP) + writeStream->writeUint32LE(vContSeq); + this_expr = (ExprTree)np->data; + walk_expr(mission, bfile, this_expr); + + if (mission == FREE) + np->data = nullptr; + } + + if (mission == DUMP) + writeStream->writeUint32LE(vEndSeq); + else + dispose_list(this_stmt->_write.print_list); + break; + + default: + break; + } + break; + + default: + break; + } + + // Postlude + switch (mission) { + case FREE: + add_bytes(sizeof(StatementType)); + free(the_stmt); + break; + default: + break; + } +} + + +// ============================ Objects =========================== + +void load_object(Common::ReadStream *f_in, ObjectPtr &the_object) { + StatementKind sentinel; + + the_object = (ObjectPtr)malloc(sizeof(ObjectType)); + add_bytes(sizeof(ObjectType)); + + the_object->inherited_from = f_in->readSint32LE(); + load_item_list(f_in, the_object->attributes, EXPR_LIST); + load_item_list(f_in, the_object->methods, STMT_LIST); + + sentinel = (StatementKind)f_in->readUint32LE(); + if (sentinel == CONT_SEQ) + load_stmt(f_in, the_object->other); + else + the_object->other = nullptr; +} + +void dump_object(Common::WriteStream *f_out, const ObjectPtr the_object) { + f_out->writeUint32LE(the_object->inherited_from); + dump_item_list(f_out, the_object->attributes, EXPR_LIST); + dump_item_list(f_out, the_object->methods, STMT_LIST); + + if (the_object->other == nullptr) { + f_out->writeUint32LE(vEndSeq); + } else { + f_out->writeUint32LE(vContSeq); + dump_stmt(f_out, the_object->other); + } +} + +void dispose_object(ObjectPtr &the_object) { + dispose_item_list(the_object->attributes, EXPR_LIST); + dispose_item_list(the_object->methods, STMT_LIST); + if (the_object->other != nullptr) + dispose_stmt(the_object->other); + + add_bytes(sizeof(ObjectType)); + free(the_object); + the_object = nullptr; +} + + +// ============================= Object Lists ======================== + +void load_obj_list(Common::ReadStream *f_in, XArrayType &obj_list) { + ObjectPtr new_object; + void *p; + int i, list_size; + + new_xarray(obj_list); + list_size = f_in->readUint32LE(); + + for (i = 0; i < list_size; ++i) { + load_object(f_in, new_object); + p = new_object; + append_to_xarray(obj_list, p); + } + + // Objects may be dynamically allocated beneath this limit. It is okay to set that limit + // at this time since this routine is only invoked when initially loading a game + Dynamic = obj_list.size(); // TODO: Check if this should be size() + 1 +} + +void dump_obj_list(Common::WriteStream *f_out, XArrayType &obj_list) { + uint i; + void *p; + ObjectPtr this_obj; + + f_out->writeUint32LE(obj_list.size()); + + for (i = 0; i < obj_list.size(); ++i) { + if (index_xarray(obj_list, i, p)) { + this_obj = (ObjectPtr)p; + dump_object(f_out, this_obj); + } + } +} + +void dispose_obj_list(XArrayType &obj_list) { + uint i; + void *p; + ObjectPtr axe_obj; + + for (i = 0; i < obj_list.size(); ++i) { + if (index_xarray(obj_list, i, p)) { + axe_obj = (ObjectPtr)p; + dispose_object(axe_obj); + } + } + + dispose_xarray(obj_list); +} + +} // End of namespace Archetype +} // End of namespace Glk |