scummvm/engines/glk/frotz/processor_streams.cpp
2019-03-01 17:19:30 +01:00

615 lines
12 KiB
C++

/* 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 Glk {
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);
if (shouldQuit())
return ZC_BAD;
} 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;
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);
if (shouldQuit())
return 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(0);
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) {
offset_t pc = (offset_t)h_start_pc;
SET_PC(pc);
} else {
SET_PC(0);
call(h_start_pc, 0, nullptr, 0);
}
os_restart_game(RESTART_END);
}
void Processor::z_save() {
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) {
// 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);
success = true;
}
} else {
success = saveGame().getCode() == Common::kNoError;
}
if (h_version <= V3)
branch(success);
else
store(success);
}
void Processor::z_restore() {
bool success = false;
if (zargc != 0) {
frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
filemode_Read, 0);
if (ref != nullptr) {
// Write data
strid_t f = glk_stream_open_file(ref, filemode_Read);
glk_get_buffer_stream(f, (char *)zmp + zargs[0], zargs[1]);
glk_stream_close(f);
success = true;
}
} else {
success = loadGame().getCode() == Common::kNoError;
}
int result = success ? 2 : -1;
if (h_version <= V3)
branch(result);
else
store(result);
}
void Processor::z_verify() {
zword checksum = 0;
// Sum all bytes in story file except header bytes
story_fp->seek(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 Frotz
} // End of namespace Glk