aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/alan3
diff options
context:
space:
mode:
authorPaul Gilbert2019-06-25 21:18:44 -0700
committerPaul Gilbert2019-07-06 15:27:07 -0700
commitcd7cf4141425e3e77b164d2dbcbae8de1eacb035 (patch)
tree97caacd0a7e26488844ada4e7cd529cae482526a /engines/glk/alan3
parentcfd66173bad8bb23ba7ba2d531fc5e98872212ce (diff)
downloadscummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.tar.gz
scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.tar.bz2
scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.zip
GLK: ALAN3: Initial files commit
Diffstat (limited to 'engines/glk/alan3')
-rw-r--r--engines/glk/alan3/acode.h724
-rw-r--r--engines/glk/alan3/act.cpp124
-rw-r--r--engines/glk/alan3/act.h38
-rw-r--r--engines/glk/alan3/actor.cpp82
-rw-r--r--engines/glk/alan3/actor.h39
-rw-r--r--engines/glk/alan3/alan3.cpp149
-rw-r--r--engines/glk/alan3/alan3.h102
-rw-r--r--engines/glk/alan3/alan_version.cpp42
-rw-r--r--engines/glk/alan3/alan_version.h36
-rw-r--r--engines/glk/alan3/alt_info.cpp381
-rw-r--r--engines/glk/alan3/alt_info.h88
-rw-r--r--engines/glk/alan3/args.cpp159
-rw-r--r--engines/glk/alan3/args.h52
-rw-r--r--engines/glk/alan3/attribute.cpp63
-rw-r--r--engines/glk/alan3/attribute.h38
-rw-r--r--engines/glk/alan3/checkentry.cpp53
-rw-r--r--engines/glk/alan3/checkentry.h56
-rw-r--r--engines/glk/alan3/class.cpp38
-rw-r--r--engines/glk/alan3/class.h40
-rw-r--r--engines/glk/alan3/compatibility.cpp69
-rw-r--r--engines/glk/alan3/compatibility.h42
-rw-r--r--engines/glk/alan3/container.cpp190
-rw-r--r--engines/glk/alan3/container.h45
-rw-r--r--engines/glk/alan3/current.cpp33
-rw-r--r--engines/glk/alan3/current.h53
-rw-r--r--engines/glk/alan3/debug.cpp1129
-rw-r--r--engines/glk/alan3/debug.h58
-rw-r--r--engines/glk/alan3/decode.cpp182
-rw-r--r--engines/glk/alan3/decode.h46
-rw-r--r--engines/glk/alan3/dictionary.cpp135
-rw-r--r--engines/glk/alan3/dictionary.h60
-rw-r--r--engines/glk/alan3/event.cpp35
-rw-r--r--engines/glk/alan3/event.h50
-rw-r--r--engines/glk/alan3/exe.cpp753
-rw-r--r--engines/glk/alan3/exe.h98
-rw-r--r--engines/glk/alan3/fnmatch.cpp234
-rw-r--r--engines/glk/alan3/fnmatch.h83
-rw-r--r--engines/glk/alan3/glkio.cpp50
-rw-r--r--engines/glk/alan3/glkio.h55
-rw-r--r--engines/glk/alan3/glkstart.cpp209
-rw-r--r--engines/glk/alan3/glkstart.h82
-rw-r--r--engines/glk/alan3/instance.cpp1152
-rw-r--r--engines/glk/alan3/instance.h97
-rw-r--r--engines/glk/alan3/inter.cpp1412
-rw-r--r--engines/glk/alan3/inter.h54
-rw-r--r--engines/glk/alan3/lists.cpp65
-rw-r--r--engines/glk/alan3/lists.h50
-rw-r--r--engines/glk/alan3/literal.cpp80
-rw-r--r--engines/glk/alan3/literal.h58
-rw-r--r--engines/glk/alan3/location.cpp130
-rw-r--r--engines/glk/alan3/location.h38
-rw-r--r--engines/glk/alan3/main.cpp893
-rw-r--r--engines/glk/alan3/main.h42
-rw-r--r--engines/glk/alan3/memory.cpp118
-rw-r--r--engines/glk/alan3/memory.h50
-rw-r--r--engines/glk/alan3/msg.cpp107
-rw-r--r--engines/glk/alan3/msg.h56
-rw-r--r--engines/glk/alan3/options.cpp40
-rw-r--r--engines/glk/alan3/options.h47
-rw-r--r--engines/glk/alan3/output.cpp508
-rw-r--r--engines/glk/alan3/output.h55
-rw-r--r--engines/glk/alan3/parameter_position.cpp103
-rw-r--r--engines/glk/alan3/parameter_position.h77
-rw-r--r--engines/glk/alan3/params.cpp333
-rw-r--r--engines/glk/alan3/params.h91
-rw-r--r--engines/glk/alan3/parse.cpp1465
-rw-r--r--engines/glk/alan3/parse.h42
-rw-r--r--engines/glk/alan3/readline.cpp170
-rw-r--r--engines/glk/alan3/readline.h40
-rw-r--r--engines/glk/alan3/resources.h40
-rw-r--r--engines/glk/alan3/reverse.cpp685
-rw-r--r--engines/glk/alan3/reverse.h43
-rw-r--r--engines/glk/alan3/rules.cpp287
-rw-r--r--engines/glk/alan3/rules.h46
-rw-r--r--engines/glk/alan3/save.cpp359
-rw-r--r--engines/glk/alan3/save.h38
-rw-r--r--engines/glk/alan3/scan.cpp255
-rw-r--r--engines/glk/alan3/scan.h45
-rw-r--r--engines/glk/alan3/score.cpp32
-rw-r--r--engines/glk/alan3/score.h37
-rw-r--r--engines/glk/alan3/set.cpp176
-rw-r--r--engines/glk/alan3/set.h64
-rw-r--r--engines/glk/alan3/stack.cpp185
-rw-r--r--engines/glk/alan3/stack.h62
-rw-r--r--engines/glk/alan3/state.cpp358
-rw-r--r--engines/glk/alan3/state.h51
-rw-r--r--engines/glk/alan3/state_stack.cpp128
-rw-r--r--engines/glk/alan3/state_stack.h45
-rw-r--r--engines/glk/alan3/syntax.cpp95
-rw-r--r--engines/glk/alan3/syntax.h44
-rw-r--r--engines/glk/alan3/sysdep.cpp500
-rw-r--r--engines/glk/alan3/sysdep.h197
-rw-r--r--engines/glk/alan3/syserr.cpp103
-rw-r--r--engines/glk/alan3/syserr.h40
-rw-r--r--engines/glk/alan3/term.cpp158
-rw-r--r--engines/glk/alan3/term.h41
-rw-r--r--engines/glk/alan3/types.cpp38
-rw-r--r--engines/glk/alan3/types.h76
-rw-r--r--engines/glk/alan3/utils.cpp108
-rw-r--r--engines/glk/alan3/utils.h42
-rw-r--r--engines/glk/alan3/version.h58
-rw-r--r--engines/glk/alan3/word.cpp82
-rw-r--r--engines/glk/alan3/word.h56
103 files changed, 17942 insertions, 0 deletions
diff --git a/engines/glk/alan3/acode.h b/engines/glk/alan3/acode.h
new file mode 100644
index 0000000000..a399d3ce43
--- /dev/null
+++ b/engines/glk/alan3/acode.h
@@ -0,0 +1,724 @@
+/* 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_ACODE
+#define GLK_ACODE
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define ACODEEXTENSION ".a3c"
+
+typedef uint32 Aptr; /* Type for an ACODE memory address used in the structures */
+/* TODO: Here's the major 32->64bit problem: Aptrs are 32 bit to fit
+ into the 32-bit structure of the Amachine, but sometimes this is
+ used to store a *real* pointer value, which on 64-bit machines are
+ 64bits. */
+
+typedef uint32 Aword; /* Type for an ACODE word */
+typedef uint32 Aaddr; /* Type for an ACODE address */
+typedef uint32 Aid; /* Type for an ACODE Instance Id value */
+typedef int32 Abool; /* Type for an ACODE Boolean value */
+typedef int32 Aint; /* Type for an ACODE Integer value */
+typedef int32 Aset; /* Type for an ACODE Set value */
+typedef int CodeValue; /* Definition for the packing process */
+
+#ifndef TRUE
+#define TRUE (0==0)
+#endif
+#ifndef FALSE
+#define FALSE (!TRUE)
+#endif
+
+/* 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, bit positions */
+typedef int WordKind;
+#define SYNONYM_WORD 0
+#define SYNONYM_BIT (((Aword)1)<<SYNONYM_WORD)
+
+#define ADJECTIVE_WORD (SYNONYM_WORD+1)
+#define ADJECTIVE_BIT (((Aword)1)<<ADJECTIVE_WORD)
+
+#define ALL_WORD (ADJECTIVE_WORD+1)
+#define ALL_BIT (((Aword)1)<<ALL_WORD)
+
+#define EXCEPT_WORD (ALL_WORD+1)
+#define EXCEPT_BIT (((Aword)1)<<EXCEPT_WORD)
+
+#define CONJUNCTION_WORD (EXCEPT_WORD+1)
+#define CONJUNCTION_BIT (((Aword)1)<<CONJUNCTION_WORD)
+
+#define PREPOSITION_WORD (CONJUNCTION_WORD+1)
+#define PREPOSITION_BIT (((Aword)1)<<PREPOSITION_WORD)
+
+#define DIRECTION_WORD (PREPOSITION_WORD+1)
+#define DIRECTION_BIT (((Aword)1)<<DIRECTION_WORD)
+
+#define IT_WORD (DIRECTION_WORD+1)
+#define IT_BIT (((Aword)1)<<IT_WORD)
+
+#define NOISE_WORD (IT_WORD+1)
+#define NOISE_BIT (((Aword)1)<<NOISE_WORD)
+
+#define NOUN_WORD (NOISE_WORD+1)
+#define NOUN_BIT (((Aword)1)<<NOUN_WORD)
+
+#define THEM_WORD (NOUN_WORD+1)
+#define THEM_BIT (((Aword)1)<<THEM_WORD)
+
+#define VERB_WORD (THEM_WORD+1)
+#define VERB_BIT (((Aword)1)<<VERB_WORD)
+
+#define PRONOUN_WORD (VERB_WORD+1)
+#define PRONOUN_BIT (((Aword)1)<<PRONOUN_WORD)
+
+#define WRD_CLASSES (PRONOUN_WORD+1)
+
+
+/* The #nowhere and NO_LOCATION constants */
+#define NO_LOCATION 0
+#define NOWHERE 1
+
+
+/* Syntax element classifications */
+#define EOS (-2) /* End Of Syntax */
+
+/* Syntax element flag bits */
+#define MULTIPLEBIT 0x1
+#define OMNIBIT 0x2
+
+
+/* Parameter Classes */
+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
+};
+
+
+/* Verb Qualifiers */
+enum QualClass {
+ Q_DEFAULT,
+ Q_AFTER,
+ Q_BEFORE,
+ Q_ONLY
+};
+
+
+/* The AMACHINE Operations */
+enum OpClass {
+ C_CONST,
+ C_STMOP,
+ C_CURVAR
+};
+
+/* AMACHINE Text Styles */
+enum TextStyle {
+ NORMAL_STYLE,
+ EMPHASIZED_STYLE,
+ PREFORMATTED_STYLE,
+ ALERT_STYLE,
+ QUOTE_STYLE
+};
+
+
+#define CONSTANT(op) ((Aword)op)
+#define INSTRUCTION(op) ((((Aword)C_STMOP)<<28)|((Aword)op))
+#define CURVAR(op) ((((Aword)C_CURVAR)<<28)|((Aword)op))
+
+enum InstClass {
+ I_LINE, /* Source line debug info */
+ I_PRINT, /* Print a string from the text file */
+ I_STYLE, /* Set output text style */
+ 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, /* Set a boolean attribute to the */
+ /* value on top of stack */
+ I_SET, /* Set a numeric attribute to the */
+ /* value on top of stack */
+ I_SETSTR, /* Set a string valued attribute to */
+ /* the string on top of stack, */
+ /* deallocate current contents first */
+ I_SETSET, /* Set a Set valued attribute to */
+ /* the Set on top of stack, */
+ /* deallocate current contents first */
+ I_NEWSET, /* Push a new, empty set at the top of stack */
+ I_ATTRIBUTE, /* Push the value of an attribute */
+ I_ATTRSTR, /* Push a copy of a string attribute */
+ I_ATTRSET, /* Push a copy of a set attribute */
+ I_UNION, /* Add a set from the top of stack to a */
+ /* set valued attribute */
+ I_GETSTR, /* Get a string contents from text
+ file, create a copy and push it
+ on top of stack */
+ I_INCR, /* Increase an attribute */
+ I_DECR, /* Decrease a numeric attribute */
+ I_INCLUDE, /* Include a value in the set on stack top */
+ I_EXCLUDE, /* Remove a value from the set on stack top */
+ I_SETSIZE, /* Push number of members in a set */
+ I_SETMEMB, /* Push the member with index <top>-1
+ from set <top> */
+ I_CONTSIZE, /* Push number of members in a container */
+ I_CONTMEMB, /* Push the member with index <top>-1
+ from container <top> */
+ I_USE,
+ I_STOP,
+ I_AT,
+ I_IN,
+ I_INSET,
+ I_HERE,
+ I_NEARBY,
+ I_NEAR,
+ I_WHERE, /* Current position of an instance */
+ I_LOCATION, /* The *location* an instance is at */
+ I_DESCRIBE,
+ I_SAY,
+ I_SAYINT,
+ I_SAYSTR,
+ I_IF,
+ I_ELSE,
+ I_ENDIF,
+ I_AND,
+ I_OR,
+ I_NE,
+ I_EQ,
+ I_STREQ, /* String compare */
+ I_STREXACT, /* Exact match */
+ I_LE,
+ I_GE,
+ I_LT,
+ I_GT,
+ I_PLUS,
+ I_MINUS,
+ I_MULT,
+ I_DIV,
+ I_NOT,
+ I_UMINUS,
+ I_RND,
+ I_RETURN,
+ I_SYSTEM,
+ I_RESTART,
+ I_BTW,
+ I_CONTAINS,
+ I_DUP,
+ I_DEPEND,
+ I_DEPCASE,
+ I_DEPEXEC,
+ I_DEPELSE,
+ I_ENDDEP,
+ I_ISA,
+ I_FRAME,
+ I_SETLOCAL,
+ I_GETLOCAL,
+ I_ENDFRAME,
+ I_LOOP,
+ I_LOOPNEXT,
+ I_LOOPEND,
+ I_SUM, /* Aggregates: */
+ I_MAX,
+ I_MIN,
+ I_COUNT, /* COUNT aggregate & limit meta-attribute */
+ I_SHOW,
+ I_PLAY,
+ I_CONCAT,
+ I_STRIP,
+ I_POP,
+ I_TRANSCRIPT,
+ I_DUPSTR /* Duplicate the string on the top of the stack */
+};
+
+enum SayForm {
+ SAY_SIMPLE,
+ SAY_DEFINITE,
+ SAY_INDEFINITE,
+ SAY_NEGATIVE,
+ SAY_PRONOUN
+};
+
+enum VarClass {
+ V_PARAM,
+ V_CURLOC,
+ V_CURACT,
+ V_CURVRB,
+ V_SCORE,
+ V_CURRENT_INSTANCE,
+ V_MAX_INSTANCE
+};
+
+/* For transitivity in HERE, IN etc. */
+enum ATrans {
+ TRANSITIVE = 0,
+ DIRECT = 1,
+ INDIRECT = 2
+};
+
+/* Predefined attributes, one is for containers and the other for locations
+ and since instances cannot be both, the attributes can have the same number */
+#define OPAQUEATTRIBUTE 1
+#define VISITSATTRIBUTE 1
+#define PREDEFINEDATTRIBUTES OPAQUEATTRIBUTE
+
+#define I_CLASS(x) ((x)>>28)
+#define I_OP(x) ((x&0x08000000)?(x)|0xf0000000:(x)&0x0fffffff)
+
+
+/* AMACHINE Table entry types */
+
+#define AwordSizeOf(x) (sizeof(x)/sizeof(Aword))
+
+struct ArticleEntry {
+ Aaddr address; /* Address of article code */
+ Abool isForm; /* Is the article a complete form? */
+};
+
+struct ClassEntry { /* CLASS TABLE */
+ Aword code; /* Own code */
+ Aaddr id; /* Address to identifier string */
+ Aint parent; /* Code for the parent class, 0 if none */
+ Aaddr name; /* Address to name printing code */
+ Aint pronoun; /* Code for the pronoun word */
+ Aaddr initialize; /* Address to initialization statements */
+ Aaddr descriptionChecks; /* Address of description checks */
+ Aaddr description; /* Address of description code */
+ ArticleEntry definite; /* Definite article entry */
+ ArticleEntry indefinite; /* Indefinite article entry */
+ ArticleEntry negative; /* Negative article entry */
+ Aaddr mentioned; /* Address of code for Mentioned clause */
+ Aaddr verbs; /* Address of verb table */
+ Aaddr entered; /* Address of code for Entered clause */
+};
+
+struct InstanceEntry { /* INSTANCE TABLE */
+ Aint code; /* Own code */
+ Aaddr id; /* Address to identifier string */
+ Aint parent; /* Code for the parent class, 0 if none */
+ Aaddr name; /* Address to name printing code */
+ Aint pronoun; /* Word code for the pronoun */
+ Aint initialLocation; /* Code for current location */
+ Aaddr initialize; /* Address to initialization statements */
+ Aint container; /* Code for a possible container property */
+ Aaddr initialAttributes; /* Address of attribute list */
+ Aaddr checks; /* Address of description checks */
+ Aaddr description; /* Address of description code */
+ ArticleEntry definite; /* Definite article entry */
+ ArticleEntry indefinite; /* Indefinite article entry */
+ ArticleEntry negative; /* Negative article entry */
+ Aaddr mentioned; /* Address to short description code */
+ Aaddr verbs; /* Address of local verb list */
+ Aaddr entered; /* Address of entered code (location only) */
+ Aaddr exits; /* Address of exit list */
+};
+
+struct AttributeEntry { /* ATTRIBUTE LIST */
+ Aint code; /* Its code */
+ Aptr value; /* Its value, a string has a dynamic
+ string pointer, a set has a pointer
+ to a dynamically allocated set */
+ Aaddr id; /* Address to the name */
+};
+
+struct AttributeHeaderEntry { /* ATTRIBUTE LIST in header */
+ Aint code; /* Its code */
+ Aword value; /* Its value, a string has a dynamic
+ string pointer, a set has a pointer
+ to a dynamically allocated set */
+ Aaddr id; /* Address to the name */
+};
+
+struct ExitEntry { /* EXIT TABLE structure */
+ Aword code; /* Direction code */
+ Aaddr checks; /* Address of check table */
+ Aaddr action; /* Address of action code */
+ Aword target; /* Id for the target location */
+};
+
+
+struct RuleEntry { /* RULE TABLE */
+ Abool alreadyRun;
+ Aaddr exp; /* Address to expression code */
+ Aaddr stms; /* Address to run */
+};
+
+
+#define RESTRICTIONCLASS_CONTAINER (-2)
+#define RESTRICTIONCLASS_INTEGER (-3)
+#define RESTRICTIONCLASS_STRING (-4)
+
+struct RestrictionEntry { /* PARAMETER RESTRICTION TABLE */
+ Aint parameterNumber; /* Parameter number */
+ Aint _class; /* Parameter class code */
+ Aaddr stms; /* Exception statements */
+};
+
+struct ContainerEntry { /* CONTAINER TABLE */
+ Aword owner; /* Owner instance index */
+ Aint _class; /* Class to allow in container */
+ Aaddr limits; /* Address to limit check code */
+ Aaddr header; /* Address to header code */
+ Aaddr empty; /* Address to code for header when empty */
+ Aaddr extractChecks; /* Address to check before extracting */
+ Aaddr extractStatements; /* Address to execute when extracting */
+};
+
+
+struct ElementEntry { /* SYNTAX ELEMENT TABLES */
+ Aint code; /* Code for this element, 0 -> parameter */
+ Aword flags; /* Flags for multiple/omni (if parameter), syntax number/verb of EOS */
+ Aaddr next; /* Address to next element table ... */
+ /* ... or restrictions if code == EOS */
+};
+
+struct SyntaxEntryPreBeta2 { /* SYNTAX TABLE */
+ Aint code; /* Code for verb word */
+ Aaddr elms; /* Address to element tables */
+};
+
+struct SyntaxEntry { /* SYNTAX TABLE */
+ Aint code; /* Code for verb word, or 0 if starting with parameter */
+ Aaddr elms; /* Address to element tables */
+ Aaddr parameterNameTable; /* Address to a table of id-addresses giving the names of the parameters */
+};
+
+struct ParameterMapEntry { /* PARAMETER MAPPING TABLE */
+ Aint syntaxNumber;
+ Aaddr parameterMapping;
+ Aint verbCode;
+};
+
+struct EventEntry { /* EVENT TABLE */
+ Aaddr id; /* Address to name string */
+ Aaddr code;
+};
+
+struct ScriptEntry { /* SCRIPT TABLE */
+ Aaddr id; /* Address to name string */
+ Aint code; /* Script number */
+ Aaddr description; /* Optional description statements */
+ Aaddr steps; /* Address to steps */
+};
+
+struct StepEntry { /* STEP TABLE */
+ Aaddr after; /* Expression to say after how many ticks? */
+ Aaddr exp; /* Expression to condition saying when */
+ Aaddr stms; /* Address to the actual code */
+};
+
+struct AltEntry { /* VERB ALTERNATIVE TABLE */
+ Aword qual; /* Verb execution qualifier */
+ Aint param; /* Parameter number */
+ Aaddr checks; /* Address of the check table */
+ Aaddr action; /* Address of the action code */
+};
+
+struct SourceFileEntry { /* SOURCE FILE NAME TABLE */
+ Aint fpos;
+ Aint len;
+};
+
+struct SourceLineEntry { /* SOURCE LINE TABLE */
+ Aint file;
+ Aint line;
+};
+
+struct StringInitEntry { /* STRING INITIALISATION TABLE */
+ Aword fpos; /* File position */
+ Aword len; /* Length */
+ Aint instanceCode; /* Where to store it */
+ Aint attributeCode;
+};
+
+struct SetInitEntry { /* SET INITIALISATION TABLE */
+ Aint size; /* Size of the initial set */
+ Aword setAddress; /* Address to the initial set */
+ Aint instanceCode; /* Where to store it */
+ Aint attributeCode;
+};
+
+struct DictionaryEntry { /* Dictionary */
+ Aaddr string; /* ACODE address to string */
+ Aword classBits; /* Word class */
+ Aword code;
+ Aaddr adjectiveRefs; /* Address to reference list */
+ Aaddr nounRefs; /* Address to reference list */
+ Aaddr pronounRefs; /* Address to reference list */
+};
+
+
+
+/* AMACHINE Header */
+
+struct ACodeHeader {
+ /* Important info */
+ char tag[4]; /* "ALAN" */
+ char version[4]; /* Version of compiler */
+ Aword uid; /* Unique id of the compiled game */
+ Aword size; /* Size of ACD-file in Awords */
+ /* Options */
+ Abool pack; /* Is the text packed and encoded ? */
+ Aword stringOffset; /* Offset to string data in game file */
+ Aword pageLength; /* Length of a displayed page */
+ Aword pageWidth; /* and width */
+ Aword debug; /* Option: debug */
+ /* Data structures */
+ Aaddr classTableAddress;
+ Aword classMax;
+ Aword entityClassId;
+ Aword thingClassId;
+ Aword objectClassId;
+ Aword locationClassId;
+ Aword actorClassId;
+ Aword literalClassId;
+ Aword integerClassId;
+ Aword stringClassId;
+ Aaddr instanceTableAddress; /* Instance table */
+ Aword instanceMax; /* Highest number of an instance */
+ Aword theHero; /* The hero instance code (id) */
+ Aaddr containerTableAddress;
+ Aword containerMax;
+ Aaddr scriptTableAddress;
+ Aword scriptMax;
+ Aaddr eventTableAddress;
+ Aword eventMax;
+ Aaddr syntaxTableAddress;
+ Aaddr parameterMapAddress;
+ Aword syntaxMax;
+ Aaddr dictionary;
+ Aaddr verbTableAddress;
+ Aaddr ruleTableAddress;
+ Aaddr messageTableAddress;
+ /* Miscellaneous */
+ Aint attributesAreaSize; /* Size of attribute data area in Awords */
+ Aint maxParameters; /* Maximum number of parameters in any syntax */
+ Aaddr stringInitTable; /* String init table address */
+ Aaddr setInitTable; /* Set init table address */
+ Aaddr start; /* Address to Start code */
+ Aword maximumScore; /* Maximum score */
+ Aaddr scores; /* Score table */
+ Aint scoreCount; /* Max index into scores table */
+ Aaddr sourceFileTable; /* Table of fpos/len for source filenames */
+ Aaddr sourceLineTable; /* Table of available source lines to break on */
+ Aaddr freq; /* Address to Char freq's for coding */
+ Aword acdcrc; /* Checksum for acd code (excl. hdr) */
+ Aword txtcrc; /* Checksum for text data file */
+ Aaddr ifids; /* Address to IFIDS */
+ Aaddr prompt;
+};
+
+struct Pre3_0beta2Header {
+ /* Important info */
+ char tag[4]; /* "ALAN" */
+ char version[4]; /* Version of compiler */
+ Aword uid; /* Unique id of the compiled game */
+ Aword size; /* Size of ACD-file in Awords */
+ /* Options */
+ Abool pack; /* Is the text packed ? */
+ Aword stringOffset; /* Offset to string data in game file */
+ Aword pageLength; /* Length of a page */
+ Aword pageWidth; /* and width */
+ Aword debug; /* Option: debug */
+ /* Data structures */
+ Aaddr classTableAddress; /* Class table */
+ Aword classMax; /* Number of classes */
+ Aword entityClassId;
+ Aword thingClassId;
+ Aword objectClassId;
+ Aword locationClassId;
+ Aword actorClassId;
+ Aword literalClassId;
+ Aword integerClassId;
+ Aword stringClassId;
+ Aaddr instanceTableAddress; /* Instance table */
+ Aword instanceMax; /* Highest number of an instance */
+ Aword theHero; /* The hero instance code (id) */
+ Aaddr containerTableAddress;
+ Aword containerMax;
+ Aaddr scriptTableAddress;
+ Aword scriptMax;
+ Aaddr eventTableAddress;
+ Aword eventMax;
+ Aaddr syntaxTableAddress;
+ Aaddr parameterMapAddress;
+ Aword syntaxMax;
+ Aaddr dictionary;
+ Aaddr verbTableAddress;
+ Aaddr ruleTableAddress;
+ Aaddr messageTableAddress;
+ /* Miscellaneous */
+ Aint attributesAreaSize; /* Size of attribute data area in Awords */
+ Aint maxParameters; /* Maximum number of parameters in any syntax */
+ Aaddr stringInitTable; /* String init table address */
+ Aaddr setInitTable; /* Set init table address */
+ Aaddr start; /* Address to Start code */
+ Aword maximumScore; /* Maximum score */
+ Aaddr scores; /* Score table */
+ Aint scoreCount; /* Max index into scores table */
+ Aaddr sourceFileTable; /* Table of fpos/len for source filenames */
+ Aaddr sourceLineTable; /* Table of available source lines to break on */
+ Aaddr freq; /* Address to Char freq's for coding */
+ Aword acdcrc; /* Checksum for acd code (excl. hdr) */
+ Aword txtcrc; /* Checksum for text data file */
+ Aaddr ifids; /* Address to IFIDS */
+};
+
+struct Pre3_0alpha5Header {
+ /* Important info */
+ char tag[4]; /* "ALAN" */
+ char version[4]; /* Version of compiler */
+ Aword uid; /* Unique id of the compiled game */
+ Aword size; /* Size of ACD-file in Awords */
+ /* Options */
+ Abool pack; /* Is the text packed ? */
+ Aword stringOffset; /* Offset to string data in game file */
+ Aword pageLength; /* Length of a page */
+ Aword pageWidth; /* and width */
+ Aword debug; /* Option: debug */
+ /* Data structures */
+ Aaddr classTableAddress; /* Class table */
+ Aword classMax; /* Number of classes */
+ Aword entityClassId;
+ Aword thingClassId;
+ Aword objectClassId;
+ Aword locationClassId;
+ Aword actorClassId;
+ Aword literalClassId;
+ Aword integerClassId;
+ Aword stringClassId;
+ Aaddr instanceTableAddress; /* Instance table */
+ Aword instanceMax; /* Highest number of an instance */
+ Aword theHero; /* The hero instance code (id) */
+ Aaddr containerTableAddress;
+ Aword containerMax;
+ Aaddr scriptTableAddress;
+ Aword scriptMax;
+ Aaddr eventTableAddress;
+ Aword eventMax;
+ Aaddr syntaxTableAddress;
+ Aaddr parameterMapAddress;
+ Aword syntaxMax;
+ Aaddr dictionary;
+ Aaddr verbTableAddress;
+ Aaddr ruleTableAddress;
+ Aaddr messageTableAddress;
+ /* Miscellaneous */
+ Aint attributesAreaSize; /* Size of attribute data area in Awords */
+ Aint maxParameters; /* Maximum number of parameters in any syntax */
+ Aaddr stringInitTable; /* String init table address */
+ Aaddr setInitTable; /* Set init table address */
+ Aaddr start; /* Address to Start code */
+ Aword maximumScore; /* Maximum score */
+ Aaddr scores; /* Score table */
+ Aint scoreCount; /* Max index into scores table */
+ Aaddr sourceFileTable; /* Table of fpos/len for source filenames */
+ Aaddr sourceLineTable; /* Table of available source lines to break on */
+ Aaddr freq; /* Address to Char freq's for coding */
+ Aword acdcrc; /* Checksum for acd code (excl. hdr) */
+ Aword txtcrc; /* Checksum for text data file */
+};
+
+/* Error message numbers */
+enum MsgKind {
+ M_UNKNOWN_WORD,
+ M_WHAT,
+ M_WHAT_WORD,
+ M_MULTIPLE,
+ M_NOUN,
+ M_AFTER_BUT,
+ M_BUT_ALL,
+ M_NOT_MUCH,
+ M_WHICH_ONE_START,
+ M_WHICH_ONE_COMMA,
+ M_WHICH_ONE_OR,
+ M_NO_SUCH,
+ M_NO_WAY,
+ M_CANT0,
+ M_SEE_START,
+ M_SEE_COMMA,
+ M_SEE_AND,
+ M_SEE_END,
+ M_CONTAINS,
+ M_CARRIES,
+ M_CONTAINS_COMMA,
+ M_CONTAINS_AND,
+ M_CONTAINS_END,
+ M_EMPTY,
+ M_EMPTYHANDED,
+ M_CANNOTCONTAIN,
+ M_SCORE,
+ M_MORE,
+ M_AGAIN,
+ M_SAVEWHERE,
+ M_SAVEOVERWRITE,
+ M_SAVEFAILED,
+ M_RESTOREFROM,
+ M_SAVEMISSING,
+ M_NOTASAVEFILE,
+ M_SAVEVERS,
+ M_SAVENAME,
+ M_REALLY,
+ M_QUITACTION,
+ M_UNDONE,
+ M_NO_UNDO,
+ M_WHICH_PRONOUN_START,
+ M_WHICH_PRONOUN_FIRST,
+ M_IMPOSSIBLE_WITH,
+ M_CONTAINMENT_LOOP,
+ M_CONTAINMENT_LOOP2,
+ MSGMAX
+};
+
+#define NO_MSG MSGMAX
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/act.cpp b/engines/glk/alan3/act.cpp
new file mode 100644
index 0000000000..87366cd3dc
--- /dev/null
+++ b/engines/glk/alan3/act.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/alan3/act.h"
+#include "glk/alan3/alt_info.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/lists.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*----------------------------------------------------------------------*/
+static void executeCommand(int verb, Parameter parameters[])
+{
+ static AltInfo *altInfos = NULL; /* Need to survive lots of different exits...*/
+ int altIndex;
+
+ /* Did we leave anything behind last time... */
+ if (altInfos != NULL)
+ free(altInfos);
+
+ altInfos = findAllAlternatives(verb, parameters);
+
+ if (anyCheckFailed(altInfos, EXECUTE_CHECK_BODY_ON_FAIL))
+ return;
+
+ /* Check for anything to execute... */
+ if (!anythingToExecute(altInfos))
+ error(M_CANT0);
+
+ /* Now perform actions! First try any BEFORE or ONLY from inside out */
+ for (altIndex = lastAltInfoIndex(altInfos); altIndex >= 0; altIndex--) {
+ if (altInfos[altIndex].alt != 0) // TODO Can this ever be NULL? Why?
+ if (altInfos[altIndex].alt->qual == (Aword)Q_BEFORE
+ || altInfos[altIndex].alt->qual == (Aword)Q_ONLY) {
+ if (!executedOk(&altInfos[altIndex]))
+ abortPlayerCommand();
+ if (altInfos[altIndex].alt->qual == (Aword)Q_ONLY)
+ return;
+ }
+ }
+
+ /* Then execute any not declared as AFTER, i.e. the default */
+ for (altIndex = 0; !altInfos[altIndex].end; altIndex++) {
+ if (altInfos[altIndex].alt != 0)
+ if (altInfos[altIndex].alt->qual != (Aword)Q_AFTER)
+ if (!executedOk(&altInfos[altIndex]))
+ abortPlayerCommand();
+ }
+
+ /* Finally, the ones declared as AFTER */
+ for (altIndex = lastAltInfoIndex(altInfos); altIndex >= 0; altIndex--) {
+ if (altInfos[altIndex].alt != 0)
+ if (!executedOk(&altInfos[altIndex]))
+ abortPlayerCommand();
+ }
+}
+
+
+/*======================================================================
+
+ action()
+
+ Execute the command. Handles acting on multiple items
+ such as ALL, THEM or lists of objects.
+
+*/
+void action(int verb, Parameter parameters[], Parameter multipleMatches[])
+{
+ int i, multiplePosition;
+ char marker[10];
+
+ multiplePosition = findMultiplePosition(parameters);
+ if (multiplePosition != -1) {
+#ifdef TODO
+ jmp_buf savedReturnLabel;
+ memcpy(savedReturnLabel, returnLabel, sizeof(returnLabel));
+ sprintf(marker, "($%d)", multiplePosition+1); /* Prepare a printout with $1/2/3 */
+ for (i = 0; !isEndOfArray(&multipleMatches[i]); i++) {
+ copyParameter(&parameters[multiplePosition], &multipleMatches[i]);
+ setGlobalParameters(parameters); /* Need to do this here since the marker use them */
+ output(marker);
+ // TODO: if execution for one parameter aborts we should return here, not to top level
+ if (setjmp(returnLabel) == NO_JUMP_RETURN)
+ executeCommand(verb, parameters);
+ if (multipleMatches[i+1].instance != EOF)
+ para();
+ }
+ memcpy(returnLabel, savedReturnLabel, sizeof(returnLabel));
+ parameters[multiplePosition].instance = 0;
+#else
+ ::error("TODO: action");
+#endif
+ } else {
+ setGlobalParameters(parameters);
+ executeCommand(verb, parameters);
+ }
+
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/act.h b/engines/glk/alan3/act.h
new file mode 100644
index 0000000000..09f867657e
--- /dev/null
+++ b/engines/glk/alan3/act.h
@@ -0,0 +1,38 @@
+/* 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_ALAN3_ACT
+#define GLK_ALAN3_ACT
+
+/* Header file for action unit of ARUN Alan System interpreter */
+
+#include "glk/alan3/params.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern void action(int verb, Parameter *parameters, Parameter *multipleMatches);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/actor.cpp b/engines/glk/alan3/actor.cpp
new file mode 100644
index 0000000000..5152305033
--- /dev/null
+++ b/engines/glk/alan3/actor.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/actor.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/container.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*======================================================================*/
+ScriptEntry *scriptOf(int actor) {
+ ScriptEntry *scr;
+
+ if (admin[actor].script != 0) {
+ for (scr = (ScriptEntry *) pointerTo(header->scriptTableAddress); !isEndOfArray(scr); scr++)
+ if (scr->code == admin[actor].script)
+ break;
+ if (!isEndOfArray(scr))
+ return scr;
+ }
+ return NULL;
+}
+
+
+/*======================================================================*/
+StepEntry *stepOf(int actor) {
+ StepEntry *step;
+ ScriptEntry *scr = scriptOf(actor);
+
+ if (scr == NULL) return NULL;
+
+ step = (StepEntry*)pointerTo(scr->steps);
+ step = &step[admin[actor].step];
+
+ return step;
+}
+
+
+/*======================================================================*/
+void describeActor(int actor)
+{
+ ScriptEntry *script = scriptOf(actor);
+
+ if (script != NULL && script->description != 0)
+ interpret(script->description);
+ else if (hasDescription(actor))
+ describeAnything(actor);
+ else {
+ printMessageWithInstanceParameter(M_SEE_START, actor);
+ printMessage(M_SEE_END);
+ if (instances[actor].container != 0)
+ describeContainer(actor);
+ }
+ admin[actor].alreadyDescribed = TRUE;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/actor.h b/engines/glk/alan3/actor.h
new file mode 100644
index 0000000000..8b2c1ad6bf
--- /dev/null
+++ b/engines/glk/alan3/actor.h
@@ -0,0 +1,39 @@
+/* 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_ALAN3_ACTOR
+#define GLK_ALAN3_ACTOR
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* FUNCTIONS */
+extern ScriptEntry *scriptOf(int actor);
+extern StepEntry *stepOf(int actor);
+extern void describeActor(int actor);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/alan3.cpp b/engines/glk/alan3/alan3.cpp
new file mode 100644
index 0000000000..d5a062b76a
--- /dev/null
+++ b/engines/glk/alan3/alan3.cpp
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/main.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/options.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+#include "common/error.h"
+#include "common/scummsys.h"
+#include "common/serializer.h"
+#include "glk/glk.h"
+#include "glk/streams.h"
+
+namespace Glk {
+namespace Alan3 {
+
+Alan3 *g_vm = nullptr;
+
+Alan3::Alan3(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
+ vm_exited_cleanly(false), _restartFlag(false), _saveSlot(-1), _pendingLook(false) {
+ g_vm = this;
+// txtfil = nullptr;
+// logfil = nullptr;
+ memory = nullptr;
+
+ verboseOption = false;
+ ignoreErrorOption = false;
+ debugOption = false;
+ traceSectionOption = false;
+ tracePushOption = false;
+ traceStackOption = false;
+ traceSourceOption = false;
+ traceInstructionOption = false;
+ transcriptOption = false;
+ logOption = false;
+ statusLineOption = true;
+ regressionTestOption = false;
+}
+
+void Alan3::runGame() {
+ if (initialize())
+ Glk::Alan3::run();
+
+ deinitialize();
+}
+
+bool Alan3::initialize() {
+ // Set up adventure name
+ _advName = getFilename();
+ if (_advName.size() > 4 && _advName[_advName.size() - 4] == '.')
+ _advName = Common::String(_advName.c_str(), _advName.size() - 4);
+
+ // first, open a window for error output
+ glkMainWin = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+ if (glkMainWin == nullptr)
+ ::error("FATAL ERROR: Cannot open initial window");
+
+ g_vm->glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
+ glkStatusWin = g_vm->glk_window_open(glkMainWin, winmethod_Above |
+ winmethod_Fixed, 1, wintype_TextGrid, 0);
+ g_vm->glk_set_window(glkMainWin);
+/*
+ // Set up the code file to point to the already opened game file
+ codfil = &_gameFile;
+ strncpy(codfnm, getFilename().c_str(), 255);
+ codfnm[255] = '\0';
+
+ if (_gameFile.size() < 8) {
+ GUIErrorMessage(_("This is too short to be a valid Alan3 file."));
+ return false;
+ }
+
+ if (_gameFile.readUint32BE() != MKTAG(2, 8, 1, 0)) {
+ GUIErrorMessage(_("This is not a valid Alan3 file."));
+ return false;
+ }
+
+ // Open up the text file
+ txtfil = new Common::File();
+ if (!txtfil->open(Common::String::format("%s.dat", _advName.c_str()))) {
+ GUIErrorMessage("Could not open adventure text data file");
+ delete txtfil;
+ return false;
+ }
+
+ // Check for a save being loaded directly from the launcher
+ _saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+*/
+ return true;
+}
+
+void Alan3::deinitialize() {
+ free(memory);
+/*
+ delete txtfil;
+ delete logfil;
+*/
+}
+
+Common::Error Alan3::readSaveData(Common::SeekableReadStream *rs) {
+ Common::Serializer s(rs, nullptr);
+ synchronizeSave(s);
+
+ return Common::kNoError;
+}
+
+Common::Error Alan3::writeGameData(Common::WriteStream *ws) {
+ Common::Serializer s(nullptr, ws);
+ synchronizeSave(s);
+
+ ws->flush();
+ return Common::kNoError;
+}
+
+// This works around gcc errors for passing packed structure fields
+void syncVal(Common::Serializer &s, uint32 *fld) {
+ uint32 &v = *fld;
+ s.syncAsUint32LE(v);
+}
+
+void Alan3::synchronizeSave(Common::Serializer &s) {
+ // TODO
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/alan3.h b/engines/glk/alan3/alan3.h
new file mode 100644
index 0000000000..ea8548a080
--- /dev/null
+++ b/engines/glk/alan3/alan3.h
@@ -0,0 +1,102 @@
+/* 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_ALAN3
+#define GLK_ALAN3
+
+#include "glk/glk_api.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/**
+ * Alan3 game interpreter
+ */
+class Alan3 : public GlkAPI {
+private:
+ bool _restartFlag;
+public:
+ bool vm_exited_cleanly;
+ Common::String _advName;
+ int _saveSlot;
+ bool _pendingLook;
+private:
+ /**
+ * Initialization
+ */
+ bool initialize();
+
+ /**
+ * Deinitialization
+ */
+ void deinitialize();
+
+ /**
+ * Synchronize data to or from a save file
+ */
+ void synchronizeSave(Common::Serializer &s);
+public:
+ /**
+ * Constructor
+ */
+ Alan3(OSystem *syst, const GlkGameDescription &gameDesc);
+
+ /**
+ * Run the game
+ */
+ void runGame();
+
+ /**
+ * Flag for the game to restart
+ */
+ void setRestart(bool flag) { _restartFlag = flag; }
+
+ /**
+ * Returns whether the game should restart
+ */
+ bool shouldRestart() const { return _restartFlag; }
+
+ /**
+ * Returns the running interpreter type
+ */
+ virtual InterpreterType getInterpreterType() const override {
+ return INTERPRETER_ALAN3;
+ }
+
+ /**
+ * Load a savegame from the passed Quetzal file chunk stream
+ */
+ virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
+
+ /**
+ * Save the game. The passed write stream represents access to the UMem chunk
+ * in the Quetzal save file that will be created
+ */
+ virtual Common::Error writeGameData(Common::WriteStream *ws) override;
+};
+
+extern Alan3 *g_vm;
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/alan_version.cpp b/engines/glk/alan3/alan_version.cpp
new file mode 100644
index 0000000000..bb244ec23f
--- /dev/null
+++ b/engines/glk/alan3/alan_version.cpp
@@ -0,0 +1,42 @@
+/* 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/alan3/alan_version.h"
+
+namespace Glk {
+namespace Alan3 {
+
+const Product alan = {
+ "Alan",
+ "Adventure Language System",
+ "Alan 3.0beta6",
+ "Alan 3.0beta6 -- Adventure Language System (2017-09-08 10:18)",
+ "2017-09-08",
+ "10:18:25",
+ "Thomas",
+ "thoni64",
+ "cygwin32",
+ {"3.0beta6", 3, 0, 6, 1504858705, "beta"}
+};
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/alan_version.h b/engines/glk/alan3/alan_version.h
new file mode 100644
index 0000000000..efc26d2af5
--- /dev/null
+++ b/engines/glk/alan3/alan_version.h
@@ -0,0 +1,36 @@
+/* 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_ALAN3_ALAN_VERSION
+#define GLK_ALAN3_ALAN_VERSION
+
+#include "glk/alan3/version.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern const Product alan;
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/alt_info.cpp b/engines/glk/alan3/alt_info.cpp
new file mode 100644
index 0000000000..b9e2dc0aba
--- /dev/null
+++ b/engines/glk/alan3/alt_info.cpp
@@ -0,0 +1,381 @@
+/* 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/alan3/alt_info.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/checkentry.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/class.h"
+#include "glk/alan3/params.h"
+#include "glk/alan3/literal.h"
+#include "glk/alan3/syntax.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* Types */
+typedef AltInfo *AltInfoFinder(int verb, Parameter parameters[]);
+
+
+/*======================================================================*/
+void primeAltInfo(AltInfo *altInfo, int level, int parameter, int instance, int cls)
+{
+ altInfo->level = level;
+ altInfo->parameter = parameter;
+ altInfo->instance = instance;
+ altInfo->_class = cls;
+ altInfo->done = FALSE;
+ altInfo->end = FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceInstanceAndItsClass(Aid instance, Aid cls)
+{
+ traceSay(instance);
+ printf("[%d]", instance);
+ if (cls != NO_CLASS)
+ printf(", inherited from %s[%d]", idOfClass(cls), cls);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceAltInfo(AltInfo *alt) {
+ switch (alt->level) {
+ case GLOBAL_LEVEL:
+ printf("GLOBAL");
+ break;
+ case LOCATION_LEVEL:
+ printf("in (location) ");
+ traceInstanceAndItsClass(current.location, alt->_class);
+ break;
+ case PARAMETER_LEVEL: {
+ char *parameterName = parameterNameInSyntax(current.verb, alt->parameter);
+ if (parameterName != NULL)
+ printf("in parameter %s(#%d)=", parameterName, alt->parameter);
+ else
+ printf("in parameter #%d=", alt->parameter);
+ traceInstanceAndItsClass(globalParameters[alt->parameter-1].instance, alt->_class);
+ break;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceVerbCheck(AltInfo *alt, bool execute)
+{
+ if (traceSectionOption && execute) {
+ printf("\n<VERB %d, ", current.verb);
+ traceAltInfo(alt);
+ printf(", CHECK:>\n");
+ }
+}
+
+
+/*======================================================================*/
+bool checkFailed(AltInfo *altInfo, bool execute)
+{
+ if (altInfo->alt != NULL && altInfo->alt->checks != 0) {
+ traceVerbCheck(altInfo, execute);
+ // TODO Why does this not generate a regression error with !
+ // Need a new regression case?
+ fail = FALSE;
+ if (checksFailed(altInfo->alt->checks, execute)) return TRUE;
+ if (fail) return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceVerbExecution(AltInfo *alt)
+{
+ if (traceSectionOption) {
+ printf("\n<VERB %d, ", current.verb);
+ traceAltInfo(alt);
+ printf(", DOES");
+ switch (alt->alt->qual) {
+ case Q_BEFORE: printf(" (BEFORE)");
+ break;
+ case Q_ONLY: printf(" (ONLY)");
+ break;
+ case Q_AFTER: printf(" (AFTER)");
+ break;
+ case Q_DEFAULT:
+ break;
+ }
+ printf(":>\n");
+ }
+}
+
+
+/*======================================================================*/
+bool executedOk(AltInfo *altInfo)
+{
+ fail = FALSE;
+ if (!altInfo->done && altInfo->alt->action != 0) {
+ traceVerbExecution(altInfo);
+ current.instance = altInfo->instance;
+ interpret(altInfo->alt->action);
+ }
+ altInfo->done = TRUE;
+ return !fail;
+}
+
+
+/*======================================================================*/
+bool canBeExecuted(AltInfo *altInfo) {
+ return altInfo->alt != NULL && altInfo->alt->action != 0;
+}
+
+
+/*======================================================================*/
+AltInfo *duplicateAltInfoArray(AltInfo original[]) {
+ int size;
+ AltInfo *duplicate;
+
+ for (size = 0; original[size].end != TRUE; size++)
+ ;
+ size++;
+ duplicate = (AltInfo *)allocate(size*sizeof(AltInfo));
+ memcpy(duplicate, original, size*sizeof(AltInfo));
+ return duplicate;
+}
+
+
+/*======================================================================*/
+int lastAltInfoIndex(AltInfo altInfo[])
+{
+ int altIndex;
+
+ /* Loop to last alternative */
+ for (altIndex = -1; !altInfo[altIndex+1].end; altIndex++)
+ ;
+ return altIndex;
+}
+
+
+/*----------------------------------------------------------------------*/
+static AltInfo *nextFreeAltInfo(AltInfoArray altInfos) {
+ return &altInfos[lastAltInfoIndex(altInfos)+1];
+}
+
+
+/*----------------------------------------------------------------------*/
+static void addAlternative(AltInfoArray altInfos, int verb, int level, Aint parameterNumber, Aint theClass, Aid theInstance, AltEntryFinder finder) {
+ AltInfo *altInfoP = nextFreeAltInfo(altInfos);
+
+ altInfoP->alt = (*finder)(verb, parameterNumber, theInstance, theClass);
+ if (altInfoP->alt != NULL) {
+ primeAltInfo(altInfoP, level, parameterNumber, theInstance, theClass);
+ altInfoP[1].end = TRUE;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void addGlobalAlternatives(AltInfoArray altInfos, int verb, AltEntryFinder finder ) {
+ addAlternative(altInfos, verb, GLOBAL_LEVEL, NO_PARAMETER, NO_CLASS, NO_INSTANCE, finder);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void addAlternativesFromParents(AltInfoArray altInfos, int verb, int level, Aint parameterNumber, Aint theClass, Aid theInstance, AltEntryFinder finder){
+ if (classes[theClass].parent != 0)
+ addAlternativesFromParents(altInfos, verb, level,
+ parameterNumber,
+ classes[theClass].parent,
+ theInstance,
+ finder);
+
+ addAlternative(altInfos, verb, level, parameterNumber, theClass, theInstance, finder);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void addAlternativesFromLocation(AltInfoArray altInfos, int verb, Aid location, AltEntryFinder finder) {
+ if (admin[location].location != 0)
+ addAlternativesFromLocation(altInfos, verb, admin[location].location, finder);
+
+ addAlternativesFromParents(altInfos, verb,
+ LOCATION_LEVEL,
+ NO_PARAMETER,
+ instances[location].parent,
+ location,
+ finder);
+
+ addAlternative(altInfos, verb, LOCATION_LEVEL, NO_PARAMETER, NO_CLASS, location, finder);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void addAlternativesFromParameter(AltInfoArray altInfos, int verb, Parameter parameters[], int parameterNumber, AltEntryFinder finder) {
+ Aid parent;
+ Aid theInstance = parameters[parameterNumber-1].instance;
+
+ if (isLiteral(theInstance))
+ parent = literals[literalFromInstance(theInstance)]._class;
+ else
+ parent = instances[theInstance].parent;
+ addAlternativesFromParents(altInfos, verb, PARAMETER_LEVEL, parameterNumber, parent, theInstance, finder);
+
+ if (!isLiteral(theInstance))
+ addAlternative(altInfos, verb, PARAMETER_LEVEL, parameterNumber, NO_CLASS, theInstance, finder);
+}
+
+
+/*======================================================================*/
+bool anyCheckFailed(AltInfoArray altInfo, bool execute)
+{
+ int altIndex;
+
+ if (altInfo != NULL)
+ for (altIndex = 0; !altInfo[altIndex].end; altIndex++) {
+ current.instance = altInfo[altIndex].instance;
+ if (checkFailed(&altInfo[altIndex], execute))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*======================================================================*/
+bool anythingToExecute(AltInfo altInfo[])
+{
+ int altIndex;
+
+ /* Check for anything to execute... */
+ if (altInfo != NULL)
+ for (altIndex = 0; !altInfo[altIndex].end; altIndex++)
+ if (canBeExecuted(&altInfo[altIndex]))
+ return TRUE;
+ return FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static VerbEntry *findVerbEntry(int verbCode, VerbEntry *entries) {
+ VerbEntry *verbEntry;
+ for (verbEntry = entries; !isEndOfArray(verbEntry); verbEntry++) {
+ if (verbEntry->code < 0) {
+ /* Verb codes are negative for Meta verbs, if so they are also 1 off to avoid EOF */
+ if (abs(verbEntry->code)-1 == verbCode)
+ return verbEntry;
+ } else {
+ if (verbEntry->code == verbCode)
+ return verbEntry;
+ }
+ }
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------*/
+static AltEntry *findAlternative(Aaddr verbTableAddress, int verbCode, int parameterNumber)
+{
+ AltEntry *alt;
+ VerbEntry *verbEntry;
+
+ if (verbTableAddress == 0) return NULL;
+
+ verbEntry = findVerbEntry(verbCode, (VerbEntry *) pointerTo(verbTableAddress));
+ if (verbEntry != NULL)
+ for (alt = (AltEntry *) pointerTo(verbEntry->alts); !isEndOfArray(alt); alt++) {
+ if (alt->param == parameterNumber || alt->param == 0) {
+ if (verbEntry->code < 0) current.meta = TRUE;
+ return alt;
+ }
+ }
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------*/
+static AltEntry *alternativeFinder(int verb, int parameterNumber, int theInstance, int theClass)
+{
+ if (theClass != NO_CLASS)
+ return findAlternative(classes[theClass].verbs, verb, parameterNumber);
+ else if (theInstance != NO_INSTANCE)
+ return findAlternative(instances[theInstance].verbs, verb, parameterNumber);
+ else
+ return findAlternative(header->verbTableAddress, verb, parameterNumber);
+}
+
+
+/*======================================================================*/
+AltInfo *findAllAlternatives(int verb, Parameter parameters[]) {
+ int parameterNumber;
+ AltInfo altInfos[1000];
+ altInfos[0].end = TRUE;
+
+ addGlobalAlternatives(altInfos, verb, &alternativeFinder);
+
+ addAlternativesFromLocation(altInfos, verb, current.location, &alternativeFinder);
+
+ for (parameterNumber = 1; !isEndOfArray(&parameters[parameterNumber-1]); parameterNumber++) {
+ addAlternativesFromParameter(altInfos, verb, parameters, parameterNumber, &alternativeFinder);
+ }
+ return duplicateAltInfoArray(altInfos);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool possibleWithFinder(int verb, Parameter parameters[], AltInfoFinder *finder) {
+ bool anything;
+ AltInfo *allAlternatives;
+
+ allAlternatives = finder(verb, parameters);
+
+ // TODO Need to do this since anyCheckFailed() call execute() which assumes the global parameters
+ setGlobalParameters(parameters);
+ if (anyCheckFailed(allAlternatives, DONT_EXECUTE_CHECK_BODY_ON_FAIL))
+ anything = FALSE;
+ else
+ anything = anythingToExecute(allAlternatives);
+
+ if (allAlternatives != NULL)
+ deallocate(allAlternatives);
+
+ return(anything);
+}
+
+
+
+/*======================================================================*/
+bool possible(int verb, Parameter inParameters[], ParameterPosition parameterPositions[])
+{
+ // This is a wrapper for possibleWithFinder() which is used in unit tests
+ // possible() should be used "for real".
+
+ return possibleWithFinder(verb, inParameters, findAllAlternatives);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/alt_info.h b/engines/glk/alan3/alt_info.h
new file mode 100644
index 0000000000..de99585018
--- /dev/null
+++ b/engines/glk/alan3/alt_info.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_ALAN3_ALT_INFO
+#define GLK_ALAN3_ALT_INFO
+
+/* An info node about the Verb Alternatives found and possibly executed */
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/acode.h"
+#include "glk/alan3/params.h"
+#include "glk/alan3/parameter_position.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* Constants */
+
+#define GLOBAL_LEVEL (0)
+#define LOCATION_LEVEL (1)
+#define PARAMETER_LEVEL (2)
+
+#define NO_PARAMETER ((Aword)-1)
+#define NO_INSTANCE ((Aword)-1)
+#define NO_CLASS ((Aword)-1)
+
+/* tryCheck() flags */
+#define EXECUTE_CHECK_BODY_ON_FAIL TRUE
+#define DONT_EXECUTE_CHECK_BODY_ON_FAIL FALSE
+
+
+/* Types */
+
+struct AltInfo {
+ bool end; /* Indicator of end in AltInfoArray, first empty has TRUE here */
+ AltEntry *alt;
+ bool done;
+ Aint level; /* 0 - Global, 1 - location, 2 - parameter */
+ Aid _class; /* In which class, only used for tracing */
+ Aid instance; /* In which instance the Alternative was found,
+ used to set current.instance and tracing */
+ Aid parameter; /* In which parameter, only used for tracing */
+};
+
+typedef AltEntry *(*AltEntryFinder)(int verb, int parameterNumber, int theInstance, int theClass);
+
+typedef AltInfo AltInfoArray[];
+
+
+
+/* Data */
+
+
+/* Functions */
+extern void primeAltInfo(AltInfo *altInfo, int level, int parameter, int instance, int cls);
+extern bool executedOk(AltInfo *altInfo);
+extern bool checkFailed(AltInfo *altInfo, bool execute);
+extern bool canBeExecuted(AltInfo *altInfo);
+extern AltInfo *duplicateAltInfoArray(AltInfoArray altInfos);
+extern int lastAltInfoIndex(AltInfoArray altInfos);
+extern bool anyCheckFailed(AltInfoArray altInfos, bool execute);
+extern bool anythingToExecute(AltInfoArray altInfos);
+extern bool possible(int verb, Parameter parameters[], ParameterPosition parameterPositions[]);
+extern AltInfo *findAllAlternatives(int verb, Parameter parameters[]);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/args.cpp b/engines/glk/alan3/args.cpp
new file mode 100644
index 0000000000..7a020cd1f0
--- /dev/null
+++ b/engines/glk/alan3/args.cpp
@@ -0,0 +1,159 @@
+/* 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/alan3/args.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/utils.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+/* The files and filenames */
+char *adventureName; /* The name of the game */
+char *adventureFileName;
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*======================================================================*/
+char *gameName(char *fullPathName) {
+ char *foundGameName = "";
+
+ if (fullPathName != NULL) {
+ foundGameName = strdup(baseNameStart(fullPathName));
+ foundGameName[strlen(foundGameName)-4] = '\0'; /* Strip off .A3C */
+ }
+
+ if (foundGameName[0] == '.' && foundGameName[1] == '/')
+ strcpy(foundGameName, &foundGameName[2]);
+
+ return foundGameName;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *removeQuotes(char *argument) {
+ char *str = strdup(&argument[1]);
+ str[strlen(str)-1] = '\0';
+ return str;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool isQuoted(char *argument) {
+ return argument[0] == '"' && strlen(argument) > 2;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *addAcodeExtension(char *advFilename) {
+ if (strlen(advFilename) < strlen(ACODEEXTENSION)
+ || compareStrings(&advFilename[strlen(advFilename)-4], ACODEEXTENSION) != 0) {
+ advFilename = (char *)realloc(advFilename, strlen(advFilename)+strlen(ACODEEXTENSION)+1);
+ strcat(advFilename, ACODEEXTENSION);
+ }
+ return advFilename;
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static void switches(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ char *argument = argv[i];
+
+ if (argument[0] == '-') {
+ switch (toLower(argument[1]))
+ {
+ case 'i':
+ ignoreErrorOption = TRUE;
+ break;
+ case 't':
+ traceSectionOption = TRUE;
+ switch (argument[2]) {
+ case '9':
+ case '8':
+ case '7':
+ case '6':
+ case '5' : traceStackOption = TRUE;
+ case '4' : tracePushOption = TRUE;
+ case '3' : traceInstructionOption = TRUE;
+ case '2' : traceSourceOption = TRUE;
+ case '\0':
+ case '1': traceSectionOption = TRUE;
+ }
+ break;
+ case 'd':
+ debugOption = TRUE;
+ break;
+ case 'l':
+ transcriptOption = TRUE;
+ logOption = FALSE;
+ break;
+ case 'v':
+ verboseOption = TRUE;
+ break;
+ case 'n':
+ statusLineOption = FALSE;
+ break;
+ case 'c':
+ logOption = TRUE;
+ transcriptOption = FALSE;
+ break;
+ case 'r':
+ regressionTestOption = TRUE;
+ break;
+ default:
+ printf("Unrecognized switch, -%c\n", argument[1]);
+ usage(argv[0]);
+ terminate(0);
+ }
+ } else {
+
+ if (isQuoted(argument))
+ adventureFileName = removeQuotes(argument);
+ else
+ adventureFileName = strdup(argument);
+
+ adventureFileName = addAcodeExtension(adventureFileName);
+
+ adventureName = gameName(adventureFileName);
+
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+bool differentInterpreterName(char *string) {
+ return strcasecmp(string, PROGNAME) != 0;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/args.h b/engines/glk/alan3/args.h
new file mode 100644
index 0000000000..f70cf3a753
--- /dev/null
+++ b/engines/glk/alan3/args.h
@@ -0,0 +1,52 @@
+/* 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_ALAN3_
+#define GLK_ALAN3_
+
+/* Argument handling */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#ifndef PROGNAME
+#ifdef HAVE_GARGLK
+#define PROGNAME "alan3"
+#else
+#define PROGNAME "arun"
+#endif
+#endif
+
+/* DATA */
+extern char *adventureName; /* The name of the game */
+extern char *adventureFileName;
+
+/* FUNCTIONS */
+extern char *gameName(char fullPathName[]);
+extern void args(int argc, char *argv[]);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/attribute.cpp b/engines/glk/alan3/attribute.cpp
new file mode 100644
index 0000000000..1e5a6dd2db
--- /dev/null
+++ b/engines/glk/alan3/attribute.cpp
@@ -0,0 +1,63 @@
+/* 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/alan3/attribute.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/lists.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*----------------------------------------------------------------------*/
+static AttributeEntry *findAttribute(AttributeEntry *attributeTable, int attributeCode)
+{
+ AttributeEntry *attribute = attributeTable;
+ while (attribute->code != attributeCode) {
+ attribute++;
+ if (isEndOfArray(attribute))
+ syserr("Attribute not found.");
+ }
+ return attribute;
+}
+
+
+/*======================================================================*/
+Aptr getAttribute(AttributeEntry *attributeTable, int attributeCode)
+{
+ AttributeEntry *attribute = findAttribute(attributeTable, attributeCode);
+
+ return attribute->value;
+}
+
+
+/*======================================================================*/
+void setAttribute(AttributeEntry *attributeTable, int attributeCode, Aptr newValue)
+{
+ AttributeEntry *attribute = findAttribute(attributeTable, attributeCode);
+
+ attribute->value = newValue;
+ gameStateChanged = TRUE;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/attribute.h b/engines/glk/alan3/attribute.h
new file mode 100644
index 0000000000..02aac0d926
--- /dev/null
+++ b/engines/glk/alan3/attribute.h
@@ -0,0 +1,38 @@
+/* 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_ALAN3_ATTRIBUTE
+#define GLK_ALAN3_ATTRIBUTE
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* FUNCTIONS */
+extern Aptr getAttribute(AttributeEntry *attributeTable, int attributeCode);
+extern void setAttribute(AttributeEntry *attributeTable, int attributeCode, Aptr newValue);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif \ No newline at end of file
diff --git a/engines/glk/alan3/checkentry.cpp b/engines/glk/alan3/checkentry.cpp
new file mode 100644
index 0000000000..51a839cbd6
--- /dev/null
+++ b/engines/glk/alan3/checkentry.cpp
@@ -0,0 +1,53 @@
+/* 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/alan3/checkentry.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/memory.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*======================================================================*/
+bool checksFailed(Aaddr adr, bool execute)
+{
+ CheckEntry *chk = (CheckEntry *) pointerTo(adr);
+ if (chk->exp == 0) {
+ if (execute == EXECUTE_CHECK_BODY_ON_FAIL)
+ interpret(chk->stms);
+ return TRUE;
+ } else {
+ while (!isEndOfArray(chk)) {
+ if (!evaluate(chk->exp)) {
+ if (execute == EXECUTE_CHECK_BODY_ON_FAIL)
+ interpret(chk->stms);
+ return TRUE;
+ }
+ chk++;
+ }
+ return FALSE;
+ }
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/checkentry.h b/engines/glk/alan3/checkentry.h
new file mode 100644
index 0000000000..a39558f51a
--- /dev/null
+++ b/engines/glk/alan3/checkentry.h
@@ -0,0 +1,56 @@
+/* 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_ALAN3_CHECKENTRY
+#define GLK_ALAN3_CHECKENTRY
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* CONSTANTS */
+#ifndef EXECUTE_CHECK_BODY_ON_FAIL
+#define EXECUTE_CHECK_BODY_ON_FAIL TRUE
+#define DONT_EXECUTE_CHECK_BODY_ON_FAIL FALSE
+#endif
+
+
+/* TYPES */
+struct CheckEntry { /* CHECK TABLE */
+ Aaddr exp; /* ACODE address to expression code */
+ Aaddr stms; /* ACODE address to statement code */
+};
+
+
+/* DATA */
+typedef CheckEntry CheckEntryArray[];
+
+
+/* FUNCTIONS */
+extern bool checksFailed(Aaddr adr, bool execute);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/class.cpp b/engines/glk/alan3/class.cpp
new file mode 100644
index 0000000000..b23d8ac3f4
--- /dev/null
+++ b/engines/glk/alan3/class.cpp
@@ -0,0 +1,38 @@
+/* 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/alan3/class.h"
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+ClassEntry *classes; /* Class table pointer */
+
+/*======================================================================*/
+char *idOfClass(int theClass) {
+ return (char *)pointerTo(classes[theClass].id);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/class.h b/engines/glk/alan3/class.h
new file mode 100644
index 0000000000..a6ff071d9c
--- /dev/null
+++ b/engines/glk/alan3/class.h
@@ -0,0 +1,40 @@
+/* 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_ALAN3_CLASS
+#define GLK_ALAN3_CLASS
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern ClassEntry *classes; /* Class table pointer */
+
+/* FUNCTIONS */
+extern char *idOfClass(int theClass);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/compatibility.cpp b/engines/glk/alan3/compatibility.cpp
new file mode 100644
index 0000000000..a8efb47a09
--- /dev/null
+++ b/engines/glk/alan3/compatibility.cpp
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/compatibility.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*----------------------------------------------------------------------*/
+static bool is3_0Alpha(char version[]) {
+ return version[3] == 3 && version[2] == 0 && version[0] == 'a';
+}
+
+/*----------------------------------------------------------------------*/
+static bool is3_0Beta(char version[]) {
+ return version[3] == 3 && version[2] == 0 && version[0] == 'b';
+}
+
+/*----------------------------------------------------------------------*/
+static int correction(char version[]) {
+ return version[1];
+}
+
+/*======================================================================*/
+bool isPreAlpha5(char version[4]) {
+ return is3_0Alpha(version) && correction(version) <5;
+}
+
+/*======================================================================*/
+bool isPreBeta2(char version[4]) {
+ return is3_0Alpha(version) || (is3_0Beta(version) && correction(version) == 1);
+}
+
+/*======================================================================*/
+bool isPreBeta3(char version[4]) {
+ return is3_0Alpha(version) || (is3_0Beta(version) && correction(version) <= 2);
+}
+
+/*======================================================================*/
+bool isPreBeta4(char version[4]) {
+ return is3_0Alpha(version) || (is3_0Beta(version) && correction(version) <= 3);
+}
+
+/*======================================================================*/
+bool isPreBeta5(char version[4]) {
+ return is3_0Alpha(version) || (is3_0Beta(version) && correction(version) <= 4);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/compatibility.h b/engines/glk/alan3/compatibility.h
new file mode 100644
index 0000000000..0187c72ff8
--- /dev/null
+++ b/engines/glk/alan3/compatibility.h
@@ -0,0 +1,42 @@
+/* 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_ALAN3_COMPATIBILITY
+#define GLK_ALAN3_COMPATIBILITY
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* FUNCTIONS: */
+extern bool isPreAlpha5(char version[4]);
+extern bool isPreBeta2(char version[4]);
+extern bool isPreBeta3(char version[4]);
+extern bool isPreBeta4(char version[4]);
+extern bool isPreBeta5(char version[4]);
+extern char *decodedGameVersion(char version[]);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/container.cpp b/engines/glk/alan3/container.cpp
new file mode 100644
index 0000000000..4b812c98d6
--- /dev/null
+++ b/engines/glk/alan3/container.cpp
@@ -0,0 +1,190 @@
+/* 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/alan3/container.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/output.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+ContainerEntry *containers; /* Container table pointer */
+
+
+/*----------------------------------------------------------------------*/
+static int countInContainer(int containerIndex) /* IN - the container to count in */
+{
+ int j = 0;
+
+ for (uint instanceIndex = 1; instanceIndex <= header->instanceMax; instanceIndex++)
+ if (isIn(instanceIndex, containerIndex, DIRECT))
+ /* Then it's in this container also */
+ j++;
+ return(j);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int sumAttributeInContainer(
+ Aint containerIndex, /* IN - the container to sum */
+ Aint attributeIndex /* IN - the attribute to sum over */
+ ) {
+ uint instanceIndex;
+ int sum = 0;
+
+ for (instanceIndex = 1; instanceIndex <= header->instanceMax; instanceIndex++)
+ if (isIn(instanceIndex, containerIndex, DIRECT)) { /* Then it's directly in this cont */
+ if (instances[instanceIndex].container != 0) /* This is also a container! */
+ sum = sum + sumAttributeInContainer(instanceIndex, attributeIndex);
+ sum = sum + getInstanceAttribute(instanceIndex, attributeIndex);
+ }
+ return(sum);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool containerIsEmpty(int container)
+{
+ int i;
+
+ for (i = 1; i <= header->instanceMax; i++)
+ if (isDescribable(i) && isIn(i, container, TRANSITIVE))
+ return FALSE;
+ return TRUE;
+}
+
+
+/*======================================================================*/
+void describeContainer(int container)
+{
+ if (!containerIsEmpty(container) && !isOpaque(container))
+ list(container);
+}
+
+
+/*======================================================================*/
+bool passesContainerLimits(Aint theContainer, Aint theAddedInstance) {
+ LimitEntry *limit;
+ Aword props;
+
+ if (!isAContainer(theContainer))
+ syserr("Checking limits for a non-container.");
+
+ /* Find the container properties */
+ props = instances[theContainer].container;
+
+ if (containers[props].limits != 0) { /* Any limits at all? */
+ for (limit = (LimitEntry *) pointerTo(containers[props].limits); !isEndOfArray(limit); limit++)
+ if (limit->atr == 1-I_COUNT) { /* TODO This is actually some encoding of the attribute number, right? */
+ if (countInContainer(theContainer) >= limit->val) {
+ interpret(limit->stms);
+ return(FALSE);
+ }
+ } else {
+ if (sumAttributeInContainer(theContainer, limit->atr) + getInstanceAttribute(theAddedInstance, limit->atr) > limit->val) {
+ interpret(limit->stms);
+ return(FALSE);
+ }
+ }
+ }
+ return(TRUE);
+}
+
+
+/*======================================================================*/
+int containerSize(int container, ATrans trans) {
+ Aint i;
+ Aint count = 0;
+
+ for (i = 1; i <= header->instanceMax; i++) {
+ if (isIn(i, container, trans))
+ count++;
+ }
+ return(count);
+}
+
+/*======================================================================*/
+void list(int container)
+{
+ int i;
+ Aword props;
+ Aword foundInstance[2] = {0,0};
+ int found = 0;
+ Aint previousThis = current.instance;
+
+ current.instance = container;
+
+ /* Find container table entry */
+ props = instances[container].container;
+ if (props == 0) syserr("Trying to list something not a container.");
+
+ for (i = 1; i <= header->instanceMax; i++) {
+ if (isDescribable(i)) {
+ /* We can only see objects and actors directly in this container... */
+ if (admin[i].location == container) { /* Yes, it's in this container */
+ if (found == 0) {
+ if (containers[props].header != 0)
+ interpret(containers[props].header);
+ else {
+ if (isAActor(containers[props].owner))
+ printMessageWithInstanceParameter(M_CARRIES, containers[props].owner);
+ else
+ printMessageWithInstanceParameter(M_CONTAINS, containers[props].owner);
+ }
+ foundInstance[0] = i;
+ } else if (found == 1)
+ foundInstance[1] = i;
+ else {
+ printMessageWithInstanceParameter(M_CONTAINS_COMMA, i);
+ }
+ found++;
+ }
+ }
+ }
+
+ if (found > 0) {
+ if (found > 1)
+ printMessageWithInstanceParameter(M_CONTAINS_AND, foundInstance[1]);
+ printMessageWithInstanceParameter(M_CONTAINS_END, foundInstance[0]);
+ } else {
+ if (containers[props].empty != 0)
+ interpret(containers[props].empty);
+ else {
+ if (isAActor(containers[props].owner))
+ printMessageWithInstanceParameter(M_EMPTYHANDED, containers[props].owner);
+ else
+ printMessageWithInstanceParameter(M_EMPTY, containers[props].owner);
+ }
+ }
+ needSpace = TRUE;
+ current.instance = previousThis;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/container.h b/engines/glk/alan3/container.h
new file mode 100644
index 0000000000..88aac47f6e
--- /dev/null
+++ b/engines/glk/alan3/container.h
@@ -0,0 +1,45 @@
+/* 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_ALAN3_CONTAINER
+#define GLK_ALAN3_CONTAINER
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern ContainerEntry *containers; /* Container table pointer */
+
+
+/* FUNCTIONS */
+extern int containerSize(int container, ATrans trans);
+extern bool passesContainerLimits(Aint container, Aint addedInstance);
+extern void describeContainer(int container);
+extern void list(int cnt);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/current.cpp b/engines/glk/alan3/current.cpp
new file mode 100644
index 0000000000..7c2dfe2d66
--- /dev/null
+++ b/engines/glk/alan3/current.cpp
@@ -0,0 +1,33 @@
+/* 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/alan3/current.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+CurVars current;
+bool gameStateChanged = FALSE;
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/current.h b/engines/glk/alan3/current.h
new file mode 100644
index 0000000000..3939816ae8
--- /dev/null
+++ b/engines/glk/alan3/current.h
@@ -0,0 +1,53 @@
+/* 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_ALAN3_CURRENT
+#define GLK_ALAN3_CURRENT
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* TYPES */
+struct CurVars {
+ int syntax,
+ verb,
+ location,
+ actor,
+ instance,
+ tick,
+ score,
+ visits,
+ sourceLine,
+ sourceFile;
+ bool meta;
+};
+
+/* DATA */
+extern CurVars current;
+extern bool gameStateChanged;
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/debug.cpp b/engines/glk/alan3/debug.cpp
new file mode 100644
index 0000000000..f368617e83
--- /dev/null
+++ b/engines/glk/alan3/debug.cpp
@@ -0,0 +1,1129 @@
+/* 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/alan3/debug.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/class.h"
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/alan_version.h"
+#include "glk/alan3/compatibility.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/event.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/readline.h"
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/utils.h"
+#include "glk/streams.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define BREAKPOINTMAX 50
+
+
+/* PUBLIC: */
+int breakpointCount = 0;
+Breakpoint breakpoint[BREAKPOINTMAX];
+
+#define debugPrefix "adbg: "
+
+/*----------------------------------------------------------------------*/
+static void showAttributes(AttributeEntry *attributes)
+{
+ AttributeEntry *at;
+ int i;
+ char str[80];
+
+ if (attributes == 0)
+ return;
+
+ i = 1;
+ for (at = attributes; !isEndOfArray(at); at++) {
+ sprintf(str, "$i$t%s[%d] = %d", (char *) pointerTo(at->id), at->code, (int)at->value);
+#if ISO == 0
+ fromIso(str, str);
+#endif
+ output(str);
+ i++;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showContents(int cnt)
+{
+ int i;
+ char str[80];
+ Abool found = FALSE;
+
+ output("$iContains:");
+ for (i = 1; i <= header->instanceMax; i++) {
+ if (isIn(i, cnt, DIRECT)) { /* Yes, it's directly in this container */
+ if (!found)
+ found = TRUE;
+ output("$i$t");
+ say(i);
+ sprintf(str, "[%d] ", i);
+ output(str);
+ }
+ }
+ if (!found)
+ output("nothing");
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *idOfInstance(int instance) {
+ int base = header->instanceTableAddress+
+ header->instanceMax*sizeof(InstanceEntry)/sizeof(Aword)+1;
+ return (char *)&memory[memory[base+instance-1]];
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayInstanceNumberAndName(int ins) {
+ char buf[1000];
+
+ sprintf(buf, "[%d] %s (\"$$", ins, idOfInstance(ins));
+ output(buf);
+ say(ins);
+ output("$$\")");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayLocationOfInstance(int ins, char *prefix) {
+ if (admin[ins].location == 0)
+ return;
+ else {
+ output(prefix);
+ if (isALocation(admin[ins].location)) {
+ output("at");
+ sayInstanceNumberAndName(admin[ins].location);
+ sayLocationOfInstance(admin[ins].location, prefix);
+ } else if (isAContainer(admin[ins].location)) {
+ if (isAObject(admin[ins].location))
+ output("in");
+ else if (isAActor(admin[ins].location))
+ output("carried by");
+ sayInstanceNumberAndName(admin[ins].location);
+ sayLocationOfInstance(admin[ins].location, prefix);
+ } else
+ output("Illegal location!");
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static void listInstance(int ins) {
+ output("$i");
+ sayInstanceNumberAndName(ins);
+ if (instances[ins].container)
+ output("(container)");
+ sayLocationOfInstance(ins, ", ");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listInstances(char *pattern)
+{
+ int ins;
+ bool found = FALSE;
+
+ for (ins = 1; ins <= header->instanceMax; ins++) {
+ if (pattern == NULL || (pattern != NULL && match(pattern, idOfInstance(ins)))) {
+ if (!found) {
+ output("Instances:");
+ found = TRUE;
+ }
+ listInstance(ins);
+ }
+ }
+ if (pattern != NULL && !found)
+ output("No instances matched the pattern.");
+}
+
+/*----------------------------------------------------------------------*/
+static void showInstance(int ins)
+{
+ char str[80];
+
+ if (ins > header->instanceMax || ins < 1) {
+ sprintf(str, "Instance index %d is out of range.", ins);
+ output(str);
+ return;
+ }
+
+ output("The");
+ sayInstanceNumberAndName(ins);
+ if (instances[ins].parent) {
+ sprintf(str, "Isa %s[%d]", idOfClass(instances[ins].parent), instances[ins].parent);
+ output(str);
+ }
+
+ if (!isA(ins, header->locationClassId) || (isA(ins, header->locationClassId) && admin[ins].location != 0)) {
+ sprintf(str, "$iLocation:");
+ output(str);
+ needSpace = TRUE;
+ sayLocationOfInstance(ins, "");
+ }
+
+ output("$iAttributes:");
+ showAttributes(admin[ins].attributes);
+
+ if (instances[ins].container)
+ showContents(ins);
+
+ if (isA(ins, header->actorClassId)) {
+ if (admin[ins].script == 0)
+ output("$iIs idle");
+ else {
+ sprintf(str, "$iExecuting script: %d, Step: %d", admin[ins].script, admin[ins].step);
+ output(str);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listObjects(void)
+{
+ int obj;
+
+ output("Objects:");
+ for (obj = 1; obj <= header->instanceMax; obj++)
+ if (isAObject(obj))
+ listInstance(obj);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showObject(int obj)
+{
+ char str[80];
+
+
+ if (!isAObject(obj)) {
+ sprintf(str, "Instance %d is not an object", obj);
+ output(str);
+ return;
+ }
+
+ showInstance(obj);
+
+}
+
+#ifdef UNDEF_WHEN_NEEDED
+/*----------------------------------------------------------------------*/
+static void showcnts(void)
+{
+ char str[80];
+ int cnt;
+
+ output("Containers:");
+ for (cnt = 1; cnt <= header->containerMax; cnt++) {
+ sprintf(str, "$i%3d: ", cnt);
+ output(str);
+ if (containers[cnt].owner != 0)
+ say(containers[cnt].owner);
+ }
+
+}
+
+/*----------------------------------------------------------------------*/
+static void showContainer(int cnt)
+{
+ char str[80];
+
+ if (cnt < 1 || cnt > header->containerMax) {
+ sprintf(str, "Container number out of range. Between 1 and %d, please.", header->containerMax);
+ output(str);
+ return;
+ }
+
+ sprintf(str, "Container %d :", cnt);
+ output(str);
+ if (containers[cnt].owner != 0) {
+ cnt = containers[cnt].owner;
+ say(cnt);
+ sprintf(str, "$iLocation: %d", where(cnt, TRUE));
+ output(str);
+ }
+ showContents(cnt);
+}
+#endif
+
+
+/*----------------------------------------------------------------------*/
+static int sourceFileNumber(char *fileName) {
+ SourceFileEntry *entries = (SourceFileEntry *)pointerTo(header->sourceFileTable);
+ int n;
+
+ for (n = 0; *(Aword*)&entries[n] != EOF; n++) {
+ char *entryName;
+ entryName = getStringFromFile(entries[n].fpos, entries[n].len);
+ if (strcmp(entryName, fileName) == 0) return n;
+ entryName = baseNameStart(entryName);
+ if (strcmp(entryName, fileName) == 0) return n;
+ }
+ return -1;
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static void printClassName(int c) {
+ output(idOfClass(c));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showClassInheritance(int c) {
+ char str[80];
+
+ if (classes[c].parent != 0) {
+ output(", Isa");
+ printClassName(classes[c].parent);
+ sprintf(str, "[%d]", classes[c].parent);
+ output(str);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showClass(int cla)
+{
+ char str[80];
+
+ if (cla < 1) {
+ sprintf(str, "Class index %d is out of range.", cla);
+ output(str);
+ return;
+ }
+
+ output("$t");
+ printClassName(cla);
+ sprintf(str, "[%d]", cla);
+ output(str);
+ showClassInheritance(cla);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listClass(int c)
+{
+ char str[80];
+
+ sprintf(str, "%3d: ", c);
+ output(str);
+ printClassName(c);
+ showClassInheritance(c);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showClassHierarchy(int thisItem, int depth)
+{
+ int i;
+ int child;
+
+ output("$i");
+ for (i=0; i < depth; i++)
+ output("$t");
+
+ listClass(thisItem);
+ for (child = 1; child <= header->classMax; child++) {
+ if (classes[child].parent == thisItem) {
+ showClassHierarchy(child, depth+1);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listLocations(void)
+{
+ int loc;
+
+ output("Locations:");
+ for (loc = 1; loc <= header->instanceMax; loc++)
+ if (isALocation(loc))
+ listInstance(loc);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showLocation(int loc)
+{
+ char str[80];
+
+
+ if (!isALocation(loc)) {
+ sprintf(str, "Instance %d is not a location.", loc);
+ output(str);
+ return;
+ }
+
+ output("The ");
+ say(loc);
+ sprintf(str, "(%d) Isa location :", loc);
+ output(str);
+
+ output("$iAttributes =");
+ showAttributes(admin[loc].attributes);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listActors(void)
+{
+ int act;
+
+ output("Actors:");
+ for (act = 1; act <= header->instanceMax; act++)
+ if (isAActor(act))
+ listInstance(act);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showActor(int act)
+{
+ char str[80];
+
+ if (!isAActor(act)) {
+ sprintf(str, "Instance %d is not an actor.", act);
+ output(str);
+ return;
+ }
+
+ showInstance(act);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void showEvents(void)
+{
+ int event, i;
+ char str[80];
+ bool scheduled;
+
+ output("Events:");
+ for (event = 1; event <= header->eventMax; event++) {
+ sprintf(str, "$i%d [%s]:", event, (char *)pointerTo(events[event].id));
+#if ISO == 0
+ fromIso(str, str);
+#endif
+ output(str);
+ scheduled = FALSE;
+ for (i = 0; i < eventQueueTop; i++)
+ if ((scheduled = (eventQueue[i].event == event)))
+ break;
+ if (scheduled) {
+ sprintf(str, "Scheduled for +%d, at ", eventQueue[i].after);
+ output(str);
+ say(eventQueue[i].where);
+ } else
+ output("Not scheduled.");
+ }
+}
+
+
+/*======================================================================*/
+char *sourceFileName(int fileNumber) {
+ SourceFileEntry *entries = (SourceFileEntry *)pointerTo(header->sourceFileTable);
+
+ return getStringFromFile(entries[fileNumber].fpos, entries[fileNumber].len);
+}
+
+
+/*======================================================================*/
+bool readLine(Common::SeekableReadStream *rs, char *line, int maxLen) {
+ if (rs->pos() < rs->size()) {
+ line[maxLen - 1] = '\0';
+
+ char c;
+ do {
+ c = rs->readByte();
+ *line++ = c;
+ } while (--maxLen > 1);
+ }
+
+ return rs->pos() < rs->size();
+}
+
+
+/*======================================================================*/
+char *readSourceLine(int file, int line) {
+ int count;
+#define SOURCELINELENGTH 1000
+ static char buffer[SOURCELINELENGTH];
+
+ frefid_t sourceFileRef = g_vm->glk_fileref_create_by_name(fileusage_TextMode, sourceFileName(file), 0);
+ strid_t sourceFile = g_vm->glk_stream_open_file(sourceFileRef, filemode_Read, 0);
+
+ if (sourceFile != NULL) {
+ for (count = 0; count < line; count++) {
+ if (!readLine(*sourceFile, buffer, SOURCELINELENGTH))
+ return NULL;
+
+ // If not read the whole line, or no newline, try to read again
+ while (strchr(buffer, '\n') == NULL) {
+ if (!readLine(*sourceFile, buffer, SOURCELINELENGTH))
+ break;
+ }
+ }
+
+ delete sourceFile;
+ return buffer;
+ }
+
+ return NULL;
+}
+
+/*======================================================================*/
+void showSourceLine(int fileNumber, int line) {
+ char *buffer = readSourceLine(fileNumber, line);
+ if (buffer != NULL) {
+ if (buffer[strlen(buffer)-1] == '\n')
+ buffer[strlen(buffer)-1] = '\0';
+ printf("<%05d>: %s", line, buffer);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listFiles() {
+ SourceFileEntry *entry;
+ int i = 0;
+ for (entry = (SourceFileEntry *)pointerTo(header->sourceFileTable); *((Aword*)entry) != EOF; entry++) {
+ printf(" %2d : %s\n", i, sourceFileName(i));
+ i++;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listLines() {
+ SourceLineEntry *entry;
+ for (entry = (SourceLineEntry *)pointerTo(header->sourceLineTable); *((Aword*)entry) != EOF; entry++)
+ printf(" %s:%d\n", sourceFileName(entry->file), entry->line);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int findSourceLineIndex(SourceLineEntry *entry, int file, int line) {
+ /* Will return index to the closest line available */
+ int i = 0;
+
+ while (!isEndOfArray(&entry[i]) && entry[i].file != file)
+ i++;
+ while (!isEndOfArray(&entry[i]) && entry[i].file == file && entry[i].line < line)
+ i++;
+ if (isEndOfArray(entry) || entry[i].file != file)
+ return i-1;
+ else
+ return i;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void listBreakpoints() {
+ int i;
+ bool found = FALSE;
+
+ for (i = 0; i < BREAKPOINTMAX; i++)
+ if (breakpoint[i].line != 0) {
+ if (!found)
+ printf("Breakpoints set:\n");
+ found = TRUE;
+ printf(" %s:%d\n", sourceFileName(breakpoint[i].file), breakpoint[i].line);
+ }
+ if (!found)
+ printf("No breakpoints set\n");
+}
+
+
+/*======================================================================*/
+int breakpointIndex(int file, int line) {
+ int i;
+
+ for (i = 0; i < BREAKPOINTMAX; i++)
+ if (breakpoint[i].line == line && breakpoint[i].file == file)
+ return i;
+ return -1;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int availableBreakpointSlot() {
+ int i;
+
+ for (i = 0; i < BREAKPOINTMAX; i++)
+ if (breakpoint[i].line == 0)
+ return i;
+ return -1;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void setBreakpoint(int file, int line) {
+ int i = breakpointIndex(file, line);
+
+ if (i != -1)
+ printf("Breakpoint already set at %s:%d\n", sourceFileName(file), line);
+ else {
+ i = availableBreakpointSlot();
+ if (i == -1)
+ printf("No room for more breakpoints. Delete one first.\n");
+ else {
+ int lineIndex = findSourceLineIndex((SourceLineEntry *)pointerTo(header->sourceLineTable), file, line);
+ SourceLineEntry *entry = (SourceLineEntry *)pointerTo(header->sourceLineTable);
+ char leadingText[100] = "Breakpoint";
+ if (entry[lineIndex].file == EOF) {
+ printf("Line %d not available\n", line);
+ } else {
+ if (entry[lineIndex].line != line)
+ sprintf(leadingText, "Line %d not available, breakpoint instead", line);
+ breakpoint[i].file = entry[lineIndex].file;
+ breakpoint[i].line = entry[lineIndex].line;
+ printf("%s set at %s:%d\n", leadingText, sourceFileName(entry[lineIndex].file), entry[lineIndex].line);
+ showSourceLine(entry[lineIndex].file, entry[lineIndex].line);
+ printf("\n");
+ }
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void deleteBreakpoint(int line, int file) {
+ int i = breakpointIndex(file, line);
+
+ if (i == -1)
+ printf("No breakpoint set at %s:%d\n", sourceFileName(file), line);
+ else {
+ breakpoint[i].line = 0;
+ printf("Breakpoint at %s:%d deleted\n", sourceFileName(file), line);
+ }
+}
+
+
+
+static bool saved_traceSection, saved_traceInstruction, saved_capitilize, saved_tracePush, saved_traceStack, saved_traceSource;
+static int loc;
+
+/*======================================================================*/
+void saveInfo(void)
+{
+ /* Save some important things */
+ saved_capitilize = capitalize; capitalize = FALSE;
+ saved_traceSection = traceSectionOption; traceSectionOption = FALSE;
+ saved_traceSource = traceSourceOption; traceSourceOption = FALSE;
+ saved_traceInstruction = traceInstructionOption; traceInstructionOption = FALSE;
+ saved_tracePush = tracePushOption; tracePushOption = FALSE;
+ saved_traceStack = traceStackOption; traceStackOption = FALSE;
+ loc = current.location; current.location = where(HERO, DIRECT);
+}
+
+
+/*======================================================================*/
+void restoreInfo(void)
+{
+ /* Restore! */
+ capitalize = saved_capitilize;
+ traceSectionOption = saved_traceSection;
+ traceInstructionOption = saved_traceInstruction;
+ traceSourceOption = saved_traceSource;
+ tracePushOption = saved_tracePush;
+ traceStackOption = saved_traceStack;
+ current.location = loc;
+}
+
+#define HELP_COMMAND 'H'
+#define QUIT_COMMAND 'Q'
+#define EXIT_COMMAND 'X'
+#define GO_COMMAND 'G'
+#define FILES_COMMAND 'F'
+#define INSTANCES_COMMAND 'I'
+#define CLASSES_COMMAND 'C'
+#define OBJECTS_COMMAND 'O'
+#define ACTORS_COMMAND 'A'
+#define LOCATIONS_COMMAND 'L'
+#define EVENTS_COMMAND 'E'
+#define BREAK_COMMAND 'B'
+#define DELETE_COMMAND 'D'
+#define TRACE_COMMAND 'R'
+#define SECTION_TRACE_COMMAND 'T'
+#define INSTRUCTION_TRACE_COMMAND 'S'
+#define NEXT_COMMAND 'N'
+#define UNKNOWN_COMMAND '?'
+#define AMBIGUOUS_COMMAND '-'
+#define TRACE_SOURCE_COMMAND 's'
+#define TRACE_SECTION_COMMAND 'e'
+#define TRACE_INSTRUCTION_COMMAND 'i'
+#define TRACE_PUSH_COMMAND 'p'
+#define TRACE_STACK_COMMAND 't'
+
+typedef struct DebugParseEntry {
+ char *command;
+ char *parameter;
+ char code;
+ char *helpText;
+} DebugParseEntry;
+
+static DebugParseEntry commandEntries[] = {
+ {"help", "", HELP_COMMAND, "this help"},
+ {"?", "", HELP_COMMAND, "d:o"},
+ {"break", "[[file:]n]", BREAK_COMMAND, "set breakpoint at source line [n] (optionally in [file])"},
+ {"delete", "[[file:]n]", DELETE_COMMAND, "delete breakpoint at source line [n] (optionally in [file])"},
+ {"files", "", FILES_COMMAND, "list source files"},
+ {"events", "", EVENTS_COMMAND, "list events"},
+ {"classes", "", CLASSES_COMMAND, "list class hierarchy"},
+ {"instances", "[n]", INSTANCES_COMMAND, "list instance(s), all, wildcard, number or name"},
+ {"objects", "[n]", OBJECTS_COMMAND, "list instance(s) that are objects"},
+ {"actors", "[n]", ACTORS_COMMAND, "list instance(s) that are actors"},
+ {"locations", "[n]", LOCATIONS_COMMAND, "list instances that are locations"},
+ {"trace", "('source'|'section'|'instruction'|'push'|'stack')", TRACE_COMMAND, "toggle various traces"},
+ {"next", "", NEXT_COMMAND, "run game and stop at next source line"},
+ {"go", "", GO_COMMAND, "go another player turn"},
+ {"exit", "", EXIT_COMMAND, "exit to game, enter 'debug' to get back"},
+ {"x", "", EXIT_COMMAND, "d:o"},
+ {"quit", "", QUIT_COMMAND, "quit game"},
+ {NULL, NULL}
+};
+
+static DebugParseEntry traceSubcommand[] = {
+ {"source", "", TRACE_SOURCE_COMMAND, ""},
+ {"section", "", TRACE_SECTION_COMMAND, ""},
+ {"instructions", "", TRACE_INSTRUCTION_COMMAND, ""},
+ {"pushs", "", TRACE_PUSH_COMMAND, ""},
+ {"stacks", "", TRACE_STACK_COMMAND, ""},
+ {NULL, NULL}
+};
+
+
+static char *spaces(int length) {
+ static char buf[200];
+ int i;
+
+ for (i = 0; i<length; i++)
+ buf[i] = ' ';
+ buf[i] = '\0';
+ return buf;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *padding(DebugParseEntry *entry, int maxLength) {
+ return spaces(maxLength-strlen(entry->command)-strlen(entry->parameter));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleHelpCommand() {
+ if (!regressionTestOption)
+ output(alan.longHeader);
+
+ DebugParseEntry *entry = commandEntries;
+
+ int maxLength = 0;
+ for (entry = commandEntries; entry->command != NULL; entry++) {
+ if (strlen(entry->command)+strlen(entry->parameter) > maxLength)
+ maxLength = strlen(entry->command)+strlen(entry->parameter);
+ }
+
+ output("$nADBG Commands (can be abbreviated):");
+ for (entry = commandEntries; entry->command != NULL; entry++) {
+ char buf[200];
+ sprintf(buf, "$i%s %s %s$n$t$t-- %s", entry->command, entry->parameter, padding(entry, maxLength), entry->helpText);
+ output(buf);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static DebugParseEntry *findEntry(char *command, DebugParseEntry *entry) {
+ while (entry->command != NULL) {
+ if (strncasecmp(command, entry->command, strlen(command)) == 0)
+ return entry;
+ entry++;
+ }
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char parseDebugCommand(char *command) {
+ DebugParseEntry *entry = findEntry(command, commandEntries);
+ if (entry != NULL) {
+ if (strlen(command) < strlen(entry->command)) {
+ /* See if there are any more partial matches */
+ if (findEntry(command, entry+1) != NULL)
+ /* TODO: we should list the possible matches somehow */
+ return AMBIGUOUS_COMMAND;
+ }
+ return entry->code;
+ } else
+ return UNKNOWN_COMMAND;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void readCommand(char buf[]) {
+ char c;
+
+ capitalize = FALSE;
+ if (anyOutput) newline();
+ do {
+ output("adbg> ");
+
+#ifdef USE_READLINE
+ if (!readline(buf)) {
+#else
+ if (fgets(buf, 255, stdin) == NULL) {
+#endif
+ newline();
+ quitGame();
+ }
+ lin = 1;
+ c = buf[0];
+ } while (c == '\0');
+}
+
+
+/*----------------------------------------------------------------------*/
+static void displaySourceLocation(int line, int fileNumber) {
+ char *cause;
+ if (anyOutput) newline();
+ if (breakpointIndex(fileNumber, line) != -1)
+ cause = "Breakpoint hit at";
+ else
+ cause = "Stepping to";
+ printf("%s %s %s:%d\n", debugPrefix, cause, sourceFileName(fileNumber), line);
+ showSourceLine(fileNumber, line);
+ printf("\n");
+ anyOutput = FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void toggleSectionTrace() {
+ if ((saved_traceSection = !saved_traceSection))
+ printf("Section trace on.");
+ else
+ printf("Section trace off.");
+}
+
+/*----------------------------------------------------------------------*/
+static void toggleInstructionTrace() {
+ if ((saved_traceInstruction = !saved_traceInstruction))
+ printf("Single instruction trace on.");
+ else
+ printf("Single instruction trace off.");
+}
+
+/*----------------------------------------------------------------------*/
+static void toggleSourceTrace() {
+ if ((saved_traceSource = !saved_traceSource))
+ printf("Source code trace on.");
+ else
+ printf("Source code trace off.");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void togglePushTrace() {
+ if ((saved_tracePush = !saved_tracePush))
+ printf("Stack Push trace on.");
+ else
+ printf("Stack Push trace off.");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void toggleStackTrace() {
+ if ((saved_traceStack = !saved_traceStack))
+ printf("Full stack trace on.");
+ else
+ printf("Full stack trace off.");
+}
+
+
+/*----------------------------------------------------------------------*/
+static int parseTraceCommand() {
+ char *subcommand = strtok(NULL, "");
+ DebugParseEntry *entry;
+ if (subcommand == 0)
+ return UNKNOWN_COMMAND;
+ else {
+ entry = findEntry(subcommand, traceSubcommand);
+ if (entry != NULL) {
+ if (strlen(subcommand) < strlen(entry->command)) {
+ if (findEntry(subcommand, entry+1) != NULL)
+ return AMBIGUOUS_COMMAND;
+ }
+ return entry->code;
+ } else
+ return UNKNOWN_COMMAND;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *printTraceState(bool state) {
+ if (state)
+ return "on - Traces";
+ else
+ return "off - Doesn't trace";
+}
+
+/*----------------------------------------------------------------------*/
+static void printTrace(void) {
+ printf("Trace section : %s entry to every section (check, description, event, actor, ...)\n", printTraceState(saved_traceSection));
+ printf("Trace source : %s every source line executed\n", printTraceState(saved_traceSource));
+ printf("Trace instruction : %s every Amachine instruction executed\n", printTraceState(saved_traceInstruction));
+ printf("Trace push : %s every push onto the Amachine stack\n", printTraceState(saved_tracePush));
+ printf("Trace stack : %s the complete stack every time\n", printTraceState(saved_traceStack));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleTraceCommand() {
+ char subcommand = parseTraceCommand();
+
+ switch (subcommand) {
+ case TRACE_SECTION_COMMAND: toggleSectionTrace(); break;
+ case TRACE_SOURCE_COMMAND: toggleSourceTrace(); break;
+ case TRACE_INSTRUCTION_COMMAND: toggleInstructionTrace(); break;
+ case TRACE_PUSH_COMMAND: togglePushTrace(); break;
+ case TRACE_STACK_COMMAND: toggleStackTrace(); break;
+ case AMBIGUOUS_COMMAND: output("Ambiguous Trace subcommand abbreviation. ? for help."); break;
+ default: printTrace();
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleBreakCommand(int fileNumber) {
+ char *parameter = strtok(NULL, ":");
+ if (parameter != NULL && isalpha((int)parameter[0])) {
+ fileNumber = sourceFileNumber(parameter);
+ if (fileNumber == -1) {
+ printf("No such file: '%s'\n", parameter);
+ return;
+ }
+ parameter = strtok(NULL, "");
+ }
+ if (parameter == NULL)
+ listBreakpoints();
+ else
+ setBreakpoint(fileNumber, atoi(parameter));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleDeleteCommand(bool calledFromBreakpoint, int line, int fileNumber) {
+ char *parameter = strtok(NULL, "");
+ if (parameter == NULL) {
+ if (calledFromBreakpoint)
+ deleteBreakpoint(line, fileNumber);
+ else
+ printf("No current breakpoint to delete\n");
+ } else
+ deleteBreakpoint(atoi(parameter), fileNumber);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleNextCommand(bool calledFromBreakpoint) {
+ stopAtNextLine = TRUE;
+ debugOption = FALSE;
+ if (!calledFromBreakpoint)
+ current.sourceLine = 0;
+ restoreInfo();
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleLocationsCommand() {
+ char *parameter = strtok(NULL, "");
+ if (parameter == 0)
+ listLocations();
+ else
+ showLocation(atoi(parameter));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleActorsCommand() {
+ char *parameter = strtok(NULL, "");
+ if (parameter == NULL)
+ listActors();
+ else
+ showActor(atoi(parameter));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleClassesCommand() {
+ char *parameter = strtok(NULL, "");
+ if (parameter == NULL || strchr(parameter, '*') != 0) {
+ output("Classes:");
+ showClassHierarchy(1, 0);
+ listInstances(parameter);
+ } else if (isdigit((int)parameter[0]))
+ showClass(atoi(parameter));
+ else {
+ printf("You have to give a class index to display. You can't use names (yet).");
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleObjectsCommand() {
+ char *parameter = strtok(NULL, "");
+ if (parameter == NULL)
+ listObjects();
+ else
+ showObject(atoi(parameter));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleInstancesCommand() {
+ char *parameter = strtok(NULL, "");
+ int i;
+
+ if (parameter == NULL || strchr(parameter, '*') != 0)
+ listInstances(parameter);
+ else if (isdigit((int)parameter[0]))
+ showInstance(atoi(parameter));
+ else {
+ for (i = 1; i<header->instanceMax; i++)
+ if (strcmp(parameter, idOfInstance(i)) == 0) {
+ showInstance(i);
+ return;
+ }
+ printf("No instance named '%s'.", parameter);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static bool exactSameVersion() {
+ return header->version[3] == alan.version.version
+ && header->version[2] == alan.version.revision
+ && header->version[1] == alan.version.correction
+ && header->version[0] == alan.version.state[0];
+}
+
+
+/*======================================================================*/
+void debug(bool calledFromBreakpoint, int line, int fileNumber)
+{
+ static bool warned = FALSE;
+
+ saveInfo();
+
+#ifdef HAVE_GLK
+ g_vm->glk_set_style(style_Preformatted);
+#endif
+
+ if (calledFromBreakpoint)
+ displaySourceLocation(line, fileNumber);
+ else {
+ if (!exactSameVersion() && !warned && !regressionTestOption) {
+ printf("<WARNING: You are debugging a game which has version %s.>\n",
+ decodedGameVersion(header->version));
+ printf("<That is not exactly the same as this interpreter (%s).>\n", alan.version.string);
+ printf("<This might cause a lot of trouble. Cross your fingers...>\n");
+ warned = TRUE;
+ }
+ }
+
+ while (TRUE) {
+
+ char commandLine[200];
+ readCommand(commandLine);
+ char *command = strtok(commandLine, " ");
+ char commandCode = parseDebugCommand(command);
+
+ switch (commandCode) {
+ case AMBIGUOUS_COMMAND: output("Ambiguous ADBG command abbreviation. ? for help."); break;
+ case ACTORS_COMMAND: handleActorsCommand(); break;
+ case BREAK_COMMAND: handleBreakCommand(fileNumber); break;
+ case CLASSES_COMMAND: handleClassesCommand(); break;
+ case DELETE_COMMAND: handleDeleteCommand(calledFromBreakpoint, line, fileNumber); break;
+ case EVENTS_COMMAND: showEvents(); break;
+ case EXIT_COMMAND: debugOption = FALSE; restoreInfo(); goto exit_debug;
+ case FILES_COMMAND: listFiles(); break;
+ case GO_COMMAND: restoreInfo(); goto exit_debug;
+ case HELP_COMMAND: handleHelpCommand(); break;
+ case INSTANCES_COMMAND: handleInstancesCommand(); break;
+ case TRACE_COMMAND: handleTraceCommand(); break;
+ case INSTRUCTION_TRACE_COMMAND: toggleInstructionTrace(); break;
+ case LOCATIONS_COMMAND: handleLocationsCommand(); break;
+ case NEXT_COMMAND: handleNextCommand(calledFromBreakpoint); goto exit_debug;
+ case OBJECTS_COMMAND: handleObjectsCommand(); break;
+ case QUIT_COMMAND: terminate(0); break;
+ case SECTION_TRACE_COMMAND: toggleSectionTrace(); break;
+ default: output("Unknown ADBG command. ? for help."); break;
+ }
+ }
+
+ exit_debug:
+#ifdef HAVE_GLK
+ g_vm->glk_set_style(style_Normal);
+#endif
+ ;
+}
+
+
+/*======================================================================*/
+void traceSay(int item)
+{
+ /*
+ Say something, but make sure we don't disturb anything and that it is
+ shown to the player. Needed for tracing. During debugging things are
+ set up to avoid this problem.
+ */
+
+ saveInfo();
+ needSpace = FALSE;
+ col = 1;
+ if (item == 0)
+ printf("$null$");
+ else
+ say(item);
+ needSpace = FALSE;
+ col = 1;
+ restoreInfo();
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/debug.h b/engines/glk/alan3/debug.h
new file mode 100644
index 0000000000..3bebe27d29
--- /dev/null
+++ b/engines/glk/alan3/debug.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_DEBUG
+#define GLK_ALAN3_DEBUG
+
+/* Header file for debug handler in Alan interpreter */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* Types: */
+
+struct Breakpoint {
+ int line;
+ int file;
+};
+
+
+/* Data: */
+extern int breakpointCount;
+extern Breakpoint breakpoint[];
+
+/* Functions: */
+extern void saveInfo(void);
+extern void restoreInfo(void);
+extern int breakpointIndex(int file, int line);
+extern char *sourceFileName(int file);
+extern char *readSourceLine(int file, int line);
+extern void showSourceLine(int fileNumber, int line);
+extern void debug(bool calledFromBreakpoint, int line, int fileNumber);
+extern void traceSay(int item);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/decode.cpp b/engines/glk/alan3/decode.cpp
new file mode 100644
index 0000000000..c06d19aa48
--- /dev/null
+++ b/engines/glk/alan3/decode.cpp
@@ -0,0 +1,182 @@
+/* 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/alan3/decode.h"
+#include "glk/alan3/acode.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/memory.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+Aword *freq; /* Cumulative character frequencies */
+
+
+/* PRIVATE DATA */
+/* Bit output */
+static int decodeBuffer; /* Bits to be input */
+static int bitsToGo; /* Bits still in buffer */
+static int garbageBits; /* Bits past EOF */
+
+
+static int inputBit(void)
+{
+ int bit;
+
+ /* More bits available ? */
+ if (!bitsToGo) {
+ /* No, so get more */
+ decodeBuffer = (textFile->pos() >= textFile->size()) ? EOF : textFile->readByte();
+ if (decodeBuffer == EOF) {
+ garbageBits++;
+ if (garbageBits > VALUEBITS-2)
+ syserr("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;
+}
+
+
+/* Current state of decoding */
+
+static CodeValue value; /* Currently seen code value */
+static CodeValue low, high; /* Current code region */
+
+
+void startDecoding(void)
+{
+ int i;
+
+ bitsToGo = 0;
+ garbageBits = 0;
+
+ value = 0;
+ for (i = 0; i < VALUEBITS; i++)
+ value = 2*value + inputBit();
+ low = 0;
+ high = TOPVALUE;
+}
+
+
+int decodeChar(void)
+{
+ long range;
+ int f;
+ int symbol;
+
+ range = (long)(high-low) + 1;
+ f = (((long)(value-low)+1)*freq[0]-1)/range;
+
+ /* 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)
+ ;
+ 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;
+}
+
+
+
+/* Structure for saved decode info */
+struct DecodeInfo {
+ long fpos;
+ int buffer;
+ int bits;
+ CodeValue value;
+ CodeValue high;
+ CodeValue low;
+};
+
+
+/*======================================================================
+
+ pushDecode()
+
+ Save so much info about the decoding process so it is possible to
+ restore and continue later.
+
+ */
+void *pushDecode(void)
+{
+ DecodeInfo *info;
+
+ info = (DecodeInfo *)allocate(sizeof(DecodeInfo));
+ info->fpos = textFile->pos();
+ info->buffer = decodeBuffer;
+ info->bits = bitsToGo;
+ info->value = value;
+ info->high = high;
+ info->low = low;
+ return(info);
+}
+
+
+/*======================================================================
+
+ popDecode()
+
+ Restore enough info about the decoding process so it is possible to
+ continue after having decoded something else.
+
+ */
+void popDecode(void *i)
+{
+ DecodeInfo *info = (DecodeInfo *) i;
+
+ textFile->seek(info->fpos);
+ decodeBuffer = info->buffer;
+ bitsToGo = info->bits;
+ value = info->value;
+ high = info->high;
+ low = info->low;
+
+ free(info);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/decode.h b/engines/glk/alan3/decode.h
new file mode 100644
index 0000000000..c239d85f35
--- /dev/null
+++ b/engines/glk/alan3/decode.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_DECODE
+#define GLK_ALAN3_DECODE
+
+/* Arithmetic decoding module in Arun */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern Aword *freq; /* Cumulated character frequencies for text decoding */
+
+/* FUNCTIONS */
+
+extern void startDecoding(void);
+extern int decodeChar(void);
+extern void *pushDecode(void);
+extern void popDecode(void *info);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/dictionary.cpp b/engines/glk/alan3/dictionary.cpp
new file mode 100644
index 0000000000..375b7f72ca
--- /dev/null
+++ b/engines/glk/alan3/dictionary.cpp
@@ -0,0 +1,135 @@
+/* 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/alan3/dictionary.h"
+#include "glk/alan3/word.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+DictionaryEntry *dictionary; /* Dictionary pointer */
+int dictionarySize;
+int conjWord; /* First conjunction in dictionary, for ',' */
+
+
+
+/* Word class query methods, move to Word.c */
+/* Word classes are numbers but in the dictionary they are generated as bits */
+static bool isVerb(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&VERB_BIT)!=0;
+}
+
+bool isVerbWord(int wordIndex) {
+ return isVerb(playerWords[wordIndex].code);
+}
+
+bool isConjunction(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&CONJUNCTION_BIT)!=0;
+}
+
+bool isConjunctionWord(int wordIndex) {
+ return isConjunction(playerWords[wordIndex].code);
+}
+
+static bool isExcept(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&EXCEPT_BIT)!=0;
+}
+
+bool isExceptWord(int wordIndex) {
+ return isExcept(playerWords[wordIndex].code);
+}
+
+static bool isThem(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&THEM_BIT)!=0;
+}
+
+bool isThemWord(int wordIndex) {
+ return isThem(playerWords[wordIndex].code);
+}
+
+static bool isIt(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&IT_BIT)!=0;
+}
+
+bool isItWord(int wordIndex) {
+ return isIt(playerWords[wordIndex].code);
+}
+
+static bool isNoun(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&NOUN_BIT)!=0;
+}
+
+bool isNounWord(int wordIndex) {
+ return isNoun(playerWords[wordIndex].code);
+}
+
+static bool isAdjective(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&ADJECTIVE_BIT)!=0;
+}
+
+bool isAdjectiveWord(int wordIndex) {
+ return isAdjective(playerWords[wordIndex].code);
+}
+
+static bool isPreposition(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&PREPOSITION_BIT)!=0;
+}
+
+bool isPrepositionWord(int wordIndex) {
+ return isPreposition(playerWords[wordIndex].code);
+}
+
+bool isAll(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&ALL_BIT)!=0;
+}
+
+bool isAllWord(int wordIndex) {
+ return isAll(playerWords[wordIndex].code);
+}
+
+static bool isDir(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&DIRECTION_BIT)!=0;
+}
+
+bool isDirectionWord(int wordIndex) {
+ return isDir(playerWords[wordIndex].code);
+}
+
+bool isNoise(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&NOISE_BIT)!=0;
+}
+
+bool isPronoun(int wordCode) {
+ return wordCode < dictionarySize && (dictionary[wordCode].classBits&PRONOUN_BIT)!=0;
+}
+
+bool isPronounWord(int wordIndex) {
+ return isPronoun(playerWords[wordIndex].code);
+}
+
+bool isLiteralWord(int wordIndex) {
+ return playerWords[wordIndex].code >= dictionarySize;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/dictionary.h b/engines/glk/alan3/dictionary.h
new file mode 100644
index 0000000000..1f5f5f9602
--- /dev/null
+++ b/engines/glk/alan3/dictionary.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_DICTIONARY
+#define GLK_ALAN3_DICTIONARY
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern DictionaryEntry *dictionary;
+extern int dictionarySize;
+extern int conjWord; /* First conjunction in dictionary */
+
+
+/* FUNCTIONS */
+extern bool isVerbWord(int wordIndex);
+extern bool isConjunctionWord(int wordIndex);
+extern bool isExceptWord(int wordIndex);
+extern bool isThemWord(int wordIndex);
+extern bool isItWord(int wordIndex);
+extern bool isNounWord(int wordIndex);
+extern bool isAdjectiveWord(int wordIndex);
+extern bool isPrepositionWord(int wordIndex);
+extern bool isAllWord(int wordIndex);
+extern bool isDirectionWord(int wordIndex);
+extern bool isPronounWord(int wordIndex);
+extern bool isLiteralWord(int wordIndex);
+
+extern bool isConjunction(int wordCode);
+extern bool isAll(int wordCode);
+extern bool isNoise(int wordCode);
+extern bool isPronoun(int wordCode);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/event.cpp b/engines/glk/alan3/event.cpp
new file mode 100644
index 0000000000..ce866be6e9
--- /dev/null
+++ b/engines/glk/alan3/event.cpp
@@ -0,0 +1,35 @@
+/* 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/alan3/event.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+int eventQueueSize = 0;
+EventQueueEntry *eventQueue = NULL;
+int eventQueueTop = 0;
+EventEntry *events;
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/event.h b/engines/glk/alan3/event.h
new file mode 100644
index 0000000000..4855548f1b
--- /dev/null
+++ b/engines/glk/alan3/event.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_EVENT
+#define GLK_ALAN3_EVENT
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* TYPES */
+typedef struct EventQueueEntry { /* EVENT QUEUE ENTRIES */
+ int after;
+ int event;
+ int where;
+} EventQueueEntry;
+
+
+/* DATA */
+/* Event queue */
+extern int eventQueueSize;
+extern EventQueueEntry *eventQueue;
+extern int eventQueueTop; /* Event queue top pointer */
+extern EventEntry *events; /* Event table pointer */
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/exe.cpp b/engines/glk/alan3/exe.cpp
new file mode 100644
index 0000000000..eb01870e2f
--- /dev/null
+++ b/engines/glk/alan3/exe.cpp
@@ -0,0 +1,753 @@
+/* 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/alan3/exe.h"
+#include "glk/alan3/actor.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/args.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/decode.h"
+#include "glk/alan3/event.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/readline.h"
+#include "glk/alan3/save.h"
+#include "glk/alan3/score.h"
+#include "glk/alan3/state.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/utils.h"
+#include "glk/alan3/word.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+
+Common::SeekableReadStream *textFile;
+
+/* Long jump buffers */
+// TODO move to longjump.c? or error.c, and abstract them into functions?
+//jmp_buf restartLabel; /* Restart long jump return point */
+//jmp_buf returnLabel; /* Error (or undo) long jump return point */
+//jmp_buf forfeitLabel; /* Player forfeit by an empty command */
+
+
+/* PRIVATE CONSTANTS */
+
+#define WIDTH 80
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static char logFileName[256] = "";
+
+/*======================================================================*/
+void setStyle(int style)
+{
+#ifdef HAVE_GLK
+ switch (style) {
+ case NORMAL_STYLE: g_vm->glk_set_style(style_Normal); break;
+ case EMPHASIZED_STYLE: g_vm->glk_set_style(style_Emphasized); break;
+ case PREFORMATTED_STYLE: g_vm->glk_set_style(style_Preformatted); break;
+ case ALERT_STYLE: g_vm->glk_set_style(style_Alert); break;
+ case QUOTE_STYLE: g_vm->glk_set_style(style_BlockQuote); break;
+ }
+#endif
+}
+
+/*======================================================================*/
+void print(Aword fpos, Aword len)
+{
+ char str[2*WIDTH]; /* String buffer */
+ int outlen = 0; /* Current output length */
+ int ch = 0;
+ int i;
+ long savfp = 0; /* Temporary saved text file position */
+ static bool printFlag = FALSE; /* Printing already? */
+ bool savedPrintFlag = printFlag;
+ void *info = NULL; /* Saved decoding info */
+
+
+ if (len == 0) return;
+
+ if (isHere(HERO, /*TRUE*/ DIRECT)) { /* Check if the player will see it */
+ if (printFlag) { /* Already printing? */
+ /* Save current text file position and/or decoding info */
+ if (header->pack)
+ info = pushDecode();
+ else
+ savfp = textFile->pos();
+ }
+ printFlag = TRUE; /* We're printing now! */
+
+ /* Position to start of text */
+ textFile->seek(fpos+header->stringOffset);
+
+ if (header->pack)
+ 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 (header->pack)
+ ch = decodeChar();
+ else
+ ch = textFile->readByte();
+
+ str[i] = ch;
+ if (textFile->pos() >= textFile->size()) /* Or end of text? */
+ break;
+ }
+ str[i] = '\0';
+#if ISO == 0
+ fromIso(str, str);
+#endif
+ output(str);
+ }
+
+ /* And restore */
+ printFlag = savedPrintFlag;
+ if (printFlag) {
+ if (header->pack)
+ popDecode(info);
+ else
+ textFile->seek(savfp);
+ }
+ }
+}
+
+
+/*======================================================================*/
+void sys(Aword fpos, Aword len)
+{
+ ::error("sys calls are unsupported");
+}
+
+
+/*======================================================================*/
+char *getStringFromFile(Aword fpos, Aword len)
+{
+ char *buf = (char *)allocate(len+1);
+ char *bufp = buf;
+
+ /* Position to start of text */
+ textFile->seek(fpos+header->stringOffset);
+
+ if (header->pack)
+ startDecoding();
+ while (len--)
+ if (header->pack)
+ *(bufp++) = decodeChar();
+ else
+ *(bufp++) = textFile->readByte();
+
+ /* Terminate string with zero */
+ *bufp = '\0';
+
+ return buf;
+}
+
+
+
+/*======================================================================*/
+void score(Aword sc)
+{
+ if (sc == 0) {
+ ParameterArray messageParameters = newParameterArray();
+ addParameterForInteger(messageParameters, current.score);
+ addParameterForInteger(messageParameters, header->maximumScore);
+ addParameterForInteger(messageParameters, current.tick);
+ printMessageWithParameters(M_SCORE, messageParameters);
+ freeParameterArray(messageParameters);
+ } else {
+ current.score += scores[sc-1];
+ scores[sc-1] = 0;
+ gameStateChanged = TRUE;
+ }
+}
+
+
+/*======================================================================*/
+void visits(Aword v)
+{
+ current.visits = v;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayUndoneCommand(char *words) {
+ static Parameter *messageParameters = NULL;
+ messageParameters = (Parameter *)ensureParameterArrayAllocated(messageParameters);
+
+ current.location = where(HERO, DIRECT);
+ clearParameterArray(messageParameters);
+ addParameterForString(&messageParameters[0], words);
+ setEndOfArray(&messageParameters[1]);
+ printMessageWithParameters(M_UNDONE, messageParameters);
+}
+
+
+/*======================================================================*/
+void undo(void) {
+ forgetGameState();
+ if (anySavedState()) {
+ recallGameState();
+ sayUndoneCommand(recreatePlayerCommand());
+ } else {
+ printMessage(M_NO_UNDO);
+ }
+#ifdef TODO
+ longjmp(returnLabel, UNDO_RETURN);
+#else
+ ::error("TODO: undo longjmp");
+#endif
+}
+
+
+/*======================================================================*/
+void quitGame(void)
+{
+#ifdef TODO
+ char buf[80];
+
+ current.location = where(HERO, DIRECT);
+ para();
+ while (TRUE) {
+ col = 1;
+ statusline();
+ printMessage(M_QUITACTION);
+#ifdef USE_READLINE
+ if (!readline(buf)) terminate(0);
+#else
+ if (gets(buf) == NULL) terminate(0);
+#endif
+ if (strcasecmp(buf, "restart") == 0)
+ longjmp(restartLabel, TRUE);
+ else if (strcasecmp(buf, "restore") == 0) {
+ restore();
+ return;
+ } else if (strcasecmp(buf, "quit") == 0) {
+ terminate(0);
+ } else if (strcasecmp(buf, "undo") == 0) {
+ if (gameStateChanged) {
+ rememberCommands();
+ rememberGameState();
+ undo();
+ } else {
+ if (anySavedState()) {
+ recallGameState();
+ sayUndoneCommand(playerWordsAsCommandString());
+ } else
+ printMessage(M_NO_UNDO);
+ longjmp(returnLabel, UNDO_RETURN);
+ }
+ }
+ }
+#endif
+ syserr("Fallthrough in QUIT");
+}
+
+
+
+/*======================================================================*/
+void restartGame(void)
+{
+ Aint previousLocation = current.location;
+#ifdef TODO
+ current.location = where(HERO, DIRECT);
+ para();
+ if (confirm(M_REALLY)) {
+ longjmp(restartLabel, TRUE);
+ }
+ current.location = previousLocation;
+#lse
+ ::error("TODO: restartGame");
+#endif
+}
+
+
+
+/*======================================================================*/
+void cancelEvent(Aword theEvent)
+{
+ int i;
+
+ for (i = eventQueueTop-1; i>=0; i--)
+ if (eventQueue[i].event == theEvent) {
+ while (i < eventQueueTop-1) {
+ eventQueue[i].event = eventQueue[i+1].event;
+ eventQueue[i].after = eventQueue[i+1].after;
+ eventQueue[i].where = eventQueue[i+1].where;
+ i++;
+ }
+ eventQueueTop--;
+ return;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void increaseEventQueue(void)
+{
+ eventQueue = (EventQueueEntry *)realloc(eventQueue, (eventQueueTop+2)*sizeof(EventQueueEntry));
+ if (eventQueue == NULL) syserr("Out of memory in increaseEventQueue()");
+
+ eventQueueSize = eventQueueTop + 2;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void moveEvent(int to, int from) {
+ eventQueue[to].event = eventQueue[from].event;
+ eventQueue[to].after = eventQueue[from].after;
+ eventQueue[to].where = eventQueue[from].where;
+}
+
+
+/*======================================================================*/
+void schedule(Aword event, Aword where, Aword after)
+{
+ int i;
+
+ if (event == 0) syserr("NULL event");
+
+ cancelEvent(event);
+ /* Check for overflow */
+ if (eventQueue == NULL || eventQueueTop == eventQueueSize)
+ increaseEventQueue();
+
+ /* Bubble this event down */
+ for (i = eventQueueTop; i >= 1 && eventQueue[i-1].after <= after; i--) {
+ moveEvent(i, i-1);
+ }
+
+ eventQueue[i].after = after;
+ eventQueue[i].where = where;
+ eventQueue[i].event = event;
+ eventQueueTop++;
+}
+
+
+// TODO Move to string.c?
+/*======================================================================*/
+Aptr concat(Aptr as1, Aptr as2)
+{
+ char *s1 = (char *)fromAptr(as1);
+ char *s2 = (char *)fromAptr(as2);
+ char *result = (char *)allocate(strlen((char*)s1)+strlen((char*)s2)+1);
+ strcpy(result, s1);
+ strcat(result, s2);
+ return toAptr(result);
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *stripCharsFromStringForwards(int count, char *initialString, char **theRest)
+{
+ int stripPosition;
+ char *strippedString;
+ char *rest;
+
+ if (count > strlen(initialString))
+ stripPosition = strlen(initialString);
+ else
+ stripPosition = count;
+ rest = strdup(&initialString[stripPosition]);
+ strippedString = strdup(initialString);
+ strippedString[stripPosition] = '\0';
+ *theRest = rest;
+ return strippedString;
+}
+
+/*----------------------------------------------------------------------*/
+static char *stripCharsFromStringBackwards(Aint count, char *initialString, char **theRest) {
+ int stripPosition;
+ char *strippedString;
+ char *rest;
+
+ if (count > strlen(initialString))
+ stripPosition = 0;
+ else
+ stripPosition = strlen(initialString)-count;
+ strippedString = strdup(&initialString[stripPosition]);
+ rest = strdup(initialString);
+ rest[stripPosition] = '\0';
+ *theRest = rest;
+ return strippedString;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int countLeadingBlanks(char *string, int position) {
+ static char blanks[] = " ";
+ return strspn(&string[position], blanks);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int skipWordForwards(char *string, int position)
+{
+ char separators[] = " .,?";
+
+ int i;
+
+ for (i = position; i<=strlen(string) && strchr(separators, string[i]) == NULL; i++)
+ ;
+ return i;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *stripWordsFromStringForwards(Aint count, char *initialString, char **theRest) {
+ int skippedChars;
+ int position = 0;
+ char *stripped;
+ int i;
+
+ for (i = count; i>0; i--) {
+ /* Ignore any initial blanks */
+ skippedChars = countLeadingBlanks(initialString, position);
+ position += skippedChars;
+ position = skipWordForwards(initialString, position);
+ }
+
+ stripped = (char *)allocate(position+1);
+ strncpy(stripped, initialString, position);
+ stripped[position] = '\0';
+
+ skippedChars = countLeadingBlanks(initialString, position);
+ *theRest = strdup(&initialString[position+skippedChars]);
+
+ return(stripped);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int skipWordBackwards(char *string, int position)
+{
+ char separators[] = " .,?";
+ int i;
+
+ for (i = position; i>0 && strchr(separators, string[i-1]) == NULL; i--)
+ ;
+ return i;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int countTrailingBlanks(char *string, int position) {
+ int skippedChars, i;
+ skippedChars = 0;
+
+ if (position > strlen(string)-1)
+ syserr("position > length in countTrailingBlanks");
+ for (i = position; i >= 0 && string[i] == ' '; i--)
+ skippedChars++;
+ return(skippedChars);
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *stripWordsFromStringBackwards(Aint count, char *initialString, char **theRest) {
+ int skippedChars;
+ char *stripped;
+ int strippedLength;
+ int position = strlen(initialString);
+ int i;
+
+ for (i = count; i>0 && position>0; i--) {
+ position -= 1;
+ /* Ignore trailing blanks */
+ skippedChars = countTrailingBlanks(initialString, position);
+ if (position - skippedChars < 0) break; /* No more words to strip */
+ position -= skippedChars;
+ position = skipWordBackwards(initialString, position);
+ }
+
+ skippedChars = countLeadingBlanks(initialString, 0);
+ strippedLength = strlen(initialString)-position-skippedChars;
+ stripped = (char *)allocate(strippedLength+1);
+ strncpy(stripped, &initialString[position+skippedChars], strippedLength);
+ stripped[strippedLength] = '\0';
+
+ if (position > 0) {
+ skippedChars = countTrailingBlanks(initialString, position-1);
+ position -= skippedChars;
+ }
+ *theRest = strdup(initialString);
+ (*theRest)[position] = '\0';
+ return(stripped);
+}
+
+
+
+/*======================================================================*/
+Aptr strip(bool stripFromBeginningNotEnd, int count, bool stripWordsNotChars, int id, int atr)
+{
+ char *initialString = (char *)fromAptr(getInstanceAttribute(id, atr));
+ char *theStripped;
+ char *theRest;
+
+ if (stripFromBeginningNotEnd) {
+ if (stripWordsNotChars)
+ theStripped = stripWordsFromStringForwards(count, initialString, &theRest);
+ else
+ theStripped = stripCharsFromStringForwards(count, initialString, &theRest);
+ } else {
+ if (stripWordsNotChars)
+ theStripped = stripWordsFromStringBackwards(count, initialString, &theRest);
+ else
+ theStripped = stripCharsFromStringBackwards(count, initialString, &theRest);
+ }
+ setInstanceStringAttribute(id, atr, theRest);
+ return toAptr(theStripped);
+}
+
+
+/*======================================================================*/
+int getContainerMember(int container, int index, bool directly) {
+ Aint i;
+ Aint count = 0;
+
+ for (i = 1; i <= header->instanceMax; i++) {
+ if (isIn(i, container, DIRECT)) {
+ count++;
+ if (count == index)
+ return i;
+ }
+ }
+ apperr("Index not in container in 'containerMember()'");
+ return 0;
+}
+
+
+/***********************************************************************\
+
+ Description Handling
+
+\***********************************************************************/
+
+
+/*======================================================================*/
+void showImage(int image, int align)
+{
+#ifdef HAVE_GLK
+ uint ecode;
+
+ if ((g_vm->glk_gestalt(gestalt_Graphics, 0) == 1) &&
+ (g_vm->glk_gestalt(gestalt_DrawImage, wintype_TextBuffer) == 1)) {
+ g_vm->glk_window_flow_break(glkMainWin);
+ printf("\n");
+ ecode = g_vm->glk_image_draw(glkMainWin, image, imagealign_MarginLeft, 0);
+ (void)ecode;
+ }
+#endif
+}
+
+
+/*======================================================================*/
+void playSound(int sound)
+{
+#ifdef HAVE_GLK
+#ifdef GLK_MODULE_SOUND
+ static schanid_t soundChannel = NULL;
+
+ if (g_vm->glk_gestalt(gestalt_Sound, 0) == 1) {
+ if (soundChannel == NULL)
+ soundChannel = g_vm->glk_schannel_create(0);
+ if (soundChannel != NULL) {
+ g_vm->glk_schannel_stop(soundChannel);
+ (void)g_vm->glk_schannel_play(soundChannel, sound);
+ }
+ }
+#endif
+#endif
+}
+
+
+
+/*======================================================================*/
+void empty(int cnt, int whr)
+{
+ int i;
+
+ for (i = 1; i <= header->instanceMax; i++)
+ if (isIn(i, cnt, DIRECT))
+ locate(i, whr);
+}
+
+
+
+/*======================================================================*/
+void use(int actor, int script)
+{
+ char str[80];
+ StepEntry *step;
+
+ if (!isAActor(actor)) {
+ sprintf(str, "Instance is not an Actor (%d).", actor);
+ syserr(str);
+ }
+
+ admin[actor].script = script;
+ admin[actor].step = 0;
+ step = stepOf(actor);
+ if (step != NULL && step->after != 0) {
+ admin[actor].waitCount = evaluate(step->after);
+ }
+
+ gameStateChanged = TRUE;
+}
+
+/*======================================================================*/
+void stop(int act)
+{
+ char str[80];
+
+ if (!isAActor(act)) {
+ sprintf(str, "Instance is not an Actor (%d).", act);
+ syserr(str);
+ }
+
+ admin[act].script = 0;
+ admin[act].step = 0;
+
+ gameStateChanged = TRUE;
+}
+
+
+
+static int randomValue = 0;
+/*----------------------------------------------------------------------*/
+int randomInteger(int from, int to)
+{
+ if (regressionTestOption) {
+ int ret = from + randomValue;
+ /* Generate them in sequence */
+ if (ret > to) {
+ ret = from;
+ randomValue = 1;
+ } else if (ret == to)
+ randomValue = 0;
+ else
+ randomValue++;
+ return ret;
+ } else {
+ if (to == from)
+ return to;
+ else if (to > from)
+ return (rand()/10)%(to-from+1)+from;
+ else
+ return (rand()/10)%(from-to+1)+to;
+ }
+}
+
+
+
+/*----------------------------------------------------------------------*/
+bool between(int val, int low, int high)
+{
+ if (high > low)
+ return low <= val && val <= high;
+ else
+ return high <= val && val <= low;
+}
+
+
+
+/*======================================================================*/
+bool contains(Aptr string, Aptr substring)
+{
+ bool found;
+
+ strlow((char *)fromAptr(string));
+ strlow((char *)fromAptr(substring));
+
+ found = (strstr((char *)fromAptr(string), (char *)fromAptr(substring)) != 0);
+
+ return found;
+}
+
+
+/*======================================================================*/
+bool streq(char a[], char b[])
+{
+ bool eq;
+
+ strlow(a);
+ strlow(b);
+
+ eq = (strcmp(a, b) == 0);
+
+ return eq;
+}
+
+
+
+/*======================================================================*/
+void startTranscript(void) {
+ time_t tick;
+
+ if (logFile != NULL)
+ return;
+
+ Common::String target = g_vm->getTargetName() + ".log";
+
+ uint fileUsage = transcriptOption ? fileusage_Transcript : fileusage_InputRecord;
+ frefid_t logFileRef = g_vm->glk_fileref_create_by_name(fileUsage, logFileName, 0);
+ logFile = g_vm->glk_stream_open_file(logFileRef, filemode_Write, 0);
+
+ if (logFile == NULL) {
+ transcriptOption = FALSE;
+ logOption = FALSE;
+ } else {
+ transcriptOption = TRUE;
+ }
+}
+
+
+/*======================================================================*/
+void stopTranscript(void) {
+ if (logFile == NULL)
+ return;
+
+ if (transcriptOption|| logOption)
+ delete logFile;
+
+ logFile = NULL;
+ transcriptOption = FALSE;
+ logOption = FALSE;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/exe.h b/engines/glk/alan3/exe.h
new file mode 100644
index 0000000000..77fb70a578
--- /dev/null
+++ b/engines/glk/alan3/exe.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_EXE
+#define GLK_ALAN3_EXE
+
+/* Header file for instruction execution unit in Alan interpreter */
+
+/* IMPORTS */
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/set.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* CONSTANTS */
+#define NO_JUMP_RETURN 0
+#define ERROR_RETURN 1
+#define UNDO_RETURN 2
+
+
+/* DATA */
+
+/* The text and message file */
+extern Common::SeekableReadStream *textFile;
+
+/* Long jump buffer for restart, errors and undo */
+//extern jmp_buf restartLabel;
+//extern jmp_buf returnLabel;
+//extern jmp_buf forfeitLabel;
+
+
+/* FUNCTIONS */
+extern void sys(Aword fpos, Aword len);
+extern void sayInteger(int val);
+extern void sayString(char *str);
+extern Aptr strip(bool stripFromBeginningNotEnd, int count, bool stripWordsNotChars, int id, int atr);
+extern Aptr concat(Aptr s1, Aptr s2);
+extern char *getStringFromFile(Aword fpos, Aword len);
+extern void print(Aword fpos, Aword len);
+extern void setStyle(int style);
+extern void showImage(int image, int align);
+extern void playSound(int sound);
+extern void score(Aword sc);
+extern void visits(Aword v);
+extern void undo(void);
+extern void quitGame(void);
+extern void restartGame(void);
+
+extern void use(int act, int scr);
+extern void stop(int act);
+
+extern void empty(int cnt, int whr);
+extern int getContainerMember(int container, int index, bool directly);
+extern int randomInContainer(int cont);
+
+extern void schedule(Aword evt, Aword whr, Aword aft);
+extern void cancelEvent(Aword evt);
+
+extern int randomInteger(int from, int to);
+extern bool between(int val, int from, int to);
+extern bool contains(Aptr string, Aptr substring);
+extern bool streq(char a[], char b[]);
+
+extern void include(int instance, int atr, Aword member);
+extern void exclude(int instance, int atr, Aword member);
+extern void increase(int instance, int atr, Aword step);
+extern void decrease(int instance, int atr, Aword step);
+
+extern void startTranscript(void);
+extern void stopTranscript(void);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/fnmatch.cpp b/engines/glk/alan3/fnmatch.cpp
new file mode 100644
index 0000000000..203a7bf4b3
--- /dev/null
+++ b/engines/glk/alan3/fnmatch.cpp
@@ -0,0 +1,234 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*-
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+#include "glk/alan3/fnmatch.h"
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define EOS '\0'
+
+#define RANGE_MATCH 1
+#define RANGE_NOMATCH 0
+#define RANGE_ERROR (-1)
+
+static int rangematch(const char *, char, int, char **);
+
+int fnmatch(const char *pattern, const char *string, int flags) {
+ const char *stringstart;
+ char *newp;
+ char c, test;
+
+ for (stringstart = string;;)
+ switch (c = *pattern++) {
+ case EOS:
+ if ((flags & FNM_LEADING_DIR) && *string == '/')
+ return (0);
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = *++pattern;
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS)
+ if (flags & FNM_PATHNAME)
+ return ((flags & FNM_LEADING_DIR) ||
+ strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH);
+ else
+ return (0);
+ else if (c == '/' && flags & FNM_PATHNAME) {
+ if ((string = strchr(string, '/')) == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = *string) != EOS) {
+ if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
+ return (0);
+ if (test == '/' && flags & FNM_PATHNAME)
+ break;
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ switch (rangematch(pattern, *string, flags, &newp)) {
+ case RANGE_ERROR:
+ goto norm;
+ case RANGE_MATCH:
+ pattern = newp;
+ break;
+ case RANGE_NOMATCH:
+ return (FNM_NOMATCH);
+ }
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = *pattern++) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ norm:
+ if (c == *string)
+ ;
+ else if ((flags & FNM_CASEFOLD) &&
+ (tolower((unsigned char)c) ==
+ tolower((unsigned char)*string)))
+ ;
+ else
+ return (FNM_NOMATCH);
+ string++;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static int
+rangematch(const char *pattern, char test, int flags, char **newp)
+{
+ int negate, ok;
+ char c, c2;
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if ( (negate = (*pattern == '!' || *pattern == '^')) )
+ ++pattern;
+
+ if (flags & FNM_CASEFOLD)
+ test = tolower((unsigned char)test);
+
+ /*
+ * A right bracket shall lose its special meaning and represent
+ * itself in a bracket expression if it occurs first in the list.
+ * -- POSIX.2 2.8.3.2
+ */
+ ok = 0;
+ c = *pattern++;
+ do {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = *pattern++;
+ if (c == EOS)
+ return (RANGE_ERROR);
+
+ if (c == '/' && (flags & FNM_PATHNAME))
+ return (RANGE_NOMATCH);
+
+ if (flags & FNM_CASEFOLD)
+ c = tolower((unsigned char)c);
+
+ if (*pattern == '-'
+ && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = *pattern++;
+ if (c2 == EOS)
+ return (RANGE_ERROR);
+
+ if (flags & FNM_CASEFOLD)
+ c2 = tolower((unsigned char)c2);
+
+ if (c <= test && test <= c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ } while ((c = *pattern++) != ']');
+
+ *newp = const_cast<char *>(pattern);
+ return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/fnmatch.h b/engines/glk/alan3/fnmatch.h
new file mode 100644
index 0000000000..d98ffa5182
--- /dev/null
+++ b/engines/glk/alan3/fnmatch.h
@@ -0,0 +1,83 @@
+/* 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_ALAN3_FNMATCH
+#define GLK_ALAN3_FNMATCH
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
+ */
+
+namespace Glk {
+namespace Alan3 {
+
+#define FNM_NOMATCH 1 /* Match failed. */
+#define FNM_NOSYS 2 /* Function not supported (unused). */
+
+#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
+#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
+#define FNM_PERIOD 0x04 /* Period must be matched by period. */
+#ifndef _POSIX_SOURCE
+#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
+#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
+#define FNM_IGNORECASE FNM_CASEFOLD
+#define FNM_FILE_NAME FNM_PATHNAME
+#endif
+
+extern int fnmatch(const char *pattern, const char *string, int flags);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
+
diff --git a/engines/glk/alan3/glkio.cpp b/engines/glk/alan3/glkio.cpp
new file mode 100644
index 0000000000..b0671561f6
--- /dev/null
+++ b/engines/glk/alan3/glkio.cpp
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/glkio.h"
+
+namespace Glk {
+namespace Alan3 {
+
+void glkio_printf(const char *fmt, ...) {
+ // If there's a savegame being loaded from the launcher, ignore any text out
+ if (g_vm->_saveSlot != -1)
+ return;
+
+ va_list argp;
+ va_start(argp, fmt);
+ if (glkMainWin) {
+ char buf[1024]; /* FIXME: buf size should be foolproof */
+ vsprintf(buf, fmt, argp);
+ g_vm->glk_put_string(buf);
+ } else {
+ // assume stdio is available in this case only
+ Common::String str = Common::String::vformat(fmt, argp);
+ warning(fmt, argp);
+ }
+
+ va_end(argp);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/glkio.h b/engines/glk/alan3/glkio.h
new file mode 100644
index 0000000000..5bcd6e6b63
--- /dev/null
+++ b/engines/glk/alan3/glkio.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_ALAN3_GLKIO
+#define GLK_ALAN3_GLKIO
+
+/* Header file for Glk output for Alan interpreter */
+
+#include "glk/alan3/alan3.h"
+#include "glk/windows.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern winid_t glkMainWin;
+extern winid_t glkStatusWin;
+
+/* NB: this header must be included in any file which calls printf() */
+
+#undef printf
+#define printf glkio_printf
+extern void glkio_printf(const char *, ...);
+
+#ifdef MAP_STDIO_TO_GLK
+#define fgetc(stream) glk_get_char_stream(stream)
+#define rewind(stream) glk_stream_set_position(stream, 0, seekmode_Start);
+#define fwrite(buf, elementSize, count, stream) glk_put_buffer_stream(stream, buf, elementSize*count);
+#define fread(buf, elementSize, count, stream) glk_get_buffer_stream(stream, buf, elementSize*count);
+#define fclose(stream) glk_stream_close(stream, NULL)
+#define fgets(buff, len, stream) glk_get_line_stream(stream, buff, len)
+#endif
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/glkstart.cpp b/engines/glk/alan3/glkstart.cpp
new file mode 100644
index 0000000000..0c54cfd447
--- /dev/null
+++ b/engines/glk/alan3/glkstart.cpp
@@ -0,0 +1,209 @@
+/* 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/alan3/glkstart.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/args.h"
+#include "glk/alan3/main.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/resources.h"
+#include "glk/alan3/utils.h"
+ //#include "glk/alan3/gi_blorb.h"
+#include "glk/streams.h"
+
+#ifdef HAVE_WINGLK
+#include "glk/alan3/WinGlk.h"
+#endif
+
+
+#ifdef HAVE_GARGLK
+#include "glk/alan3/alan_version.h"
+#endif
+
+namespace Glk {
+namespace Alan3 {
+
+glkunix_argumentlist_t glkunix_arguments[] = {
+ { "-l", glkunix_arg_NoValue, "-l: log player command and game output" },
+ { "-c", glkunix_arg_NoValue, "-c: log player commands to a file" },
+ { "-n", glkunix_arg_NoValue, "-n: no status line" },
+ { "-i", glkunix_arg_NoValue, "-i: ignore version and checksum errors" },
+ { "-d", glkunix_arg_NoValue, "-d: enter debug mode" },
+ { "-t", glkunix_arg_NoValue, "-t [<n>]: trace game execution, higher <n> gives more trace" },
+ { "-r", glkunix_arg_NoValue, "-r: refrain from printing timestamps and paging (making regression testing easier)" },
+ { "", glkunix_arg_ValueFollows, "filename: The game file to load." },
+ { NULL, glkunix_arg_End, NULL }
+};
+
+/* Resources */
+static strid_t resourceFile;
+
+/*----------------------------------------------------------------------*/
+static void openGlkWindows() {
+ glkMainWin = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+ if (glkMainWin == NULL) {
+ printf("FATAL ERROR: Cannot open initial window");
+ g_vm->glk_exit();
+ }
+#ifdef HAVE_GARGLK
+ glk_stylehint_set (wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
+#endif
+ glkStatusWin = g_vm->glk_window_open(glkMainWin, winmethod_Above |
+ winmethod_Fixed, 1, wintype_TextGrid, 0);
+ g_vm->glk_set_window(glkStatusWin);
+ g_vm->glk_set_style(style_Preformatted);
+ g_vm->glk_set_window(glkMainWin);
+}
+
+/*----------------------------------------------------------------------*/
+static void openResourceFile() {
+#ifdef TODO
+ char *originalFileName = strdup(adventureFileName);
+ char *resourceFileName = originalFileName;
+ char *extension = strrchr(resourceFileName, '.');
+ frefid_t resourceFileRef;
+// giblorb_err_t ecode;
+
+ strcpy(extension, ".a3r");
+
+ resourceFileRef = winglk_fileref_create_by_name(fileusage_BinaryMode,
+ resourceFileName, 0, FALSE);
+
+ if (glk_fileref_does_file_exist(resourceFileRef)) {
+ resourceFile = glk_stream_open_file(resourceFileRef, filemode_Read, 0);
+ ecode = giblorb_set_resource_map(resourceFile);
+ (void)ecode;
+ }
+ free(originalFileName);
+#endif
+}
+
+
+/*======================================================================*/
+int glkunix_startup_code(glkunix_startup_t *data) {
+ g_vm->glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Weight, 0);
+ g_vm->glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1);
+ g_vm->glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Indentation, 10);
+
+ /* first, open a window for error output */
+ openGlkWindows();
+
+#ifdef HAVE_GARGLK
+#if (BUILD+0) != 0
+ {
+ char name[100];
+ sprintf(name, "%s-%d", alan.shortHeader, BUILD);
+ garglk_set_program_name(name);
+ }
+#else
+ garglk_set_program_name(alan.shortHeader);
+#endif
+ char info[80];
+ sprintf(info, "%s Interpreter by Thomas Nilefalk\n", alan.shortHeader);
+ garglk_set_program_info(info);
+#endif
+
+ /* now process the command line arguments */
+ args(data->argc, data->argv);
+
+ if (adventureFileName == NULL || strcmp(adventureFileName, "") == 0) {
+ printf("You should supply a game file to play.\n");
+ usage("arun"); // TODO Find real programname from arguments
+ terminate(1);
+ }
+
+ /* Open any possible blorb resource file */
+ openResourceFile();
+
+ return TRUE;
+}
+
+
+
+#ifdef HAVE_WINGLK
+static int argCount;
+static char *argumentVector[10];
+
+/*----------------------------------------------------------------------*/
+static void splitArgs(char *commandLine) {
+ unsigned char *cp = (unsigned char *)commandLine;
+
+ while (*cp) {
+ while (*cp && isspace(*cp)) cp++;
+ if (*cp) {
+ argumentVector[argCount++] = (char *)cp;
+ if (*cp == '"') {
+ do {
+ cp++;
+ } while (*cp != '"');
+ cp++;
+ } else
+ while (*cp && !isspace(*cp))
+ cp++;
+ if (*cp) {
+ *cp = '\0';
+ cp++;
+ }
+ }
+ }
+}
+
+
+/*======================================================================*/
+int winglk_startup_code(const char* cmdline)
+{
+ char windowTitle[200];
+
+ /* Process the command line arguments */
+ argumentVector[0] = "";
+ argCount = 1;
+
+ splitArgs(strdup(cmdline));
+
+ args(argCount, argumentVector);
+
+
+ if (adventureFileName == NULL || strcmp(adventureFileName, "") == 0) {
+ adventureFileName = (char*)winglk_get_initial_filename(NULL, "Arun : Select an Alan game file",
+ "Alan Game Files (*.a3c)|*.a3c||");
+ if (adventureFileName == NULL) {
+ terminate(0);
+ }
+ adventureName = gameName(adventureFileName);
+ }
+
+ winglk_app_set_name("WinArun");
+ winglk_set_gui(IDR_ARUN);
+
+ sprintf(windowTitle, "WinArun : %s", adventureName);
+ winglk_window_set_title(windowTitle);
+ openGlkWindows();
+
+ /* Open any possible blorb resource file */
+ openResourceFile();
+
+ return TRUE;
+}
+#endif
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/glkstart.h b/engines/glk/alan3/glkstart.h
new file mode 100644
index 0000000000..4420bea2f9
--- /dev/null
+++ b/engines/glk/alan3/glkstart.h
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_GLKSTART
+#define GLK_ALAN3_GLKSTART
+
+/* glkstart.h: Unix-specific header file for GlkTerm, CheapGlk, and XGlk
+ (Unix implementations of the Glk API).
+ Designed by Andrew Plotkin <erkyrath@netcom.com>
+ http://www.eblong.com/zarf/glk/index.html
+*/
+
+/* This header defines an interface that must be used by program linked
+ with the various Unix Glk libraries -- at least, the three I wrote.
+ (I encourage anyone writing a Unix Glk library to use this interface,
+ but it's not part of the Glk spec.)
+
+ Because Glk is *almost* perfectly portable, this interface *almost*
+ doesn't have to exist. In practice, it's small.
+*/
+
+namespace Glk {
+namespace Alan3 {
+
+/* We define our own TRUE and FALSE and NULL, because ANSI
+ is a strange world. */
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define glkunix_arg_End (0)
+#define glkunix_arg_ValueFollows (1)
+#define glkunix_arg_NoValue (2)
+#define glkunix_arg_ValueCanFollow (3)
+#define glkunix_arg_NumberValue (4)
+
+struct glkunix_argumentlist_t {
+ char *name;
+ int argtype;
+ char *desc;
+};
+
+struct glkunix_startup_t {
+ int argc;
+ char **argv;
+};
+
+/* The list of command-line arguments; this should be defined in your code. */
+extern glkunix_argumentlist_t glkunix_arguments[];
+
+/* The external function; this should be defined in your code. */
+extern int glkunix_startup_code(glkunix_startup_t *data);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/instance.cpp b/engines/glk/alan3/instance.cpp
new file mode 100644
index 0000000000..49bc72d703
--- /dev/null
+++ b/engines/glk/alan3/instance.cpp
@@ -0,0 +1,1152 @@
+/* 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/alan3/instance.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/attribute.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/container.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/checkentry.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/class.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/actor.h"
+#include "glk/alan3/literal.h"
+#include "glk/alan3/dictionary.h"
+#include "glk/alan3/location.h"
+#include "glk/alan3/compatibility.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+
+InstanceEntry *instances; /* Instance table pointer */
+
+AdminEntry *admin; /* Administrative data about instances */
+AttributeEntry *attributes; /* Dynamic attribute values */
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/* Instance query methods */
+
+/*======================================================================*/
+bool isA(int instance, int ancestor)
+{
+ int parent;
+
+ if (isLiteral(instance))
+ parent = literals[instance-header->instanceMax]._class;
+ else
+ parent = instances[instance].parent;
+ while (parent != 0 && parent != ancestor)
+ parent = classes[parent].parent;
+
+ return (parent != 0);
+}
+
+
+bool isAObject(int instance)
+{
+ return isA(instance, OBJECT);
+}
+
+bool isAContainer(int instance)
+{
+ return instance != 0 && !isLiteral(instance) && instances[instance].container != 0;
+}
+
+bool isAActor(int instance)
+{
+ return isA(instance, ACTOR);
+}
+
+bool isALocation(int instance)
+{
+ return isA(instance, LOCATION);
+}
+
+
+bool isLiteral(int instance)
+{
+ return instance > header->instanceMax;
+}
+
+bool isANumeric(int instance)
+{
+ return isLiteral(instance) && literals[literalFromInstance(instance)].type == NUMERIC_LITERAL;
+}
+
+bool isAString(int instance)
+{
+ return isLiteral(instance) && literals[literalFromInstance(instance)].type == STRING_LITERAL;
+}
+
+
+/*======================================================================*/
+bool isOpaque(int container) {
+ return getInstanceAttribute(container, OPAQUEATTRIBUTE);
+}
+
+
+/*======================================================================*/
+void setInstanceAttribute(int instance, int attribute, Aptr value)
+{
+ char str[80];
+
+ if (instance > 0 && instance <= header->instanceMax) {
+ setAttribute(admin[instance].attributes, attribute, value);
+ if (isALocation(instance) && attribute != VISITSATTRIBUTE)
+ /* If it wasn't the VISITSATTRIBUTE the location may have
+ changed so describe next time */
+ admin[instance].visitsCount = 0;
+ } else {
+ sprintf(str, "Can't SET/MAKE instance (%d).", instance);
+ syserr(str);
+ }
+}
+
+
+/*======================================================================*/
+void setInstanceStringAttribute(int instance, int attribute, char *string)
+{
+ deallocate(fromAptr(getInstanceAttribute(instance, attribute)));
+ setInstanceAttribute(instance, attribute, toAptr(string));
+}
+
+
+/*======================================================================*/
+void setInstanceSetAttribute(int instance, int attribute, Aptr set)
+{
+ freeSet((Set *)fromAptr(getInstanceAttribute(instance, attribute)));
+ setInstanceAttribute(instance, attribute, set);
+}
+
+
+/*----------------------------------------------------------------------*/
+static Aptr literalAttribute(int literal, int attribute)
+{
+ if (isPreBeta3(header->version)) {
+ if (attribute == 1)
+ return literals[literalFromInstance(literal)].value;
+ else
+ return 0;
+ } else {
+ if (attribute == 0)
+ return literals[literalFromInstance(literal)].value;
+ else
+ return getAttribute(admin[header->instanceMax].attributes, attribute);
+ }
+ return(EOF);
+}
+
+
+/*======================================================================*/
+Aptr getInstanceAttribute(int instance, int attribute)
+{
+ char str[80];
+
+ if (isLiteral(instance))
+ return literalAttribute(instance, attribute);
+ else {
+ if (instance > 0 && instance <= header->instanceMax) {
+ if (attribute == -1)
+ return locationOf(instance);
+ else
+ return getAttribute(admin[instance].attributes, attribute);
+ } else {
+ sprintf(str, "Can't ATTRIBUTE item (%d).", instance);
+ syserr(str);
+ }
+ }
+ return(EOF);
+}
+
+
+/*======================================================================*/
+char *getInstanceStringAttribute(int instance, int attribute)
+{
+ return strdup((char *)fromAptr(getInstanceAttribute(instance, attribute)));
+}
+
+
+/*======================================================================*/
+Set *getInstanceSetAttribute(int instance, int attribute)
+{
+ return copySet((Set *)fromAptr(getInstanceAttribute(instance, attribute)));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void verifyInstance(int instance, char *action) {
+ char message[200];
+
+ if (instance == 0) {
+ sprintf(message, "Can't %s instance (%d).", action, instance);
+ syserr(message);
+ } else if (instance > header->instanceMax) {
+ sprintf(message, "Can't %s instance (%d > instanceMax).", action, instance);
+ syserr(message);
+ }
+}
+
+
+/*======================================================================*/
+bool isHere(int id, ATrans transitivity)
+{
+ verifyInstance(id, "HERE");
+
+ return isAt(id, current.location, transitivity);
+}
+
+
+/*======================================================================*/
+bool isNearby(int instance, ATrans transitivity)
+{
+ verifyInstance(instance, "NEARBY");
+
+ if (isALocation(instance))
+ return exitto(current.location, instance);
+ else
+ return exitto(current.location, where(instance, transitivity));
+}
+
+
+/*======================================================================*/
+bool isNear(int instance, int other, ATrans trans)
+{
+ Aint l1, l2;
+
+ verifyInstance(instance, "NEAR");
+
+ if (isALocation(instance))
+ l1 = instance;
+ else
+ l1 = where(instance, trans);
+ if (isALocation(other))
+ l2 = other;
+ else
+ l2 = where(other, trans);
+ return exitto(l2, l1);
+}
+
+
+/*======================================================================*/
+/* Look in a container to see if the instance is in it. */
+bool isIn(int instance, int container, ATrans trans)
+{
+ int loc;
+
+ if (!isAContainer(container))
+ syserr("IN in a non-container.");
+
+ if (trans == DIRECT)
+ return admin[instance].location == container;
+ else {
+ loc = admin[instance].location;
+ if (trans == INDIRECT && loc != 0 && !isA(loc, LOCATION))
+ loc = admin[loc].location;
+ while (loc != 0 && !isA(loc, LOCATION))
+ if (loc == container)
+ return TRUE;
+ else
+ loc = admin[loc].location;
+ return FALSE;
+ }
+}
+
+
+
+/*======================================================================*/
+/* Look see if an instance is AT another. */
+bool isAt(int instance, int other, ATrans trans)
+{
+ if (instance == 0 || other == 0) return FALSE;
+
+ if (isALocation(instance)) {
+ /* Nested locations */
+ /* TODO - What if the other is not a location? */
+ int current = admin[instance].location;
+ switch (trans) {
+ case DIRECT:
+ return admin[instance].location == other;
+ case INDIRECT:
+ if (current == other)
+ return FALSE;
+ current = admin[current].location;
+ case TRANSITIVE:
+ while (current != 0) {
+ if (current == other)
+ return TRUE;
+ else
+ current = admin[current].location;
+ }
+ return FALSE;
+ }
+ syserr("Unexpected value in switch in isAt() for location");
+ return FALSE;
+ } else if (isALocation(other)) {
+ /* Instance is not a location but other is */
+ switch (trans) {
+ case DIRECT:
+ return admin[instance].location == other;
+ case INDIRECT: {
+ if (admin[instance].location == other)
+ return FALSE; /* Directly, so not Indirectly */
+ /* Fall through to transitive handling of the location */
+ }
+ case TRANSITIVE: {
+ int location = locationOf(instance);
+ int current = other;
+ while (current != 0) {
+ if (current == location)
+ return TRUE;
+ else
+ current = admin[current].location;
+ }
+ return FALSE;
+ }
+ }
+ syserr("Unexpected value in switch in isAt() for non-location");
+ return FALSE;
+ } else {
+ /* Other is also not a location */
+ switch (trans) {
+ case DIRECT:
+ return positionOf(instance) == admin[other].location;
+ case INDIRECT: {
+ int location = locationOf(instance);
+ int current = other;
+ if (location == current)
+ return FALSE;
+ else
+ current = admin[current].location;
+ while (current != 0) {
+ if (current == location)
+ return TRUE;
+ else
+ current = admin[current].location;
+ }
+ return FALSE;
+ }
+ case TRANSITIVE: {
+ int location = locationOf(other);
+ int current = locationOf(instance);
+ bool ok = FALSE;
+ while (current != 0 && !ok) {
+ if (current == location)
+ ok = TRUE;
+ else
+ current = admin[current].location;
+ }
+ return ok;
+ }
+ }
+ syserr("Unexpected value in switch in isAt() for non-location");
+ return FALSE;
+ }
+}
+
+
+/*======================================================================*/
+/* Return the *location* of an instance, transitively, i.e. the first
+ location instance found when traversing the containment/position
+ links. If that didn't turn up a location see if it was in a
+ container that is somewhere, or a THING that is nowhere. It might
+ also be an ENTITY which is always everywhere so take that to mean
+ where the hero is. */
+int locationOf(int instance)
+{
+ int position;
+ int container = 0;
+
+ verifyInstance(instance, "get LOCATION of");
+
+ position = admin[instance].location;
+ while (position != 0 && !isALocation(position)) {
+ container = position; /* Remember innermost container */
+ position = admin[position].location;
+ }
+ if (position > NOWHERE) /* It was a location so return that */
+ return position;
+ else {
+ /* If we did not find a location then it might be in a container */
+ if (container != 0)
+ instance = container;
+ /* If the instance or the container it was in is a THING then its nowhere. */
+ if (isA(instance, THING))
+ return NOWHERE; /* #nowhere */
+ else if (isALocation(instance))
+ return NO_LOCATION; /* No location */
+ else
+ return locationOf(HERO);
+ }
+}
+
+
+/*======================================================================*/
+/* Return the current position of an instance, directly or not */
+/* TODO: this will be a possible duplicate of where() */
+int positionOf(int instance)
+{
+ return admin[instance].location;
+}
+
+
+/*======================================================================*/
+/* Return the current position of an instance, directly or not */
+int where(int instance, ATrans trans)
+{
+ verifyInstance(instance, "WHERE");
+
+ if (isALocation(instance))
+ return 0;
+ else if (trans == DIRECT)
+ return admin[instance].location;
+ else
+ return locationOf(instance);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool executeInheritedMentioned(int cls) {
+ if (cls == 0) return FALSE;
+
+ if (classes[cls].mentioned) {
+ interpret(classes[cls].mentioned);
+ return TRUE;
+ } else
+ return executeInheritedMentioned(classes[cls].parent);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool mention(int instance) {
+ if (instances[instance].mentioned) {
+ interpret(instances[instance].mentioned);
+ return TRUE;
+ } else
+ return executeInheritedMentioned(instances[instance].parent);
+}
+
+
+/*======================================================================*/
+void sayInstance(int instance)
+{
+#ifdef SAY_INSTANCE_WITH_PLAYER_WORDS_IF_PARAMETER
+ int p, i;
+
+ /* Find the id in the parameters... */
+ if (params != NULL)
+ for (p = 0; params[p].code != EOF; p++)
+ if (params[p].code == instance) {
+ /* Found it so.. */
+ if (params[p].firstWord == EOF) /* Any words he used? */
+ break; /* No... */
+ else { /* Yes, so use them... */
+ char *capitalized;
+ /* Assuming the noun is the last word we can simply output the adjectives... */
+ for (i = params[p].firstWord; i <= params[p].lastWord-1; i++)
+ output((char *)pointerTo(dict[wrds[i]].wrd));
+ /* ... and then the noun, capitalized if necessary */
+ if (header->capitalizeNouns) {
+ capitalized = strdup((char *)pointerTo(dict[wrds[params[p].lastWord]].wrd));
+ capitalized[0] = IsoToUpperCase(capitalized[0]);
+ output(capitalized);
+ deallocate(capitalized);
+ } else
+ output((char *)pointerTo(dict[wrds[params[p].lastWord]].wrd));
+ }
+ return;
+ }
+#endif
+ if (!mention(instance))
+ interpret(instances[instance].name);
+}
+
+
+/*======================================================================*/
+void sayInteger(int value)
+{
+ char buf[25];
+
+ if (isHere(HERO, /*FALSE*/ TRANSITIVE)) {
+ sprintf(buf, "%d", value);
+ output(buf);
+ }
+}
+
+
+/*======================================================================*/
+void sayString(char *string)
+{
+ if (isHere(HERO, /*FALSE*/ TRANSITIVE))
+ output(string);
+ deallocate(string);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayLiteral(int literal)
+{
+ char *str;
+
+ if (isANumeric(literal))
+ sayInteger(literals[literal-header->instanceMax].value);
+ else {
+ str = (char *)strdup((char *)fromAptr(literals[literal-header->instanceMax].value));
+ sayString(str);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *wordWithCode(int classBit, int code) {
+ int w;
+ char str[50];
+
+ for (w = 0; w < dictionarySize; w++)
+ if (dictionary[w].code == code && ((classBit&dictionary[w].classBits) != 0))
+ return (char *)pointerTo(dictionary[w].string);
+ sprintf(str, "Could not find word of class %d with code %d.", classBit, code);
+ syserr(str);
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool sayInheritedDefiniteForm(int cls) {
+ if (cls == 0) {
+ syserr("No default definite article");
+ return FALSE;
+ } else {
+ if (classes[cls].definite.address) {
+ interpret(classes[cls].definite.address);
+ return classes[cls].definite.isForm;
+ } else
+ return sayInheritedDefiniteForm(classes[cls].parent);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayDefinite(int instance) {
+ if (instances[instance].definite.address) {
+ interpret(instances[instance].definite.address);
+ if (!instances[instance].definite.isForm)
+ sayInstance(instance);
+ } else
+ if (!sayInheritedDefiniteForm(instances[instance].parent))
+ sayInstance(instance);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool sayInheritedIndefiniteForm(int cls) {
+ if (cls == 0) {
+ syserr("No default indefinite article");
+ return FALSE;
+ } else {
+ if (classes[cls].indefinite.address) {
+ interpret(classes[cls].indefinite.address);
+ return classes[cls].indefinite.isForm;
+ } else
+ return sayInheritedIndefiniteForm(classes[cls].parent);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayIndefinite(int instance) {
+ if (instances[instance].indefinite.address) {
+ interpret(instances[instance].indefinite.address);
+ if (!instances[instance].indefinite.isForm)
+ sayInstance(instance);
+ } else
+ if (!sayInheritedIndefiniteForm(instances[instance].parent))
+ sayInstance(instance);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool sayInheritedNegativeForm(int cls) {
+ if (cls == 0) {
+ syserr("No default negative form");
+ return FALSE;
+ } else {
+ if (classes[cls].negative.address) {
+ interpret(classes[cls].negative.address);
+ return classes[cls].negative.isForm;
+ } else
+ return sayInheritedNegativeForm(classes[cls].parent);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayNegative(int instance) {
+ if (instances[instance].negative.address) {
+ interpret(instances[instance].negative.address);
+ if (!instances[instance].negative.isForm)
+ sayInstance(instance);
+ } else
+ if (!sayInheritedNegativeForm(instances[instance].parent))
+ sayInstance(instance);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayInheritedPronoun(int instance) {
+ if (instance == 0)
+ syserr("No default pronoun");
+ else {
+ if (classes[instance].pronoun != 0)
+ output(wordWithCode(PRONOUN_BIT, classes[instance].pronoun));
+ else
+ sayInheritedPronoun(classes[instance].parent);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayPronoun(int instance) {
+ if (instances[instance].pronoun != 0)
+ output(wordWithCode(PRONOUN_BIT, instances[instance].pronoun));
+ else
+ sayInheritedPronoun(instances[instance].parent);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayArticleOrForm(int id, SayForm form)
+{
+ if (!isLiteral(id))
+ switch (form) {
+ case SAY_DEFINITE:
+ sayDefinite(id);
+ break;
+ case SAY_INDEFINITE:
+ sayIndefinite(id);
+ break;
+ case SAY_NEGATIVE:
+ sayNegative(id);
+ break;
+ case SAY_PRONOUN:
+ sayPronoun(id);
+ break;
+ case SAY_SIMPLE:
+ say(id);
+ break;
+ default:
+ syserr("Unexpected form in 'sayArticleOrForm()'");
+ }
+ else
+ say(id);
+}
+
+
+/*======================================================================*/
+void say(int instance)
+{
+ Aword previousInstance = current.instance;
+ current.instance = instance;
+
+ if (isHere(HERO, /*FALSE*/ TRANSITIVE)) {
+ if (isLiteral(instance))
+ sayLiteral(instance);
+ else {
+ verifyInstance(instance, "SAY");
+ sayInstance(instance);
+ }
+ }
+ current.instance = previousInstance;
+}
+
+
+/*======================================================================*/
+void sayForm(int instance, SayForm form)
+{
+ Aword previousInstance = current.instance;
+ current.instance = instance;
+
+ sayArticleOrForm(instance, form);
+
+ current.instance = previousInstance;
+}
+
+
+/*======================================================================*/
+bool isDescribable(int instance) {
+ return isAObject(instance) || isAActor(instance);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool inheritsDescriptionFrom(int cls)
+{
+ if (classes[cls].description != 0)
+ return TRUE;
+ else if (classes[cls].parent != 0)
+ return inheritsDescriptionFrom(classes[cls].parent);
+ else
+ return FALSE;
+}
+
+
+/*======================================================================*/
+bool hasDescription(int instance)
+{
+ if (instances[instance].description != 0)
+ return TRUE;
+ else if (instances[instance].parent != 0)
+ return inheritsDescriptionFrom(instances[instance].parent);
+ else
+ return FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void describeClass(int instance)
+{
+ if (classes[instance].description != 0) {
+ /* This class has a description, run it */
+ interpret(classes[instance].description);
+ } else {
+ /* Search up the inheritance tree, if any, to find a description */
+ if (classes[instance].parent != 0)
+ describeClass(classes[instance].parent);
+ }
+}
+
+
+/*======================================================================*/
+void describeAnything(int instance)
+{
+ if (instances[instance].description != 0) {
+ /* This instance has its own description, run it */
+ interpret(instances[instance].description);
+ } else {
+ /* Search up the inheritance tree to find a description */
+ if (instances[instance].parent != 0)
+ describeClass(instances[instance].parent);
+ }
+ admin[instance].alreadyDescribed = TRUE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void describeObject(int object)
+{
+ if (hasDescription(object))
+ describeAnything(object);
+ else {
+ printMessageWithInstanceParameter(M_SEE_START, object);
+ printMessage(M_SEE_END);
+ if (instances[object].container != 0)
+ describeContainer(object);
+ }
+ admin[object].alreadyDescribed = TRUE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool inheritedDescriptionCheck(int cls)
+{
+ if (cls == 0) return TRUE;
+ if (!inheritedDescriptionCheck(classes[cls].parent)) return FALSE;
+ if (classes[cls].descriptionChecks == 0) return TRUE;
+ return !checksFailed(classes[cls].descriptionChecks, TRUE);
+}
+
+/*----------------------------------------------------------------------*/
+static bool descriptionCheck(int instance)
+{
+ int previousInstance = current.instance;
+ bool r;
+
+ current.instance = instance;
+ if (inheritedDescriptionCheck(instances[instance].parent)) {
+ if (instances[instance].checks == 0)
+ r = TRUE;
+ else
+ r = !checksFailed(instances[instance].checks, TRUE);
+ } else
+ r = FALSE;
+ current.instance = previousInstance;
+ return r;
+}
+
+
+/*======================================================================*/
+void describeInstances(void)
+{
+ int i;
+ int lastInstanceFound = 0;
+ int found = 0;
+
+ /* First describe every object here with its own description */
+ for (i = 1; i <= header->instanceMax; i++)
+ if (admin[i].location == current.location && isAObject(i) &&
+ !admin[i].alreadyDescribed && hasDescription(i))
+ describe(i);
+
+ /* Then list all things without a description */
+ for (i = 1; i <= header->instanceMax; i++)
+ if (admin[i].location == current.location
+ && !admin[i].alreadyDescribed
+ && isAObject(i)
+ && descriptionCheck(i)) {
+ if (found == 0)
+ printMessageWithInstanceParameter(M_SEE_START, i);
+ else if (found > 1)
+ printMessageWithInstanceParameter(M_SEE_COMMA, lastInstanceFound);
+ admin[i].alreadyDescribed = TRUE;
+
+ // TODO : isOpaque()
+ if (instances[i].container && containerSize(i, DIRECT) > 0 && !getInstanceAttribute(i, OPAQUEATTRIBUTE)) {
+ if (found > 0)
+ printMessageWithInstanceParameter(M_SEE_AND, i);
+ printMessage(M_SEE_END);
+ describeContainer(i);
+ found = 0;
+ continue; /* Actually start another list. */
+ }
+ found++;
+ lastInstanceFound = i;
+ }
+
+ if (found > 0) {
+ if (found > 1) {
+ printMessageWithInstanceParameter(M_SEE_AND, lastInstanceFound);
+ }
+ printMessage(M_SEE_END);
+ }
+
+ /* Finally all actors with a separate description */
+ for (i = 1; i <= header->instanceMax; i++)
+ if (admin[i].location == current.location && i != HERO && isAActor(i)
+ && !admin[i].alreadyDescribed)
+ describe(i);
+
+ /* Clear the describe flag for all instances */
+ for (i = 1; i <= header->instanceMax; i++)
+ admin[i].alreadyDescribed = FALSE;
+}
+
+
+/*======================================================================*/
+bool describe(int instance)
+{
+ bool descriptionOk;
+ int previousInstance = current.instance;
+
+ current.instance = instance;
+ verifyInstance(instance, "DESCRIBE");
+ if (descriptionCheck(instance)) {
+ descriptionOk = TRUE;
+ if (isAObject(instance)) {
+ describeObject(instance);
+ } else if (isAActor(instance)) {
+ describeActor(instance);
+ } else
+ describeAnything(instance);
+ } else
+ descriptionOk = FALSE;
+ current.instance = previousInstance;
+ return descriptionOk;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void locateIntoContainer(Aword theInstance, Aword theContainer) {
+ if (!isA(theInstance, containers[instances[theContainer].container]._class))
+ printMessageUsing2InstanceParameters(M_CANNOTCONTAIN, theContainer, theInstance);
+ else if (passesContainerLimits(theContainer, theInstance))
+ admin[theInstance].location = theContainer;
+ else
+ abortPlayerCommand();
+}
+
+
+/*----------------------------------------------------------------------*/
+static void locateLocation(Aword loc, Aword whr)
+{
+ Aint l = whr;
+
+ /* Ensure this does not create a recursive location chain */
+ while (l != 0) {
+ if (admin[l].location == loc)
+ apperr("Locating a location that would create a recursive loop of locations containing each other.");
+ else
+ l = admin[l].location;
+ }
+ admin[loc].location = whr;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void locateObject(Aword obj, Aword whr)
+{
+ if (isAContainer(whr)) { /* Into a container */
+ locateIntoContainer(obj, whr);
+ } else {
+ admin[obj].location = whr;
+ /* Make sure the location is described since it's changed */
+ admin[whr].visitsCount = 0;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceEnteredClass(Aint theClass, bool empty) {
+ printf("\n<ENTERED in class ");
+ printf("%s", idOfClass(theClass));
+ printf("[%d]%s>\n", theClass, empty?" is empty":":");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceEnteredInstance(Aint instance, bool empty) {
+ printf("\n<ENTERED in instance ");
+ traceSay(instance);
+ printf("[%d]%s>\n", instance, empty?" is empty":"");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void executeInheritedEntered(Aint theClass) {
+ if (theClass == 0) return;
+ executeInheritedEntered(classes[theClass].parent);
+ if (traceSectionOption)
+ traceEnteredClass(theClass, classes[theClass].entered == 0);
+ if (classes[theClass].entered) {
+ interpret(classes[theClass].entered);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void executeEntered(Aint instance) {
+ int currentInstance = current.instance;
+ current.instance = instance;
+ if (admin[instance].location != 0)
+ executeEntered(admin[instance].location);
+ executeInheritedEntered(instances[instance].parent);
+ if (traceSectionOption)
+ traceEnteredInstance(instance, instances[instance].entered == 0);
+ if (instances[instance].entered != 0) {
+ interpret(instances[instance].entered);
+ }
+ current.instance = currentInstance;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int getVisits(int location) {
+ return getInstanceAttribute(location, VISITSATTRIBUTE);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void incrementVisits(int location) {
+ setInstanceAttribute(location, VISITSATTRIBUTE, getVisits(location)+1);
+ if (admin[location].location != 0)
+ /* Nested location, so increment that too */
+ incrementVisits(admin[location].location);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void revisited(void) {
+ if (anyOutput)
+ para();
+ say(where(HERO, DIRECT));
+ printMessage(M_AGAIN);
+ newline();
+ describeInstances();
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool shouldBeDescribed(void) {
+ if (!isPreBeta5(header->version))
+ return getVisits(admin[HERO].location) % (current.visits+1) == 0
+ || admin[admin[HERO].location].visitsCount == 0;
+ else
+ return admin[admin[HERO].location].visitsCount % (current.visits+1) == 0;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void locateActor(Aint movingActor, Aint whr)
+{
+ Aint previousCurrentLocation = current.location;
+ Aint previousActorLocation = admin[movingActor].location;
+ Aint previousActor = current.actor;
+ Aint previousInstance = current.instance;
+
+ /* Before leaving, remember that we visited the location */
+ if (!isPreBeta5(header->version))
+ if (movingActor == HERO)
+ incrementVisits(where(HERO, DIRECT));
+
+ /* TODO Actors locating into containers is dubious, anyway as it
+ is now it allows the hero to be located into a container. And what
+ happens with current location if so... */
+ if (isAContainer(whr))
+ locateIntoContainer(movingActor, whr);
+ else {
+ current.location = whr;
+ admin[movingActor].location = whr;
+ }
+
+ /* Now we have moved, so show what is needed... */
+ current.instance = current.location;
+
+ /* Execute possible entered */
+ current.actor = movingActor;
+ if (previousActorLocation != current.location) {
+ executeEntered(current.location);
+ }
+ current.instance = previousInstance;
+ current.actor = previousActor;
+
+ if (movingActor == HERO) {
+ if (shouldBeDescribed())
+ look();
+ else
+ revisited();
+ admin[where(HERO, DIRECT)].visitsCount++;
+ } else
+ /* Ensure that the location will be described to the hero next time */
+ admin[whr].visitsCount = 0;
+
+ if (current.actor != movingActor)
+ current.location = previousCurrentLocation;
+
+ current.instance = previousInstance;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceExtract(int instance, int containerId, char *what) {
+ if (traceSectionOption) {
+ printf("\n<EXTRACT from ");
+ traceSay(instance);
+ printf("[%d, container %d], %s:>\n", instance, containerId, what);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void containmentLoopError(int instance, int whr) {
+ ParameterArray parameters = newParameterArray();
+ if (isPreBeta4(header->version))
+ output("That would be to put something inside itself.");
+ else if (whr == instance) {
+ addParameterForInstance(parameters, instance);
+ printMessageWithParameters(M_CONTAINMENT_LOOP, parameters);
+ } else {
+ addParameterForInstance(parameters, instance);
+ addParameterForInstance(parameters, whr);
+ printMessageWithParameters(M_CONTAINMENT_LOOP2, parameters);
+ }
+ free(parameters);
+ error(NO_MSG);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void runExtractStatements(int instance, int containerId) {
+ ContainerEntry *theContainer = &containers[containerId];
+
+ if (theContainer->extractStatements != 0) {
+ traceExtract(instance, containerId, "Executing");
+ interpret(theContainer->extractStatements);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool runExtractChecks(int instance, int containerId) {
+ ContainerEntry *theContainer = &containers[containerId];
+
+ if (theContainer->extractChecks != 0) {
+ traceExtract(instance, containerId, "Checking");
+ if (checksFailed(theContainer->extractChecks, EXECUTE_CHECK_BODY_ON_FAIL)) {
+ fail = TRUE;
+ return FALSE; /* Failed! */
+ }
+ }
+ return TRUE; /* Passed! */
+}
+
+
+/*======================================================================*/
+void locate(int instance, int whr)
+{
+ int containerId;
+ int previousInstance = current.instance;
+
+ verifyInstance(instance, "LOCATE");
+ verifyInstance(whr, "LOCATE AT");
+
+ /* Will this create a containment loop? */
+ if (whr == instance || (isAContainer(instance) && isIn(whr, instance, TRANSITIVE)))
+ containmentLoopError(instance, whr);
+
+ /* First check if the instance is in a container, if so run extract checks */
+ if (isAContainer(admin[instance].location)) { /* In something? */
+ int loc = admin[instance].location;
+
+ /* Run all nested extraction checks */
+ while (isAContainer(loc)) {
+ current.instance = loc;
+ containerId = instances[loc].container;
+
+ if (!runExtractChecks(instance, containerId)) {
+ current.instance = previousInstance;
+ return;
+ }
+ runExtractStatements(instance, containerId);
+ loc = admin[loc].location;
+ }
+ current.instance = previousInstance;
+ }
+
+ if (isAActor(instance))
+ locateActor(instance, whr);
+ else if (isALocation(instance))
+ locateLocation(instance, whr);
+ else
+ locateObject(instance, whr);
+
+ gameStateChanged = TRUE;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/instance.h b/engines/glk/alan3/instance.h
new file mode 100644
index 0000000000..2b5adb3229
--- /dev/null
+++ b/engines/glk/alan3/instance.h
@@ -0,0 +1,97 @@
+/* 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_ALAN3_INSTANCE
+#define GLK_ALAN3_INSTANCE
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/set.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* Types: */
+struct AdminEntry { /* Administrative data about instances */
+ Aint location;
+ AttributeEntry *attributes;
+ Abool alreadyDescribed;
+ Aint visitsCount;
+ Aint script;
+ Aint step;
+ Aint waitCount;
+};
+
+
+/* Data: */
+extern InstanceEntry *instances; /* Instance table pointer */
+
+extern AdminEntry *admin; /* Administrative data about instances */
+extern AttributeEntry *attributes; /* Dynamic attribute values */
+
+
+/* Functions: */
+extern bool isA(int instance, int ancestor);
+extern bool isAObject(int instance);
+extern bool isAContainer(int instance);
+extern bool isAActor(int instance);
+extern bool isALocation(int instance);
+extern bool isLiteral(int instance);
+extern bool isANumeric(int instance);
+extern bool isAString(int instance);
+
+extern Aptr getInstanceAttribute(int instance, int attribute);
+extern char *getInstanceStringAttribute(int instane, int attribute);
+extern Set *getInstanceSetAttribute(int instance, int attribute);
+
+extern void setInstanceAttribute(int instance, int atr, Aptr value);
+extern void setInstanceStringAttribute(int instance, int attribute, char *string);
+extern void setInstanceSetAttribute(int instance, int atr, Aptr set);
+
+extern void say(int instance);
+extern void sayForm(int instance, SayForm form);
+extern void sayInstance(int instance);
+
+extern bool hasDescription(int instance);
+extern bool isDescribable(int instance);
+extern void describeAnything(int instance);
+extern void describeInstances(void);
+extern bool describe(int instance);
+
+extern int where(int instance, ATrans trans);
+extern int positionOf(int instance);
+extern int locationOf(int instance);
+
+extern bool isAt(int instance, int other, ATrans trans);
+extern bool isIn(int instance, int theContainer, ATrans trans);
+extern bool isHere(int instance, ATrans trans);
+extern bool isNearby(int instance, ATrans trans);
+extern bool isNear(int instance, int other, ATrans trans);
+
+extern bool isOpaque(int container);
+
+extern void locate(int instance, int whr);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/inter.cpp b/engines/glk/alan3/inter.cpp
new file mode 100644
index 0000000000..56f696658d
--- /dev/null
+++ b/engines/glk/alan3/inter.cpp
@@ -0,0 +1,1412 @@
+/* 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/alan3/inter.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/save.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/score.h"
+#include "glk/alan3/params.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/container.h"
+#include "glk/alan3/location.h"
+#include "glk/alan3/compatibility.h"
+
+#ifdef HAVE_GLK
+#define MAP_STDIO_TO_GLK
+#include "glk/alan3/glkio.h"
+#endif
+
+namespace Glk {
+namespace Alan3 {
+
+bool stopAtNextLine = FALSE;
+bool fail = FALSE;
+
+
+/* PRIVATE DATA */
+
+static int pc;
+static Stack stack = NULL;
+
+static void (*interpreterMock)(Aaddr adr) = NULL;
+
+
+/*======================================================================*/
+void setInterpreterMock(void (*mock)(Aaddr adr)) {
+ interpreterMock = mock;
+}
+
+
+/*======================================================================*/
+void setInterpreterStack(Stack theStack)
+{
+ stack = theStack;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceInstruction(char *str, ...) {
+ va_list args;
+
+ if (traceInstructionOption) {
+ va_start(args, str);
+ Common::String msg = Common::String::format(str, args);
+ va_end(args);
+
+ printf("%s", msg.c_str());
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceSkip() {
+ printf("\n : \t\t\t\t\t\t\t");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void interpretIf(Aword v)
+{
+ int lev = 1;
+ Aword i;
+
+ if (!v) {
+ /* Skip to next ELSE or ENDIF on same level */
+ if (traceInstructionOption) traceSkip();
+ while (TRUE) {
+ i = memory[pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP)
+ switch (I_OP(i)) {
+ case I_ELSE:
+ if (lev == 1) {
+ if (traceInstructionOption)
+ printf("\n%4x: ELSE\t\t\t\t\t\t", pc);
+ return;
+ }
+ break;
+ case I_IF:
+ lev++;
+ break;
+ case I_ENDIF:
+ lev--;
+ if (lev == 0) {
+ if (traceInstructionOption)
+ printf("\n%4x: ENDIF\t\t\t\t\t\t", pc);
+ return;
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void interpretElse(void)
+{
+ int lev = 1;
+ Aword i;
+
+ if (traceInstructionOption) traceSkip();
+ while (TRUE) {
+ /* Skip to ENDIF on the same level */
+ i = memory[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;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void goToLOOPEND(void) {
+ int level = 1;
+ int i;
+
+ if (traceInstructionOption) traceSkip();
+ while (TRUE) {
+ /* Skip past LOOPEND on the same level */
+ i = memory[pc];
+ if (I_CLASS(i) == (Aword)C_STMOP)
+ switch (I_OP(i)) {
+ case I_LOOPEND:
+ level--;
+ if (level == 0)
+ return;
+ break;
+ case I_LOOP:
+ level++;
+ break;
+ }
+ pc++;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void jumpBackToStartOfMatchingLOOP(void) {
+ int level = 1;
+ int i;
+
+ if (traceInstructionOption) traceSkip();
+ pc--; /* Ignore the instruction we're on */
+ while (TRUE) {
+ /* Skip back past LOOP on the same level */
+ i = memory[--pc];
+ if (I_CLASS(i) == (Aword)C_STMOP)
+ switch (I_OP(i)) {
+ case I_LOOPEND:
+ level++;
+ break;
+ case I_LOOP:
+ level--;
+ if (level == 0) {
+ return;
+ }
+ break;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void nextLoop(void)
+{
+ goToLOOPEND();
+}
+
+
+/*----------------------------------------------------------------------*/
+static void endLoop(Aint index, Aint limit)
+{
+ if (index < limit) {
+ index++;
+ push(stack, limit);
+ push(stack, index);
+ jumpBackToStartOfMatchingLOOP();
+ if (traceInstructionOption)
+ printf("\n%4x: LOOP\t\t\t\t\t\t", pc);
+ pc++;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void stackDup(void)
+{
+ push(stack, top(stack));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void depexec(Aword v)
+{
+ int lev = 1;
+ Aword i;
+ char *instructionString = "DEPELSE";
+
+ if (!v) {
+ /* The expression was not true, skip to next CASE on the same
+ level which could be a DEPCASE or DEPELSE */
+ if (traceInstructionOption) printf("\n : ");
+ while (TRUE) {
+ i = memory[pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP)
+ switch (I_OP(i)) {
+ case I_DEPEND:
+ lev++;
+ break;
+ case I_ENDDEP:
+ if (lev == 1) {
+ pc--;
+ if (traceInstructionOption)
+ printf("\n%4x: ENDDEP", pc);
+ return;
+ } else
+ lev--;
+ break;
+ case I_DEPCASE:
+ instructionString = "DEPCASE";
+ case I_DEPELSE:
+ if (lev == 1) {
+ if (traceInstructionOption)
+ printf("\n%4x: %s", pc, instructionString);
+ return;
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void depcase(void)
+{
+ int lev = 1;
+ Aword i;
+
+ /*
+ We have just executed a DEPCASE/DEPELSE clause as a result of a
+ DEPCASE catching so skip to end of DEPENDING block (next DEPEND
+ on same level) then return.
+ */
+
+ if (traceInstructionOption) printf("\n : ");
+ while (TRUE) {
+ i = memory[pc++];
+ if (I_CLASS(i) == (Aword)C_STMOP)
+ switch (I_OP(i)) {
+ case I_DEPEND:
+ lev++;
+ break;
+ case I_ENDDEP:
+ lev--;
+ if (lev == 0) {
+ pc--;
+ return;
+ }
+ break;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *booleanValue(Abool value) {
+ if (value) return " TRUE";
+ else return " FALSE";
+}
+
+/*----------------------------------------------------------------------*/
+static char *stringValue(Aptr address) {
+ static char string[1000];
+
+ sprintf(string, "0x%lx (\"%s\")\t\t", (unsigned long) address, (char *)fromAptr(address));
+ return string;
+}
+
+/*----------------------------------------------------------------------*/
+static char *pointerValue(Aptr address) {
+ static char string[100];
+
+ sprintf(string, "@%6lx",(unsigned long) address);
+ return string;
+}
+
+/*----------------------------------------------------------------------*/
+static void traceStringTopValue() {
+ if (traceInstructionOption)
+ printf("\t=%s", stringValue(top(stack)));
+}
+
+/*----------------------------------------------------------------------*/
+static void tracebooleanTopValue() {
+ if (traceInstructionOption) {
+ if (top(stack)) printf("\t=TRUE\t");
+ else printf("\t=FALSE\t");
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static void traceIntegerTopValue() {
+ if (traceInstructionOption)
+ printf("\t=%ld\t", (long)top(stack));
+}
+
+/*----------------------------------------------------------------------*/
+static void tracePointerTopValue() {
+ if (traceInstructionOption)
+ printf("\t=%s\t", pointerValue(top(stack)));
+}
+
+/*----------------------------------------------------------------------*/
+static void traceInstanceTopValue() {
+ if (traceInstructionOption) {
+ printf("\t=%ld ('", (long)top(stack));
+ traceSay(top(stack));
+ printf("')");
+ if (traceStackOption)
+ printf("\n\t\t\t\t\t\t\t");
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static char *transitivityFlag(ATrans value) {
+ switch (value) {
+ case TRANSITIVE:
+ return "Transitive";
+ case DIRECT:
+ return "Direct";
+ case INDIRECT:
+ return "Indirect";
+ }
+ syserr("Unexpected transitivity");
+ return "ERROR";
+}
+
+/*----------------------------------------------------------------------*/
+static char *printForm(SayForm form) {
+ switch (form) {
+ case SAY_SIMPLE: return "-";
+ case SAY_INDEFINITE: return "An";
+ case SAY_DEFINITE: return "The";
+ case SAY_NEGATIVE: return "No";
+ case SAY_PRONOUN: return "It";
+ }
+ return "**Unknown!!***";
+}
+
+
+static Aaddr invocation[1000];
+int recursionDepth = 0;
+
+/*----------------------------------------------------------------------*/
+static void checkForRecursion(Aaddr adr) {
+ int i;
+
+ for (i = 0; i < recursionDepth; i++)
+ if (invocation[i] == adr)
+ apperr("Interpreter recursion.");
+ invocation[recursionDepth++] = adr;
+ if (recursionDepth > 1000)
+ syserr("Interpreter call stack too deep.");
+}
+
+
+static bool skipStackDump = FALSE; /* Need to be able to skip it for LINE */
+
+
+/*----------------------------------------------------------------------*/
+static bool stillOnSameLine(Aint line, Aint file) {
+ return line != current.sourceLine || file != current.sourceFile;
+}
+
+
+/*======================================================================*/
+void interpret(Aaddr adr)
+{
+ Aaddr oldpc;
+ Aword i;
+
+ /* Check for mock implementation */
+ if (interpreterMock != NULL) {
+ interpreterMock(adr);
+ return;
+ }
+
+ /* Sanity checks: */
+ if (adr == 0) syserr("Interpreting at address 0.");
+ checkForRecursion(adr);
+
+ if (traceInstructionOption)
+ printf("\n++++++++++++++++++++++++++++++++++++++++++++++++++");
+
+ oldpc = pc;
+ pc = adr;
+ while(TRUE) {
+ if (pc > memTop)
+ syserr("Interpreting outside program.");
+
+ i = memory[pc++];
+
+ switch (I_CLASS(i)) {
+ case C_CONST:
+ if (tracePushOption) printf("\n%4x: PUSH \t%7ld\t\t\t\t\t", pc-1, (long)I_OP(i));
+ push(stack, I_OP(i));
+ if (tracePushOption && traceStackOption)
+ dumpStack(stack);
+ break;
+ case C_CURVAR:
+ if (traceInstructionOption) printf("\n%4x: ", pc-1);
+ switch (I_OP(i)) {
+ case V_PARAM:
+ if (traceInstructionOption) printf("PARAM \t%7ld\t\t\t\t=%ld\t", (long)top(stack),
+ (long)globalParameters[top(stack)-1].instance);
+ push(stack, globalParameters[pop(stack)-1].instance);
+ break;
+ case V_CURLOC:
+ if (traceInstructionOption) printf("CURLOC \t\t\t\t\t=%d\t", current.location);
+ push(stack, current.location);
+ break;
+ case V_CURACT:
+ if (traceInstructionOption) printf("CURACT \t\t\t\t\t=%d\t", current.actor);
+ push(stack, current.actor);
+ break;
+ case V_CURVRB:
+ if (traceInstructionOption) printf("CURVRB \t\t\t\t\t=%d\t", current.verb);
+ push(stack, current.verb);
+ break;
+ case V_CURRENT_INSTANCE:
+ if (traceInstructionOption) printf("CURINS \t\t\t\t\t=%d\t", current.instance);
+ push(stack, current.instance);
+ break;
+ case V_SCORE:
+ if (traceInstructionOption) printf("CURSCORE \t\t\t\t\t=%d\t", current.score);
+ push(stack, current.score);
+ break;
+ case V_MAX_INSTANCE: {
+ int instanceMax = isPreBeta3(header->version)?header->instanceMax:header->instanceMax-1;
+ if (traceInstructionOption) printf("MAXINSTANCE \t\t\t\t=%d\t", instanceMax);
+ push(stack, instanceMax);
+ break;
+ }
+ default:
+ syserr("Unknown CURVAR instruction.");
+ break;
+ }
+ if (traceStackOption)
+ dumpStack(stack);
+ break;
+
+ case C_STMOP:
+ if (traceInstructionOption) printf("\n%4x: ", pc-1);
+ switch (I_OP(i)) {
+
+ case I_DUP:
+ if (traceInstructionOption)
+ printf("DUP\t\t\t\t\t\t");
+ stackDup();
+ break;
+
+ case I_DUPSTR:
+ if (traceInstructionOption)
+ printf("DUPSTR\t\t\t\t\t\t");
+ push(stack, toAptr(strdup((char*)fromAptr(top(stack)))));
+ break;
+
+ case I_POP: {
+ Aptr top = pop(stack);
+ if (traceInstructionOption)
+ printf("POP\t%7ld", (long)top);
+ break;
+ }
+
+ case I_LINE: {
+ Aint line = pop(stack);
+ Aint file = pop(stack);
+ traceInstruction("LINE\t%7ld, %7ld\t\t\t", (long)file, (long)line);
+ if (traceStackOption)
+ dumpStack(stack);
+ skipStackDump = TRUE;
+ if (line != 0) {
+ bool atNext = stopAtNextLine && line != current.sourceLine;
+ bool atBreakpoint = breakpointIndex(file, line) != -1;
+ if (traceSourceOption && stillOnSameLine(line, file)) {
+ if (col != 1 || traceInstructionOption)
+ printf("\n");
+ showSourceLine(file, line);
+ if (!traceInstructionOption)
+ printf("\n");
+ }
+ current.sourceLine = line;
+ current.sourceFile = file;
+ if (atNext || atBreakpoint) {
+ stopAtNextLine = FALSE;
+ debug(TRUE, line, file);
+ }
+ }
+ break;
+ }
+
+ case I_PRINT: {
+ Aint fpos = pop(stack);
+ Aint len = pop(stack);
+ if (traceInstructionOption) {
+ printf("PRINT \t%7ld, %7ld\t\"", (long)fpos, (long)len);
+ col = 41; /* To break lines better! */
+ }
+ print(fpos, len);
+ if (traceInstructionOption) {
+ printf("\"");
+ if (traceStackOption)
+ printf("\n\t\t\t\t\t\t\t");
+ }
+ break;
+ }
+
+ case I_STYLE: {
+ Aint style = pop(stack);
+ if (traceInstructionOption) {
+ printf("STYLE \t%7ld\t\t\"", (long)style);
+ }
+ setStyle(style);
+ break;
+ }
+
+ case I_SYSTEM: {
+ Aint fpos = pop(stack);
+ Aint len = pop(stack);
+ if (traceInstructionOption) {
+ printf("SYSTEM \t%7ld, %7ld\t\"", (long)fpos, (long)len);
+ col = 34; /* To format it better! */
+ }
+ sys(fpos, len);
+ if (traceInstructionOption)
+ printf("\"\t\t\t\t\t\t");
+ break;
+ }
+
+ case I_GETSTR: {
+ Aint fpos = pop(stack);
+ Aint len = pop(stack);
+ if (traceInstructionOption)
+ printf("GETSTR\t%7ld, %7ld", (long)fpos, (long)len);
+ push(stack, toAptr(getStringFromFile(fpos, len)));
+ traceStringTopValue();
+ break;
+ }
+
+ case I_QUIT: {
+ if (traceInstructionOption)
+ printf("QUIT\t\t\t\t\t\t");
+ quitGame();
+ break;
+ }
+ case I_LOOK: {
+ if (traceInstructionOption)
+ printf("LOOK\t\t\t\t\t\t");
+ look();
+ break;
+ }
+ case I_SAVE: {
+ if (traceInstructionOption)
+ printf("SAVE\t\t\t\t\t\t");
+ save();
+ break;
+ }
+ case I_RESTORE: {
+ if (traceInstructionOption)
+ printf("RESTORE\t\t\t\t\t\t");
+ restore();
+ break;
+ }
+ case I_RESTART: {
+ if (traceInstructionOption)
+ printf("RESTART\t\t\t\t\t\t");
+ restartGame();
+ break;
+ }
+
+ case I_SCORE: {
+ Aint sc = pop(stack);
+ if (traceInstructionOption)
+ printf("SCORE \t%7ld\t\t=%ld\t\t\t", (long)sc, (long)scores[sc-1]);
+ score(sc);
+ break;
+ }
+ case I_VISITS: {
+ Aint v = pop(stack);
+ if (traceInstructionOption)
+ printf("VISITS \t%7ld\t\t\t\t\t", (long)v);
+ visits(v);
+ break;
+ }
+
+ case I_LIST: {
+ Aint cnt = pop(stack);
+ if (traceInstructionOption)
+ printf("LIST \t%7ld\t\t\t\t\t", (long)cnt);
+ list(cnt);
+ break;
+ }
+ case I_EMPTY: {
+ Aint cnt = pop(stack);
+ Aint whr = pop(stack);
+ if (traceInstructionOption)
+ printf("EMPTY \t%7ld, %7ld\t\t\t\t", (long)cnt, (long)whr);
+ empty(cnt, whr);
+ break;
+ }
+ case I_SCHEDULE: {
+ Aint event = pop(stack);
+ Aint where = pop(stack);
+ Aint after = pop(stack);
+ if (traceInstructionOption)
+ printf("SCHEDULE \t%7ld, %7ld, %7ld\t\t\t\t", (long)event, (long)where, (long)after);
+ schedule(event, where, after);
+ break;
+ }
+ case I_CANCEL: {
+ Aint event = pop(stack);
+ if (traceInstructionOption)
+ printf("CANCEL \t%7ld\t\t\t\t", (long)event);
+ cancelEvent(event);
+ break;
+ }
+ case I_MAKE: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ Abool val = pop(stack);
+ if (traceInstructionOption)
+ printf("MAKE \t%7ld, %7ld, %s\t\t\t", (long)id, (long)atr, booleanValue(val));
+ setInstanceAttribute(id, atr, val);
+ break;
+ }
+ case I_SET: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ Aptr val = pop(stack);
+ if (traceInstructionOption) {
+ printf("SET \t%7ld, %7ld, %7ld\t\t\t\t", (long)id, (long)atr, (long)val);
+ }
+ setInstanceAttribute(id, atr, val);
+ break;
+ }
+ case I_SETSTR: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ Aptr str = pop(stack);
+ if (traceInstructionOption) {
+ printf("SETSTR\t%7ld, %7ld, %s\t\t\t\t", (long)id, (long)atr, stringValue(str));
+ }
+ setInstanceStringAttribute(id, atr, (char *)fromAptr(str));
+ break;
+ }
+ case I_SETSET: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ Aptr set = pop(stack);
+ if (traceInstructionOption) {
+ printf("SETSET\t%7ld, %7ld, %7s\t\t", (long)id, (long)atr, pointerValue(set));
+ }
+ setInstanceSetAttribute(id, atr, set);
+ break;
+ }
+ case I_NEWSET: {
+ Set *set = newSet(0);
+ if (traceInstructionOption) {
+ printf("NEWSET\t\t\t");
+ }
+ push(stack, toAptr(set));
+ tracePointerTopValue();
+ break;
+ }
+ case I_UNION: {
+ Aptr set2 = pop(stack);
+ Aptr set1 = pop(stack);
+ if (traceInstructionOption) {
+ printf("UNION\t%7ld, %7ld\t\t\t\t", (long)set1, (long)set2);
+ }
+ push(stack, toAptr(setUnion((Set *)fromAptr(set1), (Set *)fromAptr(set2))));
+ tracePointerTopValue();
+ freeSet((Set *)fromAptr(set1));
+ freeSet((Set *)fromAptr(set2));
+ break;
+ }
+ case I_INCR: {
+ Aint step = pop(stack);
+ if (traceInstructionOption) {
+ printf("INCR\t%7ld", (long)step);
+ }
+ push(stack, pop(stack) + step);
+ traceIntegerTopValue();
+ break;
+ }
+ case I_DECR: {
+ Aint step = pop(stack);
+ if (traceInstructionOption) {
+ printf("DECR\t%7ld\t\t\t\t\t", (long)step);
+ }
+ push(stack, pop(stack) - step);
+ traceIntegerTopValue();
+ break;
+ }
+ case I_INCLUDE: {
+ Aint member = pop(stack);
+ if (traceInstructionOption) {
+ printf("INCLUDE\t%7ld\t\t\t\t\t", (long)member);
+ }
+ addToSet((Set *)fromAptr(top(stack)), member);
+ break;
+ }
+ case I_EXCLUDE: {
+ Aint member = pop(stack);
+ if (traceInstructionOption) {
+ printf("EXCLUDE\t%7ld", (long)member);
+ }
+ removeFromSet((Set *)fromAptr(top(stack)), member);
+ break;
+ }
+ case I_SETSIZE: {
+ Set *set = (Set *)fromAptr(pop(stack));
+ if (traceInstructionOption)
+ printf("SETSIZE\t%7ld\t\t", (long)set);
+ push(stack, setSize(set));
+ if (traceInstructionOption)
+ traceIntegerTopValue();
+ break;
+ }
+ case I_SETMEMB: {
+ Set *set = (Set *)fromAptr(pop(stack));
+ Aint index = pop(stack);
+ if (traceInstructionOption)
+ printf("SETMEMB\t%7ld, %7ld", (long)set, (long)index);
+ push(stack, getSetMember(set, index));
+ if (traceInstructionOption)
+ traceIntegerTopValue();
+ break;
+ }
+ case I_CONTSIZE: {
+ Abool transitivity = pop(stack);
+ Aint container = pop(stack);
+ if (traceInstructionOption)
+ printf("CONTSIZE\t%7ld, %7s\t", (long)container, transitivityFlag((ATrans)transitivity));
+ push(stack, containerSize(container, (ATrans)transitivity));
+ if (traceInstructionOption)
+ traceIntegerTopValue();
+ break;
+ }
+ case I_CONTMEMB: {
+ Abool transitivity = pop(stack);
+ Aint container = pop(stack);
+ Aint index = pop(stack);
+ if (traceInstructionOption)
+ printf("CONTMEMB\t%7ld, %7ld, %7s", (long)container, (long)index, transitivityFlag((ATrans)transitivity));
+ push(stack, getContainerMember(container, index, transitivity));
+ if (traceInstructionOption)
+ traceIntegerTopValue();
+ break;
+ }
+ case I_ATTRIBUTE: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("ATTRIBUTE %7ld, %7ld\t", (long)id, (long)atr);
+ push(stack, getInstanceAttribute(id, atr));
+ traceIntegerTopValue();
+ break;
+ }
+ case I_ATTRSTR: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("ATTRSTR \t%7ld, %7ld", (long)id, (long)atr);
+ push(stack, toAptr(getInstanceStringAttribute(id, atr)));
+ traceStringTopValue();
+ break;
+ }
+ case I_ATTRSET: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("ATTRSET \t%7ld, %7ld", (long)id, (long)atr);
+ push(stack, toAptr(getInstanceSetAttribute(id, atr)));
+ tracePointerTopValue();
+ break;
+ }
+ case I_SHOW: {
+ Aint image = pop(stack);
+ Aint align = pop(stack);
+ if (traceInstructionOption)
+ printf("SHOW \t%7ld, %7ld\t\t\t\t", (long)image, (long)align);
+ showImage(image, align);
+ break;
+ }
+ case I_PLAY: {
+ Aint sound = pop(stack);
+ if (traceInstructionOption)
+ printf("PLAY \t%7ld\t\t\t\t", (long)sound);
+ playSound(sound);
+ break;
+ }
+ case I_LOCATE: {
+ Aid id = pop(stack);
+ Aint whr = pop(stack);
+ if (traceInstructionOption)
+ printf("LOCATE \t%7ld, %7ld\t\t\t", (long)id, (long)whr);
+ locate(id, whr);
+ break;
+ }
+ case I_WHERE: {
+ Abool transitivity = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("WHERE \t%7ld, %7s", (long)id, transitivityFlag((ATrans)transitivity));
+ push(stack, where(id, (ATrans)transitivity));
+ traceInstanceTopValue();
+ break;
+ }
+ case I_LOCATION: {
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("LOCATION \t%7ld\t\t", (long)id);
+ push(stack, locationOf(id));
+ traceInstanceTopValue();
+ break;
+ }
+ case I_HERE: {
+ Abool transitivity = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("HERE \t%7ld, %s\t\t\t", (long)id, transitivityFlag((ATrans)transitivity));
+ push(stack, isHere(id, (ATrans)transitivity));
+ tracebooleanTopValue();
+ break;
+ }
+ case I_NEARBY: {
+ Abool transitivity = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("NEARBY \t%7ld, %s\t\t\t", (long)id, transitivityFlag((ATrans)transitivity));
+ push(stack, isNearby(id, (ATrans)transitivity));
+ tracebooleanTopValue();
+ break;
+ }
+ case I_NEAR: {
+ Abool transitivity = pop(stack);
+ Aint other = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("NEAR \t%7ld, %7ld, %s\t\t\t", (long)id, (long)other, transitivityFlag((ATrans)transitivity));
+ push(stack, isNear(id, other, (ATrans)transitivity));
+ tracebooleanTopValue();
+ break;
+ }
+ case I_AT: {
+ Abool transitivity = pop(stack);
+ Aint other = pop(stack);
+ Aint instance = pop(stack);
+ if (traceInstructionOption)
+ printf("AT \t%7ld, %7ld, %s", (long)instance, (long)other, transitivityFlag((ATrans)transitivity));
+ push(stack, isAt(instance, other, (ATrans)transitivity));
+ tracebooleanTopValue();
+ break;
+ }
+ case I_IN: {
+ Abool transitivity = pop(stack);
+ Aint cnt = pop(stack);
+ Aint obj = pop(stack);
+ if (traceInstructionOption)
+ printf("IN \t%7ld, %7ld, %s", (long)obj, (long)cnt, transitivityFlag((ATrans)transitivity));
+ push(stack, isIn(obj, cnt, (ATrans)transitivity));
+ tracebooleanTopValue();
+ break;
+ }
+ case I_INSET: {
+ Aptr set = pop(stack);
+ Aword element = pop(stack);
+ if (traceInstructionOption)
+ printf("INSET \t%7ld, %7ld", (long)element, (long)set);
+ push(stack, inSet((Set*)fromAptr(set), element));
+ freeSet((Set *)fromAptr(set));
+ tracebooleanTopValue();
+ break;
+ }
+ case I_USE: {
+ Aid act = pop(stack);
+ Aint scr = pop(stack);
+ if (traceInstructionOption)
+ printf("USE \t%7ld, %7ld\t\t\t\t", (long)act, (long)scr);
+ use(act, scr);
+ break;
+ }
+ case I_STOP: {
+ Aid actor = pop(stack);
+ if (traceInstructionOption)
+ printf("STOP \t%7ld\t\t\t\t\t", (long)actor);
+ stop(actor);
+ break;
+ }
+ case I_DESCRIBE: {
+ Aid id = pop(stack);
+ if (traceInstructionOption) {
+ printf("DESCRIBE \t%7ld\t\t\t", (long)id);
+ col = 41; /* To format it better! */
+ }
+ describe(id);
+ if (traceInstructionOption)
+ printf("\n\t\t\t\t\t\t");
+ break;
+ }
+ case I_SAY: {
+ Aint form = pop(stack);
+ Aid id = pop(stack);
+ if (traceInstructionOption)
+ printf("SAY\t%7s, %7ld\t\t\t", printForm((SayForm)form), (long)id);
+ if (form == SAY_SIMPLE)
+ say(id);
+ else
+ sayForm(id, (SayForm)form);
+ if (traceInstructionOption)
+ printf("\t\t\t\t\t\t\t");
+ break;
+ }
+ case I_SAYINT: {
+ Aword val = pop(stack);
+ if (traceInstructionOption)
+ printf("SAYINT\t%7ld\t\t\t\"", (long)val);
+ sayInteger(val);
+ if (traceInstructionOption)
+ printf("\"\n\t\t\t\t\t\t\t");
+ break;
+ }
+ case I_SAYSTR: {
+ Aptr adr = pop(stack);
+ if (traceInstructionOption)
+ printf("SAYSTR\t%7ld\t\ty\t", (long)adr);
+ sayString((char *)fromAptr(adr));
+ if (traceInstructionOption)
+ printf("\n\t\t\t\t\t\t");
+ break;
+ }
+ case I_IF: {
+ Aword v = pop(stack);
+ if (traceInstructionOption)
+ printf("IF \t%s\t\t\t\t\t", booleanValue(v));
+ interpretIf(v);
+ break;
+ }
+ case I_ELSE: {
+ if (traceInstructionOption)
+ printf("ELSE\t\t\t\t\t\t");
+ interpretElse();
+ break;
+ }
+ case I_ENDIF: {
+ if (traceInstructionOption)
+ printf("ENDIF\t\t\t\t\t\t");
+ break;
+ }
+ case I_AND: {
+ Aword rh = pop(stack);
+ Aword lh = pop(stack);
+ if (traceInstructionOption)
+ printf("AND \t%s, %s", booleanValue(lh), booleanValue(rh));
+ push(stack, lh && rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_OR: {
+ Aword rh = pop(stack);
+ Aword lh = pop(stack);
+ if (traceInstructionOption)
+ printf("OR \t%s, %s", booleanValue(lh), booleanValue(rh));
+ push(stack, lh || rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_NE: {
+ Aword rh = pop(stack);
+ Aword lh = pop(stack);
+ if (traceInstructionOption)
+ printf("NE \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh != rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_EQ: {
+ Aword rh = pop(stack);
+ Aword lh = pop(stack);
+ if (traceInstructionOption)
+ printf("EQ \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh == rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_STREQ: {
+ Aptr rh = pop(stack);
+ Aptr lh = pop(stack);
+ if (traceInstructionOption)
+ printf("STREQ \t0x%lx, 0x%lx", (long)lh, (long)rh);
+ push(stack, streq((char *)fromAptr(lh), (char *)fromAptr(rh)));
+ tracebooleanTopValue();
+ if (traceInstructionOption)
+ printf("\t");
+ deallocate(fromAptr(lh));
+ deallocate(fromAptr(rh));
+ break;
+ }
+ case I_STREXACT: {
+ Aptr rh = pop(stack);
+ Aptr lh = pop(stack);
+ if (traceInstructionOption)
+ printf("STREXACT \t0x%lx, 0x%lx", (long)lh, (long)rh);
+ push(stack, strcmp((char *)fromAptr(lh), (char *)fromAptr(rh)) == 0);
+ tracebooleanTopValue();
+ deallocate(fromAptr(lh));
+ deallocate(fromAptr(rh));
+ break;
+ }
+ case I_LE: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("LE \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh <= rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_GE: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("GE \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh >= rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_LT: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("LT \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh < rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_GT: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("GT \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh > rh);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_PLUS: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("PLUS \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh + rh);
+ traceIntegerTopValue();
+ break;
+ }
+ case I_MINUS: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("MINUS \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh - rh);
+ traceIntegerTopValue();
+ break;
+ }
+ case I_MULT: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("MULT \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh * rh);
+ traceIntegerTopValue();
+ break;
+ }
+ case I_DIV: {
+ Aint rh = pop(stack);
+ Aint lh = pop(stack);
+ if (traceInstructionOption)
+ printf("DIV \t%7ld, %7ld", (long)lh, (long)rh);
+ push(stack, lh / rh);
+ traceIntegerTopValue();
+ break;
+ }
+ case I_NOT: {
+ Aword val = pop(stack);
+ if (traceInstructionOption)
+ printf("NOT \t%s\t\t\t", booleanValue(val));
+ push(stack, !val);
+ tracebooleanTopValue();
+ break;
+ }
+ case I_RND: {
+ Aint from = pop(stack);
+ Aint to = pop(stack);
+ if (traceInstructionOption)
+ printf("RANDOM \t%7ld, %7ld", (long)from, (long)to);
+ push(stack, randomInteger(from, to));
+ traceIntegerTopValue();
+ break;
+ }
+ case I_BTW: {
+ Aint high = pop(stack);
+ Aint low = pop(stack);
+ Aint val = pop(stack);
+ if (traceInstructionOption)
+ printf("BETWEEN \t%7ld, %7ld, %7ld", (long)val, (long)low, (long)high);
+ push(stack, between(val, low, high));
+ traceIntegerTopValue();
+ break;
+ }
+
+ /*------------------------------------------------------------* \
+ String functions
+ \*------------------------------------------------------------*/
+ case I_CONCAT: {
+ Aptr s2 = pop(stack);
+ Aptr s1 = pop(stack);
+ if (traceInstructionOption)
+ printf("CONCAT \t%s, %s", pointerValue(s1), pointerValue(s2));
+ push(stack, concat(s1, s2));
+ traceStringTopValue();
+ deallocate(fromAptr(s1));
+ deallocate(fromAptr(s2));
+ break;
+ }
+
+ case I_CONTAINS: {
+ Aptr substring = pop(stack);
+ Aptr string = pop(stack);
+ if (traceInstructionOption)
+ printf("CONTAINS \t%s, %s", pointerValue(string), pointerValue(substring));
+ push(stack, contains(string, substring));
+ traceIntegerTopValue();
+ deallocate(fromAptr(string));
+ deallocate(fromAptr(substring));
+ break;
+ }
+
+ case I_STRIP: {
+ Aint atr = pop(stack);
+ Aid id = pop(stack);
+ Aint words = pop(stack);
+ Aint count = pop(stack);
+ Aint first = pop(stack);
+ if (traceInstructionOption)
+ printf("STRIP \t%7ld, %7ld, %7ld, %7ld, %7ld", (long)first, (long)count, (long)words, (long)id, (long)atr);
+ push(stack, strip(first, count, words, id, atr));
+ traceStringTopValue();
+ break;
+ }
+
+ /*------------------------------------------------------------
+ Aggregation
+ ------------------------------------------------------------*/
+ case I_MIN:
+ case I_SUM:
+ case I_MAX: {
+ Aint attribute = pop(stack);
+ Aint loopIndex = pop(stack);
+ Aint limit = pop(stack);
+ Aint aggregate = pop(stack);
+ switch (I_OP(i)) {
+ case I_MAX:
+ if (traceInstructionOption)
+ printf("MAX \t%7ld\t\t\t", (long)attribute);
+ if (aggregate < attribute)
+ push(stack, attribute);
+ else
+ push(stack, aggregate);
+ break;
+ case I_MIN:
+ if (traceInstructionOption)
+ printf("MIN \t%7ld\t\t\t", (long)attribute);
+ if (aggregate > attribute)
+ push(stack, attribute);
+ else
+ push(stack, aggregate);
+ break;
+ case I_SUM:
+ if (traceInstructionOption)
+ printf("SUM \t%7ld\t\t\t", (long)attribute);
+ push(stack, aggregate + attribute);
+ break;
+ }
+ traceIntegerTopValue();
+ push(stack, limit);
+ push(stack, loopIndex);
+ break;
+ }
+ case I_COUNT: {
+ Aint loopIndex = pop(stack);
+ Aint limit = pop(stack);
+ if (traceInstructionOption)
+ printf("COUNT\t\t\t");
+ push(stack, pop(stack) + 1);
+ traceIntegerTopValue();
+ push(stack, limit);
+ push(stack, loopIndex);
+ break;
+ }
+ case I_TRANSCRIPT: {
+ Aint on_or_off = pop(stack);
+ if (traceInstructionOption)
+ printf("TRANSCRIPT\t\t\t");
+ if (on_or_off)
+ startTranscript();
+ else
+ stopTranscript();
+ break;
+ }
+
+ /*------------------------------------------------------------
+ Depending On
+ ------------------------------------------------------------*/
+ case I_DEPEND:
+ if (traceInstructionOption)
+ printf("DEPEND\t\t\t\t\t\t");
+ break;
+
+ case I_DEPCASE:
+ if (traceInstructionOption)
+ printf("DEPCASE\t\t\t\t\t\t");
+ depcase();
+ break;
+
+ case I_DEPEXEC: {
+ Aword v = pop(stack);
+ if (traceInstructionOption) {
+ printf("DEPEXEC \t\t\t");
+ if (v) printf(" TRUE"); else printf("FALSE");
+ printf("\t\t\t\t\t");
+ }
+ depexec(v);
+ break;
+ }
+
+ case I_DEPELSE:
+ if (traceInstructionOption)
+ printf("DEPELSE\t\t\t\t\t\t");
+ depcase();
+ break;
+
+ case I_ENDDEP:
+ if (traceInstructionOption)
+ printf("ENDDEP\t\t\t\t\t\t");
+ pop(stack);
+ break;
+
+ case I_ISA: {
+ Aid rh = pop(stack);
+ Aid lh = pop(stack);
+ if (traceInstructionOption)
+ printf("ISA \t%7ld, %7ld\t", (long)lh, (long)rh);
+ push(stack, isA(lh, rh));
+ tracebooleanTopValue();
+ break;
+ }
+
+ case I_FRAME: {
+ Aint size = pop(stack);
+ if (traceInstructionOption)
+ printf("FRAME \t%7ld\t\t\t\t\t", (long)size);
+ newFrame(stack, size);
+ break;
+ }
+
+ case I_GETLOCAL: {
+ Aint framesBelow = pop(stack);
+ Aint variableNumber = pop(stack);
+ if (traceInstructionOption)
+ printf("GETLOCAL \t%7ld, %7ld\t", (long)framesBelow, (long)variableNumber);
+ push(stack, getLocal(stack, framesBelow, variableNumber));
+ traceIntegerTopValue();
+ break;
+ }
+
+ case I_SETLOCAL: {
+ Aint framesBelow = pop(stack);
+ Aint variableNumber = pop(stack);
+ Aint value = pop(stack);
+ if (traceInstructionOption)
+ printf("SETLOCAL \t%7ld, %7ld, %7ld\t\t", (long)framesBelow, (long)variableNumber, (long)value);
+ setLocal(stack, framesBelow, variableNumber, value);
+ break;
+ }
+
+ case I_ENDFRAME: {
+ if (traceInstructionOption)
+ printf("ENDFRAME\t\t\t\t\t\t");
+ endFrame(stack);
+ break;
+ }
+
+ case I_LOOP: {
+ Aint index = pop(stack);
+ Aint limit = pop(stack);
+ if (traceInstructionOption)
+ printf("LOOP \t\t\t\t\t\t");
+ push(stack, limit);
+ push(stack, index);
+ if (index > limit)
+ goToLOOPEND();
+ break;
+ }
+
+ case I_LOOPNEXT: {
+ if (traceInstructionOption)
+ printf("LOOPNEXT\t\t\t\t\t\t");
+ nextLoop();
+ break;
+ }
+
+ case I_LOOPEND: {
+ Aint index = pop(stack);
+ Aint limit = pop(stack);
+ if (traceInstructionOption)
+ printf("LOOPEND\t\t\t\t\t\t");
+ endLoop(index, limit);
+ break;
+ }
+
+ case I_RETURN:
+ if (traceInstructionOption)
+ printf("RETURN\n--------------------------------------------------\n");
+ pc = oldpc;
+ goto exitInterpreter;
+
+ default:
+ syserr("Unknown STMOP instruction.");
+ break;
+ }
+ if (fail) {
+ pc = oldpc;
+ goto exitInterpreter;
+ }
+ if (traceStackOption) {
+ if (!skipStackDump)
+ dumpStack(stack);
+ skipStackDump = FALSE;
+ }
+ break;
+
+ default:
+ syserr("Unknown instruction class.");
+ break;
+ }
+ }
+ exitInterpreter:
+ recursionDepth--;
+
+}
+
+/*======================================================================*/
+Aword evaluate(Aaddr adr) {
+ interpret(adr);
+ return pop(stack);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/inter.h b/engines/glk/alan3/inter.h
new file mode 100644
index 0000000000..f451a06757
--- /dev/null
+++ b/engines/glk/alan3/inter.h
@@ -0,0 +1,54 @@
+/* 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_ALAN3_INTER
+#define GLK_ALAN3_INTER
+
+/* The interpreter of Acode */
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/stack.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA: */
+
+extern bool stopAtNextLine;
+extern int currentLine;
+extern int recursionDepth;
+
+/* Global failure flag */
+extern bool fail;
+
+
+/* FUNCTIONS: */
+
+extern void setInterpreterMock(void (*mock)(Aaddr adr));
+extern void setInterpreterStack(Stack stack);
+extern void interpret(Aaddr adr);
+extern Aword evaluate(Aaddr adr);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/lists.cpp b/engines/glk/alan3/lists.cpp
new file mode 100644
index 0000000000..bb330c3301
--- /dev/null
+++ b/engines/glk/alan3/lists.cpp
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/lists.h"
+#include "glk/alan3/syserr.h"
+
+namespace Glk {
+namespace Alan3 {
+
+void initArray(void *array) {
+ implementationOfSetEndOfArray((Aword *)array);
+}
+
+/* How to know we are at end of a table or array, first Aword == EOF */
+void implementationOfSetEndOfArray(Aword *adr)
+{
+ *adr = EOF;
+}
+
+
+bool implementationOfIsEndOfList(Aword *adr)
+{
+ return *adr == EOF;
+}
+
+int lengthOfArrayImplementation(void *array_of_any_type, int element_size_in_bytes) {
+ int length;
+ int element_size = element_size_in_bytes/sizeof(Aword);
+ Aword *array = (Aword *)array_of_any_type;
+ if (array == NULL)
+ syserr("Taking length of NULL array");
+ for (length = 0; !isEndOfArray(&array[length*element_size]); length++)
+ ;
+ return length;
+}
+
+void addElementImplementation(void *array_of_any_type, void *element, int element_size_in_bytes) {
+ Aword *array = (Aword *)array_of_any_type;
+ int length = lengthOfArray(array);
+ int element_size_in_words = element_size_in_bytes/sizeof(Aword);
+ memcpy(&array[length*element_size_in_words], element, element_size_in_bytes);
+ setEndOfArray(&array[(length+1)*element_size_in_words]);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/lists.h b/engines/glk/alan3/lists.h
new file mode 100644
index 0000000000..d5b8791806
--- /dev/null
+++ b/engines/glk/alan3/lists.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_LISTS
+#define GLK_ALAN3_LISTS
+
+/* Various utility functions for handling lists and arrays */
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern void initArray(void *array);
+
+#define isEndOfArray(x) implementationOfIsEndOfList((Aword *) (x))
+extern bool implementationOfIsEndOfList(Aword *adr);
+
+#define setEndOfArray(x) implementationOfSetEndOfArray((Aword *) (x))
+extern void implementationOfSetEndOfArray(Aword *adr);
+
+#define lengthOfArray(array) lengthOfArrayImplementation((array), sizeof(*(array)))
+extern int lengthOfArrayImplementation(void *array, int element_size_in_bytes);
+
+#define addElement(array, element) addElementImplementation((array), (&element), sizeof(element))
+extern void addElementImplementation(void *array_of_any_type, void *element_of_any_size, int element_size_in_bytes);
+#endif
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/literal.cpp b/engines/glk/alan3/literal.cpp
new file mode 100644
index 0000000000..ad49aa8ed5
--- /dev/null
+++ b/engines/glk/alan3/literal.cpp
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/literal.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/memory.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+int litCount = 0;
+static LiteralEntry literalTable[100];
+LiteralEntry *literals = literalTable;
+
+
+/* PRIVATE TYPES & DATA */
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*======================================================================*/
+int instanceFromLiteral(int literalIndex) {
+ return literalIndex + header->instanceMax;
+}
+
+/*----------------------------------------------------------------------*/
+void createIntegerLiteral(int integerValue) {
+ litCount++;
+ literals[litCount]._class = header->integerClassId;
+ literals[litCount].type = NUMERIC_LITERAL;
+ literals[litCount].value = integerValue;
+}
+
+/*----------------------------------------------------------------------*/
+void createStringLiteral(char *unquotedString) {
+ litCount++;
+ literals[litCount]._class = header->stringClassId;
+ literals[litCount].type = STRING_LITERAL;
+ literals[litCount].value = toAptr(strdup(unquotedString));
+}
+
+/*----------------------------------------------------------------------*/
+void freeLiterals() {
+ int i;
+
+ for (i = 0; i <= litCount; i++)
+ if (literals[i].type == STRING_LITERAL && literals[i].value != 0) {
+ deallocate((void *)fromAptr(literals[i].value));
+ }
+ litCount = 0;}
+
+
+
+/*======================================================================*/
+int literalFromInstance(int instance) {
+ return instance - header->instanceMax;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/literal.h b/engines/glk/alan3/literal.h
new file mode 100644
index 0000000000..5afcd37b63
--- /dev/null
+++ b/engines/glk/alan3/literal.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_LITERAL
+#define GLK_ALAN3_LITERAL
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* TYPES */
+enum LiteralType {
+ NO_LITERAL, NUMERIC_LITERAL, STRING_LITERAL
+};
+
+struct LiteralEntry { /* LITERAL */
+ Aint _class; /* Class id of the literal type */
+ LiteralType type;
+ Aptr value;
+};
+
+
+/* DATA */
+extern int litCount;
+extern LiteralEntry *literals;
+
+
+/* FUNCTIONS */
+extern void createIntegerLiteral(int integerValue);
+extern void createStringLiteral(char *unquotedString);
+extern void freeLiterals(void);
+extern int literalFromInstance(int instance);
+extern int instanceFromLiteral(int literal);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/location.cpp b/engines/glk/alan3/location.cpp
new file mode 100644
index 0000000000..59dccd9a07
--- /dev/null
+++ b/engines/glk/alan3/location.cpp
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/location.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/word.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/checkentry.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/dictionary.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/current.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*----------------------------------------------------------------------*/
+static void traceExit(int location, int dir, char *what) {
+ printf("\n<EXIT %s[%d] from ",
+ (char *)pointerTo(dictionary[playerWords[currentWordIndex-1].code].string), dir);
+ traceSay(location);
+ printf("[%d], %s:>\n", location, what);
+}
+
+
+
+/*======================================================================*/
+void go(int location, int dir)
+{
+ ExitEntry *theExit;
+ bool ok;
+ Aword oldloc;
+
+ theExit = (ExitEntry *) pointerTo(instances[location].exits);
+ if (instances[location].exits != 0)
+ while (!isEndOfArray(theExit)) {
+ if (theExit->code == dir) {
+ ok = TRUE;
+ if (theExit->checks != 0) {
+ if (traceSectionOption)
+ traceExit(location, dir, "Checking");
+ ok = !checksFailed(theExit->checks, EXECUTE_CHECK_BODY_ON_FAIL);
+ }
+ if (ok) {
+ oldloc = location;
+ if (theExit->action != 0) {
+ if (traceSectionOption)
+ traceExit(location, dir, "Executing");
+ interpret(theExit->action);
+ }
+ /* Still at the same place? */
+ if (where(HERO, TRANSITIVE) == oldloc) {
+ if (traceSectionOption)
+ traceExit(location, dir, "Moving");
+ locate(HERO, theExit->target);
+ }
+ return;
+ } else
+ error(NO_MSG);
+ }
+ theExit++;
+ }
+ error(M_NO_WAY);
+}
+
+
+/*======================================================================*/
+bool exitto(int to, int from)
+{
+ ExitEntry *theExit;
+
+ if (instances[from].exits == 0)
+ return FALSE; /* No exits */
+
+ for (theExit = (ExitEntry *) pointerTo(instances[from].exits); !isEndOfArray(theExit); theExit++)
+ if (theExit->target == to)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/*======================================================================*/
+void look(void)
+{
+ int i;
+
+ /* Set describe flag for all objects and actors */
+ for (i = 1; i <= header->instanceMax; i++)
+ admin[i].alreadyDescribed = FALSE;
+
+ if (anyOutput)
+ para();
+
+ setSubHeaderStyle();
+ sayInstance(current.location);
+ setNormalStyle();
+
+ newline();
+ capitalize = TRUE;
+ if (describe(current.location))
+ describeInstances();
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/location.h b/engines/glk/alan3/location.h
new file mode 100644
index 0000000000..14cada9e4a
--- /dev/null
+++ b/engines/glk/alan3/location.h
@@ -0,0 +1,38 @@
+/* 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_ALAN3_LOCATION
+#define GLK_ALAN3_LOCATION
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern bool exitto(int to, int from);
+extern void go(int location, int dir);
+extern void look(void);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/main.cpp b/engines/glk/alan3/main.cpp
new file mode 100644
index 0000000000..a81082edaf
--- /dev/null
+++ b/engines/glk/alan3/main.cpp
@@ -0,0 +1,893 @@
+/* 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/textconsole.h"
+#include "glk/alan3/main.h"
+#include "glk/alan3/alan_version.h"
+#include "glk/alan3/args.h"
+#include "glk/alan3/class.h"
+#include "glk/alan3/compatibility.h"
+#include "glk/alan3/container.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/decode.h"
+#include "glk/alan3/dictionary.h"
+#include "glk/alan3/event.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/literal.h"
+#include "glk/alan3/location.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/parse.h"
+#include "glk/alan3/reverse.h"
+#include "glk/alan3/rules.h"
+#include "glk/alan3/scan.h"
+#include "glk/alan3/score.h"
+#include "glk/alan3/state.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/syntax.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/utils.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+
+/* Amachine structures - Static */
+VerbEntry *vrbs; /* Verb table pointer */
+
+
+/* PRIVATE DATA */
+#define STACKSIZE 100
+
+
+
+#ifdef CHECKOBJ
+/*======================================================================
+
+ checkobj()
+
+ Check that the object given is valid, else print an error message
+ or find out what he wanted.
+
+ This routine is not used any longer, kept for sentimental reasons ;-)
+
+*/
+void checkobj(Aword *obj) {
+ Aword oldobj;
+
+ if (*obj != EOF)
+ return;
+
+ oldobj = EOF;
+ for (cur.obj = OBJMIN; cur.obj <= OBJMAX; cur.obj++) {
+ /* If an object is present and it is possible to perform his action */
+ if (isHere(cur.obj) && possible())
+ if (oldobj == EOF)
+ oldobj = cur.obj;
+ else
+ error(WANT); /* And we didn't find multiple objects */
+ }
+
+ if (oldobj == EOF)
+ error(WANT); /* But we found ONE */
+
+ *obj = cur.obj = oldobj;
+ output("($o)"); /* Then he surely meant this object */
+}
+#endif
+
+
+
+
+/*----------------------------------------------------------------------*
+ *
+ * Event Handling
+ *
+ *----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+static char *eventName(int event) {
+ return stringAt(events[event].id);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void runPendingEvents(void)
+{
+ int i;
+
+ resetRules();
+ while (eventQueueTop != 0 && eventQueue[eventQueueTop-1].after == 0) {
+ eventQueueTop--;
+ if (isALocation(eventQueue[eventQueueTop].where))
+ current.location = eventQueue[eventQueueTop].where;
+ else
+ current.location = where(eventQueue[eventQueueTop].where, TRANSITIVE);
+ if (traceSectionOption) {
+ printf("\n<EVENT %s[%d] (at ", eventName(eventQueue[eventQueueTop].event),
+ eventQueue[eventQueueTop].event);
+ traceSay(current.location);
+ printf(" [%d]):>\n", current.location);
+ }
+ interpret(events[eventQueue[eventQueueTop].event].code);
+ evaluateRules(rules);
+ }
+
+ for (i = 0; i<eventQueueTop; i++)
+ eventQueue[i].after--;
+}
+
+
+
+
+/*----------------------------------------------------------------------*\
+
+ Main program and initialisation
+
+\*----------------------------------------------------------------------*/
+
+
+Common::SeekableReadStream *codfil;
+static char codfnm[256] = "";
+static char txtfnm[256] = "";
+
+
+/*----------------------------------------------------------------------
+ Calculate where to start calculating the CRC. Is different for
+ different versions. CRC is calculated from pre-beta2 memory start to
+ be compatible. If header size changes this should return beta2
+ header size for later versions.
+*/
+static int crcStart(char version[4]) {
+ /* Some earlier versions had a shorter header */
+ if (isPreAlpha5(version))
+ return sizeof(Pre3_0alpha5Header)/sizeof(Aword);
+ else if (isPreBeta2(version))
+ return sizeof(Pre3_0beta2Header)/sizeof(Aword);
+ else
+ return sizeof(ACodeHeader)/sizeof(Aword);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void readTemporaryHeader(ACodeHeader *tmphdr) {
+ codfil->seek(0);
+ codfil->read(tmphdr, sizeof(*tmphdr));
+ codfil->seek(0);
+
+ if (strncmp((char *)tmphdr, "ALAN", 4) != 0)
+ playererr("Not an Alan game file, does not start with \"ALAN\"");
+}
+
+
+/*----------------------------------------------------------------------*/
+static void reverseMemory() {
+ if (littleEndian()) {
+ if (debugOption||traceSectionOption||traceInstructionOption)
+ output("<Hmm, this is a little-endian machine, fixing byte ordering....");
+ reverseACD(); /* Reverse content of the ACD file */
+ if (debugOption||traceSectionOption||traceInstructionOption)
+ output("OK.>$n");
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void setupHeader(ACodeHeader tmphdr) {
+ if (isPreBeta2(tmphdr.version)) {
+ header = (ACodeHeader *)duplicate(&memory[0], sizeof(ACodeHeader));
+ if (isPreAlpha5(tmphdr.version)) {
+ header->ifids = 0;
+ }
+ header->prompt = 0;
+ } else if (isPreBeta3(tmphdr.version)) {
+ header = (ACodeHeader *)pointerTo(0);
+ } else {
+ header = (ACodeHeader *)pointerTo(0);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void loadAndCheckMemory(ACodeHeader tmphdr, Aword crc, char err[]) {
+ int i;
+ /* No memory allocated yet? */
+ if (memory == NULL) {
+ memory = (Aword *)allocate(tmphdr.size * sizeof(Aword));
+ }
+
+ memTop = tmphdr.size;
+ if (sizeof(Aword) * tmphdr.size > codfil->size())
+ syserr("Could not read all ACD code.");
+
+ for (Aword i = 0; i < tmphdr.size; ++i)
+ memory[i] = codfil->readUint32LE();
+
+ /* Calculate checksum */
+ for (i = crcStart(tmphdr.version); i < memTop; i++) {
+ crc += memory[i]&0xff;
+ crc += (memory[i]>>8)&0xff;
+ crc += (memory[i]>>16)&0xff;
+ crc += (memory[i]>>24)&0xff;
+#ifdef CRCLOG
+ printf("%6x\t%6lx\t%6lx\n", i, crc, memory[i]);
+#endif
+ }
+ if (crc != tmphdr.acdcrc) {
+ sprintf(err, "Checksum error in Acode (.a3c) file (0x%lx instead of 0x%lx).",
+ (unsigned long) crc, (unsigned long) tmphdr.acdcrc);
+ if (!ignoreErrorOption)
+ syserr(err);
+ else {
+ output("<WARNING! $$");
+ output(err);
+ output("$$ Ignored, proceed at your own risk.>$n");
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *decodeState(int c) {
+ static char state[3] = "\0\0";
+ switch (c) {
+ case 0: return ".";
+ case 'd': return "dev";
+ case 'a': return "alpha";
+ case 'b': return "beta";
+ default:
+ state[0] = header->version[3];
+ return state;
+ }
+}
+
+/*======================================================================*/
+char *decodedGameVersion(char version[]) {
+ static char str[100];
+ sprintf(str, "%d.%d%s%d",
+ (int)version[3],
+ (int)version[2],
+ decodeState(version[0]),
+ (int)version[1]);
+ return str;
+}
+
+/*----------------------------------------------------------------------*/
+static void incompatibleDevelopmentVersion(ACodeHeader *header) {
+ char str[80];
+ sprintf(str, "Incompatible version of ACODE program. Development versions always require exact match. Game is %ld.%ld%s%ld, interpreter %ld.%ld%s%ld!",
+ (long)(header->version[0]),
+ (long)(header->version[1]),
+ decodeState(header->version[3]),
+ (long)(header->version[2]),
+ (long)alan.version.version,
+ (long)alan.version.revision,
+ alan.version.state,
+ (long)alan.version.correction);
+ apperr(str);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void incompatibleVersion(ACodeHeader *header) {
+ char str[80];
+ sprintf(str, "Incompatible version of ACODE program. Game is %ld.%ld, interpreter %ld.%ld.",
+ (long)(header->version[0]),
+ (long)(header->version[1]),
+ (long)alan.version.version,
+ (long)alan.version.revision);
+ apperr(str);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void alphaRunningLaterGame(char gameState) {
+ output("<WARNING! You are running an alpha interpreter, but the game is generated by a");
+ if (gameState == 'b')
+ output("beta");
+ else
+ output("release");
+ output("state compiler which was released later. This might cause the game to not work fully as intended. Look for an upgraded game file.>\n");
+}
+
+/*----------------------------------------------------------------------*/
+static void nonDevelopmentRunningDevelopmentStateGame(char version[]) {
+ char errorMessage[200];
+ char versionString[100];
+
+ strcpy(errorMessage, "Games generated by a development state compiler");
+ sprintf(versionString, "(this game is v%d.%d.%d%s)", version[0], version[1],
+ version[2], decodeState(version[3]));
+ strcat(errorMessage, versionString);
+ strcat(errorMessage, "can only be run with a matching interpreter. Look for a game file generated with an alpha, beta or release state compiler.>\n");
+ apperr(errorMessage);
+}
+
+
+/*======================================================================*/
+void checkVersion(ACodeHeader *header)
+{
+ /* Strategy for version matching is:
+ 1) Development interpreters/games require exact match
+ 2) Alpha, Beta and Release interpreters will not run development games
+ 3) Alpha interpreters must warn if they run beta or release games
+ 4) Beta interpreters may introduce changes which are not alpha compatible,
+ if the change is a strict addition (i.e. if not used will not affect
+ alpha interpreters, example is introduction of a new opcode if it is
+ done at the end of the list)
+ 5) Release interpreters should run alpha and beta games without problems
+
+ NOTE that we are working with a non-reversed version string/word here.
+ */
+
+ char interpreterVersion[4];
+ bool developmentVersion;
+ bool alphaVersion;
+ int compareLength;
+ char gameState = header->version[3];
+
+ /* Construct our own version */
+ interpreterVersion[0] = alan.version.version;
+ interpreterVersion[1] = alan.version.revision;
+ interpreterVersion[2] = alan.version.correction;
+ interpreterVersion[3] = alan.version.state[0];
+
+ /* Check version of .ACD file */
+ if (debugOption && !regressionTestOption) {
+ printf("<Version of '%s' is %d.%d%s%d!>\n",
+ adventureFileName,
+ (int)header->version[0],
+ (int)header->version[1],
+ decodeState(header->version[3]),
+ (int)header->version[2]);
+ newline();
+ }
+
+ /* Development version require exact match, else only 2 digit match */
+ developmentVersion = (strcmp(alan.version.state, "dev") == 0);
+ alphaVersion = (strcmp(alan.version.state, "alpha") == 0);
+ compareLength = (developmentVersion? 3 : 2);
+
+ if (gameState == 'd' && !developmentVersion)
+ /* Development state game requires development state interpreter... */
+ nonDevelopmentRunningDevelopmentStateGame(header->version);
+ else {
+ /* Compatible if version, revision (and correction if dev state) match... */
+ if (memcmp(header->version, interpreterVersion, compareLength) != 0) {
+ /* Mismatch! */
+ if (!ignoreErrorOption) {
+ if (developmentVersion)
+ incompatibleDevelopmentVersion(header);
+ else
+ incompatibleVersion(header);
+ } else
+ output("<WARNING! Incompatible version of ACODE program.>\n");
+ } else if (developmentVersion && gameState != 'd')
+ /* ... unless interpreter is development and game not */
+ incompatibleDevelopmentVersion(header);
+ else if (alphaVersion && gameState != 'a') {
+ /* If interpreter is alpha version and the game is later, warn! */
+ alphaRunningLaterGame(gameState);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static void load(void)
+{
+ ACodeHeader tmphdr;
+ Aword crc = 0;
+ char err[100];
+
+ readTemporaryHeader(&tmphdr);
+ checkVersion(&tmphdr);
+
+ /* Allocate and load memory */
+
+ if (littleEndian())
+ reverseHdr(&tmphdr);
+
+ if (tmphdr.size <= sizeof(ACodeHeader)/sizeof(Aword))
+ syserr("Malformed game file. Too small.");
+
+ loadAndCheckMemory(tmphdr, crc, err);
+
+ reverseMemory();
+ setupHeader(tmphdr);
+
+}
+
+
+/*----------------------------------------------------------------------*/
+static void checkDebug(void)
+{
+ /* Make sure he can't debug if not allowed! */
+ if (!header->debug) {
+ if (debugOption|traceSectionOption|traceInstructionOption) {
+ printf("<Sorry, '%s' is not compiled for debug! Exiting.>\n", adventureFileName);
+ terminate(0);
+ }
+ para();
+ debugOption = FALSE;
+ traceSectionOption = FALSE;
+ traceInstructionOption = FALSE;
+ tracePushOption = FALSE;
+ }
+#ifdef TODO
+ if (debugOption || regressionTestOption) /* If debugging... */
+ srand(1); /* ... use no randomization */
+ else
+ srand(time(0)); /* Else seed random generator */
+#endif
+}
+
+
+/*----------------------------------------------------------------------*/
+static void initStaticData(void)
+{
+ /* Dictionary */
+ dictionary = (DictionaryEntry *) pointerTo(header->dictionary);
+ /* Find out number of entries in dictionary */
+ for (dictionarySize = 0; !isEndOfArray(&dictionary[dictionarySize]); dictionarySize++);
+
+ /* Scores */
+
+
+ /* All addresses to tables indexed by ids are converted to pointers,
+ then adjusted to point to the (imaginary) element before the
+ actual table so that [0] does not exist. Instead indices goes
+ from 1 and we can use [1]. */
+
+ if (header->instanceTableAddress == 0)
+ syserr("Instance table pointer == 0");
+ instances = (InstanceEntry *) pointerTo(header->instanceTableAddress);
+ instances--; /* Back up one so that first is no. 1 */
+
+
+ if (header->classTableAddress == 0)
+ syserr("Class table pointer == 0");
+ classes = (ClassEntry *) pointerTo(header->classTableAddress);
+ classes--; /* Back up one so that first is no. 1 */
+
+ if (header->containerTableAddress != 0) {
+ containers = (ContainerEntry *) pointerTo(header->containerTableAddress);
+ containers--;
+ }
+
+ if (header->eventTableAddress != 0) {
+ events = (EventEntry *) pointerTo(header->eventTableAddress);
+ events--;
+ }
+
+ /* Scores, if already allocated, copy initial data */
+ if (scores == NULL)
+ scores = (Aword *)duplicate((Aword *) pointerTo(header->scores), header->scoreCount*sizeof(Aword));
+ else
+ memcpy(scores, pointerTo(header->scores), header->scoreCount*sizeof(Aword));
+
+ if (literals == NULL)
+ literals = (LiteralEntry *)allocate(sizeof(Aword)*(MAXPARAMS+1));
+
+ stxs = (SyntaxEntry *) pointerTo(header->syntaxTableAddress);
+ vrbs = (VerbEntry *) pointerTo(header->verbTableAddress);
+ msgs = (MessageEntry *) pointerTo(header->messageTableAddress);
+ initRules(header->ruleTableAddress);
+
+ if (header->pack)
+ freq = (Aword *) pointerTo(header->freq);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void initStrings(void)
+{
+ StringInitEntry *init;
+
+ for (init = (StringInitEntry *) pointerTo(header->stringInitTable); !isEndOfArray(init); init++)
+ setInstanceAttribute(init->instanceCode, init->attributeCode, toAptr(getStringFromFile(init->fpos, init->len)));
+}
+
+/*----------------------------------------------------------------------*/
+static Aint sizeOfAttributeData(void)
+{
+ int i;
+ int size = 0;
+
+ for (i=1; i<=header->instanceMax; i++) {
+ AttributeEntry *attribute = (AttributeEntry *)pointerTo(instances[i].initialAttributes);
+ while (!isEndOfArray(attribute)) {
+ size += AwordSizeOf(AttributeEntry);
+ attribute++;
+ }
+ size += 1; /* For EOF */
+ }
+
+ if (size != header->attributesAreaSize
+ && (sizeof(AttributeHeaderEntry) == sizeof(AttributeEntry)))
+ syserr("Attribute area size calculated wrong.");
+ return size;
+}
+
+
+/*----------------------------------------------------------------------*/
+static AttributeEntry *initializeAttributes(int awordSize)
+{
+ Aword *attributeArea = (Aword *)allocate(awordSize*sizeof(Aword));
+ Aword *currentAttributeArea = attributeArea;
+ int i;
+
+ for (i=1; i<=header->instanceMax; i++) {
+ AttributeHeaderEntry *originalAttribute = (AttributeHeaderEntry *)pointerTo(instances[i].initialAttributes);
+ admin[i].attributes = (AttributeEntry *)currentAttributeArea;
+ while (!isEndOfArray(originalAttribute)) {
+ ((AttributeEntry *)currentAttributeArea)->code = originalAttribute->code;
+ ((AttributeEntry *)currentAttributeArea)->value = originalAttribute->value;
+ ((AttributeEntry *)currentAttributeArea)->id = originalAttribute->id;
+ currentAttributeArea += AwordSizeOf(AttributeEntry);
+ originalAttribute++;
+ }
+ *((Aword*)currentAttributeArea) = EOF;
+ currentAttributeArea += 1;
+ }
+
+ return (AttributeEntry *)attributeArea;
+}
+
+
+
+
+/*----------------------------------------------------------------------*/
+static void initDynamicData(void)
+{
+ int instanceId;
+
+ /* Allocate for administrative table */
+ admin = (AdminEntry *)allocate((header->instanceMax+1)*sizeof(AdminEntry));
+
+ /* Create game state copy of attributes */
+ attributes = initializeAttributes(sizeOfAttributeData());
+
+ /* Initialise string & set attributes */
+ initStrings();
+ initSets((SetInitEntry*)pointerTo(header->setInitTable));
+
+ /* Set initial locations */
+ for (instanceId = 1; instanceId <= header->instanceMax; instanceId++)
+ admin[instanceId].location = instances[instanceId].initialLocation;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void runInheritedInitialize(Aint theClass) {
+ if (theClass == 0) return;
+ runInheritedInitialize(classes[theClass].parent);
+ if (classes[theClass].initialize)
+ interpret(classes[theClass].initialize);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void runInitialize(Aint theInstance) {
+ runInheritedInitialize(instances[theInstance].parent);
+ if (instances[theInstance].initialize != 0)
+ interpret(instances[theInstance].initialize);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void initializeInstances() {
+ int instanceId;
+
+ /* Set initial locations */
+ for (instanceId = 1; instanceId <= header->instanceMax; instanceId++) {
+ current.instance = instanceId;
+ runInitialize(instanceId);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void start(void)
+{
+ int startloc;
+
+ current.tick = 0;
+ current.location = startloc = where(HERO, TRANSITIVE);
+ current.actor = HERO;
+ current.score = 0;
+
+ initializeInstances();
+
+ if (traceSectionOption)
+ printf("\n<START:>\n");
+ interpret(header->start);
+ para();
+
+ if (where(HERO, TRANSITIVE) == startloc) {
+ if (traceSectionOption)
+ printf("<CURRENT LOCATION:>");
+ look();
+ }
+ resetAndEvaluateRules(rules, header->version);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void openFiles(void)
+{
+ char str[256];
+
+ /*
+ Open Acode file
+ strcpy(codfnm, adventureFileName);
+ if ((codfil = fopen(codfnm, READ_MODE)) == NULL) {
+ strcpy(str, "Can't open adventure code file '");
+ strcat(str, codfnm);
+ strcat(str, "'.");
+ playererr(str);
+ }
+
+ // Open Text file
+ strcpy(txtfnm, adventureFileName);
+ if ((textFile = fopen(txtfnm, READ_MODE)) == NULL) {
+ strcpy(str, "Can't open adventure text data file '");
+ strcat(str, txtfnm);
+ strcat(str, "'.");
+ apperr(str);
+ }
+ */
+
+ /* If logging open log file */
+ if (transcriptOption || logOption) {
+ startTranscript();
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void init(void)
+{
+ int i;
+
+ /* Initialise some status */
+ eventQueueTop = 0; /* No pending events */
+ initStaticData();
+ initDynamicData();
+ initParsing();
+ checkDebug();
+
+ getPageSize();
+
+ /* Find first conjunction and use that for ',' handling */
+ for (i = 0; i < dictionarySize; i++)
+ if (isConjunction(i)) {
+ conjWord = i;
+ break;
+ }
+
+ /* Start the adventure */
+ if (debugOption)
+ debug(FALSE, 0, 0);
+ else
+ clear();
+
+ start();
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static bool traceActor(int theActor)
+{
+ if (traceSectionOption) {
+ printf("\n<ACTOR ");
+ traceSay(theActor);
+ printf("[%d]", theActor);
+ if (current.location != 0) {
+ printf(" (at ");
+ traceSay(current.location);
+ } else
+ printf(" (nowhere");
+ printf("[%d])", current.location);
+ }
+ return traceSectionOption;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *scriptName(int theActor, int theScript)
+{
+ ScriptEntry *scriptEntry = (ScriptEntry *)pointerTo(header->scriptTableAddress);
+
+ while (theScript > 1) {
+ scriptEntry++;
+ theScript--;
+ }
+ return (char *)pointerTo(scriptEntry->id);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void moveActor(int theActor)
+{
+ ScriptEntry *scr;
+ StepEntry *step;
+ Aint previousInstance = current.instance;
+
+ current.actor = theActor;
+ current.instance = theActor;
+ current.location = where(theActor, TRANSITIVE);
+ if (theActor == HERO) {
+#ifdef TODO
+ /* Ask him! */
+ if (setjmp(forfeitLabel) == 0) {
+ parse();
+ capitalize = TRUE;
+ fail = FALSE; /* fail only aborts one actor */
+ }
+#else
+ ::error("TODO: moveActor setjmp");
+#endif
+ } else if (admin[theActor].script != 0) {
+ for (scr = (ScriptEntry *) pointerTo(header->scriptTableAddress); !isEndOfArray(scr); scr++) {
+ if (scr->code == admin[theActor].script) {
+ /* Find correct step in the list by indexing */
+ step = (StepEntry *) pointerTo(scr->steps);
+ step = (StepEntry *) &step[admin[theActor].step];
+ /* Now execute it, maybe. First check wait count */
+ if (admin[theActor].waitCount > 0) { /* Wait some more ? */
+ if (traceActor(theActor))
+ printf(", SCRIPT %s[%ld], STEP %ld, Waiting another %ld turns>\n",
+ scriptName(theActor, admin[theActor].script),
+ (long)admin[theActor].script, (long)admin[theActor].step+1,
+ (long)admin[theActor].waitCount);
+ admin[theActor].waitCount--;
+ break;
+ }
+ /* Then check possible expression to wait for */
+ if (step->exp != 0) {
+ if (traceActor(theActor))
+ printf(", SCRIPT %s[%ld], STEP %ld, Evaluating:>\n",
+ scriptName(theActor, admin[theActor].script),
+ (long)admin[theActor].script, (long)admin[theActor].step+1);
+ if (!evaluate(step->exp))
+ break; /* Break loop, don't execute step*/
+ }
+ /* OK, so finally let him do his thing */
+ admin[theActor].step++; /* Increment step number before executing... */
+ if (!isEndOfArray(step+1) && (step+1)->after != 0) {
+ admin[theActor].waitCount = evaluate((step+1)->after);
+ }
+ if (traceActor(theActor))
+ printf(", SCRIPT %s[%ld], STEP %ld, Executing:>\n",
+ scriptName(theActor, admin[theActor].script),
+ (long)admin[theActor].script,
+ (long)admin[theActor].step);
+ interpret(step->stms);
+ step++;
+ /* ... so that we can see if he failed or is USEing another script now */
+ if (fail || (admin[theActor].step != 0 && isEndOfArray(step)))
+ /* No more steps in this script, so stop him */
+ admin[theActor].script = 0;
+ fail = FALSE; /* fail only aborts one actor */
+ break; /* We have executed a script so leave loop */
+ }
+ }
+ if (isEndOfArray(scr))
+ syserr("Unknown actor script.");
+ } else {
+ if (traceActor(theActor)) {
+ printf(", Idle>\n");
+ }
+ }
+
+ current.instance = previousInstance;
+}
+
+#define RESTARTED (setjmp(restartLabel) != NO_JUMP_RETURN)
+#define ERROR_RETURNED (setjmp(returnLabel) != NO_JUMP_RETURN)
+
+/*======================================================================*/
+void run(void)
+{
+ int i;
+ static Stack theStack = NULL; /* Needs to survive longjmp() */
+
+ openFiles();
+ load(); /* Load program */
+#ifdef TODO
+ if (RESTARTED) {
+ deleteStack(theStack);
+ }
+
+ theStack = createStack(STACKSIZE);
+ setInterpreterStack(theStack);
+
+ initStateStack();
+
+ if (!ERROR_RETURNED) /* Can happen in start section too... */
+ init(); /* Initialise and start the adventure */
+
+ while (TRUE) {
+ if (debugOption)
+ debug(FALSE, 0, 0);
+
+ if (stackDepth(theStack) != 0)
+ syserr("Stack is not empty in main loop");
+
+ if (!current.meta)
+ runPendingEvents();
+
+ /* Return here if error during execution */
+ switch (setjmp(returnLabel)) {
+ case NO_JUMP_RETURN:
+ break;
+ case ERROR_RETURN:
+ forgetGameState();
+ forceNewPlayerInput();
+ break;
+ case UNDO_RETURN:
+ forceNewPlayerInput();
+ break;
+ default:
+ syserr("Unexpected longjmp() return value");
+ }
+
+ recursionDepth = 0;
+
+ /* Move all characters, hero first */
+ rememberGameState();
+ current.meta = FALSE;
+ moveActor(header->theHero);
+
+ if (gameStateChanged)
+ rememberCommands();
+ else
+ forgetGameState();
+
+ if (!current.meta) {
+ current.tick++;
+
+ /* Remove this call? Since Eval is done up there after each event... */
+ resetAndEvaluateRules(rules, header->version);
+
+ /* Then all the other actors... */
+ for (i = 1; i <= header->instanceMax; i++)
+ if (i != header->theHero && isAActor(i)) {
+ moveActor(i);
+ resetAndEvaluateRules(rules, header->version);
+ }
+ }
+ }
+#endif
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/main.h b/engines/glk/alan3/main.h
new file mode 100644
index 0000000000..127cc111ae
--- /dev/null
+++ b/engines/glk/alan3/main.h
@@ -0,0 +1,42 @@
+/* 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_ALAN3_MAIN
+#define GLK_ALAN3_MAIN
+
+/* Header file for main unit of ARUN Alan System interpreter */
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern VerbEntry *vrbs; // Verb table pointer
+
+extern void run();
+extern void usage();
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/memory.cpp b/engines/glk/alan3/memory.cpp
new file mode 100644
index 0000000000..600e9b6c9a
--- /dev/null
+++ b/engines/glk/alan3/memory.cpp
@@ -0,0 +1,118 @@
+/* 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/alan3/memory.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/syserr.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+
+Aword *memory = NULL;
+static ACodeHeader dummyHeader; /* Dummy to use until memory allocated */
+ACodeHeader *header = &dummyHeader;
+int memTop = 0; /* Top of load memory */
+
+
+/*======================================================================*/
+void *allocate(unsigned long lengthInBytes)
+{
+ void *p = (void *)calloc((size_t)lengthInBytes, 1);
+
+ if (p == NULL)
+ syserr("Out of memory.");
+
+ return p;
+}
+
+
+/*======================================================================*/
+void deallocate(void *ptr) {
+ free(ptr);
+}
+
+
+/*======================================================================*/
+void *duplicate(void *original, unsigned long len)
+{
+ void *p = allocate(len+1);
+
+ memcpy(p, original, len);
+ return p;
+}
+
+
+typedef struct {
+ Aptr aptr;
+ void *voidp;
+} PointerMapEntry;
+
+static PointerMapEntry *pointerMap = NULL;
+static int pointerMapSize = 0;
+static int nextAptr = 1;
+
+/*======================================================================*/
+void resetPointerMap(void) {
+ if (pointerMap != NULL) free(pointerMap);
+ pointerMap = NULL;
+ pointerMapSize = 0;
+}
+
+/*======================================================================*/
+void *fromAptr(Aptr aptr) {
+ int index;
+
+ for (index=0; index < pointerMapSize && pointerMap[index].aptr != aptr; index++)
+ ;
+
+ if (index == pointerMapSize)
+ syserr("No pointerMap entry for Aptr");
+
+ return pointerMap[index].voidp;
+}
+
+
+/*======================================================================*/
+Aptr toAptr(void *ptr) {
+ int index;
+
+ if (pointerMap == NULL) {
+ pointerMap = (PointerMapEntry *)allocate(sizeof(PointerMapEntry));
+ pointerMapSize = 1;
+ }
+
+ for (index=0; index < pointerMapSize && pointerMap[index].voidp != NULL; index++)
+ ;
+ if (index == pointerMapSize) {
+ pointerMap = (PointerMapEntry *)realloc(pointerMap, (index+1)*sizeof(PointerMapEntry));
+ pointerMapSize++;
+ }
+
+ pointerMap[index].voidp = ptr;
+ pointerMap[index].aptr = nextAptr++;
+ return pointerMap[index].aptr;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/memory.h b/engines/glk/alan3/memory.h
new file mode 100644
index 0000000000..78cdcc4658
--- /dev/null
+++ b/engines/glk/alan3/memory.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_MEMORY
+#define GLK_ALAN3_MEMORY
+
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern Aword *memory;
+extern ACodeHeader *header;
+extern int memTop;
+
+
+/* FUNCTIONS */
+extern void *allocate(unsigned long lengthInBytes);
+extern void *duplicate(void *original, unsigned long len);
+extern void deallocate(void *ptr);
+
+extern void resetPointerMap();
+extern void *fromAptr(Aptr aptr);
+extern Aptr toAptr(void *ptr);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/msg.cpp b/engines/glk/alan3/msg.cpp
new file mode 100644
index 0000000000..d2d4956b85
--- /dev/null
+++ b/engines/glk/alan3/msg.cpp
@@ -0,0 +1,107 @@
+/* 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/alan3/msg.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/lists.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+MessageEntry *msgs; /* Message table pointer */
+
+/*======================================================================*/
+void printMessage(MsgKind msg) /* IN - message number */
+{
+ interpret(msgs[msg].stms);
+}
+
+
+static void (*errorHandler)(MsgKind msg) = NULL;
+
+/*======================================================================*/
+void setErrorHandler(void (*handler)(MsgKind msg)) /* IN - The error message number */
+{
+ // N.B. The error handler must not return because the standard handler does not...
+ errorHandler = handler;
+}
+
+
+/*======================================================================*/
+void error(MsgKind msgno) /* IN - The error message number */
+{
+ if (errorHandler != NULL)
+ errorHandler(msgno);
+ else {
+ /* Print an error message and longjmp to main loop. */
+ if (msgno != NO_MSG)
+ printMessage(msgno);
+ //longjmp(returnLabel, ERROR_RETURN);
+ }
+}
+
+
+/*======================================================================*/
+void abortPlayerCommand(void)
+{
+ error(NO_MSG);
+}
+
+
+/*======================================================================*/
+void printMessageWithInstanceParameter(MsgKind message, int instanceId) {
+ ParameterArray parameters = newParameterArray();
+ addParameterForInstance(parameters, instanceId);
+ printMessageWithParameters(message, parameters);
+ freeParameterArray(parameters);
+}
+
+
+/*======================================================================*/
+void printMessageUsing2InstanceParameters(MsgKind message, int instance1, int instance2) {
+ ParameterArray parameters = newParameterArray();
+ addParameterForInstance(parameters, instance1);
+ addParameterForInstance(parameters, instance2);
+ printMessageWithParameters(message, parameters);
+ freeParameterArray(parameters);
+}
+
+
+/*======================================================================*/
+void printMessageWithParameters(MsgKind msg, Parameter *messageParameters)
+{
+ Parameter *savedParameters = newParameterArray();
+
+ copyParameterArray(savedParameters, globalParameters);
+ copyParameterArray(globalParameters, messageParameters);
+
+ interpret(msgs[msg].stms);
+
+ copyParameterArray(globalParameters, savedParameters);
+ freeParameterArray(savedParameters);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/msg.h b/engines/glk/alan3/msg.h
new file mode 100644
index 0000000000..9f445329ca
--- /dev/null
+++ b/engines/glk/alan3/msg.h
@@ -0,0 +1,56 @@
+/* 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_ALAN3_MSG
+#define GLK_ALAN3_MSG
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/params.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* TYPES */
+typedef struct MessageEntry { /* MESSAGE TABLE */
+ Aaddr stms; /* Address to statements*/
+} MessageEntry;
+
+
+/* DATA */
+extern MessageEntry *msgs; /* Message table pointer */
+
+
+/* FUNCTIONS */
+extern void setErrorHandler(void (*handler)(MsgKind));
+extern void abortPlayerCommand(void);
+extern void error(MsgKind msg);
+extern bool confirm(MsgKind msgno);
+extern void printMessage(MsgKind msg);
+extern void printMessageWithParameters(MsgKind msg, Parameter *messageParameters);
+extern void printMessageWithInstanceParameter(MsgKind message, int i);
+extern void printMessageUsing2InstanceParameters(MsgKind message, int instance1, int instance2);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/options.cpp b/engines/glk/alan3/options.cpp
new file mode 100644
index 0000000000..61e72ae9d7
--- /dev/null
+++ b/engines/glk/alan3/options.cpp
@@ -0,0 +1,40 @@
+/* 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.
+ *
+ */
+
+namespace Glk {
+namespace Alan3 {
+
+bool verboseOption;
+bool ignoreErrorOption;
+bool debugOption;
+bool traceSectionOption;
+bool tracePushOption;
+bool traceStackOption;
+bool traceSourceOption;
+bool traceInstructionOption;
+bool transcriptOption;
+bool logOption;
+bool statusLineOption;
+bool regressionTestOption;
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/options.h b/engines/glk/alan3/options.h
new file mode 100644
index 0000000000..b35c83a7d2
--- /dev/null
+++ b/engines/glk/alan3/options.h
@@ -0,0 +1,47 @@
+/* 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_ALAN3_OPTIONS
+#define GLK_ALAN3_OPTIONS
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern bool verboseOption;
+extern bool ignoreErrorOption;
+extern bool debugOption;
+extern bool traceSectionOption;
+extern bool tracePushOption;
+extern bool traceStackOption;
+extern bool traceSourceOption;
+extern bool traceInstructionOption;
+extern bool transcriptOption;
+extern bool logOption;
+extern bool statusLineOption;
+extern bool regressionTestOption;
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/output.cpp b/engines/glk/alan3/output.cpp
new file mode 100644
index 0000000000..20ab604228
--- /dev/null
+++ b/engines/glk/alan3/output.cpp
@@ -0,0 +1,508 @@
+/* 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/stream.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/word.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/dictionary.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/readline.h"
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/instance.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+bool anyOutput = FALSE;
+bool capitalize = FALSE;
+bool needSpace = FALSE;
+bool skipSpace = FALSE;
+
+/* Screen formatting info */
+int col, lin;
+int pageLength, pageWidth;
+
+/* Logfile */
+strid_t logFile;
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+#if defined(HAVE_GLK) || defined(RUNNING_UNITTESTS)
+/*----------------------------------------------------------------------*/
+static int updateColumn(int currentColumn, const char *string) {
+ const char *newlinePosition = strrchr(string, '\n');
+ if (newlinePosition != NULL)
+ return &string[strlen(string)] - newlinePosition;
+ else
+ return currentColumn + strlen(string);
+}
+#endif
+
+
+/*======================================================================*/
+void setSubHeaderStyle(void) {
+#ifdef HAVE_GLK
+ g_vm->glk_set_style(style_Subheader);
+#endif
+}
+
+
+/*======================================================================*/
+void setNormalStyle(void) {
+#ifdef HAVE_GLK
+ g_vm->glk_set_style(style_Normal);
+#endif
+}
+
+/*======================================================================*/
+void newline(void) {
+#ifndef HAVE_GLK
+ char buf[256];
+
+ if (!regressionTestOption && lin == pageLength - 1) {
+ printAndLog("\n");
+ needSpace = FALSE;
+ col = 0;
+ lin = 0;
+ printMessage(M_MORE);
+ statusline();
+ fflush(stdout);
+ fgets(buf, 256, stdin);
+ getPageSize();
+ } else
+ printAndLog("\n");
+
+ lin++;
+#else
+ printAndLog("\n");
+#endif
+ col = 1;
+ needSpace = FALSE;
+}
+
+
+/*======================================================================*/
+void para(void)
+{
+ /* Make a new paragraph, i.e one empty line (one or two newlines). */
+
+#ifdef HAVE_GLK
+ if (g_vm->glk_gestalt(gestalt_Graphics, 0) == 1)
+ g_vm->glk_window_flow_break(glkMainWin);
+#endif
+ if (col != 1)
+ newline();
+ newline();
+ capitalize = TRUE;
+}
+
+
+/*======================================================================*/
+void clear(void)
+{
+#ifdef HAVE_GLK
+ g_vm->glk_window_clear(glkMainWin);
+#else
+#ifdef HAVE_ANSI
+ if (!statusLineOption) return;
+ printf("\x1b[2J");
+ printf("\x1b[%d;1H", pageLength);
+#endif
+#endif
+}
+
+
+/*----------------------------------------------------------------------*/
+static void capitalizeFirst(char *str) {
+ int i = 0;
+
+ /* Skip over space... */
+ while (i < strlen(str) && isSpace(str[i])) i++;
+ if (i < strlen(str)) {
+ str[i] = toUpper(str[i]);
+ capitalize = FALSE;
+ }
+}
+
+
+/*======================================================================*/
+void printAndLog(const char *string)
+{
+#ifdef HAVE_GLK
+ static int column = 0;
+ char *stringCopy;
+ char *stringPart;
+#endif
+
+ printf("%s", string);
+ if (!onStatusLine && transcriptOption) {
+#ifdef HAVE_GLK
+ // TODO Is this assuming only 70-char wide windows for GLK?
+ if (strlen(string) > 70-column) {
+ stringCopy = strdup(string); /* Make sure we can write NULLs */
+ stringPart = stringCopy;
+ while (strlen(stringPart) > 70-column) {
+ int p;
+ for (p = 70-column; p>0 && !isspace((int)stringPart[p]); p--);
+ stringPart[p] = '\0';
+ g_vm->glk_put_string_stream(logFile, stringPart);
+ g_vm->glk_put_char_stream(logFile, '\n');
+ column = 0;
+ stringPart = &stringPart[p+1];
+ }
+ g_vm->glk_put_string_stream(logFile, stringPart);
+ column = updateColumn(column, stringPart);
+ free(stringCopy);
+ } else {
+ g_vm->glk_put_string_stream(logFile, string);
+ column = updateColumn(column, string);
+ }
+#else
+ fprintf(logFile, "%s", string);
+#endif
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void justify(char str[])
+{
+ if (capitalize)
+ capitalizeFirst(str);
+
+#ifdef HAVE_GLK
+ printAndLog(str);
+#else
+ int i;
+ char ch;
+
+ if (col >= pageWidth && !skipSpace)
+ newline();
+
+ while (strlen(str) > pageWidth - col) {
+ i = pageWidth - col - 1;
+ while (!isSpace(str[i]) && i > 0) /* First find wrap point */
+ i--;
+ if (i == 0 && col == 1) /* If it doesn't fit at all */
+ /* Wrap immediately after this word */
+ while (!isSpace(str[i]) && str[i] != '\0')
+ i++;
+ if (i > 0) { /* If it fits ... */
+ ch = str[i]; /* Save space or NULL */
+ str[i] = '\0'; /* Terminate string */
+ printAndLog(str); /* and print it */
+ skipSpace = FALSE; /* If skipping, now we're done */
+ str[i] = ch; /* Restore character */
+ /* Skip white after printed portion */
+ for (str = &str[i]; isSpace(str[0]) && str[0] != '\0'; str++);
+ }
+ newline(); /* Then start a new line */
+ while(isSpace(str[0])) str++; /* Skip any leading space on next part */
+ }
+ printAndLog(str); /* Print tail */
+#endif
+ col = col + strlen(str); /* Update column */
+}
+
+
+/*----------------------------------------------------------------------*/
+static void space(void)
+{
+ if (skipSpace)
+ skipSpace = FALSE;
+ else {
+ if (needSpace) {
+ printAndLog(" ");
+ col++;
+ }
+ }
+ needSpace = FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayPlayerWordsForParameter(int p) {
+ int i;
+
+ for (i = globalParameters[p].firstWord; i <= globalParameters[p].lastWord; i++) {
+ justify((char *)pointerTo(dictionary[playerWords[i].code].string));
+ if (i < globalParameters[p].lastWord)
+ justify(" ");
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayParameter(int p, int form)
+{
+ int i;
+
+ for (i = 0; i <= p; i++)
+ if (isEndOfArray(&globalParameters[i]))
+ apperr("Nonexistent parameter referenced.");
+
+#ifdef ALWAYS_SAY_PARAMETERS_USING_PLAYER_WORDS
+ if (params[p].firstWord != EOF) /* Any words he used? */
+ /* Yes, so use them... */
+ sayPlayerWordsForParameter(p);
+ else
+ sayForm(params[p].code, form);
+#else
+ if (globalParameters[p].useWords) {
+ /* Ambiguous instance referenced, so use the words he used */
+ sayPlayerWordsForParameter(p);
+ } else
+ sayForm(globalParameters[p].instance, (SayForm)form);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+
+ Print an expanded symbolic reference.
+
+ N = newline
+ I = indent on a new line
+ P = new paragraph
+ L = current location name
+ O = current object -> first parameter!
+ <n> = n:th parameter
+ +<n> = definite form of n:th parameter
+ 0<n> = indefinite form of n:th parameter
+ !<n> = pronoun for the n:th parameter
+ V = current verb
+ A = current actor
+ T = tabulation
+ $ = no space needed after this, and don't capitalize
+ _ = interpret this as a single dollar, if in doubt or conflict with other symbols
+*/
+static char *printSymbol(char str[]) /* IN - The string starting with '$' */
+{
+ int advance = 2;
+
+ if (*str == '\0') printAndLog("$");
+ else switch (toLower(str[1])) {
+ case 'n':
+ newline();
+ needSpace = FALSE;
+ break;
+ case 'i':
+ newline();
+ printAndLog(" ");
+ col = 5;
+ needSpace = FALSE;
+ break;
+ case 'o':
+ space();
+ sayParameter(0, 0);
+ needSpace = TRUE; /* We did print something non-white */
+ break;
+ case '+':
+ case '0':
+ case '-':
+ case '!':
+ space();
+ if (isdigit((int)str[2])) {
+ int form;
+ switch (str[1]) {
+ case '+': form = SAY_DEFINITE; break;
+ case '0': form = SAY_INDEFINITE; break;
+ case '-': form = SAY_NEGATIVE; break;
+ case '!': form = SAY_PRONOUN; break;
+ default: form = SAY_SIMPLE; break;
+ }
+ sayParameter(str[2]-'1', form);
+ needSpace = TRUE;
+ }
+ advance = 3;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ space();
+ sayParameter(str[1]-'1', SAY_SIMPLE);
+ needSpace = TRUE; /* We did print something non-white */
+ break;
+ case 'l':
+ space();
+ say(current.location);
+ needSpace = TRUE; /* We did print something non-white */
+ break;
+ case 'a':
+ space();
+ say(current.actor);
+ needSpace = TRUE; /* We did print something non-white */
+ break;
+ case 'v':
+ space();
+ justify((char *)pointerTo(dictionary[verbWord].string));
+ needSpace = TRUE; /* We did print something non-white */
+ break;
+ case 'p':
+ para();
+ needSpace = FALSE;
+ break;
+ case 't': {
+ int i;
+ int spaces = 4-(col-1)%4;
+
+ for (i = 0; i<spaces; i++) printAndLog(" ");
+ col = col + spaces;
+ needSpace = FALSE;
+ break;
+ }
+ case '$':
+ skipSpace = TRUE;
+ capitalize = FALSE;
+ break;
+ case '_':
+ advance = 2;
+ printAndLog("$");
+ break;
+ default:
+ advance = 1;
+ printAndLog("$");
+ break;
+ }
+
+ return &str[advance];
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool inhibitSpace(char *str) {
+ return str[0] != '\0' && str[0] == '$' && str[1] == '$';
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool isSpaceEquivalent(char str[]) {
+ if (str[0] == ' ')
+ return TRUE;
+ else
+ return strncmp(str, "$p", 2) == 0
+ || strncmp(str, "$n", 2) == 0
+ || strncmp(str, "$i", 2) == 0
+ || strncmp(str, "$t", 2) == 0;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool punctuationNext(char *str) {
+ const char *punctuation = strchr(".,!?", str[0]);
+ bool end = str[1] == '\0';
+ bool space = isSpaceEquivalent(&str[1]);
+ return (punctuation != NULL && (end || space));
+}
+
+
+/*----------------------------------------------------------------------*/
+static char lastCharOf(char *str) {
+ return str[strlen(str)-1];
+}
+
+
+/*======================================================================*/
+void output(const char *original)
+{
+ char ch;
+ char *str, *copy;
+ char *symptr;
+
+ copy = strdup(original);
+ str = copy;
+
+ if (inhibitSpace(str) || punctuationNext(str))
+ needSpace = FALSE;
+ else
+ space(); /* Output space if needed (& not inhibited) */
+
+ /* Output string up to symbol and handle the symbol */
+ while ((symptr = strchr(str, '$')) != (char *) NULL) {
+ ch = *symptr; /* Terminate before symbol */
+ *symptr = '\0';
+ if (strlen(str) > 0) {
+ skipSpace = FALSE; /* Only let skipSpace through if it is
+ last in the string */
+ if (lastCharOf(str) == ' ') {
+ str[strlen(str)-1] = '\0'; /* Truncate space character */
+ justify(str); /* Output part before '$' */
+ needSpace = TRUE;
+ } else {
+ justify(str); /* Output part before '$' */
+ needSpace = FALSE;
+ }
+ }
+ *symptr = ch; /* restore '$' */
+ str = printSymbol(symptr); /* Print the symbolic reference and advance */
+ }
+
+ if (str[0] != 0) {
+ justify(str); /* Output trailing part */
+ skipSpace = FALSE;
+ if (lastCharOf(str) != ' ')
+ needSpace = TRUE;
+ }
+ if (needSpace)
+ capitalize = strchr("!?.", str[strlen(str)-1]) != 0;
+ anyOutput = TRUE;
+ free(copy);
+}
+
+
+/*======================================================================*/
+bool 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! */
+ printMessage(msgno);
+
+#ifdef USE_READLINE
+ if (!readline(buf)) return TRUE;
+#else
+ if (gets(buf) == NULL) return TRUE;
+#endif
+ col = 1;
+
+ return (buf[0] == '\0');
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/output.h b/engines/glk/alan3/output.h
new file mode 100644
index 0000000000..e889b76b89
--- /dev/null
+++ b/engines/glk/alan3/output.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_ALAN3_OUTPUT
+#define GLK_ALAN3_OUTPUT
+
+#include "glk/alan3/types.h"
+#include "glk/streams.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern int col, lin; // TODO Move to current.column & current.line?
+extern int pageLength, pageWidth;
+
+extern bool anyOutput;
+extern bool needSpace;
+extern bool capitalize;
+
+/* Log file */
+extern strid_t logFile;
+
+/* FUNCTIONS */
+extern void setSubHeaderStyle(void);
+extern void setNormalStyle(void);
+extern void newline(void);
+extern void para(void);
+extern void clear(void);
+extern void printAndLog(const char *string);
+extern void output(const char *string);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/parameter_position.cpp b/engines/glk/alan3/parameter_position.cpp
new file mode 100644
index 0000000000..185b6e633b
--- /dev/null
+++ b/engines/glk/alan3/parameter_position.cpp
@@ -0,0 +1,103 @@
+/* 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/alan3/parameter_position.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/lists.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*======================================================================*/
+void deallocateParameterPositions(ParameterPosition *parameterPositions) {
+ int i;
+ for (i = 0; !parameterPositions[i].endOfList; i++) {
+ ParameterPosition *position = &parameterPositions[i];
+ freeParameterArray(position->parameters);
+ if (position->exceptions)
+ freeParameterArray(position->exceptions);
+ }
+ deallocate(parameterPositions);
+}
+
+/*======================================================================*/
+void uncheckAllParameterPositions(ParameterPosition parameterPositions[]) {
+ int position;
+ for (position = 0; position < MAXPARAMS; position++) {
+ parameterPositions[position].checked = FALSE;
+ }
+}
+
+
+/*======================================================================*/
+void copyParameterPositions(ParameterPosition originalParameterPositions[], ParameterPosition parameterPositions[]) {
+ int i;
+ for (i = 0; !originalParameterPositions[i].endOfList; i++)
+ parameterPositions[i] = originalParameterPositions[i];
+ parameterPositions[i].endOfList = TRUE;
+}
+
+
+/*======================================================================*/
+bool equalParameterPositions(ParameterPosition parameterPositions1[], ParameterPosition parameterPositions2[]) {
+ int i;
+ for (i = 0; !parameterPositions1[i].endOfList; i++) {
+ if (parameterPositions2[i].endOfList)
+ return FALSE;
+ if (!equalParameterArrays(parameterPositions1[i].parameters, parameterPositions2[i].parameters))
+ return FALSE;
+ }
+ return parameterPositions2[i].endOfList;
+}
+
+
+/*======================================================================*/
+int findMultipleParameterPosition(ParameterPosition parameterPositions[]) {
+ Aint parameterNumber;
+ for (parameterNumber = 0; !parameterPositions[parameterNumber].endOfList; parameterNumber++)
+ if (parameterPositions[parameterNumber].explicitMultiple)
+ return parameterNumber;
+ return -1;
+}
+
+
+/*======================================================================*/
+void markExplicitMultiple(ParameterPosition parameterPositions[], Parameter parameters[]) {
+ int parameterCount;
+ for (parameterCount = 0; !parameterPositions[parameterCount].endOfList; parameterCount++)
+ if (parameterPositions[parameterCount].explicitMultiple)
+ parameters[parameterCount].instance = 0;
+}
+
+/*======================================================================*/
+void convertPositionsToParameters(ParameterPosition parameterPositions[], Parameter parameters[]) {
+ ParameterPosition *position = parameterPositions;
+
+ clearParameterArray(parameters);
+ while (!position->endOfList) {
+ addParameterToParameterArray(parameters, &position->parameters[0]);
+ position++;
+ }
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/parameter_position.h b/engines/glk/alan3/parameter_position.h
new file mode 100644
index 0000000000..a2072011a0
--- /dev/null
+++ b/engines/glk/alan3/parameter_position.h
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_PARAMETER_POSITION
+#define GLK_ALAN3_PARAMETER_POSITION
+
+/* ParameterPosition
+
+ Represents on position in the player input holding a parameter. That
+ position is filled with some words from the player, those words must
+ be disambiguated to one or more instances. There are three cases:
+
+ 1) words presuming it would be a single instance (it
+ might actually not be) "the chair"
+
+ 2) words indicating explicit mentioning of multiple instances, "the
+ book and the chair"
+
+ 3) implicit multiple using "all" or "everything except the blue
+ ball"
+
+ For all those cases the position must be able to deliver the words,
+ possible explicit or implicit multiple, and the resulting set of
+ instances.
+*/
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/params.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* Types: */
+struct ParameterPosition {
+ bool endOfList;
+ bool explicitMultiple;
+ bool all;
+ bool them;
+ bool checked;
+ Aword flags;
+ Parameter *parameters;
+ Parameter *exceptions;
+};
+
+/* Functions: */
+extern void deallocateParameterPositions(ParameterPosition *parameterPositions);
+extern void uncheckAllParameterPositions(ParameterPosition parameterPositions[]);
+extern void copyParameterPositions(ParameterPosition originalParameterPositions[], ParameterPosition parameterPositions[]);
+extern bool equalParameterPositions(ParameterPosition parameterPositions1[], ParameterPosition parameterPositions2[]);
+extern int findMultipleParameterPosition(ParameterPosition parameterPositions[]);
+extern void markExplicitMultiple(ParameterPosition parameterPositions[], Parameter parameters[]);
+extern void convertPositionsToParameters(ParameterPosition parameterPositions[], Parameter parameters[]);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/params.cpp b/engines/glk/alan3/params.cpp
new file mode 100644
index 0000000000..3cbf05dd27
--- /dev/null
+++ b/engines/glk/alan3/params.cpp
@@ -0,0 +1,333 @@
+/* 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/alan3/params.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/literal.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/syserr.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+Parameter *globalParameters = NULL;
+
+/*======================================================================*/
+Parameter *newParameter(int id) {
+ Parameter *parameter = NEW(Parameter);
+ parameter->instance = id;
+ parameter->candidates = NULL;
+
+ return parameter;
+}
+
+
+/*======================================================================*/
+Parameter *newParameterArray(void) {
+ Parameter *newArray = (Parameter *)allocate((MAXINSTANCE+1)*sizeof(Parameter));
+ setEndOfArray(newArray);
+ return newArray;
+}
+
+
+/*======================================================================*/
+void freeParameterArray(ParameterArray arrayPointer) {
+ Parameter *p;
+
+ for (p = arrayPointer; !isEndOfArray(p); p++)
+ if (p->candidates != NULL)
+ freeParameterArray(p->candidates);
+ deallocate(arrayPointer);
+}
+
+
+/*======================================================================*/
+Parameter *ensureParameterArrayAllocated(ParameterArray currentArray) {
+ if (currentArray == NULL)
+ return newParameterArray();
+ else {
+ clearParameterArray(currentArray);
+ return currentArray;
+ }
+}
+
+
+/*======================================================================*/
+bool parameterArrayIsEmpty(ParameterArray array) {
+ return array == NULL || lengthOfParameterArray(array) == 0;
+}
+
+
+/*======================================================================*/
+void clearParameter(Parameter *parameter) {
+ Parameter *candidates = parameter->candidates;
+ memset(parameter, 0, sizeof(Parameter));
+ parameter->candidates = candidates;
+ if (parameter->candidates != NULL)
+ clearParameterArray(parameter->candidates);
+}
+
+
+/*======================================================================*/
+void setGlobalParameters(ParameterArray newParameters) {
+ if (globalParameters == NULL)
+ globalParameters = newParameterArray();
+ copyParameterArray(globalParameters, newParameters);
+}
+
+
+/*======================================================================*/
+Parameter *getGlobalParameters(void) {
+ if (globalParameters == NULL)
+ globalParameters = newParameterArray();
+ return globalParameters;
+}
+
+
+/*======================================================================*/
+Parameter *getGlobalParameter(int parameterIndex) {
+ return &globalParameters[parameterIndex];
+}
+
+
+/*======================================================================*/
+Parameter *findEndOfParameterArray(Parameter *parameters) {
+ Parameter *parameter;
+ for (parameter = parameters; !isEndOfArray(parameter); parameter++);
+ return parameter;
+}
+
+
+/*======================================================================*/
+/* A parameter position with code == 0 means this is a multiple position.
+ * We must loop over this position (and replace it by each present in the
+ * matched list)
+ */
+int findMultiplePosition(Parameter parameters[]) {
+ // TODO: this should look at the isAll and isExplicitMultiple flags instead
+ int multiplePosition;
+ for (multiplePosition = 0; !isEndOfArray(&parameters[multiplePosition]); multiplePosition++)
+ if (parameters[multiplePosition].instance == 0)
+ return multiplePosition;
+ return -1;
+}
+
+
+/*======================================================================*/
+void compressParameterArray(Parameter theArray[])
+{
+ int i, j;
+
+ for (i = 0, j = 0; !isEndOfArray(&theArray[j]); j++)
+ if (theArray[j].instance != 0)
+ theArray[i++] = theArray[j];
+ setEndOfArray(&theArray[i]);
+}
+
+
+/*======================================================================*/
+int lengthOfParameterArray(Parameter theArray[])
+{
+ int i = 0;
+
+ if (theArray == NULL) return 0;
+
+ while (!isEndOfArray(&theArray[i]))
+ i++;
+ return i;
+}
+
+
+/*======================================================================*/
+bool equalParameterArrays(Parameter parameters1[], Parameter parameters2[])
+{
+ int i;
+
+ if ((parameters1 == NULL) != (parameters2 == NULL))
+ return FALSE;
+ if (parameters1 == NULL) // Because then parameter2 is also NULL
+ return TRUE;
+ for (i = 0; !isEndOfArray(&parameters1[i]); i++) {
+ if (isEndOfArray(&parameters2[i])) return FALSE;
+ if (parameters1[i].instance != parameters2[i].instance) return FALSE;
+ }
+ return isEndOfArray(&parameters2[i]);
+}
+
+
+/*======================================================================*/
+bool inParameterArray(Parameter theArray[], Aword theCode)
+{
+ int i;
+
+ for (i = 0; !isEndOfArray(&theArray[i]) && theArray[i].instance != theCode; i++);
+ return (theArray[i].instance == theCode);
+}
+
+
+/*======================================================================*/
+void copyParameter(Parameter *to, Parameter *from) {
+ Parameter *toCandidates = to->candidates;
+
+ *to = *from;
+ if (from->candidates != NULL) {
+ if (toCandidates == NULL)
+ to->candidates = newParameterArray();
+ else
+ to->candidates = toCandidates;
+ copyParameterArray(to->candidates, from->candidates);
+ } else if (toCandidates != NULL)
+ freeParameterArray(toCandidates);
+}
+
+
+/*======================================================================*/
+void addParameterToParameterArray(ParameterArray theArray, Parameter *theParameter)
+{
+ if (theArray == NULL) syserr("Adding to null parameter array");
+
+ int i;
+
+ for (i = 0; !isEndOfArray(&theArray[i]) && i < MAXINSTANCE; i++)
+ ;
+ if (isEndOfArray(&theArray[i])) {
+ copyParameter(&theArray[i], theParameter);
+ setEndOfArray(&theArray[i+1]);
+ } else
+ syserr("Couldn't find end of ParameterArray");
+}
+
+
+/*======================================================================*/
+void copyParameterArray(ParameterArray to, ParameterArray from)
+{
+ int i;
+
+ if (to == NULL && from == NULL) return;
+
+ if (to == NULL)
+ syserr("Copying to null parameter array");
+ else {
+ clearParameterArray(to);
+ for (i = 0; !isEndOfArray(&from[i]); i++)
+ addParameterToParameterArray(to, &from[i]);
+ }
+}
+
+
+/*======================================================================*/
+void subtractParameterArrays(Parameter theArray[], Parameter remove[])
+{
+ int i;
+
+ if (remove == NULL) return;
+
+ for (i = 0; !isEndOfArray(&theArray[i]); i++)
+ if (inParameterArray(remove, theArray[i].instance))
+ theArray[i].instance = 0; /* Mark empty */
+ compressParameterArray(theArray);
+}
+
+
+/*======================================================================*/
+void clearParameterArray(Parameter theArray[]) {
+ Parameter *p = &theArray[0];
+
+ for (p = &theArray[0]; !isEndOfArray(p); p++)
+ clearParameter(p);
+ setEndOfArray(theArray);
+}
+
+
+/*======================================================================*/
+void intersectParameterArrays(Parameter one[], Parameter other[])
+{
+ int i, last = 0;
+
+
+ for (i = 0; !isEndOfArray(&one[i]); i++)
+ if (inParameterArray(other, one[i].instance))
+ one[last++] = one[i];
+ setEndOfArray(&one[last]);
+}
+
+
+/*======================================================================*/
+void copyReferencesToParameterArray(Aint references[], Parameter parameterArray[])
+{
+ int i;
+
+ for (i = 0; !isEndOfArray(&references[i]); i++) {
+ parameterArray[i].instance = references[i];
+ parameterArray[i].firstWord = EOF; /* Ensure that there is no word that can be used */
+ }
+ setEndOfArray(&parameterArray[i]);
+}
+
+
+/*======================================================================*/
+void addParameterForInstance(Parameter *parameters, int instance) {
+ Parameter *parameter = findEndOfParameterArray(parameters);
+
+ parameter->instance = instance;
+ parameter->useWords = FALSE;
+
+ setEndOfArray(parameter+1);
+}
+
+
+/*======================================================================*/
+void addParameterForInteger(ParameterArray parameters, int value) {
+ Parameter *parameter = findEndOfParameterArray(parameters);
+
+ createIntegerLiteral(value);
+ parameter->instance = instanceFromLiteral(litCount);
+ parameter->useWords = FALSE;
+
+ setEndOfArray(parameter+1);
+}
+
+/*======================================================================*/
+void addParameterForString(Parameter *parameters, char *value) {
+ Parameter *parameter = findEndOfParameterArray(parameters);
+
+ createStringLiteral(value);
+ parameter->instance = instanceFromLiteral(litCount);
+ parameter->useWords = FALSE;
+
+ setEndOfArray(parameter+1);
+}
+
+/*======================================================================*/
+void printParameterArray(Parameter parameters[]) {
+ int i;
+ printf("[");
+ for (i = 0; !isEndOfArray(&parameters[i]); i++) {
+ printf("%d ", (int)parameters[i].instance);
+ }
+ printf("]\n");
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/params.h b/engines/glk/alan3/params.h
new file mode 100644
index 0000000000..4b266db16e
--- /dev/null
+++ b/engines/glk/alan3/params.h
@@ -0,0 +1,91 @@
+/* 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_ALAN3_PARAMS
+#define GLK_ALAN3_PARAMS
+
+/* Various utility functions for handling parameters */
+#include "glk/alan3/types.h"
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* TYPES */
+struct Parameter { /* PARAMETER */
+ Aid instance; /* Instance code for the parameter (0=multiple) */
+ bool isLiteral;
+ bool isPronoun;
+ bool isThem;
+ bool useWords; /* Indicate to use words instead of instance code when saying */
+ int firstWord; /* Index to first word used by player */
+ int lastWord; /* d:o to last */
+ struct Parameter *candidates; /* Array of instances possibly matching this parameter depending on player input */
+};
+
+typedef Parameter *ParameterArray;
+
+
+/* DATA */
+extern Parameter *globalParameters;
+
+
+/* FUNCTIONS */
+/* Single Parameter: */
+extern Parameter *newParameter(int instanceId);
+extern void clearParameter(Parameter *parameter);
+extern void copyParameter(Parameter *theCopy, Parameter *theOriginal);
+
+/* ParameterArray: */
+extern ParameterArray newParameterArray(void);
+extern ParameterArray ensureParameterArrayAllocated(ParameterArray currentArray);
+extern void freeParameterArray(Parameter *array);
+
+extern bool parameterArrayIsEmpty(ParameterArray parameters);
+extern void addParameterToParameterArray(ParameterArray theArray, Parameter *theParameter);
+extern void addParameterForInstance(ParameterArray parameters, int instance);
+extern void addParameterForInteger(ParameterArray parameters, int value);
+extern void addParameterForString(ParameterArray parameters, char *value);
+extern Parameter *findEndOfParameterArray(ParameterArray parameters);
+extern void compressParameterArray(ParameterArray a);
+extern int lengthOfParameterArray(ParameterArray a);
+extern bool equalParameterArrays(ParameterArray parameters1, ParameterArray parameters2);
+extern bool inParameterArray(ParameterArray l, Aword e);
+extern void copyParameterArray(ParameterArray to, ParameterArray from);
+extern void clearParameterArray(ParameterArray list);
+extern void subtractParameterArrays(ParameterArray a, ParameterArray b);
+extern void mergeParameterArrays(ParameterArray a, ParameterArray b);
+extern void intersectParameterArrays(ParameterArray a, ParameterArray b);
+extern void copyReferencesToParameterArray(Aint *references, ParameterArray parameters);
+extern void printParameterArray(ParameterArray parameters);
+
+extern int findMultiplePosition(ParameterArray parameters);
+
+/* Global Parameters: */
+extern void setGlobalParameters(ParameterArray parameters);
+extern ParameterArray getGlobalParameters(void);
+extern ParameterArray getGlobalParameter(int parameterIndex);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/parse.cpp b/engines/glk/alan3/parse.cpp
new file mode 100644
index 0000000000..fe42bc9ae0
--- /dev/null
+++ b/engines/glk/alan3/parse.cpp
@@ -0,0 +1,1465 @@
+/* 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/array.h"
+#include "glk/alan3/parse.h"
+#include "glk/alan3/act.h"
+#include "glk/alan3/alt_info.h"
+#include "glk/alan3/class.h"
+#include "glk/alan3/compatibility.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/dictionary.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/literal.h"
+#include "glk/alan3/location.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/parameter_position.h"
+#include "glk/alan3/syntax.h"
+#include "glk/alan3/scan.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/utils.h"
+#include "glk/alan3/word.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PRIVATE TYPES */
+/* To remember parameter/pronoun relations */
+struct Pronoun {
+ int pronoun;
+ int instance;
+};
+
+/*----------------------------------------------------------------------*/
+static void clearPronounList(Pronoun list[]) {
+ implementationOfSetEndOfArray((Aword *)list);
+}
+
+
+typedef Aint *(*ReferencesFinder)(int wordIndex);
+typedef void (*ParameterParser)(Parameter parameters[]);
+
+
+
+/* PRIVATE DATA */
+static Pronoun *pronouns = NULL;
+
+
+/* Syntax Parameters */
+static Parameter *previousMultipleParameters; /* Previous multiple list */
+
+
+/* For parameters that are literals we need to trick message handling to
+ * output the word and create a string literal instance if anyone wants to
+ * refer to an attribute of it (literals inherit from entity so application
+ * can have added an attribute) */
+
+/*----------------------------------------------------------------------*/
+static void addParameterForWord(Parameter *parameters, int wordIndex) {
+ Parameter *parameter = findEndOfParameterArray(parameters);
+
+ createStringLiteral((char *)pointerTo(dictionary[playerWords[wordIndex].code].string));
+ parameter->instance = instanceFromLiteral(litCount); /* A faked literal */
+ parameter->useWords = TRUE;
+ parameter->firstWord = parameter->lastWord = wordIndex;
+ setEndOfArray(parameter+1);
+}
+
+
+/*----------------------------------------------------------------------*/
+static Pronoun *allocatePronounArray(Pronoun *currentList) {
+ if (currentList == NULL)
+ currentList = (Pronoun *)allocate(sizeof(Pronoun)*(MAXPARAMS+1));
+ clearPronounList(currentList);
+ return currentList;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool endOfWords(int wordIndex) {
+ return isEndOfArray(&playerWords[wordIndex]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleDirectionalCommand() {
+ currentWordIndex++;
+ if (!endOfWords(currentWordIndex) && !isConjunctionWord(currentWordIndex))
+ error(M_WHAT);
+ else
+ go(current.location, dictionary[playerWords[currentWordIndex-1].code].code);
+ if (!endOfWords(currentWordIndex))
+ currentWordIndex++;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void errorWhichOne(Parameter alternative[]) {
+ int p; /* Index into the list of alternatives */
+ ParameterArray parameters = newParameterArray();
+
+ parameters[0] = alternative[0];
+ setEndOfArray(&parameters[1]);
+ printMessageWithParameters(M_WHICH_ONE_START, parameters);
+ for (p = 1; !isEndOfArray(&alternative[p+1]); p++) {
+ clearParameterArray(parameters);
+ addParameterToParameterArray(parameters, &alternative[p]);
+ printMessageWithParameters(M_WHICH_ONE_COMMA, parameters);
+ }
+ clearParameterArray(parameters);
+ addParameterToParameterArray(parameters, &alternative[p]);
+ printMessageWithParameters(M_WHICH_ONE_OR, parameters);
+ freeParameterArray(parameters);
+ abortPlayerCommand(); /* Return with empty error message */
+}
+
+/*----------------------------------------------------------------------*/
+static void errorWhichPronoun(int pronounWordIndex, Parameter alternatives[]) {
+ int p; /* Index into the list of alternatives */
+ Parameter *messageParameters = newParameterArray();
+
+ addParameterForWord(messageParameters, pronounWordIndex);
+ printMessageWithParameters(M_WHICH_PRONOUN_START, messageParameters);
+
+ clearParameterArray(messageParameters);
+ addParameterToParameterArray(messageParameters, &alternatives[0]);
+
+ printMessageWithParameters(M_WHICH_PRONOUN_FIRST, messageParameters);
+
+ for (p = 1; !isEndOfArray(&alternatives[p+1]); p++) {
+ clearParameterArray(messageParameters);
+ addParameterToParameterArray(messageParameters, &alternatives[p]);
+ printMessageWithParameters(M_WHICH_ONE_COMMA, messageParameters);
+ }
+ clearParameterArray(messageParameters);
+ addParameterToParameterArray(messageParameters, &alternatives[p]);
+ printMessageWithParameters(M_WHICH_ONE_OR, messageParameters);
+ freeParameterArray(messageParameters);
+ abortPlayerCommand();
+}
+
+/*----------------------------------------------------------------------*/
+static void errorWhat(int playerWordIndex) {
+ Parameter *messageParameters = newParameterArray();
+
+ addParameterForWord(messageParameters, playerWordIndex);
+ printMessageWithParameters(M_WHAT_WORD, messageParameters);
+ freeParameterArray(messageParameters);
+ abortPlayerCommand();
+}
+
+/*----------------------------------------------------------------------*/
+static void errorAfterExcept(int butWordIndex) {
+ Parameter *messageParameters = newParameterArray();
+ addParameterForWord(messageParameters, butWordIndex);
+ printMessageWithParameters(M_AFTER_BUT, messageParameters);
+ freeParameterArray(messageParameters);
+ abortPlayerCommand();
+}
+
+/*----------------------------------------------------------------------*/
+static int fakePlayerWordForAll() {
+ /* Look through the dictionary and find any ALL_WORD, then add a
+ player word so that it can be used in the message */
+ int p, d;
+
+ for (p = 0; !isEndOfArray(&playerWords[p]); p++)
+ ;
+ setEndOfArray(&playerWords[p+1]); /* Make room for one more word */
+ for (d = 0; d < dictionarySize; d++)
+ if (isAll(d)) {
+ playerWords[p].code = d;
+ return p;
+ }
+ syserr("No ALLWORD found");
+ return 0;
+}
+
+/*----------------------------------------------------------------------*/
+static void errorButAfterAll(int butWordIndex) {
+ Parameter *messageParameters = newParameterArray();
+ addParameterForWord(messageParameters, butWordIndex);
+ addParameterForWord(messageParameters, fakePlayerWordForAll());
+ printMessageWithParameters(M_BUT_ALL, messageParameters);
+ freeParameterArray(messageParameters);
+ abortPlayerCommand();
+}
+
+/*----------------------------------------------------------------------*/
+static Aint findInstanceForNoun(int wordIndex) {
+ DictionaryEntry *d = &dictionary[wordIndex];
+ if (d->nounRefs == 0 || d->nounRefs == EOF)
+ syserr("No references for noun");
+ return *(Aint*) pointerTo(d->nounRefs);
+}
+
+/*----------------------------------------------------------------------*/
+static void errorNoSuch(Parameter parameter) {
+
+ /* If there was no instance, assume the last word used is the noun,
+ * then find any instance with the noun he used */
+ if (parameter.instance == -1)
+ parameter.instance = 0;
+ if (parameter.instance == 0)
+ parameter.instance = findInstanceForNoun(playerWords[parameter.lastWord].code);
+ parameter.useWords = TRUE; /* Indicate to use words and not names */
+
+ clearParameterArray(globalParameters);
+ addParameterToParameterArray(globalParameters, &parameter);
+ error(M_NO_SUCH);
+}
+
+/*----------------------------------------------------------------------*/
+static void buildAllHere(Parameter list[]) {
+ uint instance;
+ bool found = FALSE;
+ int word = list[0].firstWord;
+
+ for (instance = 1; instance <= header->instanceMax; instance++)
+ if (isHere(instance, /*FALSE*/ TRANSITIVE)) {
+ Parameter *parameter = newParameter(instance);
+ addParameterToParameterArray(list, parameter);
+ deallocate(parameter);
+ found = TRUE;
+ }
+ if (!found)
+ errorWhat(word);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool endOfPronouns(int pronounIndex) {
+ return isEndOfArray(&pronouns[pronounIndex]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int getPronounInstances(int word, Parameter instanceParameters[]) {
+ /* Find the instance that the pronoun word could refer to, return 0
+ if none or multiple */
+ int p;
+ int instanceCount = 0;
+
+ clearParameterArray(instanceParameters);
+ for (p = 0; !endOfPronouns(p); p++)
+ if (pronouns[p].instance != 0 && dictionary[word].code == (Aword)pronouns[p].pronoun) {
+ instanceParameters[instanceCount].instance = pronouns[p].instance;
+ instanceParameters[instanceCount].useWords = FALSE; /* Can't use words since they are gone, pronouns
+ refer to parameters in previous command */
+ setEndOfArray(&instanceParameters[++instanceCount]);
+ }
+ return instanceCount;
+}
+
+/*----------------------------------------------------------------------*/
+static bool inOpaqueContainer(int originalInstance) {
+ int instance = admin[originalInstance].location;
+
+ while (isAContainer(instance)) {
+ // TODO : isOpaque()
+ if (getInstanceAttribute(instance, OPAQUEATTRIBUTE))
+ return TRUE;
+ instance = admin[instance].location;
+ }
+ return FALSE;
+}
+
+/*----------------------------------------------------------------------*/
+static bool reachable(int instance) {
+ if (isA(instance, THING) || isA(instance, LOCATION))
+ return isHere(instance, TRANSITIVE) && !inOpaqueContainer(instance);
+ else
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------*/
+static Aint *nounReferencesForWord(int wordIndex) {
+ return (Aint *) pointerTo(dictionary[playerWords[wordIndex].code].nounRefs);
+}
+
+
+/*----------------------------------------------------------------------*/
+static Aint *adjectiveReferencesForWord(int wordIndex) {
+ return (Aint *) pointerTo(dictionary[playerWords[wordIndex].code].adjectiveRefs);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void parseLiteral(Parameter parameters[]) {
+ parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++;
+ parameters[0].instance = 0;
+ parameters[0].isLiteral = TRUE;
+ setEndOfArray(&parameters[1]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void parsePronoun(Parameter parameters[]) {
+ parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++;
+ parameters[0].instance = 0;
+ parameters[0].isPronoun = TRUE;
+ setEndOfArray(&parameters[1]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool anotherAdjective(int wordIndex) {
+ return !endOfWords(wordIndex) && isAdjectiveWord(wordIndex);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool lastPossibleNoun(int wordIndex) {
+ return isNounWord(wordIndex) && (endOfWords(wordIndex+1) || !isNounWord(wordIndex+1));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void updateWithReferences(Parameter result[], int wordIndex, Aint *(*referenceFinder)(int wordIndex)) {
+ static Parameter *references = NULL; /* Instances referenced by a word */
+ references = ensureParameterArrayAllocated(references);
+
+ copyReferencesToParameterArray(referenceFinder(wordIndex), references);
+ if (lengthOfParameterArray(result) == 0)
+ copyParameterArray(result, references);
+ else
+ intersectParameterArrays(result, references);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void filterOutNonReachable(Parameter filteredCandidates[], bool (*reachable)(int)) {
+ int i;
+ for (i = 0; !isEndOfArray(&filteredCandidates[i]); i++)
+ if (!reachable(filteredCandidates[i].instance))
+ filteredCandidates[i].instance = 0;
+ compressParameterArray(filteredCandidates);
+}
+
+
+/*
+ * There are various ways the player can refer to things, some are
+ * explicit, in which case they should be kept in the input. If he said
+ * 'them', 'all' or some such the list is inferred so we must filter
+ * it w.r.t what it can mean. A special case is when he said 'the ball'
+ * and there is only one ball here, but multiple in the game. We need
+ * to be able to distinguish between these cases!!! 'them' is
+ * explicit, 'all' is inferred, exceptions can never be inferred,
+ * maybe 'all' is the only inferred?
+ */
+
+/*----------------------------------------------------------------------*/
+static void disambiguateCandidatesForPosition(ParameterPosition parameterPositions[], int position, Parameter candidates[]) {
+ int i;
+ Parameter *parameters = newParameterArray();
+
+ convertPositionsToParameters(parameterPositions, parameters);
+ for (i = 0; !isEndOfArray(&candidates[i]); i++) {
+ if (candidates[i].instance != 0) { /* Already empty? */
+ copyParameter(&parameters[position], &candidates[i]);
+ // DISAMBIGUATION!!
+ if (!reachable(candidates[i].instance) || !possible(current.verb, parameters, parameterPositions))
+ candidates[i].instance = 0; /* Then remove this candidate from list */
+ }
+ }
+ compressParameterArray(candidates);
+ freeParameterArray(parameters);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool parseAnyAdjectives(Parameter parameters[]) {
+ bool adjectiveOrNounFound = FALSE;
+ while (anotherAdjective(currentWordIndex)) {
+ if (lastPossibleNoun(currentWordIndex))
+ break;
+ adjectiveOrNounFound = TRUE;
+ currentWordIndex++;
+ }
+ return adjectiveOrNounFound;
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* Parse the input and note the word indices in the parameters,
+ matching will be done by the match* functions */
+static void parseAdjectivesAndNoun(Parameter parameters[]) {
+ int wordFirst, wordLast;
+ bool adjectiveOrNounFound = FALSE;
+
+ wordFirst = currentWordIndex;
+
+ adjectiveOrNounFound = parseAnyAdjectives(parameters);
+
+ if (!endOfWords(currentWordIndex)) {
+ if (isNounWord(currentWordIndex)) {
+ adjectiveOrNounFound = TRUE;
+ currentWordIndex++;
+ } else
+ error(M_NOUN);
+ } else if (adjectiveOrNounFound) {
+ /* Perhaps the last word could also be interpreted as a noun? */
+ if (isNounWord(currentWordIndex - 1)) {
+ // TODO When does this get executed? Maybe if conjunctions can be nouns? Or nouns be adjectives?
+ printf("DEBUG: When does this get executed? Found adjective or Noun and the previous word could also be a noun...\n");
+ } else
+ error(M_NOUN);
+ }
+
+ if (adjectiveOrNounFound) {
+ wordLast = currentWordIndex - 1;
+
+ parameters[0].firstWord = wordFirst;
+ parameters[0].lastWord = wordLast;
+ parameters[0].instance = 0; /* No instance yet! */
+ setEndOfArray(&parameters[1]);
+ } else
+ setEndOfArray(&parameters[0]);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static void parseReference(Parameter parameters[]) {
+ clearParameterArray(parameters);
+
+ if (isLiteralWord(currentWordIndex)) {
+ parseLiteral(parameters);
+ } else if (isPronounWord(currentWordIndex)) {
+ parsePronoun(parameters);
+ } else {
+ parseAdjectivesAndNoun(parameters);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void getPreviousMultipleParameters(Parameter parameters[]) {
+ int i;
+ for (i = 0; !isEndOfArray(&previousMultipleParameters[i]); i++) {
+ parameters[i].candidates = ensureParameterArrayAllocated(parameters[i].candidates);
+ setEndOfArray(&parameters[i].candidates[0]); /* No candidates */
+ if (!reachable(previousMultipleParameters[i].instance))
+ parameters[i].instance = 0;
+ else
+ parameters[i].instance = previousMultipleParameters[i].instance;
+ }
+ setEndOfArray(&parameters[i]);
+ compressParameterArray(parameters);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void parseReferenceToPreviousMultipleParameters(Parameter parameters[]) {
+ parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++;
+ parameters[0].instance = 0;
+ parameters[0].isThem = TRUE;
+ setEndOfArray(&parameters[1]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool parseOneParameter(Parameter parameters[], int parameterIndex) {
+ Parameter *parameter = newParameterArray();
+
+ // TODO Maybe this should go in the complex()?
+ if (isThemWord(currentWordIndex) && (!isPronounWord(currentWordIndex) ||
+ (isPronounWord(currentWordIndex) && lengthOfParameterArray(previousMultipleParameters) > 0))) {
+ // "them" is also a common pronoun for some instances, but if there
+ // are previous multiple parameters we give precedence to those
+ parseReferenceToPreviousMultipleParameters(parameter);
+ } else {
+ parseReference(parameter);
+ if (lengthOfParameterArray(parameter) == 0) { /* Failed to find any exceptions! */
+ freeParameterArray(parameter);
+ return FALSE;
+ }
+ }
+
+ /* Add the one we found to the parameters */
+ parameters[parameterIndex] = parameter[0];
+ setEndOfArray(&parameters[parameterIndex+1]);
+ freeParameterArray(parameter);
+ return TRUE;
+}
+
+
+/*
+ * A "simple" parameter is in one of three forms:
+ *
+ * 1) adjectives and nouns referencing a single instance (might
+ * actually match multiple instances in the game...)
+ *
+ * 2) multiple of 1) separated by conjunctions ("a and b and c")
+ *
+ * 3) a pronoun referencing a single instance
+
+ * We also need to handle "them" here since it is also a common
+ * pronoun for some instances, e.g. scissor, trouser, money,
+ * glasses, but it can also refer to the set of instances from the
+ * previous command. If it is a pronoun but there are also multiple
+ * parameter from the previous command, we give precedence to the
+ * multiple previous.
+ */
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static void simpleParameterParser(Parameter parameters[]) {
+
+ /* This will loop until all references are collected (typically "a and b and c") */
+ int parameterIndex;
+ for (parameterIndex = 0;; parameterIndex++) {
+
+ if(!parseOneParameter(parameters, parameterIndex))
+ return;
+
+ if (!endOfWords(currentWordIndex)
+ && (isConjunctionWord(currentWordIndex) && (isAdjectiveWord(currentWordIndex+1)
+ || isNounWord(currentWordIndex+1)))) {
+ /* Since this is a conjunction and the next seems to be another instance reference,
+ let's continue with that by eating the conjunction */
+ currentWordIndex++;
+ } else {
+ return;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void parseExceptions(ParameterPosition *parameterPosition, ParameterParser simpleParameterParser) {
+ int exceptWordIndex = currentWordIndex;
+ currentWordIndex++;
+ parameterPosition->exceptions = ensureParameterArrayAllocated(parameterPosition->exceptions);
+ simpleParameterParser(parameterPosition->exceptions);
+ if (lengthOfParameterArray(parameterPosition->exceptions) == 0)
+ errorAfterExcept(exceptWordIndex);
+}
+
+
+/*
+ * Complex instance references are of the form:
+ *
+ * 1) all
+ *
+ * 2) all except
+ *
+ * 3) a simple (see above) instance reference(s)
+ */
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static void complexParameterParserDelegate(ParameterPosition *parameterPosition, ParameterParser simpleParameterParser) {
+ parameterPosition->parameters = ensureParameterArrayAllocated(parameterPosition->parameters);
+
+ parameterPosition->all = FALSE;
+ parameterPosition->them = FALSE;
+ parameterPosition->explicitMultiple = FALSE;
+
+ if (isAllWord(currentWordIndex)) {
+ parameterPosition->all = TRUE;
+ parameterPosition->explicitMultiple = TRUE;
+ parameterPosition->parameters[0].firstWord = currentWordIndex;
+ parameterPosition->parameters[0].lastWord = currentWordIndex;
+ currentWordIndex++;
+ parameterPosition->parameters[0].instance = 0;
+ setEndOfArray(&parameterPosition->parameters[1]);
+ if (!endOfWords(currentWordIndex) && isExceptWord(currentWordIndex))
+ parseExceptions(parameterPosition, simpleParameterParser);
+ } else {
+ simpleParameterParser(parameterPosition->parameters);
+ if (lengthOfParameterArray(parameterPosition->parameters) > 1)
+ parameterPosition->explicitMultiple = TRUE;
+ }
+
+}
+
+/*----------------------------------------------------------------------*/
+static void complexReferencesParser(ParameterPosition *parameterPosition) {
+ complexParameterParserDelegate(parameterPosition, simpleParameterParser);
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static char *classNameAndId(int classId) {
+ static char buffer[1000] = "";
+
+ if (classId != -1)
+ sprintf(buffer, "%s[%d]", idOfClass(classId), classId);
+ else
+ sprintf(buffer, "Container");
+ return buffer;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *parameterNumberAndName(int parameterNumber) {
+ static char buffer[1000] = "";
+ /* HERE SHOULD BE current.syntax */
+ char *parameterName = parameterNameInSyntax(current.syntax, parameterNumber);
+
+ if (parameterName != NULL)
+ sprintf(buffer, "%s(#%d)", parameterName, parameterNumber);
+ else
+ sprintf(buffer, "#%d", parameterNumber);
+ return buffer;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceRestriction(RestrictionEntry *restriction, int classId, bool condition) {
+ printf("\n<SYNTAX RESTRICTION WHERE parameter %s Isa %s, %s>\n",
+ parameterNumberAndName(restriction->parameterNumber),
+ classNameAndId(classId), condition?"PASSED":"FAILED:");
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool restrictionCheck(RestrictionEntry *restriction, int instance) {
+ if (restriction->_class == RESTRICTIONCLASS_CONTAINER) {
+ if (traceSectionOption)
+ traceRestriction(restriction, -1, isAContainer(instance));
+ return isAContainer(instance);
+ } else {
+ if (traceSectionOption)
+ traceRestriction(restriction, restriction->_class, isA(instance, restriction->_class));
+ return isA(instance, restriction->_class);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void runRestriction(RestrictionEntry *restriction, Parameter parameters[]) {
+ if (restriction->stms) {
+ setGlobalParameters(parameters);
+ interpret(restriction->stms);
+ } else
+ error(M_CANT0);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int remapParameterOrder(int syntaxNumber, ParameterPosition parameterPositions[]) {
+ /* Find the syntax map, use the verb code from it and remap the parameters */
+ ParameterMapEntry *parameterMapTable;
+ Aword *parameterMap;
+ Aint parameterNumber;
+ Common::Array<ParameterPosition> savedParameterPositions;
+ savedParameterPositions.resize(MAXPARAMS + 1);
+
+ for (parameterMapTable = (ParameterMapEntry *)pointerTo(header->parameterMapAddress); !isEndOfArray(parameterMapTable); parameterMapTable++)
+ if (parameterMapTable->syntaxNumber == syntaxNumber)
+ break;
+ if (isEndOfArray(parameterMapTable))
+ syserr("Could not find syntax in mapping table.");
+
+ parameterMap = (Aword *)pointerTo(parameterMapTable->parameterMapping);
+
+ copyParameterPositions(parameterPositions, &savedParameterPositions[0]);
+
+ for (parameterNumber = 1; !savedParameterPositions[parameterNumber-1].endOfList; parameterNumber++) {
+ parameterPositions[parameterNumber-1] = savedParameterPositions[parameterMap[parameterNumber-1]-1];
+ }
+
+ return parameterMapTable->verbCode;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool hasBit(Aword flags, Aint bit) {
+ return (flags & bit) != 0;
+}
+
+/*----------------------------------------------------------------------*/
+static bool multipleAllowed(Aword flags) {
+ return hasBit(flags, MULTIPLEBIT);
+}
+
+
+
+/*
+ * There are a number of ways that the number of parameters might
+ * be more than one:
+ *
+ * 1) Player used ALL and it matched more than one
+ *
+ * 2) Player explicitly refered to multiple objects
+ *
+ * 3) Player did a single (or multiple) reference that was ambiguous
+ * in which case we need to disambiguate it (them). If we want to do
+ * this after complete parsing we must be able to see the possible
+ * parameters for each of these references, e.g.:
+ *
+ * > take the vase and the book
+ *
+ * In this case it is a single parameterPosition, but multiple
+ * explicit references and each of those might match multiple
+ * instances in the game.
+ */
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static void parseParameterPosition(ParameterPosition *parameterPosition, Aword flags, void (*complexReferencesParser)(ParameterPosition *parameterPosition)) {
+ parameterPosition->parameters = ensureParameterArrayAllocated(parameterPosition->parameters);
+
+ complexReferencesParser(parameterPosition);
+ if (lengthOfParameterArray(parameterPosition->parameters) == 0) /* No object!? */
+ error(M_WHAT);
+
+ if (parameterPosition->explicitMultiple && !multipleAllowed(flags))
+ error(M_MULTIPLE);
+}
+
+/*----------------------------------------------------------------------*/
+static ElementEntry *elementForParameter(ElementEntry *elms) {
+ /* Require a parameter if elms->code == 0! */
+ while (!isEndOfArray(elms) && elms->code != 0)
+ elms++;
+ if (isEndOfArray(elms))
+ return NULL;
+ return elms;
+}
+
+/*----------------------------------------------------------------------*/
+static ElementEntry *elementForEndOfSyntax(ElementEntry *elms) {
+ while (!isEndOfArray(elms) && elms->code != EOS)
+ elms++;
+ if (isEndOfArray(elms)) /* No match for EOS! */
+ return NULL;
+ return elms;
+}
+
+/*----------------------------------------------------------------------*/
+static ElementEntry *elementForWord(ElementEntry *elms, Aint wordCode) {
+ while (!isEndOfArray(elms) && elms->code != wordCode)
+ elms++;
+ if (isEndOfArray(elms))
+ return NULL;
+ return elms;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool isInstanceReferenceWord(int wordIndex) {
+ return isNounWord(wordIndex) || isAdjectiveWord(wordIndex) || isAllWord(wordIndex)
+ || isLiteralWord(wordIndex) || isItWord(wordIndex) || isThemWord(wordIndex) || isPronounWord(wordIndex);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool endOfPlayerCommand(int wordIndex) {
+ return endOfWords(wordIndex) || isConjunctionWord(wordIndex);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static ElementEntry *parseInputAccordingToSyntax(SyntaxEntry *syntax, ParameterPosition parameterPositions[]) {
+ ElementEntry *currentElement = elementTreeOf(syntax);
+ ElementEntry *nextElement = currentElement;
+
+ int parameterCount = 0;
+ while (nextElement != NULL) {
+ /* Traverse the possible branches of currentElement to find a match, let the actual input control what we look for */
+ parameterPositions[parameterCount].endOfList = TRUE;
+
+ if (endOfPlayerCommand(currentWordIndex)) {
+ // TODO If a conjunction word is also some other type of word, like noun? What happens?
+ return elementForEndOfSyntax(currentElement);
+ }
+
+ /* Or an instance reference ? */
+ if (isInstanceReferenceWord(currentWordIndex)) {
+ /* If so, save word info for this parameterPosition */
+ nextElement = elementForParameter(currentElement);
+ if (nextElement != NULL) {
+ // Create parameter structure for the parameter position based on player words
+ // but without resolving them
+ ParameterPosition *parameterPosition = &parameterPositions[parameterCount];
+ parseParameterPosition(parameterPosition, nextElement->flags, complexReferencesParser);
+ parameterPosition->flags = nextElement->flags;
+ parameterPosition->endOfList = FALSE;
+
+ currentElement = (ElementEntry *) pointerTo(nextElement->next);
+ parameterCount++;
+ continue;
+ }
+ }
+
+ /* Or maybe preposition? */
+ if (isPrepositionWord(currentWordIndex) || isVerbWord(currentWordIndex)) {
+ /* A preposition? Or rather, an intermediate word? */
+ nextElement = elementForWord(currentElement, dictionary[playerWords[currentWordIndex].code].code);
+ if (nextElement != NULL) {
+ currentWordIndex++; /* Word matched, go to next */
+ currentElement = (ElementEntry *) pointerTo(nextElement->next);
+ continue;
+ }
+ }
+
+ /* Anything else is an error, but we'll handle 'but' specially here */
+ if (isExceptWord(currentWordIndex))
+ errorButAfterAll(currentWordIndex);
+
+ /* If we get here we couldn't match anything... */
+ nextElement = NULL;
+ }
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool anyExplicitMultiple(ParameterPosition parameterPositions[]) {
+ int i;
+
+ for (i = 0; !parameterPositions[i].endOfList; i++)
+ if (parameterPositions[i].explicitMultiple)
+ return TRUE;
+ return FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool anyAll(ParameterPosition parameterPositions[]) {
+ int i;
+
+ for (i = 0; !parameterPositions[i].endOfList; i++)
+ if (parameterPositions[i].all)
+ return TRUE;
+ return FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void checkRestrictedParameters(ParameterPosition parameterPositions[], ElementEntry elms[]) {
+ RestrictionEntry *restriction;
+ static Parameter *localParameters = NULL;
+ int i;
+
+ localParameters = ensureParameterArrayAllocated(localParameters);
+ clearParameterArray(localParameters);
+
+ for (i=0; !parameterPositions[i].endOfList; i++)
+ addParameterToParameterArray(localParameters, &parameterPositions[i].parameters[0]);
+
+ for (restriction = (RestrictionEntry *) pointerTo(elms->next); !isEndOfArray(restriction); restriction++) {
+ ParameterPosition *parameterPosition = &parameterPositions[restriction->parameterNumber-1];
+ if (parameterPosition->explicitMultiple) {
+ /* This was a multiple parameter position, so check all multipleCandidates */
+ for (i = 0; !isEndOfArray(&parameterPosition->parameters[i]); i++) {
+ copyParameter(&localParameters[restriction->parameterNumber-1], &parameterPosition->parameters[i]);
+ if (!restrictionCheck(restriction, parameterPosition->parameters[i].instance)) {
+ /* Multiple could be both an explicit list of instance references and an expansion of ALL */
+ if (!parameterPosition->all) {
+ 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) restriction->parameterNumber);
+ setGlobalParameters(localParameters);
+ output(marker);
+ runRestriction(restriction, localParameters);
+ para();
+ }
+ parameterPosition->parameters[i].instance = 0; /* In any case remove it from the list */
+ }
+ }
+ } else {
+ if (!restrictionCheck(restriction, parameterPosition->parameters[0].instance)) {
+ runRestriction(restriction, localParameters);
+ abortPlayerCommand();
+ }
+ }
+ parameterPositions[restriction->parameterNumber - 1].checked = TRUE;
+ }
+ freeParameterArray(localParameters);
+ localParameters = NULL;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void impossibleWith(ParameterPosition parameterPositions[], int positionIndex) {
+ if (isPreBeta2(header->version)) {
+ error(M_CANT0);
+ } else {
+ printMessageWithInstanceParameter(M_IMPOSSIBLE_WITH, parameterPositions[positionIndex].parameters[0].instance);
+ error(NO_MSG);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static void checkNonRestrictedParameters(ParameterPosition parameterPositions[]) {
+ int positionIndex;
+ for (positionIndex = 0; !parameterPositions[positionIndex].endOfList; positionIndex++)
+ if (!parameterPositions[positionIndex].checked) {
+ if (parameterPositions[positionIndex].explicitMultiple) {
+ /* This was a multiple parameter position, check all multiple candidates and remove failing */
+ int i;
+ for (i = 0; !isEndOfArray(&parameterPositions[positionIndex].parameters[i]); i++)
+ if (parameterPositions[positionIndex].parameters[i].instance != 0) /* Skip any empty slots */
+ if (!isAObject(parameterPositions[positionIndex].parameters[i].instance))
+ parameterPositions[positionIndex].parameters[i].instance = 0;
+ } else if (!isAObject(parameterPositions[positionIndex].parameters[0].instance))
+ impossibleWith(parameterPositions, positionIndex);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restrictParametersAccordingToSyntax(ParameterPosition parameterPositions[], ElementEntry *elms) {
+ uncheckAllParameterPositions(parameterPositions);
+ checkRestrictedParameters(parameterPositions, elms);
+ checkNonRestrictedParameters(parameterPositions);
+ int positionIndex;
+ for (positionIndex = 0; !parameterPositions[positionIndex].endOfList; positionIndex++)
+ compressParameterArray(parameterPositions[positionIndex].parameters);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void matchPronoun(Parameter *parameter) {
+ static Parameter *pronounInstances = NULL;
+ pronounInstances = ensureParameterArrayAllocated(pronounInstances);
+
+ int pronounCandidateCount = getPronounInstances(playerWords[parameter->firstWord].code, pronounInstances);
+ if (pronounCandidateCount == 0)
+ errorWhat(parameter->firstWord);
+ else if (pronounCandidateCount > 1)
+ errorWhichPronoun(parameter->firstWord, pronounInstances);
+ else {
+ parameter->candidates[0] = pronounInstances[0];
+ setEndOfArray(&parameter->candidates[1]);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void matchNounPhrase(Parameter *parameter, ReferencesFinder adjectiveReferencesFinder, ReferencesFinder nounReferencesFinder) {
+ int i;
+
+ for (i = parameter->firstWord; i < parameter->lastWord; i++)
+ updateWithReferences(parameter->candidates, i, adjectiveReferencesFinder);
+ updateWithReferences(parameter->candidates, parameter->lastWord, nounReferencesFinder);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void instanceMatcher(Parameter *parameter) {
+ Parameter *candidates = parameter->candidates;
+ int i;
+
+ if (parameter->isLiteral) {
+ candidates[0].instance = instanceFromLiteral(playerWords[parameter->firstWord].code - dictionarySize);
+ setEndOfArray(&candidates[1]);
+ } else if (parameter->isPronoun) {
+ matchPronoun(parameter);
+ } else
+ matchNounPhrase(parameter, adjectiveReferencesForWord, nounReferencesForWord);
+
+ // Ensure that every candidate have the words, even if there where no candidates
+ candidates[0].firstWord = parameter->firstWord;
+ candidates[0].lastWord = parameter->lastWord;
+ for (i = 0; i < lengthOfParameterArray(candidates); i++) {
+ candidates[i].firstWord = parameter->firstWord;
+ candidates[i].lastWord = parameter->lastWord;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void findCandidates(Parameter parameters[], void (*instanceMatcher)(Parameter *parameter))
+{
+ int i;
+
+ for (i = 0; i < lengthOfParameterArray(parameters); i++) {
+ parameters[i].candidates = ensureParameterArrayAllocated(parameters[i].candidates);
+ instanceMatcher(&parameters[i]);
+ parameters[i].candidates[0].isPronoun = parameters[i].isPronoun;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleFailedParse(ElementEntry *elms) {
+ if (elms == NULL)
+ error(M_WHAT);
+ if (elms->next == 0) { /* No verb code, verb not declared! */
+ /* TODO Does this ever happen? */
+ error(M_CANT0);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void convertMultipleCandidatesToMultipleParameters(ParameterPosition parameterPositions[], Parameter multipleParameters[]) {
+ int parameterIndex;
+ for (parameterIndex=0; !parameterPositions[parameterIndex].endOfList; parameterIndex++)
+ if (parameterPositions[parameterIndex].explicitMultiple) {
+ compressParameterArray(parameterPositions[parameterIndex].parameters);
+ copyParameterArray(multipleParameters, parameterPositions[parameterIndex].parameters);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static ElementEntry *parseInput(ParameterPosition *parameterPositions) {
+ ElementEntry *element;
+ SyntaxEntry *stx;
+
+ stx = findSyntaxTreeForVerb(verbWordCode);
+ element = parseInputAccordingToSyntax(stx, parameterPositions);
+ handleFailedParse(element);
+ current.syntax = element->flags;
+ current.verb = remapParameterOrder(current.syntax, parameterPositions);
+ return element;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void findCandidatesForPlayerWords(ParameterPosition *parameterPosition) {
+ ParameterArray parameters = parameterPosition->parameters;
+
+ if (!parameterArrayIsEmpty(parameters)) {
+ if (parameters[0].isThem) {
+ parameterPosition->them = TRUE;
+ getPreviousMultipleParameters(parameters);
+ if (lengthOfParameterArray(parameters) == 0)
+ errorWhat(parameters[0].firstWord);
+ if (lengthOfParameterArray(parameters) > 1)
+ parameterPosition->explicitMultiple = TRUE;
+ } else if (parameterPosition->all) {
+ buildAllHere(parameters);
+ if (!parameterArrayIsEmpty(parameterPosition->exceptions))
+ findCandidates(parameterPosition->exceptions, instanceMatcher);
+ } else
+ findCandidates(parameters, instanceMatcher);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void handleMultiplePosition(ParameterPosition parameterPositions[]) {
+ int multiplePosition = findMultipleParameterPosition(parameterPositions);
+ if (anyAll(parameterPositions)) {
+ /* If the player used ALL, try to find out what was applicable */
+ disambiguateCandidatesForPosition(parameterPositions, multiplePosition, parameterPositions[multiplePosition].parameters);
+ if (lengthOfParameterArray(parameterPositions[multiplePosition].parameters) == 0)
+ errorWhat(parameterPositions[multiplePosition].parameters[0].firstWord);
+ subtractParameterArrays(parameterPositions[multiplePosition].parameters, parameterPositions[multiplePosition].exceptions);
+ if (lengthOfParameterArray(parameterPositions[multiplePosition].parameters) == 0)
+ error(M_NOT_MUCH);
+ } else if (anyExplicitMultiple(parameterPositions)) {
+ compressParameterArray(parameterPositions[multiplePosition].parameters);
+ if (lengthOfParameterArray(parameterPositions[multiplePosition].parameters) == 0) {
+ /* If there where multiple parameters but non left, exit without a */
+ /* word, assuming we have already said enough */
+ abortPlayerCommand();
+ }
+ }
+}
+
+
+/*
+ * Disambiguation is hard: there are a couple of different cases that
+ * we want to handle: Omnipotent parameter position, multiple present
+ * and non-present objects etc. The following table will show which
+ * message we would like to give in the various situations.
+ *
+ * p = present, n = non-present, 1 = single, m = multiple
+ * (1p1n = single present, single non-present)
+ *
+ * p, n, omni, result, why?
+ * -----------------------------------------------------------------
+ * 0, 0, no, errorNoSuch(w)/errorWhat(w)
+ * 0, 1, no, errorNoSuch(w)/errorWhat(w)
+ * 0, m, no, errorNoSuch(w)/errorWhat(w)
+ * 1, 0, no, ok(p)
+ * 1, 1, no, ok(p)
+ * 1, m, no, ok(p)
+ * m, 0, no, errorWhichOne(p)
+ * m, 1, no, errorWhichOne(p) only present objects should be revealed
+ * m, m, no, errorWhichOne(p) d:o
+
+ * 0, 0, yes, errorNoSuch(w)
+ * 0, 1, yes, ok(n)
+ * 0, m, yes, errorWhichOne(n) already looking "beyond" presence, might reveal undiscovered distant objects
+ * 1, 0, yes, ok(p)
+ * 1, 1, yes, ok(p) present objects have priority
+ * 1, m, yes, ok(p) present objects have priority
+ * m, 0, yes, errorWhichOne(p)
+ * m, 1, yes, errorWhichOne(p) present objects have priority, but only list present
+ * m, m, yes, errorWhichOne(p) present objects have priority, but only list present
+ */
+
+
+typedef Parameter *DisambiguationHandler(Parameter allCandidates[], Parameter presentCandidates[]);
+typedef DisambiguationHandler *DisambiguationHandlerTable[3][3][2];
+
+static Parameter *disambiguate00N(Parameter allCandidates[], Parameter presentCandidates[]) {
+ if (allCandidates[0].isPronoun)
+ errorWhat(allCandidates[0].firstWord);
+ else
+ errorNoSuch(allCandidates[0]);
+ return NULL;
+}
+static Parameter *disambiguate01N(Parameter allCandidates[], Parameter presentCandidates[]) {
+ if (allCandidates[0].isPronoun)
+ errorWhat(allCandidates[0].firstWord);
+ else
+ errorNoSuch(allCandidates[0]);
+ return NULL;
+}
+static Parameter *disambiguate0MN(Parameter allCandidates[], Parameter presentCandidates[]) {
+ if (allCandidates[0].isPronoun)
+ errorWhat(allCandidates[0].firstWord);
+ else
+ errorNoSuch(allCandidates[0]);
+ return NULL;
+}
+static Parameter *disambiguate10N(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return presentCandidates;
+}
+static Parameter *disambiguate11N(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return presentCandidates;
+}
+static Parameter *disambiguate1MN(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return presentCandidates;
+}
+static Parameter *disambiguateM0N(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(presentCandidates);
+ return NULL;
+}
+static Parameter *disambiguateM1N(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(presentCandidates);
+ return NULL;
+}
+static Parameter *disambiguateMMN(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(presentCandidates);
+ return NULL;
+}
+static Parameter *disambiguate00Y(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorNoSuch(allCandidates[0]);
+ return NULL;
+}
+static Parameter *disambiguate01Y(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return allCandidates;
+}
+static Parameter *disambiguate0MY(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(allCandidates);
+ return NULL;
+}
+static Parameter *disambiguate10Y(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return presentCandidates;
+}
+static Parameter *disambiguate11Y(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return presentCandidates;
+}
+static Parameter *disambiguate1MY(Parameter allCandidates[], Parameter presentCandidates[]) {
+ return presentCandidates;
+}
+static Parameter *disambiguateM0Y(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(presentCandidates);
+ return NULL;
+}
+static Parameter *disambiguateM1Y(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(presentCandidates);
+ return NULL;
+}
+static Parameter *disambiguateMMY(Parameter allCandidates[], Parameter presentCandidates[]) {
+ errorWhichOne(presentCandidates);return NULL;
+}
+
+static DisambiguationHandlerTable disambiguationHandlerTable =
+ {
+ { // Present == 0
+ { // Distant == 0
+ disambiguate00N, disambiguate00Y},
+ { // Distant == 1
+ disambiguate01N, disambiguate01Y},
+ { // Distant == M
+ disambiguate0MN, disambiguate0MY}},
+ { // Present == 1
+ { // Distant == 0
+ disambiguate10N, disambiguate10Y},
+ { // Distant == 1
+ disambiguate11N, disambiguate11Y},
+ { // Distant == M
+ disambiguate1MN, disambiguate1MY}},
+ { // Present == M
+ { // Distant == 0
+ disambiguateM0N, disambiguateM0Y},
+ { // Distant == 1
+ disambiguateM1N, disambiguateM1Y},
+ { // Distant == M
+ disambiguateMMN, disambiguateMMY}}
+ };
+
+/*----------------------------------------------------------------------*/
+static void disambiguateCandidates(Parameter *allCandidates, bool omnipotent, bool (*reachable)(int), DisambiguationHandlerTable handler) {
+ static Parameter *presentCandidates = NULL;
+ int present;
+ int distant;
+ Parameter *result;
+
+ presentCandidates = ensureParameterArrayAllocated(presentCandidates);
+
+ copyParameterArray(presentCandidates, allCandidates);
+ filterOutNonReachable(presentCandidates, reachable);
+
+ present = lengthOfParameterArray(presentCandidates);
+ if (present > 1) present = 2; /* 2 = M */
+
+ distant = lengthOfParameterArray(allCandidates) - present;
+ if (distant > 1) distant = 2; /* 2 = M */
+
+ result = handler[present][distant][omnipotent](allCandidates, presentCandidates);
+
+ // If we returned then it's ok, use the single candidate found
+ allCandidates[0] = result[0];
+ setEndOfArray(&allCandidates[1]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void disambiguate(ParameterPosition parameterPositions[], ElementEntry *element) {
+ /* The New Strategy! Parsing has only collected word indications,
+ not built anything, so we need to match parameters to instances here */
+
+ int position;
+ for (position = 0; !parameterPositions[position].endOfList; position++) {
+ findCandidatesForPlayerWords(&parameterPositions[position]);
+ }
+
+ /* Now we have candidates for everything the player said, except
+ if he used ALL or THEM, then we have built those as parameters, or he
+ referred to the multiple parameters of the previous command
+ using 'them, if so, they too are stored as parameters */
+
+ for (position = 0; !parameterPositions[position].endOfList; position++) {
+ ParameterPosition *parameterPosition = &parameterPositions[position];
+ bool omni = hasBit(parameterPosition->flags, OMNIBIT);
+ if (!parameterPosition->all && !parameterPosition->them) {
+ Parameter *parameters = parameterPosition->parameters;
+ int p;
+ for (p = 0; p < lengthOfParameterArray(parameters); p++) {
+ Parameter *parameter = &parameters[p];
+ Parameter *candidates = parameter->candidates;
+ disambiguateCandidates(candidates, omni, reachable, disambiguationHandlerTable);
+ parameter->instance = candidates[0].instance;
+ }
+ }
+ if (parameterPosition->all) {
+ Parameter *exceptions = parameterPosition->exceptions;
+ int p;
+ for (p = 0; p < lengthOfParameterArray(exceptions); p++) {
+ Parameter *parameter = &exceptions[p];
+ Parameter *candidates = parameter->candidates;
+ disambiguateCandidates(candidates, omni, reachable, disambiguationHandlerTable);
+ parameter->instance = candidates[0].instance;
+ }
+ }
+ }
+
+ handleMultiplePosition(parameterPositions);
+
+ /* Now perform restriction checks */
+ restrictParametersAccordingToSyntax(parameterPositions, element);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static void tryParam(Parameter parameters[], Parameter multipleParameters[]) {
+ ElementEntry *element; /* Pointer to element list */
+ static ParameterPosition *parameterPositions = NULL;
+ if (parameterPositions != NULL)
+ deallocateParameterPositions(parameterPositions);
+
+ // TODO newParameterPositionArray()!!!! Or even reallocatePP.. or cleanPP..
+ parameterPositions = (ParameterPosition *)allocate(sizeof(ParameterPosition)*(MAXPARAMS+1));
+ parameterPositions[0].endOfList = TRUE;
+
+ element = parseInput(parameterPositions);
+
+ disambiguate(parameterPositions, element);
+
+ // TODO: Now we need to convert back to legacy parameter and multipleParameter format
+ convertPositionsToParameters(parameterPositions, parameters);
+ markExplicitMultiple(parameterPositions, parameters);
+ convertMultipleCandidatesToMultipleParameters(parameterPositions, multipleParameters);
+
+ deallocateParameterPositions(parameterPositions);
+ parameterPositions = NULL;
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static void parseOneCommand(Parameter parameters[], Parameter multipleParameters[])
+{
+ tryParam(parameters, multipleParameters); /* ... to understand what he said */
+
+ /* More on this line? */
+ if (!endOfWords(currentWordIndex)) {
+ if (isConjunctionWord(currentWordIndex))
+ currentWordIndex++; /* If so skip the conjunction */
+ else
+ error(M_WHAT);
+ }
+}
+
+/*======================================================================*/
+void initParsing(void) {
+ currentWordIndex = 0;
+ continued = FALSE;
+ ensureSpaceForPlayerWords(0);
+ clearWordList(playerWords);
+
+ pronouns = allocatePronounArray(pronouns);
+ globalParameters = ensureParameterArrayAllocated(globalParameters);
+ previousMultipleParameters = ensureParameterArrayAllocated(previousMultipleParameters);
+}
+
+/*----------------------------------------------------------------------*/
+static int pronounWordForInstance(int instance) {
+ /* Scan through the dictionary to find any pronouns that can be used
+ for this instance */
+ int w;
+
+ for (w = 0; w < dictionarySize; w++)
+ if (isPronoun(w)) {
+ Aword *reference = (Aword *)pointerTo(dictionary[w].pronounRefs);
+ while (*reference != EOF) {
+ if (*reference == (Aword)instance)
+ return dictionary[w].code;
+ reference++;
+ }
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------------*/
+static void addPronounForInstance(int thePronoun, int instanceCode) {
+ int i;
+
+ for (i = 0; !endOfPronouns(i); i++)
+ if (pronouns[i].pronoun == thePronoun && pronouns[i].instance == instanceCode)
+ // Don't add the same instance twice for the same pronoun
+ return;
+ pronouns[i].pronoun = thePronoun;
+ pronouns[i].instance = instanceCode;
+ setEndOfArray(&pronouns[i + 1]);
+}
+
+/*----------------------------------------------------------------------*/
+static void notePronounsForParameters(Parameter parameters[]) {
+ /* For all parameters note which ones can be referred to by a pronoun */
+ Parameter *p;
+
+ clearPronounList(pronouns);
+ for (p = parameters; !isEndOfArray(p); p++) {
+ int pronoun = pronounWordForInstance(p->instance);
+ if (pronoun > 0)
+ addPronounForInstance(pronoun, p->instance);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void parseVerbCommand(Parameter parameters[], Parameter multipleParameters[]) {
+ verbWord = playerWords[currentWordIndex].code;
+ verbWordCode = dictionary[verbWord].code;
+ if (isPreBeta2(header->version))
+ /* Pre-beta2 didn't generate syntax elements for verb words,
+ need to skip first word which should be the verb */
+ currentWordIndex++;
+ parseOneCommand(parameters, multipleParameters);
+ notePronounsForParameters(parameters);
+ fail = FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void parseInstanceCommand(Parameter parameters[], Parameter multipleParameters[]) {
+ /* Pick up the parse tree for the syntaxes that start with an
+ instance reference and parse according to that. The
+ verbWord code is set to 0 to indicate that it is not a verb
+ but an instance that starts the command. */
+ verbWordCode = 0;
+ parseOneCommand(parameters, multipleParameters);
+ notePronounsForParameters(parameters);
+ fail = FALSE;
+}
+
+
+/*======================================================================*/
+void parse(void) {
+ /* longjmp's ahead so these need to survive to not leak memory */
+ static Parameter *parameters = NULL;
+ static Parameter *multipleParameters = NULL;
+ parameters = ensureParameterArrayAllocated(parameters);
+ multipleParameters = ensureParameterArrayAllocated(multipleParameters);
+
+ if (endOfWords(currentWordIndex)) {
+ currentWordIndex = 0;
+ scan();
+ } else if (anyOutput)
+ para();
+
+ capitalize = TRUE;
+
+ firstWord = currentWordIndex;
+ if (isVerbWord(currentWordIndex)) {
+ parseVerbCommand(parameters, multipleParameters);
+ action(current.verb, parameters, multipleParameters);
+ } else if (isDirectionWord(currentWordIndex)) {
+ clearParameterArray(previousMultipleParameters);
+ clearPronounList(pronouns);
+ handleDirectionalCommand();
+ } else if (isInstanceReferenceWord(currentWordIndex)) {
+ parseInstanceCommand(parameters, multipleParameters);
+ action(current.verb, parameters, multipleParameters);
+ } else
+ error(M_WHAT);
+
+ if (fail) error(NO_MSG);
+
+ lastWord = currentWordIndex - 1;
+ if (isConjunctionWord(lastWord))
+ lastWord--;
+
+ if (lengthOfParameterArray(parameters) > 0)
+ copyParameterArray(previousMultipleParameters, multipleParameters);
+ else
+ clearParameterArray(previousMultipleParameters);
+
+ freeParameterArray(parameters);
+ parameters = NULL;
+ freeParameterArray(multipleParameters);
+ multipleParameters = NULL;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/parse.h b/engines/glk/alan3/parse.h
new file mode 100644
index 0000000000..7425301081
--- /dev/null
+++ b/engines/glk/alan3/parse.h
@@ -0,0 +1,42 @@
+/* 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_ALAN3_PARSE
+#define GLK_ALAN3_PARSE
+
+/* Parse data for ALAN interpreter module. */
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/params.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* FUNCTIONS */
+
+extern void parse(void);
+extern void initParsing(void);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/readline.cpp b/engines/glk/alan3/readline.cpp
new file mode 100644
index 0000000000..552e3b5627
--- /dev/null
+++ b/engines/glk/alan3/readline.cpp
@@ -0,0 +1,170 @@
+/* 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/alan3/readline.h"
+
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/save.h"
+#include "glk/alan3/location.h"
+
+#include "glk/alan3/options.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/resources.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define LINELENGTH 1000
+
+/*======================================================================
+
+ readline()
+
+ Read a line from the user, with history and editing
+
+*/
+
+/* TODO - length of user buffer should be used */
+bool readline(char buffer[])
+{
+ event_t event;
+ static bool readingCommands = FALSE;
+ static frefid_t commandFileRef;
+ static strid_t commandFile;
+#ifdef HAVE_WINGLK
+ static frefid_t logFileRef;
+ INT_PTR e;
+#endif
+
+ if (readingCommands) {
+ if (g_vm->glk_get_line_stream(commandFile, buffer, 255) == 0) {
+ g_vm->glk_stream_close(commandFile, NULL);
+ readingCommands = FALSE;
+ } else {
+ g_vm->glk_set_style(style_Input);
+ printf(buffer);
+ g_vm->glk_set_style(style_Normal);
+ }
+ } else {
+ g_vm->glk_request_line_event(glkMainWin, buffer, 255, 0);
+ /* FIXME: buffer size should be infallible: all existing calls use 256 or
+ 80 character buffers, except parse which uses LISTLEN (currently 100)
+ */
+ do
+ {
+ g_vm->glk_select(&event);
+ switch (event.type) {
+ case evtype_Arrange:
+ statusline();
+ break;
+#ifdef HAVE_WINGLK
+ case winglk_evtype_GuiInput:
+ switch (event.val1) {
+ case ID_MENU_RESTART:
+ restartGame();
+ break;
+ case ID_MENU_SAVE:
+ glk_set_style(style_Input);
+ printf("save\n");
+ glk_set_style(style_Normal);
+ save();
+ para();
+ printf("> ");
+ break;
+ case ID_MENU_RESTORE:
+ glk_set_style(style_Input);
+ printf("restore\n");
+ glk_set_style(style_Normal);
+ restore();
+ look();
+ para();
+ printf("> ");
+ break;
+ case ID_MENU_RECORD:
+ if (transcriptOption || logOption) {
+ glk_stream_close(logFile, NULL);
+ transcriptOption = FALSE;
+ logOption = FALSE;
+ }
+ logFileRef = glk_fileref_create_by_prompt(fileusage_InputRecord+fileusage_TextMode, filemode_Write, 0);
+ if (logFileRef == NULL) break;
+ logFile = glk_stream_open_file(logFileRef, filemode_Write, 0);
+ if (logFile != NULL)
+ logOption = TRUE;
+ break;
+ case ID_MENU_PLAYBACK:
+ commandFileRef = glk_fileref_create_by_prompt(fileusage_InputRecord+fileusage_TextMode, filemode_Read, 0);
+ if (commandFileRef == NULL) break;
+ commandFile = glk_stream_open_file(commandFileRef, filemode_Read, 0);
+ if (commandFile != NULL)
+ if (glk_get_line_stream(commandFile, buffer, 255) != 0) {
+ readingCommands = TRUE;
+ printf(buffer);
+ return TRUE;
+ }
+ break;
+ case ID_MENU_TRANSCRIPT:
+ if (transcriptOption || logOption) {
+ glk_stream_close(logFile, NULL);
+ transcriptOption = FALSE;
+ logOption = FALSE;
+ }
+ logFileRef = glk_fileref_create_by_prompt(fileusage_Transcript+fileusage_TextMode, filemode_Write, 0);
+ if (logFileRef == NULL) break;
+ logFile = glk_stream_open_file(logFileRef, filemode_Write, 0);
+ if (logFile != NULL) {
+ transcriptOption = TRUE;
+ glk_put_string_stream(logFile, "> ");
+ }
+ break;
+ case ID_MENU_ABOUT:
+ e = DialogBox(myInstance, MAKEINTRESOURCE(IDD_ABOUT), NULL, &AboutDialogProc);
+ (void)e;
+ break;
+ }
+ break;
+#endif
+ }
+ } while (event.type != evtype_LineInput);
+ if (buffer[0] == '@') {
+ buffer[event.val1] = 0;
+ commandFileRef = g_vm->glk_fileref_create_by_name(fileusage_InputRecord+fileusage_TextMode, &buffer[1], 0);
+ commandFile = g_vm->glk_stream_open_file(commandFileRef, filemode_Read, 0);
+ if (commandFile != NULL)
+ if (g_vm->glk_get_line_stream(commandFile, buffer, 255) != 0) {
+ readingCommands = TRUE;
+ g_vm->glk_set_style(style_Input);
+ printf(buffer);
+ g_vm->glk_set_style(style_Normal);
+ }
+ } else
+ buffer[event.val1] = 0;
+ }
+ return TRUE;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/readline.h b/engines/glk/alan3/readline.h
new file mode 100644
index 0000000000..3ab217c057
--- /dev/null
+++ b/engines/glk/alan3/readline.h
@@ -0,0 +1,40 @@
+/* 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_ALAN3_READLINE
+#define GLK_ALAN3_READLINE
+
+/* Header file for user input, history and editing support */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define HISTORYLENGTH 20
+
+extern bool readline(char usrbuf[]);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/resources.h b/engines/glk/alan3/resources.h
new file mode 100644
index 0000000000..837c137ac1
--- /dev/null
+++ b/engines/glk/alan3/resources.h
@@ -0,0 +1,40 @@
+/* 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_ALAN3_RESOURCES
+#define GLK_ALAN3_RESOURCES
+
+#define IDC_STATIC (-1)
+
+#define IDR_ARUN 25000
+#define IDR_ABOUT 27000
+
+#define IDD_ABOUT 26000
+#define ID_MENU_RESTART 26001
+#define ID_MENU_SAVE 26002
+#define ID_MENU_RECORD 26003
+#define ID_MENU_PLAYBACK 26004
+#define ID_MENU_TRANSCRIPT 26005
+#define ID_MENU_RESTORE 26006
+#define ID_MENU_ABOUT 26007
+
+#endif
diff --git a/engines/glk/alan3/reverse.cpp b/engines/glk/alan3/reverse.cpp
new file mode 100644
index 0000000000..64e0ea0ad5
--- /dev/null
+++ b/engines/glk/alan3/reverse.cpp
@@ -0,0 +1,685 @@
+/* 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/alan3/types.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/checkentry.h"
+#include "glk/alan3/rules.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/utils.h"
+#include "glk/alan3/compatibility.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/memory.h"
+
+namespace Glk {
+namespace Alan3 {
+
+extern Aword *memory;
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static Aaddr memorySize = 0;
+static Aword *addressesDone = NULL;
+static int numberDone = 0;
+static int doneSize = 0;
+
+static bool alreadyDone(Aaddr address)
+{
+ int i;
+
+ if (address == 0) return TRUE;
+
+ /* Have we already done it? */
+ for (i = 0; i < numberDone; i++)
+ if (addressesDone[i] == address)
+ return TRUE;
+
+ if (doneSize == numberDone) {
+ doneSize += 100;
+ addressesDone = (Aword *)realloc(addressesDone, doneSize*sizeof(Aword));
+ }
+ addressesDone[numberDone] = address;
+ numberDone++;
+
+ return FALSE;
+}
+
+
+
+#define NATIVE(w) \
+ ( (((Aword)((w)[3]) ) & 0x000000ff) \
+ | (((Aword)((w)[2]) << 8) & 0x0000ff00) \
+ | (((Aword)((w)[1]) << 16) & 0x00ff0000) \
+ | (((Aword)((w)[0]) << 24) & 0xff000000))
+
+/*----------------------------------------------------------------------*/
+Aword reversed(Aword w) /* IN - The ACODE word to swap bytes of */
+{
+#ifdef TRYNATIVE
+ return NATIVE(&w);
+#else
+ Aword s; /* The swapped ACODE word */
+ char *wp, *sp;
+ int i;
+
+ wp = (char *) &w;
+ sp = (char *) &s;
+
+ for (i = 0; i < sizeof(Aword); i++)
+ sp[sizeof(Aword)-1 - i] = wp[i];
+
+ return s;
+#endif
+}
+
+
+void reverseWord(Aword *w) /* IN - The ACODE word to reverse bytes in */
+{
+ *w = reversed(*w);
+}
+
+void reverse(Aword *w) /* IN - The ACODE word to reverse bytes in */
+{
+ if (w < &memory[0] || w > &memory[memorySize])
+ syserr("Reversing address outside of memory");
+ reverseWord(w);
+}
+
+
+static void reverseTable(Aword adr, int elementSize)
+{
+ Aword *e = &memory[adr];
+ int i;
+
+ if (elementSize < sizeof(Aword) || elementSize % sizeof(Aword) != 0)
+ syserr("***Wrong size in 'reverseTable()' ***");
+
+ if (adr == 0) return;
+
+ while (!isEndOfArray(e)) {
+ for (i = 0; i < elementSize/sizeof(Aword); i++) {
+ reverse(e);
+ e++;
+ }
+ }
+}
+
+
+static void reverseStms(Aword adr)
+{
+ Aword *e = &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ while (TRUE) {
+ reverse(e);
+ if (*e == ((Aword)C_STMOP<<28|(Aword)I_RETURN)) break;
+ e++;
+ }
+}
+
+
+static void reverseMsgs(Aword adr)
+{
+ MessageEntry *e = (MessageEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(MessageEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->stms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseDictionary(Aword adr)
+{
+ DictionaryEntry *e = (DictionaryEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(DictionaryEntry));
+ while (!isEndOfArray(e)) {
+ if ((e->classBits & SYNONYM_BIT) == 0) { /* Do not do this for synonyms */
+ reverseTable(e->adjectiveRefs, sizeof(Aword));
+ reverseTable(e->nounRefs, sizeof(Aword));
+ reverseTable(e->pronounRefs, sizeof(Aword));
+ }
+ e++;
+ }
+ }
+}
+
+
+static void reverseChks(Aword adr)
+{
+ CheckEntry *e = (CheckEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(CheckEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->exp);
+ reverseStms(e->stms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseAlts(Aword adr)
+{
+ AltEntry *e = (AltEntry *)&memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(AltEntry));
+ while (!isEndOfArray(e)) {
+ reverseChks(e->checks);
+ reverseStms(e->action);
+ e++;
+ }
+ }
+}
+
+
+static void reverseVerbs(Aword adr)
+{
+ VerbEntry *e = (VerbEntry *)&memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(VerbEntry));
+ while (!isEndOfArray(e)) {
+ reverseAlts(e->alts);
+ e++;
+ }
+ }
+}
+
+
+static void reverseSteps(Aword adr)
+{
+ StepEntry *e = (StepEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(StepEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->after);
+ reverseStms(e->exp);
+ reverseStms(e->stms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseScrs(Aword adr)
+{
+ ScriptEntry *e = (ScriptEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(ScriptEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->description);
+ reverseSteps(e->steps);
+ e++;
+ }
+ }
+}
+
+
+static void reverseExits(Aword adr)
+{
+ ExitEntry *e = (ExitEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(ExitEntry));
+ while (!isEndOfArray(e)) {
+ reverseChks(e->checks);
+ reverseStms(e->action);
+ e++;
+ }
+ }
+}
+
+
+static void reverseClasses(Aword adr)
+{
+ ClassEntry *e = (ClassEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(ClassEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->name);
+ reverseStms(e->initialize);
+ reverseChks(e->descriptionChecks);
+ reverseStms(e->description);
+ reverseStms(e->entered);
+ reverseStms(e->definite.address);
+ reverseStms(e->indefinite.address);
+ reverseStms(e->negative.address);
+ reverseStms(e->mentioned);
+ reverseVerbs(e->verbs);
+ e++;
+ }
+ }
+}
+
+
+static void reverseInstances(Aword adr)
+{
+ InstanceEntry *e = (InstanceEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(InstanceEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->name);
+ reverseTable(e->initialAttributes, sizeof(AttributeHeaderEntry));
+ reverseStms(e->initialize);
+ reverseStms(e->definite.address);
+ reverseStms(e->indefinite.address);
+ reverseStms(e->negative.address);
+ reverseStms(e->mentioned);
+ reverseChks(e->checks);
+ reverseStms(e->description);
+ reverseVerbs(e->verbs);
+ reverseStms(e->entered);
+ reverseExits(e->exits);
+ e++;
+ }
+ }
+}
+
+
+static void reverseRestrictions(Aword adr)
+{
+ RestrictionEntry *e = (RestrictionEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(RestrictionEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->stms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseElms(Aword adr)
+{
+ ElementEntry *e = (ElementEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(ElementEntry));
+ while (!isEndOfArray(e)) {
+ if (e->code == EOS) reverseRestrictions(e->next);
+ else reverseElms(e->next);
+ e++;
+ }
+ }
+}
+
+
+static void reverseSyntaxTableCurrent(Aword adr) {
+ SyntaxEntry *e = (SyntaxEntry *) &memory[adr];
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(SyntaxEntry));
+ while (!isEndOfArray(e)) {
+ reverseElms(e->elms);
+ reverseTable(e->parameterNameTable, sizeof(Aaddr));
+ e++;
+ }
+ }
+}
+
+
+static void reverseSyntaxTablePreBeta2(Aword adr) {
+ SyntaxEntryPreBeta2 *e = (SyntaxEntryPreBeta2 *) &memory[adr];
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(SyntaxEntryPreBeta2));
+ while (!isEndOfArray(e)) {
+ reverseElms(e->elms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseSyntaxTable(Aword adr, char version[])
+{
+ if (!adr || alreadyDone(adr)) return;
+
+ if (isPreBeta2(version))
+ reverseSyntaxTablePreBeta2(adr);
+ else
+ reverseSyntaxTableCurrent(adr);
+}
+
+
+static void reverseParameterNames(Aaddr parameterMapAddress) {
+ Aaddr *e;
+ Aaddr adr;
+
+ adr = addressAfterTable(parameterMapAddress, sizeof(ParameterMapEntry));
+ reverse(&memory[adr]);
+ adr = memory[adr];
+
+ reverseTable(adr, sizeof(Aaddr));
+
+ e = (Aaddr*) &memory[adr];
+ while (!isEndOfArray(e)) {
+ reverseTable(*e, sizeof(Aaddr));
+ e++;
+ }
+}
+
+
+static void reverseParameterTable(Aword adr)
+{
+ ParameterMapEntry *e = (ParameterMapEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(ParameterMapEntry));
+ while (!isEndOfArray(e)) {
+ reverseTable(e->parameterMapping, sizeof(Aword));
+ e++;
+ }
+ }
+}
+
+
+static void reverseEvts(Aword adr)
+{
+ EventEntry *e = (EventEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(EventEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->code);
+ e++;
+ }
+ }
+}
+
+
+static void reverseLims(Aword adr)
+{
+ LimitEntry *e = (LimitEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(LimitEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->stms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseContainers(Aword adr)
+{
+ ContainerEntry *e = (ContainerEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(ContainerEntry));
+ while (!isEndOfArray(e)) {
+ reverseLims(e->limits);
+ reverseStms(e->header);
+ reverseStms(e->empty);
+ reverseChks(e->extractChecks);
+ reverseStms(e->extractStatements);
+ e++;
+ }
+ }
+}
+
+
+static void reverseRuls(Aword adr)
+{
+ RuleEntry *e = (RuleEntry *) &memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(RuleEntry));
+ while (!isEndOfArray(e)) {
+ reverseStms(e->exp);
+ reverseStms(e->stms);
+ e++;
+ }
+ }
+}
+
+
+static void reverseSetInitTable(Aaddr adr)
+{
+ SetInitEntry *e = (SetInitEntry *)&memory[adr];
+
+ if (!adr || alreadyDone(adr)) return;
+
+ if (!isEndOfArray(e)) {
+ reverseTable(adr, sizeof(SetInitEntry));
+ while (!isEndOfArray(e)) {
+ reverseTable(e->setAddress, sizeof(Aword));
+ e++;
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static void reversePreAlpha5Header(Pre3_0alpha5Header *hdr)
+{
+ int i;
+
+ /* Reverse all words in the header except the tag */
+ for (i = 1; i < sizeof(*hdr)/sizeof(Aword); i++)
+ reverseWord(&((Aword *)hdr)[i]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void reversePreAlpha5() {
+ /* NOTE that the reversePreXXX() have different header definitions */
+ Pre3_0alpha5Header *header = (Pre3_0alpha5Header *)memory;
+
+ reversePreAlpha5Header(header);
+ memorySize = header->size;
+
+ reverseDictionary(header->dictionary);
+ reverseSyntaxTable(header->syntaxTableAddress, header->version);
+ reverseParameterTable(header->parameterMapAddress);
+ reverseVerbs(header->verbTableAddress);
+ reverseClasses(header->classTableAddress);
+ reverseInstances(header->instanceTableAddress);
+ reverseScrs(header->scriptTableAddress);
+ reverseContainers(header->containerTableAddress);
+ reverseEvts(header->eventTableAddress);
+ reverseRuls(header->ruleTableAddress);
+ reverseTable(header->stringInitTable, sizeof(StringInitEntry));
+ reverseSetInitTable(header->setInitTable);
+ reverseTable(header->sourceFileTable, sizeof(SourceFileEntry));
+ reverseTable(header->sourceLineTable, sizeof(SourceLineEntry));
+ reverseStms(header->start);
+ reverseMsgs(header->messageTableAddress);
+
+ reverseTable(header->scores, sizeof(Aword));
+ reverseTable(header->freq, sizeof(Aword));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void reversePreBeta2Header(Pre3_0beta2Header *hdr)
+{
+ int i;
+
+ /* Reverse all words in the header except the tag */
+ for (i = 1; i < sizeof(*hdr)/sizeof(Aword); i++)
+ reverseWord(&((Aword *)hdr)[i]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void reversePreBeta2() {
+ /* NOTE that the reversePreXXX() have different header definitions */
+ Pre3_0beta2Header *header = (Pre3_0beta2Header *)memory;
+
+ reversePreBeta2Header(header);
+ memorySize = header->size;
+
+ reverseDictionary(header->dictionary);
+ reverseSyntaxTable(header->syntaxTableAddress, header->version);
+ reverseParameterTable(header->parameterMapAddress);
+ reverseVerbs(header->verbTableAddress);
+ reverseClasses(header->classTableAddress);
+ reverseInstances(header->instanceTableAddress);
+ reverseScrs(header->scriptTableAddress);
+ reverseContainers(header->containerTableAddress);
+ reverseEvts(header->eventTableAddress);
+ reverseRuls(header->ruleTableAddress);
+ reverseTable(header->stringInitTable, sizeof(StringInitEntry));
+ reverseSetInitTable(header->setInitTable);
+ reverseTable(header->sourceFileTable, sizeof(SourceFileEntry));
+ reverseTable(header->sourceLineTable, sizeof(SourceLineEntry));
+ reverseStms(header->start);
+ reverseMsgs(header->messageTableAddress);
+
+ reverseTable(header->scores, sizeof(Aword));
+ reverseTable(header->freq, sizeof(Aword));
+}
+
+
+/*======================================================================*/
+void reverseHdr(ACodeHeader *hdr)
+{
+ int i;
+
+ /* Reverse all words in the header except the tag and the version marking */
+ for (i = 1; i < sizeof(*hdr)/sizeof(Aword); i++)
+ reverseWord(&((Aword *)hdr)[i]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void reverseInstanceIdTable(ACodeHeader *header) {
+ reverseTable(header->instanceTableAddress+header->instanceMax*sizeof(InstanceEntry)/sizeof(Aword)+1, sizeof(Aword));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void reverseNative() {
+ /* NOTE that the reversePreXXX() have different header definitions */
+ ACodeHeader *header = (ACodeHeader *)memory;
+
+ reverseHdr(header);
+ memorySize = header->size;
+
+ reverseDictionary(header->dictionary);
+ reverseSyntaxTable(header->syntaxTableAddress, header->version);
+ if (header->debug && !isPreBeta3(header->version))
+ reverseParameterNames(header->parameterMapAddress);
+ reverseParameterTable(header->parameterMapAddress);
+ reverseVerbs(header->verbTableAddress);
+ reverseClasses(header->classTableAddress);
+ reverseInstances(header->instanceTableAddress);
+ if (header->debug && !isPreBeta3(header->version))
+ reverseInstanceIdTable(header);
+ reverseScrs(header->scriptTableAddress);
+ reverseContainers(header->containerTableAddress);
+ reverseEvts(header->eventTableAddress);
+ reverseRuls(header->ruleTableAddress);
+ reverseTable(header->stringInitTable, sizeof(StringInitEntry));
+ reverseSetInitTable(header->setInitTable);
+ reverseTable(header->sourceFileTable, sizeof(SourceFileEntry));
+ reverseTable(header->sourceLineTable, sizeof(SourceLineEntry));
+ reverseStms(header->prompt);
+ reverseStms(header->start);
+ reverseMsgs(header->messageTableAddress);
+
+ reverseTable(header->scores, sizeof(Aword));
+ reverseTable(header->freq, sizeof(Aword));
+}
+
+
+/*======================================================================
+
+ reverseACD()
+
+ Traverse all the data structures and reverse all integers.
+ Only performed in architectures with reversed byte ordering, which
+ makes the .ACD files fully compatible across architectures
+
+ */
+void reverseACD(void)
+{
+ ACodeHeader *header = (ACodeHeader *)memory;
+ char version[4];
+ int i;
+
+ /* Make a copy of the version marking to reverse */
+ for (i = 0; i <= 3; i++)
+ version[i] = header->version[i];
+ reverseWord((Aword*)&version);
+
+ if (isPreAlpha5(version))
+ reversePreAlpha5();
+ else if (isPreBeta2(version))
+ reversePreBeta2();
+ else
+ reverseNative();
+
+ free(addressesDone);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/reverse.h b/engines/glk/alan3/reverse.h
new file mode 100644
index 0000000000..ebcc0f5f8f
--- /dev/null
+++ b/engines/glk/alan3/reverse.h
@@ -0,0 +1,43 @@
+/* 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_ALAN3_REVERSE
+#define GLK_ALAN3_REVERSE
+
+/* Header file for reverse-module in Alan Interpreter */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* Functions: */
+
+extern void reverseHdr(ACodeHeader *hdr);
+extern void reverseACD(void);
+extern void reverse(Aword *word);
+extern Aword reversed(Aword word);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/rules.cpp b/engines/glk/alan3/rules.cpp
new file mode 100644
index 0000000000..4f2d60e2fc
--- /dev/null
+++ b/engines/glk/alan3/rules.cpp
@@ -0,0 +1,287 @@
+/* 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/alan3/types.h"
+#include "glk/alan3/rules.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/compatibility.h"
+
+#ifdef HAVE_GLK
+#include "glk/alan3/glkio.h"
+#endif
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA: */
+RuleEntry *rules; /* Rule table pointer */
+bool anyRuleRun;
+
+
+/* PRIVATE TYPES: */
+typedef struct RulesAdmin {
+ bool lastEval;
+ bool alreadyRun;
+} RulesAdmin;
+
+/* PRIVATE DATA: */
+static int ruleCount;
+static RulesAdmin *rulesAdmin; /* Table for administration of the rules */
+
+/*----------------------------------------------------------------------*/
+static void clearRulesAdmin(int ruleCount) {
+ int r;
+ for (r = 0; r < ruleCount; r++) {
+ rulesAdmin[r].lastEval = FALSE;
+ rulesAdmin[r].alreadyRun = FALSE;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void initRulesAdmin(int ruleCount) {
+ int r;
+
+ rulesAdmin = (RulesAdmin *)allocate(ruleCount*sizeof(RulesAdmin)+sizeof(EOF));
+ for (r = 0; r < ruleCount; r++)
+ ;
+ setEndOfArray(&rulesAdmin[r]);
+}
+
+
+/*======================================================================*/
+void initRules(Aaddr ruleTableAddress) {
+
+ rules = (RuleEntry *) pointerTo(ruleTableAddress);
+
+ if (ruleCount == 0) { /* Not initiated */
+ for (ruleCount = 0; !isEndOfArray(&rules[ruleCount]); ruleCount++)
+ ;
+ initRulesAdmin(ruleCount);
+ }
+ clearRulesAdmin(ruleCount);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceRuleStart(int rule, char *what) {
+ printf("\n<RULE %d", rule);
+ if (current.location != 0) {
+ printf(" (at ");
+ traceSay(current.location);
+ } else
+ printf(" (nowhere");
+ printf("[%d]), %s", current.location, what);
+}
+
+static bool detailedTraceOn() {
+ return traceInstructionOption || traceSourceOption || tracePushOption || traceStackOption;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void traceRuleEvaluation(int rule) {
+ if (traceSectionOption) {
+ if (detailedTraceOn()) {
+ traceRuleStart(rule, "Evaluating:>");
+ if (!traceInstructionOption)
+ printf("\n");
+ } else {
+ traceRuleStart(rule, "Evaluating to ");
+ }
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static void traceRuleResult(int rule, bool result) {
+ if (traceSectionOption) {
+ if (detailedTraceOn())
+ printf("<RULE %d %s%s", rule, "Evaluated to ", result?": true>\n":": false>\n");
+ else
+ printf(result?"true":"false");
+ }
+}
+
+/*----------------------------------------------------------------------*/
+static void traceRuleExecution(int rule) {
+ if (traceSectionOption) {
+ if (!traceInstructionOption && !traceSourceOption)
+ printf(", Executing:>\n");
+ else {
+ traceRuleStart(rule, "Executing:>");
+ if (!traceInstructionOption)
+ printf("\n");
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------*/
+static void evaluateRulesPreBeta2(void)
+{
+ bool change = TRUE;
+ int i;
+
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++)
+ rules[i-1].alreadyRun = FALSE;
+
+ while (change) {
+ change = FALSE;
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++)
+ if (!rules[i-1].alreadyRun) {
+ traceRuleEvaluation(i);
+ if (evaluate(rules[i-1].exp)) {
+ change = TRUE;
+ rules[i-1].alreadyRun = TRUE;
+ traceRuleExecution(i);
+ interpret(rules[i-1].stms);
+ } else if (traceSectionOption && !traceInstructionOption)
+ printf(":>\n");
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void evaluateRulesBeta2New(void) {
+ int i;
+
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++)
+ rules[i-1].alreadyRun = FALSE;
+
+ current.location = NOWHERE;
+ current.actor = 0;
+
+ anyRuleRun = FALSE;
+
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++) {
+ bool evaluated_value = evaluate(rules[i-1].exp);
+ traceRuleEvaluation(i);
+ rules[i-1].alreadyRun = evaluated_value;
+ }
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++) {
+ if (rules[i-1].alreadyRun) {
+ traceRuleExecution(i);
+ interpret(rules[i-1].stms);
+ anyRuleRun = TRUE;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+/* This is how beta2 thought rules should be evaluated:
+ */
+static void evaluateRulesBeta2(void)
+{
+ bool change = TRUE;
+ int i;
+
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++)
+ rules[i-1].alreadyRun = FALSE;
+
+ current.location = NOWHERE;
+ current.actor = 0;
+
+ while (change) {
+ change = FALSE;
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++)
+ if (!rules[i-1].alreadyRun) {
+ traceRuleEvaluation(i);
+ bool triggered = evaluate(rules[i-1].exp);
+ if (triggered) {
+ if (rulesAdmin[i-1].lastEval == false) {
+ change = TRUE;
+ rules[i-1].alreadyRun = TRUE;
+ traceRuleExecution(i);
+ interpret(rules[i-1].stms);
+ }
+ rulesAdmin[i-1].lastEval = triggered;
+ } else {
+ rulesAdmin[i-1].lastEval = false;
+ if (traceSectionOption && !traceInstructionOption)
+ printf(":>\n");
+ }
+ }
+ }
+}
+
+
+/*======================================================================*/
+void resetRules() {
+ int i;
+ for (i = 1; !isEndOfArray(&rules[i-1]); i++) {
+ rulesAdmin[i-1].alreadyRun = FALSE;
+ }
+}
+
+
+/*======================================================================*/
+void evaluateRules(RuleEntry rules[]) {
+ bool change = TRUE;
+ int rule;
+
+ current.location = NOWHERE;
+ current.actor = 0;
+
+ while (change) {
+ change = FALSE;
+ for (rule = 1; !isEndOfArray(&rules[rule-1]); rule++) {
+ traceRuleEvaluation(rule);
+ bool evaluated_value = evaluate(rules[rule-1].exp);
+ traceRuleResult(rule, evaluated_value);
+ if (evaluated_value == true && rulesAdmin[rule-1].lastEval == false
+ && !rulesAdmin[rule-1].alreadyRun) {
+ change = TRUE;
+ traceRuleExecution(rule);
+ interpret(rules[rule-1].stms);
+ rulesAdmin[rule-1].alreadyRun = TRUE;
+ anyRuleRun = TRUE;
+ } else {
+ if (traceSectionOption && !(traceInstructionOption || traceSourceOption))
+ printf(":>\n");
+ }
+ rulesAdmin[rule-1].lastEval = evaluated_value;
+ }
+ }
+}
+
+
+/*=======================================================================*/
+void resetAndEvaluateRules(RuleEntry rules[], char *version) {
+ if (isPreBeta2(version))
+ evaluateRulesPreBeta2();
+ else if (isPreBeta3(version))
+ evaluateRulesBeta2();
+ else {
+ resetRules();
+ evaluateRules(rules);
+ }
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/rules.h b/engines/glk/alan3/rules.h
new file mode 100644
index 0000000000..5b0e449f56
--- /dev/null
+++ b/engines/glk/alan3/rules.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_RULES
+#define GLK_ALAN3_RULES
+
+/* Header file for rules handler in Alan interpreter */
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern RuleEntry *rules; /* Rule table pointer */
+extern bool anyRuleRun; /* Did any rule run? */
+
+/* FUNCTIONS */
+extern void initRules(Aaddr rulesTableAddress);
+extern void resetAndEvaluateRules(RuleEntry rules[], char *version);
+extern void resetRules(void);
+extern void evaluateRules(RuleEntry rules[]);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/save.cpp b/engines/glk/alan3/save.cpp
new file mode 100644
index 0000000000..170cfd4a7d
--- /dev/null
+++ b/engines/glk/alan3/save.cpp
@@ -0,0 +1,359 @@
+/* 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.
+ *
+ */
+
+#ifdef TODO
+
+/*----------------------------------------------------------------------*/
+static void saveStrings(AFILE saveFile) {
+
+ StringInitEntry *initEntry;
+
+ if (header->stringInitTable != 0)
+ for (initEntry = (StringInitEntry *)pointerTo(header->stringInitTable);
+ !isEndOfArray(initEntry); initEntry++) {
+ char *attr = (char *)getInstanceStringAttribute(initEntry->instanceCode, initEntry->attributeCode);
+ Aint length = strlen(attr) + 1;
+ fwrite((void *)&length, sizeof(length), 1, saveFile);
+ fwrite((void *)attr, 1, length, saveFile);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveSets(AFILE saveFile) {
+ SetInitEntry *initEntry;
+
+ if (header->setInitTable != 0)
+ for (initEntry = (SetInitEntry *)pointerTo(header->setInitTable);
+ !isEndOfArray(initEntry); initEntry++) {
+ Set *attr = (Set *)getInstanceSetAttribute(initEntry->instanceCode, initEntry->attributeCode);
+ fwrite((void *)&attr->size, sizeof(attr->size), 1, saveFile);
+ fwrite((void *)attr->members, sizeof(attr->members[0]), attr->size, saveFile);
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveGameInfo(AFILE saveFile) {
+ fwrite((void *)"ASAV", 1, 4, saveFile);
+ fwrite((void *)&header->version, 1, sizeof(Aword), saveFile);
+ fwrite((void *)adventureName, 1, strlen(adventureName)+1, saveFile);
+ fwrite((void *)&header->uid, 1, sizeof(Aword), saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveAdmin(AFILE saveFile) {
+ fwrite((void *)&admin[1], sizeof(AdminEntry), header->instanceMax, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveAttributeArea(AFILE saveFile) {
+ fwrite((void*)attributes, header->attributesAreaSize, sizeof(Aword), saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveEventQueue(AFILE saveFile) {
+ fwrite((void *)&eventQueueTop, sizeof(eventQueueTop), 1, saveFile);
+ fwrite((void *)&eventQueue[0], sizeof(eventQueue[0]), eventQueueTop, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveCurrentValues(AFILE saveFile) {
+ fwrite((void *)&current, sizeof(current), 1, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveScores(AFILE saveFile) {
+ fwrite((void *)scores, sizeof(Aword), header->scoreCount, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void saveGame(AFILE saveFile) {
+ /* Save tag, version of interpreter, name and uid of game */
+ saveGameInfo(saveFile);
+
+ /* Save current values */
+ saveCurrentValues(saveFile);
+
+ saveAttributeArea(saveFile);
+ saveAdmin(saveFile);
+
+ saveEventQueue(saveFile);
+
+ saveScores(saveFile);
+
+ saveStrings(saveFile);
+ saveSets(saveFile);
+}
+
+
+/*======================================================================*/
+void save(void)
+{
+#ifdef HAVE_GLK
+ frefid_t saveFileRef;
+ strid_t saveFile;
+ saveFileRef = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Write, 0);
+ if (saveFileRef == NULL)
+ error(M_SAVEFAILED);
+ saveFile = glk_stream_open_file(saveFileRef, filemode_Write, 0);
+
+#else
+ FILE *saveFile;
+ char str[256];
+
+ current.location = where(HERO, DIRECT);
+ /* First save ? */
+ if (saveFileName[0] == '\0') {
+ strcpy(saveFileName, adventureName);
+ strcat(saveFileName, ".sav");
+ }
+ printMessage(M_SAVEWHERE);
+ sprintf(str, "(%s) : ", saveFileName);
+ output(str);
+#ifdef USE_READLINE
+ readline(str);
+#else
+ gets(str);
+#endif
+ if (str[0] == '\0')
+ strcpy(str, saveFileName);
+ col = 1;
+ if ((saveFile = fopen(str, READ_MODE)) != NULL)
+ /* It already existed */
+ if (!regressionTestOption) {
+ /* Ask for overwrite confirmation */
+ if (!confirm(M_SAVEOVERWRITE))
+ abortPlayerCommand(); /* Return to player without saying anything */
+ }
+ strcpy(saveFileName, str);
+ if ((saveFile = fopen(saveFileName, WRITE_MODE)) == NULL)
+ error(M_SAVEFAILED);
+#endif
+
+ saveGame(saveFile);
+
+ fclose(saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreStrings(AFILE saveFile) {
+ StringInitEntry *initEntry;
+
+ if (header->stringInitTable != 0)
+ for (initEntry = (StringInitEntry *)pointerTo(header->stringInitTable);
+ !isEndOfArray(initEntry); initEntry++) {
+ Aint length;
+ char *string;
+ fread((void *)&length, sizeof(Aint), 1, saveFile);
+ string = allocate(length+1);
+ fread((void *)string, 1, length, saveFile);
+ setInstanceAttribute(initEntry->instanceCode, initEntry->attributeCode, toAptr(string));
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreSets(AFILE saveFile) {
+ SetInitEntry *initEntry;
+
+ if (header->setInitTable != 0)
+ for (initEntry = (SetInitEntry *)pointerTo(header->setInitTable);
+ !isEndOfArray(initEntry); initEntry++) {
+ Aint setSize;
+ Set *set;
+ int i;
+
+ fread((void *)&setSize, sizeof(setSize), 1, saveFile);
+ set = newSet(setSize);
+ for (i = 0; i < setSize; i++) {
+ Aword member;
+ fread((void *)&member, sizeof(member), 1, saveFile);
+ addToSet(set, member);
+ }
+ setInstanceAttribute(initEntry->instanceCode, initEntry->attributeCode, toAptr(set));
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreScores(AFILE saveFile) {
+ fread((void *)scores, sizeof(Aword), header->scoreCount, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreEventQueue(AFILE saveFile) {
+ fread((void *)&eventQueueTop, sizeof(eventQueueTop), 1, saveFile);
+ if (eventQueueTop > eventQueueSize) {
+ deallocate(eventQueue);
+ eventQueue = allocate(eventQueueTop*sizeof(eventQueue[0]));
+ }
+ fread((void *)&eventQueue[0], sizeof(eventQueue[0]), eventQueueTop, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreAdmin(AFILE saveFile) {
+ /* Restore admin for instances, remember to reset attribute area pointer */
+ int i;
+ for (i = 1; i <= header->instanceMax; i++) {
+ AttributeEntry *currentAttributesArea = admin[i].attributes;
+ fread((void *)&admin[i], sizeof(AdminEntry), 1, saveFile);
+ admin[i].attributes = currentAttributesArea;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreAttributeArea(AFILE saveFile) {
+ fread((void *)attributes, header->attributesAreaSize, sizeof(Aword), saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreCurrentValues(AFILE saveFile) {
+ fread((void *)&current, sizeof(current), 1, saveFile);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void verifyGameId(AFILE saveFile) {
+ Aword savedUid;
+
+ fread((void *)&savedUid, sizeof(Aword), 1, saveFile);
+ if (!ignoreErrorOption && savedUid != header->uid)
+ error(M_SAVEVERS);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void verifyGameName(AFILE saveFile) {
+ char savedName[256];
+ int i = 0;
+
+ while ((savedName[i++] = fgetc(saveFile)) != '\0');
+ if (strcmp(savedName, adventureName) != 0)
+ error(M_SAVENAME);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void verifyCompilerVersion(AFILE saveFile) {
+ char savedVersion[4];
+
+ fread((void *)&savedVersion, sizeof(Aword), 1, saveFile);
+ if (!ignoreErrorOption && strncmp(savedVersion, header->version, 4))
+ error(M_SAVEVERS);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void verifySaveFile(AFILE saveFile) {
+ char string[256];
+
+ fread((void *)&string, 1, 4, saveFile);
+ string[4] = '\0';
+ if (strcmp(string, "ASAV") != 0)
+ error(M_NOTASAVEFILE);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void restoreGame(AFILE saveFile)
+{
+ if (saveFile == NULL) syserr("'restoreGame()' from a null fileref");
+
+ verifySaveFile(saveFile);
+
+ /* Verify version of compiler/interpreter of saved game with us */
+ verifyCompilerVersion(saveFile);
+
+ /* Verify name of game */
+ verifyGameName(saveFile);
+
+ /* Verify unique id of game */
+ verifyGameId(saveFile);
+
+ restoreCurrentValues(saveFile);
+ restoreAttributeArea(saveFile);
+ restoreAdmin(saveFile);
+ restoreEventQueue(saveFile);
+ restoreScores(saveFile);
+ restoreStrings(saveFile);
+ restoreSets(saveFile);
+}
+
+
+/*======================================================================*/
+void restore(void)
+{
+#ifdef HAVE_GLK
+ frefid_t saveFileRef;
+ strid_t saveFile;
+ saveFileRef = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Read, 0);
+ if (saveFileRef == NULL) return;
+ saveFile = glk_stream_open_file(saveFileRef, filemode_Read, 0);
+ if (saveFile == NULL) return;
+
+#else
+ char str[1000];
+ FILE *saveFile;
+
+ current.location = where(HERO, DIRECT);
+ /* First save ? */
+ if (saveFileName[0] == '\0') {
+ strcpy(saveFileName, adventureName);
+ strcat(saveFileName, ".sav");
+ }
+ printMessage(M_RESTOREFROM);
+ sprintf(str, "(%s) : ", saveFileName);
+ output(str);
+#ifdef USE_READLINE
+ readline(str);
+#else
+ gets(str);
+#endif
+
+ col = 1;
+ if (str[0] == '\0') {
+ strcpy(str, saveFileName);
+ }
+ if ((saveFile = fopen(str, READ_MODE)) == NULL)
+ error(M_SAVEMISSING);
+ strcpy(saveFileName, str); /* Save it for future use */
+
+#endif
+
+ restoreGame(saveFile);
+
+ fclose(saveFile);
+}
+
+#endif
diff --git a/engines/glk/alan3/save.h b/engines/glk/alan3/save.h
new file mode 100644
index 0000000000..3f12f05ccc
--- /dev/null
+++ b/engines/glk/alan3/save.h
@@ -0,0 +1,38 @@
+/* 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_ALAN3_SAVE
+#define GLK_ALAN3_SAVE
+
+/* Header file for save and restore unit in Alan interpreter */
+
+namespace Glk {
+namespace Alan3 {
+
+/* Functions: */
+extern void save();
+extern void restore();
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/scan.cpp b/engines/glk/alan3/scan.cpp
new file mode 100644
index 0000000000..9c632023f7
--- /dev/null
+++ b/engines/glk/alan3/scan.cpp
@@ -0,0 +1,255 @@
+/* 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/alan3/scan.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/dictionary.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/literal.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/params.h"
+#include "glk/alan3/readline.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/word.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+bool continued = FALSE;
+
+
+/* PRIVATE DATA */
+static char buf[1000]; /* The input buffer */
+static char isobuf[1000]; /* The input buffer in ISO */
+static bool eol = TRUE; /* Looking at End of line? Yes, initially */
+static char *token = NULL;
+
+
+/*======================================================================*/
+void forceNewPlayerInput() {
+ setEndOfArray(&playerWords[currentWordIndex]);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void unknown(char tok[]) {
+ char *str = strdup(tok);
+ Parameter *messageParameters = newParameterArray();
+
+#if ISO == 0
+ fromIso(str, str);
+#endif
+ addParameterForString(messageParameters, str);
+ printMessageWithParameters(M_UNKNOWN_WORD, messageParameters);
+ deallocate(messageParameters);
+ free(str);
+ abortPlayerCommand();
+}
+
+
+/*----------------------------------------------------------------------*/
+static int number(char tok[]) {
+ int i;
+
+ sscanf(tok, "%d", &i);
+ return i;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int lookup(char wrd[]) {
+ int i;
+
+ for (i = 0; !isEndOfArray(&dictionary[i]); i++) {
+ if (compareStrings(wrd, (char *) pointerTo(dictionary[i].string)) == 0) {
+ return (i);
+ }
+ }
+ unknown(wrd);
+ return (EOF);
+}
+
+
+/*----------------------------------------------------------------------*/
+static bool isWordCharacter(int ch) {
+ return isISOLetter(ch) || isdigit(ch) || ch == '\'' || ch == '-' || ch == '_';
+}
+
+/*----------------------------------------------------------------------*/
+static char *gettoken(char *txtBuf) {
+ static char *marker;
+ static char oldch;
+
+ if (txtBuf == NULL)
+ *marker = oldch;
+ else
+ marker = txtBuf;
+ while (*marker != '\0' && isSpace(*marker) && *marker != '\n')
+ marker++;
+ txtBuf = marker;
+ if (isISOLetter(*marker))
+ while (*marker && isWordCharacter(*marker))
+ marker++;
+ else if (isdigit((int)*marker))
+ while (isdigit((int)*marker))
+ marker++;
+ else if (*marker == '\"') {
+ marker++;
+ while (*marker != '\"')
+ marker++;
+ marker++;
+ } else if (*marker == '\0' || *marker == '\n' || *marker == ';')
+ return NULL;
+ else
+ marker++;
+ oldch = *marker;
+ *marker = '\0';
+ return txtBuf;
+}
+
+
+/*----------------------------------------------------------------------*/
+// TODO replace dependency to exe.c with injection of quitGame() and undo()
+static void getLine(void) {
+ para();
+ do {
+ statusline();
+ if (header->prompt) {
+ anyOutput = FALSE;
+ interpret(header->prompt);
+ if (anyOutput)
+ printAndLog(" ");
+ needSpace = FALSE;
+ } else
+ printAndLog("> ");
+
+#ifdef USE_READLINE
+ if (!readline(buf)) {
+#else
+ fflush(stdout);
+ if (fgets(buf, LISTLEN, stdin) == NULL) {
+#endif
+ newline();
+ quitGame();
+ }
+
+ getPageSize();
+ anyOutput = FALSE;
+ if (transcriptOption || logOption) {
+ // TODO: Refactor out the logging to log.c?
+ g_vm->glk_put_string_stream(logFile, buf);
+ g_vm->glk_put_char_stream(logFile, '\n');
+ }
+ /* If the player input an empty command he forfeited his command */
+#ifdef TODO
+ if (strlen(buf) == 0) {
+ clearWordList(playerWords);
+ longjmp(forfeitLabel, 0);
+ }
+#else
+ ::error("TODO: empty command");
+#endif
+
+#if ISO == 0
+ toIso(isobuf, buf, NATIVECHARSET);
+#else
+ strcpy(isobuf, buf);
+#endif
+ token = gettoken(isobuf);
+ if (token != NULL) {
+ if (strcmp("debug", token) == 0 && header->debug) {
+ debugOption = TRUE;
+ debug(FALSE, 0, 0);
+ token = NULL;
+ } else if (strcmp("undo", token) == 0) {
+ token = gettoken(NULL);
+ if (token != NULL) /* More tokens? */
+ error(M_WHAT);
+ undo();
+ }
+ }
+ } while (token == NULL);
+ eol = FALSE;
+}
+
+
+
+/*======================================================================*/
+void scan(void) {
+ int i;
+ int w;
+
+ if (continued) {
+ /* Player used '.' to separate commands. Read next */
+ para();
+ token = gettoken(NULL); /* Or did he just finish the command with a full stop? */
+ if (token == NULL)
+ getLine();
+ continued = FALSE;
+ } else
+ getLine();
+
+ freeLiterals();
+ playerWords[0].code = 0; // TODO This means what?
+ i = 0;
+ do {
+ ensureSpaceForPlayerWords(i+1);
+ playerWords[i].start = token;
+ playerWords[i].end = strchr(token, '\0');
+ if (isISOLetter(token[0])) {
+ w = lookup(token);
+ if (!isNoise(w))
+ playerWords[i++].code = w;
+ } else if (isdigit((int)token[0]) || token[0] == '\"') {
+ if (isdigit((int)token[0])) {
+ createIntegerLiteral(number(token));
+ } else {
+ char *unquotedString = strdup(token);
+ unquotedString[strlen(token) - 1] = '\0';
+ createStringLiteral(&unquotedString[1]);
+ free(unquotedString);
+ }
+ playerWords[i++].code = dictionarySize + litCount; /* Word outside dictionary = literal */
+ } else if (token[0] == ',') {
+ playerWords[i++].code = conjWord;
+ } else if (token[0] == '.') {
+ continued = TRUE;
+ setEndOfArray(&playerWords[i]);
+ eol = TRUE;
+ break;
+ } else
+ unknown(token);
+ setEndOfArray(&playerWords[i]);
+ eol = (token = gettoken(NULL)) == NULL;
+ } while (!eol);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/scan.h b/engines/glk/alan3/scan.h
new file mode 100644
index 0000000000..04d893765c
--- /dev/null
+++ b/engines/glk/alan3/scan.h
@@ -0,0 +1,45 @@
+/* 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_ALAN3_SCAN
+#define GLK_ALAN3_SCAN
+
+/* Player input scanner for ALAN interpreter module. */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern bool continued;
+
+
+/* FUNCTIONS */
+
+extern void forceNewPlayerInput();
+extern void scan();
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/score.cpp b/engines/glk/alan3/score.cpp
new file mode 100644
index 0000000000..f22fa14104
--- /dev/null
+++ b/engines/glk/alan3/score.cpp
@@ -0,0 +1,32 @@
+/* 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/alan3/score.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+Aword *scores; // Score table pointer
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/score.h b/engines/glk/alan3/score.h
new file mode 100644
index 0000000000..c17ed70314
--- /dev/null
+++ b/engines/glk/alan3/score.h
@@ -0,0 +1,37 @@
+/* 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_ALAN3_SCORE
+#define GLK_ALAN3_SCORE
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern Aword *scores;
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/set.cpp b/engines/glk/alan3/set.cpp
new file mode 100644
index 0000000000..1b4253abef
--- /dev/null
+++ b/engines/glk/alan3/set.cpp
@@ -0,0 +1,176 @@
+/* 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/alan3/set.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/instance.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define EXTENT 5
+
+/*======================================================================*/
+Set *newSet(int allocation) {
+ Set *theSet = NEW(Set);
+
+ if (allocation) {
+ theSet->members = (Aword *)allocate(allocation*sizeof(theSet->members[0]));
+ theSet->size = 0;
+ theSet->allocated = allocation;
+ }
+ return theSet;
+}
+
+
+/*======================================================================*/
+void initSets(SetInitEntry *initTable)
+{
+ SetInitEntry *init;
+ int i;
+
+ for (init = initTable; !isEndOfArray(init); init++) {
+ Set *set = newSet(init->size);
+ Aword *member = (Aword *)pointerTo(init->setAddress);
+ for (i = 0; i < init->size; i++, member++)
+ addToSet(set, *member);
+ setInstanceAttribute(init->instanceCode, init->attributeCode, toAptr(set));
+ }
+}
+
+
+/*======================================================================*/
+int setSize(Set *theSet) {
+ return theSet->size;
+}
+
+
+/*======================================================================*/
+void clearSet(Set *theSet) {
+ theSet->size = 0;
+}
+
+
+/*======================================================================*/
+Set *copySet(Set *theSet) {
+ Set *nset = newSet(theSet->size);
+ int i;
+
+ for (i = 1; i <= theSet->size; i++)
+ addToSet(nset, getSetMember(theSet, i));
+ return nset;
+}
+
+
+/*======================================================================*/
+Aword getSetMember(Set *theSet, Aint theMember) {
+ if (theMember > theSet->size || theMember < 1)
+ apperr("Accessing nonexisting member in a set");
+ return theSet->members[theMember-1];
+}
+
+
+/*======================================================================*/
+bool inSet(Set *theSet, Aword member)
+{
+ int i;
+
+ for (i = 1; i <= theSet->size; i++)
+ if (getSetMember(theSet, i) == member)
+ return TRUE;
+ return FALSE;
+}
+
+
+/*=======================================================================*/
+Set *setUnion(Set *set1, Set *set2)
+{
+ Set *theUnion = newSet(set1->size+set2->size);
+ int i;
+
+ for (i = 0; i < set1->size; i++)
+ addToSet(theUnion, set1->members[i]);
+ for (i = 0; i < set2->size; i++)
+ addToSet(theUnion, set2->members[i]);
+ return theUnion;
+}
+
+
+/*=======================================================================*/
+void addToSet(Set *theSet, Aword newMember)
+{
+ if (inSet(theSet, newMember)) return;
+ if (theSet->size == theSet->allocated) {
+ theSet->allocated += EXTENT;
+ theSet->members = (Aword *)realloc(theSet->members, theSet->allocated*sizeof(theSet->members[0]));
+ }
+ theSet->members[theSet->size] = newMember;
+ theSet->size++;
+}
+
+
+/*=======================================================================*/
+void removeFromSet(Set *theSet, Aword member)
+{
+ int i, j;
+
+ if (!inSet(theSet, member)) return;
+
+ for (i = 0; i < theSet->size; i++) {
+ if ((Aword)theSet->members[i] == member) {
+ for (j = i; j < theSet->size-1; j++)
+ theSet->members[j] = theSet->members[j+1];
+ theSet->size--;
+ break;
+ }
+ }
+}
+
+
+/*=======================================================================*/
+bool equalSets(Set *set1, Set *set2)
+{
+ int i;
+
+ if (set1->size != set2->size) return FALSE;
+
+ for (i = 0; i < set1->size; i++) {
+ if (!inSet(set2, set1->members[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*======================================================================*/
+void freeSet(Set *theSet) {
+ if (theSet != NULL) {
+ if (theSet->members != NULL)
+ deallocate(theSet->members);
+ deallocate(theSet);
+ }
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/set.h b/engines/glk/alan3/set.h
new file mode 100644
index 0000000000..3fb751c418
--- /dev/null
+++ b/engines/glk/alan3/set.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_SET
+#define GLK_ALAN3_SET
+
+/* Abstract datatype Set for Alan interpreter
+
+ A set is implemented as a struct holding a size and a
+ dynamically allocated array of members. Members can be
+ integers or instance numbers. Attributes of Set type is
+ allocated and the pointer to it is used as the attribute
+ value. As members are only references, clearing a set can
+ simply be done by setting the size to zero.
+*/
+
+#include "glk/alan3/acode.h"
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+struct Set {
+ int size;
+ int allocated;
+ Aword *members;
+};
+
+extern Set *newSet(int size);
+extern void initSets(SetInitEntry *initTable);
+extern int setSize(Set *theSet);
+extern void clearSet(Set *theSet);
+extern Set *copySet(Set *theSet);
+extern Aword getSetMember(Set *theSet, Aint member);
+extern bool inSet(Set *theSet, Aword member);
+extern void addToSet(Set *theSet, Aword newMember);
+extern void removeFromSet(Set *theSet, Aword member);
+extern Set *setUnion(Set *theSet, Set *other);
+extern bool equalSets(Set *theSet, Set *other);
+extern void freeSet(Set *theSet);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/stack.cpp b/engines/glk/alan3/stack.cpp
new file mode 100644
index 0000000000..a845afff53
--- /dev/null
+++ b/engines/glk/alan3/stack.cpp
@@ -0,0 +1,185 @@
+/* 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/alan3/stack.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/syserr.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*======================================================================*/
+Stack createStack(int size)
+{
+ StackStructure *theStack = NEW(StackStructure);
+
+ theStack->stack = (Aword *)allocate(size*sizeof(Aptr));
+ theStack->stackSize = size;
+ theStack->framePointer = -1;
+
+ return theStack;
+}
+
+
+/*======================================================================*/
+void deleteStack(Stack theStack)
+{
+ if (theStack == NULL)
+ syserr("deleting a NULL stack");
+
+ deallocate(theStack->stack);
+ deallocate(theStack);
+}
+
+
+/*======================================================================*/
+int stackDepth(Stack theStack) {
+ return theStack->stackp;
+}
+
+
+/*======================================================================*/
+void dumpStack(Stack theStack)
+{
+ int i;
+
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ printf("[");
+ for (i = 0; i < theStack->stackp; i++)
+ printf("%ld ", (unsigned long) theStack->stack[i]);
+ printf("]");
+ if (!traceInstructionOption && !tracePushOption)
+ printf("\n");
+}
+
+
+/*======================================================================*/
+void push(Stack theStack, Aptr i)
+{
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ if (theStack->stackp == theStack->stackSize)
+ syserr("Out of stack space.");
+ theStack->stack[(theStack->stackp)++] = i;
+}
+
+
+/*======================================================================*/
+Aptr pop(Stack theStack)
+{
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ if (theStack->stackp == 0)
+ syserr("Stack underflow.");
+ return theStack->stack[--(theStack->stackp)];
+}
+
+
+/*======================================================================*/
+Aptr top(Stack theStack)
+{
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ return theStack->stack[theStack->stackp-1];
+}
+
+
+/* The AMACHINE Block Frames */
+
+/*======================================================================*/
+void newFrame(Stack theStack, Aint noOfLocals)
+{
+ int n;
+
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ push(theStack, theStack->framePointer);
+ theStack->framePointer = theStack->stackp;
+ for (n = 0; n < noOfLocals; n++)
+ push(theStack, 0);
+}
+
+
+/*======================================================================*/
+/* Local variables are numbered 1 and up and stored on their index-1 */
+Aptr getLocal(Stack theStack, Aint framesBelow, Aint variableNumber)
+{
+ int frame;
+ int frameCount;
+
+ if (variableNumber < 1)
+ syserr("Reading a non-existing block-local variable.");
+
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ frame = theStack->framePointer;
+
+ if (framesBelow != 0)
+ for (frameCount = framesBelow; frameCount != 0; frameCount--)
+ frame = theStack->stack[frame-1];
+
+ return theStack->stack[frame + variableNumber-1];
+}
+
+
+/*======================================================================*/
+void setLocal(Stack theStack, Aint framesBelow, Aint variableNumber, Aptr value)
+{
+ int frame;
+ int frameCount;
+
+ if (variableNumber < 1)
+ syserr("Writing a non-existing block-local variable.");
+
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ frame = theStack->framePointer;
+ if (framesBelow != 0)
+ for (frameCount = framesBelow; frameCount != 0; frameCount--)
+ frame = theStack->stack[frame-1];
+
+ theStack->stack[frame + variableNumber-1] = value;
+}
+
+/*======================================================================*/
+void endFrame(Stack theStack)
+{
+ if (theStack == NULL)
+ syserr("NULL stack not supported anymore");
+
+ theStack->stackp = theStack->framePointer;
+ theStack->framePointer = pop(theStack);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/stack.h b/engines/glk/alan3/stack.h
new file mode 100644
index 0000000000..88e589f4c3
--- /dev/null
+++ b/engines/glk/alan3/stack.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_STACK
+#define GLK_ALAN3_STACK
+
+/* Header file for stack handler in Alan interpreter */
+
+#include "glk/alan3/acode.h"
+
+namespace Glk {
+namespace Alan3 {
+
+struct StackStructure {
+ Aword *stack; // Array that can take Awords
+ int stackSize;
+ int stackp;
+ int framePointer;
+};
+
+typedef StackStructure *Stack;
+
+/* FUNCTIONS: */
+
+/* NB: The stack uses Aptr size elements since we need to be able to store pointers to allocated memory */
+
+extern Stack createStack(int size);
+extern void deleteStack(Stack stack);
+extern void dumpStack(Stack theStack);
+extern Aptr pop(Stack stack);
+extern void push(Stack stack, Aptr item);
+extern Aptr top(Stack theStack);
+extern int stackDepth(Stack theStack);
+
+extern void newFrame(Stack theStack, Aint noOfLocals);
+extern void setLocal(Stack theStack, Aint blocksBelow, Aint variableNumber, Aptr value);
+extern Aptr getLocal(Stack theStack, Aint level, Aint variable);
+extern void endFrame(Stack theStack);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/state.cpp b/engines/glk/alan3/state.cpp
new file mode 100644
index 0000000000..4112659201
--- /dev/null
+++ b/engines/glk/alan3/state.cpp
@@ -0,0 +1,358 @@
+/* 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/alan3/state.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/word.h"
+#include "glk/alan3/state_stack.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/attribute.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/score.h"
+#include "glk/alan3/event.h"
+#include "glk/alan3/set.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PRIVATE TYPES */
+
+/* Implementation of the abstract type typedef struct game_state GameState */
+struct game_state {
+ /* Event queue */
+ EventQueueEntry *eventQueue;
+ int eventQueueTop; /* Event queue top pointer */
+
+ /* Scores */
+ int score;
+ Aword *scores; /* Score table pointer */
+
+ /* Instance data */
+ AdminEntry *admin; /* Administrative data about instances */
+ AttributeEntry *attributes; /* Attributes data area */
+ /* Sets and strings are dynamically allocated areas for which the
+ attribute is just a pointer to. So they are not catched by the
+ saving of attributes, instead they require special storage */
+ Set **sets; /* Array of set pointers */
+ char **strings; /* Array of string pointers */
+};
+
+/* PRIVATE DATA */
+static GameState gameState; /* TODO: Make pointer, then we don't have to copy to stack, we can just use the pointer */
+static StateStackP stateStack = NULL;
+
+static char *playerCommand;
+
+
+/*----------------------------------------------------------------------*/
+static int countStrings(void) {
+ StringInitEntry *entry;
+ int count = 0;
+
+ if (header->stringInitTable != 0)
+ for (entry = (StringInitEntry *)pointerTo(header->stringInitTable); *(Aword *)entry != EOF; entry++)
+ count++;
+ return(count);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void deallocateStrings(GameState *gState) {
+ int count = countStrings();
+ int i;
+
+ for (i = 0; i < count; i++)
+ deallocate(gState->strings[i]);
+ deallocate(gState->strings);
+}
+
+/*----------------------------------------------------------------------*/
+static int countSets(void) {
+ SetInitEntry *entry;
+ int count = 0;
+
+ if (header->setInitTable != 0)
+ for (entry = (SetInitEntry *)pointerTo(header->setInitTable); *(Aword *)entry != EOF; entry++)
+ count++;
+ return(count);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void deallocateSets(GameState *gameState) {
+ int count = countSets();
+ int i;
+
+ for (i = 0; i < count; i++)
+ freeSet(gameState->sets[i]);
+ deallocate(gameState->sets);
+}
+
+/*======================================================================*/
+void deallocateGameState(GameState *gameState) {
+
+ deallocate(gameState->admin);
+ deallocate(gameState->attributes);
+
+ if (gameState->eventQueueTop > 0) {
+ deallocate(gameState->eventQueue);
+ gameState->eventQueue = NULL;
+ }
+ if (gameState->scores)
+ deallocate(gameState->scores);
+
+ deallocateStrings(gameState);
+ deallocateSets(gameState);
+
+ memset(gameState, 0, sizeof(GameState));
+}
+
+
+/*======================================================================*/
+void forgetGameState(void) {
+ char *playerCommand;
+ popGameState(stateStack, &gameState, &playerCommand);
+ deallocateGameState(&gameState);
+ if (playerCommand != NULL)
+ deallocate(playerCommand);
+}
+
+
+/*======================================================================*/
+void initStateStack(void) {
+ if (stateStack != NULL)
+ deleteStateStack(stateStack);
+ stateStack = createStateStack(sizeof(GameState));
+}
+
+
+/*======================================================================*/
+void terminateStateStack(void) {
+ deleteStateStack(stateStack);
+ stateStack = NULL;
+}
+
+
+/*======================================================================*/
+bool anySavedState(void) {
+ return !stateStackIsEmpty(stateStack);
+}
+
+
+/*----------------------------------------------------------------------*/
+static Set **collectSets(void) {
+ SetInitEntry *entry;
+ int count = countSets();
+ Set **sets;
+ int i;
+
+ if (count == 0) return NULL;
+
+ sets = (Set **)allocate(count*sizeof(Set));
+
+ entry = (SetInitEntry *)pointerTo(header->setInitTable);
+ for (i = 0; i < count; i++)
+ sets[i] = getInstanceSetAttribute(entry[i].instanceCode, entry[i].attributeCode);
+
+ return sets;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char **collectStrings(void) {
+ StringInitEntry *entry;
+ int count = countStrings();
+ char **strings;
+ int i;
+
+ if (count == 0) return NULL;
+
+ strings = (char **)allocate(count*sizeof(char *));
+
+ entry = (StringInitEntry *)pointerTo(header->stringInitTable);
+ for (i = 0; i < count; i++)
+ strings[i] = getInstanceStringAttribute(entry[i].instanceCode, entry[i].attributeCode);
+
+ return strings;
+}
+
+
+/*======================================================================*/
+void rememberCommands(void) {
+ char *command = playerWordsAsCommandString();
+ attachPlayerCommandsToLastState(stateStack, command);
+ deallocate(command);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void collectEvents(void) {
+ gameState.eventQueueTop = eventQueueTop;
+ if (eventQueueTop > 0)
+ gameState.eventQueue = (EventQueueEntry *)duplicate(eventQueue, eventQueueTop*sizeof(EventQueueEntry));
+}
+
+
+/*----------------------------------------------------------------------*/
+static void collectInstanceData(void) {
+ gameState.admin = (AdminEntry *)duplicate(admin, (header->instanceMax+1)*sizeof(AdminEntry));
+ gameState.attributes = (AttributeEntry *)duplicate(attributes, header->attributesAreaSize*sizeof(Aword));
+ gameState.sets = collectSets();
+ gameState.strings = collectStrings();
+}
+
+
+/*----------------------------------------------------------------------*/
+static void collectScores(void) {
+ gameState.score = current.score;
+ if (scores == NULL)
+ gameState.scores = NULL;
+ else
+ gameState.scores = (Aword *)duplicate(scores, header->scoreCount*sizeof(Aword));
+}
+
+
+/*======================================================================*/
+void rememberGameState(void) {
+ collectEvents();
+ collectInstanceData();
+ collectScores();
+
+ if (stateStack == NULL)
+ initStateStack();
+
+ pushGameState(stateStack, &gameState);
+ gameStateChanged = FALSE;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void freeCurrentSetAttributes(void) {
+ SetInitEntry *entry;
+
+ if (header->setInitTable == 0) return;
+ for (entry = (SetInitEntry *)pointerTo(header->setInitTable); *(Aword *)entry != EOF; entry++) {
+ Aptr attributeValue = getAttribute(admin[entry->instanceCode].attributes, entry->attributeCode);
+ freeSet((Set*)fromAptr(attributeValue));
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void recallSets(Set **sets) {
+ SetInitEntry *entry;
+ int count = countSets();
+ int i;
+
+ if (header->setInitTable == 0) return;
+
+ entry = (SetInitEntry *)pointerTo(header->setInitTable);
+ for (i = 0; i < count; i++) {
+ setAttribute(admin[entry[i].instanceCode].attributes, entry[i].attributeCode, toAptr(sets[i]));
+ sets[i] = NULL; /* Since we reuse the saved set, we need to clear the pointer */
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void freeCurrentStringAttributes(void) {
+ StringInitEntry *entry;
+
+ if (header->stringInitTable == 0) return;
+ for (entry = (StringInitEntry *)pointerTo(header->stringInitTable); *(Aword *)entry != EOF; entry++) {
+ Aptr attributeValue = getAttribute(admin[entry->instanceCode].attributes, entry->attributeCode);
+ deallocate(fromAptr(attributeValue));
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void recallStrings(char **strings) {
+ StringInitEntry *entry;
+ int count = countStrings();
+ int i;
+
+ if (header->stringInitTable == 0) return;
+
+ entry = (StringInitEntry *)pointerTo(header->stringInitTable);
+ for (i = 0; i < count; i++) {
+ setAttribute(admin[entry[i].instanceCode].attributes, entry[i].attributeCode, toAptr(strings[i]));
+ strings[i] = NULL; /* Since we reuse the saved, we need to clear the state */
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void recallEvents(void) {
+ eventQueueTop = gameState.eventQueueTop;
+ if (eventQueueTop > 0) {
+ memcpy(eventQueue, gameState.eventQueue,
+ (eventQueueTop+1)*sizeof(EventQueueEntry));
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void recallInstances(void) {
+
+ if (admin == NULL)
+ syserr("admin[] == NULL in recallInstances()");
+
+ memcpy(admin, gameState.admin,
+ (header->instanceMax+1)*sizeof(AdminEntry));
+
+ freeCurrentSetAttributes(); /* Need to free previous set values */
+ freeCurrentStringAttributes(); /* Need to free previous string values */
+
+ memcpy(attributes, gameState.attributes,
+ header->attributesAreaSize*sizeof(Aword));
+
+ recallSets(gameState.sets);
+ recallStrings(gameState.strings);
+}
+
+
+/*----------------------------------------------------------------------*/
+static void recallScores(void) {
+ current.score = gameState.score;
+ memcpy(scores, gameState.scores, header->scoreCount*sizeof(Aword));
+}
+
+
+/*======================================================================*/
+void recallGameState(void) {
+ popGameState(stateStack, &gameState, &playerCommand);
+ recallEvents();
+ recallInstances();
+ recallScores();
+ deallocateGameState(&gameState);
+}
+
+
+/*======================================================================*/
+char *recreatePlayerCommand(void) {
+ return playerCommand;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/state.h b/engines/glk/alan3/state.h
new file mode 100644
index 0000000000..07056ee2f2
--- /dev/null
+++ b/engines/glk/alan3/state.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_ALAN3_STATE
+#define GLK_ALAN3_STATE
+
+/* Header file for instruction state and undo handling in Alan interpreter */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+struct game_state;
+typedef struct game_state GameState;
+
+/* FUNCTIONS */
+extern bool anySavedState(void);
+extern void initStateStack(void);
+extern void rememberGameState(void);
+extern void forgetGameState(void);
+extern void rememberCommands(void);
+extern void recallGameState(void);
+extern char *recreatePlayerCommand(void);
+extern void terminateStateStack(void);
+extern void deallocateGameState(GameState *gameState);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/state_stack.cpp b/engines/glk/alan3/state_stack.cpp
new file mode 100644
index 0000000000..38e8bb66ec
--- /dev/null
+++ b/engines/glk/alan3/state_stack.cpp
@@ -0,0 +1,128 @@
+/* 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/alan3/state_stack.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/state.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* CONSTANTS: */
+#define EXTENT 10
+
+
+/* PRIVATE TYPES: */
+struct StateStackStructure {
+ void **states;
+ char **commands;
+ int stackSize;
+ int stackPointer; /* Points above used stack, 0 initially */
+ int elementSize; /* Size of elements in the stack */
+};
+
+
+/*----------------------------------------------------------------------*/
+static void *reallocate(void *from, int newSize)
+{
+ void *newArea = realloc(from, newSize*sizeof(void*));
+ if (newArea == NULL)
+ syserr("Out of memory in 'reallocateStack()'");
+ return newArea;
+}
+
+/*======================================================================*/
+StateStackP createStateStack(int elementSize) {
+ StateStackP stack = NEW(StateStackStructure);
+ stack->stackSize = 0;
+ stack->stackPointer = 0;
+ stack->elementSize = elementSize;
+ return stack;
+}
+
+
+/*======================================================================*/
+void deleteStateStack(StateStackP stateStack) {
+ if (stateStack != NULL) {
+ while (stateStack->stackPointer > 0) {
+ stateStack->stackPointer--;
+ deallocateGameState((GameState *)stateStack->states[stateStack->stackPointer]);
+ deallocate(stateStack->states[stateStack->stackPointer]);
+ deallocate(stateStack->commands[stateStack->stackPointer]);
+ }
+ if (stateStack->stackSize > 0) {
+ deallocate(stateStack->states);
+ deallocate(stateStack->commands);
+ }
+ deallocate(stateStack);
+ }
+}
+
+
+/*======================================================================*/
+bool stateStackIsEmpty(StateStackP stateStack) {
+ return stateStack->stackPointer == 0;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void ensureSpaceForGameState(StateStackP stack)
+{
+ if (stack->stackPointer == stack->stackSize) {
+ stack->states = (void **)reallocate(stack->states, stack->stackSize+EXTENT);
+ stack->commands = (char **)reallocate(stack->commands, stack->stackSize+EXTENT);
+ stack->stackSize += EXTENT;
+ }
+}
+
+
+/*======================================================================*/
+void pushGameState(StateStackP stateStack, void *gameState) {
+ void *element = allocate(stateStack->elementSize);
+ memcpy(element, gameState, stateStack->elementSize);
+ ensureSpaceForGameState(stateStack);
+ stateStack->commands[stateStack->stackPointer] = NULL;
+ stateStack->states[stateStack->stackPointer++] = element;
+}
+
+
+/*======================================================================*/
+void attachPlayerCommandsToLastState(StateStackP stateStack, char *playerCommands) {
+ stateStack->commands[stateStack->stackPointer-1] = strdup(playerCommands);
+}
+
+
+/*======================================================================*/
+void popGameState(StateStackP stateStack, void *gameState, char** playerCommand) {
+ if (stateStack->stackPointer == 0)
+ syserr("Popping GameState from empty stack");
+ else {
+ stateStack->stackPointer--;
+ memcpy(gameState, stateStack->states[stateStack->stackPointer], stateStack->elementSize);
+ deallocate(stateStack->states[stateStack->stackPointer]);
+ *playerCommand = stateStack->commands[stateStack->stackPointer];
+ }
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/state_stack.h b/engines/glk/alan3/state_stack.h
new file mode 100644
index 0000000000..268b29daf3
--- /dev/null
+++ b/engines/glk/alan3/state_stack.h
@@ -0,0 +1,45 @@
+/* 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_ALAN3_STATE_STACK
+#define GLK_ALAN3_STATE_STACK
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* TYPES */
+typedef struct StateStackStructure *StateStackP;
+
+/* FUNCTIONS */
+extern StateStackP createStateStack(int elementSize);
+extern bool stateStackIsEmpty(StateStackP stateStack);
+extern void pushGameState(StateStackP stateStack, void *state);
+extern void popGameState(StateStackP stateStack, void *state, char **playerCommandPointer);
+extern void attachPlayerCommandsToLastState(StateStackP stateStack, char *playerCommand);
+extern void deleteStateStack(StateStackP stateStack);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/syntax.cpp b/engines/glk/alan3/syntax.cpp
new file mode 100644
index 0000000000..6edb1cecd3
--- /dev/null
+++ b/engines/glk/alan3/syntax.cpp
@@ -0,0 +1,95 @@
+/* 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/alan3/syntax.h"
+#include "glk/alan3/word.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/compatibility.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+SyntaxEntry *stxs; /* Syntax table pointer */
+
+
+/* PRIVATE TYPES & DATA */
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*======================================================================*/
+ElementEntry *elementTreeOf(SyntaxEntry *stx) {
+ return (ElementEntry *) pointerTo(stx->elms);
+}
+
+
+/*----------------------------------------------------------------------*/
+static SyntaxEntry *findSyntaxEntryForPreBeta2(int verbCode, SyntaxEntry *foundStx) {
+ SyntaxEntryPreBeta2 *stx;
+ for (stx = (SyntaxEntryPreBeta2*)stxs; !isEndOfArray(stx); stx++)
+ if (stx->code == verbCode) {
+ foundStx = (SyntaxEntry *)stx;
+ break;
+ }
+ return(foundStx);
+}
+
+
+/*----------------------------------------------------------------------*/
+static SyntaxEntry *findSyntaxEntry(int verbCode) {
+ SyntaxEntry *stx;
+ for (stx = stxs; !isEndOfArray(stx); stx++)
+ if (stx->code == verbCode) {
+ return stx;
+ break;
+ }
+ return NULL;
+}
+
+
+/*======================================================================*/
+SyntaxEntry *findSyntaxTreeForVerb(int verbCode) {
+ SyntaxEntry *foundStx = NULL;
+ if (isPreBeta2(header->version)) {
+ foundStx = findSyntaxEntryForPreBeta2(verbCode, foundStx);
+ } else {
+ foundStx = findSyntaxEntry(verbCode);
+ }
+ if (foundStx == NULL)
+ /* No matching syntax */
+ error(M_WHAT);
+ return foundStx;
+}
+
+
+/*======================================================================*/
+char *parameterNameInSyntax(int syntaxNumber, int parameterNumber) {
+ Aaddr adr = addressAfterTable(header->parameterMapAddress, sizeof(ParameterMapEntry));
+ Aaddr *syntaxParameterNameTable = (Aaddr *)pointerTo(memory[adr]);
+ Aaddr *parameterNameTable = (Aaddr *)pointerTo(syntaxParameterNameTable[syntaxNumber-1]);
+ return stringAt(parameterNameTable[parameterNumber-1]);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/syntax.h b/engines/glk/alan3/syntax.h
new file mode 100644
index 0000000000..de7e48423b
--- /dev/null
+++ b/engines/glk/alan3/syntax.h
@@ -0,0 +1,44 @@
+/* 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_ALAN3_SYNTAX
+#define GLK_ALAN3_SYNTAX
+
+#include "glk/alan3/types.h"
+#include "glk/alan3/memory.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern SyntaxEntry *stxs; /* Syntax table pointer */
+
+
+/* FUNCTIONS */
+extern ElementEntry *elementTreeOf(SyntaxEntry *stx);
+extern char *parameterNameInSyntax(int syntaxNumber, int parameterNumber);
+extern SyntaxEntry *findSyntaxTreeForVerb(int verbCode);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/sysdep.cpp b/engines/glk/alan3/sysdep.cpp
new file mode 100644
index 0000000000..94132a7ae0
--- /dev/null
+++ b/engines/glk/alan3/sysdep.cpp
@@ -0,0 +1,500 @@
+/* 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/alan3/sysdep.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/syserr.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#ifdef __vms__
+
+char *strdup(char str[]) /* IN - String to duplicate */
+{
+ char *new = (char *) malloc(strlen(str)+1);
+
+ if (!new)
+ syserr("Out of memory");
+ strcpy(new, str);
+ return new;
+}
+
+#endif
+
+
+#ifdef __vms__
+
+/* Cheat implementation of strftime */
+size_t strftime (
+ char *outbuf,
+ size_t len,
+ const char *format,
+ const struct tm *t)
+{
+ char buf[100];
+ time_t ticks;
+
+ time(&ticks);
+ strcpy(buf, ctime(&ticks));
+ buf[strlen(buf)-1] = '\0';
+ strcpy(outbuf, &buf[4]);
+}
+
+#endif
+
+
+#ifdef HAVE_GLK
+
+/* Note to Glk maintainers: 'native' characters are used for output, in this
+ case, Glk's Latin-1. ISO characters are Alan's internal representation,
+ stored in the .A3C file, and must be converted to native before printing.
+ Glk could just use the ISO routines directly, but its safer to maintain
+ its own tables to guard against future changes in either Alan or Glk (ie. a
+ move to Unicode).
+ */
+
+static char spaceCharacters[] =
+{
+ 0x0A, /* linefeed */
+ 0x20, /* space */
+ 0xA0, /* non-breaking space */
+ 0x00
+};
+
+static char lowerCaseCharacters[] =
+{
+ 0x61, /* a */ 0x62, /* b */ 0x63, /* c */ 0x64, /* d */
+ 0x65, /* e */ 0x66, /* f */ 0x67, /* g */ 0x68, /* h */
+ 0x69, /* i */ 0x6A, /* j */ 0x6B, /* k */ 0x6C, /* l */
+ 0x6D, /* m */ 0x6E, /* n */ 0x6F, /* o */ 0x70, /* p */
+ 0x71, /* q */ 0x72, /* r */ 0x73, /* s */ 0x74, /* t */
+ 0x75, /* u */ 0x76, /* v */ 0x77, /* w */ 0x78, /* x */
+ 0x79, /* y */ 0x7A, /* z */ 0xDF, /* ss <small sharp s> */
+ 0xE0, /* a grave */ 0xE1, /* a acute */
+ 0xE2, /* a circumflex */ 0xE3, /* a tilde */
+ 0xE4, /* a diaeresis */ 0xE5, /* a ring */
+ 0xE6, /* ae */ 0xE7, /* c cedilla */
+ 0xE8, /* e grave */ 0xE9, /* e acute */
+ 0xEA, /* e circumflex */ 0xEB, /* e diaeresis */
+ 0xEC, /* i grave */ 0xED, /* i acute */
+ 0xEE, /* i circumflex */ 0xEF, /* i diaeresis */
+ 0xF0, /* <small eth> */ 0xF1, /* n tilde */
+ 0xF2, /* o grave */ 0xF3, /* o acute */
+ 0xF4, /* o circumflex */ 0xF5, /* o tilde */
+ 0xF6, /* o diaeresis */ 0xF8, /* o slash */
+ 0xF9, /* u grave */ 0xFA, /* u acute */
+ 0xFB, /* u circumflex */ 0xFC, /* u diaeresis */
+ 0xFD, /* y acute */ 0xFE, /* <small thorn> */
+ 0xFF, /* y diaeresis */ 0x00
+};
+
+/* FIXME: ss <small sharp s> and y diaeresis have no UC analogues
+ Are they really considered LC?
+ */
+
+static char upperCaseCharacters[] =
+{
+ 0x41, /* A */ 0x42, /* B */ 0x43, /* C */ 0x44, /* D */
+ 0x45, /* E */ 0x46, /* F */ 0x47, /* G */ 0x48, /* H */
+ 0x49, /* I */ 0x4A, /* J */ 0x4B, /* K */ 0x4C, /* L */
+ 0x4D, /* M */ 0x4E, /* N */ 0x4F, /* O */ 0x50, /* P */
+ 0x51, /* Q */ 0x52, /* R */ 0x53, /* S */ 0x54, /* T */
+ 0x55, /* U */ 0x56, /* V */ 0x57, /* W */ 0x58, /* X */
+ 0x59, /* Y */ 0x5A, /* Z */
+ 0xC0, /* A grave */ 0xC1, /* A acute */
+ 0xC2, /* A circumflex */ 0xC3, /* A tilde */
+ 0xC4, /* A diaeresis */ 0xC5, /* A ring */
+ 0xC6, /* AE */ 0xC7, /* C cedilla */
+ 0xC8, /* E grave */ 0xC9, /* E acute */
+ 0xCA, /* E circumflex */ 0xCB, /* E diaeresis */
+ 0xCC, /* I grave */ 0xCD, /* I acute */
+ 0xCE, /* I circumflex */ 0xCF, /* I diaeresis */
+ 0xD0, /* <capital eth> */ 0xD1, /* N tilde */
+ 0xD2, /* O grave */ 0xD3, /* O acute */
+ 0xD4, /* O circumflex */ 0xD5, /* O tilde */
+ 0xD6, /* O diaeresis */ 0xD8, /* O slash */
+ 0xD9, /* U grave */ 0xDA, /* U acute */
+ 0xDB, /* U circumflex */ 0xDC, /* U diaeresis */
+ 0xDD, /* Y acute */ 0xDE, /* <capital thorn> */
+ 0x00
+};
+
+#else
+
+/* Theses work on native character sets */
+
+static unsigned char spaceCharacters[] = " \t\n";
+
+/* Use native characters */
+static const char lowerCaseCharacters[] = "abcdefghijklmnopqrstuvwxyzàáâãäåæçéêëìíîïðñòóôõöøùúûüýþÿ";
+
+static const char upperCaseCharacters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÛÝÞß";
+
+#endif
+
+int isSpace(unsigned int c) /* IN - Native character to test */
+{
+ return (c != '\0' && strchr((char *)spaceCharacters, c) != 0);
+}
+
+
+int isLower(unsigned int c) /* IN - Native character to test */
+{
+ return (c != '\0' && strchr((char *)lowerCaseCharacters, c) != 0);
+}
+
+
+int isUpper(unsigned int c) /* IN - Native character to test */
+{
+ return (c != '\0' && strchr((char *)upperCaseCharacters, c) != 0);
+}
+
+int isLetter(unsigned int c) /* IN - Native character to test */
+{
+ return(c != '\0' && (isLower(c)? !0: isUpper(c)));
+}
+
+
+int toLower(unsigned int c) /* IN - Native character to convert */
+{
+#ifdef HAVE_GLK
+ return g_vm->glk_char_to_lower(c);
+#else
+#ifdef __dos__
+ char *cp;
+
+ if ((cp = strchr(upperCaseCharacters, c)) != 0)
+ return(lowerCaseCharacters[cp-upperCaseCharacters]);
+ else
+ return c;
+#else
+#ifdef __mac__
+ char *cp;
+
+ if ((cp = strchr(upperCaseCharacters, c)) != 0)
+ return(lowerCaseCharacters[cp-upperCaseCharacters]);
+ else
+ return c;
+#else
+ return (isUpper(c)? c + ('a' - 'A'): c);
+#endif
+#endif
+#endif
+}
+
+int toUpper(unsigned int c) /* IN - Native character to convert */
+{
+#ifdef HAVE_GLK
+ return g_vm->glk_char_to_upper(c);
+#else
+#ifdef __dos__
+ char *cp;
+
+ if ((cp = strchr(lowerCaseCharacters, c)) != 0)
+ return(upperCaseCharacters[cp-lowerCaseCharacters]);
+ else
+ return c;
+#else
+#ifdef __mac__
+ char *cp;
+
+ if ((cp = strchr(lowerCaseCharacters, c)) != 0)
+ return(upperCaseCharacters[cp-lowerCaseCharacters]);
+ else
+ return c;
+#else
+ return (isLower(c)? c - ('a' - 'A'): c);
+#endif
+#endif
+#endif
+}
+
+char *strlow(char str[]) /* INOUT - Native string to convert */
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ *s = toLower(*s);
+ return(str);
+}
+
+
+char *strupp(char str[]) /* INOUT - Native string to convert */
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ *s = toUpper(*s);
+ return(str);
+}
+
+
+/* The following work on ISO characters */
+
+int isLowerCase(unsigned int c) /* IN - ISO character to test */
+{
+ static char lowerCaseCharacters[] = "abcdefghijklmnopqrstuvwxyz\340\341\342\343\344\345\346\347\351\352\353\354\355\356\357\360\361\362\363\364\365\366\370\371\372\373\374\375\376\377";
+ int i;
+ for (i = 0; i < strlen(lowerCaseCharacters); i++)
+ if (((unsigned int)lowerCaseCharacters[i]) == c) return 1;
+ return 0;
+}
+
+
+int isUpperCase(unsigned int c) /* IN - ISO character to test */
+{
+ //static char upperCaseCharacters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337";
+ int i;
+ for (i = 0; i < strlen(upperCaseCharacters); i++)
+ if (upperCaseCharacters[i] == c) return 1;
+ return 0;
+}
+
+
+int isISOLetter(int c) /* IN - ISO character to test */
+{
+ return (isLowerCase(c) || isUpperCase(c));
+}
+
+
+char IsoToLowerCase(int c) /* IN - ISO character to convert */
+{
+ return (isUpperCase(c)? c + ('a' - 'A'): c);
+}
+
+
+char IsoToUpperCase(int c) /* IN - ISO character to convert */
+{
+ return (isLowerCase(c)? c - ('a' - 'A'): c);
+}
+
+
+char *stringLower(char str[]) /* INOUT - ISO string to convert */
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ *s = IsoToLowerCase(*s);
+ return(str);
+}
+
+
+char *stringUpper(char str[]) /* INOUT - ISO string to convert */
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ *s = IsoToUpperCase(*s);
+ return(str);
+}
+
+/*----------------------------------------------------------------------*/
+int compareStrings(char *str1, char *str2)
+{
+ char *s1 = str1, *s2 = str2;
+
+ while (*s1 != '\0' && *s2 != '\0') {
+ if (IsoToLowerCase(*s1) < IsoToLowerCase(*s2)) return -1;
+ if (IsoToLowerCase(*s1) > IsoToLowerCase(*s2)) return 1;
+ s1++;
+ s2++;
+ }
+ return IsoToLowerCase(*s2) - IsoToLowerCase(*s1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ toIso
+
+ Converts the incoming string to ISO character set. The original is
+ in the current character set which in the case of the compiler might
+ be other than the native.
+
+ */
+void toIso(char copy[], /* OUT - Mapped string */
+ char original[], /* IN - string to convert */
+ int charset) /* IN - the current character set */
+{
+static unsigned char macMap[256]
+= {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0A,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+0xC4,0xC5,0xC7,0xC9,0xD1,0xD6,0xDC,0xE1,0xE0,0xE2,0xE4,0xE3,0xE5,0xE7,0xE9,0xE8,
+0xEA,0xEB,0xED,0xEC,0xEE,0xEF,0xF1,0xF3,0xF2,0xF4,0xF6,0xF5,0xFA,0xF9,0xFB,0xFC,
+0xB9,0xB0,0xA2,0xA3,0xA7,0xB7,0xB6,0xDF,0xAE,0xA9,0xB2,0xB4,0xA8,0xD7,0xC6,0xD8,
+0xA4,0xB1,0xCD,0xCC,0xA5,0xB5,0xF0,0xCA,0xDE,0xFE,0xA6,0xAA,0xBA,0xD4,0xE6,0xF8,
+0xBF,0xA1,0xAC,0xCE,0xCF,0xC8,0xD0,0xAB,0xBB,0xCB,0xA0,0xC0,0xC3,0xD5,0xDD,0xFD,
+0xAD,0xAF,0xDA,0xD9,0xB8,0xB3,0xF7,0xC2,0xFF,0xBC,0xBD,0xBE,0xC1,0xD2,0xD3,0xDB,
+0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F};
+
+static unsigned char dosMap[256]
+= {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0A,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+0xC7,0xFC,0xE9,0xE2,0xE4,0xE0,0xE5,0xE7,0xEA,0xEB,0xE8,0xEF,0xEE,0xEC,0xC4,0xC5,
+0xC9,0xE6,0xC6,0xF4,0xF6,0xF2,0xFB,0xF9,0xFF,0xD6,0xDC,0xA2,0xA3,0xA5,0xDE,0xA6,
+0xE1,0xED,0xF3,0xFA,0xF1,0xD1,0xAA,0xBA,0xBF,0xC0,0xC1,0xBD,0xBC,0xCF,0xAB,0xBB,
+0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+0xA1,0xA7,0xAD,0xB3,0xB8,0xB9,0xC3,0xCE,0xD2,0xD3,0xDB,0xDD,0xE3,0xF5,0xF8,0xFD,
+0xA9,0xDF,0xC8,0xB6,0xCA,0xA4,0xB5,0xAE,0xD5,0xD0,0xD4,0xF0,0xD7,0xD8,0xCB,0xC2,
+0xBE,0xB1,0xD9,0xDA,0xCD,0xCC,0xF7,0xA8,0xB0,0xB7,0xAF,0xAC,0xFE,0xB2,0xB4,0xA0};
+ unsigned char *o, *c;
+
+ switch (charset) {
+ case 0: /* ISO */
+ if (copy != original)
+ (void)strcpy(copy, original);
+ break;
+ case 1: /* Mac */
+ for (o = (unsigned char *)original, c = (unsigned char *)copy; *o; o++, c++)
+ *c = macMap[*o];
+ *c = '\0';
+ break;
+
+ case 2: /* Dos */
+ for (o = (unsigned char *)original, c = (unsigned char *)copy; *o; o++, c++)
+ *c = dosMap[*o];
+ *c = '\0';
+ break;
+ }
+}
+
+/*----------------------------------------------------------------------
+
+ fromIso
+
+ Converts a string from global Iso format to native. Only used in
+ interpreter so character set is known at compile time.
+
+ */
+void fromIso(char copy[], /* OUT - Mapped string */
+ char original[]) /* IN - string to convert */
+{
+#if ISO == 0
+ static unsigned char map[256]
+#if defined __mac__
+ = {
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0D,0x0B,0x0C,0x0D,0x0E,0x0F,
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
+ 0xCA,0xC1,0xA2,0xA3,0xB0,0xB4,0xBA,0xA4,0xAC,0xA9,0xBB,0xC7,0xC2,0xD0,0xA8,0xD1,
+ 0xA1,0xB1,0xAA,0xD5,0xAB,0xB5,0xA6,0xA5,0xD4,0xA0,0xBC,0xC8,0xD9,0xDA,0xDB,0xC0,
+ 0xCB,0xDC,0xD7,0xCC,0x80,0x81,0xAE,0x82,0xC5,0x83,0xB7,0xC9,0xB3,0xB2,0xC3,0xC4,
+ 0xC6,0x84,0xDD,0xDE,0xBD,0xCD,0x85,0xAD,0xAF,0xD3,0xD2,0xDF,0x86,0xCE,0xB8,0xA7,
+ 0x88,0x87,0x89,0x8B,0x8A,0x8C,0xBE,0x8D,0x8F,0x8E,0x90,0x91,0x93,0x92,0x94,0x95,
+ 0xB6,0x96,0x98,0x97,0x99,0x9B,0x9A,0xD6,0xBF,0x9D,0x9C,0x9E,0x9F,0xCF,0xB9,0xD8}
+#else
+#if defined __dos__
+ = {
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0D,0x0B,0x0C,0x0D,0x0E,0x0F,
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,
+ 0xFF,0xD0,0x9B,0x9C,0xE5,0x9D,0x9F,0xD1,0xF7,0xE0,0xA6,0xAE,0xFB,0xD2,0xE7,0xFA,
+ 0xF8,0xF1,0xFD,0xD3,0xFE,0xE6,0xE3,0xF9,0xD4,0xD5,0xA7,0xAF,0xAC,0xAB,0xF0,0xA8,
+ 0xA9,0xAA,0xEF,0xD6,0x8E,0x8F,0x92,0x80,0xE2,0x90,0xE4,0xEE,0xF5,0xF4,0xD7,0xAD,
+ 0xE9,0xA5,0xD8,0xD9,0xEA,0xE8,0x99,0xEC,0xED,0xF2,0xF3,0xDA,0x9A,0xDB,0x9E,0xE1,
+ 0x85,0xA0,0x83,0xDC,0x84,0x86,0x91,0x87,0x8A,0x82,0x88,0x89,0x8D,0xA1,0x8C,0x8B,
+ 0xEB,0xA4,0x95,0xA2,0x93,0xDD,0x94,0xF6,0xDE,0x97,0xA3,0x96,0x81,0xDF,0xFC,0x98}
+#endif
+ ;
+#endif
+ unsigned char *o, *c;
+
+ for (o = (unsigned char *)original, c = (unsigned char *)copy; *o; o++, c++)
+ *c = map[*o];
+ *c = '\0';
+#else
+ if (copy != original)
+ (void)strcpy(copy, original);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ toNative
+
+ Converts the incoming string to the native character set from any of
+ the others. The original is in the current character set which in
+ the case of the compiler might be other than the native.
+
+ */
+void toNative(char copy[], /* OUT - Mapped string */
+ char original[], /* IN - string to convert */
+ int charset) /* IN - the current character set */
+{
+ toIso(copy, original, charset);
+ if (NATIVECHARSET != 0)
+ fromIso(copy, copy);
+}
+
+
+/*======================================================================*/
+int littleEndian() {
+ int x = 1;
+ return (*(char *)&x == 1);
+}
+
+
+/*======================================================================*/
+char *baseNameStart(char *fullPathName) {
+ static char *delimiters = "\\>]/:";
+ int i;
+
+ for (i = strlen(fullPathName)-1; i > 0; i--)
+ if (strchr(delimiters, fullPathName[i]) != NULL)
+ return &fullPathName[i+1];
+ return(fullPathName);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/sysdep.h b/engines/glk/alan3/sysdep.h
new file mode 100644
index 0000000000..747201c188
--- /dev/null
+++ b/engines/glk/alan3/sysdep.h
@@ -0,0 +1,197 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_SYSDEP
+#define GLK_ALAN3_SYSDEP
+
+/* System dependencies file for Alan Adventure Language system
+
+ N.B. The test for symbols used here should really be of three types
+ - processor name (like PC, x86, ...)
+ - os name (DOS, WIN32, Solaris2, ...)
+ - compiler name and version (DJGPP, CYGWIN, GCC271, THINK-C, ...)
+
+ The set symbols should indicate if a feature is on or off like the GNU
+ AUTOCONFIG package does.
+
+ This is not completely done yet!
+*/
+
+#include "common/util.h"
+
+namespace Glk {
+namespace Alan3 {
+
+#define GLK
+#define HAVE_GLK
+
+#undef isspace
+#define isspace Common::isSpace
+#undef isdigit
+#define isdigit Common::isDigit
+#undef isalpha
+#define isalpha Common::isAlpha
+#undef stricmp
+#define stricmp scumm_stricmp
+#undef strdup
+#define strdup scumm_strdup
+#undef strcasecmp
+#define strcasecmp scumm_stricmp
+#undef strncasecmp
+#define strncasecmp scumm_strnicmp
+#undef rand
+#define rand() g_vm->getRandomNumber(0x7fffffff)
+
+
+/*----------------------------------------------------------------------
+
+ Below follows OS and compiler dependent settings. They should not be
+ changed except for introducing new sections when porting to new
+ environments.
+
+ */
+
+/***********************/
+/* ISO character sets? */
+/***********************/
+
+/* Common case first */
+#define ISO 1
+#define NATIVECHARSET 0
+
+#undef ISO
+#define ISO 1
+#undef NATIVECHARSET
+#define NATIVECHARSET 0
+
+/**************************/
+/* Strings for file modes */
+/**************************/
+#define READ_MODE "rb"
+#define WRITE_MODE "wb"
+
+
+/****************************/
+/* Allocates cleared bytes? */
+/****************************/
+
+#ifdef __CYGWIN__
+#define NOTCALLOC
+#endif
+
+#ifdef __MINGW32__
+#define NOTCALLOC
+#endif
+
+#ifdef __unix__
+#define NOTCALLOC
+#endif
+
+
+/****************/
+/* Have termio? */
+/****************/
+
+#ifdef HAVE_GLK
+# undef HAVE_TERMIO /* don't need TERMIO */
+#else
+# ifdef __CYGWIN__
+# define HAVE_TERMIO
+# endif
+# ifdef __unix__
+# define HAVE_TERMIO
+# endif
+#endif
+
+/*******************************/
+/* Is ANSI control available? */
+/*******************************/
+
+#ifdef HAVE_GLK
+# undef HAVE_ANSI /* don't need ANSI */
+#else
+# ifdef __CYGWIN__
+# define HAVE_ANSI
+# endif
+#endif
+
+/******************************/
+/* Use the READLINE function? */
+/******************************/
+#define USE_READLINE
+#ifdef SOME_PLATFORM_WHICH_CANT_USE_READLINE
+# undef USE_READLINE
+#endif
+
+/* Special cases and definition overrides */
+#ifdef __unix__
+#define MULTI
+#endif
+
+
+#ifdef __dos__
+
+/* Return codes */
+#define EXIT_SUCCESS 0
+#define EXIT_FAILURE 1
+
+#endif
+
+
+/* Native character functions */
+extern int isSpace(unsigned int c); /* IN - Native character to test */
+extern int isLower(unsigned int c); /* IN - Native character to test */
+extern int isUpper(unsigned int c); /* IN - Native character to test */
+extern int isLetter(unsigned int c); /* IN - Native character to test */
+extern int toLower(unsigned int c); /* IN - Native character to convert */
+extern int toUpper(unsigned int c); /* IN - Native character to convert */
+extern char *strlow(char str[]); /* INOUT - Native string to convert */
+extern char *strupp(char str[]); /* INOUT - Native string to convert */
+
+/* ISO character functions */
+extern int isISOLetter(int c); /* IN - ISO character to test */
+extern char IsoToLowerCase(int c); /* IN - ISO character to convert */
+extern char IsoToUpperCase(int c); /* IN - ISO character to convert */
+extern char *stringLower(char str[]); /* INOUT - ISO string to convert */
+extern char *stringUpper(char str[]); /* INOUT - ISO string to convert */
+extern int compareStrings(char str1[], char str2[]); /* Case-insensitive compare */
+
+/* ISO string conversion functions */
+extern void toIso(char copy[], /* OUT - Mapped string */
+ char original[], /* IN - string to convert */
+ int charset); /* IN - The current character set */
+
+extern void fromIso(char copy[], /* OUT - Mapped string */
+ char original[]); /* IN - string to convert */
+
+extern void toNative(char copy[], /* OUT - Mapped string */
+ char original[], /* IN - string to convert */
+ int charset); /* IN - current character set */
+
+extern int littleEndian(void);
+
+extern char *baseNameStart(char *fullPathName);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/syserr.cpp b/engines/glk/alan3/syserr.cpp
new file mode 100644
index 0000000000..2fceba6059
--- /dev/null
+++ b/engines/glk/alan3/syserr.cpp
@@ -0,0 +1,103 @@
+/* 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/alan3/syserr.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/debug.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/utils.h"
+
+namespace Glk {
+namespace Alan3 {
+
+static void (*handler)(char *) = NULL;
+
+/*----------------------------------------------------------------------*/
+static void runtimeError(char *errorClassification, char *errorDescription, char *blurb) {
+ output("$n$nAs you enter the twilight zone of Adventures, you stumble \
+and fall to your knees. In front of you, you can vaguely see the outlines \
+of an Adventure that never was.$n$n");
+ output(errorClassification);
+ output(errorDescription);
+ newline();
+
+ if (current.sourceLine != 0) {
+ printf("At source line %d in '%s':\n", current.sourceLine, sourceFileName(current.sourceFile));
+ printf("%s", readSourceLine(current.sourceFile, current.sourceLine));
+ }
+
+ newline();
+ output(blurb);
+
+ terminate(2);
+}
+
+
+/*======================================================================*/
+void setSyserrHandler(void (*f)(char *))
+{
+ handler = f;
+}
+
+
+/*======================================================================*/
+// TODO Make syserr() use ... as printf()
+void syserr(char *description)
+{
+ lin = 0;
+ if (handler == NULL) {
+ char *blurb = "<If you are the creator of this piece of Interactive Fiction, \
+please help debug this Alan system error. Collect *all* the sources, and, if possible, an \
+exact transcript of the commands that led to this error, in a zip-file and send \
+it to support@alanif.se. Thank you!>";
+ runtimeError("SYSTEM ERROR: ", description, blurb);
+ } else
+ handler(description);
+}
+
+
+/*======================================================================*/
+void apperr(char *description)
+{
+ if (handler == NULL) {
+ char *blurb = "<If you are playing this piece of Interactive Fiction, \
+please help the author to debug this programming error. Send an exact \
+transcript of the commands that led to this error to the author. Thank you! \
+If you *are* the author, then you have to figure this out before releasing the game.>";
+ runtimeError("APPLICATION ERROR: ", description, blurb);
+ } else
+ handler(description);
+}
+
+/*======================================================================*/
+void playererr(char *description)
+{
+ if (handler == NULL) {
+ char *blurb = "<You have probably done something that is not exactly right.>";
+ runtimeError("PLAYER ERROR: ", description, blurb);
+ } else
+ handler(description);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/syserr.h b/engines/glk/alan3/syserr.h
new file mode 100644
index 0000000000..84c2178ddb
--- /dev/null
+++ b/engines/glk/alan3/syserr.h
@@ -0,0 +1,40 @@
+/* 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_ALAN3_SYSERR
+#define GLK_ALAN3_SYSERR
+
+/* Header file for syserr unit of ARUN Alan System interpreter */
+
+namespace Glk {
+namespace Alan3 {
+
+/* Functions: */
+extern void syserr(char *msg);
+extern void apperr(char *msg);
+extern void playererr(char *msg);
+extern void setSyserrHandler(void (*handler)(char *));
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/term.cpp b/engines/glk/alan3/term.cpp
new file mode 100644
index 0000000000..6a6d25691a
--- /dev/null
+++ b/engines/glk/alan3/term.cpp
@@ -0,0 +1,158 @@
+/* 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/alan3/current.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/sysdep.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+bool onStatusLine = FALSE; /* To know if where printing the status line or not */
+
+/*======================================================================
+
+ getPageSize()
+
+ Try to get the current page size from the system, else use the ones
+ from the header.
+
+ */
+void getPageSize(void)
+{
+#ifdef HAVE_GLK
+ pageLength = 0;
+ pageWidth = 0;
+
+#else
+#ifdef HAVE_TERMIO
+
+#ifdef __linux__
+extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
+#else
+extern int ioctl();
+#endif
+ struct winsize win;
+ int ecode;
+
+ ecode = ioctl(1, TIOCGWINSZ, &win);
+
+ if (ecode != 0 || win.ws_row == 0)
+ pageLength = header->pageLength;
+ else
+ pageLength = win.ws_row;
+
+ if (ecode != 0 || win.ws_col == 0)
+ pageWidth = header->pageWidth;
+ else
+ pageWidth = win.ws_col;
+
+#else
+
+ pageLength = header->pageLength;
+ pageWidth = header->pageWidth;
+
+#endif
+#endif
+}
+
+/*======================================================================*/
+void statusline(void)
+{
+#ifdef HAVE_GLK
+ uint32 glkWidth;
+ char line[100];
+ int pcol = col;
+
+ if (!statusLineOption) return;
+ if (glkStatusWin == NULL)
+ return;
+
+ g_vm->glk_set_window(glkStatusWin);
+ g_vm->glk_window_clear(glkStatusWin);
+ g_vm->glk_window_get_size(glkStatusWin, &glkWidth, NULL);
+
+#ifdef HAVE_GARGLK
+ int i;
+ glk_set_style(style_User1);
+ for (i = 0; i < glkWidth; i++)
+ glk_put_char(' ');
+#endif
+
+ onStatusLine = TRUE;
+ col = 1;
+ g_vm->glk_window_move_cursor(glkStatusWin, 1, 0);
+ sayInstance(where(HERO, /*TRUE*/ TRANSITIVE));
+
+ // TODO Add status message1 & 2 as author customizable messages
+ if (header->maximumScore > 0)
+ sprintf(line, "Score %d(%d)/%d moves", current.score, (int)header->maximumScore, current.tick);
+ else
+ sprintf(line, "%d moves", current.tick);
+ g_vm->glk_window_move_cursor(glkStatusWin, glkWidth-strlen(line)-1, 0);
+ g_vm->glk_put_string(line);
+ needSpace = FALSE;
+
+ col = pcol;
+ onStatusLine = FALSE;
+
+ g_vm->glk_set_window(glkMainWin);
+#else
+#ifdef HAVE_ANSI
+ char line[100];
+ int i;
+ int pcol = col;
+
+ if (!statusLineOption) return;
+ /* ansi_position(1,1); ansi_bold_on(); */
+ printf("\x1b[1;1H");
+ printf("\x1b[7m");
+
+ onStatusLine = TRUE;
+ col = 1;
+ sayInstance(where(HERO, FALSE));
+
+ if (header->maximumScore > 0)
+ sprintf(line, "Score %d(%d)/%d moves", current.score, header->maximumScore, current.tick);
+ else
+ sprintf(line, "%ld moves", (long)current.tick);
+ for (i=0; i < pageWidth - col - strlen(line); i++) putchar(' ');
+ printf(line);
+ printf("\x1b[m");
+ printf("\x1b[%d;1H", pageLength);
+
+ needSpace = FALSE;
+ capitalize = TRUE;
+
+ onStatusLine = FALSE;
+ col = pcol;
+#endif
+#endif
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/term.h b/engines/glk/alan3/term.h
new file mode 100644
index 0000000000..df19a63610
--- /dev/null
+++ b/engines/glk/alan3/term.h
@@ -0,0 +1,41 @@
+/* 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_ALAN3_TERM
+#define GLK_ALAN3_TERM
+
+/* Header file for terminal functions in ARUN, the Alan interpreter */
+
+namespace Glk {
+namespace Alan3 {
+
+/* DATA */
+extern bool onStatusLine;
+
+/* FUNCTIONS */
+extern void getPageSize();
+extern void statusline();
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/types.cpp b/engines/glk/alan3/types.cpp
new file mode 100644
index 0000000000..962a9f422b
--- /dev/null
+++ b/engines/glk/alan3/types.cpp
@@ -0,0 +1,38 @@
+/* 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/alan3/acode.h"
+#include "glk/alan3/lists.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*======================================================================*/
+Aaddr addressAfterTable(Aaddr adr, int size) {
+ while (!isEndOfArray(&memory[adr])) {
+ adr += size/sizeof(Aword);
+ }
+ return adr+1;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/types.h b/engines/glk/alan3/types.h
new file mode 100644
index 0000000000..278cfa2c8c
--- /dev/null
+++ b/engines/glk/alan3/types.h
@@ -0,0 +1,76 @@
+/* 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_ALAN3_TYPES
+#define GLK_ALAN3_TYPES
+
+/* Header file for the Alan interpreter module. */
+
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/acode.h"
+#include "glk/alan3/memory.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PREPROCESSOR */
+#define FORWARD
+#define NEW(type) ((type *)allocate(sizeof(type)))
+
+
+/* CONSTANTS */
+
+#define HERO (header->theHero)
+#define ENTITY (header->entityClassId)
+#define OBJECT (header->objectClassId)
+#define LOCATION (header->locationClassId)
+#define THING (header->thingClassId)
+#define ACTOR (header->actorClassId)
+
+#define MAXPARAMS (header->maxParameters)
+#define MAXINSTANCE (header->instanceMax)
+
+#define pointerTo(x) ((void *)&memory[x])
+#define addressOf(x) ((((long)x)-((long)memory))/sizeof(Aword))
+#define stringAt(x) ((char *)pointerTo(x))
+
+#define ASIZE(x) (sizeof(x)/sizeof(Aword))
+
+/* The various tables */
+struct VerbEntry { /* VERB TABLE */
+ Aint code; /* Code for the verb */
+ Aaddr alts; /* Address to alternatives */
+};
+
+struct LimitEntry { /* LIMIT Type */
+ Aword atr; /* Attribute that limits */
+ Aword val; /* And the limiting value */
+ Aaddr stms; /* Statements if fail */
+};
+
+/* Functions: */
+extern Aaddr addressAfterTable(Aaddr adr, int size);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/utils.cpp b/engines/glk/alan3/utils.cpp
new file mode 100644
index 0000000000..bcbaa47cb0
--- /dev/null
+++ b/engines/glk/alan3/utils.cpp
@@ -0,0 +1,108 @@
+/* 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/alan3/utils.h"
+#include "glk/alan3/alan_version.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/exe.h"
+#include "glk/alan3/state.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/fnmatch.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/*======================================================================
+
+ terminate()
+
+ Terminate the execution of the adventure, e.g. close windows,
+ return buffers...
+
+ */
+void terminate(int code)
+{
+ newline();
+
+ terminateStateStack();
+
+ stopTranscript();
+
+ if (memory)
+ deallocate(memory);
+
+#ifdef HAVE_GLK
+ g_vm->glk_exit();
+#else
+ exit(code);
+#endif
+}
+
+/*======================================================================*/
+void printVersion(int buildNumber) {
+ printf("Arun - Adventure Language Interpreter version %s", alan.version.string);
+ if (buildNumber != 0) printf("-%d", buildNumber);
+ printf(" (%s %s)", alan.date, alan.time);
+}
+
+
+/*======================================================================*/
+void usage(char *programName)
+{
+#if (BUILD+0) != 0
+ printVersion(BUILD);
+#else
+ printVersion(0);
+#endif
+ printf("\n\nUsage:\n\n");
+ printf(" %s [<switches>] <adventure>\n\n", programName);
+ printf("where the possible optional switches are:\n");
+#ifdef HAVE_GLK
+ g_vm->glk_set_style(style_Preformatted);
+#endif
+ printf(" -v verbose mode\n");
+ printf(" -l log transcript to a file\n");
+ printf(" -c log player commands to a file\n");
+ printf(" -n no Status Line\n");
+ printf(" -d enter debug mode\n");
+ printf(" -t[<n>] trace game execution, higher <n> gives more trace\n");
+ printf(" -i ignore version and checksum errors\n");
+ printf(" -r make regression test easier (don't timestamp, page break, randomize...)\n");
+#ifdef HAVE_GLK
+ g_vm->glk_set_style(style_Normal);
+#endif
+}
+
+
+#ifndef FNM_CASEFOLD
+#define FNM_CASEFOLD 0
+#endif
+/*======================================================================*/
+bool match(char *pattern, char *input) {
+ return fnmatch(pattern, input, FNM_CASEFOLD) == 0;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/utils.h b/engines/glk/alan3/utils.h
new file mode 100644
index 0000000000..1a6d470504
--- /dev/null
+++ b/engines/glk/alan3/utils.h
@@ -0,0 +1,42 @@
+/* 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_ALAN3_UTILS
+#define GLK_ALAN3_UTILS
+
+/* Utility functions for the Alan interpreter */
+
+#include "glk/alan3/types.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* FUNCTIONS: */
+extern void terminate(int code);
+extern void usage(char *programName);
+extern void printVersion(int buildNumber);
+extern bool match(char *pattern, char *input);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/version.h b/engines/glk/alan3/version.h
new file mode 100644
index 0000000000..82036b682b
--- /dev/null
+++ b/engines/glk/alan3/version.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_ALAN3_VERSION
+#define GLK_ALAN3_VERSION
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Alan3 {
+
+typedef int64 Time;
+
+struct Version {
+ char* string;
+ int version;
+ int revision;
+ int correction;
+ Time time;
+ const char * state;
+};
+
+struct Product {
+ const char* name;
+ const char* slogan;
+ const char* shortHeader;
+ const char* longHeader;
+ const char* date;
+ const char* time;
+ const char* user;
+ const char* host;
+ const char* ostype;
+ Version version;
+};
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/alan3/word.cpp b/engines/glk/alan3/word.cpp
new file mode 100644
index 0000000000..450e684308
--- /dev/null
+++ b/engines/glk/alan3/word.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/word.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/lists.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+
+/* List of parsed words, index into dictionary */
+Word *playerWords = NULL;
+int currentWordIndex; /* An index into the list of playerWords */
+int firstWord, lastWord; /* Index for the first and last words for this command */
+
+/* Some variable for dynamically allocating the playerWords, which will happen in scan() */
+static int playerWordsLength = 0;
+#define PLAYER_WORDS_EXTENT 20
+
+/* What did the user say? */
+int verbWord; /* The word he used as a verb, dictionary index */
+int verbWordCode; /* The code for that verb */
+
+
+/* PRIVATE TYPES & DATA */
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+void ensureSpaceForPlayerWords(int size) {
+ int newLength = playerWordsLength+PLAYER_WORDS_EXTENT;
+
+ if (playerWordsLength < size+1) {
+ playerWords = (Word *)realloc(playerWords, newLength*sizeof(Word));
+ if (playerWords == NULL)
+ syserr("Out of memory in 'ensureSpaceForPlayerWords()'");
+ playerWordsLength = newLength;
+ }
+}
+
+
+/*======================================================================*/
+char *playerWordsAsCommandString(void) {
+ char *commandString;
+ int size = playerWords[lastWord].end - playerWords[firstWord].start;
+ commandString = (char *)allocate(size + 1);
+ strncpy(commandString, playerWords[firstWord].start, size);
+ commandString[size] = '\0';
+ return commandString;
+}
+
+
+/*======================================================================*/
+void clearWordList(Word list[]) {
+ implementationOfSetEndOfArray((Aword *)list);
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk
diff --git a/engines/glk/alan3/word.h b/engines/glk/alan3/word.h
new file mode 100644
index 0000000000..3232092b2e
--- /dev/null
+++ b/engines/glk/alan3/word.h
@@ -0,0 +1,56 @@
+/* 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_ALAN3_WORD
+#define GLK_ALAN3_WORD
+
+namespace Glk {
+namespace Alan3 {
+
+typedef struct WordEntry {
+ int code; /* The dictionary index for that word */
+ char *start; /* Where does it start */
+ char *end; /* .. and end */
+} Word;
+
+
+/* DATA */
+
+extern Word *playerWords; /* List of Parsed Word */
+extern int currentWordIndex; /* and an index into it */
+extern int firstWord;
+extern int lastWord;
+
+extern int verbWord;
+extern int verbWordCode;
+
+
+
+/* FUNCTIONS */
+extern void ensureSpaceForPlayerWords(int count);
+extern char *playerWordsAsCommandString(void);
+extern void clearWordList(Word *list);
+
+} // End of namespace Alan3
+} // End of namespace Glk
+
+#endif