summaryrefslogtreecommitdiffstats
path: root/apps/plugins/frotz
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/frotz')
-rw-r--r--apps/plugins/frotz/SOURCES22
-rw-r--r--apps/plugins/frotz/STATUS45
-rw-r--r--apps/plugins/frotz/buffer.c152
-rw-r--r--apps/plugins/frotz/dumb_frotz.h19
-rw-r--r--apps/plugins/frotz/dumb_init.c86
-rw-r--r--apps/plugins/frotz/dumb_output.c256
-rw-r--r--apps/plugins/frotz/err.c154
-rw-r--r--apps/plugins/frotz/fastmem.c1013
-rw-r--r--apps/plugins/frotz/files.c563
-rw-r--r--apps/plugins/frotz/frotz.c314
-rw-r--r--apps/plugins/frotz/frotz.h583
-rw-r--r--apps/plugins/frotz/frotz.make21
-rw-r--r--apps/plugins/frotz/frotzplugin.h56
-rw-r--r--apps/plugins/frotz/hotkey.c221
-rw-r--r--apps/plugins/frotz/input.c301
-rw-r--r--apps/plugins/frotz/main.c205
-rw-r--r--apps/plugins/frotz/math.c261
-rw-r--r--apps/plugins/frotz/object.c1003
-rw-r--r--apps/plugins/frotz/process.c798
-rw-r--r--apps/plugins/frotz/quetzal.c541
-rw-r--r--apps/plugins/frotz/random.c82
-rw-r--r--apps/plugins/frotz/redirect.c172
-rw-r--r--apps/plugins/frotz/screen.c1743
-rw-r--r--apps/plugins/frotz/setup.h67
-rw-r--r--apps/plugins/frotz/sound.c204
-rw-r--r--apps/plugins/frotz/stream.c365
-rw-r--r--apps/plugins/frotz/table.c193
-rw-r--r--apps/plugins/frotz/text.c1109
-rw-r--r--apps/plugins/frotz/variable.c304
29 files changed, 10853 insertions, 0 deletions
diff --git a/apps/plugins/frotz/SOURCES b/apps/plugins/frotz/SOURCES
new file mode 100644
index 0000000000..6a73e0c602
--- /dev/null
+++ b/apps/plugins/frotz/SOURCES
@@ -0,0 +1,22 @@
+frotz.c
+buffer.c
+err.c
+fastmem.c
+files.c
+hotkey.c
+input.c
+main.c
+math.c
+object.c
+process.c
+quetzal.c
+random.c
+redirect.c
+screen.c
+sound.c
+stream.c
+table.c
+text.c
+variable.c
+dumb_init.c
+dumb_output.c
diff --git a/apps/plugins/frotz/STATUS b/apps/plugins/frotz/STATUS
new file mode 100644
index 0000000000..3aa0f673ee
--- /dev/null
+++ b/apps/plugins/frotz/STATUS
@@ -0,0 +1,45 @@
+frotz is quite portable and is divided into 'common' and os-specific files.
+The common files are included here with minimal modifications. For the
+os-specific files I have started with dumbfrotz, the port intended for a plain
+C stdio system - it has its own screen buffering which is needed for rockbox.
+
+Things that work
+----------------
+
+Games, mostly! If the game is too large to fit in the plugin buffer it will
+stop playback and steal the audio buffer.
+
+Saving and restoring (/path/to/story.sav, no filename selection).
+
+Transcripts, command records and replays (likewise).
+
+Undo, up to the limit of available memory (the rest of the plugin buffer if
+the game fit there, or the rest of the audio buffer if not).
+
+Timed input, though it resets the timer when you enter the menu and only
+counts until you enter the keyboard.
+
+Input line preloading, though the actual displayed line after editing looks
+wrong.
+
+Things that don't work because I've not implemented it
+------------------------------------------------------
+
+Reading buttons that don't appear on the rockbox keyboard.
+
+Audible beeps (just a splash for now).
+
+Setting the random seed.
+
+Things which don't work in the original frotz anyway
+----------------------------------------------------
+
+Mouse and menus.
+
+Pictures.
+
+Non-beep sound samples.
+
+Unicode.
+
+Colours.
diff --git a/apps/plugins/frotz/buffer.c b/apps/plugins/frotz/buffer.c
new file mode 100644
index 0000000000..298ac69c20
--- /dev/null
+++ b/apps/plugins/frotz/buffer.c
@@ -0,0 +1,152 @@
+/* buffer.c - Text buffering and word wrapping
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+extern void stream_char (zchar);
+extern void stream_word (const zchar *);
+extern void stream_new_line (void);
+
+static zchar buffer[TEXT_BUFFER_SIZE];
+static int bufpos = 0;
+
+static zchar prev_c = 0;
+
+/*
+ * flush_buffer
+ *
+ * Copy the contents of the text buffer to the output streams.
+ *
+ */
+
+void flush_buffer (void)
+{
+ static bool locked = FALSE;
+
+ /* Make sure we stop when flush_buffer is called from flush_buffer.
+ Note that this is difficult to avoid as we might print a newline
+ during flush_buffer, which might cause a newline interrupt, that
+ might execute any arbitrary opcode, which might flush the buffer. */
+
+ if (locked || bufpos == 0)
+ return;
+
+ /* Send the buffer to the output streams */
+
+ buffer[bufpos] = 0;
+
+
+ locked = TRUE;
+
+ stream_word (buffer);
+
+#ifdef SPEECH_OUTPUT
+ os_speech_output(buffer);
+#endif
+
+ locked = FALSE;
+
+ /* Reset the buffer */
+
+ bufpos = 0;
+ prev_c = 0;
+
+}/* flush_buffer */
+
+/*
+ * print_char
+ *
+ * High level output function.
+ *
+ */
+
+void print_char (zchar c)
+{
+ static bool flag = FALSE;
+
+ if (message || ostream_memory || enable_buffering) {
+
+ if (!flag) {
+
+ /* Characters 0 and ZC_RETURN are special cases */
+
+ if (c == ZC_RETURN)
+ { new_line (); return; }
+ if (c == 0)
+ return;
+
+ /* Flush the buffer before a whitespace or after a hyphen */
+
+ if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (prev_c == '-' && c != '-'))
+
+
+ flush_buffer ();
+
+ /* Set the flag if this is part one of a style or font change */
+
+ if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
+ flag = TRUE;
+
+ /* Remember the current character code */
+
+ prev_c = c;
+
+ } else flag = FALSE;
+
+ /* Insert the character into the buffer */
+
+ buffer[bufpos++] = c;
+
+ if (bufpos == TEXT_BUFFER_SIZE)
+ runtime_error (ERR_TEXT_BUF_OVF);
+
+ } else stream_char (c);
+
+}/* print_char */
+
+/*
+ * new_line
+ *
+ * High level newline function.
+ *
+ */
+
+void new_line (void)
+{
+
+ flush_buffer (); stream_new_line ();
+
+}/* new_line */
+
+
+/*
+ * init_buffer
+ *
+ * Initialize buffer variables.
+ *
+ */
+
+void init_buffer(void)
+{
+ memset(buffer, 0, sizeof (zchar) * TEXT_BUFFER_SIZE);
+ bufpos = 0;
+ prev_c = 0;
+}
+
diff --git a/apps/plugins/frotz/dumb_frotz.h b/apps/plugins/frotz/dumb_frotz.h
new file mode 100644
index 0000000000..0b8415e4ae
--- /dev/null
+++ b/apps/plugins/frotz/dumb_frotz.h
@@ -0,0 +1,19 @@
+/* dumb-frotz.h
+ * $Id: dumb-frotz.h,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
+ * Frotz os functions for a standard C library and a dumb terminal.
+ * Now you can finally play Zork Zero on your Teletype.
+ *
+ * Copyright 1997, 1998 Alembic Petrofsky <alembic@petrofsky.berkeley.ca.us>.
+ * Any use permitted provided this notice stays intact.
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ */
+#include "frotz.h"
+
+/* from ../common/setup.h */
+extern f_setup_t f_setup;
+
+/* dumb-output.c */
+void dumb_init_output(void);
+void dumb_show_screen(bool show_cursor);
+void dumb_dump_screen(void);
diff --git a/apps/plugins/frotz/dumb_init.c b/apps/plugins/frotz/dumb_init.c
new file mode 100644
index 0000000000..ea08447c0c
--- /dev/null
+++ b/apps/plugins/frotz/dumb_init.c
@@ -0,0 +1,86 @@
+/* dumb-init.c
+ * $Id: dumb-init.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
+ *
+ * Copyright 1997,1998 Alva Petrofsky <alva@petrofsky.berkeley.ca.us>.
+ * Any use permitted provided this notice stays intact.
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ */
+
+#include "dumb_frotz.h"
+#include "lib/pluginlib_exit.h"
+
+static int user_screen_width = LCD_WIDTH / SYSFONT_WIDTH;
+static int user_screen_height = LCD_HEIGHT / SYSFONT_HEIGHT;
+static int user_interpreter_number = -1;
+static int user_random_seed = -1;
+static int user_tandy_bit = 0;
+
+
+void os_init_screen(void)
+{
+ if (h_version == V3 && user_tandy_bit)
+ h_config |= CONFIG_TANDY;
+
+ if (h_version >= V5 && f_setup.undo_slots == 0)
+ h_flags &= ~UNDO_FLAG;
+
+ h_screen_rows = user_screen_height;
+ h_screen_cols = user_screen_width;
+
+ if (user_interpreter_number > 0)
+ h_interpreter_number = user_interpreter_number;
+ else {
+ /* Use ms-dos for v6 (because that's what most people have the
+ * graphics files for), but don't use it for v5 (or Beyond Zork
+ * will try to use funky characters). */
+ h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
+ }
+ h_interpreter_version = 'F';
+
+ if (h_version >= V4)
+ h_config |= CONFIG_TIMEDINPUT;
+
+ if (h_version >= V5)
+ h_flags &= ~(MOUSE_FLAG | MENU_FLAG);
+
+ dumb_init_output();
+
+ h_flags &= ~GRAPHICS_FLAG;
+}
+
+int os_random_seed (void)
+{
+ if (user_random_seed == -1)
+ /* Use the rockbox tick as seed value */
+ return ((int)rb->current_tick) & 0x7fff;
+ else return user_random_seed;
+}
+
+void os_restart_game (int stage) { (void)stage; }
+
+void os_fatal (const char *s)
+{
+ rb->splash(HZ*10, s);
+ exit(1);
+}
+
+void os_init_setup(void)
+{
+ f_setup.attribute_assignment = 0;
+ f_setup.attribute_testing = 0;
+ f_setup.context_lines = 0;
+ f_setup.object_locating = 0;
+ f_setup.object_movement = 0;
+ f_setup.left_margin = 0;
+ f_setup.right_margin = 0;
+ f_setup.ignore_errors = 0;
+ f_setup.piracy = 0;
+ f_setup.undo_slots = MAX_UNDO_SLOTS;
+ f_setup.expand_abbreviations = 0;
+ f_setup.script_cols = 80;
+ f_setup.save_quetzal = 1;
+ f_setup.sound = 1;
+ f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
+
+}
diff --git a/apps/plugins/frotz/dumb_output.c b/apps/plugins/frotz/dumb_output.c
new file mode 100644
index 0000000000..eb61419195
--- /dev/null
+++ b/apps/plugins/frotz/dumb_output.c
@@ -0,0 +1,256 @@
+/* dumb-output.c
+ * $Id: dumb-output.c,v 1.2 2002/03/26 22:52:31 feedle Exp $
+ *
+ * Copyright 1997,1998 Alfresco Petrofsky <alfresco@petrofsky.berkeley.edu>.
+ * Any use permitted provided this notice stays intact.
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * Rockbox is not really a dumb terminal (it supports printing text wherever
+ * you like) but it doesn't implement a terminal type buffer, so this is
+ * close enough to be a good starting point. Keeping a copy of the graphical
+ * framebuffer would be too expensive, text+attributes is much smaller.
+ */
+
+#include "dumb_frotz.h"
+
+/* The in-memory state of the screen. */
+/* Each cell contains a style in the upper byte and a char in the lower. */
+typedef unsigned short cell;
+static cell screen_data[(LCD_WIDTH/SYSFONT_WIDTH) * (LCD_HEIGHT/SYSFONT_HEIGHT)];
+
+static cell make_cell(int style, char c) {return (style << 8) | (0xff & c);}
+static char cell_char(cell c) {return c & 0xff;}
+static int cell_style(cell c) {return c >> 8;}
+
+static int current_style = 0;
+static int cursor_row = 0, cursor_col = 0;
+
+static cell *dumb_row(int r) {return screen_data + r * h_screen_cols;}
+
+int os_char_width (zchar z)
+{
+ (void)z;
+ return 1;
+}
+
+int os_string_width (const zchar *s)
+{
+ int width = 0;
+ zchar c;
+
+ while ((c = *s++) != 0)
+ if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
+ s++;
+ else
+ width += os_char_width(c);
+
+ return width;
+}
+
+void os_set_cursor(int row, int col)
+{
+ cursor_row = row - 1; cursor_col = col - 1;
+ if (cursor_row >= h_screen_rows)
+ cursor_row = h_screen_rows - 1;
+}
+
+/* Set a cell */
+static void dumb_set_cell(int row, int col, cell c)
+{
+ dumb_row(row)[col] = c;
+}
+
+void os_set_text_style(int x)
+{
+ current_style = x & REVERSE_STYLE;
+}
+
+static void dumb_display_cell(int row, int col)
+{
+ cell cel = dumb_row(row)[col];
+ char c = cell_char(cel);
+ if (!c)
+ c = ' ';
+ char style = cell_style(cel);
+ char s[5];
+ char *end = rb->utf8encode(c, s);
+ *end = 0;
+ if (style & REVERSE_STYLE)
+ rb->lcd_set_drawmode(DRMODE_INVERSEVID);
+ rb->lcd_putsxy(col * SYSFONT_WIDTH, row * SYSFONT_HEIGHT, s);
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+}
+
+/* put a character in the cell at the cursor and advance the cursor. */
+static void dumb_display_char(char c)
+{
+ dumb_set_cell(cursor_row, cursor_col, make_cell(current_style, c));
+ if (++cursor_col == h_screen_cols) {
+ if (cursor_row == h_screen_rows - 1)
+ cursor_col--;
+ else {
+ cursor_row++;
+ cursor_col = 0;
+ }
+ }
+}
+
+void os_display_char (zchar c)
+{
+ if (c >= ZC_LATIN1_MIN /*&& c <= ZC_LATIN1_MAX*/) {
+ dumb_display_char(c);
+ } else if (c >= 32 && c <= 126) {
+ dumb_display_char(c);
+ } else if (c == ZC_GAP) {
+ dumb_display_char(' '); dumb_display_char(' ');
+ } else if (c == ZC_INDENT) {
+ dumb_display_char(' '); dumb_display_char(' '); dumb_display_char(' ');
+ }
+ return;
+}
+
+
+/* Haxor your boxor? */
+void os_display_string (const zchar *s)
+{
+ zchar c;
+
+ while ((c = *s++) != 0)
+ if (c == ZC_NEW_FONT)
+ s++;
+ else if (c == ZC_NEW_STYLE)
+ os_set_text_style(*s++);
+ else {
+ os_display_char (c);
+ }
+}
+
+void os_erase_area (int top, int left, int bottom, int right)
+{
+ int row;
+ top--; left--; bottom--; right--;
+ if (left == 0 && right == h_screen_cols - 1)
+ rb->memset(dumb_row(top), 0, (bottom - top + 1) * h_screen_cols * sizeof(cell));
+ else
+ for (row = top; row <= bottom; row++)
+ rb->memset(dumb_row(row) + left, 0, (right - left + 1) * sizeof(cell));
+}
+
+void os_scroll_area (int top, int left, int bottom, int right, int units)
+{
+ int row;
+ top--; left--; bottom--; right--;
+ if (units > 0) {
+ for (row = top; row <= bottom - units; row++)
+ rb->memcpy(dumb_row(row) + left,
+ dumb_row(row + units) + left,
+ (right - left + 1) * sizeof(cell));
+ os_erase_area(bottom - units + 2, left + 1, bottom + 1, right + 1);
+ } else if (units < 0) {
+ for (row = bottom; row >= top - units; row--)
+ rb->memcpy(dumb_row(row) + left,
+ dumb_row(row + units) + left,
+ (right - left + 1) * sizeof(cell));
+ os_erase_area(top + 1, left + 1, top - units, right + 1);
+ }
+}
+
+int os_font_data(int font, int *height, int *width)
+{
+ if (font == TEXT_FONT) {
+ *height = 1; *width = 1; return 1;
+ }
+ return 0;
+}
+
+void os_set_colour (int x, int y) { (void)x; (void)y; }
+void os_set_font (int x) { (void)x; }
+
+/* Unconditionally show whole screen. */
+void dumb_dump_screen(void)
+{
+ int r, c;
+ rb->lcd_clear_display();
+ for (r = 0; r < h_screen_height; r++)
+ for (c = 0; c < h_screen_width; c++)
+ dumb_display_cell(r, c);
+ rb->lcd_update();
+}
+
+/* Show the current screen contents. */
+void dumb_show_screen(bool show_cursor)
+{
+ (void)show_cursor;
+ dumb_dump_screen();
+}
+
+void os_more_prompt(void)
+{
+ int old_row = cursor_row;
+ int old_col = cursor_col;
+ int old_style = current_style;
+
+ current_style = REVERSE_STYLE;
+ os_display_string("[MORE]");
+ wait_for_key();
+
+ cursor_row = old_row;
+ cursor_col = old_col;
+ current_style = old_style;
+ os_erase_area(cursor_row+1, cursor_col+1, cursor_row+1, cursor_col+7);
+}
+
+void os_reset_screen(void)
+{
+ current_style = REVERSE_STYLE;
+ os_display_string("[Press key to exit]");
+ wait_for_key();
+}
+
+
+/* To make the common code happy */
+
+void os_prepare_sample (int a) { (void)a; }
+void os_finish_with_sample (int a) { (void)a; }
+void os_start_sample (int a, int b, int c, zword d)
+{
+ (void)a; (void)b; (void)c; (void)d;
+}
+void os_stop_sample (int a) { (void)a; }
+
+void dumb_init_output(void)
+{
+ if (h_version == V3) {
+ h_config |= CONFIG_SPLITSCREEN;
+ h_flags &= ~OLD_SOUND_FLAG;
+ }
+
+ if (h_version >= V5) {
+ h_flags &= ~SOUND_FLAG;
+ }
+
+ h_screen_height = h_screen_rows;
+ h_screen_width = h_screen_cols;
+
+ h_font_width = 1; h_font_height = 1;
+
+ os_erase_area(1, 1, h_screen_rows, h_screen_cols);
+}
+
+bool os_picture_data(int num, int *height, int *width)
+{
+ (void)num;
+ *height = 0;
+ *width = 0;
+ return FALSE;
+}
+
+void os_draw_picture (int num, int row, int col)
+{
+ (void)num;
+ (void)row;
+ (void)col;
+}
+
+int os_peek_colour (void) {return BLACK_COLOUR; }
diff --git a/apps/plugins/frotz/err.c b/apps/plugins/frotz/err.c
new file mode 100644
index 0000000000..61ca78ce3b
--- /dev/null
+++ b/apps/plugins/frotz/err.c
@@ -0,0 +1,154 @@
+/* err.c - Runtime error reporting functions
+ * Written by Jim Dunleavy <jim.dunleavy@erha.ie>
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+/* Define stuff for stricter Z-code error checking, for the generic
+ Unix/DOS/etc terminal-window interface. Feel free to change the way
+ player prefs are specified, or replace report_zstrict_error()
+ completely if you want to change the way errors are reported. */
+
+/* int err_report_mode = ERR_DEFAULT_REPORT_MODE; */
+
+static int error_count[ERR_NUM_ERRORS];
+
+static char *err_messages[] = {
+ "Text buffer overflow",
+ "Store out of dynamic memory",
+ "Division by zero",
+ "Illegal object",
+ "Illegal attribute",
+ "No such property",
+ "Stack overflow",
+ "Call to illegal address",
+ "Call to non-routine",
+ "Stack underflow",
+ "Illegal opcode",
+ "Bad stack frame",
+ "Jump to illegal address",
+ "Can't save while in interrupt",
+ "Nesting stream #3 too deep",
+ "Illegal window",
+ "Illegal window property",
+ "Print at illegal address",
+ "@jin called with object 0",
+ "@get_child called with object 0",
+ "@get_parent called with object 0",
+ "@get_sibling called with object 0",
+ "@get_prop_addr called with object 0",
+ "@get_prop called with object 0",
+ "@put_prop called with object 0",
+ "@clear_attr called with object 0",
+ "@set_attr called with object 0",
+ "@test_attr called with object 0",
+ "@move_object called moving object 0",
+ "@move_object called moving into object 0",
+ "@remove_object called with object 0",
+ "@get_next_prop called with object 0"
+};
+
+static void print_long (unsigned long value, int base);
+
+/*
+ * init_err
+ *
+ * Initialise error reporting.
+ *
+ */
+
+void init_err (void)
+{
+ int i;
+
+ /* Initialize the counters. */
+
+ for (i = 0; i < ERR_NUM_ERRORS; i++)
+ error_count[i] = 0;
+}
+
+/*
+ * runtime_error
+ *
+ * An error has occurred. Ignore it, pass it to os_fatal or report
+ * it according to err_report_mode.
+ *
+ * errnum : Numeric code for error (1 to ERR_NUM_ERRORS)
+ *
+ */
+
+void runtime_error (int errnum)
+{
+ int wasfirst;
+
+ if (errnum <= 0 || errnum > ERR_NUM_ERRORS)
+ return;
+
+ if (f_setup.err_report_mode == ERR_REPORT_FATAL
+ || (!f_setup.ignore_errors && errnum <= ERR_MAX_FATAL)) {
+ flush_buffer ();
+ os_fatal (err_messages[errnum - 1]);
+ return;
+ }
+
+ wasfirst = (error_count[errnum - 1] == 0);
+ error_count[errnum - 1]++;
+
+ if ((f_setup.err_report_mode == ERR_REPORT_ALWAYS)
+ || (f_setup.err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
+ long pc;
+
+ GET_PC (pc);
+ print_string ("Warning: ");
+ print_string (err_messages[errnum - 1]);
+ print_string (" (PC = ");
+ print_long (pc, 16);
+ print_char (')');
+
+ if (f_setup.err_report_mode == ERR_REPORT_ONCE) {
+ print_string (" (will ignore further occurrences)");
+ } else {
+ print_string (" (occurence ");
+ print_long (error_count[errnum - 1], 10);
+ print_char (')');
+ }
+ new_line ();
+ }
+
+} /* report_error */
+
+/*
+ * print_long
+ *
+ * Print an unsigned 32bit number in decimal or hex.
+ *
+ */
+
+static void print_long (unsigned long value, int base)
+{
+ unsigned long i;
+ char c;
+
+ for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base)
+ if (value >= i || i == 1) {
+ c = (value / i) % base;
+ print_char (c + (c <= 9 ? '0' : 'a' - 10));
+ }
+
+}/* print_long */
diff --git a/apps/plugins/frotz/fastmem.c b/apps/plugins/frotz/fastmem.c
new file mode 100644
index 0000000000..cdb4bce602
--- /dev/null
+++ b/apps/plugins/frotz/fastmem.c
@@ -0,0 +1,1013 @@
+/* fastmem.c - Memory related functions (fast version without virtual memory)
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/*
+ * New undo mechanism added by Jim Dunleavy <jim.dunleavy@erha.ie>
+ */
+
+#include "frotz.h"
+
+#define far
+
+extern void seed_random (int);
+extern void restart_screen (void);
+extern void refresh_text_style (void);
+extern void call (zword, int, zword *, int);
+extern void split_window (zword);
+extern void script_open (void);
+extern void script_close (void);
+
+extern zword save_quetzal (int, int);
+extern zword restore_quetzal (int, int);
+
+extern void erase_window (zword);
+
+extern void (*op0_opcodes[]) (void);
+extern void (*op1_opcodes[]) (void);
+extern void (*op2_opcodes[]) (void);
+extern void (*var_opcodes[]) (void);
+
+char save_name[MAX_PATH];
+char auxilary_name[MAX_PATH];
+
+zbyte far *zmp = NULL;
+zbyte far *pcp = NULL;
+
+int story_fp = -1;
+
+/*
+ * Data for the undo mechanism.
+ * This undo mechanism is based on the scheme used in Evin Robertson's
+ * Nitfol interpreter.
+ * Undo blocks are stored as differences between states.
+ */
+
+typedef struct undo_struct undo_t;
+struct undo_struct {
+ undo_t *next;
+ undo_t *prev;
+ long pc;
+ long diff_size;
+ zword frame_count;
+ zword stack_size;
+ zword frame_offset;
+ /* undo diff and stack data follow */
+};
+
+static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL;
+static zbyte *prev_zmp, *undo_diff;
+
+static int undo_count = 0;
+
+static void *arena_start = NULL, *arena_end = NULL, *arena_next = NULL;
+
+/*
+ * get_header_extension
+ *
+ * Read a value from the header extension (former mouse table).
+ *
+ */
+
+zword get_header_extension (int entry)
+{
+ zword addr;
+ zword val;
+
+ if (h_extension_table == 0 || entry > hx_table_size)
+ return 0;
+
+ addr = h_extension_table + 2 * entry;
+ LOW_WORD (addr, val)
+
+ return val;
+
+}/* get_header_extension */
+
+/*
+ * set_header_extension
+ *
+ * Set an entry in the header extension (former mouse table).
+ *
+ */
+
+void set_header_extension (int entry, zword val)
+{
+ zword addr;
+
+ if (h_extension_table == 0 || entry > hx_table_size)
+ return;
+
+ addr = h_extension_table + 2 * entry;
+ SET_WORD (addr, val)
+
+}/* set_header_extension */
+
+/*
+ * restart_header
+ *
+ * Set all header fields which hold information about the interpreter.
+ *
+ */
+
+void restart_header (void)
+{
+ zword screen_x_size;
+ zword screen_y_size;
+ zbyte font_x_size;
+ zbyte font_y_size;
+
+ int i;
+
+ SET_BYTE (H_CONFIG, h_config)
+ SET_WORD (H_FLAGS, h_flags)
+
+ if (h_version >= V4) {
+ SET_BYTE (H_INTERPRETER_NUMBER, h_interpreter_number)
+ SET_BYTE (H_INTERPRETER_VERSION, h_interpreter_version)
+ SET_BYTE (H_SCREEN_ROWS, h_screen_rows)
+ SET_BYTE (H_SCREEN_COLS, h_screen_cols)
+ }
+
+ /* It's less trouble to use font size 1x1 for V5 games, especially
+ because of a bug in the unreleased German version of "Zork 1" */
+
+ if (h_version != V6) {
+ screen_x_size = (zword) h_screen_cols;
+ screen_y_size = (zword) h_screen_rows;
+ font_x_size = 1;
+ font_y_size = 1;
+ } else {
+ screen_x_size = h_screen_width;
+ screen_y_size = h_screen_height;
+ font_x_size = h_font_width;
+ font_y_size = h_font_height;
+ }
+
+ if (h_version >= V5) {
+ SET_WORD (H_SCREEN_WIDTH, screen_x_size)
+ SET_WORD (H_SCREEN_HEIGHT, screen_y_size)
+ SET_BYTE (H_FONT_HEIGHT, font_y_size)
+ SET_BYTE (H_FONT_WIDTH, font_x_size)
+ SET_BYTE (H_DEFAULT_BACKGROUND, h_default_background)
+ SET_BYTE (H_DEFAULT_FOREGROUND, h_default_foreground)
+ }
+
+ if (h_version == V6)
+ for (i = 0; i < 8; i++)
+ storeb ((zword) (H_USER_NAME + i), h_user_name[i]);
+
+ SET_BYTE (H_STANDARD_HIGH, h_standard_high)
+ SET_BYTE (H_STANDARD_LOW, h_standard_low)
+
+}/* restart_header */
+
+/*
+ * init_memory
+ *
+ * Allocate memory and load the story file.
+ *
+ */
+
+void init_memory (void)
+{
+ long size;
+ zword addr;
+ unsigned n;
+ int i, j;
+ zbyte *audiobuf;
+ size_t buf_size;
+
+ static struct {
+ enum story story_id;
+ zword release;
+ zbyte serial[6];
+ } records[] = {
+ { SHERLOCK, 21, "871214" },
+ { SHERLOCK, 26, "880127" },
+ { BEYOND_ZORK, 47, "870915" },
+ { BEYOND_ZORK, 49, "870917" },
+ { BEYOND_ZORK, 51, "870923" },
+ { BEYOND_ZORK, 57, "871221" },
+ { ZORK_ZERO, 296, "881019" },
+ { ZORK_ZERO, 366, "890323" },
+ { ZORK_ZERO, 383, "890602" },
+ { ZORK_ZERO, 393, "890714" },
+ { SHOGUN, 292, "890314" },
+ { SHOGUN, 295, "890321" },
+ { SHOGUN, 311, "890510" },
+ { SHOGUN, 322, "890706" },
+ { ARTHUR, 54, "890606" },
+ { ARTHUR, 63, "890622" },
+ { ARTHUR, 74, "890714" },
+ { JOURNEY, 26, "890316" },
+ { JOURNEY, 30, "890322" },
+ { JOURNEY, 77, "890616" },
+ { JOURNEY, 83, "890706" },
+ { LURKING_HORROR, 203, "870506" },
+ { LURKING_HORROR, 219, "870912" },
+ { LURKING_HORROR, 221, "870918" },
+ { UNKNOWN, 0, "------" }
+ };
+
+ /* Open story file */
+
+ if ((story_fp = rb->open(story_name, O_RDONLY)) < 0)
+ os_fatal ("Cannot open story file");
+
+ /* Allocate memory for story header */
+
+ zmp = rb->plugin_get_buffer(&buf_size);
+
+ /* Load header into memory */
+
+ if (fread (zmp, 1, 64, story_fp) != 64)
+ os_fatal ("Story file read error");
+
+ /* Copy header fields to global variables */
+
+ LOW_BYTE (H_VERSION, h_version)
+
+ if (h_version < V1 || h_version > V8)
+ os_fatal ("Unknown Z-code version");
+
+ LOW_BYTE (H_CONFIG, h_config)
+
+ if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
+ os_fatal ("Byte swapped story file");
+
+ LOW_WORD (H_RELEASE, h_release)
+ LOW_WORD (H_RESIDENT_SIZE, h_resident_size)
+ LOW_WORD (H_START_PC, h_start_pc)
+ LOW_WORD (H_DICTIONARY, h_dictionary)
+ LOW_WORD (H_OBJECTS, h_objects)
+ LOW_WORD (H_GLOBALS, h_globals)
+ LOW_WORD (H_DYNAMIC_SIZE, h_dynamic_size)
+ LOW_WORD (H_FLAGS, h_flags)
+
+ for (i = 0, addr = H_SERIAL; i < 6; i++, addr++)
+ LOW_BYTE (addr, h_serial[i])
+
+ /* Auto-detect buggy story files that need special fixes */
+
+ story_id = UNKNOWN;
+
+ for (i = 0; records[i].story_id != UNKNOWN; i++) {
+
+ if (h_release == records[i].release) {
+
+ for (j = 0; j < 6; j++)
+ if (h_serial[j] != records[i].serial[j])
+ goto no_match;
+
+ story_id = records[i].story_id;
+
+ }
+
+ no_match: ; /* null statement */
+
+ }
+
+ LOW_WORD (H_ABBREVIATIONS, h_abbreviations)
+ LOW_WORD (H_FILE_SIZE, h_file_size)
+
+ /* Calculate story file size in bytes */
+
+ if (h_file_size != 0) {
+
+ story_size = (long) 2 * h_file_size;
+
+ if (h_version >= V4)
+ story_size *= 2;
+ if (h_version >= V6)
+ story_size *= 2;
+
+ } else { /* some old games lack the file size entry */
+
+ fseek (story_fp, 0, SEEK_END);
+ story_size = ftell (story_fp);
+ fseek (story_fp, 64, SEEK_SET);
+
+ }
+
+ LOW_WORD (H_CHECKSUM, h_checksum)
+ LOW_WORD (H_ALPHABET, h_alphabet)
+ LOW_WORD (H_FUNCTIONS_OFFSET, h_functions_offset)
+ LOW_WORD (H_STRINGS_OFFSET, h_strings_offset)
+ LOW_WORD (H_TERMINATING_KEYS, h_terminating_keys)
+ LOW_WORD (H_EXTENSION_TABLE, h_extension_table)
+
+ /* Zork Zero Macintosh doesn't have the graphics flag set */
+
+ if (story_id == ZORK_ZERO && h_release == 296)
+ h_flags |= GRAPHICS_FLAG;
+
+ /* Adjust opcode tables */
+
+ if (h_version <= V4) {
+ op0_opcodes[0x09] = z_pop;
+ op1_opcodes[0x0f] = z_not;
+ } else {
+ op0_opcodes[0x09] = z_catch;
+ op1_opcodes[0x0f] = z_call_n;
+ }
+
+ /* Allocate memory for story data */
+
+ if ((size_t)story_size > buf_size)
+ {
+ audiobuf = rb->plugin_get_audio_buffer(&buf_size);
+ if ((size_t)story_size > buf_size)
+ os_fatal ("Out of memory");
+ rb->memcpy(audiobuf, zmp, 64);
+ zmp = audiobuf;
+ }
+
+ /* Assign left over memory as the arena for undo alloc */
+ arena_start = arena_next = (void*)((int)(zmp + story_size + 3) & ~3);
+ arena_end = zmp + buf_size;
+
+ /* Load story file in chunks of 32KB */
+
+ n = 0x8000;
+
+ for (size = 64; size < story_size; size += n) {
+
+ if (story_size - size < 0x8000)
+ n = (unsigned) (story_size - size);
+
+ SET_PC (size)
+
+ if (fread (pcp, 1, n, story_fp) != (signed)n)
+ os_fatal ("Story file read error");
+
+ }
+
+ /* Read header extension table */
+
+ hx_table_size = get_header_extension (HX_TABLE_SIZE);
+ hx_unicode_table = get_header_extension (HX_UNICODE_TABLE);
+
+}/* init_memory */
+
+/*
+ * init_undo
+ *
+ * Allocate memory for multiple undo. It is important not to occupy
+ * all the memory available, since the IO interface may need memory
+ * during the game, e.g. for loading sounds or pictures.
+ *
+ */
+
+void init_undo (void)
+{
+ /* Allocate h_dynamic_size bytes for previous dynamic zmp state
+ + 1.5 h_dynamic_size for Quetzal diff + 2. */
+ int size = (h_dynamic_size * 5) / 2 + 2;
+ if ((arena_end - arena_start) >= size) {
+ prev_zmp = arena_start;
+ undo_diff = arena_start + h_dynamic_size;
+ arena_start = (void*)((int)(arena_start + size + 3) & ~3);
+ arena_next = arena_start;
+ memcpy (prev_zmp, zmp, h_dynamic_size);
+ } else
+ f_setup.undo_slots = 0;
+
+}/* init_undo */
+
+/*
+ * free_undo
+ *
+ * Free count undo blocks from the beginning of the undo list.
+ *
+ */
+
+static void free_undo (int count)
+{
+ undo_t *p;
+
+ if (count > undo_count)
+ count = undo_count;
+ while (count--) {
+ p = first_undo;
+ if (curr_undo == first_undo)
+ curr_undo = curr_undo->next;
+ first_undo = first_undo->next;
+ undo_count--;
+ }
+ if (first_undo)
+ first_undo->prev = NULL;
+ else
+ last_undo = NULL;
+}/* free_undo */
+
+/*
+ * reset_memory
+ *
+ * Close the story file and deallocate memory.
+ *
+ */
+
+void reset_memory (void)
+{
+ if (story_fp != -1)
+ fclose (story_fp);
+ story_fp = -1;
+
+ free_undo (undo_count);
+ undo_count = 0;
+
+ arena_start = NULL;
+ arena_end = NULL;
+ arena_next = NULL;
+ zmp = NULL;
+}/* reset_memory */
+
+/*
+ * storeb
+ *
+ * Write a byte value to the dynamic Z-machine memory.
+ *
+ */
+
+void storeb (zword addr, zbyte value)
+{
+
+ if (addr >= h_dynamic_size)
+ runtime_error (ERR_STORE_RANGE);
+
+ if (addr == H_FLAGS + 1) { /* flags register is modified */
+
+ h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
+ h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
+
+ if (value & SCRIPTING_FLAG) {
+ if (!ostream_script)
+ script_open ();
+ } else {
+ if (ostream_script)
+ script_close ();
+ }
+
+ refresh_text_style ();
+
+ }
+
+ SET_BYTE (addr, value)
+
+}/* storeb */
+
+/*
+ * storew
+ *
+ * Write a word value to the dynamic Z-machine memory.
+ *
+ */
+
+void storew (zword addr, zword value)
+{
+
+ storeb ((zword) (addr + 0), hi (value));
+ storeb ((zword) (addr + 1), lo (value));
+
+}/* storew */
+
+/*
+ * z_restart, re-load dynamic area, clear the stack and set the PC.
+ *
+ * no zargs used
+ *
+ */
+
+void z_restart (void)
+{
+ static bool first_restart = TRUE;
+
+ flush_buffer ();
+
+ os_restart_game (RESTART_BEGIN);
+
+ seed_random (0);
+
+ if (!first_restart) {
+
+ fseek (story_fp, 0, SEEK_SET);
+
+ if (fread (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size)
+ os_fatal ("Story file read error");
+
+ } else first_restart = FALSE;
+
+ restart_header ();
+ restart_screen ();
+
+ sp = fp = stack + STACK_SIZE;
+ frame_count = 0;
+
+ if (h_version != V6) {
+
+ long pc = (long) h_start_pc;
+ SET_PC (pc)
+
+ } else call (h_start_pc, 0, NULL, 0);
+
+ os_restart_game (RESTART_END);
+
+}/* z_restart */
+
+/*
+ * get_default_name
+ *
+ * Read a default file name from the memory of the Z-machine and
+ * copy it to a string.
+ *
+ */
+
+static void get_default_name (char *default_name, zword addr)
+{
+
+ if (addr != 0) {
+
+ zbyte len;
+ int i;
+
+ LOW_BYTE (addr, len)
+ addr++;
+
+ for (i = 0; i < len; i++) {
+
+ zbyte c;
+
+ LOW_BYTE (addr, c)
+ addr++;
+
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+
+ default_name[i] = c;
+
+ }
+
+ default_name[i] = 0;
+
+ if (strchr (default_name, '.') == NULL)
+ strcpy (default_name + i, ".AUX");
+
+ } else strcpy (default_name, auxilary_name);
+
+}/* get_default_name */
+
+/*
+ * z_restore, restore [a part of] a Z-machine state from disk
+ *
+ * zargs[0] = address of area to restore (optional)
+ * zargs[1] = number of bytes to restore
+ * zargs[2] = address of suggested file name
+ *
+ */
+
+void z_restore (void)
+{
+ char new_name[MAX_PATH];
+ char default_name[MAX_PATH];
+ int gfp;
+
+ zword success = 0;
+
+ if (zargc != 0) {
+
+ /* Get the file name */
+
+ get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
+
+ if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0)
+ goto finished;
+
+ strcpy (auxilary_name, default_name);
+
+ /* Open auxilary file */
+
+ if ((gfp = rb->open (new_name, O_RDONLY)) < 0)
+ goto finished;
+
+ /* Load auxilary file */
+
+ success = fread (zmp + zargs[0], 1, zargs[1], gfp);
+
+ /* Close auxilary file */
+
+ fclose (gfp);
+
+ } else {
+
+ /* Get the file name */
+
+ if (os_read_file_name (new_name, save_name, FILE_RESTORE) == 0)
+ goto finished;
+
+ strcpy (save_name, new_name);
+
+ /* Open game file */
+
+ if ((gfp = rb->open (new_name, O_RDONLY)) < 0)
+ goto finished;
+
+ success = restore_quetzal (gfp, story_fp);
+
+ /* Close game file */
+
+ fclose (gfp);
+
+ if ((short) success >= 0) {
+
+ if ((short) success > 0) {
+ zbyte old_screen_rows;
+ zbyte old_screen_cols;
+
+ /* In V3, reset the upper window. */
+ if (h_version == V3)
+ split_window (0);
+
+ LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
+ LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
+
+ /* Reload cached header fields. */
+ restart_header ();
+
+ /*
+ * Since QUETZAL files may be saved on many different machines,
+ * the screen sizes may vary a lot. Erasing the status window
+ * seems to cover up most of the resulting badness.
+ */
+ if (h_version > V3 && h_version != V6
+ && (h_screen_rows != old_screen_rows
+ || h_screen_cols != old_screen_cols))
+ erase_window (1);
+ }
+ } else
+ os_fatal ("Error reading save file");
+ }
+
+finished:
+
+ if (h_version <= V3)
+ branch (success);
+ else
+ store (success);
+
+}/* z_restore */
+
+/*
+ * mem_diff
+ *
+ * Set diff to a Quetzal-like difference between a and b,
+ * copying a to b as we go. It is assumed that diff points to a
+ * buffer which is large enough to hold the diff.
+ * mem_size is the number of bytes to compare.
+ * Returns the number of bytes copied to diff.
+ *
+ */
+
+static long mem_diff (zbyte *a, zbyte *b, zword mem_size, zbyte *diff)
+{
+ unsigned size = mem_size;
+ zbyte *p = diff;
+ unsigned j;
+ zbyte c;
+
+ for (;;) {
+ for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
+ size--;
+ if (size == 0) break;
+ size--;
+ if (j > 0x8000) {
+ *p++ = 0;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ j -= 0x8000;
+ }
+ if (j > 0) {
+ *p++ = 0;
+ j--;
+ if (j <= 0x7f) {
+ *p++ = j;
+ } else {
+ *p++ = (j & 0x7f) | 0x80;
+ *p++ = (j & 0x7f80) >> 7;
+ }
+ }
+ *p++ = c;
+ *(b - 1) ^= c;
+ }
+ return p - diff;
+}/* mem_diff */
+
+/*
+ * mem_undiff
+ *
+ * Applies a quetzal-like diff to dest
+ *
+ */
+
+static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest)
+{
+ zbyte c;
+
+ while (diff_length) {
+ c = *diff++;
+ diff_length--;
+ if (c == 0) {
+ unsigned runlen;
+
+ if (!diff_length)
+ return; /* Incomplete run */
+ runlen = *diff++;
+ diff_length--;
+ if (runlen & 0x80) {
+ if (!diff_length)
+ return; /* Incomplete extended run */
+ c = *diff++;
+ diff_length--;
+ runlen = (runlen & 0x7f) | (((unsigned) c) << 7);
+ }
+
+ dest += runlen + 1;
+ } else {
+ *dest++ ^= c;
+ }
+ }
+}/* mem_undiff */
+
+/*
+ * restore_undo
+ *
+ * This function does the dirty work for z_restore_undo.
+ *
+ */
+
+int restore_undo (void)
+{
+
+ if (f_setup.undo_slots == 0) /* undo feature unavailable */
+
+ return -1;
+
+ if (curr_undo == NULL) /* no saved game state */
+
+ return 0;
+
+ /* undo possible */
+
+ memcpy (zmp, prev_zmp, h_dynamic_size);
+ SET_PC (curr_undo->pc)
+ sp = stack + STACK_SIZE - curr_undo->stack_size;
+ fp = stack + curr_undo->frame_offset;
+ frame_count = curr_undo->frame_count;
+ mem_undiff ((zbyte *) (curr_undo + 1), curr_undo->diff_size, prev_zmp);
+ memcpy (sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
+ curr_undo->stack_size * sizeof (*sp));
+
+ curr_undo = curr_undo->prev;
+
+ restart_header ();
+
+ return 2;
+
+}/* restore_undo */
+
+/*
+ * z_restore_undo, restore a Z-machine state from memory.
+ *
+ * no zargs used
+ *
+ */
+
+void z_restore_undo (void)
+{
+
+ store ((zword) restore_undo ());
+
+}/* z_restore_undo */
+
+/*
+ * z_save, save [a part of] the Z-machine state to disk.
+ *
+ * zargs[0] = address of memory area to save (optional)
+ * zargs[1] = number of bytes to save
+ * zargs[2] = address of suggested file name
+ *
+ */
+
+void z_save (void)
+{
+ char new_name[MAX_PATH];
+ char default_name[MAX_PATH];
+ int gfp;
+
+ zword success = 0;
+
+ if (zargc != 0) {
+
+ /* Get the file name */
+
+ get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
+
+ if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0)
+ goto finished;
+
+ strcpy (auxilary_name, default_name);
+
+ /* Open auxilary file */
+
+ if ((gfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
+ goto finished;
+
+ /* Write auxilary file */
+
+ success = fwrite (zmp + zargs[0], zargs[1], 1, gfp);
+
+ /* Close auxilary file */
+
+ fclose (gfp);
+
+ } else {
+
+ /* Get the file name */
+
+ if (os_read_file_name (new_name, save_name, FILE_SAVE) == 0)
+ goto finished;
+
+ strcpy (save_name, new_name);
+
+ /* Open game file */
+
+ if ((gfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
+ goto finished;
+
+ success = save_quetzal (gfp, story_fp);
+
+ /* Close game file and check for errors */
+
+ if (fclose (gfp) != 0 || ferror (story_fp)) {
+ print_string ("Error writing save file\n");
+ goto finished;
+ }
+
+ /* Success */
+
+ success = 1;
+
+ }
+
+finished:
+
+ if (h_version <= V3)
+ branch (success);
+ else
+ store (success);
+
+}/* z_save */
+
+/*
+ * save_undo
+ *
+ * This function does the dirty work for z_save_undo.
+ *
+ */
+
+int save_undo (void)
+{
+ long diff_size;
+ zword stack_size;
+ int size;
+ undo_t *p;
+
+ if (f_setup.undo_slots == 0) /* undo feature unavailable */
+ return -1;
+
+ /* save undo possible */
+
+ while (last_undo != curr_undo) {
+ p = last_undo;
+ last_undo = last_undo->prev;
+ arena_next = p;
+ undo_count--;
+ }
+ if (last_undo)
+ last_undo->next = NULL;
+ else
+ first_undo = NULL;
+
+ if (undo_count == f_setup.undo_slots)
+ free_undo (1);
+
+ diff_size = mem_diff (zmp, prev_zmp, h_dynamic_size, undo_diff);
+ stack_size = stack + STACK_SIZE - sp;
+ do {
+ size = sizeof (undo_t) + diff_size + stack_size * sizeof (*sp);
+ if (arena_next > (void*)first_undo) {
+ /* Free space is all at the end */
+ if ((arena_end - arena_next) >= size) {
+ /* Trivial: enough room at the end */
+ p = arena_next;
+ arena_next = (void*)((int)(arena_next + size + 3) & ~3);
+ } else {
+ /* Need to wrap */
+ arena_next = arena_start;
+ p = NULL;
+ }
+ } else {
+ /* Free space is somewhere else */
+ if (((void*)first_undo - arena_next) >= size) {
+ /* There is room before the "first" undo */
+ p = arena_next;
+ arena_next = (void*)((int)(arena_next + size + 3) & ~3);
+ } else {
+ /* Not enough room, just need to free some */
+ p = NULL;
+ }
+ }
+
+ if (p == NULL)
+ free_undo (1);
+ } while (!p && undo_count);
+ if (p == NULL)
+ return -1;
+ GET_PC (p->pc)
+ p->frame_count = frame_count;
+ p->diff_size = diff_size;
+ p->stack_size = stack_size;
+ p->frame_offset = fp - stack;
+ memcpy (p + 1, undo_diff, diff_size);
+ memcpy ((zbyte *)(p + 1) + diff_size, sp, stack_size * sizeof (*sp));
+
+ if (!first_undo) {
+ p->prev = NULL;
+ first_undo = p;
+ } else {
+ last_undo->next = p;
+ p->prev = last_undo;
+ }
+ p->next = NULL;
+ curr_undo = last_undo = p;
+ undo_count++;
+ return 1;
+
+}/* save_undo */
+
+/*
+ * z_save_undo, save the current Z-machine state for a future undo.
+ *
+ * no zargs used
+ *
+ */
+
+void z_save_undo (void)
+{
+
+ store ((zword) save_undo ());
+
+}/* z_save_undo */
+
+/*
+ * z_verify, check the story file integrity.
+ *
+ * no zargs used
+ *
+ */
+
+void z_verify (void)
+{
+ zword checksum = 0;
+ long i;
+
+ /* Sum all bytes in story file except header bytes */
+
+ fseek (story_fp, 64, SEEK_SET);
+
+ for (i = 64; i < story_size; i++)
+ checksum += fgetc (story_fp);
+
+ /* Branch if the checksums are equal */
+
+ branch (checksum == h_checksum);
+
+}/* z_verify */
diff --git a/apps/plugins/frotz/files.c b/apps/plugins/frotz/files.c
new file mode 100644
index 0000000000..1baaa4073f
--- /dev/null
+++ b/apps/plugins/frotz/files.c
@@ -0,0 +1,563 @@
+/* files.c - Transscription, recording and playback
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+extern void set_more_prompts (bool);
+
+extern bool is_terminator (zchar);
+
+extern bool read_yes_or_no (const char *);
+
+char script_name[MAX_PATH];
+char command_name[MAX_PATH];
+
+#ifdef __MSDOS__
+extern char latin1_to_ibm[];
+#endif
+
+static int script_width = 0;
+
+int sfp = -1;
+int rfp = -1;
+int pfp = -1;
+
+/*
+ * script_open
+ *
+ * Open the transscript file. 'AMFV' makes this more complicated as it
+ * turns transscription on/off several times to exclude some text from
+ * the transscription file. This wasn't a problem for the original V4
+ * interpreters which always sent transscription to the printer, but it
+ * means a problem to modern interpreters that offer to open a new file
+ * every time transscription is turned on. Our solution is to append to
+ * the old transscription file in V1 to V4, and to ask for a new file
+ * name in V5+.
+ *
+ */
+
+void script_open (void)
+{
+ static bool script_valid = FALSE;
+
+ char new_name[MAX_PATH];
+
+ h_flags &= ~SCRIPTING_FLAG;
+
+ if (h_version >= V5 || !script_valid) {
+
+ if (!os_read_file_name (new_name, script_name, FILE_SCRIPT))
+ goto done;
+
+ strcpy (script_name, new_name);
+
+ }
+
+ /* Opening in "at" mode doesn't work for script_erase_input... */
+
+ if ((sfp = rb->open (script_name, O_RDWR|O_CREAT)) != -1) {
+
+ fseek (sfp, 0, SEEK_END);
+
+ h_flags |= SCRIPTING_FLAG;
+
+ script_valid = TRUE;
+ ostream_script = TRUE;
+
+ script_width = 0;
+
+ } else print_string ("Cannot open file\n");
+
+done:
+
+ SET_WORD (H_FLAGS, h_flags)
+
+}/* script_open */
+
+/*
+ * script_close
+ *
+ * Stop transscription.
+ *
+ */
+
+void script_close (void)
+{
+
+ h_flags &= ~SCRIPTING_FLAG;
+ SET_WORD (H_FLAGS, h_flags)
+
+ fclose (sfp); ostream_script = FALSE;
+ sfp = -1;
+
+}/* script_close */
+
+/*
+ * script_new_line
+ *
+ * Write a newline to the transscript file.
+ *
+ */
+
+void script_new_line (void)
+{
+
+ if (fputc ('\n', sfp) == EOF)
+ script_close ();
+
+ script_width = 0;
+
+}/* script_new_line */
+
+/*
+ * script_char
+ *
+ * Write a single character to the transscript file.
+ *
+ */
+
+void script_char (zchar c)
+{
+
+ if (c == ZC_INDENT && script_width != 0)
+ c = ' ';
+
+ if (c == ZC_INDENT)
+ { script_char (' '); script_char (' '); script_char (' '); return; }
+ if (c == ZC_GAP)
+ { script_char (' '); script_char (' '); return; }
+
+#ifdef __MSDOS__
+ if (c >= ZC_LATIN1_MIN)
+ c = latin1_to_ibm[c - ZC_LATIN1_MIN];
+#endif
+
+ fputc (c, sfp); script_width++;
+
+}/* script_char */
+
+/*
+ * script_word
+ *
+ * Write a string to the transscript file.
+ *
+ */
+
+void script_word (const zchar *s)
+{
+ int width;
+ int i;
+
+ if (*s == ZC_INDENT && script_width != 0)
+ script_char (*s++);
+
+ for (i = 0, width = 0; s[i] != 0; i++)
+
+ if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
+ i++;
+ else if (s[i] == ZC_GAP)
+ width += 3;
+ else if (s[i] == ZC_INDENT)
+ width += 2;
+ else
+ width += 1;
+
+ if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) {
+
+ if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+ s++;
+
+ script_new_line ();
+
+ }
+
+ for (i = 0; s[i] != 0; i++)
+
+ if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
+ i++;
+ else
+ script_char (s[i]);
+
+}/* script_word */
+
+/*
+ * script_write_input
+ *
+ * Send an input line to the transscript file.
+ *
+ */
+
+void script_write_input (const zchar *buf, zchar key)
+{
+ int width;
+ int i;
+
+ for (i = 0, width = 0; buf[i] != 0; i++)
+ width++;
+
+ if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols)
+ script_new_line ();
+
+ for (i = 0; buf[i] != 0; i++)
+ script_char (buf[i]);
+
+ if (key == ZC_RETURN)
+ script_new_line ();
+
+}/* script_write_input */
+
+/*
+ * script_erase_input
+ *
+ * Remove an input line from the transscript file.
+ *
+ */
+
+void script_erase_input (const zchar *buf)
+{
+ int width;
+ int i;
+
+ for (i = 0, width = 0; buf[i] != 0; i++)
+ width++;
+
+ fseek (sfp, -width, SEEK_CUR); script_width -= width;
+
+}/* script_erase_input */
+
+/*
+ * script_mssg_on
+ *
+ * Start sending a "debugging" message to the transscript file.
+ *
+ */
+
+void script_mssg_on (void)
+{
+
+ if (script_width != 0)
+ script_new_line ();
+
+ script_char (ZC_INDENT);
+
+}/* script_mssg_on */
+
+/*
+ * script_mssg_off
+ *
+ * Stop writing a "debugging" message.
+ *
+ */
+
+void script_mssg_off (void)
+{
+
+ script_new_line ();
+
+}/* script_mssg_off */
+
+/*
+ * record_open
+ *
+ * Open a file to record the player's input.
+ *
+ */
+
+void record_open (void)
+{
+ char new_name[MAX_PATH];
+
+ if (os_read_file_name (new_name, command_name, FILE_RECORD)) {
+
+ strcpy (command_name, new_name);
+
+ if ((rfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) != -1)
+ ostream_record = TRUE;
+ else
+ print_string ("Cannot open file\n");
+
+ }
+
+}/* record_open */
+
+/*
+ * record_close
+ *
+ * Stop recording the player's input.
+ *
+ */
+
+void record_close (void)
+{
+
+ fclose (rfp); ostream_record = FALSE;
+ rfp = -1;
+
+}/* record_close */
+
+/*
+ * record_code
+ *
+ * Helper function for record_char.
+ *
+ */
+
+static void record_code (int c, bool force_encoding)
+{
+
+ if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
+
+ int i;
+
+ fputc ('[', rfp);
+
+ for (i = 10000; i != 0; i /= 10)
+ if (c >= i || i == 1)
+ fputc ('0' + (c / i) % 10, rfp);
+
+ fputc (']', rfp);
+
+ } else fputc (c, rfp);
+
+}/* record_code */
+
+/*
+ * record_char
+ *
+ * Write a character to the command file.
+ *
+ */
+
+static void record_char (zchar c)
+{
+
+ if (c != ZC_RETURN) {
+ if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
+ record_code (translate_to_zscii (c), FALSE);
+ if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
+ record_code (mouse_x, TRUE);
+ record_code (mouse_y, TRUE);
+ }
+ } else record_code (1000 + c - ZC_HKEY_MIN, TRUE);
+ }
+
+}/* record_char */
+
+/*
+ * record_write_key
+ *
+ * Copy a keystroke to the command file.
+ *
+ */
+
+void record_write_key (zchar key)
+{
+
+ record_char (key);
+
+ if (fputc ('\n', rfp) == EOF)
+ record_close ();
+
+}/* record_write_key */
+
+/*
+ * record_write_input
+ *
+ * Copy a line of input to a command file.
+ *
+ */
+
+void record_write_input (const zchar *buf, zchar key)
+{
+ zchar c;
+
+ while ((c = *buf++) != 0)
+ record_char (c);
+
+ record_char (key);
+
+ if (fputc ('\n', rfp) == EOF)
+ record_close ();
+
+}/* record_write_input */
+
+/*
+ * replay_open
+ *
+ * Open a file of commands for playback.
+ *
+ */
+
+void replay_open (void)
+{
+ char new_name[MAX_PATH];
+
+ if (os_read_file_name (new_name, command_name, FILE_PLAYBACK)) {
+
+ strcpy (command_name, new_name);
+
+ if ((pfp = rb->open (new_name, O_RDONLY)) != -1) {
+
+ set_more_prompts (read_yes_or_no ("Do you want MORE prompts"));
+
+ istream_replay = TRUE;
+
+ } else print_string ("Cannot open file\n");
+
+ }
+
+}/* replay_open */
+
+/*
+ * replay_close
+ *
+ * Stop playback of commands.
+ *
+ */
+
+void replay_close (void)
+{
+
+ set_more_prompts (TRUE);
+
+ fclose (pfp); istream_replay = FALSE;
+ pfp = -1;
+
+}/* replay_close */
+
+/*
+ * replay_code
+ *
+ * Helper function for replay_key and replay_line.
+ *
+ */
+
+static int replay_code (void)
+{
+ int c;
+
+ if ((c = fgetc (pfp)) == '[') {
+
+ int c2;
+
+ c = 0;
+
+ while ((c2 = fgetc (pfp)) != EOF && c2 >= '0' && c2 <= '9')
+ c = 10 * c + c2 - '0';
+
+ return (c2 == ']') ? c : EOF;
+
+ } else return c;
+
+}/* replay_code */
+
+/*
+ * replay_char
+ *
+ * Read a character from the command file.
+ *
+ */
+
+static zchar replay_char (void)
+{
+ int c;
+
+ if ((c = replay_code ()) != EOF) {
+
+ if (c != '\n') {
+
+ if (c < 1000) {
+
+ c = translate_from_zscii (c);
+
+ if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
+ mouse_x = replay_code ();
+ mouse_y = replay_code ();
+ }
+
+ return c;
+
+ } else return ZC_HKEY_MIN + c - 1000;
+ }
+
+ ungetc ('\n', pfp);
+
+ return ZC_RETURN;
+
+ } else return ZC_BAD;
+
+}/* replay_char */
+
+/*
+ * replay_read_key
+ *
+ * Read a keystroke from a command file.
+ *
+ */
+
+zchar replay_read_key (void)
+{
+ zchar key;
+
+ key = replay_char ();
+
+ if (fgetc (pfp) != '\n') {
+
+ replay_close ();
+ return ZC_BAD;
+
+ } else return key;
+
+}/* replay_read_key */
+
+/*
+ * replay_read_input
+ *
+ * Read a line of input from a command file.
+ *
+ */
+
+zchar replay_read_input (zchar *buf)
+{
+ zchar c;
+
+ for (;;) {
+
+ c = replay_char ();
+
+ if (c == ZC_BAD || is_terminator (c))
+ break;
+
+ *buf++ = c;
+
+ }
+
+ *buf = 0;
+
+ if (fgetc (pfp) != '\n') {
+
+ replay_close ();
+ return ZC_BAD;
+
+ } else return c;
+
+}/* replay_read_input */
diff --git a/apps/plugins/frotz/frotz.c b/apps/plugins/frotz/frotz.c
new file mode 100644
index 0000000000..96029b85cb
--- /dev/null
+++ b/apps/plugins/frotz/frotz.c
@@ -0,0 +1,314 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Torne Wuff
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "dumb_frotz.h"
+#include "lib/pluginlib_exit.h"
+#include "lib/pluginlib_actions.h"
+
+PLUGIN_HEADER
+
+extern int frotz_main(void);
+extern bool hot_key_quit(void);
+
+f_setup_t f_setup;
+extern char save_name[], auxilary_name[], script_name[], command_name[];
+extern int story_fp, sfp, rfp, pfp;
+
+static struct viewport vp[NB_SCREENS];
+
+static void atexit_cleanup(void);
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ int i;
+ char* ext;
+
+ PLUGINLIB_EXIT_INIT_ATEXIT(atexit_cleanup);
+
+ if (!parameter)
+ return PLUGIN_ERROR;
+
+ rb->lcd_setfont(FONT_SYSFIXED);
+#if LCD_DEPTH > 1
+ rb->lcd_set_backdrop(NULL);
+#endif
+ rb->lcd_clear_display();
+
+ FOR_NB_SCREENS(i)
+ rb->viewport_set_defaults(&vp[i], i);
+
+ story_name = parameter;
+ strcpy(save_name, story_name);
+ ext = rb->strrchr(save_name, '.');
+ if (ext)
+ *ext = '\0';
+ strcpy(auxilary_name, save_name);
+ strcpy(script_name, save_name);
+ strcpy(command_name, save_name);
+ rb->strlcat(save_name, ".sav", MAX_PATH);
+ rb->strlcat(auxilary_name, ".aux", MAX_PATH);
+ rb->strlcat(script_name, ".scr", MAX_PATH);
+ rb->strlcat(command_name, ".rec", MAX_PATH);
+
+ frotz_main();
+
+ return PLUGIN_OK;
+}
+
+void atexit_cleanup()
+{
+ if (story_fp != -1)
+ fclose(story_fp);
+ if (sfp != -1)
+ fclose(sfp);
+ if (rfp != -1)
+ fclose(rfp);
+ if (pfp != -1)
+ fclose(pfp);
+}
+
+MENUITEM_STRINGLIST(ingame_menu, "Frotz", NULL, "Resume", "Undo", "Restart",
+ "Toggle input recording", "Play back input",
+ "Debug options", "Exit");
+
+zchar menu(void)
+{
+ switch (rb->do_menu(&ingame_menu, NULL, vp, true))
+ {
+ case 0:
+ default:
+ dumb_dump_screen();
+ return ZC_BAD;
+ case 1:
+ return ZC_HKEY_UNDO;
+ case 2:
+ return ZC_HKEY_RESTART;
+ case 3:
+ return ZC_HKEY_RECORD;
+ case 4:
+ return ZC_HKEY_PLAYBACK;
+ case 5:
+ return ZC_HKEY_DEBUG;
+ case 6:
+ return ZC_HKEY_QUIT;
+ }
+}
+
+const struct button_mapping* plugin_contexts[]={generic_actions};
+
+void wait_for_key()
+{
+ int action;
+
+ dumb_show_screen(false);
+
+ for (;;)
+ {
+ action = pluginlib_getaction(TIMEOUT_BLOCK,
+ plugin_contexts, 1);
+ switch (action)
+ {
+ case PLA_QUIT:
+ hot_key_quit();
+ break;
+
+ case PLA_FIRE:
+ return;
+ }
+ }
+}
+
+zchar do_input(int timeout, bool show_cursor)
+{
+ int action;
+ long timeout_at;
+ zchar menu_ret;
+
+ dumb_show_screen(show_cursor);
+
+ /* Convert timeout (tenths of a second) to ticks */
+ if (timeout > 0)
+ timeout = (timeout * HZ) / 10;
+ else
+ timeout = TIMEOUT_BLOCK;
+
+ timeout_at = *rb->current_tick + timeout;
+
+ for (;;)
+ {
+ action = pluginlib_getaction(timeout,
+ plugin_contexts, 1);
+ switch (action)
+ {
+ case PLA_QUIT:
+ return ZC_HKEY_QUIT;
+
+ case PLA_MENU:
+ menu_ret = menu();
+ if (menu_ret != ZC_BAD)
+ return menu_ret;
+ timeout_at = *rb->current_tick + timeout;
+ break;
+
+ case PLA_FIRE:
+ return ZC_RETURN;
+
+ case PLA_START:
+ return ZC_BAD;
+
+ default:
+ if (timeout != TIMEOUT_BLOCK &&
+ !TIME_BEFORE(*rb->current_tick, timeout_at))
+ return ZC_TIME_OUT;
+ }
+ }
+}
+
+zchar os_read_key(int timeout, bool show_cursor)
+{
+ int r;
+ char inputbuf[5];
+ short key;
+ zchar zkey;
+
+ for(;;)
+ {
+ zkey = do_input(timeout, show_cursor);
+ if (zkey != ZC_BAD)
+ return zkey;
+
+ inputbuf[0] = '\0';
+ r = rb->kbd_input(inputbuf, 5);
+ rb->lcd_setfont(FONT_SYSFIXED);
+ dumb_dump_screen();
+ if (!r)
+ {
+ rb->utf8decode(inputbuf, &key);
+ if (key > 0 && key < 256)
+ return (zchar)key;
+ }
+ }
+}
+
+zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued)
+{
+ (void)continued;
+ int r;
+ char inputbuf[256];
+ const char *in;
+ char *out;
+ short key;
+ zchar zkey;
+
+ for(;;)
+ {
+ zkey = do_input(timeout, true);
+ if (zkey != ZC_BAD)
+ return zkey;
+
+ if (max > width)
+ max = width;
+ strcpy(inputbuf, buf);
+ r = rb->kbd_input(inputbuf, 256);
+ rb->lcd_setfont(FONT_SYSFIXED);
+ dumb_dump_screen();
+ if (!r)
+ {
+ in = inputbuf;
+ out = buf;
+ while (*in && max)
+ {
+ in = rb->utf8decode(in, &key);
+ if (key > 0 && key < 256)
+ {
+ *out++ = key;
+ max--;
+ }
+ }
+ *out = '\0';
+ os_display_string(buf);
+ return ZC_RETURN;
+ }
+ }
+}
+
+bool read_yes_or_no(const char *s)
+{
+ char message_line[50];
+ const char *message_lines[] = {message_line};
+ const struct text_message message = {message_lines, 1};
+
+ rb->strlcpy(message_line, s, 49);
+ rb->strcat(message_line, "?");
+
+ if (rb->gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+zchar os_read_mouse(void)
+{
+ return 0;
+}
+
+int os_read_file_name(char *file_name, const char *default_name, int flag)
+{
+ (void)flag;
+ strcpy(file_name, default_name);
+ return TRUE;
+}
+
+void os_beep(int volume)
+{
+ rb->splashf(HZ/2, "[%s-PITCHED BEEP]", (volume==1) ? "HIGH" : "LOW");
+}
+
+static unsigned char unget_buf;
+static int unget_file;
+
+int ungetc(int c, int f)
+{
+ unget_file = f;
+ unget_buf = c;
+ return c;
+}
+
+int fgetc(int f)
+{
+ unsigned char cb;
+ if (unget_file == f)
+ {
+ unget_file = -1;
+ return unget_buf;
+ }
+ if (rb->read(f, &cb, 1) != 1)
+ return EOF;
+ return cb;
+}
+
+int fputc(int c, int f)
+{
+ unsigned char cb = c;
+ if (rb->write(f, &cb, 1) != 1)
+ return EOF;
+ return cb;
+}
diff --git a/apps/plugins/frotz/frotz.h b/apps/plugins/frotz/frotz.h
new file mode 100644
index 0000000000..bc8869cd24
--- /dev/null
+++ b/apps/plugins/frotz/frotz.h
@@ -0,0 +1,583 @@
+/*
+ * frotz.h
+ *
+ * Global declarations and definitions
+ *
+ */
+
+#include "frotzplugin.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef unsigned char zbyte;
+typedef unsigned short zword;
+
+enum story {
+ BEYOND_ZORK,
+ SHERLOCK,
+ ZORK_ZERO,
+ SHOGUN,
+ ARTHUR,
+ JOURNEY,
+ LURKING_HORROR,
+ UNKNOWN
+};
+
+typedef unsigned char zchar;
+
+/*** Constants that may be set at compile time ***/
+
+#ifndef MAX_UNDO_SLOTS
+#define MAX_UNDO_SLOTS 500
+#endif
+#ifndef TEXT_BUFFER_SIZE
+#define TEXT_BUFFER_SIZE 200
+#endif
+#ifndef INPUT_BUFFER_SIZE
+#define INPUT_BUFFER_SIZE 200
+#endif
+#ifndef STACK_SIZE
+#define STACK_SIZE 1024
+#endif
+
+/*** Story file header format ***/
+
+#define H_VERSION 0
+#define H_CONFIG 1
+#define H_RELEASE 2
+#define H_RESIDENT_SIZE 4
+#define H_START_PC 6
+#define H_DICTIONARY 8
+#define H_OBJECTS 10
+#define H_GLOBALS 12
+#define H_DYNAMIC_SIZE 14
+#define H_FLAGS 16
+#define H_SERIAL 18
+#define H_ABBREVIATIONS 24
+#define H_FILE_SIZE 26
+#define H_CHECKSUM 28
+#define H_INTERPRETER_NUMBER 30
+#define H_INTERPRETER_VERSION 31
+#define H_SCREEN_ROWS 32
+#define H_SCREEN_COLS 33
+#define H_SCREEN_WIDTH 34
+#define H_SCREEN_HEIGHT 36
+#define H_FONT_HEIGHT 38 /* this is the font width in V5 */
+#define H_FONT_WIDTH 39 /* this is the font height in V5 */
+#define H_FUNCTIONS_OFFSET 40
+#define H_STRINGS_OFFSET 42
+#define H_DEFAULT_BACKGROUND 44
+#define H_DEFAULT_FOREGROUND 45
+#define H_TERMINATING_KEYS 46
+#define H_LINE_WIDTH 48
+#define H_STANDARD_HIGH 50
+#define H_STANDARD_LOW 51
+#define H_ALPHABET 52
+#define H_EXTENSION_TABLE 54
+#define H_USER_NAME 56
+
+#define HX_TABLE_SIZE 0
+#define HX_MOUSE_X 1
+#define HX_MOUSE_Y 2
+#define HX_UNICODE_TABLE 3
+
+/*** Various Z-machine constants ***/
+
+#define V1 1
+#define V2 2
+#define V3 3
+#define V4 4
+#define V5 5
+#define V6 6
+#define V7 7
+#define V8 8
+
+#define CONFIG_BYTE_SWAPPED 0x01 /* Story file is byte swapped - V3 */
+#define CONFIG_TIME 0x02 /* Status line displays time - V3 */
+#define CONFIG_TWODISKS 0x04 /* Story file occupied two disks - V3 */
+#define CONFIG_TANDY 0x08 /* Tandy licensed game - V3 */
+#define CONFIG_NOSTATUSLINE 0x10 /* Interpr can't support status lines - V3 */
+#define CONFIG_SPLITSCREEN 0x20 /* Interpr supports split screen mode - V3 */
+#define CONFIG_PROPORTIONAL 0x40 /* Interpr uses proportional font - V3 */
+
+#define CONFIG_COLOUR 0x01 /* Interpr supports colour - V5+ */
+#define CONFIG_PICTURES 0x02 /* Interpr supports pictures - V6 */
+#define CONFIG_BOLDFACE 0x04 /* Interpr supports boldface style - V4+ */
+#define CONFIG_EMPHASIS 0x08 /* Interpr supports emphasis style - V4+ */
+#define CONFIG_FIXED 0x10 /* Interpr supports fixed width style - V4+ */
+#define CONFIG_SOUND 0x20 /* Interpr supports sound - V6 */
+
+#define CONFIG_TIMEDINPUT 0x80 /* Interpr supports timed input - V4+ */
+
+#define SCRIPTING_FLAG 0x0001 /* Outputting to transscription file - V1+ */
+#define FIXED_FONT_FLAG 0x0002 /* Use fixed width font - V3+ */
+#define REFRESH_FLAG 0x0004 /* Refresh the screen - V6 */
+#define GRAPHICS_FLAG 0x0008 /* Game wants to use graphics - V5+ */
+#define OLD_SOUND_FLAG 0x0010 /* Game wants to use sound effects - V3 */
+#define UNDO_FLAG 0x0010 /* Game wants to use UNDO feature - V5+ */
+#define MOUSE_FLAG 0x0020 /* Game wants to use a mouse - V5+ */
+#define COLOUR_FLAG 0x0040 /* Game wants to use colours - V5+ */
+#define SOUND_FLAG 0x0080 /* Game wants to use sound effects - V5+ */
+#define MENU_FLAG 0x0100 /* Game wants to use menus - V6 */
+
+#define INTERP_DEFAULT 0
+#define INTERP_DEC_20 1
+#define INTERP_APPLE_IIE 2
+#define INTERP_MACINTOSH 3
+#define INTERP_AMIGA 4
+#define INTERP_ATARI_ST 5
+#define INTERP_MSDOS 6
+#define INTERP_CBM_128 7
+#define INTERP_CBM_64 8
+#define INTERP_APPLE_IIC 9
+#define INTERP_APPLE_IIGS 10
+#define INTERP_TANDY 11
+
+#define BLACK_COLOUR 2
+#define RED_COLOUR 3
+#define GREEN_COLOUR 4
+#define YELLOW_COLOUR 5
+#define BLUE_COLOUR 6
+#define MAGENTA_COLOUR 7
+#define CYAN_COLOUR 8
+#define WHITE_COLOUR 9
+#define GREY_COLOUR 10 /* INTERP_MSDOS only */
+#define LIGHTGREY_COLOUR 10 /* INTERP_AMIGA only */
+#define MEDIUMGREY_COLOUR 11 /* INTERP_AMIGA only */
+#define DARKGREY_COLOUR 12 /* INTERP_AMIGA only */
+
+#define REVERSE_STYLE 1
+#define BOLDFACE_STYLE 2
+#define EMPHASIS_STYLE 4
+#define FIXED_WIDTH_STYLE 8
+
+#define TEXT_FONT 1
+#define PICTURE_FONT 2
+#define GRAPHICS_FONT 3
+#define FIXED_WIDTH_FONT 4
+
+#define BEEP_HIGH 1
+#define BEEP_LOW 2
+
+/*** Constants for os_restart_game */
+
+#define RESTART_BEGIN 0
+#define RESTART_WPROP_SET 1
+#define RESTART_END 2
+
+/*** Character codes ***/
+
+#define ZC_TIME_OUT 0x00
+#define ZC_NEW_STYLE 0x01
+#define ZC_NEW_FONT 0x02
+#define ZC_BACKSPACE 0x08
+#define ZC_INDENT 0x09
+#define ZC_GAP 0x0b
+#define ZC_RETURN 0x0d
+#define ZC_HKEY_MIN 0x0e
+#define ZC_HKEY_RECORD 0x0e
+#define ZC_HKEY_PLAYBACK 0x0f
+#define ZC_HKEY_SEED 0x10
+#define ZC_HKEY_UNDO 0x11
+#define ZC_HKEY_RESTART 0x12
+#define ZC_HKEY_QUIT 0x13
+#define ZC_HKEY_DEBUG 0x14
+#define ZC_HKEY_HELP 0x15
+#define ZC_HKEY_MAX 0x15
+#define ZC_ESCAPE 0x1b
+#define ZC_ASCII_MIN 0x20
+#define ZC_ASCII_MAX 0x7e
+#define ZC_BAD 0x7f
+#define ZC_ARROW_MIN 0x81
+#define ZC_ARROW_UP 0x81
+#define ZC_ARROW_DOWN 0x82
+#define ZC_ARROW_LEFT 0x83
+#define ZC_ARROW_RIGHT 0x84
+#define ZC_ARROW_MAX 0x84
+#define ZC_FKEY_MIN 0x85
+#define ZC_FKEY_MAX 0x90
+#define ZC_NUMPAD_MIN 0x91
+#define ZC_NUMPAD_MAX 0x9a
+#define ZC_SINGLE_CLICK 0x9b
+#define ZC_DOUBLE_CLICK 0x9c
+#define ZC_MENU_CLICK 0x9d
+#define ZC_LATIN1_MIN 0xa0
+#define ZC_LATIN1_MAX 0xff
+
+/*** File types ***/
+
+#define FILE_RESTORE 0
+#define FILE_SAVE 1
+#define FILE_SCRIPT 2
+#define FILE_PLAYBACK 3
+#define FILE_RECORD 4
+#define FILE_LOAD_AUX 5
+#define FILE_SAVE_AUX 6
+
+/*** Data access macros ***/
+
+#define SET_BYTE(addr,v) { zmp[addr] = v; }
+#define LOW_BYTE(addr,v) { v = zmp[addr]; }
+#define CODE_BYTE(v) { v = *pcp++; }
+
+#if defined (AMIGA)
+
+extern zbyte *pcp;
+extern zbyte *zmp;
+
+#define lo(v) ((zbyte *)&v)[1]
+#define hi(v) ((zbyte *)&v)[0]
+
+#define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); }
+#define LOW_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; }
+#define HIGH_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; }
+#define CODE_WORD(v) { hi(v) = *pcp++; lo(v) = *pcp++; }
+#define GET_PC(v) { v = pcp - zmp; }
+#define SET_PC(v) { pcp = zmp + v; }
+
+#endif
+
+/* A bunch of x86 assembly code previously appeared here. */
+
+#if !defined (AMIGA) && !defined (MSDOS_16BIT)
+
+extern zbyte *pcp;
+extern zbyte *zmp;
+
+#define lo(v) (v & 0xff)
+#define hi(v) (v >> 8)
+
+#define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); }
+#define LOW_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; }
+#define HIGH_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; }
+#define CODE_WORD(v) { v = ((zword) pcp[0] << 8) | pcp[1]; pcp += 2; }
+#define GET_PC(v) { v = pcp - zmp; }
+#define SET_PC(v) { pcp = zmp + v; }
+
+#endif
+
+
+/*** Story file header data ***/
+
+extern zbyte h_version;
+extern zbyte h_config;
+extern zword h_release;
+extern zword h_resident_size;
+extern zword h_start_pc;
+extern zword h_dictionary;
+extern zword h_objects;
+extern zword h_globals;
+extern zword h_dynamic_size;
+extern zword h_flags;
+extern zbyte h_serial[6];
+extern zword h_abbreviations;
+extern zword h_file_size;
+extern zword h_checksum;
+extern zbyte h_interpreter_number;
+extern zbyte h_interpreter_version;
+extern zbyte h_screen_rows;
+extern zbyte h_screen_cols;
+extern zword h_screen_width;
+extern zword h_screen_height;
+extern zbyte h_font_height;
+extern zbyte h_font_width;
+extern zword h_functions_offset;
+extern zword h_strings_offset;
+extern zbyte h_default_background;
+extern zbyte h_default_foreground;
+extern zword h_terminating_keys;
+extern zword h_line_width;
+extern zbyte h_standard_high;
+extern zbyte h_standard_low;
+extern zword h_alphabet;
+extern zword h_extension_table;
+extern zbyte h_user_name[8];
+
+extern zword hx_table_size;
+extern zword hx_mouse_x;
+extern zword hx_mouse_y;
+extern zword hx_unicode_table;
+
+/*** Various data ***/
+
+extern const char *story_name;
+
+extern enum story story_id;
+extern long story_size;
+
+extern zword stack[STACK_SIZE];
+extern zword *sp;
+extern zword *fp;
+extern zword frame_count;
+
+extern zword zargs[8];
+extern int zargc;
+
+extern bool ostream_screen;
+extern bool ostream_script;
+extern bool ostream_memory;
+extern bool ostream_record;
+extern bool istream_replay;
+extern bool message;
+
+extern int cwin;
+extern int mwin;
+
+extern int mouse_x;
+extern int mouse_y;
+
+extern bool enable_wrapping;
+extern bool enable_scripting;
+extern bool enable_scrolling;
+extern bool enable_buffering;
+
+
+extern char *option_zcode_path; /* dg */
+
+
+/*** Blorb stuff ***/
+/*
+bb_err_t blorb_err;
+bb_map_t *blorb_map;
+*/
+
+/*** Z-machine opcodes ***/
+
+void z_add (void);
+void z_and (void);
+void z_art_shift (void);
+void z_buffer_mode (void);
+void z_call_n (void);
+void z_call_s (void);
+void z_catch (void);
+void z_check_arg_count (void);
+void z_check_unicode (void);
+void z_clear_attr (void);
+void z_copy_table (void);
+void z_dec (void);
+void z_dec_chk (void);
+void z_div (void);
+void z_draw_picture (void);
+void z_encode_text (void);
+void z_erase_line (void);
+void z_erase_picture (void);
+void z_erase_window (void);
+void z_get_child (void);
+void z_get_cursor (void);
+void z_get_next_prop (void);
+void z_get_parent (void);
+void z_get_prop (void);
+void z_get_prop_addr (void);
+void z_get_prop_len (void);
+void z_get_sibling (void);
+void z_get_wind_prop (void);
+void z_inc (void);
+void z_inc_chk (void);
+void z_input_stream (void);
+void z_insert_obj (void);
+void z_je (void);
+void z_jg (void);
+void z_jin (void);
+void z_jl (void);
+void z_jump (void);
+void z_jz (void);
+void z_load (void);
+void z_loadb (void);
+void z_loadw (void);
+void z_log_shift (void);
+void z_make_menu (void);
+void z_mod (void);
+void z_mouse_window (void);
+void z_move_window (void);
+void z_mul (void);
+void z_new_line (void);
+void z_nop (void);
+void z_not (void);
+void z_or (void);
+void z_output_stream (void);
+void z_picture_data (void);
+void z_picture_table (void);
+void z_piracy (void);
+void z_pop (void);
+void z_pop_stack (void);
+void z_print (void);
+void z_print_addr (void);
+void z_print_char (void);
+void z_print_form (void);
+void z_print_num (void);
+void z_print_obj (void);
+void z_print_paddr (void);
+void z_print_ret (void);
+void z_print_table (void);
+void z_print_unicode (void);
+void z_pull (void);
+void z_push (void);
+void z_push_stack (void);
+void z_put_prop (void);
+void z_put_wind_prop (void);
+void z_quit (void);
+void z_random (void);
+void z_read (void);
+void z_read_char (void);
+void z_read_mouse (void);
+void z_remove_obj (void);
+void z_restart (void);
+void z_restore (void);
+void z_restore_undo (void);
+void z_ret (void);
+void z_ret_popped (void);
+void z_rfalse (void);
+void z_rtrue (void);
+void z_save (void);
+void z_save_undo (void);
+void z_scan_table (void);
+void z_scroll_window (void);
+void z_set_attr (void);
+void z_set_font (void);
+void z_set_colour (void);
+void z_set_cursor (void);
+void z_set_margins (void);
+void z_set_window (void);
+void z_set_text_style (void);
+void z_show_status (void);
+void z_sound_effect (void);
+void z_split_window (void);
+void z_store (void);
+void z_storeb (void);
+void z_storew (void);
+void z_sub (void);
+void z_test (void);
+void z_test_attr (void);
+void z_throw (void);
+void z_tokenise (void);
+void z_verify (void);
+void z_window_size (void);
+void z_window_style (void);
+
+/* Definitions for error handling functions and error codes. */
+
+/* extern int err_report_mode; */
+
+void init_err (void);
+void runtime_error (int);
+
+/* Error codes */
+#define ERR_TEXT_BUF_OVF 1 /* Text buffer overflow */
+#define ERR_STORE_RANGE 2 /* Store out of dynamic memory */
+#define ERR_DIV_ZERO 3 /* Division by zero */
+#define ERR_ILL_OBJ 4 /* Illegal object */
+#define ERR_ILL_ATTR 5 /* Illegal attribute */
+#define ERR_NO_PROP 6 /* No such property */
+#define ERR_STK_OVF 7 /* Stack overflow */
+#define ERR_ILL_CALL_ADDR 8 /* Call to illegal address */
+#define ERR_CALL_NON_RTN 9 /* Call to non-routine */
+#define ERR_STK_UNDF 10 /* Stack underflow */
+#define ERR_ILL_OPCODE 11 /* Illegal opcode */
+#define ERR_BAD_FRAME 12 /* Bad stack frame */
+#define ERR_ILL_JUMP_ADDR 13 /* Jump to illegal address */
+#define ERR_SAVE_IN_INTER 14 /* Can't save while in interrupt */
+#define ERR_STR3_NESTING 15 /* Nesting stream #3 too deep */
+#define ERR_ILL_WIN 16 /* Illegal window */
+#define ERR_ILL_WIN_PROP 17 /* Illegal window property */
+#define ERR_ILL_PRINT_ADDR 18 /* Print at illegal address */
+#define ERR_MAX_FATAL 18
+
+/* Less serious errors */
+#define ERR_JIN_0 19 /* @jin called with object 0 */
+#define ERR_GET_CHILD_0 20 /* @get_child called with object 0 */
+#define ERR_GET_PARENT_0 21 /* @get_parent called with object 0 */
+#define ERR_GET_SIBLING_0 22 /* @get_sibling called with object 0 */
+#define ERR_GET_PROP_ADDR_0 23 /* @get_prop_addr called with object 0 */
+#define ERR_GET_PROP_0 24 /* @get_prop called with object 0 */
+#define ERR_PUT_PROP_0 25 /* @put_prop called with object 0 */
+#define ERR_CLEAR_ATTR_0 26 /* @clear_attr called with object 0 */
+#define ERR_SET_ATTR_0 27 /* @set_attr called with object 0 */
+#define ERR_TEST_ATTR_0 28 /* @test_attr called with object 0 */
+#define ERR_MOVE_OBJECT_0 29 /* @move_object called moving object 0 */
+#define ERR_MOVE_OBJECT_TO_0 30 /* @move_object called moving into object 0 */
+#define ERR_REMOVE_OBJECT_0 31 /* @remove_object called with object 0 */
+#define ERR_GET_NEXT_PROP_0 32 /* @get_next_prop called with object 0 */
+#define ERR_NUM_ERRORS (32)
+
+/* There are four error reporting modes: never report errors;
+ report only the first time a given error type occurs; report
+ every time an error occurs; or treat all errors as fatal
+ errors, killing the interpreter. I strongly recommend
+ "report once" as the default. But you can compile in a
+ different default by changing the definition of
+ ERR_DEFAULT_REPORT_MODE. In any case, the player can
+ specify a report mode on the command line by typing "-Z 0"
+ through "-Z 3". */
+
+#define ERR_REPORT_NEVER (0)
+#define ERR_REPORT_ONCE (1)
+#define ERR_REPORT_ALWAYS (2)
+#define ERR_REPORT_FATAL (3)
+
+#define ERR_DEFAULT_REPORT_MODE ERR_REPORT_ONCE
+
+
+/*** Various global functions ***/
+
+zchar translate_from_zscii (zbyte);
+zbyte translate_to_zscii (zchar);
+
+void flush_buffer (void);
+void new_line (void);
+void print_char (zchar);
+void print_num (zword);
+void print_object (zword);
+void print_string (const char *);
+
+void stream_mssg_on (void);
+void stream_mssg_off (void);
+
+void ret (zword);
+void store (zword);
+void branch (bool);
+
+void storeb (zword, zbyte);
+void storew (zword, zword);
+
+/*** Interface functions ***/
+
+void os_beep (int);
+int os_char_width (zchar);
+void os_display_char (zchar);
+void os_display_string (const zchar *);
+void os_draw_picture (int, int, int);
+void os_erase_area (int, int, int, int);
+void os_fatal (const char *) __attribute__((noreturn));
+void os_finish_with_sample (int);
+int os_font_data (int, int *, int *);
+void os_init_screen (void);
+void os_more_prompt (void);
+int os_peek_colour (void);
+bool os_picture_data (int, int *, int *);
+void os_prepare_sample (int);
+void os_process_arguments (int, char *[]);
+int os_random_seed (void);
+int os_read_file_name (char *, const char *, int);
+zchar os_read_key (int, bool);
+zchar os_read_line (int, zchar *, int, int, int);
+zchar os_read_mouse (void);
+void os_reset_screen (void);
+void os_restart_game (int);
+void os_scroll_area (int, int, int, int, int);
+void os_set_colour (int, int);
+void os_set_cursor (int, int);
+void os_set_font (int);
+void os_set_text_style (int);
+void os_start_sample (int, int, int, zword);
+void os_stop_sample (int);
+int os_string_width (const zchar *);
+void os_init_setup (void);
+int os_speech_output(const zchar *);
+
+#include "setup.h"
diff --git a/apps/plugins/frotz/frotz.make b/apps/plugins/frotz/frotz.make
new file mode 100644
index 0000000000..0bf4370cca
--- /dev/null
+++ b/apps/plugins/frotz/frotz.make
@@ -0,0 +1,21 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+FROTZSRCDIR := $(APPSDIR)/plugins/frotz
+FROTZBUILDDIR := $(BUILDDIR)/apps/plugins/frotz
+
+ROCKS += $(FROTZBUILDDIR)/frotz.rock
+
+FROTZ_SRC := $(call preprocess, $(FROTZSRCDIR)/SOURCES)
+FROTZ_OBJ := $(call c2obj, $(FROTZ_SRC))
+
+# add source files to OTHER_SRC to get automatic dependencies
+OTHER_SRC += $(FROTZ_SRC)
+
+$(FROTZBUILDDIR)/frotz.rock: $(FROTZ_OBJ)
diff --git a/apps/plugins/frotz/frotzplugin.h b/apps/plugins/frotz/frotzplugin.h
new file mode 100644
index 0000000000..8caddb470d
--- /dev/null
+++ b/apps/plugins/frotz/frotzplugin.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Torne Wuff
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _FROTZPLUGIN_H_
+#define _FROTZPLUGIN_H_
+
+#include "plugin.h"
+
+/*
+ * pretend stdio.h is implemented. references to FILE * still have to be
+ * changed to int, and references to NULL into -1, but there are less of those
+ */
+#define fread(ptr, size, nmemb, stream) rb->read(stream, ptr, size*nmemb)
+#define fwrite(ptr, size, nmemb, stream) rb->write(stream, ptr, size*nmemb)
+#define fclose(stream) rb->close(stream)
+#define fseek(stream, offset, whence) rb->lseek(stream, offset, whence)
+#define ftell(stream) rb->lseek(stream, 0, SEEK_CUR)
+#define ferror(stream) 0
+
+/*
+ * we need functions for character io
+ */
+extern int ungetc(int c, int f);
+extern int fgetc(int f);
+extern int fputc(int c, int f);
+
+/*
+ * this is used instead of os_read_key for more prompts and the like
+ * since the menu can't be used there.
+ */
+extern void wait_for_key(void);
+
+/*
+ * wrappers
+ */
+#define strchr(s, c) rb->strchr(s, c)
+#define strcpy(dest, src) rb->strcpy(dest, src)
+
+#endif /* _FROTZPLUGIN_H_ */
diff --git a/apps/plugins/frotz/hotkey.c b/apps/plugins/frotz/hotkey.c
new file mode 100644
index 0000000000..d214f1f322
--- /dev/null
+++ b/apps/plugins/frotz/hotkey.c
@@ -0,0 +1,221 @@
+/* hotkey.c - Hot key functions
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+#include "lib/pluginlib_exit.h"
+
+extern int restore_undo (void);
+
+extern int read_number (void);
+
+extern bool read_yes_or_no (const char *);
+
+extern void replay_open (void);
+extern void replay_close (void);
+extern void record_open (void);
+extern void record_close (void);
+
+extern void seed_random (int);
+
+/*
+ * hot_key_debugging
+ *
+ * ...allows user to toggle cheating options on/off.
+ *
+ */
+
+static bool hot_key_debugging (void)
+{
+
+ f_setup.attribute_assignment = read_yes_or_no ("Watch attribute assignment");
+ f_setup.attribute_testing = read_yes_or_no ("Watch attribute testing");
+
+ f_setup.object_movement = read_yes_or_no ("Watch object movement");
+ f_setup.object_locating = read_yes_or_no ("Watch object locating");
+
+ return FALSE;
+
+}/* hot_key_debugging */
+
+/*
+ * hot_key_playback
+ *
+ * ...allows user to turn playback on.
+ *
+ */
+
+static bool hot_key_playback (void)
+{
+
+ rb->splash(HZ, "Playback on");
+
+ if (!istream_replay)
+ replay_open ();
+
+ return FALSE;
+
+}/* hot_key_playback */
+
+/*
+ * hot_key_recording
+ *
+ * ...allows user to turn recording on/off.
+ *
+ */
+
+static bool hot_key_recording (void)
+{
+
+ if (istream_replay) {
+ rb->splash(HZ, "Playback off");
+ replay_close ();
+ } else if (ostream_record) {
+ rb->splash(HZ, "Recording off");
+ record_close ();
+ } else {
+ rb->splash(HZ, "Recording on");
+ record_open ();
+ }
+
+ return FALSE;
+
+}/* hot_key_recording */
+
+/*
+ * hot_key_seed
+ *
+ * ...allows user to seed the random number seed.
+ *
+ */
+
+static bool hot_key_seed (void)
+{
+
+ print_string ("Enter seed value (or return to randomize): ");
+ seed_random (read_number ());
+
+ return FALSE;
+
+}/* hot_key_seed */
+
+/*
+ * hot_key_undo
+ *
+ * ...allows user to undo the previous turn.
+ *
+ */
+
+static bool hot_key_undo (void)
+{
+
+ if (restore_undo ()) {
+
+ print_string ("undo\n");
+
+ if (h_version >= V5) { /* for V5+ games we must */
+ store (2); /* store 2 (for success) */
+ return TRUE; /* and abort the input */
+ }
+
+ if (h_version <= V3) { /* for V3- games we must */
+ z_show_status (); /* draw the status line */
+ return FALSE; /* and continue input */
+ }
+
+ } else rb->splash(HZ, "No undo information available.");
+
+ return FALSE;
+
+}/* hot_key_undo */
+
+/*
+ * hot_key_restart
+ *
+ * ...allows user to start a new game.
+ *
+ */
+
+static bool hot_key_restart (void)
+{
+
+ if (read_yes_or_no ("Do you wish to restart")) {
+
+ z_restart ();
+ return TRUE;
+
+ } else return FALSE;
+
+}/* hot_key_restart */
+
+/*
+ * hot_key_quit
+ *
+ * ...allows user to exit the game.
+ *
+ */
+
+bool hot_key_quit (void)
+{
+
+ if (read_yes_or_no ("Do you wish to quit")) {
+
+ exit(0);
+
+ } else return FALSE;
+
+}/* hot_key_quit */
+
+/*
+ * handle_hot_key
+ *
+ * Perform the action associated with a so-called hot key. Return
+ * true to abort the current input action.
+ *
+ */
+
+bool handle_hot_key (zchar key)
+{
+
+ if (cwin == 0) {
+
+ bool aborting;
+
+ aborting = FALSE;
+
+ switch (key) {
+ case ZC_HKEY_RECORD: aborting = hot_key_recording (); break;
+ case ZC_HKEY_PLAYBACK: aborting = hot_key_playback (); break;
+ case ZC_HKEY_SEED: aborting = hot_key_seed (); break;
+ case ZC_HKEY_UNDO: aborting = hot_key_undo (); break;
+ case ZC_HKEY_RESTART: aborting = hot_key_restart (); break;
+ case ZC_HKEY_QUIT: aborting = hot_key_quit (); break;
+ case ZC_HKEY_DEBUG: aborting = hot_key_debugging (); break;
+ }
+
+ if (aborting)
+ return TRUE;
+
+ }
+
+ return FALSE;
+
+}/* handle_hot_key */
diff --git a/apps/plugins/frotz/input.c b/apps/plugins/frotz/input.c
new file mode 100644
index 0000000000..296bffc529
--- /dev/null
+++ b/apps/plugins/frotz/input.c
@@ -0,0 +1,301 @@
+/* input.c - High level input functions
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+extern int save_undo (void);
+
+extern zchar stream_read_key (zword, zword, bool);
+extern zchar stream_read_input (int, zchar *, zword, zword, bool, bool);
+
+extern void tokenise_line (zword, zword, zword, bool);
+
+/*
+ * is_terminator
+ *
+ * Check if the given key is an input terminator.
+ *
+ */
+
+bool is_terminator (zchar key)
+{
+
+ if (key == ZC_TIME_OUT)
+ return TRUE;
+ if (key == ZC_RETURN)
+ return TRUE;
+ if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX)
+ return TRUE;
+
+ if (h_terminating_keys != 0)
+
+ if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) {
+
+ zword addr = h_terminating_keys;
+ zbyte c;
+
+ do {
+ LOW_BYTE (addr, c)
+ if (c == 255 || key == translate_from_zscii (c))
+ return TRUE;
+ addr++;
+ } while (c != 0);
+
+ }
+
+ return FALSE;
+
+}/* is_terminator */
+
+/*
+ * z_make_menu, add or remove a menu and branch if successful.
+ *
+ * zargs[0] = number of menu
+ * zargs[1] = table of menu entries or 0 to remove menu
+ *
+ */
+
+void z_make_menu (void)
+{
+
+ /* This opcode was only used for the Macintosh version of Journey.
+ It controls menus with numbers greater than 2 (menus 0, 1 and 2
+ are system menus). Frotz doesn't implement menus yet. */
+
+ branch (FALSE);
+
+}/* z_make_menu */
+
+extern bool read_yes_or_no (const char *s);
+
+/*
+ * read_string
+ *
+ * Read a string from the current input stream.
+ *
+ */
+
+void read_string (int max, zchar *buffer)
+{
+ zchar key;
+
+ buffer[0] = 0;
+
+ do {
+
+ key = stream_read_input (max, buffer, 0, 0, FALSE, FALSE);
+
+ } while (key != ZC_RETURN);
+
+}/* read_string */
+
+/*
+ * read_number
+ *
+ * Ask the user to type in a number and return it.
+ *
+ */
+
+int read_number (void)
+{
+ zchar buffer[6];
+ int value = 0;
+ int i;
+
+ read_string (5, buffer);
+
+ for (i = 0; buffer[i] != 0; i++)
+ if (buffer[i] >= '0' && buffer[i] <= '9')
+ value = 10 * value + buffer[i] - '0';
+
+ return value;
+
+}/* read_number */
+
+/*
+ * z_read, read a line of input and (in V5+) store the terminating key.
+ *
+ * zargs[0] = address of text buffer
+ * zargs[1] = address of token buffer
+ * zargs[2] = timeout in tenths of a second (optional)
+ * zargs[3] = packed address of routine to be called on timeout
+ *
+ */
+
+void z_read (void)
+{
+ zchar buffer[INPUT_BUFFER_SIZE];
+ zword addr;
+ zchar key;
+ zbyte max, size;
+ zbyte c;
+ int i;
+
+ /* Supply default arguments */
+
+ if (zargc < 3)
+ zargs[2] = 0;
+
+ /* Get maximum input size */
+
+ addr = zargs[0];
+
+ LOW_BYTE (addr, max)
+
+ if (h_version <= V4)
+ max--;
+
+ if (max >= INPUT_BUFFER_SIZE)
+ max = INPUT_BUFFER_SIZE - 1;
+
+ /* Get initial input size */
+
+ if (h_version >= V5) {
+ addr++;
+ LOW_BYTE (addr, size)
+ } else size = 0;
+
+ /* Copy initial input to local buffer */
+
+ for (i = 0; i < size; i++) {
+ addr++;
+ LOW_BYTE (addr, c)
+ buffer[i] = translate_from_zscii (c);
+ }
+
+ buffer[i] = 0;
+
+ /* Draw status line for V1 to V3 games */
+
+ if (h_version <= V3)
+ z_show_status ();
+
+ /* Read input from current input stream */
+
+ key = stream_read_input (
+ max, buffer, /* buffer and size */
+ zargs[2], /* timeout value */
+ zargs[3], /* timeout routine */
+ TRUE, /* enable hot keys */
+ h_version == V6); /* no script in V6 */
+
+ if (key == ZC_BAD)
+ return;
+
+ /* Perform save_undo for V1 to V4 games */
+
+ if (h_version <= V4)
+ save_undo ();
+
+ /* Copy local buffer back to dynamic memory */
+
+ for (i = 0; buffer[i] != 0; i++) {
+
+ if (key == ZC_RETURN) {
+
+ if (buffer[i] >= 'A' && buffer[i] <= 'Z')
+ buffer[i] += 'a' - 'A';
+ if (buffer[i] >= 0xc0 && buffer[i] <= 0xde && buffer[i] != 0xd7)
+ buffer[i] += 0x20;
+
+ }
+
+ storeb ((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i]));
+
+ }
+
+ /* Add null character (V1-V4) or write input length into 2nd byte */
+
+ if (h_version <= V4)
+ storeb ((zword) (zargs[0] + 1 + i), 0);
+ else
+ storeb ((zword) (zargs[0] + 1), i);
+
+ /* Tokenise line if a token buffer is present */
+
+ if (key == ZC_RETURN && zargs[1] != 0)
+ tokenise_line (zargs[0], zargs[1], 0, FALSE);
+
+ /* Store key */
+
+ if (h_version >= V5)
+ store (translate_to_zscii (key));
+
+}/* z_read */
+
+/*
+ * z_read_char, read and store a key.
+ *
+ * zargs[0] = input device (must be 1)
+ * zargs[1] = timeout in tenths of a second (optional)
+ * zargs[2] = packed address of routine to be called on timeout
+ *
+ */
+
+void z_read_char (void)
+{
+ zchar key;
+
+ /* Supply default arguments */
+
+ if (zargc < 2)
+ zargs[1] = 0;
+
+ /* Read input from the current input stream */
+
+ key = stream_read_key (
+ zargs[1], /* timeout value */
+ zargs[2], /* timeout routine */
+ TRUE); /* enable hot keys */
+
+ if (key == ZC_BAD)
+ return;
+
+ /* Store key */
+
+ store (translate_to_zscii (key));
+
+}/* z_read_char */
+
+/*
+ * z_read_mouse, write the current mouse status into a table.
+ *
+ * zargs[0] = address of table
+ *
+ */
+
+void z_read_mouse (void)
+{
+ zword btn;
+
+ /* Read the mouse position and which buttons are down */
+
+ btn = os_read_mouse ();
+ hx_mouse_y = mouse_y;
+ hx_mouse_x = mouse_x;
+
+ storew ((zword) (zargs[0] + 0), hx_mouse_y);
+ storew ((zword) (zargs[0] + 2), hx_mouse_x);
+ storew ((zword) (zargs[0] + 4), btn); /* mouse button bits */
+ storew ((zword) (zargs[0] + 6), 0); /* menu selection */
+
+}/* z_read_mouse */
diff --git a/apps/plugins/frotz/main.c b/apps/plugins/frotz/main.c
new file mode 100644
index 0000000000..22c021bfd1
--- /dev/null
+++ b/apps/plugins/frotz/main.c
@@ -0,0 +1,205 @@
+/* main.c - Frotz V2.40 main function
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/*
+ * This is an interpreter for Infocom V1 to V6 games. It also supports
+ * the recently defined V7 and V8 games. Please report bugs to
+ *
+ * s.jokisch@avu.de
+ *
+ */
+
+#include "frotz.h"
+
+#ifndef MSDOS_16BIT
+#define cdecl
+#endif
+
+extern void interpret (void);
+extern void init_memory (void);
+extern void init_undo (void);
+extern void reset_memory (void);
+extern void init_buffer (void);
+extern void init_sound (void);
+extern void init_process (void);
+extern void script_close (void);
+extern void record_close (void);
+extern void replay_close (void);
+
+/* Story file name, id number and size */
+
+const char *story_name = 0;
+
+enum story story_id = UNKNOWN;
+long story_size = 0;
+
+/* Story file header data */
+
+zbyte h_version = 0;
+zbyte h_config = 0;
+zword h_release = 0;
+zword h_resident_size = 0;
+zword h_start_pc = 0;
+zword h_dictionary = 0;
+zword h_objects = 0;
+zword h_globals = 0;
+zword h_dynamic_size = 0;
+zword h_flags = 0;
+zbyte h_serial[6] = { 0, 0, 0, 0, 0, 0 };
+zword h_abbreviations = 0;
+zword h_file_size = 0;
+zword h_checksum = 0;
+zbyte h_interpreter_number = 0;
+zbyte h_interpreter_version = 0;
+zbyte h_screen_rows = 0;
+zbyte h_screen_cols = 0;
+zword h_screen_width = 0;
+zword h_screen_height = 0;
+zbyte h_font_height = 1;
+zbyte h_font_width = 1;
+zword h_functions_offset = 0;
+zword h_strings_offset = 0;
+zbyte h_default_background = 0;
+zbyte h_default_foreground = 0;
+zword h_terminating_keys = 0;
+zword h_line_width = 0;
+zbyte h_standard_high = 1;
+zbyte h_standard_low = 0;
+zword h_alphabet = 0;
+zword h_extension_table = 0;
+zbyte h_user_name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+zword hx_table_size = 0;
+zword hx_mouse_x = 0;
+zword hx_mouse_y = 0;
+zword hx_unicode_table = 0;
+
+/* Stack data */
+
+zword stack[STACK_SIZE];
+zword *sp = 0;
+zword *fp = 0;
+zword frame_count = 0;
+
+/* IO streams */
+
+bool ostream_screen = TRUE;
+bool ostream_script = FALSE;
+bool ostream_memory = FALSE;
+bool ostream_record = FALSE;
+bool istream_replay = FALSE;
+bool message = FALSE;
+
+/* Current window and mouse data */
+
+int cwin = 0;
+int mwin = 0;
+
+int mouse_y = 0;
+int mouse_x = 0;
+
+/* Window attributes */
+
+bool enable_wrapping = FALSE;
+bool enable_scripting = FALSE;
+bool enable_scrolling = FALSE;
+bool enable_buffering = FALSE;
+
+/* User options */
+
+/*
+int option_attribute_assignment = 0;
+int option_attribute_testing = 0;
+int option_context_lines = 0;
+int option_object_locating = 0;
+int option_object_movement = 0;
+int option_left_margin = 0;
+int option_right_margin = 0;
+int option_ignore_errors = 0;
+int option_piracy = 0;
+int option_undo_slots = MAX_UNDO_SLOTS;
+int option_expand_abbreviations = 0;
+int option_script_cols = 80;
+int option_save_quetzal = 1;
+*/
+
+int option_sound = 1;
+char *option_zcode_path;
+
+
+/*
+ * z_piracy, branch if the story file is a legal copy.
+ *
+ * no zargs used
+ *
+ */
+
+void z_piracy (void)
+{
+
+ branch (!f_setup.piracy);
+
+}/* z_piracy */
+
+/*
+ * main
+ *
+ * Prepare and run the game.
+ *
+ */
+
+int cdecl frotz_main (void)
+{
+
+ os_init_setup ();
+
+ init_buffer ();
+
+ init_err ();
+
+ init_memory ();
+
+ init_process ();
+
+ init_sound ();
+
+ os_init_screen ();
+
+ init_undo ();
+
+ z_restart ();
+
+ interpret ();
+
+ script_close ();
+
+ record_close ();
+
+ replay_close ();
+
+ reset_memory ();
+
+ os_reset_screen ();
+
+ return 0;
+
+}/* main */
diff --git a/apps/plugins/frotz/math.c b/apps/plugins/frotz/math.c
new file mode 100644
index 0000000000..5ff5163230
--- /dev/null
+++ b/apps/plugins/frotz/math.c
@@ -0,0 +1,261 @@
+/* math.c - Arithmetic, compare and logical opcodes
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+/*
+ * z_add, 16bit addition.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_add (void)
+{
+
+ store ((zword) ((short) zargs[0] + (short) zargs[1]));
+
+}/* z_add */
+
+/*
+ * z_and, bitwise AND operation.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_and (void)
+{
+
+ store ((zword) (zargs[0] & zargs[1]));
+
+}/* z_and */
+
+/*
+ * z_art_shift, arithmetic SHIFT operation.
+ *
+ * zargs[0] = value
+ * zargs[1] = #positions to shift left (positive) or right
+ *
+ */
+
+void z_art_shift (void)
+{
+
+ if ((short) zargs[1] > 0)
+ store ((zword) ((short) zargs[0] << (short) zargs[1]));
+ else
+ store ((zword) ((short) zargs[0] >> - (short) zargs[1]));
+
+}/* z_art_shift */
+
+/*
+ * z_div, signed 16bit division.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_div (void)
+{
+
+ if (zargs[1] == 0)
+ runtime_error (ERR_DIV_ZERO);
+
+ store ((zword) ((short) zargs[0] / (short) zargs[1]));
+
+}/* z_div */
+
+/*
+ * z_je, branch if the first value equals any of the following.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value (optional)
+ * ...
+ * zargs[3] = fourth value (optional)
+ *
+ */
+
+void z_je (void)
+{
+
+ branch (
+ zargc > 1 && (zargs[0] == zargs[1] || (
+ zargc > 2 && (zargs[0] == zargs[2] || (
+ zargc > 3 && (zargs[0] == zargs[3]))))));
+
+}/* z_je */
+
+/*
+ * z_jg, branch if the first value is greater than the second.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_jg (void)
+{
+
+ branch ((short) zargs[0] > (short) zargs[1]);
+
+}/* z_jg */
+
+/*
+ * z_jl, branch if the first value is less than the second.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_jl (void)
+{
+
+ branch ((short) zargs[0] < (short) zargs[1]);
+
+}/* z_jl */
+
+/*
+ * z_jz, branch if value is zero.
+ *
+ * zargs[0] = value
+ *
+ */
+
+void z_jz (void)
+{
+
+ branch ((short) zargs[0] == 0);
+
+}/* z_jz */
+
+/*
+ * z_log_shift, logical SHIFT operation.
+ *
+ * zargs[0] = value
+ * zargs[1] = #positions to shift left (positive) or right (negative)
+ *
+ */
+
+void z_log_shift (void)
+{
+
+ if ((short) zargs[1] > 0)
+ store ((zword) (zargs[0] << (short) zargs[1]));
+ else
+ store ((zword) (zargs[0] >> - (short) zargs[1]));
+
+}/* z_log_shift */
+
+/*
+ * z_mod, remainder after signed 16bit division.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_mod (void)
+{
+
+ if (zargs[1] == 0)
+ runtime_error (ERR_DIV_ZERO);
+
+ store ((zword) ((short) zargs[0] % (short) zargs[1]));
+
+}/* z_mod */
+
+/*
+ * z_mul, 16bit multiplication.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_mul (void)
+{
+
+ store ((zword) ((short) zargs[0] * (short) zargs[1]));
+
+}/* z_mul */
+
+/*
+ * z_not, bitwise NOT operation.
+ *
+ * zargs[0] = value
+ *
+ */
+
+void z_not (void)
+{
+
+ store ((zword) ~zargs[0]);
+
+}/* z_not */
+
+/*
+ * z_or, bitwise OR operation.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_or (void)
+{
+
+ store ((zword) (zargs[0] | zargs[1]));
+
+}/* z_or */
+
+/*
+ * z_sub, 16bit substraction.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ *
+ */
+
+void z_sub (void)
+{
+
+ store ((zword) ((short) zargs[0] - (short) zargs[1]));
+
+}/* z_sub */
+
+/*
+ * z_test, branch if all the flags of a bit mask are set in a value.
+ *
+ * zargs[0] = value to be examined
+ * zargs[1] = bit mask
+ *
+ */
+
+void z_test (void)
+{
+
+ branch ((zargs[0] & zargs[1]) == zargs[1]);
+
+}/* z_test */
diff --git a/apps/plugins/frotz/object.c b/apps/plugins/frotz/object.c
new file mode 100644
index 0000000000..676b6c93c5
--- /dev/null
+++ b/apps/plugins/frotz/object.c
@@ -0,0 +1,1003 @@
+/* object.c - Object manipulation opcodes
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+#define MAX_OBJECT 2000
+
+#define O1_PARENT 4
+#define O1_SIBLING 5
+#define O1_CHILD 6
+#define O1_PROPERTY_OFFSET 7
+#define O1_SIZE 9
+
+#define O4_PARENT 6
+#define O4_SIBLING 8
+#define O4_CHILD 10
+#define O4_PROPERTY_OFFSET 12
+#define O4_SIZE 14
+
+/*
+ * object_address
+ *
+ * Calculate the address of an object.
+ *
+ */
+
+static zword object_address (zword obj)
+{
+/* zchar obj_num[10]; */
+
+ /* Check object number */
+
+ if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
+ print_string("@Attempt to address illegal object ");
+ print_num(obj);
+ print_string(". This is normally fatal.");
+ new_line();
+ runtime_error (ERR_ILL_OBJ);
+ }
+
+ /* Return object address */
+
+ if (h_version <= V3)
+ return h_objects + ((obj - 1) * O1_SIZE + 62);
+ else
+ return h_objects + ((obj - 1) * O4_SIZE + 126);
+
+}/* object_address */
+
+/*
+ * object_name
+ *
+ * Return the address of the given object's name.
+ *
+ */
+
+zword object_name (zword object)
+{
+ zword obj_addr;
+ zword name_addr;
+
+ obj_addr = object_address (object);
+
+ /* The object name address is found at the start of the properties */
+
+ if (h_version <= V3)
+ obj_addr += O1_PROPERTY_OFFSET;
+ else
+ obj_addr += O4_PROPERTY_OFFSET;
+
+ LOW_WORD (obj_addr, name_addr)
+
+ return name_addr;
+
+}/* object_name */
+
+/*
+ * first_property
+ *
+ * Calculate the start address of the property list associated with
+ * an object.
+ *
+ */
+
+static zword first_property (zword obj)
+{
+ zword prop_addr;
+ zbyte size;
+
+ /* Fetch address of object name */
+
+ prop_addr = object_name (obj);
+
+ /* Get length of object name */
+
+ LOW_BYTE (prop_addr, size)
+
+ /* Add name length to pointer */
+
+ return prop_addr + 1 + 2 * size;
+
+}/* first_property */
+
+/*
+ * next_property
+ *
+ * Calculate the address of the next property in a property list.
+ *
+ */
+
+static zword next_property (zword prop_addr)
+{
+ zbyte value;
+
+ /* Load the current property id */
+
+ LOW_BYTE (prop_addr, value)
+ prop_addr++;
+
+ /* Calculate the length of this property */
+
+ if (h_version <= V3)
+ value >>= 5;
+ else if (!(value & 0x80))
+ value >>= 6;
+ else {
+
+ LOW_BYTE (prop_addr, value)
+ value &= 0x3f;
+
+ if (value == 0) value = 64; /* demanded by Spec 1.0 */
+
+ }
+
+ /* Add property length to current property pointer */
+
+ return prop_addr + value + 1;
+
+}/* next_property */
+
+/*
+ * unlink_object
+ *
+ * Unlink an object from its parent and siblings.
+ *
+ */
+
+static void unlink_object (zword object)
+{
+ zword obj_addr;
+ zword parent_addr;
+ zword sibling_addr;
+
+ if (object == 0) {
+ runtime_error (ERR_REMOVE_OBJECT_0);
+ return;
+ }
+
+ obj_addr = object_address (object);
+
+ if (h_version <= V3) {
+
+ zbyte parent;
+ zbyte younger_sibling;
+ zbyte older_sibling;
+ zbyte zero = 0;
+
+ /* Get parent of object, and return if no parent */
+
+ obj_addr += O1_PARENT;
+ LOW_BYTE (obj_addr, parent)
+ if (!parent)
+ return;
+
+ /* Get (older) sibling of object and set both parent and sibling
+ pointers to 0 */
+
+ SET_BYTE (obj_addr, zero)
+ obj_addr += O1_SIBLING - O1_PARENT;
+ LOW_BYTE (obj_addr, older_sibling)
+ SET_BYTE (obj_addr, zero)
+
+ /* Get first child of parent (the youngest sibling of the object) */
+
+ parent_addr = object_address (parent) + O1_CHILD;
+ LOW_BYTE (parent_addr, younger_sibling)
+
+ /* Remove object from the list of siblings */
+
+ if (younger_sibling == object)
+ SET_BYTE (parent_addr, older_sibling)
+ else {
+ do {
+ sibling_addr = object_address (younger_sibling) + O1_SIBLING;
+ LOW_BYTE (sibling_addr, younger_sibling)
+ } while (younger_sibling != object);
+ SET_BYTE (sibling_addr, older_sibling)
+ }
+
+ } else {
+
+ zword parent;
+ zword younger_sibling;
+ zword older_sibling;
+ zword zero = 0;
+
+ /* Get parent of object, and return if no parent */
+
+ obj_addr += O4_PARENT;
+ LOW_WORD (obj_addr, parent)
+ if (!parent)
+ return;
+
+ /* Get (older) sibling of object and set both parent and sibling
+ pointers to 0 */
+
+ SET_WORD (obj_addr, zero)
+ obj_addr += O4_SIBLING - O4_PARENT;
+ LOW_WORD (obj_addr, older_sibling)
+ SET_WORD (obj_addr, zero)
+
+ /* Get first child of parent (the youngest sibling of the object) */
+
+ parent_addr = object_address (parent) + O4_CHILD;
+ LOW_WORD (parent_addr, younger_sibling)
+
+ /* Remove object from the list of siblings */
+
+ if (younger_sibling == object)
+ SET_WORD (parent_addr, older_sibling)
+ else {
+ do {
+ sibling_addr = object_address (younger_sibling) + O4_SIBLING;
+ LOW_WORD (sibling_addr, younger_sibling)
+ } while (younger_sibling != object);
+ SET_WORD (sibling_addr, older_sibling)
+ }
+
+ }
+
+}/* unlink_object */
+
+/*
+ * z_clear_attr, clear an object attribute.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to be cleared
+ *
+ */
+
+void z_clear_attr (void)
+{
+ zword obj_addr;
+ zbyte value;
+
+ if (story_id == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtime_error (ERR_ILL_ATTR);
+
+ /* If we are monitoring attribute assignment display a short note */
+
+ if (f_setup.attribute_assignment) {
+ stream_mssg_on ();
+ print_string ("@clear_attr ");
+ print_object (zargs[0]);
+ print_string (" ");
+ print_num (zargs[1]);
+ stream_mssg_off ();
+ }
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_CLEAR_ATTR_0);
+ return;
+ }
+
+ /* Get attribute address */
+
+ obj_addr = object_address (zargs[0]) + zargs[1] / 8;
+
+ /* Clear attribute bit */
+
+ LOW_BYTE (obj_addr, value)
+ value &= ~(0x80 >> (zargs[1] & 7));
+ SET_BYTE (obj_addr, value)
+
+}/* z_clear_attr */
+
+/*
+ * z_jin, branch if the first object is inside the second.
+ *
+ * zargs[0] = first object
+ * zargs[1] = second object
+ *
+ */
+
+void z_jin (void)
+{
+ zword obj_addr;
+
+ /* If we are monitoring object locating display a short note */
+
+ if (f_setup.object_locating) {
+ stream_mssg_on ();
+ print_string ("@jin ");
+ print_object (zargs[0]);
+ print_string (" ");
+ print_object (zargs[1]);
+ stream_mssg_off ();
+ }
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_JIN_0);
+ branch (0 == zargs[1]);
+ return;
+ }
+
+ obj_addr = object_address (zargs[0]);
+
+ if (h_version <= V3) {
+
+ zbyte parent;
+
+ /* Get parent id from object */
+
+ obj_addr += O1_PARENT;
+ LOW_BYTE (obj_addr, parent)
+
+ /* Branch if the parent is obj2 */
+
+ branch (parent == zargs[1]);
+
+ } else {
+
+ zword parent;
+
+ /* Get parent id from object */
+
+ obj_addr += O4_PARENT;
+ LOW_WORD (obj_addr, parent)
+
+ /* Branch if the parent is obj2 */
+
+ branch (parent == zargs[1]);
+
+ }
+
+}/* z_jin */
+
+/*
+ * z_get_child, store the child of an object.
+ *
+ * zargs[0] = object
+ *
+ */
+
+void z_get_child (void)
+{
+ zword obj_addr;
+
+ /* If we are monitoring object locating display a short note */
+
+ if (f_setup.object_locating) {
+ stream_mssg_on ();
+ print_string ("@get_child ");
+ print_object (zargs[0]);
+ stream_mssg_off ();
+ }
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_GET_CHILD_0);
+ store (0);
+ branch (FALSE);
+ return;
+ }
+
+ obj_addr = object_address (zargs[0]);
+
+ if (h_version <= V3) {
+
+ zbyte child;
+
+ /* Get child id from object */
+
+ obj_addr += O1_CHILD;
+ LOW_BYTE (obj_addr, child)
+
+ /* Store child id and branch */
+
+ store (child);
+ branch (child);
+
+ } else {
+
+ zword child;
+
+ /* Get child id from object */
+
+ obj_addr += O4_CHILD;
+ LOW_WORD (obj_addr, child)
+
+ /* Store child id and branch */
+
+ store (child);
+ branch (child);
+
+ }
+
+}/* z_get_child */
+
+/*
+ * z_get_next_prop, store the number of the first or next property.
+ *
+ * zargs[0] = object
+ * zargs[1] = address of current property (0 gets the first property)
+ *
+ */
+
+void z_get_next_prop (void)
+{
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_GET_NEXT_PROP_0);
+ store (0);
+ return;
+ }
+
+ /* Property id is in bottom five (six) bits */
+
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ /* Load address of first property */
+
+ prop_addr = first_property (zargs[0]);
+
+ if (zargs[1] != 0) {
+
+ /* Scan down the property list */
+
+ do {
+ LOW_BYTE (prop_addr, value)
+ prop_addr = next_property (prop_addr);
+ } while ((value & mask) > zargs[1]);
+
+ /* Exit if the property does not exist */
+
+ if ((value & mask) != zargs[1])
+ runtime_error (ERR_NO_PROP);
+
+ }
+
+ /* Return the property id */
+
+ LOW_BYTE (prop_addr, value)
+ store ((zword) (value & mask));
+
+}/* z_get_next_prop */
+
+/*
+ * z_get_parent, store the parent of an object.
+ *
+ * zargs[0] = object
+ *
+ */
+
+void z_get_parent (void)
+{
+ zword obj_addr;
+
+ /* If we are monitoring object locating display a short note */
+
+ if (f_setup.object_locating) {
+ stream_mssg_on ();
+ print_string ("@get_parent ");
+ print_object (zargs[0]);
+ stream_mssg_off ();
+ }
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_GET_PARENT_0);
+ store (0);
+ return;
+ }
+
+ obj_addr = object_address (zargs[0]);
+
+ if (h_version <= V3) {
+
+ zbyte parent;
+
+ /* Get parent id from object */
+
+ obj_addr += O1_PARENT;
+ LOW_BYTE (obj_addr, parent)
+
+ /* Store parent */
+
+ store (parent);
+
+ } else {
+
+ zword parent;
+
+ /* Get parent id from object */
+
+ obj_addr += O4_PARENT;
+ LOW_WORD (obj_addr, parent)
+
+ /* Store parent */
+
+ store (parent);
+
+ }
+
+}/* z_get_parent */
+
+/*
+ * z_get_prop, store the value of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to be examined
+ *
+ */
+
+void z_get_prop (void)
+{
+ zword prop_addr;
+ zword wprop_val;
+ zbyte bprop_val;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_GET_PROP_0);
+ store (0);
+ return;
+ }
+
+ /* Property id is in bottom five (six) bits */
+
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ /* Load address of first property */
+
+ prop_addr = first_property (zargs[0]);
+
+ /* Scan down the property list */
+
+ for (;;) {
+ LOW_BYTE (prop_addr, value)
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property (prop_addr);
+ }
+
+ if ((value & mask) == zargs[1]) { /* property found */
+
+ /* Load property (byte or word sized) */
+
+ prop_addr++;
+
+ if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+
+ LOW_BYTE (prop_addr, bprop_val)
+ wprop_val = bprop_val;
+
+ } else LOW_WORD (prop_addr, wprop_val)
+
+ } else { /* property not found */
+
+ /* Load default value */
+
+ prop_addr = h_objects + 2 * (zargs[1] - 1);
+ LOW_WORD (prop_addr, wprop_val)
+
+ }
+
+ /* Store the property value */
+
+ store (wprop_val);
+
+}/* z_get_prop */
+
+/*
+ * z_get_prop_addr, store the address of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to be examined
+ *
+ */
+
+void z_get_prop_addr (void)
+{
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_GET_PROP_ADDR_0);
+ store (0);
+ return;
+ }
+
+ if (story_id == BEYOND_ZORK)
+ if (zargs[0] > MAX_OBJECT)
+ { store (0); return; }
+
+ /* Property id is in bottom five (six) bits */
+
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ /* Load address of first property */
+
+ prop_addr = first_property (zargs[0]);
+
+ /* Scan down the property list */
+
+ for (;;) {
+ LOW_BYTE (prop_addr, value)
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property (prop_addr);
+ }
+
+ /* Calculate the property address or return zero */
+
+ if ((value & mask) == zargs[1]) {
+
+ if (h_version >= V4 && (value & 0x80))
+ prop_addr++;
+ store ((zword) (prop_addr + 1));
+
+ } else store (0);
+
+}/* z_get_prop_addr */
+
+/*
+ * z_get_prop_len, store the length of an object property.
+ *
+ * zargs[0] = address of property to be examined
+ *
+ */
+
+void z_get_prop_len (void)
+{
+ zword addr;
+ zbyte value;
+
+ /* Back up the property pointer to the property id */
+
+ addr = zargs[0] - 1;
+ LOW_BYTE (addr, value)
+
+ /* Calculate length of property */
+
+ if (h_version <= V3)
+ value = (value >> 5) + 1;
+ else if (!(value & 0x80))
+ value = (value >> 6) + 1;
+ else {
+
+ value &= 0x3f;
+
+ if (value == 0) value = 64; /* demanded by Spec 1.0 */
+
+ }
+
+ /* Store length of property */
+
+ store (value);
+
+}/* z_get_prop_len */
+
+/*
+ * z_get_sibling, store the sibling of an object.
+ *
+ * zargs[0] = object
+ *
+ */
+
+void z_get_sibling (void)
+{
+ zword obj_addr;
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_GET_SIBLING_0);
+ store (0);
+ branch (FALSE);
+ return;
+ }
+
+ obj_addr = object_address (zargs[0]);
+
+ if (h_version <= V3) {
+
+ zbyte sibling;
+
+ /* Get sibling id from object */
+
+ obj_addr += O1_SIBLING;
+ LOW_BYTE (obj_addr, sibling)
+
+ /* Store sibling and branch */
+
+ store (sibling);
+ branch (sibling);
+
+ } else {
+
+ zword sibling;
+
+ /* Get sibling id from object */
+
+ obj_addr += O4_SIBLING;
+ LOW_WORD (obj_addr, sibling)
+
+ /* Store sibling and branch */
+
+ store (sibling);
+ branch (sibling);
+
+ }
+
+}/* z_get_sibling */
+
+/*
+ * z_insert_obj, make an object the first child of another object.
+ *
+ * zargs[0] = object to be moved
+ * zargs[1] = destination object
+ *
+ */
+
+void z_insert_obj (void)
+{
+ zword obj1 = zargs[0];
+ zword obj2 = zargs[1];
+ zword obj1_addr;
+ zword obj2_addr;
+
+ /* If we are monitoring object movements display a short note */
+
+ if (f_setup.object_movement) {
+ stream_mssg_on ();
+ print_string ("@move_obj ");
+ print_object (obj1);
+ print_string (" ");
+ print_object (obj2);
+ stream_mssg_off ();
+ }
+
+ if (obj1 == 0) {
+ runtime_error (ERR_MOVE_OBJECT_0);
+ return;
+ }
+
+ if (obj2 == 0) {
+ runtime_error (ERR_MOVE_OBJECT_TO_0);
+ return;
+ }
+
+ /* Get addresses of both objects */
+
+ obj1_addr = object_address (obj1);
+ obj2_addr = object_address (obj2);
+
+ /* Remove object 1 from current parent */
+
+ unlink_object (obj1);
+
+ /* Make object 1 first child of object 2 */
+
+ if (h_version <= V3) {
+
+ zbyte child;
+
+ obj1_addr += O1_PARENT;
+ SET_BYTE (obj1_addr, obj2)
+ obj2_addr += O1_CHILD;
+ LOW_BYTE (obj2_addr, child)
+ SET_BYTE (obj2_addr, obj1)
+ obj1_addr += O1_SIBLING - O1_PARENT;
+ SET_BYTE (obj1_addr, child)
+
+ } else {
+
+ zword child;
+
+ obj1_addr += O4_PARENT;
+ SET_WORD (obj1_addr, obj2)
+ obj2_addr += O4_CHILD;
+ LOW_WORD (obj2_addr, child)
+ SET_WORD (obj2_addr, obj1)
+ obj1_addr += O4_SIBLING - O4_PARENT;
+ SET_WORD (obj1_addr, child)
+
+ }
+
+}/* z_insert_obj */
+
+/*
+ * z_put_prop, set the value of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to set
+ * zargs[2] = value to set property to
+ *
+ */
+
+void z_put_prop (void)
+{
+ zword prop_addr;
+ zword value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_PUT_PROP_0);
+ return;
+ }
+
+ /* Property id is in bottom five or six bits */
+
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ /* Load address of first property */
+
+ prop_addr = first_property (zargs[0]);
+
+ /* Scan down the property list */
+
+ for (;;) {
+ LOW_BYTE (prop_addr, value)
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property (prop_addr);
+ }
+
+ /* Exit if the property does not exist */
+
+ if ((value & mask) != zargs[1])
+ runtime_error (ERR_NO_PROP);
+
+ /* Store the new property value (byte or word sized) */
+
+ prop_addr++;
+
+ if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+ zbyte v = zargs[2];
+ SET_BYTE (prop_addr, v)
+ } else {
+ zword v = zargs[2];
+ SET_WORD (prop_addr, v)
+ }
+
+}/* z_put_prop */
+
+/*
+ * z_remove_obj, unlink an object from its parent and siblings.
+ *
+ * zargs[0] = object
+ *
+ */
+
+void z_remove_obj (void)
+{
+
+ /* If we are monitoring object movements display a short note */
+
+ if (f_setup.object_movement) {
+ stream_mssg_on ();
+ print_string ("@remove_obj ");
+ print_object (zargs[0]);
+ stream_mssg_off ();
+ }
+
+ /* Call unlink_object to do the job */
+
+ unlink_object (zargs[0]);
+
+}/* z_remove_obj */
+
+/*
+ * z_set_attr, set an object attribute.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to set
+ *
+ */
+
+void z_set_attr (void)
+{
+ zword obj_addr;
+ zbyte value;
+
+ if (story_id == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtime_error (ERR_ILL_ATTR);
+
+ /* If we are monitoring attribute assignment display a short note */
+
+ if (f_setup.attribute_assignment) {
+ stream_mssg_on ();
+ print_string ("@set_attr ");
+ print_object (zargs[0]);
+ print_string (" ");
+ print_num (zargs[1]);
+ stream_mssg_off ();
+ }
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_SET_ATTR_0);
+ return;
+ }
+
+ /* Get attribute address */
+
+ obj_addr = object_address (zargs[0]) + zargs[1] / 8;
+
+ /* Load attribute byte */
+
+ LOW_BYTE (obj_addr, value)
+
+ /* Set attribute bit */
+
+ value |= 0x80 >> (zargs[1] & 7);
+
+ /* Store attribute byte */
+
+ SET_BYTE (obj_addr, value)
+
+}/* z_set_attr */
+
+/*
+ * z_test_attr, branch if an object attribute is set.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to test
+ *
+ */
+
+void z_test_attr (void)
+{
+ zword obj_addr;
+ zbyte value;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtime_error (ERR_ILL_ATTR);
+
+ /* If we are monitoring attribute testing display a short note */
+
+ if (f_setup.attribute_testing) {
+ stream_mssg_on ();
+ print_string ("@test_attr ");
+ print_object (zargs[0]);
+ print_string (" ");
+ print_num (zargs[1]);
+ stream_mssg_off ();
+ }
+
+ if (zargs[0] == 0) {
+ runtime_error (ERR_TEST_ATTR_0);
+ branch (FALSE);
+ return;
+ }
+
+ /* Get attribute address */
+
+ obj_addr = object_address (zargs[0]) + zargs[1] / 8;
+
+ /* Load attribute byte */
+
+ LOW_BYTE (obj_addr, value)
+
+ /* Test attribute */
+
+ branch (value & (0x80 >> (zargs[1] & 7)));
+
+}/* z_test_attr */
diff --git a/apps/plugins/frotz/process.c b/apps/plugins/frotz/process.c
new file mode 100644
index 0000000000..99ad82ca85
--- /dev/null
+++ b/apps/plugins/frotz/process.c
@@ -0,0 +1,798 @@
+/* process.c - Interpreter loop and program control
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+#ifdef DJGPP
+#include "djfrotz.h"
+#endif
+
+
+zword zargs[8];
+int zargc;
+
+static int finished = 0;
+
+static void __extended__ (void);
+static void __illegal__ (void);
+
+void (*op0_opcodes[0x10]) (void) = {
+ z_rtrue,
+ z_rfalse,
+ z_print,
+ z_print_ret,
+ z_nop,
+ z_save,
+ z_restore,
+ z_restart,
+ z_ret_popped,
+ z_catch,
+ z_quit,
+ z_new_line,
+ z_show_status,
+ z_verify,
+ __extended__,
+ z_piracy
+};
+
+void (*op1_opcodes[0x10]) (void) = {
+ z_jz,
+ z_get_sibling,
+ z_get_child,
+ z_get_parent,
+ z_get_prop_len,
+ z_inc,
+ z_dec,
+ z_print_addr,
+ z_call_s,
+ z_remove_obj,
+ z_print_obj,
+ z_ret,
+ z_jump,
+ z_print_paddr,
+ z_load,
+ z_call_n
+};
+
+void (*var_opcodes[0x40]) (void) = {
+ __illegal__,
+ z_je,
+ z_jl,
+ z_jg,
+ z_dec_chk,
+ z_inc_chk,
+ z_jin,
+ z_test,
+ z_or,
+ z_and,
+ z_test_attr,
+ z_set_attr,
+ z_clear_attr,
+ z_store,
+ z_insert_obj,
+ z_loadw,
+ z_loadb,
+ z_get_prop,
+ z_get_prop_addr,
+ z_get_next_prop,
+ z_add,
+ z_sub,
+ z_mul,
+ z_div,
+ z_mod,
+ z_call_s,
+ z_call_n,
+ z_set_colour,
+ z_throw,
+ __illegal__,
+ __illegal__,
+ __illegal__,
+ z_call_s,
+ z_storew,
+ z_storeb,
+ z_put_prop,
+ z_read,
+ z_print_char,
+ z_print_num,
+ z_random,
+ z_push,
+ z_pull,
+ z_split_window,
+ z_set_window,
+ z_call_s,
+ z_erase_window,
+ z_erase_line,
+ z_set_cursor,
+ z_get_cursor,
+ z_set_text_style,
+ z_buffer_mode,
+ z_output_stream,
+ z_input_stream,
+ z_sound_effect,
+ z_read_char,
+ z_scan_table,
+ z_not,
+ z_call_n,
+ z_call_n,
+ z_tokenise,
+ z_encode_text,
+ z_copy_table,
+ z_print_table,
+ z_check_arg_count
+};
+
+void (*ext_opcodes[0x1d]) (void) = {
+ z_save,
+ z_restore,
+ z_log_shift,
+ z_art_shift,
+ z_set_font,
+ z_draw_picture,
+ z_picture_data,
+ z_erase_picture,
+ z_set_margins,
+ z_save_undo,
+ z_restore_undo,
+ z_print_unicode,
+ z_check_unicode,
+ __illegal__,
+ __illegal__,
+ __illegal__,
+ z_move_window,
+ z_window_size,
+ z_window_style,
+ z_get_wind_prop,
+ z_scroll_window,
+ z_pop_stack,
+ z_read_mouse,
+ z_mouse_window,
+ z_push_stack,
+ z_put_wind_prop,
+ z_print_form,
+ z_make_menu,
+ z_picture_table
+};
+
+
+/*
+ * init_process
+ *
+ * Initialize process variables.
+ *
+ */
+
+void init_process (void)
+{
+ finished = 0;
+} /* init_process */
+
+
+/*
+ * load_operand
+ *
+ * Load an operand, either a variable or a constant.
+ *
+ */
+
+static void load_operand (zbyte type)
+{
+ zword value;
+
+ if (type & 2) { /* variable */
+
+ zbyte variable;
+
+ CODE_BYTE (variable)
+
+ if (variable == 0)
+ value = *sp++;
+ else if (variable < 16)
+ value = *(fp - variable);
+ else {
+ zword addr = h_globals + 2 * (variable - 16);
+ LOW_WORD (addr, value)
+ }
+
+ } else if (type & 1) { /* small constant */
+
+ zbyte bvalue;
+
+ CODE_BYTE (bvalue)
+ value = bvalue;
+
+ } else CODE_WORD (value) /* large constant */
+
+ zargs[zargc++] = value;
+
+}/* load_operand */
+
+/*
+ * load_all_operands
+ *
+ * Given the operand specifier byte, load all (up to four) operands
+ * for a VAR or EXT opcode.
+ *
+ */
+
+static void load_all_operands (zbyte specifier)
+{
+ int i;
+
+ for (i = 6; i >= 0; i -= 2) {
+
+ zbyte type = (specifier >> i) & 0x03;
+
+ if (type == 3)
+ break;
+
+ load_operand (type);
+
+ }
+
+}/* load_all_operands */
+
+/*
+ * interpret
+ *
+ * Z-code interpreter main loop
+ *
+ */
+
+void interpret (void)
+{
+
+ do {
+
+ zbyte opcode;
+
+ CODE_BYTE (opcode)
+
+ zargc = 0;
+
+ if (opcode < 0x80) { /* 2OP opcodes */
+
+ load_operand ((zbyte) (opcode & 0x40) ? 2 : 1);
+ load_operand ((zbyte) (opcode & 0x20) ? 2 : 1);
+
+ var_opcodes[opcode & 0x1f] ();
+
+ } else if (opcode < 0xb0) { /* 1OP opcodes */
+
+ load_operand ((zbyte) (opcode >> 4));
+
+ op1_opcodes[opcode & 0x0f] ();
+
+ } else if (opcode < 0xc0) { /* 0OP opcodes */
+
+ op0_opcodes[opcode - 0xb0] ();
+
+ } else { /* VAR opcodes */
+
+ zbyte specifier1;
+ zbyte specifier2;
+
+ if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */
+ CODE_BYTE (specifier1) /* and 0xfa are */
+ CODE_BYTE (specifier2) /* call opcodes */
+ load_all_operands (specifier1); /* with up to 8 */
+ load_all_operands (specifier2); /* arguments */
+ } else {
+ CODE_BYTE (specifier1)
+ load_all_operands (specifier1);
+ }
+
+ var_opcodes[opcode - 0xc0] ();
+
+ }
+
+#if defined(DJGPP) && defined(SOUND_SUPPORT)
+ if (end_of_sound_flag)
+ end_of_sound ();
+#endif
+
+ } while (finished == 0);
+
+ finished--;
+
+}/* interpret */
+
+/*
+ * call
+ *
+ * Call a subroutine. Save PC and FP then load new PC and initialise
+ * new stack frame. Note that the caller may legally provide less or
+ * more arguments than the function actually has. The call type "ct"
+ * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
+ *
+ */
+
+void call (zword routine, int argc, zword *args, int ct)
+{
+ long pc;
+ zword value;
+ zbyte count;
+ int i;
+
+ if (sp - stack < 4)
+ runtime_error (ERR_STK_OVF);
+
+ GET_PC (pc)
+
+ *--sp = (zword) (pc >> 9);
+ *--sp = (zword) (pc & 0x1ff);
+ *--sp = (zword) (fp - stack - 1);
+ *--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8)));
+
+ fp = sp;
+ frame_count++;
+
+ /* Calculate byte address of routine */
+
+ if (h_version <= V3)
+ pc = (long) routine << 1;
+ else if (h_version <= V5)
+ pc = (long) routine << 2;
+ else if (h_version <= V7)
+ pc = ((long) routine << 2) + ((long) h_functions_offset << 3);
+ else /* h_version == V8 */
+ pc = (long) routine << 3;
+
+ if (pc >= story_size)
+ runtime_error (ERR_ILL_CALL_ADDR);
+
+ SET_PC (pc)
+
+ /* Initialise local variables */
+
+ CODE_BYTE (count)
+
+ if (count > 15)
+ runtime_error (ERR_CALL_NON_RTN);
+ if (sp - stack < count)
+ runtime_error (ERR_STK_OVF);
+
+ if (f_setup.save_quetzal)
+ fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */
+
+ value = 0;
+
+ for (i = 0; i < count; i++) {
+
+ if (h_version <= V4) /* V1 to V4 games provide default */
+ CODE_WORD (value) /* values for all local variables */
+
+ *--sp = (zword) ((argc-- > 0) ? args[i] : value);
+
+ }
+
+ /* Start main loop for direct calls */
+
+ if (ct == 2)
+ interpret ();
+
+}/* call */
+
+/*
+ * ret
+ *
+ * Return from the current subroutine and restore the previous stack
+ * frame. The result may be stored (0), thrown away (1) or pushed on
+ * the stack (2). In the latter case a direct call has been finished
+ * and we must exit the interpreter loop.
+ *
+ */
+
+void ret (zword value)
+{
+ long pc;
+ int ct;
+
+ if (sp > fp)
+ runtime_error (ERR_STK_UNDF);
+
+ sp = fp;
+
+ ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8);
+ frame_count--;
+ fp = stack + 1 + *sp++;
+ pc = *sp++;
+ pc = ((long) *sp++ << 9) | pc;
+
+ SET_PC (pc)
+
+ /* Handle resulting value */
+
+ if (ct == 0)
+ store (value);
+ if (ct == 2)
+ *--sp = value;
+
+ /* Stop main loop for direct calls */
+
+ if (ct == 2)
+ finished++;
+
+}/* ret */
+
+/*
+ * branch
+ *
+ * Take a jump after an instruction based on the flag, either true or
+ * false. The branch can be short or long; it is encoded in one or two
+ * bytes respectively. When bit 7 of the first byte is set, the jump
+ * takes place if the flag is true; otherwise it is taken if the flag
+ * is false. When bit 6 of the first byte is set, the branch is short;
+ * otherwise it is long. The offset occupies the bottom 6 bits of the
+ * first byte plus all the bits in the second byte for long branches.
+ * Uniquely, an offset of 0 means return false, and an offset of 1 is
+ * return true.
+ *
+ */
+
+void branch (bool flag)
+{
+ long pc;
+ zword offset;
+ zbyte specifier;
+ zbyte off1;
+ zbyte off2;
+
+ CODE_BYTE (specifier)
+
+ off1 = specifier & 0x3f;
+
+ if (!flag)
+ specifier ^= 0x80;
+
+ if (!(specifier & 0x40)) { /* it's a long branch */
+
+ if (off1 & 0x20) /* propagate sign bit */
+ off1 |= 0xc0;
+
+ CODE_BYTE (off2)
+
+ offset = (off1 << 8) | off2;
+
+ } else offset = off1; /* it's a short branch */
+
+ if (specifier & 0x80) {
+
+ if (offset > 1) { /* normal branch */
+
+ GET_PC (pc)
+ pc += (short) offset - 2;
+ SET_PC (pc)
+
+ } else ret (offset); /* special case, return 0 or 1 */
+ }
+
+}/* branch */
+
+/*
+ * store
+ *
+ * Store an operand, either as a variable or pushed on the stack.
+ *
+ */
+
+void store (zword value)
+{
+ zbyte variable;
+
+ CODE_BYTE (variable)
+
+ if (variable == 0)
+ *--sp = value;
+ else if (variable < 16)
+ *(fp - variable) = value;
+ else {
+ zword addr = h_globals + 2 * (variable - 16);
+ SET_WORD (addr, value)
+ }
+
+}/* store */
+
+/*
+ * direct_call
+ *
+ * Call the interpreter loop directly. This is necessary when
+ *
+ * - a sound effect has been finished
+ * - a read instruction has timed out
+ * - a newline countdown has hit zero
+ *
+ * The interpreter returns the result value on the stack.
+ *
+ */
+
+int direct_call (zword addr)
+{
+ zword saved_zargs[8];
+ int saved_zargc;
+ int i;
+
+ /* Calls to address 0 return false */
+
+ if (addr == 0)
+ return 0;
+
+ /* Save operands and operand count */
+
+ for (i = 0; i < 8; i++)
+ saved_zargs[i] = zargs[i];
+
+ saved_zargc = zargc;
+
+ /* Call routine directly */
+
+ call (addr, 0, 0, 2);
+
+ /* Restore operands and operand count */
+
+ for (i = 0; i < 8; i++)
+ zargs[i] = saved_zargs[i];
+
+ zargc = saved_zargc;
+
+ /* Resulting value lies on top of the stack */
+
+ return (short) *sp++;
+
+}/* direct_call */
+
+/*
+ * __extended__
+ *
+ * Load and execute an extended opcode.
+ *
+ */
+
+static void __extended__ (void)
+{
+ zbyte opcode;
+ zbyte specifier;
+
+ CODE_BYTE (opcode)
+ CODE_BYTE (specifier)
+
+ load_all_operands (specifier);
+
+ if (opcode < 0x1d) /* extended opcodes from 0x1d on */
+ ext_opcodes[opcode] (); /* are reserved for future spec' */
+
+}/* __extended__ */
+
+/*
+ * __illegal__
+ *
+ * Exit game because an unknown opcode has been hit.
+ *
+ */
+
+static void __illegal__ (void)
+{
+
+ runtime_error (ERR_ILL_OPCODE);
+
+}/* __illegal__ */
+
+/*
+ * z_catch, store the current stack frame for later use with z_throw.
+ *
+ * no zargs used
+ *
+ */
+
+void z_catch (void)
+{
+
+ store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack));
+
+}/* z_catch */
+
+/*
+ * z_throw, go back to the given stack frame and return the given value.
+ *
+ * zargs[0] = value to return
+ * zargs[1] = stack frame
+ *
+ */
+
+void z_throw (void)
+{
+
+ if (f_setup.save_quetzal) {
+ if (zargs[1] > frame_count)
+ runtime_error (ERR_BAD_FRAME);
+
+ /* Unwind the stack a frame at a time. */
+ for (; frame_count > zargs[1]; --frame_count)
+ fp = stack + 1 + fp[1];
+ } else {
+ if (zargs[1] > STACK_SIZE)
+ runtime_error (ERR_BAD_FRAME);
+
+ fp = stack + zargs[1];
+ }
+
+ ret (zargs[0]);
+
+}/* z_throw */
+
+/*
+ * z_call_n, call a subroutine and discard its result.
+ *
+ * zargs[0] = packed address of subroutine
+ * zargs[1] = first argument (optional)
+ * ...
+ * zargs[7] = seventh argument (optional)
+ *
+ */
+
+void z_call_n (void)
+{
+
+ if (zargs[0] != 0)
+ call (zargs[0], zargc - 1, zargs + 1, 1);
+
+}/* z_call_n */
+
+/*
+ * z_call_s, call a subroutine and store its result.
+ *
+ * zargs[0] = packed address of subroutine
+ * zargs[1] = first argument (optional)
+ * ...
+ * zargs[7] = seventh argument (optional)
+ *
+ */
+
+void z_call_s (void)
+{
+
+ if (zargs[0] != 0)
+ call (zargs[0], zargc - 1, zargs + 1, 0);
+ else
+ store (0);
+
+}/* z_call_s */
+
+/*
+ * z_check_arg_count, branch if subroutine was called with >= n arg's.
+ *
+ * zargs[0] = number of arguments
+ *
+ */
+
+void z_check_arg_count (void)
+{
+
+ if (fp == stack + STACK_SIZE)
+ branch (zargs[0] == 0);
+ else
+ branch (zargs[0] <= (*fp & 0xff));
+
+}/* z_check_arg_count */
+
+/*
+ * z_jump, jump unconditionally to the given address.
+ *
+ * zargs[0] = PC relative address
+ *
+ */
+
+void z_jump (void)
+{
+ long pc;
+
+ GET_PC (pc)
+
+ pc += (short) zargs[0] - 2;
+
+ if (pc >= story_size)
+ runtime_error (ERR_ILL_JUMP_ADDR);
+
+ SET_PC (pc)
+
+}/* z_jump */
+
+/*
+ * z_nop, no operation.
+ *
+ * no zargs used
+ *
+ */
+
+void z_nop (void)
+{
+
+ /* Do nothing */
+
+}/* z_nop */
+
+/*
+ * z_quit, stop game and exit interpreter.
+ *
+ * no zargs used
+ *
+ */
+
+void z_quit (void)
+{
+
+ finished = 9999;
+
+}/* z_quit */
+
+/*
+ * z_ret, return from a subroutine with the given value.
+ *
+ * zargs[0] = value to return
+ *
+ */
+
+void z_ret (void)
+{
+
+ ret (zargs[0]);
+
+}/* z_ret */
+
+/*
+ * z_ret_popped, return from a subroutine with a value popped off the stack.
+ *
+ * no zargs used
+ *
+ */
+
+void z_ret_popped (void)
+{
+
+ ret (*sp++);
+
+}/* z_ret_popped */
+
+/*
+ * z_rfalse, return from a subroutine with false (0).
+ *
+ * no zargs used
+ *
+ */
+
+void z_rfalse (void)
+{
+
+ ret (0);
+
+}/* z_rfalse */
+
+/*
+ * z_rtrue, return from a subroutine with true (1).
+ *
+ * no zargs used
+ *
+ */
+
+void z_rtrue (void)
+{
+
+ ret (1);
+
+}/* z_rtrue */
diff --git a/apps/plugins/frotz/quetzal.c b/apps/plugins/frotz/quetzal.c
new file mode 100644
index 0000000000..a75ade856e
--- /dev/null
+++ b/apps/plugins/frotz/quetzal.c
@@ -0,0 +1,541 @@
+/* quetzal.c - Saving and restoring of Quetzal files.
+ * Written by Martin Frost <mdf@doc.ic.ac.uk>
+ *
+ * Changes for Rockbox copyright 2009 Torne Wuff
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+#define far
+
+#define get_c fgetc
+#define put_c fputc
+
+typedef unsigned long zlong;
+
+/*
+ * This is used only by save_quetzal. It probably should be allocated
+ * dynamically rather than statically.
+ */
+
+static zword frames[STACK_SIZE/4+1];
+
+/*
+ * ID types.
+ */
+
+#define makeid(a,b,c,d) ((zlong) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)))
+
+#define ID_FORM makeid ('F','O','R','M')
+#define ID_IFZS makeid ('I','F','Z','S')
+#define ID_IFhd makeid ('I','F','h','d')
+#define ID_UMem makeid ('U','M','e','m')
+#define ID_CMem makeid ('C','M','e','m')
+#define ID_Stks makeid ('S','t','k','s')
+#define ID_ANNO makeid ('A','N','N','O')
+
+/*
+ * Various parsing states within restoration.
+ */
+
+#define GOT_HEADER 0x01
+#define GOT_STACK 0x02
+#define GOT_MEMORY 0x04
+#define GOT_NONE 0x00
+#define GOT_ALL 0x07
+#define GOT_ERROR 0x80
+
+/*
+ * Macros used to write the files.
+ */
+
+#define write_byte(fp,b) (put_c (b, fp) != EOF)
+#define write_bytx(fp,b) write_byte (fp, (b) & 0xFF)
+#define write_word(fp,w) \
+ (write_bytx (fp, (w) >> 8) && write_bytx (fp, (w)))
+#define write_long(fp,l) \
+ (write_bytx (fp, (l) >> 24) && write_bytx (fp, (l) >> 16) && \
+ write_bytx (fp, (l) >> 8) && write_bytx (fp, (l)))
+#define write_chnk(fp,id,len) \
+ (write_long (fp, (id)) && write_long (fp, (len)))
+#define write_run(fp,run) \
+ (write_byte (fp, 0) && write_byte (fp, (run)))
+
+/* Read one word from file; return TRUE if OK. */
+static bool read_word (int f, zword *result)
+{
+ int a, b;
+
+ if ((a = get_c (f)) == EOF) return FALSE;
+ if ((b = get_c (f)) == EOF) return FALSE;
+
+ *result = ((zword) a << 8) | (zword) b;
+ return TRUE;
+}
+
+/* Read one long from file; return TRUE if OK. */
+static bool read_long (int f, zlong *result)
+{
+ int a, b, c, d;
+
+ if ((a = get_c (f)) == EOF) return FALSE;
+ if ((b = get_c (f)) == EOF) return FALSE;
+ if ((c = get_c (f)) == EOF) return FALSE;
+ if ((d = get_c (f)) == EOF) return FALSE;
+
+ *result = ((zlong) a << 24) | ((zlong) b << 16) |
+ ((zlong) c << 8) | (zlong) d;
+ return TRUE;
+}
+
+/*
+ * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error
+ * occurred before any damage was done, -1 on a fatal error.
+ */
+
+zword restore_quetzal (int svf, int stf)
+{
+ zlong ifzslen, currlen, tmpl;
+ zlong pc;
+ zword i, tmpw;
+ zword fatal = 0; /* Set to -1 when errors must be fatal. */
+ zbyte skip, progress = GOT_NONE;
+ int x, y;
+
+ /* Check it's really an `IFZS' file. */
+ if (!read_long (svf, &tmpl)
+ || !read_long (svf, &ifzslen)
+ || !read_long (svf, &currlen)) return 0;
+ if (tmpl != ID_FORM || currlen != ID_IFZS)
+ {
+ print_string ("This is not a saved game file!\n");
+ return 0;
+ }
+ if ((ifzslen & 1) || ifzslen<4) /* Sanity checks. */ return 0;
+ ifzslen -= 4;
+
+ /* Read each chunk and process it. */
+ while (ifzslen > 0)
+ {
+ /* Read chunk header. */
+ if (ifzslen < 8) /* Couldn't contain a chunk. */ return 0;
+ if (!read_long (svf, &tmpl)
+ || !read_long (svf, &currlen)) return 0;
+ ifzslen -= 8; /* Reduce remaining by size of header. */
+
+ /* Handle chunk body. */
+ if (ifzslen < currlen) /* Chunk goes past EOF?! */ return 0;
+ skip = currlen & 1;
+ ifzslen -= currlen + (zlong) skip;
+
+ switch (tmpl)
+ {
+ /* `IFhd' header chunk; must be first in file. */
+ case ID_IFhd:
+ if (progress & GOT_HEADER)
+ {
+ print_string ("Save file has two IFZS chunks!\n");
+ return fatal;
+ }
+ progress |= GOT_HEADER;
+ if (currlen < 13
+ || !read_word (svf, &tmpw)) return fatal;
+ if (tmpw != h_release)
+ progress = GOT_ERROR;
+
+ for (i=H_SERIAL; i<H_SERIAL+6; ++i)
+ {
+ if ((x = get_c (svf)) == EOF) return fatal;
+ if (x != zmp[i])
+ progress = GOT_ERROR;
+ }
+
+ if (!read_word (svf, &tmpw)) return fatal;
+ if (tmpw != h_checksum)
+ progress = GOT_ERROR;
+
+ if (progress & GOT_ERROR)
+ {
+ print_string ("File was not saved from this story!\n");
+ return fatal;
+ }
+ if ((x = get_c (svf)) == EOF) return fatal;
+ pc = (zlong) x << 16;
+ if ((x = get_c (svf)) == EOF) return fatal;
+ pc |= (zlong) x << 8;
+ if ((x = get_c (svf)) == EOF) return fatal;
+ pc |= (zlong) x;
+ fatal = -1; /* Setting PC means errors must be fatal. */
+ SET_PC (pc);
+
+ for (i=13; i<currlen; ++i)
+ (void) get_c (svf); /* Skip rest of chunk. */
+ break;
+ /* `Stks' stacks chunk; restoring this is quite complex. ;) */
+ case ID_Stks:
+ if (progress & GOT_STACK)
+ {
+ print_string ("File contains two stack chunks!\n");
+ break;
+ }
+ progress |= GOT_STACK;
+
+ fatal = -1; /* Setting SP means errors must be fatal. */
+ sp = stack + STACK_SIZE;
+
+ /*
+ * All versions other than V6 may use evaluation stack outside
+ * any function context. As a result a faked function context
+ * will be present in the file here. We skip this context, but
+ * load the associated stack onto the stack proper...
+ */
+ if (h_version != V6)
+ {
+ if (currlen < 8) return fatal;
+ for (i=0; i<6; ++i)
+ if (get_c (svf) != 0) return fatal;
+ if (!read_word (svf, &tmpw)) return fatal;
+ if (tmpw > STACK_SIZE)
+ {
+ print_string ("Save-file has too much stack (and I can't cope).\n");
+ return fatal;
+ }
+ currlen -= 8;
+ if ((signed)currlen < tmpw*2) return fatal;
+ for (i=0; i<tmpw; ++i)
+ if (!read_word (svf, --sp)) return fatal;
+ currlen -= tmpw*2;
+ }
+
+ /* We now proceed to load the main block of stack frames. */
+ for (fp = stack+STACK_SIZE, frame_count = 0;
+ currlen > 0;
+ currlen -= 8, ++frame_count)
+ {
+ if (currlen < 8) return fatal;
+ if (sp - stack < 4) /* No space for frame. */
+ {
+ print_string ("Save-file has too much stack (and I can't cope).\n");
+ return fatal;
+ }
+
+ /* Read PC, procedure flag and formal param count. */
+ if (!read_long (svf, &tmpl)) return fatal;
+ y = (int) (tmpl & 0x0F); /* Number of formals. */
+ tmpw = y << 8;
+
+ /* Read result variable. */
+ if ((x = get_c (svf)) == EOF) return fatal;
+
+ /* Check the procedure flag... */
+ if (tmpl & 0x10)
+ {
+ tmpw |= 0x1000; /* It's a procedure. */
+ tmpl >>= 8; /* Shift to get PC value. */
+ }
+ else
+ {
+ /* Functions have type 0, so no need to or anything. */
+ tmpl >>= 8; /* Shift to get PC value. */
+ --tmpl; /* Point at result byte. */
+ /* Sanity check on result variable... */
+ if (zmp[tmpl] != (zbyte) x)
+ {
+ print_string ("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
+ return fatal;
+ }
+ }
+ *--sp = (zword) (tmpl >> 9); /* High part of PC */
+ *--sp = (zword) (tmpl & 0x1FF); /* Low part of PC */
+ *--sp = (zword) (fp - stack - 1); /* FP */
+
+ /* Read and process argument mask. */
+ if ((x = get_c (svf)) == EOF) return fatal;
+ ++x; /* Should now be a power of 2 */
+ for (i=0; i<8; ++i)
+ if (x & (1<<i))
+ break;
+ if (x ^ (1<<i)) /* Not a power of 2 */
+ {
+ print_string ("Save-file uses incomplete argument lists (which I can't handle)\n");
+ return fatal;
+ }
+ *--sp = tmpw | i;
+ fp = sp; /* FP for next frame. */
+
+ /* Read amount of eval stack used. */
+ if (!read_word (svf, &tmpw)) return fatal;
+
+ tmpw += y; /* Amount of stack + number of locals. */
+ if (sp - stack <= tmpw)
+ {
+ print_string ("Save-file has too much stack (and I can't cope).\n");
+ return fatal;
+ }
+ if ((signed)currlen < tmpw*2) return fatal;
+ for (i=0; i<tmpw; ++i)
+ if (!read_word (svf, --sp)) return fatal;
+ currlen -= tmpw*2;
+ }
+ /* End of `Stks' processing... */
+ break;
+ /* Any more special chunk types must go in HERE or ABOVE. */
+ /* `CMem' compressed memory chunk; uncompress it. */
+ case ID_CMem:
+ if (!(progress & GOT_MEMORY)) /* Don't complain if two. */
+ {
+ (void) fseek (stf, 0, SEEK_SET);
+ i=0; /* Bytes written to data area. */
+ for (; currlen > 0; --currlen)
+ {
+ if ((x = get_c (svf)) == EOF) return fatal;
+ if (x == 0) /* Start run. */
+ {
+ /* Check for bogus run. */
+ if (currlen < 2)
+ {
+ print_string ("File contains bogus `CMem' chunk.\n");
+ for (; currlen > 0; --currlen)
+ (void) get_c (svf); /* Skip rest. */
+ currlen = 1;
+ i = 0xFFFF;
+ break; /* Keep going; may be a `UMem' too. */
+ }
+ /* Copy story file to memory during the run. */
+ --currlen;
+ if ((x = get_c (svf)) == EOF) return fatal;
+ for (; x >= 0 && i<h_dynamic_size; --x, ++i)
+ if ((y = get_c (stf)) == EOF) return fatal;
+ else
+ zmp[i] = (zbyte) y;
+ }
+ else /* Not a run. */
+ {
+ if ((y = get_c (stf)) == EOF) return fatal;
+ zmp[i] = (zbyte) (x ^ y);
+ ++i;
+ }
+ /* Make sure we don't load too much. */
+ if (i > h_dynamic_size)
+ {
+ print_string ("warning: `CMem' chunk too long!\n");
+ for (; currlen > 1; --currlen)
+ (void) get_c (svf); /* Skip rest. */
+ break; /* Keep going; there may be a `UMem' too. */
+ }
+ }
+ /* If chunk is short, assume a run. */
+ for (; i<h_dynamic_size; ++i)
+ if ((y = get_c (stf)) == EOF) return fatal;
+ else
+ zmp[i] = (zbyte) y;
+ if (currlen == 0)
+ progress |= GOT_MEMORY; /* Only if succeeded. */
+ break;
+ }
+ /* Fall right thru (to default) if already GOT_MEMORY */
+ /* `UMem' uncompressed memory chunk; load it. */
+ case ID_UMem:
+ if (!(progress & GOT_MEMORY)) /* Don't complain if two. */
+ {
+ /* Must be exactly the right size. */
+ if (currlen == h_dynamic_size)
+ {
+ if (fread (zmp, currlen, 1, svf) == 1)
+ {
+ progress |= GOT_MEMORY; /* Only on success. */
+ break;
+ }
+ }
+ else
+ print_string ("`UMem' chunk wrong size!\n");
+ /* Fall into default action (skip chunk) on errors. */
+ }
+ /* Fall thru (to default) if already GOT_MEMORY */
+ /* Unrecognised chunk type; skip it. */
+ default:
+ (void) fseek (svf, currlen, SEEK_CUR); /* Skip chunk. */
+ break;
+ }
+ if (skip)
+ (void) get_c (svf); /* Skip pad byte. */
+ }
+
+ /*
+ * We've reached the end of the file. For the restoration to have been a
+ * success, we must have had one of each of the required chunks.
+ */
+ if (!(progress & GOT_HEADER))
+ print_string ("error: no valid header (`IFhd') chunk in file.\n");
+ if (!(progress & GOT_STACK))
+ print_string ("error: no valid stack (`Stks') chunk in file.\n");
+ if (!(progress & GOT_MEMORY))
+ print_string ("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
+
+ return (progress == GOT_ALL ? 2 : fatal);
+}
+
+/*
+ * Save a game using Quetzal format. Return 1 if OK, 0 if failed.
+ */
+
+zword save_quetzal (int svf, int stf)
+{
+ zlong ifzslen = 0, cmemlen = 0, stkslen = 0;
+ zlong pc;
+ zword i, j, n;
+ zword nvars, nargs, nstk, *p;
+ zbyte var;
+ long cmempos, stkspos;
+ int c;
+
+ /* Write `IFZS' header. */
+ if (!write_chnk (svf, ID_FORM, 0)) return 0;
+ if (!write_long (svf, ID_IFZS)) return 0;
+
+ /* Write `IFhd' chunk. */
+ GET_PC (pc);
+ if (!write_chnk (svf, ID_IFhd, 13)) return 0;
+ if (!write_word (svf, h_release)) return 0;
+ for (i=H_SERIAL; i<H_SERIAL+6; ++i)
+ if (!write_byte (svf, zmp[i])) return 0;
+ if (!write_word (svf, h_checksum)) return 0;
+ if (!write_long (svf, pc << 8)) /* Includes pad. */ return 0;
+
+ /* Write `CMem' chunk. */
+ if ((cmempos = ftell (svf)) < 0) return 0;
+ if (!write_chnk (svf, ID_CMem, 0)) return 0;
+ (void) fseek (stf, 0, SEEK_SET);
+ /* j holds current run length. */
+ for (i=0, j=0, cmemlen=0; i < h_dynamic_size; ++i)
+ {
+ if ((c = get_c (stf)) == EOF) return 0;
+ c ^= (int) zmp[i];
+ if (c == 0)
+ ++j; /* It's a run of equal bytes. */
+ else
+ {
+ /* Write out any run there may be. */
+ if (j > 0)
+ {
+ for (; j > 0x100; j -= 0x100)
+ {
+ if (!write_run (svf, 0xFF)) return 0;
+ cmemlen += 2;
+ }
+ if (!write_run (svf, j-1)) return 0;
+ cmemlen += 2;
+ j = 0;
+ }
+ /* Any runs are now written. Write this (nonzero) byte. */
+ if (!write_byte (svf, (zbyte) c)) return 0;
+ ++cmemlen;
+ }
+ }
+ /*
+ * Reached end of dynamic memory. We ignore any unwritten run there may be
+ * at this point.
+ */
+ if (cmemlen & 1) /* Chunk length must be even. */
+ if (!write_byte (svf, 0)) return 0;
+
+ /* Write `Stks' chunk. You are not expected to understand this. ;) */
+ if ((stkspos = ftell (svf)) < 0) return 0;
+ if (!write_chnk (svf, ID_Stks, 0)) return 0;
+
+ /*
+ * We construct a list of frame indices, most recent first, in `frames'.
+ * These indices are the offsets into the `stack' array of the word before
+ * the first word pushed in each frame.
+ */
+ frames[0] = sp - stack; /* The frame we'd get by doing a call now. */
+ for (i = fp - stack + 4, n=0; i < STACK_SIZE+4; i = stack[i-3] + 5)
+ frames[++n] = i;
+
+ /*
+ * All versions other than V6 can use evaluation stack outside a function
+ * context. We write a faked stack frame (most fields zero) to cater for
+ * this.
+ */
+ if (h_version != V6)
+ {
+ for (i=0; i<6; ++i)
+ if (!write_byte (svf, 0)) return 0;
+ nstk = STACK_SIZE - frames[n];
+ if (!write_word (svf, nstk)) return 0;
+ for (j=STACK_SIZE-1; j >= frames[n]; --j)
+ if (!write_word (svf, stack[j])) return 0;
+ stkslen = 8 + 2*nstk;
+ }
+
+ /* Write out the rest of the stack frames. */
+ for (i=n; i>0; --i)
+ {
+ p = stack + frames[i] - 4; /* Points to call frame. */
+ nvars = (p[0] & 0x0F00) >> 8;
+ nargs = p[0] & 0x00FF;
+ nstk = frames[i] - frames[i-1] - nvars - 4;
+ pc = ((zlong) p[3] << 9) | p[2];
+
+ switch (p[0] & 0xF000) /* Check type of call. */
+ {
+ case 0x0000: /* Function. */
+ var = zmp[pc];
+ pc = ((pc + 1) << 8) | nvars;
+ break;
+ case 0x1000: /* Procedure. */
+ var = 0;
+ pc = (pc << 8) | 0x10 | nvars; /* Set procedure flag. */
+ break;
+ /* case 0x2000: */
+ default:
+ runtime_error (ERR_SAVE_IN_INTER);
+ return 0;
+ }
+ if (nargs != 0)
+ nargs = (1 << nargs) - 1; /* Make args into bitmap. */
+
+ /* Write the main part of the frame... */
+ if (!write_long (svf, pc)
+ || !write_byte (svf, var)
+ || !write_byte (svf, nargs)
+ || !write_word (svf, nstk)) return 0;
+
+ /* Write the variables and eval stack. */
+ for (j=0, ++p; j<nvars+nstk; ++j, --p)
+ if (!write_word (svf, *p)) return 0;
+
+ /* Calculate length written thus far. */
+ stkslen += 8 + 2 * (nvars + nstk);
+ }
+
+ /* Fill in variable chunk lengths. */
+ ifzslen = 3*8 + 4 + 14 + cmemlen + stkslen;
+ if (cmemlen & 1)
+ ++ifzslen;
+ (void) fseek (svf, 4, SEEK_SET);
+ if (!write_long (svf, ifzslen)) return 0;
+ (void) fseek (svf, cmempos+4, SEEK_SET);
+ if (!write_long (svf, cmemlen)) return 0;
+ (void) fseek (svf, stkspos+4, SEEK_SET);
+ if (!write_long (svf, stkslen)) return 0;
+
+ /* After all that, still nothing went wrong! */
+ return 1;
+}
diff --git a/apps/plugins/frotz/random.c b/apps/plugins/frotz/random.c
new file mode 100644
index 0000000000..6ea14d89c9
--- /dev/null
+++ b/apps/plugins/frotz/random.c
@@ -0,0 +1,82 @@
+/* random.c - Z-machine random number generator
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+static long A = 1;
+
+static int interval = 0;
+static int counter = 0;
+
+/*
+ * seed_random
+ *
+ * Set the seed value for the random number generator.
+ *
+ */
+
+void seed_random (int value)
+{
+
+ if (value == 0) { /* ask interface for seed value */
+ A = os_random_seed ();
+ interval = 0;
+ } else if (value < 1000) { /* special seed value */
+ counter = 0;
+ interval = value;
+ } else { /* standard seed value */
+ A = value;
+ interval = 0;
+ }
+
+}/* seed_random */
+
+/*
+ * z_random, store a random number or set the random number seed.
+ *
+ * zargs[0] = range (positive) or seed value (negative)
+ *
+ */
+
+void z_random ()
+{
+
+ if ((short) zargs[0] <= 0) { /* set random seed */
+
+ seed_random (- (short) zargs[0]);
+ store (0);
+
+ } else { /* generate random number */
+
+ zword result;
+
+ if (interval != 0) { /* ...in special mode */
+ result = counter++;
+ if (counter == interval) counter = 0;
+ } else { /* ...in standard mode */
+ A = 0x015a4e35L * A + 1;
+ result = (A >> 16) & 0x7fff;
+ }
+
+ store ((zword) (result % zargs[0] + 1));
+
+ }
+
+}/* z_random */
diff --git a/apps/plugins/frotz/redirect.c b/apps/plugins/frotz/redirect.c
new file mode 100644
index 0000000000..d81776dbcd
--- /dev/null
+++ b/apps/plugins/frotz/redirect.c
@@ -0,0 +1,172 @@
+/* redirect.c - Output redirection to Z-machine memory
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+#define MAX_NESTING 16
+
+extern zword get_max_width (zword);
+
+static int depth = -1;
+
+static struct {
+ zword xsize;
+ zword table;
+ zword width;
+ zword total;
+} redirect[MAX_NESTING];
+
+/*
+ * memory_open
+ *
+ * Begin output redirection to the memory of the Z-machine.
+ *
+ */
+
+void memory_open (zword table, zword xsize, bool buffering)
+{
+
+ if (++depth < MAX_NESTING) {
+
+ if (!buffering)
+ xsize = 0xffff;
+ if (buffering && (short) xsize <= 0)
+ xsize = get_max_width ((zword) (- (short) xsize));
+
+ storew (table, 0);
+
+ redirect[depth].table = table;
+ redirect[depth].width = 0;
+ redirect[depth].total = 0;
+ redirect[depth].xsize = xsize;
+
+ ostream_memory = TRUE;
+
+ } else runtime_error (ERR_STR3_NESTING);
+
+}/* memory_open */
+
+/*
+ * memory_new_line
+ *
+ * Redirect a newline to the memory of the Z-machine.
+ *
+ */
+
+void memory_new_line (void)
+{
+ zword size;
+ zword addr;
+
+ redirect[depth].total += redirect[depth].width;
+ redirect[depth].width = 0;
+
+ addr = redirect[depth].table;
+
+ LOW_WORD (addr, size)
+ addr += 2;
+
+ if (redirect[depth].xsize != 0xffff) {
+
+ redirect[depth].table = addr + size;
+ size = 0;
+
+ } else storeb ((zword) (addr + (size++)), 13);
+
+ storew (redirect[depth].table, size);
+
+}/* memory_new_line */
+
+/*
+ * memory_word
+ *
+ * Redirect a string of characters to the memory of the Z-machine.
+ *
+ */
+
+void memory_word (const zchar *s)
+{
+ zword size;
+ zword addr;
+ zchar c;
+
+ if (h_version == V6) {
+
+ int width = os_string_width (s);
+
+ if (redirect[depth].xsize != 0xffff)
+
+ if (redirect[depth].width + width > redirect[depth].xsize) {
+
+ if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+ width = os_string_width (++s);
+
+ memory_new_line ();
+
+ }
+
+ redirect[depth].width += width;
+
+ }
+
+ addr = redirect[depth].table;
+
+ LOW_WORD (addr, size)
+ addr += 2;
+
+ while ((c = *s++) != 0)
+ storeb ((zword) (addr + (size++)), translate_to_zscii (c));
+
+ storew (redirect[depth].table, size);
+
+}/* memory_word */
+
+/*
+ * memory_close
+ *
+ * End of output redirection.
+ *
+ */
+
+void memory_close (void)
+{
+
+ if (depth >= 0) {
+
+ if (redirect[depth].xsize != 0xffff)
+ memory_new_line ();
+
+ if (h_version == V6) {
+
+ h_line_width = (redirect[depth].xsize != 0xffff) ?
+ redirect[depth].total : redirect[depth].width;
+
+ SET_WORD (H_LINE_WIDTH, h_line_width)
+
+ }
+
+ if (depth == 0)
+ ostream_memory = FALSE;
+
+ depth--;
+
+ }
+
+}/* memory_close */
diff --git a/apps/plugins/frotz/screen.c b/apps/plugins/frotz/screen.c
new file mode 100644
index 0000000000..713ec2921c
--- /dev/null
+++ b/apps/plugins/frotz/screen.c
@@ -0,0 +1,1743 @@
+/* screen.c - Generic screen manipulation
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+extern void set_header_extension (int, zword);
+
+extern int direct_call (zword);
+
+static struct {
+ enum story story_id;
+ int pic;
+ int pic1;
+ int pic2;
+} mapper[] = {
+ { ZORK_ZERO, 5, 497, 498 },
+ { ZORK_ZERO, 6, 501, 502 },
+ { ZORK_ZERO, 7, 499, 500 },
+ { ZORK_ZERO, 8, 503, 504 },
+ { ARTHUR, 54, 170, 171 },
+ { SHOGUN, 50, 61, 62 },
+ { UNKNOWN, 0, 0, 0 }
+};
+
+static int font_height = 1;
+static int font_width = 1;
+
+static bool input_redraw = FALSE;
+static bool more_prompts = TRUE;
+static bool discarding = FALSE;
+static bool cursor = TRUE;
+
+static int input_window = 0;
+
+static struct {
+ zword y_pos;
+ zword x_pos;
+ zword y_size;
+ zword x_size;
+ zword y_cursor;
+ zword x_cursor;
+ zword left;
+ zword right;
+ zword nl_routine;
+ zword nl_countdown;
+ zword style;
+ zword colour;
+ zword font;
+ zword font_size;
+ zword attribute;
+ zword line_count;
+} wp[8], *cwp;
+
+
+/*
+ * winarg0
+ *
+ * Return the window number in zargs[0]. In V6 only, -3 refers to the
+ * current window.
+ *
+ */
+
+static zword winarg0 (void)
+{
+
+ if (h_version == V6 && (short) zargs[0] == -3)
+ return cwin;
+
+ if (zargs[0] >= ((h_version == V6) ? 8 : 2))
+ runtime_error (ERR_ILL_WIN);
+
+ return zargs[0];
+
+}/* winarg0 */
+
+/*
+ * winarg2
+ *
+ * Return the (optional) window number in zargs[2]. -3 refers to the
+ * current window. This optional window number was only used by some
+ * V6 opcodes: set_cursor, set_margins, set_colour.
+ *
+ */
+
+static zword winarg2 (void)
+{
+
+ if (zargc < 3 || (short) zargs[2] == -3)
+ return cwin;
+
+ if (zargs[2] >= 8)
+ runtime_error (ERR_ILL_WIN);
+
+ return zargs[2];
+
+}/* winarg2 */
+
+/*
+ * update_cursor
+ *
+ * Move the hardware cursor to make it match the window properties.
+ *
+ */
+
+static void update_cursor (void)
+{
+
+ os_set_cursor (
+ cwp->y_pos + cwp->y_cursor - 1,
+ cwp->x_pos + cwp->x_cursor - 1);
+
+}/* update_cursor */
+
+/*
+ * reset_cursor
+ *
+ * Reset the cursor of a given window to its initial position.
+ *
+ */
+
+static void reset_cursor (zword win)
+{
+ int lines = 0;
+
+ if (h_version <= V4 && win == 0)
+ lines = wp[0].y_size / hi (wp[0].font_size) - 1;
+
+ wp[win].y_cursor = hi (wp[0].font_size) * lines + 1;
+ wp[win].x_cursor = wp[win].left + 1;
+
+ if (win == cwin)
+ update_cursor ();
+
+}/* reset_cursor */
+
+/*
+ * set_more_prompts
+ *
+ * Turn more prompts on/off.
+ *
+ */
+
+void set_more_prompts (bool flag)
+{
+
+ if (flag && !more_prompts)
+ cwp->line_count = 0;
+
+ more_prompts = flag;
+
+}/* set_more_prompts */
+
+/*
+ * units_left
+ *
+ * Return the #screen units from the cursor to the end of the line.
+ *
+ */
+
+static int units_left (void)
+{
+
+ return cwp->x_size - cwp->right - cwp->x_cursor + 1;
+
+}/* units_left */
+
+/*
+ * get_max_width
+ *
+ * Return maximum width of a line in the given window. This is used in
+ * connection with the extended output stream #3 call in V6.
+ *
+ */
+
+zword get_max_width (zword win)
+{
+
+ if (h_version == V6) {
+
+ if (win >= 8)
+ runtime_error (ERR_ILL_WIN);
+
+ return wp[win].x_size - wp[win].left - wp[win].right;
+
+ } else return 0xffff;
+
+}/* get_max_width */
+
+/*
+ * countdown
+ *
+ * Decrement the newline counter. Call the newline interrupt when the
+ * counter hits zero. This is a helper function for screen_new_line.
+ *
+ */
+
+static void countdown (void)
+{
+
+ if (cwp->nl_countdown != 0)
+ if (--cwp->nl_countdown == 0)
+ direct_call (cwp->nl_routine);
+
+}/* countdown */
+
+/*
+ * screen_new_line
+ *
+ * Print a newline to the screen.
+ *
+ */
+
+void screen_new_line (void)
+{
+
+ if (discarding) return;
+
+ /* Handle newline interrupts at the start (for most cases) */
+
+ if (h_interpreter_number != INTERP_MSDOS || story_id != ZORK_ZERO || h_release != 393)
+ countdown ();
+
+ /* Check whether the last input line gets destroyed */
+
+ if (input_window == cwin)
+ input_redraw = TRUE;
+
+ /* If the cursor has not reached the bottom line, then move it to
+ the next line; otherwise scroll the window or reset the cursor
+ to the top left. */
+
+ cwp->x_cursor = cwp->left + 1;
+
+ if (cwp->y_cursor + 2 * font_height - 1 > cwp->y_size)
+
+ if (enable_scrolling) {
+
+ zword y = cwp->y_pos;
+ zword x = cwp->x_pos;
+
+ os_scroll_area (y,
+ x,
+ y + cwp->y_size - 1,
+ x + cwp->x_size - 1,
+ font_height);
+
+ } else cwp->y_cursor = 1;
+
+ else cwp->y_cursor += font_height;
+
+ update_cursor ();
+
+ /* See if we need to print a more prompt (unless the game has set
+ the line counter to -999 in order to suppress more prompts). */
+
+ if (enable_scrolling && (short) cwp->line_count != -999) {
+
+ zword above = (cwp->y_cursor - 1) / font_height;
+ zword below = (cwp->y_size - cwp->y_cursor + 1) / font_height;
+
+ cwp->line_count++;
+
+ if ((short) cwp->line_count >= (short) above + below - 1) {
+
+ if (more_prompts)
+ os_more_prompt ();
+
+ cwp->line_count = f_setup.context_lines;
+
+ }
+
+ }
+
+ /* Handle newline interrupts at the end for Zork Zero under DOS */
+
+ if (h_interpreter_number == INTERP_MSDOS && story_id == ZORK_ZERO && h_release == 393)
+ countdown ();
+
+}/* screen_new_line */
+
+/*
+ * screen_char
+ *
+ * Display a single character on the screen.
+ *
+ */
+
+void screen_char (zchar c)
+{
+ int width;
+
+ if (discarding) return;
+
+ if (c == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
+ c = ' ';
+
+ if (units_left () < (width = os_char_width (c))) {
+
+ if (!enable_wrapping)
+ { cwp->x_cursor = cwp->x_size - cwp->right; return; }
+
+ screen_new_line ();
+
+ }
+
+ os_display_char (c); cwp->x_cursor += width;
+
+}/* screen_char */
+
+/*
+ * screen_word
+ *
+ * Display a string of characters on the screen. If the word doesn't fit
+ * then use wrapping or clipping depending on the current setting of the
+ * enable_wrapping flag.
+ *
+ */
+
+void screen_word (const zchar *s)
+{
+ int width;
+
+ if (discarding) return;
+
+ if (*s == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
+ screen_char (*s++);
+
+ if (units_left () < (width = os_string_width (s))) {
+
+ if (!enable_wrapping) {
+
+ zchar c;
+
+ while ((c = *s++) != 0)
+
+ if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) {
+
+ int arg = (int) *s++;
+
+ if (c == ZC_NEW_FONT)
+ os_set_font (arg);
+ if (c == ZC_NEW_STYLE)
+ os_set_text_style (arg);
+
+ } else screen_char (c);
+
+ return;
+
+ }
+
+ if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+ width = os_string_width (++s);
+
+#ifdef AMIGA
+ if (cwin == 0) Justifiable ();
+#endif
+
+ screen_new_line ();
+
+ }
+
+ os_display_string (s); cwp->x_cursor += width;
+
+}/* screen_word */
+
+/*
+ * screen_write_input
+ *
+ * Display an input line on the screen. This is required during playback.
+ *
+ */
+
+void screen_write_input (const zchar *buf, zchar key)
+{
+ int width;
+
+ if (units_left () < (width = os_string_width (buf)))
+ screen_new_line ();
+
+ os_display_string (buf); cwp->x_cursor += width;
+
+ if (key == ZC_RETURN)
+ screen_new_line ();
+
+}/* screen_write_input */
+
+/*
+ * screen_erase_input
+ *
+ * Remove an input line that has already been printed from the screen
+ * as if it was deleted by the player. This could be necessary during
+ * playback.
+ *
+ */
+
+void screen_erase_input (const zchar *buf)
+{
+
+ if (buf[0] != 0) {
+
+ int width = os_string_width (buf);
+
+ zword y;
+ zword x;
+
+ cwp->x_cursor -= width;
+
+ y = cwp->y_pos + cwp->y_cursor - 1;
+ x = cwp->x_pos + cwp->x_cursor - 1;
+
+ os_erase_area (y, x, y + font_height - 1, x + width - 1);
+ os_set_cursor (y, x);
+
+ }
+
+}/* screen_erase_input */
+
+/*
+ * console_read_input
+ *
+ * Read an input line from the keyboard and return the terminating key.
+ *
+ */
+
+zchar console_read_input (int max, zchar *buf, zword timeout, bool continued)
+{
+ zchar key;
+ int i;
+
+ /* Make sure there is some space for input */
+
+ if (cwin == 0 && units_left () + os_string_width (buf) < 10 * font_width)
+ screen_new_line ();
+
+ /* Make sure the input line is visible */
+
+ if (continued && input_redraw)
+ screen_write_input (buf, -1);
+
+ input_window = cwin;
+ input_redraw = FALSE;
+
+ /* Get input line from IO interface */
+
+ cwp->x_cursor -= os_string_width (buf);
+ key = os_read_line (max, buf, timeout, units_left (), continued);
+ cwp->x_cursor += os_string_width (buf);
+
+ if (key != ZC_TIME_OUT)
+ for (i = 0; i < 8; i++)
+ wp[i].line_count = 0;
+
+ /* Add a newline if the input was terminated normally */
+
+ if (key == ZC_RETURN)
+ screen_new_line ();
+
+ return key;
+
+}/* console_read_input */
+
+/*
+ * console_read_key
+ *
+ * Read a single keystroke and return it.
+ *
+ */
+
+zchar console_read_key (zword timeout)
+{
+ zchar key;
+ int i;
+
+ key = os_read_key (timeout, cursor);
+
+ if (key != ZC_TIME_OUT)
+ for (i = 0; i < 8; i++)
+ wp[i].line_count = 0;
+
+ return key;
+
+}/* console_read_key */
+
+/*
+ * update_attributes
+ *
+ * Set the three enable_*** variables to make them match the attributes
+ * of the current window.
+ *
+ */
+
+static void update_attributes (void)
+{
+ zword attr = cwp->attribute;
+
+ enable_wrapping = attr & 1;
+ enable_scrolling = attr & 2;
+ enable_scripting = attr & 4;
+ enable_buffering = attr & 8;
+
+ /* Some story files forget to select wrapping for printing hints */
+
+ if (story_id == ZORK_ZERO && h_release == 366)
+ if (cwin == 0)
+ enable_wrapping = TRUE;
+ if (story_id == SHOGUN && h_release <= 295)
+ if (cwin == 0)
+ enable_wrapping = TRUE;
+
+}/* update_attributes */
+
+/*
+ * refresh_text_style
+ *
+ * Set the right text style. This can be necessary when the fixed font
+ * flag is changed, or when a new window is selected, or when the game
+ * uses the set_text_style opcode.
+ *
+ */
+
+void refresh_text_style (void)
+{
+ zword style;
+
+ if (h_version != V6) {
+
+ style = wp[0].style;
+
+ if (cwin != 0 || h_flags & FIXED_FONT_FLAG)
+ style |= FIXED_WIDTH_STYLE;
+
+ } else style = cwp->style;
+
+ if (!ostream_memory && ostream_screen && enable_buffering) {
+
+ print_char (ZC_NEW_STYLE);
+ print_char (style);
+
+ } else os_set_text_style (style);
+
+}/* refresh_text_style */
+
+/*
+ * set_window
+ *
+ * Set the current window. In V6 every window has its own set of window
+ * properties such as colours, text style, cursor position and size.
+ *
+ */
+
+static void set_window (zword win)
+{
+
+ flush_buffer ();
+
+ cwin = win; cwp = wp + win;
+
+ update_attributes ();
+
+ if (h_version == V6) {
+
+ os_set_colour (lo (cwp->colour), hi (cwp->colour));
+
+ if (os_font_data (cwp->font, &font_height, &font_width))
+ os_set_font (cwp->font);
+
+ os_set_text_style (cwp->style);
+
+ } else refresh_text_style ();
+
+ if (h_version != V6 && win != 0) {
+ wp[win].y_cursor = 1;
+ wp[win].x_cursor = 1;
+ }
+
+ update_cursor ();
+
+}/* set_window */
+
+/*
+ * erase_window
+ *
+ * Erase a window to background colour.
+ *
+ */
+
+void erase_window (zword win)
+{
+ zword y = wp[win].y_pos;
+ zword x = wp[win].x_pos;
+
+ if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA)
+ os_set_colour (lo (wp[win].colour), hi (wp[win].colour));
+
+ os_erase_area (y,
+ x,
+ y + wp[win].y_size - 1,
+ x + wp[win].x_size - 1);
+
+ if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA)
+ os_set_colour (lo (cwp->colour), hi (cwp->colour));
+
+ reset_cursor (win);
+
+ wp[win].line_count = 0;
+
+}/* erase_window */
+
+/*
+ * split_window
+ *
+ * Divide the screen into upper (1) and lower (0) windows. In V3 the upper
+ * window appears below the status line.
+ *
+ */
+
+void split_window (zword height)
+{
+ zword stat_height = 0;
+
+ flush_buffer ();
+
+ /* Calculate height of status line and upper window */
+
+ if (h_version != V6)
+ height *= hi (wp[1].font_size);
+
+ if (h_version <= V3)
+ stat_height = hi (wp[7].font_size);
+
+ /* Cursor of upper window mustn't be swallowed by the lower window */
+
+ wp[1].y_cursor += wp[1].y_pos - 1 - stat_height;
+
+ wp[1].y_pos = 1 + stat_height;
+ wp[1].y_size = height;
+
+ if ((short) wp[1].y_cursor > (short) wp[1].y_size)
+ reset_cursor (1);
+
+ /* Cursor of lower window mustn't be swallowed by the upper window */
+
+ wp[0].y_cursor += wp[0].y_pos - 1 - stat_height - height;
+
+ wp[0].y_pos = 1 + stat_height + height;
+ wp[0].y_size = h_screen_height - stat_height - height;
+
+ if ((short) wp[0].y_cursor < 1)
+ reset_cursor (0);
+
+ /* Erase the upper window in V3 only */
+
+ if (h_version == V3 && height != 0)
+ erase_window (1);
+
+}/* split_window */
+
+/*
+ * erase_screen
+ *
+ * Erase the entire screen to background colour.
+ *
+ */
+
+static void erase_screen (zword win)
+{
+ int i;
+
+ os_erase_area (1, 1, h_screen_height, h_screen_width);
+
+ if ((short) win == -1) {
+ split_window (0);
+ set_window (0);
+ reset_cursor (0);
+ }
+
+ for (i = 0; i < 8; i++)
+ wp[i].line_count = 0;
+
+}/* erase_screen */
+
+/* #ifdef AMIGA */
+
+/*
+ * resize_screen
+ *
+ * Try to adapt the window properties to a new screen size.
+ *
+ */
+
+void resize_screen (void)
+{
+
+ if (h_version != V6) {
+
+ wp[0].x_size = h_screen_width;
+ wp[1].x_size = h_screen_width;
+ wp[7].x_size = h_screen_width;
+
+ wp[0].y_size = h_screen_height - wp[1].y_size - wp[7].y_size;
+
+ }
+
+}/* resize_screen */
+
+/* #endif */
+
+/*
+ * restart_screen
+ *
+ * Prepare the screen for a new game.
+ *
+ */
+
+void restart_screen (void)
+{
+
+ /* Use default settings */
+
+ os_set_colour (h_default_foreground, h_default_background);
+
+ if (os_font_data (TEXT_FONT, &font_height, &font_width))
+ os_set_font (TEXT_FONT);
+
+ os_set_text_style (0);
+
+ cursor = TRUE;
+
+ /* Initialise window properties */
+
+ mwin = 1;
+
+ for (cwp = wp; cwp < wp + 8; cwp++) {
+ cwp->y_pos = 1;
+ cwp->x_pos = 1;
+ cwp->y_size = 0;
+ cwp->x_size = 0;
+ cwp->y_cursor = 1;
+ cwp->x_cursor = 1;
+ cwp->left = 0;
+ cwp->right = 0;
+ cwp->nl_routine = 0;
+ cwp->nl_countdown = 0;
+ cwp->style = 0;
+ cwp->colour = (h_default_background << 8) | h_default_foreground;
+ cwp->font = TEXT_FONT;
+ cwp->font_size = (font_height << 8) | font_width;
+ cwp->attribute = 8;
+ }
+
+ /* Prepare lower/upper windows and status line */
+
+ wp[0].attribute = 15;
+
+ wp[0].left = f_setup.left_margin;
+ wp[0].right = f_setup.right_margin;
+
+ wp[0].x_size = h_screen_width;
+ wp[1].x_size = h_screen_width;
+
+ if (h_version <= V3)
+ wp[7].x_size = h_screen_width;
+
+ os_restart_game (RESTART_WPROP_SET);
+
+ /* Clear the screen, unsplit it and select window 0 */
+
+ erase_screen ((zword) (-1));
+
+}/* restart_screen */
+
+/*
+ * validate_click
+ *
+ * Return false if the last mouse click occured outside the current
+ * mouse window; otherwise write the mouse arrow coordinates to the
+ * memory of the header extension table and return true.
+ *
+ */
+
+bool validate_click (void)
+{
+
+ if (mwin >= 0) {
+
+ if (mouse_y < wp[mwin].y_pos || mouse_y >= wp[mwin].y_pos + wp[mwin].y_size)
+ return FALSE;
+ if (mouse_x < wp[mwin].x_pos || mouse_x >= wp[mwin].x_pos + wp[mwin].x_size)
+ return FALSE;
+
+ hx_mouse_y = mouse_y - wp[mwin].y_pos + 1;
+ hx_mouse_x = mouse_x - wp[mwin].x_pos + 1;
+
+ } else {
+
+ if (mouse_y < 1 || mouse_y > h_screen_height)
+ return FALSE;
+ if (mouse_x < 1 || mouse_x > h_screen_width)
+ return FALSE;
+
+ hx_mouse_y = mouse_y;
+ hx_mouse_x = mouse_x;
+
+ }
+
+ if (h_version != V6) {
+ hx_mouse_y = (hx_mouse_y - 1) / h_font_height + 1;
+ hx_mouse_x = (hx_mouse_x - 1) / h_font_width + 1;
+ }
+
+ set_header_extension (HX_MOUSE_Y, hx_mouse_y);
+ set_header_extension (HX_MOUSE_X, hx_mouse_x);
+
+ return TRUE;
+
+}/* validate_click */
+
+/*
+ * screen_mssg_on
+ *
+ * Start printing a so-called debugging message. The contents of the
+ * message are passed to the message stream, a Frotz specific output
+ * stream with maximum priority.
+ *
+ */
+
+void screen_mssg_on (void)
+{
+
+ if (cwin == 0) { /* messages in window 0 only */
+
+ os_set_text_style (0);
+
+ if (cwp->x_cursor != cwp->left + 1)
+ screen_new_line ();
+
+ screen_char (ZC_INDENT);
+
+ } else discarding = TRUE; /* discard messages in other windows */
+
+}/* screen_mssg_on */
+
+/*
+ * screen_mssg_off
+ *
+ * Stop printing a "debugging" message.
+ *
+ */
+
+void screen_mssg_off (void)
+{
+
+ if (cwin == 0) { /* messages in window 0 only */
+
+ screen_new_line ();
+
+ refresh_text_style ();
+
+ } else discarding = FALSE; /* message has been discarded */
+
+}/* screen_mssg_off */
+
+/*
+ * z_buffer_mode, turn text buffering on/off.
+ *
+ * zargs[0] = new text buffering flag (0 or 1)
+ *
+ */
+
+void z_buffer_mode (void)
+{
+
+ /* Infocom's V6 games rarely use the buffer_mode opcode. If they do
+ then only to print text immediately, without any delay. This was
+ used to give the player some sign of life while the game was
+ spending much time on parsing a complicated input line. (To turn
+ off word wrapping, V6 games use the window_style opcode instead.)
+ Today we can afford to ignore buffer_mode in V6. */
+
+ if (h_version != V6) {
+
+ flush_buffer ();
+
+ wp[0].attribute &= ~8;
+
+ if (zargs[0] != 0)
+ wp[0].attribute |= 8;
+
+ update_attributes ();
+
+ }
+
+}/* z_buffer_mode */
+
+/*
+ * z_draw_picture, draw a picture.
+ *
+ * zargs[0] = number of picture to draw
+ * zargs[1] = y-coordinate of top left corner
+ * zargs[2] = x-coordinate of top left corner
+ *
+ */
+
+void z_draw_picture (void)
+{
+ zword pic = zargs[0];
+
+ zword y = zargs[1];
+ zword x = zargs[2];
+
+ int i;
+
+ flush_buffer ();
+
+ if (y == 0) /* use cursor line if y-coordinate is 0 */
+ y = cwp->y_cursor;
+ if (x == 0) /* use cursor column if x-coordinate is 0 */
+ x = cwp->x_cursor;
+
+ y += cwp->y_pos - 1;
+ x += cwp->x_pos - 1;
+
+ /* The following is necessary to make Amiga and Macintosh story
+ files work with MCGA graphics files. Some screen-filling
+ pictures of the original Amiga release like the borders of
+ Zork Zero were split into several MCGA pictures (left, right
+ and top borders). We pretend this has not happened. */
+
+ for (i = 0; mapper[i].story_id != UNKNOWN; i++)
+
+ if (story_id == mapper[i].story_id && pic == mapper[i].pic) {
+
+ int height1, width1;
+ int height2, width2;
+
+ int delta = 0;
+
+ os_picture_data (pic, &height1, &width1);
+ os_picture_data (mapper[i].pic2, &height2, &width2);
+
+ if (story_id == ARTHUR && pic == 54)
+ delta = h_screen_width / 160;
+
+ os_draw_picture (mapper[i].pic1, y + height1, x + delta);
+ os_draw_picture (mapper[i].pic2, y + height1, x + width1 - width2 - delta);
+
+ }
+
+ os_draw_picture (pic, y, x);
+
+ if (story_id == SHOGUN)
+
+ if (pic == 3) {
+
+ int height, width;
+
+ os_picture_data (59, &height, &width);
+ os_draw_picture (59, y, h_screen_width - width + 1);
+
+ }
+
+}/* z_draw_picture */
+
+/*
+ * z_erase_line, erase the line starting at the cursor position.
+ *
+ * zargs[0] = 1 + #units to erase (1 clears to the end of the line)
+ *
+ */
+
+void z_erase_line (void)
+{
+ zword pixels = zargs[0];
+ zword y, x;
+
+ flush_buffer ();
+
+ /* Clipping at the right margin of the current window */
+
+ if (--pixels == 0 || pixels > units_left ())
+ pixels = units_left ();
+
+ /* Erase from cursor position */
+
+ y = cwp->y_pos + cwp->y_cursor - 1;
+ x = cwp->x_pos + cwp->x_cursor - 1;
+
+ os_erase_area (y, x, y + font_height - 1, x + pixels - 1);
+
+}/* z_erase_line */
+
+/*
+ * z_erase_picture, erase a picture with background colour.
+ *
+ * zargs[0] = number of picture to erase
+ * zargs[1] = y-coordinate of top left corner (optional)
+ * zargs[2] = x-coordinate of top left corner (optional)
+ *
+ */
+
+void z_erase_picture (void)
+{
+ int height, width;
+
+ zword y = zargs[1];
+ zword x = zargs[2];
+
+ flush_buffer ();
+
+ if (y == 0) /* use cursor line if y-coordinate is 0 */
+ y = cwp->y_cursor;
+ if (x == 0) /* use cursor column if x-coordinate is 0 */
+ x = cwp->x_cursor;
+
+ os_picture_data (zargs[0], &height, &width);
+
+ y += cwp->y_pos - 1;
+ x += cwp->x_pos - 1;
+
+ os_erase_area (y, x, y + height - 1, x + width - 1);
+
+}/* z_erase_picture */
+
+/*
+ * z_erase_window, erase a window or the screen to background colour.
+ *
+ * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
+ *
+ */
+
+void z_erase_window (void)
+{
+
+ flush_buffer ();
+
+ if ((short) zargs[0] == -1 || (short) zargs[0] == -2)
+ erase_screen (zargs[0]);
+ else
+ erase_window (winarg0 ());
+
+}/* z_erase_window */
+
+/*
+ * z_get_cursor, write the cursor coordinates into a table.
+ *
+ * zargs[0] = address to write information to
+ *
+ */
+
+void z_get_cursor (void)
+{
+ zword y, x;
+
+ flush_buffer ();
+
+ y = cwp->y_cursor;
+ x = cwp->x_cursor;
+
+ if (h_version != V6) { /* convert to grid positions */
+ y = (y - 1) / h_font_height + 1;
+ x = (x - 1) / h_font_width + 1;
+ }
+
+ storew ((zword) (zargs[0] + 0), y);
+ storew ((zword) (zargs[0] + 2), x);
+
+}/* z_get_cursor */
+
+/*
+ * z_get_wind_prop, store the value of a window property.
+ *
+ * zargs[0] = window (-3 is the current one)
+ * zargs[1] = number of window property to be stored
+ *
+ */
+
+void z_get_wind_prop (void)
+{
+
+ flush_buffer ();
+
+ if (zargs[1] >= 16)
+ runtime_error (ERR_ILL_WIN_PROP);
+
+ store (((zword *) (wp + winarg0 ())) [zargs[1]]);
+
+}/* z_get_wind_prop */
+
+/*
+ * z_mouse_window, select a window as mouse window.
+ *
+ * zargs[0] = window number (-3 is the current) or -1 for the screen
+ *
+ */
+
+void z_mouse_window (void)
+{
+
+ mwin = ((short) zargs[0] == -1) ? -1 : winarg0 ();
+
+}/* z_mouse_window */
+
+/*
+ * z_move_window, place a window on the screen.
+ *
+ * zargs[0] = window (-3 is the current one)
+ * zargs[1] = y-coordinate
+ * zargs[2] = x-coordinate
+ *
+ */
+
+void z_move_window (void)
+{
+ zword win = winarg0 ();
+
+ flush_buffer ();
+
+ wp[win].y_pos = zargs[1];
+ wp[win].x_pos = zargs[2];
+
+ if (win == cwin)
+ update_cursor ();
+
+}/* z_move_window */
+
+/*
+ * z_picture_data, get information on a picture or the graphics file.
+ *
+ * zargs[0] = number of picture or 0 for the graphics file
+ * zargs[1] = address to write information to
+ *
+ */
+
+void z_picture_data (void)
+{
+ zword pic = zargs[0];
+ zword table = zargs[1];
+
+ int height, width;
+ int i;
+
+ bool avail = os_picture_data (pic, &height, &width);
+
+ for (i = 0; mapper[i].story_id != UNKNOWN; i++)
+
+ if (story_id == mapper[i].story_id) {
+
+ if (pic == mapper[i].pic) {
+
+ int height2, width2;
+
+ avail &= os_picture_data (mapper[i].pic1, &height2, &width2);
+ avail &= os_picture_data (mapper[i].pic2, &height2, &width2);
+
+ height += height2;
+
+ } else if (pic == mapper[i].pic1 || pic == mapper[i].pic2)
+
+ avail = FALSE;
+ }
+
+ storew ((zword) (table + 0), (zword) (height));
+ storew ((zword) (table + 2), (zword) (width));
+
+ branch (avail);
+
+}/* z_picture_data */
+
+/*
+ * z_picture_table, prepare a group of pictures for faster display.
+ *
+ * zargs[0] = address of table holding the picture numbers
+ *
+ */
+
+void z_picture_table (void)
+{
+
+ /* This opcode is used by Shogun and Zork Zero when the player
+ encounters built-in games such as Peggleboz. Nowadays it is
+ not very helpful to hold the picture data in memory because
+ even a small disk cache avoids re-loading of data. */
+
+}/* z_picture_table */
+
+/*
+ * z_print_table, print ASCII text in a rectangular area.
+ *
+ * zargs[0] = address of text to be printed
+ * zargs[1] = width of rectangular area
+ * zargs[2] = height of rectangular area (optional)
+ * zargs[3] = number of char's to skip between lines (optional)
+ *
+ */
+
+void z_print_table (void)
+{
+ zword addr = zargs[0];
+ zword x;
+ int i, j;
+
+ flush_buffer ();
+
+ /* Supply default arguments */
+
+ if (zargc < 3)
+ zargs[2] = 1;
+ if (zargc < 4)
+ zargs[3] = 0;
+
+ /* Write text in width x height rectangle */
+
+ x = cwp->x_cursor;
+
+ for (i = 0; i < zargs[2]; i++) {
+
+ if (i != 0) {
+
+ flush_buffer ();
+
+ cwp->y_cursor += font_height;
+ cwp->x_cursor = x;
+
+ update_cursor ();
+
+ }
+
+ for (j = 0; j < zargs[1]; j++) {
+
+ zbyte c;
+
+ LOW_BYTE (addr, c)
+ addr++;
+
+ print_char (c);
+
+ }
+
+ addr += zargs[3];
+
+ }
+
+}/* z_print_table */
+
+/*
+ * z_put_wind_prop, set the value of a window property.
+ *
+ * zargs[0] = window (-3 is the current one)
+ * zargs[1] = number of window property to set
+ * zargs[2] = value to set window property to
+ *
+ */
+
+void z_put_wind_prop (void)
+{
+
+ flush_buffer ();
+
+ if (zargs[1] >= 16)
+ runtime_error (ERR_ILL_WIN_PROP);
+
+ ((zword *) (wp + winarg0 ())) [zargs[1]] = zargs[2];
+
+}/* z_put_wind_prop */
+
+/*
+ * z_scroll_window, scroll a window up or down.
+ *
+ * zargs[0] = window (-3 is the current one)
+ * zargs[1] = #screen units to scroll up (positive) or down (negative)
+ *
+ */
+
+void z_scroll_window (void)
+{
+ zword win = winarg0 ();
+ zword y, x;
+
+ flush_buffer ();
+
+ /* Use the correct set of colours when scrolling the window */
+
+ if (win != cwin && h_interpreter_number != INTERP_AMIGA)
+ os_set_colour (lo (wp[win].colour), hi (wp[win].colour));
+
+ y = wp[win].y_pos;
+ x = wp[win].x_pos;
+
+ os_scroll_area (y,
+ x,
+ y + wp[win].y_size - 1,
+ x + wp[win].x_size - 1,
+ (short) zargs[1]);
+
+ if (win != cwin && h_interpreter_number != INTERP_AMIGA)
+ os_set_colour (lo (cwp->colour), hi (cwp->colour));
+
+}/* z_scroll_window */
+
+/*
+ * z_set_colour, set the foreground and background colours.
+ *
+ * zargs[0] = foreground colour
+ * zargs[1] = background colour
+ * zargs[2] = window (-3 is the current one, optional)
+ *
+ */
+
+void z_set_colour (void)
+{
+ zword win = (h_version == V6) ? winarg2 () : 0;
+
+ zword fg = zargs[0];
+ zword bg = zargs[1];
+
+ flush_buffer ();
+
+ if ((short) fg == -1) /* colour -1 is the colour at the cursor */
+ fg = os_peek_colour ();
+ if ((short) bg == -1)
+ bg = os_peek_colour ();
+
+ if (fg == 0) /* colour 0 means keep current colour */
+ fg = lo (wp[win].colour);
+ if (bg == 0)
+ bg = hi (wp[win].colour);
+
+ if (fg == 1) /* colour 1 is the system default colour */
+ fg = h_default_foreground;
+ if (bg == 1)
+ bg = h_default_background;
+
+ if (h_version == V6 && h_interpreter_number == INTERP_AMIGA)
+
+ /* Changing colours of window 0 affects the entire screen */
+
+ if (win == 0) {
+
+ int i;
+
+ for (i = 1; i < 8; i++) {
+
+ zword bg2 = hi (wp[i].colour);
+ zword fg2 = lo (wp[i].colour);
+
+ if (bg2 < 16)
+ bg2 = (bg2 == lo (wp[0].colour)) ? fg : bg;
+ if (fg2 < 16)
+ fg2 = (fg2 == lo (wp[0].colour)) ? fg : bg;
+
+ wp[i].colour = (bg2 << 8) | fg2;
+
+ }
+
+ }
+
+ wp[win].colour = (bg << 8) | fg;
+
+ if (win == cwin || h_version != V6)
+ os_set_colour (fg, bg);
+
+}/* z_set_colour */
+
+/*
+ * z_set_font, set the font for text output and store the previous font.
+ *
+ * zargs[0] = number of font or 0 to keep current font
+ *
+ */
+
+void z_set_font (void)
+{
+ zword win = (h_version == V6) ? cwin : 0;
+ zword font = zargs[0];
+
+ if (font != 0) {
+
+ if (os_font_data (font, &font_height, &font_width)) {
+
+ store (wp[win].font);
+
+ wp[win].font = font;
+ wp[win].font_size = (font_height << 8) | font_width;
+
+ if (!ostream_memory && ostream_screen && enable_buffering) {
+
+ print_char (ZC_NEW_FONT);
+ print_char (font);
+
+ } else os_set_font (font);
+
+ } else store (0);
+
+ } else store (wp[win].font);
+
+}/* z_set_font */
+
+/*
+ * z_set_cursor, set the cursor position or turn the cursor on/off.
+ *
+ * zargs[0] = y-coordinate or -2/-1 for cursor on/off
+ * zargs[1] = x-coordinate
+ * zargs[2] = window (-3 is the current one, optional)
+ *
+ */
+
+void z_set_cursor (void)
+{
+ zword win = (h_version == V6) ? winarg2 () : 1;
+
+ zword y = zargs[0];
+ zword x = zargs[1];
+
+ flush_buffer ();
+
+ /* Supply default arguments */
+
+ if (zargc < 3)
+ zargs[2] = -3;
+
+ /* Handle cursor on/off */
+
+ if ((short) y < 0) {
+
+ if ((short) y == -2)
+ cursor = TRUE;
+ if ((short) y == -1)
+ cursor = FALSE;
+
+ return;
+
+ }
+
+ /* Convert grid positions to screen units if this is not V6 */
+
+ if (h_version != V6) {
+
+ if (cwin == 0)
+ return;
+
+ y = (y - 1) * h_font_height + 1;
+ x = (x - 1) * h_font_width + 1;
+
+ }
+
+ /* Protect the margins */
+
+ if (y == 0) /* use cursor line if y-coordinate is 0 */
+ y = wp[win].y_cursor;
+ if (x == 0) /* use cursor column if x-coordinate is 0 */
+ x = wp[win].x_cursor;
+ if (x <= wp[win].left || x > wp[win].x_size - wp[win].right)
+ x = wp[win].left + 1;
+
+ /* Move the cursor */
+
+ wp[win].y_cursor = y;
+ wp[win].x_cursor = x;
+
+ if (win == cwin)
+ update_cursor ();
+
+}/* z_set_cursor */
+
+/*
+ * z_set_margins, set the left and right margins of a window.
+ *
+ * zargs[0] = left margin in pixels
+ * zargs[1] = right margin in pixels
+ * zargs[2] = window (-3 is the current one, optional)
+ *
+ */
+
+void z_set_margins (void)
+{
+ zword win = winarg2 ();
+
+ flush_buffer ();
+
+ wp[win].left = zargs[0];
+ wp[win].right = zargs[1];
+
+ /* Protect the margins */
+
+ if (wp[win].x_cursor <= zargs[0] || wp[win].x_cursor > wp[win].x_size - zargs[1]) {
+
+ wp[win].x_cursor = zargs[0] + 1;
+
+ if (win == cwin)
+ update_cursor ();
+
+ }
+
+}/* z_set_margins */
+
+/*
+ * z_set_text_style, set the style for text output.
+ *
+ * zargs[0] = style flags to set or 0 to reset text style
+ *
+ */
+
+void z_set_text_style (void)
+{
+ zword win = (h_version == V6) ? cwin : 0;
+ zword style = zargs[0];
+
+ wp[win].style |= style;
+
+ if (style == 0)
+ wp[win].style = 0;
+
+ refresh_text_style ();
+
+}/* z_set_text_style */
+
+/*
+ * z_set_window, select the current window.
+ *
+ * zargs[0] = window to be selected (-3 is the current one)
+ *
+ */
+
+void z_set_window (void)
+{
+
+ set_window (winarg0 ());
+
+}/* z_set_window */
+
+/*
+ * pad_status_line
+ *
+ * Pad the status line with spaces up to the given position.
+ *
+ */
+
+static void pad_status_line (int column)
+{
+ int spaces;
+
+ flush_buffer ();
+
+ spaces = units_left () / os_char_width (' ') - column;
+
+ /* while (spaces--) */
+ /* Justin Wesley's fix for narrow displays (Agenda PDA) */
+ while (spaces-- > 0)
+ screen_char (' ');
+
+}/* pad_status_line */
+
+/*
+ * z_show_status, display the status line for V1 to V3 games.
+ *
+ * no zargs used
+ *
+ */
+
+void z_show_status (void)
+{
+ zword global0;
+ zword global1;
+ zword global2;
+ zword addr;
+
+ bool brief = FALSE;
+
+ /* One V5 game (Wishbringer Solid Gold) contains this opcode by
+ accident, so just return if the version number does not fit */
+
+ if (h_version >= V4)
+ return;
+
+ /* Read all relevant global variables from the memory of the
+ Z-machine into local variables */
+
+ addr = h_globals;
+ LOW_WORD (addr, global0)
+ addr += 2;
+ LOW_WORD (addr, global1)
+ addr += 2;
+ LOW_WORD (addr, global2)
+
+ /* Frotz uses window 7 for the status line. Don't forget to select
+ reverse and fixed width text style */
+
+ set_window (7);
+
+ print_char (ZC_NEW_STYLE);
+ print_char (REVERSE_STYLE | FIXED_WIDTH_STYLE);
+
+ /* If the screen width is below 55 characters then we have to use
+ the brief status line format */
+
+ if (h_screen_cols < 55)
+ brief = TRUE;
+
+ /* Print the object description for the global variable 0 */
+
+ print_char (' ');
+ print_object (global0);
+
+ /* A header flag tells us whether we have to display the current
+ time or the score/moves information */
+
+ if (h_config & CONFIG_TIME) { /* print hours and minutes */
+
+ zword hours = (global1 + 11) % 12 + 1;
+
+ pad_status_line (brief ? 15 : 20);
+
+ print_string ("Time: ");
+
+ if (hours < 10)
+ print_char (' ');
+ print_num (hours);
+
+ print_char (':');
+
+ if (global2 < 10)
+ print_char ('0');
+ print_num (global2);
+
+ print_char (' ');
+
+ print_char ((global1 >= 12) ? 'p' : 'a');
+ print_char ('m');
+
+ } else { /* print score and moves */
+
+ pad_status_line (brief ? 15 : 30);
+
+ print_string (brief ? "S: " : "Score: ");
+ print_num (global1);
+
+ pad_status_line (brief ? 8 : 14);
+
+ print_string (brief ? "M: " : "Moves: ");
+ print_num (global2);
+
+ }
+
+ /* Pad the end of the status line with spaces */
+
+ pad_status_line (0);
+
+ /* Return to the lower window */
+
+ set_window (0);
+
+}/* z_show_status */
+
+/*
+ * z_split_window, split the screen into an upper (1) and lower (0) window.
+ *
+ * zargs[0] = height of upper window in screen units (V6) or #lines
+ *
+ */
+
+void z_split_window (void)
+{
+
+ split_window (zargs[0]);
+
+}/* z_split_window */
+
+/*
+ * z_window_size, change the width and height of a window.
+ *
+ * zargs[0] = window (-3 is the current one)
+ * zargs[1] = new height in screen units
+ * zargs[2] = new width in screen units
+ *
+ */
+
+void z_window_size (void)
+{
+ zword win = winarg0 ();
+
+ flush_buffer ();
+
+ wp[win].y_size = zargs[1];
+ wp[win].x_size = zargs[2];
+
+ /* Keep the cursor within the window */
+
+ if (wp[win].y_cursor > zargs[1] || wp[win].x_cursor > zargs[2])
+ reset_cursor (win);
+
+}/* z_window_size */
+
+/*
+ * z_window_style, set / clear / toggle window attributes.
+ *
+ * zargs[0] = window (-3 is the current one)
+ * zargs[1] = window attribute flags
+ * zargs[2] = operation to perform (optional, defaults to 0)
+ *
+ */
+
+void z_window_style (void)
+{
+ zword win = winarg0 ();
+ zword flags = zargs[1];
+
+ flush_buffer ();
+
+ /* Supply default arguments */
+
+ if (zargc < 3)
+ zargs[2] = 0;
+
+ /* Set window style */
+
+ switch (zargs[2]) {
+ case 0: wp[win].attribute = flags; break;
+ case 1: wp[win].attribute |= flags; break;
+ case 2: wp[win].attribute &= ~flags; break;
+ case 3: wp[win].attribute ^= flags; break;
+ }
+
+ if (cwin == win)
+ update_attributes ();
+
+}/* z_window_style */
diff --git a/apps/plugins/frotz/setup.h b/apps/plugins/frotz/setup.h
new file mode 100644
index 0000000000..a9b9360122
--- /dev/null
+++ b/apps/plugins/frotz/setup.h
@@ -0,0 +1,67 @@
+/*
+ * Various status thingies for the interpreter and interface.
+ *
+ */
+
+typedef struct frotz_setup_struct {
+ int attribute_assignment; /* done */
+ int attribute_testing; /* done */
+ int context_lines; /* done */
+ int object_locating; /* done */
+ int object_movement; /* done */
+ int left_margin; /* done */
+ int right_margin; /* done */
+ int ignore_errors; /* done */
+ int interpreter_number; /* Just dumb frotz now */
+ int piracy; /* done */
+ int undo_slots; /* done */
+ int expand_abbreviations; /* done */
+ int script_cols; /* done */
+ int save_quetzal; /* done */
+ int sound; /* done */
+ int err_report_mode; /* done */
+} f_setup_t;
+
+extern f_setup_t f_setup;
+
+
+typedef struct zcode_header_struct {
+ zbyte h_version;
+ zbyte h_config;
+ zword h_release;
+ zword h_resident_size;
+ zword h_start_pc;
+ zword h_dictionary;
+ zword h_objects;
+ zword h_globals;
+ zword h_dynamic_size;
+ zword h_flags;
+ zbyte h_serial[6];
+ zword h_abbreviations;
+ zword h_file_size;
+ zword h_checksum;
+ zbyte h_interpreter_number;
+ zbyte h_interpreter_version;
+ zbyte h_screen_rows;
+ zbyte h_screen_cols;
+ zword h_screen_width;
+ zword h_screen_height;
+ zbyte h_font_height;
+ zbyte h_font_width;
+ zword h_functions_offset;
+ zword h_strings_offset;
+ zbyte h_default_background;
+ zbyte h_default_foreground;
+ zword h_terminating_keys;
+ zword h_line_width;
+ zbyte h_standard_high;
+ zbyte h_standard_low;
+ zword h_alphabet;
+ zword h_extension_table;
+ zbyte h_user_name[8];
+
+ zword hx_table_size;
+ zword hx_mouse_x;
+ zword hx_mouse_y;
+ zword hx_unicode_table;
+} z_header_t;
diff --git a/apps/plugins/frotz/sound.c b/apps/plugins/frotz/sound.c
new file mode 100644
index 0000000000..ea0e3570e6
--- /dev/null
+++ b/apps/plugins/frotz/sound.c
@@ -0,0 +1,204 @@
+/* sound.c - Sound effect function
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+#ifdef DJGPP
+#include "djfrotz.h"
+#endif
+
+#define EFFECT_PREPARE 1
+#define EFFECT_PLAY 2
+#define EFFECT_STOP 3
+#define EFFECT_FINISH_WITH 4
+
+extern int direct_call (zword);
+
+static zword routine = 0;
+
+static int next_sample = 0;
+static int next_volume = 0;
+
+static bool locked = FALSE;
+static bool playing = FALSE;
+
+/*
+ * init_sound
+ *
+ * Initialize sound variables.
+ *
+ */
+
+void init_sound (void)
+{
+ locked = FALSE;
+ playing = FALSE;
+} /* init_sound */
+
+
+/*
+ * start_sample
+ *
+ * Call the IO interface to play a sample.
+ *
+ */
+
+static void start_sample (int number, int volume, int repeats, zword eos)
+{
+
+ static zbyte lh_repeats[] = {
+ 0x00, 0x00, 0x00, 0x01, 0xff,
+ 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0xff, 0x01, 0x01, 0xff, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ if (story_id == LURKING_HORROR)
+ repeats = lh_repeats[number];
+
+ os_start_sample (number, volume, repeats, eos);
+
+ routine = eos;
+ playing = TRUE;
+
+}/* start_sample */
+
+/*
+ * start_next_sample
+ *
+ * Play a sample that has been delayed until the previous sound effect has
+ * finished. This is necessary for two samples in The Lurking Horror that
+ * immediately follow other samples.
+ *
+ */
+
+static void start_next_sample (void)
+{
+
+ if (next_sample != 0)
+ start_sample (next_sample, next_volume, 0, 0);
+
+ next_sample = 0;
+ next_volume = 0;
+
+}/* start_next_sample */
+
+/*
+ * end_of_sound
+ *
+ * Call the Z-code routine which was given as the last parameter of
+ * a sound_effect call. This function may be called from a hardware
+ * interrupt (which requires extremely careful programming).
+ *
+ */
+
+void end_of_sound (void)
+{
+
+#if defined(DJGPP) && defined(SOUND_SUPPORT)
+ end_of_sound_flag = 0;
+#endif
+
+ playing = FALSE;
+
+ if (!locked) {
+
+ if (story_id == LURKING_HORROR)
+ start_next_sample ();
+
+ direct_call (routine);
+
+ }
+
+}/* end_of_sound */
+
+/*
+ * z_sound_effect, load / play / stop / discard a sound effect.
+ *
+ * zargs[0] = number of bleep (1 or 2) or sample
+ * zargs[1] = operation to perform (samples only)
+ * zargs[2] = repeats and volume (play sample only)
+ * zargs[3] = end-of-sound routine (play sample only, optional)
+ *
+ * Note: Volumes range from 1 to 8, volume 255 is the default volume.
+ * Repeats are stored in the high byte, 255 is infinite loop.
+ *
+ */
+
+void z_sound_effect (void)
+{
+ zword number = zargs[0];
+ zword effect = zargs[1];
+ zword volume = zargs[2];
+
+ /* By default play sound 1 at volume 8 */
+ if (zargc < 1)
+ number = 1;
+ if (zargc < 2)
+ effect = EFFECT_PLAY;
+ if (zargc < 3)
+ volume = 8;
+
+ if (number >= 3 || number == 0) {
+
+ locked = TRUE;
+
+ if (story_id == LURKING_HORROR && (number == 9 || number == 16)) {
+
+ if (effect == EFFECT_PLAY) {
+
+ next_sample = number;
+ next_volume = volume;
+
+ locked = FALSE;
+
+ if (!playing)
+ start_next_sample ();
+
+ } else locked = FALSE;
+
+ return;
+
+ }
+
+ playing = FALSE;
+
+ switch (effect) {
+
+ case EFFECT_PREPARE:
+ os_prepare_sample (number);
+ break;
+ case EFFECT_PLAY:
+ start_sample (number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
+ break;
+ case EFFECT_STOP:
+ os_stop_sample (number);
+ break;
+ case EFFECT_FINISH_WITH:
+ os_finish_with_sample (number);
+ break;
+
+ }
+
+ locked = FALSE;
+
+ } else os_beep (number);
+
+}/* z_sound_effect */
diff --git a/apps/plugins/frotz/stream.c b/apps/plugins/frotz/stream.c
new file mode 100644
index 0000000000..4ccb44451c
--- /dev/null
+++ b/apps/plugins/frotz/stream.c
@@ -0,0 +1,365 @@
+/* stream.c - IO stream implementation
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+extern bool handle_hot_key (zchar);
+
+extern bool validate_click (void);
+
+extern void replay_open (void);
+extern void replay_close (void);
+extern void memory_open (zword, zword, bool);
+extern void memory_close (void);
+extern void record_open (void);
+extern void record_close (void);
+extern void script_open (void);
+extern void script_close (void);
+
+extern void memory_word (const zchar *);
+extern void memory_new_line (void);
+extern void record_write_key (zchar);
+extern void record_write_input (const zchar *, zchar);
+extern void script_char (zchar);
+extern void script_word (const zchar *);
+extern void script_new_line (void);
+extern void script_write_input (const zchar *, zchar);
+extern void script_erase_input (const zchar *);
+extern void script_mssg_on (void);
+extern void script_mssg_off (void);
+extern void screen_char (zchar);
+extern void screen_word (const zchar *);
+extern void screen_new_line (void);
+extern void screen_write_input (const zchar *, zchar);
+extern void screen_erase_input (const zchar *);
+extern void screen_mssg_on (void);
+extern void screen_mssg_off (void);
+
+extern zchar replay_read_key (void);
+extern zchar replay_read_input (zchar *);
+extern zchar console_read_key (zword);
+extern zchar console_read_input (int, zchar *, zword, bool);
+
+extern int direct_call (zword);
+
+/*
+ * stream_mssg_on
+ *
+ * Start printing a "debugging" message.
+ *
+ */
+
+void stream_mssg_on (void)
+{
+
+ flush_buffer ();
+
+ if (ostream_screen)
+ screen_mssg_on ();
+ if (ostream_script && enable_scripting)
+ script_mssg_on ();
+
+ message = TRUE;
+
+}/* stream_mssg_on */
+
+/*
+ * stream_mssg_off
+ *
+ * Stop printing a "debugging" message.
+ *
+ */
+
+void stream_mssg_off (void)
+{
+
+ flush_buffer ();
+
+ if (ostream_screen)
+ screen_mssg_off ();
+ if (ostream_script && enable_scripting)
+ script_mssg_off ();
+
+ message = FALSE;
+
+}/* stream_mssg_off */
+
+/*
+ * z_output_stream, open or close an output stream.
+ *
+ * zargs[0] = stream to open (positive) or close (negative)
+ * zargs[1] = address to redirect output to (stream 3 only)
+ * zargs[2] = width of redirected output (stream 3 only, optional)
+ *
+ */
+
+void z_output_stream (void)
+{
+
+ flush_buffer ();
+
+ switch ((short) zargs[0]) {
+
+ case 1: ostream_screen = TRUE;
+ break;
+ case -1: ostream_screen = FALSE;
+ break;
+ case 2: if (!ostream_script) script_open ();
+ break;
+ case -2: if (ostream_script) script_close ();
+ break;
+ case 3: memory_open (zargs[1], zargs[2], zargc >= 3);
+ break;
+ case -3: memory_close ();
+ break;
+ case 4: if (!ostream_record) record_open ();
+ break;
+ case -4: if (ostream_record) record_close ();
+ break;
+
+ }
+
+}/* z_output_stream */
+
+/*
+ * stream_char
+ *
+ * Send a single character to the output stream.
+ *
+ */
+
+void stream_char (zchar c)
+{
+
+ if (ostream_screen)
+ screen_char (c);
+ if (ostream_script && enable_scripting)
+ script_char (c);
+
+}/* stream_char */
+
+/*
+ * stream_word
+ *
+ * Send a string of characters to the output streams.
+ *
+ */
+
+void stream_word (const zchar *s)
+{
+
+ if (ostream_memory && !message)
+
+ memory_word (s);
+
+ else {
+
+ if (ostream_screen)
+ screen_word (s);
+ if (ostream_script && enable_scripting)
+ script_word (s);
+
+ }
+
+}/* stream_word */
+
+/*
+ * stream_new_line
+ *
+ * Send a newline to the output streams.
+ *
+ */
+
+void stream_new_line (void)
+{
+
+ if (ostream_memory && !message)
+
+ memory_new_line ();
+
+ else {
+
+ if (ostream_screen)
+ screen_new_line ();
+ if (ostream_script && enable_scripting)
+ script_new_line ();
+
+ }
+
+}/* stream_new_line */
+
+/*
+ * z_input_stream, select an input stream.
+ *
+ * zargs[0] = input stream to be selected
+ *
+ */
+
+void z_input_stream (void)
+{
+
+ flush_buffer ();
+
+ if (zargs[0] == 0 && istream_replay)
+ replay_close ();
+ if (zargs[0] == 1 && !istream_replay)
+ replay_open ();
+
+}/* z_input_stream */
+
+/*
+ * stream_read_key
+ *
+ * Read a single keystroke from the current input stream.
+ *
+ */
+
+zchar stream_read_key ( zword timeout, zword routine,
+ bool hot_keys )
+{
+ zchar key = ZC_BAD;
+
+ flush_buffer ();
+
+ /* Read key from current input stream */
+
+continue_input:
+
+ do {
+
+ if (istream_replay)
+ key = replay_read_key ();
+ else
+ key = console_read_key (timeout);
+
+ } while (key == ZC_BAD);
+
+ /* Verify mouse clicks */
+
+ if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK)
+ if (!validate_click ())
+ goto continue_input;
+
+ /* Copy key to the command file */
+
+ if (ostream_record && !istream_replay)
+ record_write_key (key);
+
+ /* Handle timeouts */
+
+ if (key == ZC_TIME_OUT)
+ if (direct_call (routine) == 0)
+ goto continue_input;
+
+ /* Handle hot keys */
+
+ if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) {
+
+ if (h_version == V4 && key == ZC_HKEY_UNDO)
+ goto continue_input;
+ if (!handle_hot_key (key))
+ goto continue_input;
+
+ return ZC_BAD;
+
+ }
+
+ /* Return key */
+
+ return key;
+
+}/* stream_read_key */
+
+/*
+ * stream_read_input
+ *
+ * Read a line of input from the current input stream.
+ *
+ */
+
+zchar stream_read_input ( int max, zchar *buf,
+ zword timeout, zword routine,
+ bool hot_keys,
+ bool no_scripting )
+{
+ zchar key = ZC_BAD;
+
+ flush_buffer ();
+
+ /* Remove initial input from the transscript file or from the screen */
+
+ if (ostream_script && enable_scripting && !no_scripting)
+ script_erase_input (buf);
+ if (istream_replay)
+ screen_erase_input (buf);
+
+ /* Read input line from current input stream */
+
+continue_input:
+
+ do {
+
+ if (istream_replay)
+ key = replay_read_input (buf);
+ else
+ key = console_read_input (max, buf, timeout, key != ZC_BAD);
+
+ } while (key == ZC_BAD);
+
+ /* Verify mouse clicks */
+
+ if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK)
+ if (!validate_click ())
+ goto continue_input;
+
+ /* Copy input line to the command file */
+
+ if (ostream_record && !istream_replay)
+ record_write_input (buf, key);
+
+ /* Handle timeouts */
+
+ if (key == ZC_TIME_OUT)
+ if (direct_call (routine) == 0)
+ goto continue_input;
+
+ /* Handle hot keys */
+
+ if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) {
+
+ if (!handle_hot_key (key))
+ goto continue_input;
+
+ return ZC_BAD;
+
+ }
+
+ /* Copy input line to transscript file or to the screen */
+
+ if (ostream_script && enable_scripting && !no_scripting)
+ script_write_input (buf, key);
+ if (istream_replay)
+ screen_write_input (buf, key);
+
+ /* Return terminating key */
+
+ return key;
+
+}/* stream_read_input */
diff --git a/apps/plugins/frotz/table.c b/apps/plugins/frotz/table.c
new file mode 100644
index 0000000000..eb3a16366d
--- /dev/null
+++ b/apps/plugins/frotz/table.c
@@ -0,0 +1,193 @@
+/* table.c - Table handling opcodes
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+/*
+ * z_copy_table, copy a table or fill it with zeroes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = destination address or 0 for fill
+ * zargs[2] = size of table
+ *
+ * Note: Copying is safe even when source and destination overlap; but
+ * if zargs[1] is negative the table _must_ be copied forwards.
+ *
+ */
+
+void z_copy_table (void)
+{
+ zword addr;
+ zword size = zargs[2];
+ zbyte value;
+ int i;
+
+ if (zargs[1] == 0) /* zero table */
+
+ for (i = 0; i < size; i++)
+ storeb ((zword) (zargs[0] + i), 0);
+
+ else if ((short) size < 0 || zargs[0] > zargs[1]) /* copy forwards */
+
+ for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
+ addr = zargs[0] + i;
+ LOW_BYTE (addr, value)
+ storeb ((zword) (zargs[1] + i), value);
+ }
+
+ else /* copy backwards */
+
+ for (i = size - 1; i >= 0; i--) {
+ addr = zargs[0] + i;
+ LOW_BYTE (addr, value)
+ storeb ((zword) (zargs[1] + i), value);
+ }
+
+}/* z_copy_table */
+
+/*
+ * z_loadb, store a value from a table of bytes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry to store
+ *
+ */
+
+void z_loadb (void)
+{
+ zword addr = zargs[0] + zargs[1];
+ zbyte value;
+
+ LOW_BYTE (addr, value)
+
+ store (value);
+
+}/* z_loadb */
+
+/*
+ * z_loadw, store a value from a table of words.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry to store
+ *
+ */
+
+void z_loadw (void)
+{
+ zword addr = zargs[0] + 2 * zargs[1];
+ zword value;
+
+ LOW_WORD (addr, value)
+
+ store (value);
+
+}/* z_loadw */
+
+/*
+ * z_scan_table, find and store the address of a target within a table.
+ *
+ * zargs[0] = target value to be searched for
+ * zargs[1] = address of table
+ * zargs[2] = number of table entries to check value against
+ * zargs[3] = type of table (optional, defaults to 0x82)
+ *
+ * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
+ * it's a byte array. The lower bits hold the address step.
+ *
+ */
+
+void z_scan_table (void)
+{
+ zword addr = zargs[1];
+ int i;
+
+ /* Supply default arguments */
+
+ if (zargc < 4)
+ zargs[3] = 0x82;
+
+ /* Scan byte or word array */
+
+ for (i = 0; i < zargs[2]; i++) {
+
+ if (zargs[3] & 0x80) { /* scan word array */
+
+ zword wvalue;
+
+ LOW_WORD (addr, wvalue)
+
+ if (wvalue == zargs[0])
+ goto finished;
+
+ } else { /* scan byte array */
+
+ zbyte bvalue;
+
+ LOW_BYTE (addr, bvalue)
+
+ if (bvalue == zargs[0])
+ goto finished;
+
+ }
+
+ addr += zargs[3] & 0x7f;
+
+ }
+
+ addr = 0;
+
+finished:
+
+ store (addr);
+ branch (addr);
+
+}/* z_scan_table */
+
+/*
+ * z_storeb, write a byte into a table of bytes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry
+ * zargs[2] = value to be written
+ *
+ */
+
+void z_storeb (void)
+{
+
+ storeb ((zword) (zargs[0] + zargs[1]), zargs[2]);
+
+}/* z_storeb */
+
+/*
+ * z_storew, write a word into a table of words.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry
+ * zargs[2] = value to be written
+ *
+ */
+
+void z_storew (void)
+{
+
+ storew ((zword) (zargs[0] + 2 * zargs[1]), zargs[2]);
+
+}/* z_storew */
diff --git a/apps/plugins/frotz/text.c b/apps/plugins/frotz/text.c
new file mode 100644
index 0000000000..8145cfea29
--- /dev/null
+++ b/apps/plugins/frotz/text.c
@@ -0,0 +1,1109 @@
+/* text.c - Text manipulation functions
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+enum string_type {
+ LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
+};
+
+extern zword object_name (zword);
+
+static zchar decoded[10];
+static zword encoded[3];
+
+/*
+ * According to Matteo De Luigi <matteo.de.luigi@libero.it>,
+ * 0xab and 0xbb were in each other's proper positions.
+ * Sat Apr 21, 2001
+ */
+static zchar zscii_to_latin1[] = {
+ 0xe4, 0xf6, 0xfc, 0xc4, 0xd6, 0xdc, 0xdf, 0xbb,
+ 0xab, 0xeb, 0xef, 0xff, 0xcb, 0xcf, 0xe1, 0xe9,
+ 0xed, 0xf3, 0xfa, 0xfd, 0xc1, 0xc9, 0xcd, 0xd3,
+ 0xda, 0xdd, 0xe0, 0xe8, 0xec, 0xf2, 0xf9, 0xc0,
+ 0xc8, 0xcc, 0xd2, 0xd9, 0xe2, 0xea, 0xee, 0xf4,
+ 0xfb, 0xc2, 0xca, 0xce, 0xd4, 0xdb, 0xe5, 0xc5,
+ 0xf8, 0xd8, 0xe3, 0xf1, 0xf5, 0xc3, 0xd1, 0xd5,
+ 0xe6, 0xc6, 0xe7, 0xc7, 0xfe, 0xf0, 0xde, 0xd0,
+ 0xa3, 0x00, 0x00, 0xa1, 0xbf
+};
+
+/*
+ * translate_from_zscii
+ *
+ * Map a ZSCII character onto the ISO Latin-1 alphabet.
+ *
+ */
+
+zchar translate_from_zscii (zbyte c)
+{
+
+ if (c == 0xfc)
+ return ZC_MENU_CLICK;
+ if (c == 0xfd)
+ return ZC_DOUBLE_CLICK;
+ if (c == 0xfe)
+ return ZC_SINGLE_CLICK;
+
+ if (c >= 0x9b && story_id != BEYOND_ZORK) {
+
+ if (hx_unicode_table != 0) { /* game has its own Unicode table */
+
+ zbyte N;
+
+ LOW_BYTE (hx_unicode_table, N)
+
+ if (c - 0x9b < N) {
+
+ zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b);
+ zword unicode;
+
+ LOW_WORD (addr, unicode)
+
+ return (unicode < 0x100) ? (zchar) unicode : '?';
+
+ } else return '?';
+
+ } else /* game uses standard set */
+
+ if (c <= 0xdf) {
+
+ if (c == 0xdc || c == 0xdd) /* Oe and oe ligatures */
+ return '?'; /* are not ISO-Latin 1 */
+
+ return zscii_to_latin1[c - 0x9b];
+
+ } else return '?';
+ }
+
+ return c;
+
+}/* translate_from_zscii */
+
+/*
+ * translate_to_zscii
+ *
+ * Map an ISO Latin-1 character onto the ZSCII alphabet.
+ *
+ */
+
+zbyte translate_to_zscii (zchar c)
+{
+ int i;
+
+ if (c == ZC_SINGLE_CLICK)
+ return 0xfe;
+ if (c == ZC_DOUBLE_CLICK)
+ return 0xfd;
+ if (c == ZC_MENU_CLICK)
+ return 0xfc;
+
+ if (c >= ZC_LATIN1_MIN) {
+
+ if (hx_unicode_table != 0) { /* game has its own Unicode table */
+
+ zbyte N;
+ int i;
+
+ LOW_BYTE (hx_unicode_table, N)
+
+ for (i = 0x9b; i < 0x9b + N; i++) {
+
+ zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b);
+ zword unicode;
+
+ LOW_WORD (addr, unicode)
+
+ if (c == unicode)
+ return (zbyte) i;
+
+ }
+
+ return '?';
+
+ } else { /* game uses standard set */
+
+ for (i = 0x9b; i <= 0xdf; i++)
+ if (c == zscii_to_latin1[i - 0x9b])
+ return (zbyte) i;
+
+ return '?';
+
+ }
+ }
+
+ if (c == 0) /* Safety thing from David Kinder */
+ c = '?'; /* regarding his Unicode patches */
+ /* Sept 15, 2002 */
+
+ return c;
+
+}/* translate_to_zscii */
+
+/*
+ * alphabet
+ *
+ * Return a character from one of the three character sets.
+ *
+ */
+
+static zchar alphabet (int set, int index)
+{
+
+ if (h_alphabet != 0) { /* game uses its own alphabet */
+
+ zbyte c;
+
+ zword addr = h_alphabet + 26 * set + index;
+ LOW_BYTE (addr, c)
+
+ return translate_from_zscii (c);
+
+ } else /* game uses default alphabet */
+
+ if (set == 0)
+ return 'a' + index;
+ else if (set == 1)
+ return 'A' + index;
+ else if (h_version == V1)
+ return " 0123456789.,!?_#'\"/\\<-:()"[index];
+ else
+ return " ^0123456789.,!?_#'\"/\\-:()"[index];
+
+}/* alphabet */
+
+/*
+ * load_string
+ *
+ * Copy a ZSCII string from the memory to the global "decoded" string.
+ *
+ */
+
+static void load_string (zword addr, zword length)
+{
+ int resolution = (h_version <= V3) ? 2 : 3;
+ int i = 0;
+
+ while (i < 3 * resolution)
+
+ if (i < length) {
+
+ zbyte c;
+
+ LOW_BYTE (addr, c)
+ addr++;
+
+ decoded[i++] = translate_from_zscii (c);
+
+ } else decoded[i++] = 0;
+
+}/* load_string */
+
+/*
+ * encode_text
+ *
+ * Encode the Unicode text in the global "decoded" string then write
+ * the result to the global "encoded" array. (This is used to look up
+ * words in the dictionary.) Up to V3 the vocabulary resolution is
+ * two, since V4 it is three words. Because each word contains three
+ * Z-characters, that makes six or nine Z-characters respectively.
+ * Longer words are chopped to the proper size, shorter words are are
+ * padded out with 5's. For word completion we pad with 0s and 31s,
+ * the minimum and maximum Z-characters.
+ *
+ */
+
+static void encode_text (int padding)
+{
+ static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0 };
+ static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0 };
+ static zchar wait[] = { 'w', 'a', 'i', 't', 0 };
+
+ zbyte zchars[12];
+ const zchar *ptr = decoded;
+ zchar c;
+ int resolution = (h_version <= V3) ? 2 : 3;
+ int i = 0;
+
+ /* Expand abbreviations that some old Infocom games lack */
+
+ if (f_setup.expand_abbreviations)
+
+ if (padding == 0x05 && decoded[1] == 0)
+
+ switch (decoded[0]) {
+ case 'g': ptr = again; break;
+ case 'x': ptr = examine; break;
+ case 'z': ptr = wait; break;
+ }
+
+ /* Translate string to a sequence of Z-characters */
+
+ while (i < 3 * resolution)
+
+ if ((c = *ptr++) != 0) {
+
+ int index, set;
+ zbyte c2;
+
+ /* Search character in the alphabet */
+
+ for (set = 0; set < 3; set++)
+ for (index = 0; index < 26; index++)
+ if (c == alphabet (set, index))
+ goto letter_found;
+
+ /* Character not found, store its ZSCII value */
+
+ c2 = translate_to_zscii (c);
+
+ zchars[i++] = 5;
+ zchars[i++] = 6;
+ zchars[i++] = c2 >> 5;
+ zchars[i++] = c2 & 0x1f;
+
+ continue;
+
+ letter_found:
+
+ /* Character found, store its index */
+
+ if (set != 0)
+ zchars[i++] = ((h_version <= V2) ? 1 : 3) + set;
+
+ zchars[i++] = index + 6;
+
+ } else zchars[i++] = padding;
+
+ /* Three Z-characters make a 16bit word */
+
+ for (i = 0; i < resolution; i++)
+
+ encoded[i] =
+ (zchars[3 * i + 0] << 10) |
+ (zchars[3 * i + 1] << 5) |
+ (zchars[3 * i + 2]);
+
+ encoded[resolution - 1] |= 0x8000;
+
+}/* encode_text */
+
+/*
+ * z_check_unicode, test if a unicode character can be read and printed.
+ *
+ * zargs[0] = Unicode
+ *
+ */
+
+void z_check_unicode (void)
+{
+ zword c = zargs[0];
+
+ if (c >= 0x20 && c <= 0x7e)
+ store (3);
+ else if (c == 0xa0)
+ store (1);
+ else if (c >= 0xa1 && c <= 0xff)
+ store (3);
+ else
+ store (0);
+
+}/* z_check_unicode */
+
+/*
+ * z_encode_text, encode a ZSCII string for use in a dictionary.
+ *
+ * zargs[0] = address of text buffer
+ * zargs[1] = length of ASCII string
+ * zargs[2] = offset of ASCII string within the text buffer
+ * zargs[3] = address to store encoded text in
+ *
+ * This is a V5+ opcode and therefore the dictionary resolution must be
+ * three 16bit words.
+ *
+ */
+
+void z_encode_text (void)
+{
+ int i;
+
+ load_string ((zword) (zargs[0] + zargs[2]), zargs[1]);
+
+ encode_text (0x05);
+
+ for (i = 0; i < 3; i++)
+ storew ((zword) (zargs[3] + 2 * i), encoded[i]);
+
+}/* z_encode_text */
+
+/*
+ * decode_text
+ *
+ * Convert encoded text to Unicode. The encoded text consists of 16bit
+ * words. Every word holds 3 Z-characters (5 bits each) plus a spare
+ * bit to mark the last word. The Z-characters translate to ZSCII by
+ * looking at the current current character set. Some select another
+ * character set, others refer to abbreviations.
+ *
+ * There are several different string types:
+ *
+ * LOW_STRING - from the lower 64KB (byte address)
+ * ABBREVIATION - from the abbreviations table (word address)
+ * HIGH_STRING - from the end of the memory map (packed address)
+ * EMBEDDED_STRING - from the instruction stream (at PC)
+ * VOCABULARY - from the dictionary (byte address)
+ *
+ * The last type is only used for word completion.
+ *
+ */
+
+#define outchar(c) if (st==VOCABULARY) *ptr++=c; else print_char(c)
+
+static void decode_text (enum string_type st, zword addr)
+{
+ zchar *ptr;
+ long byte_addr;
+ zchar c2;
+ zword code;
+ zbyte c, prev_c = 0;
+ int shift_state = 0;
+ int shift_lock = 0;
+ int status = 0;
+
+ ptr = NULL; /* makes compilers shut up */
+ byte_addr = 0;
+
+ /* Calculate the byte address if necessary */
+
+ if (st == ABBREVIATION)
+
+ byte_addr = (long) addr << 1;
+
+ else if (st == HIGH_STRING) {
+
+ if (h_version <= V3)
+ byte_addr = (long) addr << 1;
+ else if (h_version <= V5)
+ byte_addr = (long) addr << 2;
+ else if (h_version <= V7)
+ byte_addr = ((long) addr << 2) + ((long) h_strings_offset << 3);
+ else /* h_version == V8 */
+ byte_addr = (long) addr << 3;
+
+ if (byte_addr >= story_size)
+ runtime_error (ERR_ILL_PRINT_ADDR);
+
+ }
+
+ /* Loop until a 16bit word has the highest bit set */
+
+ if (st == VOCABULARY)
+ ptr = decoded;
+
+ do {
+
+ int i;
+
+ /* Fetch the next 16bit word */
+
+ if (st == LOW_STRING || st == VOCABULARY) {
+ LOW_WORD (addr, code)
+ addr += 2;
+ } else if (st == HIGH_STRING || st == ABBREVIATION) {
+ HIGH_WORD (byte_addr, code)
+ byte_addr += 2;
+ } else
+ CODE_WORD (code)
+
+ /* Read its three Z-characters */
+
+ for (i = 10; i >= 0; i -= 5) {
+
+ zword abbr_addr;
+ zword ptr_addr;
+
+ c = (code >> i) & 0x1f;
+
+ switch (status) {
+
+ case 0: /* normal operation */
+
+ if (shift_state == 2 && c == 6)
+ status = 2;
+
+ else if (h_version == V1 && c == 1)
+ new_line ();
+
+ else if (h_version >= V2 && shift_state == 2 && c == 7)
+ new_line ();
+
+ else if (c >= 6)
+ outchar (alphabet (shift_state, c - 6));
+
+ else if (c == 0)
+ outchar (' ');
+
+ else if (h_version >= V2 && c == 1)
+ status = 1;
+
+ else if (h_version >= V3 && c <= 3)
+ status = 1;
+
+ else {
+
+ shift_state = (shift_lock + (c & 1) + 1) % 3;
+
+ if (h_version <= V2 && c >= 4)
+ shift_lock = shift_state;
+
+ break;
+
+ }
+
+ shift_state = shift_lock;
+
+ break;
+
+ case 1: /* abbreviation */
+
+ ptr_addr = h_abbreviations + 64 * (prev_c - 1) + 2 * c;
+
+ LOW_WORD (ptr_addr, abbr_addr)
+ decode_text (ABBREVIATION, abbr_addr);
+
+ status = 0;
+ break;
+
+ case 2: /* ZSCII character - first part */
+
+ status = 3;
+ break;
+
+ case 3: /* ZSCII character - second part */
+
+ c2 = translate_from_zscii ((prev_c << 5) | c);
+ outchar (c2);
+
+ status = 0;
+ break;
+
+ }
+
+ prev_c = c;
+
+ }
+
+ } while (!(code & 0x8000));
+
+ if (st == VOCABULARY)
+ *ptr = 0;
+
+}/* decode_text */
+
+#undef outchar
+
+/*
+ * z_new_line, print a new line.
+ *
+ * no zargs used
+ *
+ */
+
+void z_new_line (void)
+{
+
+ new_line ();
+
+}/* z_new_line */
+
+/*
+ * z_print, print a string embedded in the instruction stream.
+ *
+ * no zargs used
+ *
+ */
+
+void z_print (void)
+{
+
+ decode_text (EMBEDDED_STRING, 0);
+
+}/* z_print */
+
+/*
+ * z_print_addr, print a string from the lower 64KB.
+ *
+ * zargs[0] = address of string to print
+ *
+ */
+
+void z_print_addr (void)
+{
+
+ decode_text (LOW_STRING, zargs[0]);
+
+}/* z_print_addr */
+
+/*
+ * z_print_char print a single ZSCII character.
+ *
+ * zargs[0] = ZSCII character to be printed
+ *
+ */
+
+void z_print_char (void)
+{
+
+ print_char (translate_from_zscii (zargs[0]));
+
+}/* z_print_char */
+
+/*
+ * z_print_form, print a formatted table.
+ *
+ * zargs[0] = address of formatted table to be printed
+ *
+ */
+
+void z_print_form (void)
+{
+ zword count;
+ zword addr = zargs[0];
+
+ bool first = TRUE;
+
+ for (;;) {
+
+ LOW_WORD (addr, count)
+ addr += 2;
+
+ if (count == 0)
+ break;
+
+ if (!first)
+ new_line ();
+
+ while (count--) {
+
+ zbyte c;
+
+ LOW_BYTE (addr, c)
+ addr++;
+
+ print_char (translate_from_zscii (c));
+
+ }
+
+ first = FALSE;
+
+ }
+
+}/* z_print_form */
+
+/*
+ * print_num
+ *
+ * Print a signed 16bit number.
+ *
+ */
+
+void print_num (zword value)
+{
+ int i;
+
+ /* Print sign */
+
+ if ((short) value < 0) {
+ print_char ('-');
+ value = - (short) value;
+ }
+
+ /* Print absolute value */
+
+ for (i = 10000; i != 0; i /= 10)
+ if (value >= i || i == 1)
+ print_char ('0' + (value / i) % 10);
+
+}/* print_num */
+
+/*
+ * z_print_num, print a signed number.
+ *
+ * zargs[0] = number to print
+ *
+ */
+
+void z_print_num (void)
+{
+
+ print_num (zargs[0]);
+
+}/* z_print_num */
+
+/*
+ * print_object
+ *
+ * Print an object description.
+ *
+ */
+
+void print_object (zword object)
+{
+ zword addr = object_name (object);
+ zword code = 0x94a5;
+ zbyte length;
+
+ LOW_BYTE (addr, length)
+ addr++;
+
+ if (length != 0)
+ LOW_WORD (addr, code)
+
+ if (code == 0x94a5) { /* encoded text 0x94a5 == empty string */
+
+ print_string ("object#"); /* supply a generic name */
+ print_num (object); /* for anonymous objects */
+
+ } else decode_text (LOW_STRING, addr);
+
+}/* print_object */
+
+/*
+ * z_print_obj, print an object description.
+ *
+ * zargs[0] = number of object to be printed
+ *
+ */
+
+void z_print_obj (void)
+{
+
+ print_object (zargs[0]);
+
+}/* z_print_obj */
+
+/*
+ * z_print_paddr, print the string at the given packed address.
+ *
+ * zargs[0] = packed address of string to be printed
+ *
+ */
+
+void z_print_paddr (void)
+{
+
+ decode_text (HIGH_STRING, zargs[0]);
+
+}/* z_print_paddr */
+
+/*
+ * z_print_ret, print the string at PC, print newline then return true.
+ *
+ * no zargs used
+ *
+ */
+
+void z_print_ret (void)
+{
+
+ decode_text (EMBEDDED_STRING, 0);
+ new_line ();
+ ret (1);
+
+}/* z_print_ret */
+
+/*
+ * print_string
+ *
+ * Print a string of ASCII characters.
+ *
+ */
+
+void print_string (const char *s)
+{
+ char c;
+
+ while ((c = *s++) != 0)
+
+ if (c == '\n')
+ new_line ();
+ else
+ print_char (c);
+
+}/* print_string */
+
+/*
+ * z_print_unicode
+ *
+ * zargs[0] = Unicode
+ *
+ */
+
+void z_print_unicode (void)
+{
+
+ print_char ((zargs[0] <= 0xff) ? zargs[0] : '?');
+
+}/* z_print_unicode */
+
+/*
+ * lookup_text
+ *
+ * Scan a dictionary searching for the given word. The first argument
+ * can be
+ *
+ * 0x00 - find the first word which is >= the given one
+ * 0x05 - find the word which exactly matches the given one
+ * 0x1f - find the last word which is <= the given one
+ *
+ * The return value is 0 if the search fails.
+ *
+ */
+
+static zword lookup_text (int padding, zword dct)
+{
+ zword entry_addr;
+ zword entry_count;
+ zword entry;
+ zword addr;
+ zbyte entry_len;
+ zbyte sep_count;
+ int resolution = (h_version <= V3) ? 2 : 3;
+ int entry_number;
+ int lower, upper;
+ int i;
+ bool sorted;
+
+ encode_text (padding);
+
+ LOW_BYTE (dct, sep_count) /* skip word separators */
+ dct += 1 + sep_count;
+ LOW_BYTE (dct, entry_len) /* get length of entries */
+ dct += 1;
+ LOW_WORD (dct, entry_count) /* get number of entries */
+ dct += 2;
+
+ if ((short) entry_count < 0) { /* bad luck, entries aren't sorted */
+
+ entry_count = - (short) entry_count;
+ sorted = FALSE;
+
+ } else sorted = TRUE; /* entries are sorted */
+
+ lower = 0;
+ upper = entry_count - 1;
+
+ while (lower <= upper) {
+
+ if (sorted) /* binary search */
+ entry_number = (lower + upper) / 2;
+ else /* linear search */
+ entry_number = lower;
+
+ entry_addr = dct + entry_number * entry_len;
+
+ /* Compare word to dictionary entry */
+
+ addr = entry_addr;
+
+ for (i = 0; i < resolution; i++) {
+ LOW_WORD (addr, entry)
+ if (encoded[i] != entry)
+ goto continuing;
+ addr += 2;
+ }
+
+ return entry_addr; /* exact match found, return now */
+
+ continuing:
+
+ if (sorted) /* binary search */
+
+ if (encoded[i] > entry)
+ lower = entry_number + 1;
+ else
+ upper = entry_number - 1;
+
+ else lower++; /* linear search */
+
+ }
+
+ /* No exact match has been found */
+
+ if (padding == 0x05)
+ return 0;
+
+ entry_number = (padding == 0x00) ? lower : upper;
+
+ if (entry_number == -1 || entry_number == entry_count)
+ return 0;
+
+ return dct + entry_number * entry_len;
+
+}/* lookup_text */
+
+/*
+ * tokenise_text
+ *
+ * Translate a single word to a token and append it to the token
+ * buffer. Every token consists of the address of the dictionary
+ * entry, the length of the word and the offset of the word from
+ * the start of the text buffer. Unknown words cause empty slots
+ * if the flag is set (such that the text can be scanned several
+ * times with different dictionaries); otherwise they are zero.
+ *
+ */
+
+static void tokenise_text (zword text, zword length, zword from, zword parse, zword dct, bool flag)
+{
+ zword addr;
+ zbyte token_max, token_count;
+
+ LOW_BYTE (parse, token_max)
+ parse++;
+ LOW_BYTE (parse, token_count)
+
+ if (token_count < token_max) { /* sufficient space left for token? */
+
+ storeb (parse++, token_count + 1);
+
+ load_string ((zword) (text + from), length);
+
+ addr = lookup_text (0x05, dct);
+
+ if (addr != 0 || !flag) {
+
+ parse += 4 * token_count;
+
+ storew ((zword) (parse + 0), addr);
+ storeb ((zword) (parse + 2), length);
+ storeb ((zword) (parse + 3), from);
+
+ }
+
+ }
+
+}/* tokenise_text */
+
+/*
+ * tokenise_line
+ *
+ * Split an input line into words and translate the words to tokens.
+ *
+ */
+
+void tokenise_line (zword text, zword token, zword dct, bool flag)
+{
+ zword addr1;
+ zword addr2;
+ zbyte length;
+ zbyte c;
+
+ length = 0; /* makes compilers shut up */
+
+ /* Use standard dictionary if the given dictionary is zero */
+
+ if (dct == 0)
+ dct = h_dictionary;
+
+ /* Remove all tokens before inserting new ones */
+
+ storeb ((zword) (token + 1), 0);
+
+ /* Move the first pointer across the text buffer searching for the
+ beginning of a word. If this succeeds, store the position in a
+ second pointer. Move the first pointer searching for the end of
+ the word. When it is found, "tokenise" the word. Continue until
+ the end of the buffer is reached. */
+
+ addr1 = text;
+ addr2 = 0;
+
+ if (h_version >= V5) {
+ addr1++;
+ LOW_BYTE (addr1, length)
+ }
+
+ do {
+
+ zword sep_addr;
+ zbyte sep_count;
+ zbyte separator;
+
+ /* Fetch next ZSCII character */
+
+ addr1++;
+
+ if (h_version >= V5 && addr1 == text + 2 + length)
+ c = 0;
+ else
+ LOW_BYTE (addr1, c)
+
+ /* Check for separator */
+
+ sep_addr = dct;
+
+ LOW_BYTE (sep_addr, sep_count)
+ sep_addr++;
+
+ do {
+
+ LOW_BYTE (sep_addr, separator)
+ sep_addr++;
+
+ } while (c != separator && --sep_count != 0);
+
+ /* This could be the start or the end of a word */
+
+ if (sep_count == 0 && c != ' ' && c != 0) {
+
+ if (addr2 == 0)
+ addr2 = addr1;
+
+ } else if (addr2 != 0) {
+
+ tokenise_text (
+ text,
+ (zword) (addr1 - addr2),
+ (zword) (addr2 - text),
+ token, dct, flag );
+
+ addr2 = 0;
+
+ }
+
+ /* Translate separator (which is a word in its own right) */
+
+ if (sep_count != 0)
+
+ tokenise_text (
+ text,
+ (zword) (1),
+ (zword) (addr1 - text),
+ token, dct, flag );
+
+ } while (c != 0);
+
+}/* tokenise_line */
+
+/*
+ * z_tokenise, make a lexical analysis of a ZSCII string.
+ *
+ * zargs[0] = address of string to analyze
+ * zargs[1] = address of token buffer
+ * zargs[2] = address of dictionary (optional)
+ * zargs[3] = set when unknown words cause empty slots (optional)
+ *
+ */
+
+void z_tokenise (void)
+{
+
+ /* Supply default arguments */
+
+ if (zargc < 3)
+ zargs[2] = 0;
+ if (zargc < 4)
+ zargs[3] = 0;
+
+ /* Call tokenise_line to do the real work */
+
+ tokenise_line (zargs[0], zargs[1], zargs[2], zargs[3] != 0);
+
+}/* z_tokenise */
+
+/*
+ * completion
+ *
+ * Scan the vocabulary to complete the last word on the input line
+ * (similar to "tcsh" under Unix). The return value is
+ *
+ * 2 ==> completion is impossible
+ * 1 ==> completion is ambiguous
+ * 0 ==> completion is successful
+ *
+ * The function also returns a string in its second argument. In case
+ * of 2, the string is empty; in case of 1, the string is the longest
+ * extension of the last word on the input line that is common to all
+ * possible completions (for instance, if the last word on the input
+ * is "fo" and its only possible completions are "follow" and "folly"
+ * then the string is "ll"); in case of 0, the string is an extension
+ * to the last word that results in the only possible completion.
+ *
+ */
+
+int completion (const zchar *buffer, zchar *result)
+{
+ zword minaddr;
+ zword maxaddr;
+ zchar *ptr;
+ zchar c;
+ int len;
+ int i;
+
+ *result = 0;
+
+ /* Copy last word to "decoded" string */
+
+ len = 0;
+
+ while ((c = *buffer++) != 0)
+
+ if (c != ' ') {
+
+ if (len < 9)
+ decoded[len++] = c;
+
+ } else len = 0;
+
+ decoded[len] = 0;
+
+ /* Search the dictionary for first and last possible extensions */
+
+ minaddr = lookup_text (0x00, h_dictionary);
+ maxaddr = lookup_text (0x1f, h_dictionary);
+
+ if (minaddr == 0 || maxaddr == 0 || minaddr > maxaddr)
+ return 2;
+
+ /* Copy first extension to "result" string */
+
+ decode_text (VOCABULARY, minaddr);
+
+ ptr = result;
+
+ for (i = len; (c = decoded[i]) != 0; i++)
+ *ptr++ = c;
+ *ptr = 0;
+
+ /* Merge second extension with "result" string */
+
+ decode_text (VOCABULARY, maxaddr);
+
+ for (i = len, ptr = result; (c = decoded[i]) != 0; i++, ptr++)
+ if (*ptr != c) break;
+ *ptr = 0;
+
+ /* Search was ambiguous or successful */
+
+ return (minaddr == maxaddr) ? 0 : 1;
+
+}/* completion */
diff --git a/apps/plugins/frotz/variable.c b/apps/plugins/frotz/variable.c
new file mode 100644
index 0000000000..3e5c6e0c09
--- /dev/null
+++ b/apps/plugins/frotz/variable.c
@@ -0,0 +1,304 @@
+/* variable.c - Variable and stack related opcodes
+ * Copyright (c) 1995-1997 Stefan Jokisch
+ *
+ * This file is part of Frotz.
+ *
+ * Frotz 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.
+ *
+ * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "frotz.h"
+
+/*
+ * z_dec, decrement a variable.
+ *
+ * zargs[0] = variable to decrement
+ *
+ */
+
+void z_dec (void)
+{
+ zword value;
+
+ if (zargs[0] == 0)
+ (*sp)--;
+ else if (zargs[0] < 16)
+ (*(fp - zargs[0]))--;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD (addr, value)
+ value--;
+ SET_WORD (addr, value)
+ }
+
+}/* z_dec */
+
+/*
+ * z_dec_chk, decrement a variable and branch if now less than value.
+ *
+ * zargs[0] = variable to decrement
+ * zargs[1] = value to check variable against
+ *
+ */
+
+void z_dec_chk (void)
+{
+ zword value;
+
+ if (zargs[0] == 0)
+ value = --(*sp);
+ else if (zargs[0] < 16)
+ value = --(*(fp - zargs[0]));
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD (addr, value)
+ value--;
+ SET_WORD (addr, value)
+ }
+
+ branch ((short) value < (short) zargs[1]);
+
+}/* z_dec_chk */
+
+/*
+ * z_inc, increment a variable.
+ *
+ * zargs[0] = variable to increment
+ *
+ */
+
+void z_inc (void)
+{
+ zword value;
+
+ if (zargs[0] == 0)
+ (*sp)++;
+ else if (zargs[0] < 16)
+ (*(fp - zargs[0]))++;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD (addr, value)
+ value++;
+ SET_WORD (addr, value)
+ }
+
+}/* z_inc */
+
+/*
+ * z_inc_chk, increment a variable and branch if now greater than value.
+ *
+ * zargs[0] = variable to increment
+ * zargs[1] = value to check variable against
+ *
+ */
+
+void z_inc_chk (void)
+{
+ zword value;
+
+ if (zargs[0] == 0)
+ value = ++(*sp);
+ else if (zargs[0] < 16)
+ value = ++(*(fp - zargs[0]));
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD (addr, value)
+ value++;
+ SET_WORD (addr, value)
+ }
+
+ branch ((short) value > (short) zargs[1]);
+
+}/* z_inc_chk */
+
+/*
+ * z_load, store the value of a variable.
+ *
+ * zargs[0] = variable to store
+ *
+ */
+
+void z_load (void)
+{
+ zword value;
+
+ if (zargs[0] == 0)
+ value = *sp;
+ else if (zargs[0] < 16)
+ value = *(fp - zargs[0]);
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD (addr, value)
+ }
+
+ store (value);
+
+}/* z_load */
+
+/*
+ * z_pop, pop a value off the game stack and discard it.
+ *
+ * no zargs used
+ *
+ */
+
+void z_pop (void)
+{
+
+ sp++;
+
+}/* z_pop */
+
+/*
+ * z_pop_stack, pop n values off the game or user stack and discard them.
+ *
+ * zargs[0] = number of values to discard
+ * zargs[1] = address of user stack (optional)
+ *
+ */
+
+void z_pop_stack (void)
+{
+
+ if (zargc == 2) { /* it's a user stack */
+
+ zword size;
+ zword addr = zargs[1];
+
+ LOW_WORD (addr, size)
+
+ size += zargs[0];
+ storew (addr, size);
+
+ } else sp += zargs[0]; /* it's the game stack */
+
+}/* z_pop_stack */
+
+/*
+ * z_pull, pop a value off...
+ *
+ * a) ...the game or a user stack and store it (V6)
+ *
+ * zargs[0] = address of user stack (optional)
+ *
+ * b) ...the game stack and write it to a variable (other than V6)
+ *
+ * zargs[0] = variable to write value to
+ *
+ */
+
+void z_pull (void)
+{
+ zword value;
+
+ if (h_version != V6) { /* not a V6 game, pop stack and write */
+
+ value = *sp++;
+
+ if (zargs[0] == 0)
+ *sp = value;
+ else if (zargs[0] < 16)
+ *(fp - zargs[0]) = value;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ SET_WORD (addr, value)
+ }
+
+ } else { /* it's V6, but is there a user stack? */
+
+ if (zargc == 1) { /* it's a user stack */
+
+ zword size;
+ zword addr = zargs[0];
+
+ LOW_WORD (addr, size)
+
+ size++;
+ storew (addr, size);
+
+ addr += 2 * size;
+ LOW_WORD (addr, value)
+
+ } else value = *sp++; /* it's the game stack */
+
+ store (value);
+
+ }
+
+}/* z_pull */
+
+/*
+ * z_push, push a value onto the game stack.
+ *
+ * zargs[0] = value to push onto the stack
+ *
+ */
+
+void z_push (void)
+{
+
+ *--sp = zargs[0];
+
+}/* z_push */
+
+/*
+ * z_push_stack, push a value onto a user stack then branch if successful.
+ *
+ * zargs[0] = value to push onto the stack
+ * zargs[1] = address of user stack
+ *
+ */
+
+void z_push_stack (void)
+{
+ zword size;
+ zword addr = zargs[1];
+
+ LOW_WORD (addr, size)
+
+ if (size != 0) {
+
+ storew ((zword) (addr + 2 * size), zargs[0]);
+
+ size--;
+ storew (addr, size);
+
+ }
+
+ branch (size);
+
+}/* z_push_stack */
+
+/*
+ * z_store, write a value to a variable.
+ *
+ * zargs[0] = variable to be written to
+ * zargs[1] = value to write
+ *
+ */
+
+void z_store (void)
+{
+ zword value = zargs[1];
+
+ if (zargs[0] == 0)
+ *sp = value;
+ else if (zargs[0] < 16)
+ *(fp - zargs[0]) = value;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ SET_WORD (addr, value)
+ }
+
+}/* z_store */