aboutsummaryrefslogtreecommitdiff
path: root/engines/gargoyle
diff options
context:
space:
mode:
authorPaul Gilbert2018-11-11 21:18:50 -0800
committerPaul Gilbert2018-12-08 19:05:59 -0800
commita083eb3d5ca682a0b465d383413f62ff38b8e814 (patch)
treeb31a455c695ddfe2f68eb8d237e3cbd09281e10e /engines/gargoyle
parent38a458139aef488acf48abfcf61233c529984cb1 (diff)
downloadscummvm-rg350-a083eb3d5ca682a0b465d383413f62ff38b8e814.tar.gz
scummvm-rg350-a083eb3d5ca682a0b465d383413f62ff38b8e814.tar.bz2
scummvm-rg350-a083eb3d5ca682a0b465d383413f62ff38b8e814.zip
GLK: FROTZ: Added bulk of game processor
Diffstat (limited to 'engines/gargoyle')
-rw-r--r--engines/gargoyle/frotz/buffer.cpp2
-rw-r--r--engines/gargoyle/frotz/err.cpp18
-rw-r--r--engines/gargoyle/frotz/err.h72
-rw-r--r--engines/gargoyle/frotz/frotz.cpp15
-rw-r--r--engines/gargoyle/frotz/frotz.h53
-rw-r--r--engines/gargoyle/frotz/frotz_types.h116
-rw-r--r--engines/gargoyle/frotz/glk_interface.cpp272
-rw-r--r--engines/gargoyle/frotz/glk_interface.h147
-rw-r--r--engines/gargoyle/frotz/mem.cpp130
-rw-r--r--engines/gargoyle/frotz/mem.h93
-rw-r--r--engines/gargoyle/frotz/processor.cpp659
-rw-r--r--engines/gargoyle/frotz/processor.h1322
-rw-r--r--engines/gargoyle/frotz/processor_input.cpp207
-rw-r--r--engines/gargoyle/frotz/processor_maths.cpp105
-rw-r--r--engines/gargoyle/frotz/processor_objects.cpp735
-rw-r--r--engines/gargoyle/frotz/processor_screen.cpp455
-rw-r--r--engines/gargoyle/frotz/processor_streams.cpp533
-rw-r--r--engines/gargoyle/frotz/processor_table.cpp125
-rw-r--r--engines/gargoyle/frotz/processor_text.cpp904
-rw-r--r--engines/gargoyle/frotz/processor_variables.cpp203
-rw-r--r--engines/gargoyle/glk.h20
-rw-r--r--engines/gargoyle/module.mk10
22 files changed, 6070 insertions, 126 deletions
diff --git a/engines/gargoyle/frotz/buffer.cpp b/engines/gargoyle/frotz/buffer.cpp
index 15ec771bfc..7d9e64465b 100644
--- a/engines/gargoyle/frotz/buffer.cpp
+++ b/engines/gargoyle/frotz/buffer.cpp
@@ -61,7 +61,7 @@ void Buffer::flush() {
void Buffer::printChar(zchar c) {
static bool flag = false;
- if (g_vm->_message || g_vm->_ostream_memory || g_vm->_enableBuffering) {
+ if (g_vm->message || g_vm->ostream_memory || g_vm->enable_buffering) {
if (!flag) {
// Characters 0 and ZC_RETURN are special cases
if (c == ZC_RETURN) {
diff --git a/engines/gargoyle/frotz/err.cpp b/engines/gargoyle/frotz/err.cpp
index f9a3ce9580..f75b764ad0 100644
--- a/engines/gargoyle/frotz/err.cpp
+++ b/engines/gargoyle/frotz/err.cpp
@@ -67,14 +67,15 @@ Errors::Errors() {
Common::fill(&_count[0], &_count[ERR_NUM_ERRORS], 0);
}
-void Errors::runtimeError(int errNum) {
- int wasfirst;
+void Errors::runtimeError(ErrorCode errNum) {
+#ifdef TODO
+ int wasfirst;
if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
return;
- if (g_vm->_options._err_report_mode == ERR_REPORT_FATAL
- || (!g_vm->_options._ignore_errors && errNum <= ERR_MAX_FATAL)) {
+ if (g_vm->_err_report_mode == ERR_REPORT_FATAL
+ || (!g_vm->_ignore_errors && errNum <= ERR_MAX_FATAL)) {
g_vm->_buffer.flush();
error(ERR_MESSAGES[errNum - 1]);
return;
@@ -83,9 +84,9 @@ void Errors::runtimeError(int errNum) {
wasfirst = (_count[errNum - 1] == 0);
_count[errNum - 1]++;
- if ((g_vm->_options._err_report_mode == ERR_REPORT_ALWAYS)
- || (g_vm->_options._err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
- long pc;
+ if ((g_vm->_err_report_mode == ERR_REPORT_ALWAYS)
+ || (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
+ long pc = g_vm->_processor
GET_PC(pc);
printString("Warning: ");
printString(ERR_MESSAGES[errNum - 1]);
@@ -93,7 +94,7 @@ void Errors::runtimeError(int errNum) {
printLong(pc, 16);
printChar(')');
- if (g_vm->_options._err_report_mode == ERR_REPORT_ONCE) {
+ if (_err_report_mode == ERR_REPORT_ONCE) {
printString(" (will ignore further occurrences)");
} else {
printString(" (occurence ");
@@ -103,6 +104,7 @@ void Errors::runtimeError(int errNum) {
newLine();
}
+#endif
}
void Errors::printLong(uint value, int base) {
diff --git a/engines/gargoyle/frotz/err.h b/engines/gargoyle/frotz/err.h
index f61d7269af..e71c9881e8 100644
--- a/engines/gargoyle/frotz/err.h
+++ b/engines/gargoyle/frotz/err.h
@@ -28,26 +28,51 @@
namespace Gargoyle {
namespace Frotz {
-#define ERR_NUM_ERRORS 33
-#define ERR_MAX_FATAL 19
+enum ErrorCode {
+ ERR_TEXT_BUF_OVF = 1, ///< Text buffer overflow
+ ERR_STORE_RANGE = 2, ///< Store out of dynamic memory
+ ERR_DIV_ZERO = 3, ///< Division by zero
+ ERR_ILL_OBJ = 4, ///< Illegal object
+ ERR_ILL_ATTR = 5, ///< Illegal attribute
+ ERR_NO_PROP = 6, ///< No such property
+ ERR_STK_OVF = 7, ///< Stack overflow
+ ERR_ILL_CALL_ADDR = 8, ///< Call to illegal address
+ ERR_CALL_NON_RTN = 9, ///< Call to non-routine
+ ERR_STK_UNDF = 10, ///< Stack underflow
+ ERR_ILL_OPCODE = 11, ///< Illegal opcode
+ ERR_BAD_FRAME = 12, ///< Bad stack frame
+ ERR_ILL_JUMP_ADDR = 13, ///< Jump to illegal address
+ ERR_SAVE_IN_INTER = 14, ///< Can't save while in interrupt
+ ERR_STR3_NESTING = 15, ///< Nesting stream #3 too deep
+ ERR_ILL_WIN = 16, ///< Illegal window
+ ERR_ILL_WIN_PROP = 17, ///< Illegal window property
+ ERR_ILL_PRINT_ADDR = 18, ///< Print at illegal address
+ ERR_DICT_LEN = 19, ///< Illegal dictionary word length
+ ERR_MAX_FATAL = 19,
+
+ // Less serious errors
+ ERR_JIN_0 = 20, ///< @jin called with object 0
+ ERR_GET_CHILD_0 = 21, ///< @get_child called with object 0
+ ERR_GET_PARENT_0 = 22, ///< @get_parent called with object 0
+ ERR_GET_SIBLING_0 = 23, ///< @get_sibling called with object 0
+ ERR_GET_PROP_ADDR_0 = 24, ///< @get_prop_addr called with object 0
+ ERR_GET_PROP_0 = 25, ///< @get_prop called with object 0
+ ERR_PUT_PROP_0 = 26, ///< @put_prop called with object 0
+ ERR_CLEAR_ATTR_0 = 27, ///< @clear_attr called with object 0
+ ERR_SET_ATTR_0 = 28, ///< @set_attr called with object 0
+ ERR_TEST_ATTR_0 = 29, ///< @test_attr called with object 0
+ ERR_MOVE_OBJECT_0 = 30, ///< @move_object called moving object 0
+ ERR_MOVE_OBJECT_TO_0 = 31, ///< @move_object called moving into object 0
+ ERR_REMOVE_OBJECT_0 = 32, ///< @remove_object called with object 0
+ ERR_GET_NEXT_PROP_0 = 33, ///< @get_next_prop called with object 0
+ ERR_NUM_ERRORS = 33
+};
class Errors {
private:
static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
int _count[ERR_NUM_ERRORS];
-public:
- /**
- * Constructor
- */
- Errors();
-
- /**
- * An error has occurred. Ignore it, pass it to os_fatal or report
- * it according to err_report_mode.
- * @param errNum Numeric code for error (1 to ERR_NUM_ERRORS)
- */
- void runtimeError(int errNum);
-
+private:
/**
* Print an unsigned 32bit number in decimal or hex.
*/
@@ -67,6 +92,23 @@ public:
* Add a newline
*/
void newLine();
+protected:
+ /**
+ * Get the PC. Is implemented by the Processor class, which derives from Errors
+ */
+ virtual zword getPC() const = 0;
+public:
+ /**
+ * Constructor
+ */
+ Errors();
+
+ /**
+ * An error has occurred. Ignore it, pass it to os_fatal or report
+ * it according to err_report_mode.
+ * @param errNum Numeric code for error (1 to ERR_NUM_ERRORS)
+ */
+ void runtimeError(ErrorCode errNum);
};
} // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index 64b9f54449..c0a9442ad4 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -28,27 +28,18 @@ namespace Frotz {
Frotz *g_vm;
-Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
- _storyId(UNKNOWN), _storySize(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
- _ostream_screen(true), _ostream_script(false), _ostream_memory(false),
- _ostream_record(false), _istream_replay(false), _message(false),
- _cwin(0), _mwin(0), _mouse_x(0), _mouse_y(0), _menu_selected(0),
- _enableWrapping(false), _enableScripting(true), _enableScrolling(false),
- _enableBuffering(false), _reserveMem(0) {
+Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ Processor(syst, gameDesc) {
g_vm = this;
- Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
}
void Frotz::runGame(Common::SeekableReadStream *gameFile) {
+ story_fp = gameFile;
initialize();
// TODO
}
-void Frotz::initialize() {
- _mem.initialize();
-}
-
Common::Error Frotz::loadGameState(int slot) {
return Common::kNoError;
}
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index 4dd583449b..c032265453 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -23,11 +23,7 @@
#ifndef GARGOYLE_FROTZ_FROTZ
#define GARGOYLE_FROTZ_FROTZ
-#include "gargoyle/glk.h"
-#include "gargoyle/frotz/frotz_types.h"
-#include "gargoyle/frotz/buffer.h"
-#include "gargoyle/frotz/err.h"
-#include "gargoyle/frotz/mem.h"
+#include "gargoyle/frotz/processor.h"
namespace Gargoyle {
namespace Frotz {
@@ -35,52 +31,7 @@ namespace Frotz {
/**
* Frotz interpreter for Z-code games
*/
-class Frotz : public Glk {
-private:
- /**
- * Perform any initialization
- */
- void initialize();
-public:
- UserOptions _options;
- Header _header;
- Buffer _buffer;
- Mem _mem;
-
- // Story file name, id number and size
- Common::SeekableReadStream *_gameFile;
- Story _storyId;
- size_t _storySize;
-
- // Stack data
- zword _stack[STACK_SIZE];
- zword *_sp;
- zword *_fp;
- zword _frameCount;
-
- // IO streams
- bool _ostream_screen;
- bool _ostream_script;
- bool _ostream_memory;
- bool _ostream_record;
- bool _istream_replay;
- bool _message;
-
- // Current window and mouse data
- int _cwin;
- int _mwin;
- int _mouse_y;
- int _mouse_x;
- int _menu_selected;
-
- // Window attributes
- bool _enableWrapping;
- bool _enableScripting;
- bool _enableScrolling;
- bool _enableBuffering;
-
- // Size of memory to reserve (in bytes)
- size_t _reserveMem;
+class Frotz : public Processor {
public:
/**
* Constructor
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 3c7312c1bc..c4f4298a4e 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -35,15 +35,6 @@ namespace Frotz {
#define lo(v) (v & 0xff)
#define hi(v) (v >> 8)
-#define SET_WORD(addr,v) WRITE_BE_UINT16(addr,v)
-#define LOW_WORD(addr,v) READ_BE_UINT16(addr,v)
-#define HIGH_WORD(addr,v) READ_BE_UINT16(addr,v)
-#define HIGH_LONG(addr,v) READ_BE_UINT32(addr,v)
-#define CODE_WORD(v) v = g_vm->_mem.readWord()
-#define CODE_IDX_WORD(v,i) v = g_vm->_mem.readWord(i)
-#define GET_PC(v) v = g_vm->_mem.getPC()
-#define SET_PC(v) g_vm->_mem.setPC(v);
-
/* 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;
@@ -113,6 +104,113 @@ enum Story {
UNKNOWN
};
+enum Version {
+ V1 = 1,
+ V2 = 2,
+ V3 = 3,
+ V4 = 4,
+ V5 = 5,
+ V6 = 6,
+ V7 = 7,
+ V8 = 8,
+ V9 = 9
+};
+
+enum ConfigFlag {
+ CONFIG_BYTE_SWAPPED = 0x01, ///< Story file is byte swapped - V3
+ CONFIG_TIME = 0x02, ///< Status line displays time - V3
+ CONFIG_TWODISKS = 0x04, ///< Story file occupied two disks - V3
+ CONFIG_TANDY = 0x08, ///< Tandy licensed game - V3
+ CONFIG_NOSTATUSLINE = 0x10, ///< Interpr can't support status lines - V3
+ CONFIG_SPLITSCREEN = 0x20, ///< Interpr supports split screen mode - V3
+ CONFIG_PROPORTIONAL = 0x40, ///< Interpr uses proportional font - V3
+
+ CONFIG_COLOUR = 0x01, ///< Interpr supports colour - V5+
+ CONFIG_PICTURES = 0x02, ///< Interpr supports pictures - V6
+ CONFIG_BOLDFACE = 0x04, ///< Interpr supports boldface style - V4+
+ CONFIG_EMPHASIS = 0x08, ///< Interpr supports emphasis style - V4+
+ CONFIG_FIXED = 0x10, ///< Interpr supports fixed width style - V4+
+ CONFIG_SOUND = 0x20, ///< Interpr supports sound - V6
+ CONFIG_TIMEDINPUT = 0x80, ///< Interpr supports timed input - V4+
+
+ SCRIPTING_FLAG = 0x0001, ///< Outputting to transscription file - V1+
+ FIXED_FONT_FLAG = 0x0002, ///< Use fixed width font - V3+
+ REFRESH_FLAG = 0x0004, ///< Refresh the screen - V6
+ GRAPHICS_FLAG = 0x0008, ///< Game wants to use graphics - V5+
+ OLD_SOUND_FLAG = 0x0010, ///< Game wants to use sound effects - V3
+ UNDO_FLAG = 0x0010, ///< Game wants to use UNDO feature - V5+
+ MOUSE_FLAG = 0x0020, ///< Game wants to use a mouse - V5+
+ COLOUR_FLAG = 0x0040, ///< Game wants to use colours - V5+
+ SOUND_FLAG = 0x0080, ///< Game wants to use sound effects - V5+
+ MENU_FLAG = 0x0100 ///< Game wants to use menus - V6
+};
+
+enum {
+ TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency - V6
+};
+
+enum FrotzInterp {
+#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
+};
+
+enum Colour {
+ BLACK_COLOUR = 2,
+ RED_COLOUR = 3,
+ GREEN_COLOUR = 4,
+ YELLOW_COLOUR = 5,
+ BLUE_COLOUR = 6,
+ MAGENTA_COLOUR = 7,
+ CYAN_COLOUR = 8,
+ WHITE_COLOUR = 9,
+ GREY_COLOUR = 10, ///< INTERP_MSDOS only
+ LIGHTGREY_COLOUR = 10, ///< INTERP_AMIGA only
+ MEDIUMGREY_COLOUR = 11, ///< INTERP_AMIGA only
+ DARKGREY_COLOUR = 12, ///< INTERP_AMIGA only
+ TRANSPARENT_COLOUR = 15 ///< ZSpec 1.1
+};
+
+enum Style {
+ REVERSE_STYLE = 1,
+ BOLDFACE_STYLE = 2,
+ EMPHASIS_STYLE = 4,
+ FIXED_WIDTH_STYLE = 8
+};
+
+enum FontStyle {
+ TEXT_FONT = 1,
+ PICTURE_FONT = 2,
+ GRAPHICS_FONT = 3,
+ FIXED_WIDTH_FONT = 4
+};
+
+/*** Constants for os_beep */
+
+#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
+
+/*** Constants for os_menu */
+
+#define MENU_NEW 0
+#define MENU_ADD 1
+#define MENU_REMOVE 2
+
typedef byte zbyte;
typedef uint zchar;
typedef uint16 zword;
diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
new file mode 100644
index 0000000000..e1311fbc62
--- /dev/null
+++ b/engines/gargoyle/frotz/glk_interface.cpp
@@ -0,0 +1,272 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/glk_interface.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ Glk(syst, gameDesc),
+ oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
+ curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
+ curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
+ gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr),
+ gos_linewin(nullptr), gos_channel(nullptr), cwin(0), mwin(0), mouse_x(0), mouse_y(0),
+ menu_selected(0), ostream_screen(false), ostream_script(false), ostream_memory(false),
+ ostream_record(false), istream_replay(false), message(false),
+ enable_wrapping(false), enable_scripting(false), enable_scrolling(false),
+ enable_buffering(false), next_sample(0), next_volume(0),
+ _soundLocked(false), _soundPlaying(false) {
+ Common::fill(&statusline[0], &statusline[256], '\0');
+}
+
+int GlkInterface::os_char_width(zchar z) {
+ return 1;
+}
+
+int GlkInterface::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;
+}
+
+int GlkInterface::os_string_length(zchar *s) {
+ int length = 0;
+ while (*s++) length++;
+ return length;
+}
+
+void GlkInterface::os_prepare_sample(int a) {
+ glk_sound_load_hint(a, 1);
+}
+
+void GlkInterface::os_finish_with_sample(int a) {
+ glk_sound_load_hint(a, 0);
+}
+
+void GlkInterface::os_start_sample(int number, int volume, int repeats, zword eos) {
+ int vol;
+
+ if (!gos_channel) {
+ gos_channel = glk_schannel_create(0);
+ if (!gos_channel)
+ return;
+ }
+
+ switch (volume) {
+ case 1: vol = 0x02000; break;
+ case 2: vol = 0x04000; break;
+ case 3: vol = 0x06000; break;
+ case 4: vol = 0x08000; break;
+ case 5: vol = 0x0a000; break;
+ case 6: vol = 0x0c000; break;
+ case 7: vol = 0x0e000; break;
+ case 8: vol = 0x10000; break;
+ default: vol = 0x20000; break;
+ }
+
+ // we dont do repeating or eos-callback for now...
+ glk_schannel_play_ext(gos_channel, number, 1, 0);
+ glk_schannel_set_volume(gos_channel, vol);
+}
+
+void GlkInterface::os_stop_sample(int a) {
+ if (!gos_channel)
+ return;
+ glk_schannel_stop(gos_channel);
+}
+
+void GlkInterface::os_beep(int volume) {
+}
+
+void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
+ // TODO
+}
+
+void GlkInterface::start_next_sample() {
+ // TODO
+}
+
+void GlkInterface::gos_update_width() {
+ glui32 width;
+ if (gos_upper) {
+ glk_window_get_size(gos_upper, &width, nullptr);
+ h_screen_cols = width;
+ SET_BYTE(H_SCREEN_COLS, width);
+ if (curx > width) {
+ glk_window_move_cursor(gos_upper, 0, cury - 1);
+ curx = 1;
+ }
+ }
+}
+
+void GlkInterface::gos_update_height() {
+ glui32 height_upper;
+ glui32 height_lower;
+ if (gos_curwin) {
+ glk_window_get_size(gos_upper, nullptr, &height_upper);
+ glk_window_get_size(gos_lower, nullptr, &height_lower);
+ h_screen_rows = height_upper + height_lower + 1;
+ SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
+ }
+}
+
+void GlkInterface::reset_status_ht() {
+ glui32 height;
+ if (gos_upper) {
+ glk_window_get_size(gos_upper, nullptr, &height);
+ if (mach_status_ht != height) {
+ glk_window_set_arrangement(
+ glk_window_get_parent(gos_upper),
+ winmethod_Above | winmethod_Fixed,
+ mach_status_ht, nullptr);
+ }
+ }
+}
+
+void GlkInterface::erase_window(zword w) {
+ if (w == 0)
+ glk_window_clear(gos_lower);
+ else if (gos_upper) {
+#ifdef GARGLK
+ garglk_set_reversevideo_stream(
+ glk_window_get_stream(gos_upper),
+ true);
+#endif /* GARGLK */
+
+ memset(statusline, ' ', sizeof statusline);
+ glk_window_clear(gos_upper);
+ reset_status_ht();
+ curr_status_ht = 0;
+ }
+}
+
+void GlkInterface::split_window(zword lines) {
+ if (!gos_upper)
+ return;
+
+ // The top line is always set for V1 to V3 games
+ if (h_version < V4)
+ lines++;
+
+ if (!lines || lines > curr_status_ht) {
+ glui32 height;
+
+ glk_window_get_size(gos_upper, nullptr, &height);
+ if (lines != height)
+ glk_window_set_arrangement(
+ glk_window_get_parent(gos_upper),
+ winmethod_Above | winmethod_Fixed,
+ lines, nullptr);
+ curr_status_ht = lines;
+ }
+ mach_status_ht = lines;
+ if (cury > lines)
+ {
+ glk_window_move_cursor(gos_upper, 0, 0);
+ curx = cury = 1;
+ }
+ gos_update_width();
+
+ if (h_version == V3)
+ glk_window_clear(gos_upper);
+}
+
+void GlkInterface::restart_screen() {
+ erase_window(0);
+ erase_window(1);
+ split_window(0);
+}
+
+void GlkInterface::packspaces(zchar *src, zchar *dst) {
+ int killing = 0;
+ while (*src) {
+ if (*src == 0x20202020)
+ *src = ' ';
+ if (*src == ' ')
+ killing++;
+ else
+ killing = 0;
+ if (killing > 2)
+ src++;
+ else
+ *dst++ = *src++;
+ }
+
+ *dst = 0;
+}
+
+void GlkInterface::smartstatusline() {
+ zchar packed[256];
+ zchar buf[256];
+ zchar *a, *b, *c, *d;
+ int roomlen, scorelen, scoreofs;
+ int len, tmp;
+
+ packspaces(statusline, packed);
+ len = os_string_length(packed);
+
+ a = packed;
+ while (a[0] == ' ')
+ a++;
+
+ b = a;
+ while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
+ b++;
+
+ c = b;
+ while (c[0] == ' ')
+ c++;
+
+ d = packed + len - 1;
+ while (d[0] == ' ' && d > c)
+ d--;
+ if (d[0] != ' ' && d[0] != 0)
+ d++;
+ if (d < c)
+ d = c;
+
+ roomlen = b - a;
+ scorelen = d - c;
+ scoreofs = h_screen_cols - scorelen - 2;
+ if (scoreofs <= roomlen)
+ scoreofs = roomlen + 2;
+
+ for (tmp = 0; tmp < h_screen_cols; tmp++)
+ buf[tmp] = ' ';
+
+ memcpy(buf + 1 + scoreofs, c, scorelen * sizeof(zchar));
+ memcpy(buf + 1, a, roomlen * sizeof(zchar));
+
+ glk_window_move_cursor(gos_upper, 0, 0);
+ glk_put_buffer_uni(buf, h_screen_cols);
+ glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
new file mode 100644
index 0000000000..ea3f60cfe8
--- /dev/null
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -0,0 +1,147 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_FROTZ_GLK_INTERFACE
+#define GARGOYLE_FROTZ_GLK_INTERFACE
+
+#include "gargoyle/glk.h"
+#include "gargoyle/frotz/mem.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+enum SoundEffect {
+ EFFECT_PREPARE = 1,
+ EFFECT_PLAY = 2,
+ EFFECT_STOP = 3,
+ EFFECT_FINISH_WITH = 4
+};
+
+/**
+ * Implements an intermediate interface on top of the GLK layer, providing screen
+ * and sound effect handling
+ */
+class GlkInterface : public Glk, public virtual Mem {
+public:
+ zchar statusline[256];
+ int oldstyle;
+ int curstyle;
+ int cury;
+ int curx;
+ int fixforced;
+
+ int curr_fg;
+ int curr_bg;
+ int curr_font;
+ int prev_font;
+ int temp_font;
+
+ int curr_status_ht;
+ int mach_status_ht;
+
+ winid_t gos_status;
+ winid_t gos_upper;
+ winid_t gos_lower;
+ winid_t gos_curwin;
+ int gos_linepending;
+ zchar *gos_linebuf;
+ winid_t gos_linewin;
+ schanid_t gos_channel;
+
+ // Current window and mouse data
+ int cwin;
+ int mwin;
+ int mouse_y;
+ int mouse_x;
+ int menu_selected;
+
+ // IO streams
+ bool ostream_screen;
+ bool ostream_script;
+ bool ostream_memory;
+ bool ostream_record;
+ bool istream_replay;
+ bool message;
+
+ // Window attributes
+ bool enable_wrapping;
+ bool enable_scripting;
+ bool enable_scrolling;
+ bool enable_buffering;
+
+ // Sound fields
+ int next_sample;
+ int next_volume;
+
+ bool _soundLocked;
+ bool _soundPlaying;
+protected:
+ int os_char_width(zchar z);
+ int os_string_width(const zchar *s);
+ int os_string_length(zchar *s);
+ void os_prepare_sample(int a);
+ void os_finish_with_sample(int a);
+
+ /**
+ * Play the given sample at the given volume (ranging from 1 to 8 and
+ * 255 meaning a default volume). The sound is played once or several
+ * times in the background (255 meaning forever). In Z-code 3 the
+ * repeats value is always 0 and the number of repeats is taken from
+ * the sound file itself. The end_of_sound function is called as soon
+ * as the sound finishes.
+ */
+ void os_start_sample(int number, int volume, int repeats, zword eos);
+
+ void os_stop_sample(int a);
+ void os_beep(int volume);
+
+ /**
+ * Call the IO interface to play a sample.
+ */
+ void start_sample(int number, int volume, int repeats, zword eos);
+
+ void start_next_sample();
+ void gos_update_width();
+ void gos_update_height();
+ void reset_status_ht();
+ void erase_window(zword w);
+ void split_window(zword lines);
+ void restart_screen();
+
+ /**
+ * statusline overflowed the window size ... bad game!
+ * so ... split status text into regions, reformat and print anew.
+ */
+ void packspaces(zchar *src, zchar *dst);
+
+ void smartstatusline();
+public:
+ /**
+ * Constructor
+ */
+ GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc);
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index 1b43e93d20..cb5a73b8d1 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -22,12 +22,13 @@
#include "gargoyle/frotz/mem.h"
#include "gargoyle/frotz/frotz.h"
+#include "common/memstream.h"
#include "common/textconsole.h"
namespace Gargoyle {
namespace Frotz {
-const Mem::StoryEntry Mem::RECORDS[25] = {
+const Header::StoryEntry Header::RECORDS[25] = {
{ SHERLOCK, 21, "871214" },
{ SHERLOCK, 26, "880127" },
{ BEYOND_ZORK, 47, "870915" },
@@ -55,28 +56,99 @@ const Mem::StoryEntry Mem::RECORDS[25] = {
{ UNKNOWN, 0, "------" }
};
-Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0) {
+void Header::loadHeader(Common::SeekableReadStream &f) {
+ h_version = f.readByte();
+ h_config = f.readByte();
+
+ if (h_version < V1 || h_version > V8)
+ error("Unknown Z-code version");
+
+ if (h_version == V6)
+ error("Cannot play Z-code version 6");
+
+ if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
+ error("Byte swapped story file");
+
+ h_release = f.readUint16BE();
+ h_resident_size = f.readUint16BE();
+ h_start_pc = f.readUint16BE();
+ h_dictionary = f.readUint16BE();
+ h_objects = f.readUint16BE();
+ h_globals = f.readUint16BE();
+ h_dynamic_size = f.readUint16BE();
+ h_flags = f.readUint16BE();
+ f.read(h_serial, 6);
+
+ /* Auto-detect buggy story files that need special fixes */
+ _storyId = UNKNOWN;
+
+ for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
+ if (h_release == RECORDS[i]._release) {
+ if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
+ _storyId = RECORDS[i]._storyId;
+ break;
+ }
+ }
+ }
+
+ h_abbreviations = f.readUint16BE();
+ h_file_size = f.readUint16BE();
+ h_checksum = f.readUint16BE();
+
+ f.seek(H_FUNCTIONS_OFFSET);
+ h_functions_offset = f.readUint16BE();
+ h_strings_offset = f.readUint16BE();
+ f.seek(H_TERMINATING_KEYS);
+ h_terminating_keys = f.readUint16BE();
+ f.seek(H_ALPHABET);
+ h_alphabet = f.readUint16BE();
+ h_extension_table = f.readUint16BE();
+
+
+ // Zork Zero Macintosh doesn't have the graphics flag set
+ if (_storyId == ZORK_ZERO && h_release == 296)
+ h_flags |= GRAPHICS_FLAG;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0) {
}
void Mem::initialize() {
-/*
- long size;
- zword addr;
- unsigned n;
- int i, j;
- */
initializeStoryFile();
+ loadGameHeader();
+
+ // Allocate memory for story data
+ if ((zmp = (zbyte *)realloc(zmp, story_size)) == nullptr)
+ error("Out of memory");
+
+ // Load story file in chunks of 32KB
+ uint n = 0x8000;
+
+ for (uint size = 64; size < story_size; size += n) {
+ if (story_size - size < 0x8000)
+ n = story_size - size;
+
+ setPC(size);
- // TODO: More stuff
+ if (story_fp->read(pcp, n) != n)
+ error("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);
+ hx_flags = get_header_extension(HX_FLAGS);
}
void Mem::initializeStoryFile() {
- Common::SeekableReadStream *f = g_vm->_gameFile;
+ Common::SeekableReadStream *f = story_fp;
giblorb_map_t *map;
giblorb_result_t res;
uint32 magic;
- story_fp = f;
magic = f->readUint32BE();
if (magic == MKTAG('F', 'O', 'R', 'M')) {
@@ -101,5 +173,41 @@ void Mem::initializeStoryFile() {
error("This file is too small to be a Z-code file.");
}
+void Mem::loadGameHeader() {
+ // Load header
+ zmp = new byte[64];
+ story_fp->seek(blorb_ofs);
+ story_fp->read(zmp, 64);
+
+ Common::MemoryReadStream h(zmp, 64);
+ loadHeader(h);
+
+ // 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
+ story_size = blorb_len;
+ }
+}
+
+zword Mem::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;
+}
+
} // End of namespace Scott
} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index 68c7b85ece..d37cdd3d99 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -28,11 +28,71 @@
namespace Gargoyle {
namespace Frotz {
+#define SET_WORD(addr,v) zmp[addr] = hi(v); zmp[addr+1] = lo(v)
+#define LOW_WORD(addr,v) v = READ_BE_UINT16(&zmp[addr])
+#define HIGH_WORD(addr,v) v = READ_BE_UINT16(&zmp[addr])
+#define HIGH_LONG(addr,v) v = READ_BE_UINT32(&zmp[addr])
+#define SET_BYTE(addr,v) zmp[addr] = v
+#define LOW_BYTE(addr,v) v = zmp[addr]
+
+enum HeaderByte {
+ H_VERSION = 0,
+ H_CONFIG = 1,
+ H_RELEASE = 2,
+ H_RESIDENT_SIZE = 4,
+ H_START_PC = 6,
+ H_DICTIONARY = 8,
+ H_OBJECTS = 10,
+ H_GLOBALS = 12,
+ H_DYNAMIC_SIZE = 14,
+ H_FLAGS = 16,
+ H_SERIAL = 18,
+ H_ABBREVIATIONS = 24,
+ H_FILE_SIZE = 26,
+ H_CHECKSUM = 28,
+ H_INTERPRETER_NUMBER = 30,
+ H_INTERPRETER_VERSION = 31,
+ H_SCREEN_ROWS = 32,
+ H_SCREEN_COLS = 33,
+ H_SCREEN_WIDTH = 34,
+ H_SCREEN_HEIGHT = 36,
+ H_FONT_HEIGHT = 38, ///< this is the font width in V5
+ H_FONT_WIDTH = 39, ///< this is the font height in V5
+ H_FUNCTIONS_OFFSET = 40,
+ H_STRINGS_OFFSET = 42,
+ H_DEFAULT_BACKGROUND = 44,
+ H_DEFAULT_FOREGROUND = 45,
+ H_TERMINATING_KEYS = 46,
+ H_LINE_WIDTH = 48,
+ H_STANDARD_HIGH = 50,
+ H_STANDARD_LOW = 51,
+ H_ALPHABET = 52,
+ H_EXTENSION_TABLE = 54,
+ H_USER_NAME = 56
+};
+
+enum {
+ HX_TABLE_SIZE = 0,
+ HX_MOUSE_X = 1,
+ HX_MOUSE_Y = 2,
+ HX_UNICODE_TABLE = 3,
+ HX_FLAGS = 4,
+ HX_FORE_COLOUR = 5,
+ HX_BACK_COLOUR = 6
+};
/**
* Story file header data
*/
struct Header {
+private:
+ struct StoryEntry {
+ Story _storyId;
+ zword _release;
+ char _serial[7];
+ };
+ static const StoryEntry RECORDS[25];
+public:
zbyte h_version;
zbyte h_config;
zword h_release;
@@ -75,6 +135,11 @@ struct Header {
zword hx_fore_colour;
zword hx_back_colour;
+ Story _storyId;
+
+ /**
+ * Constructor
+ */
Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
@@ -84,22 +149,22 @@ struct Header {
h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
h_alphabet(0), h_extension_table(0),
hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
- hx_flags(0), hx_fore_colour(0), hx_back_colour(0) {
+ hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
Common::fill(&h_serial[0], &h_serial[6], '\0');
Common::fill(&h_user_name[0], &h_user_name[8], '\0');
}
+
+ /**
+ * Load the header
+ */
+ void loadHeader(Common::SeekableReadStream &f);
};
class Mem : public Header {
- struct StoryEntry {
- Story _storyId;
- zword _release;
- char _serial[7];
- };
- static const StoryEntry RECORDS[25];
-private:
+protected:
Common::SeekableReadStream *story_fp;
uint blorb_ofs, blorb_len;
+ uint story_size;
byte *pcp;
byte *zmp;
private:
@@ -107,6 +172,11 @@ private:
* Handles setting the story file, parsing it if it's a Blorb file
*/
void initializeStoryFile();
+
+ /**
+ * Handles loading the game header
+ */
+ void loadGameHeader();
public:
/**
* Constructor
@@ -116,7 +186,7 @@ public:
/**
* Initialize
*/
- void initialize();
+ virtual void initialize();
/**
* Read a word
@@ -142,6 +212,11 @@ public:
* Set the PC
*/
void setPC(uint ofs) { pcp = zmp + ofs; }
+
+ /**
+ * Read a value from the header extension (former mouse table).
+ */
+ zword get_header_extension(int entry);
};
} // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
new file mode 100644
index 0000000000..1a31898a2a
--- /dev/null
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -0,0 +1,659 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+#include "gargoyle/frotz/frotz.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Stubs to replace with actual code
+zword save_undo() { return 0; }
+zword restore_undo() { return 0; }
+
+
+Opcode Processor::var_opcodes[64] = {
+ &Processor::__illegal__,
+ &Processor::z_je,
+ &Processor::z_jl,
+ &Processor::z_jg,
+ &Processor::z_dec_chk,
+ &Processor::z_inc_chk,
+ &Processor::z_jin,
+ &Processor::z_test,
+ &Processor::z_or,
+ &Processor::z_and,
+ &Processor::z_test_attr,
+ &Processor::z_set_attr,
+ &Processor::z_clear_attr,
+ &Processor::z_store,
+ &Processor::z_insert_obj,
+ &Processor::z_loadw,
+ &Processor::z_loadb,
+ &Processor::z_get_prop,
+ &Processor::z_get_prop_addr,
+ &Processor::z_get_next_prop,
+ &Processor::z_add,
+ &Processor::z_sub,
+ &Processor::z_mul,
+ &Processor::z_div,
+ &Processor::z_mod,
+ &Processor::z_call_s,
+ &Processor::z_call_n,
+ &Processor::z_set_colour,
+ &Processor::z_throw,
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::z_call_s,
+ &Processor::z_storew,
+ &Processor::z_storeb,
+ &Processor::z_put_prop,
+ &Processor::z_read,
+ &Processor::z_print_char,
+ &Processor::z_print_num,
+ &Processor::z_random,
+ &Processor::z_push,
+ &Processor::z_pull,
+ &Processor::z_split_window,
+ &Processor::z_set_window,
+ &Processor::z_call_s,
+ &Processor::z_erase_window,
+ &Processor::z_erase_line,
+ &Processor::z_set_cursor,
+ &Processor::z_get_cursor,
+ &Processor::z_set_text_style,
+ &Processor::z_buffer_mode,
+ &Processor::z_output_stream,
+ &Processor::z_input_stream,
+ &Processor::z_sound_effect,
+ &Processor::z_read_char,
+ &Processor::z_scan_table,
+ &Processor::z_not,
+ &Processor::z_call_n,
+ &Processor::z_call_n,
+ &Processor::z_tokenise,
+ &Processor::z_encode_text,
+ &Processor::z_copy_table,
+ &Processor::z_print_table,
+ &Processor::z_check_arg_count
+};
+
+Opcode Processor::ext_opcodes[64] = {
+ &Processor::z_save,
+ &Processor::z_restore,
+ &Processor::z_log_shift,
+ &Processor::z_art_shift,
+ &Processor::z_set_font,
+ &Processor::__illegal__, // glkify - Processor::z_draw_picture,
+ &Processor::__illegal__, // glkify - Processor::z_picture_data,
+ &Processor::__illegal__, // glkify - Processor::z_erase_picture,
+ &Processor::__illegal__, // glkify - Processor::z_set_margins,
+ &Processor::z_save_undo,
+ &Processor::z_restore_undo,
+ &Processor::z_print_unicode,
+ &Processor::z_check_unicode,
+ &Processor::z_set_true_colour, // spec 1.1
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::__illegal__, // glkify - Processor::z_move_window,
+ &Processor::__illegal__, // glkify - Processor::z_window_size,
+ &Processor::__illegal__, // glkify - Processor::z_window_style,
+ &Processor::__illegal__, // glkify - Processor::z_get_wind_prop,
+ &Processor::__illegal__, // glkify - Processor::z_scroll_window,
+ &Processor::z_pop_stack,
+ &Processor::__illegal__, // glkify - Processor::z_read_mouse,
+ &Processor::__illegal__, // glkify - Processor::z_mouse_window,
+ &Processor::z_push_stack,
+ &Processor::__illegal__, // glkify - Processor::z_put_wind_prop,
+ &Processor::z_print_form,
+ &Processor::z_make_menu,
+ &Processor::__illegal__, // glkify - Processor::z_picture_table
+ &Processor::z_buffer_screen, // spec 1.1
+};
+
+Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ GlkInterface(syst, gameDesc), Mem(), Errors(), UserOptions(),
+ _finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
+ zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
+ _randomInterval(0), _randomCtr(0), first_restart(true) {
+ static const Opcode OP0_OPCODES[16] = {
+ &Processor::z_rtrue,
+ &Processor::z_rfalse,
+ &Processor::z_print,
+ &Processor::z_print_ret,
+ &Processor::z_nop,
+ &Processor::z_save,
+ &Processor::z_restore,
+ &Processor::z_restart,
+ &Processor::z_ret_popped,
+ &Processor::z_catch,
+ &Processor::z_quit,
+ &Processor::z_new_line,
+ &Processor::z_show_status,
+ &Processor::z_verify,
+ &Processor::__extended__,
+ &Processor::z_piracy
+ };
+ static const Opcode OP1_OPCODES[16] = {
+ &Processor::z_jz,
+ &Processor::z_get_sibling,
+ &Processor::z_get_child,
+ &Processor::z_get_parent,
+ &Processor::z_get_prop_len,
+ &Processor::z_inc,
+ &Processor::z_dec,
+ &Processor::z_print_addr,
+ &Processor::z_call_s,
+ &Processor::z_remove_obj,
+ &Processor::z_print_obj,
+ &Processor::z_ret,
+ &Processor::z_jump,
+ &Processor::z_print_paddr,
+ &Processor::z_load,
+ &Processor::z_call_n
+ };
+
+ Common::copy(&OP0_OPCODES[0], &OP0_OPCODES[16], op0_opcodes);
+ Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], op1_opcodes);
+ Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
+ Common::fill(&zargs[0], &zargs[8], 0);
+}
+
+void Processor::initialize() {
+ Mem::initialize();
+
+ if (h_version <= V4) {
+ op0_opcodes[9] = &Processor::z_pop;
+ op1_opcodes[15] = &Processor::z_not;
+ } else {
+ op0_opcodes[9] = &Processor::z_catch;
+ op1_opcodes[15] = &Processor::z_call_n;
+ }
+}
+
+void Processor::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 {
+ // large constant
+ CODE_WORD(value);
+ }
+
+ zargs[zargc++] = value;
+}
+
+void Processor::load_all_operands(zbyte specifier) {
+ for (int i = 6; i >= 0; i -= 2) {
+ zbyte type = (specifier >> i) & 0x03;
+
+ if (type == 3)
+ break;
+
+ load_operand(type);
+ }
+}
+
+void Processor::interpret() {
+ 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);
+
+ (*this.*var_opcodes[opcode & 0x1f])();
+
+ } else if (opcode < 0xb0) {
+ // 1OP opcodes
+ load_operand((zbyte)(opcode >> 4));
+
+ (*this.*op1_opcodes[opcode & 0x0f])();
+
+ } else if (opcode < 0xc0) {
+ // 0OP opcodes
+ (*this.*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);
+ }
+
+ (*this.*var_opcodes[opcode - 0xc0])();
+ }
+
+#if defined(DJGPP) && defined(SOUND_SUPPORT)
+ if (end_of_sound_flag)
+ end_of_sound();
+#endif
+ } while (!_finished);
+
+ _finished--;
+}
+
+void Processor::call(zword routine, int argc, zword *args, int ct) {
+ long pc;
+ zword value;
+ zbyte count;
+ int i;
+
+ if (_sp - _stack < 4)
+ runtimeError(ERR_STK_OVF);
+
+ GET_PC(pc);
+
+ *--_sp = (zword)(pc >> 9);
+ *--_sp = (zword)(pc & 0x1ff);
+ *--_sp = (zword)(_fp - _stack - 1);
+ *--_sp = (zword)(argc | (ct << (_save_quetzal ? 12 : 8)));
+
+ _fp = _sp;
+ _frameCount++;
+
+ // 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 if (h_version <= V8)
+ pc = (long)routine << 3;
+ else {
+ // h_version == V9
+ long indirect = (long)routine << 2;
+ HIGH_LONG(indirect, pc);
+ }
+
+ if ((uint)pc >= story_size)
+ runtimeError(ERR_ILL_CALL_ADDR);
+
+ SET_PC(pc);
+
+ // Initialise local variables
+ CODE_BYTE(count);
+
+ if (count > 15)
+ runtimeError(ERR_CALL_NON_RTN);
+ if (_sp - _stack < count)
+ runtimeError(ERR_STK_OVF);
+
+ if (_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();
+}
+
+void Processor::ret(zword value) {
+ long pc;
+ int ct;
+
+ if (_sp > _fp)
+ runtimeError(ERR_STK_UNDF);
+
+ _sp = _fp;
+
+ ct = *_sp++ >> (_save_quetzal ? 12 : 8);
+ _frameCount--;
+ _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++;
+}
+
+void Processor::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 {
+ // It's a short branch
+ offset = off1;
+ }
+
+ if (specifier & 0x80) {
+ if (offset > 1) {
+ // normal branch
+ GET_PC(pc);
+ pc += (short)offset - 2;
+ SET_PC(pc);
+ } else {
+ // special case, return 0 or 1
+ ret(offset);
+ }
+ }
+}
+
+void Processor::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);
+ }
+}
+
+int Processor::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++;
+}
+
+void Processor::seed_random(int value) {
+ if (value == 0) {
+ // Now using random values
+ _randomInterval = 0;
+ } else if (value < 1000) {
+ // special seed value
+ _randomCtr = 0;
+ _randomInterval = value;
+ } else {
+ // standard seed value
+ _random.setSeed(value);
+ _randomInterval = 0;
+ }
+}
+
+void Processor::__extended__() {
+ zbyte opcode;
+ zbyte specifier;
+
+ CODE_BYTE(opcode);
+ CODE_BYTE(specifier);
+
+ load_all_operands(specifier);
+
+ if (opcode < 0x1e) // extended opcodes from 0x1e on
+ (*this.*ext_opcodes[opcode])(); // are reserved for future spec'
+}
+
+void Processor::__illegal__() {
+ runtimeError(ERR_ILL_OPCODE);
+}
+
+void Processor::z_catch() {
+ store(_save_quetzal ? _frameCount : (zword)(_fp - _stack));
+}
+
+void Processor::z_throw() {
+ if (_save_quetzal) {
+ if (zargs[1] > _frameCount)
+ runtimeError(ERR_BAD_FRAME);
+
+ // Unwind the stack a frame at a time.
+ for (; _frameCount > zargs[1]; --_frameCount)
+ _fp = _stack + 1 + _fp[1];
+ } else {
+ if (zargs[1] > STACK_SIZE)
+ runtimeError(ERR_BAD_FRAME);
+
+ _fp = _stack + zargs[1];
+ }
+
+ ret(zargs[0]);
+}
+
+void Processor::z_call_n() {
+ if (zargs[0] != 0)
+ call(zargs[0], zargc - 1, zargs + 1, 1);
+}
+
+void Processor::z_call_s() {
+ if (zargs[0] != 0)
+ call(zargs[0], zargc - 1, zargs + 1, 0);
+ else
+ store(0);
+}
+
+void Processor::z_check_arg_count() {
+ if (_fp == _stack + STACK_SIZE)
+ branch(zargs[0] == 0);
+ else
+ branch(zargs[0] <= (*_fp & 0xff));
+}
+
+void Processor::z_jump() {
+ long pc;
+ GET_PC(pc);
+
+ pc += (short)zargs[0] - 2;
+
+ if ((uint)pc >= story_size)
+ runtimeError(ERR_ILL_JUMP_ADDR);
+
+ SET_PC(pc);
+}
+
+void Processor::z_nop() {
+ // Do nothing
+}
+
+void Processor::z_quit() {
+ _finished = 9999;
+}
+
+void Processor::z_ret() {
+ ret(zargs[0]);
+}
+
+void Processor::z_ret_popped() {
+ ret(*_sp++);
+}
+
+void Processor::z_rfalse() {
+ ret(0);
+}
+
+void Processor::z_rtrue() {
+ ret(1);
+}
+
+void Processor::z_random() {
+ if ((short) zargs[0] <= 0) {
+ // set random seed
+ seed_random(- (short) zargs[0]);
+ store(0);
+
+ } else {
+ // generate random number
+ zword result;
+ if (_randomInterval != 0) {
+ // ...in special mode
+ result = _randomCtr++;
+ if (_randomCtr == _randomInterval)
+ _randomCtr = 0;
+ } else {
+ // ...in standard mode
+ result = _random.getRandomNumber(0xffff);
+ }
+
+ store((zword)(result % zargs[0] + 1));
+ }
+}
+
+void Processor::z_sound_effect() {
+ zword number = zargs[0];
+ zword effect = zargs[1];
+ zword volume = zargs[2];
+
+ if (zargc < 1)
+ number = 0;
+ if (zargc < 2)
+ effect = EFFECT_PLAY;
+ if (zargc < 3)
+ volume = 8;
+
+ if (number >= 3 || number == 0) {
+ _soundLocked = true;
+
+ if (_storyId == LURKING_HORROR && (number == 9 || number == 16)) {
+ if (effect == EFFECT_PLAY) {
+ next_sample = number;
+ next_volume = volume;
+
+ _soundLocked = false;
+
+ if (!_soundPlaying)
+ start_next_sample();
+ } else {
+ _soundLocked = false;
+ }
+ return;
+ }
+
+ _soundPlaying = 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;
+ }
+
+ _soundLocked = false;
+ } else {
+ os_beep(number);
+ }
+}
+
+void Processor::z_piracy() {
+ branch(!_piracy);
+}
+
+void Processor::z_save_undo(void) {
+ store((zword)save_undo());
+}
+
+void Processor::z_restore_undo(void) {
+ store((zword)restore_undo());
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
new file mode 100644
index 0000000000..074100abab
--- /dev/null
+++ b/engines/gargoyle/frotz/processor.h
@@ -0,0 +1,1322 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_FROTZ_PROCESSOR
+#define GARGOYLE_FROTZ_PROCESSOR
+
+#include "gargoyle/frotz/buffer.h"
+#include "gargoyle/frotz/err.h"
+#include "gargoyle/frotz/mem.h"
+#include "gargoyle/frotz/glk_interface.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define CODE_BYTE(v) v = *pcp++
+#define CODE_WORD(v) v = READ_BE_UINT16(pcp += 2)
+#define CODE_IDX_WORD(v,i) v = READ_BE_UINT16(pcp + i)
+#define GET_PC(v) v = pcp - zmp
+#define SET_PC(v) pcp = zmp + v
+
+
+enum string_type {
+ LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
+};
+
+class Processor;
+typedef void (Processor::*Opcode)();
+
+/**
+ * Zcode processor
+ */
+class Processor : public virtual Mem, public Errors, public GlkInterface, public UserOptions {
+private:
+ Opcode op0_opcodes[16];
+ Opcode op1_opcodes[16];
+ static Opcode var_opcodes[64];
+ static Opcode ext_opcodes[64];
+ int _finished;
+ zword zargs[8];
+ int zargc;
+ uint _randomInterval;
+ uint _randomCtr;
+ bool first_restart;
+
+ // Stack data
+ zword _stack[STACK_SIZE];
+ zword *_sp;
+ zword *_fp;
+ zword _frameCount;
+
+ // Text related fields
+ static zchar ZSCII_TO_LATIN1[];
+ zchar *_decoded, *_encoded;
+ int _resolution;
+private:
+ /**
+ * \defgroup General support methods
+ * @{
+ */
+
+ /**
+ * Load an operand, either a variable or a constant.
+ */
+ void load_operand(zbyte type);
+
+ /**
+ * Given the operand specifier byte, load all (up to four) operands
+ * for a VAR or EXT opcode.
+ */
+ void load_all_operands(zbyte specifier);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * Store an operand, either as a variable or pushed on the stack.
+ */
+ void store(zword value);
+
+ /*
+ * 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);
+
+ /**
+ * Set the seed value for the random number generator.
+ */
+ void seed_random(int value);
+
+ /**@}*/
+
+ /**
+ * \defgroup Input support methods
+ * @{
+ */
+
+ /**
+ * Check if the given key is an input terminator.
+ */
+ bool is_terminator(zchar key);
+
+ /**
+ * Ask the user a question; return true if the answer is yes.
+ */
+ bool read_yes_or_no(const char *s);
+
+ /**
+ * Read a string from the current input stream.
+ */
+ void read_string(int max, zchar *buffer);
+
+ /**
+ * Ask the user to type in a number and return it.
+ */
+ int read_number();
+
+ /**@}*/
+
+ /**
+ * \defgroup Object support methods
+ * @{
+ */
+
+ /**
+ * Calculate the address of an object.
+ */
+ zword object_address(zword obj);
+
+ /**
+ * Return the address of the given object's name.
+ */
+ zword object_name(zword object);
+
+ /**
+ * Calculate the start address of the property list associated with an object.
+ */
+ zword first_property(zword obj);
+
+ /**
+ * Calculate the address of the next property in a property list.
+ */
+ zword next_property(zword prop_addr);
+
+ /**
+ * Unlink an object from its parent and siblings.
+ */
+ void unlink_object(zword object);
+
+ /**@}*/
+
+ /**
+ * \defgroup Screen support methods
+ * @{
+ */
+
+ void screen_char(zchar c);
+ void screen_new_line();
+ void screen_word(const zchar *s);
+ void screen_mssg_on();
+ void screen_mssg_off();
+
+ /**@}*/
+
+ /**
+ * \defgroup Stream support methods
+ * @{
+ */
+
+ /**
+ * Write a single character to the scrollback buffer.
+ *
+ */
+ void scrollback_char(zchar c);
+
+ /**
+ * Write a string to the scrollback buffer.
+ */
+ void scrollback_word(const zchar *s);
+
+ /**
+ * Send an input line to the scrollback buffer.
+ */
+ void scrollback_write_input(const zchar *buf, zchar key);
+
+ /**
+ * Remove an input line from the scrollback buffer.
+ */
+ void scrollback_erase_input(const zchar *buf);
+
+ /**
+ * Start printing a "debugging" message.
+ */
+ void stream_mssg_on();
+
+ /**
+ * Stop printing a "debugging" message.
+ */
+ void stream_mssg_off();
+
+ /**
+ * Send a single character to the output stream.
+ */
+ void stream_char(zchar c);
+
+ /**
+ * Send a string of characters to the output streams.
+ */
+ void stream_word(const zchar *s);
+
+ /**
+ * Send a newline to the output streams.
+ */
+ void stream_new_line();
+
+ /**
+ * Read a single keystroke from the current input stream.
+ */
+ zchar stream_read_key(zword timeout, zword routine, bool hot_keys);
+
+ /**
+ * 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);
+
+ /**@}*/
+
+ /**
+ * \defgroup Text support methods
+ * @{
+ */
+
+ /**
+ * Map a ZSCII character into Unicode.
+ */
+ zchar translate_from_zscii(zbyte c);
+
+ /**
+ * Convert a Unicode character to ZSCII, returning 0 on failure.
+ */
+ zbyte unicode_to_zscii(zchar c);
+
+ /**
+ * Map a Unicode character onto the ZSCII alphabet.
+ *
+ */
+ zbyte translate_to_zscii(zchar c);
+
+ /**
+ * Return a character from one of the three character sets.
+ */
+ zchar alphabet(int set, int index);
+
+ /**
+ * Find the number of bytes used for dictionary resolution.
+ */
+ void find_resolution();
+
+ /**
+ * Copy a ZSCII string from the memory to the global "decoded" string.
+ */
+ void load_string(zword addr, zword length);
+
+ /**
+ * 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, from V4 it is three, and from V9 it is any number of 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.
+ */
+ void encode_text(int padding);
+
+ /**
+ * 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.
+ */
+ void decode_text(string_type st, zword addr);
+
+ /**
+ * Print a signed 16bit number.
+ */
+ void print_num(zword value);
+
+ /**
+ * print_object
+ *
+ * Print an object description.
+ *
+ */
+ void print_object(zword object);
+
+ /**
+ * 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.
+ */
+ zword lookup_text(int padding, zword dct);
+
+ /**
+ * 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.
+ *
+ */
+ void tokenise_text(zword text, zword length, zword from, zword parse, zword dct, bool flag);
+
+ /**
+ * Split an input line into words and translate the words to tokens.
+ */
+ void tokenise_line(zword text, zword token, zword dct, bool flag);
+
+ /**
+ * 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);
+
+ /**
+ * Convert a Unicode character to lowercase.
+ * Taken from Zip2000 by Kevin Bracey.
+ */
+ zchar unicode_tolower(zchar c);
+
+ /**@}*/
+private:
+ /**
+ * \defgroup General Opcode methods
+ * @{
+ */
+
+ /*
+ * Load and execute an extended opcode.
+ */
+ void __extended__();
+
+ /*
+ * Exit game because an unknown opcode has been hit.
+ */
+ void __illegal__();
+
+ /*
+ * Store the current _stack frame for later use with z_throw.
+ *
+ * no zargs used
+ */
+ void z_catch();
+
+ /**
+ * Go back to the given _stack frame and return the given value.
+ *
+ * zargs[0] = value to return
+ * zargs[1] = _stack frame
+ */
+ void z_throw();
+
+ /*
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * Branch if subroutine was called with >= n arg's.
+ *
+ * zargs[0] = number of arguments
+ */
+ void z_check_arg_count();
+
+ /**
+ * Jump unconditionally to the given address.
+ *
+ * zargs[0] = PC relative address
+ */
+ void z_jump();
+
+ /*
+ * No operation.
+ *
+ * no zargs used
+ */
+ void z_nop();
+
+ /*
+ * Stop game and exit interpreter.
+ *
+ * no zargs used
+ */
+ void z_quit();
+
+ /*
+ * Return from a subroutine with the given value.
+ *
+ * zargs[0] = value to return
+ */
+ void z_ret();
+
+ /*
+ * Return from a subroutine with a value popped off the stack.
+ *
+ * no zargs used
+ */
+ void z_ret_popped();
+
+ /*
+ * Return from a subroutine with false (0).
+ *
+ * no zargs used
+ */
+ void z_rfalse();
+
+ /*
+ * Return from a subroutine with true (1).
+ *
+ * no zargs used
+ */
+ void z_rtrue();
+
+ /**
+ * Store a random number or set the random number seed.
+ *
+ * zargs[0] = range (positive) or seed value (negative)
+ */
+ void z_random();
+
+ /**
+ * 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();
+
+ /**
+ * Branch if the story file is a legal copy
+ */
+ void z_piracy();
+
+ /**
+ * Save the current Z-machine state for a future undo.
+ *
+ * no zargs used
+ */
+ void z_save_undo();
+
+ /**
+ * Restore a Z-machine state from memory.
+ *
+ * no zargs used
+ */
+ void z_restore_undo();
+
+ /**@}*/
+
+ /**
+ * \defgroup Input Opcode methods
+ * @{
+ */
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * z_read_mouse, write the current mouse status into a table.
+ *
+ * zargs[0] = address of table
+ */
+ void z_read_mouse();
+
+ /**@}*/
+
+ /**
+ * \defgroup Math Opcode methods
+ * @{
+ */
+
+ /**
+ * 16 bit addition.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_add();
+
+ /**
+ * Bitwise AND operation.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_and();
+
+ /**
+ * Arithmetic SHIFT operation.
+ *
+ * zargs[0] = value
+ * zargs[1] = #positions to shift left (positive) or right
+ */
+ void z_art_shift();
+
+ /**
+ * Signed 16bit division.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_div();
+
+ /**
+ * B ranch 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();
+
+ /**
+ * Branch if the first value is greater than the second.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_jg();
+
+ /**
+ * Branch if the first value is less than the second.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_jl();
+
+ /**
+ * Branch if value is zero.
+ *
+ * zargs[0] = value
+ */
+ void z_jz();
+
+ /**
+ * Logical SHIFT operation.
+ *
+ * zargs[0] = value
+ * zargs[1] = #positions to shift left (positive) or right (negative)
+ */
+ void z_log_shift();
+
+ /*
+ * Remainder after signed 16bit division.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_mod();
+
+ /**
+ * 16 bit multiplication.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_mul();
+
+ /**
+ * Bitwise NOT operation.
+ *
+ * zargs[0] = value
+ */
+ void z_not();
+
+ /**
+ * Bitwise OR operation.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_or();
+
+ /**
+ * 16 bit substraction.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_sub();
+
+ /**
+ * 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();
+
+ /**@}*/
+
+ /**
+ * \defgroup Object Opcode methods
+ * @{
+ */
+
+ /**
+ * Branch if the first object is inside the second.
+ *
+ * zargs[0] = first object
+ * zargs[1] = second object
+ */
+ void z_jin();
+
+ /**
+ * Store the child of an object.
+ *
+ * zargs[0] = object
+ */
+ void z_get_child();
+
+ /**
+ * 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();
+
+ /**
+ * Store the parent of an object.
+ *
+ * zargs[0] = object
+ */
+ void z_get_parent();
+
+ /**
+ * Store the value of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to be examined
+ */
+ void z_get_prop();
+
+ /**
+ * Store the address of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to be examined
+ */
+ void z_get_prop_addr();
+
+ /**
+ * Store the length of an object property.
+ *
+ * zargs[0] = address of property to be examined
+ */
+ void z_get_prop_len();
+
+ /**
+ * Store the sibling of an object.
+ *
+ * zargs[0] = object
+ */
+ void z_get_sibling();
+
+ /**
+ * Make an object the first child of another object.
+ *
+ * zargs[0] = object to be moved
+ * zargs[1] = destination object
+ */
+ void z_insert_obj();
+
+ /**
+ * 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();
+
+ /**
+ * Unlink an object from its parent and siblings.
+ *
+ * zargs[0] = object
+ */
+ void z_remove_obj();
+
+ /**
+ * Set an object attribute.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to set
+ */
+ void z_set_attr();
+
+ /**
+ * Branch if an object attribute is set.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to test
+ */
+ void z_test_attr();
+
+ /**
+ * Clear an object attribute.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to be cleared
+ */
+ void z_clear_attr();
+
+ /**@}*/
+
+ /**
+ * \defgroup Screen Opcode methods
+ * @{
+ */
+
+ /**
+ * Turn text buffering on/off.
+ *
+ * zargs[0] = new text buffering flag (0 or 1)
+ */
+ void z_buffer_mode();
+
+ /**
+ * Set the screen buffering mode.
+ *
+ * zargs[0] = mode
+ */
+ void z_buffer_screen();
+
+ /**
+ * 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();
+
+ /**
+ * Erase a window or the screen to background colour.
+ *
+ * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
+ */
+ void z_erase_window();
+
+ /**
+ * Write the cursor coordinates into a table.
+ *
+ * zargs[0] = address to write information to
+ */
+ void z_get_cursor();
+
+ /**
+ * 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();
+
+ /**
+ * Set the foreground and background colours
+ * to specific RGB colour values.
+ *
+ * zargs[0] = foreground colour
+ * zargs[1] = background colour
+ * zargs[2] = window (-3 is the current one, optional)
+ */
+ void z_set_true_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();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * Select the current window.
+ *
+ * zargs[0] = window to be selected (-3 is the current one)
+ */
+ void z_set_window();
+
+ /**
+ * Display the status line for V1 to V3 games.
+ *
+ * no zargs used
+ */
+ void pad_status_line(int column);
+
+ /**
+ * Display the status line for V1 to V3 games.
+ *
+ * no zargs used
+ */
+ void z_show_status();
+
+ /**
+ * 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();
+
+ /**@}*/
+
+ /**
+ * \defgroup Stream Opcode methods
+ * @{
+ */
+
+ /**
+ * Select an input stream.
+ *
+ * zargs[0] = input stream to be selected
+ */
+ void z_input_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();
+
+ /**
+ * Re-load dynamic area, clear the stack and set the PC.
+ *
+ * no zargs used
+ */
+ void z_restart();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * Check the story file integrity.
+ *
+ * no zargs used
+ */
+ void z_verify();
+
+ /**@}*/
+
+ /**
+ * \defgroup Table Opcode methods
+ * @{
+ */
+
+ /**
+ * 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();
+
+ /**
+ * Store a value from a table of bytes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry to store
+ */
+ void z_loadb();
+
+ /**
+ * Store a value from a table of words.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry to store
+ */
+ void z_loadw();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**@}*/
+
+ /**
+ * \defgroup Text Opcode methods
+ * @{
+ */
+
+ /**
+ * Test if a unicode character can be printed (bit 0) and read (bit 1).
+ *
+ * zargs[0] = Unicode
+ */
+ void z_check_unicode();
+
+ /**
+ * 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();
+
+ /**
+ * Print a new line.
+ *
+ * no zargs used
+ *
+ */
+ void z_new_line();
+
+ /**
+ * Print a string embedded in the instruction stream.
+ *
+ * no zargs used
+ */
+ void z_print();
+
+ /**
+ * Print a string from the lower 64KB.
+ *
+ * zargs[0] = address of string to print
+ */
+ void z_print_addr();
+
+ /**
+ * Print a single ZSCII character.
+ *
+ * zargs[0] = ZSCII character to be printed
+ */
+ void z_print_char();
+
+ /**
+ * Print a formatted table.
+ *
+ * zargs[0] = address of formatted table to be printed
+ */
+ void z_print_form();
+
+ /**
+ * Print a signed number.
+ *
+ * zargs[0] = number to print
+ */
+ void z_print_num();
+
+ /**
+ * Print an object description.
+ *
+ * zargs[0] = number of object to be printed
+ */
+ void z_print_obj();
+
+ /**
+ * Print the string at the given packed address.
+ *
+ * zargs[0] = packed address of string to be printed
+ */
+ void z_print_paddr();
+
+ /*
+ * Print the string at PC, print newline then return true.
+ *
+ * no zargs used
+ */
+ void z_print_ret();
+
+ /**
+ * Print a string of ASCII characters.
+ */
+ void print_string(const char *s);
+
+ /**
+ * Print unicode character
+ *
+ * zargs[0] = Unicode
+ */
+ void z_print_unicode();
+
+ /**
+ * 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();
+
+ /**@}*/
+
+ /**
+ * \defgroup Variable Opcode methods
+ * @{
+ */
+
+ /**
+ * Decrement a variable.
+ *
+ * zargs[0] = variable to decrement
+ */
+ void z_dec();
+
+ /**
+ * 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();
+
+ /**
+ * Increment a variable.
+ *
+ * zargs[0] = variable to increment
+ */
+ void z_inc();
+
+ /**
+ * 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();
+
+ /**
+ * Store the value of a variable.
+ *
+ * zargs[0] = variable to store
+ */
+ void z_load();
+
+ /**
+ * Pop a value off the game stack and discard it.
+ *
+ * no zargs used
+ */
+ void z_pop();
+
+ /**
+ * 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();
+
+ /**
+ * 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();
+
+ /**
+ * Push a value onto the game stack.
+ *
+ * zargs[0] = value to push onto the stack
+ */
+ void z_push();
+
+ /**
+ * 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();
+
+ /**
+ * Write a value to a variable.
+ *
+ * zargs[0] = variable to be written to
+ * zargs[1] = value to write
+ */
+ void z_store();
+
+ /**@}*/
+protected:
+ /**
+ * Get the PC. Is implemented by the Processor class, which derives from Errors
+ */
+ virtual zword getPC() const { return pcp - zmp; }
+public:
+ /**
+ * Constructor
+ */
+ Processor(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+ /**
+ * Initialization
+ */
+ virtual void initialize() override;
+
+ /**
+ * Z-code interpreter main loop
+ */
+ void interpret();
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
new file mode 100644
index 0000000000..e7f92ab607
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -0,0 +1,207 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Implement method stubs
+static zchar stream_read_key(zword, zword, bool) { return 0; }
+static zchar stream_read_input(int, zchar *, zword, zword, bool, bool) { return 0;}
+static void storeb(zword, zchar) {}
+static void storew(zword, zword) {}
+static void save_undo() {}
+static zword os_read_mouse() { return 0; }
+
+
+#define INPUT_BUFFER_SIZE 200
+
+void Processor::z_make_menu() {
+ // 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).
+ branch (false);
+}
+
+bool Processor::read_yes_or_no(const char *s) {
+ zchar key;
+
+ print_string(s);
+ print_string("? (y/n) >");
+
+ key = stream_read_key(0, 0, false);
+
+ if (key == 'y' || key == 'Y') {
+ print_string("y\n");
+ return true;
+ } else {
+ print_string("n\n");
+ return false;
+ }
+}
+
+void Processor::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);
+}
+
+int Processor::read_number() {
+ 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;
+
+}
+
+void Processor::z_read() {
+ 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
+ false, // 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) {
+ buffer[i] = unicode_tolower (buffer[i]);
+ }
+
+ 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));
+}
+
+void Processor::z_read_char() {
+ 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
+ false // enable hot keys
+ );
+
+ if (key == ZC_BAD)
+ return;
+
+ // Store key
+ store (translate_to_zscii (key));
+}
+
+void Processor::z_read_mouse(){
+ zword btn;
+
+ // Read the mouse position, the last menu click 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), menu_selected); // menu selection
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_maths.cpp b/engines/gargoyle/frotz/processor_maths.cpp
new file mode 100644
index 0000000000..e64dd55a1a
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_maths.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::z_add() {
+ store((zword)((short)zargs[0] + (short)zargs[1]));
+}
+
+void Processor::z_and() {
+ store((zword)(zargs[0] & zargs[1]));
+}
+
+void Processor::z_art_shift() {
+ if ((short)zargs[1] > 0)
+ store((zword)((short)zargs[0] << (short)zargs[1]));
+ else
+ store((zword)((short)zargs[0] >> - (short)zargs[1]));
+}
+
+void Processor::z_div() {
+ if (zargs[1] == 0)
+ runtimeError(ERR_DIV_ZERO);
+
+ store((zword)((short)zargs[0] / (short)zargs[1]));
+}
+
+void Processor::z_je() {
+ branch(
+ zargc > 1 && (zargs[0] == zargs[1] || (
+ zargc > 2 && (zargs[0] == zargs[2] || (
+ zargc > 3 && (zargs[0] == zargs[3])))))
+ );
+}
+
+void Processor::z_jg() {
+ branch((short)zargs[0] > (short)zargs[1]);
+}
+
+void Processor::z_jl() {
+ branch((short)zargs[0] < (short)zargs[1]);
+}
+
+void Processor::z_jz() {
+ branch((short)zargs[0] == 0);
+}
+
+void Processor::z_log_shift() {
+ if ((short)zargs[1] > 0)
+ store((zword)(zargs[0] << (short)zargs[1]));
+ else
+ store((zword)(zargs[0] >> - (short)zargs[1]));
+}
+
+void Processor::z_mod() {
+ if (zargs[1] == 0)
+ runtimeError(ERR_DIV_ZERO);
+
+ store((zword)((short)zargs[0] % (short)zargs[1]));
+}
+
+void Processor::z_mul() {
+ store((zword)((short)zargs[0] * (short)zargs[1]));
+}
+
+void Processor::z_not() {
+ store((zword)~zargs[0]);
+}
+
+void Processor::z_or() {
+ store((zword)(zargs[0] | zargs[1]));
+}
+
+void Processor::z_sub() {
+ store((zword)((short)zargs[0] - (short)zargs[1]));
+}
+
+void Processor::z_test() {
+ branch((zargs[0] & zargs[1]) == zargs[1]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_objects.cpp b/engines/gargoyle/frotz/processor_objects.cpp
new file mode 100644
index 0000000000..3d2b17162d
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_objects.cpp
@@ -0,0 +1,735 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Method stubs to implement
+static void new_line() {}
+
+#define MAX_OBJECT 2000
+
+enum O1 {
+ O1_PARENT = 4,
+ O1_SIBLING = 5,
+ O1_CHILD = 6,
+ O1_PROPERTY_OFFSET = 7,
+ O1_SIZE = 9
+};
+
+enum O4 {
+ O4_PARENT = 6,
+ O4_SIBLING = 8,
+ O4_CHILD = 10,
+ O4_PROPERTY_OFFSET = 12,
+ O4_SIZE = 14
+};
+
+zword Processor::object_address(zword obj) {
+ // 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();
+ runtimeError(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);
+}
+
+zword Processor::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;
+}
+
+zword Processor::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;
+}
+
+zword Processor::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)
+ // demanded by Spec 1.0
+ value = 64;
+ }
+
+ // Add property length to current property pointer
+ return prop_addr + value + 1;
+}
+
+void Processor::unlink_object(zword object) {
+ zword obj_addr;
+ zword parent_addr;
+ zword sibling_addr;
+
+ if (object == 0) {
+ runtimeError(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);
+ }
+ }
+}
+
+void Processor::z_clear_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (_storyId == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute assignment display a short note
+ if (_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) {
+ runtimeError(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);
+}
+
+void Processor::z_jin() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_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) {
+ runtimeError(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]);
+ }
+}
+
+void Processor::z_get_child() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@get_child ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(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);
+ }
+}
+
+void Processor::z_get_next_prop() {
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(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])
+ runtimeError(ERR_NO_PROP);
+ }
+
+ // Return the property id
+ LOW_BYTE(prop_addr, value);
+ store((zword) (value & mask));
+}
+
+void Processor::z_get_parent() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@get_parent ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(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);
+ }
+}
+
+void Processor::z_get_prop() {
+ zword prop_addr;
+ zword wprop_val;
+ zbyte bprop_val;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(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);
+}
+
+void Processor::z_get_prop_addr() {
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PROP_ADDR_0);
+ store(0);
+ return;
+ }
+
+ if (_storyId == 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);
+ }
+}
+
+void Processor::z_get_prop_len() {
+ 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);
+}
+
+void Processor::z_get_sibling() {
+ zword obj_addr;
+
+ if (zargs[0] == 0) {
+ runtimeError(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);
+ }
+}
+
+void Processor::z_insert_obj() {
+ 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 (_object_movement) {
+ stream_mssg_on();
+ print_string("@move_obj ");
+ print_object(obj1);
+ print_string(" ");
+ print_object(obj2);
+ stream_mssg_off();
+ }
+
+ if (obj1 == 0) {
+ runtimeError(ERR_MOVE_OBJECT_0);
+ return;
+ }
+
+ if (obj2 == 0) {
+ runtimeError(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);
+ }
+}
+
+void Processor::z_put_prop() {
+ zword prop_addr;
+ zword value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(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])
+ runtimeError(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);
+ }
+}
+
+void Processor::z_remove_obj() {
+ // If we are monitoring object movements display a short note
+ if (_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]);
+}
+
+void Processor::z_set_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (_storyId == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute assignment display a short note
+ if (_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) {
+ runtimeError(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);
+}
+
+void Processor::z_test_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute testing display a short note
+ if (_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) {
+ runtimeError(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)));
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_screen.cpp b/engines/gargoyle/frotz/processor_screen.cpp
new file mode 100644
index 0000000000..5b3b8cb379
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_screen.cpp
@@ -0,0 +1,455 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Method stubs to implement
+static void storew(zword, zword) {}
+static void print_char(zchar) {}
+
+
+void Processor::screen_char(zchar c) {
+ // TODO
+}
+
+void Processor::screen_new_line() {
+ // TODO
+}
+
+void Processor::screen_word(const zchar *s) {
+ // TODO
+}
+
+void Processor::screen_mssg_on() {
+ // TODO
+}
+
+void Processor::screen_mssg_off() {
+ // TODO
+}
+
+
+void Processor::z_buffer_mode() {
+}
+
+void Processor::z_buffer_screen() {
+ store(0);
+}
+
+void Processor::z_erase_line() {
+ int i;
+
+ if (gos_upper && gos_curwin == gos_upper) {
+ for (i = 0; i < h_screen_cols + 1 - curx; i++)
+ glk_put_char(' ');
+ glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
+ }
+}
+
+void Processor::z_erase_window() {
+ short w = zargs[0];
+ if (w == -2)
+ {
+ if (gos_upper) {
+ glk_set_window(gos_upper);
+#ifdef GARGLK
+ garglk_set_zcolors(curr_fg, curr_bg);
+#endif /* GARGLK */
+ glk_window_clear(gos_upper);
+ glk_set_window(gos_curwin);
+ }
+ glk_window_clear(gos_lower);
+ }
+ if (w == -1)
+ {
+ if (gos_upper) {
+ glk_set_window(gos_upper);
+#ifdef GARGLK
+ garglk_set_zcolors(curr_fg, curr_bg);
+#endif /* GARGLK */
+ glk_window_clear(gos_upper);
+ }
+ glk_window_clear(gos_lower);
+ split_window(0);
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+ }
+ if (w == 0)
+ glk_window_clear(gos_lower);
+ if (w == 1 && gos_upper)
+ glk_window_clear(gos_upper);
+}
+
+void Processor::z_get_cursor() {
+ storew((zword) (zargs[0] + 0), cury);
+ storew((zword) (zargs[0] + 2), curx);
+}
+
+void Processor::z_print_table() {
+ zword addr = zargs[0];
+ zword x;
+ int i, j;
+
+ // Supply default arguments
+ if (zargc < 3)
+ zargs[2] = 1;
+ if (zargc < 4)
+ zargs[3] = 0;
+
+ // Write text in width x height rectangle
+ x = curx;
+
+ for (i = 0; i < zargs[2]; i++) {
+ if (i != 0) {
+ cury += 1;
+ curx = x;
+ }
+
+ for (j = 0; j < zargs[1]; j++) {
+
+ zbyte c;
+
+ LOW_BYTE(addr, c);
+ addr++;
+
+ print_char(c);
+ }
+
+ addr += zargs[3];
+ }
+}
+
+#define zB(i) ((((i >> 10) & 0x1F) << 3) | (((i >> 10) & 0x1F) >> 2))
+#define zG(i) ((((i >> 5) & 0x1F) << 3) | (((i >> 5) & 0x1F) >> 2))
+#define zR(i) ((((i ) & 0x1F) << 3) | (((i ) & 0x1F) >> 2))
+
+#define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i))
+
+void Processor::z_set_true_colour() {
+ int zfore = zargs[0];
+ int zback = zargs[1];
+
+ if (!(zfore < 0))
+ zfore = zRGB(zargs[0]);
+
+ if (!(zback < 0))
+ zback = zRGB(zargs[1]);
+
+#ifdef GARGLK
+ garglk_set_zcolors(zfore, zback);
+#endif /* GARGLK */
+
+ curr_fg = zfore;
+ curr_bg = zback;
+}
+
+static const int zcolor_map[] = {
+ -2, ///< 0 = current
+ -1, ///< 1 = default
+ 0x0000, ///< 2 = black
+ 0x001D, ///< 3 = red
+ 0x0340, ///< 4 = green
+ 0x03BD, ///< 5 = yellow
+ 0x59A0, ///< 6 = blue
+ 0x7C1F, ///< 7 = magenta
+ 0x77A0, ///< 8 = cyan
+ 0x7FFF, ///< 9 = white
+ 0x5AD6, ///< 10 = light grey
+ 0x4631, ///< 11 = medium grey
+ 0x2D6B, ///< 12 = dark grey
+};
+
+#define zcolor_NUMCOLORS (13)
+
+void Processor::z_set_colour() {
+ int zfore = zargs[0];
+ int zback = zargs[1];
+
+ switch (zfore) {
+ case -1:
+ zfore = -3;
+ break;
+
+ case 0:
+ case 1:
+ zfore = zcolor_map[zfore];
+ break;
+
+ default:
+ if (zfore < zcolor_NUMCOLORS)
+ zfore = zRGB(zcolor_map[zfore]);
+ break;
+ }
+
+ switch (zback) {
+ case -1:
+ zback = -3;
+
+ case 0:
+ case 1:
+ zback = zcolor_map[zback];
+ break;
+
+ default:
+ if (zback < zcolor_NUMCOLORS)
+ zback = zRGB(zcolor_map[zback]);
+ break;
+ }
+
+#ifdef GARGLK
+ garglk_set_zcolors(zfore, zback);
+#endif /* GARGLK */
+
+ curr_fg = zfore;
+ curr_bg = zback;
+}
+
+void Processor::z_set_font() {
+ zword font = zargs[0];
+
+ switch (font) {
+ case 0:
+ // previous font
+ temp_font = curr_font;
+ curr_font = prev_font;
+ prev_font = temp_font;
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ store (curr_font);
+ break;
+
+ case 1: /* normal font */
+ prev_font = curr_font;
+ curr_font = 1;
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ store (prev_font);
+ break;
+
+ case 4: /* fixed-pitch font*/
+ prev_font = curr_font;
+ curr_font = 4;
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ store (prev_font);
+ break;
+
+ case 2: // picture font, undefined per 1.1
+ case 3: // character graphics font
+ default: // unavailable
+ store (0);
+ break;
+ }
+}
+
+void Processor::z_set_cursor() {
+ cury = zargs[0];
+ curx = zargs[1];
+
+ if (gos_upper) {
+ if (cury > mach_status_ht) {
+ mach_status_ht = cury;
+ reset_status_ht();
+ }
+
+ glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
+ }
+}
+
+void Processor::z_set_text_style() {
+ int style;
+
+ if (zargs[0] == 0)
+ curstyle = 0;
+ else if (zargs[0] != 0xf000) /* not tickle time */
+ curstyle |= zargs[0];
+
+ if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
+ style = curstyle | FIXED_WIDTH_STYLE;
+ else
+ style = curstyle;
+
+ if (gos_linepending && gos_curwin == gos_linewin)
+ return;
+
+ if (style & REVERSE_STYLE) {
+#ifdef GARGLK
+ garglk_set_reversevideo(true);
+#endif /* GARGLK */
+ }
+
+ if (style & FIXED_WIDTH_STYLE) {
+ if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+ glk_set_style(style_BlockQuote); // monoz
+ else if (style & EMPHASIS_STYLE)
+ glk_set_style(style_Alert); // monoi
+ else if (style & BOLDFACE_STYLE)
+ glk_set_style(style_Subheader); // monob
+ else
+ glk_set_style(style_Preformatted); // monor
+ } else {
+ if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+ glk_set_style(style_Note); // propz
+ else if (style & EMPHASIS_STYLE)
+ glk_set_style(style_Emphasized); // propi
+ else if (style & BOLDFACE_STYLE)
+ glk_set_style(style_Header); // propb
+ else
+ glk_set_style(style_Normal); // propr
+ }
+
+ if (curstyle == 0) {
+#ifdef GARGLK
+ garglk_set_reversevideo(false);
+#endif /* GARGLK */
+ }
+}
+
+void Processor::z_set_window() {
+ int win = zargs[0];
+
+ if (win == 0) {
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+ } else {
+ if (gos_upper)
+ glk_set_window(gos_upper);
+ gos_curwin = gos_upper;
+ }
+
+ if (win == 0)
+ enable_scripting = true;
+ else
+ enable_scripting = false;
+
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+}
+
+void Processor::pad_status_line(int column) {
+ int spaces;
+ spaces = (h_screen_cols + 1 - curx) - column;
+ while (spaces-- > 0)
+ print_char(' ');
+}
+
+void Processor::z_show_status() {
+ zword global0;
+ zword global1;
+ zword global2;
+ zword addr;
+
+ bool brief = false;
+
+ if (!gos_upper)
+ return;
+
+ // 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);
+
+ // Move to top of the status window, and print in reverse style.
+ glk_set_window(gos_upper);
+ gos_curwin = gos_upper;
+
+#ifdef GARGLK
+ garglk_set_reversevideo(true);
+#endif /* GARGLK */
+
+ curx = cury = 1;
+ glk_window_move_cursor(gos_upper, 0, 0);
+
+ // 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
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+}
+
+void Processor::z_split_window() {
+ split_window(zargs[0]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
new file mode 100644
index 0000000000..5fe526fbe4
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -0,0 +1,533 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Implement method stubs
+static void os_scrollback_char(zchar) {}
+static void os_scrollback_erase(zword) {}
+static void flush_buffer() {}
+static void script_open() {}
+static void script_close() {}
+static void script_mssg_on() {}
+static void script_mssg_off() {}
+static void script_char(zchar) {}
+static void script_word(const zchar *) {}
+static void script_new_line() {}
+static void script_erase_input(const zchar *) {}
+static void script_write_input(zchar *, char) {}
+static void memory_open(zword, zword, bool) {}
+static void memory_close() {}
+static void memory_word(const zchar *) {}
+static void memory_new_line() {}
+static void replay_open() {}
+static void replay_close() {}
+static zchar replay_read_key() { return 0; }
+static zchar replay_read_input(zchar *) { return 0; }
+static zchar console_read_key(zword) { return 0; }
+static zchar console_read_input(uint, zchar *, uint, bool) { return 0; }
+static void record_open() {}
+static void record_close() {}
+static void record_write_key(zchar) {}
+static void record_write_input(zchar *, zchar) {}
+static void os_restart_game(zword) {}
+static void restart_header() {}
+
+
+void Processor::scrollback_char (zchar c) {
+ if (c == ZC_INDENT)
+ { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; }
+ if (c == ZC_GAP)
+ { scrollback_char (' '); scrollback_char (' '); return; }
+
+ os_scrollback_char(c);
+}
+
+void Processor::scrollback_word(const zchar *s) {
+ int i;
+
+ for (i = 0; s[i] != 0; i++) {
+ if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
+ i++;
+ else
+ scrollback_char(s[i]);
+ }
+}
+
+void Processor::scrollback_write_input(const zchar *buf, zchar key) {
+ int i;
+
+ for (i = 0; buf[i] != 0; i++)
+ scrollback_char (buf[i]);
+
+ if (key == ZC_RETURN)
+ scrollback_char ('\n');
+}
+
+void Processor::scrollback_erase_input(const zchar *buf) {
+ int width;
+ int i;
+
+ for (i = 0, width = 0; buf[i] != 0; i++)
+ width++;
+
+ os_scrollback_erase(width);
+
+}
+
+void Processor::stream_mssg_on() {
+ flush_buffer();
+
+ if (ostream_screen)
+ screen_mssg_on();
+ if (ostream_script && enable_scripting)
+ script_mssg_on();
+
+ message = true;
+}
+
+void Processor::stream_mssg_off() {
+ flush_buffer();
+
+ if (ostream_screen)
+ screen_mssg_off();
+ if (ostream_script && enable_scripting)
+ script_mssg_off();
+
+ message = false;
+}
+
+void Processor::stream_char(zchar c) {
+ if (ostream_screen)
+ screen_char(c);
+ if (ostream_script && enable_scripting)
+ script_char(c);
+ if (enable_scripting)
+ scrollback_char(c);
+}
+
+void Processor::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);
+ if (enable_scripting)
+ scrollback_word(s);
+ }
+}
+
+void Processor::stream_new_line() {
+ if (ostream_memory && !message)
+ memory_new_line();
+ else {
+ if (ostream_screen)
+ screen_new_line();
+ if (ostream_script && enable_scripting)
+ script_new_line();
+ if (enable_scripting)
+ os_scrollback_char ('\n');
+ }
+}
+
+zchar Processor::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);
+
+ // 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;
+
+ // Return key
+ return key;
+}
+
+zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword routine,
+ bool hot_keys, bool no_scripting) {
+ zchar key = ZC_BAD;
+ bool no_scrollback = no_scripting;
+
+ if (h_version == V6 && _storyId == UNKNOWN && !ostream_script)
+ no_scrollback = false;
+
+ flush_buffer();
+
+ // Remove initial input from the transscript file or from the screen
+ if (ostream_script && enable_scripting && !no_scripting)
+ script_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);
+
+ // 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;
+
+ // Copy input line to transscript file or to the screen
+ if (ostream_script && enable_scripting && !no_scripting)
+ script_write_input(buf, key);
+
+ // Return terminating key
+ return key;
+}
+
+void Processor::z_input_stream() {
+ flush_buffer();
+
+ if (zargs[0] == 0 && istream_replay)
+ replay_close();
+ if (zargs[0] == 1 && !istream_replay)
+ replay_open();
+}
+
+void Processor::z_output_stream() {
+ 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;
+ default:
+ break;
+ }
+}
+
+void Processor::z_restart(void) {
+ flush_buffer();
+
+ os_restart_game(RESTART_BEGIN);
+
+ seed_random(0);
+
+ if (!first_restart) {
+ story_fp->seek(blorb_ofs);
+
+ if (story_fp->read(zmp, h_dynamic_size) != h_dynamic_size)
+ error("Story file read error");
+
+ } else {
+ first_restart = false;
+ }
+
+ restart_header();
+ restart_screen();
+
+ _sp = _fp = _stack + STACK_SIZE;
+ _frameCount = 0;
+
+ if (h_version != V6 && h_version != V9) {
+ long pc = (long)h_start_pc;
+ SET_PC(pc);
+ } else {
+ call(h_start_pc, 0, nullptr, 0);
+ }
+
+ os_restart_game(RESTART_END);
+}
+
+
+void Processor::z_save(void) {
+#ifdef TODO
+ bool success = false;
+
+ if (zargc != 0) {
+ // Open auxilary file
+ frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
+ filemode_Write, 0);
+ if (ref == nullptr)
+ goto finished;
+
+ // Write data
+ strid_t f = glk_stream_open_file(ref, filemode_Write);
+
+ glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
+
+ stream_result_t result;
+ glk_stream_close(f, &result);
+
+ } else {
+ long pc;
+ zword addr;
+ zword nsp, nfp;
+ int skip;
+ int i;
+
+ /* Open game file */
+
+ if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
+ goto finished;
+
+ if (option_save_quetzal) {
+ success = save_quetzal (gfp, story_fp, blorb_ofs);
+ } else {
+ /* Write game file */
+
+ fputc ((int) hi (h_release), gfp);
+ fputc ((int) lo (h_release), gfp);
+ fputc ((int) hi (h_checksum), gfp);
+ fputc ((int) lo (h_checksum), gfp);
+
+ GET_PC (pc)
+
+ fputc ((int) (pc >> 16) & 0xff, gfp);
+ fputc ((int) (pc >> 8) & 0xff, gfp);
+ fputc ((int) (pc) & 0xff, gfp);
+
+ nsp = (int) (_sp - _stack);
+ nfp = (int) (_fp - _stack);
+
+ fputc ((int) hi (nsp), gfp);
+ fputc ((int) lo (nsp), gfp);
+ fputc ((int) hi (nfp), gfp);
+ fputc ((int) lo (nfp), gfp);
+
+ for (i = nsp; i < STACK_SIZE; i++) {
+ fputc ((int) hi (_stack[i]), gfp);
+ fputc ((int) lo (_stack[i]), gfp);
+ }
+
+ fseek (story_fp, blorb_ofs, SEEK_SET);
+
+ for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
+ if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
+ fputc (skip, gfp);
+ fputc (zmp[addr], gfp);
+ skip = 0;
+ } else skip++;
+ }
+
+ /* Close game file and check for errors */
+
+ if (fclose (gfp) == EOF || 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);
+#endif
+}
+
+void Processor::z_restore() {
+#ifdef TODO
+ FILE *gfp;
+
+ zword success = 0;
+
+ if (zargc != 0) {
+
+ /* Get the file name */
+
+ /* Open auxilary file */
+
+ if ((gfp = frotzopenprompt(FILE_LOAD_AUX)) == nullptr)
+ goto finished;
+
+ /* Load auxilary file */
+
+ success = fread (zmp + zargs[0], 1, zargs[1], gfp);
+
+ /* Close auxilary file */
+
+ fclose (gfp);
+
+ } else {
+
+ long pc;
+ zword release;
+ zword addr;
+ int i;
+
+ /* Open game file */
+
+ if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
+ goto finished;
+
+ if (option_save_quetzal) {
+ success = restore_quetzal (gfp, story_fp, blorb_ofs);
+
+ } else {
+ /* Load game file */
+
+ release = (unsigned) fgetc (gfp) << 8;
+ release |= fgetc (gfp);
+
+ (void) fgetc (gfp);
+ (void) fgetc (gfp);
+
+ /* Check the release number */
+
+ if (release == h_release) {
+
+ pc = (long) fgetc (gfp) << 16;
+ pc |= (unsigned) fgetc (gfp) << 8;
+ pc |= fgetc (gfp);
+
+ SET_PC (pc);
+
+ _sp = _stack + (fgetc (gfp) << 8);
+ _sp += fgetc (gfp);
+ _fp = _stack + (fgetc (gfp) << 8);
+ _fp += fgetc (gfp);
+
+ for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
+ _stack[i] = (unsigned) fgetc (gfp) << 8;
+ _stack[i] |= fgetc (gfp);
+ }
+
+ fseek (story_fp, blorb_ofs, SEEK_SET);
+
+ for (addr = 0; addr < h_dynamic_size; addr++) {
+ int skip = fgetc (gfp);
+ for (i = 0; i < skip; i++)
+ zmp[addr++] = fgetc (story_fp);
+ zmp[addr] = fgetc (gfp);
+ (void) fgetc (story_fp);
+ }
+
+ /* Check for errors */
+
+ if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
+ success = -1;
+ else
+
+ /* Success */
+
+ success = 2;
+
+ } else print_string ("Invalid save file\n");
+ }
+
+ if ((short) success >= 0) {
+
+ /* Close game file */
+
+ fclose (gfp);
+
+ 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);
+#endif
+}
+
+void Processor::z_verify(void) {
+ zword checksum = 0;
+
+ // Sum all bytes in story file except header bytes
+ story_fp->seek(blorb_ofs + 64);
+
+ for (uint i = 64; i < story_size; i++)
+ checksum += story_fp->readByte();
+
+ // Branch if the checksums are equal
+ branch(checksum == h_checksum);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_table.cpp b/engines/gargoyle/frotz/processor_table.cpp
new file mode 100644
index 0000000000..4e660ca693
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_table.cpp
@@ -0,0 +1,125 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Method stubs to implement
+static void storeb(zword, zbyte) {}
+static void storew(zword, zword) {}
+
+
+void Processor::z_copy_table() {
+ 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);
+ }
+ }
+}
+
+void Processor::z_loadb() {
+ zword addr = zargs[0] + zargs[1];
+ zbyte value;
+
+ LOW_BYTE(addr, value);
+
+ store(value);
+}
+
+void Processor::z_loadw() {
+ zword addr = zargs[0] + 2 * zargs[1];
+ zword value;
+
+ LOW_WORD(addr, value);
+
+ store(value);
+}
+
+void Processor::z_scan_table() {
+ 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);
+}
+
+void Processor::z_storeb() {
+ storeb((zword) (zargs[0] + zargs[1]), zargs[2]);
+}
+
+void Processor::z_storew() {
+ storew((zword)(zargs[0] + 2 * zargs[1]), zargs[2]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_text.cpp b/engines/gargoyle/frotz/processor_text.cpp
new file mode 100644
index 0000000000..e660ac1fd9
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_text.cpp
@@ -0,0 +1,904 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Replace method stubs
+static void new_line() {}
+static void print_char(zchar) {}
+static void storeb(zword, zchar) {}
+static void storew(zword, zword) {}
+
+
+zchar Processor::ZSCII_TO_LATIN1[] = {
+ 0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb,
+ 0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9,
+ 0x0ed, 0x0f3, 0x0fa, 0x0fd, 0x0c1, 0x0c9, 0x0cd, 0x0d3,
+ 0x0da, 0x0dd, 0x0e0, 0x0e8, 0x0ec, 0x0f2, 0x0f9, 0x0c0,
+ 0x0c8, 0x0cc, 0x0d2, 0x0d9, 0x0e2, 0x0ea, 0x0ee, 0x0f4,
+ 0x0fb, 0x0c2, 0x0ca, 0x0ce, 0x0d4, 0x0db, 0x0e5, 0x0c5,
+ 0x0f8, 0x0d8, 0x0e3, 0x0f1, 0x0f5, 0x0c3, 0x0d1, 0x0d5,
+ 0x0e6, 0x0c6, 0x0e7, 0x0c7, 0x0fe, 0x0f0, 0x0de, 0x0d0,
+ 0x0a3, 0x153, 0x152, 0x0a1, 0x0bf
+};
+
+zchar Processor::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 && _storyId != 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);
+
+ if (unicode < 0x20)
+ return '?';
+
+ return unicode;
+ } else {
+ return '?';
+ }
+ } else {
+ // game uses standard set
+ if (c <= 0xdf) {
+ return ZSCII_TO_LATIN1[c - 0x9b];
+ } else {
+ return '?';
+ }
+ }
+ }
+
+ return (zchar)c;
+}
+
+zbyte Processor::unicode_to_zscii(zchar c) {
+ int i;
+
+ if (c >= ZC_LATIN1_MIN) {
+ if (hx_unicode_table != 0) {
+ // game has its own Unicode table
+ zbyte N;
+ 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 0;
+ } else {
+ // game uses standard set
+ for (i = 0x9b; i <= 0xdf; i++)
+ if (c == ZSCII_TO_LATIN1[i - 0x9b])
+ return (zbyte) i;
+
+ return 0;
+ }
+ }
+
+ return (zbyte)c;
+}
+
+zbyte Processor::translate_to_zscii(zchar c) {
+ if (c == ZC_SINGLE_CLICK)
+ return 0xfe;
+ if (c == ZC_DOUBLE_CLICK)
+ return 0xfd;
+ if (c == ZC_MENU_CLICK)
+ return 0xfc;
+ if (c == 0)
+ return 0;
+
+ c = unicode_to_zscii (c);
+ if (c == 0)
+ c = '?';
+
+ return (zbyte)c;
+}
+
+zchar Processor::alphabet(int set, int index) {
+ if (h_version > V1 && set == 2 && index == 1)
+ // always newline
+ return '\r';
+
+ 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];
+ }
+}
+
+void Processor::find_resolution() {
+ zword dct = h_dictionary;
+ zword entry_count;
+ zbyte sep_count;
+ zbyte entry_len;
+
+ LOW_BYTE(dct, sep_count);
+ dct += 1 + sep_count; // skip word separators
+ LOW_BYTE(dct, entry_len);
+ dct += 1; // skip entry length
+ LOW_WORD(dct, entry_count);
+ dct += 2; // get number of entries
+
+ if (h_version < V9) {
+ _resolution = (h_version <= V3) ? 2 : 3;
+ } else {
+ zword addr = dct;
+ zword code;
+
+ if (entry_count == 0)
+ runtimeError(ERR_DICT_LEN);
+
+ // check the first word in the dictionary
+ do {
+ LOW_WORD(addr, code);
+ addr += 2;
+ } while (!(code & 0x8000) && (addr - dct < entry_len + 1));
+
+ _resolution = (addr - dct) / 2;
+ }
+
+ if (2 * _resolution > entry_len) {
+ runtimeError(ERR_DICT_LEN);
+ }
+
+ _decoded = (zchar *)malloc (sizeof (zchar) * (3 * _resolution) + 1);
+ _encoded = (zchar *)malloc (sizeof (zchar) * _resolution);
+}
+
+void Processor::load_string (zword addr, zword length) {
+ int i = 0;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ while (i < 3 * _resolution) {
+ if (i < length) {
+ zbyte c;
+ LOW_BYTE(addr, c);
+ addr++;
+
+ _decoded[i++] = translate_from_zscii(c);
+ } else {
+ _decoded[i++] = 0;
+ }
+ }
+}
+
+void Processor::encode_text(int padding) {
+ static const zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0, 0, 0, 0 };
+ static const zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0, 0 };
+ static const zchar wait[] = { 'w', 'a', 'i', 't', 0, 0, 0, 0, 0 };
+
+ zbyte *zchars;
+ const zchar *ptr;
+ zchar c;
+ int i = 0;
+
+ if (_resolution == 0) find_resolution();
+
+ zchars = new byte[3 * (_resolution + 1)];
+ ptr = _decoded;
+
+ // Expand abbreviations that some old Infocom games lack
+ if (_expand_abbreviations && (h_version <= V8)) {
+ if (padding == 0x05 && _decoded[1] == 0) {
+ switch (_decoded[0]) {
+ case 'g': ptr = again; break;
+ case 'x': ptr = examine; break;
+ case 'z': ptr = wait; break;
+ default: break;
+ }
+ }
+ }
+
+ // Translate string to a sequence of Z-characters
+ while (i < 3 * _resolution) {
+ if ((c = *ptr++) != 0) {
+ int index, set;
+ zbyte c2;
+
+ if (c == ' ') {
+ zchars[i++] = 0;
+ continue;
+ }
+
+ // 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;
+ delete[] zchars;
+}
+
+#define outchar(c) if (st == VOCABULARY) *ptr++=c; else print_char(c)
+
+void Processor::decode_text(enum string_type st, zword addr) {
+ zchar *ptr = nullptr;
+ long byte_addr = 0;
+ zchar c2;
+ zword code;
+ zbyte c, prev_c = 0;
+ int shift_state = 0;
+ int shift_lock = 0;
+ int status = 0;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ // 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 if (h_version <= V8)
+ byte_addr = (long)addr << 3;
+ else {
+ // h_version == V9
+ long indirect = (long)addr << 2;
+ HIGH_LONG(indirect, byte_addr);
+ }
+
+ if ((uint)byte_addr >= story_size)
+ runtimeError(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;
+ zchar zc;
+
+ 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
+ zc = (prev_c << 5) | c;
+
+ if (zc > 767) {
+ // Unicode escape
+ while (zc-- > 767) {
+ if (st == LOW_STRING || st == VOCABULARY) {
+ LOW_WORD(addr, c2);
+ addr += 2;
+ } else if (st == HIGH_STRING || st == ABBREVIATION) {
+ HIGH_WORD(byte_addr, c2);
+ byte_addr += 2;
+ } else
+ CODE_WORD(c2);
+
+ outchar(c2 ^ 0xFFFF);
+ }
+ } else {
+ c2 = translate_from_zscii(zc);
+ outchar(c2);
+ }
+
+ status = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ prev_c = c;
+ }
+ } while (!(code & 0x8000));
+
+ if (st == VOCABULARY)
+ *ptr = 0;
+}
+
+#undef outchar
+
+void Processor::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);
+
+}
+
+void Processor::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);
+ }
+}
+
+void Processor::print_string(const char *s) {
+ char c;
+
+ while ((c = *s++) != 0) {
+ if (c == '\n')
+ new_line();
+ else
+ print_char(c);
+ }
+}
+
+zword Processor::lookup_text(int padding, zword dct) {
+ zword entry_addr;
+ zword entry_count;
+ zword entry;
+ zword addr;
+ zbyte entry_len;
+ zbyte sep_count;
+ int entry_number;
+ int lower, upper;
+ int i;
+ bool sorted;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ 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 {
+ // linear search
+ lower++;
+ }
+ }
+
+ // 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;
+}
+
+void Processor::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);
+ }
+ }
+}
+
+void Processor::tokenise_line(zword text, zword token, zword dct, bool flag) {
+ zword addr1;
+ zword addr2;
+ zbyte length = 0;
+ zbyte c;
+
+ // 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);
+}
+
+int Processor::completion(const zchar *buffer, zchar *result) {
+ zword minaddr;
+ zword maxaddr;
+ zchar *ptr;
+ zchar c;
+ int len;
+ int i;
+
+ *result = 0;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ // Copy last word to "_decoded" string
+ len = 0;
+
+ while ((c = *buffer++) != 0)
+ if (c != ' ') {
+ if (len < 3 * _resolution)
+ _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;
+}
+
+zchar Processor::unicode_tolower(zchar c) {
+ static const byte tolower_basic_latin[0x100] = {
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xD7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xDF,
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+ };
+ static const byte tolower_latin_extended_a[0x80] = {
+ 0x01,0x01,0x03,0x03,0x05,0x05,0x07,0x07,0x09,0x09,0x0B,0x0B,0x0D,0x0D,0x0F,0x0F,
+ 0x11,0x11,0x13,0x13,0x15,0x15,0x17,0x17,0x19,0x19,0x1B,0x1B,0x1D,0x1D,0x1F,0x1F,
+ 0x21,0x21,0x23,0x23,0x25,0x25,0x27,0x27,0x29,0x29,0x2B,0x2B,0x2D,0x2D,0x2F,0x2F,
+ 0x00,0x31,0x33,0x33,0x35,0x35,0x37,0x37,0x38,0x3A,0x3A,0x3C,0x3C,0x3E,0x3E,0x40,
+ 0x40,0x42,0x42,0x44,0x44,0x46,0x46,0x48,0x48,0x49,0x4B,0x4B,0x4D,0x4D,0x4F,0x4F,
+ 0x51,0x51,0x53,0x53,0x55,0x55,0x57,0x57,0x59,0x59,0x5B,0x5B,0x5D,0x5D,0x5F,0x5F,
+ 0x61,0x61,0x63,0x63,0x65,0x65,0x67,0x67,0x69,0x69,0x6B,0x6B,0x6D,0x6D,0x6F,0x6F,
+ 0x71,0x71,0x73,0x73,0x75,0x75,0x77,0x77,0x00,0x7A,0x7A,0x7C,0x7C,0x7E,0x7E,0x7F
+ };
+ static const byte tolower_greek[0x50] = {
+ 0x80,0x81,0x82,0x83,0x84,0x85,0xAC,0x87,0xAD,0xAE,0xAF,0x8B,0xCC,0x8D,0xCD,0xCE,
+ 0x90,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xC0,0xC1,0xA2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xAC,0xAD,0xAE,0xAF,
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF
+ };
+ static const byte tolower_cyrillic[0x60] = {
+ 0x00,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F
+ };
+
+ if (c < 0x0100)
+ c = tolower_basic_latin[c];
+ else if (c == 0x0130)
+ c = 0x0069; // Capital I with dot -> lower case i
+ else if (c == 0x0178)
+ c = 0x00FF; // Capital Y diaeresis -> lower case y diaeresis
+ else if (c < 0x0180)
+ c = tolower_latin_extended_a[c - 0x100] + 0x100;
+ else if (c >= 0x380 && c < 0x3D0)
+ c = tolower_greek[c - 0x380] + 0x300;
+ else if (c >= 0x400 && c < 0x460)
+ c = tolower_cyrillic[c - 0x400] + 0x400;
+
+ return c;
+}
+
+
+void Processor::z_check_unicode() {
+ zword c = zargs[0];
+ zword result = 0;
+
+ if (c <= 0x1f) {
+ if ((c == 0x08) || (c == 0x0d) || (c == 0x1b))
+ result = 2;
+ } else if (c <= 0x7e) {
+ result = 3;
+ } else {
+ // we support unicode
+ result = 1;
+ }
+
+ store (result);
+}
+
+void Processor::z_encode_text() {
+ int i;
+
+ load_string((zword) (zargs[0] + zargs[2]), zargs[1]);
+
+ encode_text(0x05);
+
+ for (i = 0; i < _resolution; i++)
+ storew((zword) (zargs[3] + 2 * i), _encoded[i]);
+
+}
+
+void Processor::z_new_line() {
+ new_line ();
+}
+
+void Processor::z_print () {
+ decode_text(EMBEDDED_STRING, 0);
+}
+
+void Processor::z_print_addr() {
+ decode_text(LOW_STRING, zargs[0]);
+}
+
+void Processor::z_print_char() {
+ print_char (translate_from_zscii(zargs[0]));
+}
+
+void Processor::z_print_form() {
+ 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;
+ }
+}
+
+void Processor::z_print_num() {
+ print_num (zargs[0]);
+}
+
+void Processor::z_print_obj() {
+ print_object(zargs[0]);
+}
+
+void Processor::z_print_paddr() {
+ decode_text (HIGH_STRING, zargs[0]);
+}
+
+void Processor::z_print_ret() {
+ decode_text(EMBEDDED_STRING, 0);
+ new_line();
+ ret(1);
+}
+
+void Processor::z_print_unicode() {
+ if (zargs[0] < 0x20)
+ print_char('?');
+ else
+ print_char(zargs[0]);
+}
+
+void Processor::z_tokenise() {
+ // 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);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_variables.cpp b/engines/gargoyle/frotz/processor_variables.cpp
new file mode 100644
index 0000000000..24bbd28fe3
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_variables.cpp
@@ -0,0 +1,203 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Replace method stubs
+static void storew(zword, zword) {}
+
+
+void Processor::z_dec() {
+ 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);
+ }
+}
+
+void Processor::z_dec_chk() {
+ 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]);
+}
+
+void Processor::z_inc() {
+ 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);
+ }
+}
+
+void Processor::z_inc_chk() {
+ 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]);
+}
+
+void Processor::z_load() {
+ 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);
+}
+
+void Processor::z_pop() {
+ _sp++;
+}
+
+void Processor::z_pop_stack() {
+ 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 {
+ // it's the game stack
+ _sp += zargs[0];
+ }
+}
+
+void Processor::z_pull() {
+ 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 {
+ // it's the game stack
+ value = *_sp++;
+ }
+
+ store(value);
+ }
+}
+
+void Processor::z_push() {
+ *--_sp = zargs[0];
+}
+
+void Processor::z_push_stack() {
+ 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);
+}
+
+void Processor::z_store() {
+ 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);
+ }
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 9dc7f97b2c..a7a119866f 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -72,7 +72,7 @@ public:
glui32 size, winid_t keyWin);
void glk_window_get_arrangement(winid_t win, glui32 *method,
glui32 *size, winid_t *keyWin);
- winid_t glk_window_iterate(winid_t win, glui32 *rock);
+ winid_t glk_window_iterate(winid_t win, glui32 *rock = 0);
glui32 glk_window_get_rock(winid_t win);
glui32 glk_window_get_type(winid_t win);
winid_t glk_window_get_parent(winid_t win);
@@ -85,8 +85,8 @@ public:
strid_t glk_window_get_echo_stream(winid_t win);
void glk_set_window(winid_t win);
- strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock);
- strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock);
+ strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+ strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
void glk_stream_close(strid_t str, stream_result_t *result);
strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
glui32 glk_stream_get_rock(strid_t str) const;
@@ -114,10 +114,10 @@ public:
glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
- frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
- frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock);
- frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock);
- frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock);
+ frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock = 0);
+ frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock = 0);
+ frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock = 0);
+ frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock = 0);
void glk_fileref_destroy(frefid_t fref);
frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
glui32 glk_fileref_get_rock(frefid_t fref);
@@ -169,8 +169,8 @@ public:
glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
- strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock);
- strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock);
+ strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+ strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
void glk_request_char_event_uni(winid_t win);
void glk_request_line_event_uni(winid_t win, glui32 *buf,
@@ -206,7 +206,7 @@ public:
#ifdef GLK_MODULE_SOUND
- schanid_t glk_schannel_create(glui32 rock);
+ schanid_t glk_schannel_create(glui32 rock = 0);
void glk_schannel_destroy(schanid_t chan);
schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
glui32 glk_schannel_get_rock(schanid_t chan);
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index d359429487..9a1b4c40c1 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -26,7 +26,17 @@ MODULE_OBJS := \
frotz/detection_tables.o \
frotz/err.o \
frotz/frotz.o \
+ frotz/glk_interface.o \
frotz/mem.o \
+ frotz/processor.o \
+ frotz/processor_input.o \
+ frotz/processor_maths.o \
+ frotz/processor_objects.o \
+ frotz/processor_screen.o \
+ frotz/processor_streams.o \
+ frotz/processor_table.o \
+ frotz/processor_text.o \
+ frotz/processor_variables.o \
scott/detection.o \
scott/scott.o