aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/alan2
diff options
context:
space:
mode:
authorFilippos Karapetis2018-12-27 14:48:11 +0200
committerFilippos Karapetis2018-12-28 13:41:39 +0200
commit91490c27adfde5f41b21bb2f3a6f648aab4270d0 (patch)
tree9af66c83e768a1219c7164fcbf70e3e66a35be8f /engines/glk/alan2
parentbba8eadfaef76365429249368ca8e6d1385dc3e8 (diff)
downloadscummvm-rg350-91490c27adfde5f41b21bb2f3a6f648aab4270d0.tar.gz
scummvm-rg350-91490c27adfde5f41b21bb2f3a6f648aab4270d0.tar.bz2
scummvm-rg350-91490c27adfde5f41b21bb2f3a6f648aab4270d0.zip
GLK: ALAN2: Initial work on the Alan2 subengine
Diffstat (limited to 'engines/glk/alan2')
-rw-r--r--engines/glk/alan2/acode.h289
-rw-r--r--engines/glk/alan2/alan2.cpp41
-rw-r--r--engines/glk/alan2/alan2.h67
-rw-r--r--engines/glk/alan2/decode.cpp124
-rw-r--r--engines/glk/alan2/decode.h70
-rw-r--r--engines/glk/alan2/execute.cpp1065
-rw-r--r--engines/glk/alan2/execute.h134
-rw-r--r--engines/glk/alan2/interpreter.cpp768
-rw-r--r--engines/glk/alan2/interpreter.h59
-rw-r--r--engines/glk/alan2/parse.cpp976
-rw-r--r--engines/glk/alan2/parse.h112
-rw-r--r--engines/glk/alan2/rules.cpp87
-rw-r--r--engines/glk/alan2/rules.h51
-rw-r--r--engines/glk/alan2/saveload.cpp218
-rw-r--r--engines/glk/alan2/saveload.h55
-rw-r--r--engines/glk/alan2/types.h271
-rw-r--r--engines/glk/alan2/util.h88
17 files changed, 4475 insertions, 0 deletions
diff --git a/engines/glk/alan2/acode.h b/engines/glk/alan2/acode.h
new file mode 100644
index 0000000000..764eb1967b
--- /dev/null
+++ b/engines/glk/alan2/acode.h
@@ -0,0 +1,289 @@
+/* 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 GLK_ALAN2_ACODE
+#define GLK_ALAN2_ACODE
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Alan2 {
+
+typedef size_t Aptr; // Type for an ACODE memory address
+typedef uint32 Aword; // Type for an ACODE word
+typedef uint32 Aaddr; // Type for an ACODE address
+typedef uint32 Abool; // Type for an ACODE Boolean value
+typedef int32 Aint; // Type for an ACODE Integer value
+typedef int CodeValue; // Definition for the packing process
+
+// Constants for the Acode file, words/block & bytes/block
+#define BLOCKLEN 256L
+#define BLOCKSIZE (BLOCKLEN*sizeof(Aword))
+
+
+// Definitions for the packing process
+#define VALUEBITS 16
+
+#define EOFChar 256
+#define TOPVALUE (((CodeValue)1<<VALUEBITS) - 1) // Highest value possible
+
+// Half and quarter points in the code value range
+#define ONEQUARTER (TOPVALUE/4+1) // Point after first quarter
+#define HALF (2*ONEQUARTER) // Point after first half
+#define THREEQUARTER (3*ONEQUARTER) // Point after third quarter
+
+
+// AMACHINE Word Classes
+typedef int WrdKind;
+#define WRD_SYN 0 // 1 - Synonym
+#define WRD_ADJ 1 // 2 - Adjective
+#define WRD_ALL 2 // 4 - All
+#define WRD_BUT 3 // 8 - But
+#define WRD_CONJ 4 // 16 - Conjunction
+#define WRD_PREP 5 // 32 - Preposition
+#define WRD_DIR 6 // 64 - Direction
+#define WRD_IT 7 // 128 - It
+#define WRD_NOISE 8 // 256 - Noise word
+#define WRD_NOUN 9 // 512 - Noun
+#define WRD_ACT 10 // 1024 - Actor
+#define WRD_THEM 11 // 2048 - Them
+#define WRD_VRB 12 // 4096 - Verb
+#define WRD_CLASSES 13
+
+
+
+// Syntax element classifications
+#define EOS (-2) // End Of Syntax
+
+// Syntax element flag bits
+#define MULTIPLEBIT 0x1
+#define OMNIBIT 0x2
+
+
+// Parameter Classes
+typedef enum ClaKind { // NOTE! These must have the same order as
+ CLA_OBJ = 1, // the name classes in NAM.H
+ CLA_CNT = (int)CLA_OBJ<<1,
+ CLA_ACT = (int)CLA_CNT<<1,
+ CLA_NUM = (int)CLA_ACT<<1,
+ CLA_STR = (int)CLA_NUM<<1,
+ CLA_COBJ = (int)CLA_STR<<1,
+ CLA_CACT = (int)CLA_COBJ<<1
+} ClaKind;
+
+
+// Verb Qualifiers
+typedef enum QualClass {
+ Q_DEFAULT,
+ Q_AFTER,
+ Q_BEFORE,
+ Q_ONLY
+} QualClass;
+
+
+// The AMACHINE Operations
+typedef enum OpClass {
+ C_CONST,
+ C_STMOP,
+ C_CURVAR
+} OpClass;
+
+typedef enum InstClass {
+ I_PRINT, // Print a string from the text file
+ I_QUIT,
+ I_LOOK,
+ I_SAVE,
+ I_RESTORE,
+ I_LIST, // List contents of a container
+ I_EMPTY,
+ I_SCORE,
+ I_VISITS,
+ I_SCHEDULE,
+ I_CANCEL,
+ I_LOCATE,
+ I_MAKE,
+ I_SET, // Set a numeric attribute to the
+ // value on top of stack
+ I_STRSET, // Set a string valued attribute to a
+ // copy of the string on top of stack,
+ // deallocate current contents first
+ I_GETSTR, // Get a string contents from text
+ // file, create a copy and push it
+ // on top of stack
+ I_INCR, // Increment an attribute
+ I_DECR, // Decrement a numeric attribute
+ I_USE,
+ I_IN,
+ I_DESCRIBE,
+ I_SAY,
+ I_SAYINT,
+ I_SAYSTR,
+ I_IF,
+ I_ELSE,
+ I_ENDIF,
+ I_ATTRIBUTE,
+ I_STRATTR, // Push a copy of a string attribute
+ I_HERE,
+ I_NEAR,
+ I_WHERE,
+ I_AND,
+ I_OR,
+ I_NE,
+ I_EQ,
+ I_STREQ, // String compare
+ I_STREXACT,
+ I_LE,
+ I_GE,
+ I_LT,
+ I_GT,
+ I_PLUS,
+ I_MINUS,
+ I_MULT,
+ I_DIV,
+ I_NOT,
+ I_UMINUS,
+ I_RND,
+ I_SUM, // SUM-aggregate
+ I_MAX, // MAX-aggregate
+ I_COUNT, // COUNT-aggregate
+ I_RETURN,
+ I_SYSTEM,
+ I_RESTART, // INTRODUCED: v2.7
+ I_BTW, // INTRODUCED: v2.8
+ I_CONTAINS, // -""-
+ I_DEPSTART, // -""-
+ I_DEPCASE, // -""-
+ I_DEPEXEC, // -""-
+ I_DEPELSE, // -""-
+ I_DEPEND // -""-
+} InstClass;
+
+
+typedef enum VarClass {
+ V_PARAM,
+ V_CURLOC,
+ V_CURACT,
+ V_CURVRB,
+ V_SCORE
+} VarClass;
+
+
+#define I_CLASS(x) ((x)>>28)
+#define I_OP(x) ((x&0x8000000)?(x)|0x0f0000000:(x)&0x0fffffff)
+
+
+typedef struct AcdHdr {
+// Important info
+ char vers[4]; // 01 - Version of compiler
+ Aword size; // 02 - Size of ACD-file in Awords
+// Options
+ Abool pack; // 03 - Is the text packed ?
+ Aword paglen; // 04 - Length of a page
+ Aword pagwidth; // 05 - and width
+ Aword debug; // 06 - Option debug
+// Data structures
+ Aaddr dict; // 07 - Dictionary
+ Aaddr oatrs; // 08 - Object default attributes
+ Aaddr latrs; // 09 - Location default attributes
+ Aaddr aatrs; // 0a - Actor default attributes
+ Aaddr acts; // 0b - Actor table
+ Aaddr objs; // 0c - Object table
+ Aaddr locs; // 0d - Location table
+ Aaddr stxs; // 0e - Syntax table
+ Aaddr vrbs; // 0f - Verb table
+ Aaddr evts; // 10 - Event table
+ Aaddr cnts; // 11 - Container table
+ Aaddr ruls; // 12 - Rule table
+ Aaddr init; // 13 - String init table
+ Aaddr start; // 14 - Start code
+ Aword msgs; // 15 - Messages table
+// Miscellaneous
+ Aword objmin, objmax; // 16 - Interval for object codes
+ Aword actmin, actmax; // 18 - Interval for actor codes
+ Aword cntmin, cntmax; // 1a - Interval for container codes
+ Aword locmin, locmax; // 1c - Interval for location codes
+ Aword dirmin, dirmax; // 1e - Interval for direction codes
+ Aword evtmin, evtmax; // 20 - Interval for event codes
+ Aword rulmin, rulmax; // 22 - Interval for rule codes
+ Aword maxscore; // 24 - Maximum score
+ Aaddr scores; // 25 - Score table
+ Aaddr freq; // 26 - Address to Char freq's for coding
+ Aword acdcrc; // 27 - Checksum for acd code (excl. hdr)
+ Aword txtcrc; // 28 - Checksum for text data file
+} AcdHdr;
+
+// Error message numbers
+typedef enum MsgKind {
+ M_HUH, // Obsolete
+ M_WHAT,
+ M_WHAT_ALL,
+ M_WHAT_IT,
+ M_WHAT_THEM,
+ M_MULTIPLE,
+ M_WANT,
+ M_NOUN,
+ M_AFTER_BUT,
+ M_BUT_ALL,
+ M_NOT_MUCH,
+ M_WHICH_ONE,
+ M_NO_SUCH,
+ M_NO_WAY,
+ M_CANT0,
+ M_CANT,
+ M_NOTHING, // Obsolete
+ M_SEEOBJ1,
+ M_SEEOBJ2,
+ M_SEEOBJ3,
+ M_SEEOBJ4,
+ M_SEEACT,
+ M_CONTAINS1,
+ M_CONTAINS2,
+ M_CONTAINS3,
+ M_CONTAINS4,
+ M_CONTAINS5,
+ M_EMPTY1,
+ M_EMPTY2,
+ M_SCORE1,
+ M_SCORE2,
+ M_UNKNOWN_WORD,
+ M_MORE,
+ M_AGAIN,
+ M_SAVEWHERE,
+ M_SAVEOVERWRITE,
+ M_SAVEFAILED,
+ M_SAVEMISSING,
+ M_SAVEVERS,
+ M_SAVENAME,
+ M_RESTOREFROM,
+ M_REALLY, // CHANGED: v2.7 from M_RESTART
+ M_QUITACTION, // INTRODUCED: v2.7, so M_ARTICLE moved
+ M_ARTICLE, // INTRODUCED: v2.6 but replaced the M_NOMSG
+ MSGMAX
+} MsgKind;
+
+#define M_ARTICLE26 M_QUITACTION
+#define M_MSGMAX26 M_ARTICLE
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/alan2.cpp b/engines/glk/alan2/alan2.cpp
index 3869e34c90..c29a124f9a 100644
--- a/engines/glk/alan2/alan2.cpp
+++ b/engines/glk/alan2/alan2.cpp
@@ -21,6 +21,10 @@
*/
#include "glk/alan2/alan2.h"
+#include "glk/alan2/decode.h"
+#include "glk/alan2/execute.h"
+#include "glk/alan2/interpreter.h"
+#include "glk/alan2/saveload.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "common/error.h"
@@ -32,14 +36,24 @@
namespace Glk {
namespace Alan2 {
+Alan2 *_vm = nullptr;
Alan2::Alan2(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
vm_exited_cleanly(false) {
+ _vm = this;
}
void Alan2::runGame(Common::SeekableReadStream *gameFile) {
_gameFile = gameFile;
+ // TODO: Initialize these properly
+ int tmp = 0;
+ Common::String gameFileName;
+ _decode = new Decode(nullptr, nullptr);
+ _execute = new Execute();
+ _saveLoad = new SaveLoad(gameFileName, nullptr, nullptr, nullptr, nullptr, &tmp);
+ _interpreter = new Interpreter(_execute, _saveLoad, _stack);
+
if (!is_gamefile_valid())
return;
@@ -70,5 +84,32 @@ bool Alan2::is_gamefile_valid() {
return true;
}
+void Alan2::output(const Common::String str)
+{
+ // TODO
+}
+
+void Alan2::printMessage(MsgKind msg)
+{
+ // TODO
+}
+
+void Alan2::printError(MsgKind msg)
+{
+ // TODO
+}
+
+void Alan2::paragraph()
+{
+ if (col != 1)
+ newLine();
+ newLine();
+}
+
+void Alan2::newLine()
+{
+ // TODO
+}
+
} // End of namespace Alan2
} // End of namespace Glk
diff --git a/engines/glk/alan2/alan2.h b/engines/glk/alan2/alan2.h
index f4515516f7..716e198c74 100644
--- a/engines/glk/alan2/alan2.h
+++ b/engines/glk/alan2/alan2.h
@@ -26,10 +26,20 @@
#include "common/scummsys.h"
#include "common/stack.h"
#include "glk/glk_api.h"
+#include "glk/alan2/acode.h"
+#include "glk/alan2/types.h"
namespace Glk {
namespace Alan2 {
+typedef Common::FixedStack<Aptr, 100> Alan2Stack;
+class Decode;
+class Execute;
+class Interpreter;
+class SaveLoad;
+
+#define N_EVTS 100
+
/**
* Alan2 game interpreter
*/
@@ -67,8 +77,65 @@ public:
* Save the game to the passed stream
*/
virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+
+ /**
+ * Output a string to the screen
+ */
+ void output(const Common::String str);
+
+ /**
+ * Print a message from the message table
+ */
+ void printMessage(MsgKind msg);
+
+ /**
+ * Print an error from the message table, force new player input and abort
+ */
+ void printError(MsgKind msg);
+
+ /**
+ * Make a new paragraph, i.e one empty line (one or two newlines)
+ */
+ void paragraph();
+
+ /**
+ * Print the the status line on the top of the screen
+ */
+ void statusLine();
+
+ /**
+ * Make a newline, but check for screen full
+ */
+ void newLine();
+
+ // Engine variables
+ Alan2Stack *_stack;
+ int pc;
+ ParamElem *params;
+ Aword *memory; // The Amachine memory
+ int memTop; // Top of memory
+ CurVars cur; // Amachine variables
+ int col;
+ bool fail;
+ int scores[100]; // FIXME: type + size
+ AcdHdr *header;
+ bool _needSpace; // originally "needsp"
+
+ EvtElem *evts; // Event table pointer
+ bool looking = false; // LOOKING? flag
+ int dscrstkp = 0; // Describe-stack pointer
+ Common::File *_txtFile;
+ bool _anyOutput;
+ winid_t _bottomWindow;
+
+ Decode *_decode;
+ Execute *_execute;
+ Interpreter *_interpreter;
+ SaveLoad *_saveLoad;
};
+extern Alan2 *_vm;
+
} // End of namespace Alan2
} // End of namespace Glk
diff --git a/engines/glk/alan2/decode.cpp b/engines/glk/alan2/decode.cpp
new file mode 100644
index 0000000000..ba144123c6
--- /dev/null
+++ b/engines/glk/alan2/decode.cpp
@@ -0,0 +1,124 @@
+/* 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/alan2/decode.h"
+
+namespace Glk {
+namespace Alan2 {
+
+int Decode::inputBit() {
+ int bit;
+
+ if (!_bitsToGo) { // More bits available ?
+ _decodeBuffer = _txtFile->readByte(); // No, so get more
+ if (_txtFile->eos()) {
+ _garbageBits++;
+
+ if (_garbageBits > VALUEBITS - 2)
+ error("Error in encoded data file.");
+ } else
+ _bitsToGo = 8; // Another Char, 8 new bits
+ }
+
+ bit = _decodeBuffer & 1; // Get next bit
+ _decodeBuffer = _decodeBuffer >> 1; // and remove it
+ _bitsToGo--;
+
+ return bit;
+}
+
+void Decode::startDecoding() {
+ _bitsToGo = 0;
+ _garbageBits = 0;
+
+ _value = 0;
+ for (int i = 0; i < VALUEBITS; i++)
+ _value = 2 * _value + inputBit();
+
+ _low = 0;
+ _high = TOPVALUE;
+}
+
+int Decode::decodeChar() {
+ const long range = (long)(_high - _low) + 1;
+ const int f = (((long)(_value - _low) + 1) * _freq[0] - 1) / range;
+ int symbol;
+
+ // Find the symbol
+ for (symbol = 1; _freq[symbol] > f; symbol++);
+
+ _high = _low + range * _freq[symbol - 1] / _freq[0] - 1;
+ _low = _low + range * _freq[symbol] / _freq[0];
+
+ for (;;) {
+ if (_high < HALF) {
+ // Do nothing
+ } else if (_low >= HALF) {
+ _value = _value - HALF;
+ _low = _low - HALF;
+ _high = _high - HALF;
+ } else if (_low >= ONEQUARTER && _high < THREEQUARTER) {
+ _value = _value - ONEQUARTER;
+ _low = _low - ONEQUARTER;
+ _high = _high - ONEQUARTER;
+ } else
+ break;
+
+ // Scale up the range
+ _low = 2 * _low;
+ _high = 2 * _high + 1;
+ _value = 2 * _value + inputBit();
+ }
+
+ return symbol - 1;
+}
+
+// Save so much about the decoding process, so it is possible to restore
+// and continue later.
+DecodeInfo* Decode::pushDecode() {
+ DecodeInfo *info = new DecodeInfo();
+
+ info->fpos = _txtFile->pos();
+ info->buffer = _decodeBuffer;
+ info->bits = _bitsToGo;
+ info->value = _value;
+ info->high = _high;
+ info->low = _low;
+
+ return info;
+}
+
+// Restore enough info about the decoding process, so it is possible to
+// continue after having decoded something else
+void Decode::popDecode (DecodeInfo *info){
+ _txtFile->seek(info->fpos, SEEK_CUR);
+ _decodeBuffer = info->buffer;
+ _bitsToGo = info->bits;
+ _value = info->value;
+ _high = info->high;
+ _low = info->low;
+
+ delete info;
+}
+
+} // End of namespace Alan2
+} // End of namespace Glk
diff --git a/engines/glk/alan2/decode.h b/engines/glk/alan2/decode.h
new file mode 100644
index 0000000000..fff6819878
--- /dev/null
+++ b/engines/glk/alan2/decode.h
@@ -0,0 +1,70 @@
+/* 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 GLK_ALAN2_DECODE
+#define GLK_ALAN2_DECODE
+
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/acode.h"
+#include "common/file.h"
+
+namespace Glk {
+namespace Alan2 {
+
+// Structure for saved decode info
+typedef struct DecodeInfo {
+ long fpos;
+ int buffer;
+ int bits;
+ CodeValue value;
+ CodeValue high;
+ CodeValue low;
+} DecodeInfo;
+
+class Decode
+{
+public:
+ Decode(Common::File *txtFile, Aword *freq): _txtFile(txtFile), _freq(freq) {}
+ void startDecoding();
+ int decodeChar();
+ DecodeInfo *pushDecode();
+ void popDecode(DecodeInfo *info);
+ int inputBit();
+
+private:
+ // Bit output
+ int _decodeBuffer; // Bits to be input
+ int _bitsToGo; // Bits still in buffer
+ int _garbageBits; // Bits past EOF
+
+ Aword *_freq;
+ Common::File *_txtFile;
+
+ // Current state of decoding
+ CodeValue _value; // Currently seen code value
+ CodeValue _low, _high; // Current code region
+};
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/execute.cpp b/engines/glk/alan2/execute.cpp
new file mode 100644
index 0000000000..5b81b303d9
--- /dev/null
+++ b/engines/glk/alan2/execute.cpp
@@ -0,0 +1,1065 @@
+/* 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 "common/stack.h"
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/execute.h"
+#include "glk/alan2/interpreter.h"
+#include "glk/alan2/saveload.h"
+#include "glk/alan2/types.h"
+#include "glk/alan2/util.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "decode.h"
+
+namespace Glk {
+namespace Alan2 {
+
+#define WIDTH 80
+
+// Is there an exit from one location to another ?
+bool Execute::exitto(int to, int from) {
+ if (_locs[from - LOCMIN].exts == 0)
+ return false; // No exits
+
+ for (ExtElem *ext = (ExtElem *)addrTo(_locs[from - LOCMIN].exts); !endOfTable(ext); ext++)
+ if (ext->next == to)
+ return true;
+
+ return false;
+}
+
+/*
+ * Count the number of items in a container.
+ *
+ * @param cnt The container to count
+ */
+int Execute::count(int cnt) {
+ int j = 0;
+
+ for (int i = OBJMIN; i <= OBJMAX; i++)
+ if (in(i, cnt))
+ j++; // Then it's in this container also
+
+ return j;
+}
+
+/*
+ * Sum the values of one attribute in a container.Recursively.
+ *
+ * @param atr The attribute to sum over
+ * @param cnt the container to sum
+ */
+int Execute::sumatr(Aword atr, Aword cnt) {
+ int sum = 0;
+
+ for (int i = OBJMIN; i <= OBJMAX; i++) {
+ if (_objs[i - OBJMIN].loc == cnt) { // Then it's in this container
+ if (_objs[i - OBJMIN].cont != 0) // This is also a container!
+ sum = sum + sumatr(atr, i);
+ sum = sum + attribute(i, atr);
+ }
+ }
+
+ return sum;
+}
+
+/**
+ * Checks if a limit for a container is exceeded.
+ *
+ * @param cnt Container code
+ * @param obj The object to add
+ */
+//
+bool Execute::checklim(Aword cnt, Aword obj) {
+ LimElem *lim;
+ Aword props;
+
+ _vm->fail = true;
+ if (!isCnt(cnt))
+ error("Checking limits for a non-container.");
+
+ // Find the container properties
+ if (isObj(cnt))
+ props = _objs[cnt - OBJMIN].cont;
+ else if (isAct(cnt))
+ props = _acts[cnt - ACTMIN].cont;
+ else
+ props = cnt;
+
+
+ if (_cnts[props - CNTMIN].lims != 0) { /* Any limits at all? */
+ for (lim = (LimElem *)addrTo(_cnts[props - CNTMIN].lims); !endOfTable(lim); lim++) {
+ if (lim->atr == 0) {
+ if (count(cnt) >= lim->val) {
+ _vm->_interpreter->interpret(lim->stms);
+ return true; // Limit check failed
+ }
+ } else {
+ if (sumatr(lim->atr, cnt) + attribute(obj, lim->atr) > lim->val) {
+ _vm->_interpreter->interpret(lim->stms);
+ return true;
+ }
+ }
+ }
+ }
+
+ _vm->fail = false;
+
+ return false;
+}
+
+void Execute::print(Aword fpos, Aword len) {
+ char str[2 * WIDTH]; // String buffer
+ int outlen = 0; // Current output length
+ int ch;
+ int i;
+ long savfp; // Temporary saved text file position
+ bool printFlag = false; // Printing already?
+ bool savedPrintFlag = printFlag;
+ DecodeInfo *info; // Saved decoding info
+
+
+ if (len == 0)
+ return;
+
+ if (isHere(HERO)) { // Check if the player will see it
+ if (printFlag) { // Already printing?
+ // Save current text file position and/or decoding info
+ if (_vm->header->pack)
+ info = _vm->_decode->pushDecode();
+ else
+ savfp = _vm->_txtFile->pos();
+ }
+
+ printFlag = true; // We're printing now!
+ _vm->_txtFile->seek(fpos, SEEK_CUR); // Position to start of text
+ if (_vm->header->pack)
+ _vm->_decode->startDecoding();
+
+ for (outlen = 0; outlen != len; outlen = outlen + strlen(str)) {
+ // Fill the buffer from the beginning
+ for (i = 0; i <= WIDTH || (i > WIDTH && ch != ' '); i++) {
+ if (outlen + i == len) // No more characters?
+ break;
+ if (_vm->header->pack)
+ ch = _vm->_decode->decodeChar();
+ else
+ ch = _vm->_txtFile->readSByte();
+ if (ch == EOFChar) // Or end of text?
+ break;
+ str[i] = ch;
+ }
+
+ str[i] = '\0';
+
+ // TODO
+ /*
+#if ISO == 0
+ fromIso(str, str);
+#endif
+ */
+ _vm->output(str);
+ }
+
+ // And restore
+ printFlag = savedPrintFlag;
+ if (printFlag) {
+ if (_vm->header->pack)
+ _vm->_decode->popDecode(info);
+ else
+ _vm->_txtFile->seek(savfp, SEEK_CUR);
+ }
+ }
+}
+
+void Execute::sys(Aword fpos, Aword len) {
+ char *command;
+
+ getstr(fpos, len); // Returns address to string on stack
+ command = (char *)_vm->_stack->pop();
+
+ warning("Request to execute system command %s", command);
+ free(command);
+}
+
+void Execute::getstr(Aword fpos, Aword len) {
+ char *buf = new char[len + 1];
+
+ _vm->_stack->push((Aptr) buf); // Push the address to the string
+ _vm->_txtFile->seek(fpos, SEEK_CUR); // Position to start of text
+ if (_vm->header->pack)
+ _vm->_decode->startDecoding();
+ while (len--) {
+ if (_vm->header->pack)
+ *(buf++) = _vm->_decode->decodeChar();
+ else
+ *(buf++) = _vm->_txtFile->readSByte();
+ }
+ *buf = '\0';
+}
+
+void Execute::score(Aword sc) {
+ char buf[80];
+
+ if (sc == 0) {
+ _vm->printMessage(M_SCORE1);
+ sprintf(buf, "%d", _vm->cur.score);
+ _vm->output(buf);
+ _vm->printMessage(M_SCORE2);
+ sprintf(buf, "%ld.", (unsigned long)_vm->header->maxscore);
+ _vm->output(buf);
+ } else {
+ _vm->cur.score += _vm->scores[sc - 1];
+ _vm->scores[sc - 1] = 0;
+ }
+}
+
+void Execute::visits(Aword v) {
+ _vm->cur.visits = v;
+}
+
+bool Execute::confirm(MsgKind msgno) {
+ char buf[80];
+
+ // This is a bit of a hack since we really want to compare the input,
+ // it could be affirmative, but for now any input is NOT!
+ _vm->printMessage(msgno);
+
+ // TODO
+#if 0
+ //_vm->glk_request_line_event(_bottomWindow, buf, 80 - 1, 0);
+
+#ifdef USE_READLINE
+ if (!readline(buf)) return true;
+#else
+ if (gets(buf) == NULL) return true;
+#endif
+
+#endif
+ _vm->col = 1;
+
+ return (buf[0] == '\0');
+}
+
+
+void Execute::quit() {
+ char buf[80];
+ char choices[10];
+
+ _vm->paragraph();
+ while (true) {
+ _vm->col = 1;
+ _vm->statusLine();
+ _vm->printMessage(M_QUITACTION);
+
+ // TODO
+#if 0
+#ifdef USE_READLINE
+ if (!readline(buf)) terminate(0);
+#else
+ if (gets(buf) == NULL) terminate(0);
+#endif
+#endif
+
+ if (strcmp(buf, "restart") == 0) {
+ //longjmp(restart_label, true); // TODO
+ } else if (strcmp(buf, "restore") == 0) {
+ _vm->_saveLoad->restore();
+ return;
+ } else if (strcmp(buf, "quit") == 0) {
+ _vm->quitGame();
+ }
+ }
+ error("Fallthrough in QUIT");
+}
+
+void Execute::restart() {
+ _vm->paragraph();
+ if (confirm(M_REALLY)) {
+ //longjmp(restart_label, true); // TODO
+ } else
+ return;
+
+ error("Fallthrough in RESTART");
+}
+
+/*----------------------------------------------------------------------
+ eventchk()
+
+ Check if any events are pending. If so execute them.
+ */
+
+void Execute::eventchk() {
+ while (etop != 0 && eventq[etop - 1].time == _vm->cur.tick) {
+ etop--;
+ if (isLoc(eventq[etop].where))
+ _vm->cur.loc = eventq[etop].where;
+ else
+ _vm->cur.loc = where(eventq[etop].where);
+
+ // TODO
+#if 0
+ if (trcflg) {
+ debug("\n<EVENT %d (at ", eventq[etop].event);
+ debugsay(_vm->cur.loc);
+ debug("):>\n");
+ }
+#endif
+ _vm->_interpreter->interpret(_vm->evts[eventq[etop].event - EVTMIN].code);
+ }
+}
+
+void Execute::cancl(Aword evt) {
+ int i;
+
+ for (i = etop - 1; i >= 0; i--) {
+ if (eventq[i].event == evt) {
+ while (i < etop - 1) {
+ eventq[i].event = eventq[i + 1].event;
+ eventq[i].time = eventq[i + 1].time;
+ eventq[i].where = eventq[i + 1].where;
+ i++;
+ }
+
+ etop--;
+ return;
+ }
+ }
+}
+
+void Execute::schedule(Aword evt, Aword whr, Aword aft) {
+ int i;
+ int time;
+
+ cancl(evt);
+ // Check for overflow
+ if (etop == N_EVTS)
+ error("Out of event space.");
+
+ time = _vm->cur.tick+aft;
+
+ // Bubble this event down
+ for (i = etop; i >= 1 && eventq[i-1].time <= time; i--) {
+ eventq[i].event = eventq[i-1].event;
+ eventq[i].time = eventq[i-1].time;
+ eventq[i].where = eventq[i-1].where;
+ }
+
+ eventq[i].time = time;
+ eventq[i].where = whr;
+ eventq[i].event = evt;
+ etop++;
+}
+
+/**
+ * Get an attribute value from an attribute list
+ *
+ * @param atradr ACODE address to attribute table
+ * @param atr The attribute to read
+ */
+Aptr Execute::getatr(Aaddr atradr, Aaddr atr) {
+ AtrElem *at = (AtrElem *) addrTo(atradr);
+ return at[atr-1].val;
+}
+
+/**
+ * Set a particular attribute to a value.
+ *
+ * @param atradr ACODE address to attribute table
+ * @param atr Attribute code
+ * @param val New value
+ */
+void Execute::setatr(Aaddr atradr, Aword atr, Aword val) {
+ AtrElem *at = (AtrElem *) addrTo(atradr);
+ at[atr-1].val = val;
+}
+
+void Execute::makloc(Aword loc, Aword atr, Aword val) {
+ setatr(_locs[loc - LOCMIN].atrs, atr, val);
+}
+
+void Execute::makobj(Aword obj, Aword atr, Aword val) {
+ setatr(_objs[obj - OBJMIN].atrs, atr, val);
+}
+
+void Execute::makact(Aword act, Aword atr, Aword val) {
+ setatr(_acts[act - ACTMIN].atrs, atr, val);
+}
+
+void Execute::make(Aword id, Aword atr, Aword val) {
+ if (isObj(id))
+ makobj(id, atr, val);
+ else if (isLoc(id))
+ makloc(id, atr, val);
+ else if (isAct(id))
+ makact(id, atr, val);
+ else
+ error("Can't MAKE item (%ld).", (unsigned long)id);
+}
+
+void Execute::setloc(Aword loc, Aword atr, Aword val) {
+ setatr(_locs[loc - LOCMIN].atrs, atr, val);
+ _locs[loc - LOCMIN].describe = 0;
+}
+
+void Execute::setobj(Aword obj, Aword atr, Aword val) {
+ setatr(_objs[obj - OBJMIN].atrs, atr, val);
+}
+
+void Execute::setact(Aword act, Aword atr, Aword val) {
+ setatr(_acts[act - ACTMIN].atrs, atr, val);
+}
+
+void Execute::set(Aword id, Aword atr, Aword val) {
+ if (isObj(id))
+ setobj(id, atr, val);
+ else if (isLoc(id))
+ setloc(id, atr, val);
+ else if (isAct(id))
+ setact(id, atr, val);
+ else
+ error("Can't SET item (%ld).", (unsigned long)id);
+}
+
+void Execute::setstr(Aword id, Aword atr, Aword str) {
+ free((char *)attribute(id, atr));
+ set(id, atr, str);
+}
+
+/**
+ * Increment a particular attribute by a value.
+ *
+ * @param atradr ACODE address to attribute table
+ * @param atr Attribute code
+ * @param step Step to increment by
+ */
+void Execute::incratr(Aaddr atradr, Aword atr, Aword step) {
+ AtrElem *at = (AtrElem *) addrTo(atradr);
+ at[atr-1].val += step;
+}
+
+void Execute::incrloc(Aword loc, Aword atr, Aword step) {
+ incratr(_locs[loc - LOCMIN].atrs, atr, step);
+ _locs[loc - LOCMIN].describe = 0;
+}
+
+void Execute::incrobj(Aword obj, Aword atr, Aword step) {
+ incratr(_objs[obj - OBJMIN].atrs, atr, step);
+}
+
+void Execute::incract(Aword act, Aword atr, Aword step) {
+ incratr(_acts[act - ACTMIN].atrs, atr, step);
+}
+
+void Execute::incr(Aword id, Aword atr, Aword step) {
+ if (isObj(id))
+ incrobj(id, atr, step);
+ else if (isLoc(id))
+ incrloc(id, atr, step);
+ else if (isAct(id))
+ incract(id, atr, step);
+ else
+ error("Can't INCR item (%ld).", (unsigned long)id);
+}
+
+void Execute::decr(Aword id, Aword atr, Aword step) {
+ if (isObj(id))
+ incrobj(id, atr, -step);
+ else if (isLoc(id))
+ incrloc(id, atr, -step);
+ else if (isAct(id))
+ incract(id, atr, -step);
+ else
+ error("Can't DECR item (%ld).", (unsigned long)id);
+}
+
+Aptr Execute::locatr(Aword loc, Aword atr) {
+ return getatr(_locs[loc - LOCMIN].atrs, atr);
+}
+
+Aptr Execute::objatr(Aword obj, Aword atr) {
+ return getatr(_objs[obj - OBJMIN].atrs, atr);
+}
+
+Aptr Execute::actatr(Aword act, Aword atr) {
+ return getatr(_acts[act - ACTMIN].atrs, atr);
+}
+
+Aptr Execute::litatr(Aword lit, Aword atr) {
+ if (atr == 1)
+ return litValues[lit - LITMIN].value;
+ else
+ error("Unknown attribute for literal (%ld).", (unsigned long) atr);
+}
+
+Aptr Execute::attribute(Aword id, Aword atr) {
+ if (isObj(id))
+ return objatr(id, atr);
+ else if (isLoc(id))
+ return locatr(id, atr);
+ else if (isAct(id))
+ return actatr(id, atr);
+ else if (isLit(id))
+ return litatr(id, atr);
+ else
+ error("Can't ATTRIBUTE item (%ld).", (unsigned long) id);
+}
+
+Aptr Execute::strattr(Aword id, Aword atr) {
+ Common::String result = (char *)attribute(id, atr);
+ return (Aptr)result.c_str();
+}
+
+Aword Execute::objloc(Aword obj) {
+ if (isCnt(_objs[obj - OBJMIN].loc)) { // In something ?
+ if (isObj(_objs[obj - OBJMIN].loc) || isAct(_objs[obj - OBJMIN].loc))
+ return(where(_objs[obj - OBJMIN].loc));
+ else // Containers not anywhere is where the hero is!
+ return(where(HERO));
+ } else {
+ return(_objs[obj - OBJMIN].loc);
+ }
+}
+
+Aword Execute::actloc(Aword act) {
+ return(_acts[act - ACTMIN].loc);
+}
+
+Aword Execute::where(Aword id) {
+ if (isObj(id))
+ return objloc(id);
+ else if (isAct(id))
+ return actloc(id);
+ else
+ error("Can't WHERE item (%ld).", (unsigned long) id);
+}
+
+Aint Execute::agrmax(Aword atr, Aword whr) {
+ Aword i;
+ Aint max = 0;
+
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ if (isLoc(whr)) {
+ if (where(i) == whr && attribute(i, atr) > max)
+ max = attribute(i, atr);
+ } else if (_objs[i - OBJMIN].loc == whr && attribute(i, atr) > max)
+ max = attribute(i, atr);
+ }
+
+ return(max);
+}
+
+Aint Execute::agrsum(Aword atr, Aword whr) {
+ Aword i;
+ Aint sum = 0;
+
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ if (isLoc(whr)) {
+ if (where(i) == whr)
+ sum += attribute(i, atr);
+ } else if (_objs[i-OBJMIN].loc == whr)
+ sum += attribute(i, atr);
+ }
+
+ return(sum);
+}
+
+Aint Execute::agrcount(Aword whr) {
+ Aword i;
+ Aword count = 0;
+
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ if (isLoc(whr)) {
+ if (where(i) == whr)
+ count++;
+ } else if (_objs[i-OBJMIN].loc == whr)
+ count++;
+ }
+
+ return(count);
+}
+
+void Execute::locobj(Aword obj, Aword whr) {
+ if (isCnt(whr)) { // Into a container
+ if (whr == obj)
+ error("Locating something inside itself.");
+ if (checklim(whr, obj))
+ return;
+ else
+ _objs[obj-OBJMIN].loc = whr;
+ } else {
+ _objs[obj-OBJMIN].loc = whr;
+ // Make sure the location is described since it's changed
+ _locs[whr-LOCMIN].describe = 0;
+ }
+}
+
+void Execute::locact(Aword act, Aword whr) {
+ Aword prevact = _vm->cur.act;
+ Aword prevloc = _vm->cur.loc;
+
+ _vm->cur.loc = whr;
+ _acts[act - ACTMIN].loc = whr;
+
+ if (act == HERO) {
+ if (_locs[_acts[act - ACTMIN].loc-LOCMIN].describe % (_vm->cur.visits+1) == 0)
+ look();
+ else {
+ if (_vm->_anyOutput)
+ _vm->paragraph();
+
+ say(where(HERO));
+ _vm->printMessage(M_AGAIN);
+ _vm->newLine();
+ dscrobjs();
+ dscracts();
+ }
+
+ _locs[where(HERO)-LOCMIN].describe++;
+ _locs[where(HERO)-LOCMIN].describe %= (_vm->cur.visits+1);
+ } else
+ _locs[whr-LOCMIN].describe = 0;
+
+ if (_locs[_vm->cur.loc-LOCMIN].does != 0) {
+ _vm->cur.act = act;
+ _vm->_interpreter->interpret(_locs[_vm->cur.loc-LOCMIN].does);
+ _vm->cur.act = prevact;
+ }
+
+ if (_vm->cur.act != act)
+ _vm->cur.loc = prevloc;
+}
+
+
+void Execute::locate(Aword id, Aword whr) {
+ if (isObj(id))
+ locobj(id, whr);
+ else if (isAct(id))
+ locact(id, whr);
+ else
+ error("Can't LOCATE item (%ld).", (unsigned long) id);
+}
+
+Abool Execute::objhere(Aword obj) {
+ if (isCnt(_objs[obj - OBJMIN].loc)) { // In something?
+ if (isObj(_objs[obj - OBJMIN].loc) || isAct(_objs[obj - OBJMIN].loc))
+ return(isHere(_objs[obj - OBJMIN].loc));
+ else // If the container wasn't anywhere, assume where HERO is!
+ return(where(HERO) == _vm->cur.loc);
+ } else
+ return(_objs[obj - OBJMIN].loc == _vm->cur.loc);
+}
+
+Aword Execute::acthere(Aword act) {
+ return _acts[act - ACTMIN].loc == _vm->cur.loc;
+}
+
+Abool Execute::isHere(Aword id) {
+ if (isObj(id))
+ return objhere(id);
+ else if (isAct(id))
+ return acthere(id);
+ else
+ error("Can't HERE item (%ld).", (unsigned long)id);
+}
+
+Aword Execute::objnear(Aword obj) {
+ if (isCnt(_objs[obj-OBJMIN].loc)) { // In something?
+ if (isObj(_objs[obj-OBJMIN].loc) || isAct(_objs[obj-OBJMIN].loc))
+ return(isNear(_objs[obj-OBJMIN].loc));
+ else // If the container wasn't anywhere, assume here, so not nearby!
+ return(false);
+ } else {
+ return(exitto(where(obj), _vm->cur.loc));
+ }
+}
+
+Aword Execute::actnear(Aword act) {
+ return(exitto(where(act), _vm->cur.loc));
+}
+
+
+Abool Execute::isNear(Aword id) {
+ if (isObj(id))
+ return objnear(id);
+ else if (isAct(id))
+ return actnear(id);
+ else
+ error("Can't NEAR item (%ld).", (unsigned long) id);
+}
+
+Abool Execute::in(Aword obj, Aword cnt) {
+ if (!isObj(obj))
+ return(false);
+ if (!isCnt(cnt))
+ error("IN in a non-container.");
+
+ return(_objs[obj - OBJMIN].loc == cnt);
+}
+
+void Execute::sayloc(Aword loc) {
+ _vm->_interpreter->interpret(_locs[loc - LOCMIN].nams);
+}
+
+void Execute::sayobj(Aword obj) {
+ _vm->_interpreter->interpret(_objs[obj - OBJMIN].dscr2);
+}
+
+void Execute::sayact(Aword act) {
+ _vm->_interpreter->interpret(_acts[act - ACTMIN].nam);
+}
+
+void Execute::sayint(Aword val) {
+ char buf[25];
+
+ if (isHere(HERO)) {
+ sprintf(buf, "%ld", (unsigned long) val);
+ _vm->output(buf);
+ }
+}
+
+void Execute::saystr(char *str) {
+ if (isHere(HERO))
+ _vm->output(str);
+ free(str);
+}
+
+void Execute::saylit(Aword lit) {
+ if (isNum(lit))
+ sayint(litValues[lit - LITMIN].value);
+ else {
+ Common::String str = (char *)litValues[lit - LITMIN].value;
+ saystr((char *)str.c_str());
+ }
+}
+
+void Execute::sayarticle(Aword id) {
+ if (!isObj(id))
+ error("Trying to say article of something *not* an object.");
+ if (_objs[id - OBJMIN].art != 0)
+ _vm->_interpreter->interpret(_objs[id - OBJMIN].art);
+ else
+ _vm->printMessage(M_ARTICLE);
+}
+
+void Execute::say(Aword id) {
+ if (isHere(HERO)) {
+ if (isObj(id))
+ sayobj(id);
+ else if (isLoc(id))
+ sayloc(id);
+ else if (isAct(id))
+ sayact(id);
+ else if (isLit(id))
+ saylit(id);
+ else
+ error("Can't SAY item (%ld).", (unsigned long)id);
+ }
+}
+
+void Execute::dscrloc(Aword loc) {
+ if (_locs[loc - LOCMIN].dscr != 0)
+ _vm->_interpreter->interpret(_locs[loc - LOCMIN].dscr);
+}
+
+void Execute::dscrobj(Aword obj) {
+ _objs[obj - OBJMIN].describe = false;
+ if (_objs[obj - OBJMIN].dscr1 != 0)
+ _vm->_interpreter->interpret(_objs[obj - OBJMIN].dscr1);
+ else {
+ _vm->printMessage(M_SEEOBJ1);
+ sayarticle(obj);
+ say(obj);
+ _vm->printMessage(M_SEEOBJ4);
+ if (_objs[obj - OBJMIN].cont != 0)
+ list(obj);
+ }
+}
+
+
+void Execute::dscract(Aword act) {
+ ScrElem *scr = NULL;
+
+ if (_acts[act - ACTMIN].script != 0) {
+ for (scr = (ScrElem *) addrTo(_acts[act - ACTMIN].scradr); !endOfTable(scr); scr++)
+ if (scr->code == _acts[act - ACTMIN].script)
+ break;
+ if (endOfTable(scr))
+ scr = NULL;
+ }
+
+ if (scr != NULL && scr->dscr != 0)
+ _vm->_interpreter->interpret(scr->dscr);
+ else if (_acts[act - ACTMIN].dscr != 0)
+ _vm->_interpreter->interpret(_acts[act - ACTMIN].dscr);
+ else {
+ _vm->_interpreter->interpret(_acts[act - ACTMIN].nam);
+ _vm->printMessage(M_SEEACT);
+ }
+
+ _acts[act - ACTMIN].describe = false;
+}
+
+void Execute::describe(Aword id) {
+ for (int i = 0; i < _describeStack.size(); i++) {
+ _describeStack.push(id);
+
+ if (isObj(id))
+ dscrobj(id);
+ else if (isLoc(id))
+ dscrloc(id);
+ else if (isAct(id))
+ dscract(id);
+ else
+ error("Can't DESCRIBE item (%ld).", (unsigned long)id);
+ }
+
+ _describeStack.pop();
+}
+
+void Execute::use(Aword act, Aword scr) {
+ if (!isAct(act))
+ error("Item is not an Actor (%ld).", (unsigned long) act);
+
+ _acts[act - ACTMIN].script = scr;
+ _acts[act - ACTMIN].step = 0;
+}
+
+void Execute::list(Aword cnt) {
+ int i;
+ Aword props;
+ Aword prevobj;
+ bool found = false;
+ bool multiple = false;
+
+ // Find container properties
+ if (isObj(cnt))
+ props = _objs[cnt-OBJMIN].cont;
+ else if (isAct(cnt))
+ props = _acts[cnt-ACTMIN].cont;
+ else
+ props = cnt;
+
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ if (in(i, cnt)) { // Yes, it's in this container
+ if (!found) {
+ found = true;
+ if (_cnts[props-CNTMIN].header != 0)
+ _vm->_interpreter->interpret(_cnts[props-CNTMIN].header);
+ else {
+ _vm->printMessage(M_CONTAINS1);
+ if (_cnts[props-CNTMIN].nam != 0) // It has it's own name
+ _vm->_interpreter->interpret(_cnts[props-CNTMIN].nam);
+ else
+ say(_cnts[props-CNTMIN].parent); // It is actually an object or actor
+
+ _vm->printMessage(M_CONTAINS2);
+ }
+ } else {
+ if (multiple) {
+ _vm->_needSpace = false;
+ _vm->printMessage(M_CONTAINS3);
+ }
+
+ multiple = true;
+ sayarticle(prevobj);
+ say(prevobj);
+ }
+
+ prevobj = i;
+ }
+ }
+
+ if (found) {
+ if (multiple)
+ _vm->printMessage(M_CONTAINS4);
+
+ sayarticle(prevobj);
+ say(prevobj);
+ _vm->printMessage(M_CONTAINS5);
+ } else {
+ if (_cnts[props-CNTMIN].empty != 0)
+ _vm->_interpreter->interpret(_cnts[props-CNTMIN].empty);
+ else {
+ _vm->printMessage(M_EMPTY1);
+
+ if (_cnts[props-CNTMIN].nam != 0) // It has it's own name
+ _vm->_interpreter->interpret(_cnts[props-CNTMIN].nam);
+ else
+ say(_cnts[props-CNTMIN].parent); // It is actually an actor or object
+
+ _vm->printMessage(M_EMPTY2);
+ }
+ }
+
+ _vm->_needSpace = true;
+}
+
+void Execute::empty(Aword cnt, Aword whr) {
+ for (int i = OBJMIN; i <= OBJMAX; i++)
+ if (in(i, cnt))
+ locate(i, whr);
+}
+
+// Description of current location
+void Execute::dscrobjs() {
+ int i;
+ int prevobj;
+ bool found = false;
+ bool multiple = false;
+
+ // First describe everything here with its own description
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ if (_objs[i - OBJMIN].loc == _vm->cur.loc &&
+ _objs[i - OBJMIN].describe &&
+ _objs[i - OBJMIN].dscr1)
+ describe(i);
+ }
+
+ // Then list everything else here
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ if (_objs[i - OBJMIN].loc == _vm->cur.loc &&
+ _objs[i - OBJMIN].describe) {
+ if (!found) {
+ _vm->printMessage(M_SEEOBJ1);
+ sayarticle(i);
+ say(i);
+ found = true;
+ }
+ else {
+ if (multiple) {
+ _vm->_needSpace = false;
+ _vm->printMessage(M_SEEOBJ2);
+ sayarticle(prevobj);
+ say(prevobj);
+ }
+
+ multiple = true;
+ }
+
+ prevobj = i;
+ }
+ }
+
+ if (found) {
+ if (multiple) {
+ _vm->printMessage(M_SEEOBJ3);
+ sayarticle(prevobj);
+ say(prevobj);
+ }
+
+ _vm->printMessage(M_SEEOBJ4);
+ }
+
+ // Set describe flag for all objects
+ for (i = OBJMIN; i <= OBJMAX; i++)
+ _objs[i-OBJMIN].describe = true;
+}
+
+
+void Execute::dscracts() {
+ for (int i = HERO+1; i <= ACTMAX; i++)
+ if (_acts[i-ACTMIN].loc == _vm->cur.loc &&
+ _acts[i-ACTMIN].describe)
+ describe(i);
+
+ // Set describe flag for all actors
+ for (int i = HERO; i <= ACTMAX; i++)
+ _acts[i-ACTMIN].describe = true;
+}
+
+void Execute::look() {
+ int i;
+ _vm->looking = true;
+
+ // Set describe flag for all objects and actors
+ for (i = OBJMIN; i <= OBJMAX; i++)
+ _objs[i-OBJMIN].describe = true;
+ for (i = ACTMIN; i <= ACTMAX; i++)
+ _acts[i-ACTMIN].describe = true;
+
+ if (_vm->_anyOutput)
+ _vm->paragraph();
+
+ //glk_set_style(style_Subheader); // TODO
+ _vm->_needSpace = false;
+ say(_vm->cur.loc);
+
+ _vm->_needSpace = false;
+ _vm->output(".");
+
+ //glk_set_style(style_Normal); // TODO
+ _vm->newLine();
+ _vm->_needSpace = false;
+ describe(_vm->cur.loc);
+ dscrobjs();
+ dscracts();
+ _vm->looking = false;
+}
+
+Aword Execute::rnd(Aword from, Aword to) {
+ if (to == from)
+ return to;
+ else if (to > from)
+ return to; // TODO
+ //return (rand()/10)%(to-from+1)+from;
+ else
+ return to; // TODO
+ //return (rand()/10)%(from-to+1)+to;
+}
+
+// Between
+Abool Execute::btw(Aint val, Aint low, Aint high) {
+ if (high > low)
+ return low <= val && val <= high;
+ else
+ return high <= val && val <= low;
+}
+
+// TODO: Replace this with Common::String functionality
+Aword Execute::contains(Aptr string, Aptr substring) {
+ Common::String str = (char *)string;
+ char *substr = (char *)substring;
+ bool result = str.contains(substr);
+
+ free((char *)string);
+ free((char *)substring);
+
+ return result;
+}
+
+// TODO: Replace this with Common::String functionality
+// Compare two strings approximately, ignore case
+Abool Execute::streq(char *a, char *b) {
+ Common::String str1 = a;
+ Common::String str2 = b;
+ bool result = str1.equalsIgnoreCase(str2);
+
+ free(a);
+ free(b);
+
+ return result;
+}
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
diff --git a/engines/glk/alan2/execute.h b/engines/glk/alan2/execute.h
new file mode 100644
index 0000000000..82d5c070c6
--- /dev/null
+++ b/engines/glk/alan2/execute.h
@@ -0,0 +1,134 @@
+/* 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 GLK_ALAN2_EXECUTE
+#define GLK_ALAN2_EXECUTE
+
+#include "glk/alan2/acode.h"
+#include "glk/alan2/rules.h"
+#include "common/list.h"
+
+namespace Glk {
+namespace Alan2 {
+
+class Execute {
+public:
+ Execute() {}
+
+ void sys(Aword fpos, Aword len);
+ bool confirm(MsgKind msgno);
+ Aptr attribute(Aword item, Aword atr);
+ void say(Aword item);
+ void saynum(Aword num);
+ void saystr(char *str);
+ Aptr strattr(Aword id, Aword atr);
+ void setstr(Aword id, Aword atr, Aword str);
+ void getstr(Aword fpos, Aword len);
+ void print(Aword fpos, Aword len);
+ void look();
+ void make(Aword id, Aword atr, Aword val);
+ void set(Aword id, Aword atr, Aword val);
+ void incr(Aword id, Aword atr, Aword step);
+ void decr(Aword id, Aword atr, Aword step);
+ void use(Aword act, Aword scr);
+ void describe(Aword id);
+ void list(Aword cnt);
+ void locate(Aword id, Aword whr);
+ void empty(Aword cnt, Aword whr);
+ void score(Aword sc);
+ void visits(Aword v);
+
+ void eventchk();
+ void schedule(Aword evt, Aword whr, Aword aft);
+ void cancl(Aword evt);
+
+ void quit();
+ void restart();
+ void sayint(Aword val);
+ Aword rnd(Aword from, Aword to);
+ Abool btw(Aint val, Aint from, Aint to);
+ Aword contains(Aptr string, Aptr substring);
+ Abool streq(char a[], char b[]);
+ Abool in(Aword obj, Aword cnt);
+ Aword where(Aword item);
+ Aint agrmax(Aword atr, Aword whr);
+ Aint agrsum(Aword atr, Aword whr);
+ Aint agrcount(Aword whr);
+ Abool isHere(Aword item);
+ Abool isNear(Aword item);
+
+private:
+ bool exitto(int to, int from);
+ int count(int cnt);
+ int sumatr(Aword atr, Aword cnt);
+ bool checklim(Aword cnt, Aword obj);
+ Aptr getatr(Aaddr atradr, Aaddr atr);
+ void setatr(Aaddr atradr, Aword atr, Aword val);
+ void makloc(Aword loc, Aword atr, Aword val);
+ void makobj(Aword obj, Aword atr, Aword val);
+ void makact(Aword act, Aword atr, Aword val);
+ void setloc(Aword loc, Aword atr, Aword val);
+ void setobj(Aword obj, Aword atr, Aword val);
+ void setact(Aword act, Aword atr, Aword val);
+ void incratr(Aaddr atradr, Aword atr, Aword step);
+ void incrloc(Aword loc, Aword atr, Aword step);
+ void incrobj(Aword obj, Aword atr, Aword step);
+ void incract(Aword act, Aword atr, Aword step);
+ Aword objloc(Aword obj);
+ Aword actloc(Aword act);
+ void locobj(Aword obj, Aword whr);
+ void locact(Aword act, Aword whr);
+ Abool objhere(Aword obj);
+ Aword acthere(Aword act);
+ Aword objnear(Aword obj);
+ Aword actnear(Aword act);
+ void sayloc(Aword loc);
+ void sayobj(Aword obj);
+ void sayact(Aword act);
+ void saylit(Aword lit);
+ void sayarticle(Aword id);
+ void dscrloc(Aword loc);
+ void dscrobj(Aword obj);
+ void dscract(Aword act);
+ void dscrobjs();
+ void dscracts();
+ Aptr locatr(Aword loc, Aword atr);
+ Aptr objatr(Aword obj, Aword atr);
+ Aptr actatr(Aword act, Aword atr);
+ Aptr litatr(Aword lit, Aword atr);
+
+ // The event queue
+ EvtqElem *eventq; // Event queue
+ int etop; // Event queue top pointer
+
+ // Amachine data structures
+ ActElem *_acts; // Actor table pointer
+ LocElem *_locs; // Location table pointer
+ ObjElem *_objs; // Object table pointer
+ CntElem *_cnts; // Container table pointer
+ Common::Stack<Aword> _describeStack;
+};
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/interpreter.cpp b/engines/glk/alan2/interpreter.cpp
new file mode 100644
index 0000000000..c8c53a0b4a
--- /dev/null
+++ b/engines/glk/alan2/interpreter.cpp
@@ -0,0 +1,768 @@
+/* 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/alan2/acode.h"
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/execute.h"
+#include "glk/alan2/interpreter.h"
+#include "glk/alan2/saveload.h"
+#include "glk/alan2/types.h"
+#include "common/debug.h"
+
+namespace Glk {
+namespace Alan2 {
+
+// TODO: Refactor these into debug flags
+const bool trcflg = false;
+const bool stpflg = false;
+
+void Interpreter::if_(Aword v) {
+ int lev = 1;
+ Aword i;
+
+ if (!v) {
+ // Skip to next ELSE or ENDIF on same level
+ while (true) {
+ i = _vm->memory[_vm->pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP) {
+ switch (I_OP(i)) {
+ case I_ELSE:
+ if (lev == 1)
+ return;
+ break;
+ case I_IF:
+ lev++;
+ break;
+ case I_ENDIF:
+ lev--;
+ if (lev == 0)
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Interpreter::else_() {
+ int lev = 1;
+ Aword i;
+
+ while (true) {
+ // Skip to ENDIF on the same level
+ i = _vm->memory[_vm->pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP) {
+ switch (I_OP(i)) {
+ case I_ENDIF:
+ lev--;
+ if (lev == 0)
+ return;
+ break;
+ case I_IF:
+ lev++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void Interpreter::depstart() {
+ // A DEPSTART was executed, so skip across the redundant DEPCASE to
+ // start at the first expression
+ _vm->pc++;
+}
+
+void Interpreter::swap() {
+ Aptr v1 = _stack->pop();
+ Aptr v2 = _stack->pop();
+
+ _stack->push(v1);
+ _stack->push(v2);
+}
+
+void Interpreter::depexec(Aword v) {
+ int lev = 1;
+ Aword i;
+
+ if (!v) {
+ // The expression was not true, skip to next CASE on the same
+ // level which could be a DEPCASE or DEPELSE
+ while (true) {
+ i = _vm->memory[_vm->pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP) {
+ switch (I_OP(i)) {
+ case I_DEPSTART:
+ lev++;
+ break;
+ case I_DEPEND:
+ if (lev == 1)
+ return;
+ lev--;
+ break;
+ case I_DEPCASE:
+ case I_DEPELSE:
+ if (lev == 1)
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Interpreter::depcase() {
+ int lev = 1;
+ Aword i;
+
+ // Skip to end of DEPENDING block (next DEPEND on same level) because
+ // we have just executed a DEPCASE/DEPELSE statement as a result of a DEPCASE
+ // catching
+
+ while (true) {
+ i = _vm->memory[_vm->pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP) {
+ switch (I_OP(i)) {
+ case I_DEPSTART:
+ lev++;
+ break;
+ case I_DEPEND:
+ lev--;
+ if (lev == 0) return;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void Interpreter::curVar(Aword i) {
+ switch (I_OP(i)) {
+ case V_PARAM:
+ if (stpflg)
+ debug("PARAM \t%5ld\t\t(%ld)", _stack->top(), _vm->params[_stack->top() - 1].code);
+ _stack->push(_vm->params[_stack->pop() - 1].code);
+ break;
+ case V_CURLOC:
+ if (stpflg)
+ debug("CURLOC \t\t\t(%d)", _vm->cur.loc);
+ _stack->push(_vm->cur.loc);
+ break;
+ case V_CURACT:
+ if (stpflg)
+ debug("CURACT \t\t\t(%d)", _vm->cur.act);
+ _stack->push(_vm->cur.act);
+ break;
+ case V_CURVRB:
+ if (stpflg)
+ debug("CURVRB \t\t\t(%d)", _vm->cur.vrb);
+ _stack->push(_vm->cur.vrb);
+ break;
+ case V_SCORE:
+ if (stpflg)
+ debug("CURSCORE \t\t\t(%d)", _vm->cur.score);
+ _stack->push(_vm->cur.score);
+ break;
+ default:
+ error("Unknown CURVAR instruction.");
+ }
+}
+
+void Interpreter::adaptOldOpcodes(Aword i) {
+ switch (I_OP(i)) {
+ case I_AND:
+ case I_OR:
+ case I_NE:
+ case I_EQ:
+ case I_STREQ:
+ case I_STREXACT:
+ case I_LE:
+ case I_GE:
+ case I_LT:
+ case I_GT:
+ case I_PLUS:
+ case I_MINUS:
+ case I_MULT:
+ case I_DIV:
+ if (_vm->header->vers[0] == 2 && _vm->header->vers[1] == 7) // Check for 2.7 version
+ swap();
+ }
+}
+
+void Interpreter::stMop(Aword i, Aaddr oldpc) {
+ Aptr arg1, arg2, arg3, lh, rh;
+
+ adaptOldOpcodes(i);
+
+ switch (I_OP(i)) {
+ case I_PRINT:
+ arg1 = _stack->pop(); // fpos
+ arg2 = _stack->pop(); // len
+ if (stpflg) {
+ debug("PRINT \t%5ld, %5ld\t\"", arg1, arg2);
+ _vm->col = 34; // To format it better!
+ }
+ _execute->print(arg1, arg2);
+ if (stpflg)
+ debug("\"");
+ break;
+ case I_SYSTEM:
+ arg1 = _stack->pop(); // fpos
+ arg2 = _stack->pop(); // len
+ if (stpflg) {
+ debug("SYSTEM \t%5ld, %5ld\t\"", arg1, arg2);
+ _vm->col = 34; // To format it better!
+ }
+ _execute->sys(arg1, arg2);
+ break;
+ case I_GETSTR:
+ arg1 = _stack->pop(); // fpos
+ arg2 = _stack->pop(); // len
+ if (stpflg)
+ debug("GETSTR\t%5ld, %5ld", arg1, arg2);
+ _execute->getstr(arg1, arg2);
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_QUIT:
+ if (stpflg)
+ debug("QUIT");
+ _execute->quit();
+ break;
+ case I_LOOK:
+ if (stpflg)
+ debug("LOOK");
+ _execute->look();
+ break;
+ case I_SAVE:
+ if (stpflg)
+ debug("SAVE");
+ _saveLoad->save();
+ break;
+ case I_RESTORE:
+ if (stpflg)
+ debug("RESTORE");
+ _saveLoad->restore();
+ break;
+ case I_RESTART:
+ if (stpflg)
+ debug("RESTART");
+ _execute->restart();
+ break;
+ case I_LIST:
+ arg1 = _stack->pop(); // cnt
+ if (stpflg)
+ debug("LIST \t%5ld", arg1);
+ _execute->list(arg1);
+ break;
+ case I_EMPTY:
+ arg1 = _stack->pop(); // cnt
+ arg2 = _stack->pop(); // whr
+ if (stpflg)
+ debug("EMPTY \t%5ld, %5ld", arg1, arg2);
+ _execute->empty(arg1, arg2);
+ break;
+ case I_SCORE:
+ arg1 = _stack->pop(); // score
+ if (stpflg)
+ debug("SCORE \t%5ld\t\t(%ld)", arg1, _vm->scores[arg1 - 1]);
+ _execute->score(arg1);
+ break;
+ case I_VISITS:
+ arg1 = _stack->pop(); // visits
+ if (stpflg)
+ debug("VISITS \t%5ld", arg1);
+ _execute->visits(arg1);
+ break;
+ case I_SCHEDULE:
+ arg1 = _stack->pop(); // evt
+ arg2 = _stack->pop(); // whr
+ arg3 = _stack->pop(); // aft
+ if (stpflg)
+ debug("SCHEDULE \t%5ld, %5ld, %5ld", arg1, arg2, arg3);
+ _execute->schedule(arg1, arg2, arg3);
+ break;
+ case I_CANCEL:
+ arg1 = _stack->pop(); // evt
+ if (stpflg)
+ debug("CANCEL \t%5ld", arg1);
+ _execute->cancl(arg1);
+ break;
+ case I_MAKE:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ arg3 = _stack->pop(); // val
+ if (stpflg) {
+ debug("MAKE \t%5ld, %5ld, ", arg1, arg2);
+ if (arg3)
+ debug("TRUE");
+ else
+ debug("FALSE");
+ }
+ _execute->make(arg1, arg2, arg3);
+ break;
+ case I_SET:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ arg3 = _stack->pop(); // val
+ if (stpflg)
+ debug("SET \t%5ld, %5ld, %5ld", arg1, arg2, arg3);
+ _execute->set(arg1, arg2, arg3);
+ break;
+ case I_STRSET:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ arg3 = _stack->pop(); // str
+ if (stpflg)
+ debug("STRSET\t%5ld, %5ld, %5ld", arg1, arg2, arg3);
+ _execute->setstr(arg1, arg2, arg3);
+ break;
+ case I_INCR:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ arg3 = _stack->pop(); // step
+ if (stpflg)
+ debug("INCR\t%5ld, %5ld, %5ld", arg1, arg2, arg3);
+ _execute->incr(arg1, arg2, arg3);
+ break;
+ case I_DECR:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ arg3 = _stack->pop(); // step
+ if (stpflg)
+ debug("DECR\t%5ld, %5ld, %5ld", arg1, arg2, arg3);
+ _execute->decr(arg1, arg2, arg3);
+ break;
+ case I_ATTRIBUTE:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ if (stpflg)
+ debug("ATTRIBUTE %5ld, %5ld", arg1, arg2);
+ _stack->push(_execute->attribute(arg1, arg2));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_STRATTR:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // atr
+ if (stpflg)
+ debug("STRATTR \t%5ld, %5ld", arg1, arg2);
+ _stack->push(_execute->strattr(arg1, arg2));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_LOCATE:
+ arg1 = _stack->pop(); // id
+ arg2 = _stack->pop(); // whr
+ if (stpflg)
+ debug("LOCATE \t%5ld, %5ld", arg1, arg2);
+ _execute->locate(arg1, arg2);
+ break;
+ case I_WHERE:
+ arg1 = _stack->pop(); // id
+ if (stpflg)
+ debug("WHERE \t%5ld", arg1);
+ _stack->push(_execute->where(arg1));
+ if (stpflg)
+ debug("\t\t(%ld)", _stack->top());
+ break;
+ case I_HERE:
+ arg1 = _stack->pop(); // id
+ if (stpflg)
+ debug("HERE \t%5ld", arg1);
+ _stack->push(_execute->isHere(arg1));
+ if (stpflg) {
+ if (_stack->top())
+ debug("\t(TRUE)");
+ else
+ debug("\t(FALSE)");
+ }
+ break;
+ case I_NEAR:
+ arg1 = _stack->pop(); // id
+ if (stpflg)
+ debug("NEAR \t%5ld", arg1);
+ _stack->push(_execute->isNear(arg1));
+ if (stpflg) {
+ if (_stack->top())
+ debug("\t(TRUE)");
+ else
+ debug("\t(FALSE)");
+ }
+ break;
+ case I_USE:
+ arg1 = _stack->pop(); // act
+ arg2 = _stack->pop(); // scr
+ if (stpflg)
+ debug("USE \t%5ld, %5ld", arg1, arg2);
+ _execute->use(arg1, arg2);
+ break;
+ case I_IN:
+ arg1 = _stack->pop(); // obj
+ arg2 = _stack->pop(); // cnt
+ if (stpflg)
+ debug("IN \t%5ld, %5ld ", arg1, arg2);
+ _stack->push(_execute->in(arg1, arg2));
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_DESCRIBE:
+ arg1 = _stack->pop(); // id
+ if (stpflg) {
+ debug("DESCRIBE \t%5ld\t", arg1);
+ _vm->col = 34; // To format it better!
+ }
+ _execute->describe(arg1);
+ break;
+ case I_SAY:
+ arg1 = _stack->pop(); // id
+ if (stpflg)
+ debug("SAY \t%5ld\t\t\"", arg1);
+ _execute->say(arg1);
+ if (stpflg)
+ debug("\"");
+ break;
+ case I_SAYINT:
+ arg1 = _stack->pop(); // val
+ if (stpflg)
+ debug("SAYINT\t%5ld\t\t\"", arg1);
+ _execute->sayint(arg1);
+ if (stpflg)
+ debug("\"");
+ break;
+ case I_SAYSTR:
+ arg1 = _stack->pop(); // adr
+ if (stpflg)
+ debug("SAYSTR\t%5ld\t\t\"", arg1);
+ _execute->saystr((char *)arg1);
+ if (stpflg)
+ debug("\"");
+ break;
+ case I_IF:
+ arg1 = _stack->pop(); // v
+ if (stpflg) {
+ debug("IF \t");
+ if (arg1) debug(" TRUE"); else debug("FALSE");
+ }
+ if_(arg1);
+ break;
+ case I_ELSE:
+ if (stpflg)
+ debug("ELSE");
+ else_();
+ break;
+ case I_ENDIF:
+ if (stpflg)
+ debug("ENDIF");
+ break;
+ case I_AND:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg) {
+ debug("AND \t");
+ if (lh) debug("TRUE, "); else debug("FALSE, ");
+ if (rh) debug("TRUE"); else debug("FALSE");
+ }
+ _stack->push(lh && rh);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_OR:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg) {
+ debug("OR \t");
+ if (lh) debug("TRUE, "); else debug("FALSE, ");
+ if (rh) debug("TRUE"); else debug("FALSE");
+ }
+ _stack->push(lh || rh);
+ if (stpflg) {
+ if (_stack->top())
+ debug("\t(TRUE)");
+ else
+ debug("\t(FALSE)");
+ }
+ break;
+ case I_NE:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("NE \t%5ld, %5ld", lh, rh);
+ _stack->push(lh != rh);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_EQ:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("EQ \t%5ld, %5ld", lh, rh);
+ _stack->push(lh == rh);
+ if (stpflg) {
+ if (_stack->top())
+ debug("\t(TRUE)");
+ else
+ debug("\t(FALSE)");
+ }
+ break;
+ case I_STREQ:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("STREQ \t%5ld, %5ld", lh, rh);
+ _stack->push(_execute->streq((char *)lh, (char *)rh));
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_STREXACT:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("STREXACT \t%5ld, %5ld", lh, rh);
+ _stack->push(strcmp((char *)lh, (char *)rh) == 0);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ free((void *)lh);
+ free((void *)rh);
+ break;
+ case I_LE:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("LE \t%5ld, %5ld", lh, rh);
+ _stack->push(lh <= rh);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_GE:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("GE \t%5ld, %5ld", lh, rh);
+ _stack->push(lh >= rh);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_LT:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("LT \t%5ld, %5ld", lh, rh);
+ _stack->push((signed int)lh < (signed int)rh);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_GT:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("GT \t%5ld, %5ld", lh, rh);
+ _stack->push(lh > rh);
+ if (stpflg)
+ if (_stack->top()) debug("\t(TRUE)"); else debug("\t(FALSE)");
+ break;
+ case I_PLUS:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("PLUS \t%5ld, %5ld", lh, rh);
+ _stack->push(lh + rh);
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_MINUS:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("MINUS \t%5ld, %5ld", lh, rh);
+ _stack->push(lh - rh);
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_MULT:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("MULT \t%5ld, %5ld", lh, rh);
+ _stack->push(lh * rh);
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_DIV:
+ rh = _stack->pop();
+ lh = _stack->pop();
+ if (stpflg)
+ debug("DIV \t%5ld, %5ld", lh, rh);
+ _stack->push(lh / rh);
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_NOT:
+ arg1 = _stack->pop(); // val
+ if (stpflg) {
+ debug("NOT \t");
+ if (arg1) debug("TRUE"); else debug("FALSE");
+ }
+ _stack->push(!arg1);
+ if (stpflg)
+ if (_stack->top()) debug("\t\t(TRUE)"); else debug("\t\t(FALSE)");
+ break;
+ case I_MAX:
+ arg1 = _stack->pop(); // atr
+ arg2 = _stack->pop(); // whr
+ if (stpflg)
+ debug("MAX \t%5ld, %5ld", arg1, arg2);
+ _stack->push(_execute->agrmax(arg1, arg2));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_SUM:
+ arg1 = _stack->pop(); // atr
+ arg2 = _stack->pop(); // whr
+ if (stpflg)
+ debug("SUM \t%5ld, %5ld", arg1, arg2);
+ _stack->push(_execute->agrsum(arg1, arg2));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_COUNT:
+ arg1 = _stack->pop(); // whr
+ if (stpflg)
+ debug("COUNT \t%5ld", arg1);
+ _stack->push(_execute->agrcount(arg1));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_RND:
+ arg1 = _stack->pop(); // from
+ arg2 = _stack->pop(); // to
+ if (stpflg)
+ debug("RANDOM \t%5ld, %5ld", arg1, arg2);
+ _stack->push(_execute->rnd(arg1, arg2));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_BTW:
+ arg1 = _stack->pop(); // high
+ arg2 = _stack->pop(); // low
+ arg3 = _stack->pop(); // val
+ if (stpflg)
+ debug("BETWEEN \t%5ld, %5ld, %5ld", arg1, arg2, arg3);
+ _stack->push(_execute->btw(arg1, arg2, arg3));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_CONTAINS:
+ arg1 = _stack->pop(); // substring
+ arg2 = _stack->pop(); // string
+ if (stpflg)
+ debug("CONTAINS \t%5ld, %5ld", arg2, arg1);
+ _stack->push(_execute->contains(arg2, arg1));
+ if (stpflg)
+ debug("\t(%ld)", _stack->top());
+ break;
+ case I_DEPSTART:
+ if (stpflg)
+ debug("DEPSTART");
+ depstart();
+ break;
+ case I_DEPCASE:
+ if (stpflg)
+ debug("DEPCASE");
+ depcase();
+ break;
+ case I_DEPEXEC:
+ arg1 = _stack->pop(); // v
+ if (stpflg) {
+ debug("DEPEXEC \t");
+ if (arg1) debug(" TRUE"); else debug("FALSE");
+ }
+ depexec(arg1);
+ break;
+ case I_DEPELSE:
+ if (stpflg)
+ debug("DEPELSE");
+ depcase();
+ break;
+ case I_DEPEND:
+ if (stpflg)
+ debug("DEPEND");
+ break;
+ case I_RETURN:
+ if (stpflg)
+ debug("RETURN\n--------------------------------------------------\n");
+ _vm->pc = oldpc;
+ return;
+ default:
+ error("Unknown STMOP instruction.");
+ }
+
+ if (_vm->fail) {
+ _vm->pc = oldpc;
+ return; // TODO: Return true/false, and stop execution if necessary
+ }
+}
+
+void Interpreter::interpret(Aaddr adr) {
+ Aaddr oldpc = _vm->pc;
+ Aword i;
+
+ if (stpflg)
+ debug("\n++++++++++++++++++++++++++++++++++++++++++++++++++");
+
+ _vm->pc = adr;
+
+ while(true) {
+ if (stpflg)
+ debug("\n%4x: ", _vm->pc);
+
+ if (_vm->pc > _vm->memTop)
+ error("Interpreting outside program.");
+
+ i = _vm->memory[_vm->pc++];
+
+ switch (I_CLASS(i)) {
+ case C_CONST:
+ if (stpflg)
+ debug("PUSH \t%5ld", I_OP(i));
+ _stack->push(I_OP(i));
+ break;
+ case C_CURVAR:
+ curVar(i);
+ break;
+ case C_STMOP:
+ stMop(i, oldpc);
+ break;
+ default:
+ error("Unknown instruction class.");
+ }
+ }
+}
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
diff --git a/engines/glk/alan2/interpreter.h b/engines/glk/alan2/interpreter.h
new file mode 100644
index 0000000000..dbc1558079
--- /dev/null
+++ b/engines/glk/alan2/interpreter.h
@@ -0,0 +1,59 @@
+/* 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 GLK_ALAN2_INTERPRETER
+#define GLK_ALAN2_INTERPRETER
+
+#include "glk/alan2/acode.h"
+#include "glk/alan2/alan2.h"
+
+namespace Glk {
+namespace Alan2 {
+
+class Execute;
+class SaveLoad;
+
+class Interpreter {
+public:
+ Interpreter(Execute *execute, SaveLoad *saveLoad, Alan2Stack *stack) : _execute(execute), _saveLoad(saveLoad), _stack(stack) {}
+ void interpret(Aaddr adr);
+
+private:
+ void if_(Aword v);
+ void else_();
+ void depstart();
+ void swap();
+ void depexec(Aword v);
+ void depcase();
+ void curVar(Aword i);
+ void stMop(Aword i, Aaddr oldpc);
+ void adaptOldOpcodes(Aword i);
+
+ Execute *_execute;
+ SaveLoad *_saveLoad;
+ Alan2Stack *_stack;
+};
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/parse.cpp b/engines/glk/alan2/parse.cpp
new file mode 100644
index 0000000000..ebb0af94bf
--- /dev/null
+++ b/engines/glk/alan2/parse.cpp
@@ -0,0 +1,976 @@
+/* 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 "common/stack.h"
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/execute.h"
+#include "glk/alan2/interpreter.h"
+#include "glk/alan2/parse.h"
+#include "glk/alan2/types.h"
+#include "glk/alan2/util.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "decode.h"
+
+namespace Glk {
+namespace Alan2 {
+
+// All procedures for getting a command and turning it into a list of
+// dictionary entries are placed here.
+
+Parser::Parser() {
+ wrds[0] = EOF;
+
+ // TODO
+}
+
+void Parser::unknown(char *inputStr) {
+ Common::String str = Common::String::format("'%s'?", inputStr);
+
+ // TODO
+#if 0
+#if ISO == 0
+ fromIso(str, str);
+#endif
+#endif
+
+ _vm->output(str);
+ eol = true;
+ _vm->printError(M_UNKNOWN_WORD);
+}
+
+int Parser::lookup(char *wrd) {
+#if 0
+ for (int i = 0; !endOfTable(&dict[i]); i++) {
+ if (strcmp(wrd, (char *) addrTo(dict[i].wrd)) == 0)
+ return (i);
+ }
+#endif
+
+ unknown(wrd);
+ return(EOF);
+}
+
+char *Parser::gettoken(char *tokenBuffer) {
+ static char *marker;
+ static char oldch;
+
+ if (tokenBuffer == NULL)
+ *marker = oldch;
+ else
+ marker = tokenBuffer;
+
+ while (*marker != '\0' && Common::isSpace(*marker) && *marker != '\n') marker++;
+ tokenBuffer = marker;
+
+ if (Common::isAlpha(*marker))
+ while (*marker && (Common::isAlpha(*marker) || Common::isDigit(*marker) || *marker == '\'')) marker++;
+ else if (Common::isDigit(*marker))
+ while (Common::isDigit(*marker)) marker++;
+ else if (*marker == '\"') {
+ marker++;
+ while (*marker != '\"') marker++;
+ marker++;
+ } else if (*marker == '\0' || *marker == '\n')
+ return NULL;
+ else
+ marker++;
+
+ oldch = *marker;
+ *marker = '\0';
+ return tokenBuffer;
+}
+
+void Parser::agetline() {
+ static char buf[LISTLEN + 1]; // The input buffer
+ static char isobuf[LISTLEN + 1]; // The input buffer in ISO
+
+ _vm->paragraph();
+
+ // TODO
+#if 0
+
+ do {
+#if defined(HAVE_ANSI) || defined(GLK)
+ statusline();
+#endif
+ debug("> ");
+
+#if 0
+ if (logflg)
+ fprintf(logfil, "> ");
+#endif
+
+#ifdef USE_READLINE
+ if (!readline(buf)) {
+ newline();
+ quit();
+ }
+#else
+ if (fgets(buf, LISTLEN, stdin) == NULL) {
+ newline();
+ quit();
+ }
+#endif
+
+ getPageSize();
+ anyOutput = FALSE;
+
+ if (logflg)
+ fprintf(logfil, "%s\n", buf);
+
+#if ISO == 0
+ toIso(isobuf, buf, NATIVECHARSET);
+#else
+ strcpy(isobuf, buf);
+#endif
+
+ token = gettoken(isobuf);
+
+ if (token != NULL && strcmp("debug", token) == 0 && _vm->header->debug) {
+ dbgflg = true;
+ debug();
+ token = NULL;
+ }
+ } while (token == NULL);
+
+ eol = false;
+ lin = 1;
+
+#endif
+}
+
+void Parser::scan() {
+ int i;
+ int w;
+ char *str;
+
+ agetline();
+ wrds[0] = 0;
+
+ for (i = 0; i < litCount; i++)
+ if (litValues[i].type == TYPSTR && litValues[i].value != 0)
+ free((char *) litValues[i].value);
+
+ litCount = 0;
+
+ do {
+ if (Common::isAlpha(token[0])) {
+ Common::String tmp = token;
+ tmp.toLowercase();
+ strcpy(token, tmp.c_str());
+
+ w = lookup(token);
+
+ // TODO
+ //if (!isNoise(w))
+ // wrds[i++] = w;
+ } else if (Common::isDigit(token[0])) {
+ if (litCount > MAXPARAMS)
+ error("Too many parameters.");
+
+ wrds[i++] = dictsize + litCount; // Word outside dictionary = literal
+ litValues[litCount].type = TYPNUM;
+ litValues[litCount++].value = atoi(token);
+ } else if (token[0] == '\"') {
+ if (litCount > MAXPARAMS)
+ error("Too many parameters.");
+
+ wrds[i++] = dictsize + litCount; // Word outside dictionary = literal
+ litValues[litCount].type = TYPSTR;
+
+ // Remove the string quotes while copying
+ Common::String tmp = token;
+ tmp.deleteChar(0);
+ tmp.deleteLastChar();
+ strcpy(str, tmp.c_str());
+
+ litValues[litCount++].value = (Aptr) str;
+ } else if (token[0] == ',') {
+ //wrds[i++] = conjWord; // TODO
+ } else
+ unknown(token);
+
+ wrds[i] = EOF;
+ eol = (token = gettoken(NULL)) == NULL;
+ } while (!eol);
+}
+
+// Search for a non-verb command
+void Parser::nonverb() {
+ if (isDir(wrds[wrdidx])) {
+ wrdidx++;
+ if (wrds[wrdidx] != EOF && !isConj(wrds[wrdidx]))
+ _vm->printError(M_WHAT);
+// TODO
+#if 0
+ else
+ go(dict[wrds[wrdidx-1]].code);
+#endif
+
+ if (wrds[wrdidx] != EOF)
+ wrdidx++;
+ } else
+ _vm->printError(M_WHAT);
+}
+
+Abool Parser::objhere(Aword obj) {
+ if (isCnt(objs[obj - OBJMIN].loc)) { // In something?
+ if (isObj(objs[obj - OBJMIN].loc) || isAct(objs[obj - OBJMIN].loc))
+ return(isHere(objs[obj - OBJMIN].loc));
+// TODO
+#if 0
+ else // If the container wasn't anywhere, assume where HERO is!
+ return(where(HERO) == _vm->cur.loc);
+#endif
+
+ } else
+ return(objs[obj - OBJMIN].loc == _vm->cur.loc);
+}
+
+
+Aword Parser::acthere(Aword act) {
+ return(acts[act - ACTMIN].loc == _vm->cur.loc);
+}
+
+Abool Parser::isHere(Aword id) {
+ if (isObj(id))
+ return objhere(id);
+ else if (isAct(id))
+ return acthere(id);
+ else
+ error("Can't HERE item (%ld).", (unsigned long)id);
+}
+
+// ----------------------------------------------------------------------------
+
+// Build a list of objects matching 'all'
+void Parser::buildall(ParamElem list[]) {
+ int o, i = 0;
+ bool found = false;
+
+ for (o = OBJMIN; o <= OBJMAX; o++) {
+ if (isHere(o)) {
+ found = true;
+ list[i].code = o;
+ list[i++].firstWord = EOF;
+ }
+ }
+
+ if (!found)
+ _vm->printError(M_WHAT_ALL);
+ else
+ list[i].code = EOF;
+}
+
+void Parser::listCopy(ParamElem a[], ParamElem b[]) {
+ int i;
+
+ for (i = 0; b[i].code != EOF; i++)
+ a[i] = b[i];
+
+ a[i].code = EOF;
+}
+
+bool Parser::listContains(ParamElem l[], Aword e) {
+ int i;
+
+ for (i = 0; l[i].code != EOF && l[i].code != e; i++);
+
+ return (l[i].code == e);
+}
+
+void Parser::listIntersection(ParamElem a[], ParamElem b[]) {
+ int i, last = 0;
+
+ for (i = 0; a[i].code != EOF; i++)
+ if (listContains(b, a[i].code))
+ a[last++] = a[i];
+
+ a[last].code = EOF;
+}
+
+// Copy the refs (in dictionary) to a paramList
+void Parser::listCopyFromDictionary(ParamElem p[], Aword r[]) {
+ int i;
+
+ for (i = 0; r[i] != EOF; i++) {
+ p[i].code = r[i];
+ p[i].firstWord = EOF;
+ }
+
+ p[i].code = EOF;
+}
+
+int Parser::listLength(ParamElem a[]) {
+ int i = 0;
+
+ while (a[i].code != EOF)
+ i++;
+
+ return (i);
+}
+
+// Compact a list, i.e remove any NULL elements
+void Parser::listCompact(ParamElem a[]) {
+ int i, j;
+
+ for (i = 0, j = 0; a[j].code != EOF; j++)
+ if (a[j].code != 0)
+ a[i++] = a[j];
+
+ a[i].code = EOF;
+}
+
+// Merge the paramElems of one list into the first
+void Parser::listMerge(ParamElem a[], ParamElem b[]) {
+ int i, last;
+
+ for (last = 0; a[last].code != EOF; last++); // Find end of list
+
+ for (i = 0; b[i].code != EOF; i++) {
+ if (!listContains(a, b[i].code)) {
+ a[last++] = b[i];
+ a[last].code = EOF;
+ }
+ }
+}
+
+// Subtract two lists
+void Parser::listSubtract(ParamElem a[], ParamElem b[]) {
+ for (int i = 0; a[i].code != EOF; i++)
+ if (listContains(b, a[i].code))
+ a[i].code = 0; // Mark empty
+
+ listCompact(a);
+}
+
+// Match an unambigous object reference
+void Parser::unambig(ParamElem plst[]) {
+ int i;
+ bool found = false; // Adjective or noun found?
+ static ParamElem *refs; // Entities referenced by word
+ static ParamElem *savlst; // Saved list for backup at EOF
+ int firstWord, lastWord; // The words the player used
+
+ if (refs == NULL)
+ refs = new ParamElem[MAXENTITY + 1];
+
+ if (savlst == NULL)
+ savlst = new ParamElem[MAXENTITY + 1];
+
+ if (isLiteral(wrds[wrdidx])) {
+ // Transform the word into a reference to the literal value
+ plst[0].code = wrds[wrdidx++]-dictsize+LITMIN;
+ plst[0].firstWord = EOF; // No words used!
+ plst[1].code = EOF;
+ return;
+ }
+
+ plst[0].code = EOF; // Make empty
+
+ if (isIt(wrds[wrdidx])) {
+ wrdidx++;
+ // Use last object in previous command!
+ for (i = listLength(pparams)-1; i >= 0 && (pparams[i].code == 0 || pparams[i].code >= LITMIN); i--);
+
+ if (i < 0)
+ _vm->printError(M_WHAT_IT);
+
+ if (!isHere(pparams[i].code)) {
+ params[0].code = pparams[i].code;
+ params[0].firstWord = EOF;
+ params[1].code = EOF;
+ _vm->printError(M_NO_SUCH);
+ }
+
+ plst[0] = pparams[i];
+ plst[0].firstWord = EOF; // No words used!
+ plst[1].code = EOF;
+ return;
+ }
+
+ firstWord = wrdidx;
+
+ while (wrds[wrdidx] != EOF && isAdj(wrds[wrdidx])) {
+ // If this word can be a noun and there is no noun following break loop
+ if (isNoun(wrds[wrdidx]) && (wrds[wrdidx+1] == EOF || !isNoun(wrds[wrdidx+1])))
+ break;
+
+ listCopyFromDictionary(refs, (Aword *)addrTo(dict[wrds[wrdidx]].adjrefs));
+ listCopy(savlst, plst); // To save it for backtracking
+
+ if (found)
+ listIntersection(plst, refs);
+ else {
+ listCopy(plst, refs);
+ found = true;
+ }
+
+ wrdidx++;
+ }
+
+ if (wrds[wrdidx] != EOF) {
+ if (isNoun(wrds[wrdidx])) {
+ listCopyFromDictionary(refs, (Aword *)addrTo(dict[wrds[wrdidx]].nounrefs));
+ if (found)
+ listIntersection(plst, refs);
+ else {
+ listCopy(plst, refs);
+ found = true;
+ }
+
+ wrdidx++;
+ } else
+ _vm->printError(M_NOUN);
+ } else if (found) {
+ if (isNoun(wrds[wrdidx-1])) {
+ // Perhaps the last word was also a noun?
+ listCopy(plst, savlst); // Restore to before last adjective
+ listCopyFromDictionary(refs, (Aword *)addrTo(dict[wrds[wrdidx-1]].nounrefs));
+ if (plst[0].code == EOF)
+ listCopy(plst, refs);
+ else
+ listIntersection(plst, refs);
+ } else
+ _vm->printError(M_NOUN);
+ }
+
+ lastWord = wrdidx - 1;
+
+ // Allow remote objects, but resolve ambiguities by presence
+ if (listLength(plst) > 1) {
+ for (i=0; plst[i].code != EOF; i++)
+ if (!isHere(plst[i].code))
+ plst[i].code = 0;
+
+ listCompact(plst);
+ }
+
+ if (listLength(plst) > 1 || (found && listLength(plst) == 0)) {
+ params[0].code = 0; /* Just make it anything != EOF */
+ params[0].firstWord = firstWord; /* Remember words for errors below */
+ params[0].lastWord = lastWord;
+ params[1].code = EOF; /* But be sure to terminate */
+
+ if (listLength(plst) > 1)
+ _vm->printError(M_WHICH_ONE);
+ else if (found && listLength(plst) == 0)
+ _vm->printError(M_NO_SUCH);
+ } else {
+ plst[0].firstWord = firstWord;
+ plst[0].lastWord = lastWord;
+ }
+}
+
+// Match a simple verb command
+void Parser::simple(ParamElem olst[]) {
+ static ParamElem *tlst = NULL;
+ int savidx = wrdidx;
+ bool savplur = false;
+ int i;
+
+ if (tlst == NULL)
+ tlst = new ParamElem[MAXENTITY + 1];
+
+ tlst[0].code = EOF;
+
+ for (;;) {
+ if (isThem(wrds[wrdidx])) {
+ plural = true;
+
+ for (i = 0; pmlst[i].code != EOF; i++)
+ if (!isHere(pmlst[i].code))
+ pmlst[i].code = 0;
+
+ listCompact(pmlst);
+
+ if (listLength(pmlst) == 0)
+ _vm->printError(M_WHAT_THEM);
+
+ listCopy(olst, pmlst);
+ olst[0].firstWord = EOF; // No words used
+ wrdidx++;
+ } else {
+ unambig(olst); // Look for unambigous noun phrase
+
+ if (listLength(olst) == 0) { // Failed!
+ listCopy(olst, tlst);
+ wrdidx = savidx;
+ plural = savplur;
+ return;
+ }
+ }
+
+ listMerge(tlst, olst);
+
+ if (wrds[wrdidx] != EOF
+ && (isConj(wrds[wrdidx])
+ && (isAdj(wrds[wrdidx+1]) || isNoun(wrds[wrdidx+1])))) {
+ // More parameters in a conjunction separated list ?
+ savplur = plural;
+ savidx = wrdidx;
+ wrdidx++;
+ plural = true;
+ } else {
+ listCopy(olst, tlst);
+ return;
+ }
+ }
+}
+
+// Match a complex verb command
+void Parser::complex(ParamElem olst[]) {
+ // Above this procedure we can use the is* tests, but not below since
+ // they work on words.Below all is converted to indices into the
+ // entity tables.Particularly this goes for literals...
+
+ static ParamElem *alst = NULL;
+
+ if (alst == NULL)
+ alst = new ParamElem[MAXENTITY + 1];
+
+ if (isAll(wrds[wrdidx])) {
+ plural = true;
+ buildall(alst); // Build list of all objects
+ wrdidx++;
+ if (wrds[wrdidx] != EOF && isBut(wrds[wrdidx])) {
+ wrdidx++;
+ simple(olst);
+
+ if (listLength(olst) == 0)
+ _vm->printError(M_AFTER_BUT);
+
+ listSubtract(alst, olst);
+ if (listLength(alst) == 0)
+ _vm->printError(M_NOT_MUCH);
+ }
+
+ listCopy(olst, alst);
+ allLength = listLength(olst);
+ } else
+ simple(olst); // Look for simple noun group
+}
+
+bool Parser::claCheck(ClaElem *cla) {
+ bool ok = false;
+
+ if ((cla->classes&(Aword)CLA_OBJ) != 0)
+ ok = ok || isObj(params[cla->code-1].code);
+ if ((cla->classes&(Aword)CLA_CNT) != 0)
+ ok = ok || isCnt(params[cla->code-1].code);
+ if ((cla->classes&(Aword)CLA_ACT) != 0)
+ ok = ok || isAct(params[cla->code-1].code);
+ if ((cla->classes&(Aword)CLA_NUM) != 0)
+ ok = ok || isNum(params[cla->code-1].code);
+ if ((cla->classes&(Aword)CLA_STR) != 0)
+ ok = ok || isStr(params[cla->code-1].code);
+ if ((cla->classes&(Aword)CLA_COBJ) != 0)
+ ok = ok || (isCnt(params[cla->code-1].code) && isObj(params[cla->code-1].code));
+ if ((cla->classes&(Aword)CLA_CACT) != 0)
+ ok = ok || (isCnt(params[cla->code-1].code) && isAct(params[cla->code-1].code));
+
+ return ok;
+}
+
+// In case the syntax did not indicate omnipotent powers (allowed
+// access to remote object), we need to remove non - present parameters
+void Parser::resolve(ParamElem plst[]) {
+ if (allLength > 0)
+ return; // ALL has already done this
+
+ // Resolve ambiguities by presence
+ for (int i = 0; plst[i].code != EOF; i++) {
+ if (plst[i].code < LITMIN) // Literals are always 'here'
+ if (!isHere(plst[i].code)) {
+ params[0] = plst[i]; // Copy error param as first one for message
+ params[1].code = EOF; // But be sure to terminate
+ _vm->printError(M_NO_SUCH);
+ }
+ }
+}
+
+bool Parser::endOfTable(StxElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool Parser::endOfTable(ElmElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool Parser::endOfTable(ClaElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool Parser::endOfTable(VrbElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool Parser::endOfTable(AltElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool Parser::endOfTable(ChkElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+/**
+ * Find the verb alternative wanted in a verb list and return
+ * the address to it.
+ *
+ * @param vrbsadr Address to start of list
+ * @param param Which parameter to match
+ */
+AltElem *Parser::findalt(Aword vrbsadr, Aword param) {
+ VrbElem *vrb;
+ AltElem *alt;
+
+ if (vrbsadr == 0)
+ return NULL;
+
+ for (vrb = (VrbElem *)addrTo(vrbsadr); !endOfTable(vrb); vrb++) {
+ if (vrb->code == _vm->cur.vrb) {
+ for (alt = (AltElem *)addrTo(vrb->alts); !endOfTable(alt); alt++)
+ if (alt->param == param || alt->param == 0)
+ return alt;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Tries a check, returns TRUE if it passed, FALSE otherwise
+ *
+ * @param adr ACODE address to check table
+ * @param act Act if it fails?
+ */
+bool Parser::trycheck(Aaddr adr, bool act) {
+ ChkElem *chk = (ChkElem *)addrTo(adr);
+
+ if (chk->exp == 0) {
+ _vm->_interpreter->interpret(chk->stms);
+ return false;
+ } else {
+ while (!endOfTable(chk)) {
+ _vm->_interpreter->interpret(chk->exp);
+ if (!(Abool)_vm->_stack->pop()) {
+ if (act)
+ _vm->_interpreter->interpret(chk->stms);
+ return false;
+ }
+ chk++;
+ }
+ return true;
+ }
+}
+
+// Check if current action is possible according to the CHECKs.
+bool Parser::possible() {
+ AltElem *alt[MAXPARAMS + 2]; // List of alt-pointers, one for each param
+ int i; // Parameter index
+
+ _vm->fail = false;
+ alt[0] = findalt(_vm->header->vrbs, 0);
+
+ // Perform global checks
+ if (alt[0] != 0 && alt[0]->checks != 0) {
+ if (!trycheck(alt[0]->checks, false))
+ return false;
+ if (_vm->fail)
+ return false;
+ }
+
+ // Now CHECKs in this location
+ alt[1] = findalt(locs[_vm->cur.loc - LOCMIN].vrbs, 0);
+ if (alt[1] != 0 && alt[1]->checks != 0)
+ if (!trycheck(alt[1]->checks, false))
+ return false;
+
+ for (i = 0; params[i].code != EOF; i++) {
+ alt[i + 2] = findalt(objs[params[i].code - OBJMIN].vrbs, i + 1);
+ // CHECKs in a possible parameter
+ if (alt[i + 2] != 0 && alt[i + 2]->checks != 0)
+ if (!trycheck(alt[i + 2]->checks, false))
+ return false;
+ }
+
+ for (i = 0; i < 2 || params[i - 2].code != EOF; i++)
+ if (alt[i] != 0 && alt[i]->action != 0)
+ break;
+ if (i >= 2 && params[i - 2].code == EOF)
+ // Didn't find any code for this verb/object combination
+ return false;
+ else
+ return true;
+}
+
+void Parser::tryMatch(ParamElem mlst[]) {
+ ElmElem *elms; // Pointer to element list
+ StxElem *stx; // Pointer to syntax list
+ ClaElem *cla; // Pointer to class definitions
+ bool anyPlural = false; // Any parameter that was plural?
+ int i, p;
+ static ParamElem *tlst = NULL; // List of params found by complex()
+ static bool *checked = NULL; // Corresponding parameter checked?
+
+ if (tlst == NULL) {
+ tlst = new ParamElem[MAXENTITY + 1];
+ checked = new bool[MAXENTITY + 1];
+ }
+
+ for (stx = stxs; !endOfTable(stx); stx++)
+ if (stx->code == vrbcode)
+ break;
+
+ if (endOfTable(stx))
+ _vm->printError(M_WHAT);
+
+ elms = (ElmElem *) addrTo(stx->elms);
+
+ while (true) {
+ // End of input?
+ if (wrds[wrdidx] == EOF || isConj(wrds[wrdidx])) {
+ while (!endOfTable(elms) && elms->code != EOS)
+ elms++;
+
+ if (endOfTable(elms))
+ _vm->printError(M_WHAT);
+ else
+ break;
+ } else {
+ // A preposition?
+ if (isPrep(wrds[wrdidx])) {
+ while (!endOfTable(elms) && elms->code != dict[wrds[wrdidx]].code)
+ elms++;
+
+ if (endOfTable(elms))
+ _vm->printError(M_WHAT);
+ else
+ wrdidx++;
+ } else {
+ // Must be a parameter!
+ while (!endOfTable(elms) && elms->code != 0)
+ elms++;
+
+ if (endOfTable(elms))
+ _vm->printError(M_WHAT);
+
+ // Get it!
+ plural = false;
+ complex(tlst);
+
+ if (listLength(tlst) == 0) // No object!?
+ _vm->printError(M_WHAT);
+
+ if ((elms->flags & OMNIBIT) == 0) // Omnipotent parameter?
+ resolve(tlst); // If its not an omnipotent parameter, resolve by presence
+
+ if (plural) {
+ if ((elms->flags & MULTIPLEBIT) == 0) // Allowed multiple?
+ _vm->printError(M_MULTIPLE);
+ else {
+ // Mark this as the multiple position in which to insert
+ // actual parameter values later
+ params[paramidx++].code = 0;
+ listCopy(mlst, tlst);
+ anyPlural = true;
+ }
+ } else
+ params[paramidx++] = tlst[0];
+ params[paramidx].code = EOF;
+ }
+
+ elms = (ElmElem *) addrTo(elms->next);
+ }
+ }
+
+ // Now perform class checks
+ if (elms->next == 0) // No verb code, verb not declared!
+ _vm->printError(M_CANT0);
+
+ for (p = 0; params[p].code != EOF; p++) /* Mark all parameters unchecked */
+ checked[p] = false;
+
+ for (cla = (ClaElem *) addrTo(elms->next); !endOfTable(cla); cla++) {
+ if (params[cla->code - 1].code == 0) {
+ // This was a multiple parameter, so check all and remove failing
+ for (i = 0; mlst[i].code != EOF; i++) {
+ params[cla->code-1] = mlst[i];
+ if (!claCheck(cla)) {
+ // Multiple could be both an explicit list of params and an ALL
+ if (allLength == 0) {
+ char marker[80];
+ // It wasn't ALL, we need to say something about it, so
+ // prepare a printout with $1/2/3
+ sprintf(marker, "($%ld)", (unsigned long) cla->code);
+ _vm->output(marker);
+ _vm->_interpreter->interpret(cla->stms);
+ _vm->paragraph();
+ }
+
+ mlst[i].code = 0; // In any case remove it from the list
+ }
+ }
+
+ params[cla->code - 1].code = 0;
+ } else {
+ if (!claCheck(cla)) {
+ _vm->_interpreter->interpret(cla->stms);
+ _vm->printError(MSGMAX); // Return to player without saying anything
+ }
+ }
+
+ checked[cla->code - 1] = true; // Remember that it's already checked
+ }
+
+ // Now check the rest of the parameters, must be objects
+ for (p = 0; params[p].code != EOF; p++) {
+ if (!checked[p]) {
+ if (params[p].code == 0) {
+ // This was a multiple parameter, check all and remove failing
+ for (i = 0; mlst[i].code != EOF; i++) {
+ if (mlst[i].code != 0 && !isObj(mlst[i].code)) // Skip any empty slots
+ mlst[i].code = 0;
+ }
+ } else if (!isObj(params[p].code))
+ _vm->printError(M_CANT0);
+ }
+ }
+
+ // Set verb code
+ _vm->cur.vrb = ((Aword *) cla)[1]; // Take first word after end of table!
+
+ // Finally, if ALL was used, try to find out what was applicable
+ if (allLength > 0) {
+ for (p = 0; params[p].code != 0; p++); // Find multiple marker
+
+ for (i = 0; i < allLength; i++) {
+ if (mlst[i].code != 0) { // Already empty?
+ params[p] = mlst[i];
+
+ if (!possible())
+ mlst[i].code = 0; // Remove this from list
+ }
+ }
+
+ params[p].code = 0; // Restore multiple marker
+ listCompact(mlst);
+
+ if (listLength(mlst) == 0) {
+ params[0].code = EOF;
+ _vm->printError(M_WHAT_ALL);
+ }
+ } else if (anyPlural) {
+ listCompact(mlst);
+
+ // If there where multiple parameters but non left, exit without a
+ // word, assuming we have already said enough
+ if (listLength(mlst) == 0)
+ _vm->printError(MSGMAX);
+ }
+
+ plural = anyPlural; // Remember that we found plural objects
+}
+
+// Find the verb class (not used currently) and 'tryMatch()'
+void Parser::match(ParamElem *mlst) {
+ tryMatch(mlst); // try to understand what the user said
+
+ if (wrds[wrdidx] != EOF && !isConj(wrds[wrdidx]))
+ _vm->printError(M_WHAT);
+ if (wrds[wrdidx] != EOF) // More on this line?
+ wrdidx++; // If so skip the AND
+}
+
+// Execute all activities commanded.Handles possible multiple actions
+// such as THEM or lists of objects.
+void Parser::action(ParamElem plst[]) {
+ int i, mpos;
+ char marker[10];
+
+ if (plural) {
+ // The code == 0 means this is a multiple position. We must loop
+ // over this position (and replace it by each present in the plst)
+ for (mpos = 0; params[mpos].code != 0; mpos++); // Find multiple position
+
+ sprintf(marker, "($%d)", mpos + 1); // Prepare a printout with $1/2/3
+
+ for (i = 0; plst[i].code != EOF; i++) {
+ params[mpos] = plst[i];
+ _vm->output(marker);
+ //do_it(); // TODO
+
+ if (plst[i + 1].code != EOF)
+ _vm->paragraph();
+ }
+
+ params[mpos].code = 0;
+ } //else // TODO
+ //do_it();
+}
+
+void Parser::parse() {
+ if (mlst == NULL) { // Allocate large enough paramlists
+ mlst = new ParamElem[MAXENTITY + 1];
+ mlst[0].code = EOF;
+ pmlst = new ParamElem[MAXENTITY + 1];
+ params = new ParamElem[MAXENTITY + 1];
+ params[0].code = EOF;
+ pparams = new ParamElem[MAXENTITY + 1];
+ }
+
+ if (wrds[wrdidx] == EOF) {
+ wrdidx = 0;
+ scan();
+ } else if (false/*anyOutput*/) // TODO
+ _vm->paragraph();
+
+ allLength = 0;
+ paramidx = 0;
+ listCopy(pparams, params);
+ params[0].code = EOF;
+ listCopy(pmlst, mlst);
+ mlst[0].code = EOF;
+
+ if (isVerb(wrds[wrdidx])) {
+ vrbwrd = wrds[wrdidx];
+ vrbcode = dict[vrbwrd].code;
+ wrdidx++;
+ match(mlst);
+ action(mlst); // mlst contains possible multiple params
+ } else {
+ params[0].code = EOF;
+ pmlst[0].code = EOF;
+ nonverb();
+ }
+}
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
diff --git a/engines/glk/alan2/parse.h b/engines/glk/alan2/parse.h
new file mode 100644
index 0000000000..4642e74ee0
--- /dev/null
+++ b/engines/glk/alan2/parse.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 GLK_ALAN2_PARSE
+#define GLK_ALAN2_PARSE
+
+namespace Glk {
+namespace Alan2 {
+
+#define LISTLEN 100
+
+class Parser {
+public:
+ Parser();
+
+ /**
+ * Parse a new player command
+ */
+ void parse();
+
+private:
+ void unknown(char *inputStr);
+ int lookup(char *wrd);
+ char *gettoken(char *tokenBuffer);
+ void agetline();
+ void scan();
+ void nonverb();
+ Abool objhere(Aword obj);
+ Aword acthere(Aword act);
+ Abool isHere(Aword id);
+ void buildall(ParamElem list[]);
+ void listCopy(ParamElem a[], ParamElem b[]);
+ bool listContains(ParamElem l[], Aword e);
+ void listIntersection(ParamElem a[], ParamElem b[]);
+ void listCopyFromDictionary(ParamElem p[], Aword r[]);
+ int listLength(ParamElem a[]);
+ void listCompact(ParamElem a[]);
+ void listMerge(ParamElem a[], ParamElem b[]);
+ void listSubtract(ParamElem a[], ParamElem b[]);
+ void unambig(ParamElem plst[]);
+ void simple(ParamElem olst[]);
+ void complex(ParamElem olst[]);
+ bool claCheck(ClaElem *cla);
+ void resolve(ParamElem plst[]);
+ bool endOfTable(StxElem *addr);
+ bool endOfTable(ElmElem *addr);
+ bool endOfTable(ClaElem *addr);
+ bool endOfTable(VrbElem *addr);
+ bool endOfTable(AltElem *addr);
+ bool endOfTable(ChkElem *addr);
+ AltElem *findalt(Aword vrbsadr, Aword param);
+ bool trycheck(Aaddr adr, bool act);
+ bool possible();
+ void tryMatch(ParamElem mlst[]);
+ void match(ParamElem *mlst);
+ void action(ParamElem plst[]);
+
+ // TODO: Initialize / move these
+ int wrds[LISTLEN / 2]; // List of parsed words
+ int wrdidx; // and an index into it
+
+ bool plural = false;
+
+ // Syntax Parameters
+ int paramidx; // Index in params
+ ParamElem *params; // List of params
+ static ParamElem *pparams; // Previous parameter list
+ static ParamElem *mlst; // Multiple objects list
+ static ParamElem *pmlst; // Previous multiple list
+
+ StxElem *stxs; // Syntax table pointer
+ LocElem *locs; // Location table pointer
+
+ // Literals
+ LitElem litValues[MAXPARAMS + 1];
+
+ // What did the user say?
+ int vrbwrd; // The word he used
+ int vrbcode; // The code for that verb
+
+ bool eol; // Looking at End of line? Yes, initially
+ char *token;
+
+ WrdElem *dict; // Dictionary pointer
+ int dictsize;
+
+ int allLength; // No. of objects matching 'all'
+};
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/rules.cpp b/engines/glk/alan2/rules.cpp
new file mode 100644
index 0000000000..14c867133f
--- /dev/null
+++ b/engines/glk/alan2/rules.cpp
@@ -0,0 +1,87 @@
+/* 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/alan2/acode.h"
+#include "glk/alan2/interpreter.h"
+#include "glk/alan2/rules.h"
+#include "glk/alan2/types.h"
+#include "common/debug.h"
+
+namespace Glk {
+namespace Alan2 {
+
+// TODO: Refactor these into debug flags
+const bool trcflg = false;
+const bool stpflg = false;
+
+void Rules::parseRules() {
+ bool change = true;
+ int i;
+
+ for (i = 1; !endOfTable(&_ruls[i - 1]); i++)
+ _ruls[i - 1].run = false;
+
+ while (change) {
+ change = false;
+ for (i = 1; !endOfTable(&_ruls[i - 1]); i++) {
+ if (!_ruls[i - 1].run) {
+ if (trcflg) {
+ debug("\n<RULE %d (at ", i);
+ //debugsay(cur.loc); // TODO
+ if (!stpflg)
+ debug("), Evaluating");
+ else
+ debug("), Evaluating:>\n");
+ }
+
+ _interpreter->interpret(_ruls[i - 1].exp);
+
+ if (_stack->pop()) {
+ change = true;
+ _ruls[i - 1].run = true;
+
+ if (trcflg)
+ if (!stpflg)
+ debug(", Executing:>\n");
+ else {
+ debug("\nRULE %d (at ", i);
+ //debugsay(cur.loc); // TODO
+ debug("), Executing:>\n");
+ }
+
+ _interpreter->interpret(_ruls[i - 1].stms);
+
+ } else if (trcflg && !stpflg) {
+ debug(":>\n");
+ }
+ }
+ }
+ }
+}
+
+bool Rules::endOfTable(RulElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
diff --git a/engines/glk/alan2/rules.h b/engines/glk/alan2/rules.h
new file mode 100644
index 0000000000..9d5e8bf119
--- /dev/null
+++ b/engines/glk/alan2/rules.h
@@ -0,0 +1,51 @@
+/* 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 GLK_ALAN2_RULES
+#define GLK_ALAN2_RULES
+
+#include "common/stack.h"
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/types.h"
+
+namespace Glk {
+namespace Alan2 {
+
+class Interpreter;
+
+class Rules {
+public:
+ Rules(RulElem *rules, Alan2Stack *stack, Interpreter *interpreter) : _ruls(rules), _stack(stack), _interpreter(interpreter) {}
+ void parseRules();
+
+private:
+ bool endOfTable(RulElem *addr);
+
+ RulElem *_ruls;
+ Alan2Stack *_stack;
+ Interpreter *_interpreter;
+};
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/saveload.cpp b/engines/glk/alan2/saveload.cpp
new file mode 100644
index 0000000000..c68c54d481
--- /dev/null
+++ b/engines/glk/alan2/saveload.cpp
@@ -0,0 +1,218 @@
+/* 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 "common/savefile.h"
+#include "glk/alan2/acode.h"
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/saveload.h"
+#include "glk/alan2/types.h"
+
+namespace Glk {
+namespace Alan2 {
+
+void SaveLoad::save() {
+ int i;
+ Common::SaveFileManager *saveFileMan = _vm->getSaveFileManager();
+ Common::OutSaveFile *saveFile = nullptr;
+ Common::String str;
+ AtrElem *atr;
+
+ // TODO
+#if 0
+ frefid_t fref = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Write, 0);
+ if (fref == NULL)
+ _vm->printError(M_SAVEFAILED);
+
+ strcpy(str, garglk_fileref_get_name(fref));
+ glk_fileref_destroy(fref);
+#endif
+
+ if (str.empty())
+ str = _prevSaveName;
+
+ _vm->col = 1;
+
+ // TODO
+#if 0
+ if ((savfil = fopen(str, READ_MODE)) != NULL)
+ // It already existed
+ if (!confirm(M_SAVEOVERWRITE))
+ _vm->printError(MSGMAX); // Return to player without saying anything
+
+ if ((savfil = fopen(str, WRITE_MODE)) == NULL)
+ _vm->printError(M_SAVEFAILED);
+#endif
+
+ saveFile = saveFileMan->openForSaving(str);
+
+ _prevSaveName = str;
+
+ // Save version of interpreter and name of game
+ saveFile->write(_vm->header->vers, sizeof(Aword));
+ saveFile->writeString(_gameName);
+ saveFile->writeByte('\0');
+ // Save current values
+ saveFile->write(&_vm->cur, sizeof(_vm->cur));
+
+ // Save actors
+ for (i = ACTMIN; i <= ACTMAX; i++) {
+ saveFile->writeUint32LE(_actors[i - ACTMIN].loc);
+ saveFile->writeUint32LE(_actors[i - ACTMIN].script);
+ saveFile->writeUint32LE(_actors[i - ACTMIN].step);
+ saveFile->writeUint32LE(_actors[i - ACTMIN].count);
+ if (_actors[i - ACTMIN].atrs)
+ for (atr = (AtrElem *)addrTo(_actors[i - ACTMIN].atrs); !endOfTable(atr); atr++)
+ saveFile->writeUint32LE(atr->val);
+ }
+
+ // Save locations
+ for (i = LOCMIN; i <= LOCMAX; i++) {
+ saveFile->writeUint32LE(_locations[i - LOCMIN].describe);
+ if (_locations[i - LOCMIN].atrs)
+ for (atr = (AtrElem *)addrTo(_locations[i - LOCMIN].atrs); !endOfTable(atr); atr++)
+ saveFile->writeUint32LE(atr->val);
+ }
+
+ // Save objects
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ saveFile->writeUint32LE(_objects[i - OBJMIN].loc);
+ if (_objects[i - OBJMIN].atrs)
+ for (atr = (AtrElem *)addrTo(_objects[i - OBJMIN].atrs); !endOfTable(atr); atr++)
+ saveFile->writeUint32LE(atr->val);
+ }
+
+ // Save the event queue
+ _events[*_eventTop].time = 0; // Mark the top
+ for (i = 0; i < *_eventTop + 1; i++)
+ saveFile->write(&_events[i], sizeof(_events[0]));
+
+ // Save scores
+ for (i = 0; _vm->scores[i] != EOF; i++)
+ saveFile->writeUint32LE(_vm->scores[i]);
+
+ delete saveFile;
+}
+
+void SaveLoad::restore() {
+ int i, tmp;
+ Common::SaveFileManager *saveFileMan = _vm->getSaveFileManager();
+ Common::InSaveFile *saveFile = nullptr;
+ Common::String str;
+ AtrElem *atr;
+ char savedVersion[4];
+ char savedName[256];
+
+ // TODO
+#if 0
+ frefid_t fref = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Read, 0);
+ if (fref == NULL)
+ _vm->printError(M_SAVEFAILED);
+
+ strcpy(str, garglk_fileref_get_name(fref));
+ glk_fileref_destroy(fref);
+#endif
+
+ _vm->col = 1;
+
+ if (str.empty())
+ str = _prevSaveName; // Use the name temporarily
+
+ if ((saveFile = saveFileMan->openForLoading(str)) == nullptr) {
+ _vm->printError(M_SAVEMISSING);
+ return;
+ }
+
+ _prevSaveName = str; // Save it for future use
+
+ saveFile->read(savedVersion, sizeof(Aword));
+
+ // 4f - save file version check doesn't seem to work on PC's!
+ if (strncmp(savedVersion, _vm->header->vers, 4)) {
+ delete saveFile;
+ _vm->printError(M_SAVEVERS);
+ return;
+ }
+
+ i = 0;
+
+ while ((savedName[i++] = saveFile->readByte()) != '\0');
+
+ if (savedName != _gameName) {
+ delete saveFile;
+ _vm->printError(M_SAVENAME);
+ return;
+ }
+
+ // Restore current values
+ saveFile->read(&_vm->cur, sizeof(_vm->cur));
+
+ // Restore actors
+ for (i = ACTMIN; i <= ACTMAX; i++) {
+ _actors[i - ACTMIN].loc = saveFile->readUint32LE();
+ _actors[i - ACTMIN].script = saveFile->readUint32LE();
+ _actors[i - ACTMIN].step = saveFile->readUint32LE();
+ _actors[i - ACTMIN].count = saveFile->readUint32LE();
+
+ if (_actors[i - ACTMIN].atrs)
+ for (atr = (AtrElem *)addrTo(_actors[i - ACTMIN].atrs); !endOfTable(atr); atr++)
+ atr->val = saveFile->readUint32LE();
+ }
+
+ // Restore locations
+ for (i = LOCMIN; i <= LOCMAX; i++) {
+ _locations[i - LOCMIN].describe = saveFile->readUint32LE();
+ if (_locations[i - LOCMIN].atrs)
+ for (atr = (AtrElem *)addrTo(_locations[i - LOCMIN].atrs); !endOfTable(atr); atr++)
+ atr->val = saveFile->readUint32LE();
+ }
+
+ // Restore objects
+ for (i = OBJMIN; i <= OBJMAX; i++) {
+ _objects[i - OBJMIN].loc = saveFile->readUint32LE();
+ if (_objects[i - OBJMIN].atrs)
+ for (atr = (AtrElem *)addrTo(_objects[i - OBJMIN].atrs); !endOfTable(atr); atr++)
+ atr->val = saveFile->readUint32LE();
+ }
+
+ // Restore the event queue
+ *_eventTop = 0;
+ do {
+ saveFile->read(&_events[*_eventTop], sizeof(_events[0]));
+ (*_eventTop)++;
+ } while (_events[*_eventTop - 1].time != 0);
+
+ (*_eventTop)--;
+
+ // Restore scores
+ for (i = 0; _vm->scores[i] != EOF; i++)
+ _vm->scores[i] = saveFile->readUint32LE();
+
+ delete saveFile;
+}
+
+bool SaveLoad::endOfTable(AtrElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
diff --git a/engines/glk/alan2/saveload.h b/engines/glk/alan2/saveload.h
new file mode 100644
index 0000000000..32a02bb5c4
--- /dev/null
+++ b/engines/glk/alan2/saveload.h
@@ -0,0 +1,55 @@
+/* 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 GLK_ALAN2_SAVELOAD
+#define GLK_ALAN2_SAVELOAD
+
+#include "common/scummsys.h"
+#include "glk/alan2/types.h"
+
+namespace Glk {
+namespace Alan2 {
+
+class SaveLoad {
+public:
+ SaveLoad(Common::String &gameName, ActElem *actors, LocElem *locations, ObjElem *objects, EvtqElem *events, int *eventTop) :
+ _gameName(gameName), _actors(actors), _locations(locations), _objects(objects), _events(events), _eventTop(eventTop) {}
+ void save();
+ void restore();
+private:
+ bool endOfTable(AtrElem *addr);
+
+ Common::String _prevSaveName;
+
+ // Save state related variables
+ Common::String _gameName;
+ ActElem *_actors;
+ LocElem *_locations;
+ ObjElem *_objects;
+ EvtqElem *_events;
+ int *_eventTop;
+};
+
+} // End of namespace Alan2
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan2/types.h b/engines/glk/alan2/types.h
new file mode 100644
index 0000000000..b403cf997c
--- /dev/null
+++ b/engines/glk/alan2/types.h
@@ -0,0 +1,271 @@
+/* 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 GLK_ALAN2_TYPES
+#define GLK_ALAN2_TYPES
+
+#include "glk/alan2/acode.h"
+
+namespace Glk {
+namespace Alan2 {
+
+// CONSTANTS
+
+#define ACTMIN (_vm->header->actmin)
+#define ACTMAX (_vm->header->actmax)
+#define OBJMIN (_vm->header->objmin)
+#define OBJMAX (_vm->header->objmax)
+#define LOCMIN (_vm->header->locmin)
+#define LOCMAX (_vm->header->locmax)
+#define CNTMIN (_vm->header->cntmin)
+#define CNTMAX (_vm->header->cntmax)
+#define LITMIN (_vm->header->locmax + 1)
+#define LITMAX (_vm->header->locmax + 1 + litCount)
+#define EVTMIN (_vm->header->evtmin)
+#define EVTMAX (_vm->header->evtmax)
+
+#define HERO ACTMIN
+
+
+#define addrTo(x) (&_vm->memory[x])
+
+// The word classes are represented as numbers but in the dictonary they are generated as bits
+#define isVerb(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_VRB))!=0)
+#define isConj(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_CONJ))!=0)
+#define isBut(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_BUT))!=0)
+#define isThem(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_THEM))!=0)
+#define isIt(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_IT))!=0)
+#define isNoun(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_NOUN))!=0)
+#define isAdj(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_ADJ))!=0)
+#define isPrep(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_PREP))!=0)
+#define isAll(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_ALL))!=0)
+#define isDir(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_DIR))!=0)
+#define isNoise(word) (word < dictsize && (dict[word].wordClass&((Aword)1L<<WRD_NOISE))!=0)
+#define isLiteral(word) (word >= dictsize)
+
+
+// TYPES
+
+// Amachine variables
+typedef struct CurVars {
+ int
+ vrb,
+ obj,
+ loc,
+ act,
+ tick,
+ score,
+ visits;
+} CurVars;
+
+// The various tables
+typedef struct WrdElem { // Dictionary
+ Aaddr wrd; // ACODE address to string
+ Aword wordClass; // Word class
+ Aword code;
+ Aaddr adjrefs; // Address to reference list
+ Aaddr nounrefs; // Address to reference list
+} WrdElem;
+
+typedef struct ActElem { // ACTOR TABLE
+ Aword loc; // Location
+ Abool describe; // Description flag
+ Aaddr nam; // Address to name printing code
+ Aaddr atrs; // Address to attribute list
+ Aword cont; // Code for the container props if any
+ Aword script; // Which script is he using
+ Aaddr scradr; // Address to script table
+ Aword step;
+ Aword count;
+ Aaddr vrbs;
+ Aaddr dscr; // Address of description code
+} ActElem;
+
+typedef struct ScrElem { // SCRIPT TABLE
+ Aword code; // Script number
+ Aaddr dscr; // Optional description statements
+ Aaddr steps; // Address to steps
+} ScrElem;
+
+typedef struct StepElem { // STEP TABLE
+ Aword after; // After how many ticks?
+ Aaddr exp; // Address to expression saying when
+ Aaddr stm; // Address to the actual code
+} StepElem;
+
+typedef struct LocElem { // LOCATION TABLE
+ Aaddr nams; // Address of name printing code
+ Aaddr dscr; // Address of description code
+ Aaddr does; // Address of does code
+ Aword describe; // Description flag & counter
+ Aaddr atrs; // Address of attribute list
+ Aaddr exts; // Address of exit list
+ Aaddr vrbs; // Address of local verb list
+} LocElem;
+
+typedef struct ExtElem { // EXIT TABLE structure
+ Abool done; // Flag for reverse/convert process
+ Aword code; // Direction code
+ Aaddr checks; // Address of check table
+ Aaddr action; // Address of action code
+ Aword next; // Number of next location
+} ExtElem;
+
+typedef struct ChkElem { // CHECK TABLE
+ Aaddr exp; // ACODE address to expression code
+ Aaddr stms; // ACODE address to statement code
+} ChkElem;
+
+typedef struct VrbElem { // VERB TABLE
+ Aword code; // Code for the verb
+ Aaddr alts; // Address to alternatives
+} VrbElem;
+
+typedef struct StxElem { // SYNTAX TABLE
+ Aword code; // Code for verb word
+ Aaddr elms; // Address to element tables
+} StxElem;
+
+typedef struct ElmElem26 { // ELEMENT TABLES
+ Aword code; // Code for this element, 0 -> parameter
+ Abool multiple; // May be multiple (if parameter)
+ Aaddr next; // Address to next element table ...
+ // ... or class check if EOS
+} ElmElem26;
+
+typedef struct ElmElem { // ELEMENT TABLES
+ Aword code; // Code for this element, 0 -> parameter
+ Aword flags; // Flags for multiple/omni (if parameter)
+ // CHANGED: v2.7 from Abool for multiple
+ Aaddr next; // Address to next element table ...
+ // ... or class check if EOS
+} ElmElem;
+
+typedef struct ClaElem { // CLASS DEFINITION TABLE
+ Aword code; // Parameter number
+ Aword classes; // Parameter classes
+ Aaddr stms; // Exception statements
+} ClaElem;
+
+typedef struct AltElem { // VERB ALTERNATIVE TABLE
+ Abool done; // Flag for patching (reverse/convert) process
+ Aword param; // Parameter number
+ Aword qual; // Verb execution qualifier
+ Aaddr checks; // Address of the check table
+ Aaddr action; // Address of the action code
+} AltElem;
+
+typedef struct AtrElem { // ATTRIBUTE LIST
+ Aword val; // Its value
+ Aaddr stradr; // Address to the name
+} AtrElem;
+
+typedef struct ObjElem25 { // OBJECT TABLE of 2.5 format
+ Aword loc; // Current location
+ Abool describe; // Describe flag
+ Aaddr atrs; // Address of attribute list
+ Aword cont; // Index to container properties if any
+ Aaddr vrbs; // Address to local verb table
+ Aaddr dscr1; // Address to Aword description code
+ Aaddr dscr2; // Address to short description code
+} ObjElem25;
+
+typedef struct ObjElem { // OBJECT TABLE
+ Aword loc; // Current location
+ Abool describe; // Describe flag
+ Aaddr atrs; // Address of attribute list
+ Aword cont; // Index to container properties if any
+ Aaddr vrbs; // Address to local verb table
+ Aaddr dscr1; // Address to Aword description code
+ Aaddr art; // Article printing code? Else use default
+ // INTRODUCED: v2.6
+ Aaddr dscr2; // Address to short description code
+} ObjElem;
+
+typedef struct CntElem { // CONTAINER TABLE
+ Aaddr lims; // Address to limit check code
+ Aaddr header; // Address to header code
+ Aaddr empty; // Address to empty code
+ Aword parent; // Object or actor index
+ Aaddr nam; // Address to statement printing name
+} CntElem;
+
+typedef struct LimElem { // LIMIT Type
+ Aword atr; // Attribute that limits
+ Aword val; // And the limiting value
+ Aaddr stms; // Statements if fail
+} LimElem;
+
+typedef struct RulElem { // RULE TABLE
+ Abool run; // Is rule already run?
+ Aaddr exp; // Address to expression code
+ Aaddr stms; // Address to run
+} RulElem;
+
+typedef struct EvtElem { // EVENT TABLE
+ Aaddr stradr; // Address to name string
+ Aaddr code; // Address of code to run
+} EvtElem;
+
+typedef struct EvtqElem { // EVENT QUEUE ELEMENT
+ int time;
+ int event;
+ int where;
+} EvtqElem;
+
+typedef struct IniElem { // STRING INITIALISATION TABLE
+ Aword fpos; // File position
+ Aword len; // Length
+ Aword adr; // Where to store the string
+} IniElem;
+
+typedef struct MsgElem26 { // MESSAGE TABLE
+ Aword fpos; // File position
+ Aword len; // Length of message
+} MsgElem26;
+
+typedef struct MsgElem { // MESSAGE TABLE
+ Aaddr stms; // Address to statements
+ // Changed v2.7 from fpos+len in .dat
+} MsgElem;
+
+
+typedef struct ParamElem { // PARAMETER
+ Aword code; // Code for this parameter (0=multiple)
+ Aword firstWord; // Index to first word used by player
+ Aword lastWord; // d:o to last
+} ParamElem;
+
+typedef enum Type {TYPNUM, TYPSTR} Type;
+
+typedef struct LitElem { // LITERAL
+ Type type;
+ Aptr value;
+} LitElem;
+
+#define MAXPARAMS 9
+#define MAXENTITY (_vm->header->actmax)
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/alan2/util.h b/engines/glk/alan2/util.h
new file mode 100644
index 0000000000..3f5a482cd7
--- /dev/null
+++ b/engines/glk/alan2/util.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 GLK_ALAN2_UTIL
+#define GLK_ALAN2_UTIL
+
+#include "glk/alan2/acode.h"
+#include "glk/alan2/alan2.h"
+#include "glk/alan2/types.h"
+
+namespace Glk {
+namespace Alan2 {
+
+// TODO: Move these
+LitElem litValues[];
+uint32 litCount = 0; // for LITMAX
+extern ActElem *acts; // Actor table pointer
+extern ObjElem *objs; // Object table pointer
+
+// Type checks
+bool isObj(Aword x) {
+ return x >= OBJMIN && x <= OBJMAX;
+}
+
+bool isAct(Aword x) {
+ return x >= ACTMIN && x <= ACTMAX;
+}
+
+bool isCnt(Aword x) {
+ return (x >= CNTMIN && x <= CNTMAX) ||
+ (isObj(x) && objs[x - OBJMIN].cont != 0) ||
+ (isAct(x) && acts[x - ACTMIN].cont != 0);
+}
+
+bool isLoc(Aword x) {
+ return x >= LOCMIN && x <= LOCMAX;
+}
+
+bool isNum(Aword x) {
+ return x >= LITMIN && x <= LITMAX && litValues[x - LITMIN].type == TYPNUM;
+}
+
+bool isStr(Aword x) {
+ return x >= LITMIN && x <= LITMAX && litValues[x - LITMIN].type == TYPSTR;
+}
+
+bool isLit(Aword x) {
+ return x >= LITMIN && x <= LITMAX;
+}
+
+bool endOfTable(LimElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool endOfTable(ScrElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+bool endOfTable(ExtElem *addr) {
+ Aword *x = (Aword *)addr;
+ return *x == EOF;
+}
+
+} // End of namespace Alan2
+} // Engine of namespace GLK
+
+#endif