aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/jacl/jacl_main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/jacl/jacl_main.cpp')
-rw-r--r--engines/glk/jacl/jacl_main.cpp1582
1 files changed, 1582 insertions, 0 deletions
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