[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