From 7a52f21c0bb267d6851ede650298b1d084493ef6 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Mon, 12 Nov 2018 10:54:59 -0800 Subject: GLK: FROTZ: Added script/record/transcript/replay methods --- engines/gargoyle/frotz/processor.cpp | 3 +- engines/gargoyle/frotz/processor.h | 101 ++++++++- engines/gargoyle/frotz/processor_input.cpp | 40 +++- engines/gargoyle/frotz/processor_streams.cpp | 306 ++++++++++++++++++++++++--- engines/gargoyle/glk.h | 4 +- engines/gargoyle/streams.cpp | 36 ++-- engines/gargoyle/streams.h | 6 +- 7 files changed, 436 insertions(+), 60 deletions(-) diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp index 934101332d..ac18a23593 100644 --- a/engines/gargoyle/frotz/processor.cpp +++ b/engines/gargoyle/frotz/processor.cpp @@ -136,7 +136,8 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : _finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0), zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0), _randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false), - _bufPos(0), _locked(false), _prevC('\0') { + _bufPos(0), _locked(false), _prevC('\0'), script_width(0), + sfp(nullptr), rfp(nullptr), pfp(nullptr) { static const Opcode OP0_OPCODES[16] = { &Processor::z_rtrue, &Processor::z_rfalse, diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h index f9352ec19c..50c951a589 100644 --- a/engines/gargoyle/frotz/processor.h +++ b/engines/gargoyle/frotz/processor.h @@ -79,6 +79,10 @@ private: Opcode op1_opcodes[16]; static Opcode var_opcodes[64]; static Opcode ext_opcodes[64]; + + // Stream related fields + int script_width; + strid_t sfp, rfp, pfp; private: /** * \defgroup General support methods @@ -358,7 +362,102 @@ private: */ void script_close(); - /**@}*/ + /** + * Write a newline to the transscript file. + */ + void script_new_line(); + + /** + * Write a single character to the transscript file. + */ + void script_char(zchar c); + + /** + * Write a string to the transscript file. + */ + void script_word(const zchar *s); + + /** + * Send an input line to the transscript file. + */ + void script_write_input(const zchar *buf, zchar key); + + /** + * Remove an input line from the transscript file. + */ + void script_erase_input(const zchar *buf); + + /** + * Start sending a "debugging" message to the transscript file. + */ + void script_mssg_on(); + + /** + * Stop writing a "debugging" message. + */ + void script_mssg_off(); + + /** + * Open a file to record the player's input. + */ + void record_open(); + + /** + * Stop recording the player's input. + */ + void record_close(); + + /** + * Helper function for record_char. + */ + void record_code(int c, bool force_encoding); + + /** + * Write a character to the command file. + */ + void record_char(zchar c); + + /** + * Copy a keystroke to the command file. + */ + void record_write_key(zchar key); + + /** + * Copy a line of input to a command file. + */ + void record_write_input(const zchar *buf, zchar key); + + /** + * Open a file of commands for playback. + */ + void replay_open(); + + /** + * Stop playback of commands. + */ + void replay_close(); + + /* + * Helper function for replay_key and replay_line. + */ + int replay_code(); + + /** + * Read a character from the command file. + */ + zchar replay_char(); + + /** + * Read a keystroke from a command file. + */ + zchar replay_read_key(); + + /* + * Read a line of input from a command file. + */ + zchar replay_read_input(zchar *buf); + + /**@}*/ /** * \defgroup Text support methods diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp index 48951d2402..89aa1889fd 100644 --- a/engines/gargoyle/frotz/processor_input.cpp +++ b/engines/gargoyle/frotz/processor_input.cpp @@ -31,13 +31,6 @@ 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; @@ -65,6 +58,39 @@ void Processor::read_string(int max, zchar *buffer) { } while (key != ZC_RETURN); } +bool Processor::is_terminator(zchar key) { + if (key == ZC_TIME_OUT) + return true; + if (key == ZC_RETURN) + return true; + if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) + return true; + + if (h_terminating_keys != 0) { + if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) { + + zword addr = h_terminating_keys; + zbyte c; + + do { + LOW_BYTE(addr, c); + if (c == 255 || key == translate_from_zscii(c)) + return true; + addr++; + } while (c != 0); + } + } + + return false; +} + +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); +} + int Processor::read_number() { zchar buffer[6]; int value = 0; diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp index 44062757ef..95205a58bd 100644 --- a/engines/gargoyle/frotz/processor_streams.cpp +++ b/engines/gargoyle/frotz/processor_streams.cpp @@ -28,30 +28,15 @@ namespace Frotz { // TODO: Implement method stubs static void os_scrollback_char(zchar) {} static void os_scrollback_erase(zword) {} -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) {} -void Processor::scrollback_char (zchar c) { +void Processor::scrollback_char(zchar c) { if (c == ZC_INDENT) { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; } if (c == ZC_GAP) @@ -219,13 +204,282 @@ continue_input: } void Processor::script_open() { - // TODO + h_flags &= ~SCRIPTING_FLAG; + + frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, + filemode_WriteAppend); + sfp = glk_stream_open_file(fref, filemode_WriteAppend); + + if (sfp != nullptr) { + sfp->setPosition(0, seekmode_End); + + h_flags |= SCRIPTING_FLAG; + + script_valid = true; + ostream_script = true; + + script_width = 0; + } else { + print_string("Cannot open file\n"); + } + + SET_WORD(H_FLAGS, h_flags); } void Processor::script_close() { - // TODO + h_flags &= ~SCRIPTING_FLAG; + SET_WORD(H_FLAGS, h_flags); + + glk_stream_close(sfp); + ostream_script = false; +} + +void Processor::script_new_line() { + script_char('\n'); + script_width = 0; +} + +void Processor::script_char(zchar c) { + if (c == ZC_INDENT && script_width != 0) + c = ' '; + + if (c == ZC_INDENT) { + script_char(' '); + script_char(' '); + script_char(' '); + return; + } + if (c == ZC_GAP) { + script_char(' '); + script_char(' '); + return; + } + + sfp->putCharUni(c); + script_width++; +} + +void Processor::script_word(const zchar *s) { + int width; + int i; + + if (*s == ZC_INDENT && script_width != 0) + script_char(*s++); + + for (i = 0, width = 0; s[i] != 0; i++) { + if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT) + i++; + else if (s[i] == ZC_GAP) + width += 3; + else if (s[i] == ZC_INDENT) + width += 2; + else + width += 1; + } + + if (_script_cols != 0 && script_width + width > _script_cols) { + if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP) + s++; + + script_new_line(); + } + + for (i = 0; s[i] != 0; i++) { + if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE) + i++; + else + script_char(s[i]); + } +} + +void Processor::script_write_input(const zchar *buf, zchar key) { + int width; + int i; + + for (i = 0, width = 0; buf[i] != 0; i++) + width++; + + if (_script_cols != 0 && script_width + width > _script_cols) + script_new_line(); + + for (i = 0; buf[i] != 0; i++) + script_char(buf[i]); + + if (key == ZC_RETURN) + script_new_line(); +} + +void Processor::script_erase_input(const zchar *buf) { + int width; + int i; + + for (i = 0, width = 0; buf[i] != 0; i++) + width++; + + sfp->setPosition(-width, seekmode_Current); + script_width -= width; +} + +void Processor::script_mssg_on() { + if (script_width != 0) + script_new_line(); + + script_char(ZC_INDENT); +} + +void Processor::script_mssg_off() { + script_new_line(); +} + +void Processor::record_open() { + frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Write); + if ((rfp = glk_stream_open_file(fref, filemode_Write)) != nullptr) + ostream_record = true; + else + print_string("Cannot open file\n"); +} + +void Processor::record_close() { + glk_stream_close(rfp); + ostream_record = false; +} + +void Processor::record_code(int c, bool force_encoding) { + if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) { + int i; + + rfp->putChar('['); + + for (i = 10000; i != 0; i /= 10) + if (c >= i || i == 1) + rfp->putChar('0' + (c / i) % 10); + + rfp->putChar(']'); + } else { + rfp->putChar(c); + } +} + +void Processor::record_char(zchar c) { + if (c != ZC_RETURN) { + if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) { + record_code(translate_to_zscii(c), false); + if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) { + record_code(mouse_x, true); + record_code(mouse_y, true); + } + } else { + record_code(1000 + c - ZC_HKEY_MIN, true); + } + } +} + +void Processor::record_write_key(zchar key) { + record_char(key); + rfp->putChar('\n'); +} + +void Processor::record_write_input(const zchar *buf, zchar key) { + zchar c; + + while ((c = *buf++) != 0) + record_char(c); + + record_write_key(key); +} + +void Processor::replay_open() { + frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Read); + if ((pfp = glk_stream_open_file(fref, filemode_Read)) != nullptr) + istream_replay = true; + else + print_string("Cannot open file\n"); +} + +void Processor::replay_close() { + glk_stream_close(pfp); + istream_replay = false; } +int Processor::replay_code() { + int c; + + if ((c = pfp->getChar()) == '[') { + int c2; + + c = 0; + + while ((c2 = pfp->getChar()) != EOF && c2 >= '0' && c2 <= '9') + c = 10 * c + c2 - '0'; + + return (c2 == ']') ? c : EOF; + } else { + return c; + } +} + +zchar Processor::replay_char() { + int c; + + if ((c = replay_code()) != EOF) { + if (c != '\n') { + if (c < 1000) { + + c = translate_from_zscii(c); + + if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) { + mouse_x = replay_code(); + mouse_y = replay_code(); + } + + return c; + } else { + return ZC_HKEY_MIN + c - 1000; + } + } + + pfp->unputBuffer("\n", 1); + return ZC_RETURN; + + } else { + return ZC_BAD; + } +} + +zchar Processor::replay_read_key() { + zchar key = replay_char(); + + if (pfp->getChar() != '\n') { + replay_close(); + return ZC_BAD; + } else { + return key; + } +} + +zchar Processor::replay_read_input(zchar *buf) { + zchar c; + + for (;;) { + c = replay_char(); + + if (c == ZC_BAD || is_terminator(c)) + break; + + *buf++ = c; + } + + *buf = 0; + + if (pfp->getChar() != '\n') { + replay_close(); + return ZC_BAD; + } else { + return c; + } +} + + void Processor::z_input_stream() { flush_buffer(); @@ -260,7 +514,7 @@ void Processor::z_output_stream() { } } -void Processor::z_restart(void) { +void Processor::z_restart() { flush_buffer(); os_restart_game(RESTART_BEGIN); @@ -293,8 +547,7 @@ void Processor::z_restart(void) { os_restart_game(RESTART_END); } - -void Processor::z_save(void) { +void Processor::z_save() { #ifdef TODO bool success = false; @@ -310,8 +563,7 @@ void Processor::z_save(void) { glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]); - stream_result_t result; - glk_stream_close(f, &result); + glk_stream_close(f); } else { long pc; @@ -430,8 +682,8 @@ void Processor::z_restore() { release = (unsigned) fgetc (gfp) << 8; release |= fgetc (gfp); - (void) fgetc (gfp); - (void) fgetc (gfp); + () fgetc (gfp); + () fgetc (gfp); /* Check the release number */ @@ -460,7 +712,7 @@ void Processor::z_restore() { for (i = 0; i < skip; i++) zmp[addr++] = fgetc (story_fp); zmp[addr] = fgetc (gfp); - (void) fgetc (story_fp); + () fgetc (story_fp); } /* Check for errors */ @@ -519,7 +771,7 @@ finished: #endif } -void Processor::z_verify(void) { +void Processor::z_verify() { zword checksum = 0; // Sum all bytes in story file except header bytes diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h index a7a119866f..2f3620f876 100644 --- a/engines/gargoyle/glk.h +++ b/engines/gargoyle/glk.h @@ -66,7 +66,7 @@ public: winid_t glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock = 0) const; - void glk_window_close(winid_t win, stream_result_t *result); + void glk_window_close(winid_t win, stream_result_t *result = nullptr); void glk_window_get_size(winid_t win, glui32 *width, glui32 *height); void glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keyWin); @@ -87,7 +87,7 @@ public: 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); + void glk_stream_close(strid_t str, stream_result_t *result = nullptr); strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const; glui32 glk_stream_get_rock(strid_t str) const; void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode); diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp index 41f3caf1b4..d5671f136a 100644 --- a/engines/gargoyle/streams.cpp +++ b/engines/gargoyle/streams.cpp @@ -458,32 +458,30 @@ glui32 MemoryStream::getPosition() const { return ((unsigned char *)_bufPtr - (unsigned char *)_buf); } -void MemoryStream::setPosition(glui32 pos, glui32 seekMode) { - glsi32 newPos = pos; - +void MemoryStream::setPosition(glsi32 pos, glui32 seekMode) { if (!_unicode) { if (seekMode == seekmode_Current) - newPos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + newPos; + pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos; else if (seekMode == seekmode_End) - newPos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + newPos; + pos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + pos; else - /* newPos = newPos */; - if (newPos < 0) - newPos = 0; - if (newPos > ((unsigned char *)_bufEof - (unsigned char *)_buf)) - newPos = ((unsigned char *)_bufEof - (unsigned char *)_buf); - _bufPtr = (unsigned char *)_buf + newPos; + /* pos = pos */; + if (pos < 0) + pos = 0; + if (pos > ((unsigned char *)_bufEof - (unsigned char *)_buf)) + pos = ((unsigned char *)_bufEof - (unsigned char *)_buf); + _bufPtr = (unsigned char *)_buf + pos; } else { if (seekMode == seekmode_Current) - newPos = ((glui32 *)_bufPtr - (glui32 *)_buf) + newPos; + pos = ((glui32 *)_bufPtr - (glui32 *)_buf) + pos; else if (seekMode == seekmode_End) - newPos = ((glui32 *)_bufEof - (glui32 *)_buf) + newPos; + pos = ((glui32 *)_bufEof - (glui32 *)_buf) + pos; - if (newPos < 0) - newPos = 0; - if (newPos > ((glui32 *)_bufEof - (glui32 *)_buf)) - newPos = ((glui32 *)_bufEof - (glui32 *)_buf); - _bufPtr = (glui32 *)_buf + newPos; + if (pos < 0) + pos = 0; + if (pos > ((glui32 *)_bufEof - (glui32 *)_buf)) + pos = ((glui32 *)_bufEof - (glui32 *)_buf); + _bufPtr = (glui32 *)_buf + pos; } } @@ -1000,7 +998,7 @@ glui32 FileStream::getPosition() const { return _outFile ? _outFile->pos() : _inStream->pos(); } -void FileStream::setPosition(glui32 pos, glui32 seekMode) { +void FileStream::setPosition(glsi32 pos, glui32 seekMode) { _lastOp = 0; if (_unicode) pos *= 4; diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h index 772b0215c2..ea9c651e45 100644 --- a/engines/gargoyle/streams.h +++ b/engines/gargoyle/streams.h @@ -223,7 +223,7 @@ public: return 0; } - virtual void setPosition(glui32 pos, glui32 seekMode) {} + virtual void setPosition(glsi32 pos, glui32 seekMode) {} virtual void setStyle(glui32 val) {} @@ -390,7 +390,7 @@ public: virtual glui32 getPosition() const override; - virtual void setPosition(glui32 pos, glui32 seekMode) override; + virtual void setPosition(glsi32 pos, glui32 seekMode) override; /** * Get a character from the stream @@ -492,7 +492,7 @@ public: virtual glui32 getPosition() const override; - virtual void setPosition(glui32 pos, glui32 seekMode) override; + virtual void setPosition(glsi32 pos, glui32 seekMode) override; /** * Get a character from the stream -- cgit v1.2.3