diff options
author | Paul Gilbert | 2019-06-25 21:18:44 -0700 |
---|---|---|
committer | Paul Gilbert | 2019-07-06 15:27:07 -0700 |
commit | cd7cf4141425e3e77b164d2dbcbae8de1eacb035 (patch) | |
tree | 97caacd0a7e26488844ada4e7cd529cae482526a /engines/glk/alan3 | |
parent | cfd66173bad8bb23ba7ba2d531fc5e98872212ce (diff) | |
download | scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.tar.gz scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.tar.bz2 scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.zip |
GLK: ALAN3: Initial files commit
Diffstat (limited to 'engines/glk/alan3')
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(¶meters[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(¶meters[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 = ¶meterPositions[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(¶meters[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(¶meters1[i]); i++) { + if (isEndOfArray(¶meters2[i])) return FALSE; + if (parameters1[i].instance != parameters2[i].instance) return FALSE; + } + return isEndOfArray(¶meters2[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(¶meterArray[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(¶meters[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(¶meters[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, ¶meter); + 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(¶meters[1]); +} + + +/*----------------------------------------------------------------------*/ +static void parsePronoun(Parameter parameters[]) { + parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++; + parameters[0].instance = 0; + parameters[0].isPronoun = TRUE; + setEndOfArray(¶meters[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(¶meters[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(¶meters[1]); + } else + setEndOfArray(¶meters[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(¶meters[i].candidates[0]); /* No candidates */ + if (!reachable(previousMultipleParameters[i].instance)) + parameters[i].instance = 0; + else + parameters[i].instance = previousMultipleParameters[i].instance; + } + setEndOfArray(¶meters[i]); + compressParameterArray(parameters); +} + + +/*----------------------------------------------------------------------*/ +static void parseReferenceToPreviousMultipleParameters(Parameter parameters[]) { + parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++; + parameters[0].instance = 0; + parameters[0].isThem = TRUE; + setEndOfArray(¶meters[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(¶meters[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(¶meterPosition->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 = ¶meterPositions[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, ¶meterPositions[i].parameters[0]); + + for (restriction = (RestrictionEntry *) pointerTo(elms->next); !isEndOfArray(restriction); restriction++) { + ParameterPosition *parameterPosition = ¶meterPositions[restriction->parameterNumber-1]; + if (parameterPosition->explicitMultiple) { + /* This was a multiple parameter position, so check all multipleCandidates */ + for (i = 0; !isEndOfArray(¶meterPosition->parameters[i]); i++) { + copyParameter(&localParameters[restriction->parameterNumber-1], ¶meterPosition->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(¶meterPositions[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(¶meter->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(¶meters[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(¶meterPositions[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 = ¶meterPositions[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 = ¶meters[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 *)¤t, 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 *)¤t, 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 |