aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorPaul Gilbert2019-10-27 21:17:49 -0700
committerPaul Gilbert2019-11-11 18:20:29 -0800
commit7ee61fa5d4a6dfd6880e48b83a8dae1d485d1e94 (patch)
tree0272e5cc42c21fe9e8ed59291594bcfad5e343b3 /engines
parent0b3f59c3f87debc8fc25cb5e540be87904207cbb (diff)
downloadscummvm-rg350-7ee61fa5d4a6dfd6880e48b83a8dae1d485d1e94.tar.gz
scummvm-rg350-7ee61fa5d4a6dfd6880e48b83a8dae1d485d1e94.tar.bz2
scummvm-rg350-7ee61fa5d4a6dfd6880e48b83a8dae1d485d1e94.zip
GLK: ARCHETYPE: Added converted files
Diffstat (limited to 'engines')
-rw-r--r--engines/glk/archetype/archetype.cpp876
-rw-r--r--engines/glk/archetype/archetype.h95
-rw-r--r--engines/glk/archetype/array.cpp66
-rw-r--r--engines/glk/archetype/array.h69
-rw-r--r--engines/glk/archetype/crypt.cpp82
-rw-r--r--engines/glk/archetype/crypt.h64
-rw-r--r--engines/glk/archetype/error.cpp80
-rw-r--r--engines/glk/archetype/error.h49
-rw-r--r--engines/glk/archetype/expression.cpp129
-rw-r--r--engines/glk/archetype/expression.h90
-rw-r--r--engines/glk/archetype/game_stat.cpp121
-rw-r--r--engines/glk/archetype/game_stat.h46
-rw-r--r--engines/glk/archetype/heap_sort.cpp148
-rw-r--r--engines/glk/archetype/heap_sort.h48
-rw-r--r--engines/glk/archetype/id_table.cpp73
-rw-r--r--engines/glk/archetype/id_table.h77
-rw-r--r--engines/glk/archetype/interpreter.cpp478
-rw-r--r--engines/glk/archetype/interpreter.h130
-rw-r--r--engines/glk/archetype/keywords.cpp152
-rw-r--r--engines/glk/archetype/keywords.h149
-rw-r--r--engines/glk/archetype/linked_list.cpp90
-rw-r--r--engines/glk/archetype/linked_list.h80
-rw-r--r--engines/glk/archetype/misc.cpp197
-rw-r--r--engines/glk/archetype/misc.h184
-rw-r--r--engines/glk/archetype/parser.cpp291
-rw-r--r--engines/glk/archetype/parser.h89
-rw-r--r--engines/glk/archetype/saveload.cpp672
-rw-r--r--engines/glk/archetype/saveload.h79
-rw-r--r--engines/glk/archetype/semantic.cpp234
-rw-r--r--engines/glk/archetype/semantic.h112
-rw-r--r--engines/glk/archetype/statement.h98
-rw-r--r--engines/glk/archetype/string.cpp133
-rw-r--r--engines/glk/archetype/string.h143
-rw-r--r--engines/glk/archetype/sys_object.cpp305
-rw-r--r--engines/glk/archetype/sys_object.h46
-rw-r--r--engines/glk/archetype/timestamp.cpp50
-rw-r--r--engines/glk/archetype/timestamp.h62
-rw-r--r--engines/glk/archetype/token.cpp441
-rw-r--r--engines/glk/archetype/token.h62
-rw-r--r--engines/glk/archetype/wrap.cpp147
-rw-r--r--engines/glk/archetype/wrap.h65
-rw-r--r--engines/glk/module.mk21
42 files changed, 6619 insertions, 4 deletions
diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index d2409bfce3..4b19c57854 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -20,8 +20,16 @@
*
*/
-#include "glk/archetype/archetype.h"
#include "common/config-manager.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/crypt.h"
+#include "glk/archetype/expression.h"
+#include "glk/archetype/heap_sort.h"
+#include "glk/archetype/misc.h"
+#include "glk/archetype/saveload.h"
+#include "glk/archetype/sys_object.h"
+#include "glk/archetype/timestamp.h"
+#include "glk/archetype/wrap.h"
namespace Glk {
namespace Archetype {
@@ -29,15 +37,44 @@ namespace Archetype {
Archetype *g_vm;
Archetype::Archetype(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
- _saveSlot(-1) {
+ _saveSlot(-1) {
g_vm = this;
}
void Archetype::runGame() {
-
+ initialize();
+ interpret();
+ deinitialize();
}
bool Archetype::initialize() {
+ crypt_init();
+ expression_init();
+ heap_sort_init();
+ misc_init();
+ saveload_init();
+ sys_object_init();
+ timestamp_init();
+
+ // keywords
+ new_xarray(Literals);
+ new_xarray(Vocabulary);
+
+ new_xarray(Type_ID_List);
+ new_xarray(Object_ID_List);
+ new_xarray(Attribute_ID_List);
+
+ // parser
+ Abbreviate = 0x7fffffff;
+ new_list(Proximate);
+ new_list(object_names);
+ new_list(verb_names);
+
+ // semantic
+ new_xarray(Type_List);
+ new_xarray(Object_List);
+ NullStr = NewConstStr("null");
+
return true;
}
@@ -54,5 +91,838 @@ Common::Error Archetype::writeGameData(Common::WriteStream *ws) {
return Common::kWritingFailed;
}
+void Archetype::interpret() {
+ Translating = false;
+ bool success = loadGame();
+ _gameFile.close();
+
+ if (!success)
+ error("Could not load game");
+
+ ContextType context;
+ ResultType result;
+ undefine(result);
+
+ if (!send_message(OP_SEND, find_message("START"), MainObject, result, context))
+ error("Cannot execute; no ''START'' message for main object.");
+
+ cleanup(result);
+}
+
+bool Archetype::loadGame() {
+ return false;
+}
+
+void Archetype::write(const String &fmt, ...) {
+ // TODO
+}
+
+void Archetype::writeln(const String &fmt, ...) {
+ // TODO
+}
+
+void Archetype::readln(String &s) {
+ // TODO
+}
+
+char Archetype::ReadKey() {
+ // TODO
+ return '\0';
+}
+
+void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextType &context,
+ DesiredType desired) {
+ NodePtr np;
+ bool done, first_pass;
+ ListType attrs;
+ int parent;
+ void *p, *original;
+ ExprTree e;
+ ContextType c;
+
+ cleanup(result);
+
+ if (desired == NAME) {
+ result._kind = IDENT;
+ result._ident.ident_kind = ATTRIBUTE_ID;
+ result._ident.ident_int = the_attr;
+ return;
+ }
+
+ if (the_obj == 0)
+ // system object - all attributes UNDEFINED
+ return;
+
+ if (!index_xarray(Object_List, the_obj, original)) {
+ g_vm->writeln("Internal error: cannot reference object %d", the_obj);
+ return;
+ }
+
+ // Return UNDEFINED for attempting to reference any attribute of a destroyed object
+ if (original == nullptr)
+ return;
+
+ // It is important to change the context before a lookup so that any non-scalar expressions
+ // that are referenced will be evaluated in the light of that object's context
+ c = context;
+ c.self = the_obj; // references to self must be right
+ c.each = 0;
+
+ first_pass = true;
+ p = original;
+ done = false;
+
+ // Inheritance loop
+ do {
+ ObjectType &obj = *((ObjectPtr)p);
+ attrs = obj.attributes;
+ parent = obj.inherited_from;
+
+ np = find_item(attrs, the_attr);
+ if (np != nullptr || parent == 0) {
+ done = true;
+ } else {
+ // Track back
+ if (!index_xarray(Type_List, parent, p)) {
+ writeln("Internal error: lookup cannot find parent type %d", parent);
+ return;
+ }
+
+ first_pass = false;
+ }
+ } while (!done);
+
+ if (np == nullptr)
+ // not found anywhere
+ return;
+
+ switch (desired) {
+ case RVALUE:
+ eval_expr((ExprPtr)np->data, result, c, RVALUE);
+ break;
+
+ // Getting an inherited LVALUE is tricky. We must remember that since we have come
+ // this far, we definitely will return an ATTR_PTR result
+ case LVALUE:
+ if (first_pass) {
+ result._kind = ATTR_PTR;
+ result._attr.acl_attr = np;
+ } else {
+ // inherited - must create new node }
+ result._kind = ATTR_PTR;
+ result._attr.acl_attr = (NodePtr)malloc(sizeof(NodeType));
+
+ e = (ExprTree)malloc(sizeof(ExprNode));
+ undefine(*e);
+ eval_expr((ExprPtr)np->data, *e, c, RVALUE);
+
+ result._attr.acl_attr->data = e;
+ result._attr.acl_attr->key = the_attr;
+ insert_item(((ObjectPtr)original)->attributes, result._attr.acl_attr);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool Archetype::send_message(int transport, int message_sent, int recipient,
+ ResultType &result, ContextType &context) {
+ bool done, find_other;
+ ObjectPtr op, original;
+ ResultType r;
+ NodePtr np;
+ StatementPtr st;
+ void *p;
+ ContextType c;
+
+ if (message_sent == 0) {
+ cleanup(result);
+ return false;
+ }
+
+ if ((Debug & DEBUG_MSGS) > 0) {
+ r._kind = IDENT;
+ r._ident.ident_kind = OBJECT_ID;
+ r._ident.ident_int = context.self;
+ wrapout(" : ", false);
+ display_result(r);
+
+ if (transport == OP_SEND)
+ wrapout(" sending ", false);
+ else
+ wrapout(" passing ", false);
+
+ if (index_xarray(Vocabulary, message_sent, p)) {
+ String str = String::format("'%s'", ((StringPtr)p)->c_str());
+ wrapout(str, false);
+ }
+
+ if (transport == OP_SEND_TO_TYPE)
+ r._ident.ident_kind = TYPE_ID;
+
+ wrapout(" to ", false);
+ r._ident.ident_int = recipient;
+ display_result(r);
+ wrapout("", true);
+ }
+
+ // Trying to send a message to a destroyed object results in UNDEFINED
+
+ if ((((transport == OP_SEND_TO_TYPE) && index_xarray(Type_List, recipient, p))
+ || index_xarray(Object_List, recipient, p))
+ && (p != nullptr)) {
+ c = context;
+ c.each = 0;
+ c.message = message_sent;
+ if (transport == OP_SEND) {
+ c.sender = context.self;
+ c.self = recipient;
+ }
+
+ op = (ObjectPtr)p;
+ original = op;
+ done = false;
+ find_other = false;
+ while (!done) {
+ if (find_other) {
+ st = op->other;
+ } else {
+ np = find_item(op->methods, message_sent);
+ if (np != nullptr)
+ st = (StatementPtr)np->data;
+ else
+ st = nullptr;
+ }
+
+ if (st != nullptr) {
+ // found it
+ exec_stmt(st, result, c);
+ return true;
+ } else {
+ // no message for recipient
+ if (op->inherited_from == 0) {
+ if (find_other) {
+ done = true;
+ } else {
+ op = original;
+ find_other = true;
+ }
+ }
+ else if (index_xarray(Type_List, op->inherited_from, p)) {
+ op = (ObjectPtr)p;
+ } else {
+ wraperr("Internal error: invalid inheritance");
+ return false;
+ }
+ }
+ }
+ }
+
+ // If we get here, it means that there was not even a "default" handler for
+ // the message in the given object or its lineage. Return ABSENT
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_ABSENT;
+
+ return false;
+}
+
+void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &context, DesiredType desired) {
+ ResultType r1, r2;
+ int i;
+ ExprTree e;
+ bool b;
+
+ // It is very important to make sure that the "kind" fields of our temporary result variables
+ // are properly set to RESERVED/UNDEFINED before doing anything with them, so that if someone
+ // tries to clean them up later on, they won"t try to dispose of a string that isn't there
+ undefine(r1);
+ undefine(r2);
+
+ cleanup(result);
+
+ if (the_expr == nullptr)
+ return;
+
+ // Check: if this is a lone attribute, look it up in this object"s table
+ if (the_expr->_kind == IDENT && the_expr->_ident.ident_kind == ATTRIBUTE_ID)
+ lookup(context.self, the_expr->_ident.ident_int, result, context, desired);
+
+ else if (the_expr->_kind == RESERVED) {
+ // it is a special reserved word that requires an action
+ switch (the_expr->_reserved.keyword) {
+ case RW_READ:
+ case RW_KEY:
+ result._kind = STR_PTR;
+ if (the_expr->_reserved.keyword == RW_READ)
+ result._str.acl_str = ReadLine(true); // read full line
+ else
+ result._str.acl_str = ReadLine(false); // read single key
+
+ Rows = 0;
+ cursor_reset(); // user will have had to hit <RETURN>
+ break;
+
+ case RW_MESSAGE:
+ result._kind = MESSAGE;
+ result._msgTextQuote.index = context.message;
+ break;
+
+ case RW_EACH:
+ case RW_SELF:
+ case RW_SENDER:
+ result._kind = IDENT;
+ result._ident.ident_kind = OBJECT_ID;
+
+ switch (the_expr->_reserved.keyword) {
+ case RW_EACH:
+ result._ident.ident_int = context.each;
+ break;
+ case RW_SELF:
+ result._ident.ident_int = context.self;
+ break;
+ case RW_SENDER:
+ result._ident.ident_int = context.sender;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (the_expr->_kind == OPER) {
+ // It's an operator, need to evaulate it
+ switch (the_expr->_oper.op_name) {
+ case OP_SEND:
+ case OP_PASS:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+
+ if (r2._kind == IDENT && (r2._ident.ident_kind == OBJECT_ID || r2._ident.ident_kind == TYPE_ID)) {
+ // Object 0 is the system object and always receives string messages
+
+ if (r2._ident.ident_kind == OBJECT_ID && r2._ident.ident_int == 0) {
+ if (convert_to(STR_PTR, r1))
+ send_to_system(the_expr->_oper.op_name, *r1._str.acl_str, result, context);
+
+ } else if (convert_to(MESSAGE, r1)) {
+ if (r2._ident.ident_kind == TYPE_ID)
+ b = send_message(OP_SEND_TO_TYPE, r1._msgTextQuote.index, r2._ident.ident_int,
+ result, context);
+ else
+ b = send_message(the_expr->_oper.op_name, r1._msgTextQuote.index,
+ r2._ident.ident_int, result, context);
+ }
+ }
+ break;
+
+ case OP_DOT:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+
+ if (r1._kind == IDENT && r1._ident.ident_kind == OBJECT_ID) {
+ eval_expr(the_expr->_oper.right, r2, context, NAME);
+ if (r2._kind == IDENT && r2._ident.ident_kind == ATTRIBUTE_ID)
+ lookup(r1._ident.ident_int, r2._ident.ident_int, result, context, desired);
+ }
+ break;
+
+ case OP_ASSIGN:
+ if (desired == NAME)
+ return;
+
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ eval_expr(the_expr->_oper.left, r1, context, LVALUE);
+
+ if (!assignment(r1, result))
+ cleanup(result);
+ else if (desired == LVALUE) {
+ cleanup(result);
+ result._kind = ATTR_PTR;
+ result._attr.acl_attr = r1._attr.acl_attr;
+ }
+ break;
+
+ case OP_C_MULTIPLY:
+ case OP_C_DIVIDE:
+ case OP_C_PLUS:
+ case OP_C_MINUS:
+ case OP_C_CONCAT:
+ if (desired == NAME)
+ return;
+
+ // Do the two operations using a dummy expression node
+ e = (ExprTree)malloc(sizeof(ExprNode));
+ *e = *the_expr;
+
+ switch (the_expr->_oper.op_name) {
+ case OP_C_MULTIPLY:
+ e->_oper.op_name = OP_MULTIPLY;
+ break;
+ case OP_C_DIVIDE:
+ e->_oper.op_name = OP_DIVIDE;
+ break;
+ case OP_C_PLUS:
+ e->_oper.op_name = OP_PLUS;
+ break;
+ case OP_C_MINUS:
+ e->_oper.op_name = OP_MINUS;
+ break;
+ case OP_C_CONCAT:
+ e->_oper.op_name = OP_CONCAT;
+ break;
+ default:
+ break;
+ }
+
+ eval_expr(e, r1, context, RVALUE);
+ e->_oper.op_name = OP_ASSIGN;
+ e->_oper.right = &r1;
+
+ eval_expr(e, result, context, desired);
+ free(e);
+ break;
+
+ case OP_CHS:
+ case OP_NUMERIC:
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ if (!convert_to(NUMERIC, result))
+ cleanup(result);
+ else if (the_expr->_oper.op_name == OP_CHS)
+ result._numeric.acl_int = -result._numeric.acl_int;
+ break;
+
+ case OP_STRING:
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ if (!convert_to(STR_PTR, result))
+ cleanup(result);
+ break;
+
+ case OP_LENGTH:
+ eval_expr(the_expr->_oper.right, r1, context, RVALUE);
+ if (convert_to(STR_PTR, r1)) {
+ result._kind = NUMERIC;
+ result._numeric.acl_int = r1._str.acl_str->size();
+ }
+ break;
+
+ // For the random operator, we must be careful: ? "01234" should select a random digit
+ // out of that set, not attempt to convert it to 1234 and take a random number in the
+ // range 1 - 1234. However, we can neither immediately convert it to string, because
+ // ? 6 should produce a value in the range 1 - 6, not the character "6". }
+ case OP_RANDOM:
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ if (result._kind == NUMERIC)
+ // convert x < range to 1 <= x <= range
+ result._numeric.acl_int = g_vm->getRandomNumber(result._numeric.acl_int - 1) + 1;
+ else if (convert_to(STR_PTR, result)) {
+ // Replace the string with a single random character for it
+ String &s = *result._str.acl_str;
+ s = s[g_vm->getRandomNumber(s.size() - 1)];
+ }
+ break;
+
+ case OP_NOT:
+ result._kind = RESERVED;
+ if (eval_condition(the_expr->_oper.right, context))
+ result._reserved.keyword = RW_FALSE;
+ else
+ result._reserved.keyword = RW_TRUE;
+ break;
+
+ case OP_PLUS:
+ case OP_MINUS:
+ case OP_MULTIPLY:
+ case OP_DIVIDE:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(NUMERIC, r1) && convert_to(NUMERIC, r2)) {
+ result._kind = NUMERIC;
+ switch (the_expr->_oper.op_name) {
+ case OP_PLUS:
+ result._numeric.acl_int = r1._numeric.acl_int + r2._numeric.acl_int;
+ break;
+ case OP_MINUS:
+ result._numeric.acl_int = r1._numeric.acl_int - r2._numeric.acl_int;
+ break;
+ case OP_MULTIPLY:
+ result._numeric.acl_int = r1._numeric.acl_int * r2._numeric.acl_int;
+ break;
+ case OP_DIVIDE:
+ result._numeric.acl_int = r1._numeric.acl_int / r2._numeric.acl_int;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case OP_AND:
+ result._kind = RESERVED;
+ if (eval_condition(the_expr->_oper.left, context) && eval_condition(the_expr->_oper.right, context))
+ result._reserved.keyword = RW_TRUE;
+ else
+ result._reserved.keyword = RW_FALSE;
+ break;
+
+ case OP_OR:
+ if (eval_condition(the_expr->_oper.left, context) || eval_condition(the_expr->_oper.right, context))
+ result._reserved.keyword = RW_TRUE;
+ else
+ result._reserved.keyword = RW_FALSE;
+ break;
+
+ case OP_POWER:
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ if (convert_to(NUMERIC, r2) && convert_to(NUMERIC, r1)) {
+ result._kind = NUMERIC;
+ result._numeric.acl_int = 1;
+ for (i = 1; i <= r2._numeric.acl_int; ++i)
+ result._numeric.acl_int *= r1._numeric.acl_int;
+ }
+ break;
+
+ case OP_CONCAT:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
+ result._kind = STR_PTR;
+ result._str.acl_str = MakeNewDynStr(*r1._str.acl_str + *r2._str.acl_str);
+ }
+ break;
+
+ case OP_LEFTFROM:
+ case OP_RIGHTFROM:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(STR_PTR, r1) && convert_to(NUMERIC, r2)) {
+ result._kind = STR_PTR;
+ if (the_expr->_oper.op_name == OP_LEFTFROM)
+ result._str.acl_str = MakeNewDynStr(r1._str.acl_str->left(r2._numeric.acl_int));
+ else
+ result._str.acl_str = MakeNewDynStr(r1._str.acl_str->right(r2._numeric.acl_int));
+ }
+ break;
+
+ case OP_WITHIN:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
+ result._kind = NUMERIC;
+ result._numeric.acl_int = r2._str.acl_str->indexOf(*r1._str.acl_str);
+ if (result._numeric.acl_int == -1)
+ cleanup(result);
+ }
+ break;
+
+ case OP_EQ:
+ case OP_NE:
+ case OP_LT:
+ case OP_GT:
+ case OP_LE:
+ case OP_GE:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+
+ result._kind = RESERVED;
+ if (result_compare(the_expr->_oper.op_name, r1, r2))
+ result._reserved.keyword = RW_TRUE;
+ else
+ result._reserved.keyword = RW_FALSE;
+ break;
+
+ default:
+ g_vm->writeln("Internal error: \"%s\" not yet supported", Operators[the_expr->_oper.op_name]);
+ break;
+ }
+
+ cleanup(r1);
+ cleanup(r2);
+
+ if ((Debug & DEBUG_EXPR) > 0) {
+ wrapout(" -- ", false);
+ display_expr(the_expr);
+ wrapout(" ==> ", false);
+ display_result(result);
+ wrapout("", true);
+ }
+ } else {
+ switch (desired) {
+ case RVALUE:
+ copy_result(result, *the_expr);
+ break;
+ case LVALUE:
+ result = *the_expr;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool Archetype::eval_condition(ExprTree the_expr, ContextType &context) {
+ ResultType result;
+ bool failure;
+
+ undefine(result);
+ eval_expr(the_expr, result, context, RVALUE);
+
+ failure = (result._kind == RESERVED) && (result._reserved.keyword = RW_UNDEFINED
+ || result._reserved.keyword == RW_FALSE || result._reserved.keyword == RW_ABSENT);
+
+ cleanup(result);
+ return !failure;
+}
+
+void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType &context) {
+ NodePtr np;
+ void *p, *q;
+ ResultType r1, r2;
+ CasePairPtr this_case;
+ bool b;
+ ContextType c;
+ int i;
+ ObjectPtr the_object;
+ bool verbose;
+
+ undefine(r1);
+ undefine(r2);
+ cleanup(result);
+
+ verbose = (Debug & DEBUG_STMT) > 0;
+
+ if (verbose)
+ wrapout(" == ", false);
+
+ switch (the_stmt->_kind) {
+ case COMPOUND:
+ np = nullptr;
+ b = false;
+ while (!b && iterate_list(the_stmt->_compound.statements, np)) {
+ cleanup(result);
+ exec_stmt((StatementPtr)np->data, result, context);
+
+ b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+ }
+ break;
+
+ case ST_EXPR:
+ if (verbose)
+ display_expr(the_stmt->_expr.expression);
+
+ switch (the_stmt->_expr.expression->_kind) {
+ case QUOTE_LIT:
+ if (index_xarray(Literals, the_stmt->_expr.expression->_msgTextQuote.index, p)) {
+ result._kind = TEXT_LIT;
+ result._msgTextQuote.index = the_stmt->_expr.expression->_msgTextQuote.index;
+ wrapout(*((StringPtr)p), true);
+ }
+ break;
+
+ case MESSAGE:
+ b = send_message(OP_PASS, the_stmt->_expr.expression->_msgTextQuote.index,
+ context.self, result, context);
+ default:
+ eval_expr(the_stmt->_expr.expression, result, context, RVALUE);
+ break;
+ }
+ break;
+
+ case ST_WRITE:
+ case ST_WRITES:
+ case ST_STOP:
+ if (verbose) {
+ switch (the_stmt->_kind) {
+ case ST_WRITE:
+ wrapout("write ", false);
+ break;
+ case ST_WRITES:
+ wrapout("writes ", false);
+ break;
+ case ST_STOP:
+ wrapout("stop ", false);
+ break;
+ default:
+ break;
+ }
+
+ wrapout(" ", false);
+ np = nullptr;
+ while (iterate_list(the_stmt->_write.print_list, np)) {
+ display_expr((ExprTree)np->data);
+ if (np->next != the_stmt->_write.print_list)
+ wrapout(", ", false);
+ }
+
+ wrapout("", true);
+ }
+
+ np = nullptr;
+ while (iterate_list(the_stmt->_write.print_list, np)) {
+ cleanup(result);
+ eval_expr((ExprTree)np->data, result, context, RVALUE);
+ write_result(result);
+ }
+
+ if (the_stmt->_kind == ST_WRITE) {
+ wrapout("", true);
+ }
+ else if (the_stmt->_kind == ST_STOP) {
+ g_vm->writeln();
+ g_vm->writeln();
+ error("%s", VERSION);
+ }
+ break;
+
+ case ST_IF:
+ if (verbose) {
+ wrapout("if: Testing ", false);
+ display_expr(the_stmt->_if.condition);
+ }
+ if (eval_condition(the_stmt->_if.condition, context)) {
+ if (verbose)
+ wrapout(" Evaluated true; executing then branch", true);
+ exec_stmt(the_stmt->_if.then_branch, result, context);
+
+ }
+ else if (the_stmt->_if.else_branch != nullptr) {
+ if (verbose)
+ wrapout(" Evaluated false; executing else branch", true);
+ exec_stmt(the_stmt->_if.else_branch, result, context);
+ }
+ break;
+
+ case ST_CASE:
+ if (verbose) {
+ wrapout("case ", false);
+ display_expr(the_stmt->_case.test_expr);
+ wrapout(" of", false);
+ wrapout("", true);
+ }
+
+ eval_expr(the_stmt->_case.test_expr, r1, context, RVALUE);
+ np = nullptr;
+
+ while (iterate_list(the_stmt->_case.cases, np)) {
+ this_case = (CasePairPtr)np->data;
+
+ //with this_case^ do begin
+ eval_expr(this_case->value, r2, context, RVALUE);
+ if ((r2._kind == RESERVED && r2._reserved.keyword == RW_DEFAULT)
+ || result_compare(OP_EQ, r1, r2)) {
+ exec_stmt(this_case->action, result, context);
+ cleanup(r1);
+ cleanup(r2);
+ return;
+ }
+
+ cleanup(r2);
+ }
+
+ cleanup(result);
+ cleanup(r1);
+ break;
+
+ case ST_BREAK:
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_BREAK;
+ break;
+
+ case ST_FOR:
+ b = false;
+ c = context;
+ c.each = 1;
+
+ while (!b && c.each < (int)Object_List.size()) {
+ if (eval_condition(the_stmt->_loop.selection, c)) {
+ exec_stmt(the_stmt->_loop.action, result, c);
+ b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+ cleanup(result);
+ }
+
+ ++c.each;
+ }
+ break;
+
+ case ST_WHILE:
+ b = false;
+ while (!b && eval_condition(the_stmt->_loop.selection, context)) {
+ exec_stmt(the_stmt->_loop.action, result, context);
+ b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+ cleanup(result);
+ }
+ break;
+
+ case ST_CREATE:
+ eval_expr(the_stmt->_create.new_name, r1, context, LVALUE);
+
+ // Attempt a dummy assignment just to see if it works
+ result._kind = IDENT;
+ result._ident.ident_kind = OBJECT_ID;
+ result._ident.ident_int = 0;
+
+ if (!assignment(r1, result)) {
+ cleanup(result);
+ }
+ else {
+ // do it for real
+ the_object = (ObjectPtr)malloc(sizeof(ObjectType));
+ the_object->inherited_from = the_stmt->_create.archetype;
+ new_list(the_object->attributes);
+ new_list(the_object->methods);
+ the_object->other = nullptr;
+ p = the_object;
+
+ // NOTE: Search the list for an empty slot; if none found, append
+ i = Dynamic;
+ b = true;
+ while (access_xarray(Object_List, i, q, PEEK_ACCESS) && q != nullptr)
+ ++i;
+
+ if (i >= (int)Object_List.size())
+ append_to_xarray(Object_List, p);
+ else
+ b = access_xarray(Object_List, i, p, POKE_ACCESS);
+
+ // Now we know its number; go back and update the result"s object reference.
+ // "Return" this same value
+ ((ExprPtr)r1._attr.acl_attr->data)->_ident.ident_int = i;
+ copy_result(result, *(ExprPtr)r1._attr.acl_attr->data);
+
+ cleanup(r1);
+ }
+ break;
+
+ // Just dispose of the indicated object in the Object_List. Shrink the list only if
+ // the very last object was destroyed
+ case ST_DESTROY:
+ eval_expr(the_stmt->_destroy.victim, result, context, RVALUE);
+ if (result._kind == IDENT && result._ident.ident_kind == OBJECT_ID
+ && index_xarray(Object_List, result._ident.ident_int, p)) {
+ the_object = (ObjectPtr)p;
+ dispose_object(the_object);
+ p = nullptr;
+ b = access_xarray(Object_List, result._ident.ident_int, p, POKE_ACCESS);
+ if (result._ident.ident_int == ((int)Object_List.size() - 1))
+ shrink_xarray(Object_List);
+ } else {
+ wraperr("Can only destroy previously created objects");
+ }
+
+ cleanup(result);
+ break;
+
+ default:
+ wraperr("Internal error: statement not supported yet");
+ break;
+ }
+
+ if (verbose)
+ wrapout("", true); // finish off dangling lines
+}
+
} // End of namespace Archetype
} // End of namespace Glk
diff --git a/engines/glk/archetype/archetype.h b/engines/glk/archetype/archetype.h
index 709ebc643b..dec950ca74 100644
--- a/engines/glk/archetype/archetype.h
+++ b/engines/glk/archetype/archetype.h
@@ -24,6 +24,11 @@
#define ARCHETYPE_ARCHETYPE
#include "glk/glk_api.h"
+#include "glk/archetype/array.h"
+#include "glk/archetype/interpreter.h"
+#include "glk/archetype/semantic.h"
+#include "glk/archetype/statement.h"
+#include "glk/archetype/string.h"
namespace Glk {
namespace Archetype {
@@ -34,6 +39,23 @@ namespace Archetype {
class Archetype : public GlkAPI {
private:
int _saveSlot;
+ bool Translating;
+public:
+ // keywords.cpp
+ XArrayType Literals, Vocabulary;
+ XArrayType Type_ID_List, Object_ID_List, Attribute_ID_List;
+
+ // parser.cpp
+ String Command;
+ int Abbreviate;
+ ListType Proximate;
+ ListType verb_names;
+ ListType object_names;
+
+ // semantic.cpp
+ XArrayType Type_List, Object_List;
+ ListType Overlooked;
+ StringPtr NullStr;
private:
/**
* Engine initialization
@@ -44,6 +66,64 @@ private:
* Engine cleanup
*/
void deinitialize();
+
+ /**
+ * Main interpreter method
+ */
+ void interpret();
+
+ /**
+ * Loads the text adventure game
+ */
+ bool loadGame();
+
+ /**
+ * Given an object number, attribute number, anddesired_type, returns the value of the lookup
+ * in the given result.If the desired_type is LVALUE, then it creates a new attribute node
+ * in the object's own attribute list(if not already existing) and returns a pointer to it.
+ * If RVALUE, it evaluates any expression it may find, returning the result of the evaluation.
+ *
+ * Also performs inheritance, looking back through the object's family tree to find the attribute.
+ */
+ void lookup(int the_obj, int the_attr, ResultType &result, ContextType &context, DesiredType desired);
+
+ /**
+ * Sends the given message number to the object of the given number. This procedure performs
+ * inheritance; that is, it will search back through the object's ancestry in order to find
+ * someone to perform the message.Has to do something tricky with the default message:
+ * it must first search the entire ancestry for an explicit message, then search again for
+ * a default, if none found.
+ * @param transport how to send the message : sending to an object,
+ * passing to an object, or sending(passing) to a type.
+ * @param message message to send
+ * @param recipient number of object to receive message
+ * @param result Output result of the sending
+ * @param context Context
+ * @returns true if the recipient handles the message; false if it doesn't
+ */
+ bool send_message(int transport, int message_sent, int recipient, ResultType &result,
+ ContextType &context);
+
+ /**
+ * Evaluates the given expression
+ */
+ void eval_expr(ExprTree the_expr, ResultType &result, ContextType &context, DesiredType desired);
+
+ /**
+ * Evaluates the given expression as though it were a condition. Will succeed if the given
+ * expression is not UNDEFINED and not false.
+ * @param the_expr Expression to evaluate
+ * @returns true if the condition can be considered true; false otherwise
+ */
+ bool eval_condition(ExprTree the_expr, ContextType &context);
+
+ /**
+ * Given a pointer to a statement, executes that statement. Very heavily called
+ * @param the_stmt pointer to statement to be executed
+ * @param result the "value" of the execution(for example, the last expression
+ * of a compound statement
+ */
+ void exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType &context);
public:
/**
* Constructor
@@ -78,6 +158,21 @@ public:
bool loadingSavegame() const {
return _saveSlot != -1;
}
+
+ /**
+ * Write some text to the screen
+ */
+ void write(const String &fmt, ...);
+
+ /**
+ * Write a line to the screen
+ */
+ void writeln(const String &fmt, ...);
+ void writeln() { writeln(""); }
+
+ void readln(String &s);
+
+ char ReadKey();
};
extern Archetype *g_vm;
diff --git a/engines/glk/archetype/array.cpp b/engines/glk/archetype/array.cpp
new file mode 100644
index 0000000000..b816306fc3
--- /dev/null
+++ b/engines/glk/archetype/array.cpp
@@ -0,0 +1,66 @@
+/* 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/array.h"
+
+namespace Glk {
+namespace Archetype {
+
+void new_xarray(XArrayType &the_xarray) {
+ the_xarray.clear();
+}
+
+void dispose_xarray(XArrayType &the_xarray) {
+ the_xarray.clear();
+}
+
+void append_to_xarray(XArrayType &the_xarray, void *element) {
+ the_xarray.push_back(element);
+}
+
+bool access_xarray(XArrayType &the_xarray, int index, void *&result, AccessType direction) {
+ if (index < 0 || index >= (int)the_xarray.size())
+ return false;
+
+ switch (direction) {
+ case PEEK_ACCESS:
+ result = the_xarray[index];
+ break;
+ case POKE_ACCESS:
+ the_xarray[index] = result;
+ break;
+ }
+
+ return true;
+}
+
+bool index_xarray(XArrayType &the_xarray, int index, void *&result) {
+ return access_xarray(the_xarray, index, result, PEEK_ACCESS);
+}
+
+void shrink_xarray(XArrayType &the_xarray) {
+ if (!the_xarray.empty())
+ the_xarray.resize(the_xarray.size() - 1);
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/array.h b/engines/glk/archetype/array.h
new file mode 100644
index 0000000000..17379f1057
--- /dev/null
+++ b/engines/glk/archetype/array.h
@@ -0,0 +1,69 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_XARRAY
+#define ARCHETYPE_XARRAY
+
+#include "common/array.h"
+
+namespace Glk {
+namespace Archetype {
+
+typedef Common::Array<void *> XArrayType;
+
+enum AccessType { POKE_ACCESS, PEEK_ACCESS };
+
+/**
+ * Initialize an extendible array
+ */
+extern void new_xarray(XArrayType &the_xarray);
+
+/**
+ * Disposes the array
+ */
+extern void dispose_xarray(XArrayType &the_xarray);
+
+/**
+ * Appends a new element to the end of the array
+ */
+extern void append_to_xarray(XArrayType &the_xarray, void *element);
+
+/**
+ * Access a given index in the array to either set or retrieve an entry
+ * @param the_xarray The array to access
+ */
+extern bool access_xarray(XArrayType &the_xarray, int index, void *&result, AccessType direction);
+
+/**
+ * Access a given index in the array
+ */
+extern bool index_xarray(XArrayType &the_xarray, int index, void *&result);
+
+/**
+ * Removes the last element of the array
+ */
+extern void shrink_xarray(XArrayType &the_xarray);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/crypt.cpp b/engines/glk/archetype/crypt.cpp
new file mode 100644
index 0000000000..d99dab0907
--- /dev/null
+++ b/engines/glk/archetype/crypt.cpp
@@ -0,0 +1,82 @@
+/* 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/crypt.h"
+#include "glk/archetype/archetype.h"
+
+namespace Glk {
+namespace Archetype {
+
+byte CryptMask;
+EncryptionType Encryption;
+
+void crypt_init() {
+ Encryption = NONE;
+ CryptMask = 0x55;
+}
+
+void cryptinit(EncryptionType crypt_kind, uint seed) {
+ CryptMask = seed & 0xff;
+ Encryption = crypt_kind;
+
+ if (Encryption == COMPLEX)
+ g_vm->setRandomNumberSeed(seed);
+}
+
+void cryptstr(Common::String &s) {
+ byte nextMask;
+
+ switch (Encryption) {
+ case SIMPLE:
+ for (uint i = 0; i < s.size(); ++i)
+ s.setChar(s[i] ^ CryptMask, i);
+ break;
+
+ case PURPLE:
+ for (uint i = 0; i < s.size(); ++i) {
+ s.setChar(s[i] ^ CryptMask, i);
+ CryptMask += s[i] & 7;
+ }
+ break;
+
+ case UNPURPLE:
+ for (uint i = 0; i < s.size(); ++i) {
+ nextMask = CryptMask + (s[i] & 7);
+ s.setChar(s[i] ^ CryptMask, i);
+ CryptMask = nextMask;
+ }
+ break;
+
+ case COMPLEX:
+ for (uint i = 0; i < s.size(); ++i) {
+ s.setChar(s[i] ^ CryptMask, i);
+ CryptMask = g_vm->getRandomNumber(0x100);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/crypt.h b/engines/glk/archetype/crypt.h
new file mode 100644
index 0000000000..d978210e9a
--- /dev/null
+++ b/engines/glk/archetype/crypt.h
@@ -0,0 +1,64 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_CRYPT
+#define ARCHETYPE_CRYPT
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum EncryptionType {
+ NONE, SIMPLE, PURPLE, UNPURPLE, COMPLEX, DEBUGGING_ON
+};
+
+extern byte CryptMask;
+extern EncryptionType Encryption;
+
+/**
+ * Initializes fields local to the file
+ */
+extern void crypt_init();
+
+extern void cryptinit(EncryptionType crypt_kind, uint seed);
+
+/**
+ * Encrypts or decrypts a string.Since all encryption methods are based on XOR,
+ * the same method both encrypts anddecrypts.
+ * If <method> is SIMPLE, the CryptMask is simply XORed with each byte in the string.
+ * If <method> is PURPLE, the CryptMask is changed each time after using it,
+ * by adding to it the lowest three bits of the result of the last encrypted
+ * byte.This way the mask changes frequently anddynamically in a way that
+ * is difficult to predict.
+ * If <method> is UNPURPLE, the same algorithm as PURPLE is used except that
+ * the next CryptMask must be determined before altering the byte under consideration.
+ * if <method> is COMPLEX, a pseudorandom sequence is used to alter the
+ * CryptMask.This can make prediction well-nigh impossible.
+ */
+extern void cryptstr(Common::String &s);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/error.cpp b/engines/glk/archetype/error.cpp
new file mode 100644
index 0000000000..18cb4d6acb
--- /dev/null
+++ b/engines/glk/archetype/error.cpp
@@ -0,0 +1,80 @@
+/* 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/error.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/token.h"
+
+namespace Glk {
+namespace Archetype {
+
+void hit_eof(progfile &f, AclType expecting, int specific) {
+ if (KeepLooking) {
+ KeepLooking = false;
+ g_vm->write("Found end of file; expected ");
+ write_token(expecting, specific);
+ g_vm->writeln();
+ }
+}
+
+void expected(progfile &f, AclType expect_ttype, int expect_specific) {
+ if (KeepLooking) {
+ f.sourcePos();
+ g_vm->write("Expected ");
+ write_token(expect_ttype, expect_specific);
+ g_vm->write("; found ");
+ write_token(f.ttype, f.tnum);
+ g_vm->writeln();
+ }
+}
+
+void expect_general(progfile &f, const String &general_desc) {
+ if (KeepLooking) {
+ f.sourcePos();
+ g_vm->write("Expected %s; found ", general_desc.c_str());
+ write_token(f.ttype, f.tnum);
+ g_vm->writeln();
+ }
+}
+
+void error_message(progfile &f, const String &message) {
+ if (KeepLooking) {
+ f.sourcePos();
+ g_vm->writeln(message);
+ }
+}
+
+bool insist_on(progfile &f, AclType some_type, int some_number) {
+ if (!get_token(f)) {
+ hit_eof(f, some_type, some_number);
+ return false;
+ } else if (f.ttype != some_type && f.tnum != some_number) {
+ expected(f, some_type, some_number);
+ KeepLooking = false;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/error.h b/engines/glk/archetype/error.h
new file mode 100644
index 0000000000..c6b70c0228
--- /dev/null
+++ b/engines/glk/archetype/error.h
@@ -0,0 +1,49 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_ERROR
+#define ARCHETYPE_ERROR
+
+/* Writes out all kinds of compile-time errors.Does not perform a halt;
+ * expects the program itself to "unravel" the process
+ */
+
+#include "glk/archetype/misc.h"
+
+namespace Glk {
+namespace Archetype {
+
+extern void hit_eof(progfile &f, AclType expecting, int specific);
+extern void expected(progfile &f, AclType expect_ttype, int expect_specific);
+extern void expect_general(progfile &f, const String &general_desc);
+extern void error_message(progfile &f, const String &message);
+
+/**
+ * Used when a particular token is insisted upon by the syntax, usually
+ * for readability.It will be an error for the token not to exist.
+ */
+extern bool insist_on(progfile &f, AclType some_type, int some_number);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/expression.cpp b/engines/glk/archetype/expression.cpp
new file mode 100644
index 0000000000..6f71dd502d
--- /dev/null
+++ b/engines/glk/archetype/expression.cpp
@@ -0,0 +1,129 @@
+/* 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/expression.h"
+
+namespace Glk {
+namespace Archetype {
+
+bool Right_Assoc[NUM_OPERS + 1];
+bool Binary[NUM_OPERS + 1];
+int8 Precedence[NUM_OPERS + 1];
+
+void expression_init() {
+ Binary[OP_LPAREN] = false;
+ Binary[OP_DOT] = true;
+ Binary[OP_CHS] = false;
+ Binary[OP_NUMERIC] = false;
+ Binary[OP_STRING] = false;
+ Binary[OP_RANDOM] = false;
+ Binary[OP_LENGTH] = false;
+ Binary[OP_POWER] = true;
+ Binary[OP_MULTIPLY] = true;
+ Binary[OP_DIVIDE] = true;
+ Binary[OP_PLUS] = true;
+ Binary[OP_MINUS] = true;
+ Binary[OP_CONCAT] = true;
+ Binary[OP_WITHIN] = true;
+ Binary[OP_LEFTFROM] = true;
+ Binary[OP_RIGHTFROM] = true;
+ Binary[OP_EQ] = true;
+ Binary[OP_NE] = true;
+ Binary[OP_GT] = true;
+ Binary[OP_LT] = true;
+ Binary[OP_GE] = true;
+ Binary[OP_LE] = true;
+ Binary[OP_NOT] = false;
+ Binary[OP_AND] = true;
+ Binary[OP_OR] = true;
+ Binary[OP_C_MULTIPLY] = true;
+ Binary[OP_C_DIVIDE] = true;
+ Binary[OP_C_PLUS] = true;
+ Binary[OP_C_MINUS] = true;
+ Binary[OP_C_CONCAT] = true;
+ Binary[OP_ASSIGN] = true;
+ Binary[OP_SEND] = true;
+ Binary[OP_PASS] = true;
+
+ // Initialize the Right_Assoc table as follows : anything unary must be right-associative;
+ // all others are assumed left-associative.After the loop, right-associative binary operators
+ // are explicity set
+ for (int i = 0; i <= NUM_OPERS; ++i)
+ Right_Assoc[i] = !Binary[i];
+
+ Right_Assoc[OP_POWER] = true;
+ Right_Assoc[OP_C_MULTIPLY] = true;
+ Right_Assoc[OP_C_DIVIDE] = true;
+ Right_Assoc[OP_C_PLUS] = true;
+ Right_Assoc[OP_C_MINUS] = true;
+ Right_Assoc[OP_C_CONCAT] = true;
+ Right_Assoc[OP_ASSIGN] = true;
+
+
+ Precedence[OP_LPAREN] = 14; // must always be the higest
+ Precedence[OP_DOT] = 13;
+
+ Precedence[OP_CHS] = 12;
+ Precedence[OP_NUMERIC] = 12;
+ Precedence[OP_STRING] = 12;
+ Precedence[OP_RANDOM] = 12;
+ Precedence[OP_LENGTH] = 12;
+
+ Precedence[OP_POWER] = 11;
+
+ Precedence[OP_MULTIPLY] = 10;
+ Precedence[OP_DIVIDE] = 10;
+
+ Precedence[OP_PLUS] = 9;
+ Precedence[OP_MINUS] = 9;
+ Precedence[OP_CONCAT] = 9;
+
+ Precedence[OP_WITHIN] = 8;
+
+ Precedence[OP_LEFTFROM] = 7;
+ Precedence[OP_RIGHTFROM] = 7;
+
+ Precedence[OP_SEND] = 6;
+ Precedence[OP_PASS] = 6;
+
+ Precedence[OP_EQ] = 5;
+ Precedence[OP_NE] = 5;
+ Precedence[OP_GT] = 5;
+ Precedence[OP_LT] = 5;
+ Precedence[OP_GE] = 5;
+ Precedence[OP_LE] = 5;
+
+ Precedence[OP_NOT] = 4;
+ Precedence[OP_AND] = 3;
+ Precedence[OP_OR] = 2;
+
+ Precedence[OP_C_MULTIPLY] = 1;
+ Precedence[OP_C_DIVIDE] = 1;
+ Precedence[OP_C_PLUS] = 1;
+ Precedence[OP_C_MINUS] = 1;
+ Precedence[OP_C_CONCAT] = 1;
+ Precedence[OP_ASSIGN] = 1;
+}
+
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/expression.h b/engines/glk/archetype/expression.h
new file mode 100644
index 0000000000..3fc600787a
--- /dev/null
+++ b/engines/glk/archetype/expression.h
@@ -0,0 +1,90 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_EXPRESSION
+#define ARCHETYPE_EXPRESSION
+
+#include "glk/archetype/keywords.h"
+#include "glk/archetype/linked_list.h"
+#include "glk/archetype/misc.h"
+
+namespace Glk {
+namespace Archetype {
+
+const int OP_LPAREN = NUM_OPERS + 1; // book-keeping operator
+const int OP_SEND_TO_TYPE = NUM_OPERS + 2; // for use with interpreter
+
+struct OperNode {
+ int8 op_name;
+ union ExprNode *left, *right;
+};
+
+struct MessageTextQuoteNode {
+ int index;
+};
+
+struct NumericNode {
+ int acl_int;
+};
+
+struct StrNode {
+ StringPtr acl_str;
+};
+
+struct AttrNode {
+ NodePtr acl_attr;
+};
+
+struct ReservedNode {
+ int8 keyword;
+};
+
+struct IdentNode {
+ ClassifyType ident_kind;
+ int ident_int;
+};
+
+union ExprNode {
+ AclType _kind;
+ OperNode _oper;
+ NumericNode _numeric;
+ MessageTextQuoteNode _msgTextQuote;
+ StrNode _str;
+ AttrNode _attr;
+ ReservedNode _reserved;
+ IdentNode _ident;
+};
+
+typedef ExprNode *ExprPtr;
+typedef ExprPtr ExprTree;
+
+// Global variables
+extern bool Right_Assoc[NUM_OPERS + 1];
+extern bool Binary[NUM_OPERS + 1];
+extern int8 Precedence[NUM_OPERS + 1];
+
+extern void expression_init();
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/game_stat.cpp b/engines/glk/archetype/game_stat.cpp
new file mode 100644
index 0000000000..9f6a8b85d6
--- /dev/null
+++ b/engines/glk/archetype/game_stat.cpp
@@ -0,0 +1,121 @@
+/* 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/game_stat.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/crypt.h"
+#include "glk/archetype/saveload.h"
+#include "glk/archetype/statement.h"
+#include "glk/archetype/timestamp.h"
+
+namespace Glk {
+namespace Archetype {
+
+void save_game_state(Common::WriteStream *bfile, XArrayType &objects) {
+ int i;
+ void *p;
+
+ // Write out the timestamp associated with the original game
+ bfile->writeUint32LE(GTimeStamp);
+
+ // Get the encryption straight - reset the seed
+ cryptinit(Encryption, GTimeStamp);
+
+ for (i = 0; i < Dynamic - 1; ++i) {
+ if (index_xarray(objects, i, p)) {
+ ObjectPtr op = (ObjectPtr)p;
+ bfile->writeUint32LE(vContSeq);
+
+ dump_item_list(bfile, op->attributes, EXPR_LIST);
+ }
+ }
+
+ for (i = Dynamic; i < (int)objects.size(); ++i) {
+ if (index_xarray(objects, i, p)) {
+ bfile->writeUint32LE(vContSeq);
+ dump_object(bfile, (ObjectPtr)p);
+ }
+ }
+
+ bfile->writeUint32LE(vEndSeq);
+}
+
+bool load_game_state(Common::ReadStream *bfile, XArrayType &objects) {
+ int i;
+ void *p;
+ ObjectPtr op;
+ TimestampType tstamp;
+ StatementKind sentinel;
+
+ // Check the time stamp
+ tstamp = bfile->readUint32LE();
+ if (tstamp != GTimeStamp) {
+ g_vm->writeln("State file does not match original .ACX file");
+ return false;
+ }
+
+ // Get the encryption straight - reset the seed.Be careful upon loading since we have
+ // to do UNPURPLE instead of PURPLE
+ if (Encryption == PURPLE)
+ Encryption = UNPURPLE;
+ cryptinit(Encryption, GTimeStamp);
+
+ // Need to flush out the previous attributes andload in the new ones. Dynamically allocated
+ // objects are a little different since they might vary between game states
+ for (i = 0; i < Dynamic - 1; ++i) {
+ if (index_xarray(objects, i, p)) {
+ sentinel = (StatementKind)bfile->readUint32LE();
+ op = (ObjectPtr)p;
+ dispose_item_list(op->attributes, EXPR_LIST);
+ load_item_list(bfile, op->attributes, EXPR_LIST);
+ }
+ }
+
+ // Flush dynamic objects.Dispose of each object andshrink back the xarray
+
+ for (i = objects.size() - 1; i >= Dynamic; --i) {
+ if (index_xarray(objects, i, p)) {
+ op = (ObjectPtr)p;
+ dispose_object(op);
+ }
+
+ shrink_xarray(objects);
+ }
+
+ // sentinel has been set from before
+ sentinel = (StatementKind)bfile->readUint32LE();
+ while (sentinel == CONT_SEQ) {
+ load_object(bfile, op);
+ p = op;
+ append_to_xarray(objects, p);
+
+ sentinel = (StatementKind)bfile->readUint32LE();
+ }
+
+ if (Encryption == UNPURPLE)
+ Encryption = PURPLE;
+
+ return true;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/game_stat.h b/engines/glk/archetype/game_stat.h
new file mode 100644
index 0000000000..ae4a1bb2ef
--- /dev/null
+++ b/engines/glk/archetype/game_stat.h
@@ -0,0 +1,46 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_GAME_STAT
+#define ARCHETYPE_GAME_STAT
+
+/* Functions and procedures that help with the saving and loading of
+ * game states. Only the attribute records of a given object list are ever
+ * saved or loaded; statements and other object information, such as
+ * the Dynamic pointer, are really constant across a game; as long as
+ * we know that the game states belong to a particular game, we don't
+ * need to save any more.
+ */
+
+#include "glk/archetype/array.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace Archetype {
+
+extern void save_game_state(Common::WriteStream *bfile, XArrayType &objects);
+extern bool load_game_state(Common::ReadStream *bfile, XArrayType &objects);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/heap_sort.cpp b/engines/glk/archetype/heap_sort.cpp
new file mode 100644
index 0000000000..98753ccc70
--- /dev/null
+++ b/engines/glk/archetype/heap_sort.cpp
@@ -0,0 +1,148 @@
+/* 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/heap_sort.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/misc.h"
+
+namespace Glk {
+namespace Archetype {
+
+const char *const CANT_PEEK = "Internal error: cannot peek into heap";
+const char *const CANT_POKE = "Internal error: cannot poke into heap";
+
+HeapType H;
+
+void heap_sort_init() {
+ new_xarray(H);
+}
+
+static bool lighter(const Element one, const Element two) {
+ return *static_cast<StringPtr>(one) < *static_cast<StringPtr>(two);
+}
+
+static void heapup() {
+ int L, parent;
+ Element Lp, parentp = nullptr;
+ Element temp;
+
+ L = H.size();
+ while (L > 0) {
+ if ((L % 2) == 0)
+ parent = L / 2;
+ else
+ parent = (L - 1) / 2;
+
+ if (!(access_xarray(H, L, Lp, PEEK_ACCESS) && access_xarray(H, parent, parentp, PEEK_ACCESS)))
+ g_vm->writeln(CANT_PEEK);
+
+ if (lighter(Lp, parentp)) {
+ temp = parentp;
+ if (!(access_xarray(H, parent, Lp, POKE_ACCESS) && access_xarray(H, L, temp, POKE_ACCESS)))
+ g_vm->writeln(CANT_POKE);
+ L = parent;
+ } else {
+ L = 0;
+ }
+ }
+}
+
+static void heapdown() {
+ uint L, compare, lc, rc;
+ Element lp;
+ Element lcp, rcp;
+ Element comparep;
+ Element temp;
+
+ L = 0;
+ while (L < H.size()) {
+ lc = L * 2;
+ if (lc >= H.size()) {
+ L = lc;
+ } else {
+ rc = lc + 1;
+ if (!access_xarray(H, lc, lcp, PEEK_ACCESS))
+ g_vm->writeln(CANT_PEEK);
+
+ if (rc >= H.size()) {
+ compare = lc;
+ comparep = lcp;
+ } else {
+ if (!access_xarray(H, rc, rcp, PEEK_ACCESS))
+ g_vm->writeln(CANT_PEEK);
+ if (lighter(lcp, rcp)) {
+ compare = lc;
+ comparep = lcp;
+ } else {
+ compare = rc;
+ comparep = rcp;
+ }
+ }
+
+ if (!access_xarray(H, L, lp, PEEK_ACCESS))
+ g_vm->writeln(CANT_PEEK);
+ if (!lighter(comparep, lp)) {
+ temp = comparep;
+ if (!(access_xarray(H, compare, lp, POKE_ACCESS) && access_xarray(H, L, temp, POKE_ACCESS)))
+ g_vm->writeln(CANT_POKE);
+ L = compare;
+ } else {
+ L = H.size();
+ }
+ }
+ }
+}
+
+bool pop_heap(Element &e) {
+ Element temp;
+
+ if (H.empty()) {
+ return false;
+ } else {
+ if (!(access_xarray(H, 0, e, PEEK_ACCESS) && access_xarray(H, H.size() - 1, temp, PEEK_ACCESS)
+ && access_xarray(H, 0, temp, POKE_ACCESS)))
+ g_vm->writeln(CANT_PEEK);
+
+ shrink_xarray(H);
+ heapdown();
+ return true;
+ }
+}
+
+void drop_on_heap(Element e) {
+ append_to_xarray(H, e);
+ heapup();
+}
+
+void drop_str_on_heap(const String &s) {
+ StringPtr sp = NewDynStr(s);
+ void *p = (void *)sp;
+ drop_on_heap(p);
+}
+
+void reinit_heap() {
+ dispose_xarray(H);
+ new_xarray(H);
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/heap_sort.h b/engines/glk/archetype/heap_sort.h
new file mode 100644
index 0000000000..5e2512407f
--- /dev/null
+++ b/engines/glk/archetype/heap_sort.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_HEAP_SORT
+#define ARCHETYPE_HEAP_SORT
+
+#include "glk/archetype/array.h"
+#include "glk/archetype/string.h"
+
+namespace Glk {
+namespace Archetype {
+
+typedef XArrayType HeapType;
+typedef void *Element;
+
+extern HeapType H;
+
+// Public methods
+extern void heap_sort_init();
+extern bool pop_heap(Element &e);
+extern void drop_on_heap(Element e);
+extern void drop_str_on_heap(const String &s);
+extern void reinit_heap();
+
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/id_table.cpp b/engines/glk/archetype/id_table.cpp
new file mode 100644
index 0000000000..a611503b71
--- /dev/null
+++ b/engines/glk/archetype/id_table.cpp
@@ -0,0 +1,73 @@
+/* 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/id_table.h"
+#include "glk/archetype/array.h"
+
+namespace Glk {
+namespace Archetype {
+
+ClassifyType DefaultClassification;
+
+// Static variables
+IdRecPtr hash[BUCKETS];
+XArrayType h_index;
+
+int add_ident(const String &id_str) {
+ int hasher;
+ IdRecPtr p, new_rec;
+
+ hasher = (int)(toupper(id_str[1])) - 65; // A..Z => 65..90 => 0..25
+ if (hasher < 0 || hasher > 25)
+ hasher = 26;
+
+ p = hash[hasher];
+ while (p->next && *p->next->id_name < id_str)
+ p = p->next;
+
+ if (p->next == nullptr || *p->next->id_name > id_str) {
+ new_rec = new IdRecType();
+ append_to_xarray(h_index, new_rec);
+
+ new_rec->id_kind = DefaultClassification;
+ new_rec->id_index = h_index.size() - 1;
+ new_rec->id_integer = new_rec->id_index;
+ new_rec->id_name = NewConstStr(id_str);
+ new_rec->next = p->next;
+
+ p->next = new_rec;
+ return h_index.size() - 1;
+ } else {
+ // found existing identifier
+ return p->next->id_index;
+ }
+}
+
+bool index_ident(int index, IdRecPtr &id_ptr) {
+ void *p;
+ bool result = index_xarray(h_index, index, p);
+ id_ptr = (IdRecPtr)p;
+ return result;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/id_table.h b/engines/glk/archetype/id_table.h
new file mode 100644
index 0000000000..2aff6d6ce9
--- /dev/null
+++ b/engines/glk/archetype/id_table.h
@@ -0,0 +1,77 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_ID_TABLE
+#define ARCHETYPE_ID_TABLE
+
+#include "glk/archetype/misc.h"
+#include "glk/archetype/string.h"
+
+/**
+ * Contains the necessary data structures andfunctions for adding to andreferring
+ * to the ID table(a very busy little structure).
+ *
+ * The ID table is a 27-element hash table with one bucket for each letter; identifiers
+ * are hashed according to their first letter. The last bucket is for identifiers beginning with
+ * an underscore. The ID table is cross_indexed by an xarray containing pointers to id_records.
+ * The ID table is complex enough that it should probably not be accessed directly but rather
+ * only through its procedures.In this way the data type, its primary instantiation, andassociated
+ * code comprise one stand-alone module which must be "used" by any module wishing to modify or
+ * query the table.
+ */
+namespace Glk {
+namespace Archetype {
+
+const int BUCKETS = 27; // 1 per letter of alphabet, plus the underscore
+
+struct IdRecType {
+ ClassifyType id_kind;
+ int id_integer; // What integer the ID gets written as
+ int id_index; // The ID's index in the ID table
+ StringPtr id_name;
+ IdRecType *next;
+};
+typedef IdRecType *IdRecPtr;
+
+// Public methods
+/**
+ * Adds the given identifier to the ID table, andreturns its index. There are no duplications;
+ * if the identifier already exists, its existing index is returned.
+ * @param id_str String containing identifier name
+ * @returns The index of the identifier
+ */
+extern int add_ident(const String &id_str);
+
+/**
+ * A quick little wrapper to the index_xarray function.
+ * @param index Number of the identifier
+ * @param id_ptr Out pointer to the id_record for that identifier
+ * @returns True if the requested identifier exists in the table
+ */
+extern bool index_ident(int index, IdRecPtr &id_ptr);
+
+extern ClassifyType DefaultClassification;
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
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
diff --git a/engines/glk/archetype/interpreter.h b/engines/glk/archetype/interpreter.h
new file mode 100644
index 0000000000..2ee357aad5
--- /dev/null
+++ b/engines/glk/archetype/interpreter.h
@@ -0,0 +1,130 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_INTERPRETER
+#define ARCHETYPE_INTERPRETER
+
+#include "glk/archetype/expression.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum DesiredType { LVALUE, RVALUE, NAME };
+
+typedef ExprNode ResultType;
+
+struct ContextType {
+ int sender, self, each, message;
+};
+
+extern int MainObject;
+
+extern void interpreter_init();
+
+/**
+ * A short wrapper to NewDynStr which basically uses the stack as temporary string storage.
+ * If you want to use a string constructor expression as an argument, call this function,
+ * since it does not take strings by reference but by value. Expensive on the stack but
+ * only briefly; it saves cluttering eval_expr
+ */
+extern StringPtr MakeNewDynStr(const String &s);
+
+/**
+ * Given a string message, returns its number in the Vocabulary list, or -1 if it was not found.
+ * At present, it simply uses a very inefficient O(N) lookup. If speed begins to become a
+ * consideration, this can be changed.
+ * @param message message to find number of
+ * @returns the number of the message in the Vocabulary list.
+ */
+extern int find_message(const String &message);
+
+/**
+ * Converts a scalar expression node to a target type. Deals primarily with numeric -> string
+ * or string -> numeric conversions in their many incarnations.
+ * @param target_type type to convert to
+ * @param the_scalar scalar to convert
+ */
+extern bool convert_to(AclType target_type, ResultType &the_scalar);
+
+/**
+ * Used to initialize previously unused result records. Does not expect that there might be
+ * a string pointer lurking within.
+ */
+extern void undefine(ResultType &result);
+
+/**
+ * To be used on temporary result variables after their usefulness is finished. Like 'undefine' above,
+ * except that it is used only for results that have actually been used - in other words, results with
+ * their "kind" field set properly.
+ */
+extern void cleanup(ResultType &result);
+
+/**
+ * Does an rvalue-like copy from r2 to r1
+ */
+extern void copy_result(ResultType &r1, const ResultType &r2);
+
+/**
+ * Compares two result nodes according to the given operator.
+ * @returns true if they can; false if they cannot
+ */
+extern bool result_compare(short comparison, ResultType &r1, ResultType &r2);
+
+/**
+ * Given the result of an LVALUE evaluation and a result to assign to the attribute,
+ * performs the assignment if possible.
+ * @param target hopefully points to attribute to receive assignment
+ * @param value Result to assign
+ * @returns Returns true if the assignment was successful; false otherwise
+ */
+extern bool assignment(ResultType &target, ResultType &value);
+
+/**
+ * Writes the given result to screen w/o terminating it with a newline
+ */
+extern void write_result(ResultType &result);
+
+/**
+ * For purposes of debugging.
+ * Strings are enclosed in double quotes.
+ * Messages are enclosed in single quotes.
+ * Quote literals are preceded by >>
+ */
+extern void display_result(ResultType &result);
+
+/**
+ * Given an expression tree, displays the thing on screen.
+ */
+extern void display_expr(ExprTree the_tree);
+
+/**
+ * Loads a game into memory from a binary input file. Checks for errors
+ * in the header or incompatible versions.
+ * @param f_in Input file
+ */
+extern bool load_game(Common::ReadStream *f_in);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/keywords.cpp b/engines/glk/archetype/keywords.cpp
new file mode 100644
index 0000000000..c794750699
--- /dev/null
+++ b/engines/glk/archetype/keywords.cpp
@@ -0,0 +1,152 @@
+/* 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/keywords.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/misc.h"
+
+namespace Glk {
+namespace Archetype {
+
+LookupType Reserved_Wds = {
+ nullptr,
+ "ABSENT",
+ "FALSE",
+ "TRUE",
+ "UNDEFINED",
+ "based",
+ "break",
+ "case",
+ "create",
+ "default",
+ "destroy",
+ "do",
+ "each",
+ "else",
+ "end",
+ "for",
+ "if",
+ "include",
+ "key",
+ "keyword",
+ "message",
+ "methods",
+ "named",
+ "null",
+ "of",
+ "on",
+ "read",
+ "self",
+ "sender",
+ "stop",
+ "then",
+ "type",
+ "while",
+ "write",
+ "writes"
+};
+
+LookupType Operators = {
+ nullptr,
+ "&",
+ "&:=",
+ "*",
+ "*:=",
+ "+",
+ "+:=",
+ "-",
+ "-->",
+ "-:=",
+ "->",
+ ".",
+ "/",
+ "/:=",
+ ":=",
+ "<",
+ "<=",
+ "=",
+ ">",
+ ">=",
+ "?",
+ "^",
+ "and",
+ "chs",
+ "leftfrom",
+ "length",
+ "not",
+ "numeric",
+ "or",
+ "rightfrom",
+ "string",
+ "within",
+ "~=",
+ nullptr,
+ nullptr
+};
+
+void load_text_list(Common::ReadStream *fIn, XArrayType &the_list) {
+ int i, n;
+ String s;
+
+ new_xarray(the_list);
+ n = fIn->readUint16LE();
+ for (i = 0; i < n; ++i) {
+ load_string(fIn, s);
+ append_to_xarray(the_list, NewConstStr(s));
+ }
+}
+
+void dump_text_list(Common::WriteStream *fOut, XArrayType &the_list) {
+ void *p;
+
+ fOut->writeUint16LE(the_list.size());
+ for (uint i = 0; i < the_list.size(); ++i) {
+ if (index_xarray(the_list, i, p))
+ dump_string(fOut, *(StringPtr)(p));
+ }
+}
+
+void dispose_text_list(XArrayType &the_list) {
+ void *p;
+
+ for (uint i = 0; i < the_list.size(); ++i) {
+ if (index_xarray(the_list, i, p))
+ free((StringPtr)p);
+ }
+
+ dispose_xarray(the_list);
+}
+
+void load_id_info(Common::ReadStream *bfile) {
+ load_text_list(bfile, g_vm->Type_ID_List);
+ load_text_list(bfile, g_vm->Object_ID_List);
+ load_text_list(bfile, g_vm->Attribute_ID_List);
+}
+
+void dump_id_info(Common::WriteStream *bfile) {
+ dump_text_list(bfile, g_vm->Type_ID_List);
+ dump_text_list(bfile, g_vm->Object_ID_List);
+ dump_text_list(bfile, g_vm->Attribute_ID_List);
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/keywords.h b/engines/glk/archetype/keywords.h
new file mode 100644
index 0000000000..f36c66e20b
--- /dev/null
+++ b/engines/glk/archetype/keywords.h
@@ -0,0 +1,149 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_KEYWORDS
+#define ARCHETYPE_KEYWORDS
+
+#include "common/stream.h"
+#include "glk/archetype/array.h"
+#include "glk/archetype/string.h"
+
+namespace Glk {
+namespace Archetype {
+
+// max length of any reserved word or operator
+const int SHORT_STR_LEN = 9;
+
+enum {
+ NUM_RWORDS = 34,
+ NUM_OPERS = 32,
+ MAX_ELEMENTS = 34 // max(NUM_RWORDS, NUM_OPERS)
+};
+
+enum ReservedWordId {
+ RW_ABSENT = 1,
+ RW_FALSE = 2,
+ RW_TRUE = 3,
+ RW_UNDEFINED = 4,
+ RW_BASED = 5,
+ RW_BREAK = 6,
+ RW_CASE = 7,
+ RW_CREATE = 8,
+ RW_DEFAULT = 9,
+ RW_DESTROY = 10,
+ RW_DO = 11,
+ RW_EACH = 12,
+ RW_ELSE = 13,
+ RW_END = 14,
+ RW_FOR = 15,
+ RW_IF = 16,
+ RW_INCLUDE = 17,
+ RW_KEY = 18,
+ RW_KEYWORD = 19,
+ RW_MESSAGE = 20,
+ RW_METHODS = 21,
+ RW_NAMED = 22,
+ RW_NULL = 23,
+ RW_OF = 24,
+ RW_ON = 25,
+ RW_READ = 26,
+ RW_SELF = 27,
+ RW_SENDER = 28,
+ RW_STOP = 29,
+ RW_THEN = 30,
+ RW_TYPE = 31,
+ RW_WHILE = 32,
+ RW_WRITE = 33,
+ RW_WRITES = 34
+};
+
+enum OperatorId {
+ OP_CONCAT = 1,
+ OP_C_CONCAT = 2,
+ OP_MULTIPLY = 3,
+ OP_C_MULTIPLY = 4,
+ OP_PLUS = 5,
+ OP_C_PLUS = 6,
+ OP_MINUS = 7,
+ OP_PASS = 8,
+ OP_C_MINUS = 9,
+ OP_SEND = 10,
+ OP_DOT = 11,
+ OP_DIVIDE = 12,
+ OP_C_DIVIDE = 13,
+ OP_ASSIGN = 14,
+ OP_LT = 15,
+ OP_LE = 16,
+ OP_EQ = 17,
+ OP_GT = 18,
+ OP_GE = 19,
+ OP_RANDOM = 20,
+ OP_POWER = 21,
+ OP_AND = 22,
+ OP_CHS = 23,
+ OP_LEFTFROM = 24,
+ OP_LENGTH = 25,
+ OP_NOT = 26,
+ OP_NUMERIC = 27,
+ OP_OR = 28,
+ OP_RIGHTFROM = 29,
+ OP_STRING = 30,
+ OP_WITHIN = 31,
+ OP_NE = 32
+};
+
+//typedef char ShortStrType[SHORT_STR_LEN];
+typedef const char *const LookupType[MAX_ELEMENTS + 1];
+
+extern LookupType Reserved_Wds, Operators;
+
+// Methods
+
+/**
+ * Loads an xarray of test literals into memory from the given file
+ */
+extern void load_text_list(Common::ReadStream *fIn, XArrayType &the_list);
+
+/**
+ * Dumps the given xarray of text literals to the given file
+ */
+extern void dump_text_list(Common::WriteStream *fOut, XArrayType &the_list);
+
+/**
+ * Disposes with all memory associated with the given xarray of text literals
+ */
+extern void dispose_text_list(XArrayType &the_list);
+
+/**
+ * Loads all ID information from the given binary file
+ */
+extern void load_id_info(Common::ReadStream *bfile);
+
+/**
+ * Dumps all ID information to the given binary file
+ */
+extern void dump_id_info(Common::WriteStream *bfile);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/linked_list.cpp b/engines/glk/archetype/linked_list.cpp
new file mode 100644
index 0000000000..90cca17981
--- /dev/null
+++ b/engines/glk/archetype/linked_list.cpp
@@ -0,0 +1,90 @@
+/* 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/linked_list.h"
+#include "glk/archetype/misc.h"
+
+namespace Glk {
+namespace Archetype {
+
+void new_list(ListType &the_list) {
+ the_list = new NodeType();
+}
+
+void dispose_list(ListType &the_list) {
+ NodePtr theNode, axe;
+ for (theNode = the_list->next; theNode != the_list; ) {
+ axe = theNode;
+ theNode = theNode->next;
+ add_bytes(-(int)sizeof(*axe));
+ free(axe);
+ }
+
+}
+
+bool iterate_list(ListType &the_list, NodePtr index) {
+ if (index == nullptr)
+ index = the_list->next;
+ else
+ index = index->next;
+
+ return index != the_list;
+}
+
+void append_to_list(ListType &the_list, NodePtr the_node) {
+ the_list->data = the_node->data;
+ the_list->key = the_node->key;
+ the_node->next = the_list->next;
+ the_list->next = the_node;
+
+ the_list = the_node;
+}
+
+NodePtr index_list(ListType &the_list, int number) {
+ int i = 0;
+ NodePtr p = the_list->next;
+
+ while (i < number && p != the_list) {
+ p = p->next;
+ ++i;
+ }
+
+ return (p == the_list) ? nullptr : p;
+}
+
+void insert_item(ListType &the_list, NodePtr the_item) {
+ NodePtr p;
+ for (p = the_list; p->next != the_list && p->next->key > the_item->key; p = p->next) {}
+
+ the_item->next = p->next;
+ p->next = the_item;
+}
+
+NodePtr find_item(ListType &the_list, int the_key) {
+ NodePtr p;
+ for (p = the_list->next; p != the_list && the_key < p->key; p = p->next) {}
+
+ return (p == the_list || the_key != p->key) ? nullptr : p;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/linked_list.h b/engines/glk/archetype/linked_list.h
new file mode 100644
index 0000000000..372de365b8
--- /dev/null
+++ b/engines/glk/archetype/linked_list.h
@@ -0,0 +1,80 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_LINKED_LIST
+#define ARCHETYPE_LINKED_LIST
+
+#include "common/list.h"
+
+namespace Glk {
+namespace Archetype {
+
+struct NodeType {
+ void *data;
+ int key;
+ NodeType *next;
+};
+typedef NodeType *NodePtr;
+
+typedef NodePtr ListType;
+
+/**
+ * Allocates a header node and points it to itself
+ */
+extern void new_list(ListType &the_list);
+
+/**
+ * Throws away all the memory that makes up an entire list structure. Is a "shallow dispose";
+ * i.e. only disposes of the structure, not the data.
+ */
+extern void dispose_list(ListType &the_list);
+
+/**
+ * Iterates through the given list
+ */
+extern bool iterate_list(ListType &the_list, NodePtr index);
+
+/**
+ * Appends a new item to the list
+ */
+extern void append_to_list(ListType &the_list, NodePtr the_node);
+
+/**
+ * Permits a linked list to be indexed like an array in O(N) time.
+ */
+extern NodePtr index_list(ListType &the_list, int number);
+
+/**
+ * Ordered insert; average time O(N/2). Inserts in descending order
+ */
+extern void insert_item(ListType &the_list, NodePtr the_item);
+
+/**
+ * Given a list and a key, finds the first item in the list corresponding to that key.
+ * Expects that the elements have been sorted in descending order
+ */
+extern NodePtr find_item(ListType &the_list, int the_key);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/misc.cpp b/engines/glk/archetype/misc.cpp
new file mode 100644
index 0000000000..34c067c2b0
--- /dev/null
+++ b/engines/glk/archetype/misc.cpp
@@ -0,0 +1,197 @@
+/* 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/misc.h"
+#include "glk/archetype/archetype.h"
+#include "glk/quetzal.h"
+
+namespace Glk {
+namespace Archetype {
+
+const char *const VERSION_STUB = "Archetype version ";
+const char *const VERSION = "Archetype version 1.02";
+const double VERSION_NUM = 1.01;
+
+size_t Bytes;
+int Debug;
+bool KeepLooking;
+bool AllErrors;
+
+void *Prior, *NextExit;
+
+void misc_init() {
+ //NextExit = ExitProc;
+ //ExitProc = @exit_prog;
+ //HeapError = @HeapFunc;
+ //Mark(Prior)
+ Bytes = 0;
+ Debug = 0;
+ KeepLooking = true;
+ AllErrors = false;
+}
+
+/*----------------------------------------------------------------------*/
+
+bool progfile::open(const String &name) {
+ filename = name;
+
+ if (!_file.open(name)) {
+ return false;
+
+ } else {
+ file_line = 0;
+ line_buffer = "";
+ line_pos = 0;
+ newlines = false;
+ consumed = true;
+ last_ch = NULL_CH;
+
+ return true;
+ }
+}
+
+void progfile::close() {
+ _file.close();
+}
+
+bool progfile::readChar(char &ch) {
+ if (last_ch != NULL_CH) {
+ ch = last_ch;
+ last_ch = NULL_CH;
+ } else {
+ ++line_pos;
+ while (line_pos >= (int)line_buffer.size()) {
+ if (_file.eos()) {
+ ch = NULL_CH;
+ return false;
+ }
+
+ line_buffer = QuetzalReader::readString(&_file);
+ line_buffer += NEWLINE_CH;
+ ++file_line;
+ line_pos = 0;
+ }
+
+ ch = line_buffer[line_pos];
+ }
+
+ return true;
+}
+
+void progfile::unreadChar(char ch) {
+ last_ch = ch;
+}
+
+void progfile::sourcePos() {
+ /* With the /A switch specified, multiple source_pos messages can be called,
+ * so long as there is no fatal syntax error.Otherwise, the first error
+ * of any kind, regardless of severity, is the only error printed.This is
+ * done as a courtesy to those of us without scrolling DOS windows
+ */
+ if (KeepLooking) {
+ if (!AllErrors)
+ KeepLooking = false;
+
+ g_vm->writeln("Error in %s at line %d", filename.c_str(), file_line);
+ g_vm->writeln(line_buffer);
+
+ String s;
+ for (int i = 0; i < line_pos; ++i)
+ s += ' ';
+ s += '^';
+ g_vm->writeln(s);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+void add_bytes(int delta) {
+ Bytes += delta;
+
+ if ((Debug & DEBUG_BYTES) != 0) {
+ if (delta >= 0)
+ g_vm->write("Allocated ");
+ else
+ g_vm->write("Deallocated ");
+
+ g_vm->writeln("%.3d bytes. Current consumed memory: %.6d", ABS(delta), Bytes);
+ }
+}
+
+String formatFilename(const String &name, const String &ext, bool replace) {
+ String s;
+ int period = 0;
+ bool noExt;
+
+ // Check for a period for an extension
+ period = name.lastIndexOf('.');
+ noExt = period == -1;
+
+ if (replace || noExt) {
+ return name + "." + ext;
+ } else {
+ return String(name.c_str(), name.c_str() + period + 1) + ext;
+ }
+}
+
+void load_string(Common::ReadStream *fIn, String &the_string) {
+ char buffer[257];
+ size_t strSize = fIn->readByte();
+ fIn->read(buffer, strSize);
+ buffer[strSize] = '\0';
+
+ the_string = String(buffer);
+ cryptstr(the_string);
+}
+
+void dump_string(Common::WriteStream *fOut, const String &the_string) {
+ assert(the_string.size() < 256);
+ fOut->writeByte(the_string.size());
+
+ if (Encryption == NONE) {
+ fOut->write(the_string.c_str(), the_string.size());
+
+ } else {
+ String tmp = the_string;
+ cryptstr(tmp);
+ fOut->write(tmp.c_str(), tmp.size());
+ }
+}
+
+StringPtr NewConstStr(const String &s) {
+ return new String(s);
+}
+
+void FreeConstStr(StringPtr &sp) {
+ delete sp;
+}
+
+StringPtr NewDynStr(const String &s) {
+ return new String(s);
+}
+
+void FreeDynStr(StringPtr &sp) {
+ delete sp;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/misc.h b/engines/glk/archetype/misc.h
new file mode 100644
index 0000000000..0799077a85
--- /dev/null
+++ b/engines/glk/archetype/misc.h
@@ -0,0 +1,184 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_MISC
+#define ARCHETYPE_MISC
+
+#include "common/file.h"
+#include "glk/archetype/crypt.h"
+#include "glk/archetype/string.h"
+
+namespace Glk {
+namespace Archetype {
+
+#define MAX_STRING 255
+#define NULL_CH '\0'
+#define NEWLINE_CH '\r'
+
+enum {
+ DEBUG_BYTES = 0x01,
+ DEBUG_MSGS = 0x02,
+ DEBUG_EXPR = 0x04,
+ DEBUG_STMT = 0x08
+};
+
+enum AclType {
+ RESERVED, IDENT, MESSAGE, OPER, TEXT_LIT, QUOTE_LIT, NUMERIC, PUNCTUATION,
+ STR_PTR, ATTR_PTR, BAD_TOKEN, NEWLINE
+};
+
+/**
+ * Source program file/accounting structure.With such a file, it is important to keep
+ * not only the file pointer, but also fields to keep track of position in the source file
+ * andthe compiler state, or the context of the tokenizer
+ */
+class progfile {
+private:
+ Common::File _file; // The physical file
+public:
+ String filename; // to do with error tracking
+ String line_buffer;
+ int file_line;
+ int line_pos;
+
+ bool newlines; // having to do with the tokenizer context
+ char last_ch;
+ bool consumed;
+ AclType ttype;
+ int tnum;
+public:
+ /**
+ * Constructor
+ */
+ progfile() : file_line(0), line_pos(0), newlines(false), last_ch(NULL_CH),
+ consumed(false), ttype(RESERVED), tnum(0) {}
+
+ /**
+ * Opens an Archetype program source file.
+ * @param name Filename
+ */
+ bool open(const String &name);
+
+ /**
+ * Closes an Archetype program source code file.
+ */
+ void close();
+
+ /**
+ * Reads a single character from the given progfile, performing all appropriate housekeeping.
+ *
+ * It appends an internal newline to the end of every line taken from the file; it is up to
+ * the tokenizer as to whether to consider it white space or a token.
+ * @param c The output character
+ * @returns True if the character was safely read from the file
+ */
+ bool readChar(char &ch);
+
+ /**
+ * Has the effect of putting a character back on the data stream.
+ * Closely cooperates with read_char above.
+ * @param ch Character to un-read
+ */
+ void unreadChar(char ch);
+
+ /**
+ * Writes out the current position in the source file nicely for error messages
+ * and so forth.It will, however, only write this out once per execution of the program.
+ * This is to prevent messages scrolling uncontrollably off the screen.
+ */
+ void sourcePos();
+};
+
+enum ClassifyType { TYPE_ID, OBJECT_ID, ATTRIBUTE_ID, ENUMERATE_ID, UNDEFINED_ID };
+
+extern const char *const VERSION;
+extern const char *const VERSION_STUB;
+extern const double VERSION_NUM;
+extern size_t Bytes; // Bytes consumed by allocated memory
+extern int Debug;
+extern bool KeepLooking;
+extern bool AllErrors;
+
+/**
+ * Performs initialization of fields local to the file
+ */
+extern void misc_init();
+
+/**
+ * Provides a method of keeping track of the size, in allocation, of the used part of the heap.
+ * @param delta if positive, the number allocated; if negative, the number deallocated.
+ */
+extern void add_bytes(int delta);
+
+/**
+ * Given a name andextension, tacks on the given extension if none given.
+ * of <name> are ".<ext>", or else tacks those four characters on.
+ * @param name Filename
+ * @param ext Extension
+ * @param replace Whether to replace existing extension
+ */
+extern String formatFilename(const String &name, const String &ext, bool replace);
+
+/**
+ * Given an input stream, reads in a Pascal style string with the first byte being the length.
+ * Secondarily, it also decrypts the string
+ * @param fIn Input file
+ * @param the_string Output string
+ */
+extern void load_string(Common::ReadStream *fIn, String &the_string);
+
+/**
+ * Given an untyped file variable anda string variable, writes to the file first the length
+ * of the string andthen the string itself.
+ * @param fOut Output file
+ * @param the_string The string to output
+ */
+extern void dump_string(Common::WriteStream *fOut, const String &the_string);
+
+/**
+ * Used for allocating string space that is not expected to be disposed of
+ * until the end of the program andis never expected to change.
+ * Only the very minimum space necessary to store the string is used;
+ * thereby using minimal space andincurring no fragmentation
+ */
+extern StringPtr NewConstStr(const String &s);
+
+/**
+ * Frees a const string
+ */
+extern void FreeConstStr(StringPtr &sp);
+
+/**
+ * Dynamic strings were originally strings where we need speed and yet we need to allocate
+ * only the string space necessary. These days, we can simply just allocate a new string
+ */
+extern StringPtr NewDynStr(const String &s);
+
+/**
+ * Frees a dynamic string
+ */
+extern void FreeDynStr(StringPtr &sp);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/parser.cpp b/engines/glk/archetype/parser.cpp
new file mode 100644
index 0000000000..0063e2818c
--- /dev/null
+++ b/engines/glk/archetype/parser.cpp
@@ -0,0 +1,291 @@
+/* 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/parser.h"
+#include "glk/archetype/archetype.h"
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace Archetype {
+
+const int WORD_LEN = 32;
+
+struct ParseType {
+ StringPtr word;
+ int object;
+};
+typedef ParseType *ParsePtr;
+
+/**
+ * Is a word character
+ */
+static bool isWordChar(char c) {
+ return Common::isAlnum(c) || c == '-' || c == '\"';
+}
+
+/**
+ * Puts into lowercase the given character.
+ */
+static char locase(char ch) {
+ return tolower(ch);
+}
+
+void normalize_string(const String &first, String &second) {
+ int i, j, lfirst;
+ bool in_word, done;
+
+ i = j = 0;
+ in_word = false;
+ done = false;
+ lfirst = first.size() - 1;
+ second = " ";
+
+ do {
+ if (i > lfirst || !isWordChar(first[i])) {
+ if (in_word) {
+ j = 0;
+ in_word = false;
+ second = second + " ";
+ }
+ else {
+ ++i;
+ }
+
+ if (i > lfirst)
+ done = true;
+
+ } else if (in_word) {
+ if (j < g_vm->Abbreviate) {
+ second = second + locase(first[i]);
+ ++j;
+ }
+
+ ++i;
+ } else {
+ in_word = true;
+ }
+ } while (!done);
+}
+
+void add_parse_word(TargetListType which_list, String &the_word, int the_object) {
+ ListType the_list;
+ String tempstr;
+ NodePtr np;
+ ParsePtr pp;
+ int bar;
+
+ if (which_list == PARSER_VERBLIST)
+ the_list = g_vm->verb_names;
+ else
+ the_list = g_vm->object_names;
+
+ the_word += '|';
+
+ do {
+ bar = the_word.indexOf('|');
+ if (bar != -1) {
+ pp = (ParsePtr)malloc(sizeof(ParseType));
+ tempstr = the_word.left(bar - 1).left(g_vm->Abbreviate);
+
+ pp->word = NewConstStr(tempstr);
+ pp->word->toLowercase();
+ the_word = String(the_word.c_str() + bar + 1);
+ pp->object = the_object;
+
+ np = (NodePtr)malloc(sizeof(NodeType));
+ np->key = pp->word->size();
+ np->data = pp;
+
+ insert_item(the_list, np);
+ }
+ } while (bar != 0);
+}
+
+static void parse_sentence_substitute(int start, ParsePtr pp, int &next_starting) {
+ int sublen = pp->word->size();
+
+ if (sublen > g_vm->Abbreviate)
+ sublen = g_vm->Abbreviate;
+
+ g_vm->Command = g_vm->Command.left(start)
+ + String::format("%%%c%c^", pp->object >> 8, pp->object & 0xff)
+ + String(g_vm->Command.c_str() + start + sublen + 1);
+
+ next_starting = next_starting - sublen + 4;
+}
+
+static bool parse_sentence_next_chunk(int &start_at, String &the_chunk, int &next_starting) {
+ int i;
+
+ if (next_starting == 0) {
+ return false;
+ } else {
+ do {
+ start_at = next_starting;
+ the_chunk = g_vm->Command.mid(start_at);
+
+ i = the_chunk.indexOf('%');
+ if (i == -1) {
+ next_starting = 0;
+ } else {
+ the_chunk = the_chunk.left(i - 1);
+ next_starting = next_starting + i + 3;
+ }
+
+ the_chunk.trim();
+ } while (!(next_starting == 0 || !the_chunk.empty()));
+
+ return !the_chunk.empty();
+ }
+}
+
+void parse_sentence() {
+ const int nfillers = 3;
+ const char *const FILTERS[nfillers] = { " a ", " an ", " the " };
+ int next_starting;
+ String s;
+ NodePtr np, near_match, far_match;
+ ParsePtr pp;
+ int i, lchunk;
+
+ // Rip out those fillers
+ s = g_vm->Command;
+ for (i = 0; i < nfillers; ++i) {
+ int filterIndex;
+ while ((filterIndex = g_vm->Command.indexOf(FILTERS[i])) != -1)
+ g_vm->Command.del(filterIndex, strlen(FILTERS[i]) - 1);
+ }
+
+ // Restore the original string if filler removal destroyed it completely
+ if (g_vm->Command == " ")
+ g_vm->Command = s;
+
+ // Two passes: one matching all verbs and prepositions from the verb list, longest strings first
+
+ np = nullptr;
+ while (iterate_list(g_vm->verb_names, np)) {
+ pp = (ParsePtr)np->data;
+ s = String::format(" %s ", pp->word->left(g_vm->Abbreviate).c_str());
+
+ i = g_vm->Command.indexOf(s);
+ if (i != -1)
+ parse_sentence_substitute(i, pp, next_starting);
+ }
+
+ // Second pass: carefully search for the remaining string chunks; search only the part
+ // of the noun list of the same length; give preference to those in the Proximate list
+ next_starting = 1;
+
+ while (parse_sentence_next_chunk(i, s, next_starting)) {
+ lchunk = s.size() - 1;
+
+ np = find_item(g_vm->object_names, lchunk);
+ if (np != nullptr) {
+ near_match = nullptr;
+ far_match = nullptr;
+
+ do {
+ pp = (ParsePtr)np->data;
+ if (pp->word->left(g_vm->Abbreviate) == s) {
+ if (find_item(g_vm->Proximate, pp->object) != nullptr)
+ near_match = np;
+ else
+ far_match = np;
+ }
+ } while (!(iterate_list(g_vm->object_names, np) && (lchunk == (int)((ParsePtr)np->data)->word->size())));
+
+ if (near_match != nullptr)
+ parse_sentence_substitute(i, (ParsePtr)near_match->data, next_starting);
+ else if (far_match != nullptr)
+ parse_sentence_substitute(i, (ParsePtr)far_match->data, next_starting);
+ }
+ }
+
+ g_vm->Command.trim();
+}
+
+bool pop_object(int &intback, String &strback) {
+ int i;
+
+ if (g_vm->Command.empty()) {
+ return false;
+ } else {
+ if (g_vm->Command.firstChar() == '%') {
+ // parsed object
+ intback = ((int)g_vm->Command[1] << 8) | ((int)g_vm->Command[2]);
+ g_vm->Command.del(0, 4);
+ }
+ else {
+ intback = -1;
+ i = g_vm->Command.indexOf('%');
+ if (i < 0)
+ i = g_vm->Command.size();
+
+ strback = g_vm->Command.left(i);
+ g_vm->Command = g_vm->Command.mid(i);
+ strback.trim();
+ }
+
+ g_vm->Command.trim();
+ return true;
+ }
+}
+
+int find_object(const String &s) {
+ NodePtr np;
+
+ np = nullptr;
+ while (iterate_list(g_vm->object_names, np)) {
+ if (*((ParsePtr)np->data)->word == s)
+ return ((ParsePtr)np->data)->object;
+ }
+
+ np = nullptr;
+ while (iterate_list(g_vm->verb_names, np)) {
+ if (*((ParsePtr)np->data)->word == s)
+ return ((ParsePtr)np->data)->object;
+ }
+
+ return 0;
+}
+
+void clear_parse_list(ListType &the_list) {
+ ParsePtr pp;
+ NodePtr np = nullptr;
+
+ while (iterate_list(the_list, np)) {
+ pp = (ParsePtr)np->data;
+ FreeConstStr(pp->word);
+ free(pp);
+ }
+
+ dispose_list(the_list);
+ new_list(the_list);
+}
+
+void new_parse_list() {
+ clear_parse_list(g_vm->verb_names);
+ clear_parse_list(g_vm->object_names);
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/parser.h b/engines/glk/archetype/parser.h
new file mode 100644
index 0000000000..c5185b0f35
--- /dev/null
+++ b/engines/glk/archetype/parser.h
@@ -0,0 +1,89 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_PARSER
+#define ARCHETYPE_PARSER
+
+#include "glk/archetype/string.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum TargetListType { PARSER_VERBLIST, PARSER_NOUNLIST };
+
+/**
+ * Given a string, creates a string with one and only one space between each word
+ * @param first the string to be normalized
+ * @param second the normalized string
+ */
+extern void normalize_string(const String &first, String &second);
+
+/**
+ * Adds another word to one of the lists to match. If the given word has vertical bars in it,
+ * the bars are considered delimiters and each delimited word is added to the available list
+ */
+extern void add_parse_word(TargetListType which_list, String &the_word, int the_object);
+
+/**
+ * Parses the previously given sentence into a string of object references.
+ * The verbpreps list is searched first, followed by the nounphrases list.
+ * It does not identify any parts of speech; it is strictly substitutional.
+ *
+ * Also removes all instances of the words "a", "an", "the".
+ *
+ * NOTES:
+ * When an object is matched, its name is replaced by the sequence
+ * <percent sign><high byte><low byte><caret>. The percent will
+ * indicate the beginning of the encoded number; the caret indicates
+ * the end and also serves the purpose of keeping the trim() procedure
+ * from trimming off objects 9 or 13 or the like.
+ *
+ * Objects are matched as words; they must be surrounded by spaces.
+ * When they are replaced in the Command string, they leave the spaces
+ * on both sides so as not to interfere with further matching
+ */
+extern void parse_sentence();
+
+/**
+ * Pops the first object number off of the parsed Command string and sends the number back.
+ * If Command does not begin with an object marker, sends back the unparseable string.
+ * @param intback will be -1 if there was no object; otherwise, the number of the object.
+ * @param strback will contain the (trimmed) unparseable chunk if intback is -1; unchanged otherwise
+ * @returns true if there was anything to be popped; false otherwise
+ */
+extern bool pop_object(int &intback, String &strback);
+
+/**
+ * Performs a subset of the normal parse_sentence algorithm. Given a single string,
+ * find the number of the first object that matches.
+ */
+extern int find_object(const String &s);
+
+/**
+ * Called in order to force a full deletion of the parse lists, in order that new ones may be built up
+ */
+extern void new_parse_list();
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
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
diff --git a/engines/glk/archetype/saveload.h b/engines/glk/archetype/saveload.h
new file mode 100644
index 0000000000..fe6251be9c
--- /dev/null
+++ b/engines/glk/archetype/saveload.h
@@ -0,0 +1,79 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_ERROR
+#define ARCHETYPE_ERROR
+
+/* Contains routines for both saving and loading binary ACX files. Also
+ * contains routines for disposing of the major ACL structures, in order to
+ * be able to throw away the old before loading in the new
+ */
+
+#include "glk/archetype/linked_list.h"
+#include "glk/archetype/statement.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum ContentType { STMT_LIST, EXPR_LIST, CASE_LIST };
+enum MissionType { LOAD, DUMP, FREE, DISPLAY };
+
+struct ObjectType {
+ int inherited_from; // index to Type_List
+ ListType attributes;
+ ListType methods;
+ StatementPtr other;
+};
+typedef ObjectType *ObjectPtr;
+
+// Global variables
+extern StatementKind vEndSeq, vContSeq; // to make BlockWrite happy
+extern int Dynamic;
+extern bool Translating;
+
+extern void saveload_init();
+
+extern void load_item_list(Common::ReadStream *f_in, ListType elements, ContentType content);
+extern void dump_item_list(Common::WriteStream *f_out, ListType elements, ContentType content);
+extern void dispose_item_list(ListType &elements, ContentType content);
+
+extern void load_expr(Common::ReadStream *f_in, ExprTree &the_expr);
+extern void dump_expr(Common::WriteStream *f_out, ExprTree &the_expr);
+extern void dispose_expr(ExprTree &the_expr);
+
+extern void load_stmt(Common::ReadStream *f_in, StatementPtr &the_stmt);
+extern void dump_stmt(Common::WriteStream *f_out, StatementPtr &the_stmt);
+extern void dispose_stmt(StatementPtr &the_stmt);
+
+extern void load_object(Common::ReadStream *f_in, ObjectPtr &the_object);
+extern void dump_object(Common::WriteStream *f_out, const ObjectPtr the_object);
+extern void dispose_object(ObjectPtr &the_object);
+
+extern void load_obj_list(Common::ReadStream *f_in, XArrayType &obj_list);
+extern void dump_obj_list(Common::WriteStream *f_out, XArrayType &obj_list);
+extern void dispose_obj_list(XArrayType &obj_list);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/semantic.cpp b/engines/glk/archetype/semantic.cpp
new file mode 100644
index 0000000000..4e2cd37205
--- /dev/null
+++ b/engines/glk/archetype/semantic.cpp
@@ -0,0 +1,234 @@
+/* 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/semantic.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/error.h"
+#include "glk/archetype/id_table.h"
+
+namespace Glk {
+namespace Archetype {
+
+typedef int *IntegerPtr;
+
+int classify_as(progfile &f, int id_number, ClassifyType interpretation, void *ptr_to_data) {
+ IdRecPtr the_id_ptr;
+ String error_string;
+ int result = 0;
+
+ if (!index_ident(id_number, the_id_ptr)) {
+ error_message(f, "Attempt to classify unencountered identifier");
+ } else {
+ //with the_id_ptr^ do begin
+ if (the_id_ptr->id_kind == interpretation)
+ result = the_id_ptr->id_integer;
+
+ // If the existing id_kind is the DefaultClassification, we're allowed to
+ // change it; otherwise there's a conflict
+ else if (the_id_ptr->id_kind == DefaultClassification) {
+ the_id_ptr->id_kind = interpretation;
+ the_id_ptr->id_integer = the_id_ptr->id_index;
+
+ switch (the_id_ptr->id_kind) {
+ case TYPE_ID:
+ append_to_xarray(g_vm->Type_List, ptr_to_data);
+ append_to_xarray(g_vm->Type_ID_List, (void *)the_id_ptr->id_name);
+ the_id_ptr->id_integer = g_vm->Type_List.size() - 1;
+ break;
+
+ case OBJECT_ID:
+ if (ptr_to_data == nullptr) {
+ the_id_ptr->id_integer = 0;
+ } else {
+ // Object_List may have grown by unnamed objects between calls to classify_as.
+ // Fill in the intervening spaces with "null"
+ while (g_vm->Object_ID_List.size() < g_vm->Object_List.size())
+ append_to_xarray(g_vm->Object_ID_List, (void *)g_vm->NullStr);
+
+ append_to_xarray(g_vm->Object_List, ptr_to_data);
+ append_to_xarray(g_vm->Object_ID_List, (void *)the_id_ptr->id_name);
+ the_id_ptr->id_integer = g_vm->Object_List.size() - 1;
+ }
+ break;
+
+ case ATTRIBUTE_ID:
+ append_to_xarray(g_vm->Attribute_ID_List, (void *)the_id_ptr->id_name);
+ the_id_ptr->id_integer = g_vm->Attribute_ID_List.size() - 1;
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ error_string = String::format("Identifier type conflict: \"%s\" already declared as ",
+ the_id_ptr->id_name->c_str());
+
+ switch (the_id_ptr->id_kind) {
+ case TYPE_ID:
+ error_string = error_string + "a type";
+ break;
+ case OBJECT_ID:
+ error_string = error_string + "an object";
+ break;
+ case ATTRIBUTE_ID:
+ error_string = error_string + "an attribute";
+ break;
+ case ENUMERATE_ID:
+ error_string = error_string + "a keyword";
+ break;
+ default:
+ break;
+ }
+
+ error_message(f, error_string);
+ the_id_ptr->id_integer = 0;
+ }
+
+ result = the_id_ptr->id_integer;
+ }
+
+ return result;
+}
+
+void get_meaning(int id_number, ClassifyType &meaning, int &number) {
+ IdRecPtr the_id_ptr;
+
+ if (!index_ident(id_number, the_id_ptr)) {
+ error("Internal error: attempt to find meaning of unencountered identifier");
+ } else {
+ meaning = the_id_ptr->id_kind;
+ number = the_id_ptr->id_integer;
+ }
+}
+
+void add_undefined(int the_ID) {
+ NodePtr np;
+ IntegerPtr ip;
+
+ np = find_item(g_vm->Overlooked, the_ID);
+ if (np != nullptr) {
+ ++*((IntegerPtr)np->data);
+ } else {
+ np = (NodePtr)malloc(sizeof(NodeType));
+ np->key = the_ID;
+ ip = (IntegerPtr)malloc(sizeof(int));
+ *ip = 1; // TODO: Should this be 0-based?
+ np->data = ip;
+ insert_item(g_vm->Overlooked, np);
+ }
+}
+
+bool display_undefined() {
+ NodePtr np = nullptr;
+ IntegerPtr ip;
+ IdRecPtr id_rec;
+ bool exists = false;
+
+ while (iterate_list(g_vm->Overlooked, np)) {
+ if (!exists) {
+ g_vm->writeln("The following identifiers were not explicitly defined.");
+ exists = true;
+ }
+
+ ip = (IntegerPtr)np->data;
+ g_vm->write("Used %d", *ip);
+ if (*ip == 1)
+ g_vm->write(" time: ");
+ else
+ g_vm->write(" times: ");
+
+ if (index_ident(np->key, id_rec))
+ g_vm->writeln("%s", id_rec->id_name->c_str());
+ else
+ g_vm->writeln("<unknown identifier>");
+
+ free(ip);
+ }
+
+ dispose_list(g_vm->Overlooked);
+
+ return exists;
+}
+
+bool verify_expr(progfile &f, ExprTree the_expr) {
+ bool success = true;
+
+ switch (the_expr->_kind) {
+ case OPER:
+ switch (the_expr->_oper.op_name) {
+ case OP_DOT:
+ if (the_expr->_oper.right->_kind != IDENT) {
+ error_message(f, "Right side of dot must be an identifier");
+ success = false;
+ }
+ else if (the_expr->_oper.right->_ident.ident_kind != ATTRIBUTE_ID) {
+ the_expr->_oper.right->_ident.ident_int = classify_as(f,
+ the_expr->_oper.right->_ident.ident_int, ATTRIBUTE_ID, nullptr);
+ }
+
+ the_expr->_oper.right->_ident.ident_kind = ATTRIBUTE_ID;
+ if (the_expr->_oper.right->_ident.ident_int == 0)
+ success = false;
+
+ case OP_ASSIGN:
+ case OP_C_CONCAT:
+ case OP_C_MULTIPLY:
+ case OP_C_DIVIDE:
+ case OP_C_PLUS:
+ case OP_C_MINUS:
+ if (the_expr->_oper.left->_kind == IDENT) {
+ get_meaning(the_expr->_oper.left->_ident.ident_int,
+ the_expr->_oper.left->_ident.ident_kind, the_expr->_oper.left->_ident.ident_int);
+
+ if (the_expr->_oper.left->_ident.ident_kind != ATTRIBUTE_ID) {
+ error_message(f, "Left side of assignment is not an attribute");
+ success = false;
+ }
+ }
+ else if (!(the_expr->_oper.left->_kind == OPER &&
+ the_expr->_oper.left->_oper.op_name == OP_DOT)) {
+ error_message(f, "Left side of assignment must reference an attribute");
+ success = false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (success) {
+ if (Binary[the_expr->_oper.op_name])
+ success = verify_expr(f, the_expr->_oper.left);
+ }
+ if (success)
+ success = verify_expr(f, the_expr->_oper.right);
+ break;
+
+ default:
+ break;
+ }
+
+ return success;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/semantic.h b/engines/glk/archetype/semantic.h
new file mode 100644
index 0000000000..2166e81220
--- /dev/null
+++ b/engines/glk/archetype/semantic.h
@@ -0,0 +1,112 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_SEMANTIC
+#define ARCHETYPE_SEMANTIC
+
+/* Used by the SYNTAX unit, it provides the high-level semantic checking
+ * as well as .ACX file output.
+ */
+
+#include "glk/archetype/array.h"
+#include "glk/archetype/expression.h"
+#include "glk/archetype/linked_list.h"
+
+namespace Glk {
+namespace Archetype {
+
+/**
+ * Works closely with the ID_Table to create and verify the various semantic
+ * interpretations of identifiers, which are classified as either:
+ * TYPE_ID: names a type definition template in the type list.
+ * OBJECT_ID: names an object instantiation in the object list.
+ * ATTRIBUTE_ID: an attribute identifier.
+ * ENUMERATE_ID: an identifier like "open" or "closed" which is simply
+ * assigned so that it can be tested.
+ * UNDEFINED_ID: Not defined anywhere. If /K is asserted for CREATE, then
+ * this is the value returned by default; otherwise,
+ * ENUMERATE_ID is.
+ *
+ * @param f the progfile that is being read. Since this function is part
+ * of the first "pass", it needs access to the file being read.
+ * @param id_number the index in the ID table
+ * @param interpretation one of the constants above
+ * @param ptr_to_data (IN) if not nil, points to the data that the identifier represents
+ * (when first encountered)
+ * @returns depends on interpretation:
+ * TYPE_ID: the index in Type_List
+ * OBJECT_ID: the index in Object_List
+ * ATTRIBUTE_ID: the order the identifier was declared in, i.e.
+ * for the first attribute encountered, 1, for the
+ * second, 2, etc.
+ * ENUMERATE_ID: the unchanged id_number, for a simple unique number.
+ * UNDEFINED_ID: same as ENUMERATE_ID
+ *
+ * In any case, classify_as returns 0 if there was an error.
+ * Such an error will have been printed by this routine, so there
+ * will be no need for the caller to print out its own.
+ */
+extern int classify_as(progfile &f, int id_number, ClassifyType interpretation, void *ptr_to_data);
+
+/**
+ * Given an ID_Table index, finds what it represents and returns an appropriate enumerated
+ * type and index.
+ *
+ * If /K is asserted, default return is UNDEFINED_ID; else it is ENUMERATE_ID.
+ * @param id_number integer index to ID_Table
+ * @param meaning Output classification of ID
+ * @param number Output integer appropriate to classification
+ */
+extern void get_meaning(int id_number, ClassifyType &meaning, int &number);
+
+/**
+ * Used for adding the number of an undefined identifier to a list to be produced
+ * at the end of translation.
+ */
+extern void add_undefined(int the_ID);
+
+/**
+ * Displays the list of undefined identifiers collected with add_undefined
+ */
+extern bool display_undefined();
+
+/**
+ * Assumes that expression tree contains no OP_LPAREN nodes.
+ * Ensures the following:
+ * 1. All OP_DOT operators have identifiers as their right-hand
+ * arguments, which are classified as ATTRIBUTE_ID's.
+ * 2. All assignment operators have OP_DOT operators or identifiers
+ * as their left-hand arguments, and any such identifiers are
+ * classified as ATTRIBUTE_ID's.
+ * This is necessary because the only way to use the OP_DOT operator is
+ * to discover the value of some attribute, and attributes are the only
+ * things which may be assigned to.
+ *
+ * @param f Program file (for logging errors)
+ * @param the_expr Expression to be verified
+ */
+extern bool verify_expr(progfile &f, ExprTree the_expr);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/statement.h b/engines/glk/archetype/statement.h
new file mode 100644
index 0000000000..0be8c1b9a0
--- /dev/null
+++ b/engines/glk/archetype/statement.h
@@ -0,0 +1,98 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_STATEMENT
+#define ARCHETYPE_STATEMENT
+
+#include "glk/archetype/expression.h"
+#include "glk/archetype/linked_list.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum StatementKind {
+ COMPOUND, ST_EXPR, ST_IF, ST_CASE, ST_FOR, ST_WHILE, ST_BREAK, ST_CREATE,
+ ST_DESTROY, ST_WRITE, ST_WRITES, ST_STOP, CONT_SEQ, END_SEQ
+};
+
+union StatementType;
+typedef StatementType *StatementPtr;
+
+struct StmtCompound {
+ ListType statements;
+};
+
+struct StmtExpr {
+ ExprTree expression;
+};
+
+struct StmtIf {
+ ExprTree condition;
+ StatementPtr then_branch;
+ StatementPtr else_branch;
+};
+
+struct StmtCase {
+ ExprTree test_expr;
+ ListType cases;
+};
+
+struct StmtCreate {
+ int archetype;
+ ExprTree new_name;
+};
+
+struct StmtDestroy {
+ ExprTree victim;
+};
+
+struct StmtLoop {
+ ExprTree selection;
+ StatementPtr action;
+};
+
+struct StmtWrite {
+ ListType print_list;
+};
+
+union StatementType {
+ StatementKind _kind;
+ StmtCompound _compound;
+ StmtExpr _expr;
+ StmtIf _if;
+ StmtCase _case;
+ StmtCreate _create;
+ StmtDestroy _destroy;
+ StmtLoop _loop;
+ StmtWrite _write;
+};
+
+struct CasePairType {
+ ExprTree value;
+ StatementPtr action;
+};
+typedef CasePairType *CasePairPtr;
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/string.cpp b/engines/glk/archetype/string.cpp
new file mode 100644
index 0000000000..4db4688a7d
--- /dev/null
+++ b/engines/glk/archetype/string.cpp
@@ -0,0 +1,133 @@
+/* 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/string.h"
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace Archetype {
+
+int String::indexOf(char c) const {
+ const char *p = strchr(c_str(), c);
+ return p ? p - c_str() : -1;
+}
+
+int String::indexOf(const String &substr) const {
+ const char *c = strstr(c_str(), substr.c_str());
+ return c ? c - c_str() : -1;
+}
+
+int String::lastIndexOf(char c) const {
+ for (int i = (int)size() - 1; i >= 0; --i) {
+ if (operator[](i) == c)
+ return i;
+ }
+
+ return -1;
+}
+
+void String::trim() {
+ while (!empty() && (lastChar() == ' ' || lastChar() == '\t' || lastChar() == '\n'
+ || lastChar() == '\r'))
+ deleteLastChar();
+}
+
+String operator+(const String &x, const String &y) {
+ Common::String tmp = Common::operator+(x, y);
+ return String(tmp.c_str());
+}
+
+String operator+(const char *x, const String &y) {
+ Common::String tmp = Common::operator+(x, y);
+ return String(tmp.c_str());
+}
+
+String operator+(const String &x, const char *y) {
+ Common::String tmp = Common::operator+(x, y);
+ return String(tmp.c_str());
+}
+
+String operator+(const String &x, char y) {
+ Common::String tmp = Common::operator+(x, y);
+ return String(tmp.c_str());
+}
+
+String operator+(char x, const String &y) {
+ Common::String tmp = Common::operator+(x, y);
+ return String(tmp.c_str());
+}
+
+bool operator==(const char *x, const String &y) {
+ return Common::operator==(x, y);
+}
+
+bool operator!=(const char *x, const String &y) {
+ return Common::operator!=(x, y);
+}
+
+String String::format(const char *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ Common::String tmp = Common::String::vformat(fmt, va);
+ va_end(va);
+ return String(tmp);
+}
+
+String String::vformat(const char *fmt, va_list args) {
+ Common::String tmp = Common::String::vformat(fmt, args);
+ return String(tmp);
+}
+
+int String::val(int *code) {
+ if (code)
+ *code = 0;
+
+ return atoi(c_str());
+}
+
+String String::left(size_t count) const {
+ return String(c_str(), c_str() + MIN(count, (size_t)size()));
+}
+
+String String::right(size_t count) const {
+ size_t len = size();
+ return String(c_str() + len - MIN(count, len), c_str() + len);
+}
+
+String String::mid(size_t start, size_t count) const {
+ int max = (int)size() - start;
+ return String(c_str() + start, c_str() + start + MIN((int)count, max));
+}
+
+String String::mid(size_t start) const {
+ return String(c_str() + start);
+}
+
+void String::del(size_t start, size_t count) {
+ if (start)
+ (*this) = left(start) + mid(start + count - 1);
+ else
+ (*this) = mid(count);
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/string.h b/engines/glk/archetype/string.h
new file mode 100644
index 0000000000..f85ed37d1a
--- /dev/null
+++ b/engines/glk/archetype/string.h
@@ -0,0 +1,143 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_STRING
+#define ARCHETYPE_STRING
+
+#include "common/str.h"
+
+namespace Glk {
+namespace Archetype {
+
+class String : public Common::String {
+public:
+ String() : Common::String() {}
+ String(const char *str) : Common::String(str) {}
+ String(const char *str, uint32 len) : Common::String(str, len) {}
+ String(const char *beginP, const char *endP) : Common::String(beginP, endP) {}
+ String(const Common::String &str) : Common::String(str) {}
+ explicit String(char c) : Common::String(c) {}
+
+ String &operator=(const char *str) {
+ Common::String::operator=(str);
+ return *this;
+ }
+ String &operator=(const Common::String &str) {
+ Common::String::operator=(str);
+ return *this;
+ }
+ String &operator=(char c) {
+ Common::String::operator=(c);
+ return *this;
+ }
+ String &operator+=(const char *str) {
+ Common::String::operator+=(str);
+ return *this;
+ }
+ String &operator+=(const Common::String &str) {
+ Common::String::operator+=(str);
+ return *this;
+ }
+ String &operator+=(char c) {
+ Common::String::operator+=(c);
+ return *this;
+ }
+
+ static String format(const char *fmt, ...) GCC_PRINTF(1, 2);
+
+ static String vformat(const char *fmt, va_list args);
+
+ /**
+ * Returns the index of a character within this string
+ */
+ int indexOf(char c) const;
+
+ /**
+ * Returns the index of a substring within this string
+ */
+ int indexOf(const String &substr) const;
+
+ /**
+ * Returns the last index of a character in a string, or -1 if it isn't present
+ */
+ int lastIndexOf(char c) const;
+
+ /**
+ * Trims spaces(and tabs and newlines) off the ends of a given string
+ */
+ void trim();
+
+ /**
+ * Gets a substring of the string
+ */
+ String substring(int index, int count) const {
+ return String(c_str() + index, c_str() + index + count);
+ }
+
+ /**
+ * Converts a string to a value
+ * @param code Optional returns non-value of character index
+ */
+ int val(int *code);
+
+ /**
+ * Returns a given number of chracters from the start of a string
+ */
+ String left(size_t count) const;
+
+ /**
+ * Returns a given number of characters from the end of a string
+ */
+ String right(size_t count) const;
+
+ /**
+ * Returns a substring of another string
+ */
+ String mid(size_t start) const;
+ String mid(size_t start, size_t count) const;
+
+ /**
+ * Delete a range within a string
+ */
+ void del(size_t start, size_t count);
+};
+
+// Append two strings to form a new (temp) string
+String operator+(const String &x, const String &y);
+
+String operator+(const char *x, const String &y);
+String operator+(const String &x, const char *y);
+
+String operator+(const String &x, char y);
+String operator+(char x, const String &y);
+
+// Some useful additional comparison operators for Strings
+bool operator==(const char *x, const String &y);
+bool operator!=(const char *x, const String &y);
+
+typedef String *StringPtr;
+typedef String ShortStringType;
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
new file mode 100644
index 0000000000..c30a1b87f6
--- /dev/null
+++ b/engines/glk/archetype/sys_object.cpp
@@ -0,0 +1,305 @@
+/* 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/sys_object.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/game_stat.h"
+#include "glk/archetype/heap_sort.h"
+#include "glk/archetype/parser.h"
+#include "glk/archetype/wrap.h"
+#include "common/algorithm.h"
+#include "common/savefile.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum SysStateType {
+ IDLING, INIT_SORTER, OPEN_SORTER, CLOSE_SORTER, NEXT_SORTED, PLAYER_CMD,
+ NORMALIZE, ABBR, OPEN_PARSER, VERB_LIST, NOUN_LIST, CLOSE_PARSER, INIT_PARSER,
+ WHICH_OBJECT, ROLL_CALL, PRESENT, PARSE, NEXT_OBJECT, DEBUG_MESSAGES,
+ DEBUG_EXPRESSIONS, DEBUG_STATEMENTS, DEBUG_MEMORY, FREE_MEMORY, SAVE_STATE, LOAD_STATE
+};
+
+const char *const StateLookup[LOAD_STATE + 1] = {
+ "IDLING", "INIT SORTER", "OPEN SORTER", "CLOSE SORTER", "NEXT SORTED", "PLAYER CMD",
+ "NORMALIZE", "ABBR", "OPEN PARSER", "VERB LIST", "NOUN LIST", "CLOSE PARSER", "INIT PARSER",
+ "WHICH OBJECT", "ROLL CALL", "PRESENT", "PARSE", "NEXT OBJECT", "DEBUG MESSAGES",
+ "DEBUG EXPRESSIONS", "DEBUG STATEMENTS", "DEBUG MEMORY", "FREE MEMORY", "SAVE STATE", "LOAD STATE"
+};
+
+// Global variables which retain the state of the system object between calls
+SysStateType sys_state;
+TargetListType target_list;
+
+void sys_object_init() {
+ sys_state = IDLING;
+ target_list = PARSER_VERBLIST;
+}
+
+static bool figure_state(const String &s) {
+ for (int st = IDLING; st <= LOAD_STATE; ++st) {
+ if (StateLookup[st] == s) {
+ sys_state = (SysStateType)st;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void send_to_system(int transport, String &strmsg, ResultType &result, ContextType &context) {
+ int the_caller;
+ int obj_index;
+ String nomatch;
+ NodePtr np;
+ void *p;
+
+ if (transport == OP_SEND)
+ the_caller = context.self;
+ else
+ the_caller = context.sender;
+
+ do {
+ cleanup(result);
+
+ switch (sys_state) {
+ case IDLING:
+ if (figure_state(strmsg)) {
+ switch (sys_state) {
+ case PLAYER_CMD:
+ case ABBR:
+ case SAVE_STATE:
+ case LOAD_STATE:
+ case OPEN_PARSER:
+ case OPEN_SORTER:
+ case WHICH_OBJECT:
+ return; // come back again!
+
+ case INIT_SORTER:
+ reinit_heap();
+ sys_state = OPEN_SORTER;
+ return;
+
+ case INIT_PARSER:
+ new_parse_list();
+ sys_state = OPEN_PARSER;
+ return;
+
+ default:
+ break;
+ }
+ }
+
+ case PLAYER_CMD:
+ normalize_string(strmsg, g_vm->Command);
+ sys_state = IDLING;
+ break;
+
+ case NORMALIZE:
+ // last normalized command
+ result._kind = STR_PTR;
+ result._str.acl_str = NewDynStr(g_vm->Command);
+ sys_state = IDLING;
+
+ case ABBR:
+ result._kind = STR_PTR;
+ result._str.acl_str = NewDynStr(strmsg);
+
+ if (convert_to(NUMERIC, result)) {
+ g_vm->Abbreviate = result._numeric.acl_int;
+ }
+ else {
+ wraperr("Warning: non-numeric abbreviation message sent to system");
+ cleanup(result);
+ }
+ sys_state = IDLING;
+ break;
+
+ case OPEN_PARSER:
+ if (figure_state(strmsg)) {
+ switch (sys_state) {
+ case CLOSE_PARSER:
+ sys_state = IDLING;
+ break;
+
+ case VERB_LIST:
+ target_list = PARSER_VERBLIST;
+ sys_state = OPEN_PARSER;
+ break;
+
+ case NOUN_LIST:
+ target_list = PARSER_NOUNLIST;
+ sys_state = OPEN_PARSER;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else {
+ add_parse_word(target_list, strmsg, the_caller);
+ }
+
+ return;
+
+ case OPEN_SORTER:
+ if (figure_state(strmsg)) {
+ switch (sys_state) {
+ case CLOSE_SORTER:
+ sys_state = IDLING;
+ break;
+ default:
+ break;
+ }
+ } else {
+ drop_str_on_heap(strmsg);
+ }
+ return;
+
+ case NEXT_SORTED:
+ if (!pop_heap(p)) {
+ cleanup(result);
+ } else {
+ result._kind = STR_PTR;
+ result._str.acl_str = (StringPtr)p;
+ sys_state = IDLING;
+ }
+ break;
+
+ case WHICH_OBJECT:
+ obj_index = find_object(strmsg);
+ if (obj_index != 0) {
+ result._kind = IDENT;
+ result._ident.ident_kind = OBJECT_ID;
+ result._ident.ident_int = obj_index;
+ }
+ sys_state = IDLING;
+ break;
+
+ case ROLL_CALL:
+ dispose_list(g_vm->Proximate);
+ new_list(g_vm->Proximate);
+ sys_state = IDLING;
+ break;
+
+ case PRESENT:
+ np = (NodePtr)malloc(sizeof(NodeType));
+ np->data = nullptr;
+ np->key = the_caller;
+
+ insert_item(g_vm->Proximate, np);
+ sys_state = IDLING;
+ break;
+
+ case PARSE:
+ parse_sentence();
+ sys_state = IDLING;
+ break;
+
+ case NEXT_OBJECT:
+ if (!pop_object(obj_index, nomatch)) {
+ cleanup(result);
+ } else if (obj_index < 0) {
+ result._kind = STR_PTR;
+ result._str.acl_str = NewDynStr(nomatch);
+ } else {
+ result._kind = IDENT;
+ result._ident.ident_kind = OBJECT_ID;
+ result._ident.ident_int = obj_index;
+ }
+
+ sys_state = IDLING;
+ break;
+
+ case DEBUG_MESSAGES:
+ Debug = Debug ^ DEBUG_MSGS;
+ sys_state = IDLING;
+ break;
+
+ case DEBUG_EXPRESSIONS:
+ Debug = Debug ^ DEBUG_EXPR;
+ sys_state = IDLING;
+ break;
+
+ case DEBUG_STATEMENTS:
+ Debug = Debug ^ DEBUG_STMT;
+ sys_state = IDLING;
+ break;
+
+ case DEBUG_MEMORY:
+ wrapout("", true); // get to beginning of line
+ //g_vm->writeln("Maximum memory request: %d bytes", MaxAvail);
+ //g_vm->writeln("Actual free memory: %d bytes", MemAvail);
+ sys_state = IDLING;
+ break;
+
+ case FREE_MEMORY:
+ result._kind = NUMERIC;
+ result._numeric.acl_int = 0xffff; // MemAvail;
+ sys_state = IDLING;
+ break;
+
+ case SAVE_STATE: {
+ Common::OutSaveFile *stfile = g_system->getSavefileManager()->openForSaving(strmsg);
+ if (stfile == nullptr) {
+ g_vm->writeln("Error opening %s", strmsg.c_str());
+ cleanup(result);
+ }
+ else {
+ save_game_state(stfile, g_vm->Object_List);
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_TRUE;
+
+ stfile->finalize();
+ delete stfile;
+ }
+
+ sys_state = IDLING;
+ break;
+ }
+
+ case LOAD_STATE: {
+ Common::InSaveFile *stfile = g_system->getSavefileManager()->openForLoading(strmsg);
+ if (stfile == nullptr) {
+ g_vm->writeln("Error opening %s", strmsg.c_str());
+ cleanup(result);
+ } else {
+ result._kind = RESERVED;
+ result._reserved.keyword = load_game_state(stfile, g_vm->Object_List) ? RW_TRUE : RW_FALSE;
+ delete stfile;
+ }
+
+ sys_state = IDLING;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (g_vm->shouldQuit())
+ sys_state = IDLING;
+ } while (sys_state != IDLING);
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/sys_object.h b/engines/glk/archetype/sys_object.h
new file mode 100644
index 0000000000..5fa9982d45
--- /dev/null
+++ b/engines/glk/archetype/sys_object.h
@@ -0,0 +1,46 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_SYS_OBJECT
+#define ARCHETYPE_SYS_OBJECT
+
+#include "glk/archetype/string.h"
+#include "glk/archetype/interpreter.h"
+
+namespace Glk {
+namespace Archetype {
+
+extern void sys_object_init();
+
+/**
+ * Is the receiver of all "system calls" and the only object that receives
+ * messages in the form of strings rather than message constants.
+ *
+ * Notes: Uses a global variable called sys_state to keep track of its state
+ * between calls.
+ */
+extern void send_to_system(int transport, String &strmsg, ResultType &result, ContextType &context);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/timestamp.cpp b/engines/glk/archetype/timestamp.cpp
new file mode 100644
index 0000000000..d146e4c168
--- /dev/null
+++ b/engines/glk/archetype/timestamp.cpp
@@ -0,0 +1,50 @@
+/* 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/timestamp.h"
+#include "common/system.h"
+
+namespace Glk {
+namespace Archetype {
+
+TimestampType GTimeStamp;
+
+void timestamp_init() {
+ GTimeStamp = 0;
+}
+
+void get_time_stamp(TimestampType &tstamp) {
+ // Get the time and date
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+
+ // Normalize the year
+ tstamp = ((td.tm_year - 1992) % 64) << 26;
+ tstamp |= td.tm_mon << 22;
+ tstamp |= td.tm_mday << 17;
+ tstamp |= td.tm_hour << 12;
+ tstamp |= td.tm_min << 6;
+ tstamp |= td.tm_sec;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/timestamp.h b/engines/glk/archetype/timestamp.h
new file mode 100644
index 0000000000..893ba438ce
--- /dev/null
+++ b/engines/glk/archetype/timestamp.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_TIMESTAMP
+#define ARCHETYPE_TIMESTAMP
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Archetype {
+
+typedef uint32 TimestampType;
+
+extern TimestampType GTimeStamp;
+
+extern void timestamp_init();
+
+/**
+ * Creates a compressed long integer that contains all the necessary time information.
+ * There are enough bits in a 32-bit word to do this :
+ *
+ * Variable Range Bits
+ * -------- ----- ----
+ * Year 0-63 6
+ * Month 1-12 4
+ * Day 0-31 5
+ * Hour 0-23 5
+ * Minute 0-59 6
+ * Second 0-59 6
+ *
+ * Note that Year does not quite fit comfortably into this scheme.The actual returned value
+ * is 1980-2099, a span of 119 years; but we are using only 63. Year 0 is considered 1992
+ * and the upper limit is 2055 before it goes back to year 0 (1992) again.
+ *
+ * The DayOfWeek information is thrown away because it is redundant, and the Sec100 information
+ * is thrown away because it is unnecessarily precise
+ */
+extern void get_time_stamp(TimestampType &tstamp);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/token.cpp b/engines/glk/archetype/token.cpp
new file mode 100644
index 0000000000..3be0e2365b
--- /dev/null
+++ b/engines/glk/archetype/token.cpp
@@ -0,0 +1,441 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers || c == 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 || c == 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 || c == g_vm->write to the Free Software
+ * Foundation || c == Inc. || c == 51 Franklin Street || c == Fifth Floor || c == Boston || c == MA 02110-1301 || c == USA.
+ *
+ */
+
+#include "glk/archetype/token.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/id_table.h"
+#include "glk/archetype/misc.h"
+#include "glk/archetype/keywords.h"
+
+namespace Glk {
+namespace Archetype {
+
+enum StateType { START, STOP, DECIDE, WHITE, COMMENT, QUOTE, LITERAL, IDENTIFIER, NUMBER, OPERATOR };
+
+bool isWhitespace(char c) {
+ return c == ' ' || c == '\t' || c == NEWLINE_CH;
+}
+
+bool isLiteralType(char c) {
+ return c == '"' || c == '\'';
+}
+
+bool isLetter(char c) {
+ return Common::isAlpha(c);
+}
+
+bool isDigit(char c) {
+ return Common::isDigit(c);
+}
+
+bool isStartChar(char c) {
+ return Common::isAlpha(c) || c == '_';
+}
+
+bool isIdChar(char c) {
+ return isStartChar(c) || isDigit(c);
+}
+
+bool isLongOper(char c) {
+ return c == '<' || c == '>' || c == ':' || c == '+' || c == '-' || c == '*'
+ || c == '/' || c == '&' || c == '~';
+}
+
+bool isOperChar(char c) {
+ return isLongOper(c) || c == '=' || c == '.' || c == '^' || c == '?';
+}
+
+/**
+ * Performs a binary search on the given ordered array, passing back the
+ * index of the given string if it's in the array.
+ * Used for quickly finding an operator or reserved word.
+ * @param the_array ordered array of short strings
+ * @param elements number of elements in the array
+ * @param match_str string to match
+ * @param a_index Outputs the array index
+ * @returns true if match_str was an element in the_array; false otherwise
+ */
+static bool binary_search(const LookupType &the_array, int elements,
+ const ShortStringType &match_str, int &a_index) {
+ int left = 0, right = elements - 1, mid;
+
+ do {
+ mid = (left + right) / 2;
+ if (match_str < the_array[mid])
+ right = mid - 1;
+ else
+ left = mid + 1;
+ } while (match_str != the_array[mid] && left <= right);
+
+ if (match_str != the_array[mid]) {
+ return false;
+ } else {
+ a_index = mid;
+ return true;
+ }
+}
+
+/**
+ * Searches the given unordered xarray for a string matching the given
+ * string; if found, returns the index in the list of the string. If
+ * not found, adds it to the list.
+ * @param the_xarray xarray to be searched
+ * @param the_str string to be compared
+ * @returns The index of the_str in the_xarray.
+ */
+static int add_unique_str(XArrayType &the_xarray, const String &the_str) {
+ StringPtr new_str;
+ int i;
+ void *p;
+
+ // Duplicate the given string
+ new_str = NewConstStr(the_str);
+
+ if (the_xarray.empty()) {
+ append_to_xarray(the_xarray, (void *)new_str);
+ return the_xarray.size() - 1;
+ } else {
+ i = 1;
+ while (index_xarray(the_xarray, i, p) && *((StringPtr)p) != the_str)
+ ++i;
+
+ if (*((StringPtr)p) == the_str) {
+ FreeConstStr(new_str);
+ return i;
+ } else {
+ append_to_xarray(the_xarray, (void *)new_str);
+ return the_xarray.size() - 1;
+ }
+ }
+}
+
+/**
+ * Similar to the above, except that it is to be used when the strings are
+ * not expected to repeat much.
+ */
+static int add_non_unique_str(XArrayType &the_xarray, const String &the_str) {
+ append_to_xarray(the_xarray, (void *)NewConstStr(the_str));
+ return the_xarray.size() - 1;
+}
+
+bool get_token(progfile &f) {
+ StateType state;
+ bool more_chars;
+ char bracket, next_ch = '\0';
+ String s;
+
+ // Check for old token. f.newlines may have changed while an old token was unconsumed,
+ // so if the unconsumed token was a NEWLINE and f.newlines is false, we must continue
+ // and get another token; otherwise we jump out with what we have
+ if (!f.consumed) {
+ f.consumed = true;
+
+ if (!((f.ttype == NEWLINE) && !f.newlines))
+ return true;
+ }
+
+ more_chars = true;
+ state = START;
+
+ while (state != STOP) {
+ switch (state) {
+ case START:
+ if (f.readChar(next_ch)) {
+ state = DECIDE;
+ } else {
+ more_chars = false;
+ state = STOP;
+ }
+ break;
+
+ case DECIDE:
+ if (!more_chars)
+ state = STOP;
+ else if (isWhitespace(next_ch))
+ state = WHITE;
+ else if (isLiteralType(next_ch))
+ state = LITERAL;
+ else if (isStartChar(next_ch))
+ state = IDENTIFIER;
+ else if (isDigit(next_ch))
+ state = NUMBER;
+ else if (isOperChar(next_ch))
+ state = OPERATOR;
+ else {
+ // a single-character token
+ switch (next_ch) {
+ case '#':
+ state = COMMENT;
+ case ';':
+ if (!f.newlines) {
+ state = START;
+ }
+ else {
+ f.ttype = NEWLINE;
+ f.tnum = (int)NEWLINE_CH;
+ state = STOP;
+ }
+ default:
+ f.ttype = PUNCTUATION;
+ f.tnum = (int)next_ch;
+ state = STOP;
+ }
+ break;
+ }
+ break;
+
+ case WHITE:
+ while (state == WHITE && isWhitespace(next_ch)) {
+ if (next_ch == NEWLINE_CH && f.newlines) {
+ f.ttype = NEWLINE;
+ state = STOP;
+ } else {
+ more_chars = f.readChar(next_ch);
+ }
+ }
+ if (state == WHITE) {
+ if (more_chars)
+ // decide on new non-white character
+ state = DECIDE;
+ else
+ state = STOP;
+ }
+ break;
+
+ case COMMENT:
+ case QUOTE:
+ s = "";
+ more_chars = f.readChar(next_ch);
+ while (more_chars && next_ch != NEWLINE_CH) {
+ s = s + next_ch;
+ more_chars = f.readChar(next_ch);
+ }
+ if (state == COMMENT) {
+ if (more_chars)
+ state = START;
+ else
+ state = STOP;
+ } else {
+ // quoted literal
+ f.unreadChar(next_ch); // leave \n for the next guy
+ f.ttype = QUOTE_LIT;
+ f.tnum = add_non_unique_str(g_vm->Literals, s);
+ state = STOP;
+ }
+ break;
+
+ case LITERAL:
+ bracket = next_ch;
+ s = "";
+ more_chars = f.readChar(next_ch); // start the loop
+ while (more_chars && next_ch != NEWLINE_CH && next_ch != bracket) {
+ if (next_ch == '\\') {
+ more_chars = f.readChar(next_ch);
+ switch (next_ch) {
+ case 't':
+ next_ch = '\t';
+ break;
+ case 'b':
+ next_ch = '\x8';
+ break;
+ case 'e':
+ next_ch = (char)27;
+ break;
+ case'n':
+ s = s + '\r';
+ next_ch = '\n';
+ break;
+ }
+ }
+ s = s + next_ch;
+
+ more_chars = f.readChar(next_ch);
+ }
+
+ if (next_ch != bracket) {
+ f.sourcePos();
+ error("Unterminated literal");
+ } else {
+ switch (bracket) {
+ case '"':
+ f.ttype = TEXT_LIT;
+ f.tnum = add_non_unique_str(g_vm->Literals, s);
+ break;
+ case '\'':
+ f.ttype = MESSAGE;
+ f.tnum = add_unique_str(g_vm->Vocabulary, s);
+ break;
+ default:
+ error("Programmer error: unknown literal type");
+ break;
+ }
+
+ state = STOP;
+ }
+ break;
+
+ case IDENTIFIER:
+ s = "";
+ while (isIdChar(next_ch)) {
+ s = s + next_ch;
+ more_chars = f.readChar(next_ch);
+ }
+ if (!isIdChar(next_ch))
+ f.unreadChar(next_ch);
+
+ // Check for reserved words or operators
+ if (binary_search(Reserved_Wds, NUM_RWORDS, s, f.tnum))
+ f.ttype = RESERVED;
+ else if (binary_search(Operators, NUM_OPERS, s, f.tnum))
+ f.ttype = OPER;
+ else {
+ f.ttype = IDENT;
+ f.tnum = add_ident(s);
+ }
+
+ state = STOP;
+ break;
+
+ case NUMBER:
+ s = "";
+ while (more_chars && isDigit(next_ch)) {
+ s = s + next_ch;
+ more_chars = f.readChar(next_ch);
+ }
+
+ if (!isDigit(next_ch))
+ f.unreadChar(next_ch);
+ f.ttype = NUMERIC;
+
+ f.tnum = atoi(s.c_str());
+ state = STOP;
+ break;
+
+ case OPERATOR:
+ s = "";
+
+ while (more_chars && isLongOper(next_ch) && s != ">>") {
+ // have to stop short with >>
+ s = s + next_ch;
+ more_chars = f.readChar(next_ch);
+ }
+
+ if (s == ">>") {
+ f.unreadChar(next_ch);
+ state = QUOTE;
+ } else {
+ if (!isOperChar(next_ch))
+ f.unreadChar(next_ch);
+ else
+ s = s + next_ch;
+
+ state = STOP;
+
+ if (s == ":") {
+ f.ttype = PUNCTUATION;
+ f.tnum = (int)':';
+ } else if (!binary_search(Operators, NUM_OPERS, s, f.tnum)) {
+ f.sourcePos();
+ error("Unknown operator %s", s.c_str());
+ } else {
+ f.ttype = OPER;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return more_chars;
+}
+
+void write_token(AclType the_type, int the_number) {
+ StringPtr str_ptr;
+ IdRecPtr the_id_ptr;
+ void *p;
+
+ switch (the_type) {
+ case IDENT:
+ if (the_number < 0) {
+ g_vm->write("an identifier");
+ }
+ else {
+ g_vm->write("<identifier %d >: ", the_number);
+ if (index_ident(the_number, the_id_ptr))
+ g_vm->write("\"%s\"", the_id_ptr->id_name);
+ }
+ break;
+
+ case RESERVED:
+ if (the_number < 0)
+ g_vm->write("a reserved word");
+ else
+ g_vm->write("reserved word \"%s\"", Reserved_Wds[the_number]);
+ break;
+
+ case OPER:
+ if (the_number < 0)
+ g_vm->write("an operator");
+ else
+ g_vm->write("operator \"%s\"", Operators[the_number]);
+ break;
+
+ case PUNCTUATION:
+ g_vm->write("%c", (char)the_number);
+ break;
+
+ case TEXT_LIT:
+ if (the_number < 0)
+ g_vm->write("a text literal");
+ else if (index_xarray(g_vm->Literals, the_number, p)) {
+ str_ptr = (StringPtr)p;
+ g_vm->write("\"%s\"", str_ptr->c_str());
+ }
+ else {
+ g_vm->write("<text literal %d >: ", the_number);
+ }
+ break;
+
+ case MESSAGE:
+ if (the_number < 0)
+ g_vm->write("a message");
+ else if (index_xarray(g_vm->Vocabulary, the_number, p)) {
+ str_ptr = (StringPtr)p;
+ g_vm->write("\'%s\'", str_ptr->c_str());
+ } else {
+ g_vm->write("<message %d>: ", the_number);
+ }
+ break;
+
+ case NUMERIC:
+ g_vm->write("the number %d", the_number);
+ break;
+
+ default:
+ g_vm->write("<unknown token>");
+ }
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/token.h b/engines/glk/archetype/token.h
new file mode 100644
index 0000000000..eda985eaf9
--- /dev/null
+++ b/engines/glk/archetype/token.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_TOKEN
+#define ARCHETYPE_TOKEN
+
+#include "glk/archetype/misc.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Archetype {
+
+/*
+extern bool isLiteralType(char c);
+extern bool isLetter(char c);
+extern bool isDigit(char c);
+extern bool isStartChar(char c);
+extern bool isIdChar(char c);
+extern bool isLongOper(char c);
+extern bool isOperChar(char c);
+*/
+
+/**
+ * State machine which passes out the next token from the file f_in.
+ *
+ * A token is a constant (including parse words and literal text),
+ * a reserved word, or an operator (including the curly braces).
+ * @param f The input file
+ * @returns True if there is a token available, false if the file is empty
+ */
+extern bool get_token(progfile &f);
+
+/**
+ * Given a token type andtoken number, writes out the proper string (without terminating the line).
+ * @param the_type the token type
+ * @param the_number the token number
+ */
+extern void write_token(AclType the_type, int the_number);
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/wrap.cpp b/engines/glk/archetype/wrap.cpp
new file mode 100644
index 0000000000..6ef961f0d3
--- /dev/null
+++ b/engines/glk/archetype/wrap.cpp
@@ -0,0 +1,147 @@
+/* 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/wrap.h"
+#include "glk/archetype/archetype.h"
+
+namespace Glk {
+namespace Archetype {
+
+const int
+ MAXCOLS = 75, // leave room for punctuation
+ SAFETY_MARGIN = 3,
+ MAXROWS = 24,
+
+ REVERSE_VID = 3,
+ BOLDFACE = 8;
+
+int Rows;
+int cursor;
+
+void wrap_init() {
+ cursor_reset();
+ Rows = 0;
+}
+
+static void wrap_wait() {
+#ifdef UNUSED
+ char ch;
+
+ TextColor(BOLDFACE); TextBackground(REVERSE_VID);
+ write('Hit any key to continue...');
+ ch := ReadKey;
+ write(chr(13));
+ NormVideo;
+ ClrScr; //or ClrEol if you don't want the whole screen }
+ Rows : = 0
+#endif
+}
+
+void wrapint(int i, bool terminate) {
+ String s = String::format("%d", i);
+ wrapout(s, terminate);
+}
+
+void wrapout(const String &str, bool terminate) {
+ int thisline, maxchars, startnext;
+ String s = str;
+
+ // 'thisline' starts out as the maximum number of characters that can be
+ // written before a newline; it gets trimmed back to being the number of
+ // characters from the string that are actually written on this line. }
+ maxchars = MAXCOLS - cursor;
+
+ const char CHARS[7] = { '.', ',', ':', ';', ')', '-', '"' };
+ for (int i = 0; i < 7; ++i) {
+ if (s[0] == CHARS[i]) {
+ maxchars += SAFETY_MARGIN;
+ break;
+ }
+ }
+
+ thisline = maxchars;
+ while (thisline < (int)s.size()) {
+ while (thisline >= 0 && s[thisline] != ' ')
+ --thisline;
+ }
+
+ // If we were unable to find a wrapping point then it means one of two
+ // things : a) the string is too long to fit on one line, andmust be
+ // split unnaturally; or b) we are near the end of a line andmust wrap
+ // the entire string; i.e.print nothing, finish the line andgo on
+ if (thisline == 0 && s.size() > MAXCOLS)
+ thisline = maxchars + 1;
+
+ g_vm->writeln(s.substring(0, thisline - 1));
+ ++Rows;
+ if (Rows >= MAXROWS)
+ wrap_wait();
+
+ startnext = thisline;
+ while (startnext < (int)s.size() && s[startnext] == ' ') {
+ ++startnext;
+
+ s = s.substring(startnext, s.size());
+ cursor = 1;
+ thisline = MAXCOLS - cursor;
+ }
+
+ g_vm->write(s);
+ cursor += s.size();
+
+ if (terminate) {
+ g_vm->writeln();
+ ++Rows;
+ if (Rows >= MAXROWS)
+ wrap_wait();
+ cursor = 1;
+ }
+}
+
+void wraperr(const String &s) {
+ if (cursor > 1)
+ g_vm->writeln();
+ g_vm->writeln(s);
+
+ String tmp;
+ for (int i = 1; i < cursor; ++i)
+ tmp += ' ';
+ g_vm->write(tmp);
+}
+
+StringPtr ReadLine(bool full_line) {
+ String s;
+
+ if (full_line)
+ g_vm->readln(s);
+ else
+ s = g_vm->ReadKey();
+
+ return NewDynStr(s);
+}
+
+void cursor_reset() {
+ cursor = 1;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/wrap.h b/engines/glk/archetype/wrap.h
new file mode 100644
index 0000000000..7195303b9f
--- /dev/null
+++ b/engines/glk/archetype/wrap.h
@@ -0,0 +1,65 @@
+/* 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.
+ *
+ */
+
+#ifndef ARCHETYPE_WRAP
+#define ARCHETYPE_WRAP
+
+#include "glk/archetype/misc.h"
+
+namespace Glk {
+namespace Archetype {
+
+extern int Rows;
+
+/**
+ * When we want to wrap a number
+ */
+extern void wrapint(int i, bool terminate);
+
+/**
+ * Given a string, writes it out to screen, making sure that if it exceeds the screen columns,
+ * it is broken at natural word boundaries (i.e. white space)
+ */
+extern void wrapout(const String &s, bool terminate);
+
+/**
+ * Used for printing run-time errors. It will print the error message on
+ * a line by itself and pick up the next line at the exact same cursor position.
+ */
+extern void wraperr(const String &s);
+
+/**
+ * Hides the extra stack space necessary for performing a readln() so that
+ * it won't affect eval_expr
+ */
+extern StringPtr ReadLine(bool full_line);
+
+/**
+ * Used for directly resetting the cursor position by means other than
+ * physically wrapping it around
+ */
+extern void cursor_reset();
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 449607cdf0..8915c669f3 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -146,7 +146,26 @@ endif
ifdef ENABLE_GLK_ARCHETYPE
MODULE_OBJS += \
archetype/archetype.o \
- archetype/detection.o
+ archetype/array.o \
+ archetype/crypt.o \
+ archetype/detection.o \
+ archetype/error.o \
+ archetype/expression.o \
+ archetype/game_stat.o \
+ archetype/heap_sort.o \
+ archetype/id_table.o \
+ archetype/interpreter.o \
+ archetype/keywords.o \
+ archetype/linked_list.o \
+ archetype/misc.o \
+ archetype/parser.o \
+ archetype/saveload.o \
+ archetype/semantic.o \
+ archetype/string.o \
+ archetype/sys_object.o \
+ archetype/timestamp.o \
+ archetype/token.o \
+ archetype/wrap.o
endif
ifdef ENABLE_GLK_FROTZ