aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/archetype/interpreter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/archetype/interpreter.cpp')
-rw-r--r--engines/glk/archetype/interpreter.cpp478
1 files changed, 478 insertions, 0 deletions
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
new file mode 100644
index 0000000000..108b948605
--- /dev/null
+++ b/engines/glk/archetype/interpreter.cpp
@@ -0,0 +1,478 @@
+/* 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/interpreter.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/misc.h"
+#include "glk/archetype/saveload.h"
+#include "glk/archetype/timestamp.h"
+#include "glk/archetype/wrap.h"
+
+namespace Glk {
+namespace Archetype {
+
+int MainObject;
+
+void interpreter_init() {
+ MainObject = 0;
+}
+
+StringPtr MakeNewDynStr(const String &s) {
+ return NewDynStr(s);
+}
+
+int find_message(const String &message) {
+ void *p;
+
+ for (uint i = 0; i < g_vm->Vocabulary.size(); ++i) {
+ if (!index_xarray(g_vm->Vocabulary, i, p))
+ g_vm->writeln("Internal error - cannot index element %d of Vocabulary", i);
+ else if (message == *((StringPtr)p))
+ return i;
+ }
+
+ return -1;
+}
+
+bool convert_to(AclType target_type, ResultType &the_scalar) {
+ int code;
+ char dir_from = '\0';
+ int the_number = -1;
+ String s1;
+ void *p;
+ bool boolval = false;
+
+ if (target_type == the_scalar._kind)
+ return true;
+
+ switch (target_type) {
+ case QUOTE_LIT:
+ case TEXT_LIT:
+ case IDENT:
+ case RESERVED:
+ return false;
+ default:
+ break;
+ }
+
+ switch (the_scalar._kind) {
+ case NUMERIC:
+ dir_from = 'N';
+ the_number = the_scalar._numeric.acl_int;
+ break;
+
+ case MESSAGE:
+ dir_from = 'S';
+ if (index_xarray(g_vm->Vocabulary, the_scalar._msgTextQuote.index, p))
+ s1 = *(StringPtr)p;
+ break;
+
+ case TEXT_LIT:
+ case QUOTE_LIT:
+ dir_from = 'S';
+ if (index_xarray(g_vm->Literals, the_scalar._msgTextQuote.index, p))
+ s1 = *(StringPtr)p;
+ break;
+
+ case STR_PTR:
+ // string memory will be disposed ONLY if successful convert
+ dir_from = 'S';
+ s1 = *the_scalar._str.acl_str;
+ break;
+
+ case IDENT:
+ //with the_scalar do begin
+ dir_from = 'S';
+
+ switch (the_scalar._ident.ident_kind) {
+ case ENUMERATE_ID:
+ dir_from = 'N';
+ the_number = the_scalar._ident.ident_int;
+ break;
+
+ case OBJECT_ID:
+ if (the_scalar._ident.ident_int == 0)
+ s1 = "system";
+ else if (index_xarray(g_vm->Object_ID_List, the_scalar._ident.ident_int, p)) {
+ if (p == nullptr)
+ s1 = "null";
+ else
+ s1 = *(StringPtr)p;
+ } else {
+ return false;
+ }
+ break;
+
+ case TYPE_ID:
+ if (the_scalar._ident.ident_int == 0)
+ s1 = "null";
+ else if (index_xarray(g_vm->Type_ID_List, the_scalar._ident.ident_int, p))
+ s1 = *(StringPtr)p;
+ else
+ return false;
+ break;
+
+ case ATTRIBUTE_ID:
+ if (index_xarray(g_vm->Attribute_ID_List, the_scalar._ident.ident_int, p))
+ s1 = *((StringPtr)p);
+ else
+ return false;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case RESERVED:
+ if (the_scalar._reserved.keyword == RW_TRUE || the_scalar._reserved.keyword == RW_FALSE) {
+ dir_from = 'B';
+ boolval = (the_scalar._reserved.keyword == RW_TRUE);
+ } else {
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (target_type == STR_PTR || target_type == MESSAGE) {
+ if (the_scalar._kind == STR_PTR)
+ FreeDynStr(the_scalar._str.acl_str); // we know this will succeed
+
+ the_scalar._kind = target_type;
+
+ switch (dir_from) {
+ case 'N':
+ s1 = String::format("%d", the_number);
+ break;
+ case 'B':
+ s1 = boolval ? "TRUE" : "false";
+ break;
+ default:
+ break;
+ }
+
+ if (target_type == MESSAGE)
+ the_scalar._msgTextQuote.index = find_message(s1);
+ else
+ the_scalar._str.acl_str = NewDynStr(s1);
+
+ return true;
+ } else {
+ // numeric conversions
+ switch (dir_from) {
+ case 'N':
+ the_scalar._kind = NUMERIC;
+ the_scalar._numeric.acl_int = the_number;
+ return true;
+
+ case 'B':
+ the_scalar._kind = NUMERIC;
+ the_scalar._numeric.acl_int = boolval ? 1 : 0;
+ break;
+
+ case 'S':
+ s1.trim();
+ the_number = s1.val(&code);
+
+ if (code != 0) {
+ return false;
+ } else {
+ // successful
+ if (the_scalar._kind == STR_PTR)
+ FreeDynStr(the_scalar._str.acl_str); // memory no longer needed
+
+ the_scalar._kind = NUMERIC;
+ the_scalar._numeric.acl_int = the_number;
+ }
+
+ return true;
+
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void undefine(ResultType &result) {
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_UNDEFINED;
+}
+
+void cleanup(ResultType &result) {
+ if (result._kind == STR_PTR)
+ FreeDynStr(result._str.acl_str);
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_UNDEFINED;
+}
+
+void copy_result(ResultType &r1, const ResultType &r2) {
+ cleanup(r1);
+ r1 = r2;
+ if (r1._kind == STR_PTR)
+ r1._str.acl_str = NewDynStr(*r2._str.acl_str);
+}
+
+bool result_compare(short comparison, ResultType &r1, ResultType &r2) {
+ bool verdict = false;
+
+ // Try numeric reckoning first, then string reckoning
+ if (convert_to(NUMERIC, r1) && convert_to(NUMERIC, r2)) {
+ switch (comparison) {
+ case OP_EQ:
+ case OP_NE:
+ verdict = r1._numeric.acl_int == r2._numeric.acl_int;
+ break;
+ case OP_LT:
+ verdict = r1._numeric.acl_int < r2._numeric.acl_int;
+ break;
+ case OP_LE:
+ verdict = r1._numeric.acl_int <= r2._numeric.acl_int;
+ break;
+ case OP_GT:
+ verdict = r1._numeric.acl_int > r2._numeric.acl_int;
+ break;
+ case OP_GE:
+ verdict = r1._numeric.acl_int >= r2._numeric.acl_int;
+ break;
+ default:
+ break;
+ }
+
+ } else if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
+ switch (comparison) {
+ case OP_EQ:
+ case OP_NE:
+ verdict = *r1._str.acl_str == *r2._str.acl_str;
+ break;
+ case OP_LT:
+ verdict = *r1._str.acl_str < *r2._str.acl_str;
+ break;
+ case OP_LE:
+ verdict = *r1._str.acl_str <= *r2._str.acl_str;
+ break;
+ case OP_GT:
+ verdict = *r1._str.acl_str > *r2._str.acl_str;
+ break;
+ case OP_GE:
+ verdict = *r1._str.acl_str >= *r2._str.acl_str;
+ break;
+ default:
+ break;
+ }
+
+ } else if (r1._kind == r2._kind) {
+ switch (r1._kind) {
+ case RESERVED:
+ switch (comparison) {
+ case OP_EQ:
+ case OP_NE:
+ verdict = r1._reserved.keyword == r2._reserved.keyword;
+ break;
+ default:
+ break;
+ }
+
+ case IDENT:
+ if (r1._ident.ident_kind == r2._ident.ident_kind) {
+ switch (comparison) {
+ case OP_EQ:
+ case OP_NE:
+ verdict = r1._ident.ident_int == r2._ident.ident_int;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (comparison == OP_NE)
+ return !verdict;
+ else
+ return verdict;
+}
+
+bool assignment(ResultType &target, ResultType &value) {
+ ExprPtr e;
+
+ if (target._kind != ATTR_PTR) {
+ wraperr("Warning: attempted assignment to a non-attribute");
+ return false;
+ } else {
+ e = (ExprPtr)target._attr.acl_attr->data;
+
+ // If the current expression starts with an operator, we know it isn't a flat result
+ // and must therefore be disposed of before proceeding. Otherwise simply clean up
+ // the previous expression node
+ if (e->_kind != OPER) {
+ cleanup(*e);
+ } else {
+ dispose_expr(e);
+ e = (ExprPtr)malloc(sizeof(ExprNode));
+ undefine(*e);
+ }
+ }
+
+ copy_result(*e, value);
+ target._attr.acl_attr->data = e;
+
+ return true;
+}
+
+void write_result(ResultType &result) {
+ ResultType r1;
+
+ undefine(r1);
+ if (result._kind == STR_PTR)
+ wrapout(*result._str.acl_str, false);
+ else if (result._kind == RESERVED)
+ wrapout(Reserved_Wds[result._reserved.keyword], false);
+ else {
+ if (result._kind == ATTR_PTR)
+ copy_result(r1, *(ResultType *)result._attr.acl_attr->data);
+ else
+ copy_result(r1, result);
+ if (convert_to(STR_PTR, r1))
+ wrapout(*r1._str.acl_str, false);
+ cleanup(r1);
+ }
+}
+
+void display_result(ResultType &result) {
+ String enclose;
+
+ switch (result._kind) {
+ case STR_PTR:
+ case TEXT_LIT:
+ enclose = "\"";
+ break;
+ case QUOTE_LIT:
+ enclose = " ";
+ wrapout(">>", false);
+ break;
+ case MESSAGE:
+ enclose = "\'";
+ default:
+ enclose = ' ';
+ }
+
+ if (enclose != " ")
+ wrapout(enclose, false);
+ write_result(result);
+ if (enclose != " ")
+ wrapout(enclose, false);
+}
+
+void display_expr(ExprTree the_tree) {
+ if (the_tree->_kind != OPER) {
+ display_result(*the_tree);
+ } else {
+ if (Binary[the_tree->_oper.op_name]) {
+ wrapout(" (", false);
+ display_expr(the_tree->_oper.left);
+ wrapout(") ", false);
+ }
+
+ wrapout(Operators[the_tree->_oper.op_name], false);
+ wrapout(" (", false);
+ display_expr(the_tree->_oper.right);
+ wrapout(") ", false);
+ }
+}
+
+bool load_game(Common::ReadStream *f_in) {
+ int length;
+ char ch = '\0';
+ double fileVersion;
+
+ // First, check the initial version string against that in the misc unit
+ length = strlen(VERSION_STUB);
+ for (int i = 0; i < length; ++i) {
+ ch = (char)f_in->readByte();
+ if (ch != VERSION_STUB[i]) {
+ g_vm->writeln("This file is not an Archetype file.");
+ return false;
+ }
+ }
+
+ // Bleed off string version information
+ while (!f_in->eos() && ch != 26)
+ ch = f_in->readByte();
+
+ // Check encoded version
+ // TODO: Figure out real format
+ fileVersion = 0;
+ (void)f_in->readUint32LE();
+
+ if (fileVersion > VERSION_NUM) {
+ g_vm->writeln("This version of PERFORM is %.1f; file version is %.1f",
+ VERSION_NUM, fileVersion);
+ g_vm->writeln("Cannot PERFORM this file.");
+ return false;
+ }
+
+ // Get encryption information
+ Encryption = (EncryptionType)f_in->readUint16LE();
+
+ // Read the timestamp. It is used to verify saved game states, and also to prime the encryption
+ GTimeStamp = f_in->readUint32LE();
+
+ // Initialize the encrypter. This is done by using the global time stamp as a starting point
+ // and using the Encryption variable to decide the method. Be careful here; the PURPLE
+ // or Dynamic encryption works differently in that we have to set Encryption to UNPURPLE
+ // (since we're decoding) and then back to PURPLE again in case they save any game states.
+ // See load_game_state in the GAMESTAT unit for similar machinations
+ if (Encryption == PURPLE)
+ Encryption = UNPURPLE;
+ cryptinit(Encryption, GTimeStamp);
+
+ // Where's the main object?
+ MainObject = f_in->readSint32LE();
+
+ load_obj_list(f_in, g_vm->Object_List);
+ load_obj_list(f_in, g_vm->Type_List);
+
+ load_text_list(f_in, g_vm->Literals);
+ load_text_list(f_in, g_vm->Vocabulary);
+
+ if (Encryption == DEBUGGING_ON) {
+ g_vm->writeln("Loading debugging information");
+ load_id_info(f_in);
+ }
+
+ if (Encryption == UNPURPLE)
+ Encryption = PURPLE;
+
+ return true;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk