aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/jacl
diff options
context:
space:
mode:
authordreammaster2019-10-04 05:50:25 +0100
committerPaul Gilbert2019-10-07 19:01:51 -0700
commitaf4f390ab26c2a181b3dc69dfd93e8fcf5bc30a0 (patch)
tree05e38513a7a11de5288815fba6ee1fca56e5efc6 /engines/glk/jacl
parenta00ed41679b9cdc6a4f2ede5c500a658f600103c (diff)
downloadscummvm-rg350-af4f390ab26c2a181b3dc69dfd93e8fcf5bc30a0.tar.gz
scummvm-rg350-af4f390ab26c2a181b3dc69dfd93e8fcf5bc30a0.tar.bz2
scummvm-rg350-af4f390ab26c2a181b3dc69dfd93e8fcf5bc30a0.zip
GLK: JACL: Adding subengine files
Diffstat (limited to 'engines/glk/jacl')
-rw-r--r--engines/glk/jacl/constants.h184
-rw-r--r--engines/glk/jacl/csv.h106
-rw-r--r--engines/glk/jacl/detection.cpp8
-rw-r--r--engines/glk/jacl/detection.h8
-rw-r--r--engines/glk/jacl/detection_tables.h6
-rw-r--r--engines/glk/jacl/display.cpp265
-rw-r--r--engines/glk/jacl/encapsulate.cpp279
-rw-r--r--engines/glk/jacl/errors.cpp169
-rw-r--r--engines/glk/jacl/findroute.cpp320
-rw-r--r--engines/glk/jacl/glk_saver.cpp272
-rw-r--r--engines/glk/jacl/glk_startup.cpp166
-rw-r--r--engines/glk/jacl/interpreter.cpp3458
-rw-r--r--engines/glk/jacl/jacl.cpp4
-rw-r--r--engines/glk/jacl/jacl.h4
-rw-r--r--engines/glk/jacl/jacl_main.cpp1582
-rw-r--r--engines/glk/jacl/jpp.cpp236
-rw-r--r--engines/glk/jacl/language.h574
-rw-r--r--engines/glk/jacl/libcsv.cpp529
-rw-r--r--engines/glk/jacl/loader.cpp1775
-rw-r--r--engines/glk/jacl/logging.cpp52
-rw-r--r--engines/glk/jacl/parser.cpp1918
-rw-r--r--engines/glk/jacl/prototypes.h200
-rw-r--r--engines/glk/jacl/resolvers.cpp1320
-rw-r--r--engines/glk/jacl/types.h224
-rw-r--r--engines/glk/jacl/utils.cpp246
-rw-r--r--engines/glk/jacl/version.h26
26 files changed, 13917 insertions, 14 deletions
diff --git a/engines/glk/jacl/constants.h b/engines/glk/jacl/constants.h
new file mode 100644
index 0000000000..f7c00d10d6
--- /dev/null
+++ b/engines/glk/jacl/constants.h
@@ -0,0 +1,184 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+namespace Glk {
+namespace JACL {
+
+#define MAX_WORDS 20
+#define STACK_SIZE 20
+#define MAX_UNDO 100
+#define MAX_OBJECTS 1000
+
+/* LOCATION ATTRIBUTE VALUES */
+
+#define VISITED 1
+#define DARK 2
+#define ON_WATER 4
+#define UNDER_WATER 8
+#define WITHOUT_AIR 16
+#define OUTDOORS 32
+#define MID_AIR 64
+#define TIGHT_ROPE 128
+#define POLLUTED 256
+#define SOLVED 512
+#define MID_WATER 1024
+#define DARKNESS 2048
+#define MAPPED 4096
+#define KNOWN 8192
+
+/* OBJECT ATTRIBUTE VALUES */
+
+#define CLOSED 1
+#define LOCKED 2
+#define DEAD 4
+#define IGNITABLE 8
+#define WORN 16
+#define CONCEALING 32
+#define LUMINOUS 64
+#define WEARABLE 128
+#define CLOSABLE 256
+#define LOCKABLE 512
+#define ANIMATE 1024
+#define LIQUID 2048
+#define CONTAINER 4096
+#define SURFACE 8192
+#define PLURAL 16384
+#define FLAMMABLE 32768
+#define BURNING 65536
+#define LOCATION 131072
+#define ON 262144
+#define DAMAGED 524288
+#define FEMALE 1048576
+#define POSSESSIVE 2097152
+#define OUT_OF_REACH 4194304
+#define TOUCHED 8388608
+#define SCORED 16777216
+#define SITTING 33554432
+#define NPC 67108864
+#define DONE 134217728
+#define GAS 268435456
+#define NO_TAB 536870912
+#define NOT_IMPORTANT 1073741824
+
+/* LOCATION INTEGER ARRAY INDEXES */
+
+#define NORTH_DIR 0
+#define SOUTH_DIR 1
+#define EAST_DIR 2
+#define WEST_DIR 3
+#define NORTHEAST_DIR 4
+#define NORTHWEST_DIR 5
+#define SOUTHEAST_DIR 6
+#define SOUTHWEST_DIR 7
+#define UP_DIR 8
+#define DOWN_DIR 9
+#define IN_DIR 10
+#define OUT_DIR 11
+
+/* ALL UP, THERE ARE 16 OBJECT ELEMENTS, THESE 6
+ ARE THE ONLY ONES ACCESSED BY THE INTERPRETER */
+
+#define PARENT integer[0]
+#define QUANTITY integer[1]
+#define MASS integer[2]
+#define BEARING integer[3]
+#define VELOCITY integer[4]
+#define X integer[14]
+#define Y integer[15]
+
+/* SYSTEM VARIABLES */
+
+#define COMPASS integer_resolve("compass")
+#define DESTINATION integer_resolve("destination")
+#define TOTAL_MOVES integer_resolve("total_moves")
+#define TIME integer_resolve("time")
+#define SCORE integer_resolve("score")
+#define INTERNAL_VERSION integer_resolve("internal_version")
+#define DISPLAY_MODE integer_resolve("display_mode")
+#define MAX_RAND integer_resolve("max_rand")
+#define INTERRUPTED integer_resolve("interrupted")
+#define SOUND_ENABLED integer_resolve("sound_enabled")
+#define GRAPHICS_ENABLED integer_resolve("graphics_enabled")
+#define TIMER_ENABLED integer_resolve("timer_enabled")
+#define MULTI_PREFIX integer_resolve("multi_prefix")
+#define NOTIFY integer_resolve("notify")
+#define DEBUG integer_resolve("debug")
+
+/* SYSTEM INTEGER CONSTANTS */
+
+#define SOUND_SUPPORTED cinteger_resolve("sound_supported")
+#define GRAPHICS_SUPPORTED cinteger_resolve("graphics_supported")
+#define TIMER_SUPPORTED cinteger_resolve("timer_supported")
+
+/* ABBREVIATIONS */
+
+#define HELD player
+#define HERE get_here()
+
+/* CONSTANTS */
+
+#define SYSTEM_ATTRIBUTE 0
+#define USER_ATTRIBUTE 1
+
+#define FALSE 0
+#define TRUE 1
+
+#define UNRESTRICT 0
+#define RESTRICT 1
+
+#define SEEK_SET 0
+
+#define SEEK_END 2
+
+#define SCENERY 100
+#define HEAVY 99
+#define NOWHERE 0
+
+#define LOG_ONLY 0
+#define PLUS_STDOUT 1
+#define PLUS_STDERR 2
+#define ONLY_STDERR 3
+#define ONLY_STDOUT 4
+
+#define INT_TYPE 1
+#define STR_TYPE 2
+#define CINT_TYPE 3
+#define CSTR_TYPE 4
+#define ATT_TYPE 5
+#define OBJ_TYPE 6
+
+#define CRI_NONE 0
+#define CRI_ATTRIBUTE 1
+#define CRI_USER_ATTRIBUTE 2
+#define CRI_PARENT 3
+#define CRI_SCOPE 4
+
+#define BOLD 1
+#define NOTE 2
+#define INPUT 3
+#define HEADER 4
+#define SUBHEADER 5
+#define REVERSE 6
+#define PRE 7
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/csv.h b/engines/glk/jacl/csv.h
new file mode 100644
index 0000000000..f979f003b3
--- /dev/null
+++ b/engines/glk/jacl/csv.h
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 JACL_CSV
+#define JACL_CSV
+
+#include "common/stream.h"
+
+namespace Glk {
+namespace JACL {
+
+#define CSV_MAJOR 3
+#define CSV_MINOR 0
+#define CSV_RELEASE 0
+
+/* Error Codes */
+#define CSV_SUCCESS 0
+#define CSV_EPARSE 1 /* Parse error in strict mode */
+#define CSV_ENOMEM 2 /* Out of memory while increasing buffer size */
+#define CSV_ETOOBIG 3 /* Buffer larger than SIZE_MAX needed */
+#define CSV_EINVALID 4 /* Invalid code,should never be received from csv_error*/
+
+
+/* parser options */
+#define CSV_STRICT 1 /* enable strict mode */
+#define CSV_REPALL_NL 2 /* report all unquoted carriage returns and linefeeds */
+#define CSV_STRICT_FINI 4 /* causes csv_fini to return CSV_EPARSE if last
+ field is quoted and doesn't containg ending
+ quote */
+#define CSV_APPEND_NULL 8 /* Ensure that all fields are null-ternimated */
+
+
+/* Character values */
+#define CSV_TAB 0x09
+#define CSV_SPACE 0x20
+#define CSV_CR 0x0d
+#define CSV_LF 0x0a
+#define CSV_COMMA 0x2c
+#define CSV_QUOTE 0x22
+
+struct csv_parser {
+ int pstate; /* Parser state */
+ int quoted; /* Is the current field a quoted field? */
+ size_t spaces; /* Number of continious spaces after quote or in a non-quoted field */
+ unsigned char *entry_buf; /* Entry buffer */
+ size_t entry_pos; /* Current position in entry_buf (and current size of entry) */
+ size_t entry_size; /* Size of entry buffer */
+ int status; /* Operation status */
+ unsigned char options;
+ unsigned char quote_char;
+ unsigned char delim_char;
+ int (*is_space)(unsigned char);
+ int (*is_term)(unsigned char);
+ size_t blk_size;
+ void *(*malloc_func)(size_t);
+ void *(*realloc_func)(void *, size_t);
+ void (*free_func)(void *);
+};
+
+/* Function Prototypes */
+int csv_init(struct csv_parser *p, unsigned char options);
+int csv_fini(struct csv_parser *p, void (*cb1)(void *, size_t, void *), void (*cb2)(int, void *), void *data);
+void csv_free(struct csv_parser *p);
+int csv_error(struct csv_parser *p);
+char *csv_strerror(int error);
+size_t csv_parse(struct csv_parser *p, const void *s, size_t len, void (*cb1)(void *, size_t, void *), void (*cb2)(int, void *), void *data);
+size_t csv_write(void *dest, size_t dest_size, const void *src, size_t src_size);
+int csv_fwrite(Common::WriteStream *fp, const void *src, size_t src_size);
+size_t csv_write2(void *dest, size_t dest_size, const void *src, size_t src_size, unsigned char quote);
+int csv_fwrite2(Common::WriteStream *fp, const void *src, size_t src_size, unsigned char quote);
+int csv_get_opts(struct csv_parser *p);
+int csv_set_opts(struct csv_parser *p, unsigned char options);
+void csv_set_delim(struct csv_parser *p, unsigned char c);
+void csv_set_quote(struct csv_parser *p, unsigned char c);
+unsigned char csv_get_delim(struct csv_parser *p);
+unsigned char csv_get_quote(struct csv_parser *p);
+void csv_set_space_func(struct csv_parser *p, int (*f)(unsigned char));
+void csv_set_term_func(struct csv_parser *p, int (*f)(unsigned char));
+void csv_set_realloc_func(struct csv_parser *p, void *(*)(void *, size_t));
+void csv_set_free_func(struct csv_parser *p, void (*)(void *));
+void csv_set_blk_size(struct csv_parser *p, size_t);
+size_t csv_get_buffer_size(struct csv_parser *p);
+
+} // End of namespace JACL
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/jacl/detection.cpp b/engines/glk/jacl/detection.cpp
index d0e6f344b5..63d0485fa7 100644
--- a/engines/glk/jacl/detection.cpp
+++ b/engines/glk/jacl/detection.cpp
@@ -22,10 +22,10 @@
#include "glk/jacl/detection.h"
#include "glk/jacl/detection_tables.h"
-#include "glk/jacl/common/debug.h"
-#include "glk/jacl/common/file.h"
-#include "glk/jacl/common/md5.h"
-#include "glk/jacl/engines/game.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "engines/game.h"
namespace Glk {
namespace JACL {
diff --git a/engines/glk/jacl/detection.h b/engines/glk/jacl/detection.h
index b93ea36422..f576b66906 100644
--- a/engines/glk/jacl/detection.h
+++ b/engines/glk/jacl/detection.h
@@ -23,10 +23,10 @@
#ifndef JACL_DETECTION
#define JACL_DETECTION
-#include "glk/jacl/common/fs.h"
-#include "glk/jacl/common/hash-str.h"
-#include "glk/jacl/engines/game.h"
-#include "glk/jacl/glk/detection.h"
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "engines/game.h"
+#include "glk/detection.h"
namespace Glk {
namespace JACL {
diff --git a/engines/glk/jacl/detection_tables.h b/engines/glk/jacl/detection_tables.h
index d5f86c8f1c..a5d7b03b6f 100644
--- a/engines/glk/jacl/detection_tables.h
+++ b/engines/glk/jacl/detection_tables.h
@@ -20,9 +20,9 @@
*
*/
-#include "glk/jacl/engines/game.h"
-#include "glk/jacl/common/gui_options.h"
-#include "glk/jacl/common/language.h"
+#include "engines/game.h"
+#include "common/gui_options.h"
+#include "common/language.h"
namespace Glk {
namespace JACL {
diff --git a/engines/glk/jacl/display.cpp b/engines/glk/jacl/display.cpp
new file mode 100644
index 0000000000..db59efbdb1
--- /dev/null
+++ b/engines/glk/jacl/display.cpp
@@ -0,0 +1,265 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+extern char temp_buffer[];
+extern char function_name[];
+
+extern struct object_type *object[];
+extern struct variable_type *variable[];
+
+extern char *word[];
+
+extern int player;
+extern int wp;
+extern int objects;
+extern int custom_error;
+
+extern int spaced;
+
+int check_light(int where) {
+ int index;
+
+ if ((object[where]->attributes & DARK) == FALSE)
+ return (TRUE);
+ else {
+ for (index = 1; index <= objects; index++) {
+ if ((object[index]->attributes & LUMINOUS)
+ && scope(index, "*present"))
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+char *sentence_output(int index, int capital) {
+ if (!strcmp(object[index]->article, "name")) {
+ strcpy(temp_buffer, object[index]->inventory);
+ } else {
+ strcpy(temp_buffer, object[index]->definite);
+ strcat(temp_buffer, " ");
+ strcat(temp_buffer, object[index]->inventory);
+ }
+
+ if (capital)
+ temp_buffer[0] = toupper(temp_buffer[0]);
+
+ return (temp_buffer);
+}
+
+char *isnt_output(int index, bool) {
+ if (object[index]->attributes & PLURAL)
+ return (cstring_resolve("ARENT")->value);
+ else
+ return (cstring_resolve("ISNT")->value);
+}
+
+char *is_output(int index, bool) {
+ if (object[index]->attributes & PLURAL)
+ return (cstring_resolve("ARE")->value);
+ else
+ return (cstring_resolve("IS")->value);
+}
+
+char *sub_output(int index, int capital) {
+ if (object[index]->attributes & PLURAL) {
+ strcpy(temp_buffer, cstring_resolve("THEY_WORD")->value);
+ } else {
+ if (index == player) {
+ strcpy(temp_buffer, cstring_resolve("YOU_WORD")->value);
+ } else if (object[index]->attributes & ANIMATE) {
+ if (object[index]->attributes & FEMALE) {
+ strcpy(temp_buffer, cstring_resolve("SHE_WORD")->value);
+ } else {
+ strcpy(temp_buffer, cstring_resolve("HE_WORD")->value);
+ }
+ } else {
+ strcpy(temp_buffer, cstring_resolve("IT_WORD")->value);
+ }
+ }
+
+ if (capital)
+ temp_buffer[0] = toupper(temp_buffer[0]);
+
+ return temp_buffer;
+}
+
+char *obj_output(int index, int capital) {
+ if (object[index]->attributes & PLURAL) {
+ strcpy(temp_buffer, cstring_resolve("THEM_WORD")->value);
+ } else {
+ if (index == player) {
+ strcpy(temp_buffer, cstring_resolve("YOURSELF_WORD")->value);
+ } else if (object[index]->attributes & ANIMATE) {
+ if (object[index]->attributes & FEMALE) {
+ strcpy(temp_buffer, cstring_resolve("HER_WORD")->value);
+ } else {
+ strcpy(temp_buffer, cstring_resolve("HIM_WORD")->value);
+ }
+ } else {
+ strcpy(temp_buffer, cstring_resolve("IT_WORD")->value);
+ }
+ }
+
+ if (capital)
+ temp_buffer[0] = toupper(temp_buffer[0]);
+
+ return temp_buffer;
+}
+
+char *it_output(int index, bool) {
+ if (object[index]->attributes & ANIMATE) {
+ return sentence_output(index, FALSE);
+ } else {
+ if (object[index]->attributes & PLURAL) {
+ return (cstring_resolve("THEM_WORD")->value);
+ } else {
+ return (cstring_resolve("IT_WORD")->value);
+ }
+ }
+}
+
+char *that_output(int index, int capital) {
+ if (object[index]->attributes & PLURAL) {
+ strcpy(temp_buffer, cstring_resolve("THOSE_WORD")->value);
+ } else {
+ strcpy(temp_buffer, cstring_resolve("THAT_WORD")->value);
+ }
+
+ if (capital)
+ temp_buffer[0] = toupper(temp_buffer[0]);
+
+ return temp_buffer;
+}
+
+char *doesnt_output(int index, bool) {
+ if (object[index]->attributes & PLURAL)
+ return (cstring_resolve("DONT")->value);
+ else
+ return (cstring_resolve("DOESNT")->value);
+}
+
+char *does_output(int index, bool) {
+ if (object[index]->attributes & PLURAL)
+ return (cstring_resolve("DO")->value);
+ else
+ return (cstring_resolve("DOES")->value);
+}
+
+char *list_output(int index, int capital) {
+ if (!strcmp(object[index]->article, "name")) {
+ strcpy(temp_buffer, object[index]->inventory);
+ } else {
+ strcpy(temp_buffer, object[index]->article);
+ strcat(temp_buffer, " ");
+ strcat(temp_buffer, object[index]->inventory);
+ }
+
+ if (capital)
+ temp_buffer[0] = toupper(temp_buffer[0]);
+
+ return (temp_buffer);
+}
+
+char *plain_output(int index, int capital) {
+ strcpy(temp_buffer, object[index]->inventory);
+
+ if (capital)
+ temp_buffer[0] = toupper(temp_buffer[0]);
+
+ return (temp_buffer);
+}
+
+char *long_output(int index) {
+ if (!strcmp(object[index]->described, "function")) {
+ strcpy(function_name, "long_");
+ strcat(function_name, object[index]->label);
+ if (execute(function_name) == FALSE) {
+ unkfunrun(function_name);
+ }
+
+ // THE BUFFER IS RETURNED EMPTY AS THE TEXT IS OUTPUT BY
+ // WRITE STATEMENTS IN THE FUNCTION CALLED
+ temp_buffer[0] = 0;
+ return (temp_buffer);
+ } else {
+ return (object[index]->described);
+ }
+}
+
+void no_it() {
+ write_text(cstring_resolve("NO_IT")->value);
+ write_text(word[wp]);
+ write_text(cstring_resolve("NO_IT_END")->value);
+ custom_error = TRUE;
+}
+
+void look_around() {
+ /* THIS FUNCTION DISPLAYS THE DESCRIPTION OF THE CURRENT LOCATION ALONG
+ * WITH ANY OBJECTS CURRENTLY IN IT */
+
+ if (!check_light(HERE)) {
+ /* THE CURRENT LOCATION HAS 'DARK' AND NO SOURCE OF LIGHT IS
+ * CURRENTLY PRESENT */
+ execute("+dark_description");
+ return;
+ }
+
+ if (execute("+before_look") != FALSE)
+ return;
+
+ execute("+title");
+
+ if (DISPLAY_MODE->value) {
+ /* THE INTERPRETER IS IN VERBOSE MODE SO TEMPORARILY TAKE AWAYS THE
+ * 'VISITED' ATTRIBUTE */
+ object[HERE]->attributes &= ~1L;
+ }
+
+ strcpy(function_name, "look_");
+ strcat(function_name, object[HERE]->label);
+ execute(function_name);
+
+ /* GIVE THE LOCATION THE ATTRIBUTES 'VISITED', 'KNOWN', AND 'MAPPED' NOW
+ * THAT THE LOOK FUNCTION HAS RUN */
+ object[HERE]->attributes = object[HERE]->attributes | KNOWN;
+ object[HERE]->attributes = object[HERE]->attributes | VISITED;
+ object[HERE]->attributes = object[HERE]->attributes | MAPPED;
+
+ execute("+object_descriptions");
+
+ strcpy(function_name, "after_look_");
+ strcat(function_name, object[HERE]->label);
+ execute(function_name);
+
+ execute("+after_look");
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/encapsulate.cpp b/engines/glk/jacl/encapsulate.cpp
new file mode 100644
index 0000000000..8e65ba3932
--- /dev/null
+++ b/engines/glk/jacl/encapsulate.cpp
@@ -0,0 +1,279 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+extern struct synonym_type *synonym_table;
+extern struct filter_type *filter_table;
+
+char text_buffer[1024];
+
+/* THIS IS A STRING CONSTANT TO POINT TO WHENEVER A COMMA IS
+ * USED IN THE PLAYER'S INPUT */
+char *comma = "comma\0";
+char *then = "then\0";
+char *word[MAX_WORDS];
+int quoted[MAX_WORDS];
+int percented[MAX_WORDS];
+int wp;
+
+void
+encapsulate() {
+ int index,
+ length;
+ int position = 0;
+ int new_word = TRUE;
+
+ length = strlen(text_buffer);
+
+ /* QUOTED IS USED TO STORE WHETHER EACH WORD WAS ENCLOSED IN QUOTES
+ * IN THE PLAYERS COMMAND - RESET EACH WORD TO NO */
+ for (index = 0; index < MAX_WORDS; index++) {
+ quoted[index] = 0;
+ percented[index] = 0;
+ }
+
+ for (index = 0; index < length; index++) {
+
+ switch (text_buffer[index]) {
+ case ':':
+ case '\t':
+ case ' ':
+ case ',':
+ text_buffer[index] = 0;
+ new_word = TRUE;
+ break;
+ case ';':
+ case '#':
+ case '\r':
+ case '\n':
+ /* TERMINATE THE WHOLE COMMAND ON HITTING A NEWLINE CHARACTER, A
+ * SEMICOLON OR A HASH */
+ text_buffer[index] = 0;
+ index = length;
+ break;
+ case '"':
+ index++;
+ /* NEED TO REMEMBER THAT THIS WORD WAS ENCLOSED IN QUOTES FOR
+ * THE COMMAND 'write'*/
+ quoted[position] = 1;
+
+ word[position] = &text_buffer[index];
+
+ if (position < MAX_WORDS)
+ position++;
+
+ /* IF A WORD IS ENCLOSED IN QUOTES, KEEP GOING UNTIL THE END
+ * OF THE LINE OR A CLOSING QUOTE IS FOUND, NOT BREAKING AT
+ * WHITESPACE AS USUAL */
+ for (; index < length; index++) {
+ if (text_buffer[index] == '"') {
+ text_buffer[index] = 0;
+ new_word = TRUE;
+ break;
+ } else if (text_buffer[index] == '\r' || text_buffer[index] == '\n') {
+ text_buffer[index] = 0;
+ index = length;
+ break;
+ }
+ }
+ break;
+ default:
+ if (new_word) {
+ if (text_buffer[index] == '%' && text_buffer[index + 1] != ' ' && text_buffer[index + 1] != '\t') {
+ percented[position]++;
+ break;
+ }
+ word[position] = &text_buffer[index];
+ new_word = FALSE;
+ if (position < MAX_WORDS)
+ position++;
+ }
+ break;
+ }
+
+ }
+
+ /* NULL OUT ALL THE WORD POINTERS BEYOND THE LAST WORD */
+ for (index = position; index < MAX_WORDS; index++)
+ word[index] = NULL;
+
+ wp = 0;
+}
+
+// THIS VERSION OF ENCAPSULATE DOESN'T LOOK FOR CERTAIN SPECIAL CHARACTERS
+void
+command_encapsulate() {
+ int index,
+ length;
+ int position = 0;
+ int new_word = TRUE;
+
+ length = strlen(text_buffer);
+
+ // QUOTED IS USED TO STORE WHETHER EACH WORD WAS ENCLOSED IN QUOTES
+ // IN THE PLAYERS COMMAND - RESET EACH WORD TO NO
+ for (index = 0; index < MAX_WORDS; index++) {
+ quoted[index] = 0;
+ }
+
+ for (index = 0; index < length; index++) {
+
+ // REDUSE EVERYTHING TO LOWER CASE EXCEPT TEXT ENCLOSED IN QUOTES
+ text_buffer[index] = tolower((int) text_buffer[index]);
+
+ switch (text_buffer[index]) {
+ case ':':
+ case '\t':
+ case ' ':
+ text_buffer[index] = 0;
+ new_word = TRUE;
+ break;
+ case ',':
+ text_buffer[index] = 0;
+ // SET THIS WORD TO POINT TO A STRING CONSTANT OF 'comma' AS THE
+ // COMMA ITSELF WILL BE NULLED OUT TO TERMINATE THE PRECEEDING
+ // WORD IN THE COMMAND.
+ word[position] = comma;
+ if (position < MAX_WORDS)
+ position++;
+ new_word = TRUE;
+ break;
+ case '.':
+ text_buffer[index] = 0;
+ // SET THIS WORD TO POINT TO A STRING CONSTANT OF 'comma' AS THE
+ // COMMA ITSELF WILL BE NULLED OUT TO TERMINATE THE PRECEEDING
+ // WORD IN THE COMMAND
+ word[position] = then;
+ if (position < MAX_WORDS)
+ position++;
+ new_word = TRUE;
+ break;
+ case ';':
+ case '\r':
+ case '\n':
+ // TERMINATE THE WHOLE COMMAND ON HITTING A NEWLINE CHARACTER, A
+ // SEMICOLON OR A HASH
+ text_buffer[index] = 0;
+ index = length;
+ break;
+ case '"':
+ index++;
+ // NEED TO REMEMBER THAT THIS WORD WAS ENCLOSED IN QUOTES FOR
+ // THE COMMAND 'write'
+ quoted[position] = 1;
+
+ word[position] = &text_buffer[index];
+
+ if (position < MAX_WORDS)
+ position++;
+
+ // IF A WORD IS ENCLOSED IN QUOTES, KEEP GOING UNTIL THE END
+ // OF THE LINE OR A CLOSING QUOTE IS FOUND, NOT BREAKING AT
+ // WHITESPACE AS USUAL
+ for (; index < length; index++) {
+ if (text_buffer[index] == '"') {
+ text_buffer[index] = 0;
+ new_word = TRUE;
+ break;
+ } else if (text_buffer[index] == '\r' || text_buffer[index] == '\n') {
+ text_buffer[index] = 0;
+ index = length;
+ break;
+ }
+ }
+ break;
+ default:
+ if (new_word) {
+ word[position] = &text_buffer[index];
+ new_word = FALSE;
+ if (position < MAX_WORDS)
+ position++;
+ }
+ break;
+ }
+
+ }
+
+ // NULL OUT ALL THE WORD POINTERS BEYOND THE LAST WORD
+ for (index = position; index < MAX_WORDS; index++) {
+ word[index] = NULL;
+ }
+
+ wp = 0;
+}
+
+void
+jacl_truncate() {
+ int index,
+ counter,
+ match;
+ int position = 0;
+
+ struct synonym_type *synonym;
+ struct filter_type *filter = filter_table;
+
+ // REMOVE ALL THE DEFINED 'filter's FROM THE PLAYER'S COMMAND
+ if (filter != NULL) {
+ while (word[position] != NULL) {
+ match = FALSE;
+ do {
+ if (!strcmp(word[position], filter->word)) {
+ for (index = position; word[index + 1] != NULL;
+ index++)
+ word[index] = word[index + 1];
+ word[index] = NULL;
+ match = TRUE;
+ }
+ filter = filter->next_filter;
+ } while (filter != NULL && !match);
+ filter = filter_table;
+ if (!match)
+ position++;
+ };
+ }
+
+ // SUBTITUTE ALL THE DEFINED 'synonym's IN THE PLAYER'S COMMAND
+ if (synonym_table != NULL) {
+ for (counter = 0; word[counter] != NULL; counter++) {
+ synonym = synonym_table;
+ do {
+ if (!strcmp(word[counter], synonym->original)) {
+ word[counter] = synonym->standard;
+ break;
+ }
+ if (synonym->next_synonym != NULL)
+ synonym = synonym->next_synonym;
+ else
+ break;
+ } while (TRUE);
+ }
+ }
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/errors.cpp b/engines/glk/jacl/errors.cpp
new file mode 100644
index 0000000000..fc791add48
--- /dev/null
+++ b/engines/glk/jacl/errors.cpp
@@ -0,0 +1,169 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+extern struct function_type *executing_function;
+extern char *word[];
+
+extern char error_buffer[];
+
+void badparrun() {
+ sprintf(error_buffer, BAD_PARENT, executing_function->name);
+
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void notintrun() {
+ sprintf(error_buffer, NOT_INTEGER, executing_function->name, word[0]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void unkfunrun(char *name) {
+ sprintf(error_buffer, UNKNOWN_FUNCTION_RUN, name);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void unkkeyerr(int line, int wordno) {
+ sprintf(error_buffer, UNKNOWN_KEYWORD_ERR, line, word[wordno]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void unkatterr(int line, int wordno) {
+ sprintf(error_buffer, UNKNOWN_ATTRIBUTE_ERR, line,
+ word[wordno]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void unkvalerr(int line, int wordno) {
+ sprintf(error_buffer, UNKNOWN_VALUE_ERR, line,
+ word[wordno]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void noproprun(int) {
+ sprintf(error_buffer, INSUFFICIENT_PARAMETERS_RUN, executing_function->name, word[0]);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void noobjerr(int line) {
+ sprintf(error_buffer, NO_OBJECT_ERR,
+ line, word[0]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void noproperr(int line) {
+ sprintf(error_buffer, INSUFFICIENT_PARAMETERS_ERR,
+ line, word[0]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void nongloberr(int line) {
+ sprintf(error_buffer, NON_GLOBAL_FIRST, line);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void nofnamerr(int line) {
+ sprintf(error_buffer, NO_NAME_FUNCTION, line);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void unkobjerr(int line, int wordno) {
+ sprintf(error_buffer, UNDEFINED_ITEM_ERR, line, word[wordno]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void maxatterr(int line, int wordno) {
+ sprintf(error_buffer,
+ MAXIMUM_ATTRIBUTES_ERR, line, word[wordno]);
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void unkobjrun(int wordno) {
+ sprintf(error_buffer, UNDEFINED_ITEM_RUN, executing_function->name, word[wordno]);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void unkattrun(int wordno) {
+ sprintf(error_buffer, UNKNOWN_ATTRIBUTE_RUN, executing_function->name, word[wordno]);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void unkdirrun(int wordno) {
+ sprintf(error_buffer, UNDEFINED_DIRECTION_RUN,
+ executing_function->name, word[wordno]);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void badparun() {
+ sprintf(error_buffer, BAD_PARENT, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void badplrrun(int value) {
+ sprintf(error_buffer, BAD_PLAYER, executing_function->name, value);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void badptrrun(char *name, int value) {
+ sprintf(error_buffer, BAD_POINTER, executing_function->name, name, value);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void unkvarrun(char *variable) {
+ sprintf(error_buffer, UNDEFINED_CONTAINER_RUN, executing_function->name, arg_text_of(variable));
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void unkstrrun(char *variable) {
+ sprintf(error_buffer, UNDEFINED_STRING_RUN, executing_function->name, variable);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void unkscorun(char *scope) {
+ sprintf(error_buffer, UNKNOWN_SCOPE_RUN, executing_function->name, scope);
+ log_error(error_buffer, PLUS_STDOUT);
+}
+
+void totalerrs(int errors) {
+ if (errors == 1)
+ sprintf(error_buffer, ERROR_DETECTED);
+ else {
+ sprintf(error_buffer, ERRORS_DETECTED, errors);
+ }
+
+ log_error(error_buffer, PLUS_STDERR);
+}
+
+void outofmem() {
+ log_error(OUT_OF_MEMORY, PLUS_STDERR);
+ error("Terminated");
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/findroute.cpp b/engines/glk/jacl/findroute.cpp
new file mode 100644
index 0000000000..d05dbea590
--- /dev/null
+++ b/engines/glk/jacl/findroute.cpp
@@ -0,0 +1,320 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+extern struct object_type *object[];
+extern int objects;
+
+/**************************************/
+/* Queue functions */
+/**************************************/
+
+struct QueueNode {
+ int val;
+ int val2;
+ struct QueueNode *next;
+};
+
+struct Queue {
+ QueueNode *head;
+ QueueNode *tail;
+};
+
+void qInit(Queue *q) {
+ q->head = q->tail = NULL;
+}
+
+void qDelete(Queue *q) {
+ QueueNode *node, *next;
+
+ for (node = q->head; node != NULL; node = next) {
+ next = node->next;
+ free(node);
+ }
+
+ q->head = q->tail = NULL;
+}
+
+int qIsEmpty(Queue *q) {
+ return (q->head == NULL);
+}
+
+void qDebug(Queue *q) {
+ debug("Queue:");
+
+ if (q->head == NULL) {
+ debug(" empty");
+ } else {
+ QueueNode *node;
+ for (node = q->head; node != NULL; node = node->next) {
+ debug(" %d (%d)", node->val, node->val2);
+ }
+ }
+
+ debug("\n");
+}
+
+void qAppend(Queue *q, int val, int val2) {
+ QueueNode *node = (QueueNode *) malloc(sizeof(QueueNode));
+ node->val = val;
+ node->val2 = val2;
+ node->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = node;
+ } else {
+ q->tail->next = node;
+ q->tail = node;
+ }
+}
+
+void qPop(Queue *q, int *val, int *val2) {
+ //assert(q->head != NULL);
+
+ *val = q->head->val;
+ *val2 = q->head->val2;
+
+ if (q->head == q->tail) {
+ q->head = q->tail = NULL;
+ } else {
+ q->head = q->head->next;
+ }
+}
+
+void qTest() {
+ int val, val2;
+ Queue q;
+
+ qInit(&q);
+ qDebug(&q);
+
+ debug("\nAdd 3, 0\n");
+ qAppend(&q, 3, 0);
+ qDebug(&q);
+
+ debug("\nAdd 4, 2\n");
+ qAppend(&q, 4, 2);
+ qDebug(&q);
+
+ debug("\nAdd 5, 1\n");
+ qAppend(&q, 5, 1);
+ qDebug(&q);
+
+ qPop(&q, &val, &val2);
+ debug("\nPop %d, %d\n", val, val2);
+ qDebug(&q);
+
+ qPop(&q, &val, &val2);
+ debug("\nPop %d, %d\n", val, val2);
+ qDebug(&q);
+
+ debug("\nAdd 6, 3\n");
+ qAppend(&q, 6, 3);
+ qDebug(&q);
+
+ debug("\nDelete all\n");
+ qDelete(&q);
+ qDebug(&q);
+}
+
+/**************************************/
+/* Set functions */
+/**************************************/
+
+/* linked list for hash table */
+struct SetNode {
+ int val;
+ struct SetNode *next;
+};
+
+#define SET_HASHSIZE 101
+
+struct Set {
+ SetNode *node[SET_HASHSIZE];
+};
+
+void setInit(Set *set) {
+ int n;
+
+ for (n = 0; n < SET_HASHSIZE; n++) {
+ set->node[n] = NULL;
+ }
+}
+
+void setDelete(Set *set) {
+ int n;
+
+ for (n = 0; n < SET_HASHSIZE; n++) {
+ SetNode *node, *next;
+
+ for (node = set->node[n]; node != NULL; node = next) {
+ next = node->next;
+ free(node);
+ }
+
+ set->node[n] = NULL;
+ }
+}
+
+void setDebug(Set *set) {
+ int n;
+
+ debug("Set:");
+
+ for (n = 0; n < SET_HASHSIZE; n++) {
+ SetNode *node;
+
+ for (node = set->node[n]; node != NULL; node = node->next) {
+ debug(" %d", node->val);
+ }
+ }
+
+ debug("\n");
+}
+
+int setHash(int val) {
+ return abs(val) % SET_HASHSIZE;
+}
+
+void setAdd(Set *set, int val) {
+ SetNode *node;
+ int n = setHash(val);
+
+ /* check if val is already in the set */
+
+ for (node = set->node[n]; node != NULL; node = node->next) {
+ if (node->val == val) {
+ return;
+ }
+ }
+
+ node = (SetNode *) malloc(sizeof(SetNode));
+ node->val = val;
+ node->next = set->node[n];
+ set->node[n] = node;
+}
+
+/* returns 1 if the set contains val, otherwise returns 0 */
+
+int setContains(Set *set, int val) {
+ SetNode *node;
+ int n = setHash(val);
+
+ for (node = set->node[n]; node != NULL; node = node->next) {
+ if (node->val == val) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void setTest() {
+ Set s;
+
+ setInit(&s);
+ setDebug(&s);
+
+ debug("\nAdd 34\n");
+ setAdd(&s, 34);
+ setDebug(&s);
+
+ debug("\nAdd 56\n");
+ setAdd(&s, 56);
+ setDebug(&s);
+
+ debug("\nAdd 34 again\n");
+ setAdd(&s, 34);
+ setDebug(&s);
+
+ debug("\nAdd %d\n", 34 + SET_HASHSIZE);
+ setAdd(&s, 34 + SET_HASHSIZE);
+ setDebug(&s);
+
+ debug("\nAdd 78\n");
+ setAdd(&s, 78);
+ setDebug(&s);
+
+ debug("\nDelete all\n");
+ setDelete(&s);
+ setDebug(&s);
+}
+
+/**************************************/
+
+#define DIR_NONE -1
+
+int find_route(int fromRoom, int toRoom, int known) {
+ Set visited;
+ Queue q;
+ int firstTime;
+ int result = DIR_NONE;
+
+ setInit(&visited);
+ qInit(&q);
+
+ qAppend(&q, fromRoom, DIR_NONE);
+ setAdd(&visited, fromRoom);
+ firstTime = 1;
+
+ while (!qIsEmpty(&q)) {
+ int n, dir, firstDir;
+ qPop(&q, &n, &firstDir);
+
+ if (n == toRoom) {
+ result = firstDir;
+ break;
+ }
+
+ for (dir = 0; dir < 12 ; dir++) {
+ int dest = object[n]->integer[dir];
+
+ if (dest < 1 || dest > objects) continue;
+
+ if (object[dest] == NULL) continue;
+
+ if (dest != NOWHERE && !setContains(&visited, dest)) {
+ if (!known || (object[dest]->attributes & KNOWN)) {
+ qAppend(&q, dest, (firstTime ? dir : firstDir));
+ setAdd(&visited, dest);
+ }
+ }
+ }
+
+ firstTime = 0;
+ }
+
+ setDelete(&visited);
+ qDelete(&q);
+
+ return result;
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/glk_saver.cpp b/engines/glk/jacl/glk_saver.cpp
new file mode 100644
index 0000000000..abd718fceb
--- /dev/null
+++ b/engines/glk/jacl/glk_saver.cpp
@@ -0,0 +1,272 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+extern struct object_type *object[];
+extern struct integer_type *integer_table;
+extern struct integer_type *integer[];
+extern struct function_type *function_table;
+extern struct string_type *string_table;
+
+extern schanid_t sound_channel[];
+
+extern char temp_buffer[];
+
+extern int objects;
+extern int integers;
+extern int functions;
+extern int strings;
+extern int player;
+
+extern int it;
+extern int them[];
+extern int her;
+extern int him;
+extern int parent;
+
+extern int noun[];
+
+int save_game(frefid_t saveref) {
+ struct integer_type *current_integer = integer_table;
+ struct function_type *current_function = function_table;
+ struct string_type *current_string = string_table;
+
+ int index, counter;
+ strid_t bookmark = NULL;
+
+ bookmark = g_vm->glk_stream_open_file(saveref, filemode_Write, 0);
+
+ if (bookmark == NULL) {
+ return (FALSE);
+ }
+
+ /* WE'RE DONE WITH THE FILE REFERENCE NOW THAT THE STREAM
+ * HAS BEEN SUCCESSFULLY OPENED */
+ g_vm->glk_fileref_destroy(saveref);
+
+ /* THIS IS WRITTEN TO HELP VALIDATE THE SAVED GAME
+ * BEFORE CONTINUING TO LOAD IT */
+ write_integer(bookmark, objects);
+ write_integer(bookmark, integers);
+ write_integer(bookmark, functions);
+ write_integer(bookmark, strings);
+
+ while (current_integer != NULL) {
+ write_integer(bookmark, current_integer->value);
+ current_integer = current_integer->next_integer;
+ }
+
+ while (current_function != NULL) {
+ write_integer(bookmark, current_function->call_count);
+ current_function = current_function->next_function;
+ }
+
+ for (index = 1; index <= objects; index++) {
+ if (object[index]->nosave)
+ continue;
+
+ for (counter = 0; counter < 16; counter++) {
+ write_integer(bookmark, object[index]->integer[counter]);
+ }
+
+ write_long(bookmark, object[index]->attributes);
+ write_long(bookmark, object[index]->user_attributes);
+ }
+
+ /* WRITE OUT ALL THE CURRENT VALUES OF THE STRING VARIABLES */
+ while (current_string != NULL) {
+ for (index = 0; index < 255; index++) {
+ g_vm->glk_put_char_stream(bookmark, current_string->value[index]);
+ }
+ current_string = current_string->next_string;
+ }
+
+ write_integer(bookmark, player);
+ write_integer(bookmark, noun[3]);
+
+ /* SAVE THE CURRENT VOLUME OF EACH OF THE SOUND CHANNELS */
+ for (index = 0; index < 8; index++) {
+ sprintf(temp_buffer, "volume[%d]", index);
+ write_integer(bookmark, cinteger_resolve(temp_buffer)->value);
+ }
+
+ /* SAVE THE CURRENT VALUE OF THE GLK TIMER */
+ write_integer(bookmark, cinteger_resolve("timer")->value);
+
+ /* CLOSE THE STREAM */
+ g_vm->glk_stream_close(bookmark, NULL);
+
+ TIME->value = FALSE;
+ return (TRUE);
+}
+
+int restore_game(frefid_t saveref, int warn) {
+ struct integer_type *current_integer = integer_table;
+ struct function_type *current_function = function_table;
+ struct string_type *current_string = string_table;
+
+ int index, counter;
+ int file_objects,
+ file_integers,
+ file_functions,
+ file_strings;
+ strid_t bookmark;
+
+ bookmark = g_vm->glk_stream_open_file(saveref, filemode_Read, 0);
+
+ if (!bookmark) {
+ return (FALSE);
+ }
+
+ /* WE'RE DONE WITH THE FILE REFERENCE NOW THAT THE STREAM
+ * HAS BEEN SUCCESSFULLY OPENED */
+ g_vm->glk_fileref_destroy(saveref);
+
+ /* THIS IS WRITTEN TO HELP VALIDATE THE SAVED GAME
+ * BEFORE CONTINUING TO LOAD IT */
+ file_objects = read_integer(bookmark);
+ file_integers = read_integer(bookmark);
+ file_functions = read_integer(bookmark);
+ file_strings = read_integer(bookmark);
+
+ if (file_objects != objects
+ || file_integers != integers
+ || file_functions != functions
+ || file_strings != strings) {
+ if (warn == FALSE) {
+ log_error(cstring_resolve("BAD_SAVED_GAME")->value, PLUS_STDOUT);
+ }
+ g_vm->glk_stream_close(bookmark, NULL);
+ return (FALSE);
+ }
+
+ while (current_integer != NULL) {
+ current_integer->value = read_integer(bookmark);
+ current_integer = current_integer->next_integer;
+ }
+
+ while (current_function != NULL) {
+ current_function->call_count = read_integer(bookmark);
+ current_function = current_function->next_function;
+ }
+
+ for (index = 1; index <= objects; index++) {
+ if (object[index]->nosave)
+ continue;
+
+ for (counter = 0; counter < 16; counter++) {
+ object[index]->integer[counter] = read_integer(bookmark);
+ }
+
+ object[index]->attributes = read_integer(bookmark);
+ object[index]->user_attributes = read_integer(bookmark);
+ }
+
+ while (current_string != NULL) {
+ for (index = 0; index < 255; index++) {
+ current_string->value[index] = g_vm->glk_get_char_stream(bookmark);
+ }
+ current_string = current_string->next_string;
+ }
+
+ player = read_integer(bookmark);
+ noun[3] = read_integer(bookmark);
+
+ /* RESTORE THE CURRENT VOLUME OF EACH OF THE SOUND CHANNELS */
+ for (index = 0; index < 8; index++) {
+ sprintf(temp_buffer, "volume[%d]", index);
+ counter = read_integer(bookmark);
+ cinteger_resolve(temp_buffer)->value = counter;
+
+ if (SOUND_SUPPORTED->value) {
+ /* SET THE GLK VOLUME */
+ g_vm->glk_schannel_set_volume(sound_channel[index], (glui32) counter);
+ }
+ }
+
+ /* RESTORE THE CURRENT VALUE OF THE GLK TIMER */
+ counter = read_integer(bookmark);
+ cinteger_resolve("timer")->value = counter;
+
+ /* SET THE GLK TIMER */
+ g_vm->glk_request_timer_events((glui32) counter);
+
+ /* CLOSE THE STREAM */
+ g_vm->glk_stream_close(bookmark, NULL);
+
+ TIME->value = FALSE;
+ return (TRUE);
+}
+
+void write_integer(strid_t stream, int x) {
+ unsigned char c;
+
+ c = (unsigned char)(x) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+ c = (unsigned char)(x >> 8) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+ c = (unsigned char)(x >> 16) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+ c = (unsigned char)(x >> 24) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+}
+
+int read_integer(strid_t stream) {
+ int a, b, c, d;
+ a = (int) g_vm->glk_get_char_stream(stream);
+ b = (int) g_vm->glk_get_char_stream(stream);
+ c = (int) g_vm->glk_get_char_stream(stream);
+ d = (int) g_vm->glk_get_char_stream(stream);
+ return a | (b << 8) | (c << 16) | (d << 24);
+}
+
+void write_long(strid_t stream, long x) {
+ unsigned char c;
+
+ c = (unsigned char)(x) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+ c = (unsigned char)(x >> 8) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+ c = (unsigned char)(x >> 16) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+ c = (unsigned char)(x >> 24) & 0xFF;
+ g_vm->glk_put_char_stream(stream, c);
+}
+
+long read_long(strid_t stream) {
+ long a, b, c, d;
+ a = (long) g_vm->glk_get_char_stream(stream);
+ b = (long) g_vm->glk_get_char_stream(stream);
+ c = (long) g_vm->glk_get_char_stream(stream);
+ d = (long) g_vm->glk_get_char_stream(stream);
+ return a | (b << 8) | (c << 16) | (d << 24);
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/glk_startup.cpp b/engines/glk/jacl/glk_startup.cpp
new file mode 100644
index 0000000000..b49a6cd0f5
--- /dev/null
+++ b/engines/glk/jacl/glk_startup.cpp
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/constants.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/prototypes.h"
+#include "glk/jacl/version.h"
+#include "glk/glk_api.h"
+#include "glk/streams.h"
+
+namespace Glk {
+namespace JACL {
+
+int jpp_error = FALSE;
+
+extern strid_t game_stream;
+extern char game_file[];
+extern char temp_buffer[];
+extern char error_buffer[];
+extern char processed_file[];
+
+short int encrypt;
+extern short int release;
+
+struct glkunix_startup_t {
+ int argc;
+ char **argv;
+};
+
+struct glkunix_argumentlist_t {
+ const char *_switch;
+ int _value;
+ const char *_description;
+};
+
+glkunix_startup_t *arguments;
+
+/* THE STREAM FOR OPENING UP THE ARCHIVE CONTAINING GRAPHICS AND SOUND */
+extern strid_t blorb_stream;
+
+/* PROTOTYPE FOR NEEDED UTILITY FUNCTION */
+void create_paths();
+
+#ifdef UNUSED
+glkunix_argumentlist_t glkunix_arguments[] = {
+ {"", glkunix_arg_ValueFollows, "filename: The game file to load." },
+
+ {"-noencrypt", glkunix_arg_NoValue, "-noencrypt: Don't encrypt the processed game file."},
+ {"-release", glkunix_arg_NoValue, "-release: Don't include the debug libraries in the .j2 file."},
+ { NULL, glkunix_arg_End, NULL }
+};
+
+int glkunix_startup_code(glkunix_startup_t *data) {
+ int index = 0;
+
+ arguments = data;
+
+#ifdef GARGLK
+ sprintf(temp_buffer, "JACL %d.%d.%d", J_VERSION, J_RELEASE, J_BUILD);
+ garglk_set_program_name(temp_buffer);
+ sprintf(temp_buffer, "JACL %d.%d.%d by Stuart Allen.\n", J_VERSION, J_RELEASE, J_BUILD);
+ garglk_set_program_info(temp_buffer);
+#endif
+
+ /* YOU CAN PUT OTHER STARTUP CODE IN glkunix_startup_code(). THIS SHOULD
+ * GENERALLY BE LIMITED TO FINDING AND OPENING DATA FILES. */
+
+ if (arguments->argc == 1) {
+ sprintf(error_buffer, "%s^", NO_GAME);
+ jpp_error = TRUE;
+
+ /* WE NEED TO RETURN TRUE HERE SO THE INTERPRETER WILL OPEN A
+ * GLK WINDOWS TO DISPLAY THE ERROR MESSAGE IN */
+ return (TRUE);
+ } else {
+ strcpy(temp_buffer, arguments->argv[1]);
+
+ /* THERE IS AT LEAST ONE ARGUMENT, POSSIBLY JUST THE GAME FILE, BUT
+ * LOOK THROUGH THE LIST FOR ANYTHING THAT NEEDS ACTING ON */
+ for (index = 0; index < data->argc; index++) {
+ if (!strcmp(*data->argv, "-noencrypt")) {
+ encrypt = FALSE;
+ } else if (!strcmp(*data->argv, "-release")) {
+ release = TRUE;
+ }
+
+ /* INCREMENT THE POINTER TO THE NEXT ARGUMENT */
+ data->argv++;
+ }
+ }
+
+ /* SETUP ALL THE EXPECTED PATHS */
+ //create_paths(temp_buffer);
+
+ /* PREPROCESS THE FILE AND WRITE IT OUT TO THE NEW FILE */
+ /* WARNING: THIS FUNCTION USES stdio FUNCTIONS TO CREATE FILES
+ * IN SUBDIRECTORIES. IT IS PORTABLE ACROSS MODERN DESKTOPS, IE
+ * WINDOWS, MAC, UNIX ETC, BUT IT'S NOT GLK CODE... */
+ if (jpp() == FALSE) {
+ jpp_error = TRUE;
+
+ /* WE NEED TO RETURN TRUE HERE SO THE INTERPRETER WILL OPEN A
+ * GLK WINDOWS TO DISPLAY THE ERROR MESSAGE IN */
+ return (TRUE);
+ }
+
+ /* THIS OPENS AN ARBITRARY FILE, IN READ-ONLY MODE. NOTE THAT THIS FUNCTION
+ * IS *ONLY* AVAILABLE DURING glkunix_startup_code(). IT IS INHERENT
+ * NON-PORTABLE; IT SHOULD NOT AND CANNOT BE CALLED FROM INSIDE
+ * glk_main() NOTE: The middle argument FALSE indicates a binary file. */
+ game_stream = glkunix_stream_open_pathname(processed_file, FALSE, 0);
+
+ if (!game_stream) {
+ strcpy(error_buffer, NOT_FOUND);
+ jpp_error = TRUE;
+
+ /* WE NEED TO RETURN TRUE HERE SO THE INTERPRETER WILL OPEN A
+ * GLK WINDOWS TO DISPLAY THE ERROR MESSAGE IN */
+ return (TRUE);
+ }
+
+ /* SET THE LIBRARY'S IDEA OF THE "CURRENT DIRECTORY" FOR THE EXECUTING
+ * PROGRAM. THE ARGUMENT SHOULD BE THE NAME OF A FILE (NOT A DIRECTORY).
+ * WHEN THIS IS SET, fileref_create_by_name() WILL CREATE FILES IN THE SAME
+ * DIRECTORY AS THAT FILE, AND create_by_prompt() WILL BASE DEFAULT
+ * FILENAMES OFF OF THE FILE. IF THIS IS NOT CALLED, THE LIBRARY WORKS IN
+ * THE UNIX CURRENT WORKING DIRECTORY, AND PICKS REASONABLE DEFAULT
+ * DEFAULTS. */
+ glkunix_set_base_file(game_file);
+
+ /* Set title of game */
+#ifdef GARGLK
+ char *s;
+ s = strrchr(game_file, '\\');
+ if (!s) s = strrchr(game_file, '/');
+ garglk_set_story_name(s ? s + 1 : game_file);
+#endif
+
+ /* RETURN TRUE ERRORS OR NOT SO THE MAIN WINDOWS CAN BE OPENED AND
+ * ANY ERROR MESSAGE DISPLAYED */
+ return (TRUE);
+}
+#endif
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/interpreter.cpp b/engines/glk/jacl/interpreter.cpp
new file mode 100644
index 0000000000..47c07c8acd
--- /dev/null
+++ b/engines/glk/jacl/interpreter.cpp
@@ -0,0 +1,3458 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+#include "glk/jacl/csv.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace JACL {
+
+#ifdef WIN32
+struct flock {
+ short l_type;
+ short l_whence;
+ long l_start;
+ long l_len;
+ long l_pid;
+};
+
+#define F_DUPFD 0
+#define F_GETFD 1
+#define F_SETFD 2
+#define F_GETFL 3
+#define F_SETFL 4
+#define F_GETLK 5
+#define F_SETLK 6
+#define F_SETLKW 7
+
+#define F_RDLCK 0
+#define F_WRLCK 1
+#define F_UNLCK 2
+
+int fcntl(int __fd, int __cmd, ...) {
+ return 0;
+}
+#endif /* WIN32 */
+
+#ifndef strcasestr
+char *strcasestr(const char *s, const char *find) {
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != 0) {
+ c = (char)tolower((unsigned char)c);
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return (NULL);
+ } while ((char)tolower((unsigned char)sc) != c);
+ } while (scumm_strnicmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+#endif
+
+#define MAX_TRY 10
+
+struct flock read_lck;
+int read_fd;
+struct flock write_lck;
+int write_fd;
+
+char *url_encode(char *str);
+char to_hex(char code);
+
+char *location_attributes[] = {
+ "VISITED ", "DARK ", "ON_WATER ", "UNDER_WATER ", "WITHOUT_AIR ", "OUTDOORS ",
+ "MID_AIR ", "TIGHT_ROPE ", "POLLUTED ", "SOLVED ", "MID_WATER ", "DARKNESS ",
+ "MAPPED ", "KNOWN ",
+ NULL
+};
+
+char *object_attributes[] = {
+ "CLOSED ", "LOCKED ", "DEAD ", "IGNITABLE ", "WORN ", "CONCEALING ",
+ "LUMINOUS ", "WEARABLE ", "CLOSABLE ", "LOCKABLE ", "ANIMATE ", "LIQUID ",
+ "CONTAINER ", "SURFACE ", "PLURAL ", "FLAMMABLE ", "BURNING ", "LOCATION ",
+ "ON ", "DAMAGED ", "FEMALE ", "POSSESSIVE ", "OUT_OF_REACH ", "TOUCHED ",
+ "SCORED ", "SITTING ", "NPC ", "DONE ", "GAS ", "NO_TAB ",
+ "NOT_IMPORTANT ", NULL
+};
+
+char *object_elements[] = {
+ "parent", "capacity", "mass", "bearing", "velocity", "next", "previous",
+ "child", "index", "status", "state", "counter", "points", "class", "x", "y",
+ NULL
+};
+
+char *location_elements[] = {
+ "north", "south", "east", "west", "northeast", "northwest", "southeast",
+ "southwest", "up", "down", "in", "out", "points", "class", "x", "y",
+ NULL
+};
+
+struct csv_parser parser_csv;
+char in_name[1024];
+char out_name[1024];
+Common::SeekableReadStream *infile;
+Common::WriteStream *outfile;
+
+int stack = 0;
+int proxy_stack = 0;
+
+int field_no = 0;
+
+struct stack_type backup[STACK_SIZE];
+struct proxy_type proxy_backup[STACK_SIZE];
+
+struct function_type *resolved_function = NULL;
+struct string_type *resolved_string = NULL;
+
+struct string_type *new_string = NULL;
+struct string_type *current_cstring = NULL;
+struct string_type *previous_cstring = NULL;
+
+struct cinteger_type *new_cinteger = NULL;
+struct cinteger_type *current_cinteger = NULL;
+struct cinteger_type *previous_cinteger = NULL;
+
+long bit_mask;
+extern int encrypted;
+extern int after_from;
+extern int last_exact;
+
+extern char temp_directory[];
+extern char data_directory[];
+char csv_buffer[1024];
+
+int resolved_attribute;
+
+/* THE ITERATION VARIABLE USED FOR LOOPS */
+int *loop_integer = NULL;
+int *select_integer = NULL;
+
+int criterion_value = 0;
+int criterion_type = 0;
+int criterion_negate = FALSE;
+int current_level;
+int execution_level;
+int *ask_integer;
+int new_x;
+int new_y;
+
+int interrupted = FALSE;
+char string_buffer[2048];
+char argument_buffer[1024];
+#ifdef GLK
+extern schanid_t sound_channel[];
+extern strid_t game_stream;
+extern winid_t mainwin;
+extern winid_t statuswin;
+extern winid_t current_window;
+
+extern strid_t mainstr;
+extern strid_t statusstr;
+extern strid_t quotestr;
+extern strid_t inputstr;
+int top_of_loop = 0;
+int top_of_select = 0;
+int top_of_while = 0;
+int top_of_iterate = 0;
+int top_of_update = 0;
+int top_of_do_loop = 0;
+#else
+extern FILE *file;
+char option_buffer[2024];
+int style_stack[100];
+int style_index = 0;
+long top_of_loop = 0;
+long top_of_select = 0;
+long top_of_while = 0;
+long top_of_iterate = 0;
+long top_of_update = 0;
+long top_of_do_loop = 0;
+
+#endif
+
+#ifdef __NDS__
+extern int bold_mode;
+extern int pre_mode;
+extern int reverse_mode;
+extern int input_mode;
+extern int subheader_mode;
+extern int note_mode;
+#endif
+
+extern char user_id[];
+extern char prefix[];
+extern char text_buffer[];
+extern char chunk_buffer[];
+extern char *word[];
+
+extern char bookmark[];
+extern char file_prompt[];
+
+/* CONTAINED IN PARSER.C */
+extern int object_list[4][MAX_WORDS];
+extern int list_size[];
+extern int max_size[];
+
+/* CONTAINED IN ENCAPSULATE.C */
+extern int quoted[];
+
+extern struct object_type *object[];
+extern struct integer_type *integer_table;
+extern struct integer_type *integer[];
+extern struct cinteger_type *cinteger_table;
+extern struct attribute_type *attribute_table;
+extern struct string_type *string_table;
+extern struct string_type *cstring_table;
+extern struct function_type *function_table;
+extern struct function_type *executing_function;
+extern struct command_type *completion_list;
+extern struct word_type *grammar_table;
+extern struct synonym_type *synonym_table;
+extern struct filter_type *filter_table;
+
+extern char function_name[];
+extern char temp_buffer[];
+extern char error_buffer[];
+extern char proxy_buffer[];
+
+extern char default_function[];
+extern char override[];
+
+extern int noun[];
+extern int wp;
+extern int start_of_this_command;
+extern int start_of_last_command;
+extern int buffer_index;
+extern int objects;
+extern int integers;
+extern int player;
+extern int oec;
+extern int *object_element_address;
+extern int *object_backup_address;
+extern int walkthru_running;
+
+// VALUES FROM LOADER
+extern int value_resolved;
+
+extern Common::WriteStream *transcript;
+extern char margin_string[];
+
+char integer_buffer[16];
+char called_name[1024];
+char scope_criterion[24];
+char *output;
+
+void terminate(int code) {
+ // FREE ANY EXTRA RAM ALLOCATED BY THE CSV PARSER
+ csv_free(&parser_csv);
+
+#ifdef GLK
+ int index;
+ event_t event;
+
+ // FLUSH THE GLK WINDOW SO THE ERROR GETS DISPLAYED IMMEDIATELY.
+ g_vm->glk_select_poll(&event);
+
+ /* CLOSE THE SOUND CHANNELS */
+ for (index = 0; index < 8; index++) {
+ if (sound_channel[index] != NULL) {
+ g_vm->glk_schannel_destroy(sound_channel[index]);
+ }
+ }
+
+ /* CLOSE THE STREAM */
+ if (game_stream != NULL) {
+ g_vm->glk_stream_close(game_stream, NULL);
+ }
+
+ g_vm->glk_exit();
+#else
+ if (file != NULL) /* CLOSE THE GAME FILE */
+ fclose(file);
+
+ exit(code);
+#endif
+}
+
+void build_proxy() {
+ int index;
+
+ proxy_buffer[0] = 0;
+
+ /* LOOP THROUGH ALL THE PARAMETERS OF THE PROXY COMMAND
+ AND BUILD THE MOVE TO BE ISSUED ON THE PLAYER'S BEHALF */
+ for (index = 1; word[index] != NULL; index++) {
+ strcat(proxy_buffer, text_of_word(index));
+ }
+
+ for (index = 0; index < (int)strlen(proxy_buffer); index++) {
+ if (proxy_buffer[index] == '~') {
+ proxy_buffer[index] = '\"';
+ }
+ }
+
+ //printf("--- proxy buffer = \"%s\"\n", proxy_buffer);
+}
+
+void cb1(void *s, size_t i, void *not_used) {
+ struct string_type *resolved_cstring;
+
+ //sprintf (temp_buffer, "Trying to set field %d to equal %s^", field_no, (char *) s);
+ //write_text(temp_buffer);
+
+ sprintf(temp_buffer, "field[%d]", field_no);
+
+ if ((resolved_cstring = cstring_resolve(temp_buffer)) != NULL) {
+ //write_text("Resolved ");
+ //write_text(temp_buffer);
+ //write_text("^");
+ strncpy(resolved_cstring->value, (const char *)s, i);
+ resolved_cstring->value[i] = 0;
+ //sprintf(temp_buffer, "Setting field %d to ~%s~^", field_no, (char *) s);
+ //write_text(temp_buffer);
+ // INCREMENT THE FIELD NUMBER SO THE NEXT ONE GETS STORED IN THE RIGHT CONSTANT
+ field_no++;
+ } else {
+ write_text("Can't resolve ");
+ write_text(temp_buffer);
+ write_text("^");
+ }
+
+}
+
+void cb2(int c, void *not_used) {
+ // THE END OF THE RECORD HAS BEEN REACHED, EXPORT THE NUMBER OF FIELDS READ
+ struct cinteger_type *resolved_cinteger;
+
+ if ((resolved_cinteger = cinteger_resolve("field_count")) != NULL) {
+ resolved_cinteger->value = field_no;
+ }
+}
+
+int execute(char *funcname) {
+ int index;
+ int counter;
+ int *container;
+
+ int object_1,
+ object_2;
+
+ /* THESE VARIABLE KEEP TRACK OF if AND endif COMMANDS TO DECIDE WHETHER
+ *THE CURRENT LINE OF CODE SHOULD BE EXECUTED OR NOT */
+ int currentLevel = 0;
+ int executionLevel = 0;
+
+ /* THESE ARE USED AS FILE POINTER OFFSETS TO RETURN TO FIXED
+ * POINTS IN THE GAME FILE */
+#ifdef GLK
+ int result;
+ int before_command = 0;
+#else
+ long before_command = 0;
+#endif
+
+
+ strncpy(called_name, funcname, 1023);
+
+ /* GET THE FUNCTION OBJECT BY THE FUNCTION NAME */
+ resolved_function = function_resolve(called_name);
+
+ if (resolved_function == NULL) {
+ //printf("--- failed to find %s\n", called_name);
+ return (FALSE);
+ }
+
+#ifdef GLK
+ push_stack(g_vm->glk_stream_get_position(game_stream));
+#else
+ push_stack(ftell(file));
+#endif
+
+ top_of_loop = 0;
+ top_of_select = 0;
+ top_of_while = 0;
+ top_of_iterate = 0;
+ top_of_update = 0;
+ top_of_do_loop = 0;
+
+ executing_function = resolved_function;
+ executing_function->call_count++;
+
+ // CREATE ALL THE PASSED ARGUMENTS AS JACL INTEGER CONSTANTS
+ set_arguments(called_name);
+
+ // SET function_name TO THE CORE NAME STORED IN THE FUNCTION OBJECT
+ // LEAVING called_name TO CONTAIN THE FULL ARGUMENT LIST
+ strncpy(function_name, executing_function->name, 80);
+ strncpy(cstring_resolve("function_name")->value, executing_function->name, 80);
+
+ //sprintf(temp_buffer, "--- starting to execute %s^", function_name);
+ //write_text(temp_buffer);
+
+ // JUMP TO THE POINT IN THE PROCESSED GAME FILE WHERE THIS FUNCTION STARTS
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, executing_function->position, seekmode_Start);
+ before_command = executing_function->position;
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fseek(file, executing_function->position, SEEK_SET);
+ before_command = executing_function->position;
+ fgets(text_buffer, 1024, file);
+#endif
+
+ if (encrypted) jacl_decrypt(text_buffer);
+
+ while (text_buffer[0] != 125 && !interrupted) {
+ encapsulate();
+ if (word[0] == NULL);
+ else if (!strcmp(word[0], "endwhile")) {
+ currentLevel--;
+ if (currentLevel < executionLevel) {
+ // THIS ENDWHILE COMMAND WAS BEING EXECUTED,
+ // NOT JUST COUNTED.
+ if (top_of_while == FALSE) {
+ sprintf(error_buffer, NO_WHILE, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_while, seekmode_Start);
+#else
+ fseek(file, top_of_while, SEEK_SET);
+#endif
+ executionLevel = currentLevel;
+ }
+ }
+ } else if (!strcmp(word[0], "enditerate")) {
+ currentLevel--;
+ if (currentLevel < executionLevel) {
+ // THIS ENDITERATE COMMAND WAS BEING EXECUTED,
+ // NOT JUST COUNTED.
+ if (top_of_iterate == FALSE) {
+ sprintf(error_buffer, NO_ITERATE, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_iterate, seekmode_Start);
+#else
+ fseek(file, top_of_iterate, SEEK_SET);
+#endif
+ executionLevel = currentLevel;
+ }
+ }
+ } else if (!strcmp(word[0], "endupdate")) {
+ currentLevel--;
+ if (currentLevel < executionLevel) {
+ // THIS ENDUPDATE COMMAND WAS BEING EXECUTED,
+ // NOT JUST COUNTED.
+ if (top_of_update == FALSE) {
+ sprintf(error_buffer, NO_UPDATE, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_update, seekmode_Start);
+#else
+ fseek(file, top_of_update, SEEK_SET);
+#endif
+ executionLevel = currentLevel;
+ }
+ }
+ } else if (!strcmp(word[0], "print") && currentLevel != executionLevel) {
+ // SKIP THIS BLOCK OF PLAIN TEXT UNTIL IT FINDS A
+ // LINE THAT STARTS WITH A '.' OR A '}'
+#ifdef GLK
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+
+ if (encrypted) jacl_decrypt(text_buffer);
+
+ while (text_buffer[0] != '.') {
+ if (text_buffer[0] == '}') {
+ // HIT THE END OF THE FUNCTION, JUST BAIL OUT
+ return (exit_function(TRUE));
+ }
+
+ // GET THE NEXT LINE
+#ifdef GLK
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+
+ if (encrypted) jacl_decrypt(text_buffer);
+
+ }
+ } else if (!strcmp(word[0], "endif")) {
+ currentLevel--;
+ if (currentLevel < executionLevel) {
+ /* THIS SHOULD NEVER HAPPEN */
+ executionLevel = currentLevel;
+ }
+ } else if (!strcmp(word[0], "endall")) {
+ currentLevel = 0;
+ executionLevel = 0;
+ } else if (!strcmp(word[0], "else")) {
+ if (currentLevel == executionLevel) {
+ executionLevel--;
+ } else if (currentLevel == executionLevel + 1) {
+ executionLevel++;
+ }
+ } else if (currentLevel == executionLevel) {
+ if (!strcmp(word[0], "look")) {
+ // THIS IS JUST HERE FOR BACKWARDS COMPATIBILITY
+ object[HERE]->attributes &= ~1L;
+ look_around();
+ } else if (!strcmp(word[0], "repeat")) {
+#ifdef GLK
+ top_of_do_loop = g_vm->glk_stream_get_position(game_stream);
+#else
+ top_of_do_loop = ftell(file);
+#endif
+ } else if (!strcmp(word[0], "until")) {
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (top_of_do_loop == FALSE) {
+ sprintf(error_buffer, NO_REPEAT, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else if (!condition()) {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_do_loop, seekmode_Start);
+#else
+ fseek(file, top_of_do_loop, SEEK_SET);
+#endif
+ }
+ }
+ } else if (!strcmp(word[0], "untilall")) {
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (top_of_do_loop == FALSE) {
+ sprintf(error_buffer, NO_REPEAT, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else if (!and_condition()) {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_do_loop, seekmode_Start);
+#else
+ fseek(file, top_of_do_loop, SEEK_SET);
+#endif
+
+ }
+ }
+ } else if (!strcmp(word[0], "iterate")) {
+ int i;
+
+ // A NEW iterate LOOP MEANS STARTING BACK AT THE FIRST FIELD
+ field_no = 0;
+
+ currentLevel++;
+ /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
+ EXECUTING, NOT THE LINE AFTER */
+
+ top_of_iterate = before_command;
+
+ // infile REMAINS OPEN DURING THE ITERATION, ONLY NEEDS
+ // OPENING THE FIRST TIME
+ if (infile == NULL) {
+ strcpy(temp_buffer, data_directory);
+ strcat(temp_buffer, prefix);
+ strcat(temp_buffer, "-");
+ strcat(temp_buffer, text_of_word(1));
+ strcat(temp_buffer, ".csv");
+
+ infile = File::openForReading(temp_buffer);
+
+ if (word[2] != NULL && !strcmp(word[2], "skip_header")) {
+ assert(infile);
+ infile->read(csv_buffer, 1024);
+ }
+ }
+
+ if (infile == NULL) {
+ sprintf(error_buffer, "Failed to open file %s: %s\n", temp_buffer, strerror(errno));
+ log_error(error_buffer, LOG_ONLY);
+ infile = NULL;
+ } else {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ // IF THERE IS ANOTHER RECORD TO READ FROM THE CSV FILE THEN
+ // SET THE field[] CONSTANTS AND INCREMENT THE executionLevel
+ infile->read(csv_buffer, 1024);
+
+ if (infile->pos() < infile->size()) {
+ i = strlen(csv_buffer);
+ //sprintf (temp_buffer, "Read ~%s~ with %d bytes.^", csv_buffer, i);
+ //write_text(temp_buffer);
+ if (csv_parse(&parser_csv, csv_buffer, i, cb1, cb2, (void *) NULL) != (uint)i) {
+ sprintf(error_buffer, "Error parsing file: %s\n", csv_strerror(csv_error(&parser_csv)));
+ log_error(error_buffer, PLUS_STDOUT);
+ delete infile;
+ infile = NULL;
+ } else {
+ // A LINE HAS BEEN SUCCESSFULLY READ, EXECUTE THE CONTENTS OF THE LOOP
+ executionLevel++;
+ }
+ } else {
+ delete infile;
+ infile = NULL;
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "update")) {
+ int i;
+
+ // SET UP THE RECORD LOCKING STRUCTURE, THE ADDRESS OF WHICH
+ // IS PASSED TO THE fcntl() SYSTEM CALL
+ write_lck.l_type = F_WRLCK; // SETTING A WRITE LOCK
+ write_lck.l_whence = 0; // OFFSET l_start FROM BEGINNING OF FILE
+ write_lck.l_start = 0LL;
+ write_lck.l_len = 0LL; // UNTIL THE END OF THE FILE ADDRESS SPACE
+
+ read_lck.l_type = F_RDLCK; // SETTING A READ LOCK
+ read_lck.l_whence = 0; // OFFSET l_start FROM BEGINNING OF FILE
+ read_lck.l_start = 0LL;
+ read_lck.l_len = 0LL; // UNTIL THE END OF THE FILE ADDRESS SPACE
+
+ // A NEW iterate LOOP MEANS STARTING BACK AT THE FIRST FIELD
+ field_no = 0;
+
+ currentLevel++;
+ // THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
+ // EXECUTING, NOT THE LINE AFTER
+
+ top_of_update = before_command;
+
+ // infile REMAINS OPEN DURING THE ITERATION, ONLY NEEDS
+ // OPENING THE FIRST TIME
+ if (infile == NULL) {
+ strcpy(in_name, data_directory);
+ strcat(in_name, prefix);
+ strcat(in_name, "-");
+ strcat(in_name, text_of_word(1));
+ strcat(in_name, ".csv");
+
+ infile = File::openForReading(in_name);
+ }
+
+ if (outfile == NULL) {
+ // OPEN A TEMPORARY OUTPUT FILE TO WRITE THE MODIFICATIONS TO
+ strcpy(out_name, data_directory);
+ strcat(out_name, prefix);
+ strcat(out_name, "-");
+ strcat(out_name, text_of_word(1));
+ strcat(out_name, "-");
+ strcat(out_name, user_id);
+ strcat(out_name, ".csv");
+
+ outfile = File::openForWriting(out_name);
+ }
+
+ if (infile == NULL) {
+ sprintf(error_buffer, "Failed to open input CSV file ~%s~: %s\n", in_name, strerror(errno));
+ log_error(error_buffer, LOG_ONLY);
+ if (outfile != NULL) {
+ delete outfile;
+ outfile = NULL;
+ }
+ return (exit_function(TRUE));
+ } else {
+ if (outfile == NULL) {
+ sprintf(error_buffer, "Failed to open output CSV file ~%s~: %s\n", out_name, strerror(errno));
+ log_error(error_buffer, LOG_ONLY);
+ if (infile != NULL) {
+ delete infile;
+ infile = NULL;
+ }
+ return (exit_function(TRUE));
+ } else {
+#ifdef FILE_CTL
+ int tryCtr = 0;
+ write_fd = fileno(outfile);
+ // ATTEMPT LOCKING OUTPUT FILE MAX_TRY TIMES BEFORE GIVING UP.
+ while (fcntl(write_fd, F_SETLK, &write_lck) < 0) {
+ if (errno == EAGAIN || errno == EACCES) {
+ // THERE MIGHT BE OTHER ERROR CASES IN WHICH
+ // USERS MIGHT TRY AGAIN
+ if (++tryCtr < MAX_TRY) {
+ jacl_sleep(1000);
+ continue;
+ }
+ sprintf(error_buffer, "File busy unable to get lock on output file.\n");
+ log_error(error_buffer, PLUS_STDOUT);
+ return (exit_function(TRUE));
+ }
+ }
+
+ tryCtr = 0;
+
+ read_fd = fileno(infile);
+ // ATTEMPT LOCKING OUTPUT FILE MAX_TRY TIMES BEFORE GIVING UP.
+ while (fcntl(read_fd, F_SETLK, &read_lck) < 0) {
+ if (errno == EAGAIN || errno == EACCES) {
+ // THERE MIGHT BE OTHER ERROR CASES IN WHICH
+ // USERS MIGHT TRY AGAIN
+ if (++tryCtr < MAX_TRY) {
+ jacl_sleep(1000);
+ continue;
+ }
+ sprintf(error_buffer, "File busy unable to get lock on input file.\n");
+ log_error(error_buffer, PLUS_STDOUT);
+ return (exit_function(TRUE));
+ }
+ }
+#endif
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ // IF THERE IS ANOTHER RECORD TO READ FROM THE CSV FILE THEN
+ // SET THE field[] CONSTANTS AND INCREMENT THE executionLevel
+ infile->read(csv_buffer, 1024);
+ if (infile->pos() < infile->size()) {
+ i = strlen(csv_buffer);
+ if (csv_parse(&parser_csv, csv_buffer, i, cb1, cb2, (int *) &field_no) != (uint)i) {
+ sprintf(error_buffer, "Error parsing file: %s\n", csv_strerror(csv_error(&parser_csv)));
+ log_error(error_buffer, PLUS_STDOUT);
+ read_lck.l_type = F_UNLCK; // SETTING A READ LOCK
+ fcntl(read_fd, F_SETLK, &read_lck);
+ delete infile;
+ infile = NULL;
+ } else {
+ // A LINE HAS BEEN SUCCESSFULLY READ, EXECUTE THE CONTENTS OF THE LOOP
+ executionLevel++;
+ }
+ } else {
+ write_lck.l_type = F_UNLCK; // REMOVE THE WRITE LOCK
+ fcntl(write_fd, F_SETLK, &write_lck);
+ delete outfile;
+
+ read_lck.l_type = F_UNLCK; // REMOVE THE READ LOCK
+ fcntl(read_fd, F_SETLK, &read_lck);
+ delete infile;
+
+ rename(out_name, in_name);
+
+ outfile = NULL;
+ infile = NULL;
+ }
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "while")) {
+ currentLevel++;
+ /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
+ EXECUTING, NOT THE LINE AFTER */
+ top_of_while = before_command;
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else if (condition()) {
+ executionLevel++;
+ }
+ } else if (!strcmp(word[0], "whileall")) {
+ currentLevel++;
+ /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
+ EXECUTING, NOT THE LINE AFTER */
+ top_of_while = before_command;
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else if (and_condition()) {
+ executionLevel++;
+ }
+ } else if (!strcmp(word[0], "loop")) {
+ /* THE LOOP COMMAND LOOPS ONCE FOR EACH DEFINED
+ * OBJECT (FOREACH) */
+#ifdef GLK
+ top_of_loop = g_vm->glk_stream_get_position(game_stream);
+#else
+ top_of_loop = ftell(file);
+#endif
+ if (word[1] == NULL) {
+ // IF NONE IS SUPPLIED DEFAULT TO noun3
+ loop_integer = &noun[2];
+ } else {
+ // STORE THE CONTAINER TO PUT THE CURRENT OBJECT IN
+ loop_integer = container_resolve(word[1]);
+
+ // IF THE SUPPLIED CONTAINER CAN'T BE RESOLVED
+ // DEFAULT TO noun3
+ if (loop_integer == NULL)
+ loop_integer = &noun[2];
+ }
+
+ // SET THE VALUE OF THE LOOP INDEX TO POINT TO THE FIRST OBJECT
+ *loop_integer = 1;
+
+ } else if (!strcmp(word[0], "endloop")) {
+ if (top_of_loop == FALSE) {
+ sprintf(error_buffer, NO_LOOP, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+ *loop_integer += 1;
+ if (*loop_integer > objects) {
+ top_of_loop = FALSE;
+ *loop_integer = 0;
+ } else {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_loop, seekmode_Start);
+#else
+ fseek(file, top_of_loop, SEEK_SET);
+#endif
+ }
+ }
+ } else if (!strcmp(word[0], "select")) {
+ /* THE SELECT COMMAND LOOPS ONCE FOR EACH DEFINED
+ * OBJECT THAT MATCHES THE SUPPLIED CRITERION */
+#ifdef GLK
+ top_of_select = g_vm->glk_stream_get_position(game_stream);
+#else
+ top_of_select = ftell(file);
+#endif
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else if (word[2] == NULL) {
+ // IF NONE IS SUPPLIED DEFAULT TO noun3
+ select_integer = &noun[2];
+ } else {
+ // STORE THE CONTAINER TO PUT THE CURRENT OBJECT IN
+ select_integer = container_resolve(word[2]);
+
+ // IF THE SUPPLIED CONTAINER CAN'T BE RESOLVED
+ // DEFAULT TO noun3
+ if (select_integer == NULL) {
+ select_integer = &noun[2];
+ }
+ }
+
+ // SET THE VALUE OF THE SELECT INDEX TO ONE BEFORE THE
+ // FIRST OBJECT. THE NEXT FUNCTION AUTOMATICALLY INCREMENTS
+ // THE INDEX BY ONE AT THE START OF THE WHILE LOOP.
+ *select_integer = 0;
+
+ if (word[1][0] == '!') {
+ criterion_negate = TRUE;
+ strcpy(argument_buffer, &word[1][1]);
+ } else {
+ criterion_negate = FALSE;
+ strcpy(argument_buffer, word[1]);
+ }
+
+ // DETERMINE THE CRITERION FOR SELETION
+ if (!strcmp(argument_buffer, "*held")
+ || !strcmp(argument_buffer, "*here")
+ || !strcmp(argument_buffer, "*anywhere")
+ || !strcmp(argument_buffer, "*present")) {
+ criterion_type = CRI_SCOPE;
+ strncpy(scope_criterion, argument_buffer, 20);
+ } else if ((criterion_value = attribute_resolve(argument_buffer))) {
+ criterion_type = CRI_ATTRIBUTE;
+ } else if ((criterion_value = user_attribute_resolve(argument_buffer))) {
+ criterion_type = CRI_USER_ATTRIBUTE;
+ } else {
+ // USE VALUE OF AS A CATCH ALL IF IT IS NOT AN ATTRIBUTE OR SCOPE
+ criterion_value = value_of(argument_buffer);
+
+ if (value_resolved) {
+ criterion_type = CRI_PARENT;
+ } else {
+ // CAN'T RESOLVE CRITERION
+ criterion_type = CRI_NONE;
+ }
+ }
+
+ if (criterion_type != CRI_NONE) {
+ if (select_next() == FALSE) {
+ *select_integer = 0;
+ top_of_select = 0;
+ }
+ } else {
+ *select_integer = 0;
+ }
+
+ if (*select_integer == 0) {
+ // THERE ARE NO MATCHING OBJECTS SO JUMP TO THE endselect
+#ifdef GLK
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+
+ if (encrypted) jacl_decrypt(text_buffer);
+
+ while (text_buffer[0] != '}') {
+ encapsulate();
+ if (word[0] != NULL && !strcmp(word[0], "endselect")) {
+ break;
+ }
+#ifdef GLK
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ }
+ }
+ } else if (!strcmp(word[0], "endselect")) {
+ if (top_of_select == FALSE) {
+ sprintf(error_buffer, NO_LOOP, executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+ if (select_next(/* select_integer, criterion_type, criterion_value, scope_criterion */)) {
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, top_of_select, seekmode_Start);
+#else
+ fseek(file, top_of_select, SEEK_SET);
+#endif
+ } else {
+ *select_integer = 0;
+ top_of_select = 0;
+ }
+ }
+ } else if (!strcmp(word[0], "break")) {
+ currentLevel++;
+ executionLevel--;
+#ifdef GLK
+ } else if (!strcmp(word[0], "cursor")) {
+ if (word[2] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ if (current_window == statuswin) {
+ g_vm->glk_window_move_cursor(statuswin, value_of(word[1], TRUE), value_of(word[2], TRUE));
+ } else {
+ log_error(BAD_CURSOR, PLUS_STDOUT);
+ }
+ }
+ } else if (!strcmp(word[0], "stop")) {
+ int channel;
+
+ if (SOUND_SUPPORTED->value) {
+ /* SET THE CHANNEL TO STOP, IF SUPPLIED */
+ if (word[1] == NULL) {
+ channel = 0;
+ } else {
+ channel = value_of(word[1], TRUE);
+
+ /* SANITY CHECK THE CHANNEL SELECTED */
+ if (channel < 0 || channel > 7) {
+ channel = 0;
+ }
+ }
+ g_vm->glk_schannel_stop(sound_channel[channel]);
+ }
+ } else if (!strcmp(word[0], "volume")) {
+ int channel, volume;
+
+ if (SOUND_SUPPORTED->value) {
+ /* SET THE CHANNEL TO STOP, IF SUPPLIED */
+ if (word[2] == NULL) {
+ channel = 0;
+ } else {
+ channel = value_of(word[2], TRUE);
+
+ /* SANITY CHECK THE CHANNEL SELECTED */
+ if (channel < 0 || channel > 7) {
+ channel = 0;
+ }
+ }
+
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ volume = value_of(word[1], TRUE);
+
+ /* SANITY CHECK THE CHANNEL SELECTED */
+ if (volume < 0) {
+ volume = 0;
+ }
+
+ if (volume > 100) {
+ volume = 100;
+ }
+
+ /* STORE A COPY OF THE CURRENT VOLUME FOR ACCESS
+ * FROM JACL CODE */
+ sprintf(temp_buffer, "volume[%d]", channel);
+ cinteger_resolve(temp_buffer)->value = volume;
+
+ /* NOW SCALE THE 0-100 VOLUME TO THE 0-65536 EXPECTED
+ * BY Glk */
+ volume = volume * 655;
+
+ /* SET THE VOLUME */
+ g_vm->glk_schannel_set_volume(sound_channel[channel], (glui32) volume);
+ }
+ }
+ } else if (!strcmp(word[0], "timer")) {
+ if (TIMER_SUPPORTED->value && TIMER_ENABLED->value) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ index = value_of(word[1], TRUE);
+ /* DON'T ALLOW NEGATIVE VALUES, BUT NO UPPER LIMIT */
+ if (index < 0) index = 0;
+
+ /* SET THE GLK TIMER */
+ g_vm->glk_request_timer_events((glui32) index);
+
+ /* EXPOSE THE CURRENT VALUE THROUGH A JACL CONSTANT
+ SO THAT GAME CODE CAN READ THE IT */
+ cinteger_resolve("timer")->value = index;
+ }
+ }
+ } else if (!strcmp(word[0], "sound")) {
+ int channel;
+ glui32 repeats;
+
+ if (SOUND_SUPPORTED->value && SOUND_ENABLED->value) {
+ /* SET THE CHANNEL TO USE, IF SUPPLIED */
+ if (word[2] == NULL) {
+ channel = 0;
+ } else {
+ channel = value_of(word[2], TRUE);
+
+ /* SANITY CHECK THE CHANNEL SELECTED */
+ if (channel < 0 || channel > 7) {
+ channel = 0;
+ }
+ }
+
+ /* SET THE NUMBER OF REPEATS, IF SUPPLIED */
+ if (word[3] == NULL) {
+ repeats = 1;
+ } else {
+ repeats = value_of(word[3], TRUE);
+ }
+
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (g_vm->glk_schannel_play_ext(sound_channel[channel], (glui32) value_of(word[1], TRUE), repeats, channel + 1) == 0) {
+ /* THE CHANNEL NUMBER IS PASSED SO THAT THE SOUND
+ * NOTIFICATION EVENT CAN USE THE INFORMATION
+ * IT HAS 1 ADDED TO IT SO THAT IT IS A NON-ZERO
+ * NUMBER AND THE EVENT IS ACTIVATED */
+ sprintf(error_buffer, "Unable to play sound: %ld", value_of(word[1], FALSE));
+ log_error(error_buffer, PLUS_STDERR);
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "image")) {
+ if (GRAPHICS_SUPPORTED->value && GRAPHICS_ENABLED->value) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (g_vm->glk_image_draw(mainwin, (glui32) value_of(word[1], TRUE), imagealign_InlineDown, 0) == 0) {
+ sprintf(error_buffer, "Unable to draw image: %ld", value_of(word[1], FALSE));
+ log_error(error_buffer, PLUS_STDERR);
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ /* GET A POINTER TO THE STRING BEING MODIFIED */
+ if ((resolved_string = string_resolve(word[1])) == NULL) {
+ unkstrrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ // PROMPT THE USER TO INPUT A STRING AND STORE IT IN THE
+ // RESOLVED VARIABLE
+ get_string(resolved_string->value);
+ }
+
+ } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) {
+ int low, high;
+
+ int insist = FALSE;
+
+ /* THE ONLY DIFFERENCE WITH THE getnumber COMMAND IS THAT
+ * IT INSISTS THE PLAYER GIVES A LEGAL RESPONSE */
+ if (!strcmp(word[0], "getnumber")) {
+ insist = TRUE;
+ }
+
+ if (word[3] != NULL) {
+ ask_integer = container_resolve(word[1]);
+ if (ask_integer == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ low = value_of(word[2], TRUE);
+ high = value_of(word[3], TRUE);
+
+ if (high == -1 || low == -1) {
+ return (exit_function(TRUE));
+ }
+
+ *ask_integer = get_number(insist, low, high);
+ } else {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ }
+ } else if (!strcmp(word[0], "getyesorno")) {
+ if (word[1] != NULL) {
+ ask_integer = container_resolve(word[1]);
+ if (ask_integer == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ *ask_integer = get_yes_or_no();
+ } else {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ }
+ } else if (!strcmp(word[0], "clear")) {
+ if (!walkthru_running) {
+ g_vm->glk_window_clear(current_window);
+ }
+ } else if (!strcmp(word[0], "terminate")) {
+ terminate(0);
+ } else if (!strcmp(word[0], "more")) {
+ if (word[1] == NULL) {
+ more("[MORE]");
+ } else {
+ more(word[1]);
+ }
+ } else if (!strcmp(word[0], "style")) {
+ /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK
+ * STREAM STYLES */
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (!strcmp(word[1], "bold")
+ || !strcmp(word[1], "emphasised")) {
+ g_vm->glk_set_style(style_Emphasized);
+ } else if (!strcmp(word[1], "note")) {
+ g_vm->glk_set_style(style_Note);
+ } else if (!strcmp(word[1], "input")) {
+ g_vm->glk_set_style(style_Input);
+ } else if (!strcmp(word[1], "header")) {
+ g_vm->glk_set_style(style_Header);
+ } else if (!strcmp(word[1], "subheader")) {
+ g_vm->glk_set_style(style_Subheader);
+ } else if (!strcmp(word[1], "reverse")
+ || !strcmp(word[1], "inverse")) {
+ if (current_window == mainwin) {
+ g_vm->glk_set_style(style_User2);
+ } else {
+ g_vm->glk_set_style(style_User1);
+ }
+ } else if (!strcmp(word[1], "pre")
+ || !strcmp(word[1], "preformatted")) {
+ g_vm->glk_set_style(style_Preformatted);
+ } else if (!strcmp(word[1], "normal")) {
+ g_vm->glk_set_style(style_Normal);
+ }
+ }
+ } else if (!strcmp(word[0], "flush")) {
+ } else if (!strcmp(word[0], "hyperlink")) {
+ /* OUTPUT LINK TEXT AS PLAIN TEXT UNDER Glk */
+ if (word[2] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ } else {
+ write_text(text_of_word(1));
+ }
+#else
+#ifdef __NDS__
+ } else if (!strcmp(word[0], "flush")) {
+ jflush();
+ } else if (!strcmp(word[0], "cursor")) {
+ if (word[2] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ printf("\x1b[%d;%dH", (int) value_of(word[1], TRUE), (int) value_of(word[2], TRUE));
+ }
+ } else if (!strcmp(word[0], "stop")) {
+ } else if (!strcmp(word[0], "volume")) {
+ } else if (!strcmp(word[0], "timer")) {
+ } else if (!strcmp(word[0], "sound")) {
+ } else if (!strcmp(word[0], "image")) {
+ } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ /* GET A POINTER TO THE STRING BEING MODIFIED */
+ if ((resolved_string = string_resolve(word[1])) == NULL) {
+ unkstrrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ // PROMPT THE USER TO INPUT A STRING AND STORE IT IN THE
+ // RESOLVED VARIABLE
+ get_string(resolved_string->value);
+ }
+
+ } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) {
+ int low, high;
+
+ int insist = FALSE;
+
+ /* THE ONLY DIFFERENCE WITH THE getnumber COMMAND IS THAT
+ * IT INSISTS THE PLAYER GIVES A LEGAL RESPONSE */
+ if (!strcmp(word[0], "getnumber")) {
+ insist = TRUE;
+ }
+
+ if (word[3] != NULL) {
+ ask_integer = container_resolve(word[1]);
+ if (ask_integer == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ low = value_of(word[2], TRUE);
+ high = value_of(word[3], TRUE);
+
+ if (high == -1 || low == -1) {
+ return (exit_function(TRUE));
+ }
+
+ *ask_integer = get_number(insist, low, high);
+ } else {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ }
+ } else if (!strcmp(word[0], "getyesorno")) {
+ if (word[1] != NULL) {
+ ask_integer = container_resolve(word[1]);
+ if (ask_integer == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ *ask_integer = get_yes_or_no();
+ } else {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ }
+ } else if (!strcmp(word[0], "clear")) {
+ clrscrn();
+ } else if (!strcmp(word[0], "terminate")) {
+ terminate(0);
+ } else if (!strcmp(word[0], "more")) {
+ if (word[1] == NULL) {
+ more("[MORE]");
+ } else {
+ more(word[1]);
+ }
+ } else if (!strcmp(word[0], "style")) {
+ /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK
+ * STREAM STYLES */
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (!strcmp(word[1], "bold")
+ || !strcmp(word[1], "emphasised")) {
+ printf("\x1b[37;1m"); // SET TO BRIGHT WHITE
+ bold_mode = TRUE;
+ } else if (!strcmp(word[1], "note")) {
+ printf("\x1b[34;1m"); // SET TO BRIGHT BLUE
+ note_mode = TRUE;
+ } else if (!strcmp(word[1], "input")) {
+ printf("\x1b[32;0m"); // SET TO DIM GREEN
+ input_mode = TRUE;
+ } else if (!strcmp(word[1], "header")) {
+ printf("\x1b[37;0m"); // SET TO DIM WHITE
+ } else if (!strcmp(word[1], "subheader")) {
+ printf("\x1b[33;1m"); // SET TO BRIGHT YELLOW
+ subheader_mode = TRUE;
+ } else if (!strcmp(word[1], "reverse")
+ || !strcmp(word[1], "inverse")) {
+ printf("\x1b[7m"); // SET TO DIM WHITE
+ reverse_mode = TRUE;
+ } else if (!strcmp(word[1], "pre")
+ || !strcmp(word[1], "preformatted")) {
+ printf("\x1b[37;0m"); // SET TO DIM WHITE
+ pre_mode = TRUE;
+ } else if (!strcmp(word[1], "normal")) {
+ printf("\x1b[37;0m"); // SET TO DIM WHITE
+ bold_mode = FALSE;
+ pre_mode = FALSE;
+ reverse_mode = FALSE;
+ input_mode = FALSE;
+ subheader_mode = FALSE;
+ note_mode = FALSE;
+ }
+ }
+ } else if (!strcmp(word[0], "hyperlink")) {
+ /* OUTPUT LINK TEXT AS PLAIN TEXT UNDER Glk */
+ if (word[2] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ } else {
+ write_text(text_of_word(1));
+ }
+#else
+ /* HERE STARTS THE CGIJACL-ONLY FUNCTIONS */
+ } else if (!strcmp(word[0], "option")) {
+ /* USED TO ADD AN OPTION TO AN HTML LIST */
+ if (word[1] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ } else {
+ index = value_of(word[1]);
+ if (word[2] != NULL) {
+ sprintf(option_buffer, "<option value=\"%d\">",
+ index);
+ } else {
+ object_names(index, temp_buffer);
+ sprintf(option_buffer, "<option value=\"%s\">", temp_buffer);
+ }
+
+ write_text(option_buffer);
+ list_output(index, TRUE);
+ write_text(temp_buffer);
+
+ }
+ } else if (!strcmp(word[0], "getenv")) {
+ struct string_type *resolved_setstring = NULL;
+
+ if (word[2] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ } else {
+ // GET A POINTER TO THE STRING BEING MODIFIED
+ if ((resolved_setstring = string_resolve(word[1])) == NULL) {
+ unkstrrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ // COPY THE VARIABLE OF THE CGI VARIABLE INTO THE SPECIFIED STRING VARIABLE
+ if (getenv(text_of_word(2)) != NULL) {
+ strncpy(resolved_setstring->value, getenv(text_of_word(2)), 255);
+ } else {
+ strncpy(resolved_setstring->value, "", 255);
+ }
+ }
+ } else if (!strcmp(word[0], "button")) {
+ /* USED TO CREATE AN HTML BUTTON */
+ if (word[1] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ }
+ if (word[2] != NULL) {
+ sprintf(option_buffer, "<input class=~button~ type=~image~ src=~%s~ name=~verb~ value=~", text_of_word(2));
+ strcat(option_buffer, text_of_word(1));
+ strcat(option_buffer, "~>");
+ write_text(option_buffer);
+ } else {
+ sprintf(option_buffer, "<input class=~button~ type=~submit~ style=~width: 90px; margin: 5px;~ name=~verb~ value=~%s~>", text_of_word(1));
+ write_text(option_buffer);
+ }
+ } else if (!strcmp(word[0], "hidden")) {
+ sprintf(temp_buffer, "<INPUT TYPE=\"hidden\" NAME=\"user_id\" VALUE=\"%s\">", user_id);
+ write_text(temp_buffer);
+ } else if (!strcmp(word[0], "control")) {
+ /* USED TO CREATE A HYPERLINK THAT IS AN IMAGE */
+ if (word[2] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ } else {
+ sprintf(option_buffer, "<a href=\"?command=%s&amp;user_id=%s\"><img border=0 SRC=\"", text_of_word(2), user_id);
+ strcat(option_buffer, text_of_word(1));
+ strcat(option_buffer, "\"></a>");
+ write_text(option_buffer);
+ }
+ } else if (!strcmp(word[0], "hyperlink") || !strcmp(word[0], "hyperlinkNE")) {
+ string_buffer[0] = 0;
+
+ /* USED TO CREATE A HYPERLINK WITH SESSION INFORMATION INCLUDED */
+ if (word[2] == NULL) {
+ noproprun();
+ pop_stack();
+ return (TRUE);
+ } else {
+ char *encoded;
+
+ if (!strcmp(word[0], "hyperlink")) {
+ encoded = url_encode(text_of_word(2));
+ } else {
+ encoded = text_of_word(2);
+ }
+
+ if (word[3] == NULL) {
+ sprintf(string_buffer, "<a href=\"?command=%s&amp;user_id=%s\">", encoded, user_id);
+ strcat(string_buffer, text_of_word(1));
+ strcat(string_buffer, "</a>");
+ } else {
+ sprintf(string_buffer, "<a class=\"%s\" href=\"?command=", text_of_word(3));
+ strcat(string_buffer, encoded);
+ sprintf(option_buffer, "&amp;user_id=%s\">%s</a>", user_id, text_of_word(1));
+ strcat(string_buffer, option_buffer);
+ }
+
+ if (!strcmp(word[0], "hyperlink")) {
+ free(encoded);
+ }
+
+ write_text(string_buffer);
+ }
+ } else if (!strcmp(word[0], "prompt")) {
+ /* USED TO OUTPUT A HTML INPUT CONTROL THAT CONTAINS SESSION INFORMATION */
+ if (word[1] != NULL) {
+ sprintf(temp_buffer, "<input id=\"JACLCommandPrompt\" type=text name=~command~ onKeyPress=~%s~>\n", word[1]);
+ write_text(temp_buffer);
+ } else {
+ sprintf(temp_buffer, "<input id=\"JACLCommandPrompt\" type=text name=~command~>\n");
+ write_text(temp_buffer);
+ }
+ sprintf(temp_buffer, "<input type=hidden name=\"user_id\" value=\"%s\">", user_id);
+ write_text(temp_buffer);
+ } else if (!strcmp(word[0], "style")) {
+ /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK
+ * STREAM STYLES */
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (!strcmp(word[1], "bold")
+ || !strcmp(word[1], "emphasised")) {
+ write_text("<b>");
+ style_stack[style_index++] = BOLD;
+ } else if (!strcmp(word[1], "note")) {
+ write_text("<i>");
+ style_stack[style_index++] = NOTE;
+ } else if (!strcmp(word[1], "input")) {
+ write_text("<i>");
+ style_stack[style_index++] = INPUT;
+ } else if (!strcmp(word[1], "header")) {
+ write_text("<h1>");
+ style_stack[style_index++] = HEADER;
+ } else if (!strcmp(word[1], "subheader")) {
+ write_text("<h2>");
+ style_stack[style_index++] = SUBHEADER;
+ } else if (!strcmp(word[1], "reverse")
+ || !strcmp(word[1], "inverse")) {
+ write_text("<b>");
+ style_stack[style_index++] = REVERSE;
+ } else if (!strcmp(word[1], "pre")
+ || !strcmp(word[1], "preformatted")) {
+ write_text("<pre>");
+ style_stack[style_index++] = PRE;
+ } else if (!strcmp(word[1], "normal")) {
+ style_index--;
+ for (; style_index > -1; style_index--) {
+ switch (style_stack[style_index]) {
+ case BOLD:
+ write_text("</b>");
+ break;
+ case NOTE:
+ write_text("</i>");
+ break;
+ case INPUT:
+ write_text("</i>");
+ break;
+ case HEADER:
+ write_text("</h1>");
+ break;
+ case SUBHEADER:
+ write_text("</h2>");
+ break;
+ case REVERSE:
+ write_text("</b>");
+ break;
+ case PRE:
+ write_text("</pre>");
+ break;
+ }
+ }
+ style_index = 0;
+ }
+ }
+ /* THESE FINAL COMMANDS HAVE NO EFFECT UNDER CGIJACL
+ AND THERE IS NO HARM IN IGNORING THEM */
+ } else if (!strcmp(word[0], "flush")) {
+ } else if (!strcmp(word[0], "image")) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ if (word[2] == NULL) {
+ sprintf(option_buffer, "<img src=~%s~>", text_of_word(1));
+ } else {
+ sprintf(option_buffer, "<img class=~%s~ src=~%s~>", text_of_word(2), text_of_word(1));
+ }
+
+ write_text(option_buffer);
+ }
+ } else if (!strcmp(word[0], "sound")) {
+ if (word[2] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ write_text("<audio autoplay=~autoplay~>");
+ if (word[3] == NULL) {
+ sprintf(option_buffer, "<source src=~%s~ type=~%s~>", text_of_word(1), text_of_word(2));
+ write_text(option_buffer);
+ }
+ write_text("</audio>");
+ }
+ } else if (!strcmp(word[0], "cursor")) {
+ } else if (!strcmp(word[0], "timer")) {
+ } else if (!strcmp(word[0], "volume")) {
+ } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) {
+ } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) {
+ } else if (!strcmp(word[0], "getyesorno")) {
+ } else if (!strcmp(word[0], "clear")) {
+ } else if (!strcmp(word[0], "more")) {
+ } else if (!strcmp(word[0], "terminate")) {
+#endif
+#endif
+ } else if (!strcmp(word[0], "proxy")) {
+ /* THE PROXY COMMAND ISSUES A MOVE ON THE PLAYER'S BEHALF
+ * ALL STATE MUST BE SAVED SO THE CURRENT MOVE CAN CONTINUE
+ * ONCE THE PROXIED MOVE IS COMPLETE */
+#ifdef GLK
+ push_stack(g_vm->glk_stream_get_position(game_stream));
+#else
+ push_stack(ftell(file));
+#endif
+ push_proxy();
+
+ build_proxy();
+
+ // TEXT BUFFER IS THE NORMAL ARRAY FOR HOLDING THE PLAYERS
+ // MOVE FOR PROCESSING
+ strncpy(text_buffer, proxy_buffer, 1024);
+
+ command_encapsulate();
+
+ jacl_truncate();
+
+ preparse();
+
+ pop_proxy();
+
+ pop_stack();
+ } else if (!strcmp(word[0], "override")) {
+ /* TELLS THE INTERPRETER TO LOOK FOR AN _override FUNCTION
+ * TO EXECUTE IN PLACE OF ANY CODE THAT FOLLOWS THIS LINE.
+ * THIS COMMAND IS USED EXCLUSIVELY IN GLOBAL FUNCTIONS
+ * ASSOCIATED WITH GRAMMAR LINES */
+ if (execute(override) == TRUE) {
+ return (exit_function(TRUE));
+ } else {
+ if (execute(default_function) == TRUE) {
+ return (exit_function(TRUE));
+ }
+ }
+ } else if (!strcmp(word[0], "execute") || !strcmp(word[0], "call")) {
+ /* CALLS ANOTHER JACL FUNCTION */
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/
+ string_buffer[0] = 0;
+
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ strcat(string_buffer, arg_text_of_word(counter));
+ }
+
+ if (function_resolve(string_buffer) == NULL && !strcmp(word[0], "execute")) {
+ char *argstart;
+
+ /* REMOVE ANY PARAMETERS FROM FUNCTION NAME
+ BEFORE DISPLAYING ERROR MESSAGE */
+ argstart = strchr(string_buffer, '<');
+ if (argstart != NULL)
+ *argstart = 0;
+
+ sprintf(error_buffer, UNDEFINED_FUNCTION, executing_function->name, string_buffer);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+ execute(string_buffer);
+ }
+ }
+ } else if (!strcmp(word[0], "points")) {
+ /* INCREASE THE PLAYER'S SCORE AND POTENTIALLY INFORM THEM OF THE INCREASE */
+ if (word[1] != NULL) {
+ SCORE->value += value_of(word[1], TRUE);
+ if (NOTIFY->value) {
+#ifdef GLK
+ g_vm->glk_set_style(style_Note);
+#else
+#ifdef __NDS__
+ printf("\x1b[34;1m"); // SET TO BRIGHT BLUE
+ note_mode = TRUE;
+#else
+ write_text("<b><i>");
+#endif
+#endif
+ write_text(cstring_resolve("SCORE_UP")->value);
+ sprintf(temp_buffer, "%ld", value_of(word[1], TRUE));
+ write_text(temp_buffer);
+ if (value_of(word[1], TRUE) == 1) {
+ write_text(cstring_resolve("POINT")->value);
+ } else {
+ write_text(cstring_resolve("POINTS")->value);
+ }
+#ifdef GLK
+ g_vm->glk_set_style(style_Normal);
+#else
+#ifdef __NDS__
+ printf("\x1b[37;0m"); // SET TO DIM WHITE
+ note_mode = FALSE;
+#else
+ write_text("</i></b>");
+#endif
+#endif
+ }
+ } else {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ }
+ } else if (!strcmp(word[0], "print")) {
+ int non_space = FALSE;
+
+ // DISPLAYS A BLOCK OF PLAIN TEXT UNTIL IT FINDS A
+ // LINE THAT STARTS WITH A '.' OR A '}'
+#ifdef GLK
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+
+ if (encrypted) jacl_decrypt(text_buffer);
+
+ while (text_buffer[0] != '.' && text_buffer[0] != '}') {
+ index = 0;
+ non_space = FALSE;
+
+ /* REMOVE ANY NEWLINE CHARACTERS */
+ while (text_buffer[index] != 0) {
+ if (text_buffer[index] == '|' && non_space == FALSE) {
+ /* THE BAR CHARACTER IS CHANGED TO A SPACE TO
+ * ALLOW INDENTING OF NEW PARAGRAPHS ETC */
+ text_buffer[index] = ' ';
+ } else if (text_buffer[index] == '\r') {
+ text_buffer[index] = 0;
+ break;
+ } else if (text_buffer[index] == '\n') {
+ text_buffer[index] = 0;
+ break;
+ } else if (text_buffer[index] != ' ' && text_buffer[index] != '\t') {
+ non_space = TRUE;
+ }
+
+ index++;
+ }
+
+ if (text_buffer[0] != 0) {
+ // CHECK IF THERE IS THE NEED TO ADD AN
+ // IMPLICIT SPACE
+ index = strlen(text_buffer);
+
+ if (text_buffer[index - 1] == '\\') {
+ // A BACKSLASH IS USED TO INDICATE AN IMPLICIT
+ // SPACE SHOULD NOT BE PRINTED
+ text_buffer[index - 1] = 0;
+ } else if (text_buffer[index - 1] != '^') {
+ // ADD AN IMPLICIT SPACE IF THE PREVIOUS LINE
+ // DIDN'T END WITH A CARRIAGE RETURN
+ strcat(text_buffer, " ");
+ }
+
+ // OUTPUT THE LINE READ AS PLAIN TEXT
+ write_text(text_buffer);
+ }
+
+ // GET THE NEXT LINE
+#ifdef GLK
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+
+ if (encrypted) jacl_decrypt(text_buffer);
+ }
+ } else if (!strcmp(word[0], "mesg")) {
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ warning("%s", text_of_word(counter));
+ }
+ } else if (!strcmp(word[0], "error")) {
+ write_text("ERROR: In function ~");
+ write_text(executing_function->name);
+ write_text("~, ");
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ write_text(text_of_word(counter));
+ }
+ } else if (!strcmp(word[0], "debug") && DEBUG->value) {
+ write_text("DEBUG: ");
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ write_text(text_of_word(counter));
+ }
+ } else if (!strcmp(word[0], "write")) {
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ output = text_of_word(counter);
+ if (*output != 0) {
+ // IF THE OUTPUT ISN'T AN EMPTY STRING, DISPLAY IT
+ write_text(output);
+ }
+ }
+ } else if (!strcmp(word[0], "length")) {
+ if (word[2] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ if ((container = container_resolve(word[1])) == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ *container = strlen(text_of(word[2]));
+ }
+ } else if (!strcmp(word[0], "savegame")) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if ((container = container_resolve(word[1])) == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ } else {
+ if (word[2] == NULL) {
+ *container = save_interaction(NULL);
+ } else {
+ *container = save_interaction(arg_text_of_word(2));
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "restoregame")) {
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if ((container = container_resolve(word[1])) == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ } else {
+ if (word[2] == NULL) {
+ *container = restore_interaction(NULL);
+ } else {
+ *container = restore_interaction(arg_text_of_word(2));
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "restartgame")) {
+ restart_game();
+ execute("+intro");
+ eachturn();
+#ifdef GLK
+ } else if (!strcmp(word[0], "undomove")) {
+ undoing();
+ } else if (!strcmp(word[0], "updatestatus")) {
+ status_line();
+#else
+ } else if (!strcmp(word[0], "undomove")) {
+ } else if (!strcmp(word[0], "updatestatus")) {
+#endif
+ } else if (!strcmp(word[0], "split")) {
+
+ // 0 1 2 3 4
+ // split counter source delimiter destination
+
+ int *split_container;
+ char split_buffer[256] = "";
+ char container_buffer[256] = "";
+ char delimiter[256] = "";
+ char *match = NULL;
+ struct string_type *resolved_splitstring = NULL;
+
+ strcpy(split_buffer, text_of_word(2));
+ strcpy(delimiter, text_of_word(3));
+
+ char *source = split_buffer;
+
+ if (word[4] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ split_container = container_resolve(var_text_of_word(1));
+
+ if (split_container == NULL) {
+ unkvarrun(var_text_of_word(1));
+ return (exit_function(TRUE));
+ } else {
+ *split_container = 0;
+ match = source; // THERE IS ALWAYS ONE MATCH, EVEN IF
+ // NO DELIMETERS ARE FOUND
+
+ while ((match = strstr(source, delimiter))) {
+ *match = 0;
+ strcpy(container_buffer, var_text_of_word(4));
+ strcat(container_buffer, "[");
+ sprintf(integer_buffer, "%d", *split_container);
+ strcat(container_buffer, integer_buffer);
+ strcat(container_buffer, "]");
+
+ if ((resolved_splitstring = string_resolve(container_buffer)) == NULL) {
+ unkstrrun(var_text_of_word(4));
+ return (exit_function(TRUE));
+ } else {
+ strcpy(resolved_splitstring->value, source);
+ source = match + strlen(delimiter);
+ (*split_container)++;
+ }
+ }
+ strcpy(container_buffer, var_text_of_word(4));
+ strcat(container_buffer, "[");
+ sprintf(integer_buffer, "%d", *split_container);
+ strcat(container_buffer, integer_buffer);
+ strcat(container_buffer, "]");
+
+ if ((resolved_splitstring = string_resolve(container_buffer)) == NULL) {
+ unkstrrun(word[1]);
+ return (exit_function(TRUE));
+ } else {
+ strcpy(resolved_splitstring->value, source);
+ (*split_container)++;
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "setstring") ||
+ !strcmp(word[0], "addstring")) {
+ char setstring_buffer[2048] = "";
+ struct string_type *resolved_setstring = NULL;
+
+ if (word[2] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ /* GET A POINTER TO THE STRING BEING MODIFIED */
+ if ((resolved_setstring = string_resolve(var_text_of_word(1))) == NULL) {
+ unkstrrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/
+ for (counter = 2; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ strcat(setstring_buffer, text_of_word(counter));
+ }
+
+ /* setstring_buffer IS NOW FILLED, COPY THE UP TO 256 BYTES OF
+ * IT INTO THE STRING */
+ if (!strcmp(word[0], "setstring")) {
+ strncpy(resolved_setstring->value, setstring_buffer, 255);
+ } else {
+ /* CALCULATE HOW MUCH SPACE IS LEFT IN THE STRING */
+ counter = 255 - strlen(resolved_setstring->value);
+ /* THIS IS A addstring COMMAND, SO USE STRNCAT INSTEAD */
+ strncat(resolved_setstring->value, setstring_buffer, counter);
+ }
+ }
+ } else if (!strcmp(word[0], "padstring")) {
+ char setstring_buffer[2048] = "";
+ struct string_type *resolved_setstring = NULL;
+ string_buffer[0] = 0;
+
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ /* GET A POINTER TO THE STRING BEING MODIFIED */
+ if ((resolved_setstring = string_resolve(word[1])) == NULL) {
+ unkstrrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ index = value_of(word[3], TRUE);
+
+ for (counter = 0; counter < index; counter++) {
+ strcat(setstring_buffer, text_of_word(2));
+ }
+
+ /* setstring_buffer IS NOW FILLED, COPY THE UP TO 256 BYTES OF
+ * IT INTO THE STRING */
+ strncpy(resolved_setstring->value, setstring_buffer, 255);
+ }
+ } else if (!strcmp(word[0], "return")) {
+ /* RETURN FROM THIS FUNCTION, POSSIBLY RETURNING AN INTEGER VALUE */
+ if (word[1] == NULL) {
+ return (exit_function(TRUE));
+ } else {
+ index = value_of(word[1], TRUE);
+ return (exit_function(index));
+ }
+ } else if (!strcmp(word[0], "position")) {
+ /* MOVE AN OBJECT TO ITS NEW X,Y COORDINATES BASED ON ITS CURRENT VALUES
+ * FOR x, y, bearing, velocity */
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ object_1 = value_of(word[1], TRUE);
+
+ if (object_1 < 1 || object_1 > objects) {
+ badptrrun(word[1], object_1);
+ return (exit_function(TRUE));
+ } else {
+ new_position((double) object[object_1]->X,
+ (double) object[object_1]->Y,
+ (double) object[object_1]->BEARING,
+ (double) object[object_1]->VELOCITY);
+
+ object[object_1]->X = new_x;
+ object[object_1]->Y = new_y;
+ }
+ }
+ } else if (!strcmp(word[0], "bearing")) {
+ /* CALCULATE THE BEARING BETWEEN TWO OBJECTS */
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if ((container = container_resolve(word[1])) == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ }
+
+ object_1 = value_of(word[2], TRUE);
+
+ if (object_1 < 1 || object_1 > objects) {
+ badptrrun(word[2], object_1);
+ return (exit_function(TRUE));
+ } else {
+ object_2 = value_of(word[3], TRUE);
+
+ if (object_2 < 1 || object_2 > objects) {
+ badptrrun(word[3], object_2);
+ return (exit_function(TRUE));
+ } else {
+ if (container != NULL
+ && object_1 != FALSE
+ && object_2 != FALSE) {
+ *container = bearing((double) object[object_1]->X,
+ (double) object[object_1]->Y,
+ (double) object[object_2]->X,
+ (double) object[object_2]->Y);
+ }
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "distance")) {
+ /* CALCULATE THE DISTANCE BETWEEN TWO OBJECTS */
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ container = container_resolve(word[1]);
+
+ object_1 = value_of(word[2], TRUE);
+
+ if (object_1 < 1 || object_1 > objects) {
+ badptrrun(word[2], object_1);
+ return (exit_function(TRUE));
+ } else {
+ object_2 = value_of(word[3], TRUE);
+
+ if (object_2 < 1 || object_2 > objects) {
+ badptrrun(word[3], object_2);
+ return (exit_function(TRUE));
+ } else {
+ if (container != NULL
+ && object_1 != FALSE
+ && object_2 != FALSE) {
+ *container = distance((double)
+ object[object_1]->X,
+ (double)
+ object[object_1]->Y,
+ (double)
+ object[object_2]->X,
+ (double)
+ object[object_2]->Y);
+ }
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "dir_to") ||
+ !strcmp(word[0], "npc_to")) {
+ /* CALCULATE THE FIRST DIRECTION TO TRAVEL IN GET TO
+ * A SPECIFIED LOCATION */
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ container = container_resolve(word[1]);
+
+ object_1 = value_of(word[2], TRUE);
+
+ if (object_1 < 1 || object_1 > objects) {
+ badptrrun(word[2], object_1);
+ return (exit_function(TRUE));
+ } else {
+ object_2 = value_of(word[3], TRUE);
+
+ if (object_2 < 1 || object_2 > objects) {
+ badptrrun(word[3], object_2);
+ return (exit_function(TRUE));
+ } else {
+ if (container != NULL
+ && object_1 != FALSE
+ && object_2 != FALSE) {
+ if (!strcmp(word[0], "dir_to")) {
+ *container = find_route(object_1, object_2, TRUE);
+ } else {
+ *container = find_route(object_1, object_2, FALSE);
+ }
+ }
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "set")) {
+ /* SET THE VALUE OF AN ELEMENT TO A SUPPLIED INTEGER */
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ container = container_resolve(var_text_of_word(1));
+
+ if (container == NULL) {
+ unkvarrun(word[1]);
+ return (exit_function(TRUE));
+ } else {
+ int mark = 2; // SET mark TO POINT TO THE FIRST OPERATOR
+ while (word[mark + 1] != NULL) {
+ counter = value_of(word[mark + 1], TRUE);
+
+ if (word[mark][0] == '+')
+ *container += counter;
+ else if (word[mark][0] == '-')
+ *container -= counter;
+ else if (word[mark][0] == '*')
+ *container = *container * counter;
+ else if (word[mark][0] == '%')
+ *container = *container % counter;
+ else if (word[mark][0] == '/') {
+ if (counter == 0) {
+ sprintf(error_buffer, DIVIDE_BY_ZERO,
+ executing_function->name);
+ log_error(error_buffer, PLUS_STDOUT);
+ } else
+ *container = *container / counter;
+ } else if (!strcmp(word[mark], "locationof")) {
+ *container = grand_of(counter, FALSE);
+ } else if (!strcmp(word[mark], "grandof")) {
+ *container = grand_of(counter, TRUE);
+ } else if (word[mark][0] == '=') {
+ *container = counter;
+ } else {
+ sprintf(error_buffer, ILLEGAL_OPERATOR,
+ executing_function->name,
+ word[2]);
+ log_error(error_buffer, PLUS_STDOUT);
+ }
+
+ mark += 2;
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "ensure")) {
+ /* USED TO GIVE OR TAKE AN ATTRIBUTE TO OR FROM AND OBJECT */
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if ((bit_mask = attribute_resolve(arg_text_of(word[3])))) {
+ index = value_of(word[1], TRUE);
+ if (index < 1 || index > objects) {
+ badptrrun(word[1], index);
+ return (exit_function(TRUE));
+ } else {
+ if (!strcmp(word[2], "has")) {
+ object[index]->attributes =
+ object[index]->attributes | bit_mask;
+ } else if (!strcmp(word[2], "hasnt")) {
+ bit_mask = ~bit_mask;
+ object[index]->attributes =
+ object[index]->attributes & bit_mask;
+ }
+ }
+ } else if ((bit_mask = user_attribute_resolve(arg_text_of(word[3])))) {
+ index = value_of(word[1], TRUE);
+ if (index < 1 || index > objects) {
+ badptrrun(word[1], index);
+ return (exit_function(TRUE));
+ } else {
+ if (!strcmp(word[2], "has")) {
+ object[index]->user_attributes =
+ object[index]->user_attributes | bit_mask;
+ } else if (!strcmp(word[2], "hasnt")) {
+ bit_mask = ~bit_mask;
+ object[index]->user_attributes =
+ object[index]->user_attributes & bit_mask;
+ }
+ }
+ } else {
+ unkattrun(3);
+ return (exit_function(TRUE));
+ }
+ }
+ } else if (!strcmp(word[0], "append")) {
+ int first = TRUE;
+
+ if (word[2] == NULL) {
+ // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ strcpy(temp_buffer, data_directory);
+ strcat(temp_buffer, prefix);
+ strcat(temp_buffer, "-");
+ strcat(temp_buffer, text_of_word(1));
+ strcat(temp_buffer, ".csv");
+
+ outfile = File::openForWriting(temp_buffer);
+
+ if (outfile == NULL) {
+ sprintf(error_buffer, "Failed to open file %s: %s\n", temp_buffer, strerror(errno));
+ log_error(error_buffer, PLUS_STDOUT);
+ } else {
+ for (counter = 2; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ output = text_of_word(counter);
+ if (*output != 0) {
+ if (first == FALSE) {
+ outfile->writeByte(',');
+ }
+ csv_fwrite(outfile, output, (size_t) strlen(output));
+ first = FALSE;
+ }
+ }
+
+ // TERMINATE THE LINE
+ outfile->writeByte('\n');
+
+ // FLUSH AND CLOSE THE FILE
+ outfile->flush();
+ }
+
+ delete outfile;
+ outfile = NULL;
+ }
+ } else if (!strcmp(word[0], "insert")) {
+ int first = TRUE;
+
+ if (word[1] == NULL) {
+ // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ if (outfile == NULL) {
+ log_error("Insert statement not inside an 'update' loop.", PLUS_STDOUT);
+ } else {
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ output = text_of_word(counter);
+ if (*output != 0) {
+ if (first == FALSE) {
+ outfile->writeByte(',');
+ }
+ csv_fwrite(outfile, output, (size_t) strlen(output));
+ first = FALSE;
+ }
+ }
+
+ // TERMINATE THE LINE
+ outfile->writeByte('\n');
+ }
+ }
+ } else if (!strcmp(word[0], "inspect")) {
+ if (word[1] == NULL) {
+ // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND
+ noproprun();
+ return (exit_function(TRUE));
+ } else {
+ inspect(value_of(word[1], TRUE));
+ }
+ } else if (!strcmp(word[0], "move")) {
+ /* THIS COMMAND IS USED TO MOVE AN OBJECT TO HAVE ANOTHER PARENT
+ * INCLUDING MODIFYING ALL QUANTITY VALUES BASED ON THE OBJECTS MASS */
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun();
+ return (exit_function(TRUE));
+ }
+
+ index = value_of(word[1], TRUE);
+ if (index < 1 || index > objects) {
+ badptrrun(word[1], index);
+ return (exit_function(TRUE));
+ } else {
+ object_2 = object[index]->PARENT;
+ if (object_2 && !(object[object_2]->attributes & LOCATION)) {
+ object[object_2]->QUANTITY += object[index]->MASS;
+ }
+ object_1 = value_of(word[3], TRUE);
+ if (object_1 < 1 || object_1 > objects) {
+ badptrrun(word[1], object_1);
+ return (exit_function(TRUE));
+ } else {
+ object[index]->PARENT = object_1;
+ if (!(object[object_1]->attributes & LOCATION))
+ object[object_1]->QUANTITY -= object[index]->MASS;
+ }
+ }
+ } else if (!strcmp(word[0], "ifstringall")) {
+ /* CHECK IF A STRING EQUALS OR CONTAINS ANOTHER STRING */
+ currentLevel++;
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else if (and_strcondition()) {
+ executionLevel++;
+ }
+ } else if (!strcmp(word[0], "ifstring")) {
+ /* CHECK IF A STRING EQUALS OR CONTAINS ANOTHER STRING */
+ currentLevel++;
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else if (strcondition()) {
+ executionLevel++;
+ }
+ } else if (!strcmp(word[0], "ifexecute")) {
+ currentLevel++;
+ if (word[1] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else {
+ /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/
+ string_buffer[0] = 0;
+
+ for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
+ strcat(string_buffer, arg_text_of_word(counter));
+ }
+
+ if (execute(string_buffer)) {
+ executionLevel++;
+ }
+ }
+ } else if (!strcmp(word[0], "if")) {
+ currentLevel++;
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else if (condition()) {
+ executionLevel++;
+ }
+ } else if (!strcmp(word[0], "ifall")) {
+ currentLevel++;
+ if (word[3] == NULL) {
+ /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
+ noproprun(0);
+ return (exit_function(TRUE));
+ } else if (and_condition()) {
+ executionLevel++;
+ }
+ } else {
+ sprintf(error_buffer, UNKNOWN_COMMAND,
+ executing_function->name, word[0]);
+ log_error(error_buffer, PLUS_STDOUT);
+ }
+ } else if (!strcmp(word[wp], "if")
+ || !strcmp(word[wp], "ifall")
+ || !strcmp(word[wp], "ifstring")
+ || !strcmp(word[wp], "ifstringall")
+ || !strcmp(word[wp], "ifexecute")
+ || !strcmp(word[wp], "iterate")
+ || !strcmp(word[wp], "update")
+ || !strcmp(word[wp], "while")
+ || !strcmp(word[wp], "whileall")) {
+ currentLevel++;
+ }
+
+#ifdef GLK
+ before_command = g_vm->glk_stream_get_position(game_stream);
+ glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ before_command = ftell(file);
+ fgets(text_buffer, 1024, file);
+#endif
+ if (encrypted) jacl_decrypt(text_buffer);
+ };
+
+ return (exit_function(TRUE));
+}
+
+int exit_function(int return_code) {
+ if (infile != NULL) {
+ read_lck.l_type = F_UNLCK; // SETTING A READ LOCK
+ fcntl(read_fd, F_SETLK, &read_lck);
+ delete infile;
+ infile = NULL;
+ }
+
+ if (outfile != NULL) {
+ write_lck.l_type = F_UNLCK; // SETTING A WRITE LOCK
+ fcntl(write_fd, F_SETLK, &write_lck);
+ delete outfile;
+ outfile = NULL;
+ }
+
+ /* POP THE STACK REGARDLESS OF THE RETURN CODE */
+ pop_stack();
+
+ return (return_code);
+}
+
+char *object_names(int object_index, char *names_buffer) {
+ /* THIS FUNCTION CREATES A LIST OF ALL AN OBJECT'S NAMES.
+ THE escape ARGUMENT INDICATES WHETHER A + SIGN SHOULD BE
+ USED IN PLACE OF A SPACE BETWEEN EACH OF THE NAMES */
+ struct name_type *current_name = object[object_index]->first_name;
+ names_buffer[0] = 0;
+
+ while (current_name != NULL) {
+ strcat(names_buffer, " ");
+ strcat(names_buffer, current_name->name);
+ current_name = current_name->next_name;
+ }
+
+ return names_buffer;
+}
+
+int distance(double x1, double y1, double x2, double y2) {
+ /* THIS FUNCTION CALCULATES THE DISTANCE BETWEEN TWO POINTS IN A
+ TWO-DIMENSIONAL PLANE */
+ double delta_x,
+ delta_y;
+ double distance,
+ total;
+
+ /*
+ * Object two in which quadrant compared to object one? 0 x = opp, y =
+ * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd
+ * + 180 degrees 3 x = adj, y = opp + 270 degrees
+ */
+
+ /*
+ * DETERMINE WHICH QUADRANT OBJECT TWO IS IN
+ */
+
+ if (x2 > x1) {
+ /*
+ * OBJECT TWO IS IN 1 OR 2
+ */
+ delta_x = x2 - x1;
+ if (y2 > y1) {
+ delta_y = y2 - y1;
+ } else {
+ delta_y = y1 - y2;
+ }
+ } else {
+ /*
+ * OBJECT TWO IS IN 3 OR 4
+ */
+ delta_x = x1 - x2;
+ if (y2 > y1) {
+ delta_y = y2 - y1;
+ } else {
+ delta_y = y1 - y2;
+ }
+ }
+
+ delta_y = delta_y * delta_y;
+ delta_x = delta_x * delta_x;
+
+ total = delta_y + delta_x;
+
+ distance = sqrt(total);
+
+ return ((int) distance);
+}
+
+void new_position(double x1, double y1, double bearing, double velocity) {
+ double delta_x,
+ delta_y;
+ double radians;
+
+ /*
+ * Object two in which quadrant compared to object one? 0 x = opp, y =
+ * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd
+ * + 180 degrees 3 x = adj, y = opp + 270 degrees
+ */
+
+ /*
+ * sin finds opp, cos finds adj
+ */
+
+ if (bearing < 91) {
+ radians = bearing * 2.0 * M_PI / 360.;
+ delta_x = velocity * sin(radians);
+ delta_y = velocity * cos(radians);
+ new_x = x1 + delta_x;
+ new_y = y1 + delta_y;
+ } else if (bearing < 181) {
+ bearing -= 90;
+ radians = bearing * 2.0 * M_PI / 360.;
+ delta_y = velocity * sin(radians);
+ delta_x = velocity * cos(radians);
+ new_x = x1 + delta_x;
+ new_y = y1 - delta_y;
+ } else if (bearing < 271) {
+ bearing -= 180;
+ radians = bearing * 2.0 * M_PI / 360.;
+ delta_x = velocity * sin(radians);
+ delta_y = velocity * cos(radians);
+ new_x = x1 - delta_x;
+ new_y = y1 - delta_y;
+ } else {
+ bearing -= 270;
+ radians = bearing * 2.0 * M_PI / 360.;
+ delta_y = velocity * sin(radians);
+ delta_x = velocity * cos(radians);
+ new_x = x1 - delta_x;
+ new_y = y1 + delta_y;
+ }
+}
+
+int bearing(double x1, double y1, double x2, double y2) {
+ int quadrant;
+ double delta_x,
+ delta_y;
+ double oppoadj;
+ double bearing;
+
+ /*
+ * Object two in which quadrant compared to object one? 0 x = opp, y =
+ * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd
+ * + 180 degrees 3 x = adj, y = opp + 270 degrees
+ */
+
+ if (x2 > x1) {
+ delta_x = x2 - x1;
+ if (y2 > y1) {
+ quadrant = 0;
+ delta_y = y2 - y1;
+ oppoadj = delta_x / delta_y;
+ } else {
+ quadrant = 1;
+ delta_y = y1 - y2;
+ oppoadj = delta_y / delta_x;
+ }
+ } else {
+ delta_x = x1 - x2;
+ if (y2 > y1) {
+ quadrant = 3;
+ delta_y = y2 - y1;
+ oppoadj = delta_y / delta_x;
+ } else {
+ quadrant = 2;
+ delta_y = y1 - y2;
+ oppoadj = delta_x / delta_y;
+ }
+ }
+
+ bearing = atan(oppoadj);
+ bearing = bearing / (2.0 * M_PI) * 360.;
+ bearing = bearing + (90 * quadrant);
+
+ return ((int) bearing);
+}
+
+void set_arguments(char *function_call) {
+ /* THIS FUNCTION CREATES AN ARRAY OF JACL INTEGER CONSTANTS TO
+ REPRESENT THE ARGUMENTS PASSED TO A JACL FUNCTION */
+ int index,
+ counter,
+ length;
+ int position = 0; /* STORE THE INDEX OF THE WORD */
+ /* SETTING new_word TO FALSE SKIPS THE FIRST */
+ /* WORD WHICH IS THE FUNCTION NAME */
+ int new_word = FALSE;
+
+ char *arg_ptr[MAX_WORDS];
+ int arg_value[MAX_WORDS];
+
+ struct integer_type *resolved_integer;
+ struct cinteger_type *resolved_cinteger;
+
+ /* SPLIT UP THE FUNCTION CALL STRING AND EXTRACT THE ARGUMENTS */
+ length = strlen(function_call);
+
+ for (index = 0; index < length; index++) {
+ if (function_call[index] == '<') {
+ argument_buffer[index] = 0;
+ new_word = TRUE;
+ } else {
+ // COPY THE CHARACTER FROM THE CALLED NAME INTO THE CURRENT
+ // ARGUMENT BUFFER
+ argument_buffer[index] = function_call[index];
+ if (new_word) {
+ // THIS IS THE FIRST CHARACTER OF A NEW ARGUMENT SO STORE
+ // THE ADDRESS OF THIS CHARACTER IN THE ARGUMENT BUFFER
+ arg_ptr[position] = &argument_buffer[index];
+ new_word = FALSE;
+ if (position < MAX_WORDS)
+ position++;
+ }
+ }
+ }
+
+ argument_buffer[index] = 0;
+
+ /* CLEAR THE NEXT ARGUMENT POINTER */
+ arg_ptr[position] = NULL;
+
+ /* STORE THE INTEGER VALUE OF EACH ARGUMENT PASSED*/
+ index = 0;
+ while (arg_ptr[index] != NULL) {
+ //arg_value[index] = value_of(arg_ptr[index], TRUE);
+
+ if ((resolved_integer = integer_resolve(arg_ptr[index])) != NULL) {
+ arg_value[index] = resolved_integer->value;
+ } else if ((resolved_cinteger = cinteger_resolve(arg_ptr[index])) != NULL) {
+ arg_value[index] = resolved_cinteger->value;
+ } else if (object_element_resolve(arg_ptr[index])) {
+ arg_value[index] = oec;
+ } else if ((counter = object_resolve(arg_ptr[index])) != -1) {
+ if (counter < 1 || counter > objects) {
+ badptrrun(arg_ptr[index], counter);
+ pop_stack();
+ return;
+ } else {
+ arg_value[index] = counter;
+ }
+ } else if (validate(arg_ptr[index])) {
+ arg_value[index] = atoi(arg_ptr[index]);
+ } else {
+ arg_value[index] = -1;
+ }
+
+ index++;
+ }
+
+ /* THE CURRENT ARGUMENTS HAVE ALREADY BEEN PUSHED ONTO THE STACK
+ * AND STORED IF PASSED AS AN ARGUMENT TO THIS FUNCTION SO IT IS
+ * OKAY TO CLEAR THEM AND SET THE NEW VALUES */
+ clear_cinteger("arg");
+ clear_cstring("string_arg");
+
+ /* CREATE A CONSTANT FOR EACH ARGUMENT AFTER THE CORE FUNCTION NAME */
+ index = 0;
+ while (arg_ptr[index] != NULL) {
+ if (index == 0) noun[3] = arg_value[index];
+ add_cinteger("arg", arg_value[index]);
+ //printf("--- %s = %s\n", arg_ptr[index], arg_text_of(arg_ptr[index]));
+ add_cstring("string_arg", arg_text_of(arg_ptr[index]));
+ index++;
+ }
+}
+
+void pop_stack() {
+ int index, counter;
+
+ stack--;
+
+ clear_cinteger("arg");
+ clear_cstring("string_arg");
+
+ /* RECREATE THE arg ARRAY FOR THIS STACK FRAME */
+ for (index = 0; index < backup[stack].argcount; index++) {
+ if (index == 0) noun[3] = backup[stack].arguments[0];
+ add_cinteger("arg", backup[stack].arguments[index]);
+ }
+
+ /* RECREATE THE string_arg ARRAY FOR THIS STACK FRAME */
+ for (index = 0; index < backup[stack].argcount; index++) {
+ add_cstring("string_arg", backup[stack].str_arguments[index]);
+ }
+
+ /* RESTORE THE CONTENTS OF text_buffer */
+ for (counter = 0; counter < 1024; counter++)
+ text_buffer[counter] = backup[stack].text_buffer[counter];
+
+ /* RESTORE THE CONTENTS OF called_name */
+ //for (counter = 0; counter < 256; counter++)
+ //called_name[counter] = backup[stack].called_name[counter];
+ strncpy(called_name, backup[stack].called_name, 1024);
+
+ /* RESTORE THE CONTENTS OF scope_criterion */
+ //for (counter = 0; counter < 21; counter++)
+ // scope_criterion[counter] = backup[stack].scope_criterion[counter];
+ strncpy(scope_criterion, backup[stack].scope_criterion, 20);
+
+ /* RESTORE THE STORED FUNCTION NAMES THAT ARE USED WHEN AN
+ * 'override' COMMAND IS ENCOUNTERED IN THE CURRENT FUNCTION */
+ strncpy(override, backup[stack].override, 80);
+ strncpy(default_function, backup[stack].default_function, 80);
+
+ /* RESTORE ALL THE WORD POINTERS */
+ for (counter = 0; counter < MAX_WORDS; counter++) {
+ word[counter] = backup[stack].word[counter];
+ quoted[counter] = backup[stack].quoted[counter];
+ }
+
+ executing_function = backup[stack].function;
+
+ if (executing_function != NULL) {
+ strncpy(function_name, executing_function->name, 80);
+ strncpy(cstring_resolve("function_name")->value, executing_function->name, 80);
+ }
+
+ wp = backup[stack].wp;
+ top_of_loop = backup[stack].top_of_loop;
+ outfile = backup[stack].outfile;
+ infile = backup[stack].infile;
+ top_of_select = backup[stack].top_of_select;
+ top_of_while = backup[stack].top_of_while;
+ top_of_iterate = backup[stack].top_of_iterate;
+ top_of_update = backup[stack].top_of_update;
+ top_of_do_loop = backup[stack].top_of_do_loop;
+ criterion_value = backup[stack].criterion_value;
+ criterion_type = backup[stack].criterion_type;
+ criterion_negate = backup[stack].criterion_negate;
+ current_level = backup[stack].current_level;
+ execution_level = backup[stack].execution_level;
+ loop_integer = backup[stack].loop_integer;
+ select_integer = backup[stack].select_integer;
+
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, backup[stack].address, seekmode_Start);
+#else
+ fseek(file, backup[stack].address, SEEK_SET);
+#endif
+
+}
+
+void push_stack(int32 file_pointer) {
+ /* COPY ALL THE CURRENT SYSTEM DATA ONTO THE STACK */
+ int index;
+ int counter = 0;
+
+ if (stack == STACK_SIZE) {
+ log_error("Stack overflow.", PLUS_STDERR);
+ terminate(45);
+ } else {
+ backup[stack].infile = infile;
+ infile = NULL;
+ backup[stack].outfile = outfile;
+ outfile = NULL;
+ backup[stack].function = executing_function;
+ backup[stack].address = file_pointer;
+ backup[stack].wp = wp;
+ backup[stack].top_of_loop = top_of_loop;
+ backup[stack].top_of_select = top_of_select;
+ backup[stack].top_of_while = top_of_while;
+ backup[stack].top_of_iterate = top_of_iterate;
+ backup[stack].top_of_update = top_of_update;
+ backup[stack].top_of_do_loop = top_of_do_loop;
+ backup[stack].criterion_value = criterion_value;
+ backup[stack].criterion_type = criterion_type;
+ backup[stack].criterion_negate = criterion_negate;
+ backup[stack].current_level = current_level;
+ backup[stack].execution_level = execution_level;
+ backup[stack].loop_integer = loop_integer;
+ backup[stack].select_integer = select_integer;
+
+ /* MAKE A COPY OF THE CURRENT CONTENTS OF text_buffer */
+ for (counter = 0; counter < 1024; counter++)
+ backup[stack].text_buffer[counter] = text_buffer[counter];
+
+ /* MAKE A COPY OF THE CURRENT CONTENTS OF called_name */
+ strncpy(backup[stack].called_name, called_name, 1024);
+
+ // MAKE A COPY OF THE CURRENT CONTENTS OF scope_criterion
+ strncpy(backup[stack].scope_criterion, scope_criterion, 20);
+
+ /* COPY THE STORED FUNCTION NAMES THAT ARE USED WHEN AN
+ * 'override' COMMAND IS ENCOUNTERED IN THE CURRENT FUNCTION */
+ strncpy(backup[stack].override, override, 80);
+ strncpy(backup[stack].default_function, default_function, 80);
+
+ /* PUSH ALL THE WORD POINTERS ONTO THE STACK */
+ for (counter = 0; counter < MAX_WORDS; counter++) {
+ backup[stack].word[counter] = word[counter];
+ backup[stack].quoted[counter] = quoted[counter];
+ }
+
+ // PUSH ALL THE ARGUMENTS AS INTEGERS ONTO THE STACK
+ index = 0;
+ current_cinteger = cinteger_table;
+
+ if (current_cinteger != NULL) {
+ do {
+ if (!strcmp(current_cinteger->name, "arg")) {
+ backup[stack].arguments[index++] = current_cinteger->value;
+ }
+ current_cinteger = current_cinteger->next_cinteger;
+ } while (current_cinteger != NULL);
+ }
+
+ // STORE THE NUMBER OF ARGUMENTS PASSED TO THIS FUNCTION
+ // THIS IS THE SAME NUMBER FOR STRINGS AND INTEGERS
+ backup[stack].argcount = index;
+
+ // PUSH ALL THE ARGUMENTS AS STRINGS STRING ONTO THE STACK
+ index = 0;
+ current_cstring = cstring_table;
+
+ if (current_cstring != NULL) {
+ do {
+ if (!strcmp(current_cstring->name, "string_arg")) {
+ strncpy(backup[stack].str_arguments[index++], current_cstring->value, 255);
+ }
+
+ current_cstring = current_cstring->next_string;
+ } while (current_cstring != NULL);
+ }
+ }
+
+ // PUSH ON TO THE NEXT STACK FRAME
+ stack++;
+}
+
+void pop_proxy() {
+ int index, counter;
+
+ proxy_stack--;
+
+ clear_cinteger("$integer");
+ clear_cstring("$string");
+ clear_cstring("$word");
+
+ /* RECREATE THE integer ARRAY FOR THIS STACK FRAME */
+ for (index = 0; index < proxy_backup[proxy_stack].integercount; index++) {
+ add_cinteger("$integer", proxy_backup[proxy_stack].integer[index]);
+ }
+
+ /* RECREATE THE text ARRAY FOR THIS STACK FRAME */
+ for (index = 0; index < proxy_backup[proxy_stack].textcount; index++) {
+ add_cstring("$string", proxy_backup[proxy_stack].text[index]);
+ }
+
+ /* RECREATE THE $word ARRAY FOR THIS STACK FRAME */
+ for (index = 0; index < proxy_backup[proxy_stack].commandcount; index++) {
+ add_cstring("$word", proxy_backup[proxy_stack].command[index]);
+ }
+
+ /* RESTORE ALL THE NOUN POINTERS */
+ for (counter = 0; counter < 4; counter++)
+ noun[counter] = proxy_backup[proxy_stack].object_pointers[counter];
+
+ /* PUSH ALL THE RESOLVED OBJECTS ONTO THE STACK */
+ for (index = 0; index < 4; index++) {
+ list_size[index] = proxy_backup[proxy_stack].list_size[index];
+ max_size[index] = proxy_backup[proxy_stack].max_size[index];
+ for (counter = 0; counter < max_size[index]; counter++) {
+ object_list[index][counter] = proxy_backup[proxy_stack].object_list[index][counter];
+ }
+ }
+
+ start_of_this_command = proxy_backup[proxy_stack].start_of_this_command;
+ start_of_last_command = proxy_backup[proxy_stack].start_of_last_command;
+ after_from = proxy_backup[proxy_stack].after_from;
+ last_exact = proxy_backup[proxy_stack].last_exact;
+}
+
+void push_proxy() {
+ /* COPY ALL THE CURRENT SYSTEM DATA ONTO THE STACK */
+ int index;
+ int counter = 0;
+ int command = 0;
+ int text = 0;
+
+ current_cinteger = cinteger_table;
+ current_cstring = cstring_table;
+
+ if (proxy_stack == STACK_SIZE) {
+ log_error("Stack overflow.", PLUS_STDERR);
+ terminate(45);
+ } else {
+ proxy_backup[proxy_stack].start_of_this_command = start_of_this_command;
+ proxy_backup[proxy_stack].start_of_last_command = start_of_last_command;
+
+ /* PUSH ALL THE OBJECT POINTERS ONTO THE STACK */
+ for (counter = 0; counter < 4; counter++)
+ proxy_backup[proxy_stack].object_pointers[counter] = noun[counter];
+
+ /* PUSH ALL THE RESOLVED OBJECTS ONTO THE STACK */
+ for (index = 0; index < 4; index++) {
+ for (counter = 0; counter < max_size[index]; counter++) {
+ proxy_backup[proxy_stack].object_list[index][counter]
+ = object_list[index][counter];
+ }
+ proxy_backup[proxy_stack].list_size[index] = list_size[index];
+ proxy_backup[proxy_stack].max_size[index] = max_size[index];
+ }
+
+ /* PUSH ALL THE CURRENT COMMAND INTEGERS ONTO THE STACK */
+ counter = 0;
+
+ if (current_cinteger != NULL) {
+ do {
+ if (!strcmp(current_cinteger->name, "$integer")) {
+ proxy_backup[proxy_stack].integer[counter++] = current_cinteger->value;
+ }
+ current_cinteger = current_cinteger->next_cinteger;
+ } while (current_cinteger != NULL);
+ }
+
+ proxy_backup[proxy_stack].integercount = counter;
+
+ // PUSH ALL THE TEXT STRING SUPPLIED BY THE CURRENT COMMAND ONTO THE STACK
+ text = 0;
+ command = 0;
+
+ if (current_cstring != NULL) {
+ do {
+ if (!strcmp(current_cstring->name, "$string")) {
+ strncpy(proxy_backup[proxy_stack].text[text++], current_cstring->value, 255);
+ proxy_backup[proxy_stack].text[counter++][255] = 0;
+ } else if (!strcmp(current_cstring->name, "$word")) {
+ strncpy(proxy_backup[proxy_stack].command[command++], current_cstring->value, 255);
+ }
+
+ current_cstring = current_cstring->next_string;
+ } while (current_cstring != NULL);
+ }
+
+ proxy_backup[proxy_stack].textcount = counter;
+ proxy_backup[proxy_stack].commandcount = command;
+ proxy_backup[proxy_stack].after_from = after_from;
+ proxy_backup[proxy_stack].last_exact = last_exact;
+ }
+
+ // PUSH ON TO THE NEXT STACK FRAME
+ proxy_stack++;
+}
+
+int condition() {
+ /* COMPARE GROUPS OF TWO ELEMENTS. RETURN TRUE IF ANY ONE GROUP OF
+ * ELEMENTS COMPARE 'TRUE' */
+ int first;
+
+ first = 1;
+
+ while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
+ if (logic_test(first))
+ return (TRUE);
+ else
+ first = first + 3;
+ }
+ return (FALSE);
+}
+
+int and_condition() {
+ /* COMPARE GROUPS OF TWO ELEMENTS. RETURN FALSE IF ANY ONE GROUP OF
+ * ELEMENTS COMPARE 'FALSE' */
+ int first;
+
+ first = 1;
+
+ while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
+ if (logic_test(first) == FALSE)
+ return (FALSE);
+ else
+ first = first + 3;
+ }
+ return (TRUE);
+}
+
+int logic_test(int first) {
+ long index,
+ compare;
+
+ resolved_attribute = FALSE;
+
+ index = value_of(word[first], TRUE);
+ compare = value_of(word[first + 2], TRUE);
+
+ if (!strcmp(word[first + 1], "=") || !strcmp(word[first + 1], "==")) {
+ if (index == compare)
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], ">")) {
+ if (index > compare)
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "<")) {
+ if (index < compare)
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "is")) {
+ if (index < 1 || index > objects) {
+ unkobjrun(first);
+ return (FALSE);
+ } else
+ return (scope(index, word[first + 2]));
+ } else if (!strcmp(word[first + 1], "isnt")) {
+ if (index < 1 || index > objects) {
+ unkobjrun(first);
+ return (FALSE);
+ } else
+ return (!scope(index, word[first + 2]));
+ } else if (!strcmp(word[first + 1], "has"))
+ if (index < 1 || index > objects) {
+ unkobjrun(first);
+ return (FALSE);
+ } else {
+ if (resolved_attribute == SYSTEM_ATTRIBUTE) {
+ return (object[index]->attributes & compare);
+ } else {
+ return (object[index]->user_attributes & compare);
+ }
+ }
+ else if (!strcmp(word[first + 1], "hasnt"))
+ if (index < 1 || index > objects) {
+ unkobjrun(first);
+ return (FALSE);
+ } else {
+ if (resolved_attribute == SYSTEM_ATTRIBUTE) {
+ return (!(object[index]->attributes & compare));
+ } else {
+ return (!(object[index]->user_attributes & compare));
+ }
+ }
+ else if (!strcmp(word[first + 1], "!=")
+ || !strcmp(word[first + 1], "<>")) {
+ if (index != compare)
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], ">=")
+ || !strcmp(word[first + 1], "=>")) {
+ if (index >= compare)
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "<=")
+ || !strcmp(word[first + 1], "=<")) {
+ if (index <= compare)
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "grandof")) {
+ /* GRANDOF SAYS THAT AN OBJECT IS THE EVENTUAL PARENT OF ANOTHER OBJECT, NOT
+ * NECESSARILY IMMEDIATE */
+ if (index < 1 || index > objects) {
+ unkobjrun(first);
+ return (FALSE);
+ } else {
+ if (compare < 1 || compare > objects) {
+ unkobjrun(first + 2);
+ return (FALSE);
+ } else {
+ if (parent_of(index, compare, UNRESTRICT))
+ return (TRUE);
+ else
+ return (FALSE);
+ }
+ }
+ } else if (!strcmp(word[first + 1], "!grandof")) {
+ if (index < 1 || index > objects) {
+ unkobjrun(first);
+ return (FALSE);
+ } else {
+ if (compare < 1 || compare > objects) {
+ unkobjrun(first + 2);
+ return (FALSE);
+ } else {
+ if (parent_of(index, compare, UNRESTRICT))
+ return (FALSE);
+ else
+ return (TRUE);
+ }
+ }
+ } else {
+ sprintf(error_buffer,
+ "ERROR: In function \"%s\", illegal operator \"%s\".^",
+ executing_function->name, word[2]);
+ write_text(error_buffer);
+ return (FALSE);
+ }
+}
+
+int strcondition() {
+ int first;
+
+ first = 1;
+
+ while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
+ if (str_test(first))
+ return (TRUE);
+ else
+ first = first + 3;
+ }
+ return (FALSE);
+}
+
+int and_strcondition() {
+ int first;
+
+ first = 1;
+
+ while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
+ if (str_test(first) == FALSE)
+ return (FALSE);
+ else
+ first = first + 3;
+ }
+ return (TRUE);
+}
+
+int str_test(int first) {
+ char *index;
+ char *compare;
+
+ // GET THE TWO STRING VALUES TO COMPARE
+
+ index = arg_text_of_word(first);
+ compare = arg_text_of_word(first + 2);
+
+ if (!strcmp(word[first + 1], "==") || !strcmp(word[first + 1], "=")) {
+ if (!scumm_stricmp(index, compare)) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ } else if (!strcmp(word[first + 1], "!contains")) {
+ if (strcasestr(index, compare))
+ return (FALSE);
+ else
+ return (TRUE);
+ } else if (!strcmp(word[first + 1], "contains")) {
+ if (strcasestr(index, compare))
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "<>") || !strcmp(word[first + 1], "!=")) {
+ if (scumm_stricmp(index, compare))
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "==C") || !strcmp(word[first + 1], "=C")) {
+ if (!strcmp(index, compare)) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ } else if (!strcmp(word[first + 1], "!containsC")) {
+ if (strstr(index, compare))
+ return (FALSE);
+ else
+ return (TRUE);
+ } else if (!strcmp(word[first + 1], "containsC")) {
+ if (strstr(index, compare))
+ return (TRUE);
+ else
+ return (FALSE);
+ } else if (!strcmp(word[first + 1], "<>C") || !strcmp(word[first + 1], "!=C")) {
+ if (strcmp(index, compare))
+ return (TRUE);
+ else
+ return (FALSE);
+ } else {
+ sprintf(error_buffer,
+ "ERROR: In function \"%s\", illegal operator \"%s\".^",
+ executing_function->name, word[2]);
+ write_text(error_buffer);
+ return (FALSE);
+ }
+}
+
+void add_cinteger(char *name, int value) {
+ /* THIS FUNCTION ADDS A NEW JACL CONSTANT TO THE LIST */
+
+ if ((new_cinteger = (struct cinteger_type *)
+ malloc(sizeof(struct cinteger_type))) == NULL)
+ outofmem();
+ else {
+ if (cinteger_table == NULL) {
+ cinteger_table = new_cinteger;
+ } else {
+ /* FIND LAST CONSTANT IN LIST */
+ current_cinteger = cinteger_table;
+ while (current_cinteger->next_cinteger != NULL) {
+ current_cinteger = current_cinteger->next_cinteger;
+ }
+ current_cinteger->next_cinteger = new_cinteger;
+ }
+ strncpy(new_cinteger->name, name, 40);
+ new_cinteger->name[40] = 0;
+ new_cinteger->value = value;
+ new_cinteger->next_cinteger = NULL;
+ }
+}
+
+void clear_cinteger(char *name) {
+ /* FREE CONSTANTS THAT HAVE SUPPLIED NAME*/
+
+ //printf("--- clear integer %s\n", name);
+ if (cinteger_table != NULL) {
+ current_cinteger = cinteger_table;
+ previous_cinteger = cinteger_table;
+ while (current_cinteger != NULL) {
+ //sprintf(temp_buffer, "--- checking integer %s^", current_cinteger->name);
+ //write_text(temp_buffer);
+ if (!strcmp(current_cinteger->name, name)) {
+ //sprintf(temp_buffer, "--- found integer %s^", name);
+ //write_text(temp_buffer);
+ /* FREE THIS CONSTANT */
+ if (previous_cinteger == current_cinteger) {
+ // THE INTEGER BEING CLEARED IS THE FIRST INTEGER IN THE LIST
+ cinteger_table = current_cinteger->next_cinteger;
+ previous_cinteger = current_cinteger->next_cinteger;
+ free(current_cinteger);
+ current_cinteger = previous_cinteger;
+ } else {
+ previous_cinteger->next_cinteger = current_cinteger->next_cinteger;
+ free(current_cinteger);
+ current_cinteger = previous_cinteger->next_cinteger;
+ }
+ } else {
+ previous_cinteger = current_cinteger;
+ current_cinteger = current_cinteger->next_cinteger;
+ }
+ }
+ }
+ //printf("--- leaving clear integer\n");
+}
+
+void add_cstring(char *name, char *value) {
+ /* ADD A STRING CONSTANT WITH THE SUPPLIED NAME AND VALUE */
+
+ if ((new_string = (struct string_type *)
+ malloc(sizeof(struct string_type))) == NULL)
+ outofmem();
+ else {
+ if (cstring_table == NULL) {
+ cstring_table = new_string;
+ } else {
+ /* FIND LAST STRING IN LIST */
+ current_cstring = cstring_table;
+ while (current_cstring->next_string != NULL) {
+ current_cstring = current_cstring->next_string;
+ }
+ current_cstring->next_string = new_string;
+ }
+ strncpy(new_string->name, name, 40);
+ new_string->name[40] = 0;
+ strncpy(new_string->value, value, 255);
+ new_string->value[255] = 0;
+ new_string->next_string = NULL;
+ }
+}
+
+void clear_cstring(char *name) {
+ /* FREE CONSTANTS THAT HAVE SUPPLIED NAME*/
+ if (cstring_table != NULL) {
+ current_cstring = cstring_table;
+ previous_cstring = cstring_table;
+ while (current_cstring != NULL) {
+ if (!strcmp(current_cstring->name, name)) {
+ /* FREE THIS STRING */
+ if (previous_cstring == current_cstring) {
+ cstring_table = current_cstring->next_string;
+ previous_cstring = current_cstring->next_string;
+ free(current_cstring);
+ current_cstring = previous_cstring;
+ } else {
+ previous_cstring->next_string = current_cstring->next_string;
+ free(current_cstring);
+ current_cstring = previous_cstring->next_string;
+ }
+ } else {
+ previous_cstring = current_cstring;
+ current_cstring = current_cstring->next_string;
+ }
+ }
+ }
+}
+
+void inspect(int object_num) {
+ // THIS FUNCTION DISPLAYS THE STATE OF A JACL OBJECT FOR DEBUGGING
+
+ int index, attribute_value;
+
+ struct attribute_type *pointer = attribute_table;
+
+ if (object_num < 1 || object_num > objects) {
+ badptrrun(word[1], object_num);
+ return;
+ }
+
+ write_text("label: ");
+ write_text(object[object_num]->label);
+
+ if (object[object_num]->attributes & LOCATION) {
+ // OUTPUT ALL THE ATTRIBUTES WITH LOCATION ATTRIBUTE TEXT
+ write_text("^has location attributes: ");
+ index = 0;
+ attribute_value = 1;
+ while (location_attributes[index] != NULL) {
+ if (object[object_num]->attributes & attribute_value) {
+ write_text(location_attributes[index]);
+ }
+ index++;
+ attribute_value *= 2;
+ }
+ } else {
+ // OUTPUT ALL THE ATTRIBUTES WITH OBJECT ATTRIBUTE TEXT
+ write_text("^has object attributes: ");
+ index = 0;
+ attribute_value = 1;
+ while (object_attributes[index] != NULL) {
+ if (object[object_num]->attributes & attribute_value) {
+ write_text(object_attributes[index]);
+ }
+ index++;
+ attribute_value *= 2;
+ }
+
+ write_text("^has user attributes: ");
+ attribute_value = 1;
+ }
+
+ if (pointer != NULL) {
+ // THERE ARE USER ATTRIBUTES, SO CHECK IF THIS OBJECT OR LOCATION
+ // HAS ANY OF THEM
+ do {
+ if (object[object_num]->user_attributes & pointer->value) {
+ write_text(pointer->name);
+ write_text(" ");
+ }
+
+ pointer = pointer->next_attribute;
+ } while (pointer != NULL);
+ }
+
+ write_text("^");
+
+ index = 0;
+ if (object[object_num]->attributes & LOCATION) {
+ while (location_elements[index] != NULL) {
+ if (index < 12) {
+ if (object[object_num]->integer[index] < 1 || object[object_num]->integer[index] > objects) {
+ sprintf(temp_buffer, "%s: nowhere (%d)^", location_elements[index], object[object_num]->integer[index]);
+ } else {
+ sprintf(temp_buffer, "%s: %s (%d)^", location_elements[index], object[object[object_num]->integer[index]]->label, object[object_num]->integer[index]);
+ }
+ } else {
+ sprintf(temp_buffer, "%s: %d^", location_elements[index], object[object_num]->integer[index]);
+ }
+ write_text(temp_buffer);
+ index++;
+ }
+ } else {
+ while (object_elements[index] != NULL) {
+ if (index == 0) {
+ sprintf(temp_buffer, "%s: %s (%d)^", object_elements[index], object[object[object_num]->integer[index]]->label, object[object_num]->integer[index]);
+ } else {
+ sprintf(temp_buffer, "%s: %d^", object_elements[index], object[object_num]->integer[index]);
+ }
+ write_text(temp_buffer);
+ index++;
+ }
+ }
+}
+
+int grand_of(int child, int objs_only) {
+ /* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL
+ * A 'PARENT' IS REACHED */
+
+ /* objs_only ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN A
+ * LOCATION */
+
+ int parent;
+
+ if (object[child]->PARENT != NOWHERE) {
+ /* STORE THE CHILDS PARENT OBJECT */
+ parent = object[child]->PARENT;
+
+ if (object[parent]->attributes & LOCATION) {
+ if (objs_only) {
+ /* THE CHILDS PARENT IS LOCATION AND SEARCH IS RESTRICTED TO
+ * OBJECTS */
+ return (child);
+ } else {
+ return (parent);
+ }
+ } else {
+ /* KEEP LOOKING UP THE TREE UNTIL THE CHILD HAS NO
+ * PARENT */
+ return (grand_of(parent, objs_only));
+ }
+ } else {
+ /* THE SPECIFIED OBJECT HAS NO PARENT */
+ return (child);
+ }
+}
+
+int select_next() {
+ while (++*select_integer <= objects) {
+ switch (criterion_type) {
+ case CRI_ATTRIBUTE:
+ if (object[*select_integer]->attributes & criterion_value) {
+ if (!criterion_negate) {
+ return TRUE;
+ }
+ } else {
+ if (criterion_negate) {
+ return TRUE;
+ }
+ }
+ break;
+ case CRI_USER_ATTRIBUTE:
+ if (object[*select_integer]->user_attributes & criterion_value) {
+ if (!criterion_negate) {
+ return TRUE;
+ }
+ } else {
+ if (criterion_negate) {
+ return TRUE;
+ }
+ }
+ break;
+ case CRI_PARENT:
+ if (object[*select_integer]->PARENT == criterion_value) {
+ if (!criterion_negate) {
+ return TRUE;
+ }
+ } else {
+ if (criterion_negate) {
+ return TRUE;
+ }
+ }
+ break;
+ case CRI_SCOPE:
+ if (scope(*select_integer, scope_criterion)) {
+ if (!criterion_negate) {
+ return TRUE;
+ }
+ } else {
+ if (criterion_negate) {
+ return TRUE;
+ }
+ }
+ break;
+ }
+ }
+
+ return (FALSE);
+}
+
+/* Converts an integer value to its hex character*/
+char to_hex(char code) {
+ static char hex[] = "0123456789abcdef";
+ return hex[code & 15];
+}
+
+/* Returns a url-encoded version of str */
+/* IMPORTANT: be sure to free() the returned string after use */
+char *url_encode(char *str) {
+ char *pstr = str, *buf = (char *)malloc(strlen(str) * 3 + 1), *pbuf = buf;
+ while (*pstr) {
+ if (Common::isAlnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
+ *pbuf++ = *pstr;
+ else if (*pstr == ' ')
+ *pbuf++ = '+';
+ else
+ *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
+ pstr++;
+ }
+ *pbuf = '\0';
+ return buf;
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/jacl.cpp b/engines/glk/jacl/jacl.cpp
index 0178b1ce31..c967a8891c 100644
--- a/engines/glk/jacl/jacl.cpp
+++ b/engines/glk/jacl/jacl.cpp
@@ -21,7 +21,7 @@
*/
#include "glk/jacl/jacl.h"
-#include "glk/jacl/common/config-manager.h"
+#include "common/config-manager.h"
namespace Glk {
namespace JACL {
@@ -29,7 +29,7 @@ namespace JACL {
JACL *g_vm;
JACL::JACL(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
- _saveSlot(-1) {
+ _saveSlot(-1) {
g_vm = this;
}
diff --git a/engines/glk/jacl/jacl.h b/engines/glk/jacl/jacl.h
index ce04c890e5..b49b121b5d 100644
--- a/engines/glk/jacl/jacl.h
+++ b/engines/glk/jacl/jacl.h
@@ -77,7 +77,9 @@ public:
/**
* Returns true if a savegame is being loaded directly from the ScummVM launcher
*/
- bool loadingSavegame() const { return _saveSlot != -1; }
+ bool loadingSavegame() const {
+ return _saveSlot != -1;
+ }
};
extern JACL *g_vm;
diff --git a/engines/glk/jacl/jacl_main.cpp b/engines/glk/jacl/jacl_main.cpp
new file mode 100644
index 0000000000..6b77f85d97
--- /dev/null
+++ b/engines/glk/jacl/jacl_main.cpp
@@ -0,0 +1,1582 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/csv.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/prototypes.h"
+#include "glk/jacl/version.h"
+
+namespace Glk {
+namespace JACL {
+
+int convert_to_utf32(unsigned char *text);
+
+glui32 status_width, status_height;
+
+schanid_t sound_channel[8] = { NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+ };
+
+event_t *cancelled_event;
+
+extern struct csv_parser parser_csv;
+
+extern char text_buffer[];
+extern char *word[];
+extern short int quoted[];
+extern short int punctuated[];
+extern int wp;
+
+extern int custom_error;
+extern int interrupted;
+
+extern int jpp_error;
+
+extern int it;
+extern int them[];
+extern int her;
+extern int him;
+
+extern int oops_word;
+
+#ifdef WINGLK
+struct string_type *resolved_string;
+#endif
+
+char include_directory[81] = "\0";
+char temp_directory[81] = "\0";
+char data_directory[81] = "\0";
+char special_prompt[81] = "\n: \0";
+char file_prompt[5] = ": \0";
+char bookmark[81] = "\0";
+char walkthru[81] = "\0";
+
+char function_name[81];
+
+extern char default_function[84];
+char override[81];
+
+char temp_buffer[1024];
+char error_buffer[1024];
+unsigned char chunk_buffer[4096];
+#ifndef NOUNICODE
+glui32 chunk_buffer_uni[4096];
+#endif
+char proxy_buffer[1024];
+
+char oops_buffer[1024];
+char oopsed_current[1024];
+char last_command[1024];
+char *blank_command = "blankjacl\0";
+char *current_command = (char *) NULL;
+char command_buffer[1024];
+#ifndef NOUNICODE
+glui32 command_buffer_uni[1024];
+#endif
+char players_command[1024];
+
+int walkthru_running = FALSE;
+
+int start_of_last_command;
+int start_of_this_command;
+
+int objects, integers, functions, strings;
+
+/* A STREAM FOR THE GAME FILE, WHEN IT'S OPEN. */
+strid_t game_stream = NULL;
+
+/* THE STREAM FOR OPENING UP THE ARCHIVE CONTAINING GRAPHICS AND SOUND */
+strid_t blorb_stream;
+
+/* A FILE REFERENCE FOR THE TRANSCRIPT FILE. */
+static frefid_t script_fref = NULL;
+/* A STREAM FOR THE TRANSCRIPT FILE, WHEN IT'S OPEN. */
+static strid_t script_stream = NULL;
+
+int noun[4];
+int player = 0;
+
+int noun3_backup;
+int player_backup = 0;
+
+int variable_contents;
+int oec;
+int *object_element_address,
+ *object_backup_address;
+
+short int spaced = TRUE;
+
+int delay = 0;
+
+/* START OF GLK STUFF */
+
+/* POINTERS TO THE GLK WINDOWS */
+winid_t mainwin = NULL;
+winid_t statuswin = NULL;
+winid_t promptwin = NULL;
+winid_t inputwin = NULL;
+winid_t current_window = NULL;
+
+/* POINTERS TO THE WINDOWS STREAMS */
+strid_t mainstr = NULL;
+strid_t statusstr = NULL;
+strid_t promptstr = NULL;
+strid_t inputstr = NULL;
+
+/* END OF GLK STUFF */
+
+char user_id[] = "local";
+char prefix[81] = "\0";
+char blorb[81] = "\0";
+char game_path[256] = "\0";
+char game_file[256] = "\0";
+char processed_file[256] = "\0";
+
+struct object_type *object[MAX_OBJECTS];
+struct integer_type *integer_table = NULL;
+struct cinteger_type *cinteger_table = NULL;
+struct window_type *window_table = NULL;
+struct attribute_type *attribute_table = NULL;
+struct string_type *string_table = NULL;
+struct string_type *cstring_table = NULL;
+struct function_type *function_table = NULL;
+struct function_type *executing_function = NULL;
+struct command_type *completion_list = NULL;
+struct word_type *grammar_table = NULL;
+struct synonym_type *synonym_table = NULL;
+struct filter_type *filter_table = NULL;
+
+// Forward declarations
+static void word_check();
+static void version_info();
+
+
+void glk_main() {
+ int index;
+ frefid_t blorb_file;
+
+ override[0] = 0;
+
+ /* ALLOC AN EVENT TO STORE A CANCELLED EVENT IN */
+ if ((cancelled_event = (event_t *) malloc(sizeof(event_t))) == NULL)
+ outofmem();
+
+ /* CREATE style_User1 FOR USE IN THE STATUS LINE */
+ g_vm->glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
+ g_vm->glk_stylehint_set(wintype_TextBuffer, style_User2, stylehint_ReverseColor, 1);
+
+ /* OPEN THE MAIN WINDOW THE GLK WINDOWS */
+ mainwin = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
+
+ if (!mainwin) {
+ /* IT'S POSSIBLE THAT THE MAIN WINDOW FAILED TO OPEN. THERE's
+ * NOTHING WE CAN DO WITHOUT IT, SO EXIT. */
+ return;
+ } else {
+ /* GET A REFERENCE TO mainwin's STREAM */
+ mainstr = g_vm->glk_window_get_stream(mainwin);
+ }
+
+ /* SET THE CURRENT OUTPUT STREAM TO PRINT TO IT. */
+ jacl_set_window(mainwin);
+
+ /* OPEN A THIRD WINDOW: A TEXT GRID, BELOW THE MAIN WINDOW, ONE LINE
+ * HIGH. THIS IS THE WINDOW TO DISPLAY THE COMMAND PROMPT IN */
+ //promptwin = g_vm->glk_window_open(mainwin, winmethod_Below | winmethod_Fixed,
+ // 3, wintype_TextBuffer, 0);
+
+ /* SET THIS TO DETERMINE THE SYTEM OF INPUT TO USE */
+ //inputwin = promptwin;
+ inputwin = mainwin;
+
+ if (jpp_error) {
+ /* THERE WAS AN ERROR DURING PREPROCESSING. NOW THAT THERE IS AN
+ * OPEN GLK WINDOW, OUTPUT THE ERROR MESSAGE AND EXIT */
+ log_error(error_buffer, FALSE);
+ terminate(200);
+ }
+
+ /* OPEN THE BLORB FILE IF ONE EXISTS */
+#ifndef WINGLK
+ blorb_file = g_vm->glk_fileref_create_by_name(fileusage_BinaryMode, blorb, 0);
+#else
+ strcpy(temp_buffer, game_path);
+ strcat(temp_buffer, blorb);
+ strcpy(blorb, temp_buffer);
+ blorb_file = wing_vm->glk_fileref_create_by_name(fileusage_BinaryMode, blorb, 0, 0);
+#endif
+
+#ifdef UNUSED
+ if (blorb_file != NULL && g_vm->glk_fileref_does_file_exist(blorb_file)) {
+ blorb_stream = g_vm->glk_stream_open_file(blorb_file, filemode_Read, 0);
+
+ if (blorb_stream != NULL) {
+ /* IF THE FILE EXISTS, SET THE RESOURCE MAP */
+ giblorb_set_resource_map(blorb_stream);
+ }
+ }
+#endif
+ // INTIALISE THE CSV PARSER
+ csv_init(&parser_csv, CSV_APPEND_NULL);
+
+ /* NO PREPROCESSOR ERRORS, LOAD THE GAME FILE */
+ read_gamefile();
+
+ execute("+bootstrap");
+
+ // OPEN A SECOND WINDOW: A TEXT GRID, ABOVE THE MAIN WINDOW, ONE LINE
+ // HIGH. IT IS POSSIBLE THAT THIS WILL FAIL ALSO, BUT WE ACCEPT THAT.
+ statuswin = g_vm->glk_window_open(mainwin, winmethod_Above | winmethod_Fixed,
+ 0, wintype_TextGrid, 0);
+
+ // GET A REFERENCE TO statuswin's STREAM
+ if (statuswin != NULL) {
+ statusstr = g_vm->glk_window_get_stream(statuswin);
+ }
+
+#ifdef WINGLK
+ if ((resolved_string = cstring_resolve("game_title")) != NULL) {
+ wing_vm->glk_window_set_title(resolved_string->value);
+ } else {
+ sprintf(temp_buffer, "JACL v%d.%d.%d ", J_VERSION, J_RELEASE, J_BUILD);
+ wing_vm->glk_window_set_title(temp_buffer);
+ }
+#endif
+
+ if (SOUND_SUPPORTED->value) {
+ /* CREATE THE EIGHT SOUND CHANNELS */
+ for (index = 0; index < 8; index++) {
+ sound_channel[index] = g_vm->glk_schannel_create(0);
+ }
+ }
+
+ jacl_set_window(mainwin);
+
+ execute("+intro");
+
+ if (object[2] == NULL) {
+ log_error(CANT_RUN, PLUS_STDERR);
+ terminate(43);
+ }
+
+ /* DUMMY RETRIEVE OF 'HERE' FOR TESTING OF GAME STATE */
+ get_here();
+
+ eachturn();
+
+ /* TOP OF COMMAND LOOP */
+ do {
+ int gotline;
+ event_t ev;
+
+ custom_error = FALSE;
+
+ jacl_set_window(mainwin);
+
+ execute("+bottom");
+
+ status_line();
+
+ if (current_command != NULL) {
+ strcpy(last_command, current_command);
+ }
+
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ jacl_set_window(inputwin);
+ }
+
+ /* OUTPUT THE CUSTOM COMMAND PROMPT */
+ write_text(string_resolve("command_prompt")->value);
+
+#ifdef NOUNICODE
+ g_vm->glk_request_line_event(inputwin, command_buffer, 255, 0);
+#else
+ g_vm->glk_request_line_event_uni(inputwin, command_buffer_uni, 255, 0);
+#endif
+
+ jacl_set_window(inputwin);
+
+ gotline = FALSE;
+
+ while (!gotline) {
+ /* GRAB AN EVENT. */
+ g_vm->glk_select(&ev);
+
+ switch (ev.type) {
+
+ case evtype_LineInput:
+ if (ev.window == inputwin) {
+ gotline = TRUE;
+ jacl_set_window(mainwin);
+ /* REALLY THE EVENT CAN *ONLY* BE FROM MAINWIN,
+ * BECAUSE WE NEVER REQUEST LINE INPUT FROM THE
+ * STATUS WINDOW. BUT WE DO A PARANOIA TEST,
+ * BECAUSE COMMANDBUF IS ONLY FILLED IF THE LINE
+ * EVENT COMES FROM THE MAINWIN REQUEST. IF THE
+ * LINE EVENT COMES FROM ANYWHERE ELSE, WE IGNORE
+ * IT. */
+ }
+ break;
+
+ case evtype_SoundNotify:
+ /* A SOUND HAS FINISHED PLAYING CALL +sound_finished
+ * WITH THE RESOUCE NUMBER AS THE FIRST ARGUMENT
+ * AND THE CHANNEL NUMBER AS THE SECOND ARGUMENT */
+ sprintf(temp_buffer, "+sound_finished<%d<%d", (int) ev.val1, (int) ev.val2 - 1);
+ execute(temp_buffer);
+ break;
+
+ case evtype_Timer:
+ /* A TIMER EVENT IS TRIGGERED PERIODICALLY IF THE GAME
+ * REQUESTS THEM. THIS SIMPLY EXECUTES THE FUNCTION
+ * +timer WHICH IS LIKE +eachturn EXCEPT IT DOESN'T
+ * WAIT FOR THE PLAYER TO TYPE A COMMAND */
+
+ jacl_set_window(mainwin);
+ execute("+timer");
+ break;
+
+ case evtype_Arrange:
+ /* WINDOWS HAVE CHANGED SIZE, SO WE HAVE TO REDRAW THE
+ * STATUS WINDOW. */
+ status_line();
+ break;
+ }
+ }
+
+ // THE PLAYER'S INPUT WILL BE UTF-32. CONVERT IT TO UTF-8 AND NULL TERMINATE IT
+#ifndef NOUNICODE
+ convert_to_utf8(command_buffer_uni, ev.val1);
+#endif
+
+ current_command = command_buffer;
+
+ /* SET ALL THE OUTPUT TO GO TO mainwin NOW THE COMMAND HAS BEEN READ */
+ if (inputwin == promptwin) {
+ jacl_set_window(mainwin);
+ write_text(string_resolve("command_prompt")->value);
+ g_vm->glk_set_style(style_Input);
+ write_text(current_command);
+ g_vm->glk_set_style(style_Normal);
+ write_text("^");
+ }
+
+ execute("+top");
+
+ index = 0;
+
+ if (*current_command) {
+ while (*(current_command + index) && index < 1024) {
+ if (*(current_command + index) == '\r' || *(current_command + index) == '\n') {
+ break;
+ } else {
+ text_buffer[index] = *(current_command + index);
+ index++;
+ }
+ }
+ }
+
+ text_buffer[index] = 0;
+
+ if (text_buffer[0] == 0) {
+ /* NO COMMAND WAS SPECIFIED, FILL THE COMMAND IN AS 'blankjacl'
+ * FOR THE GAME TO PROCESS AS DESIRED */
+ strcpy(text_buffer, "blankjacl");
+ current_command = blank_command;
+ }
+
+ command_encapsulate();
+ jacl_truncate();
+
+ index = 0;
+
+ /* SET THE INTEGER INTERRUPTED TO FALSE. IF THIS IS SET TO
+ * TRUE BY ANY COMMAND, FURTHER PROCESSING WILL STOP */
+ INTERRUPTED->value = FALSE;
+
+ interrupted = FALSE;
+
+ if (word[0] != NULL) {
+ if (strcmp(word[0], "undo")) {
+ /* COMMAND DOES NOT EQUAL undo */
+ save_game_state();
+ }
+
+ if (word[0][0] == '*') {
+ if (script_stream) {
+ write_text(cstring_resolve("COMMENT_RECORDED")->value);
+ } else {
+ write_text(cstring_resolve("COMMENT_IGNORED")->value);
+ }
+ } else {
+ /* COMMAND IS NOT NULL, START PROCESSING IT */
+ preparse();
+ }
+ } else {
+ /* NO COMMAND WAS SPECIFIED, FILL THE COMMAND IN AS 'blankjacl'
+ * FOR THE GAME TO PROCESS AS DESIRED */
+ strcpy(text_buffer, "blankjacl");
+ command_encapsulate();
+ preparse();
+ }
+
+ } while (TRUE);
+}
+
+void preparse() {
+ int position;
+
+ // THE INTERRUPTED VARIABLE IS USED TO STOP LATER ACTIONS IN A COMMAND
+ // IF ANY ONE
+ while (word[wp] != NULL && INTERRUPTED->value == FALSE) {
+ //printf("--- preparse %s\n", word[wp]);
+ // PROCESS THE CURRENT COMMAND
+ // CREATE THE command STRINGS FROM THIS POINT ONWARDS SO THE VERB OF
+ // THE CURRENT COMMAND IS ALWAYS command[0].
+
+ clear_cstring("command");
+
+ position = wp;
+
+ while (word[position] != NULL && strcmp(word[position], cstring_resolve("THEN_WORD")->value)) {
+ add_cstring("command", word[position]);
+ position++;
+ };
+
+ // PROCESS THE COMMAND
+ word_check();
+
+ /* THE PREVIOUS COMMAND HAS FINISHED, LOOK FOR ANOTHER COMMAND */
+ while (word[wp] != NULL) {
+ if (word[wp] != NULL && !strcmp(word[wp], cstring_resolve("THEN_WORD")->value)) {
+ wp++;
+ break;
+ }
+ wp++;
+ }
+ }
+}
+
+void word_check() {
+ int index;
+
+ /* REMEMBER THE START OF THIS COMMAND TO SUPPORT 'oops' AND 'again' */
+ start_of_this_command = wp;
+ //printf("--- command starts at %d\n", start_of_this_command);
+
+ /* START CHECKING THE PLAYER'S COMMAND FOR SYSTEM COMMANDS */
+ if (!strcmp(word[wp], cstring_resolve("QUIT_WORD")->value) || !strcmp(word[wp], "q")) {
+ if (execute("+quit_game") == FALSE) {
+ TIME->value = FALSE;
+ write_text(cstring_resolve("SURE_QUIT")->value);
+ if (get_yes_or_no()) {
+ newline();
+ execute("+score");
+ terminate(0);
+ } else {
+ write_text(cstring_resolve("RETURN_GAME")->value);
+ }
+ }
+ } else if (!strcmp(word[wp], cstring_resolve("RESTART_WORD")->value)) {
+ if (execute("+restart_game") == FALSE) {
+ TIME->value = FALSE;
+ write_text(cstring_resolve("SURE_RESTART")->value);
+ if (get_yes_or_no()) {
+ write_text(cstring_resolve("RESTARTING")->value);
+ restart_game();
+ g_vm->glk_window_clear(current_window);
+ execute("+intro");
+ eachturn();
+ } else {
+ write_text(cstring_resolve("RETURN_GAME")->value);
+ }
+ }
+ } else if (!strcmp(word[wp], cstring_resolve("UNDO_WORD")->value)) {
+ if (execute("+undo_move") == FALSE) {
+ undoing();
+ }
+ } else if (!strcmp(word[wp], cstring_resolve("OOPS_WORD")->value) || !strcmp(word[wp], "o")) {
+ //printf("--- oops word is %d\n", oops_word);
+ if (word[++wp] != NULL) {
+ if (oops_word == -1) {
+ if (TOTAL_MOVES->value == 0) {
+ write_text(cstring_resolve("NO_MOVES")->value);
+ TIME->value = FALSE;
+ } else {
+ write_text(cstring_resolve("CANT_CORRECT")->value);
+ TIME->value = FALSE;
+ }
+ } else {
+ strcpy(oops_buffer, word[wp]);
+ strcpy(text_buffer, last_command);
+ command_encapsulate();
+ //printf("--- trying to replace %s with %s\n", word[oops_word], oops_buffer);
+ jacl_truncate();
+ word[oops_word] = (char *) &oops_buffer;
+
+ /* BUILD A PLAIN STRING REPRESENTING THE NEW COMMAND */
+ oopsed_current[0] = 0;
+ index = 0;
+
+ while (word[index] != NULL) {
+ if (oopsed_current[0] != 0) {
+ strcat(oopsed_current, " ");
+ }
+
+ strcat(oopsed_current, word[index]);
+
+ index++;
+ }
+
+ current_command = oopsed_current;
+ //printf("--- current command is: %s\n", current_command);
+
+ /* PROCESS THE FIXED COMMAND ONLY */
+ wp = start_of_last_command;
+ word_check();
+ }
+ } else {
+ write_text(cstring_resolve("BAD_OOPS")->value);
+ TIME->value = FALSE;
+ }
+ } else if (!strcmp(word[wp], cstring_resolve("AGAIN_WORD")->value) || !strcmp(word[wp], "g")) {
+ if (TOTAL_MOVES->value == 0) {
+ write_text(cstring_resolve("NO_MOVES")->value);
+ TIME->value = FALSE;
+ } else if (last_command[0] == 0) {
+ write_text(cstring_resolve("NOT_CLEVER")->value);
+ TIME->value = FALSE;
+ } else {
+ strcpy(text_buffer, last_command);
+ current_command = last_command;
+ command_encapsulate();
+ jacl_truncate();
+ //printf("--- command started at %d\n", start_of_last_command);
+ wp = start_of_last_command;
+ word_check();
+ }
+ } else if (!strcmp(word[wp], cstring_resolve("SCRIPT_WORD")->value) || !strcmp(word[wp], "transcript")) {
+ scripting();
+ } else if (!strcmp(word[wp], cstring_resolve("UNSCRIPT_WORD")->value)) {
+ if (!script_stream) {
+ write_text(cstring_resolve("SCRIPTING_ALREADY_OFF")->value);
+ } else {
+ /* Close the file. */
+ g_vm->glk_put_string_stream(script_stream, "\nEND OF A TRANSCRIPT\n");
+ g_vm->glk_stream_close(script_stream, NULL);
+ write_text(cstring_resolve("SCRIPTING_OFF")->value);
+ script_stream = NULL;
+ }
+ } else if (!strcmp(word[wp], cstring_resolve("WALKTHRU_WORD")->value)) {
+ walking_thru();
+ } else if (!strcmp(word[wp], cstring_resolve("INFO_WORD")->value) || !strcmp(word[wp], "version")) {
+ version_info();
+ write_text("you can redistribute it and/or modify it under the ");
+ write_text("terms of the GNU General Public License as published by ");
+ write_text("the Free Software Foundation; either version 2 of the ");
+ write_text("License, or any later version.^^");
+ write_text("This program is distributed in the hope that it will be ");
+ write_text("useful, but WITHOUT ANY WARRANTY; without even the ");
+ write_text("implied warranty of MERCHANTABILITY or FITNESS FOR A ");
+ write_text("PARTICULAR PURPOSE. See the GNU General Public License ");
+ write_text("for more details.^^");
+ write_text("You should have received a copy of the GNU General ");
+ write_text("Public License along with this program; if not, write ");
+ write_text("to the Free Software Foundation, Inc., 675 Mass Ave, ");
+ write_text("Cambridge, MA 02139, USA.^^");
+ sprintf(temp_buffer, "OBJECTS DEFINED: %d^", objects);
+ write_text(temp_buffer);
+ TIME->value = FALSE;
+ } else {
+ /* NO WORD HAS BEEN MARKED AS AN ERROR YET*/
+ oops_word = -1;
+
+ /* THIS IS NOT A SYSTEM COMMAND, CALL parser TO PROCESS THE COMMAND */
+ parser();
+ }
+
+ start_of_last_command = start_of_this_command;
+}
+
+void version_info() {
+ char buffer[80];
+
+ sprintf(buffer, "JACL Interpreter v%d.%d.%d ", J_VERSION, J_RELEASE,
+ J_BUILD);
+ write_text(buffer);
+ sprintf(buffer, "/ %d object.^", MAX_OBJECTS);
+ write_text(buffer);
+ write_text("Copyright (c) 1992-2010 Stuart Allen.^^");
+}
+
+void save_game_state() {
+ /* THIS FUNCTION MAKES AN IN-MEMORY COPY OF THE GAME STATE AFTER EACH
+ * OF THE PLAYER'S COMMANDS SO THE 'undo' COMMAND CAN BE USED */
+ int index,
+ counter;
+
+ struct integer_type *current_integer = integer_table;
+ struct function_type *current_function = function_table;
+
+ do {
+ current_function->call_count_backup = current_function->call_count;
+ current_function = current_function->next_function;
+ } while (current_function != NULL);
+
+ do {
+ current_integer->value_backup = current_integer->value;
+ current_integer = current_integer->next_integer;
+ } while (current_integer != NULL);
+
+ for (index = 1; index <= objects; index++) {
+ if (object[index]->nosave)
+ continue;
+
+ for (counter = 0; counter < 16; counter++) {
+ object[index]->integer_backup[counter] =
+ object[index]->integer[counter];
+ }
+
+ object[index]->attributes_backup = object[index]->attributes;
+ object[index]->user_attributes_backup = object[index]->user_attributes;
+ }
+
+ player_backup = player;
+ noun3_backup = noun[3];
+}
+
+int save_interaction(char *filename) {
+ frefid_t saveref;
+
+ jacl_set_window(inputwin);
+
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ newline();
+ }
+
+ if (filename == NULL) {
+ saveref = g_vm->glk_fileref_create_by_prompt(fileusage_SavedGame | fileusage_BinaryMode, filemode_Write, 0);
+ } else {
+ saveref = g_vm->glk_fileref_create_by_name(fileusage_SavedGame | fileusage_BinaryMode, filename, 0);
+
+ }
+
+ jacl_set_window(mainwin);
+
+ if (!saveref) {
+ write_text(cstring_resolve("CANT_SAVE")->value);
+ return (FALSE);
+ } else {
+ if (save_game(saveref)) {
+ return (TRUE);
+ } else {
+ write_text(cstring_resolve("CANT_SAVE")->value);
+ return (FALSE);
+ }
+ }
+}
+
+void restore_game_state() {
+ /* THIS FUNCTION IS CALLED AS A RESULT OF THE PLAYER USING THE 'undo'
+ * COMMAND */
+ int index,
+ counter;
+
+ struct integer_type *current_integer = integer_table;
+ struct function_type *current_function = function_table;
+
+ do {
+ current_function->call_count = current_function->call_count_backup;
+ current_function = current_function->next_function;
+ } while (current_function != NULL);
+
+
+ do {
+ current_integer->value = current_integer->value_backup;
+ current_integer = current_integer->next_integer;
+ } while (current_integer != NULL);
+
+ for (index = 1; index <= objects; index++) {
+ if (object[index]->nosave)
+ continue;
+
+ for (counter = 0; counter < 16; counter++)
+ object[index]->integer[counter] =
+ object[index]->integer_backup[counter];
+
+ object[index]->attributes = object[index]->attributes_backup;
+ object[index]->user_attributes = object[index]->user_attributes_backup;
+ }
+
+ player = player_backup;
+ noun[3] = noun3_backup;
+
+ write_text(cstring_resolve("MOVE_UNDONE")->value);
+ object[HERE]->attributes &= ~1;
+ execute("+top");
+ execute("+look_around");
+ execute("+bottom");
+ TIME->value = FALSE;
+}
+
+void write_text(char *string_buffer) {
+ int index,
+ length;
+
+ if (!strcmp(string_buffer, "tilde")) {
+ g_vm->glk_put_string("~");
+ return;
+ } else if (!strcmp(string_buffer, "caret")) {
+ g_vm->glk_put_string("^");
+ return;
+ }
+
+ length = strlen(string_buffer);
+
+ for (index = 0; index < length; index++) {
+ if (*(string_buffer + index) == '^') {
+ chunk_buffer[index] = '\n';
+ } else if (*(string_buffer + index) == '~') {
+ chunk_buffer[index] = '\"';
+ } else {
+ chunk_buffer[index] = *(string_buffer + index);
+ }
+ }
+
+ chunk_buffer[index] = 0;
+
+ /* PRINT THE CONTENTS OF string_buffer */
+#ifdef NOUNICODE
+ g_vm->glk_put_string(chunk_buffer);
+#else
+ chunk_buffer_uni[(glui32) convert_to_utf32(chunk_buffer)] = 0;
+ g_vm->glk_put_string_uni(chunk_buffer_uni);
+#endif
+}
+
+void jacl_sleep(unsigned int mseconds) {
+ g_system->delayMillis(mseconds);
+}
+
+void status_line() {
+ int cursor, index;
+ winid_t pair_window;
+
+ if (!statuswin) {
+ return;
+ } else {
+ // THERE IS AN EXISTING STATUS WINDOW, MAKE SURE A NEW SIZE HASN'T BEEN
+ // REQUESTED
+ g_vm->glk_window_get_size(statuswin, &status_width, &status_height);
+ if (status_height != (uint)integer_resolve("status_window")->value) {
+ // HEIGHT HAS CHANGED, UPDATE THE WINDOW
+ pair_window = g_vm->glk_window_get_parent(statuswin);
+ g_vm->glk_window_set_arrangement(pair_window, winmethod_Above | winmethod_Fixed, integer_resolve("status_window")->value, statuswin);
+ g_vm->glk_window_get_size(statuswin, &status_width, &status_height);
+ }
+ }
+
+ if (status_height == 0) {
+ // THE STATUS WINDOW CAN'T BE CLOSED, ONLY SET TO HAVE A HEIGHT OF ZERO
+ return;
+ }
+
+ jacl_set_window(statuswin);
+ g_vm->glk_window_clear(statuswin);
+
+ if (execute("+update_status_window") == FALSE) {
+ g_vm->glk_set_style(style_User1);
+
+ /* DISPLAY THE INVERSE STATUS LINE AT THE TOP OF THE SCREEN */
+ for (index = 0; index < (int)status_width; index++) {
+ temp_buffer[index] = ' ';
+ }
+ temp_buffer[index] = 0;
+ write_text(temp_buffer);
+
+ /* PRINT THE LOCATION'S TITLE ON THE LEFT. */
+ g_vm->glk_window_move_cursor(statuswin, 1, 0);
+ write_text(sentence_output(HERE, TRUE));
+
+ /* BUILD THE SCORE/ MOVES STRING */
+ temp_buffer[0] = 0;
+ sprintf(temp_buffer, "Score: %d Moves: %d", SCORE->value, TOTAL_MOVES->value);
+
+ cursor = status_width - strlen(temp_buffer);
+ cursor--;
+ g_vm->glk_window_move_cursor(statuswin, cursor, 0);
+ write_text(temp_buffer);
+ }
+
+ jacl_set_window(mainwin);
+
+}
+
+void newline() {
+ /* START A NEW LINE ON THE SCREEN */
+ write_text("\n");
+}
+
+void more(char *message) {
+ int character;
+
+ jacl_set_window(inputwin);
+
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ newline();
+ }
+
+ g_vm->glk_set_style(style_Emphasized);
+ write_text(message);
+ g_vm->glk_set_style(style_Normal);
+
+ character = get_key();
+
+ if (inputwin == mainwin) newline();
+}
+
+int get_key() {
+ event_t ev;
+
+ g_vm->glk_request_char_event(inputwin);
+
+ while (1) {
+
+ g_vm->glk_select(&ev);
+
+ switch (ev.type) {
+ case evtype_CharInput:
+ if (ev.window == inputwin) {
+ return (ev.val1);
+ }
+ break;
+ }
+ }
+
+}
+
+int get_number(int insist, int low, int high) {
+ char *cx;
+ char commandbuf[256];
+ int response;
+ int gotline;
+ event_t ev;
+
+ status_line();
+
+ sprintf(temp_buffer, cstring_resolve("TYPE_NUMBER")->value, low, high);
+
+ /* THIS LOOP IS IDENTICAL TO THE MAIN COMMAND LOOP IN g_vm->glk_main(). */
+
+ while (1) {
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ jacl_set_window(inputwin);
+ }
+
+ write_text(temp_buffer);
+ jacl_set_window(mainwin);
+
+ g_vm->glk_request_line_event(inputwin, commandbuf, 255, 0);
+
+ gotline = FALSE;
+ while (!gotline) {
+
+ g_vm->glk_select(&ev);
+
+ switch (ev.type) {
+ case evtype_LineInput:
+ if (ev.window == inputwin) {
+ gotline = TRUE;
+ }
+ break;
+
+ case evtype_Arrange:
+ status_line();
+ break;
+ }
+ }
+
+ commandbuf[ev.val1] = '\0';
+ for (cx = commandbuf; *cx == ' '; cx++) { };
+
+ if (validate(cx)) {
+ response = atoi(cx);
+ if (response >= low && response <= high) {
+ return (response);
+ }
+ }
+
+ if (!insist) {
+ return (-1);
+ } else {
+ write_text(cstring_resolve("INVALID_SELECTION")->value);
+ }
+ }
+}
+
+void get_string(char *string_buffer) {
+ char *cx;
+ char commandbuf[256];
+ int gotline;
+ event_t ev;
+
+ status_line();
+
+ /* THIS LOOP IS IDENTICAL TO THE MAIN COMMAND LOOP IN g_vm->glk_main(). */
+
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ jacl_set_window(inputwin);
+ }
+
+ jacl_set_window(mainwin);
+
+ g_vm->glk_request_line_event(inputwin, commandbuf, 255, 0);
+
+ gotline = FALSE;
+ while (!gotline) {
+
+ g_vm->glk_select(&ev);
+
+ switch (ev.type) {
+ case evtype_LineInput:
+ if (ev.window == inputwin) {
+ gotline = TRUE;
+ }
+ break;
+
+ case evtype_Arrange:
+ status_line();
+ break;
+ }
+ }
+
+ commandbuf[ev.val1] = '\0';
+ for (cx = commandbuf; *cx == ' '; cx++) { };
+
+ // COPY UP TO 255 BYTES OF THE ENTERED TEXT INTO THE SUPPLIED STRING
+ strncpy(string_buffer, cx, 255);
+}
+
+int get_yes_or_no() {
+ char *cx;
+ char commandbuf[256];
+ int gotline;
+ event_t ev;
+
+ status_line();
+
+ /* THIS LOOP IS IDENTICAL TO THE MAIN COMMAND LOOP IN g_vm->glk_main(). */
+
+ while (1) {
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ jacl_set_window(inputwin);
+ }
+
+ write_text(cstring_resolve("YES_OR_NO")->value);
+ jacl_set_window(mainwin);
+
+ g_vm->glk_request_line_event(inputwin, commandbuf, 255, 0);
+
+ gotline = FALSE;
+ while (!gotline) {
+
+ g_vm->glk_select(&ev);
+
+ switch (ev.type) {
+ case evtype_LineInput:
+ if (ev.window == inputwin) {
+ gotline = TRUE;
+ }
+ break;
+
+ case evtype_Arrange:
+ status_line();
+ break;
+ }
+ }
+
+ commandbuf[ev.val1] = '\0';
+ for (cx = commandbuf; *cx == ' '; cx++) { };
+
+ // PUSH THE FIRST NON-SPACE CHARACTER TO LOWER FOR COMPARISON
+ // WITH CONSTANT
+ *cx = tolower(*cx);
+
+ if (*cx == cstring_resolve("YES_WORD")->value[0]) {
+ return TRUE;
+ } else if (*cx == cstring_resolve("NO_WORD")->value[0]) {
+ return FALSE;
+ }
+
+ }
+}
+
+char get_character(char *message) {
+ char *cx;
+ char commandbuf[256];
+ int gotline;
+ event_t ev;
+
+ status_line();
+
+ /* THIS LOOP IS IDENTICAL TO THE MAIN COMMAND LOOP IN g_vm->glk_main(). */
+
+ while (1) {
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ jacl_set_window(inputwin);
+ }
+
+ write_text(message);
+ g_vm->glk_request_line_event(inputwin, commandbuf, 255, 0);
+ jacl_set_window(mainwin);
+
+ gotline = FALSE;
+ while (!gotline) {
+
+ g_vm->glk_select(&ev);
+
+ switch (ev.type) {
+ case evtype_LineInput:
+ if (ev.window == inputwin) {
+ gotline = TRUE;
+ }
+ break;
+
+ case evtype_Arrange:
+ status_line();
+ break;
+ }
+ }
+
+ commandbuf[ev.val1] = '\0';
+ for (cx = commandbuf; *cx == ' '; cx++) { };
+
+ return (*cx);
+ }
+}
+
+strid_t open_glk_file(uint usage, uint mode, char *filename) {
+
+ frefid_t file_reference;
+ strid_t stream_reference;
+
+ file_reference = g_vm->glk_fileref_create_by_name(usage, filename, 0);
+
+ if (file_reference) {
+ stream_reference = g_vm->glk_stream_open_file(file_reference, (FileMode)mode, 0);
+
+ if (stream_reference) {
+ /* WE'RE DONE WITH THE FILE REFERENCE NOW THAT THE STREAM
+ * HAS BEEN SUCCESSFULLY OPENED */
+ g_vm->glk_fileref_destroy(file_reference);
+
+ return (stream_reference);
+ }
+ }
+
+ return (strid_t) NULL;
+}
+
+void scripting() {
+ if (script_stream) {
+ write_text(cstring_resolve("SCRIPTING_ALREADY_ON")->value);
+ return;
+ }
+
+ /* IF WE'VE TURNED ON SCRIPTING BEFORE, USE THE SAME FILE REFERENCE;
+ * OTHERWISE, PROMPT THE PLAYER FOR A FILE. */
+ if (!script_fref) {
+ script_fref = g_vm->glk_fileref_create_by_prompt(
+ fileusage_Transcript | fileusage_TextMode,
+ filemode_WriteAppend, 0);
+ if (!script_fref) {
+ write_text(cstring_resolve("CANT_WRITE_SCRIPT")->value);
+ return;
+ }
+ }
+
+ /* OPEN THE TRANSCRIPT FILE */
+ script_stream = g_vm->glk_stream_open_file(script_fref, filemode_WriteAppend, 0);
+
+ if (!script_stream) {
+ write_text(cstring_resolve("CANT_WRITE_SCRIPT")->value);
+ return;
+ }
+ write_text(cstring_resolve("SCRIPTING_ON")->value);
+ g_vm->glk_window_set_echo_stream(mainwin, script_stream);
+ g_vm->glk_put_string_stream(script_stream, "TRANSCRIPT OF: ");
+ g_vm->glk_put_string_stream(script_stream, cstring_resolve("game_title")->value);
+ g_vm->glk_put_string_stream(script_stream, "\n");
+}
+
+void undoing() {
+ if (TOTAL_MOVES->value && strcmp(last_command, cstring_resolve("UNDO_WORD")->value)) {
+ restore_game_state();
+ } else {
+ write_text(cstring_resolve("NO_UNDO")->value);
+ TIME->value = FALSE;
+ }
+}
+
+void walking_thru() {
+ int result, index;
+
+ int length;
+ char script_line[81];
+
+ /* A FILE REFERENCE FOR THE WALKTHRU FILE. */
+ frefid_t walkthru_fref = NULL;
+
+ /* A STREAM FOR THE WALKTHRU FILE, WHEN IT'S OPEN. */
+ strid_t walkthru_stream = NULL;
+
+ walkthru_fref = g_vm->glk_fileref_create_by_prompt(fileusage_Data | fileusage_TextMode, filemode_Read, 0);
+
+ if (!walkthru_fref) {
+ write_text(cstring_resolve("ERROR_READING_WALKTHRU")->value);
+ return;
+ }
+
+ /* OPEN THE WALKTHRU FILE */
+ walkthru_stream = g_vm->glk_stream_open_file(walkthru_fref, filemode_Read, 0);
+
+ if (!walkthru_stream) {
+ write_text(cstring_resolve("ERROR_READING_WALKTHRU")->value);
+ return;
+ }
+
+ walkthru_running = TRUE;
+
+ /* ISSUE ALL THE COMMANDS STORE IN THE WALKTHRU FILE */
+
+ /* WE'RE DONE WITH THE FILE REFERENCE NOW THAT THE STREAM
+ * HAS BEEN SUCCESSFULLY OPENED */
+ g_vm->glk_fileref_destroy(walkthru_fref);
+
+ result = g_vm->glk_get_line_stream(walkthru_stream, text_buffer, (glui32) 80);
+
+ /* SET TO LOWER CASE AND STRIP NEWLINES */
+ length = strlen(text_buffer);
+ for (index = 0; index < length; index++) {
+ if (text_buffer[index] == '\r' ||
+ text_buffer[index] == '\n') {
+ text_buffer[index] = 0;
+ break;
+ }
+ }
+
+ strcpy(script_line, text_buffer);
+
+ while (result && INTERRUPTED->value == FALSE) {
+ /* THERE COULD BE A LOT OF PROCESSING GOING ON HERE BEFORE GETTING
+ * TO THE NEXT EVENT LOOP SO CALL g_vm->glk_tick AFTER EACH LINE READ */
+ g_vm->glk_tick();
+ command_encapsulate();
+ jacl_truncate();
+ if (word[0] != NULL) {
+ custom_error = FALSE;
+
+ execute("+bottom");
+
+ write_text(string_resolve("command_prompt")->value);
+ g_vm->glk_set_style(style_Input);
+ write_text(script_line);
+ newline();
+ g_vm->glk_set_style(style_Normal);
+
+ execute("+top");
+
+ preparse();
+ }
+
+ result = g_vm->glk_get_line_stream(walkthru_stream, text_buffer, (glui32) 80);
+
+ /* SET TO LOWER CASE AND STRIP NEWLINES */
+ length = strlen(text_buffer);
+ for (index = 0; index < length; index++) {
+ if (text_buffer[index] == '\r' ||
+ text_buffer[index] == '\n') {
+ text_buffer[index] = 0;
+ break;
+ }
+ }
+
+ strcpy(script_line, text_buffer);
+ }
+
+ /* CLOSE THE STREAM */
+ g_vm->glk_stream_close(walkthru_stream, NULL);
+
+ /* FINISH UP */
+ walkthru_running = FALSE;
+}
+
+int restore_interaction(char *filename) {
+ frefid_t saveref;
+
+ jacl_set_window(inputwin);
+
+ if (inputwin == promptwin) {
+ g_vm->glk_window_clear(promptwin);
+ newline();
+ }
+
+ if (filename == NULL) {
+ saveref = g_vm->glk_fileref_create_by_prompt(fileusage_SavedGame | fileusage_BinaryMode, filemode_Read, 0);
+ } else {
+ saveref = g_vm->glk_fileref_create_by_name(fileusage_SavedGame | fileusage_BinaryMode, filename, 0);
+ }
+
+ jacl_set_window(mainwin);
+
+ if (!saveref) {
+ write_text(cstring_resolve("CANT_RESTORE")->value);
+ return (FALSE);
+ }
+
+ if (restore_game(saveref, TRUE) == FALSE) {
+ write_text(cstring_resolve("CANT_RESTORE")->value);
+ return (FALSE);
+ } else {
+ return (TRUE);
+ }
+}
+
+glui32 glk_get_bin_line_stream(strid_t file_stream, char *buffer, glui32 max_length) {
+ int character = 0;
+
+ register int index = 0;
+
+ character = g_vm->glk_get_char_stream(file_stream);
+ while (character != -1 && index < (int) max_length) {
+ *(buffer + index) = (char) character;
+ index++;
+ if (character == (int) '\n' ||
+ character == (int) '\r') {
+ break;
+ }
+ character = g_vm->glk_get_char_stream(file_stream);
+ };
+
+ *(buffer + index) = 0;
+
+ return ((glui32) index);
+}
+
+void jacl_set_window(winid_t new_window) {
+ current_window = new_window;
+ g_vm->glk_set_window(new_window);
+}
+
+#ifdef READLINE
+char **command_completion(char *text, int start, int end) {
+ /* READLINE TAB COMPLETION CODE */
+ char **options;
+
+ options = (char **) NULL;
+
+ if (start == 0)
+ options = completion_matches(text, verb_generator);
+ else
+ options = completion_matches(text, object_generator);
+
+ return (options);
+}
+#endif
+
+char *object_generator(char *text, int state) {
+ static int len;
+ static struct command_type *now;
+ struct command_type *to_send;
+ struct name_type *current_name = (struct name_type *) NULL;
+
+ /* IF THIS IS A NEW WORD TO COMPLETE, INITIALIZE NOW. THIS INCLUDES
+ SAVING THE LENGTH OF TEXT FOR EFFICIENCY, AND INITIALIZING THE INDEX
+ VARIABLE TO 0. */
+
+ if (!state) {
+ /* BUILD THE LIST */
+ int index;
+ completion_list = NULL;
+
+ /* LOOP THROUGH ALL THE OBJECTS AND SEE IF THEY ARE IN
+ THE CURRENT LOCATION */
+ for (index = 1; index <= objects; index++) {
+ if (parent_of(HERE, index, UNRESTRICT) && !(object[index]->attributes & NO_TAB)) {
+ /* LOOP THROUGH ALL THE OBJECTS NAMES AND
+ THEM TO THE COMPLETION LIST */
+ current_name = object[index]->first_name;
+ while (current_name) {
+ add_word(current_name->name);
+ current_name = current_name->next_name;
+ }
+ }
+ }
+ now = completion_list;
+ len = strlen(text);
+ }
+
+ while (now != NULL) {
+ if (!strncmp(text, now->word, len)) {
+ to_send = now;
+ now = now->next;
+ return ((char *) to_send->word);
+ }
+ now = now->next;
+ }
+
+ return (char *) NULL;
+}
+
+char *verb_generator(char *text, int state) {
+ static int len;
+ static struct command_type *now;
+ struct command_type *to_send;
+ struct word_type *pointer;
+
+ /* IF THIS IS A NEW WORD TO COMPLETE, INITIALIZE NOW. THIS INCLUDES
+ SAVING THE LENGTH OF TEXT FOR EFFICIENCY, AND INITIALIZING THE INDEX
+ VARIABLE TO 0. */
+
+ if (!state) {
+ /* BUILD THE LIST */
+ completion_list = NULL;
+
+ pointer = grammar_table;
+ while (pointer != NULL) {
+ add_word(pointer->word);
+ pointer = pointer->next_sibling;
+ }
+
+ add_word("walkthru");
+
+ now = completion_list;
+ len = strlen(text);
+ }
+
+ while (now != NULL) {
+ if (!strncmp(text, now->word, len)) {
+ to_send = now;
+ now = now->next;
+
+ /* MALLOC A COPY AND RETURN A POINTER TO THE COPY */
+ return ((char *) to_send->word);
+ }
+ now = now->next;
+ }
+
+ return (char *) NULL;
+}
+
+/* ADD A COPY OF STRING TO A LIST OF STRINGS IF IT IS NOT
+ ALREADY IN THE LIST. THIS IS FOR THE USE OF READLINE */
+void add_word(char *newWord) {
+ static struct command_type *current_word = NULL;
+ struct command_type *previous_word = NULL;
+
+ /* DON'T ADD WORDS SUCH AS *present TO THE LIST*/
+ if (*newWord == '*')
+ return;
+
+ if (current_word != NULL)
+ previous_word = current_word;
+
+ current_word = (struct command_type *) malloc(sizeof(struct command_type));
+
+ if (current_word != NULL) {
+ if (completion_list == NULL) {
+ completion_list = current_word;
+ }
+
+ strncpy(current_word->word, newWord, 40);
+ current_word->word[40] = 0;
+ current_word->next = NULL;
+
+ if (previous_word != NULL) {
+ previous_word->next = current_word;
+ }
+ }
+}
+
+void convert_to_utf8(glui32 *text, int len) {
+ int i, k;
+
+ i = 0;
+ k = 0;
+
+ /*convert UTF-32 to UTF-8 */
+ while (i < len) {
+ if (text[i] < 0x80) {
+ command_buffer[k] = text[i];
+ k++;
+ } else if (text[i] < 0x800) {
+ command_buffer[k ] = (0xC0 | ((text[i] & 0x7C0) >> 6));
+ command_buffer[k + 1] = (0x80 | (text[i] & 0x03F));
+ k = k + 2;
+ } else if (text[i] < 0x10000) {
+ command_buffer[k ] = (0xE0 | ((text[i] & 0xF000) >> 12));
+ command_buffer[k + 1] = (0x80 | ((text[i] & 0x0FC0) >> 6));
+ command_buffer[k + 2] = (0x80 | (text[i] & 0x003F));
+ k = k + 3;
+ } else if (text[i] < 0x200000) {
+ command_buffer[k ] = (0xF0 | ((text[i] & 0x1C0000) >> 18));
+ command_buffer[k + 1] = (0x80 | ((text[i] & 0x03F000) >> 12));
+ command_buffer[k + 2] = (0x80 | ((text[i] & 0x000FC0) >> 6));
+ command_buffer[k + 3] = (0x80 | (text[i] & 0x00003F));
+ k = k + 4;
+ } else {
+ command_buffer[k] = '?';
+ k++;
+ }
+ i++;
+ }
+
+ /* null-terminated string */
+ command_buffer[k] = '\0';
+}
+
+#ifndef NOUNICODE
+int convert_to_utf32(unsigned char *text) {
+ int text_len;
+ int rlen;
+
+ if (!text) {
+ return 0;
+ }
+
+ text_len = strlen((const char *)text);
+
+ if (!text_len) {
+ return 0;
+ }
+
+ rlen = (int) parse_utf8(text, text_len, chunk_buffer_uni, text_len);
+
+ return (rlen);
+}
+
+glui32 parse_utf8(unsigned char *buf, glui32 buflen, glui32 *out, glui32 outlen) {
+ glui32 pos = 0;
+ glui32 outpos = 0;
+ glui32 res;
+ glui32 val0, val1, val2, val3;
+
+ while (outpos < outlen) {
+ if (pos >= buflen)
+ break;
+
+ val0 = buf[pos++];
+
+ if (val0 < 0x80) {
+ res = val0;
+ out[outpos++] = res;
+ continue;
+ }
+
+ if ((val0 & 0xe0) == 0xc0) {
+ if (pos + 1 > buflen) {
+ warning("incomplete two-byte character");
+ break;
+ }
+ val1 = buf[pos++];
+ if ((val1 & 0xc0) != 0x80) {
+ warning("malformed two-byte character");
+ break;
+ }
+ res = (val0 & 0x1f) << 6;
+ res |= (val1 & 0x3f);
+ out[outpos++] = res;
+ continue;
+ }
+
+ if ((val0 & 0xf0) == 0xe0) {
+ if (pos + 2 > buflen) {
+ warning("incomplete three-byte character");
+ break;
+ }
+ val1 = buf[pos++];
+ val2 = buf[pos++];
+ if ((val1 & 0xc0) != 0x80) {
+ warning("malformed three-byte character");
+ break;
+ }
+ if ((val2 & 0xc0) != 0x80) {
+ warning("malformed three-byte character");
+ break;
+ }
+ res = (((val0 & 0xf) << 12) & 0x0000f000);
+ res |= (((val1 & 0x3f) << 6) & 0x00000fc0);
+ res |= (((val2 & 0x3f)) & 0x0000003f);
+ out[outpos++] = res;
+ continue;
+ }
+
+ if ((val0 & 0xf0) == 0xf0) {
+ if ((val0 & 0xf8) != 0xf0) {
+ warning("malformed four-byte character");
+ break;
+ }
+ if (pos + 3 > buflen) {
+ warning("incomplete four-byte character");
+ break;
+ }
+ val1 = buf[pos++];
+ val2 = buf[pos++];
+ val3 = buf[pos++];
+ if ((val1 & 0xc0) != 0x80) {
+ warning("malformed four-byte character");
+ break;
+ }
+ if ((val2 & 0xc0) != 0x80) {
+ warning("malformed four-byte character");
+ break;
+ }
+ if ((val3 & 0xc0) != 0x80) {
+ warning("malformed four-byte character");
+ break;
+ }
+ res = (((val0 & 0x7) << 18) & 0x1c0000);
+ res |= (((val1 & 0x3f) << 12) & 0x03f000);
+ res |= (((val2 & 0x3f) << 6) & 0x000fc0);
+ res |= (((val3 & 0x3f)) & 0x00003f);
+ out[outpos++] = res;
+ continue;
+ }
+
+ warning("malformed character");
+ }
+
+ return outpos;
+}
+#endif
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/jpp.cpp b/engines/glk/jacl/jpp.cpp
new file mode 100644
index 0000000000..f036b75aca
--- /dev/null
+++ b/engines/glk/jacl/jpp.cpp
@@ -0,0 +1,236 @@
+/* jpp.c --- The JACL Preprocessor
+ (C) 2001 Andreas Matthias
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "glk/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+#include "glk/jacl/version.h"
+#include "common/file.h"
+
+namespace Glk {
+namespace JACL {
+
+extern char text_buffer[];
+extern char temp_buffer[];
+extern char *word[];
+extern short int quoted[];
+extern short int punctuated[];
+extern int wp;
+
+extern char user_id[];
+extern char prefix[];
+extern char game_path[];
+extern char game_file[];
+extern char processed_file[];
+
+extern short int encrypted;
+
+extern char include_directory[];
+extern char temp_directory[];
+
+extern char error_buffer[];
+
+int lines_written;
+
+Common::WriteStream *outputFile = NULL;
+Common::SeekableReadStream *inputFile = NULL;
+
+char *stripped_line;
+
+/* INDICATES THAT THE CURRENT '.j2' FILE BEING WORKED
+ * WITH BEING PREPARED FOR RELEASE (DON'T INCLUDE DEBUG LIBARIES) */
+short int release = FALSE;
+
+/* INDICATES THAT THE CURRENT '.j2' FILE BEING WORKED
+ * SHOULD BE ENCRYPTED */
+short int do_encrypt = TRUE;
+
+/* INDICATES THAT THE CURRENT '.processed' FILE BRING WRITTEN SHOULD NOW
+ * HAVE EACH LINE ENCRYPTED AS THE FIRST NONE COMMENT LINE HAS BEEN HIT */
+short int encrypting = FALSE;
+
+int jpp() {
+ // TODO: Find out if this is actually used
+#ifdef UNUSED
+ int game_version;
+
+ lines_written = 0;
+
+ /* CHECK IF GAME FILE IS ALREADY A PROCESSED FILE BY LOOKING FOR THE
+ * STRING "#encrypted" OR "#processed" WITHIN THE FIRST FIVE LINES OF
+ * THE GAME FILE IF SO, RETURN THE GAME FILE AS THE PROCESSED FILE */
+ inputFile = File::open(game_file);
+
+ if (inputFile) {
+ int index = 0;
+ char *result = NULL;
+
+ if (inputFile->read(text_buffer, 1024) != 1024) {
+ sprintf(error_buffer, CANT_OPEN_SOURCE, game_file);
+ return (FALSE);
+ }
+
+ while (inputFile->pos() < inputFile->size() && index < 10) {
+ if (strstr(text_buffer, "#processed")) {
+ /* THE GAME FILE IS ALREADY A PROCESSED FILE, JUST USE IT
+ * DIRECTLY */
+ if (sscanf(text_buffer, "#processed:%d", &game_version)) {
+ if (INTERPRETER_VERSION < game_version) {
+ sprintf(error_buffer, OLD_INTERPRETER, game_version);
+ return (FALSE);
+ }
+ }
+ strcpy(processed_file, game_file);
+
+ return (TRUE);
+ }
+ if (inputFile->read(text_buffer, 1024) != 1024)
+ break;
+
+ index++;
+ }
+
+ delete inputFile;
+ } else {
+ sprintf(error_buffer, NOT_FOUND);
+ return (FALSE);
+ }
+
+ /* SAVE A TEMPORARY FILENAME INTO PROCESSED_FILE */
+ sprintf(processed_file, "%s%s.j2", temp_directory, prefix);
+
+ /* ATTEMPT TO OPEN THE PROCESSED FILE IN THE TEMP DIRECTORY */
+ if ((outputFile = fopen(processed_file, "w")) == NULL) {
+ /* NO LUCK, TRY OPEN THE PROCESSED FILE IN THE CURRENT DIRECTORY */
+ sprintf(processed_file, "%s.j2", prefix);
+ if ((outputFile = fopen(processed_file, "w")) == NULL) {
+ /* NO LUCK, CAN'T CONTINUE */
+ sprintf(error_buffer, CANT_OPEN_PROCESSED, processed_file);
+ return (FALSE);
+ }
+ }
+
+ if (process_file(game_file, (char *) NULL) == FALSE) {
+ return (FALSE);
+ }
+
+ fclose(outputFile);
+#else
+ error("TODO");
+#endif
+
+ /* ALL OKAY, RETURN TRUE */
+ return (TRUE);
+}
+
+int process_file(char *sourceFile1, char *sourceFile2) {
+ char temp_buffer1[1025];
+ char temp_buffer2[1025];
+ Common::File *srcFile = NULL;
+ char *includeFile = NULL;
+
+ /* THIS FUNCTION WILL CREATE A PROCESSED FILE THAT HAS HAD ALL
+ * LEADING AND TRAILING WHITE SPACE REMOVED AND ALL INCLUDED
+ * FILES INSERTED */
+ srcFile = File::openForReading(sourceFile1);
+
+ if (!srcFile) {
+ if (sourceFile2 != NULL) {
+ srcFile = File::openForReading(sourceFile2);
+ if (!srcFile) {
+ sprintf(error_buffer, CANT_OPEN_OR, sourceFile1, sourceFile2);
+ return (FALSE);
+ }
+
+ } else {
+ sprintf(error_buffer, CANT_OPEN_SOURCE, sourceFile1);
+ return (FALSE);
+ }
+ }
+
+ *text_buffer = 0;
+
+ if (srcFile->read(text_buffer, 1024) != 1024) {
+ sprintf(error_buffer, READ_ERROR);
+ delete srcFile;
+ return (FALSE);
+ }
+
+ while (srcFile->pos() < srcFile->size() && *text_buffer != 0) {
+ if (!strncmp(text_buffer, "#include", 8) ||
+ (!strncmp(text_buffer, "#debug", 6) & !release)) {
+ includeFile = strrchr(text_buffer, '"');
+
+ if (includeFile != NULL)
+ *includeFile = 0;
+
+ includeFile = strchr(text_buffer, '"');
+
+ if (includeFile != NULL) {
+ strcpy(temp_buffer1, game_path);
+ strcat(temp_buffer1, includeFile + 1);
+ strcpy(temp_buffer2, include_directory);
+ strcat(temp_buffer2, includeFile + 1);
+ if (process_file(temp_buffer1, temp_buffer2) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ sprintf(error_buffer, BAD_INCLUDE);
+ return (FALSE);
+ }
+ } else {
+ /* STRIP WHITESPACE FROM LINE BEFORE WRITING TO OUTPUTFILE. */
+ stripped_line = stripwhite(text_buffer);
+
+ if (!encrypting && *stripped_line != '#' && *stripped_line != '\0' && do_encrypt & release) {
+ /* START ENCRYPTING FROM THE FIRST NON-COMMENT LINE IN
+ * THE SOURCE FILE */
+ outputFile->writeString("#encrypted\n");
+ encrypting = TRUE;
+ }
+
+ /* ENCRYPT PROCESSED FILE IF REQUIRED */
+ if (encrypting) {
+ jacl_encrypt(stripped_line);
+ }
+
+ outputFile->writeString(stripped_line);
+
+ lines_written++;
+ if (lines_written == 1) {
+ sprintf(temp_buffer, "#processed:%d\n", INTERPRETER_VERSION);
+ outputFile->writeString(temp_buffer);
+ }
+ }
+
+ *text_buffer = 0;
+
+ if (srcFile->read(text_buffer, 1024) != 1024)
+ // EOF HAS BEEN REACHED
+ break;
+ }
+
+ delete srcFile;
+
+ /* ALL OKAY, RETURN TRUE */
+ return (TRUE);
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/language.h b/engines/glk/jacl/language.h
new file mode 100644
index 0000000000..6fef37229b
--- /dev/null
+++ b/engines/glk/jacl/language.h
@@ -0,0 +1,574 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 JACL {
+
+/* THIS FILE CONTAINS ALL THE TEXT OUTPUT INTERNALLY BY THE INTERPRETER.
+ THE STRINGS FOR ANY GIVEN LANGUAGE ARE DIVIDED INTO TWO SECTIONS, ONE
+ THAT CONTAINS THE TEXT OUTPUT DURING REGULAR PLAY AND ONE THAT CONTAINS
+ THE TEXT OUTPUT DURING ERROR CONDITIONS (POSSIBLY BEFORE ANY GIVEN GAME
+ HAS FINISHED LOADING.
+
+ TO TRANSLATE A GAME TO LANGUAGE THAT IS NOT CURRENTLY SUPPORTED YOU
+ NEED TO TRANSLATE english.library AND verbs.library INTO YOUR CHOSEN
+ LANGUAGE.
+
+ THE NATIVE_LANGUAGE CONSTANT BELOW INDICATES WHAT SHOULD BE THE DEFAULT
+ LANGUAGE OF THE INTERPRETER WHEN IT IS COMPILED. THE 'GAME MESSAGES'
+ BLOCK CONTAINS THE SAME STRINGS AS THE FILE english.library (AND OTHER
+ LANGUAGES) AND THESE ARE THE VALUES USED BY THE INTERPRETER IF THOSE
+ CONSTANTS ARE NO DEFINED BY ANY GIVEN GAME.
+
+ THE 'SYSTEM MESSAGES' BLOCK CONTAINS ERROR MESSAGES AND OTHER SYSTEM-
+ LEVEL MESSAGES THAT ARE MOST COMMONLY SEEN DURING THE DEVELOPMENT OF A
+ GAME. THESE MESSAGES CANNOT BE OVERRIDDEN BY ANY SPECIFIC GAME. */
+
+
+#define ENGLISH 1
+#define GERMAN 2
+#define FRENCH 3
+
+#define NATIVE_LANGUAGE ENGLISH
+
+#if NATIVE_LANGUAGE==ENGLISH
+/* GAME MESSAGES */
+#define COMMENT_IGNORED "No transcript running, comment ignored.^"
+#define COMMENT_RECORDED "Comment recorded.^"
+#define YES_WORD "yes"
+#define NO_WORD "no"
+#define YES_OR_NO "^Please enter ~yes~ or ~no~: "
+#define INVALID_SELECTION "Invalid selection.^"
+#define RESTARTING "^Restarting...^"
+#define RETURN_GAME "^Returning to game.^"
+#define SCRIPTING_ON "Scripting on.^"
+#define SCRIPTING_OFF "Scripting off.^"
+#define SCRIPTING_ALREADY_OFF "Scripting already off.^"
+#define SCRIPTING_ALREADY_ON "Scripting already off.^"
+#define CANT_WRITE_SCRIPT "Unable to write to script file.^"
+#define ERROR_READING_WALKTHRU "Error reading walkthru file.^"
+#define BAD_OOPS "You must follow the ~oops~ command with the word you wish to use instead.^"
+#define CANT_CORRECT "I can't correct the last command using ~oops~, sorry.^"
+#define SURE_QUIT "Are you sure you want to quit?^"
+#define SURE_RESTART "Are you sure you want to restart?^"
+#define NOT_CLEVER "It wasn't so clever as to be worth repeating.^"
+#define NO_MOVES "But you haven't done anything yet!^"
+#define TYPE_NUMBER "^Type a number between %d and %d: "
+#define BY "By ~"
+#define REFERRING_TO "~, are you referring to:^"
+#define WALKTHRU_WORD "walkthru"
+#define INFO_WORD "info"
+#define RESTART_WORD "restart"
+#define AGAIN_WORD "again"
+#define SCRIPT_WORD "script"
+#define UNSCRIPT_WORD "unscript"
+#define QUIT_WORD "quit"
+#define UNDO_WORD "undo"
+#define OOPS_WORD "oops"
+#define FROM_WORD "from"
+#define EXCEPT_WORD "except"
+#define FOR_WORD "for"
+#define BUT_WORD "but"
+#define AND_WORD "and"
+#define THEN_WORD "then"
+#define OF_WORD "of"
+#define SHE_WORD "she"
+#define HE_WORD "he"
+#define THAT_WORD "that"
+#define THEM_WORD "them"
+#define THOSE_WORD "those"
+#define THEY_WORD "they"
+#define IT_WORD "it"
+#define ITSELF_WORD "itself"
+#define HIM_WORD "him"
+#define HIMSELF_WORD "himself"
+#define HER_WORD "her"
+#define HERSELF_WORD "herself"
+#define THEMSELVES_WORD "themselves"
+#define YOU_WORD "you"
+#define YOURSELF_WORD "yourself"
+#define ONES_WORD "ones"
+#define NO_MULTI_VERB "You can't refer to multiple objects directly after the word ~%s~.^"
+#define NO_MULTI_START "You can't refer to multiple objects at the start of a command.^"
+#define PERSON_CONCEALING "%s doesn't seem to be carrying any such thing.^"
+#define PERSON_POSSESSIVE "%s isn't about to let you take anything of theirs.^"
+#define CONTAINER_CLOSED "%s is closed.^"
+#define CONTAINER_CLOSED_FEM "%s is closed.^"
+#define FROM_NON_CONTAINER "The word ~%s~ must be followed by a container.^"
+#define DOUBLE_EXCEPT "You can only use the word ~%s~ once per object reference.^"
+#define NONE_HELD "You are not holding anything like that.^"
+#define NO_OBJECTS "I don't see what you are referring to.^"
+#define NO_FILENAME "Save and restore commands must be followed by a filename.^"
+#define MOVE_UNDONE "Previous move undone.^^"
+#define NO_UNDO "Nothing to undo.^"
+#define CANT_SAVE "Unable to save game state to file.^"
+#define CANT_RESTORE "Unable to restore game state from file.^"
+#define GAME_SAVED "Game saved.^"
+#define INCOMPLETE_SENTENCE "The sentence you typed was incomplete.^"
+#define UNKNOWN_OBJECT "You can't see any such thing as ~"
+#define UNKNOWN_OBJECT_END "~.^"
+#define CANT_USE_WORD "You can't use the word ~"
+#define IN_CONTEXT "~ in that context.^"
+#define DONT_SEE "You don't see "
+#define HERE_WORD " here.^"
+#define BAD_SAVED_GAME "Attempt to restore incompatible saved-game file."
+#define ARENT "aren't"
+#define ISNT "isn't"
+#define ARE "are"
+#define IS "is"
+#define DONT "don't"
+#define DOESNT "doesn't"
+#define DO "do"
+#define DOES "does"
+#define SCORE_UP "^[YOUR SCORE JUST WENT UP BY "
+#define POINT " POINT]^"
+#define POINTS " POINTS]^"
+#define STARTING "Starting."
+#define NO_IT "You must have referred to an appropriate noun previously to use the word ~"
+#define NO_IT_END "~.^"
+#define BACK_REFERENCE "You must have referred to a noun previously in the same sentence to use the word ~"
+#define BACK_REFERENCE_END "~.^"
+#define WHEN_YOU_SAY "When you say ~"
+#define MUST_SPECIFY "~, you must specify whether you mean "
+#define OR_WORD " or "
+
+/* SYSTEM MESSAGES */
+#define READ_ERROR "Error reading game file."
+#define OLD_INTERPRETER "Interpreter version is older than game file (v%d), can't continue."
+#define BAD_CURSOR "You can only use the ~cursor~ command in the status window.^"
+#define INCOMPLETE_GRAMMAR "Incomplete grammar statement."
+#define GAME_MODIFIED "Game file modified, reloading.\n"
+#define NOT_INTEGER "In function \"%s\", \"%s\" command requires integer parameter."
+#define NO_NAME_FUNCTION "In line %d, a function must have at least one name."
+#define MAXIMUM_ATTRIBUTES_ERR "In line %d, unable to create attribute \"%s\", maximum number of attributes already defined."
+#define BAD_PLAYER "In function \"%s\", attempt to use object pointer \"player\" while it does not point to an object (%d)."
+#define BAD_PARENT "In function \"%s\", attempt to use the variable 'here' while the variable 'player' does not have a legal parent."
+#define BAD_POINTER "In function \"%s\", attempt to use object pointer \"%s\" that does not point to an object (%d)."
+#define ILLEGAL_LABEL "In line %d, reserved word \"%s\" used as label."
+#define USED_LABEL_INT "In line %d, \"%s\" is already used as a variable label."
+#define USED_LABEL_CINT "In line %d, \"%s\" is already used as an integer constant label."
+#define USED_LABEL_STR "In line %d, \"%s\" is already used as a string label."
+#define USED_LABEL_CSTR "In line %d, \"%s\" is already used as a string constant label."
+#define USED_LABEL_ATT "In line %d, \"%s\" is already used as an attribute label."
+#define USED_LABEL_OBJ "In line %d, \"%s\" is already used as an object or location label."
+#define NO_OBJECT_ERR "In line %d, property \"%s\" defined before first object or location."
+#define BAD_INCLUDE "'#include' directive must be followed by file name enclosed in double quotes."
+#define BAD_PARAMETER "Unknown or inappropriate type of container '%s' associated with parameter '%s'."
+#define BAD_VALUE "Value '%s' cannot be stored in container '%s'."
+#define NO_MEDIA "WebJACL: Media file \"%s\" not found, external media support disabled.\n"
+#define MEDIA_REGISTERED "WebJACL: Registered %d media.\n"
+#define CLEANING_UP "WebJACL: Cleaning up...\n"
+#define NO_GAME "No game file specified, can't continue."
+#define NO_PORT "WebJACL: No port number specified (-p <number>), using default port %d.\n"
+#define WEBJACL_CONFIGURED "WebJACL server configured on %s:%d\n"
+#define NOT_FOUND "Unable to open game file, can't continue."
+#define CANT_OPEN "Unable to open processed file \"%s\", can't continue."
+#define CANT_RUN "A JACL game must contain at least one object (to represent the player), and at least one location (for the player to start in).^"
+#define NO_PLAYER "The object pointer 'player' does not point to an object.^"
+#define SELF_REFERENCE "In function \"%s\", reference to object \"%s\" whose parent is itself."
+#define EXECUTING "Executing function \"%s\".\n"
+#define NO_WHILE "In function \"%s\", 'endwhile' command without matching 'while' command."
+#define NO_ITERATE "In function \"%s\", 'enditerate' command without matching 'iterate' command."
+#define NO_UPDATE "In function \"%s\", 'endupdate' command without matching 'update' command."
+#define NO_REPEAT "In function \"%s\", 'until' command without matching 'repeat' command."
+#define NO_LOOP "In function \"%s\", 'endloop' command without matching 'loop' command."
+#define UNDEFINED_FUNCTION "In function \"%s\", attempt to execute undefined function \"%s\"."
+#define DIVIDE_BY_ZERO "In function \"%s\", division by zero error."
+#define ILLEGAL_OPERATOR "In function \"%s\", illegal operator \"%s\"."
+#define UNKNOWN_COMMAND "In function \"%s\", unknown command \"%s\"."
+#define STACK_OVERFLOW "Stack overflow."
+#define ILLEGAL_OPERATOR "In function \"%s\", illegal operator \"%s\"."
+#define OUT_OF_RANGE "In function \"%s\", element \"%s\" out of range (%d)."
+#define GLOBAL_SELF "Reference to 'self' from global function \"%s\"."
+#define NON_GLOBAL_FIRST "In line %d, non-global function before object or location."
+#define MAXIMUM_EXCEEDED "Maximum number of objects exceeded, can't continue."
+#define ERROR_DETECTED "1 error detected."
+#define ERRORS_DETECTED "%d errors detected."
+#define UNKOWN_OBJECT_RUN "In function \"%s\", reference to unknown object \"%s\"."
+#define UNKNOWN_FUNCTION_RUN "Attempt to execute unknown function \"%s\"."
+#define UNKNOWN_KEYWORD_ERR "In line %d, unknown keyword \"%s\"."
+#define UNKNOWN_ATTRIBUTE_ERR "In line %d, unknown attribute \"%s\"."
+#define UNKNOWN_VALUE_ERR "In line %d, unable to resolve value \"%s\"."
+#define UNKNOWN_ATTRIBUTE_RUN "In function \"%s\", reference to unknown attribute \"%s\"."
+#define INSUFFICIENT_PARAMETERS_RUN "In function \"%s\", \"%s\" command with insufficient parameters."
+#define INSUFFICIENT_PARAMETERS_ERR "In line %d, \"%s\" keyword with insufficient parameters."
+#define UNDEFINED_ITEM_ERR "In line %d, reference to undefined item \"%s\"."
+#define UNDEFINED_ITEM_RUN "In function \"%s\", reference to undefined object \"%s\"."
+#define UNDEFINED_DIRECTION_RUN "In function \"%s\", reference to undefined direction \"%s\"."
+#define UNKNOWN_SCOPE_RUN "In function \"%s\", reference to unknown scope \"%s\"."
+#define UNDEFINED_STRING_RUN "In function \"%s\", reference to undefined string \"%s\"."
+#define UNDEFINED_CONTAINER_RUN "In function \"%s\", reference to undefined container \"%s\"."
+#define OUT_OF_MEMORY "Out of memory, can't continue."
+#define CANT_OPEN_PROCESSED "Unable to open output file \"%s\" for writing, can't continue."
+#define CANT_OPEN_OR "Unable to open source file \"%s\" or \"%s\", can't continue."
+#define CANT_OPEN_SOURCE "Unable to open source file \"%s\", can't continue."
+#endif
+
+#if NATIVE_LANGUAGE==FRENCH
+/* GAME MESSAGES */
+#define COMMENT_IGNORED "Pas de transcription en cours, commentaire ignoré.^"
+#define COMMENT_RECORDED "Commentaire enregistré.^"
+#define YES_WORD "oui"
+#define NO_WORD "no"
+#define YES_OR_NO "^Merci d'entrer ~oui~ ou ~non~: "
+#define INVALID_SELECTION "Sélection invalide.^"
+#define RESTARTING "^En train de recommencer...^"
+#define RETURN_GAME "^Retour au jeu.^"
+#define SCRIPTING_ON "Début de transcription.^"
+#define SCRIPTING_OFF "Fin de transcription.^"
+#define SCRIPTING_ALREADY_OFF "Transcription déjà terminée.^"
+#define SCRIPTING_ALREADY_ON "Transcription déjà en cours.^"
+#define CANT_WRITE_SCRIPT "Impossible d'écrire le fichier de transcription.^"
+#define ERROR_READING_WALKTHRU "Erreur lors de la lecture du fichier de solution.^"
+#define BAD_OOPS "Vous devez faire suivre la commande ~oops~ par le mot que vous souhaitez remplacer.^"
+#define CANT_CORRECT "Désolé, je ne peux corriger la dernière commande en utilisant ~oops~.^"
+#define SURE_QUIT "Êtes-vous certain de vouloir quitter ?^"
+#define SURE_RESTART "Êtes-vous certain de vouloir recommencer ?^"
+#define NOT_CLEVER "Ce n'était pas si intelligent au point de vouloir répéter cela.^"
+#define NO_MOVES "Mais vous n'avez rien fait pour le moment !^"
+#define TYPE_NUMBER "^Entrez un nombre entre %d et %d: "
+#define BY "By ~"
+#define REFERRING_TO "Faites-vous référence à :^"
+#define WALKTHRU_WORD "solution"
+#define INFO_WORD "info"
+#define RESTART_WORD "recommencer"
+#define AGAIN_WORD "encore"
+#define SCRIPT_WORD "script"
+#define UNSCRIPT_WORD "unscript"
+#define QUIT_WORD "quitter"
+#define UNDO_WORD "annuler"
+#define OOPS_WORD "oops"
+#define FROM_WORD "depuis"
+#define EXCEPT_WORD "excepté"
+#define FOR_WORD "pour"
+#define BUT_WORD "mais"
+#define AND_WORD "et"
+#define THEN_WORD "puis"
+#define OF_WORD "de"
+#define SHE_WORD "elle"
+#define HE_WORD "il"
+#define THAT_WORD "ce"
+#define THEM_WORD "ces"
+#define THOSE_WORD "ceux"
+#define THEY_WORD "ils"
+#define IT_WORD "il"
+#define ITSELF_WORD "lui-même"
+#define HIM_WORD "him"
+#define HIMSELF_WORD "lui-même"
+#define HER_WORD "her"
+#define HERSELF_WORD "elle-même"
+#define THEMSELVES_WORD "eux-même"
+#define YOU_WORD "you"
+#define YOURSELF_WORD "yourself"
+#define ONES_WORD "ceux"
+#define NO_MULTI_VERB "Vous ne pouvez vous référer à de multiples objets après le mot \"%s\".^"
+#define NO_MULTI_START "Vous ne pouvez vous référer à de multiples objets au début d'une commande.^"
+#define PERSON_CONCEALING "%s ne semble par porter cela.^"
+#define PERSON_POSSESSIVE "%s ne vous laissera pas lui prendre ses affaires.^"
+#define CONTAINER_CLOSED "%s est fermé.^"
+#define CONTAINER_CLOSED_FEM "%s est fermé.^"
+#define FROM_NON_CONTAINER "Le mot \"%s\" doit être suivi par un contenant.^"
+#define DOUBLE_EXCEPT "Vous ne pouvez utiliser le mot \"%s\" qu'une fois par référence d'objet.^"
+#define NONE_HELD "Vous ne portez rien de cela.^"
+#define NO_OBJECTS "Je ne comprends pas ce à quoi vous vous réferez.^"
+#define NO_FILENAME "Les commandes de sauvegarde et lecture doivent être suivies par un nom de fichier.^"
+#define MOVE_UNDONE "Le tour précédent a été annulé.^^"
+#define NO_UNDO "Il n'y a rien à annuler.^"
+#define CANT_SAVE "Impossible de sauvegarder l'état du jeu sur fichier.^"
+#define CANT_RESTORE "Impossible de récupérer l'état du jeu depuis le fichier.^"
+#define GAME_SAVED "Jeu sauvegardé.^"
+#define INCOMPLETE_SENTENCE "La phrase que vous avez tapée n'est pas complète.^"
+#define UNKNOWN_OBJECT "Vous ne pouvez voir ~"
+#define UNKNOWN_OBJECT_END "~.^"
+#define CANT_USE_WORD "Vous ne pouvez utiliser le mot ~"
+#define IN_CONTEXT "~ dans ce contexte.^"
+#define DONT_SEE "Vous ne voyez pas "
+#define HERE_WORD " ici.^"
+#define BAD_SAVED_GAME "Ficher de sauvegarde incompatible."
+#define ARENT "ne sont pas"
+#define ISNT "n'est pas"
+#define ARE "sont"
+#define IS "est"
+#define DONT "ne font pas"
+#define DOESNT "ne fait pas"
+#define DO "font"
+#define DOES "fait"
+#define SCORE_UP "^[VOTRE SCORE VIENT D'AUGMENTER DE "
+#define POINT " POINT]^"
+#define POINTS " POINTS]^"
+#define STARTING "Démarrage."
+#define NO_IT "Vous devez préalablement avoir fait référence à un nom reconnu pour pouvoir utiliser le mot ~"
+#define NO_IT_END "~.^"
+#define BACK_REFERENCE "Vous devez préalablement avoir fait référence à un nom reconnu dans la même phrase pour pouvoir utiliser le mot ~"
+#define BACK_REFERENCE_END "~.^"
+#define WHEN_YOU_SAY "When you say ~"
+#define MUST_SPECIFY "~, vous devez spécifier si vous voulez dire "
+#define OR_WORD " ou "
+
+/* SYSTEM MESSAGES */
+#define READ_ERROR "Erreur de lecture du fichier de jeu."
+#define OLD_INTERPRETER "La version de l'interpréteur est plus ancienne que le fichier de jeu (v%d), impossible de continuer."
+#define BAD_CURSOR "You can only use the ~cursor~ command in the status window.^"
+#define INCOMPLETE_GRAMMAR "Incomplete grammar statement."
+#define GAME_MODIFIED "Game file modified, reloading.\n"
+#define NOT_INTEGER "In function \"%s\", \"%s\" command requires integer parameter."
+#define NO_NAME_FUNCTION "In line %d, a function must have at least one name."
+#define MAXIMUM_ATTRIBUTES_ERR "In line %d, unable to create attribute \"%s\", maximum number of attributes already defined."
+#define BAD_PLAYER "In function \"%s\", attempt to use object pointer \"player\" while it does not point to an object (%d)."
+#define BAD_PARENT "In function \"%s\", attempt to use the variable 'here' while the variable 'player' does not have a legal parent."
+#define BAD_POINTER "In function \"%s\", attempt to use object pointer \"%s\" that does not point to an object (%d)."
+#define ILLEGAL_LABEL "In line %d, reserved word \"%s\" used as label."
+#define USED_LABEL_INT "In line %d, \"%s\" is already used as a variable label."
+#define USED_LABEL_CINT "In line %d, \"%s\" is already used as an integer constant label."
+#define USED_LABEL_STR "In line %d, \"%s\" is already used as a string label."
+#define USED_LABEL_CSTR "In line %d, \"%s\" is already used as a string constant label."
+#define USED_LABEL_ATT "In line %d, \"%s\" is already used as an attribute label."
+#define USED_LABEL_OBJ "In line %d, \"%s\" is already used as an object or location label."
+#define NO_OBJECT_ERR "In line %d, property \"%s\" defined before first object or location."
+#define BAD_INCLUDE "'#include' directive must be followed by file name enclosed in double quotes."
+#define BAD_PARAMETER "Unknown or inappropriate type of container '%s' associated with parameter '%s'."
+#define BAD_VALUE "Value '%s' cannot be stored in container '%s'."
+#define NO_MEDIA "WebJACL: Media file \"%s\" not found, external media support disabled.\n"
+#define MEDIA_REGISTERED "WebJACL: Registered %d media.\n"
+#define CLEANING_UP "WebJACL: Cleaning up...\n"
+#define NO_GAME "No game file specified, can't continue."
+#define NO_PORT "WebJACL: No port number specified (-p <number>), using default port %d.\n"
+#define WEBJACL_CONFIGURED "WebJACL server configured on %s:%d\n"
+#define NOT_FOUND "Unable to open game file, can't continue."
+#define CANT_OPEN "Unable to open processed file \"%s\", can't continue."
+#define CANT_RUN "A JACL game must contain at least one object (to represent the player), and at least one location (for the player to start in).^"
+#define NO_PLAYER "The object pointer 'player' does not point to an object.^"
+#define SELF_REFERENCE "In function \"%s\", reference to object \"%s\" whose parent is itself."
+#define EXECUTING "Executing function \"%s\".\n"
+#define NO_WHILE "In function \"%s\", 'endwhile' command without matching 'while' command."
+#define NO_ITERATE "In function \"%s\", 'enditerate' command without matching 'iterate' command."
+#define NO_UPDATE "In function \"%s\", 'endupdate' command without matching 'update' command."
+#define NO_REPEAT "In function \"%s\", 'until' command without matching 'repeat' command."
+#define NO_LOOP "In function \"%s\", 'endloop' command without matching 'loop' command."
+#define UNDEFINED_FUNCTION "In function \"%s\", attempt to execute undefined function \"%s\"."
+#define DIVIDE_BY_ZERO "In function \"%s\", division by zero error."
+#define ILLEGAL_OPERATOR "In function \"%s\", illegal operator \"%s\"."
+#define UNKNOWN_COMMAND "In function \"%s\", unknown command \"%s\"."
+#define STACK_OVERFLOW "Stack overflow."
+#define ILLEGAL_OPERATOR "In function \"%s\", illegal operator \"%s\"."
+#define OUT_OF_RANGE "In function \"%s\", element \"%s\" out of range (%d)."
+#define GLOBAL_SELF "Reference to 'self' from global function \"%s\"."
+#define NON_GLOBAL_FIRST "In line %d, non-global function before object or location."
+#define MAXIMUM_EXCEEDED "Maximum number of objects exceeded, can't continue."
+#define ERROR_DETECTED "1 error detected."
+#define ERRORS_DETECTED "%d errors detected."
+#define UNKOWN_OBJECT_RUN "In function \"%s\", reference to unknown object \"%s\"."
+#define UNKNOWN_FUNCTION_RUN "Attempt to execute unknown function \"%s\"."
+#define UNKNOWN_KEYWORD_ERR "In line %d, unknown keyword \"%s\"."
+#define UNKNOWN_ATTRIBUTE_ERR "In line %d, unknown attribute \"%s\"."
+#define UNKNOWN_VALUE_ERR "In line %d, unable to resolve value \"%s\"."
+#define UNKNOWN_ATTRIBUTE_RUN "In function \"%s\", reference to unknown attribute \"%s\"."
+#define INSUFFICIENT_PARAMETERS_RUN "In function \"%s\", \"%s\" command with insufficient parameters."
+#define INSUFFICIENT_PARAMETERS_ERR "In line %d, \"%s\" keyword with insufficient parameters."
+#define UNDEFINED_ITEM_ERR "In line %d, reference to undefined item \"%s\"."
+#define UNDEFINED_ITEM_RUN "In function \"%s\", reference to undefined object \"%s\"."
+#define UNDEFINED_DIRECTION_RUN "In function \"%s\", reference to undefined direction \"%s\"."
+#define UNKNOWN_SCOPE_RUN "In function \"%s\", reference to unknown scope \"%s\"."
+#define UNDEFINED_STRING_RUN "In function \"%s\", reference to undefined string \"%s\"."
+#define UNDEFINED_CONTAINER_RUN "In function \"%s\", reference to undefined container \"%s\"."
+#define OUT_OF_MEMORY "Out of memory, can't continue."
+#define CANT_OPEN_PROCESSED "Unable to open output file \"%s\" for writing, can't continue."
+#define CANT_OPEN_OR "Unable to open source file \"%s\" or \"%s\", can't continue."
+#define CANT_OPEN_SOURCE "Unable to open source file \"%s\", can't continue."
+#endif
+
+#if NATIVE_LANGUAGE==GERMAN
+/* GAME MESSAGES */
+#define COMMENT_IGNORED "No transcript running, comment ignored.^"
+#define COMMENT_RECORDED "Comment recorded.^"
+#define YES_WORD "yes"
+#define NO_WORD "no"
+#define YES_OR_NO "^Please enter ~yes~ or ~no~: "
+#define YES_WORD "yes"
+#define NO_WORD "no"
+#define INVALID_SELECTION "Invalid selection.^"
+#define RESTARTING "^Restarting...^"
+#define RETURN_GAME "^Returning to game.^"
+#define SCRIPTING_ON "Scripting on.^"
+#define SCRIPTING_OFF "Scripting off.^"
+#define SCRIPTING_ALREADY_OFF "Scripting already off.^"
+#define SCRIPTING_ALREADY_ON "Scripting already off.^"
+#define CANT_WRITE_SCRIPT "Unable to write to script file.^"
+#define ERROR_READING_WALKTHRU "Error reading walkthru file.^"
+#define BAD_OOPS "You must follow the ~oops~ command with the word you wish to use instead.^"
+#define CANT_CORRECT "I can't correct the last command using ~oops~, sorry.^"
+#define SURE_QUIT "Are you sure you want to quit?^"
+#define SURE_RESTART "Are you sure you want to restart?^"
+#define NOT_CLEVER "It wasn't so clever as to be worth repeating.^"
+#define NO_MOVES "But you haven't done anything yet!^"
+#define TYPE_NUMBER "^Type a number between %d and %d: "
+#define BY "By ~"
+#define REFERRING_TO "~, are you referring to:^"
+#define WALKTHRU_WORD "walkthru"
+#define INFO_WORD "info"
+#define RESTART_WORD "restart"
+#define AGAIN_WORD "again"
+#define SCRIPT_WORD "script"
+#define UNSCRIPT_WORD "unscript"
+#define QUIT_WORD "quit"
+#define UNDO_WORD "undo"
+#define OOPS_WORD "oops"
+#define FROM_WORD "from"
+#define EXCEPT_WORD "except"
+#define FOR_WORD "for"
+#define BUT_WORD "but"
+#define AND_WORD "and"
+#define THEN_WORD "then"
+#define OF_WORD "of"
+#define SHE_WORD "she"
+#define HE_WORD "he"
+#define THAT_WORD "that"
+#define THEM_WORD "them"
+#define THOSE_WORD "those"
+#define THEY_WORD "they"
+#define IT_WORD "it"
+#define ITSELF_WORD "itself"
+#define HIM_WORD "him"
+#define HIMSELF_WORD "himself"
+#define HER_WORD "her"
+#define HERSELF_WORD "herself"
+#define THEMSELVES_WORD "themselves"
+#define YOU_WORD "you"
+#define YOURSELF_WORD "yourself"
+#define ONES_WORD "ones"
+#define NO_MULTI_VERB "You can't refer to multiple objects directly after the word ~%s~.^"
+#define NO_MULTI_START "You can't refer to multiple objects at the start of a command.^"
+#define PERSON_CONCEALING "%s doesn't seem to be carrying any such thing.^"
+#define PERSON_POSSESSIVE "%s isn't about to let you take anything of theirs.^"
+#define CONTAINER_CLOSED "%s is closed.^"
+#define CONTAINER_CLOSED_FEM "%s is closed.^"
+#define FROM_NON_CONTAINER "The word ~%s~ must be followed by a container.^"
+#define DOUBLE_EXCEPT "You can only use the word ~%s~ once per object reference.^"
+#define NONE_HELD "You are not holding anything like that.^"
+#define NO_OBJECTS "I don't see what you are referring to.^"
+#define NO_FILENAME "Save and restore commands must be followed by a filename.^"
+#define MOVE_UNDONE "Previous move undone.^^"
+#define NO_UNDO "Nothing to undo.^"
+#define CANT_SAVE "Unable to save game state to file.^"
+#define CANT_RESTORE "Unable to restore game state from file.^"
+#define GAME_SAVED "Game saved.^"
+#define INCOMPLETE_SENTENCE "The sentence you typed was incomplete.^"
+#define UNKNOWN_OBJECT "You can't see any such thing as ~"
+#define UNKNOWN_OBJECT_END "~.^"
+#define CANT_USE_WORD "You can't use the word ~"
+#define IN_CONTEXT "~ in that context.^"
+#define DONT_SEE "You don't see "
+#define HERE_WORD " here.^"
+#define BAD_SAVED_GAME "Attempt to restore incompatible saved-game file."
+#define ARENT "aren't"
+#define ISNT "isn't"
+#define ARE "are"
+#define IS "is"
+#define DONT "don't"
+#define DOESNT "doesn't"
+#define DO "do"
+#define DOES "does"
+#define SCORE_UP "^[YOUR SCORE JUST WENT UP BY "
+#define POINT " POINT]^"
+#define POINTS " POINTS]^"
+#define STARTING "Starting."
+#define NO_IT "You must have referred to an appropriate noun previously to use the word ~"
+#define NO_IT_END "~.^"
+#define BACK_REFERENCE "You must have referred to a noun previously in the same sentence to use the word ~"
+#define BACK_REFERENCE_END "~.^"
+#define WHEN_YOU_SAY "When you say ~"
+#define MUST_SPECIFY "~, you must specify whether you mean "
+#define OR_WORD " or "
+
+/* SYSTEM MESSAGES */
+#define READ_ERROR "Error reading game file."
+#define OLD_INTERPRETER "Interpreter version is older than game file (v%d), can't continue."
+#define BAD_CURSOR "You can only use the ~cursor~ command in the status window.^"
+#define INCOMPLETE_GRAMMAR "Incomplete grammar statement."
+#define GAME_MODIFIED "Game file modified, reloading.\n"
+#define NOT_INTEGER "In function \"%s\", \"%s\" command requires integer parameter."
+#define NO_NAME_FUNCTION "In line %d, a function must have at least one name."
+#define MAXIMUM_ATTRIBUTES_ERR "In line %d, unable to create attribute \"%s\", maximum number of attributes already defined."
+#define BAD_PLAYER "In function \"%s\", attempt to use object pointer \"player\" while it does not point to an object (%d)."
+#define BAD_PARENT "In function \"%s\", attempt to use the variable 'here' while the variable 'player' does not have a legal parent."
+#define BAD_POINTER "In function \"%s\", attempt to use object pointer \"%s\" that does not point to an object (%d)."
+#define ILLEGAL_LABEL "In line %d, reserved word \"%s\" used as label."
+#define USED_LABEL_INT "In line %d, \"%s\" is already used as a variable label."
+#define USED_LABEL_CINT "In line %d, \"%s\" is already used as an integer constant label."
+#define USED_LABEL_STR "In line %d, \"%s\" is already used as a string label."
+#define USED_LABEL_CSTR "In line %d, \"%s\" is already used as a string constant label."
+#define USED_LABEL_ATT "In line %d, \"%s\" is already used as an attribute label."
+#define USED_LABEL_OBJ "In line %d, \"%s\" is already used as an object or location label."
+#define NO_OBJECT_ERR "In line %d, property \"%s\" defined before first object or location."
+#define BAD_INCLUDE "'#include' directive must be followed by file name enclosed in double quotes."
+#define BAD_PARAMETER "Unknown or inappropriate type of container '%s' associated with parameter '%s'."
+#define BAD_VALUE "Value '%s' cannot be stored in container '%s'."
+#define NO_MEDIA "WebJACL: Media file \"%s\" not found, external media support disabled.\n"
+#define MEDIA_REGISTERED "WebJACL: Registered %d media.\n"
+#define CLEANING_UP "WebJACL: Cleaning up...\n"
+#define NO_GAME "No game file specified, can't continue."
+#define NO_PORT "WebJACL: No port number specified (-p <number>), using default port %d.\n"
+#define WEBJACL_CONFIGURED "WebJACL server configured on %s:%d\n"
+#define NOT_FOUND "Unable to open game file, can't continue."
+#define CANT_OPEN "Unable to open processed file \"%s\", can't continue."
+#define CANT_RUN "A JACL game must contain at least one object (to represent the player), and at least one location (for the player to start in).^"
+#define NO_PLAYER "The object pointer 'player' does not point to an object.^"
+#define SELF_REFERENCE "In function \"%s\", reference to object \"%s\" whose parent is itself."
+#define EXECUTING "Executing function \"%s\".\n"
+#define NO_WHILE "In function \"%s\", 'endwhile' command without matching 'while' command."
+#define NO_ITERATE "In function \"%s\", 'enditerate' command without matching 'iterate' command."
+#define NO_UPDATE "In function \"%s\", 'endupdate' command without matching 'update' command."
+#define NO_REPEAT "In function \"%s\", 'until' command without matching 'repeat' command."
+#define NO_LOOP "In function \"%s\", 'endloop' command without matching 'loop' command."
+#define UNDEFINED_FUNCTION "In function \"%s\", attempt to execute undefined function \"%s\"."
+#define DIVIDE_BY_ZERO "In function \"%s\", division by zero error."
+#define ILLEGAL_OPERATOR "In function \"%s\", illegal operator \"%s\"."
+#define UNKNOWN_COMMAND "In function \"%s\", unknown command \"%s\"."
+#define STACK_OVERFLOW "Stack overflow."
+#define ILLEGAL_OPERATOR "In function \"%s\", illegal operator \"%s\"."
+#define OUT_OF_RANGE "In function \"%s\", element \"%s\" out of range (%d)."
+#define GLOBAL_SELF "Reference to 'self' from global function \"%s\"."
+#define NON_GLOBAL_FIRST "In line %d, non-global function before object or location."
+#define MAXIMUM_EXCEEDED "Maximum number of objects exceeded, can't continue."
+#define ERROR_DETECTED "1 error detected."
+#define ERRORS_DETECTED "%d errors detected."
+#define UNKOWN_OBJECT_RUN "In function \"%s\", reference to unknown object \"%s\"."
+#define UNKNOWN_FUNCTION_RUN "Attempt to execute unknown function \"%s\"."
+#define UNKNOWN_KEYWORD_ERR "In line %d, unknown keyword \"%s\"."
+#define UNKNOWN_ATTRIBUTE_ERR "In line %d, unknown attribute \"%s\"."
+#define UNKNOWN_VALUE_ERR "In line %d, unable to resolve value \"%s\"."
+#define UNKNOWN_ATTRIBUTE_RUN "In function \"%s\", reference to unknown attribute \"%s\"."
+#define INSUFFICIENT_PARAMETERS_RUN "In function \"%s\", \"%s\" command with insufficient parameters."
+#define INSUFFICIENT_PARAMETERS_ERR "In line %d, \"%s\" keyword with insufficient parameters."
+#define UNDEFINED_ITEM_ERR "In line %d, reference to undefined item \"%s\"."
+#define UNDEFINED_ITEM_RUN "In function \"%s\", reference to undefined object \"%s\"."
+#define UNDEFINED_DIRECTION_RUN "In function \"%s\", reference to undefined direction \"%s\"."
+#define UNKNOWN_SCOPE_RUN "In function \"%s\", reference to unknown scope \"%s\"."
+#define UNDEFINED_STRING_RUN "In function \"%s\", reference to undefined string \"%s\"."
+#define UNDEFINED_CONTAINER_RUN "In function \"%s\", reference to undefined container \"%s\"."
+#define OUT_OF_MEMORY "Out of memory, can't continue."
+#define CANT_OPEN_PROCESSED "Unable to open output file \"%s\" for writing, can't continue."
+#define CANT_OPEN_OR "Unable to open source file \"%s\" or \"%s\", can't continue."
+#define CANT_OPEN_SOURCE "Unable to open source file \"%s\", can't continue."
+#endif
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/libcsv.cpp b/engines/glk/jacl/libcsv.cpp
new file mode 100644
index 0000000000..408e60b683
--- /dev/null
+++ b/engines/glk/jacl/libcsv.cpp
@@ -0,0 +1,529 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/csv.h"
+
+namespace Glk {
+namespace JACL {
+
+#define VERSION "3.0.0"
+
+//#define SIZE_MAX ((size_t)-1)
+
+#define ROW_NOT_BEGUN 0
+#define FIELD_NOT_BEGUN 1
+#define FIELD_BEGUN 2
+#define FIELD_MIGHT_HAVE_ENDED 3
+
+/*
+ Explanation of states
+ ROW_NOT_BEGUN There have not been any fields encountered for this row
+ FIELD_NOT_BEGUN There have been fields but we are currently not in one
+ FIELD_BEGUN We are in a field
+ FIELD_MIGHT_HAVE_ENDED
+ We encountered a double quote inside a quoted field, the
+ field is either ended or the quote is literal
+*/
+
+#define MEM_BLK_SIZE 128
+
+#define SUBMIT_FIELD(p) \
+ do { \
+ if (!quoted) \
+ entry_pos -= spaces; \
+ if (p->options & CSV_APPEND_NULL) \
+ ((p)->entry_buf[entry_pos+1]) = '\0'; \
+ if (cb1) \
+ cb1(p->entry_buf, entry_pos, data); \
+ pstate = FIELD_NOT_BEGUN; \
+ entry_pos = quoted = spaces = 0; \
+ } while (0)
+
+#define SUBMIT_ROW(p, c) \
+ do { \
+ if (cb2) \
+ cb2(c, data); \
+ pstate = ROW_NOT_BEGUN; \
+ entry_pos = quoted = spaces = 0; \
+ } while (0)
+
+#define SUBMIT_CHAR(p, c) ((p)->entry_buf[entry_pos++] = (c))
+
+static char *csv_errors[] = {"success",
+ "error parsing data while strict checking enabled",
+ "memory exhausted while increasing buffer size",
+ "data size too large",
+ "invalid status code"
+ };
+
+int csv_error(struct csv_parser *p) {
+ /* Return the current status of the parser */
+ return p->status;
+}
+
+char *csv_strerror(int status) {
+ /* Return a textual description of status */
+ if (status >= CSV_EINVALID || status < 0)
+ return csv_errors[CSV_EINVALID];
+ else
+ return csv_errors[status];
+}
+
+int csv_get_opts(struct csv_parser *p) {
+ /* Return the currently set options of parser */
+ if (p == NULL)
+ return -1;
+
+ return p->options;
+}
+
+int csv_set_opts(struct csv_parser *p, unsigned char options) {
+ /* Set the options */
+ if (p == NULL)
+ return -1;
+
+ p->options = options;
+ return 0;
+}
+
+int csv_init(struct csv_parser *p, unsigned char options) {
+ /* Initialize a csv_parser object returns 0 on success, -1 on error */
+ if (p == NULL)
+ return -1;
+
+ p->entry_buf = NULL;
+ p->pstate = ROW_NOT_BEGUN;
+ p->quoted = 0;
+ p->spaces = 0;
+ p->entry_pos = 0;
+ p->entry_size = 0;
+ p->status = 0;
+ p->options = options;
+ p->quote_char = CSV_QUOTE;
+ p->delim_char = CSV_COMMA;
+ p->is_space = NULL;
+ p->is_term = NULL;
+ p->blk_size = MEM_BLK_SIZE;
+ p->malloc_func = NULL;
+ p->realloc_func = realloc;
+ p->free_func = free;
+
+ return 0;
+}
+
+void csv_free(struct csv_parser *p) {
+ /* Free the entry_buffer of csv_parser object */
+ if (p == NULL)
+ return;
+
+ if (p->entry_buf)
+ p->free_func(p->entry_buf);
+
+ p->entry_buf = NULL;
+ p->entry_size = 0;
+
+ return;
+}
+
+int csv_fini(struct csv_parser *p, void (*cb1)(void *, size_t, void *), void (*cb2)(int c, void *), void *data) {
+ /* Finalize parsing. Needed, for example, when file does not end in a newline */
+ int quoted = p->quoted;
+ int pstate = p->pstate;
+ size_t spaces = p->spaces;
+ size_t entry_pos = p->entry_pos;
+
+ if (p == NULL)
+ return -1;
+
+
+ if (p->pstate == FIELD_BEGUN && p->quoted && p->options & CSV_STRICT && p->options & CSV_STRICT_FINI) {
+ /* Current field is quoted, no end-quote was seen, and CSV_STRICT_FINI is set */
+ p->status = CSV_EPARSE;
+ return -1;
+ }
+
+ switch (p->pstate) {
+ case FIELD_MIGHT_HAVE_ENDED:
+ p->entry_pos -= p->spaces + 1; /* get rid of spaces and original quote */
+ /* Fall-through */
+ case FIELD_NOT_BEGUN:
+ case FIELD_BEGUN:
+ quoted = p->quoted, pstate = p->pstate;
+ spaces = p->spaces, entry_pos = p->entry_pos;
+ SUBMIT_FIELD(p);
+ SUBMIT_ROW(p, -1);
+ case ROW_NOT_BEGUN: /* Already ended properly */
+ ;
+ }
+
+ /* Reset parser */
+ p->spaces = p->quoted = p->entry_pos = p->status = 0;
+ p->pstate = ROW_NOT_BEGUN;
+
+ return 0;
+}
+
+void csv_set_delim(struct csv_parser *p, unsigned char c) {
+ /* Set the delimiter */
+ if (p) p->delim_char = c;
+}
+
+void csv_set_quote(struct csv_parser *p, unsigned char c) {
+ /* Set the quote character */
+ if (p) p->quote_char = c;
+}
+
+unsigned char csv_get_delim(struct csv_parser *p) {
+ /* Get the delimiter */
+ return p->delim_char;
+}
+
+unsigned char csv_get_quote(struct csv_parser *p) {
+ /* Get the quote character */
+ return p->quote_char;
+}
+
+void csv_set_space_func(struct csv_parser *p, int (*f)(unsigned char)) {
+ /* Set the space function */
+ if (p) p->is_space = f;
+}
+
+void csv_set_term_func(struct csv_parser *p, int (*f)(unsigned char)) {
+ /* Set the term function */
+ if (p) p->is_term = f;
+}
+
+void csv_set_realloc_func(struct csv_parser *p, void *(*f)(void *, size_t)) {
+ /* Set the realloc function used to increase buffer size */
+ if (p && f) p->realloc_func = f;
+}
+
+void csv_set_free_func(struct csv_parser *p, void (*f)(void *)) {
+ /* Set the free function used to free the buffer */
+ if (p && f) p->free_func = f;
+}
+
+void csv_set_blk_size(struct csv_parser *p, size_t size) {
+ /* Set the block size used to increment buffer size */
+ if (p) p->blk_size = size;
+}
+
+size_t csv_get_buffer_size(struct csv_parser *p) {
+ /* Get the size of the entry buffer */
+ if (p)
+ return p->entry_size;
+ return 0;
+}
+
+static int csv_increase_buffer(struct csv_parser *p) {
+ /* Increase the size of the entry buffer. Attempt to increase size by
+ * p->blk_size, if this is larger than SIZE_MAX try to increase current
+ * buffer size to SIZE_MAX. If allocation fails, try to allocate halve
+ * the size and try again until successful or increment size is zero.
+ */
+
+ size_t to_add = p->blk_size;
+ void *vp;
+
+ if (p->entry_size >= SIZE_MAX - to_add)
+ to_add = SIZE_MAX - p->entry_size;
+
+ if (!to_add) {
+ p->status = CSV_ETOOBIG;
+ return -1;
+ }
+
+ while ((vp = p->realloc_func(p->entry_buf, p->entry_size + to_add)) == NULL) {
+ to_add /= 2;
+ if (!to_add) {
+ p->status = CSV_ENOMEM;
+ return -1;
+ }
+ }
+
+ /* Update entry buffer pointer and entry_size if successful */
+ p->entry_buf = (unsigned char *)vp;
+ p->entry_size += to_add;
+ return 0;
+}
+
+size_t csv_parse(struct csv_parser *p, const void *s, size_t len, void (*cb1)(void *, size_t, void *),
+ void (*cb2)(int c, void *), void *data) {
+ unsigned const char *us = (unsigned const char *)s; /* Access input data as array of unsigned char */
+ unsigned char c; /* The character we are currently processing */
+ size_t pos = 0; /* The number of characters we have processed in this call */
+
+ /* Store key fields into local variables for performance */
+ unsigned char delim = p->delim_char;
+ unsigned char quote = p->quote_char;
+ int (*is_space)(unsigned char) = p->is_space;
+ int (*is_term)(unsigned char) = p->is_term;
+ int quoted = p->quoted;
+ int pstate = p->pstate;
+ size_t spaces = p->spaces;
+ size_t entry_pos = p->entry_pos;
+
+
+ if (!p->entry_buf && pos < len) {
+ /* Buffer hasn't been allocated yet and len > 0 */
+ if (csv_increase_buffer(p) != 0) {
+ p->quoted = quoted, p->pstate = pstate, p->spaces = spaces, p->entry_pos = entry_pos;
+ return pos;
+ }
+ }
+
+ while (pos < len) {
+ /* Check memory usage, increase buffer if neccessary */
+ if (entry_pos == ((p->options & CSV_APPEND_NULL) ? p->entry_size - 1 : p->entry_size)) {
+ if (csv_increase_buffer(p) != 0) {
+ p->quoted = quoted, p->pstate = pstate, p->spaces = spaces, p->entry_pos = entry_pos;
+ return pos;
+ }
+ }
+
+ c = us[pos++];
+
+ switch (pstate) {
+ case ROW_NOT_BEGUN:
+ case FIELD_NOT_BEGUN:
+ if (is_space ? is_space(c) : c == CSV_SPACE || c == CSV_TAB) { /* Space or Tab */
+ continue;
+ } else if (is_term ? is_term(c) : c == CSV_CR || c == CSV_LF) { /* Carriage Return or Line Feed */
+ if (pstate == FIELD_NOT_BEGUN) {
+ SUBMIT_FIELD(p);
+ SUBMIT_ROW(p, (unsigned char)c);
+ } else { /* ROW_NOT_BEGUN */
+ /* Don't submit empty rows by default */
+ if (p->options & CSV_REPALL_NL) {
+ SUBMIT_ROW(p, (unsigned char)c);
+ }
+ }
+ continue;
+ } else if (c == delim) { /* Comma */
+ SUBMIT_FIELD(p);
+ break;
+ } else if (c == quote) { /* Quote */
+ pstate = FIELD_BEGUN;
+ quoted = 1;
+ } else { /* Anything else */
+ pstate = FIELD_BEGUN;
+ quoted = 0;
+ SUBMIT_CHAR(p, c);
+ }
+ break;
+ case FIELD_BEGUN:
+ if (c == quote) { /* Quote */
+ if (quoted) {
+ SUBMIT_CHAR(p, c);
+ pstate = FIELD_MIGHT_HAVE_ENDED;
+ } else {
+ /* STRICT ERROR - double quote inside non-quoted field */
+ if (p->options & CSV_STRICT) {
+ p->status = CSV_EPARSE;
+ p->quoted = quoted, p->pstate = pstate, p->spaces = spaces, p->entry_pos = entry_pos;
+ return pos - 1;
+ }
+ SUBMIT_CHAR(p, c);
+ spaces = 0;
+ }
+ } else if (c == delim) { /* Comma */
+ if (quoted) {
+ SUBMIT_CHAR(p, c);
+ } else {
+ SUBMIT_FIELD(p);
+ }
+ } else if (is_term ? is_term(c) : c == CSV_CR || c == CSV_LF) { /* Carriage Return or Line Feed */
+ if (!quoted) {
+ SUBMIT_FIELD(p);
+ SUBMIT_ROW(p, (unsigned char)c);
+ } else {
+ SUBMIT_CHAR(p, c);
+ }
+ } else if (!quoted && (is_space ? is_space(c) : c == CSV_SPACE || c == CSV_TAB)) { /* Tab or space for non-quoted field */
+ SUBMIT_CHAR(p, c);
+ spaces++;
+ } else { /* Anything else */
+ SUBMIT_CHAR(p, c);
+ spaces = 0;
+ }
+ break;
+ case FIELD_MIGHT_HAVE_ENDED:
+ /* This only happens when a quote character is encountered in a quoted field */
+ if (c == delim) { /* Comma */
+ entry_pos -= spaces + 1; /* get rid of spaces and original quote */
+ SUBMIT_FIELD(p);
+ } else if (is_term ? is_term(c) : c == CSV_CR || c == CSV_LF) { /* Carriage Return or Line Feed */
+ entry_pos -= spaces + 1; /* get rid of spaces and original quote */
+ SUBMIT_FIELD(p);
+ SUBMIT_ROW(p, (unsigned char)c);
+ } else if (is_space ? is_space(c) : c == CSV_SPACE || c == CSV_TAB) { /* Space or Tab */
+ SUBMIT_CHAR(p, c);
+ spaces++;
+ } else if (c == quote) { /* Quote */
+ if (spaces) {
+ /* STRICT ERROR - unescaped double quote */
+ if (p->options & CSV_STRICT) {
+ p->status = CSV_EPARSE;
+ p->quoted = quoted, p->pstate = pstate, p->spaces = spaces, p->entry_pos = entry_pos;
+ return pos - 1;
+ }
+ spaces = 0;
+ SUBMIT_CHAR(p, c);
+ } else {
+ /* Two quotes in a row */
+ pstate = FIELD_BEGUN;
+ }
+ } else { /* Anything else */
+ /* STRICT ERROR - unescaped double quote */
+ if (p->options & CSV_STRICT) {
+ p->status = CSV_EPARSE;
+ p->quoted = quoted, p->pstate = pstate, p->spaces = spaces, p->entry_pos = entry_pos;
+ return pos - 1;
+ }
+ pstate = FIELD_BEGUN;
+ spaces = 0;
+ SUBMIT_CHAR(p, c);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ p->quoted = quoted, p->pstate = pstate, p->spaces = spaces, p->entry_pos = entry_pos;
+ return pos;
+}
+
+size_t csv_write(void *dest, size_t dest_size, const void *src, size_t src_size) {
+ unsigned char *cdest = (unsigned char *)dest;
+ const unsigned char *csrc = (const unsigned char *)src;
+ size_t chars = 0;
+
+ if (src == NULL)
+ return 0;
+
+ if (cdest == NULL)
+ dest_size = 0;
+
+ if (dest_size > 0)
+ *cdest++ = '"';
+ chars++;
+
+ while (src_size) {
+ if (*csrc == '"') {
+ if (dest_size > chars)
+ *cdest++ = '"';
+ if (chars < SIZE_MAX) chars++;
+ }
+ if (dest_size > chars)
+ *cdest++ = *csrc;
+ if (chars < SIZE_MAX) chars++;
+ src_size--;
+ csrc++;
+ }
+
+ if (dest_size > chars)
+ *cdest = '"';
+ if (chars < SIZE_MAX) chars++;
+
+ return chars;
+}
+
+int csv_fwrite(Common::WriteStream *fp, const void *src, size_t src_size) {
+ const unsigned char *csrc = (const unsigned char *)src;
+
+ if (fp == NULL || src == NULL)
+ return 0;
+
+ fp->writeByte('"');
+
+ while (src_size) {
+ if (*csrc == '"') {
+ fp->writeByte('"');
+ }
+ fp->writeByte(*csrc);
+ src_size--;
+ csrc++;
+ }
+
+ fp->writeByte('"');
+ return 0;
+}
+
+size_t csv_write2(void *dest, size_t dest_size, const void *src, size_t src_size, unsigned char quote) {
+ unsigned char *cdest = (unsigned char *)dest;
+ const unsigned char *csrc = (const unsigned char *)src;
+ size_t chars = 0;
+
+ if (src == NULL)
+ return 0;
+
+ if (dest == NULL)
+ dest_size = 0;
+
+ if (dest_size > 0)
+ *cdest++ = quote;
+ chars++;
+
+ while (src_size) {
+ if (*csrc == quote) {
+ if (dest_size > chars)
+ *cdest++ = quote;
+ if (chars < SIZE_MAX) chars++;
+ }
+ if (dest_size > chars)
+ *cdest++ = *csrc;
+ if (chars < SIZE_MAX) chars++;
+ src_size--;
+ csrc++;
+ }
+
+ if (dest_size > chars)
+ *cdest = quote;
+ if (chars < SIZE_MAX) chars++;
+
+ return chars;
+}
+
+int csv_fwrite2(Common::WriteStream *fp, const void *src, size_t src_size, unsigned char quote) {
+ const unsigned char *csrc = (const unsigned char *)src;
+
+ if (fp == NULL || src == NULL)
+ return 0;
+
+ fp->writeByte(quote);
+
+ while (src_size) {
+ if (*csrc == quote) {
+ fp->writeByte(quote);
+ }
+ fp->writeByte(*csrc);
+ src_size--;
+ csrc++;
+ }
+
+ fp->writeByte(quote);
+ return 0;
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/loader.cpp b/engines/glk/jacl/loader.cpp
new file mode 100644
index 0000000000..345aa18aad
--- /dev/null
+++ b/engines/glk/jacl/loader.cpp
@@ -0,0 +1,1775 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+#include "glk/jacl/version.h"
+
+namespace Glk {
+namespace JACL {
+
+/* INDICATES THAT THE CURRENT '.j2' FILE BEING WORKED
+ * WITH IS ENCRYPTED */
+int encrypted = FALSE;
+
+extern char text_buffer[];
+extern char temp_buffer[];
+extern char prefix[];
+extern char error_buffer[];
+extern char *word[];
+extern int quoted[];
+extern int punctuated[];
+extern int wp;
+
+#ifdef GLK
+extern schanid_t sound_channel[];
+#else
+#ifndef __NDS__
+extern struct parameter_type *parameter_table;
+struct parameter_type *current_parameter = NULL;
+struct parameter_type *new_parameter;
+#endif
+#endif
+
+extern struct object_type *object[];
+extern struct integer_type *integer_table;
+extern struct integer_type *integer[];
+extern struct cinteger_type *cinteger_table;
+extern struct string_type *string_table;
+extern struct string_type *cstring_table;
+extern struct attribute_type *attribute_table;
+extern struct function_type *function_table;
+extern struct function_type *executing_function;
+extern struct command_type *completion_list;
+extern struct word_type *grammar_table;
+extern struct synonym_type *synonym_table;
+extern struct filter_type *filter_table;
+
+
+struct string_type *current_string = NULL;
+struct integer_type *current_integer = NULL;
+struct integer_type *last_system_integer = NULL;
+
+extern struct string_type *current_cstring;
+extern struct cinteger_type *current_cinteger;
+
+#ifdef GLK
+extern strid_t game_stream;
+#else
+extern FILE *file;
+#endif
+
+extern int objects;
+extern int integers;
+extern int functions;
+extern int strings;
+extern int player;
+
+extern int it;
+extern int them[];
+extern int her;
+extern int him;
+extern int parent;
+
+extern int noun[];
+
+int value_resolved;
+
+void read_gamefile() {
+ int index,
+ counter,
+ errors;
+#ifdef GLK
+ int result;
+#endif
+ int location_count = 0;
+ int object_count = 0;
+ int line = 0;
+ int self_parent = 0;
+
+ long start_of_file = 0;
+#ifdef GLK
+ glui32 current_file_position;
+#else
+ long current_file_position;
+#endif
+
+ long bit_mask;
+
+ struct filter_type *current_filter = NULL;
+ struct filter_type *new_filter = NULL;
+ struct attribute_type *current_attribute = NULL;
+ struct attribute_type *new_attribute = NULL;
+ struct cinteger_type *resolved_cinteger = NULL;
+ struct synonym_type *current_synonym = NULL;
+ struct synonym_type *new_synonym = NULL;
+ struct function_type *current_function = NULL;
+ struct name_type *current_name = NULL;
+
+ char function_name[81];
+
+ // CREATE SOME SYSTEM VARIABLES
+
+ // THIS IS USED BY JACL FUNCTIONS TO PASS STRING VALUES BACK
+ // TO THE INTERPRETER AS JACL FUNCTION CAN ONLY RETURN
+ // AN INTEGER
+ create_string("return_value", "");
+
+ create_cstring("function_name", "JACL*Internal");
+
+ // THESE ARE THE FIELDS FOR THE CSV PARSER
+ create_cstring("field", "field0");
+ create_cstring("field", "field1");
+ create_cstring("field", "field2");
+ create_cstring("field", "field3");
+ create_cstring("field", "field4");
+ create_cstring("field", "field5");
+ create_cstring("field", "field6");
+ create_cstring("field", "field7");
+ create_cstring("field", "field8");
+ create_cstring("field", "field9");
+ create_cstring("field", "field10");
+ create_cstring("field", "field11");
+ create_cstring("field", "field12");
+ create_cstring("field", "field13");
+ create_cstring("field", "field14");
+ create_cstring("field", "field15");
+ create_cstring("field", "field16");
+ create_cstring("field", "field17");
+ create_cstring("field", "field18");
+ create_cstring("field", "field19");
+
+ create_cinteger("field_count", 0);
+
+ create_integer("compass", 0);
+ // START AT -1 AS TIME PASSES BEFORE THE FIRST PROMPT
+ create_integer("total_moves", -1);
+ create_integer("time", TRUE);
+ create_integer("score", 0);
+ create_integer("display_mode", 0);
+ create_integer("internal_version", J_VERSION);
+ create_integer("max_rand", 100);
+ create_integer("destination", 0);
+ create_integer("interrupted", 0);
+ create_integer("debug", 0);
+ create_integer("graphics_enabled", 0);
+ create_integer("sound_enabled", 0);
+ create_integer("timer_enabled", 0);
+ create_integer("multi_prefix", 0);
+ create_integer("notify", 1);
+ create_integer("debug", 0);
+ create_integer("linebreaks", 1);
+
+ /* STORE THIS SO THE SECOND PASS KNOWS WHERE TO START
+ * SETTING VALUES FROM (EVERYTHING BEFORE THIS IN THE
+ * VARIABLE TABLE IS A SYSTEM VARIABLE */
+ last_system_integer = current_integer;
+
+ /* CREATE SOME SYSTEM CONSTANTS */
+ create_cinteger("graphics_supported", 0);
+ create_cinteger("sound_supported", 0);
+ create_cinteger("timer_supported", 0);
+ create_cinteger("GLK", 0);
+ create_cinteger("CGI", 1);
+ create_cinteger("NDS", 2);
+#ifdef GLK
+ create_cinteger("interpreter", 0);
+#else
+#ifdef __NDS__
+ create_cinteger("interpreter", 2);
+#else
+ create_cinteger("interpreter", 1);
+#endif
+#endif
+
+ /* TEST FOR AVAILABLE FUNCTIONALITY BEFORE EXECUTING ANY JACL CODE */
+
+#ifdef GLK
+ GRAPHICS_SUPPORTED->value = (int)g_vm->glk_gestalt(gestalt_Graphics, 0);
+ GRAPHICS_ENABLED->value = (int)g_vm->glk_gestalt(gestalt_Graphics, 0);
+ SOUND_SUPPORTED->value = (int)g_vm->glk_gestalt(gestalt_Sound, 0);
+ SOUND_ENABLED->value = (int)g_vm->glk_gestalt(gestalt_Sound, 0);
+ TIMER_SUPPORTED->value = (int)g_vm->glk_gestalt(gestalt_Timer, 0);
+ TIMER_ENABLED->value = (int)g_vm->glk_gestalt(gestalt_Timer, 0);
+#else
+ GRAPHICS_SUPPORTED->value = TRUE;
+ GRAPHICS_ENABLED->value = TRUE;
+ SOUND_SUPPORTED->value = TRUE;
+ SOUND_ENABLED->value = TRUE;
+ TIMER_SUPPORTED->value = FALSE;
+ TIMER_ENABLED->value = FALSE;
+#endif
+
+ create_cinteger("true", 1);
+ create_cinteger("false", 0);
+ create_cinteger("null", 0);
+ create_cinteger("nowhere", 0);
+ create_cinteger("heavy", HEAVY);
+ create_cinteger("scenery", SCENERY);
+ create_cinteger("north", NORTH_DIR);
+ create_cinteger("south", SOUTH_DIR);
+ create_cinteger("east", EAST_DIR);
+ create_cinteger("west", WEST_DIR);
+ create_cinteger("northeast", NORTHEAST_DIR);
+ create_cinteger("northwest", NORTHWEST_DIR);
+ create_cinteger("southeast", SOUTHEAST_DIR);
+ create_cinteger("southwest", SOUTHWEST_DIR);
+ create_cinteger("up", UP_DIR);
+ create_cinteger("down", DOWN_DIR);
+ create_cinteger("in", IN_DIR);
+ create_cinteger("out", OUT_DIR);
+ create_cinteger("parent", 0);
+ create_cinteger("quantity", 1);
+ create_cinteger("capacity", 1);
+ create_cinteger("mass", 2);
+ create_cinteger("bearing", 3);
+ create_cinteger("velocity", 4);
+ create_cinteger("next", 5);
+ create_cinteger("previous", 6);
+ create_cinteger("child", 7);
+ create_cinteger("index", 8);
+ create_cinteger("status", 9);
+ create_cinteger("state", 10);
+ create_cinteger("counter", 11);
+ create_cinteger("points", 12);
+ create_cinteger("class", 13);
+ create_cinteger("x", 14);
+ create_cinteger("y", 15);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("volume", 100);
+ create_cinteger("timer", 500);
+
+ set_defaults();
+
+ /* CREATE A DUMMY FUNCTION TO BE USED WHEN AN ERROR MESSAGE
+ IS PRINTED AS A RESULT OF CODE CALLED BY THE INTERPRETER */
+ if ((function_table = (struct function_type *)
+ malloc(sizeof(struct function_type))) == NULL)
+ outofmem();
+ else {
+ current_function = function_table;
+ strcpy(current_function->name, "JACL*Internal");
+ current_function->position = 0;
+ current_function->self = 0;
+ current_function->call_count = 0;
+ current_function->call_count_backup = 0;
+ current_function->next_function = NULL;
+ }
+
+ executing_function = function_table;
+
+ errors = 0;
+ objects = 0;
+ integers = 0;
+ functions = 0;
+ strings = 0;
+
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, start_of_file, seekmode_Start);
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fseek(file, start_of_file, SEEK_SET);
+ fgets(text_buffer, 1024, file);
+#endif
+
+ line++;
+
+ if (!encrypted && strstr(text_buffer, "#encrypted")) {
+ encrypted = TRUE;
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ }
+
+ if (encrypted) jacl_decrypt(text_buffer);
+
+#ifdef GLK
+ while (result) {
+#else
+ while (!feof(file)) {
+#endif
+ encapsulate();
+ if (word[0] == NULL);
+ else if (text_buffer[0] == '{') {
+#ifdef GLK
+ while (result) {
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ while (!feof(file)) {
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ if (!encrypted && strstr(text_buffer, "#encrypted")) {
+ encrypted = TRUE;
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ }
+ if (encrypted) jacl_decrypt(text_buffer);
+ if (text_buffer[0] == '}')
+ break;
+ }
+ }
+ else {
+ if (!strcmp(word[0], "grammar")) {
+ if (word[++wp] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ if (grammar_table == NULL) {
+ if ((grammar_table = (struct word_type *)
+ malloc(sizeof(struct word_type))) == NULL)
+ outofmem();
+ else {
+ strncpy(grammar_table->word, word[wp], 40);
+ grammar_table->word[40] = 0;
+ grammar_table->next_sibling = NULL;
+ grammar_table->first_child = NULL;
+ build_grammar_table(grammar_table);
+ }
+ } else
+ build_grammar_table(grammar_table);
+ }
+ } else if (!strcmp(word[0], "object")
+ || !strcmp(word[0], "location")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (legal_label_check(word[1], line, OBJ_TYPE)) {
+ errors++;
+ } else {
+ objects++;
+
+ if (objects == MAX_OBJECTS) {
+ log_error(MAXIMUM_EXCEEDED, PLUS_STDERR);
+ terminate(47);
+ } else {
+ if ((object[objects] = (struct object_type *)
+ malloc(sizeof(struct object_type))) == NULL)
+ outofmem();
+
+ strncpy(object[objects]->label, word[1], 40);
+
+ object[objects]->label[40] = 0;
+ object[objects]->first_plural = NULL;
+
+ strcpy(object[objects]->described, object[objects]->label);
+ strcpy(object[objects]->inventory, object[objects]->label);
+ strcpy(object[objects]->article, "the");
+ strcpy(object[objects]->definite, "the");
+ object[objects]->attributes = FALSE;
+ object[objects]->user_attributes = FALSE;
+
+ for (counter = 0; counter < 16; counter++)
+ object[objects]->integer[counter] = 0;
+ }
+ object[objects]->nosave = FALSE;
+ }
+ } else if (!strcmp(word[0], "synonym")) {
+ if (word[++wp] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ if ((new_synonym = (struct synonym_type *)
+ malloc(sizeof(struct synonym_type))) == NULL)
+ outofmem();
+ else {
+ if (synonym_table == NULL) {
+ synonym_table = new_synonym;
+ } else {
+ current_synonym->next_synonym = new_synonym;
+ }
+ }
+ current_synonym = new_synonym;
+ strncpy(current_synonym->original, word[wp], 40);
+ current_synonym->original[40] = 0;
+ if (word[++wp] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ strncpy(current_synonym->standard, word[wp], 40);
+ current_synonym->standard[40] = 0;
+ }
+ current_synonym->next_synonym = NULL;
+ }
+ } else if (!strcmp(word[0], "parameter")) {
+#ifndef GLK
+#ifndef __NDS__
+ if (word[2] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ if ((new_parameter = (struct parameter_type *)
+ malloc(sizeof(struct parameter_type))) == NULL)
+ outofmem();
+ else {
+ if (parameter_table == NULL) {
+ parameter_table = new_parameter;
+ } else {
+ current_parameter->next_parameter =
+ new_parameter;
+ }
+ current_parameter = new_parameter;
+ strncpy(current_parameter->name, word[1], 40);
+ current_parameter->name[40] = 0;
+ strncpy(current_parameter->container, word[2], 40);
+ current_parameter->container[40] = 0;
+ current_parameter->next_parameter = NULL;
+ }
+
+ if (word[4] != NULL) {
+ if (validate(word[3]))
+ current_parameter->low = atoi(word[3]);
+ else
+ current_parameter->low = -65535;
+
+ if (validate(word[4]))
+ current_parameter->high = atoi(word[4]);
+ else
+ current_parameter->high = 65535;
+ } else {
+ current_parameter->low = -65535;
+ current_parameter->high = 65535;
+ }
+
+ }
+#endif
+#endif
+ } else if (!strcmp(word[0], "constant")) {
+ if (word[2] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ /* CHECK IF MORE THAN ONE VALUE IS SUPPLIED AND CREATE
+ ADDITIONAL CONSTANTS IF REQUIRED */
+ index = 2;
+
+ while (word[index] != NULL && index < MAX_WORDS) {
+ if (quoted[index] == TRUE || !validate(word[index])) {
+ if (legal_label_check(word[1], line, CSTR_TYPE)) {
+ errors++;
+ } else {
+ create_cstring(word[1], word[index]);
+ }
+ } else {
+ if (legal_label_check(word[1], line, CINT_TYPE)) {
+ errors++;
+ } else {
+ create_cinteger(word[1], value_of(word[index])/*, FALSE */);
+ if (!value_resolved) {
+ unkvalerr(line, index);
+ errors++;
+ }
+ }
+ }
+ index++;
+ }
+ }
+ } else if (!strcmp(word[0], "attribute")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (legal_label_check(word[1], line, ATT_TYPE)) {
+ errors++;
+ } else if (current_attribute != NULL && current_attribute->value == 1073741824) {
+ maxatterr(line, 1);
+ errors++;
+ } else {
+ if ((new_attribute = (struct attribute_type *)
+ malloc(sizeof(struct attribute_type))) == NULL)
+ outofmem();
+ else {
+ if (attribute_table == NULL) {
+ attribute_table = new_attribute;
+ new_attribute->value = 1;
+ } else {
+ current_attribute->next_attribute = new_attribute;
+ new_attribute->value = current_attribute->value * 2;
+ }
+ current_attribute = new_attribute;
+ strncpy(current_attribute->name, word[1], 40);
+ current_attribute->name[40] = 0;
+ current_attribute->next_attribute = NULL;
+ }
+
+ /* CHECK IF MORE THAN ONE VALUE IS SUPPLIED AND CREATE
+ ADDITIONAL CONSTANTS IF REQUIRED */
+ index = 2;
+ while (word[index] != NULL && index < MAX_WORDS) {
+ if (legal_label_check(word[index], line, ATT_TYPE)) {
+ errors++;
+ } else if (current_attribute != NULL && current_attribute->value == 1073741824) {
+ maxatterr(line, index);
+ errors++;
+ } else {
+ if ((new_attribute = (struct attribute_type *)
+ malloc(sizeof(struct attribute_type))) == NULL)
+ outofmem();
+ else {
+ current_attribute->next_attribute = new_attribute;
+ new_attribute->value = current_attribute->value * 2;
+ current_attribute = new_attribute;
+ strncpy(current_attribute->name, word[index], 40);
+ current_attribute->name[40] = 0;
+ current_attribute->next_attribute = NULL;
+ }
+ }
+ index++;
+ }
+ }
+ } else if (!strcmp(word[0], "string")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (legal_label_check(word[1], line, STR_TYPE)) {
+ errors++;
+ } else {
+ if (word[2] == NULL) {
+ create_string(word[1], "");
+ } else {
+ create_string(word[1], word[2]);
+ index = 3;
+ while (word[index] != NULL && index < MAX_WORDS) {
+ create_string(word[1], word[index]);
+ index++;
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "filter")) {
+ if (word[++wp] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ if ((new_filter = (struct filter_type *)
+ malloc(sizeof(struct filter_type))) == NULL)
+ outofmem();
+ else {
+ if (filter_table == NULL) {
+ filter_table = new_filter;
+ } else {
+ current_filter->next_filter = new_filter;
+ }
+ current_filter = new_filter;
+ strncpy(current_filter->word, word[wp], 40);
+ current_filter->word[40] = 0;
+ current_filter->next_filter = NULL;
+ }
+ }
+ } else if (!strcmp(word[0], "string_array")) {
+ if (word[2] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (legal_label_check(word[1], line, STR_TYPE)) {
+ errors++;
+ } else {
+ int x;
+
+ index = value_of(word[2], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, 2);
+ errors++;
+ }
+
+ for (x = 0; x < index; x++) {
+ create_string(word[1], word[3]);
+ }
+ }
+ } else if (!strcmp(word[0], "integer_array")) {
+ if (word[2] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (legal_label_check(word[1], line, INT_TYPE)) {
+ errors++;
+ } else {
+ int default_value, x;
+
+ if (word[3] != NULL) {
+ default_value = value_of(word[3], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, 3);
+ errors++;
+ }
+ } else {
+ default_value = 0;
+ }
+
+ /* THIS IS THE NUMBER OF ARRAY ELEMENTS TO MAKE */
+ index = value_of(word[2], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, 2);
+ errors++;
+ }
+
+ for (x = 0; x < index; x++) {
+ create_integer(word[1], default_value);
+ }
+ }
+ } else if (!strcmp(word[0], "integer")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (legal_label_check(word[1], line, INT_TYPE)) {
+ errors++;
+ } else {
+ create_integer(word[1], 0);
+
+ /* CHECK IF MORE THAN ONE VALUE IS SUPPLIED AND CREATE
+ ADDITIONAL VARIABLES IF REQUIRED */
+ index = 3;
+ while (word[index] != NULL && index < MAX_WORDS) {
+ create_integer(word[1], 0);
+ index++;
+ }
+ }
+ }
+ }
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+
+ if (!encrypted && strstr(text_buffer, "#encrypted")) {
+ encrypted = TRUE;
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ }
+ if (encrypted) jacl_decrypt(text_buffer);
+ }
+
+ if (errors) {
+ totalerrs(errors);
+ terminate(48);
+ }
+
+ /*************************************************************************
+ * START OF SECOND PASS *
+ *************************************************************************/
+
+ /* IF NO SIZE IS SPECIFIED FOR THE STATUS WINDOW, SET IT TO 1 */
+ if (integer_resolve("status_window") == NULL) {
+ create_integer("status_window", 1);
+ }
+
+ /* IF NO STRING IS SPECIFIED FOR THE COMMAND PROMPT, SET IT TO "^> " */
+ if (string_resolve("command_prompt") == NULL) {
+ create_string("command_prompt", "^> ");
+ }
+
+ /* IF NO STRING IS SPECIFIED FOR THE GAME_TITLE, SET IT TO THE FILENAME */
+ if (cstring_resolve("game_title") == NULL) {
+ create_cstring("game_title", prefix);
+ }
+
+ create_language_constants();
+
+ /* MUST RE-DETERMINE THE POINT IN THE GAME FILE THAT ENCRYPTION STARTS */
+ encrypted = FALSE;
+
+ /* SET CURRENT VARIABLE TO POINT TO THE FIRST USER VARIABLE THAT WAS
+ * CREATED AFTER THE SYSTEM VARIABLES */
+ current_integer = last_system_integer;
+
+ line = 0;
+#ifdef GLK
+ g_vm->glk_stream_set_position(game_stream, start_of_file, seekmode_Start);
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fseek(file, start_of_file, SEEK_SET);
+ fgets(text_buffer, 1024, file);
+#endif
+
+ line++;
+
+ if (!encrypted && strstr(text_buffer, "#encrypted")) {
+ encrypted = TRUE;
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ }
+ if (encrypted) jacl_decrypt(text_buffer);
+
+#ifdef GLK
+ while (result) {
+#else
+ while (!feof(file)) {
+#endif
+ encapsulate();
+ if (word[0] == NULL);
+ else if (text_buffer[0] == '{') {
+ word[wp]++; /* MOVE THE START OF THE FIRST WORD ONLY
+ * TO PAST THE '{'. */
+ if (word[wp][0] == 0) {
+ nofnamerr(line);
+ errors++;
+ } else {
+ while (word[wp] != NULL && wp < MAX_WORDS) {
+ if (word[wp][0] == '+') {
+ strncpy(function_name, word[wp], 80);
+ function_name[80] = 0;
+ self_parent = 0;
+ } else if (word[wp][0] == '*') {
+ char *last_underscore = (char *) NULL;
+
+ /* ALLOW MANUAL NAMING OF ASSOCIATED FUNCTIONS */
+ /* TO GIVE CLASS-LIKE BEHAVIOR */
+ strncpy(function_name, word[wp] + 1, 80);
+ function_name[80] = 0;
+
+ /* LOOK FOR THE FINAL UNDERSCORE AND SEE IF */
+ /* IT IS FOLLOWED BY AN OBJECT LABEL */
+ last_underscore = strrchr(word[wp], '_');
+ if (last_underscore != NULL) {
+ self_parent = object_resolve(last_underscore + 1);
+ } else {
+ self_parent = 0;
+ }
+ } else if (object_count == 0) {
+ nongloberr(line);
+ errors++;
+ } else {
+ strncpy(function_name, word[wp], 59);
+ strcat(function_name, "_");
+ strcat(function_name, object[object_count]->label);
+ self_parent = object_count;
+ }
+ if (function_table == NULL) {
+ if ((function_table = (struct function_type *)
+ malloc(sizeof(struct function_type))) == NULL)
+ outofmem();
+ else {
+ // STORE THE NUMBER OF FUNCTION DEFINED TO
+ // HELP VALIDATE SAVED GAME FILES
+ functions++;
+
+ current_function = function_table;
+ strcpy(current_function->name, function_name);
+#ifdef GLK
+ current_function->position = g_vm->glk_stream_get_position(game_stream);
+#else
+ current_function->position = ftell(file);
+#endif
+ current_function->call_count = 0;
+ current_function->call_count_backup = 0;
+ current_function->self = self_parent;
+ current_function->next_function = NULL;
+ }
+ } else {
+ if ((current_function->next_function =
+ (struct function_type *)
+ malloc(sizeof(struct function_type))) == NULL)
+ outofmem();
+ else {
+ // STORE THE NUMBER OF FUNCTION DEFINED TO
+ // HELP VALIDATE SAVED GAME FILES
+ functions++;
+
+ current_function = current_function->next_function;
+ strcpy(current_function->name, function_name);
+#ifdef GLK
+ current_function->position = g_vm->glk_stream_get_position(game_stream);
+#else
+ current_function->position = ftell(file);
+#endif
+ current_function->call_count = 0;
+ current_function->call_count_backup = 0;
+ current_function->self = self_parent;
+ current_function->next_function = NULL;
+ }
+ }
+ wp++;
+ }
+ }
+
+#ifdef GLK
+ while (result) {
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ while (!feof(file)) {
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+
+ if (!encrypted && strstr(text_buffer, "#encrypted")) {
+ encrypted = TRUE;
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ }
+ if (encrypted) jacl_decrypt(text_buffer);
+ if (text_buffer[0] == '}')
+ break;
+ }
+ }
+ else if (!strcmp(word[0], "string_array")) {
+ } else if (!strcmp(word[0], "integer_array")) {
+ if (word[2] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ int x;
+
+ /* THIS IS THE NUMBER OF ARRAY ELEMENTS TO MAKE */
+ index = value_of(word[2], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, 2);
+ errors++;
+ }
+
+ for (x = 0; x < index; x++) {
+ current_integer = current_integer->next_integer;
+ }
+ }
+ } else if (!strcmp(word[0], "integer")) {
+ if (word[2] != NULL) {
+ current_integer = current_integer->next_integer;
+ current_integer->value = value_of(word[2], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, 2);
+ errors++;
+ }
+ index = 3;
+ while (word[index] != NULL && index < MAX_WORDS) {
+ current_integer = current_integer->next_integer;
+ current_integer->value = value_of(word[index], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, index);
+ errors++;
+ }
+ index++;
+ }
+ } else {
+ current_integer = current_integer->next_integer;
+ current_integer->value = FALSE;
+ }
+
+ /* CONSUME ALL THESE KEYWORDS TO AVOID AN UNKNOWN KEYWORD */
+ /* ERROR DURING THE SECOND PASS (ALL WORK DONE IN FIRST PASS) */
+ } else if (!strcmp(word[0], "constant"));
+ else if (!strcmp(word[0], "string"));
+ else if (!strcmp(word[0], "attribute"));
+ else if (!strcmp(word[0], "parameter"));
+ else if (!strcmp(word[0], "synonym"));
+ else if (!strcmp(word[0], "grammar"));
+ else if (!strcmp(word[0], "filter"));
+ else if (!strcmp(word[0], "has")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else {
+ for (index = 1; word[index] != NULL && index < MAX_WORDS; index++) {
+ if ((bit_mask = attribute_resolve(word[index]))) {
+ object[object_count]->attributes = object[object_count]->attributes | bit_mask;
+ } else if ((bit_mask = user_attribute_resolve(word[index]))) {
+ object[object_count]->user_attributes = object[object_count]->user_attributes | bit_mask;
+ } else {
+ unkatterr(line, index);
+ errors++;
+ }
+ }
+ }
+ } else if (!strcmp(word[0], "object")
+ || !strcmp(word[0], "location")) {
+ object_count++;
+
+ if (!strcmp(word[0], "object")) {
+ object[object_count]->MASS = SCENERY;
+ if (location_count == 0)
+ object[object_count]->PARENT = 0;
+ else
+ object[object_count]->PARENT = location_count;
+ } else {
+ location_count = object_count;
+ object[object_count]->PARENT = 0;
+ object[object_count]->attributes =
+ object[object_count]->attributes | LOCATION;
+ }
+
+
+ if ((object[object_count]->first_name =
+ (struct name_type *) malloc(sizeof(struct name_type)))
+ == NULL)
+ outofmem();
+ else {
+ current_name = object[object_count]->first_name;
+ if (word[2] != NULL) {
+ strncpy(current_name->name, word[2], 40);
+ } else {
+ strncpy(current_name->name, object[object_count]->label, 40);
+ }
+ current_name->name[40] = 0;
+ current_name->next_name = NULL;
+ }
+
+ wp = 3;
+
+ while (word[wp] != NULL && wp < MAX_WORDS) {
+ if ((current_name->next_name = (struct name_type *)
+ malloc(sizeof(struct name_type))) == NULL)
+ outofmem();
+ else {
+ current_name = current_name->next_name;
+ strncpy(current_name->name, word[wp], 40);
+ current_name->name[40] = 0;
+ current_name->next_name = NULL;
+ }
+ wp++;
+ }
+ } else if (!strcmp(word[0], "plural")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else {
+ if ((object[object_count]->first_plural =
+ (struct name_type *) malloc(sizeof(struct name_type)))
+ == NULL)
+ outofmem();
+ else {
+ current_name = object[object_count]->first_plural;
+ strncpy(current_name->name, word[1], 40);
+ current_name->name[40] = 0;
+ current_name->next_name = NULL;
+ }
+
+ wp = 2;
+
+ while (word[wp] != NULL && wp < MAX_WORDS) {
+ if ((current_name->next_name = (struct name_type *)
+ malloc(sizeof(struct name_type))) == NULL)
+ outofmem();
+ else {
+ current_name = current_name->next_name;
+ strncpy(current_name->name, word[wp], 40);
+ current_name->name[40] = 0;
+ current_name->next_name = NULL;
+ }
+ wp++;
+ }
+ }
+ } else if (!strcmp(word[0], "static")) {
+ if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else
+ object[object_count]->nosave = TRUE;
+ } else if (!strcmp(word[0], "player")) {
+ if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else
+ player = object_count;
+ } else if (!strcmp(word[0], "short")) {
+ if (word[2] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else {
+ strncpy(object[object_count]->article, word[1], 10);
+ object[object_count]->article[10] = 0;
+ strncpy(object[object_count]->inventory, word[2], 40);
+ object[object_count]->inventory[40] = 0;
+ }
+ } else if (!strcmp(word[0], "definite")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else {
+ strncpy(object[object_count]->definite, word[1], 10);
+ object[object_count]->definite[10] = 0;
+ }
+ } else if (!strcmp(word[0], "long")) {
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else {
+ strncpy(object[object_count]->described, word[1], 80);
+ object[object_count]->described[80] = 0;
+ }
+ } else if ((resolved_cinteger = cinteger_resolve(word[0])) != NULL) {
+ index = resolved_cinteger->value;
+ if (word[1] == NULL) {
+ noproperr(line);
+ errors++;
+ } else if (object_count == 0) {
+ noobjerr(line);
+ errors++;
+ } else if (!strcmp(word[1], "here")) {
+ object[object_count]->integer[index] = location_count;
+ } else {
+ object[object_count]->integer[index] = value_of(word[1], FALSE);
+ if (!value_resolved) {
+ unkvalerr(line, 1);
+ errors++;
+ }
+ }
+ } else {
+ unkkeyerr(line, 0);
+ errors++;
+ }
+
+#ifdef GLK
+ current_file_position = g_vm->glk_stream_get_position(game_stream);
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ current_file_position = ftell(file);
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+
+ if (!encrypted && strstr(text_buffer, "#encrypted")) {
+ encrypted = TRUE;
+#ifdef GLK
+ result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
+#else
+ fgets(text_buffer, 1024, file);
+#endif
+ line++;
+ }
+ if (encrypted) jacl_decrypt(text_buffer);
+ }
+
+ /* CREATE THE CONSTANT THE RECORDS THE TOTAL NUMBER OF OBJECTS */
+ create_cinteger("objects", objects);
+
+ /* LOOP THROUGH ALL THE OBJECTS AND CALL THEIR CONSTRUCTORS
+ for (index = 1; index <= objects; index++) {
+ strcpy (function_name, "constructor_");
+ strcat (function_name, object[index]->label);
+ execute (function_name);
+ }
+ */
+
+ if (errors) {
+ totalerrs(errors);
+ terminate(48);
+ }
+}
+
+void build_grammar_table(struct word_type *pointer) {
+ do {
+ if (!strcmp(word[wp], pointer->word)) {
+ if (pointer->first_child == NULL && word[wp + 1] != NULL) {
+ if ((pointer->first_child = (struct word_type *)
+ malloc(sizeof(struct word_type)))
+ == NULL)
+ outofmem();
+ else {
+ pointer = pointer->first_child;
+ strncpy(pointer->word, word[++wp], 40);
+ pointer->word[40] = 0;
+ pointer->next_sibling = NULL;
+ pointer->first_child = NULL;
+ }
+ } else {
+ pointer = pointer->first_child;
+ wp++;
+ }
+ } else {
+ if (pointer->next_sibling == NULL) {
+ if ((pointer->next_sibling = (struct word_type *)
+ malloc(sizeof(struct word_type)))
+ == NULL)
+ outofmem();
+ else {
+ pointer = pointer->next_sibling;
+ strncpy(pointer->word, word[wp], 40);
+ pointer->word[40] = 0;
+ pointer->next_sibling = NULL;
+ pointer->first_child = NULL;
+ }
+ } else
+ pointer = pointer->next_sibling;
+ }
+ } while (word[wp] != NULL && wp < MAX_WORDS);
+}
+
+int legal_label_check(char *word, int line, int type) {
+ struct integer_type *integer_pointer = integer_table;
+ struct cinteger_type *cinteger_pointer = cinteger_table;
+ struct string_type *string_pointer = string_table;
+ struct string_type *cstring_pointer = cstring_table;
+ struct attribute_type *attribute_pointer = attribute_table;
+
+ int index;
+
+ if (!strcmp(word, "here") ||
+ !strcmp(word, "player") ||
+ !strcmp(word, "integer") ||
+ !strcmp(word, "arg") ||
+ !strcmp(word, "string_arg") ||
+ !strcmp(word, "arg") ||
+ !strcmp(word, "$word") ||
+ !strcmp(word, "self") ||
+ !strcmp(word, "this") ||
+ !strcmp(word, "noun1") ||
+ !strcmp(word, "noun2") ||
+ !strcmp(word, "noun3") ||
+ !strcmp(word, "noun4") ||
+ !strcmp(word, "objects") ||
+ validate(word)) {
+ sprintf(error_buffer, ILLEGAL_LABEL, line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ }
+
+ if (type == CSTR_TYPE) {
+ if (!strcmp(word, "command_prompt")) {
+ sprintf(error_buffer, USED_LABEL_STR, line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ }
+ }
+
+ while (integer_pointer != NULL && type != INT_TYPE) {
+ if (!strcmp(word, integer_pointer->name)) {
+ sprintf(error_buffer, USED_LABEL_INT, line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ } else
+ integer_pointer = integer_pointer->next_integer;
+ }
+
+
+ while (cinteger_pointer != NULL && type != CINT_TYPE) {
+ if (!strcmp(word, cinteger_pointer->name)) {
+ sprintf(error_buffer, USED_LABEL_CINT, line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ } else
+ cinteger_pointer = cinteger_pointer->next_cinteger;
+ }
+
+ while (string_pointer != NULL && type != STR_TYPE) {
+ if (!strcmp(word, string_pointer->name)) {
+ sprintf(error_buffer, USED_LABEL_STR, line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ } else
+ string_pointer = string_pointer->next_string;
+ }
+
+ while (cstring_pointer != NULL && type != CSTR_TYPE) {
+ if (!strcmp(word, cstring_pointer->name)) {
+ sprintf(error_buffer, USED_LABEL_CSTR, line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ } else
+ cstring_pointer = cstring_pointer->next_string;
+ }
+
+ /* DON'T CHECK FOR ATT_TYPE AS YOU CAN'T HAVE ATTRIBUTE ARRAYS. */
+ while (attribute_pointer != NULL) {
+ if (!strcmp(word, attribute_pointer->name)) {
+ sprintf(error_buffer, USED_LABEL_ATT, line, word);
+ write_text(error_buffer);
+
+ return (TRUE);
+ } else
+ attribute_pointer = attribute_pointer->next_attribute;
+ }
+
+ for (index = 1; index <= objects; index++) {
+ if (!strcmp(word, object[index]->label)) {
+ sprintf(error_buffer, USED_LABEL_OBJ,
+ line, word);
+ log_error(error_buffer, PLUS_STDERR);
+
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+void restart_game() {
+ int index;
+
+ struct integer_type *current_integer;
+ struct integer_type *previous_integer;
+ struct synonym_type *current_synonym;
+ struct synonym_type *previous_synonym;
+ struct name_type *current_name;
+ struct name_type *next_name;
+ struct function_type *current_function;
+ struct function_type *previous_function;
+ struct string_type *current_string;
+ struct string_type *previous_string;
+ struct attribute_type *current_attribute;
+ struct attribute_type *previous_attribute;
+ struct cinteger_type *previous_cinteger;
+ struct filter_type *current_filter;
+ struct filter_type *previous_filter;
+
+#ifdef GLK
+ if (SOUND_SUPPORTED->value) {
+ /* STOP ALL SOUNDS AND SET VOLUMES BACK TO 100% */
+ for (index = 0; index < 4; index++) {
+ g_vm->glk_schannel_stop(sound_channel[index]);
+ g_vm->glk_schannel_set_volume(sound_channel[index], 65535);
+
+ /* STORE A COPY OF THE CURRENT VOLUME FOR ACCESS
+ * FROM JACL CODE */
+ sprintf(temp_buffer, "volume[%d]", index);
+ cinteger_resolve(temp_buffer)->value = 100;
+ }
+ }
+#endif
+
+ /* FREE ALL OBJECTS */
+ for (index = 1; index <= objects; index++) {
+ current_name = object[index]->first_name;
+ while (current_name->next_name != NULL) {
+ next_name = current_name->next_name;
+ free(current_name);
+ current_name = next_name;
+ }
+ free(current_name);
+ free(object[index]);
+ }
+
+ /* FREE ALL VARIABLES */
+
+ if (integer_table != NULL) {
+ if (integer_table->next_integer != NULL) {
+ do {
+ current_integer = integer_table;
+ previous_integer = integer_table;
+ while (current_integer->next_integer != NULL) {
+ previous_integer = current_integer;
+ current_integer = current_integer->next_integer;
+ }
+ free(current_integer);
+ previous_integer->next_integer = NULL;
+ } while (previous_integer != integer_table);
+ }
+
+ free(integer_table);
+ integer_table = NULL;
+ }
+
+ /* FREE ALL FUNCTIONS */
+ if (function_table != NULL) {
+ if (function_table->next_function != NULL) {
+ do {
+ current_function = function_table;
+ previous_function = function_table;
+ while (current_function->next_function != NULL) {
+ previous_function = current_function;
+ current_function = current_function->next_function;
+ }
+ free(current_function);
+ previous_function->next_function = NULL;
+ } while (previous_function != function_table);
+ }
+
+ free(function_table);
+ function_table = NULL;
+ }
+
+ /* FREE ALL FILTERS */
+ if (filter_table != NULL) {
+ if (filter_table->next_filter != NULL) {
+ do {
+ current_filter = filter_table;
+ previous_filter = filter_table;
+ while (current_filter->next_filter != NULL) {
+ previous_filter = current_filter;
+ current_filter = current_filter->next_filter;
+ }
+ free(current_filter);
+ previous_filter->next_filter = NULL;
+ } while (previous_filter != filter_table);
+ }
+
+ free(filter_table);
+ filter_table = NULL;
+ }
+
+ /* FREE ALL STRINGS */
+ if (string_table != NULL) {
+ if (string_table->next_string != NULL) {
+ do {
+ current_string = string_table;
+ previous_string = string_table;
+ while (current_string->next_string != NULL) {
+ previous_string = current_string;
+ current_string = current_string->next_string;
+ }
+ free(current_string);
+ previous_string->next_string = NULL;
+ } while (previous_string != string_table);
+ }
+
+ free(string_table);
+ string_table = NULL;
+ }
+
+ /* FREE ALL ATTRIBUTES */
+ if (attribute_table != NULL) {
+ if (attribute_table->next_attribute != NULL) {
+ do {
+ current_attribute = attribute_table;
+ previous_attribute = attribute_table;
+ while (current_attribute->next_attribute != NULL) {
+ previous_attribute = current_attribute;
+ current_attribute = current_attribute->next_attribute;
+ }
+ free(current_attribute);
+ previous_attribute->next_attribute = NULL;
+ } while (previous_attribute != attribute_table);
+ }
+
+ free(attribute_table);
+ attribute_table = NULL;
+ }
+
+ /* FREE ALL CONSTANTS */
+ if (cinteger_table != NULL) {
+ if (cinteger_table->next_cinteger != NULL) {
+ do {
+ current_cinteger = cinteger_table;
+ previous_cinteger = cinteger_table;
+ while (current_cinteger->next_cinteger != NULL) {
+ previous_cinteger = current_cinteger;
+ current_cinteger = current_cinteger->next_cinteger;
+ }
+ free(current_cinteger);
+ previous_cinteger->next_cinteger = NULL;
+ } while (previous_cinteger != cinteger_table);
+ }
+
+ free(cinteger_table);
+ cinteger_table = NULL;
+ }
+
+ if (cstring_table != NULL) {
+ if (cstring_table->next_string != NULL) {
+ do {
+ current_string = cstring_table;
+ previous_string = cstring_table;
+ while (current_string->next_string != NULL) {
+ previous_string = current_string;
+ current_string = current_string->next_string;
+ }
+ free(current_string);
+ previous_string->next_string = NULL;
+ } while (previous_string != cstring_table);
+ }
+
+ free(cstring_table);
+ cstring_table = NULL;
+ }
+
+ /* FREE ALL SYNONYMS */
+ if (synonym_table != NULL) {
+ if (synonym_table->next_synonym != NULL) {
+ do {
+ current_synonym = synonym_table;
+ previous_synonym = synonym_table;
+ while (current_synonym->next_synonym != NULL) {
+ previous_synonym = current_synonym;
+ current_synonym = current_synonym->next_synonym;
+ }
+ free(current_synonym);
+ previous_synonym->next_synonym = NULL;
+ } while (previous_synonym != synonym_table);
+ }
+ free(synonym_table);
+ synonym_table = NULL;
+ }
+
+ free_from(grammar_table);
+ grammar_table = NULL;
+
+ read_gamefile();
+}
+
+void free_from(struct word_type *x) {
+ if (x) {
+ free_from(x->first_child);
+ free_from(x->next_sibling);
+ free(x);
+ }
+}
+
+void set_defaults() {
+ /* RESET THE BACK-REFERENCE VARIABLES */
+ them[0] = 0;
+ it = 0;
+ her = 0;
+ him = 0;
+}
+
+void create_cinteger(char *name, int value) {
+ struct cinteger_type *new_cinteger = NULL;
+
+ if ((new_cinteger = (struct cinteger_type *)
+ malloc(sizeof(struct cinteger_type))) == NULL) {
+ outofmem();
+ } else {
+ if (cinteger_table == NULL) {
+ cinteger_table = new_cinteger;
+ } else {
+ current_cinteger->next_cinteger = new_cinteger;
+ }
+
+ current_cinteger = new_cinteger;
+ strncpy(current_cinteger->name, name, 40);
+ current_cinteger->name[40] = 0;
+ current_cinteger->value = value;
+ current_cinteger->next_cinteger = NULL;
+ }
+}
+
+void create_integer(char *name, int value) {
+ struct integer_type *new_integer = NULL;
+
+ if ((new_integer = (struct integer_type *)
+ malloc(sizeof(struct integer_type))) == NULL) {
+ outofmem();
+ } else {
+ /* KEEP A COUNT OF HOW MANY INTEGERS ARE DEFINED TO
+ * VALIDATE SAVED GAMES */
+ integers++;
+
+ if (integer_table == NULL) {
+ integer_table = new_integer;
+ } else {
+ current_integer->next_integer = new_integer;
+ }
+ current_integer = new_integer;
+ strncpy(current_integer->name, name, 40);
+ current_integer->name[40] = 0;
+ current_integer->value = value;
+ current_integer->next_integer = NULL;
+ }
+}
+
+void create_string(char *name, char *value) {
+ struct string_type *new_string = NULL;
+
+ if ((new_string = (struct string_type *)
+ malloc(sizeof(struct string_type))) == NULL) {
+ outofmem();
+ } else {
+ /* KEEP A COUNT OF HOW MANY STRINGS ARE DEFINED TO
+ * VALIDATE SAVED GAMES */
+ strings++;
+
+ if (string_table == NULL) {
+ string_table = new_string;
+ } else {
+ current_string->next_string = new_string;
+ }
+ current_string = new_string;
+ strncpy(current_string->name, name, 40);
+ current_string->name[40] = 0;
+
+ if (value != NULL) {
+ strncpy(current_string->value, value, 255);
+ } else {
+ /* IF NO VALUE IS SUPPLIED, JUST NULL-TERMINATE
+ * THE STRING */
+ current_string->value[0] = 0;
+ }
+
+ current_string->value[255] = 0;
+ current_string->next_string = NULL;
+ }
+}
+
+void create_cstring(char *name, char *value) {
+ struct string_type *new_string = NULL;
+
+ if ((new_string = (struct string_type *)
+ malloc(sizeof(struct string_type))) == NULL) {
+ outofmem();
+ } else {
+ if (cstring_table == NULL) {
+ cstring_table = new_string;
+ } else {
+ current_cstring->next_string = new_string;
+ }
+ current_cstring = new_string;
+ strncpy(current_cstring->name, name, 40);
+ current_cstring->name[40] = 0;
+
+ if (value != NULL) {
+ strncpy(current_cstring->value, value, 255);
+ } else {
+ /* IF NO VALUE IS SUPPLIED, JUST NULL-TERMINATE
+ * THE STRING */
+ current_cstring->value[0] = 0;
+ }
+
+ current_cstring->value[255] = 0;
+ current_cstring->next_string = NULL;
+ }
+}
+
+void create_language_constants() {
+ /* SET THE DEFAULT LANGUAGE CONSTANTS IF ANY OR ALL OF THEM
+ * ARE MISSING FROM THE GAME THAT IS BEING LOADED. DEFAULT
+ * TO THE NATIVE_LANGUAGE SETTING IN language.h */
+
+ if (cstring_resolve("COMMENT_IGNORED") == NULL)
+ create_cstring("COMMENT_IGNORED", COMMENT_IGNORED);
+ if (cstring_resolve("COMMENT_RECORDED") == NULL)
+ create_cstring("COMMENT_RECORDED", COMMENT_RECORDED);
+ if (cstring_resolve("YES_WORD") == NULL)
+ create_cstring("YES_WORD", YES_WORD);
+ if (cstring_resolve("NO_WORD") == NULL)
+ create_cstring("NO_WORD", NO_WORD);
+ if (cstring_resolve("YES_OR_NO") == NULL)
+ create_cstring("YES_OR_NO", YES_OR_NO);
+ if (cstring_resolve("INVALID_SELECTION") == NULL)
+ create_cstring("INVALID_SELECTION", INVALID_SELECTION);
+ if (cstring_resolve("RESTARTING") == NULL)
+ create_cstring("RESTARTING", RESTARTING);
+ if (cstring_resolve("RETURN_GAME") == NULL)
+ create_cstring("RETURN_GAME", RETURN_GAME);
+ if (cstring_resolve("SCRIPTING_ON") == NULL)
+ create_cstring("SCRIPTING_ON", SCRIPTING_ON);
+ if (cstring_resolve("SCRIPTING_OFF") == NULL)
+ create_cstring("SCRIPTING_OFF", SCRIPTING_OFF);
+ if (cstring_resolve("SCRIPTING_ALREADY_OFF") == NULL)
+ create_cstring("SCRIPTING_ALREADY_OFF", SCRIPTING_ALREADY_OFF);
+ if (cstring_resolve("SCRIPTING_ALREADY_ON") == NULL)
+ create_cstring("SCRIPTING_ALREADY_OFF", SCRIPTING_ALREADY_OFF);
+ if (cstring_resolve("CANT_WRITE_SCRIPT") == NULL)
+ create_cstring("CANT_WRITE_SCRIPT", CANT_WRITE_SCRIPT);
+ if (cstring_resolve("ERROR_READING_WALKTHRU") == NULL)
+ create_cstring("ERROR_READING_WALKTHRU", ERROR_READING_WALKTHRU);
+ if (cstring_resolve("BAD_OOPS") == NULL)
+ create_cstring("BAD_OOPS", BAD_OOPS);
+ if (cstring_resolve("CANT_CORRECT") == NULL)
+ create_cstring("CANT_CORRECT", CANT_CORRECT);
+ if (cstring_resolve("SURE_QUIT") == NULL)
+ create_cstring("SURE_QUIT", SURE_QUIT);
+ if (cstring_resolve("SURE_RESTART") == NULL)
+ create_cstring("SURE_RESTART", SURE_RESTART);
+ if (cstring_resolve("NOT_CLEVER") == NULL)
+ create_cstring("NOT_CLEVER", NOT_CLEVER);
+ if (cstring_resolve("NO_MOVES") == NULL)
+ create_cstring("NO_MOVES", NO_MOVES);
+ if (cstring_resolve("TYPE_NUMBER") == NULL)
+ create_cstring("TYPE_NUMBER", TYPE_NUMBER);
+ if (cstring_resolve("BY") == NULL)
+ create_cstring("BY", BY);
+ if (cstring_resolve("REFERRING_TO") == NULL)
+ create_cstring("REFERRING_TO", REFERRING_TO);
+ if (cstring_resolve("WALKTHRU_WORD") == NULL)
+ create_cstring("WALKTHRU_WORD", WALKTHRU_WORD);
+ if (cstring_resolve("INFO_WORD") == NULL)
+ create_cstring("INFO_WORD", INFO_WORD);
+ if (cstring_resolve("RESTART_WORD") == NULL)
+ create_cstring("RESTART_WORD", RESTART_WORD);
+ if (cstring_resolve("AGAIN_WORD") == NULL)
+ create_cstring("AGAIN_WORD", AGAIN_WORD);
+ if (cstring_resolve("SCRIPT_WORD") == NULL)
+ create_cstring("SCRIPT_WORD", SCRIPT_WORD);
+ if (cstring_resolve("UNSCRIPT_WORD") == NULL)
+ create_cstring("UNSCRIPT_WORD", UNSCRIPT_WORD);
+ if (cstring_resolve("QUIT_WORD") == NULL)
+ create_cstring("QUIT_WORD", QUIT_WORD);
+ if (cstring_resolve("UNDO_WORD") == NULL)
+ create_cstring("UNDO_WORD", UNDO_WORD);
+ if (cstring_resolve("OOPS_WORD") == NULL)
+ create_cstring("OOPS_WORD", OOPS_WORD);
+ if (cstring_resolve("FROM_WORD") == NULL)
+ create_cstring("FROM_WORD", FROM_WORD);
+ if (cstring_resolve("EXCEPT_WORD") == NULL)
+ create_cstring("EXCEPT_WORD", EXCEPT_WORD);
+ if (cstring_resolve("FOR_WORD") == NULL)
+ create_cstring("FOR_WORD", FOR_WORD);
+ if (cstring_resolve("BUT_WORD") == NULL)
+ create_cstring("BUT_WORD", BUT_WORD);
+ if (cstring_resolve("AND_WORD") == NULL)
+ create_cstring("AND_WORD", AND_WORD);
+ if (cstring_resolve("THEN_WORD") == NULL)
+ create_cstring("THEN_WORD", THEN_WORD);
+ if (cstring_resolve("OF_WORD") == NULL)
+ create_cstring("OF_WORD", OF_WORD);
+ if (cstring_resolve("SHE_WORD") == NULL)
+ create_cstring("SHE_WORD", SHE_WORD);
+ if (cstring_resolve("HE_WORD") == NULL)
+ create_cstring("HE_WORD", HE_WORD);
+ if (cstring_resolve("THAT_WORD") == NULL)
+ create_cstring("THAT_WORD", THAT_WORD);
+ if (cstring_resolve("THEM_WORD") == NULL)
+ create_cstring("THEM_WORD", THEM_WORD);
+ if (cstring_resolve("THOSE_WORD") == NULL)
+ create_cstring("THOSE_WORD", THOSE_WORD);
+ if (cstring_resolve("THEY_WORD") == NULL)
+ create_cstring("THEY_WORD", THEY_WORD);
+ if (cstring_resolve("IT_WORD") == NULL)
+ create_cstring("IT_WORD", IT_WORD);
+ if (cstring_resolve("ITSELF_WORD") == NULL)
+ create_cstring("ITSELF_WORD", ITSELF_WORD);
+ if (cstring_resolve("HIM_WORD") == NULL)
+ create_cstring("HIM_WORD", HIM_WORD);
+ if (cstring_resolve("HIMSELF_WORD") == NULL)
+ create_cstring("HIMSELF_WORD", HIMSELF_WORD);
+ if (cstring_resolve("HER_WORD") == NULL)
+ create_cstring("HER_WORD", HER_WORD);
+ if (cstring_resolve("HERSELF_WORD") == NULL)
+ create_cstring("HERSELF_WORD", HERSELF_WORD);
+ if (cstring_resolve("THEMSELVES_WORD") == NULL)
+ create_cstring("THEMSELVES_WORD", THEMSELVES_WORD);
+ if (cstring_resolve("YOU_WORD") == NULL)
+ create_cstring("YOU_WORD", YOU_WORD);
+ if (cstring_resolve("YOURSELF_WORD") == NULL)
+ create_cstring("YOURSELF_WORD", YOURSELF_WORD);
+ if (cstring_resolve("ONES_WORD") == NULL)
+ create_cstring("ONES_WORD", ONES_WORD);
+ if (cstring_resolve("NO_MULTI_VERB") == NULL)
+ create_cstring("NO_MULTI_VERB", NO_MULTI_VERB);
+ if (cstring_resolve("NO_MULTI_START") == NULL)
+ create_cstring("NO_MULTI_START", NO_MULTI_START);
+ if (cstring_resolve("PERSON_CONCEALING") == NULL)
+ create_cstring("PERSON_CONCEALING", PERSON_CONCEALING);
+ if (cstring_resolve("PERSON_POSSESSIVE") == NULL)
+ create_cstring("PERSON_POSSESSIVE", PERSON_POSSESSIVE);
+ if (cstring_resolve("CONTAINER_CLOSED") == NULL)
+ create_cstring("CONTAINER_CLOSED", CONTAINER_CLOSED);
+ if (cstring_resolve("CONTAINER_CLOSED_FEM") == NULL)
+ create_cstring("CONTAINER_CLOSED_FEM", CONTAINER_CLOSED_FEM);
+ if (cstring_resolve("FROM_NON_CONTAINER") == NULL)
+ create_cstring("FROM_NON_CONTAINER", FROM_NON_CONTAINER);
+ if (cstring_resolve("DOUBLE_EXCEPT") == NULL)
+ create_cstring("DOUBLE_EXCEPT", DOUBLE_EXCEPT);
+ if (cstring_resolve("NONE_HELD") == NULL)
+ create_cstring("NONE_HELD", NONE_HELD);
+ if (cstring_resolve("NO_OBJECTS") == NULL)
+ create_cstring("NO_OBJECTS", NO_OBJECTS);
+ if (cstring_resolve("NO_FILENAME") == NULL)
+ create_cstring("NO_FILENAME", NO_FILENAME);
+ if (cstring_resolve("MOVE_UNDONE") == NULL)
+ create_cstring("MOVE_UNDONE", MOVE_UNDONE);
+ if (cstring_resolve("NO_UNDO") == NULL)
+ create_cstring("NO_UNDO", NO_UNDO);
+ if (cstring_resolve("CANT_SAVE") == NULL)
+ create_cstring("CANT_SAVE", CANT_SAVE);
+ if (cstring_resolve("CANT_RESTORE") == NULL)
+ create_cstring("CANT_RESTORE", CANT_RESTORE);
+ if (cstring_resolve("GAME_SAVED") == NULL)
+ create_cstring("GAME_SAVED", GAME_SAVED);
+ if (cstring_resolve("INCOMPLETE_SENTENCE") == NULL)
+ create_cstring("INCOMPLETE_SENTENCE", INCOMPLETE_SENTENCE);
+ if (cstring_resolve("UNKNOWN_OBJECT") == NULL)
+ create_cstring("UNKNOWN_OBJECT", UNKNOWN_OBJECT);
+ if (cstring_resolve("UNKNOWN_OBJECT_END") == NULL)
+ create_cstring("UNKNOWN_OBJECT_END", UNKNOWN_OBJECT_END);
+ if (cstring_resolve("CANT_USE_WORD") == NULL)
+ create_cstring("CANT_USE_WORD", CANT_USE_WORD);
+ if (cstring_resolve("IN_CONTEXT") == NULL)
+ create_cstring("IN_CONTEXT", IN_CONTEXT);
+ if (cstring_resolve("DONT_SEE") == NULL)
+ create_cstring("DONT_SEE", DONT_SEE);
+ if (cstring_resolve("HERE_WORD") == NULL)
+ create_cstring("HERE_WORD", HERE_WORD);
+ if (cstring_resolve("BAD_SAVED_GAME") == NULL)
+ create_cstring("BAD_SAVED_GAME", BAD_SAVED_GAME);
+ if (cstring_resolve("ARENT") == NULL)
+ create_cstring("ARENT", ARENT);
+ if (cstring_resolve("ISNT") == NULL)
+ create_cstring("ISNT", ISNT);
+ if (cstring_resolve("ARE") == NULL)
+ create_cstring("ARE", ARE);
+ if (cstring_resolve("IS") == NULL)
+ create_cstring("IS", IS);
+ if (cstring_resolve("DONT") == NULL)
+ create_cstring("DONT", DONT);
+ if (cstring_resolve("DOESNT") == NULL)
+ create_cstring("DOESNT", DOESNT);
+ if (cstring_resolve("DO") == NULL)
+ create_cstring("DO", DO);
+ if (cstring_resolve("DOES") == NULL)
+ create_cstring("DOES", DOES);
+ if (cstring_resolve("SCORE_UP") == NULL)
+ create_cstring("SCORE_UP", SCORE_UP);
+ if (cstring_resolve("POINT") == NULL)
+ create_cstring("POINT", POINT);
+ if (cstring_resolve("POINTS") == NULL)
+ create_cstring("POINTS", POINTS);
+ if (cstring_resolve("STARTING") == NULL)
+ create_cstring("STARTING", STARTING);
+ if (cstring_resolve("NO_IT") == NULL)
+ create_cstring("NO_IT", NO_IT);
+ if (cstring_resolve("NO_IT_END") == NULL)
+ create_cstring("NO_IT_END", NO_IT_END);
+ if (cstring_resolve("BACK_REFERENCE") == NULL)
+ create_cstring("BACK_REFERENCE", BACK_REFERENCE);
+ if (cstring_resolve("BACK_REFERENCE_END") == NULL)
+ create_cstring("BACK_REFERENCE_END", BACK_REFERENCE_END);
+ if (cstring_resolve("WHEN_YOU_SAY") == NULL)
+ create_cstring("WHEN_YOU_SAY", WHEN_YOU_SAY);
+ if (cstring_resolve("MUST_SPECIFY") == NULL)
+ create_cstring("MUST_SPECIFY", MUST_SPECIFY);
+ if (cstring_resolve("OR_WORD") == NULL)
+ create_cstring("OR_WORD", OR_WORD);
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/logging.cpp b/engines/glk/jacl/logging.cpp
new file mode 100644
index 0000000000..97376c52b2
--- /dev/null
+++ b/engines/glk/jacl/logging.cpp
@@ -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.
+ *
+ */
+
+#include "glk/jacl/jacl.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+#include "glk/jacl/language.h"
+
+namespace Glk {
+namespace JACL {
+
+extern char user_id[];
+extern char prefix[];
+
+void log_error(char *message, int console) {
+ /* LOG A MESSAGE TO THE CONSOLE */
+
+ char consoleMessage[256];
+ event_t event;
+
+ // BUILD A STRING SUITABLE FOR DISPLAY ON THE CONSOLE.
+ sprintf(consoleMessage, "ERROR: %s^", message);
+
+ g_vm->glk_set_style(style_Alert);
+ write_text(consoleMessage);
+ g_vm->glk_set_style(style_Normal);
+
+ // FLUSH THE GLK WINDOW SO THE ERROR GETS DISPLAYED IMMEDIATELY.
+ g_vm->glk_select_poll(&event);
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/parser.cpp b/engines/glk/jacl/parser.cpp
new file mode 100644
index 0000000000..b9787c8ac7
--- /dev/null
+++ b/engines/glk/jacl/parser.cpp
@@ -0,0 +1,1918 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+#define FIRST_LIST noun_number-2
+#define SECOND_LIST noun_number
+
+int object_list[4][MAX_OBJECTS];
+int multiple_resolved[MAX_OBJECTS];
+
+// THIS IS THE NUMBER OF OBJECTS LEFT IN THE LIST
+int list_size[4];
+
+// THIS IS THE INDEX OF THE FINAL OBJECT
+int max_size[4];
+
+/* object_list[] HAS THE FOLLOWING INDEXES:
+ * noun1 : 0
+ * noun2 : 1
+ * noun1 EXCEPTIONS : 2
+ * noun2 EXCEPTIONS : 3
+ */
+
+int selection;
+
+int matches = 0;
+int highest_confidence = 0;
+int prime_suspect = 0;
+int done = 0;
+int backup_pointer = 0;
+int everything = 0;
+
+int confidence[MAX_OBJECTS];
+int possible_objects[MAX_OBJECTS];
+
+int it;
+int them[MAX_OBJECTS];
+int her;
+int him;
+int parent;
+
+int custom_error;
+
+int oops_word;
+int last_exact;
+
+char *expected_scope[3];
+
+// THIS ARRAY DEFINES THE OBJECTS THAT THE CURRENT OBJECTS ARE
+// SUPPOSED TO BE A CHILD OF
+int from_objects[MAX_OBJECTS];
+
+int after_from;
+char *from_word;
+
+int object_expected = FALSE;
+
+char default_function[84];
+char object_name[84];
+
+char base_function[84];
+char before_function[84];
+char after_function[84];
+char local_after_function[84];
+
+extern char text_buffer[];
+extern char function_name[];
+extern char temp_buffer[];
+extern char error_buffer[];
+extern char override[];
+extern char *word[];
+
+extern int quoted[];
+
+extern struct object_type *object[];
+extern int objects;
+
+extern int noun[];
+extern int wp;
+extern int player;
+
+extern struct word_type *grammar_table;
+extern struct function_type *executing_function;
+extern struct object_type *object[];
+extern struct variable_type *variable[];
+
+void parser() {
+ // THIS FUNCTION COMPARES THE WORDS IN THE PLAYER'S COMMAND TO THE
+ // GRAMMAR TREE OF POSSIBLE COMMANDS
+
+ struct word_type *pointer;
+ struct word_type *matched_word = NULL;
+
+ int index;
+ int current_noun = 0;
+
+ // RESET TO START OF PROCESSING
+
+ ////printf("--- in parser\n");
+
+ // THIS IS USED TO STORE THE LAST EXACT WORD MATCH IN THE PLAYERS COMMAND
+ last_exact = -1;
+ after_from = -1;
+ from_objects[0] = 0;
+
+ noun[0] = FALSE;
+ noun[1] = FALSE;
+
+ // RESET BOTH THE LISTS TO BE EMPTY
+ //printf("--- reset lists\n");
+ for (index = 0; index < 4; index++) {
+ list_size[index] = 0;
+ max_size[index] = 0;
+ }
+
+ //printf("--- clear $integer\n");
+ clear_cinteger("$integer");
+ //printf("--- clear $string\n");
+ clear_cstring("$string");
+ //printf("--- clear action\n");
+ clear_cstring("action");
+
+ if (grammar_table == NULL) {
+ // THERE ARE NO USER DEFINED COMMANDS AVAILABLE, SO THE USER'S
+ // COMMAND IS INEVITABLY INVALID
+ //printf("--- no grammar table\n");
+ INTERRUPTED->value = TRUE;
+ diagnose();
+ return;
+ }
+
+ //printf("--- set pointer to grammar table\n");
+ // START AT THE TOP OF THE GRAMMAR TREE
+ pointer = grammar_table;
+
+ while (word[wp] != NULL && pointer != NULL) {
+ //printf("--- wp = %d\n", wp);
+ //printf("--- word[%d] = %s\n", wp, word[wp]);
+ object_expected = FALSE;
+
+ if (!strcmp(cstring_resolve("THEN_WORD")->value, word[wp])) {
+ // CONSIDER THIS THE END OF THIS COMMAND AS 'THEN' IS
+ // TREATED LIKE A FULL STOP
+ break;
+ } else if ((matched_word = exact_match(pointer)) != NULL) {
+ // THIS WORD WAS AN EXACT MATCH FOR ONE OF THE POSSIBLE WORDS
+ // AT THE CURRENT GRAMMAR TREE LEVEL - MOVE ON!
+ pointer = matched_word;
+ pointer = pointer->first_child;
+ } else if ((matched_word = object_match(pointer, current_noun)) != NULL) {
+ // THIS WAS AN OBJECT PLACE HOLDER AT THIS GRAMMAR LEVEL AND
+ // THIS POINT IN THE PLAYER'S COMMAND COULD BE RESOLVED TO
+ // AT LEAST ONE OBJECT
+
+ if (list_size[current_noun] > 1) {
+ // MULTIPLE OBJECTS WERE RETURNED
+ if (matched_word->word[1] != '*') {
+ if (last_exact == -1) {
+ write_text(cstring_resolve("NO_MULTI_START")->value);
+ } else {
+ sprintf(error_buffer, cstring_resolve("NO_MULTI_VERB")->value, word[last_exact]);
+ write_text(error_buffer);
+ }
+
+ INTERRUPTED->value = TRUE;
+ return;
+ }
+ }
+
+ // STORE THE EXPECTED SCOPE FOR LATER CHECKING
+ expected_scope[current_noun] = (char *) &matched_word->word;
+ //printf("--- expected scope for noun%d is %s\n", current_noun, expected_scope[current_noun]);
+
+ // THE NEXT MATCH OR GROUP OF MATCHES SHOULD BE IN THE SECOND
+ // LIST OF OBJECTS
+ current_noun++;
+
+ // PUSH ON FROM THE POINT A MATCH WAS FOUND....
+ pointer = matched_word;
+ pointer = pointer->first_child;
+ } else {
+ // THIS IS AN UNKNOWN WORD
+ if (oops_word == -1 && word[wp] != NULL) {
+ oops_word = wp;
+ }
+
+ if (custom_error == TRUE) {
+ // THERE HAS BEEN SOME CUSTOM ERROR DISPLAYED ALREADY
+ // SO JUST RETURN
+ TIME->value = FALSE;
+ INTERRUPTED->value = TRUE;
+ return;
+ } else {
+ // THERE ARE NO MORE POSIBILITIES, THE WORD CAN'T BE
+ // USED IN THIS CONTEXT
+ INTERRUPTED->value = TRUE;
+ diagnose();
+ return;
+ }
+ }
+ };
+
+ if (pointer == NULL) {
+ // THIS CAN ONLY HAPPEN IF MOVING THE POINTER TO ITS
+ // FIRST CHILD RESULTS IN A NULL - AN INCOMPLETE
+ // GRAMMAR STATEMENT.
+ log_error(INCOMPLETE_GRAMMAR, PLUS_STDOUT);
+ INTERRUPTED->value = TRUE;
+ return;
+ }
+
+ //printf("--- first list has %d objects\n", list_size[0]);
+ //printf("--- first list has %d max\n", max_size[0]);
+ //printf("--- second list has %d objects\n", list_size[1]);
+ //printf("--- second list has %d max\n", max_size[1]);
+
+ /* THE PLAYER'S MOVE HAS NO MORE WORDS, AND A BRANCH OF
+ * THE GRAMMAR TREE HAS BEEN FOUND THAT SO FAR MATCHES
+ * THIS MOVE. SCAN THROUGH ALL THE CHILDREN OF THE FINAL
+ * WORD OF THE PLAYER'S COMMAND AND FIND A FUNCTION BASE.
+ * (FUNCTION BASES BEGIN WITH '>'). IF THIS IS FOUND,
+ * CALL THE APPROPRIATE JACL FUNCTIONS IN THE GAME CODE
+ * IF NO FUNCTION BASE IS FOUND, MORE WORDS WERE EXPECTED
+ * IN ORDER TO CONSTRUCT A COMPLETE COMMAND. SHOW ERROR. */
+ do {
+ if (pointer->word[0] == '>') {
+ //printf("--- found action %s\n", pointer->word);
+ /* CALL ALL THE APPROPRIATE FUNCTIONS FOR EACH OF */
+ /* THE OBJECTS IN THE SET */
+ add_cstring("action", &pointer->word[1]);
+
+ if (list_size[0] > 1) {
+ /* FIRST IS MULTI, PRESUME SECOND IS SINGLE OR ZERO AS YOU
+ * CAN HAVE COMMANDS WITH TWO MULTIPLE OBJECT REFERENCES */
+ noun[1] = first_available(1);
+
+ for (index = 0; index < max_size[0]; index++) {
+ /* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
+ if (object_list[0][index] != 0) {
+ noun[0] = object_list[0][index];
+
+ if (MULTI_PREFIX->value) {
+ // DISPLAY THE OBJECT BEING ACTED ON
+ plain_output(noun[0], FALSE);
+ write_text(temp_buffer);
+ write_text(": ");
+ }
+
+ //printf("--- 1 calling functions for %s\n", object[noun[0]]->label);
+ call_functions(pointer->word);
+
+ /* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
+ * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
+ if (INTERRUPTED->value) {
+ break;
+ }
+ }
+ }
+ } else {
+ // FIRST LIST ISN'T MULTI
+ //printf("--- first list isn't multi\n");
+ if (list_size[1] > 1) {
+ //printf("--- second list is multi\n");
+ // ONLY SECOND IS MULTI
+ noun[0] = first_available(0);
+
+ for (index = 0; index < max_size[1]; index++) {
+ /* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
+ if (object_list[1][index] != 0) {
+ noun[1] = object_list[1][index];
+
+ if (MULTI_PREFIX->value) {
+ // DISPLAY THE OBJECT BEING ACTED ON
+ plain_output(noun[0], FALSE);
+ write_text(temp_buffer);
+ write_text(": ");
+ }
+
+ //printf("--- 2 calling functions for %s\n", object[noun[1]]->label);
+ call_functions(pointer->word);
+
+ /* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
+ * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
+ if (INTERRUPTED->value) {
+ break;
+ }
+ }
+ }
+ } else {
+ //printf("--- second list isn't multi\n");
+ // NEITHER OBJECT REFERENCE IS MULTI
+ if (list_size[0] == 0) {
+ noun[0] = 0;
+ noun[1] = 0;
+
+ /* THIS IS AN OBJECT-LESS COMMAND */
+ call_functions(pointer->word);
+ } else {
+ noun[0] = first_available(0);
+ noun[1] = first_available(1);
+
+ //printf("--- 3 calling functions for %s and %s\n", object[noun[0]]->label, object[noun[1]]->label);
+ call_functions(pointer->word);
+ }
+ }
+ }
+ if (TIME->value == FALSE) {
+ /* IS THIS ENOUGH TO INDICATE THAT THE OTHER COMMANDS
+ * SHOULDN'T BE PERFORMED??? */
+ //INTERRUPTED->value = TRUE;
+ }
+ return;
+ } else {
+ //printf("--- move to next grammar sibling\n");
+ // MOVE THROUGH THE OPTIONS AT THIS LEVEL OF THE GRAMMAR TREE
+ // TO FIND THE ACTION THAT MATCHES THIS COMMAND
+ if (pointer->next_sibling == NULL) {
+ break;
+ } else {
+ pointer = pointer->next_sibling;
+ }
+ }
+ } while (TRUE);
+
+ /* THERE IS NO FUNCTION AS A CHILD OF THE LAST WORD OF THE PLAYER'S
+ * COMMAND SO DISPLAY AN IN-GAME ERROR TO THE PLAYER. THIS IS LIKELY
+ * TO BE AN INCOMPLETE COMMAND */
+ INTERRUPTED->value = TRUE;
+ diagnose();
+ return;
+}
+
+/* THIS FUNCTION RETURNS THE FIRST OBJECT IN THE SPECIFIED LIST
+ * OR 0 IF IT IS EMPTY. THE FIRST OBJECT IN THE LIST IS THE FIRST
+ * ELEMENT THAT IS NON-ZERO BEFORE THE max-size IS REACHED. */
+int first_available(int list_number) {
+ int index;
+
+ if (list_size[list_number] == 0) return (0);
+
+ //printf("--- looking for next object in list\n");
+ for (index = 0; index < max_size[list_number]; index++) {
+ if (object_list[list_number][index] != 0) {
+ //printf("--- returning object %s\n", object[object_list[list_number][index]]->label);
+ return (object_list[list_number][index]);
+ }
+ }
+
+ //printf("--- no objects left in list\n");
+ /* NO OBJECTS LEFT IN THE LIST */
+ return (0);
+}
+
+void call_functions(char *base_name) {
+ /* THIS FUNCTION CALLS ALL THE APPROPRIATE JACL FUNCTIONS TO RESPOND
+ * TO A PLAYER'S COMMAND GIVEN A BASE FUNCTION NAME AND THE CURRENT
+ * VALUE OF noun1 AND noun2 */
+
+ /* THE DEFAULT IS THAT THE COMMAND IS SUCCESSFUL AND THAT TIME SHOULD
+ * PASS. IF THE COMMAND FAILS, 'TIME' WILL BE SET TO FALSE */
+ TIME->value = TRUE;
+
+ strncpy(base_function, base_name + 1, 80);
+ strcat(base_function, "_");
+
+ strncpy(override, base_function, 80);
+
+ strcpy(before_function, "+before_");
+ strcat(before_function, base_name + 1);
+
+ strcpy(after_function, "+after_");
+ strcat(after_function, base_name + 1);
+
+ strcpy(local_after_function, "after_");
+ strcat(local_after_function, base_name + 1);
+ if (noun[1] != FALSE) {
+ strcat(local_after_function, "_");
+ strcat(local_after_function, object[noun[1]]->label);
+ }
+ if (noun[0] != FALSE) {
+ strcat(local_after_function, "_");
+ strcat(local_after_function, object[noun[0]]->label);
+ }
+
+ /* THIS IS CALLED IF AN 'override' COMMAND IS EXECUTED
+ * IN A LIBRARY FUNCTION BUT THE OBJECT-OR-LOCATION-SPECIFIC
+ * OVERRIDE DOES NOT EXIST. IT IS SET TO '+default_func' */
+ strcpy(default_function, "+default_");
+ strcat(default_function, base_name + 1);
+
+ /* EXECUTE THE GLOBAL *DEFAULT* BEFORE FUNCTION
+ * AND RETURN IF IT RETURNS TRUE */
+ if (execute("+before") != FALSE)
+ return;
+
+ /* EXECUTE THE VERB-SPECIFIC BEFORE
+ FUNCTION AND RETURN IF IT RETURNS TRUE */
+ if (execute(before_function) != FALSE)
+ return;
+
+ if (noun[0] == FALSE) { /* USER'S COMMAND HAS NO NOUNS */
+ strcat(base_function, object[HERE]->label);
+ /* EXECUTE THE FUNCTION 'func_here' */
+ if (execute(base_function) == FALSE) {
+ /* THIS LOCATION-SPECIFIC FUNCTION DOES NOT
+ * EXIST OR HAS ISSUED A 'break false' COMMAND.
+ * EXECUTE THE FUNCTION '+func'
+ * WITH THE POSSIBLILITY OF
+ * EXECUTING THE FUNCTION 'func_override_here'
+ * IF AN 'override' COMMAND IS FOUND IN '+func'
+ * IF THIS OVERRIDE FUNCTION ISN'T FOUND
+ * THE DEFAULT FUNCTION WILL BE EXECUTED */
+
+ /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
+ * IS NEEDED */
+ strcat(override, "override_");
+ strcat(override, object[HERE]->label);
+
+ /* CREATE THE FUNCTION NAME '+func' */
+ strcpy(base_function, "+");
+ strcat(base_function, base_name + 1);
+
+ /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
+ if (execute(base_function) == FALSE)
+ unkfunrun(base_function);
+ }
+ } else if (noun[1] == FALSE) { /* USER'S COMMAND HAS ONE NOUN */
+ strcat(base_function, object[noun[0]]->label);
+ /* EXECUTE THE FUNCTION 'func_noun1' */
+ if (execute(base_function) == FALSE) {
+ /* THIS OBJECT-SPECIFIC FUNCTION DOES NOT
+ * EXIST OR HAS ISSUED A 'break false' COMMAND.
+ * EXECUTE THE FUNCTION '+func'
+ * WITH THE POSSIBLILITY OF
+ * EXECUTING THE FUNCTION 'func_override_noun1'
+ * IF AN 'override' COMMAND IS FOUND IN '+func'
+ * IF THIS OVERRIDE FUNCTION ISN'T FOUND
+ * THE DEFAULT FUNCTION WILL BE EXECUTED */
+
+ /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
+ * IS NEEDED */
+ strcat(override, "override_");
+ strcat(override, object[noun[0]]->label);
+
+ /* CREATE THE FUNCTION NAME '+func' */
+ strcpy(base_function, "+");
+ strcat(base_function, base_name + 1);
+
+ /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
+ if (execute(base_function) == FALSE)
+ unkfunrun(base_function);
+ }
+ } else { /* USER'S COMMAND HAS TWO NOUNS */
+ strcat(base_function, object[noun[1]]->label);
+ strcat(base_function, "_");
+ strcat(base_function, object[noun[0]]->label);
+ /* EXECUTE THE FUNCTION 'func_noun2_noun1'
+ * IE give_to_fred THAT IS ASSOCIATED WITH
+ * THE OBJECT flint_stone */
+ if (execute(base_function) == FALSE) {
+ /* THIS OBJECTS-SPECIFIC FUNCTION DOES NOT
+ * EXIST OR HAS ISSUED A 'break false' COMMAND.
+ * EXECUTE THE FUNCTION '+func'
+ * WITH THE POSSIBLILITY OF
+ * EXECUTING THE FUNCTION 'func_override_noun2_noun1'
+ * IF AN 'override' COMMAND IS FOUND IN '+func'
+ * IF THIS OVERRIDE FUNCTION ISN'T FOUND
+ * THE DEFAULT FUNCTION WILL BE EXECUTED */
+
+ /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
+ * IS NEEDED */
+ strcat(override, object[noun[1]]->label);
+ strcat(override, "_override_");
+ strcat(override, object[noun[0]]->label);
+
+ /* CREATE THE FUNCTION NAME '+func' */
+ strcpy(base_function, "+");
+ strcat(base_function, base_name + 1);
+
+ /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
+ if (execute(base_function) == FALSE)
+ unkfunrun(base_function);
+ }
+ }
+
+ /* EXECUTE THE LOCAL AFTER FUNCTION
+ * AND RETURN IF IT RETURNS TRUE */
+ if (execute(local_after_function) != FALSE)
+ return;
+
+ /* EXECUTE THE VERB-SPECIFIC AFTER
+ * FUNCTION AND RETURN IF IT RETURNS TRUE */
+ if (execute(after_function) != FALSE)
+ return;
+
+ /* EXECUTE THE GLOBAL *DEFAULT* AFTER FUNCTION
+ * AND RETURN IF IT RETURNS TRUE */
+ if (execute("+after") != FALSE)
+ return;
+
+
+ //sprintf(temp_buffer, "TIME = %d", TIME->value);
+ //log_error(temp_buffer, PLUS_STDERR);
+
+ if (TIME->value) {
+ //printf("--- %s\n", base_function);
+ eachturn();
+ }
+
+ return;
+}
+
+struct word_type *object_match(struct word_type *iterator, int noun_number) {
+ /* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
+ * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY OBJECT PLACE HOLDERS */
+
+ /* STORE WHETHER BY THE END AN OBJECT PLACEHOLDER WAS ENCOUNTERED AND
+ * DISPLAY A MESSAGE IF NO OBJECTS MATCHED */
+ int object_was_option = FALSE;
+
+ do {
+ /* THIS LOOP MEANS THAT CERTAIN ERRORS SUCH AS TAKING FROM A
+ * CLOSED CONTAINER CAN OCCUR MORE THAN ONCE */
+ if ((iterator->word[0] == '*')) {
+ object_was_option = TRUE;
+ if (build_object_list(iterator, noun_number)) {
+ /* RETURN THE POINT IN THE GRAMMAR TREE THAT MATCHED TO
+ * CONTINUE ON FROM */
+ //printf("--- returned TRUE from build_object_list\n");
+ return (iterator);
+ }
+ }
+
+ if (custom_error == TRUE) {
+ /* AN ERROR OCCURED IN THE FIRST OBJECT PLACEHOLDER, DON'T
+ * TRY ANY OTHERS */
+ return (NULL);
+ }
+ } while ((iterator = iterator->next_sibling) != NULL);
+
+ /* THERE WERE NO OBJECT PLACE HOLDERS OR, IF THERE WERE, NO
+ * MATCHING OBJECTS COULD BE RESOLVED */
+ //printf("--- returning null from object_match\n");
+
+ if (object_was_option) {
+ /* NO OBJECT MATCHED, BUT AN OBJECT WAS VALID AT THIS POINT IN THE
+ * PLAYER'S COMMAND */
+ diagnose();
+ custom_error = TRUE;
+ }
+
+ return (NULL);
+}
+
+struct word_type *exact_match(struct word_type *pointer) {
+ /* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
+ * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY EXACT MATCHES WITH THE
+ * CURRENT WORD IN THE PLAYER'S COMMAND.
+ * AN EXACT MATCH IS ANYTHING THAT ISN'T AN OBJECT PLACE HOLDER. */
+ struct word_type *iterator = pointer;
+
+ do {
+ if (iterator->word[0] == '*') {
+ /* THIS IS AN OBJECT PLACE HOLDER, THEREFORE IGNORE AND
+ * KEEP LOOKING FOR EXACT MATCHES */
+ } else if (!strcmp(iterator->word, "$string")) {
+ add_cstring("$string", word[wp]);
+ last_exact = wp;
+ wp++;
+ return (iterator);
+ } else if (!strcmp(iterator->word, "$integer") &&
+ validate(word[wp])) {
+ add_cinteger("$integer", atoi(word[wp]));
+ last_exact = wp;
+ wp++;
+ return (iterator);
+ } else if (!strcmp(word[wp], iterator->word)) {
+ last_exact = wp;
+ wp++;
+ return (iterator);
+ }
+ } while ((iterator = iterator->next_sibling) != NULL);
+
+ /* THERE WERE NO EXACT MATCHES, SO RETURN FALSE */
+ return (NULL);
+}
+
+int is_terminator(struct word_type *scope_word) {
+ struct word_type *terminator = scope_word->first_child;
+
+ if (terminator != NULL) {
+ /* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
+ * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
+ do {
+ /* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
+ * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
+ * OF THE OBJECT REFERENCE PART OF THE COMMAND */
+ if (!strcmp(word[wp], terminator->word)
+ || (!strcmp(terminator->word, "$integer")
+ && validate(word[wp]))) {
+ return (TRUE);
+ }
+ } while ((terminator = terminator->next_sibling) != NULL);
+ }
+
+ return (FALSE);
+}
+
+int build_object_list(struct word_type *scope_word, int noun_number) {
+ /* THIS FUNCTION BUILDS A LIST OF OBJECTS FROM THE PLAYER'S COMMAND
+ * AND RETURNS THE NUMBER OF OBJECTS IN THAT LIST */
+
+ int index, counter;
+ int resolved_object;
+ char *except_word;
+
+ //printf("--- entering build object list starting at %s with a scope_word of %s\n", word[wp], scope_word->word);
+ /* LOOK AHEAD FOR A FROM CLAUSE AND STORE from_object IF SO */
+ if (get_from_object(scope_word, noun_number) == FALSE) {
+ /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
+ * DISPLAYED */
+ //printf("--- from had an error\n");
+ return (FALSE);
+ }
+
+ while (word[wp] != NULL) {
+ /* LOOP THROUGH WORDS IN THE PLAYER'S INPUT ENDING WHEN EITHER
+ * THERE ARE NO MORE WORDS OR ONE OF THE CHILD NODES OF THE
+ * CURRENT scope_word NODE IS REACHED INDICATING THERE ARE NO
+ * MORE OBJECTS TO RESOLVE */
+
+ if (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value) ||
+ !strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value)) {
+ /* START ADDING ALL FUTURE RESOLVED OBJECTS TO A SECOND LIST
+ * TO REMOVE FROM THE FIRST */
+ except_word = word[wp];
+
+ wp++;
+
+ if (word[wp] != NULL && !strcmp(word[wp], cstring_resolve("FOR_WORD")->value)) {
+ /* SKIP PAST THE WORD 'FOR' */
+ wp++;
+ }
+
+ /* LOOK FORWARD FOR A FROM CLAUSE */
+ if (get_from_object(scope_word, noun_number) == FALSE) {
+ /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
+ * DISPLAYED */
+ return (FALSE);
+ }
+
+ /* MOVE TO THE SECOND LIST THAT WILL ULTIMATELY BE SUBTRACTED
+ * FROM THE FIRST LIST */
+ if (noun_number < 2) {
+ /* CREATE A 'them' SET THAT CAN BE REFERRED TO IN THE
+ * 'except' CLAUSE */
+ set_them(noun_number);
+ /* JUMP TO THE 'EXCEPT' LIST THAT CORRESPONDS TO THIS
+ * RESOLVED LIST */
+ noun_number = noun_number + 2;
+ } else {
+ sprintf(error_buffer, cstring_resolve("DOUBLE_EXCEPT")->value, except_word);
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);
+ }
+ } else if (after_from != -1 && !strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
+ /* SET THE WORD POINTER TO AFTER THE ALREADY-PROCESSED FROM
+ * CLAUSE (IF ONE EXISTED) AND CONTINUE */
+ wp = after_from;
+ //printf("--- hit from in processing moving on to word '%s'\n", word[wp]);
+
+ /* IF NO OBJECTS WERE MATCHED BY THE TIME WE HIT FROM THEN EITHER
+ * PRESUME THE PLAYER MEANT 'ALL FROM' OR PRINT AN ERROR */
+ if (list_size[noun_number] == 0) {
+ //printf("--- adding all due to empty list.\n");
+ add_all(scope_word, noun_number);
+ }
+
+ /* LOOK FOR THE NEXT FROM CLAUSE */
+ if (get_from_object(scope_word, noun_number) == FALSE) {
+ /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
+ * DISPLAYED */
+ return (FALSE);
+ }
+ } else if (!strcmp("then", word[wp])) {
+ break;
+ } else if (is_terminator(scope_word)) {
+ /* THERE ARE NO MORE OBJECTS TO RESOLVE */
+
+ //printf("--- %s is a build list terminator\n", word[wp]);
+ break;
+ } else if (!strcmp(word[wp], "comma") ||
+ !strcmp(word[wp], cstring_resolve("AND_WORD")->value)) {
+ /* JUST MOVE ONTO THE NEXT WORD AND SEE WHAT COME NEXT */
+ wp++;
+ } else {
+ /* CALL noun_resolve TO FETCH THE FIRST MATCHING OBJECT */
+ /* FALSE INDICATES THAT WE ARE NOT LOOKING FOR A FROM OBJECT */
+ resolved_object = noun_resolve(scope_word, FALSE, noun_number);
+
+ if (resolved_object == -1) {
+ /* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
+ * BEING USED */
+ index = 0;
+
+ while (multiple_resolved[index] != 0) {
+ /* ADD ALL THE RESOLVED OBJECTS TO THE LIST */
+ add_to_list(noun_number, multiple_resolved[index]);
+ index++;
+ }
+ } else if (resolved_object) {
+ /* ADD IT TO THE LIST */
+ add_to_list(noun_number, resolved_object);
+ } else {
+ /* NO OBJECTS COULD BE RESOLVED, THIS MIGHT NOT BE A BAD
+ * THING YET IF THERE ARE OTHER OBJECT PLACEHOLDERS TO
+ * COME THAT ARE LESS RESTRICTIVE */
+ return (FALSE);
+ }
+ }
+ }
+
+ if (noun_number > 1 && list_size[noun_number] != 0) {
+ /* A SECOND EXCEPTION LIST EXISTS, SUBTRACT IT FROM THE FIRST */
+ //printf("--- there are some exceptions.\n");
+
+ //printf("--- first list: %d, second list: %d\n", max_size[FIRST_LIST], max_size[SECOND_LIST]);
+ /* LOOP THROUGH ALL THE ITEMS IN THE SECOND LIST */
+ for (index = 0; index < max_size[SECOND_LIST]; index++) {
+ if (object_list[SECOND_LIST][index] != 0) {
+ /* THIS OBJECT IS A REAL OBJECT SO LOOP THROUGH ALL THE ITEMS
+ * IN THE FIRST LIST */
+ //printf("--- exception object is %s\n", object[object_list[SECOND_LIST][index]]->label);
+ for (counter = 0; counter < max_size[FIRST_LIST]; counter++) {
+ /* LOOP THROUGH ALL THE OBJECTS IN THE FIRST LIST
+ * IF AN OBJECT FROM THE SECOND LIST EXISTS IN THE FIRST
+ * LIST, REMOVE IT */
+ //printf("--- comparing %s = %s\n", object[object_list[FIRST_LIST][counter]]->label, object[object_list[SECOND_LIST][index]]->label);
+ if (object_list[FIRST_LIST][counter] ==
+ object_list[SECOND_LIST][index]) {
+
+ //printf("--- removing object %s\n", object[object_list[FIRST_LIST][counter]]->label);
+ object_list[FIRST_LIST][counter] = 0;
+ list_size[FIRST_LIST]--;
+ }
+ }
+ }
+ }
+ }
+
+ if (noun_number > 1) {
+ /* IF THERE WERE EXCEPTIONS, MOVE BACK TO THE FIRST LIST */
+ noun_number = FIRST_LIST;
+ }
+
+ /* THE RETURN TRUE IF AN OBJECT COULD BE RESOLVED */
+ if (list_size[noun_number] != 0) {
+ /* SET THEM ARRAY */
+ set_them(noun_number);
+
+ return (TRUE);
+ } else {
+ /* THE LIST IS NOW EMPTY, DISPLAY ' I DON'T SEE WHAT YOU
+ * ARE REFERRING TO.' ERROR */
+ if (!strcmp(scope_word->word, "*held") ||
+ !strcmp(scope_word->word, "**held")) {
+ write_text(cstring_resolve("NONE_HELD")->value);
+ } else {
+ write_text(cstring_resolve("NO_OBJECTS")->value);
+ }
+
+ custom_error = TRUE;
+ return (FALSE);
+ }
+}
+
+void set_them(int noun_number) {
+ int index, counter;
+
+ if (list_size[noun_number] == 1) {
+ /* THERE IS ONLY ONE OBJECT LEFT IN THE LIST, FIND IT AND ADD
+ * TO THEM IF REQUIRED */
+ for (index = 0; index < max_size[noun_number]; index++) {
+ if (object_list[noun_number][index] != 0) {
+ if (object[object_list[noun_number][index]]->attributes & PLURAL) {
+ them[0] = object_list[noun_number][index];
+ them[1] = 0;
+ break;
+ }
+ }
+ }
+ } else {
+ /* THERE IS MORE THAN ONE OBJECT IN THE LIST SO COPY IT TO THE
+ * 'THEM' LIST */
+ counter = 0;
+
+ for (index = 0; index < max_size[noun_number]; index++) {
+ if (object_list[noun_number][index] != 0) {
+ them[counter] = object_list[noun_number][index];
+ //printf("--- storing %s in them list\n", object[them[counter]]->label);
+ counter++;
+ }
+ }
+
+ /* NULL TERMINATE THE LIST */
+ them[counter] = 0;
+ }
+}
+
+void add_all(struct word_type *scope_word, int noun_number) {
+ int index, counter;
+
+ //printf("--- trying to add all\n");
+ counter = 0;
+
+ for (index = 1; index <= objects; index++) {
+ if ((object[index]->MASS < HEAVY) &&
+ !(object[index]->attributes & LOCATION)) {
+ if (is_direct_child_of_from(index) &&
+ scope(index, scope_word->word, RESTRICT)) {
+ //printf("--- objects parent is %s\n", object[object[index]->PARENT]->label);
+ add_to_list(noun_number, index);
+ }
+ }
+ }
+}
+
+int is_child_of_from(int child) {
+ /* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
+ * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
+ int index = 0;
+
+ if (from_objects[0] == 0) {
+ /* THERE HAS BEEN NO FROM CLAUSE */
+ return (TRUE);
+ }
+
+ while (from_objects[index] != 0) {
+ //printf("--- in is_child from object is %s, child is %s\n", object[from_objects[index]]->label, object[child]->label);
+ /* THIS OLD WAY OF DOING THINGS ALLOWS SPECIFIC 'take thing from box'
+ * WHEN thing IS INSIDE SOMETHING ELSE INSIDE box. THAT IS KINDA COOL
+ * BUT NOT PARTICULARLY NECESSARY. BY ONLY CHECKING IMMEDIATE CHILDREN
+ * THE NON-RESTRICTIVE VERSION OF parent_of CAN BE USED FROM SCOPE
+ * WHEN SAYING take all from box BECAUSE THIS FROM STOPS OBJECT IN
+ * OBJECTS FROM BEING TAKING FROM SOMETHING, SO scope DOESN'T HAVE
+ * TO. THIS BEHAVIOUR WOULD NOT BE NECESSARY WHEN NOT taking all from */
+
+ //if (parent_of(from_objects[index], child, RESTRICT)) {
+ if (object[child]->PARENT == from_objects[index]) {
+ //printf("--- %s is in %s\n", object[child]->label, object[from_objects[index]]->label);
+ return (TRUE);
+ }
+ index++;
+ }
+
+ return (FALSE);
+}
+
+int is_direct_child_of_from(int child) {
+ /* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
+ * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
+ int index = 0;
+
+ if (from_objects[0] == 0) {
+ /* THERE HAS BEEN NO FROM CLAUSE */
+ return (TRUE);
+ }
+
+ while (from_objects[index] != 0) {
+ //printf("--- in is_direct from object is %s\n", object[from_objects[index]]->label);
+ if (object[child]->PARENT == from_objects[index]) {
+ //printf("--- object %s is in the from object\n", object[child]->label);
+ return (TRUE);
+ }
+ index++;
+ }
+
+ return (FALSE);
+}
+
+int get_from_object(struct word_type *scope_word, int noun_number) {
+ /* THIS FUNCTION LOOKS AHEAD TO FIND IF THE CURRENT OBJECT REFERENCE
+ * IS QUALIFIED BY A 'FROM' WORD. IT RETURNS FALSE ON AN ERROR
+ * CONDITION AND TRUE OTHERWISE, REGARDLESS OF WHETHER A FROM OBJECT
+ * IS SPECIFIED */
+
+ int index, counter, from_object;
+
+ /* TAKE A COPY OF THE CURRENT VALUE OF wp */
+ int backup = wp;
+
+ /* SET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
+ struct word_type *terminator = scope_word->first_child;
+
+ /* SEE IF 'FROM' IS ONE OF THE TERMINATORS OF THIS CURRENT OBJECT
+ * PLACEHOLDER. IF SO, DON'T LOOK FOR A FROM OBJECT */
+ if (terminator != NULL) {
+ //printf("--- checking if terminator word (%s) is from\n", terminator->word);
+ if (!strcmp(cstring_resolve("FROM_WORD")->value, terminator->word)) {
+ //printf("--- from is a terminator, don't get a from object\n");
+ return (TRUE);
+ }
+ while ((terminator = terminator->next_sibling) != NULL);
+ }
+
+ /* LOOP FROM THE CURRENT WORD TO THE NEXT TERMINATOR AND LOOK FOR THE
+ * WORD 'FROM' AND STORE THE FOLLOWING OBJECT */
+ while (word[wp] != NULL) {
+ //printf("--- from loop checking %s\n", word[wp]);
+ if (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
+ from_word = word[wp];
+ wp++;
+
+ /* scope_word FOR THE CURRENT OBJECT IS PASSED ONLY SO
+ * noun_resolve CAN FIND OUT THE APPROPRIATE TERMINATORS
+ * THE ACCEPTABLE SCOPE FOR THE FROM OBJECT SHOULD BE
+ * AT LEAST *present */
+ /* TRUE INDICATES THAT WE ARE LOOKING FOR A FROM OBJECT */
+ from_object = noun_resolve(scope_word, TRUE, noun_number);
+
+ /* STORE THE wp FROM AFTER THE RESOLVED FROM OBJECT SO
+ * WE CAN JUMP FORWARD TO HERE AGAIN WHEN WE HIT THIS
+ * FROM CLAUSE IN PROCESSING (THIS FUNCTION IS A LOOK-AHEAD */
+ after_from = wp;
+
+ //printf("--- looked forward and found a from object of %s\n", object[from_object]->label);
+
+ if (from_object == -1) {
+ /* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
+ * BEING USED */
+ index = 0;
+ counter = 0;
+ /* LOOK THROUGH ALL THE OBJECTS RESOLVED AND CHECK THAT
+ * THEY ARE ALL VALID FROM OBJECTS */
+ while (multiple_resolved[index] != 0) {
+ if (verify_from_object(multiple_resolved[index]) == FALSE) {
+ /* AS SOON AS ONE IS BAD, ABORT THE FROM CLAUSE */
+ return (FALSE);
+ } else {
+ from_objects[counter] = multiple_resolved[index];
+ counter++;
+ }
+ index++;
+ }
+
+ /* NULL TERMINATE THE LIST */
+ from_objects[counter] = 0;
+
+ /* OBJECTS HAVE BEEN SET AND ARE VALID, RESTORE THE WORD
+ * POINTER */
+ wp = backup;
+ return (TRUE);
+ } else if (from_object) {
+ if (verify_from_object(from_object) == FALSE) {
+ return (FALSE);
+ } else {
+ /* ADD THIS OBJECT TO THE NULL TERMINATED LIST */
+ from_objects[0] = from_object;
+ from_objects[1] = 0;
+
+ /* OBJECT HAS BEEN SET AND IS VALID, RESTORE THE WORD
+ * POINTER */
+ wp = backup;
+ return (TRUE);
+ }
+ } else {
+ /* FROM UNKNOWN OBJECT, DISPLAY ERROR */
+ diagnose();
+ custom_error = TRUE;
+ return (FALSE);
+ }
+ //printf("--- finished processing from clause, next word is %s\n", word[wp]);
+ } else if (!strcmp(cstring_resolve("EXCEPT_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("BUT_WORD")->value, word[wp])) {
+ /* THIS IS THE LIMIT OF THE EFFECT ANY FROM OBJECT ANYWAY,
+ * SO TREAT IT LIKE A TERMINATOR */
+ //printf("--- %s is a get_from_object except word\n", word[wp]);
+ wp = backup;
+ return (TRUE);
+ } else if (is_terminator(scope_word) || !strcmp("then", word[wp])) {
+ /* THERE ARE NO MORE OBJECTS TO RESOLVE */
+
+ //printf("--- %s is a get_from_object terminator\n", word[wp]);
+ wp = backup;
+ return (TRUE);
+ }
+ wp++;
+ }
+
+ /* HIT THE END OF THE PLAYER'S COMMAND BEFORE A 'FROM' WORD */
+ //printf("--- no from clause specified in this block\n");
+ wp = backup;
+ return (TRUE);
+}
+
+int verify_from_object(int from_object) {
+ //printf("--- from object is %s\n", object[from_object]->label);
+ //if (!(object[from_object]->attributes & CONTAINER) &&
+ // !(object[from_object]->attributes & SURFACE) &&
+ // !(object[from_object]->attributes & ANIMATE)) {
+ // sprintf (error_buffer, FROM_NON_CONTAINER, from_word);
+ // write_text (error_buffer);
+ // custom_error = TRUE;
+ // return (FALSE);
+ //} else if (object[from_object]->attributes & CLOSED) {
+ if (object[from_object]->attributes & CONTAINER && object[from_object]->attributes & CLOSED) {
+ //printf("--- container is concealing\n");
+ if (object[from_object]->attributes & FEMALE) {
+ sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED_FEM")->value, sentence_output(from_object, TRUE));
+ } else {
+ sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED")->value, sentence_output(from_object, TRUE));
+ }
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);
+ /* IF THE PERSON IS CONCEALING, THEN THE OBJECT CAN'T BE REFERRED TO
+ * IF THE PERSON IS POSSESSIVE LET THE LIBRARY HANDLE THE RESPONSE
+ } else if (object[from_object]->attributes & POSSESSIVE) {
+ //printf("--- container is closed\n");
+ sprintf (error_buffer, PERSON_POSSESSIVE, sentence_output(from_object, TRUE));
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);
+ } else if (object[from_object]->attributes & CONCEALING) {
+ //printf("--- container is closed\n");
+ sprintf (error_buffer, PERSON_CONCEALING, sentence_output(from_object, TRUE));
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);*/
+ }
+
+ //printf("--- set from object just fine\n");
+ return (TRUE);
+}
+
+void add_to_list(int noun_number, int resolved_object) {
+ /* ADD THIS OBJECT TO THE OBJECT LIST DEPENDING */
+ /* AND SET IT, THEM, HER AND HIM */
+ if (!(object[resolved_object]->attributes & ANIMATE))
+ it = resolved_object;
+ if (object[resolved_object]->attributes & ANIMATE
+ && object[resolved_object]->attributes & FEMALE)
+ her = resolved_object;
+ if (object[resolved_object]->attributes & ANIMATE
+ && !(object[resolved_object]->attributes & FEMALE))
+ him = resolved_object;
+
+ //printf("--- adding_object %s to list %d at index %d\n", object[resolved_object]->label, noun_number, max_size[noun_number]);
+ object_list[noun_number][max_size[noun_number]] = resolved_object;
+ list_size[noun_number]++;
+ max_size[noun_number]++;
+}
+
+int noun_resolve(struct word_type *scope_word, int finding_from, int noun_number) {
+ /* THIS FUNCTION STARTS LOOKING AT THE PLAYER'S COMMAND FROM wp ONWARDS
+ * AND LOOKS FOR OBJECTS IN THE SCOPE SPECIFIED BY THE GRAMMAR ELEMENT
+ * POINTED TO BY THE PASSED pointer */
+
+ /* THIS IS SET TO TRUE WHEN THE CURRENT WORD MATCHES THE CURRENT OBJECT */
+ int object_matched;
+
+ /* THIS IS SET TO > 0 WHEN THE PLURAL FORM OF AN OBJECT IS USED */
+ int return_limit = 0;
+
+ int index;
+ int counter;
+ int first_word = TRUE;
+
+ struct word_type *terminator = scope_word->first_child;
+ struct name_type *current_name;
+
+ matches = 0;
+ highest_confidence = 0;
+ prime_suspect = 0;
+ done = FALSE;
+ backup_pointer = wp;
+ everything = FALSE;
+
+ if (word[wp] == NULL) {
+ /* NOTHING TO RESOLVE... */
+ return (FALSE);
+ }
+
+ /* SET THE CONFIDENCE FOR EACH OBJECT TO 1 TO INDICATE THAT THEY ARE
+ * ALL EQUALLY POSSIBLE AT THE MOMENT. SET TO ZERO TO DISCOUNT */
+ for (index = 1; index <= objects; index++)
+ confidence[index] = 1;
+
+ /* CLEAR THE OBJECT NAME BEFORE BUILDING IT WAS WE GO ALONG */
+ object_name[0] = 0;
+
+ /* CHECK FOR A QUANTITY QUALIFIER */
+ if (validate(word[wp])) {
+ /* FIRST WORD IS AN INTEGER AND SECOND WORD IS 'OF' SO
+ * TREAT THIS AS A LIMIT QUALIFIER BEFORE STARTING TO
+ * PROCESS THE REST OF THE WORDS */
+ if (word[wp + 1] != NULL && !strcmp(word[wp + 1], cstring_resolve("OF_WORD")->value)) {
+ return_limit = atoi(word[wp]);
+
+ /* MAKE SURE THE RETURN LIMIT IS SOMETHING SENSIBLE */
+ if (return_limit < 1) {
+ return_limit = 1;
+ }
+
+ object_expected = TRUE;
+ strcpy(object_name, word[wp]);
+ strcat(object_name, " ");
+ strcat(object_name, cstring_resolve("OF_WORD")->value);
+
+ /* MOVE THE WORD POINTER TO AFTER THE 'OF' */
+ wp = wp + 2;
+ }
+ /* IF AN INTEGER IS NOT FOLLOWED BY 'OF', PRESUME IT IS AN OBJECT
+ * NAME */
+ }
+
+ // CLEAR THE ERROR BUFFER AND USE IT TO STORE ALL THE WORDS THE PLAYER
+ // HAS USED TO REFER TO THE OBJECT
+ error_buffer[0] = 0;
+
+ while (word[wp] != NULL) {
+ // ADD THE WORDS USED TO error_buffer FOR POSSIBLE USE
+ // IN A DISABMIGUATE EMESSAGE
+ if (first_word == FALSE) {
+ strcat(error_buffer, " ");
+ }
+ strcat(error_buffer, word[wp]);
+ first_word = FALSE;
+
+ /* LOOP THROUGH WORDS IN THE PLAYER'S INPUT */
+
+ /* RESET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
+ terminator = scope_word->first_child;
+
+ if (terminator != NULL) {
+ /* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
+ * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
+ do {
+ /* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
+ * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
+ * OF THE OBJECT REFERENCE PART OF THE COMMAND */
+ //printf("--- checking terminator word %s\n", terminator->word);
+ if (!strcmp(word[wp], terminator->word)
+ || (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value))
+ || (!strcmp(word[wp], cstring_resolve("AND_WORD")->value))
+ || (!strcmp(word[wp], "comma"))
+ || (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value))
+ || (!strcmp(word[wp], cstring_resolve("THEN_WORD")->value))
+ || (!strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value))
+ || (!strcmp(terminator->word, "$integer")
+ && validate(word[wp]))) {
+ if (!matches) {
+ /* A TERMINATOR HAS BEEN FOUND BEFORE A
+ * SINGLE MATCHING OBJECT NAME. */
+ return (FALSE);
+ } else {
+ /* A TERMINATOR HAS BEEN FOUND, BUT NOT
+ * BEFORE MATCHING OBJECT NAMES. JUMP FORWARD TO
+ * RESOLVING INTO OBJECT NUMBER(S) */
+ done = TRUE;
+ break;
+ }
+ }
+ } while ((terminator = terminator->next_sibling) != NULL);
+ }
+
+ if (done == TRUE) {
+ /* A TERMINATING WORD HAS BEEN REACHED, SO DON'T TEST THIS
+ * WORD AGAINST THE OBJECT NAMES, JUST MOVE ON TO CHOSING
+ * AN OBJECT. */
+ //printf("--- %s is a terminator\n", word[wp]);
+ break;
+ }
+
+ //puts("--- passed checking for a terminator");
+
+ /* ADD THE CURRENT WORD TO THE NAME OF THE OBJECT THE PLAYER
+ * IS TRYING TO REFER TO FOR USE IN AN ERROR MESSAGE IF
+ * LATER REQUIRED */
+ if (object_name[0] != 0)
+ strcat(object_name, " ");
+
+ strcat(object_name, word[wp]);
+
+ if (!strcmp("everything", word[wp])) {
+ /* ALL THIS NEEDS TO SIGNIFY IS THAT IT IS OKAY TO RETURN MULTIPLE
+ * RESULTS. EVERYTHING ELSE SHOULD TAKE CARE OF ITSELF GIVEN ALL
+ * OBJECTS START OF WITH A CONFIDENCE OF 1, AND ALL OBJECTS WITH
+ * A CONFIDENCE > 0 ARE RETURNED WHEN return_limit > 1 */
+ if (return_limit == FALSE) {
+ return_limit = MAX_OBJECTS;
+ }
+
+ /* THIS IS USED TO ALTER THE BEHAVIOUR OF SCOPE SELECTION LATER */
+ everything = TRUE;
+
+ matches = 0;
+
+ //printf("--- entering for loop\n");
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ matches++;
+ }
+ }
+ //printf("--- exiting for loop\n");
+
+ if (word[wp + 1] != NULL && !strcmp(cstring_resolve("OF_WORD")->value, word[wp + 1])) {
+ /* MOVE PAST THE 'OF' IF IT IS NEXT */
+ wp++;
+ }
+ //printf("--- finished setting matches to %d for all\n", matches);
+ } else {
+ /* THE CURRENT WORD IS NOT ONE OF THE NEXT POSSIBLE WORDS IN THE
+ * PLAYER'S COMMAND OR ANY OF THE SPECIAL MEANING WORDS, THEREFORE
+ * TEST IT AGAINST ALL OF THE NAMES OF ALL OF THE OBJECTS. */
+ for (index = 1; index <= objects; index++) {
+ if (!confidence[index]) {
+ /* SKIP OBJECTS THAT HAVE ALREADY
+ * BEEN EXCLUDED BY A PREVIOUS WORD */
+ continue;
+ }
+
+ /* NEXT WORD IN PLAYERS INPUT IS YET TO
+ * BE TESTED AGAINST ALL THIS OBJECT'S NAMES */
+ object_matched = FALSE;
+
+ if (!strcmp(cstring_resolve("IT_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("ITSELF_WORD")->value, word[wp])) {
+ if (it == FALSE) {
+ no_it();
+ return (FALSE);
+ } else {
+ if (index == it) {
+ object_matched = TRUE;
+ }
+ }
+ } else if (!strcmp(cstring_resolve("HER_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("HERSELF_WORD")->value, word[wp])) {
+ if (her == FALSE) {
+ no_it();
+ return (FALSE);
+ } else {
+ if (index == her) {
+ object_matched = TRUE;
+ }
+ }
+ } else if (!strcmp(cstring_resolve("HIM_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("HIMSELF_WORD")->value, word[wp])) {
+ if (him == FALSE) {
+ no_it();
+ return (FALSE);
+ } else {
+ if (index == him) {
+ object_matched = TRUE;
+ }
+ }
+ } else if (!strcmp(cstring_resolve("THEM_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("THEMSELVES_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("ONES_WORD")->value, word[wp])) {
+ /* THIS NEED ONLY BE THE SIZE OF 'THEM', BUT NO HARM
+ * IN MAKING IT THE FULL SIZE */
+ if (return_limit == FALSE) {
+ return_limit = MAX_OBJECTS;
+ }
+
+ /* LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
+ * SEE IF THIS OBJECT IS PRESENT */
+ counter = 0;
+
+ while (them[counter] != 0) {
+ if (them[counter] == index) {
+ //printf("--- found previous them object %s\n", object[index]->label);
+ object_matched = TRUE;
+ break;
+ }
+ counter++;
+ }
+ /*} else if (!strcmp("1", word[wp])) {
+ return_limit = 1;
+
+ // LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
+ // SEE IF THIS OBJECT IS PRESENT
+ counter = 0;
+
+ while (them[counter] != 0) {
+ if (them[counter] == index) {
+ //printf("--- found previous them object %s\n", object[index]->label);
+ object_matched = TRUE;
+ break;
+ }
+ counter++;
+ }*/
+ } else {
+ current_name = object[index]->first_name;
+
+ while (current_name != NULL) {
+ /* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
+ if (!strcmp(word[wp], current_name->name)) {
+ /* CURRENT WORD MATCHES THE CURRENT NAME
+ *OF THE CURRENT OBJECT */
+ //printf("--- %s is a name match of object %d\n", word[wp], index);
+ object_matched = TRUE;
+
+ /* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
+ * NOT HAVE THE SAME NAME TWICE */
+ break;
+ }
+ current_name = current_name->next_name;
+ }
+
+ /* NOW LOOP THROUGH ALL THE OJBECTS PLURAL NAMES */
+ current_name = object[index]->first_plural;
+
+ while (current_name != NULL) {
+ /* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
+ if (!strcmp(word[wp], current_name->name)) {
+ /* CURRENT WORD MATCHES THE CURRENT NAME
+ *OF THE CURRENT OBJECT */
+ //printf("--- %s is a plural name match of object %d\n", word[wp], index);
+ object_matched = TRUE;
+
+ /* IT IS NOW OKAY FOR THIS FUNCTION TO RETURN MORE
+ * THAT ONE MATCHING OBJECT AS THE PLURAL FORM
+ * HAS BEEN USED. THIS IS INDICATED BY RETURNING
+ * -1 TO INDICATED THAT THE OBJECT LIST IS STORED
+ * IN A GLOBALLY ACCESSIBLE ARRAY */
+ if (return_limit == FALSE) {
+ return_limit = MAX_OBJECTS;
+ }
+
+ /* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
+ * NOT HAVE THE SAME NAME TWICE */
+ break;
+ }
+ current_name = current_name->next_name;
+ }
+ }
+
+ if (object_matched) {
+ /* THE CURRENT WORD MATCHES ONE OF THE NAMES OF THE
+ * CURRENT OBJECT. */
+ if (confidence[index] == 1) {
+ /* OBJECT HAD NOT YET BEEN COUNTED AS MATCH SO INCREMENT
+ * THE NUMBER OF OBJECTS THAT MATCH SO FAR. */
+ matches++;
+ }
+ if (confidence[index] != FALSE) {
+ /* IF OBJECT HAS NOT BEEN EXCLUDED, INCREMENT THE
+ * NUMBER OF NAMES IT HAS MATCHING. */
+ confidence[index]++;
+ }
+ } else {
+ /* THE CURRENT WORD IS NOT ONE OF THE NAMES OF THE
+ * CURRENT OBJECT.
+ * IF THE OBJECT HAD PREVIOUSLY BEEN A CANDIDATE, DECREMENT
+ * THE NUMBER OF MATCHING OBJECTS. */
+ if (confidence[index] > 1)
+ matches--;
+
+ /* AS THE CURRENT WORD DID NOT MATCH ANY OF THE NAMES OF
+ * THE THIS OBJECT, EXCLUDE IT FROM CONTENTION. */
+ confidence[index] = FALSE;
+ }
+ } /* MOVE ON TO NEXT OBJECT FOR IN LOOP */
+ }
+
+ if (!matches) {
+ /* IF THERE ARE NO REMAINING MATCHES DON'T MOVE ON TO THE NEXT
+ * WORD IN THE PLAYER'S INPUT. */
+ //printf("--- %s isnt a name match for any object\n", word[wp]);
+
+ /* THIS WORD IS A LIKELY BE INCORRECT AS IT DIDN'T MATCH
+ * ANY OBJECTS */
+ if (oops_word == -1 && word[wp] != NULL) {
+ oops_word = wp;
+ }
+
+ break;
+ }
+
+ /* MOVE ON TO THE NEXT WORD IN THE PLAYER'S INPUT. */
+ //printf("--- moving on to next word\n");
+ wp++;
+ }
+
+ //printf("--- finished processing words\n");
+
+ /***************************************************
+ * FINISHED LOOKING THROUGH THE PLAYER'S INPUT NOW *
+ * MOVE THE POINTER BACK FOR PARSER TO PROCESS *
+ * THIS SHOULDN'T BE DONE FOR A WORD LIKE 'AND' *
+ ***************************************************/
+
+ if (return_limit == FALSE) {
+ /* THE RETURN LIMIT WAS NEVER SET, SO TREAT THIS AS AN IMPLICIT 1 */
+ return_limit = 1;
+ }
+
+ if (matches == 0) {
+ /* THE PLAYER'S INPUT COULD NOT BE RESOLVED TO A SINGLE OBJECT
+ * BUT NO TERMINATING WORD WAS USED BEFORE A NON-TERMINATING
+ * WORD IN A PLACE WHERE AN OBJECT COULD BE SPECIFIED ---
+ * IN OTHER WORDS, PRESUME THE PLAYER WAS TRYING TO REFER TO
+ * AN OBJECT BUT SOMEHOW GOT IT WRONG */
+
+ //printf("--- matches = 0\n");
+ /* THIS VARIABLE IS USED TO CHANGE THE BEHAVIOR OF diagnose()
+ * SHOULD THIS COMMAND TURN OUT TO BE INVALID */
+ object_expected = TRUE;
+
+ wp = backup_pointer; /* BACK UP THE CURRENT WORD POINTER.
+ * SO THE PARSER CAN INVESTIAGE THE
+ * POSIBILITY THAT THIS ISN'T A
+ * REFERENCE TO AN OBJECT AT ALL */
+
+ return (FALSE); /* RETURN TO PARSER WITH NO MATCHING
+ * OBJECT HAVING BEEN FOUND. */
+ }
+
+ //printf("--- starting with %d matches\n", matches);
+
+ /* LOOP THROUGH ALL OBJECTS AND REMOVE FROM ANY OBJECT THAT IS NOT IN THE
+ * CURRENT LOCATION, PROVIDED THE VERB DOESN'T ALLOW THE OBJECT TO
+ * BE *ANYWHERE. */
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE && strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere") && strcmp(scope_word->word, "*location")) {
+ if (scope(index, "*present", UNRESTRICT) == FALSE) {
+ matches--;
+ confidence[index] = FALSE;
+ //printf("--- removing %s for not being present\n", object[index]->label);
+ }
+ }
+
+ /* IF THE OBJECT IS IMPLICITLY INCLUDED DUE TO A MULTIPLE RETURN
+ * LIMIT, REMOVE IT IF IT IS PART OF THE SCENERY */
+ if (confidence[index] == 1 && return_limit > 1) {
+ if ((object[index]->MASS >= HEAVY) ||
+ (object[index]->attributes & LOCATION)) {
+ matches--;
+ confidence[index] = 0;
+ //printf("--- removing %s for being scenery\n", object[index]->label);
+ }
+ }
+
+ /* FOR ALL CONTENDERS, CALCULATE THE CONFIDENCE AS A PERCENTAGE,
+ * SO LONG AS WE ARE STILL LOOKING FOR A SINGLE OBJECT */
+ if (confidence[index] != FALSE && return_limit == 1) {
+ current_name = object[index]->first_name;
+ counter = 0;
+ while (current_name != NULL) {
+ counter++;
+ current_name = current_name->next_name;
+ }
+ confidence[index] = ((confidence[index] - 1) * 100) / counter;
+ }
+ }
+
+ // CALCULATE THE HIGHEST CONFIDENCE OF ALL THE POSSIBLE OBJECTS
+ highest_confidence = 0;
+
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] > highest_confidence) {
+ highest_confidence = confidence[index];
+ }
+ }
+
+ /* REMOVE ANY OBJECTS THAT ARE NOT IN THEIR VERB'S SPECIFIED SCOPE */
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ if (scope(index, "*present", UNRESTRICT) != FALSE) {
+ // TAKE SPECIAL NOT OF AN OBJECT THAT IS PRESENT
+ // AND AT LEAST EQUAL FOR THE HIGHEST CONFIDENCE
+ // IN CASE NO OBJECT ARE LEFT AFTER SPECIFIED SCOPE
+ // IS USED TO FILTER THE LIST */
+ if (confidence[index] == highest_confidence) {
+ prime_suspect = index;
+ }
+ //printf("--- storing %s as prime_suspect\n", object[index]->label);
+ }
+
+ if (finding_from) {
+ if (strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere")) {
+ if (scope(index, "*present") == FALSE) {
+ matches--;
+ confidence[index] = FALSE;
+ continue;
+ }
+ }
+ } else if (scope(index, scope_word->word, (everything && !from_objects[0])) == FALSE) {
+ /* IF everything IS TRUE, scope IS RESTRICTED */
+ //printf("--- removing %s due to regular scope\n", object[index]->label);
+ matches--;
+ confidence[index] = FALSE;
+ continue;
+ }
+
+ /* CHECK IF THIS OBJECT IS IN ACCORDANCE WITH ANY FROM CLAUSE
+ * THAT MAY OR MAY NOT HAVE USED */
+ if (is_child_of_from(index) == FALSE) {
+ matches--;
+ confidence[index] = FALSE;
+ //printf("--- removing %s due to from clause\n", object[index]->label);
+ }
+ }
+ }
+
+ //printf("--- there are %d matches left\n", matches);
+
+ /* THIS LOOP REMOVES ANY OBJECT THAT ARE NOT EQUAL TO THE HIGHEST
+ * CONFIDENCE UNLESS A PLURAL NAME WAS USED. IN THAT CASE, ONLY
+ * EXCLUDE OBJECTS THAT DO NOT HAVE ONE OF THE NAMES SUPPLIED */
+ if (matches > 1 && return_limit == 1) {
+ // CALCULATE THE HIGHEST CONFIDENCE NOW THAT OBJECTS
+ // NOT IN SCOPE HAVE BEEN REMOVED
+ highest_confidence = 0;
+
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] > highest_confidence) {
+ highest_confidence = confidence[index];
+ }
+ }
+
+ //printf("--- removing lower confidence objects\n");
+ for (index = 1; index <= objects; index++) {
+ /* REMOVE ANY OBJECTS THAT ARE NOT EQUAL
+ TO THE HIGHEST CONFIDENCE. */
+ if (confidence[index] != FALSE) {
+ if (confidence[index] < highest_confidence) {
+ //printf("--- removing %s due to confidence of %d being under %d\n", object[index]->label, confidence[index], highest_confidence);
+ confidence[index] = FALSE;
+ matches--;
+ }
+ }
+ }
+ }
+
+ if (matches == 0) {
+ /* IF THIS LEAVES NO OBJECT, RETURN THE OBJECT THAT WAS PRESENT
+ * AND SET THE APPROPRIATE POINTERS. */
+ if (prime_suspect != FALSE) {
+ return (prime_suspect);
+ } else {
+ object_expected = TRUE;
+ wp = backup_pointer;
+ return (FALSE);
+ }
+ }
+
+ if (matches == 1) {
+ /* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
+ * APPROPRIATE POINTERS. */
+ //printf("--- only one object left\n");
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ return (index);
+ }
+ }
+ }
+
+ if (return_limit > 1) {
+ /* THE PLURAL NAME OF AN OBJECT WAS USED OR A QUANTITY QUALIFIER */
+
+ counter = 0;
+
+ //printf("--- return_limit == TRUE\n");
+
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ /* ADD EACH OBJECT TO multiple_resolved UNTIL
+ * THE return_limit IS REACHED */
+ multiple_resolved[counter] = index;
+ return_limit--;
+ //printf("--- adding %s to multiple_resolved\n", object[index]->label);
+ counter++;
+
+ if (return_limit == 0) {
+ break;
+ }
+ }
+ }
+
+ /* NULL TERMINATE THE LIST */
+ multiple_resolved[counter] = 0;
+
+ //printf("--- returning multiple objects\n");
+ /* RETURN -1 TO INDICATED THERE ARE MULTIPLE OBJECTS */
+ return (-1);
+ }
+
+ /* AN AMBIGUOUS REFERENCE WAS MADE. ATTEMPT TO CALL ALL THE disambiguate
+ * FUNCTIONS TO SEE IF ANY OF THE OBJECT WANTS TO TAKE PREFERENCE IN
+ * THIS CIRCUMSTANCE */
+ int situation = noun_number;
+
+ if (finding_from) {
+ situation += 4;
+ }
+
+ /*
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ strcpy(function_name, "disambiguate");
+ strcat(function_name, "_");
+ strcat(function_name, object[index]->label);
+ strcat(function_name, "<");
+ sprintf(temp_buffer, "%d", situation);
+ strcat(function_name, temp_buffer);
+
+ // CALL THE DISAMBIGUATION FUNCTION ATTACHED TO EACH OF THE
+ // POSSIBLE OBJECTS
+ int return_code = execute (function_name);
+
+ if (return_code == 1) {
+ // THIS OBJECT CAN CLAIMED OWNERSHIP OF THIS COMMAND
+ return (index);
+ } else if (return_code == -1) {
+ // THIS OBJECT HAS REJECTED OWNERSHIP OF THIS COMMAND
+ confidence[index] = FALSE;
+ matches--;
+ }
+ }
+ }
+ */
+
+ // CHECK IF ALL THE OBJECTS WERE REJECTED
+ if (matches == 0) {
+ return (prime_suspect);
+ }
+
+ // CHECK IF ONLY ONE OBJECT NOW REMAINS
+ if (matches == 1) {
+ /* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
+ * APPROPRIATE POINTERS. */
+ //printf("--- only one object left\n");
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ return (index);
+ }
+ }
+ }
+
+#if defined GLK || defined __NDS__
+ /* NO OBJECT HAS CLAIMED OWNERSHIP, PROMPT THE PLAYER TO SPECIFY
+ * WHICH ONE THEY REQUIRE. */
+ counter = 1;
+ write_text(cstring_resolve("BY")->value);
+ write_text(error_buffer);
+ write_text(cstring_resolve("REFERRING_TO")->value);
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ possible_objects[counter] = index;
+ sprintf(text_buffer, " [%d] ", counter);
+ write_text(text_buffer);
+ sentence_output(index, 0);
+ write_text(temp_buffer);
+ matches--;
+ if (counter < 9)
+ counter++;
+ write_text("^");
+ }
+ }
+
+ /* GET A NUMBER: don't insist, low = 1, high = counter */
+ selection = get_number(FALSE, 1, counter - 1);
+
+ if (selection == -1) {
+ write_text(cstring_resolve("INVALID_SELECTION")->value);
+ custom_error = TRUE;
+ return (FALSE);
+ } else {
+ write_text("^");
+ return (possible_objects[selection]);
+ }
+#else
+ /* IF MORE THAT ONE OBJECT STILL REMAINS, PROMPT THE PLAYER TO SPECIFY
+ * WHICH ONE THEY REQUIRE. */
+ write_text(cstring_resolve("WHEN_YOU_SAY")->value);
+ write_text(error_buffer);
+ write_text(cstring_resolve("MUST_SPECIFY")->value);
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ sentence_output(index, 0);
+ write_text(temp_buffer);
+ matches--;
+ if (matches == 0) {
+ write_text(".");
+ break;
+ } else if (matches == 1) {
+ write_text(cstring_resolve("OR_WORD")->value);
+ } else
+ write_text(", ");
+ }
+ }
+ write_text("^");
+
+ custom_error = TRUE;
+ return (FALSE);
+#endif
+}
+
+void diagnose() {
+ if (custom_error) {
+ TIME->value = FALSE;
+ return;
+ }
+ if (word[wp] == NULL)
+ write_text(cstring_resolve("INCOMPLETE_SENTENCE")->value);
+ else if (object_expected && wp != 0) {
+ write_text(cstring_resolve("UNKNOWN_OBJECT")->value);
+ write_text(object_name);
+ write_text(cstring_resolve("UNKNOWN_OBJECT_END")->value);
+ } else {
+ write_text(cstring_resolve("CANT_USE_WORD")->value);
+ write_text(word[wp]);
+ write_text(cstring_resolve("IN_CONTEXT")->value);
+ }
+ TIME->value = FALSE;
+}
+
+int scope(int index, char *expected, int restricted) {
+ /* THIS FUNCTION DETERMINES IF THE SPECIFIED OBJECT IS IN THE SPECIFIED
+ * SCOPE - IT RETURNS TRUE IF SO, FALSE IF NOT. */
+
+ int temp = 0;
+
+ /* WHEN THE ARGUMENT restricted IS TRUE IT HAS A MORE LIMITED
+ * SENSE OF WHAT IS ACCEPTABLE */
+
+ if (!strcmp(expected, "*held") || !strcmp(expected, "**held")) {
+ if (object[index]->PARENT == HELD) {
+ return (TRUE);
+ } else if (object[index]->MASS >= HEAVY) {
+ /* ALLOW AND OBJECT TO BE CONSIDERED HELD IF IT HAS A
+ * MASS OF HEAVY OR GREATER AND ITS DIRECT PARENT IS
+ * BEING HELD. IE, A LABEL ON A JAR CAN BE SHOWN BECAUSE
+ * THE JAR IS BEING HELD. */
+ temp = object[index]->PARENT;
+ if (temp > 0 && temp < objects) {
+ if (object[temp]->PARENT == HELD) {
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+ } else {
+ return (FALSE);
+ }
+ } else if (!strcmp(expected, "*location")) {
+ if (object[index]->attributes & LOCATION) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ } else if (!strcmp(expected, "*here") || !strcmp(expected, "**here")) {
+ if (object[index]->PARENT == HERE || index == HERE) {
+ /* THE OBJECT IN QUESTION IS IN THE PLAYER'S CURRENT LOCATION
+ * OR IS THE PLAYER'S CURRENT LOCATION. */
+ return (TRUE);
+ } else if (object[index]->PARENT == HELD) {
+ /* IT IS ONLY A PROBLEM FOR THE OBJECT TO BE AN IMMEDIATE CHILD
+ * OF THE PLAYER. THE PLAYER CAN STILL TAKE AN OBJECT THAT IS IN
+ * SOMETHING THEY ARE CARRYING */
+ return (FALSE);
+ } else {
+ /* IS THE OBJECT A CHILD OF THE CURRENT LOCATION SOMEHOW */
+ return (parent_of(HERE, index, restricted));
+ }
+ } else if (!strcmp(expected, "*anywhere") || !strcmp(expected, "**anywhere")) {
+ return (TRUE);
+ } else if (!strcmp(expected, "*inside") || !strcmp(expected, "**inside")) {
+ if (object_list[0][0] > 0 && object_list[0][0] < objects) {
+ return (parent_of(object_list[0][0], index, restricted));
+ } else {
+ // THERE IS NO PREVIOUS OBJECT SO TREAT THIS LIKE A *here
+ return (parent_of(HERE, index, restricted));
+ }
+ } else if (!strcmp(expected, "*present") || !strcmp(expected, "**present")) {
+ if (index == HERE) {
+ return (TRUE);
+ } else {
+ if (find_parent(index)) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ }
+ } else {
+ unkscorun(expected);
+ return (FALSE);
+ }
+}
+
+int find_parent(int index) {
+ /* THIS FUNCTION WILL SET THE GLOBAL VARIABLE parent TO
+ * THE OBJECT THAT IS AT THE TOP OF THE POSSESSION TREE.
+ * IT WILL RETURN TRUE IF THE OBJECT IS VISIBLE TO THE
+ * PLAYER */
+ //printf("--- find parent of %s\n", object[index]->label);
+
+ if (!(object[index]->attributes & LOCATION) &&
+ object[index]->PARENT != NOWHERE) {
+
+ parent = object[index]->PARENT;
+ //printf("--- parent is %s\n", object[parent]->label);
+
+ if (index == parent) {
+ /* THIS OBJECT HAS ITS PARENT SET TO ITSELF */
+ sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
+ log_error(error_buffer, PLUS_STDOUT);
+ return (FALSE);
+ } else if (!(object[parent]->attributes & LOCATION)
+ && ((object[parent]->attributes & CLOSED && object[parent]->attributes & CONTAINER)
+ || object[parent]->attributes & CONCEALING)) {
+ //printf("--- %s is closed, so return FALSE\n", object[parent]->label);
+ return (FALSE);
+ } else if (parent == HERE || parent == HELD) {
+ /* THE OBJECT IS THE PLAYER'S CURRENT LOCATION OR BEING HELD */
+ return (TRUE);
+ } else {
+ if (object[parent]->attributes & LOCATION) {
+ //printf("--- %s is a location, so dont recuse\n", object[parent]->label);
+ return (FALSE);
+ } else {
+ //printf("--- %s isnt a location, so recuse\n", object[parent]->label);
+ return (find_parent(parent));
+ }
+ }
+ } else {
+ if (index == HERE)
+ /* THE OBJECT IS THE PLAYER'S CURRENT LOCATION. */
+ return (TRUE);
+ else
+ return (FALSE);
+ }
+}
+
+int parent_of(int parent, int child, int restricted) {
+ /* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL
+ * 'PARENT' IS REACHED (RETURN TRUE), OR THE TOP OF THE TREE OR A CLOSED
+ * OR CONCEALING OBJECT IS REACHED (RETURN FALSE). */
+
+ /* restricted ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN AN
+ * OBJECT WITH A mass OF heavy OR LESS THAT IS NOT THE SUPPLIED
+ * PARENT ie. DON'T ACCEPT OBJECTS IN SUB OBJECTS */
+
+ int index;
+
+ //printf("--- parent is %s, child is %s\n", object[parent]->label, object[child]->label);
+ if (child == parent) {
+ return (TRUE);
+ } else if (!(object[child]->attributes & LOCATION) &&
+ object[child]->PARENT != NOWHERE) {
+ /* STORE THE CHILDS PARENT OBJECT */
+ index = object[child]->PARENT;
+ //printf("--- %s is the parent of %s\n", object[index]->label, object[child]->label);
+
+ if (index == child) {
+ /* THIS CHILD HAS IT'S PARENT SET TO ITSELF */
+ sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
+ log_error(error_buffer, PLUS_STDOUT);
+ //printf("--- self parent.\n");
+ return (FALSE);
+ } else if (!(object[index]->attributes & LOCATION)
+ && ((object[index]->attributes & CLOSED && object[index]->attributes & CONTAINER)
+ || object[index]->attributes & CONCEALING)) {
+ /* THE CHILDS PARENT IS CLOSED OR CONCEALING - CANT BE SEEN */
+ //printf("--- parent %s is closed\n", object[index]->label);
+ return (FALSE);
+ } else if (restricted && object[index]->MASS < HEAVY && index != parent) {
+ //printf("--- scenery object.\n");
+ return (FALSE);
+ } else {
+ //printf("--- comparing %s with %s\n", object[index]->label, object[parent]->label);
+ if (index == parent) {
+ /* YES, IS PARENT OF CHILD */
+ return (TRUE);
+ } else if (object[index]->attributes & LOCATION) {
+ return (FALSE);
+ } else {
+ /* KEEP LOOKING UP THE TREE TILL THE CHILD HAS NO MORE
+ * PARENTS */
+ return (parent_of(parent, index, restricted));
+ }
+ }
+ } else {
+ /* THE SPECIFIED OBJECT HAS NO PARENT */
+ return (FALSE);
+ }
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/prototypes.h b/engines/glk/jacl/prototypes.h
new file mode 100644
index 0000000000..dabfb9d1f6
--- /dev/null
+++ b/engines/glk/jacl/prototypes.h
@@ -0,0 +1,200 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 JACL {
+
+#ifdef GLK
+strid_t open_glk_file(uint usage, uint mode, char *filename);
+glui32 glk_get_bin_line_stream(strid_t file_stream, char *buffer, glui32 max_length);
+glui32 parse_utf8(unsigned char *buf, glui32 buflen, glui32 *out, glui32 outlen);
+void convert_to_utf8(glui32 *text, int len);
+glui32 parse_utf8(unsigned char *buf, glui32 buflen, glui32 *out, glui32 outlen);
+#else
+void update_parameters();
+#endif
+
+int bearing(double x1, double y1, double x2, double y2);
+int distance(double x1, double y1, double x2, double y2);
+int strcondition();
+int and_strcondition();
+int logic_test(int first);
+int str_test(int first);
+int first_available(int list_number);
+int validate(char *string);
+int noun_resolve(struct word_type *scope_word, int finding_from, int noun_number);
+int get_from_object(struct word_type *scope_word, int noun_number);
+int is_direct_child_of_from(int child);
+int is_child_of_from(int child);
+int verify_from_object(int from_object);
+int find_parent(int index);
+int scope(int index, char *expected, int restricted = 0);
+int object_element_resolve(char *textstring);
+int execute(char *funcname);
+int object_resolve(char object_string[]);
+int random_number();
+void log_error(char *message, int console);
+int parent_of(int parent, int child, int restricted);
+int grand_of(int child, int objs_only);
+int check_light(int where);
+int find_route(int fromRoom, int toRoom, int known);
+int exit_function(int return_code);
+int count_resolve(char *textstring);
+void jacl_set_window(winid_t new_window);
+void create_cstring(char *name, char *value);
+void create_string(char *name, char *value);
+void create_integer(char *name, int value);
+void create_cinteger(char *name, int value);
+void scripting();
+void undoing();
+void walking_thru();
+void create_paths(char *full_path);
+int get_key();
+char get_character(char *message);
+int get_yes_or_no();
+void get_string(char *string_buffer);
+int get_number(int insist, int low, int high);
+int save_interaction(char *filename);
+int restore_interaction(char *filename);
+void jacl_encrypt(char *string);
+void jacl_decrypt(char *string);
+void log_message(char *message, int console);
+void set_them(int noun_number);
+void preparse();
+void inspect(int object_num);
+void add_all(struct word_type *scope_word, int noun_number);
+void add_to_list(int noun_number, int resolved_object);
+void call_functions(char *base_name);
+int build_object_list(struct word_type *scope_word, int noun_number);
+long value_of(char *value, int run_time = 0);
+long attribute_resolve(char *attribute);
+long user_attribute_resolve(char *name);
+struct word_type *exact_match(struct word_type *pointer);
+struct word_type *object_match(struct word_type *iterator, int noun_number);
+struct integer_type *integer_resolve(char *name);
+struct integer_type *integer_resolve_indexed(char *name, int index);
+struct function_type *function_resolve(char *name);
+struct string_type *string_resolve(char *name);
+struct string_type *string_resolve_indexed(char *name, int index);
+struct string_type *cstring_resolve(char *name);
+struct string_type *cstring_resolve_indexed(char *name, int index);
+struct cinteger_type *cinteger_resolve(char *name);
+struct cinteger_type *cinteger_resolve_indexed(char *name, int index);
+int array_length_resolve(char *textstring);
+int legal_label_check(char *word, int line, int type);
+char *object_names(int object_index, char *names_buffer);
+char *arg_text_of(char *string);
+char *arg_text_of_word(int wordnumber);
+char *var_text_of_word(int wordnumber);
+char *text_of(char *string);
+char *text_of_word(int wordnumber);
+char *expand_function(char *name);
+int *container_resolve(char *container_name);
+int condition();
+int and_condition();
+void free_from(struct word_type *x);
+void eachturn();
+int jacl_whitespace(char character);
+int get_here();
+char *stripwhite(char *string);
+void command_encapsulate();
+void encapsulate();
+void jacl_truncate();
+void parser();
+void diagnose();
+void look_around();
+char *macro_resolve(char *textstring);
+char *plain_output(int index, int capital);
+char *sub_output(int index, int capital);
+char *obj_output(int index, int capital);
+char *that_output(int index, int capital);
+char *sentence_output(int index, int capital);
+char *isnt_output(int index, bool unused = false);
+char *is_output(int index, bool unused = false);
+char *it_output(int index, bool unused = false);
+char *doesnt_output(int index, bool unused = false);
+char *does_output(int index, bool unused = false);
+char *list_output(int index, int capital);
+char *long_output(int index);
+void terminate(int code);
+void set_arguments(char *function_call);
+void pop_stack();
+void push_stack(int32 file_pointer);
+void pop_proxy();
+void push_proxy();
+void write_text(char *string_buffer);
+void status_line();
+void newline();
+int save_game(frefid_t saveref);
+int restore_game(frefid_t saveref, int warn);
+void write_integer(strid_t stream, int x);
+int read_integer(strid_t stream);
+void write_long(strid_t stream, long x);
+long read_long(strid_t stream);
+void save_game_state();
+void restore_game_state();
+void add_cstring(char *name, char *value);
+void clear_cstring(char *name);
+void add_cinteger(char *name, int value);
+void clear_cinteger(char *name);
+void restart_game();
+void read_gamefile();
+void new_position(double x1, double y1, double bearing, double velocity);
+void build_grammar_table(struct word_type *pointer);
+void unkvalerr(int line, int wordno);
+void totalerrs(int errors);
+void unkatterr(int line, int wordno);
+void unkfunrun(char *name);
+void nofnamerr(int line);
+void nongloberr(int line);
+void unkkeyerr(int line, int wordno);
+void maxatterr(int line, int wordno);
+void unkattrun(int wordno);
+void badptrrun(char *name, int value);
+void badplrrun(int value);
+void badparrun();
+void notintrun();
+void noproprun(int unused = 0);
+void noproperr(int line);
+void noobjerr(int line);
+void unkobjerr(int line, int wordno);
+void unkobjrun(int wordno);
+void unkdirrun(int wordno);
+void unkscorun(char *scope);
+void unkstrrun(char *variable);
+void unkvarrun(char *variable);
+void outofmem();
+void set_defaults();
+void no_it();
+void more(char *message);
+int jpp();
+int process_file(char *sourceFile1, char *sourceFile2);
+char *strip_return(char *string);
+char *object_generator(char *text, int state);
+char *verb_generator(char *text, int state);
+void add_word(char *word);
+void create_language_constants();
+int select_next();
+void jacl_sleep(unsigned int mseconds);
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/resolvers.cpp b/engines/glk/jacl/resolvers.cpp
new file mode 100644
index 0000000000..cf42911bfd
--- /dev/null
+++ b/engines/glk/jacl/resolvers.cpp
@@ -0,0 +1,1320 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+#ifdef GLK
+extern glui32 status_width, status_height;
+extern winid_t statuswin;
+#endif
+
+#ifdef __NDS__
+extern int screen_width;
+extern int screen_depth;
+#endif
+
+extern struct object_type *object[];
+extern struct integer_type *integer_table;
+extern struct integer_type *integer[];
+extern struct cinteger_type *cinteger_table;
+extern struct attribute_type *attribute_table;
+extern struct string_type *string_table;
+extern struct string_type *cstring_table;
+extern struct function_type *function_table;
+extern struct function_type *executing_function;
+extern struct command_type *completion_list;
+extern struct word_type *grammar_table;
+extern struct synonym_type *synonym_table;
+extern struct filter_type *filter_table;
+
+extern char function_name[];
+extern char temp_buffer[];
+extern char error_buffer[];
+extern char integer_buffer[16];
+
+#ifndef GLK
+#ifndef __NDS__
+extern char game_url[];
+extern char user_id[];
+#endif
+#endif
+
+extern int noun[];
+extern int quoted[];
+extern int percented[];
+extern char *word[];
+
+extern int resolved_attribute;
+
+extern int objects;
+extern int integers;
+extern int player;
+extern int oec;
+extern int *object_element_address;
+extern int *object_backup_address;
+
+extern int value_resolved;
+
+char macro_function[84];
+int value_has_been_resolved;
+
+int *container_resolve(char *container_name) {
+ container_name = arg_text_of(container_name);
+
+ /* IN JACL, A 'CONTAINER' IS ANYTHING THAT CAN STORE AN INTEGER */
+ struct integer_type *resolved_integer;
+
+ if ((resolved_integer = integer_resolve(container_name)) != NULL)
+ return (&resolved_integer->value);
+ else if (object_element_resolve(container_name))
+ return (object_element_address);
+ else if (!strcmp(container_name, "noun1"))
+ return (&noun[0]);
+ else if (!strcmp(container_name, "noun2"))
+ return (&noun[1]);
+ else if (!strcmp(container_name, "noun3"))
+ return (&noun[2]);
+ else if (!strcmp(container_name, "noun4"))
+ return (&noun[3]);
+ else if (!strcmp(container_name, "player"))
+ return (&player);
+ else if (!strcmp(container_name, "here"))
+ return (&object[player]->PARENT);
+ else
+ return ((int *) NULL);
+}
+
+char *var_text_of_word(int wordnumber) {
+ char *value;
+
+ if (percented[wordnumber] == FALSE) {
+ return (word[wordnumber]);
+ } else {
+ value_has_been_resolved = TRUE;
+ value = arg_text_of(word[wordnumber]);
+ while (value_has_been_resolved && percented[wordnumber]) {
+ value = arg_text_of(value);
+ percented[wordnumber]--;
+ }
+
+ return (value);
+ }
+}
+
+char *arg_text_of_word(int wordnumber) {
+ char *value;
+
+ if (quoted[wordnumber] == 1) {
+ return (word[wordnumber]);
+ } else {
+ value_has_been_resolved = TRUE;
+ value = arg_text_of(word[wordnumber]);
+ while (value_has_been_resolved && percented[wordnumber]) {
+ value = arg_text_of(value);
+ percented[wordnumber]--;
+ }
+
+ return (value);
+ }
+}
+
+char *text_of_word(int wordnumber) {
+ char *value;
+
+ if (quoted[wordnumber] == 1) {
+ return (word[wordnumber]);
+ } else {
+ value_has_been_resolved = TRUE;
+ value = text_of(word[wordnumber]);
+ while (value_has_been_resolved && percented[wordnumber]) {
+ value = text_of(value);
+ percented[wordnumber]--;
+ }
+
+ return (value);
+ }
+}
+
+char *text_of(char *string) {
+ struct integer_type *resolved_integer;
+ struct cinteger_type *resolved_cinteger;
+ struct string_type *resolved_string;
+ struct string_type *resolved_cstring;
+ char *return_string;
+
+ int index;
+
+ /* CHECK IF THE SUPPLIED STRING IS THE NAME OF A STRING CONSTANT,
+ * IF NOT, RETURN THE STRING LITERAL */
+ if ((return_string = macro_resolve(string)) != NULL) {
+ value_has_been_resolved = FALSE;
+ return (return_string);
+ } else if ((resolved_integer = integer_resolve(string)) != NULL) {
+ value_has_been_resolved = FALSE;
+ integer_buffer[0] = 0;
+ sprintf(integer_buffer, "%d", resolved_integer->value);
+ return (integer_buffer);
+ } else if ((resolved_cinteger = cinteger_resolve(string)) != NULL) {
+ value_has_been_resolved = FALSE;
+ integer_buffer[0] = 0;
+ sprintf(integer_buffer, "%d", resolved_cinteger->value);
+ return (integer_buffer);
+ } else if (object_element_resolve(string)) {
+ value_has_been_resolved = FALSE;
+ integer_buffer[0] = 0;
+ sprintf(integer_buffer, "%d", oec);
+ return (integer_buffer);
+ } else if ((index = object_resolve(string)) != -1) {
+ value_has_been_resolved = FALSE;
+ if (index < 1 || index > objects) {
+ badptrrun(string, index);
+ return ("");
+ } else {
+ return (object[index]->label);
+ }
+ } else if ((resolved_string = string_resolve(string)) != NULL) {
+ return (resolved_string->value);
+ } else if ((resolved_cstring = cstring_resolve(string)) != NULL) {
+ return (resolved_cstring->value);
+ } else if (function_resolve(string) != NULL) {
+ value_has_been_resolved = FALSE;
+ sprintf(integer_buffer, "%d", execute(string));
+ return (integer_buffer);
+#ifndef GLK
+#ifndef __NDS__
+ } else if (!strcmp(string, "$url")) {
+ value_has_been_resolved = FALSE;
+ return (game_url);
+ } else if (!strcmp(string, "$user_id")) {
+ value_has_been_resolved = FALSE;
+ return (user_id);
+#endif
+#endif
+ } else {
+ value_has_been_resolved = FALSE;
+ return (string);
+ }
+}
+
+char *arg_text_of(char *string) {
+ struct string_type *resolved_string;
+ struct string_type *resolved_cstring;
+ char *macro_text;
+
+ /* CHECK IF THE SUPPLIED STRING IS THE NAME OF A STRING CONSTANT,
+ * IF NOT, RETURN THE STRING LITERAL */
+ if ((macro_text = macro_resolve(string)) != NULL) {
+ value_has_been_resolved = FALSE;
+ return (macro_text);
+ } else if ((resolved_string = string_resolve(string)) != NULL) {
+ return (resolved_string->value);
+ } else if ((resolved_cstring = cstring_resolve(string)) != NULL) {
+ value_has_been_resolved = FALSE;
+ return (resolved_cstring->value);
+ } else {
+ value_has_been_resolved = FALSE;
+ return (string);
+ }
+}
+
+int validate(char *string) {
+ int index,
+ count;
+
+ if (string == NULL) {
+ return (FALSE);
+ }
+
+ /* CHECK IF THE SUPPLIED STRING IS A VALID INTEGER */
+ count = strlen(string);
+
+ /* LOOP OVER THE WHOLE STRING MAKING SURE THAT EACH CHARACTER IS EITHER
+ * A DIGIT OR A MINUS SIGN */
+ for (index = 0; index < count; index++) {
+ if (!Common::isDigit((int) * (string + index)) && string[index] != '-') {
+ //printf ("'%c' is not a digit\n", *(string + index));
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+long value_of(char *value, int run_time) {
+ long compare;
+
+ value_resolved = TRUE;
+
+ value = arg_text_of(value);
+
+ /* RETURN THE INTEGER VALUE OF A STRING */
+ struct integer_type *resolved_integer;
+ struct cinteger_type *resolved_cinteger;
+
+ if (!strcmp(value, "**held")) {
+ return (FALSE);
+ } else if (!strcmp(value, "**here")) {
+ return (FALSE);
+ } else if (!strcmp(value, "**anywhere")) {
+ return (FALSE);
+ } else if (!strcmp(value, "**present")) {
+ return (FALSE);
+ } else if (!strcmp(value, "*held")) {
+ return (FALSE);
+ } else if (!strcmp(value, "*here")) {
+ return (FALSE);
+ } else if (!strcmp(value, "*anywhere")) {
+ return (FALSE);
+ } else if (!strcmp(value, "*present")) {
+ return (FALSE);
+ } else if (!strcmp(value, "random")) {
+ return random_number();
+#ifdef GLK
+ } else if (!strcmp(value, "status_height")) {
+ g_vm->glk_window_get_size(statuswin, &status_width, &status_height);
+ return status_height;
+ } else if (!strcmp(value, "status_width")) {
+ g_vm->glk_window_get_size(statuswin, &status_width, &status_height);
+ return status_width;
+#else
+#ifdef __NDS__
+ } else if (!strcmp(value, "status_height")) {
+ return screen_depth;
+ } else if (!strcmp(value, "status_width")) {
+ return screen_width;
+#else
+ } else if (!strcmp(value, "status_height")) {
+ value_resolved = FALSE;
+ return -1;
+ } else if (!strcmp(value, "status_width")) {
+ value_resolved = FALSE;
+ return -1;
+#endif
+#endif
+ } else if (!strcmp(value, "unixtime")) {
+ return g_system->getMillis() / 1000;
+ } else if (validate(value)) {
+ return (atoi(value));
+ } else if ((resolved_cinteger = cinteger_resolve(value)) != NULL) {
+ return (resolved_cinteger->value);
+ } else if ((resolved_integer = integer_resolve(value)) != NULL) {
+ return (resolved_integer->value);
+ } else if (function_resolve(value) != NULL) {
+ return (execute(value));
+ } else if (object_element_resolve(value)) {
+ return (oec);
+ } else if ((compare = attribute_resolve(value))) {
+ resolved_attribute = SYSTEM_ATTRIBUTE;
+ return (compare);
+ } else if ((compare = user_attribute_resolve(value))) {
+ resolved_attribute = USER_ATTRIBUTE;
+ return (compare);
+ } else if ((compare = object_resolve(value)) != -1) {
+ return (compare);
+ } else if (*value == '@') {
+ return (count_resolve(value));
+ } else {
+ if (run_time) {
+ unkvarrun(value);
+ }
+ value_resolved = FALSE;
+ return (-1);
+ }
+}
+
+struct integer_type *integer_resolve(char *name) {
+ int index,
+ iterator,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ strncpy(expression, name, 80);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '[') {
+ /* THIS MAY STILL BE AN OBJECT ELEMENT IF A CLOSING ] */
+ /* IS FOUND BEFORE AN OPENING ( */
+ expression[index] = 0;
+ delimiter = index + 1;
+ /* LOOK FOR THE CLOSING ], BUT IF YOU FIND A ( FIRST */
+ /* THEN THIS EXPRESSION IS NOT AN ARRAY */
+ for (iterator = counter; iterator > 0; iterator--) {
+ if (expression[iterator] == ']') {
+ expression[iterator] = 0;
+ break;
+ } else if (expression[iterator] == '(') {
+ /* NOT A VARIABLE ARRAY */
+ return (FALSE);
+ }
+ }
+ break;
+ } else if (expression[index] == '<') {
+ /* HIT A < BEFORE A [ THEREFORE */
+ /* IS A FUNCTION CALL, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == '(') {
+ /* HIT A ( BEFORE A [ THEREFORE */
+ /* IS AN OBJECT ELEMENT, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == ' ')
+ return (NULL);
+ }
+
+ // NO DELIMITER FOUND, TRY AS UNINDEXED VARIABLE
+ if (delimiter == 0) {
+ return (integer_resolve_indexed(name, 0));
+ }
+
+ // NO STRING BEFORE DELIMITER
+ if (delimiter == 1) {
+ return (NULL);
+ }
+
+ counter = value_of(&expression[delimiter], TRUE);
+
+ if (counter > -1) {
+ return (integer_resolve_indexed(expression, counter));
+ } else {
+ /* INDEX OUT OF RANGE */
+ return (NULL);
+ }
+}
+
+struct integer_type *integer_resolve_indexed(char *name, int index) {
+ struct integer_type *pointer = integer_table;
+
+ if (pointer == NULL)
+ return (NULL);
+
+ do {
+ if (!strcmp(name, pointer->name)) {
+ if (index == 0) {
+ return (pointer);
+ } else {
+ /* THIS VARIABLE DOES MATCH, BUT WERE NOT AT THE
+ * RIGHT INDEX YET SO MOVE ON */
+ pointer = pointer->next_integer;
+ index--;
+ }
+ } else
+ pointer = pointer->next_integer;
+ } while (pointer != NULL);
+
+ /* IF index != 0, INDEX OUT OF RANGE, OTHERWISE NOT VARIABLE */
+ return (NULL);
+}
+
+struct cinteger_type *cinteger_resolve(char *name) {
+ int index,
+ iterator,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ strncpy(expression, name, 80);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '[') {
+ /* THIS MAY STILL BE AN OBJECT ELEMENT IF A CLOSING ] */
+ /* IS FOUND BEFORE AN OPENING ( */
+ expression[index] = 0;
+ delimiter = index + 1;
+ /* LOOK FOR THE CLOSING ], BUT IF YOU FIND A ( FIRST */
+ /* THEN THIS EXPRESSION IS NOT AN ARRAY */
+ for (iterator = counter; iterator > 0; iterator--) {
+ if (expression[iterator] == ']') {
+ expression[iterator] = 0;
+ break;
+ } else if (expression[iterator] == '(') {
+ /* NOT A CONSTANT ARRAY */
+ return (FALSE);
+ }
+ }
+ break;
+ } else if (expression[index] == '<') {
+ /* HIT A < BEFORE A [ THEREFORE */
+ /* IS A FUNCTION CALL, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == '(') {
+ /* HIT A ( BEFORE A [ THEREFORE */
+ /* IS AN OBJECT ELEMENT, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == ' ')
+ return (NULL);
+ }
+
+ // NO DELIMITER FOUND, TRY AS UNINDEXED CONSTANT
+ if (delimiter == 0) {
+ return (cinteger_resolve_indexed(name, 0));
+ }
+
+ // NO STRING BEFORE DELIMITER
+ if (delimiter == 1) {
+ return (NULL);
+ }
+
+ counter = value_of(&expression[delimiter], TRUE);
+
+ if (counter > -1) {
+ return (cinteger_resolve_indexed(expression, counter));
+ } else {
+ /* INDEX OUT OF RANGE */
+ return (NULL);
+ }
+}
+
+struct cinteger_type *cinteger_resolve_indexed(char *name, int index) {
+ struct cinteger_type *pointer = cinteger_table;
+
+ if (pointer == NULL)
+ return (NULL);
+
+ do {
+ if (!strcmp(name, pointer->name)) {
+ if (index == 0) {
+ return (pointer);
+ } else {
+ /* THIS VARIABLE DOES MATCH, BUT WERE NOT AT THE
+ * RIGHT INDEX YET SO MOVE ON */
+ pointer = pointer->next_cinteger;
+ index--;
+ }
+ } else
+ pointer = pointer->next_cinteger;
+ } while (pointer != NULL);
+
+ /* IF index != 0, INDEX OUT OF RANGE, OTHERWISE NOT VARIABLE */
+ return (NULL);
+}
+
+struct string_type *string_resolve(char *name) {
+ int index,
+ iterator,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ strncpy(expression, name, 80);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '[') {
+ expression[index] = 0;
+ delimiter = index + 1;
+ for (iterator = counter; iterator > 0; iterator--) {
+ if (expression[iterator] == ']') {
+ expression[iterator] = 0;
+ break;
+ }
+ }
+ break;
+ } else if (expression[index] == '<') {
+ /* HIT A < BEFORE A [ THEREFORE */
+ /* IS A FUNCTION CALL, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == '(') {
+ /* HIT A ( BEFORE A [ THEREFORE */
+ /* IS AN OBJECT ELEMENT, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == ' ')
+ return (NULL);
+ }
+
+ if (delimiter == 0) {
+ /* NO DELIMITER FOUND, TRY AS UNINDEXED VARIABLE */
+ return (string_resolve_indexed(name, 0));
+ }
+
+ if (delimiter == 1) {
+ /* NO STRING BEFORE DELIMITER */
+ return (NULL);
+ }
+
+ counter = value_of(&expression[delimiter], TRUE);
+
+ if (counter > -1) {
+ return (string_resolve_indexed(expression, counter));
+ } else
+ return (NULL);
+}
+
+struct string_type *string_resolve_indexed(char *name, int index) {
+ struct string_type *pointer = string_table;
+
+ if (pointer == NULL)
+ return (NULL);
+
+ do {
+ if (!strcmp(name, pointer->name)) {
+ if (index == 0) {
+ return (pointer);
+ } else {
+ /* THIS STRING DOES MATCH, BUT WERE NOT AT THE
+ * RIGHT INDEX YET SO MOVE ON */
+ pointer = pointer->next_string;
+ index--;
+ }
+ } else {
+ pointer = pointer->next_string;
+ }
+ } while (pointer != NULL);
+
+ return (NULL);
+}
+
+struct string_type *cstring_resolve(char *name) {
+ int index,
+ iterator,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ strncpy(expression, name, 80);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '[') {
+ expression[index] = 0;
+ delimiter = index + 1;
+ for (iterator = counter; iterator > 0; iterator--) {
+ if (expression[iterator] == ']') {
+ expression[iterator] = 0;
+ break;
+ }
+ }
+ break;
+ } else if (expression[index] == '<') {
+ /* HIT A < BEFORE A [ THEREFORE */
+ /* IS A FUNCTION CALL, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == '(') {
+ /* HIT A ( BEFORE A [ THEREFORE */
+ /* IS AN OBJECT ELEMENT, NOT AN ARRAY */
+ return (NULL);
+ } else if (expression[index] == ' ')
+ return (NULL);
+ }
+
+ if (delimiter == 0) {
+ /* NO DELIMITER FOUND, TRY AS UNINDEXED VARIABLE */
+ return (cstring_resolve_indexed(name, 0));
+ }
+
+ if (delimiter == 1) {
+ /* NO STRING BEFORE DELIMITER */
+ return (NULL);
+ }
+
+ counter = value_of(&expression[delimiter], TRUE);
+
+ if (counter > -1) {
+ return (cstring_resolve_indexed(expression, counter));
+ } else
+ return (NULL);
+}
+
+struct string_type *cstring_resolve_indexed(char *name, int index) {
+ struct string_type *pointer = cstring_table;
+
+ if (pointer == NULL)
+ return (NULL);
+
+ do {
+ if (!strcmp(name, pointer->name)) {
+ if (index == 0) {
+ return (pointer);
+ } else {
+ /* THIS STRING DOES MATCH, BUT WERE NOT AT THE
+ * RIGHT INDEX YET SO MOVE ON */
+ pointer = pointer->next_string;
+ index--;
+ }
+ } else {
+ pointer = pointer->next_string;
+ }
+ } while (pointer != NULL);
+
+ return (NULL);
+}
+
+struct function_type *function_resolve(char *name) {
+ char *full_name;
+ char core_name[84];
+ int index;
+
+ struct function_type *pointer = function_table;
+
+ if (function_table == NULL)
+ return (NULL);
+
+ /* STRIP ARGUMENTS OFF FIRST, THEN EXPAND RESOLVE NAME */
+ index = 0;
+
+ while (*name && index < 80) {
+ if (*name == '<') {
+ break;
+ } else {
+ core_name[index++] = *name++;
+ }
+ }
+ core_name[index] = 0;
+
+ /* GET A POINTER TO A STRING THAT REPRESENTS THE EXPANDED NAME OF
+ * THE FUNCTION */
+ full_name = (char *) expand_function(core_name);
+
+ /* LOOP THROUGH ALL THE FUNCTIONS LOOKING FOR A FUNCTION THAT
+ * HAS THIS EXPANDED FULL NAME */
+ do {
+ if (!strcmp(full_name, pointer->name))
+ return (pointer);
+ else
+ pointer = pointer->next_function;
+ } while (pointer != NULL);
+
+ /* RETURN A POINTER TO THE STRUCTURE THAT ENCAPSULATES THE FUNCTION */
+ return (NULL);
+}
+
+char *expand_function(char *name) {
+ /* THIS FUNCTION TAKES A SCOPE FUNCTION CALL SUCH AS noun1.function
+ * AND REOLVE THE ACTUAL FUNCTION NAME SUCH AS function_key */
+ int index,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ strncpy(expression, name, 80);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '.') {
+ expression[index] = 0;
+ delimiter = index + 1;
+ break;
+ }
+ }
+
+ if (delimiter == FALSE) {
+ /* THIS FUNCTION DOESN'T CONTAIN A '.', SO RETURN IT AS IS */
+ return (arg_text_of(name));
+ }
+
+ /* THE ORIGINAL STRING IS NOW CUT INTO TWO STRINGS:
+ * expression.delimiter */
+
+ index = value_of(expression, TRUE);
+
+ if (index < 1 || index > objects) {
+ return ((char *) name);
+ }
+
+ if (cinteger_resolve(&expression[delimiter]) != NULL ||
+ integer_resolve(&expression[delimiter]) != NULL ||
+ object_element_resolve(&expression[delimiter])) {
+ /* THE DELIMETER RESOLVES TO A CONSTANT, VARIABLE OR OBJECT
+ * ELEMENT, SO TAKE NOTE OF THAT */
+ sprintf(function_name, "%ld", value_of(&expression[delimiter], TRUE));
+ } else {
+ strcpy(function_name, &expression[delimiter]);
+ }
+ strcat(function_name, "_");
+ strcat(function_name, object[index]->label);
+
+ return ((char *) function_name);
+}
+
+char *macro_resolve(char *testString) {
+ int index,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ strncpy(expression, testString, 80);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '{' || expression[index] == '}') {
+ expression[index] = 0;
+ if (!delimiter)
+ delimiter = index + 1;
+ }
+ }
+
+ if (delimiter == FALSE)
+ return (NULL);
+
+ if (*expression != 0) {
+ index = value_of(expression, TRUE);
+ } else {
+ index = 0;
+ }
+
+ if (!strcmp(&expression[delimiter], "list")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (list_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "plain")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (plain_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "long")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (long_output(index));
+ }
+ } else if (!strcmp(&expression[delimiter], "sub")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (sub_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "obj")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (obj_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "that")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (that_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "it")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (it_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "doesnt")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (doesnt_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "does")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (does_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "isnt")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (isnt_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "is")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (is_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "the")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (sentence_output(index, FALSE));
+ }
+ } else if (!strcmp(&expression[delimiter], "s")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ if (object[index]->attributes & PLURAL) {
+ strcpy(temp_buffer, "");
+ } else {
+ strcpy(temp_buffer, "s");
+ }
+ return (temp_buffer);
+ }
+ } else if (!strcmp(&expression[delimiter], "names")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (object_names(index, temp_buffer));
+ }
+ } else if (!strcmp(&expression[delimiter], "label")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (object[index]->label);
+ }
+ } else if (!strcmp(&expression[delimiter], "List")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (list_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Plain")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (plain_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Sub")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (sub_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Obj")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (obj_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "That")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (that_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "It")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (it_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Doesnt")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (doesnt_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Does")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (does_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Isnt")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (isnt_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "Is")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (is_output(index, TRUE));
+ }
+ } else if (!strcmp(&expression[delimiter], "The")) {
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (NULL);
+ } else {
+ return (sentence_output(index, TRUE));
+ }
+ } else {
+ strcpy(macro_function, "+macro_");
+ strcat(macro_function, &expression[delimiter]);
+ strcat(macro_function, "<");
+ sprintf(temp_buffer, "%d", index);
+ strcat(macro_function, temp_buffer);
+
+ // BUILD THE FUNCTION NAME AND PASS THE OBJECT AS
+ // THE ONLY ARGUMENT
+ if (execute(macro_function)) {
+ return (string_resolve("return_value")->value);
+ }
+ }
+
+ return (NULL);
+}
+
+int count_resolve(char *testString) {
+ struct function_type *resolved_function = NULL;
+
+ if (*(testString + 1) == 0) {
+ // @ ON ITS OWN, SO RETURN THE CALL COUNT OF THE CURRENTLY EXECUTING
+ // FUNCTION
+ return (executing_function->call_count);
+ } else if ((resolved_function = function_resolve(testString + 1)) != NULL) {
+ return (resolved_function->call_count);
+ } else {
+ return array_length_resolve(testString);
+ }
+}
+
+int array_length_resolve(char *testString) {
+ int counter = 0;
+ char *array_name = &testString[1];
+
+ struct integer_type *integer_pointer = integer_table;
+ struct cinteger_type *cinteger_pointer = cinteger_table;
+ struct string_type *string_pointer = string_table;
+ struct string_type *cstring_pointer = cstring_table;
+
+ if (integer_pointer != NULL) {
+ do {
+ if (!strcmp(array_name, integer_pointer->name)) {
+ counter++;
+ }
+ integer_pointer = integer_pointer->next_integer;
+ } while (integer_pointer != NULL);
+ }
+
+ /* IF ONE OR MORE INTEGERS WITH THIS NAME WERE FOUND
+ RETURN THE COUNT */
+ if (counter)
+ return (counter);
+
+ if (string_pointer != NULL) {
+ do {
+ if (!strcmp(array_name, string_pointer->name)) {
+ counter++;
+ }
+ string_pointer = string_pointer->next_string;
+ } while (string_pointer != NULL);
+ }
+
+ /* IF ONE OR MORE STRINGS WITH THIS NAME WERE FOUND
+ RETURN THE COUNT */
+ if (counter)
+ return (counter);
+
+ if (cinteger_pointer != NULL) {
+ do {
+ if (!strcmp(array_name, cinteger_pointer->name)) {
+ counter++;
+ }
+ cinteger_pointer = cinteger_pointer->next_cinteger;
+ } while (cinteger_pointer != NULL);
+ }
+
+ /* IF ONE OR MORE INTEGER CONSTANTS WITH THIS NAME WERE FOUND
+ RETURN THE COUNT */
+ if (counter)
+ return (counter);
+
+ if (cstring_pointer != NULL) {
+ do {
+ if (!strcmp(array_name, cstring_pointer->name)) {
+ counter++;
+ }
+ cstring_pointer = cstring_pointer->next_string;
+ } while (cstring_pointer != NULL);
+ }
+
+ /* IF ONE OR MORE STRING CONSTANTS WITH THIS NAME WERE FOUND
+ RETURN THE COUNT */
+ if (counter)
+ return (counter);
+
+ /* NO VARIABLES OR STRINGS FOUND */
+ return (0);
+}
+
+int object_element_resolve(char *testString) {
+ int index,
+ iterator,
+ counter;
+ int delimiter = 0;
+ char expression[84];
+
+ struct integer_type *resolved_integer;
+ struct cinteger_type *resolved_cinteger;
+
+ strncpy(expression, testString, 80);
+
+ //sprintf(temp_buffer, "incoming = %s^", testString);
+ //write_text (temp_buffer);
+
+ counter = strlen(expression);
+
+ for (index = 0; index < counter; index++) {
+ if (expression[index] == '(') {
+ expression[index] = 0;
+ delimiter = index + 1;
+ for (iterator = counter; iterator > 0; iterator--) {
+ if (expression[iterator] == ')') {
+ expression[iterator] = 0;
+ break;
+ }
+ }
+ break;
+ } else if (expression[index] == '<') {
+ /* HIT A < BEFORE A [ THEREFORE */
+ /* IS A FUNCTION CALL, NOT AN ARRAY */
+ return (FALSE);
+ } else if (expression[index] == '[') {
+ /* HIT A [ BEFORE A ( THEREFORE */
+ /* THIS EXPRESSION IS AN ARRAY, NOT AN OBJECT ELEMENT */
+ /* UNLESS A CLOSING ] IS FOUND BEFORE THE OPENING ( */
+ /* ie. COULD BE AN array[index](element) FORMAT */
+ /* SEARCH FORWARD... */
+ for (; index < counter; index++) {
+ if (expression[index] == ']') {
+ /* BREAK OUT AND KEEP LOOKING FOR A ( */
+ break;
+ } else if (expression[index] == '(') {
+ /* THIS EXPRESSION IS DEFINITELY AN ARRAY WITH AN */
+ /* OBJECT ELEMENT AS THE INDEX */
+ return (FALSE);
+ }
+ }
+ } else if (expression[index] == ' ')
+ return (FALSE);
+ }
+
+ // NO DELIMITER FOUND OR NO STRING BEFORE DELIMITER
+ if (delimiter == FALSE || delimiter == 1)
+ return (FALSE);
+
+ index = object_resolve(expression);
+
+ if (index == -1) {
+ //sprintf(temp_buffer, "expression %s is not an object^", expression);
+ //write_text(temp_buffer);
+
+ // COULDN'T BE RESOLVED AS AN OBJECT, TRY AS A VARIABLE
+ if ((resolved_integer = integer_resolve(expression)) != NULL) {
+ index = resolved_integer->value;
+ } else if ((resolved_cinteger = cinteger_resolve(expression)) != NULL) {
+ index = resolved_cinteger->value;
+ }
+ }
+
+ if (index < 1 || index > objects) {
+ badptrrun(expression, index);
+ return (FALSE);
+ }
+
+ counter = value_of(&expression[delimiter], TRUE);
+
+ if (counter < 0 || counter > 15) {
+ sprintf(error_buffer,
+ "ERROR: In function \"%s\", element \"%s\" out of range (%d).^",
+ executing_function->name, &expression[delimiter], counter);
+ write_text(error_buffer);
+ return (FALSE);
+ } else {
+ oec = object[index]->integer[counter];
+ object_element_address = &object[index]->integer[counter];
+ return (TRUE);
+ }
+}
+
+int object_resolve(char object_string[]) {
+ int index;
+
+ if (!strcmp(object_string, "noun1"))
+ return (noun[0]);
+ else if (!strcmp(object_string, "noun2"))
+ return (noun[1]);
+ else if (!strcmp(object_string, "noun3"))
+ return (noun[2]);
+ else if (!strcmp(object_string, "noun4"))
+ return (noun[3]);
+ else if (!strcmp(object_string, "player"))
+ return (player);
+ else if (!strcmp(object_string, "here"))
+ return (HERE);
+ else if (!strcmp(object_string, "self") ||
+ !strcmp(object_string, "this")) {
+ if (executing_function != NULL && executing_function->self == 0) {
+ sprintf(error_buffer,
+ "ERROR: Reference to 'self' from global function \"%s\".^",
+ executing_function->name);
+ write_text(error_buffer);
+ } else
+ return (executing_function->self);
+ } else {
+ for (index = 1; index <= objects; index++) {
+ if (!strcmp(object_string, object[index]->label))
+ return (index);
+ }
+ }
+
+ return (-1);
+}
+
+long attribute_resolve(char *attribute) {
+ long bit_mask;
+
+ if (!strcmp(attribute, "VISITED"))
+ return (VISITED);
+ else if (!strcmp(attribute, "DARK"))
+ return (DARK);
+ else if (!strcmp(attribute, "ON_WATER"))
+ return (ON_WATER);
+ else if (!strcmp(attribute, "UNDER_WATER"))
+ return (UNDER_WATER);
+ else if (!strcmp(attribute, "WITHOUT_AIR"))
+ return (WITHOUT_AIR);
+ else if (!strcmp(attribute, "OUTDOORS"))
+ return (OUTDOORS);
+ else if (!strcmp(attribute, "MID_AIR"))
+ return (MID_AIR);
+ else if (!strcmp(attribute, "TIGHT_ROPE"))
+ return (TIGHT_ROPE);
+ else if (!strcmp(attribute, "POLLUTED"))
+ return (POLLUTED);
+ else if (!strcmp(attribute, "SOLVED"))
+ return (SOLVED);
+ else if (!strcmp(attribute, "MID_WATER"))
+ return (MID_WATER);
+ else if (!strcmp(attribute, "DARKNESS")) {
+ bit_mask = DARKNESS;
+ if (check_light(HERE)) {
+ bit_mask = ~bit_mask;
+ object[HERE]->attributes = object[HERE]->attributes & bit_mask;
+ } else {
+ object[HERE]->attributes = object[HERE]->attributes | bit_mask;
+ }
+ return (DARKNESS);
+ } else if (!strcmp(attribute, "MAPPED"))
+ return (MAPPED);
+ else if (!strcmp(attribute, "KNOWN"))
+ return (KNOWN);
+ else if (!strcmp(attribute, "CLOSED"))
+ return (CLOSED);
+ else if (!strcmp(attribute, "LOCKED"))
+ return (LOCKED);
+ else if (!strcmp(attribute, "DEAD"))
+ return (DEAD);
+ else if (!strcmp(attribute, "IGNITABLE"))
+ return (IGNITABLE);
+ else if (!strcmp(attribute, "WORN"))
+ return (WORN);
+ else if (!strcmp(attribute, "CONCEALING"))
+ return (CONCEALING);
+ else if (!strcmp(attribute, "LUMINOUS"))
+ return (LUMINOUS);
+ else if (!strcmp(attribute, "WEARABLE"))
+ return (WEARABLE);
+ else if (!strcmp(attribute, "CLOSABLE"))
+ return (CLOSABLE);
+ else if (!strcmp(attribute, "LOCKABLE"))
+ return (LOCKABLE);
+ else if (!strcmp(attribute, "ANIMATE"))
+ return (ANIMATE);
+ else if (!strcmp(attribute, "LIQUID"))
+ return (LIQUID);
+ else if (!strcmp(attribute, "CONTAINER"))
+ return (CONTAINER);
+ else if (!strcmp(attribute, "SURFACE"))
+ return (SURFACE);
+ else if (!strcmp(attribute, "PLURAL"))
+ return (PLURAL);
+ else if (!strcmp(attribute, "FLAMMABLE"))
+ return (FLAMMABLE);
+ else if (!strcmp(attribute, "BURNING"))
+ return (BURNING);
+ else if (!strcmp(attribute, "LOCATION"))
+ return (LOCATION);
+ else if (!strcmp(attribute, "ON"))
+ return (ON);
+ else if (!strcmp(attribute, "DAMAGED"))
+ return (DAMAGED);
+ else if (!strcmp(attribute, "FEMALE"))
+ return (FEMALE);
+ else if (!strcmp(attribute, "POSSESSIVE"))
+ return (POSSESSIVE);
+ else if (!strcmp(attribute, "OUT_OF_REACH"))
+ return (OUT_OF_REACH);
+ else if (!strcmp(attribute, "TOUCHED"))
+ return (TOUCHED);
+ else if (!strcmp(attribute, "SCORED"))
+ return (SCORED);
+ else if (!strcmp(attribute, "SITTING"))
+ return (SITTING);
+ else if (!strcmp(attribute, "NPC"))
+ return (NPC);
+ else if (!strcmp(attribute, "DONE"))
+ return (DONE);
+ else if (!strcmp(attribute, "GAS"))
+ return (MAPPED);
+ else if (!strcmp(attribute, "NO_TAB"))
+ return (NO_TAB);
+ else if (!strcmp(attribute, "NOT_IMPORTANT"))
+ return (NOT_IMPORTANT);
+ else
+ return (0);
+}
+
+long user_attribute_resolve(char *name) {
+ struct attribute_type *pointer = attribute_table;
+
+ if (pointer == NULL)
+ return (0);
+
+ do {
+ if (!strcmp(name, pointer->name)) {
+ return (pointer->value);
+ } else
+ pointer = pointer->next_attribute;
+ } while (pointer != NULL);
+
+ /* ATTRIBUTE NOT FOUND */
+ return (0);
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/types.h b/engines/glk/jacl/types.h
new file mode 100644
index 0000000000..3cfb2afde0
--- /dev/null
+++ b/engines/glk/jacl/types.h
@@ -0,0 +1,224 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/constants.h"
+#include "common/file.h"
+
+namespace Glk {
+namespace JACL {
+
+#define DIR_SEPARATOR '/'
+#define DATA_DIR "data/"
+#define TEMP_DIR "temp/"
+#define INCLUDE_DIR "include/"
+
+// THIS STRUCTURE CONTAINS ALL THE INFORMATION THAT NEEDS TO BE
+// SAVED IN ORDER TO CALL parse() RECURSIVELY
+struct proxy_type {
+ int object_pointers[4]; // NOUN1 -> NOUN4
+ int integer[MAX_WORDS]; // THE COMANDS INTEGERS
+ char text[MAX_WORDS][256]; // THE COMANDS STRINGS
+ char command[MAX_WORDS][256]; // THE WHOLE COMMAND
+ int object_list[4][MAX_OBJECTS]; // THE RESOLVED OBJECTS
+ int list_size[4]; // THE SIZE OF THE ABOVE LISTS
+ int max_size[4]; // THE LAST USED INDEX OF THE ABOVE LISTS
+ int start_of_this_command; // PREPARSE STATE
+ int start_of_last_command; // PREPARSE STATE
+ int integercount; // THE NUMBER OF INTEGERS SAVED
+ int textcount; // THE NUMBER OF STRINGS SAVED
+ int commandcount; // THE NUMBER OF WORDS IN COMMAND
+ int last_exact; // WORD POINTER FOR MATCH
+ int after_from; // WORD POINTER FOR FROM WORD
+};
+
+struct stack_type {
+ Common::SeekableReadStream *infile;
+ Common::WriteStream *outfile;
+ int arguments[MAX_WORDS];
+ char str_arguments[MAX_WORDS][256];
+ char text_buffer[1024];
+ char called_name[1024];
+ char override[84];
+ char scope_criterion[24];
+ char default_function[84];
+ char *word[MAX_WORDS];
+ int quoted[MAX_WORDS];
+ int wp;
+ int argcount;
+ int *loop_integer;
+ int *select_integer;
+ int criterion_value;
+ int criterion_type;
+ int criterion_negate;
+ int current_level;
+ int execution_level;
+#ifdef GLK
+ int top_of_loop;
+ int top_of_select;
+ int top_of_while;
+ int top_of_iterate;
+ int top_of_update;
+ int top_of_do_loop;
+ int address;
+#else
+ long top_of_loop;
+ long top_of_select;
+ long top_of_while;
+ long top_of_iterate;
+ long top_of_update;
+ long top_of_do_loop;
+ long address;
+#endif
+ struct function_type *function;
+};
+
+struct object_type {
+ char label[44];
+ char article[12];
+ char definite[12];
+ struct name_type *first_name;
+ struct name_type *first_plural;
+ char inventory[44];
+ char described[84];
+ int user_attributes;
+ int user_attributes_backup;
+ int attributes;
+ int attributes_backup;
+ int integer[16];
+ int integer_backup[16];
+ int nosave;
+};
+
+struct integer_type {
+ char name[44];
+ int value;
+ int value_backup;
+ struct integer_type *next_integer;
+};
+
+struct cinteger_type {
+ char name[44];
+ int value;
+ struct cinteger_type *next_cinteger;
+};
+
+struct attribute_type {
+ char name[44];
+ int value;
+ struct attribute_type *next_attribute;
+};
+
+struct string_type {
+ char name[44];
+ char value[256];
+ struct string_type *next_string;
+};
+
+struct function_type {
+ char name[84];
+#ifdef GLK
+ glui32 position;
+#else
+ long position;
+#endif
+
+ int self;
+ int call_count;
+ int call_count_backup;
+ struct function_type *next_function;
+};
+
+struct command_type {
+ char word[44];
+ struct command_type *next;
+};
+
+#ifdef GLK
+struct window_type {
+ char name[44];
+ winid_t glk_window;
+ glui32 glk_type;
+ struct window_type *next_window;
+};
+#endif
+
+struct word_type {
+ char word[44];
+ struct word_type *first_child;
+ struct word_type *next_sibling;
+};
+
+struct synonym_type {
+ char original[44];
+ char standard[44];
+ struct synonym_type *next_synonym;
+};
+
+struct name_type {
+ char name[44];
+ struct name_type *next_name;
+};
+
+struct filter_type {
+ char word[44];
+ struct filter_type *next_filter;
+};
+
+#ifndef GLK
+struct parameter_type {
+ char name[44];
+ char container[44];
+ int low;
+ int high;
+ struct parameter_type *next_parameter;
+};
+#endif
+
+class File : public Common::File {
+public:
+ static File *openForReading(const Common::String &name) {
+ File *f = new File();
+ if (f->open(name))
+ return f;
+
+ delete f;
+ return nullptr;
+ }
+
+ static Common::WriteStream *openForWriting(const Common::String &name) {
+ Common::DumpFile *df = new Common::DumpFile();
+ if (df->open(name))
+ return df;
+
+ delete df;
+ return nullptr;
+ }
+public:
+ File() : Common::File() {}
+ File(const Common::String &name) {
+ Common::File::open(name);
+ assert(isOpen());
+ }
+};
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/utils.cpp b/engines/glk/jacl/utils.cpp
new file mode 100644
index 0000000000..700d64c436
--- /dev/null
+++ b/engines/glk/jacl/utils.cpp
@@ -0,0 +1,246 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+extern char function_name[];
+
+extern struct object_type *object[];
+extern struct variable_type *variable[];
+
+extern int objects;
+extern int player;
+
+extern char game_file[];
+extern char game_path[];
+extern char prefix[];
+extern char blorb[];
+extern char bookmark[];
+extern char walkthru[];
+extern char include_directory[];
+extern char temp_directory[];
+extern char data_directory[];
+extern char temp_buffer[];
+
+void eachturn() {
+ /* INCREMENT THE TOTAL NUMBER OF MOVES MADE AND CALL THE 'EACHTURN'
+ * FUNCTION FOR THE CURRENT LOCATION AND THE GLOBAL 'EACHTURN'
+ * FUNCTION. THESE FUNCTIONS CONTAIN ANY CODE THAT SIMULATED EVENTS
+ * OCCURING DUE TO THE PASSING OF TIME */
+ TOTAL_MOVES->value++;
+ execute("+eachturn");
+ strcpy(function_name, "eachturn_");
+ strcat(function_name, object[HERE]->label);
+ execute(function_name);
+ execute("+system_eachturn");
+
+ /* SET TIME TO FALSE SO THAT NO MORE eachturn FUNCTIONS ARE EXECUTED
+ * UNTIL EITHER THE COMMAND PROMPT IS RETURNED TO (AND TIME IS SET
+ * TO TRUE AGAIN, OR IT IS MANUALLY SET TO TRUE BY A VERB THAT CALLS
+ * MORE THAN ONE proxy COMMAND. THIS IS BECAUSE OTHERWISE A VERB THAT
+ * USES A proxy COMMAND TO TRANSLATE THE VERB IS RESULT IN TWO MOVES
+ * (OR MORE) HAVING PASSED FOR THE ONE ACTION. */
+ TIME->value = FALSE;
+}
+
+int get_here() {
+ /* THIS FUNCTION RETURNS THE VALUE OF 'here' IN A SAFE, ERROR CHECKED
+ * WAY */
+ if (player < 1 || player > objects) {
+ badplrrun(player);
+ terminate(44);
+ } else if (object[player]->PARENT < 1 || object[player]->PARENT > objects || object[player]->PARENT == player) {
+ badparrun();
+ terminate(44);
+ } else {
+ return (object[player]->PARENT);
+ }
+
+ /* SHOULDN'T GET HERE, JUST TRYING TO KEEP VisualC++ HAPPY */
+ return 1;
+}
+
+char *strip_return(char *string) {
+ /* STRIP ANY TRAILING RETURN OR NEWLINE OFF THE END OF A STRING */
+ int index;
+ int length = strlen(string);
+
+ for (index = 0; index < length; index++) {
+ switch (string[index]) {
+ case '\r':
+ case '\n':
+ string[index] = 0;
+ break;
+ }
+ }
+
+ return (char *) string;
+}
+
+int random_number() {
+ return g_vm->getRandomNumber(0x7fffffff);
+}
+
+void create_paths(char *full_path) {
+ int index;
+ char *last_slash;
+
+ /* SAVE A COPY OF THE SUPPLIED GAMEFILE NAME */
+ strcpy(game_file, full_path);
+
+ /* FIND THE LAST SLASH IN THE SPECIFIED GAME PATH AND REMOVE THE GAME
+ * FILE SUFFIX IF ANY EXISTS */
+ last_slash = (char *) NULL;
+
+ /* GET A POINTER TO THE LAST SLASH IN THE FULL PATH */
+ last_slash = strrchr(full_path, DIR_SEPARATOR);
+
+ for (index = strlen(full_path); index >= 0; index--) {
+ if (full_path[index] == DIR_SEPARATOR) {
+ /* NO '.' WAS FOUND BEFORE THE LAST SLASH WAS REACHED,
+ * THERE IS NO FILE EXTENSION */
+ break;
+ } else if (full_path[index] == '.') {
+ full_path[index] = 0;
+ break;
+ }
+ }
+
+ /* STORE THE GAME PATH AND THE GAME FILENAME PARTS SEPARATELY */
+ if (last_slash == (char *) NULL) {
+ /* GAME MUST BE IN CURRENT DIRECTORY SO THERE WILL BE NO GAME PATH */
+ strcpy(prefix, full_path);
+ game_path[0] = 0;
+
+ /* THIS ADDITION OF ./ TO THE FRONT OF THE GAMEFILE IF IT IS IN THE
+ * CURRENT DIRECTORY IS REQUIRED TO KEEP Gargoyle HAPPY. */
+#ifdef __NDS__
+ sprintf(temp_buffer, "%c%s", DIR_SEPARATOR, game_file);
+#else
+ sprintf(temp_buffer, ".%c%s", DIR_SEPARATOR, game_file);
+#endif
+ strcpy(game_file, temp_buffer);
+ } else {
+ /* STORE THE DIRECTORY THE GAME FILE IS IN WITH THE TRAILING
+ * SLASH IF THERE IS ONE */
+ last_slash++;
+ strcpy(prefix, last_slash);
+ *last_slash = '\0';
+ strcpy(game_path, full_path);
+ }
+
+#ifdef GLK
+ /* SET DEFAULT WALKTHRU FILE NAME */
+ sprintf(walkthru, "%s.walkthru", prefix);
+
+ /* SET DEFAULT SAVED GAME FILE NAME */
+ sprintf(bookmark, "%s.bookmark", prefix);
+
+ /* SET DEFAULT BLORB FILE NAME */
+ sprintf(blorb, "%s.blorb", prefix);
+#endif
+
+ /* SET DEFAULT FILE LOCATIONS IF NOT SET BY THE USER IN CONFIG */
+ if (include_directory[0] == 0) {
+ strcpy(include_directory, game_path);
+ strcat(include_directory, INCLUDE_DIR);
+ }
+
+ if (temp_directory[0] == 0) {
+ strcpy(temp_directory, game_path);
+ strcat(temp_directory, TEMP_DIR);
+ }
+
+ if (data_directory[0] == 0) {
+ strcpy(data_directory, game_path);
+ strcat(data_directory, DATA_DIR);
+ }
+}
+
+int jacl_whitespace(int character) {
+ /* CHECK IF A CHARACTER IS CONSIDERED WHITE SPACE IN THE JACL LANGUAGE */
+ switch (character) {
+ case ':':
+ case '\t':
+ case ' ':
+ return (TRUE);
+ default:
+ return (FALSE);
+ }
+}
+
+char *stripwhite(char *string) {
+ int i;
+
+ /* STRIP WHITESPACE FROM THE START AND END OF STRING. */
+ while (jacl_whitespace(*string)) string++;
+
+ i = strlen(string) - 1;
+
+ while (i >= 0 && ((jacl_whitespace(*(string + i))) || *(string + i) == '\n' || *(string + i) == '\r')) i--;
+
+#ifdef WIN32
+ i++;
+ *(string + i) = '\r';
+#endif
+ i++;
+ *(string + i) = '\n';
+ i++;
+ *(string + i) = '\0';
+
+ return string;
+}
+
+void jacl_encrypt(char *string) {
+ int index, length;
+
+ length = strlen(string);
+
+ for (index = 0; index < length; index++) {
+ if (string[index] == '\n' || string[index] == '\r') {
+ return;
+ }
+ string[index] = string[index] ^ 255;
+ }
+}
+
+void jacl_decrypt(char *string) {
+ int index, length;
+
+ length = strlen(string);
+
+ for (index = 0; index < length; index++) {
+ if (string[index] == '\n' || string[index] == '\r') {
+ return;
+ }
+ string[index] = string[index] ^ 255;
+ }
+}
+
+} // End of namespace JACL
+} // End of namespace Glk
diff --git a/engines/glk/jacl/version.h b/engines/glk/jacl/version.h
new file mode 100644
index 0000000000..255a248930
--- /dev/null
+++ b/engines/glk/jacl/version.h
@@ -0,0 +1,26 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#define J_VERSION 2
+#define J_RELEASE 9
+#define J_BUILD 0
+#define INTERPRETER_VERSION 209