[Scummvm-git-logs] scummvm master -> 7ca72705320760450d9afc32fc5659b0f661d064

dreammaster paulfgilbert at gmail.com
Tue Nov 12 04:04:22 CET 2019


This automated email contains information about 22 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
0b3f59c3f8 GLK: ARCHETYPE: Skeleton engine
7ee61fa5d4 GLK: ARCHETYPE: Added converted files
be85010699 GLK: ARCHETYPE: Various startup fixes
d4cba56b41 GLK: ARCHETYPE: Further loading fixes
fdb5ead5ca GLK: ARCHETYPE: Fixes for string decryption
e1911f9aff GLK: Fix structures using unions
3165d52a7b GLK: ARCHETYPE: Adding GLK hookups
d51d3d4086 COMMON: Allow for enabling/disabling debug channels by number
a3c646133f GLK: ARCHETYPE: Switch from original's Debug enum to debug channels
c570d6f4b2 GLK: ARCHETYPE: Fix array indexing to match original's 1 based
53fd662ac0 GLK: ARCHETYPE: Change to use C++ new/delete
381bd5e9a7 GLK: ARCHETYPE: Further array indexing fixes
1b8e171944 GLK: ARCHETYPE: Janitorial
a8367cb808 GLK: ARCHETYPE: Shift debug output to use ScummVM debug
38e5463601 GLK: ARCHETYPE: Further cleanup and debug reroute
5d8b8752e8 GLK: ARCHETYPE: Script handler fixes
86e7717e62 GLK: ARCHETYPE: Cleanup and fixes for text display
19cc1221f3 GLK: ARCHETYPE: Engine fixes for input line processing
ab5939ba44 GLK: ARCHETYPE: Fix quitting the game
f3b02a115f GLK: ARCHETYPE: Cleaner exit when game is quit
75def2b480 GLK: ARCHETYPE: Fixes to correctly parse multi-word inputs
7ca7270532 GLK: ARCHETYPE: gcc compilation fixes


Commit: 0b3f59c3f87debc8fc25cb5e540be87904207cbb
    https://github.com/scummvm/scummvm/commit/0b3f59c3f87debc8fc25cb5e540be87904207cbb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Skeleton engine

Changed paths:
  A engines/glk/archetype/archetype.cpp
  A engines/glk/archetype/archetype.h
  A engines/glk/archetype/detection.cpp
  A engines/glk/archetype/detection.h
  A engines/glk/archetype/detection_tables.h
    engines/glk/configure.engine
    engines/glk/detection.cpp
    engines/glk/glk_types.h
    engines/glk/module.mk


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
new file mode 100644
index 0000000..d2409bf
--- /dev/null
+++ b/engines/glk/archetype/archetype.cpp
@@ -0,0 +1,58 @@
+/* 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/archetype.h"
+#include "common/config-manager.h"
+
+namespace Glk {
+namespace Archetype {
+
+Archetype *g_vm;
+
+Archetype::Archetype(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
+	_saveSlot(-1) {
+	g_vm = this;
+}
+
+void Archetype::runGame() {
+
+}
+
+bool Archetype::initialize() {
+	return true;
+}
+
+void Archetype::deinitialize() {
+}
+
+Common::Error Archetype::readSaveData(Common::SeekableReadStream *rs) {
+	// TODO
+	return Common::kReadingFailed;
+}
+
+Common::Error Archetype::writeGameData(Common::WriteStream *ws) {
+	// TODO
+	return Common::kWritingFailed;
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/archetype.h b/engines/glk/archetype/archetype.h
new file mode 100644
index 0000000..709ebc6
--- /dev/null
+++ b/engines/glk/archetype/archetype.h
@@ -0,0 +1,88 @@
+/* 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_ARCHETYPE
+#define ARCHETYPE_ARCHETYPE
+
+#include "glk/glk_api.h"
+
+namespace Glk {
+namespace Archetype {
+
+/**
+ * Archetype game interpreter
+ */
+class Archetype : public GlkAPI {
+private:
+	int _saveSlot;
+private:
+	/**
+	 * Engine initialization
+	 */
+	bool initialize();
+
+	/**
+	 * Engine cleanup
+	 */
+	void deinitialize();
+public:
+	/**
+	 * Constructor
+	 */
+	Archetype(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Run the game
+	 */
+	virtual void runGame() override;
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override {
+		return INTERPRETER_ARCHETYPE;
+	}
+
+	/**
+	 * Savegames aren't supported for Archetype games
+	 */
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
+
+	/**
+	 * Savegames aren't supported for Archetype games
+	 */
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
+
+	/**
+	 * Returns true if a savegame is being loaded directly from the ScummVM launcher
+	 */
+	bool loadingSavegame() const {
+		return _saveSlot != -1;
+	}
+};
+
+extern Archetype *g_vm;
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/detection.cpp b/engines/glk/archetype/detection.cpp
new file mode 100644
index 0000000..c5f7e19
--- /dev/null
+++ b/engines/glk/archetype/detection.cpp
@@ -0,0 +1,93 @@
+/* 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/detection.h"
+#include "glk/archetype/detection_tables.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace Archetype {
+
+void ArchetypeMetaEngine::getSupportedGames(PlainGameList &games) {
+	for (const PlainGameDescriptor *pd = ARCHETYPE_GAME_LIST; pd->gameId; ++pd)
+		games.push_back(*pd);
+}
+
+GameDescriptor ArchetypeMetaEngine::findGame(const char *gameId) {
+	for (const PlainGameDescriptor *pd = ARCHETYPE_GAME_LIST; pd->gameId; ++pd) {
+		if (!strcmp(gameId, pd->gameId))
+			return *pd;
+	}
+
+	return GameDescriptor::empty();
+}
+
+bool ArchetypeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+	// Loop through the files of the folder
+	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+		// Check for a recognised filename
+		if (file->isDirectory())
+			continue;
+
+		Common::String filename = file->getName();
+		if (!filename.hasSuffixIgnoreCase(".acx"))
+			continue;
+
+		Common::File gameFile;
+		if (!gameFile.open(*file))
+			continue;
+
+		gameFile.seek(0);
+		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+		uint32 filesize = gameFile.size();
+
+		// Scan through the Archetype game list for a match
+		const GlkDetectionEntry *p = ARCHETYPE_GAMES;
+		while (p->_md5 && p->_filesize != filesize && md5 != p->_md5)
+			++p;
+
+		if (!p->_gameId) {
+			const PlainGameDescriptor &desc = ARCHETYPE_GAME_LIST[0];
+			gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
+		} else {
+			// Found a match
+			PlainGameDescriptor gameDesc = findGame(p->_gameId);
+			gameList.push_back(GlkDetectedGame(p->_gameId, gameDesc.description, filename));
+		}
+	}
+
+	return !gameList.empty();
+}
+
+void ArchetypeMetaEngine::detectClashes(Common::StringMap &map) {
+	for (const PlainGameDescriptor *pd = ARCHETYPE_GAME_LIST; pd->gameId; ++pd) {
+		if (map.contains(pd->gameId))
+			error("Duplicate game Id found - %s", pd->gameId);
+		map[pd->gameId] = "";
+	}
+}
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/archetype/detection.h b/engines/glk/archetype/detection.h
new file mode 100644
index 0000000..05b3a96
--- /dev/null
+++ b/engines/glk/archetype/detection.h
@@ -0,0 +1,60 @@
+/* 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_DETECTION
+#define ARCHETYPE_DETECTION
+
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "engines/game.h"
+#include "glk/detection.h"
+
+namespace Glk {
+namespace Archetype {
+
+class ArchetypeMetaEngine {
+public:
+	/**
+	 * Get a list of supported games
+	 */
+	static void getSupportedGames(PlainGameList &games);
+
+	/**
+	 * Returns a game description for the given game Id, if it's supported
+	 */
+	static GameDescriptor findGame(const char *gameId);
+
+	/**
+	 * Detect supported games
+	 */
+	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+
+	/**
+	 * Check for game Id clashes with other sub-engines
+	 */
+	static void detectClashes(Common::StringMap &map);
+};
+
+} // End of namespace Archetype
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/archetype/detection_tables.h b/engines/glk/archetype/detection_tables.h
new file mode 100644
index 0000000..fdee12b
--- /dev/null
+++ b/engines/glk/archetype/detection_tables.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.
+ *
+ */
+
+#include "engines/game.h"
+#include "common/gui_options.h"
+#include "common/language.h"
+
+namespace Glk {
+namespace Archetype {
+
+const PlainGameDescriptor ARCHETYPE_GAME_LIST[] = {
+	{ "archetype", "Archetype IF Game" },
+
+	{ "guesstheanimal", "Guess the Animal" },
+	{ "gorreven", "The Gorreven Papers" },
+	{ "starshipsolitaire", "The Starship Solitaire adventure" },
+
+	{ nullptr, nullptr }
+};
+
+const GlkDetectionEntry ARCHETYPE_GAMES[] = {
+	DT_ENTRY0("guesstheanimal", "f1c4d7ba35db9f217eacd84181b4bb33", 1266),
+	DT_ENTRY0("gorreven", "073a996b158474a2c419bc1d9dc8d44b", 66793),
+	DT_ENTRY0("starshipsolitaire", "6c86208d0d84fb11f81bf5b1f6fedb84", 55762),
+
+	DT_END_MARKER
+};
+
+} // End of namespace Archetype
+} // End of namespace Glk
diff --git a/engines/glk/configure.engine b/engines/glk/configure.engine
index 727aa59..507bcfe 100644
--- a/engines/glk/configure.engine
+++ b/engines/glk/configure.engine
@@ -1,10 +1,11 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine glk "Glk Interactive Fiction games" no "glk_adrift glk_advsys glk_alan2 glk_alan3 glk_frotz glk_glulxe glk_hugo glk_jacl glk_level9 glk_magnetic glk_quest glk_scott glk_tads" "Base" "16bit freetype2 jpeg png"
+add_engine glk "Glk Interactive Fiction games" no "glk_adrift glk_advsys glk_alan2 glk_alan3 glk_archetype glk_frotz glk_glulxe glk_hugo glk_jacl glk_level9 glk_magnetic glk_quest glk_scott glk_tads" "Base" "16bit freetype2 jpeg png"
 add_engine glk_adrift "ADRIFT" no
 add_engine glk_advsys "AdvSys" no
 add_engine glk_alan2 "Alan2" no
 add_engine glk_alan3 "Alan3" no
+add_engine glk_archetype "Archetype" no
 add_engine glk_frotz "Frotz" no
 add_engine glk_glulxe "Glulxe" no
 add_engine glk_hugo "Hugo" no
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 1e465e9..9cbda0f 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -44,6 +44,11 @@
 #include "glk/alan3/alan3.h"
 #endif
 
+#ifdef ENABLE_GLK_ARCHETYPE
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/detection.h"
+#endif
+
 #ifdef ENABLE_GLK_FROTZ
 #include "glk/frotz/detection.h"
 #include "glk/frotz/frotz.h"
@@ -217,6 +222,10 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 	if ((*engine = create<Glk::Alan3::Alan3MetaEngine, Glk::Alan3::Alan3>(syst, gameDesc)) != nullptr) {}
 	else
 #endif
+#ifdef ENABLE_GLK_ARCHETYPE
+	if ((*engine = create<Glk::Archetype::ArchetypeMetaEngine, Glk::Archetype::Archetype>(syst, gameDesc)) != nullptr) {}
+	else
+#endif
 #ifdef ENABLE_GLK_FROTZ
 	if ((*engine = create<Glk::Frotz::FrotzMetaEngine, Glk::Frotz::Frotz>(syst, gameDesc)) != nullptr) {}
 	else
@@ -300,6 +309,9 @@ PlainGameList GlkMetaEngine::getSupportedGames() const {
 #ifdef ENABLE_GLK_ALAN3
 	Glk::Alan3::Alan3MetaEngine::getSupportedGames(list);
 #endif
+#ifdef ENABLE_GLK_ARCHETYPE
+	Glk::Archetype::ArchetypeMetaEngine::getSupportedGames(list);
+#endif
 #ifdef ENABLE_GLK_FROTZ
 	Glk::Frotz::FrotzMetaEngine::getSupportedGames(list);
 #endif
@@ -348,6 +360,9 @@ PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
 #ifdef ENABLE_GLK_ALAN3
 	FIND_GAME(Alan3);
 #endif
+#ifdef ENABLE_GLK_ARCHETYPE
+	FIND_GAME(Archetype);
+#endif
 #ifdef ENABLE_GLK_FROTZ
 	FIND_GAME(Frotz);
 #endif
@@ -398,6 +413,9 @@ DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
 #ifdef ENABLE_GLK_ALAN3
 	Glk::Alan3::Alan3MetaEngine::detectGames(fslist, detectedGames);
 #endif
+#ifdef ENABLE_GLK_ARCHETYPE
+	Glk::Archetype::ArchetypeMetaEngine::detectGames(fslist, detectedGames);
+#endif
 #ifdef ENABLE_GLK_FROTZ
 	Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
 #endif
@@ -443,6 +461,9 @@ void GlkMetaEngine::detectClashes() const {
 #ifdef ENABLE_GLK_ALAN3
 	Glk::Alan3::Alan3MetaEngine::detectClashes(map);
 #endif
+#ifdef ENABLE_GLK_ARCHETYPE
+	Glk::Archetype::ArchetypeMetaEngine::detectClashes(map);
+#endif
 #ifdef ENABLE_GLK_FROTZ
 	Glk::Frotz::FrotzMetaEngine::detectClashes(map);
 #endif
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index d3db3d4..e730f15 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -40,6 +40,7 @@ enum InterpreterType {
 	INTERPRETER_AGILITY,
 	INTERPRETER_ALAN2,
 	INTERPRETER_ALAN3,
+	INTERPRETER_ARCHETYPE,
 	INTERPRETER_BOCFEL,
 	INTERPRETER_FROTZ,
 	INTERPRETER_GEAS,
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 840a1d8..449607c 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -143,6 +143,12 @@ MODULE_OBJS += \
 	alan3/word.o
 endif
 
+ifdef ENABLE_GLK_ARCHETYPE
+MODULE_OBJS += \
+	archetype/archetype.o \
+	archetype/detection.o
+endif
+
 ifdef ENABLE_GLK_FROTZ
 MODULE_OBJS += \
 	frotz/bitmap_font.o \


Commit: 7ee61fa5d4a6dfd6880e48b83a8dae1d485d1e94
    https://github.com/scummvm/scummvm/commit/7ee61fa5d4a6dfd6880e48b83a8dae1d485d1e94
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Added converted files

Changed paths:
  A engines/glk/archetype/array.cpp
  A engines/glk/archetype/array.h
  A engines/glk/archetype/crypt.cpp
  A engines/glk/archetype/crypt.h
  A engines/glk/archetype/error.cpp
  A engines/glk/archetype/error.h
  A engines/glk/archetype/expression.cpp
  A engines/glk/archetype/expression.h
  A engines/glk/archetype/game_stat.cpp
  A engines/glk/archetype/game_stat.h
  A engines/glk/archetype/heap_sort.cpp
  A engines/glk/archetype/heap_sort.h
  A engines/glk/archetype/id_table.cpp
  A engines/glk/archetype/id_table.h
  A engines/glk/archetype/interpreter.cpp
  A engines/glk/archetype/interpreter.h
  A engines/glk/archetype/keywords.cpp
  A engines/glk/archetype/keywords.h
  A engines/glk/archetype/linked_list.cpp
  A engines/glk/archetype/linked_list.h
  A engines/glk/archetype/misc.cpp
  A engines/glk/archetype/misc.h
  A engines/glk/archetype/parser.cpp
  A engines/glk/archetype/parser.h
  A engines/glk/archetype/saveload.cpp
  A engines/glk/archetype/saveload.h
  A engines/glk/archetype/semantic.cpp
  A engines/glk/archetype/semantic.h
  A engines/glk/archetype/statement.h
  A engines/glk/archetype/string.cpp
  A engines/glk/archetype/string.h
  A engines/glk/archetype/sys_object.cpp
  A engines/glk/archetype/sys_object.h
  A engines/glk/archetype/timestamp.cpp
  A engines/glk/archetype/timestamp.h
  A engines/glk/archetype/token.cpp
  A engines/glk/archetype/token.h
  A engines/glk/archetype/wrap.cpp
  A engines/glk/archetype/wrap.h
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/archetype.h
    engines/glk/module.mk


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index d2409bf..4b19c57 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 709ebc6..dec950c 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 0000000..b816306
--- /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 0000000..17379f1
--- /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 0000000..d99dab0
--- /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 0000000..d978210
--- /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 0000000..18cb4d6
--- /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 0000000..c6b70c0
--- /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 0000000..6f71dd5
--- /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 0000000..3fc6007
--- /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 0000000..9f6a8b8
--- /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 0000000..ae4a1bb
--- /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 0000000..98753cc
--- /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 0000000..5e25124
--- /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 0000000..a611503
--- /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 0000000..2aff6d6
--- /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 0000000..108b948
--- /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 0000000..2ee357a
--- /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 0000000..c794750
--- /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 0000000..f36c66e
--- /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 0000000..90cca17
--- /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 0000000..372de36
--- /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 0000000..34c067c
--- /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 0000000..0799077
--- /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 0000000..0063e28
--- /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 0000000..c5185b0
--- /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 0000000..2a965e3
--- /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 0000000..fe6251b
--- /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 0000000..4e2cd37
--- /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 0000000..2166e81
--- /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 0000000..0be8c1b
--- /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 0000000..4db4688
--- /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 0000000..f85ed37
--- /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 0000000..c30a1b8
--- /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 0000000..5fa9982
--- /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 0000000..d146e4c1
--- /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 0000000..893ba43
--- /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 0000000..3be0e23
--- /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 0000000..eda985e
--- /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 0000000..6ef961f
--- /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 0000000..7195303
--- /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 449607c..8915c66 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


Commit: be8501069952b2633cd62939d35dba6fc83bed13
    https://github.com/scummvm/scummvm/commit/be8501069952b2633cd62939d35dba6fc83bed13
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Various startup fixes

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/archetype.h
    engines/glk/archetype/expression.cpp
    engines/glk/archetype/expression.h
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/keywords.h
    engines/glk/archetype/linked_list.cpp
    engines/glk/archetype/misc.cpp
    engines/glk/archetype/misc.h
    engines/glk/archetype/saveload.cpp
    engines/glk/archetype/saveload.h


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 4b19c57..37b40ab 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -93,7 +93,7 @@ Common::Error Archetype::writeGameData(Common::WriteStream *ws) {
 
 void Archetype::interpret() {
 	Translating = false;
-	bool success = loadGame();
+	bool success = load_game(&_gameFile);
 	_gameFile.close();
 
 	if (!success)
@@ -109,10 +109,6 @@ void Archetype::interpret() {
 	cleanup(result);
 }
 
-bool Archetype::loadGame() {
-	return false;
-}
-
 void Archetype::write(const String &fmt, ...) {
 	// TODO
 }
@@ -773,7 +769,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		else if (the_stmt->_kind == ST_STOP) {
 			g_vm->writeln();
 			g_vm->writeln();
-			error("%s", VERSION);
+			error("%f", VERSION_NUM);
 		}
 		break;
 
diff --git a/engines/glk/archetype/archetype.h b/engines/glk/archetype/archetype.h
index dec950c..f671df3 100644
--- a/engines/glk/archetype/archetype.h
+++ b/engines/glk/archetype/archetype.h
@@ -73,11 +73,6 @@ private:
 	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.
diff --git a/engines/glk/archetype/expression.cpp b/engines/glk/archetype/expression.cpp
index 6f71dd5..f468b4f 100644
--- a/engines/glk/archetype/expression.cpp
+++ b/engines/glk/archetype/expression.cpp
@@ -25,11 +25,12 @@
 namespace Glk {
 namespace Archetype {
 
-bool Right_Assoc[NUM_OPERS + 1];
-bool Binary[NUM_OPERS + 1];
-int8 Precedence[NUM_OPERS + 1];
+bool Right_Assoc[NUM_OPERS + 2];
+bool Binary[NUM_OPERS + 2];
+int8 Precedence[NUM_OPERS + 2];
 
 void expression_init() {
+	Binary[OP_NOP]        = false;
 	Binary[OP_LPAREN]     = false;
 	Binary[OP_DOT]        = true;
 	Binary[OP_CHS]        = false;
@@ -124,6 +125,5 @@ void expression_init() {
 	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
index 3fc6007..4e927ec 100644
--- a/engines/glk/archetype/expression.h
+++ b/engines/glk/archetype/expression.h
@@ -78,9 +78,9 @@ 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 bool Right_Assoc[NUM_OPERS + 2];
+extern bool Binary[NUM_OPERS + 2];
+extern int8 Precedence[NUM_OPERS + 2];
 
 extern void expression_init();
 
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index 108b948..de4273f 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -423,14 +423,20 @@ bool load_game(Common::ReadStream *f_in) {
 		}
 	}
 
+	// Get the textual version of the game's version. This is easier than dealing with
+	// the Pascal Real type the game files use for their version number
+	String versionStr;
+	while (!f_in->eos() && (ch = (char)f_in->readByte()) != '\n')
+		versionStr += ch;
+	fileVersion = atof(versionStr.c_str());
+
 	// 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;
+	// Skip over 6 byte Real encoded version number
 	(void)f_in->readUint32LE();
+	(void)f_in->readUint16LE();
 
 	if (fileVersion > VERSION_NUM) {
 		g_vm->writeln("This version of PERFORM is %.1f; file version is %.1f",
@@ -440,7 +446,7 @@ bool load_game(Common::ReadStream *f_in) {
 	}
 
 	// Get encryption information
-	Encryption = (EncryptionType)f_in->readUint16LE();
+	Encryption = (EncryptionType)f_in->readByte();
 
 	// Read the timestamp.  It is used to verify saved game states, and also to prime the encryption
 	GTimeStamp = f_in->readUint32LE();
@@ -455,7 +461,7 @@ bool load_game(Common::ReadStream *f_in) {
 	cryptinit(Encryption, GTimeStamp);
 
 	// Where's the main object?
-	MainObject = f_in->readSint32LE();
+	MainObject = f_in->readUint16LE();
 
 	load_obj_list(f_in, g_vm->Object_List);
 	load_obj_list(f_in, g_vm->Type_List);
diff --git a/engines/glk/archetype/keywords.h b/engines/glk/archetype/keywords.h
index f36c66e..118a119 100644
--- a/engines/glk/archetype/keywords.h
+++ b/engines/glk/archetype/keywords.h
@@ -77,6 +77,7 @@ enum ReservedWordId {
 };
 
 enum OperatorId {
+	OP_NOP = 0,
 	OP_CONCAT = 1,
 	OP_C_CONCAT = 2,
 	OP_MULTIPLY = 3,
diff --git a/engines/glk/archetype/linked_list.cpp b/engines/glk/archetype/linked_list.cpp
index 90cca17..bc4719c 100644
--- a/engines/glk/archetype/linked_list.cpp
+++ b/engines/glk/archetype/linked_list.cpp
@@ -27,7 +27,11 @@ namespace Glk {
 namespace Archetype {
 
 void new_list(ListType &the_list) {
-	the_list = new NodeType();
+	the_list = (ListType)malloc(sizeof(NodeType));
+	add_bytes(sizeof(NodeType));
+	the_list->key = 0;
+	the_list->data = nullptr;
+	the_list->next = nullptr;
 }
 
 void dispose_list(ListType &the_list) {
diff --git a/engines/glk/archetype/misc.cpp b/engines/glk/archetype/misc.cpp
index 34c067c..e5a858b 100644
--- a/engines/glk/archetype/misc.cpp
+++ b/engines/glk/archetype/misc.cpp
@@ -28,8 +28,7 @@ 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;
+const double VERSION_NUM = 1.02;
 
 size_t Bytes;
 int Debug;
diff --git a/engines/glk/archetype/misc.h b/engines/glk/archetype/misc.h
index 0799077..8d93ce7 100644
--- a/engines/glk/archetype/misc.h
+++ b/engines/glk/archetype/misc.h
@@ -110,7 +110,6 @@ public:
 
 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
diff --git a/engines/glk/archetype/saveload.cpp b/engines/glk/archetype/saveload.cpp
index 2a965e3..5c018c8 100644
--- a/engines/glk/archetype/saveload.cpp
+++ b/engines/glk/archetype/saveload.cpp
@@ -42,7 +42,7 @@ void saveload_init() {
 // ===================== Forward Declarations =======================
 
 static void walk_item_list(MissionType mission, Common::Stream *bfile,
-	ListType elements, ContentType content);
+	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);
 
@@ -50,11 +50,11 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 
 // Wrappers
 
-void load_item_list(Common::ReadStream *f_in, ListType elements, ContentType content) {
+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) {
+void dump_item_list(Common::WriteStream *f_out, ListType &elements, ContentType content) {
 	walk_item_list(DUMP, f_out, elements, content);
 }
 
@@ -70,7 +70,7 @@ void dispose_item_list(ListType &elements, ContentType content) {
  * @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) {
+void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elements, ContentType content) {
 	StatementKind sentinel;
 	StatementPtr this_stmt;
 	ExprTree this_expr;
@@ -85,7 +85,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType element
 	switch (mission) {
 	case LOAD:
 		assert(readStream);
-		sentinel = (StatementKind)readStream->readUint32LE();
+		sentinel = (StatementKind)readStream->readUint16LE();
 		new_list(elements);
 		yet_more = sentinel == CONT_SEQ;
 		break;
@@ -107,13 +107,13 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType element
 			assert(readStream);
 			np = (NodePtr)malloc(sizeof(NodeType));
 			add_bytes(sizeof(NodeType));
-			np->key = readStream->readSint32LE();
+			np->key = readStream->readSint16LE();
 			break;
 
 		case DUMP:
 			assert(writeStream);
-			writeStream->writeUint32LE(vContSeq);
-			writeStream->writeSint32LE(np->key);
+			writeStream->writeUint16LE(vContSeq);
+			writeStream->writeSint16LE(np->key);
 			break;
 
 		default:
@@ -189,7 +189,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType element
 		case LOAD:
 			assert(readStream);
 			append_to_list(elements, np);
-			sentinel = (StatementKind)readStream->readUint32LE();
+			sentinel = (StatementKind)readStream->readUint16LE();
 			yet_more = sentinel == CONT_SEQ;
 			break;
 
@@ -206,7 +206,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType element
 	// Postlude
 	switch (mission) {
 	case DUMP:
-		writeStream->writeUint32LE(vEndSeq);
+		writeStream->writeUint16LE(vEndSeq);
 		break;
 	case FREE:
 		dispose_list(elements);
@@ -260,7 +260,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 		assert(readStream);
 		the_expr = (ExprTree)malloc(sizeof(ExprNode));
 		add_bytes(sizeof(ExprNode));
-		the_expr->_kind = (AclType)readStream->readUint32LE();
+		the_expr->_kind = (AclType)readStream->readUint16LE();
 		break;
 
 	case DUMP:
@@ -271,7 +271,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 		while (the_expr->_kind == OPER && the_expr->_oper.op_name == OP_LPAREN)
 			the_expr = the_expr->_oper.right;
 
-		writeStream->writeUint32LE(the_expr->_kind);
+		writeStream->writeUint16LE(the_expr->_kind);
 		break;
 
 	case FREE:
@@ -306,10 +306,10 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case NUMERIC:
 		switch (mission) {
 		case LOAD:
-			the_expr->_numeric.acl_int = readStream->readSint32LE();
+			the_expr->_numeric.acl_int = readStream->readSint16LE();
 			break;
 		case DUMP:
-			writeStream->writeSint32LE(the_expr->_numeric.acl_int);
+			writeStream->writeSint16LE(the_expr->_numeric.acl_int);
 			break;
 		default:
 			break;
@@ -321,10 +321,10 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case QUOTE_LIT:
 		switch (mission) {
 		case LOAD:
-			the_expr->_msgTextQuote.index = readStream->readSint32LE();
+			the_expr->_msgTextQuote.index = readStream->readSint16LE();
 			break;
 		case DUMP:
-			writeStream->writeSint32LE(the_expr->_msgTextQuote.index);
+			writeStream->writeSint16LE(the_expr->_msgTextQuote.index);
 			break;
 		default:
 			break;
@@ -334,8 +334,8 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case IDENT:
 		switch (mission) {
 		case LOAD:
-			the_expr->_ident.ident_kind = (ClassifyType)readStream->readUint32LE();
-			the_expr->_ident.ident_int = readStream->readSint32LE();
+			the_expr->_ident.ident_kind = (ClassifyType)readStream->readUint16LE();
+			the_expr->_ident.ident_int = readStream->readSint16LE();
 			break;
 		case DUMP:
 			if (Translating && the_expr->_ident.ident_kind == DefaultClassification) {
@@ -348,8 +348,8 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 				the_expr->_ident.ident_int = temp;
 			}
 
-			writeStream->writeUint32LE(the_expr->_ident.ident_kind);
-			writeStream->writeSint32LE(the_expr->_ident.ident_int);
+			writeStream->writeUint16LE(the_expr->_ident.ident_kind);
+			writeStream->writeSint16LE(the_expr->_ident.ident_int);
 			break;
 		default:
 			break;
@@ -439,7 +439,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 		if (readStream->eos())
 			return;
 
-		sentinel = (StatementKind)readStream->readUint32LE();
+		sentinel = (StatementKind)readStream->readUint16LE();
 		if (sentinel == END_SEQ)
 			return;
 
@@ -450,10 +450,10 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 
 	case DUMP:
 		if (the_stmt == nullptr) {
-			writeStream->writeUint32LE(vEndSeq);
+			writeStream->writeUint16LE(vEndSeq);
 			return;
 		} else {
-			writeStream->writeUint32LE(this_stmt->_kind);
+			writeStream->writeUint16LE(this_stmt->_kind);
 		}
 		break;
 
@@ -491,10 +491,10 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 	case ST_CREATE:
 		switch (mission) {
 		case LOAD:
-			this_stmt->_create.archetype = readStream->readSint32LE();
+			this_stmt->_create.archetype = readStream->readSint16LE();
 			break;
 		case DUMP:
-			writeStream->writeSint32LE(this_stmt->_create.archetype);
+			writeStream->writeSint16LE(this_stmt->_create.archetype);
 			break;
 		default:
 			break;
@@ -519,7 +519,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 		switch (mission) {
 		case LOAD:
 			new_list(this_stmt->_write.print_list);
-			sentinel = (StatementKind)readStream->readUint32LE();
+			sentinel = (StatementKind)readStream->readUint16LE();
 
 			while (sentinel != END_SEQ) {
 				walk_expr(mission, bfile, this_expr);
@@ -529,7 +529,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 				np->data = this_expr;
 				append_to_list(this_stmt->_write.print_list, np);
 
-				sentinel = (StatementKind)readStream->readUint32LE();
+				sentinel = (StatementKind)readStream->readUint16LE();
 			}
 			break;
 
@@ -538,7 +538,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 			np = nullptr;
 			while (iterate_list(this_stmt->_write.print_list, np)) {
 				if (mission == DUMP)
-					writeStream->writeUint32LE(vContSeq);
+					writeStream->writeUint16LE(vContSeq);
 				this_expr = (ExprTree)np->data;
 				walk_expr(mission, bfile, this_expr);
 
@@ -547,7 +547,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 			}
 
 			if (mission == DUMP)
-				writeStream->writeUint32LE(vEndSeq);
+				writeStream->writeUint16LE(vEndSeq);
 			else
 				dispose_list(this_stmt->_write.print_list);
 			break;
@@ -581,11 +581,11 @@ void load_object(Common::ReadStream *f_in, ObjectPtr &the_object) {
 	the_object = (ObjectPtr)malloc(sizeof(ObjectType));
 	add_bytes(sizeof(ObjectType));
 
-	the_object->inherited_from = f_in->readSint32LE();
+	the_object->inherited_from = f_in->readSint16LE();
 	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();
+	sentinel = (StatementKind)f_in->readUint16LE();
 	if (sentinel == CONT_SEQ)
 		load_stmt(f_in, the_object->other);
 	else
@@ -593,14 +593,14 @@ void load_object(Common::ReadStream *f_in, ObjectPtr &the_object) {
 }
 
 void dump_object(Common::WriteStream *f_out, const ObjectPtr the_object) {
-	f_out->writeUint32LE(the_object->inherited_from);
+	f_out->writeUint16LE(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);
+		f_out->writeUint16LE(vEndSeq);
 	} else {
-		f_out->writeUint32LE(vContSeq);
+		f_out->writeUint16LE(vContSeq);
 		dump_stmt(f_out, the_object->other);
 	}
 }
@@ -625,7 +625,7 @@ void load_obj_list(Common::ReadStream *f_in, XArrayType &obj_list) {
 	int i, list_size;
 
 	new_xarray(obj_list);
-	list_size = f_in->readUint32LE();
+	list_size = f_in->readUint16LE();
 
 	for (i = 0; i < list_size; ++i) {
 		load_object(f_in, new_object);
@@ -643,7 +643,7 @@ void dump_obj_list(Common::WriteStream *f_out, XArrayType &obj_list) {
 	void *p;
 	ObjectPtr this_obj;
 
-	f_out->writeUint32LE(obj_list.size());
+	f_out->writeUint16LE(obj_list.size());
 
 	for (i = 0; i < obj_list.size(); ++i) {
 		if (index_xarray(obj_list, i, p)) {
diff --git a/engines/glk/archetype/saveload.h b/engines/glk/archetype/saveload.h
index fe6251b..0f98eb2 100644
--- a/engines/glk/archetype/saveload.h
+++ b/engines/glk/archetype/saveload.h
@@ -53,8 +53,8 @@ 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 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);


Commit: d4cba56b418d04d6bca2ded3cabd3376aab5d3f3
    https://github.com/scummvm/scummvm/commit/d4cba56b418d04d6bca2ded3cabd3376aab5d3f3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Further loading fixes

Changed paths:
    engines/glk/archetype/saveload.cpp


diff --git a/engines/glk/archetype/saveload.cpp b/engines/glk/archetype/saveload.cpp
index 5c018c8..bd236a8 100644
--- a/engines/glk/archetype/saveload.cpp
+++ b/engines/glk/archetype/saveload.cpp
@@ -85,7 +85,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 	switch (mission) {
 	case LOAD:
 		assert(readStream);
-		sentinel = (StatementKind)readStream->readUint16LE();
+		sentinel = (StatementKind)readStream->readByte();
 		new_list(elements);
 		yet_more = sentinel == CONT_SEQ;
 		break;
@@ -112,7 +112,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 
 		case DUMP:
 			assert(writeStream);
-			writeStream->writeUint16LE(vContSeq);
+			writeStream->writeByte(vContSeq);
 			writeStream->writeSint16LE(np->key);
 			break;
 
@@ -189,7 +189,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 		case LOAD:
 			assert(readStream);
 			append_to_list(elements, np);
-			sentinel = (StatementKind)readStream->readUint16LE();
+			sentinel = (StatementKind)readStream->readByte();
 			yet_more = sentinel == CONT_SEQ;
 			break;
 
@@ -206,7 +206,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 	// Postlude
 	switch (mission) {
 	case DUMP:
-		writeStream->writeUint16LE(vEndSeq);
+		writeStream->writeByte(vEndSeq);
 		break;
 	case FREE:
 		dispose_list(elements);
@@ -260,7 +260,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 		assert(readStream);
 		the_expr = (ExprTree)malloc(sizeof(ExprNode));
 		add_bytes(sizeof(ExprNode));
-		the_expr->_kind = (AclType)readStream->readUint16LE();
+		the_expr->_kind = (AclType)readStream->readByte();
 		break;
 
 	case DUMP:
@@ -271,7 +271,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 		while (the_expr->_kind == OPER && the_expr->_oper.op_name == OP_LPAREN)
 			the_expr = the_expr->_oper.right;
 
-		writeStream->writeUint16LE(the_expr->_kind);
+		writeStream->writeByte(the_expr->_kind);
 		break;
 
 	case FREE:
@@ -306,10 +306,10 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case NUMERIC:
 		switch (mission) {
 		case LOAD:
-			the_expr->_numeric.acl_int = readStream->readSint16LE();
+			the_expr->_numeric.acl_int = readStream->readUint32LE();
 			break;
 		case DUMP:
-			writeStream->writeSint16LE(the_expr->_numeric.acl_int);
+			writeStream->writeSint32LE(the_expr->_numeric.acl_int);
 			break;
 		default:
 			break;
@@ -334,7 +334,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case IDENT:
 		switch (mission) {
 		case LOAD:
-			the_expr->_ident.ident_kind = (ClassifyType)readStream->readUint16LE();
+			the_expr->_ident.ident_kind = (ClassifyType)readStream->readByte();
 			the_expr->_ident.ident_int = readStream->readSint16LE();
 			break;
 		case DUMP:
@@ -348,7 +348,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 				the_expr->_ident.ident_int = temp;
 			}
 
-			writeStream->writeUint16LE(the_expr->_ident.ident_kind);
+			writeStream->writeByte(the_expr->_ident.ident_kind);
 			writeStream->writeSint16LE(the_expr->_ident.ident_int);
 			break;
 		default:
@@ -426,7 +426,6 @@ void dispose_stmt(StatementPtr &the_stmt) {
 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);
@@ -439,7 +438,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 		if (readStream->eos())
 			return;
 
-		sentinel = (StatementKind)readStream->readUint16LE();
+		sentinel = (StatementKind)readStream->readByte();
 		if (sentinel == END_SEQ)
 			return;
 
@@ -450,10 +449,10 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 
 	case DUMP:
 		if (the_stmt == nullptr) {
-			writeStream->writeUint16LE(vEndSeq);
+			writeStream->writeByte(vEndSeq);
 			return;
 		} else {
-			writeStream->writeUint16LE(this_stmt->_kind);
+			writeStream->writeByte(the_stmt->_kind);
 		}
 		break;
 
@@ -467,50 +466,49 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 	}
 
 	// Main walk
-	assert(this_stmt);
-	switch (this_stmt->_kind) {
+	switch (the_stmt->_kind) {
 	case COMPOUND:
-		walk_item_list(mission, bfile, this_stmt->_compound.statements, STMT_LIST);
+		walk_item_list(mission, bfile, the_stmt->_compound.statements, STMT_LIST);
 		break;
 
 	case ST_EXPR:
-		walk_expr(mission, bfile, this_stmt->_expr.expression);
+		walk_expr(mission, bfile, the_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);
+		walk_expr(mission, bfile, the_stmt->_if.condition);
+		walk_stmt(mission, bfile, the_stmt->_if.then_branch);
+		walk_stmt(mission, bfile, the_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);
+		walk_expr(mission, bfile, the_stmt->_case.test_expr);
+		walk_item_list(mission, bfile, the_stmt->_case.cases, CASE_LIST);
 		break;
 
 	case ST_CREATE:
 		switch (mission) {
 		case LOAD:
-			this_stmt->_create.archetype = readStream->readSint16LE();
+			the_stmt->_create.archetype = readStream->readSint16LE();
 			break;
 		case DUMP:
-			writeStream->writeSint16LE(this_stmt->_create.archetype);
+			writeStream->writeSint16LE(the_stmt->_create.archetype);
 			break;
 		default:
 			break;
 		}
 
-		walk_expr(mission, bfile, this_stmt->_create.new_name);
+		walk_expr(mission, bfile, the_stmt->_create.new_name);
 		break;
 
 	case ST_DESTROY:
-		walk_expr(mission, bfile, this_stmt->_destroy.victim);
+		walk_expr(mission, bfile, the_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);
+		walk_expr(mission, bfile, the_stmt->_loop.selection);
+		walk_stmt(mission, bfile, the_stmt->_loop.action);
 		break;
 
 	case ST_WRITE:
@@ -518,8 +516,8 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 	case ST_STOP:
 		switch (mission) {
 		case LOAD:
-			new_list(this_stmt->_write.print_list);
-			sentinel = (StatementKind)readStream->readUint16LE();
+			new_list(the_stmt->_write.print_list);
+			sentinel = (StatementKind)readStream->readByte();
 
 			while (sentinel != END_SEQ) {
 				walk_expr(mission, bfile, this_expr);
@@ -527,18 +525,18 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 				add_bytes(sizeof(NodeType));
 
 				np->data = this_expr;
-				append_to_list(this_stmt->_write.print_list, np);
+				append_to_list(the_stmt->_write.print_list, np);
 
-				sentinel = (StatementKind)readStream->readUint16LE();
+				sentinel = (StatementKind)readStream->readByte();
 			}
 			break;
 
 		case DUMP:
 		case FREE:
 			np = nullptr;
-			while (iterate_list(this_stmt->_write.print_list, np)) {
+			while (iterate_list(the_stmt->_write.print_list, np)) {
 				if (mission == DUMP)
-					writeStream->writeUint16LE(vContSeq);
+					writeStream->writeByte(vContSeq);
 				this_expr = (ExprTree)np->data;
 				walk_expr(mission, bfile, this_expr);
 
@@ -547,9 +545,9 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 			}
 
 			if (mission == DUMP)
-				writeStream->writeUint16LE(vEndSeq);
+				writeStream->writeByte(vEndSeq);
 			else
-				dispose_list(this_stmt->_write.print_list);
+				dispose_list(the_stmt->_write.print_list);
 			break;
 
 		default:
@@ -585,7 +583,7 @@ void load_object(Common::ReadStream *f_in, ObjectPtr &the_object) {
 	load_item_list(f_in, the_object->attributes, EXPR_LIST);
 	load_item_list(f_in, the_object->methods, STMT_LIST);
 
-	sentinel = (StatementKind)f_in->readUint16LE();
+	sentinel = (StatementKind)f_in->readByte();
 	if (sentinel == CONT_SEQ)
 		load_stmt(f_in, the_object->other);
 	else


Commit: fdb5ead5ca91a92c11192e61d23beee90e4caacb
    https://github.com/scummvm/scummvm/commit/fdb5ead5ca91a92c11192e61d23beee90e4caacb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Fixes for string decryption

Changed paths:
    engines/glk/archetype/crypt.cpp
    engines/glk/archetype/crypt.h
    engines/glk/archetype/misc.cpp


diff --git a/engines/glk/archetype/crypt.cpp b/engines/glk/archetype/crypt.cpp
index d99dab0..1b59163 100644
--- a/engines/glk/archetype/crypt.cpp
+++ b/engines/glk/archetype/crypt.cpp
@@ -28,10 +28,40 @@ namespace Archetype {
 
 byte CryptMask;
 EncryptionType Encryption;
+static uint RandSeed;
+const int RANDOM_KEY = 33797;
 
 void crypt_init() {
 	Encryption = NONE;
 	CryptMask = 0x55;
+	RandSeed = 0;
+}
+
+static void setDecryptionSeed(uint new_seed) {
+	RandSeed = new_seed;
+}
+
+static void cycleRandomSeed() {
+	uint16 c = RandSeed & 0xffff;
+	uint val = (uint)c * RANDOM_KEY;
+	c <<= 3;
+	c = (c & 0xff) | ((((c >> 8) + c) & 0xff) << 8);
+	val += (uint)c << 16;
+
+	uint16 b = RandSeed >> 16;
+	val += (uint)b << 16;
+	b <<= 2;
+	val += (uint)b << 16;
+	val += (uint)(b & 0xff) << 24;
+	b <<= 5;
+	val += (uint)(b & 0xff) << 24;
+
+	RandSeed = val + 1;
+}
+
+static uint getDeterministicRandomNumber(uint limit) {
+	cycleRandomSeed();
+	return (limit == 0) ? 0 : (RandSeed >> 16) % limit;
 }
 
 void cryptinit(EncryptionType crypt_kind, uint seed) {
@@ -39,37 +69,38 @@ void cryptinit(EncryptionType crypt_kind, uint seed) {
 	Encryption = crypt_kind;
 
 	if (Encryption == COMPLEX)
-		g_vm->setRandomNumberSeed(seed);
+		setDecryptionSeed(seed);
 }
 
-void cryptstr(Common::String &s) {
+void cryptstr(char *buffer, size_t length) {
 	byte nextMask;
+	char *p = buffer;
 
 	switch (Encryption) {
 	case SIMPLE:
-		for (uint i = 0; i < s.size(); ++i)
-			s.setChar(s[i] ^ CryptMask, i);
+		for (size_t i = 0; i < length; ++i, ++p)
+			*p ^= CryptMask;
 		break;
 
 	case PURPLE:
-		for (uint i = 0; i < s.size(); ++i) {
-			s.setChar(s[i] ^ CryptMask, i);
-			CryptMask += s[i] & 7;
+		for (size_t i = 0; i < length; ++i, ++p) {
+			*p ^= CryptMask;
+			CryptMask += *p & 7;
 		}
 		break;
 
 	case UNPURPLE:
-		for (uint i = 0; i < s.size(); ++i) {
-			nextMask = CryptMask + (s[i] & 7);
-			s.setChar(s[i] ^ CryptMask, i);
+		for (size_t i = 0; i < length; ++i, ++p) {
+			nextMask = CryptMask + (*p & 7);
+			*p ^= CryptMask;
 			CryptMask = nextMask;
 		}
 		break;
 
 	case COMPLEX:
-		for (uint i = 0; i < s.size(); ++i) {
-			s.setChar(s[i] ^ CryptMask, i);
-			CryptMask = g_vm->getRandomNumber(0x100);
+		for (size_t i = 0; i < length; ++i, ++p) {
+			*p ^= CryptMask;
+			CryptMask = (byte)getDeterministicRandomNumber(0x100);
 		}
 		break;
 
diff --git a/engines/glk/archetype/crypt.h b/engines/glk/archetype/crypt.h
index d978210..d92fe39 100644
--- a/engines/glk/archetype/crypt.h
+++ b/engines/glk/archetype/crypt.h
@@ -23,8 +23,7 @@
 #ifndef ARCHETYPE_CRYPT
 #define ARCHETYPE_CRYPT
 
-#include "common/scummsys.h"
-#include "common/str.h"
+#include "glk/archetype/string.h"
 
 namespace Glk {
 namespace Archetype {
@@ -56,7 +55,7 @@ extern void cryptinit(EncryptionType crypt_kind, uint seed);
  * 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);
+extern void cryptstr(char *buffer, size_t length);
 
 } // End of namespace Archetype
 } // End of namespace Glk
diff --git a/engines/glk/archetype/misc.cpp b/engines/glk/archetype/misc.cpp
index e5a858b..c10bba1 100644
--- a/engines/glk/archetype/misc.cpp
+++ b/engines/glk/archetype/misc.cpp
@@ -155,11 +155,13 @@ String formatFilename(const String &name, const String &ext, bool replace) {
 void load_string(Common::ReadStream *fIn, String &the_string) {
 	char buffer[257];
 	size_t strSize = fIn->readByte();
+	(void)fIn->readByte();		// Redundant second copy of the length
+
 	fIn->read(buffer, strSize);
 	buffer[strSize] = '\0';
 
+	cryptstr(buffer, strSize);
 	the_string = String(buffer);
-	cryptstr(the_string);
 }
 
 void dump_string(Common::WriteStream *fOut, const String &the_string) {
@@ -170,9 +172,12 @@ void dump_string(Common::WriteStream *fOut, const String &the_string) {
 		fOut->write(the_string.c_str(), the_string.size());
 
 	} else {
-		String tmp = the_string;
-		cryptstr(tmp);
-		fOut->write(tmp.c_str(), tmp.size());
+		char buffer[257];
+		strncpy(buffer, the_string.c_str(), 256);
+		buffer[256] = '\0';
+
+		cryptstr(buffer, the_string.size());
+		fOut->write(buffer, the_string.size());
 	}
 }
 


Commit: e1911f9aff91a2264b6d05c3fda8505bed07739a
    https://github.com/scummvm/scummvm/commit/e1911f9aff91a2264b6d05c3fda8505bed07739a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: Fix structures using unions

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/expression.h
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/interpreter.h
    engines/glk/archetype/keywords.cpp
    engines/glk/archetype/saveload.cpp
    engines/glk/archetype/semantic.cpp
    engines/glk/archetype/statement.h
    engines/glk/archetype/sys_object.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 37b40ab..5ab3744 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -101,6 +101,7 @@ void Archetype::interpret() {
 
 	ContextType context;
 	ResultType result;
+
 	undefine(result);
 
 	if (!send_message(OP_SEND, find_message("START"), MainObject, result, context))
@@ -140,8 +141,8 @@ void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextTyp
 
 	if (desired == NAME) {
 		result._kind = IDENT;
-		result._ident.ident_kind = ATTRIBUTE_ID;
-		result._ident.ident_int = the_attr;
+		result._data._ident.ident_kind = ATTRIBUTE_ID;
+		result._data._ident.ident_int = the_attr;
 		return;
 	}
 
@@ -202,19 +203,19 @@ void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextTyp
 	case LVALUE:
 		if (first_pass) {
 			result._kind = ATTR_PTR;
-			result._attr.acl_attr = np;
+			result._data._attr.acl_attr = np;
 		} else {
 			// inherited - must create new node }
 			result._kind = ATTR_PTR;
-			result._attr.acl_attr = (NodePtr)malloc(sizeof(NodeType));
+			result._data._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);
+			result._data._attr.acl_attr->data = e;
+			result._data._attr.acl_attr->key = the_attr;
+			insert_item(((ObjectPtr)original)->attributes, result._data._attr.acl_attr);
 		}
 		break;
 
@@ -228,7 +229,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 	bool done, find_other;
 	ObjectPtr op, original;
 	ResultType r;
-	NodePtr np;
+	NodePtr np;  
 	StatementPtr st;
 	void *p;
 	ContextType c;
@@ -240,8 +241,8 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 
 	if ((Debug & DEBUG_MSGS) > 0) {
 		r._kind = IDENT;
-		r._ident.ident_kind = OBJECT_ID;
-		r._ident.ident_int = context.self;
+		r._data._ident.ident_kind = OBJECT_ID;
+		r._data._ident.ident_int = context.self;
 		wrapout(" : ", false);
 		display_result(r);
 
@@ -256,10 +257,10 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 		}
 
 		if (transport == OP_SEND_TO_TYPE)
-			r._ident.ident_kind = TYPE_ID;
+			r._data._ident.ident_kind = TYPE_ID;
 
 		wrapout(" to ", false);
-		r._ident.ident_int = recipient;
+		r._data._ident.ident_int = recipient;
 		display_result(r);
 		wrapout("", true);
 	}
@@ -319,7 +320,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 	// 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;
+	result._data._reserved.keyword = RW_ABSENT;
 
 	return false;
 }
@@ -342,19 +343,19 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 		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);
+	if (the_expr->_kind == IDENT && the_expr->_data._ident.ident_kind == ATTRIBUTE_ID)
+		lookup(context.self, the_expr->_data._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) {
+		switch (the_expr->_data._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
+			if (the_expr->_data._reserved.keyword == RW_READ)
+				result._data._str.acl_str = ReadLine(true);			// read full line
 			else
-				result._str.acl_str = ReadLine(false);			// read single key
+				result._data._str.acl_str = ReadLine(false);			// read single key
 
 			Rows = 0;
 			cursor_reset();				// user will have had to hit <RETURN>
@@ -362,24 +363,24 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 
 		case RW_MESSAGE:
 			result._kind = MESSAGE;
-			result._msgTextQuote.index = context.message;
+			result._data._msgTextQuote.index = context.message;
 			break;
 
 		case RW_EACH:
 		case RW_SELF:
 		case RW_SENDER:
 			result._kind = IDENT;
-			result._ident.ident_kind = OBJECT_ID;
+			result._data._ident.ident_kind = OBJECT_ID;
 
-			switch (the_expr->_reserved.keyword) {
+			switch (the_expr->_data._reserved.keyword) {
 			case RW_EACH:
-				result._ident.ident_int = context.each;
+				result._data._ident.ident_int = context.each;
 				break;
 			case RW_SELF:
-				result._ident.ident_int = context.self;
+				result._data._ident.ident_int = context.self;
 				break;
 			case RW_SENDER:
-				result._ident.ident_int = context.sender;
+				result._data._ident.ident_int = context.sender;
 				break;
 			default:
 				break;
@@ -391,37 +392,37 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 		}
 	} else if (the_expr->_kind == OPER) {
 		// It's an operator, need to evaulate it
-		switch (the_expr->_oper.op_name) {
+		switch (the_expr->_data._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);
+			eval_expr(the_expr->_data._oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._oper.right, r2, context, RVALUE);
 
-			if (r2._kind == IDENT && (r2._ident.ident_kind == OBJECT_ID || r2._ident.ident_kind == TYPE_ID)) {
+			if (r2._kind == IDENT && (r2._data._ident.ident_kind == OBJECT_ID || r2._data._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 (r2._data._ident.ident_kind == OBJECT_ID && r2._data._ident.ident_int == 0) {
 					if (convert_to(STR_PTR, r1))
-						send_to_system(the_expr->_oper.op_name, *r1._str.acl_str, result, context);
+						send_to_system(the_expr->_data._oper.op_name, *r1._data._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,
+					if (r2._data._ident.ident_kind == TYPE_ID)
+						b = send_message(OP_SEND_TO_TYPE, r1._data._msgTextQuote.index, r2._data._ident.ident_int,
 							result, context);
 					else
-						b = send_message(the_expr->_oper.op_name, r1._msgTextQuote.index,
-							r2._ident.ident_int, result, context);
+						b = send_message(the_expr->_data._oper.op_name, r1._data._msgTextQuote.index,
+							r2._data._ident.ident_int, result, context);
 				}
 			}
 			break;
 
 		case OP_DOT:
-			eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._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);
+			if (r1._kind == IDENT && r1._data._ident.ident_kind == OBJECT_ID) {
+				eval_expr(the_expr->_data._oper.right, r2, context, NAME);
+				if (r2._kind == IDENT && r2._data._ident.ident_kind == ATTRIBUTE_ID)
+					lookup(r1._data._ident.ident_int, r2._data._ident.ident_int, result, context, desired);
 			}
 			break;
 
@@ -429,15 +430,15 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			if (desired == NAME)
 				return;
 
-			eval_expr(the_expr->_oper.right, result, context, RVALUE);
-			eval_expr(the_expr->_oper.left, r1, context, LVALUE);
+			eval_expr(the_expr->_data._oper.right, result, context, RVALUE);
+			eval_expr(the_expr->_data._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;
+				result._data._attr.acl_attr = r1._data._attr.acl_attr;
 			}
 			break;
 
@@ -453,29 +454,29 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			e = (ExprTree)malloc(sizeof(ExprNode));
 			*e = *the_expr;
 
-			switch (the_expr->_oper.op_name) {
+			switch (the_expr->_data._oper.op_name) {
 			case OP_C_MULTIPLY:
-				e->_oper.op_name = OP_MULTIPLY;
+				e->_data._oper.op_name = OP_MULTIPLY;
 				break;
 			case OP_C_DIVIDE:
-				e->_oper.op_name = OP_DIVIDE;
+				e->_data._oper.op_name = OP_DIVIDE;
 				break;
 			case OP_C_PLUS:
-				e->_oper.op_name = OP_PLUS;
+				e->_data._oper.op_name = OP_PLUS;
 				break;
 			case OP_C_MINUS:
-				e->_oper.op_name = OP_MINUS;
+				e->_data._oper.op_name = OP_MINUS;
 				break;
 			case OP_C_CONCAT:
-				e->_oper.op_name = OP_CONCAT;
+				e->_data._oper.op_name = OP_CONCAT;
 				break;
 			default:
 				break;
 			}
 
 			eval_expr(e, r1, context, RVALUE);
-			e->_oper.op_name = OP_ASSIGN;
-			e->_oper.right = &r1;
+			e->_data._oper.op_name = OP_ASSIGN;
+			e->_data._oper.right = &r1;
 
 			eval_expr(e, result, context, desired);
 			free(e);
@@ -483,24 +484,24 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 
 		case OP_CHS:
 		case OP_NUMERIC:
-			eval_expr(the_expr->_oper.right, result, context, RVALUE);
+			eval_expr(the_expr->_data._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;
+			else if (the_expr->_data._oper.op_name == OP_CHS)
+				result._data._numeric.acl_int = -result._data._numeric.acl_int;
 			break;
 
 		case OP_STRING:
-			eval_expr(the_expr->_oper.right, result, context, RVALUE);
+			eval_expr(the_expr->_data._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);
+			eval_expr(the_expr->_data._oper.right, r1, context, RVALUE);
 			if (convert_to(STR_PTR, r1)) {
 				result._kind = NUMERIC;
-				result._numeric.acl_int = r1._str.acl_str->size();
+				result._data._numeric.acl_int = r1._data._str.acl_str->size();
 			}
 			break;
 
@@ -509,45 +510,45 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			// 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);
+			eval_expr(the_expr->_data._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;
+				result._data._numeric.acl_int = g_vm->getRandomNumber(result._data._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;
+				String &s = *result._data._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;
+			if (eval_condition(the_expr->_data._oper.right, context))
+				result._data._reserved.keyword = RW_FALSE;
 			else
-				result._reserved.keyword = RW_TRUE;
+				result._data._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);
+			eval_expr(the_expr->_data._oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._oper.right, r2, context, RVALUE);
 			if (convert_to(NUMERIC, r1) && convert_to(NUMERIC, r2)) {
 				result._kind = NUMERIC;
-				switch (the_expr->_oper.op_name) {
+				switch (the_expr->_data._oper.op_name) {
 				case OP_PLUS:
-					result._numeric.acl_int = r1._numeric.acl_int + r2._numeric.acl_int;
+					result._data._numeric.acl_int = r1._data._numeric.acl_int + r2._data._numeric.acl_int;
 					break;
 				case OP_MINUS:
-					result._numeric.acl_int = r1._numeric.acl_int - r2._numeric.acl_int;
+					result._data._numeric.acl_int = r1._data._numeric.acl_int - r2._data._numeric.acl_int;
 					break;
 				case OP_MULTIPLY:
-					result._numeric.acl_int = r1._numeric.acl_int * r2._numeric.acl_int;
+					result._data._numeric.acl_int = r1._data._numeric.acl_int * r2._data._numeric.acl_int;
 					break;
 				case OP_DIVIDE:
-					result._numeric.acl_int = r1._numeric.acl_int / r2._numeric.acl_int;
+					result._data._numeric.acl_int = r1._data._numeric.acl_int / r2._data._numeric.acl_int;
 					break;
 				default:
 					break;
@@ -557,59 +558,59 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 
 		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;
+			if (eval_condition(the_expr->_data._oper.left, context) && eval_condition(the_expr->_data._oper.right, context))
+				result._data._reserved.keyword = RW_TRUE;
 			else
-				result._reserved.keyword = RW_FALSE;
+				result._data._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;
+			if (eval_condition(the_expr->_data._oper.left, context) || eval_condition(the_expr->_data._oper.right, context))
+				result._data._reserved.keyword = RW_TRUE;
 			else
-				result._reserved.keyword = RW_FALSE;
+				result._data._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);
+			eval_expr(the_expr->_data._oper.right, r2, context, RVALUE);
+			eval_expr(the_expr->_data._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;
+				result._data._numeric.acl_int = 1;
+				for (i = 1; i <= r2._data._numeric.acl_int; ++i)
+					result._data._numeric.acl_int *= r1._data._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);
+			eval_expr(the_expr->_data._oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._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);
+				result._data._str.acl_str = MakeNewDynStr(*r1._data._str.acl_str + *r2._data._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);
+			eval_expr(the_expr->_data._oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._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));
+				if (the_expr->_data._oper.op_name == OP_LEFTFROM)
+					result._data._str.acl_str = MakeNewDynStr(r1._data._str.acl_str->left(r2._data._numeric.acl_int));
 				else
-					result._str.acl_str = MakeNewDynStr(r1._str.acl_str->right(r2._numeric.acl_int));
+					result._data._str.acl_str = MakeNewDynStr(r1._data._str.acl_str->right(r2._data._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);
+			eval_expr(the_expr->_data._oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._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)
+				result._data._numeric.acl_int = r2._data._str.acl_str->indexOf(*r1._data._str.acl_str);
+				if (result._data._numeric.acl_int == -1)
 					cleanup(result);
 			}
 			break;
@@ -620,18 +621,18 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 		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);
+			eval_expr(the_expr->_data._oper.left, r1, context, RVALUE);
+			eval_expr(the_expr->_data._oper.right, r2, context, RVALUE);
 
 			result._kind = RESERVED;
-			if (result_compare(the_expr->_oper.op_name, r1, r2))
-				result._reserved.keyword = RW_TRUE;
+			if (result_compare(the_expr->_data._oper.op_name, r1, r2))
+				result._data._reserved.keyword = RW_TRUE;
 			else
-				result._reserved.keyword = RW_FALSE;
+				result._data._reserved.keyword = RW_FALSE;
 			break;
 
 		default:
-			g_vm->writeln("Internal error: \"%s\" not yet supported", Operators[the_expr->_oper.op_name]);
+			g_vm->writeln("Internal error: \"%s\" not yet supported", Operators[the_expr->_data._oper.op_name]);
 			break;
 		}
 
@@ -666,8 +667,8 @@ bool Archetype::eval_condition(ExprTree the_expr, ContextType &context) {
 	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);
+	failure = (result._kind == RESERVED) && (result._data._reserved.keyword = RW_UNDEFINED
+		|| result._data._reserved.keyword == RW_FALSE || result._data._reserved.keyword == RW_ABSENT);
 
 	cleanup(result);
 	return !failure;
@@ -697,32 +698,32 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 	case COMPOUND:
 		np = nullptr;
 		b = false;
-		while (!b && iterate_list(the_stmt->_compound.statements, np)) {
+		while (!b && iterate_list(the_stmt->_data._compound.statements, np)) {
 			cleanup(result);
 			exec_stmt((StatementPtr)np->data, result, context);
 
-			b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+			b = (result._kind == RESERVED) && (result._data._reserved.keyword == RW_BREAK);
 		}
 		break;
 
 	case ST_EXPR:
 		if (verbose)
-			display_expr(the_stmt->_expr.expression);
+			display_expr(the_stmt->_data._expr.expression);
 
-		switch (the_stmt->_expr.expression->_kind) {
+		switch (the_stmt->_data._expr.expression->_kind) {
 		case QUOTE_LIT:
-			if (index_xarray(Literals, the_stmt->_expr.expression->_msgTextQuote.index, p)) {
+			if (index_xarray(Literals, the_stmt->_data._expr.expression->_data._msgTextQuote.index, p)) {
 				result._kind = TEXT_LIT;
-				result._msgTextQuote.index = the_stmt->_expr.expression->_msgTextQuote.index;
+				result._data._msgTextQuote.index = the_stmt->_data._expr.expression->_data._msgTextQuote.index;
 				wrapout(*((StringPtr)p), true);
 			}
 			break;
 
 		case MESSAGE:
-			b = send_message(OP_PASS, the_stmt->_expr.expression->_msgTextQuote.index,
+			b = send_message(OP_PASS, the_stmt->_data._expr.expression->_data._msgTextQuote.index,
 				context.self, result, context);
 		default:
-			eval_expr(the_stmt->_expr.expression, result, context, RVALUE);
+			eval_expr(the_stmt->_data._expr.expression, result, context, RVALUE);
 			break;
 		}
 		break;
@@ -747,9 +748,9 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 			wrapout(" ", false);
 			np = nullptr;
-			while (iterate_list(the_stmt->_write.print_list, np)) {
+			while (iterate_list(the_stmt->_data._write.print_list, np)) {
 				display_expr((ExprTree)np->data);
-				if (np->next != the_stmt->_write.print_list)
+				if (np->next != the_stmt->_data._write.print_list)
 					wrapout(", ", false);
 			}
 
@@ -757,7 +758,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		}
 
 		np = nullptr;
-		while (iterate_list(the_stmt->_write.print_list, np)) {
+		while (iterate_list(the_stmt->_data._write.print_list, np)) {
 			cleanup(result);
 			eval_expr((ExprTree)np->data, result, context, RVALUE);
 			write_result(result);
@@ -776,38 +777,38 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 	case ST_IF:
 		if (verbose) {
 			wrapout("if: Testing ", false);
-			display_expr(the_stmt->_if.condition);
+			display_expr(the_stmt->_data._if.condition);
 		}
-		if (eval_condition(the_stmt->_if.condition, context)) {
+		if (eval_condition(the_stmt->_data._if.condition, context)) {
 			if (verbose)
 				wrapout(" Evaluated true; executing then branch", true);
-			exec_stmt(the_stmt->_if.then_branch, result, context);
+			exec_stmt(the_stmt->_data._if.then_branch, result, context);
 
 		}
-		else if (the_stmt->_if.else_branch != nullptr) {
+		else if (the_stmt->_data._if.else_branch != nullptr) {
 			if (verbose)
 				wrapout(" Evaluated false; executing else branch", true);
-			exec_stmt(the_stmt->_if.else_branch, result, context);
+			exec_stmt(the_stmt->_data._if.else_branch, result, context);
 		}
 		break;
 
 	case ST_CASE:
 		if (verbose) {
 			wrapout("case ", false);
-			display_expr(the_stmt->_case.test_expr);
+			display_expr(the_stmt->_data._case.test_expr);
 			wrapout(" of", false);
 			wrapout("", true);
 		}
 
-		eval_expr(the_stmt->_case.test_expr, r1, context, RVALUE);
+		eval_expr(the_stmt->_data._case.test_expr, r1, context, RVALUE);
 		np = nullptr;
 
-		while (iterate_list(the_stmt->_case.cases, np)) {
+		while (iterate_list(the_stmt->_data._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)
+			if ((r2._kind == RESERVED && r2._data._reserved.keyword == RW_DEFAULT)
 				|| result_compare(OP_EQ, r1, r2)) {
 				exec_stmt(this_case->action, result, context);
 				cleanup(r1);
@@ -824,7 +825,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 	case ST_BREAK:
 		result._kind = RESERVED;
-		result._reserved.keyword = RW_BREAK;
+		result._data._reserved.keyword = RW_BREAK;
 		break;
 
 	case ST_FOR:
@@ -833,9 +834,9 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		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);
+			if (eval_condition(the_stmt->_data._loop.selection, c)) {
+				exec_stmt(the_stmt->_data._loop.action, result, c);
+				b = (result._kind == RESERVED) && (result._data._reserved.keyword == RW_BREAK);
 				cleanup(result);
 			}
 
@@ -845,20 +846,20 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 	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);
+		while (!b && eval_condition(the_stmt->_data._loop.selection, context)) {
+			exec_stmt(the_stmt->_data._loop.action, result, context);
+			b = (result._kind == RESERVED) && (result._data._reserved.keyword == RW_BREAK);
 			cleanup(result);
 		}
 		break;
 
 	case ST_CREATE:
-		eval_expr(the_stmt->_create.new_name, r1, context, LVALUE);
+		eval_expr(the_stmt->_data._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;
+		result._data._ident.ident_kind = OBJECT_ID;
+		result._data._ident.ident_int = 0;
 
 		if (!assignment(r1, result)) {
 			cleanup(result);
@@ -866,7 +867,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		else {
 			// do it for real
 			the_object = (ObjectPtr)malloc(sizeof(ObjectType));
-			the_object->inherited_from = the_stmt->_create.archetype;
+			the_object->inherited_from = the_stmt->_data._create.archetype;
 			new_list(the_object->attributes);
 			new_list(the_object->methods);
 			the_object->other = nullptr;
@@ -885,8 +886,8 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 			// 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);
+			((ExprPtr)r1._data._attr.acl_attr->data)->_data._ident.ident_int = i;
+			copy_result(result, *(ExprPtr)r1._data._attr.acl_attr->data);
 
 			cleanup(r1);
 		}
@@ -895,14 +896,14 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		// 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)) {
+		eval_expr(the_stmt->_data._destroy.victim, result, context, RVALUE);
+		if (result._kind == IDENT && result._data._ident.ident_kind == OBJECT_ID
+				&& index_xarray(Object_List, result._data._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))
+			b = access_xarray(Object_List, result._data._ident.ident_int, p, POKE_ACCESS);
+			if (result._data._ident.ident_int == ((int)Object_List.size() - 1))
 				shrink_xarray(Object_List);
 		} else {
 			wraperr("Can only destroy previously created objects");
diff --git a/engines/glk/archetype/expression.h b/engines/glk/archetype/expression.h
index 4e927ec..6ccc0b0 100644
--- a/engines/glk/archetype/expression.h
+++ b/engines/glk/archetype/expression.h
@@ -35,7 +35,7 @@ const int OP_SEND_TO_TYPE = NUM_OPERS + 2;	// for use with interpreter
 
 struct OperNode {
 	int8 op_name;
-	union ExprNode *left, *right;
+	struct ExprNode *left, *right;
 };
 
 struct MessageTextQuoteNode {
@@ -63,8 +63,7 @@ struct IdentNode {
 	int ident_int;
 };
 
-union ExprNode {
-	AclType _kind;
+union ExprNodeData {
 	OperNode _oper;
 	NumericNode _numeric;
 	MessageTextQuoteNode _msgTextQuote;
@@ -74,6 +73,11 @@ union ExprNode {
 	IdentNode _ident;
 };
 
+struct ExprNode {
+	AclType _kind;
+	ExprNodeData _data;
+};
+
 typedef ExprNode *ExprPtr;
 typedef ExprPtr ExprTree;
 
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index de4273f..21f732b 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -77,42 +77,42 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 	switch (the_scalar._kind) {
     case NUMERIC:
         dir_from = 'N';
-		the_number = the_scalar._numeric.acl_int;
+		the_number = the_scalar._data._numeric.acl_int;
 		break;
 
 	case MESSAGE:
 		dir_from = 'S';
-		if (index_xarray(g_vm->Vocabulary, the_scalar._msgTextQuote.index, p))
+		if (index_xarray(g_vm->Vocabulary, the_scalar._data._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))
+		if (index_xarray(g_vm->Literals, the_scalar._data._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;
+		s1 = *the_scalar._data._str.acl_str;
 		break;
 
 	case IDENT:
 		//with the_scalar do begin
         dir_from = 'S';
         
-		switch (the_scalar._ident.ident_kind) {
+		switch (the_scalar._data._ident.ident_kind) {
 		case ENUMERATE_ID:
 			dir_from = 'N';
-			the_number = the_scalar._ident.ident_int;
+			the_number = the_scalar._data._ident.ident_int;
 			break;
 
 		case OBJECT_ID:
-			if (the_scalar._ident.ident_int == 0)
+			if (the_scalar._data._ident.ident_int == 0)
 				s1 = "system";
-			else if (index_xarray(g_vm->Object_ID_List, the_scalar._ident.ident_int, p)) {
+			else if (index_xarray(g_vm->Object_ID_List, the_scalar._data._ident.ident_int, p)) {
 				if (p == nullptr)
 					s1 = "null";
 				else
@@ -123,16 +123,16 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 			break;
 
 		case TYPE_ID:
-			if (the_scalar._ident.ident_int == 0)
+			if (the_scalar._data._ident.ident_int == 0)
 				s1 = "null";
-			else if (index_xarray(g_vm->Type_ID_List, the_scalar._ident.ident_int, p))
+			else if (index_xarray(g_vm->Type_ID_List, the_scalar._data._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))
+			if (index_xarray(g_vm->Attribute_ID_List, the_scalar._data._ident.ident_int, p))
 				s1 = *((StringPtr)p);
 			else
 				return false;
@@ -144,9 +144,9 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 		break;
 
 	case RESERVED:
-		if (the_scalar._reserved.keyword == RW_TRUE || the_scalar._reserved.keyword == RW_FALSE) {
+		if (the_scalar._data._reserved.keyword == RW_TRUE || the_scalar._data._reserved.keyword == RW_FALSE) {
 			dir_from = 'B';
-			boolval = (the_scalar._reserved.keyword == RW_TRUE);
+			boolval = (the_scalar._data._reserved.keyword == RW_TRUE);
 		} else {
 			return false;
 		}
@@ -158,7 +158,7 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 
 	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
+			FreeDynStr(the_scalar._data._str.acl_str);	// we know this will succeed
 
 		the_scalar._kind = target_type;
 
@@ -174,9 +174,9 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 		}
 
 		if (target_type == MESSAGE)
-			the_scalar._msgTextQuote.index = find_message(s1);
+			the_scalar._data._msgTextQuote.index = find_message(s1);
 		else
-			the_scalar._str.acl_str = NewDynStr(s1);
+			the_scalar._data._str.acl_str = NewDynStr(s1);
 
 		return true;
 	} else {
@@ -184,12 +184,12 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 		switch (dir_from) {
 		case 'N':
 			the_scalar._kind = NUMERIC;
-			the_scalar._numeric.acl_int = the_number;
+			the_scalar._data._numeric.acl_int = the_number;
 			return true;
 
 		case 'B':
 			the_scalar._kind = NUMERIC;
-			the_scalar._numeric.acl_int = boolval ? 1 : 0;
+			the_scalar._data._numeric.acl_int = boolval ? 1 : 0;
 			break;
 
 		case  'S':
@@ -201,10 +201,10 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 			} else {
 				// successful
 				if (the_scalar._kind == STR_PTR)
-					FreeDynStr(the_scalar._str.acl_str);		// memory no longer needed
+					FreeDynStr(the_scalar._data._str.acl_str);		// memory no longer needed
 
 				the_scalar._kind = NUMERIC;
-				the_scalar._numeric.acl_int = the_number;
+				the_scalar._data._numeric.acl_int = the_number;
 			}
 
 			return true;
@@ -219,21 +219,21 @@ bool convert_to(AclType target_type, ResultType &the_scalar) {
 
 void undefine(ResultType &result) {
 	result._kind = RESERVED;
-	result._reserved.keyword = RW_UNDEFINED;
+	result._data._reserved.keyword = RW_UNDEFINED;
 }
 
 void cleanup(ResultType &result) {
 	if (result._kind == STR_PTR)
-		FreeDynStr(result._str.acl_str);
+		FreeDynStr(result._data._str.acl_str);
 	result._kind = RESERVED;
-	result._reserved.keyword = RW_UNDEFINED;
+	result._data._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);
+		r1._data._str.acl_str = NewDynStr(*r2._data._str.acl_str);
 }
 
 bool result_compare(short comparison, ResultType &r1, ResultType &r2) {
@@ -244,19 +244,19 @@ bool result_compare(short comparison, ResultType &r1, ResultType &r2) {
 		switch (comparison) {
 		case OP_EQ:
 		case OP_NE:
-			verdict = r1._numeric.acl_int == r2._numeric.acl_int;
+			verdict = r1._data._numeric.acl_int == r2._data._numeric.acl_int;
 			break;
 		case OP_LT:
-			verdict = r1._numeric.acl_int <  r2._numeric.acl_int;
+			verdict = r1._data._numeric.acl_int <  r2._data._numeric.acl_int;
 			break;
 		case OP_LE:
-			verdict = r1._numeric.acl_int <= r2._numeric.acl_int;
+			verdict = r1._data._numeric.acl_int <= r2._data._numeric.acl_int;
 			break;
 		case OP_GT:
-			verdict = r1._numeric.acl_int >  r2._numeric.acl_int;
+			verdict = r1._data._numeric.acl_int >  r2._data._numeric.acl_int;
 			break;
 		case OP_GE:
-			verdict = r1._numeric.acl_int >= r2._numeric.acl_int;
+			verdict = r1._data._numeric.acl_int >= r2._data._numeric.acl_int;
 			break;
 		default:
 			break;
@@ -266,19 +266,19 @@ bool result_compare(short comparison, ResultType &r1, ResultType &r2) {
 		switch (comparison) {
 		case OP_EQ:
 		case OP_NE:
-			verdict = *r1._str.acl_str == *r2._str.acl_str;
+			verdict = *r1._data._str.acl_str == *r2._data._str.acl_str;
 			break;
 		case OP_LT:
-			verdict = *r1._str.acl_str < *r2._str.acl_str;
+			verdict = *r1._data._str.acl_str < *r2._data._str.acl_str;
 			break;
 		case OP_LE:
-			verdict = *r1._str.acl_str <= *r2._str.acl_str;
+			verdict = *r1._data._str.acl_str <= *r2._data._str.acl_str;
 			break;
 		case OP_GT:
-			verdict = *r1._str.acl_str > *r2._str.acl_str;
+			verdict = *r1._data._str.acl_str > *r2._data._str.acl_str;
 			break;
 		case OP_GE:
-			verdict = *r1._str.acl_str >= *r2._str.acl_str;
+			verdict = *r1._data._str.acl_str >= *r2._data._str.acl_str;
 			break;
 		default:
 			break;
@@ -290,18 +290,18 @@ bool result_compare(short comparison, ResultType &r1, ResultType &r2) {
 			switch (comparison) {
 			case OP_EQ:
 			case OP_NE:
-				verdict = r1._reserved.keyword ==  r2._reserved.keyword;
+				verdict = r1._data._reserved.keyword ==  r2._data._reserved.keyword;
 				break;
 			default:
 				break;
 			}
 
 		case IDENT:
-			if (r1._ident.ident_kind == r2._ident.ident_kind) {
+			if (r1._data._ident.ident_kind == r2._data._ident.ident_kind) {
 				switch (comparison) {
 				case OP_EQ:
 				case OP_NE:
-					verdict = r1._ident.ident_int ==  r2._ident.ident_int;
+					verdict = r1._data._ident.ident_int ==  r2._data._ident.ident_int;
 					break;
 				default:
 					break;
@@ -327,7 +327,7 @@ bool assignment(ResultType &target, ResultType &value) {
 		wraperr("Warning: attempted assignment to a non-attribute");
 		return false;
 	} else {
-		e = (ExprPtr)target._attr.acl_attr->data;
+		e = (ExprPtr)target._data._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
@@ -342,7 +342,7 @@ bool assignment(ResultType &target, ResultType &value) {
 	}
 
 	copy_result(*e, value);
-	target._attr.acl_attr->data = e;
+	target._data._attr.acl_attr->data = e;
 
 	return true;
 }
@@ -352,16 +352,16 @@ void write_result(ResultType &result) {
 
 	undefine(r1);
 	if (result._kind == STR_PTR)
-		wrapout(*result._str.acl_str, false);
+		wrapout(*result._data._str.acl_str, false);
 	else if (result._kind == RESERVED)
-		wrapout(Reserved_Wds[result._reserved.keyword], false);
+		wrapout(Reserved_Wds[result._data._reserved.keyword], false);
 	else {
 		if (result._kind == ATTR_PTR)
-			copy_result(r1, *(ResultType *)result._attr.acl_attr->data);
+			copy_result(r1, *(ResultType *)result._data._attr.acl_attr->data);
 		else
 			copy_result(r1, result);
 		if (convert_to(STR_PTR, r1))
-			wrapout(*r1._str.acl_str, false);
+			wrapout(*r1._data._str.acl_str, false);
 		cleanup(r1);
 	}
 }
@@ -395,15 +395,15 @@ void display_expr(ExprTree the_tree) {
 	if (the_tree->_kind != OPER) {
 		display_result(*the_tree);
 	} else {
-		if (Binary[the_tree->_oper.op_name]) {
+		if (Binary[the_tree->_data._oper.op_name]) {
 			wrapout(" (", false);
-			display_expr(the_tree->_oper.left);
+			display_expr(the_tree->_data._oper.left);
 			wrapout(") ", false);
 		}
 
-		wrapout(Operators[the_tree->_oper.op_name], false);
+		wrapout(Operators[the_tree->_data._oper.op_name], false);
 		wrapout(" (", false);
-		display_expr(the_tree->_oper.right);
+		display_expr(the_tree->_data._oper.right);
 		wrapout(") ", false);
 	}
 }
diff --git a/engines/glk/archetype/interpreter.h b/engines/glk/archetype/interpreter.h
index 2ee357a..331cd7a 100644
--- a/engines/glk/archetype/interpreter.h
+++ b/engines/glk/archetype/interpreter.h
@@ -35,6 +35,8 @@ typedef ExprNode ResultType;
 
 struct ContextType {
 	int sender, self, each, message;
+
+	ContextType() : sender(0), self(0), each(0), message(0) {}
 };
 
 extern int MainObject;
diff --git a/engines/glk/archetype/keywords.cpp b/engines/glk/archetype/keywords.cpp
index c794750..e3514c6 100644
--- a/engines/glk/archetype/keywords.cpp
+++ b/engines/glk/archetype/keywords.cpp
@@ -109,6 +109,7 @@ void load_text_list(Common::ReadStream *fIn, XArrayType &the_list) {
 
 	new_xarray(the_list);
 	n = fIn->readUint16LE();
+
 	for (i = 0; i < n; ++i) {
 		load_string(fIn, s);
 		append_to_xarray(the_list, NewConstStr(s));
diff --git a/engines/glk/archetype/saveload.cpp b/engines/glk/archetype/saveload.cpp
index bd236a8..2816a1f 100644
--- a/engines/glk/archetype/saveload.cpp
+++ b/engines/glk/archetype/saveload.cpp
@@ -268,8 +268,8 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 			return;
 
 		assert(writeStream);
-		while (the_expr->_kind == OPER && the_expr->_oper.op_name == OP_LPAREN)
-			the_expr = the_expr->_oper.right;
+		while (the_expr->_kind == OPER && the_expr->_data._oper.op_name == OP_LPAREN)
+			the_expr = the_expr->_data._oper.right;
 
 		writeStream->writeByte(the_expr->_kind);
 		break;
@@ -288,28 +288,28 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case OPER:
 		switch (mission) {
 		case LOAD:
-			the_expr->_oper.op_name = readStream->readSByte();
-			the_expr->_oper.left = nullptr;
+			the_expr->_data._oper.op_name = readStream->readSByte();
+			the_expr->_data._oper.left = nullptr;
 			break;
 		case DUMP:
-			writeStream->writeSByte(the_expr->_oper.op_name);
+			writeStream->writeSByte(the_expr->_data._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);
+		if (Binary[the_expr->_data._oper.op_name])
+			walk_expr(mission, bfile, the_expr->_data._oper.left);
+		walk_expr(mission, bfile, the_expr->_data._oper.right);
 		break;
 
 	case NUMERIC:
 		switch (mission) {
 		case LOAD:
-			the_expr->_numeric.acl_int = readStream->readUint32LE();
+			the_expr->_data._numeric.acl_int = readStream->readUint32LE();
 			break;
 		case DUMP:
-			writeStream->writeSint32LE(the_expr->_numeric.acl_int);
+			writeStream->writeSint32LE(the_expr->_data._numeric.acl_int);
 			break;
 		default:
 			break;
@@ -321,10 +321,10 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case QUOTE_LIT:
 		switch (mission) {
 		case LOAD:
-			the_expr->_msgTextQuote.index = readStream->readSint16LE();
+			the_expr->_data._msgTextQuote.index = readStream->readSint16LE();
 			break;
 		case DUMP:
-			writeStream->writeSint16LE(the_expr->_msgTextQuote.index);
+			writeStream->writeSint16LE(the_expr->_data._msgTextQuote.index);
 			break;
 		default:
 			break;
@@ -334,22 +334,22 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case IDENT:
 		switch (mission) {
 		case LOAD:
-			the_expr->_ident.ident_kind = (ClassifyType)readStream->readByte();
-			the_expr->_ident.ident_int = readStream->readSint16LE();
+			the_expr->_data._ident.ident_kind = (ClassifyType)readStream->readByte();
+			the_expr->_data._ident.ident_int = readStream->readSint16LE();
 			break;
 		case DUMP:
-			if (Translating && the_expr->_ident.ident_kind == DefaultClassification) {
+			if (Translating && the_expr->_data._ident.ident_kind == DefaultClassification) {
 				// may have changed meaning
-				get_meaning(the_expr->_ident.ident_int, ID_kind, temp);
+				get_meaning(the_expr->_data._ident.ident_int, ID_kind, temp);
 				if (ID_kind == UNDEFINED_ID)
-					add_undefined(the_expr->_ident.ident_int);
+					add_undefined(the_expr->_data._ident.ident_int);
 			} else {
-				the_expr->_ident.ident_kind = ID_kind;
-				the_expr->_ident.ident_int = temp;
+				the_expr->_data._ident.ident_kind = ID_kind;
+				the_expr->_data._ident.ident_int = temp;
 			}
 
-			writeStream->writeByte(the_expr->_ident.ident_kind);
-			writeStream->writeSint16LE(the_expr->_ident.ident_int);
+			writeStream->writeByte(the_expr->_data._ident.ident_kind);
+			writeStream->writeSint16LE(the_expr->_data._ident.ident_int);
 			break;
 		default:
 			break;
@@ -359,10 +359,10 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case RESERVED:
 		switch (mission) {
 		case LOAD:
-			the_expr->_reserved.keyword = readStream->readSByte();
+			the_expr->_data._reserved.keyword = readStream->readSByte();
 			break;
 		case DUMP:
-			writeStream->writeSByte(the_expr->_reserved.keyword);
+			writeStream->writeSByte(the_expr->_data._reserved.keyword);
 			break;
 		default:
 			break;
@@ -372,13 +372,13 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	case STR_PTR:
 		switch (mission) {
 		case LOAD:
-			the_expr->_str.acl_str = LoadDynStr(readStream);
+			the_expr->_data._str.acl_str = LoadDynStr(readStream);
 			break;
 		case DUMP:
-			dump_string(writeStream, *the_expr->_str.acl_str);
+			dump_string(writeStream, *the_expr->_data._str.acl_str);
 			break;
 		case FREE:
-			FreeDynStr(the_expr->_str.acl_str);
+			FreeDynStr(the_expr->_data._str.acl_str);
 			break;
 		default:
 			break;
@@ -468,47 +468,47 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 	// Main walk
 	switch (the_stmt->_kind) {
 	case COMPOUND:
-		walk_item_list(mission, bfile, the_stmt->_compound.statements, STMT_LIST);
+		walk_item_list(mission, bfile, the_stmt->_data._compound.statements, STMT_LIST);
 		break;
 
 	case ST_EXPR:
-		walk_expr(mission, bfile, the_stmt->_expr.expression);
+		walk_expr(mission, bfile, the_stmt->_data._expr.expression);
 		break;
 
 	case ST_IF:
-		walk_expr(mission, bfile, the_stmt->_if.condition);
-		walk_stmt(mission, bfile, the_stmt->_if.then_branch);
-		walk_stmt(mission, bfile, the_stmt->_if.else_branch);
+		walk_expr(mission, bfile, the_stmt->_data._if.condition);
+		walk_stmt(mission, bfile, the_stmt->_data._if.then_branch);
+		walk_stmt(mission, bfile, the_stmt->_data._if.else_branch);
 		break;
 
 	case ST_CASE:
-		walk_expr(mission, bfile, the_stmt->_case.test_expr);
-		walk_item_list(mission, bfile, the_stmt->_case.cases, CASE_LIST);
+		walk_expr(mission, bfile, the_stmt->_data._case.test_expr);
+		walk_item_list(mission, bfile, the_stmt->_data._case.cases, CASE_LIST);
 		break;
 
 	case ST_CREATE:
 		switch (mission) {
 		case LOAD:
-			the_stmt->_create.archetype = readStream->readSint16LE();
+			the_stmt->_data._create.archetype = readStream->readSint16LE();
 			break;
 		case DUMP:
-			writeStream->writeSint16LE(the_stmt->_create.archetype);
+			writeStream->writeSint16LE(the_stmt->_data._create.archetype);
 			break;
 		default:
 			break;
 		}
 
-		walk_expr(mission, bfile, the_stmt->_create.new_name);
+		walk_expr(mission, bfile, the_stmt->_data._create.new_name);
 		break;
 
 	case ST_DESTROY:
-		walk_expr(mission, bfile, the_stmt->_destroy.victim);
+		walk_expr(mission, bfile, the_stmt->_data._destroy.victim);
 		break;
 
 	case ST_FOR:
 	case ST_WHILE:
-		walk_expr(mission, bfile, the_stmt->_loop.selection);
-		walk_stmt(mission, bfile, the_stmt->_loop.action);
+		walk_expr(mission, bfile, the_stmt->_data._loop.selection);
+		walk_stmt(mission, bfile, the_stmt->_data._loop.action);
 		break;
 
 	case ST_WRITE:
@@ -516,7 +516,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 	case ST_STOP:
 		switch (mission) {
 		case LOAD:
-			new_list(the_stmt->_write.print_list);
+			new_list(the_stmt->_data._write.print_list);
 			sentinel = (StatementKind)readStream->readByte();
 
 			while (sentinel != END_SEQ) {
@@ -525,7 +525,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 				add_bytes(sizeof(NodeType));
 
 				np->data = this_expr;
-				append_to_list(the_stmt->_write.print_list, np);
+				append_to_list(the_stmt->_data._write.print_list, np);
 
 				sentinel = (StatementKind)readStream->readByte();
 			}
@@ -534,7 +534,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 		case DUMP:
 		case FREE:
 			np = nullptr;
-			while (iterate_list(the_stmt->_write.print_list, np)) {
+			while (iterate_list(the_stmt->_data._write.print_list, np)) {
 				if (mission == DUMP)
 					writeStream->writeByte(vContSeq);
 				this_expr = (ExprTree)np->data;
@@ -547,7 +547,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 			if (mission == DUMP)
 				writeStream->writeByte(vEndSeq);
 			else
-				dispose_list(the_stmt->_write.print_list);
+				dispose_list(the_stmt->_data._write.print_list);
 			break;
 
 		default:
diff --git a/engines/glk/archetype/semantic.cpp b/engines/glk/archetype/semantic.cpp
index 4e2cd37..132f530 100644
--- a/engines/glk/archetype/semantic.cpp
+++ b/engines/glk/archetype/semantic.cpp
@@ -174,19 +174,19 @@ bool verify_expr(progfile &f, ExprTree the_expr) {
 
 	switch (the_expr->_kind) {
 	case OPER:
-		switch (the_expr->_oper.op_name) {
+		switch (the_expr->_data._oper.op_name) {
 		case OP_DOT:
-			if (the_expr->_oper.right->_kind != IDENT) {
+			if (the_expr->_data._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);
+			else if (the_expr->_data._oper.right->_data._ident.ident_kind != ATTRIBUTE_ID) {
+				the_expr->_data._oper.right->_data._ident.ident_int = classify_as(f,
+					the_expr->_data._oper.right->_data._ident.ident_int, ATTRIBUTE_ID, nullptr);
 			}
 
-			the_expr->_oper.right->_ident.ident_kind = ATTRIBUTE_ID;
-			if (the_expr->_oper.right->_ident.ident_int == 0)
+			the_expr->_data._oper.right->_data._ident.ident_kind = ATTRIBUTE_ID;
+			if (the_expr->_data._oper.right->_data._ident.ident_int == 0)
 				success = false;
 
 		case OP_ASSIGN:
@@ -195,17 +195,17 @@ bool verify_expr(progfile &f, ExprTree the_expr) {
 		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->_data._oper.left->_kind == IDENT) {
+				get_meaning(the_expr->_data._oper.left->_data._ident.ident_int,
+					the_expr->_data._oper.left->_data._ident.ident_kind, the_expr->_data._oper.left->_data._ident.ident_int);
 
-				if (the_expr->_oper.left->_ident.ident_kind != ATTRIBUTE_ID) {
+				if (the_expr->_data._oper.left->_data._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)) {
+			else if (!(the_expr->_data._oper.left->_kind == OPER &&
+				the_expr->_data._oper.left->_data._oper.op_name == OP_DOT)) {
 				error_message(f, "Left side of assignment must reference an attribute");
 				success = false;
 			}
@@ -216,11 +216,11 @@ bool verify_expr(progfile &f, ExprTree the_expr) {
 		}
 
 		if (success) {
-			if (Binary[the_expr->_oper.op_name])
-				success = verify_expr(f, the_expr->_oper.left);
+			if (Binary[the_expr->_data._oper.op_name])
+				success = verify_expr(f, the_expr->_data._oper.left);
 		}
 		if (success)
-			success = verify_expr(f, the_expr->_oper.right);
+			success = verify_expr(f, the_expr->_data._oper.right);
 		break;
 
 	default:
diff --git a/engines/glk/archetype/statement.h b/engines/glk/archetype/statement.h
index 0be8c1b..62c2095 100644
--- a/engines/glk/archetype/statement.h
+++ b/engines/glk/archetype/statement.h
@@ -34,7 +34,7 @@ enum StatementKind {
 	ST_DESTROY, ST_WRITE, ST_WRITES, ST_STOP, CONT_SEQ, END_SEQ
 };
 
-union StatementType;
+struct StatementType;
 typedef StatementType *StatementPtr;
 
 struct StmtCompound {
@@ -74,8 +74,7 @@ struct StmtWrite {
 	ListType print_list;
 };
 
-union StatementType {
-	StatementKind _kind;
+union StatementTypeData {
 	StmtCompound _compound;
 	StmtExpr _expr;
 	StmtIf _if;
@@ -86,6 +85,11 @@ union StatementType {
 	StmtWrite _write;
 };
 
+struct StatementType {
+	StatementKind _kind;
+	StatementTypeData _data;
+};
+
 struct CasePairType {
 	ExprTree value;
 	StatementPtr action;
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index c30a1b8..d4191a3 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -117,15 +117,15 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 		case NORMALIZE:
 			// last normalized command
 			result._kind = STR_PTR;
-			result._str.acl_str = NewDynStr(g_vm->Command);
+			result._data._str.acl_str = NewDynStr(g_vm->Command);
 			sys_state = IDLING;
 
 		case ABBR:
 			result._kind = STR_PTR;
-			result._str.acl_str = NewDynStr(strmsg);
+			result._data._str.acl_str = NewDynStr(strmsg);
 
 			if (convert_to(NUMERIC, result)) {
-				g_vm->Abbreviate = result._numeric.acl_int;
+				g_vm->Abbreviate = result._data._numeric.acl_int;
 			}
 			else {
 				wraperr("Warning: non-numeric abbreviation message sent to system");
@@ -180,7 +180,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 				cleanup(result);
 			} else {
 				result._kind = STR_PTR;
-				result._str.acl_str = (StringPtr)p;
+				result._data._str.acl_str = (StringPtr)p;
 				sys_state = IDLING;
 			}
 			break;
@@ -189,8 +189,8 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			obj_index = find_object(strmsg);
 			if (obj_index != 0) {
 				result._kind = IDENT;
-				result._ident.ident_kind = OBJECT_ID;
-				result._ident.ident_int = obj_index;
+				result._data._ident.ident_kind = OBJECT_ID;
+				result._data._ident.ident_int = obj_index;
 			}
 			sys_state = IDLING;
 			break;
@@ -220,11 +220,11 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 				cleanup(result);
 			} else if (obj_index < 0) {
 				result._kind = STR_PTR;
-				result._str.acl_str = NewDynStr(nomatch);
+				result._data._str.acl_str = NewDynStr(nomatch);
 			} else {
 				result._kind = IDENT;
-				result._ident.ident_kind = OBJECT_ID;
-				result._ident.ident_int = obj_index;
+				result._data._ident.ident_kind = OBJECT_ID;
+				result._data._ident.ident_int = obj_index;
 			}
 
 			sys_state = IDLING;
@@ -254,7 +254,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 
 		case FREE_MEMORY:
 			result._kind = NUMERIC;
-			result._numeric.acl_int = 0xffff;		// MemAvail;
+			result._data._numeric.acl_int = 0xffff;		// MemAvail;
 			sys_state = IDLING;
 			break;
 
@@ -267,7 +267,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			else {
 				save_game_state(stfile, g_vm->Object_List);
 				result._kind = RESERVED;
-				result._reserved.keyword = RW_TRUE;
+				result._data._reserved.keyword = RW_TRUE;
 
 				stfile->finalize();
 				delete stfile;
@@ -284,7 +284,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 				cleanup(result);
 			} else {
 				result._kind = RESERVED;
-				result._reserved.keyword = load_game_state(stfile, g_vm->Object_List) ? RW_TRUE : RW_FALSE;
+				result._data._reserved.keyword = load_game_state(stfile, g_vm->Object_List) ? RW_TRUE : RW_FALSE;
 				delete stfile;
 			}
 


Commit: 3165d52a7b586a7b1442ba8ae7e3167d852d220f
    https://github.com/scummvm/scummvm/commit/3165d52a7b586a7b1442ba8ae7e3167d852d220f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Adding GLK hookups

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/archetype.h


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 5ab3744..416cf6c 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -34,6 +34,8 @@
 namespace Glk {
 namespace Archetype {
 
+#define MAX_INPUT_LINE 100
+
 Archetype *g_vm;
 
 Archetype::Archetype(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
@@ -75,10 +77,15 @@ bool Archetype::initialize() {
 	new_xarray(Object_List);
 	NullStr = NewConstStr("null");
 
+	// GLK window
+	_mainWindow = glk_window_open(0, 0, 0, wintype_TextBuffer);
+	glk_set_window(_mainWindow);
+
 	return true;
 }
 
 void Archetype::deinitialize() {
+	glk_window_close(_mainWindow);
 }
 
 Common::Error Archetype::readSaveData(Common::SeekableReadStream *rs) {
@@ -110,16 +117,43 @@ void Archetype::interpret() {
 	cleanup(result);
 }
 
-void Archetype::write(const String &fmt, ...) {
-	// TODO
+void Archetype::write(const String fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	Common::String s = Common::String::format(fmt.c_str(), ap);
+	va_end(ap);
+
+	glk_put_buffer(s.c_str(), s.size());
 }
 
-void Archetype::writeln(const String &fmt, ...) {
-	// TODO
+void Archetype::writeln(const String fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	Common::String s = Common::String::format(fmt.c_str(), ap);
+	va_end(ap);
+
+	s += '\n';
+	glk_put_buffer(s.c_str(), s.size());
 }
 
 void Archetype::readln(String &s) {
-	// TODO
+	event_t ev;
+	char buffer[MAX_INPUT_LINE + 1];
+
+	glk_request_line_event(_mainWindow, buffer, MAX_INPUT_LINE, 0);
+
+	do {
+		glk_select(&ev);
+		if (ev.type == evtype_Quit) {
+			glk_cancel_line_event(_mainWindow, &ev);
+			return;
+		} else if (ev.type == evtype_LineInput)
+			break;
+	} while (ev.type != evtype_Quit);
+
+	buffer[ev.val1] = 0;
+
+	s = String(buffer);
 }
 
 char Archetype::ReadKey() {
diff --git a/engines/glk/archetype/archetype.h b/engines/glk/archetype/archetype.h
index f671df3..85e3a4e 100644
--- a/engines/glk/archetype/archetype.h
+++ b/engines/glk/archetype/archetype.h
@@ -40,6 +40,7 @@ class Archetype : public GlkAPI {
 private:
 	int _saveSlot;
 	bool Translating;
+	winid_t _mainWindow;
 public:
 	// keywords.cpp
 	XArrayType Literals, Vocabulary;
@@ -157,12 +158,12 @@ public:
 	/**
 	 * Write some text to the screen
 	 */
-	void write(const String &fmt, ...);
+	void write(const String fmt, ...);
 
 	/**
 	 * Write a line to the screen
 	 */
-	void writeln(const String &fmt, ...);
+	void writeln(const String fmt, ...);
 	void writeln() { writeln(""); }
 
 	void readln(String &s);


Commit: d51d3d40861446f849564bb6af07ab35cb2c3e77
    https://github.com/scummvm/scummvm/commit/d51d3d40861446f849564bb6af07ab35cb2c3e77
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
COMMON: Allow for enabling/disabling debug channels by number

Changed paths:
    common/debug-channels.h
    common/debug.cpp


diff --git a/common/debug-channels.h b/common/debug-channels.h
index 0fb8006..a2df351 100644
--- a/common/debug-channels.h
+++ b/common/debug-channels.h
@@ -80,7 +80,7 @@ public:
 	void clearAllDebugChannels();
 
 	/**
-	 * Enables an debug channel.
+	 * Enables a debug channel.
 	 *
 	 * @param name the name of the debug channel to enable
 	 * @return true on success, false on failure
@@ -88,13 +88,29 @@ public:
 	bool enableDebugChannel(const String &name);
 
 	/**
-	 * Disables an debug channel.
+	 * Enables a debug channel.
+	 *
+	 * @param channel The debug channel
+	 * @return true on success, false on failure
+	 */
+	bool enableDebugChannel(uint32 channel);
+
+	/**
+	 * Disables a debug channel.
 	 *
 	 * @param name the name of the debug channel to disable
 	 * @return true on success, false on failure
 	 */
 	bool disableDebugChannel(const String &name);
 
+	/**
+	 * Disables a debug channel.
+	 *
+	 * @param channel The debug channel
+	 * @return true on success, false on failure
+	 */
+	bool disableDebugChannel(uint32 channel);
+
 	typedef List<DebugChannel> DebugChannelList;
 
 	/**
diff --git a/common/debug.cpp b/common/debug.cpp
index 5db8990..568d2d5 100644
--- a/common/debug.cpp
+++ b/common/debug.cpp
@@ -78,6 +78,11 @@ bool DebugManager::enableDebugChannel(const String &name) {
 	}
 }
 
+bool DebugManager::enableDebugChannel(uint32 channel) {
+	gDebugChannelsEnabled |= channel;
+	return true;
+}
+
 bool DebugManager::disableDebugChannel(const String &name) {
 	DebugChannelMap::iterator i = gDebugChannels.find(name);
 
@@ -91,6 +96,11 @@ bool DebugManager::disableDebugChannel(const String &name) {
 	}
 }
 
+bool DebugManager::disableDebugChannel(uint32 channel) {
+	gDebugChannelsEnabled &= ~channel;
+	return true;
+}
+
 DebugManager::DebugChannelList DebugManager::listDebugChannels() {
 	DebugChannelList tmp;
 	for (DebugChannelMap::iterator i = gDebugChannels.begin(); i != gDebugChannels.end(); ++i)


Commit: a3c646133f9c173b23ff775e03563e337be1cdaf
    https://github.com/scummvm/scummvm/commit/a3c646133f9c173b23ff775e03563e337be1cdaf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:29-08:00

Commit Message:
GLK: ARCHETYPE: Switch from original's Debug enum to debug channels

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/archetype.h
    engines/glk/archetype/misc.cpp
    engines/glk/archetype/misc.h
    engines/glk/archetype/sys_object.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 416cf6c..78ba906 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "common/config-manager.h"
+#include "common/debug-channels.h"
 #include "glk/archetype/archetype.h"
 #include "glk/archetype/crypt.h"
 #include "glk/archetype/expression.h"
@@ -41,6 +42,11 @@ Archetype *g_vm;
 Archetype::Archetype(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
 		_saveSlot(-1) {
 	g_vm = this;
+
+	DebugMan.addDebugChannel(DEBUG_BYTES, "bytes", "Memory usage");
+	DebugMan.addDebugChannel(DEBUG_MSGS, "messages", "Messages debugging");
+	DebugMan.addDebugChannel(DEBUG_EXPR, "expressions", "Expressions debugging");
+	DebugMan.addDebugChannel(DEBUG_STMT, "statements", "Statements debugging");
 }
 
 void Archetype::runGame() {
@@ -268,12 +274,12 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 	void *p;
 	ContextType c;
 
-	if (message_sent == 0) {
+	if (message_sent == -1) {
 		cleanup(result);
 		return false;
 	}
 
-	if ((Debug & DEBUG_MSGS) > 0) {
+	if (DebugMan.isDebugChannelEnabled(DEBUG_MSGS)) {
 		r._kind = IDENT;
 		r._data._ident.ident_kind = OBJECT_ID;
 		r._data._ident.ident_int = context.self;
@@ -673,7 +679,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 		cleanup(r1);
 		cleanup(r2);
 
-		if ((Debug & DEBUG_EXPR) > 0) {
+		if (DebugMan.isDebugChannelEnabled(DEBUG_EXPR)) {
 			wrapout(" -- ", false);
 			display_expr(the_expr);
 			wrapout("  ==>  ", false);
@@ -723,7 +729,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 	undefine(r2);
 	cleanup(result);
 
-	verbose = (Debug & DEBUG_STMT) > 0;
+	verbose = DebugMan.isDebugChannelEnabled(DEBUG_STMT);
 
 	if (verbose)
 		wrapout(" == ", false);
diff --git a/engines/glk/archetype/archetype.h b/engines/glk/archetype/archetype.h
index 85e3a4e..8df78e5 100644
--- a/engines/glk/archetype/archetype.h
+++ b/engines/glk/archetype/archetype.h
@@ -33,6 +33,13 @@
 namespace Glk {
 namespace Archetype {
 
+enum DebugFlag {
+	DEBUG_BYTES = 0x01,
+	DEBUG_MSGS = 0x02,
+	DEBUG_EXPR = 0x04,
+	DEBUG_STMT = 0x08
+};
+
 /**
  * Archetype game interpreter
  */
diff --git a/engines/glk/archetype/misc.cpp b/engines/glk/archetype/misc.cpp
index c10bba1..3818e31 100644
--- a/engines/glk/archetype/misc.cpp
+++ b/engines/glk/archetype/misc.cpp
@@ -23,6 +23,7 @@
 #include "glk/archetype/misc.h"
 #include "glk/archetype/archetype.h"
 #include "glk/quetzal.h"
+#include "common/debug-channels.h"
 
 namespace Glk {
 namespace Archetype {
@@ -31,7 +32,6 @@ const char *const VERSION_STUB = "Archetype version ";
 const double VERSION_NUM = 1.02;
 
 size_t Bytes;
-int Debug;
 bool KeepLooking;
 bool AllErrors;
 
@@ -43,7 +43,6 @@ void misc_init() {
 	//HeapError = @HeapFunc;
 	//Mark(Prior)
 	Bytes = 0;
-	Debug = 0;
 	KeepLooking = true;
 	AllErrors = false;
 }
@@ -126,7 +125,7 @@ void progfile::sourcePos() {
 void add_bytes(int delta) {
 	Bytes += delta;
 
-	if ((Debug & DEBUG_BYTES) != 0) {
+	if (DebugMan.isDebugChannelEnabled(DEBUG_BYTES)) {
 		if (delta >= 0)
 			g_vm->write("Allocated   ");
 		else
diff --git a/engines/glk/archetype/misc.h b/engines/glk/archetype/misc.h
index 8d93ce7..ee08097 100644
--- a/engines/glk/archetype/misc.h
+++ b/engines/glk/archetype/misc.h
@@ -34,13 +34,6 @@ namespace Archetype {
 #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
@@ -113,7 +106,6 @@ enum ClassifyType { TYPE_ID, OBJECT_ID, ATTRIBUTE_ID, ENUMERATE_ID, UNDEFINED_ID
 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;
 
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index d4191a3..f4402fd 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -27,6 +27,7 @@
 #include "glk/archetype/parser.h"
 #include "glk/archetype/wrap.h"
 #include "common/algorithm.h"
+#include "common/debug-channels.h"
 #include "common/savefile.h"
 
 namespace Glk {
@@ -231,17 +232,26 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			break;
 
 		case DEBUG_MESSAGES:
-			Debug = Debug ^ DEBUG_MSGS;
+			if (DebugMan.isDebugChannelEnabled(DEBUG_MSGS))
+				DebugMan.disableDebugChannel(DEBUG_MSGS);
+			else
+				DebugMan.enableDebugChannel(DEBUG_MSGS);
 			sys_state = IDLING;
 			break;
 
 		case DEBUG_EXPRESSIONS:
-			Debug = Debug ^ DEBUG_EXPR;
+			if (DebugMan.isDebugChannelEnabled(DEBUG_EXPRESSIONS))
+				DebugMan.disableDebugChannel(DEBUG_EXPRESSIONS);
+			else
+				DebugMan.enableDebugChannel(DEBUG_EXPRESSIONS);
 			sys_state = IDLING;
 			break;
 
 		case DEBUG_STATEMENTS:
-			Debug = Debug ^ DEBUG_STMT;
+			if (DebugMan.isDebugChannelEnabled(DEBUG_STATEMENTS))
+				DebugMan.disableDebugChannel(DEBUG_STATEMENTS);
+			else
+				DebugMan.enableDebugChannel(DEBUG_STATEMENTS);
 			sys_state = IDLING;
 			break;
 


Commit: c570d6f4b29e62f6177bf3c046d0e5761c57b5cc
    https://github.com/scummvm/scummvm/commit/c570d6f4b29e62f6177bf3c046d0e5761c57b5cc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Fix array indexing to match original's 1 based

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/array.cpp
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/wrap.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 78ba906..4f388da 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -274,7 +274,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 	void *p;
 	ContextType c;
 
-	if (message_sent == -1) {
+	if (message_sent == 0) {
 		cleanup(result);
 		return false;
 	}
diff --git a/engines/glk/archetype/array.cpp b/engines/glk/archetype/array.cpp
index b816306..79a486f 100644
--- a/engines/glk/archetype/array.cpp
+++ b/engines/glk/archetype/array.cpp
@@ -38,15 +38,15 @@ void append_to_xarray(XArrayType &the_xarray, void *element) {
 }
 
 bool access_xarray(XArrayType &the_xarray, int index, void *&result, AccessType direction) {
-	if (index < 0 || index >= (int)the_xarray.size())
+	if (index < 1 || index > (int)the_xarray.size())
 		return false;
 
 	switch (direction) {
 	case PEEK_ACCESS:
-		result = the_xarray[index];
+		result = the_xarray[index - 1];
 		break;
 	case POKE_ACCESS:
-		the_xarray[index] = result;
+		the_xarray[index - 1] = result;
 		break;
 	}
 
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index 21f732b..3beca74 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -43,14 +43,14 @@ StringPtr MakeNewDynStr(const String &s) {
 int find_message(const String &message) {
 	void *p;
 
-	for (uint i = 0; i < g_vm->Vocabulary.size(); ++i) {
+	for (uint i = 1; 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;
+	return 0;
 }
 
 bool convert_to(AclType target_type, ResultType &the_scalar) {
diff --git a/engines/glk/archetype/wrap.cpp b/engines/glk/archetype/wrap.cpp
index 6ef961f..44fddb8 100644
--- a/engines/glk/archetype/wrap.cpp
+++ b/engines/glk/archetype/wrap.cpp
@@ -72,7 +72,7 @@ void wrapout(const String &str, bool terminate) {
 
 	const char CHARS[7] = { '.', ',', ':', ';', ')', '-', '"' };
 	for (int i = 0; i < 7; ++i) {
-		if (s[0] == CHARS[i]) {
+		if (!s.empty() && s[0] == CHARS[i]) {
 			maxchars += SAFETY_MARGIN;
 			break;
 		}


Commit: 53fd662ac0bfc0263409290116ad3f76dfee33ac
    https://github.com/scummvm/scummvm/commit/53fd662ac0bfc0263409290116ad3f76dfee33ac
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Change to use C++ new/delete

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/expression.h
    engines/glk/archetype/id_table.h
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/keywords.cpp
    engines/glk/archetype/linked_list.cpp
    engines/glk/archetype/linked_list.h
    engines/glk/archetype/parser.cpp
    engines/glk/archetype/saveload.cpp
    engines/glk/archetype/saveload.h
    engines/glk/archetype/semantic.cpp
    engines/glk/archetype/statement.h
    engines/glk/archetype/sys_object.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 4f388da..e7e8da2 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -247,9 +247,9 @@ void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextTyp
 		} else {
 			// inherited - must create new node }
 			result._kind = ATTR_PTR;
-			result._data._attr.acl_attr = (NodePtr)malloc(sizeof(NodeType));
+			result._data._attr.acl_attr = new NodeType();
 
-			e = (ExprTree)malloc(sizeof(ExprNode));
+			e = new ExprNode();
 			undefine(*e);
 			eval_expr((ExprPtr)np->data, *e, c, RVALUE);
 
@@ -491,7 +491,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 				return;
 
 			// Do the two operations using a dummy expression node
-			e = (ExprTree)malloc(sizeof(ExprNode));
+			e = new ExprNode();
 			*e = *the_expr;
 
 			switch (the_expr->_data._oper.op_name) {
@@ -519,7 +519,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			e->_data._oper.right = &r1;
 
 			eval_expr(e, result, context, desired);
-			free(e);
+			delete e;
 			break;
 
 		case OP_CHS:
@@ -906,7 +906,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		}
 		else {
 			// do it for real
-			the_object = (ObjectPtr)malloc(sizeof(ObjectType));
+			the_object = new ObjectType();
 			the_object->inherited_from = the_stmt->_data._create.archetype;
 			new_list(the_object->attributes);
 			new_list(the_object->methods);
diff --git a/engines/glk/archetype/expression.h b/engines/glk/archetype/expression.h
index 6ccc0b0..90b1dab 100644
--- a/engines/glk/archetype/expression.h
+++ b/engines/glk/archetype/expression.h
@@ -33,9 +33,11 @@ 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 ExprNode;
+
 struct OperNode {
 	int8 op_name;
-	struct ExprNode *left, *right;
+	ExprNode *left, *right;
 };
 
 struct MessageTextQuoteNode {
@@ -76,6 +78,10 @@ union ExprNodeData {
 struct ExprNode {
 	AclType _kind;
 	ExprNodeData _data;
+
+	ExprNode() : _kind(RESERVED) {
+		_data._reserved.keyword = RW_UNDEFINED;
+	}
 };
 
 typedef ExprNode *ExprPtr;
diff --git a/engines/glk/archetype/id_table.h b/engines/glk/archetype/id_table.h
index 2aff6d6..4202e39 100644
--- a/engines/glk/archetype/id_table.h
+++ b/engines/glk/archetype/id_table.h
@@ -49,6 +49,9 @@ struct IdRecType {
 	int id_index;			// The ID's index in the ID table
 	StringPtr id_name;
 	IdRecType *next;
+
+	IdRecType() : id_kind(UNDEFINED_ID), id_integer(0), id_index(0),
+		id_name(nullptr), next(nullptr) {}
 };
 typedef IdRecType *IdRecPtr;
 
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index 3beca74..c88f584 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -336,7 +336,7 @@ bool assignment(ResultType &target, ResultType &value) {
 			cleanup(*e);
 		} else {
 			dispose_expr(e);
-			e = (ExprPtr)malloc(sizeof(ExprNode));
+			e = new ExprNode();
 			undefine(*e);
 		}
 	}
diff --git a/engines/glk/archetype/keywords.cpp b/engines/glk/archetype/keywords.cpp
index e3514c6..97cac84 100644
--- a/engines/glk/archetype/keywords.cpp
+++ b/engines/glk/archetype/keywords.cpp
@@ -131,7 +131,7 @@ void dispose_text_list(XArrayType &the_list) {
 
 	for (uint i = 0; i < the_list.size(); ++i) {
 		if (index_xarray(the_list, i, p))
-			free((StringPtr)p);
+			delete (StringPtr)p;
 	}
 
 	dispose_xarray(the_list);
diff --git a/engines/glk/archetype/linked_list.cpp b/engines/glk/archetype/linked_list.cpp
index bc4719c..eef55b4 100644
--- a/engines/glk/archetype/linked_list.cpp
+++ b/engines/glk/archetype/linked_list.cpp
@@ -27,7 +27,7 @@ namespace Glk {
 namespace Archetype {
 
 void new_list(ListType &the_list) {
-	the_list = (ListType)malloc(sizeof(NodeType));
+	the_list = new NodeType();
 	add_bytes(sizeof(NodeType));
 	the_list->key = 0;
 	the_list->data = nullptr;
@@ -40,7 +40,7 @@ void dispose_list(ListType &the_list) {
 		axe = theNode;
 		theNode = theNode->next;
 		add_bytes(-(int)sizeof(*axe));
-		free(axe);
+		delete axe;
 	}
 
 }
diff --git a/engines/glk/archetype/linked_list.h b/engines/glk/archetype/linked_list.h
index 372de36..e0f4b08 100644
--- a/engines/glk/archetype/linked_list.h
+++ b/engines/glk/archetype/linked_list.h
@@ -32,6 +32,8 @@ struct NodeType {
 	void *data;
 	int key;
 	NodeType *next;
+
+	NodeType() : data(nullptr), key(0), next(nullptr) {}
 };
 typedef NodeType *NodePtr;
 
diff --git a/engines/glk/archetype/parser.cpp b/engines/glk/archetype/parser.cpp
index 0063e28..8c1c47c 100644
--- a/engines/glk/archetype/parser.cpp
+++ b/engines/glk/archetype/parser.cpp
@@ -32,6 +32,8 @@ const int WORD_LEN = 32;
 struct ParseType {
 	StringPtr word;
 	int object;
+
+	ParseType() : word(nullptr), object(0) {}
 };
 typedef ParseType *ParsePtr;
 
@@ -103,7 +105,7 @@ void add_parse_word(TargetListType which_list, String &the_word, int the_object)
 	do {
 		bar = the_word.indexOf('|');
 		if (bar != -1) {
-			pp = (ParsePtr)malloc(sizeof(ParseType));
+			pp = new ParseType();
 			tempstr = the_word.left(bar - 1).left(g_vm->Abbreviate);
 
 			pp->word = NewConstStr(tempstr);
@@ -111,7 +113,7 @@ void add_parse_word(TargetListType which_list, String &the_word, int the_object)
 			the_word = String(the_word.c_str() + bar + 1);
 			pp->object = the_object;
 
-			np = (NodePtr)malloc(sizeof(NodeType));
+			np = new NodeType();
 			np->key = pp->word->size();
 			np->data = pp;
 
@@ -275,7 +277,7 @@ void clear_parse_list(ListType &the_list) {
 	while (iterate_list(the_list, np)) {
 		pp = (ParsePtr)np->data;
 		FreeConstStr(pp->word);
-		free(pp);
+		delete pp;
 	}
 
 	dispose_list(the_list);
diff --git a/engines/glk/archetype/saveload.cpp b/engines/glk/archetype/saveload.cpp
index 2816a1f..7c47d77 100644
--- a/engines/glk/archetype/saveload.cpp
+++ b/engines/glk/archetype/saveload.cpp
@@ -105,7 +105,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 		switch (mission) {
 		case LOAD:
 			assert(readStream);
-			np = (NodePtr)malloc(sizeof(NodeType));
+			np = new NodeType();
 			add_bytes(sizeof(NodeType));
 			np->key = readStream->readSint16LE();
 			break;
@@ -156,7 +156,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 		case CASE_LIST:
 			switch (mission) {
 			case LOAD:
-				this_case = (CasePairPtr)malloc(sizeof(CasePairType));
+				this_case = new CasePairType();
 				add_bytes(sizeof(CasePairType));
 				walk_expr(mission, bfile, this_case->value);
 				walk_stmt(mission, bfile, this_case->action);
@@ -172,7 +172,7 @@ void walk_item_list(MissionType mission, Common::Stream *bfile, ListType &elemen
 
 				if (mission == FREE) {
 					add_bytes(sizeof(CasePairType));
-					free(this_case);
+					delete this_case;
 				}
 				break;
 
@@ -258,7 +258,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	switch (mission) {
 	case LOAD:
 		assert(readStream);
-		the_expr = (ExprTree)malloc(sizeof(ExprNode));
+		the_expr = new ExprNode();
 		add_bytes(sizeof(ExprNode));
 		the_expr->_kind = (AclType)readStream->readByte();
 		break;
@@ -392,7 +392,7 @@ static void walk_expr(MissionType mission, Common::Stream *bfile, ExprTree &the_
 	// Postlude
 	switch (mission) {
 	case FREE:
-		free(the_expr);
+		delete the_expr;
 		the_expr = nullptr;
 		break;
 	default:
@@ -442,7 +442,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 		if (sentinel == END_SEQ)
 			return;
 
-		the_stmt = (StatementPtr)malloc(sizeof(StatementType));
+		the_stmt = new StatementType();
 		add_bytes(sizeof(StatementType));
 		the_stmt->_kind = sentinel;
 		break;
@@ -521,7 +521,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 
 			while (sentinel != END_SEQ) {
 				walk_expr(mission, bfile, this_expr);
-				np = (NodePtr)malloc(sizeof(NodeType));
+				np = new NodeType();
 				add_bytes(sizeof(NodeType));
 
 				np->data = this_expr;
@@ -563,7 +563,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 	switch (mission) {
 	case FREE:
 		add_bytes(sizeof(StatementType));
-		free(the_stmt);
+		delete the_stmt;
 		break;
 	default:
 		break;
@@ -576,7 +576,7 @@ static void walk_stmt(MissionType mission, Common::Stream *bfile, StatementPtr &
 void load_object(Common::ReadStream *f_in, ObjectPtr &the_object) {
 	StatementKind sentinel;
 
-	the_object = (ObjectPtr)malloc(sizeof(ObjectType));
+	the_object = new ObjectType();
 	add_bytes(sizeof(ObjectType));
 
 	the_object->inherited_from = f_in->readSint16LE();
@@ -610,7 +610,7 @@ void dispose_object(ObjectPtr &the_object) {
 		dispose_stmt(the_object->other);
 
 	add_bytes(sizeof(ObjectType));
-	free(the_object);
+	delete the_object;
 	the_object = nullptr;
 }
 
diff --git a/engines/glk/archetype/saveload.h b/engines/glk/archetype/saveload.h
index 0f98eb2..55f1ca6 100644
--- a/engines/glk/archetype/saveload.h
+++ b/engines/glk/archetype/saveload.h
@@ -43,6 +43,9 @@ struct ObjectType {
 	ListType attributes;
 	ListType methods;
 	StatementPtr other;
+
+	ObjectType() : inherited_from(0), attributes(nullptr), methods(nullptr),
+		other(nullptr) {}
 };
 typedef ObjectType *ObjectPtr;
 
diff --git a/engines/glk/archetype/semantic.cpp b/engines/glk/archetype/semantic.cpp
index 132f530..9e0660f 100644
--- a/engines/glk/archetype/semantic.cpp
+++ b/engines/glk/archetype/semantic.cpp
@@ -128,9 +128,9 @@ void add_undefined(int the_ID) {
 	if (np != nullptr) {
 		++*((IntegerPtr)np->data);
 	} else {
-		np = (NodePtr)malloc(sizeof(NodeType));
+		np = new NodeType();
 		np->key = the_ID;
-		ip = (IntegerPtr)malloc(sizeof(int));
+		ip = new int();
 		*ip = 1;			// TODO: Should this be 0-based?
 		np->data = ip;
 		insert_item(g_vm->Overlooked, np);
@@ -161,7 +161,7 @@ bool display_undefined() {
 		else
 			g_vm->writeln("<unknown identifier>");
 
-		free(ip);
+		delete ip;
 	}
 
 	dispose_list(g_vm->Overlooked);
diff --git a/engines/glk/archetype/statement.h b/engines/glk/archetype/statement.h
index 62c2095..14dff86 100644
--- a/engines/glk/archetype/statement.h
+++ b/engines/glk/archetype/statement.h
@@ -88,6 +88,10 @@ union StatementTypeData {
 struct StatementType {
 	StatementKind _kind;
 	StatementTypeData _data;
+
+	StatementType() : _kind(COMPOUND) {
+		_data._compound.statements = nullptr;
+	}
 };
 
 struct CasePairType {
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index f4402fd..ebbe882 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -203,7 +203,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			break;
 
 		case PRESENT:
-			np = (NodePtr)malloc(sizeof(NodeType));
+			np = new NodeType();
 			np->data = nullptr;
 			np->key = the_caller;
 


Commit: 381bd5e9a73cd75618a8a5a13f272927aa402187
    https://github.com/scummvm/scummvm/commit/381bd5e9a73cd75618a8a5a13f272927aa402187
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Further array indexing fixes

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/array.cpp
    engines/glk/archetype/game_stat.cpp
    engines/glk/archetype/heap_sort.cpp
    engines/glk/archetype/id_table.cpp
    engines/glk/archetype/keywords.cpp
    engines/glk/archetype/linked_list.cpp
    engines/glk/archetype/linked_list.h
    engines/glk/archetype/saveload.cpp
    engines/glk/archetype/semantic.cpp
    engines/glk/archetype/token.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index e7e8da2..d17f0c7 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -873,7 +873,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		c = context;
 		c.each = 1;
 
-		while (!b && c.each < (int)Object_List.size()) {
+		while (!b && c.each <= (int)Object_List.size()) {
 			if (eval_condition(the_stmt->_data._loop.selection, c)) {
 				exec_stmt(the_stmt->_data._loop.action, result, c);
 				b = (result._kind == RESERVED) && (result._data._reserved.keyword == RW_BREAK);
@@ -903,8 +903,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 		if (!assignment(r1, result)) {
 			cleanup(result);
-		}
-		else {
+		} else {
 			// do it for real
 			the_object = new ObjectType();
 			the_object->inherited_from = the_stmt->_data._create.archetype;
@@ -919,7 +918,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 			while (access_xarray(Object_List, i, q, PEEK_ACCESS) && q != nullptr)
 				++i;
 
-			if (i >= (int)Object_List.size())
+			if (i > (int)Object_List.size())
 				append_to_xarray(Object_List, p);
 			else
 				b = access_xarray(Object_List, i, p, POKE_ACCESS);
@@ -943,7 +942,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 			dispose_object(the_object);
 			p = nullptr;
 			b = access_xarray(Object_List, result._data._ident.ident_int, p, POKE_ACCESS);
-			if (result._data._ident.ident_int == ((int)Object_List.size() - 1))
+			if (result._data._ident.ident_int == (int)Object_List.size())
 				shrink_xarray(Object_List);
 		} else {
 			wraperr("Can only destroy previously created objects");
diff --git a/engines/glk/archetype/array.cpp b/engines/glk/archetype/array.cpp
index 79a486f..33147d1 100644
--- a/engines/glk/archetype/array.cpp
+++ b/engines/glk/archetype/array.cpp
@@ -38,7 +38,10 @@ void append_to_xarray(XArrayType &the_xarray, void *element) {
 }
 
 bool access_xarray(XArrayType &the_xarray, int index, void *&result, AccessType direction) {
-	if (index < 1 || index > (int)the_xarray.size())
+	if (index <= 0)
+		error("Invalid index - double check arrays were 1 based in original");
+
+	if (index > (int)the_xarray.size())
 		return false;
 
 	switch (direction) {
diff --git a/engines/glk/archetype/game_stat.cpp b/engines/glk/archetype/game_stat.cpp
index 9f6a8b8..015065a 100644
--- a/engines/glk/archetype/game_stat.cpp
+++ b/engines/glk/archetype/game_stat.cpp
@@ -40,7 +40,7 @@ void save_game_state(Common::WriteStream *bfile, XArrayType &objects) {
 	// Get the encryption straight - reset the seed
 	cryptinit(Encryption, GTimeStamp);
 
-	for (i = 0; i < Dynamic - 1; ++i) {
+	for (i = 1; i < Dynamic; ++i) {
 		if (index_xarray(objects, i, p)) {
 			ObjectPtr op = (ObjectPtr)p;
 			bfile->writeUint32LE(vContSeq);
@@ -49,7 +49,7 @@ void save_game_state(Common::WriteStream *bfile, XArrayType &objects) {
 		}
 	}
 
-	for (i = Dynamic; i < (int)objects.size(); ++i) {
+	for (i = Dynamic; i <= (int)objects.size(); ++i) {
 		if (index_xarray(objects, i, p)) {
 			bfile->writeUint32LE(vContSeq);
 			dump_object(bfile, (ObjectPtr)p);
@@ -81,7 +81,7 @@ bool load_game_state(Common::ReadStream *bfile, XArrayType &objects) {
 
 	// 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) {
+	for (i = 1; i < Dynamic; ++i) {
 		if (index_xarray(objects, i, p)) {
 			sentinel = (StatementKind)bfile->readUint32LE();
 			op = (ObjectPtr)p;
@@ -92,7 +92,7 @@ bool load_game_state(Common::ReadStream *bfile, XArrayType &objects) {
 
 	// Flush dynamic objects.Dispose of each object andshrink back the xarray
 
-	for (i = objects.size() - 1; i >= Dynamic; --i) {
+	for (i = objects.size(); i >= Dynamic; --i) {
 		if (index_xarray(objects, i, p)) {
 			op = (ObjectPtr)p;
 			dispose_object(op);
diff --git a/engines/glk/archetype/heap_sort.cpp b/engines/glk/archetype/heap_sort.cpp
index 98753cc..2f0edd2 100644
--- a/engines/glk/archetype/heap_sort.cpp
+++ b/engines/glk/archetype/heap_sort.cpp
@@ -46,7 +46,7 @@ static void heapup() {
 	Element temp;
 
 	L = H.size();
-	while (L > 0) {
+	while (L > 1) {
 		if ((L % 2) == 0)
 			parent = L / 2;
 		else
@@ -73,17 +73,17 @@ static void heapdown() {
 	Element comparep;
 	Element temp;
 
-	L = 0;
+	L = 1;
 	while (L < H.size()) {
 		lc = L * 2;
-		if (lc >= H.size()) {
+		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()) {
+			if (rc > H.size()) {
 				compare = lc;
 				comparep = lcp;
 			} else {
@@ -106,7 +106,7 @@ static void heapdown() {
 					g_vm->writeln(CANT_POKE);
 				L = compare;
 			} else {
-				L = H.size();
+				L = H.size() + 1;
 			}
 		}
 	}
diff --git a/engines/glk/archetype/id_table.cpp b/engines/glk/archetype/id_table.cpp
index a611503..5bae511 100644
--- a/engines/glk/archetype/id_table.cpp
+++ b/engines/glk/archetype/id_table.cpp
@@ -49,13 +49,13 @@ int add_ident(const String &id_str) {
 		append_to_xarray(h_index, new_rec);
 		
 		new_rec->id_kind    = DefaultClassification;
-		new_rec->id_index   = h_index.size() - 1;
+		new_rec->id_index   = h_index.size();
 		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;
+		return h_index.size();
 	} else {
 		// found existing identifier
 		return p->next->id_index;
diff --git a/engines/glk/archetype/keywords.cpp b/engines/glk/archetype/keywords.cpp
index 97cac84..05d6bca 100644
--- a/engines/glk/archetype/keywords.cpp
+++ b/engines/glk/archetype/keywords.cpp
@@ -120,7 +120,7 @@ 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) {
+	for (uint i = 1; i <= the_list.size(); ++i) {
 		if (index_xarray(the_list, i, p))
 			dump_string(fOut, *(StringPtr)(p));
 	}
@@ -129,7 +129,7 @@ void dump_text_list(Common::WriteStream *fOut, XArrayType &the_list) {
 void dispose_text_list(XArrayType &the_list) {
 	void *p;
 
-	for (uint i = 0; i < the_list.size(); ++i) {
+	for (uint i = 1; i <= the_list.size(); ++i) {
 		if (index_xarray(the_list, i, p))
 			delete (StringPtr)p;
 	}
diff --git a/engines/glk/archetype/linked_list.cpp b/engines/glk/archetype/linked_list.cpp
index eef55b4..0965e65 100644
--- a/engines/glk/archetype/linked_list.cpp
+++ b/engines/glk/archetype/linked_list.cpp
@@ -29,9 +29,7 @@ namespace Archetype {
 void new_list(ListType &the_list) {
 	the_list = new NodeType();
 	add_bytes(sizeof(NodeType));
-	the_list->key = 0;
-	the_list->data = nullptr;
-	the_list->next = nullptr;
+	the_list->next = the_list;
 }
 
 void dispose_list(ListType &the_list) {
@@ -45,7 +43,7 @@ void dispose_list(ListType &the_list) {
 
 }
 
-bool iterate_list(ListType &the_list, NodePtr index) {
+bool iterate_list(ListType &the_list, NodePtr &index) {
 	if (index == nullptr)
 		index = the_list->next;
 	else
diff --git a/engines/glk/archetype/linked_list.h b/engines/glk/archetype/linked_list.h
index e0f4b08..6f8fea8 100644
--- a/engines/glk/archetype/linked_list.h
+++ b/engines/glk/archetype/linked_list.h
@@ -53,7 +53,7 @@ extern void dispose_list(ListType &the_list);
 /**
  * Iterates through the given list
  */
-extern bool iterate_list(ListType &the_list, NodePtr index);
+extern bool iterate_list(ListType &the_list, NodePtr &index);
 
 /**
  * Appends a new item to the list
diff --git a/engines/glk/archetype/saveload.cpp b/engines/glk/archetype/saveload.cpp
index 7c47d77..61069e3 100644
--- a/engines/glk/archetype/saveload.cpp
+++ b/engines/glk/archetype/saveload.cpp
@@ -633,7 +633,7 @@ void load_obj_list(Common::ReadStream *f_in, XArrayType &obj_list) {
 
 	// 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
+	Dynamic = obj_list.size() + 1;
 }
 
 void dump_obj_list(Common::WriteStream *f_out, XArrayType &obj_list) {
@@ -643,7 +643,7 @@ void dump_obj_list(Common::WriteStream *f_out, XArrayType &obj_list) {
 
 	f_out->writeUint16LE(obj_list.size());
 
-	for (i = 0; i < obj_list.size(); ++i) {
+	for (i = 1; i <= obj_list.size(); ++i) {
 		if (index_xarray(obj_list, i, p)) {
 			this_obj = (ObjectPtr)p;
 			dump_object(f_out, this_obj);
@@ -656,7 +656,7 @@ void dispose_obj_list(XArrayType &obj_list) {
 	void *p;
 	ObjectPtr axe_obj;
 
-	for (i = 0; i < obj_list.size(); ++i) {
+	for (i = 1; i <= obj_list.size(); ++i) {
 		if (index_xarray(obj_list, i, p)) {
 			axe_obj = (ObjectPtr)p;
 			dispose_object(axe_obj);
diff --git a/engines/glk/archetype/semantic.cpp b/engines/glk/archetype/semantic.cpp
index 9e0660f..5f84ade 100644
--- a/engines/glk/archetype/semantic.cpp
+++ b/engines/glk/archetype/semantic.cpp
@@ -52,7 +52,7 @@ int classify_as(progfile &f, int id_number, ClassifyType interpretation, void *p
 			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;
+				the_id_ptr->id_integer = g_vm->Type_List.size();
 				break;
 
 			case OBJECT_ID:
@@ -66,13 +66,13 @@ int classify_as(progfile &f, int id_number, ClassifyType interpretation, void *p
 
 					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;
+					the_id_ptr->id_integer = g_vm->Object_List.size();
 				}
 				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;
+				the_id_ptr->id_integer = g_vm->Attribute_ID_List.size();
 				break;
 
 			default:
diff --git a/engines/glk/archetype/token.cpp b/engines/glk/archetype/token.cpp
index 3be0e23..9d550a5 100644
--- a/engines/glk/archetype/token.cpp
+++ b/engines/glk/archetype/token.cpp
@@ -112,7 +112,7 @@ static int add_unique_str(XArrayType &the_xarray, const String &the_str) {
 
 	if (the_xarray.empty()) {
 		append_to_xarray(the_xarray, (void *)new_str);
-		return the_xarray.size() - 1;
+		return the_xarray.size();
 	} else {
 		i = 1;
 		while (index_xarray(the_xarray, i, p) && *((StringPtr)p) != the_str)
@@ -123,7 +123,7 @@ static int add_unique_str(XArrayType &the_xarray, const String &the_str) {
 			return i;
 		} else {
 			append_to_xarray(the_xarray, (void *)new_str);
-			return the_xarray.size() - 1;
+			return the_xarray.size();
 		}
 	}
 }
@@ -134,7 +134,7 @@ static int add_unique_str(XArrayType &the_xarray, const String &the_str) {
  */
 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;
+	return the_xarray.size();
 }
 
 bool get_token(progfile &f) {


Commit: 1b8e171944d5092a164205fd78d7ab8c1ed2587f
    https://github.com/scummvm/scummvm/commit/1b8e171944d5092a164205fd78d7ab8c1ed2587f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Janitorial

Changed paths:
    engines/glk/archetype/parser.cpp
    engines/glk/archetype/sys_object.cpp
    engines/glk/archetype/token.cpp


diff --git a/engines/glk/archetype/parser.cpp b/engines/glk/archetype/parser.cpp
index 8c1c47c..4867cbb 100644
--- a/engines/glk/archetype/parser.cpp
+++ b/engines/glk/archetype/parser.cpp
@@ -67,8 +67,7 @@ void normalize_string(const String &first, String &second) {
 				j = 0;
 				in_word = false;
 				second = second + " ";
-			}
-			else {
+			} else {
 				++i;
 			}
 
@@ -102,24 +101,25 @@ void add_parse_word(TargetListType which_list, String &the_word, int the_object)
 
 	the_word += '|';
 
-	do {
+	for (;;) {
 		bar = the_word.indexOf('|');
-		if (bar != -1) {
-			pp = new ParseType();
-			tempstr = the_word.left(bar - 1).left(g_vm->Abbreviate);
+		if (bar == -1)
+			break;
 
-			pp->word = NewConstStr(tempstr);
-			pp->word->toLowercase();
-			the_word = String(the_word.c_str() + bar + 1);
-			pp->object = the_object;
+		pp = new ParseType();
+		tempstr = the_word.left(bar).left(g_vm->Abbreviate);
 
-			np = new NodeType();
-			np->key = pp->word->size();
-			np->data = pp;
+		pp->word = NewConstStr(tempstr);
+		pp->word->toLowercase();
+		the_word = String(the_word.c_str() + bar + 1);
+		pp->object = the_object;
 
-			insert_item(the_list, np);
-		}
-	} while (bar != 0);
+		np = new NodeType();
+		np->key = pp->word->size();
+		np->data = pp;
+
+		insert_item(the_list, np);
+	}
 }
 
 static void parse_sentence_substitute(int start, ParsePtr pp, int &next_starting) {
@@ -235,8 +235,7 @@ bool pop_object(int &intback, String &strback) {
 			// parsed object
 			intback = ((int)g_vm->Command[1] << 8) | ((int)g_vm->Command[2]);
 			g_vm->Command.del(0, 4);
-		}
-		else {
+		} else {
 			intback = -1;
 			i = g_vm->Command.indexOf('%');
 			if (i < 0)
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index ebbe882..761d14e 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -127,8 +127,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 
 			if (convert_to(NUMERIC, result)) {
 				g_vm->Abbreviate = result._data._numeric.acl_int;
-			}
-			else {
+			} else {
 				wraperr("Warning: non-numeric abbreviation message sent to system");
 				cleanup(result);
 			}
@@ -155,8 +154,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 				default:
 					break;
 				}
-			}
-			else {
+			} else {
 				add_parse_word(target_list, strmsg, the_caller);
 			}
 
@@ -273,8 +271,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			if (stfile == nullptr) {
 				g_vm->writeln("Error opening %s", strmsg.c_str());
 				cleanup(result);
-			}
-			else {
+			} else {
 				save_game_state(stfile, g_vm->Object_List);
 				result._kind = RESERVED;
 				result._data._reserved.keyword = RW_TRUE;
diff --git a/engines/glk/archetype/token.cpp b/engines/glk/archetype/token.cpp
index 9d550a5..060dd1d 100644
--- a/engines/glk/archetype/token.cpp
+++ b/engines/glk/archetype/token.cpp
@@ -188,8 +188,7 @@ bool get_token(progfile &f) {
 				case ';':
 					if (!f.newlines) {
 						state = START;
-					}
-					else {
+					} else {
 						f.ttype = NEWLINE;
 						f.tnum = (int)NEWLINE_CH;
 						state = STOP;
@@ -379,8 +378,7 @@ void write_token(AclType the_type, int the_number) {
 	case IDENT:
 		if (the_number < 0) {
 			g_vm->write("an identifier");
-		}
-		else {
+		} 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);
@@ -411,8 +409,7 @@ void write_token(AclType the_type, int the_number) {
 		else if (index_xarray(g_vm->Literals, the_number, p)) {
 			str_ptr = (StringPtr)p;
 			g_vm->write("\"%s\"", str_ptr->c_str());
-		}
-		else {
+		} else {
 			g_vm->write("<text literal %d >: ", the_number);
 		}
 		break;


Commit: a8367cb808d632c9dbeef700d8c98bbebbbd1fff
    https://github.com/scummvm/scummvm/commit/a8367cb808d632c9dbeef700d8c98bbebbbd1fff
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Shift debug output to use ScummVM debug

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/misc.cpp
    engines/glk/archetype/sys_object.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index d17f0c7..572aea4 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -283,26 +283,27 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 		r._kind = IDENT;
 		r._data._ident.ident_kind = OBJECT_ID;
 		r._data._ident.ident_int = context.self;
-		wrapout(" : ", false);
+		
+		debugN(" : ");
 		display_result(r);
 
 		if (transport == OP_SEND)
-			wrapout(" sending ", false);
+			debugN(" sending ");
 		else
-			wrapout(" passing ", false);
+			debugN(" passing ");
 
 		if (index_xarray(Vocabulary, message_sent, p)) {
 			String str = String::format("'%s'", ((StringPtr)p)->c_str());
-			wrapout(str, false);
+			debugN(str.c_str());
 		}
 
 		if (transport == OP_SEND_TO_TYPE)
 			r._data._ident.ident_kind = TYPE_ID;
 
-		wrapout(" to ", false);
+		debugN(" to ");
 		r._data._ident.ident_int = recipient;
 		display_result(r);
-		wrapout("", true);
+		debug("");
 	}
 
 	// Trying to send a message to a destroyed object results in UNDEFINED
@@ -680,11 +681,11 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 		cleanup(r2);
 
 		if (DebugMan.isDebugChannelEnabled(DEBUG_EXPR)) {
-			wrapout(" -- ", false);
+			debugN(" -- ");
 			display_expr(the_expr);
-			wrapout("  ==>  ", false);
+			debugN("  ==>  ");
 			display_result(result);
-			wrapout("", true);
+			debug("");
 		}
 	} else {
 		switch (desired) {
@@ -732,7 +733,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 	verbose = DebugMan.isDebugChannelEnabled(DEBUG_STMT);
 
 	if (verbose)
-		wrapout(" == ", false);
+		debugN(" == ");
 
 	switch (the_stmt->_kind) {
 	case COMPOUND:
@@ -774,27 +775,27 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		if (verbose) {
 			switch (the_stmt->_kind) {
 			case ST_WRITE:
-				wrapout("write ", false);
+				debugN("write ");
 				break;
 			case ST_WRITES:
-				wrapout("writes ", false);
+				debugN("writes ");
 				break;
 			case ST_STOP:
-				wrapout("stop ", false);
+				debugN("stop ");
 				break;
 			default:
 				break;
 			}
 
-			wrapout(" ", false);
+			debugN(" ");
 			np = nullptr;
 			while (iterate_list(the_stmt->_data._write.print_list, np)) {
 				display_expr((ExprTree)np->data);
 				if (np->next != the_stmt->_data._write.print_list)
-					wrapout(", ", false);
+					debugN(", ");
 			}
 
-			wrapout("", true);
+			debug("");
 		}
 
 		np = nullptr;
@@ -816,28 +817,28 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 	case ST_IF:
 		if (verbose) {
-			wrapout("if: Testing ", false);
+			debugN("if: Testing ");
 			display_expr(the_stmt->_data._if.condition);
 		}
 		if (eval_condition(the_stmt->_data._if.condition, context)) {
 			if (verbose)
-				wrapout(" Evaluated true; executing then branch", true);
+				debug(" Evaluated true; executing then branch");
 			exec_stmt(the_stmt->_data._if.then_branch, result, context);
 
 		}
 		else if (the_stmt->_data._if.else_branch != nullptr) {
 			if (verbose)
-				wrapout(" Evaluated false; executing else branch", true);
+				debug(" Evaluated false; executing else branch");
 			exec_stmt(the_stmt->_data._if.else_branch, result, context);
 		}
 		break;
 
 	case ST_CASE:
 		if (verbose) {
-			wrapout("case ", false);
+			debugN("case ");
 			display_expr(the_stmt->_data._case.test_expr);
-			wrapout(" of", false);
-			wrapout("", true);
+			debugN(" of");
+			debug("");
 		}
 
 		eval_expr(the_stmt->_data._case.test_expr, r1, context, RVALUE);
@@ -957,7 +958,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 	}
 
 	if (verbose)
-		wrapout("", true);		// finish off dangling lines
+		debug("");		// finish off dangling lines
 }
 
 } // End of namespace Archetype
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index c88f584..0ace84e 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -352,16 +352,16 @@ void write_result(ResultType &result) {
 
 	undefine(r1);
 	if (result._kind == STR_PTR)
-		wrapout(*result._data._str.acl_str, false);
+		debugN(result._data._str.acl_str->c_str());
 	else if (result._kind == RESERVED)
-		wrapout(Reserved_Wds[result._data._reserved.keyword], false);
+		debugN(Reserved_Wds[result._data._reserved.keyword]);
 	else {
 		if (result._kind == ATTR_PTR)
 			copy_result(r1, *(ResultType *)result._data._attr.acl_attr->data);
 		else
 			copy_result(r1, result);
 		if (convert_to(STR_PTR, r1))
-			wrapout(*r1._data._str.acl_str, false);
+			debugN(r1._data._str.acl_str->c_str());
 		cleanup(r1);
 	}
 }
@@ -385,10 +385,10 @@ void display_result(ResultType &result) {
 	}
 
 	if (enclose != " ")
-		wrapout(enclose, false);
+		debugN(enclose.c_str());
 	write_result(result);
 	if (enclose != " ")
-		wrapout(enclose, false);
+		debugN(enclose.c_str());
 }
 
 void display_expr(ExprTree the_tree) {
@@ -396,15 +396,15 @@ void display_expr(ExprTree the_tree) {
 		display_result(*the_tree);
 	} else {
 		if (Binary[the_tree->_data._oper.op_name]) {
-			wrapout(" (", false);
+			debugN(" (");
 			display_expr(the_tree->_data._oper.left);
-			wrapout(") ", false);
+			debugN(") ");
 		}
 
 		wrapout(Operators[the_tree->_data._oper.op_name], false);
-		wrapout(" (", false);
+		debugN(" (");
 		display_expr(the_tree->_data._oper.right);
-		wrapout(") ", false);
+		debugN(") ");
 	}
 }
 
diff --git a/engines/glk/archetype/misc.cpp b/engines/glk/archetype/misc.cpp
index 3818e31..066c6c9 100644
--- a/engines/glk/archetype/misc.cpp
+++ b/engines/glk/archetype/misc.cpp
@@ -126,12 +126,9 @@ void add_bytes(int delta) {
 	Bytes += delta;
 
 	if (DebugMan.isDebugChannelEnabled(DEBUG_BYTES)) {
-		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 line = (delta >= 0) ? "Allocated   " : "Deallocated ";
+		line += String::format("%.3d bytes.  Current consumed memory: %.6d", ABS(delta), Bytes);
+		debug(line.c_str());
 	}
 }
 
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index 761d14e..9550671 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -254,9 +254,8 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			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);
+			//debug("Maximum memory request: %d bytes", MaxAvail);
+			//debug("Actual free memory:     %d bytes", MemAvail);
 			sys_state = IDLING;
 			break;
 


Commit: 38e5463601d7d8cb0687076adba488f8ea398d92
    https://github.com/scummvm/scummvm/commit/38e5463601d7d8cb0687076adba488f8ea398d92
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Further cleanup and debug reroute

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/semantic.cpp
    engines/glk/archetype/wrap.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 572aea4..8aeafa1 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -245,7 +245,7 @@ void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextTyp
 			result._kind = ATTR_PTR;
 			result._data._attr.acl_attr = np;
 		} else {
-			// inherited - must create new node }
+			// inherited - must create new node
 			result._kind = ATTR_PTR;
 			result._data._attr.acl_attr = new NodeType();
 
@@ -264,6 +264,8 @@ void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextTyp
 	}
 }
 
+static int scummvm = 0;
+
 bool Archetype::send_message(int transport, int message_sent, int recipient,
 		ResultType &result, ContextType &context) {
 	bool done, find_other;
@@ -280,6 +282,9 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 	}
 
 	if (DebugMan.isDebugChannelEnabled(DEBUG_MSGS)) {
+		++scummvm;
+		debugN(String::format("%d  ", scummvm).c_str());
+
 		r._kind = IDENT;
 		r._data._ident.ident_kind = OBJECT_ID;
 		r._data._ident.ident_int = context.self;
@@ -347,8 +352,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 						op = original;
 						find_other = true;
 					}
-				}
-				else if (index_xarray(Type_List, op->inherited_from, p)) {
+				} else if (index_xarray(Type_List, op->inherited_from, p)) {
 					op = (ObjectPtr)p;
 				} else {
 					wraperr("Internal error:  invalid inheritance");
@@ -396,7 +400,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			if (the_expr->_data._reserved.keyword == RW_READ)
 				result._data._str.acl_str = ReadLine(true);			// read full line
 			else
-				result._data._str.acl_str = ReadLine(false);			// read single key
+				result._data._str.acl_str = ReadLine(false);		// read single key
 
 			Rows = 0;
 			cursor_reset();				// user will have had to hit <RETURN>
@@ -549,7 +553,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			// 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". }
+			// ? 6 should produce a value in the range 1 - 6, not the character "6"
 		case OP_RANDOM:
 			eval_expr(the_expr->_data._oper.right, result, context, RVALUE);
 			if (result._kind == NUMERIC)
@@ -651,7 +655,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
 				result._kind = NUMERIC;
 				result._data._numeric.acl_int = r2._data._str.acl_str->indexOf(*r1._data._str.acl_str);
-				if (result._data._numeric.acl_int == -1)
+				if (result._data._numeric.acl_int == 0)
 					cleanup(result);
 			}
 			break;
diff --git a/engines/glk/archetype/semantic.cpp b/engines/glk/archetype/semantic.cpp
index 5f84ade..c7ed368 100644
--- a/engines/glk/archetype/semantic.cpp
+++ b/engines/glk/archetype/semantic.cpp
@@ -145,21 +145,21 @@ bool display_undefined() {
 
 	while (iterate_list(g_vm->Overlooked, np)) {
 		if (!exists) {
-			g_vm->writeln("The following identifiers were not explicitly defined.");
+			debugN("The following identifiers were not explicitly defined.");
 			exists = true;
 		}
 
 		ip = (IntegerPtr)np->data;
-		g_vm->write("Used %d", *ip);
+		debugN("Used %d", *ip);
 		if (*ip == 1)
-			g_vm->write(" time:   ");
+			debugN(" time:   ");
 		else
-			g_vm->write(" times:  ");
+			debugN(" times:  ");
 
 		if (index_ident(np->key, id_rec))
-			g_vm->writeln("%s", id_rec->id_name->c_str());
+			debug("%s", id_rec->id_name->c_str());
 		else
-			g_vm->writeln("<unknown identifier>");
+			debug("<unknown identifier>");
 
 		delete ip;
 	}
diff --git a/engines/glk/archetype/wrap.cpp b/engines/glk/archetype/wrap.cpp
index 44fddb8..ddc3850 100644
--- a/engines/glk/archetype/wrap.cpp
+++ b/engines/glk/archetype/wrap.cpp
@@ -51,7 +51,7 @@ static void wrap_wait() {
 	ch := ReadKey;
 	write(chr(13));
 	NormVideo;
-	ClrScr;				//or ClrEol if you don't want the whole screen }
+	ClrScr;				// or ClrEol if you don't want the whole screen
 	Rows : = 0
 #endif
 }
@@ -67,7 +67,7 @@ void wrapout(const String &str, bool terminate) {
 
 	// '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. }
+	// characters from the string that are actually written on this line
 	maxchars = MAXCOLS - cursor;
 
 	const char CHARS[7] = { '.', ',', ':', ';', ')', '-', '"' };


Commit: 5d8b8752e82abb91746c01dd61865d0ad6944e3d
    https://github.com/scummvm/scummvm/commit/5d8b8752e82abb91746c01dd61865d0ad6944e3d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Script handler fixes

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/string.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 8aeafa1..38eb83f 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -433,6 +433,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			break;
 
 		default:
+			result = *the_expr;
 			break;
 		}
 	} else if (the_expr->_kind == OPER) {
@@ -645,7 +646,8 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 				if (the_expr->_data._oper.op_name == OP_LEFTFROM)
 					result._data._str.acl_str = MakeNewDynStr(r1._data._str.acl_str->left(r2._data._numeric.acl_int));
 				else
-					result._data._str.acl_str = MakeNewDynStr(r1._data._str.acl_str->right(r2._data._numeric.acl_int));
+					result._data._str.acl_str = MakeNewDynStr(r1._data._str.acl_str->right(
+						r1._data._str.acl_str->size() - r2._data._numeric.acl_int + 1));
 			}
 			break;
 
@@ -654,7 +656,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			eval_expr(the_expr->_data._oper.right, r2, context, RVALUE);
 			if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
 				result._kind = NUMERIC;
-				result._data._numeric.acl_int = r2._data._str.acl_str->indexOf(*r1._data._str.acl_str);
+				result._data._numeric.acl_int = r2._data._str.acl_str->indexOf(*r1._data._str.acl_str) + 1;
 				if (result._data._numeric.acl_int == 0)
 					cleanup(result);
 			}
@@ -712,8 +714,11 @@ bool Archetype::eval_condition(ExprTree the_expr, ContextType &context) {
 	undefine(result);
 	eval_expr(the_expr, result, context, RVALUE);
 
-	failure = (result._kind == RESERVED) && (result._data._reserved.keyword = RW_UNDEFINED
-		|| result._data._reserved.keyword == RW_FALSE || result._data._reserved.keyword == RW_ABSENT);
+	failure = (result._kind == RESERVED) && (
+		result._data._reserved.keyword == RW_UNDEFINED ||
+		result._data._reserved.keyword == RW_FALSE ||
+		result._data._reserved.keyword == RW_ABSENT
+	);
 
 	cleanup(result);
 	return !failure;
@@ -767,6 +772,8 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		case MESSAGE:
 			b = send_message(OP_PASS, the_stmt->_data._expr.expression->_data._msgTextQuote.index,
 				context.self, result, context);
+			break;
+
 		default:
 			eval_expr(the_stmt->_data._expr.expression, result, context, RVALUE);
 			break;
@@ -811,8 +818,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 
 		if (the_stmt->_kind == ST_WRITE) {
 			wrapout("", true);
-		}
-		else if (the_stmt->_kind == ST_STOP) {
+		} else if (the_stmt->_kind == ST_STOP) {
 			g_vm->writeln();
 			g_vm->writeln();
 			error("%f", VERSION_NUM);
@@ -826,13 +832,12 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		}
 		if (eval_condition(the_stmt->_data._if.condition, context)) {
 			if (verbose)
-				debug(" Evaluated true; executing then branch");
+				debug(" Evaluated TRUE; executing then branch");
 			exec_stmt(the_stmt->_data._if.then_branch, result, context);
 
-		}
-		else if (the_stmt->_data._if.else_branch != nullptr) {
+		} else if (the_stmt->_data._if.else_branch != nullptr) {
 			if (verbose)
-				debug(" Evaluated false; executing else branch");
+				debug(" Evaluated FALSE; executing else branch");
 			exec_stmt(the_stmt->_data._if.else_branch, result, context);
 		}
 		break;
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index 0ace84e..a9ee1bb 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -295,6 +295,7 @@ bool result_compare(short comparison, ResultType &r1, ResultType &r2) {
 			default:
 				break;
 			}
+			break;
 
 		case IDENT:
 			if (r1._data._ident.ident_kind == r2._data._ident.ident_kind) {
@@ -380,8 +381,10 @@ void display_result(ResultType &result) {
 		break;
 	case MESSAGE:
 		enclose = "\'";
+		break;
 	default:
 		enclose = ' ';
+		break;
 	}
 
 	if (enclose != " ")
@@ -401,7 +404,7 @@ void display_expr(ExprTree the_tree) {
 			debugN(") ");
 		}
 
-		wrapout(Operators[the_tree->_data._oper.op_name], false);
+		debugN(Operators[the_tree->_data._oper.op_name]);
 		debugN(" (");
 		display_expr(the_tree->_data._oper.right);
 		debugN(") ");
diff --git a/engines/glk/archetype/string.cpp b/engines/glk/archetype/string.cpp
index 4db4688..9295ced 100644
--- a/engines/glk/archetype/string.cpp
+++ b/engines/glk/archetype/string.cpp
@@ -98,10 +98,30 @@ String String::vformat(const char *fmt, va_list args) {
 }
 
 int String::val(int *code) {
+	const char *srcP = c_str();
+	int result = 0, sign = 0, idx = 1;
+
+	if (*srcP == '-') {
+		sign = -1;
+		++srcP;
+		++idx;
+	}
+
+	for (; *srcP; ++srcP, ++idx) {
+		if (*srcP < '0' || *srcP > '9') {
+			// Invalid character
+			if (code)
+				*code = idx;
+			return result;
+		}
+
+		result = (result * 10) + (*srcP - '0');
+	}
+
 	if (code)
 		*code = 0;
 
-	return atoi(c_str());
+	return result;
 }
 
 String String::left(size_t count) const {


Commit: 86e7717e62b11e1f35145d14d55e012379a40e50
    https://github.com/scummvm/scummvm/commit/86e7717e62b11e1f35145d14d55e012379a40e50
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Cleanup and fixes for text display

Changed paths:
  R engines/glk/archetype/wrap.cpp
  R engines/glk/archetype/wrap.h
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/archetype.h
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/sys_object.cpp
    engines/glk/module.mk


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 38eb83f..4ea197e 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -30,7 +30,6 @@
 #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 {
@@ -142,7 +141,7 @@ void Archetype::writeln(const String fmt, ...) {
 	glk_put_buffer(s.c_str(), s.size());
 }
 
-void Archetype::readln(String &s) {
+String Archetype::readLine() {
 	event_t ev;
 	char buffer[MAX_INPUT_LINE + 1];
 
@@ -152,19 +151,30 @@ void Archetype::readln(String &s) {
 		glk_select(&ev);
 		if (ev.type == evtype_Quit) {
 			glk_cancel_line_event(_mainWindow, &ev);
-			return;
+			return "";
 		} else if (ev.type == evtype_LineInput)
 			break;
 	} while (ev.type != evtype_Quit);
 
 	buffer[ev.val1] = 0;
 
-	s = String(buffer);
+	return String(buffer);
 }
 
-char Archetype::ReadKey() {
-	// TODO
-	return '\0';
+char Archetype::readKey() {
+	glk_request_char_event(_mainWindow);
+
+	event_t ev;
+	while (ev.type != evtype_CharInput) {
+		glk_select(&ev);
+
+		if (ev.type == evtype_Quit) {
+			glk_cancel_char_event(_mainWindow);
+			return '\0';
+		}
+	}
+
+	return (char)ev.val1;
 }
 
 void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextType &context,
@@ -355,7 +365,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 				} else if (index_xarray(Type_List, op->inherited_from, p)) {
 					op = (ObjectPtr)p;
 				} else {
-					wraperr("Internal error:  invalid inheritance");
+					error("Internal error:  invalid inheritance");
 					return false;
 				}
 			}
@@ -398,12 +408,12 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 		case RW_KEY:
 			result._kind = STR_PTR;
 			if (the_expr->_data._reserved.keyword == RW_READ)
-				result._data._str.acl_str = ReadLine(true);			// read full line
-			else
-				result._data._str.acl_str = ReadLine(false);		// read single key
-
-			Rows = 0;
-			cursor_reset();				// user will have had to hit <RETURN>
+				result._data._str.acl_str = MakeNewDynStr(readLine());	// read full line
+			else {
+				String s;
+				s += readKey();
+				result._data._str.acl_str = MakeNewDynStr(s);	// read single key
+			}
 			break;
 
 		case RW_MESSAGE:
@@ -753,6 +763,9 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 			exec_stmt((StatementPtr)np->data, result, context);
 
 			b = (result._kind == RESERVED) && (result._data._reserved.keyword == RW_BREAK);
+
+			if (shouldQuit())
+				return;
 		}
 		break;
 
@@ -765,7 +778,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 			if (index_xarray(Literals, the_stmt->_data._expr.expression->_data._msgTextQuote.index, p)) {
 				result._kind = TEXT_LIT;
 				result._data._msgTextQuote.index = the_stmt->_data._expr.expression->_data._msgTextQuote.index;
-				wrapout(*((StringPtr)p), true);
+				writeln(*((StringPtr)p));
 			}
 			break;
 
@@ -817,7 +830,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		}
 
 		if (the_stmt->_kind == ST_WRITE) {
-			wrapout("", true);
+			g_vm->writeln();
 		} else if (the_stmt->_kind == ST_STOP) {
 			g_vm->writeln();
 			g_vm->writeln();
@@ -900,6 +913,9 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 			exec_stmt(the_stmt->_data._loop.action, result, context);
 			b = (result._kind == RESERVED) && (result._data._reserved.keyword == RW_BREAK);
 			cleanup(result);
+
+			if (shouldQuit())
+				return;
 		}
 		break;
 
@@ -955,14 +971,14 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 			if (result._data._ident.ident_int == (int)Object_List.size())
 				shrink_xarray(Object_List);
 		} else {
-			wraperr("Can only destroy previously created objects");
+			error("Can only destroy previously created objects");
 		}
 
 		cleanup(result);
 		break;
 
 	default:
-		wraperr("Internal error:  statement not supported yet");
+		error("Internal error:  statement not supported yet");
 		break;
 	}
 
diff --git a/engines/glk/archetype/archetype.h b/engines/glk/archetype/archetype.h
index 8df78e5..a54fa17 100644
--- a/engines/glk/archetype/archetype.h
+++ b/engines/glk/archetype/archetype.h
@@ -173,9 +173,15 @@ public:
 	void writeln(const String fmt, ...);
 	void writeln() { writeln(""); }
 
-	void readln(String &s);
+	/**
+	 * Read an input line typed by the player
+	 */
+	String readLine();
 
-	char ReadKey();
+	/**
+	 * Read in a single key
+	 */
+	char readKey();
 };
 
 extern Archetype *g_vm;
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index a9ee1bb..15328a1 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -25,7 +25,6 @@
 #include "glk/archetype/misc.h"
 #include "glk/archetype/saveload.h"
 #include "glk/archetype/timestamp.h"
-#include "glk/archetype/wrap.h"
 
 namespace Glk {
 namespace Archetype {
@@ -325,7 +324,7 @@ bool assignment(ResultType &target, ResultType &value) {
 	ExprPtr e;
 
 	if (target._kind != ATTR_PTR) {
-		wraperr("Warning: attempted assignment to a non-attribute");
+		error("Warning: attempted assignment to a non-attribute");
 		return false;
 	} else {
 		e = (ExprPtr)target._data._attr.acl_attr->data;
@@ -377,7 +376,7 @@ void display_result(ResultType &result) {
 		break;
 	case QUOTE_LIT:
 		enclose = " ";
-		wrapout(">>", false);
+		debugN(">>");
 		break;
 	case MESSAGE:
 		enclose = "\'";
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index 9550671..233fde1 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -25,7 +25,6 @@
 #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/debug-channels.h"
 #include "common/savefile.h"
@@ -128,7 +127,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			if (convert_to(NUMERIC, result)) {
 				g_vm->Abbreviate = result._data._numeric.acl_int;
 			} else {
-				wraperr("Warning: non-numeric abbreviation message sent to system");
+				error("Warning: non-numeric abbreviation message sent to system");
 				cleanup(result);
 			}
 			sys_state = IDLING;
diff --git a/engines/glk/archetype/wrap.cpp b/engines/glk/archetype/wrap.cpp
deleted file mode 100644
index ddc3850..0000000
--- a/engines/glk/archetype/wrap.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/* 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.empty() && 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
deleted file mode 100644
index 7195303..0000000
--- a/engines/glk/archetype/wrap.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* 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 8915c66..1a5bdea 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -164,8 +164,7 @@ MODULE_OBJS += \
 	archetype/string.o \
 	archetype/sys_object.o \
 	archetype/timestamp.o \
-	archetype/token.o \
-	archetype/wrap.o
+	archetype/token.o
 endif
 
 ifdef ENABLE_GLK_FROTZ


Commit: 19cc1221f3433a6178e7e0286c4b96e8359d4390
    https://github.com/scummvm/scummvm/commit/19cc1221f3433a6178e7e0286c4b96e8359d4390
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Engine fixes for input line processing

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/parser.cpp
    engines/glk/archetype/sys_object.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 4ea197e..3792938 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -293,7 +293,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 
 	if (DebugMan.isDebugChannelEnabled(DEBUG_MSGS)) {
 		++scummvm;
-		debugN(String::format("%d  ", scummvm).c_str());
+		//debugN(String::format("%d  ", scummvm).c_str());
 
 		r._kind = IDENT;
 		r._data._ident.ident_kind = OBJECT_ID;
diff --git a/engines/glk/archetype/parser.cpp b/engines/glk/archetype/parser.cpp
index 4867cbb..0d2dcbb 100644
--- a/engines/glk/archetype/parser.cpp
+++ b/engines/glk/archetype/parser.cpp
@@ -128,8 +128,10 @@ static void parse_sentence_substitute(int start, ParsePtr pp, int &next_starting
 	if (sublen > g_vm->Abbreviate)
 		sublen = g_vm->Abbreviate;
 
+	// WORKAROUND: Original encoded object number as two bytes. ScummVM strings don't like
+	// 0 bytes in the middle of the string, so we encode it as plain text
 	g_vm->Command = g_vm->Command.left(start)
-		+ String::format("%%%c%c^", pp->object >> 8, pp->object & 0xff)
+		+ String::format("%%%d^", pp->object)
 		+ String(g_vm->Command.c_str() + start + sublen + 1);
 
 	next_starting = next_starting - sublen + 4;
@@ -163,7 +165,7 @@ static bool parse_sentence_next_chunk(int &start_at, String &the_chunk, int &nex
 void parse_sentence() {
 	const int nfillers = 3;
 	const char *const FILTERS[nfillers] = { " a ", " an ", " the " };
-	int next_starting;
+	int next_starting = 0;
 	String s;
 	NodePtr np, near_match, far_match;
 	ParsePtr pp;
@@ -233,8 +235,9 @@ bool pop_object(int &intback, String &strback) {
 	} 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);
+			int nextPos = -1;
+			intback = String(g_vm->Command.c_str() + 1).val(&nextPos);
+			g_vm->Command = String(g_vm->Command.c_str() + nextPos + 1);
 		} else {
 			intback = -1;
 			i = g_vm->Command.indexOf('%');
diff --git a/engines/glk/archetype/sys_object.cpp b/engines/glk/archetype/sys_object.cpp
index 233fde1..ad98afb 100644
--- a/engines/glk/archetype/sys_object.cpp
+++ b/engines/glk/archetype/sys_object.cpp
@@ -108,6 +108,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 					break;
 				}
 			}
+			break;
 
 		case PLAYER_CMD:
 			normalize_string(strmsg, g_vm->Command);
@@ -119,6 +120,7 @@ void send_to_system(int transport, String &strmsg, ResultType &result, ContextTy
 			result._kind = STR_PTR;
 			result._data._str.acl_str = NewDynStr(g_vm->Command);
 			sys_state = IDLING;
+			break;
 
 		case ABBR:
 			result._kind = STR_PTR;


Commit: ab5939ba443b31cbecb727825346a331a84856a7
    https://github.com/scummvm/scummvm/commit/ab5939ba443b31cbecb727825346a331a84856a7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Fix quitting the game

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/interpreter.h


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 3792938..395edd6 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -125,7 +125,7 @@ void Archetype::interpret() {
 void Archetype::write(const String fmt, ...) {
 	va_list ap;
 	va_start(ap, fmt);
-	Common::String s = Common::String::format(fmt.c_str(), ap);
+	Common::String s = Common::String::vformat(fmt.c_str(), ap);
 	va_end(ap);
 
 	glk_put_buffer(s.c_str(), s.size());
@@ -134,7 +134,7 @@ void Archetype::write(const String fmt, ...) {
 void Archetype::writeln(const String fmt, ...) {
 	va_list ap;
 	va_start(ap, fmt);
-	Common::String s = Common::String::format(fmt.c_str(), ap);
+	Common::String s = Common::String::vformat(fmt.c_str(), ap);
 	va_end(ap);
 
 	s += '\n';
@@ -826,7 +826,8 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		while (iterate_list(the_stmt->_data._write.print_list, np)) {
 			cleanup(result);
 			eval_expr((ExprTree)np->data, result, context, RVALUE);
-			write_result(result);
+			String line = get_result_string(result);
+			g_vm->write("%s", line.c_str());
 		}
 
 		if (the_stmt->_kind == ST_WRITE) {
@@ -834,7 +835,8 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		} else if (the_stmt->_kind == ST_STOP) {
 			g_vm->writeln();
 			g_vm->writeln();
-			error("%f", VERSION_NUM);
+			g_vm->writeln("%f", VERSION_NUM);
+			g_vm->quitGame();
 		}
 		break;
 
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index 15328a1..654a301 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -347,23 +347,30 @@ bool assignment(ResultType &target, ResultType &value) {
 	return true;
 }
 
-void write_result(ResultType &result) {
+String get_result_string(ResultType &result) {
 	ResultType r1;
+	String str;
 
 	undefine(r1);
 	if (result._kind == STR_PTR)
-		debugN(result._data._str.acl_str->c_str());
+		str = result._data._str.acl_str->c_str();
 	else if (result._kind == RESERVED)
-		debugN(Reserved_Wds[result._data._reserved.keyword]);
+		str = Reserved_Wds[result._data._reserved.keyword];
 	else {
 		if (result._kind == ATTR_PTR)
 			copy_result(r1, *(ResultType *)result._data._attr.acl_attr->data);
 		else
 			copy_result(r1, result);
 		if (convert_to(STR_PTR, r1))
-			debugN(r1._data._str.acl_str->c_str());
+			str = r1._data._str.acl_str->c_str();
 		cleanup(r1);
 	}
+
+	return str;
+}
+
+void write_result(ResultType &result) {
+	debugN("%s", get_result_string(result).c_str());
 }
 
 void display_result(ResultType &result) {
diff --git a/engines/glk/archetype/interpreter.h b/engines/glk/archetype/interpreter.h
index 331cd7a..2b5c8d8 100644
--- a/engines/glk/archetype/interpreter.h
+++ b/engines/glk/archetype/interpreter.h
@@ -102,6 +102,11 @@ extern bool result_compare(short comparison, ResultType &r1, ResultType &r2);
 extern bool assignment(ResultType &target, ResultType &value);
 
 /**
+ * Gets a textual version of a passed result
+ */
+extern String get_result_string(ResultType &result);
+
+/**
  * Writes the given result to screen w/o terminating it with a newline
  */
 extern void write_result(ResultType &result);


Commit: f3b02a115fbacfffb76065df881d5cbaa28c17da
    https://github.com/scummvm/scummvm/commit/f3b02a115fbacfffb76065df881d5cbaa28c17da
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Cleaner exit when game is quit

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/glk.cpp
    engines/glk/glk.h


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index 395edd6..f10fa06 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -833,9 +833,8 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		if (the_stmt->_kind == ST_WRITE) {
 			g_vm->writeln();
 		} else if (the_stmt->_kind == ST_STOP) {
+			// End the game
 			g_vm->writeln();
-			g_vm->writeln();
-			g_vm->writeln("%f", VERSION_NUM);
 			g_vm->quitGame();
 		}
 		break;
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index b546299..5af9983 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -46,7 +46,7 @@ namespace Glk {
 GlkEngine *g_vm;
 
 GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc) :
-		_gameDescription(gameDesc), Engine(syst), _random("Glk"), _blorb(nullptr),
+		_gameDescription(gameDesc), Engine(syst), _random("Glk"), _quitFlag(false), _blorb(nullptr),
 		_clipboard(nullptr), _conf(nullptr), _debugger(nullptr), _events(nullptr), _pictures(nullptr),
 		_screen(nullptr), _selection(nullptr), _sounds(nullptr), _streams(nullptr), _windows(nullptr),
 		_copySelect(false), _terminated(false), _pcSpeaker(nullptr), _loadSaveSlot(-1),
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 79e5425..8daa7ce 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -80,6 +80,7 @@ protected:
 	int _loadSaveSlot;
 	Common::File _gameFile;
 	PCSpeaker *_pcSpeaker;
+	bool _quitFlag;
 
 	// Engine APIs
 	virtual Common::Error run();
@@ -241,6 +242,21 @@ public:
 	 * Set a random number seed
 	 */
 	void setRandomNumberSeed(uint seed) { _random.setSeed(seed); }
+
+	/**
+	 * Flags to quit the game
+	 */
+	void quitGame() {
+		_quitFlag = true;
+		Engine::quitGame();
+	}
+
+	/**
+	 * Returns true if the game should be quit
+	 */
+	bool shouldQuit() const {
+		return _quitFlag || Engine::shouldQuit();
+	}
 };
 
 extern GlkEngine *g_vm;


Commit: 75def2b4808fae0aef52bb5ba165a9d10936dd05
    https://github.com/scummvm/scummvm/commit/75def2b4808fae0aef52bb5ba165a9d10936dd05
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: Fixes to correctly parse multi-word inputs

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/parser.cpp
    engines/glk/archetype/string.cpp
    engines/glk/archetype/string.h


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index f10fa06..cf22afc 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -274,7 +274,7 @@ void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextTyp
 	}
 }
 
-static int scummvm = 0;
+static int messageCtr = 0;
 
 bool Archetype::send_message(int transport, int message_sent, int recipient,
 		ResultType &result, ContextType &context) {
@@ -292,8 +292,8 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 	}
 
 	if (DebugMan.isDebugChannelEnabled(DEBUG_MSGS)) {
-		++scummvm;
-		//debugN(String::format("%d  ", scummvm).c_str());
+		++messageCtr;
+		//debugN(String::format("%d  ", messageCtr).c_str());
 
 		r._kind = IDENT;
 		r._data._ident.ident_kind = OBJECT_ID;
diff --git a/engines/glk/archetype/parser.cpp b/engines/glk/archetype/parser.cpp
index 0d2dcbb..49cd8f1 100644
--- a/engines/glk/archetype/parser.cpp
+++ b/engines/glk/archetype/parser.cpp
@@ -131,8 +131,8 @@ static void parse_sentence_substitute(int start, ParsePtr pp, int &next_starting
 	// WORKAROUND: Original encoded object number as two bytes. ScummVM strings don't like
 	// 0 bytes in the middle of the string, so we encode it as plain text
 	g_vm->Command = g_vm->Command.left(start)
-		+ String::format("%%%d^", pp->object)
-		+ String(g_vm->Command.c_str() + start + sublen + 1);
+		+ String::format(" %%%d^", pp->object)
+		+ g_vm->Command.mid(start + sublen + 1);
 
 	next_starting = next_starting - sublen + 4;
 }
@@ -140,7 +140,7 @@ static void parse_sentence_substitute(int start, ParsePtr pp, int &next_starting
 static bool parse_sentence_next_chunk(int &start_at, String &the_chunk, int &next_starting) {
 	int i;
 
-	if (next_starting == 0) {
+	if (next_starting == -1) {
 		return false;
 	} else {
 		do {
@@ -149,14 +149,16 @@ static bool parse_sentence_next_chunk(int &start_at, String &the_chunk, int &nex
 
 			i = the_chunk.indexOf('%');
 			if (i == -1) {
-				next_starting = 0;
+				next_starting = -1;
 			} else {
-				the_chunk = the_chunk.left(i - 1);
-				next_starting = next_starting + i + 3;
+				next_starting = the_chunk.indexOf("^", i) + 1;
+				assert(next_starting != 0);
+
+				the_chunk = the_chunk.left(i);
 			}
 
 			the_chunk.trim();
-		} while (!(next_starting == 0 || !the_chunk.empty()));
+		} while (!(next_starting == -1 || !the_chunk.empty()));
 
 		return !the_chunk.empty();
 	}
@@ -197,10 +199,10 @@ void parse_sentence() {
 
 	// 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;
+	next_starting = 0;
 
 	while (parse_sentence_next_chunk(i, s, next_starting)) {
-		lchunk = s.size() - 1;
+		lchunk = s.size();
 
 		np = find_item(g_vm->object_names, lchunk);
 		if (np != nullptr) {
@@ -215,7 +217,7 @@ void parse_sentence() {
 					else
 						far_match = np;
 				}
-			} while (!(iterate_list(g_vm->object_names, np) && (lchunk == (int)((ParsePtr)np->data)->word->size())));
+			} while (iterate_list(g_vm->object_names, np) && (lchunk = (int)((ParsePtr)np->data)->word->size()) != 0);
 
 			if (near_match != nullptr)
 				parse_sentence_substitute(i, (ParsePtr)near_match->data, next_starting);
diff --git a/engines/glk/archetype/string.cpp b/engines/glk/archetype/string.cpp
index 9295ced..d20a1c9 100644
--- a/engines/glk/archetype/string.cpp
+++ b/engines/glk/archetype/string.cpp
@@ -36,6 +36,11 @@ int String::indexOf(const String &substr) const {
 	return c ? c - c_str() : -1;
 }
 
+int String::indexOf(const String &substr, int start) const {
+	const char *c = strstr(c_str() + start, 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)
@@ -49,6 +54,9 @@ void String::trim() {
 	while (!empty() && (lastChar() == ' ' || lastChar() == '\t' || lastChar() == '\n'
 			|| lastChar() == '\r'))
 		deleteLastChar();
+
+	while (hasPrefix(" ") || hasPrefix("\t") || hasPrefix("\n"))
+		deleteChar(0);
 }
 
 String operator+(const String &x, const String &y) {
diff --git a/engines/glk/archetype/string.h b/engines/glk/archetype/string.h
index f85ed37..5afa4d6 100644
--- a/engines/glk/archetype/string.h
+++ b/engines/glk/archetype/string.h
@@ -77,6 +77,11 @@ public:
 	int indexOf(const String &substr) const;
 
 	/**
+	 * Returns the index of a substring within this string starting at a given index
+	 */
+	int indexOf(const String &substr, int start) const;
+
+	/**
 	 * Returns the last index of a character in a string, or -1 if it isn't present
 	 */
 	int lastIndexOf(char c) const;


Commit: 7ca72705320760450d9afc32fc5659b0f661d064
    https://github.com/scummvm/scummvm/commit/7ca72705320760450d9afc32fc5659b0f661d064
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-11T18:20:30-08:00

Commit Message:
GLK: ARCHETYPE: gcc compilation fixes

Changed paths:
    engines/glk/archetype/archetype.cpp
    engines/glk/archetype/interpreter.cpp
    engines/glk/archetype/misc.cpp
    engines/glk/archetype/string.cpp


diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index cf22afc..b5ef41f 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -309,7 +309,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 
 		if (index_xarray(Vocabulary, message_sent, p)) {
 			String str = String::format("'%s'", ((StringPtr)p)->c_str());
-			debugN(str.c_str());
+			debugN("%s", str.c_str());
 		}
 
 		if (transport == OP_SEND_TO_TYPE)
@@ -318,7 +318,7 @@ bool Archetype::send_message(int transport, int message_sent, int recipient,
 		debugN(" to ");
 		r._data._ident.ident_int = recipient;
 		display_result(r);
-		debug("");
+		debug("%s", "");
 	}
 
 	// Trying to send a message to a destroyed object results in UNDEFINED
@@ -384,7 +384,6 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 	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
@@ -463,10 +462,10 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 
 				} else if (convert_to(MESSAGE, r1)) {
 					if (r2._data._ident.ident_kind == TYPE_ID)
-						b = send_message(OP_SEND_TO_TYPE, r1._data._msgTextQuote.index, r2._data._ident.ident_int,
+						send_message(OP_SEND_TO_TYPE, r1._data._msgTextQuote.index, r2._data._ident.ident_int,
 							result, context);
 					else
-						b = send_message(the_expr->_data._oper.op_name, r1._data._msgTextQuote.index,
+						send_message(the_expr->_data._oper.op_name, r1._data._msgTextQuote.index,
 							r2._data._ident.ident_int, result, context);
 				}
 			}
@@ -701,7 +700,7 @@ void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &co
 			display_expr(the_expr);
 			debugN("  ==>  ");
 			display_result(result);
-			debug("");
+			debug("%s", "");
 		}
 	} else {
 		switch (desired) {
@@ -819,7 +818,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 					debugN(", ");
 			}
 
-			debug("");
+			debug("%s", "");
 		}
 
 		np = nullptr;
@@ -860,8 +859,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 		if (verbose) {
 			debugN("case ");
 			display_expr(the_stmt->_data._case.test_expr);
-			debugN(" of");
-			debug("");
+			debug(" of");
 		}
 
 		eval_expr(the_stmt->_data._case.test_expr, r1, context, RVALUE);
@@ -984,7 +982,7 @@ void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType
 	}
 
 	if (verbose)
-		debug("");		// finish off dangling lines
+		debug("%s", "");		// finish off dangling lines
 }
 
 } // End of namespace Archetype
diff --git a/engines/glk/archetype/interpreter.cpp b/engines/glk/archetype/interpreter.cpp
index 654a301..485a408 100644
--- a/engines/glk/archetype/interpreter.cpp
+++ b/engines/glk/archetype/interpreter.cpp
@@ -394,10 +394,10 @@ void display_result(ResultType &result) {
 	}
 
 	if (enclose != " ")
-		debugN(enclose.c_str());
+		debugN("%s", enclose.c_str());
 	write_result(result);
 	if (enclose != " ")
-		debugN(enclose.c_str());
+		debugN("%s", enclose.c_str());
 }
 
 void display_expr(ExprTree the_tree) {
@@ -410,7 +410,7 @@ void display_expr(ExprTree the_tree) {
 			debugN(") ");
 		}
 
-		debugN(Operators[the_tree->_data._oper.op_name]);
+		debugN("%s", Operators[the_tree->_data._oper.op_name]);
 		debugN(" (");
 		display_expr(the_tree->_data._oper.right);
 		debugN(") ");
diff --git a/engines/glk/archetype/misc.cpp b/engines/glk/archetype/misc.cpp
index 066c6c9..f2c0c78 100644
--- a/engines/glk/archetype/misc.cpp
+++ b/engines/glk/archetype/misc.cpp
@@ -127,8 +127,8 @@ void add_bytes(int delta) {
 
 	if (DebugMan.isDebugChannelEnabled(DEBUG_BYTES)) {
 		String line = (delta >= 0) ? "Allocated   " : "Deallocated ";
-		line += String::format("%.3d bytes.  Current consumed memory: %.6d", ABS(delta), Bytes);
-		debug(line.c_str());
+		line += String::format("%.3u bytes.  Current consumed memory: %.6u", (uint)ABS(delta), (uint)Bytes);
+		debug("%s", line.c_str());
 	}
 }
 
diff --git a/engines/glk/archetype/string.cpp b/engines/glk/archetype/string.cpp
index d20a1c9..849affa 100644
--- a/engines/glk/archetype/string.cpp
+++ b/engines/glk/archetype/string.cpp
@@ -129,7 +129,7 @@ int String::val(int *code) {
 	if (code)
 		*code = 0;
 
-	return result;
+	return sign * result;
 }
 
 String String::left(size_t count) const {





More information about the Scummvm-git-logs mailing list