diff options
27 files changed, 13919 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&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&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, "&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 diff --git a/engines/glk/module.mk b/engines/glk/module.mk index 5cdb9b1933..b3a88ea6bd 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -185,6 +185,8 @@ MODULE_OBJS := \ jacl/glk_saver.o \ jacl/glk_startup.o \ jacl/interpreter.o \ + jacl/jacl.o \ + jacl/jacl_main.o \ jacl/jpp.o \ jacl/libcsv.o \ jacl/loader.o \ |