aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/frotz/processor_streams.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/frotz/processor_streams.cpp')
-rw-r--r--engines/glk/frotz/processor_streams.cpp786
1 files changed, 786 insertions, 0 deletions
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
new file mode 100644
index 0000000000..b7b23b2db9
--- /dev/null
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -0,0 +1,786 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/processor.h"
+#include "glk/frotz/quetzal.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+zchar Processor::console_read_input(int max, zchar *buf, zword timeout, bool continued) {
+ return os_read_line(max, buf, timeout, max, continued);
+}
+
+zchar Processor::console_read_key(zword timeout) {
+ return os_read_key(timeout, 0);
+}
+
+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::script_open() {
+ 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() {
+ 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();
+
+ 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() {
+ 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() {
+#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]);
+
+ glk_stream_close(f);
+
+ } else {
+ long pc;
+ zword addr;
+ zword nsp, nfp;
+ int skip;
+ int i;
+
+ /* Open game file */
+
+ if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
+ goto finished;
+
+ if (_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 (_save_quetzal) {
+ success = restore_quetzal (gfp, story_fp, blorb_ofs);
+
+ } else {
+ /* Load game file */
+
+ release = (unsigned) fgetc (gfp) << 8;
+ release |= fgetc (gfp);
+
+ () fgetc (gfp);
+ () 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);
+ () 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() {
+ 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