Initial commit
This commit is contained in:
87
engines/glk/zcode/bitmap_font.cpp
Normal file
87
engines/glk/zcode/bitmap_font.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/bitmap_font.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
BitmapFont::BitmapFont(const Graphics::Surface &src, const Common::Point &size,
|
||||
uint srcWidth, uint srcHeight, unsigned char startingChar, bool isFixedWidth) :
|
||||
_startingChar(startingChar), _size(size) {
|
||||
assert(src.format.bytesPerPixel == 1);
|
||||
assert((src.w % srcWidth) == 0);
|
||||
assert((src.h % srcHeight) == 0);
|
||||
|
||||
// Set up a characters array
|
||||
_chars.resize((src.w / srcWidth) * (src.h / srcHeight));
|
||||
|
||||
// Iterate through loading characters
|
||||
Common::Rect r(srcWidth, srcHeight);
|
||||
int charsPerRow = src.w / srcWidth;
|
||||
for (uint idx = 0; idx < _chars.size(); ++idx) {
|
||||
r.moveTo((idx % charsPerRow) * srcWidth, (idx / charsPerRow) * srcHeight);
|
||||
int srcCharWidth = isFixedWidth ? r.width() : getSourceCharacterWidth(idx, src, r);
|
||||
int destCharWidth = (size.x * srcCharWidth + (srcWidth - 1)) / srcWidth;
|
||||
Common::Rect charBounds(r.left, r.top, r.left + srcCharWidth, r.bottom);
|
||||
|
||||
_chars[idx].create(destCharWidth, size.y, src.format);
|
||||
_chars[idx].blitFrom(src, charBounds, Common::Rect(0, 0, _chars[idx].w, _chars[idx].h));
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
|
||||
const Graphics::ManagedSurface &c = _chars[chr - _startingChar];
|
||||
for (int yCtr = 0; yCtr < c.h; ++yCtr) {
|
||||
const byte *srcP = (const byte *)c.getBasePtr(0, yCtr);
|
||||
|
||||
for (int xCtr = 0; xCtr < c.w; ++xCtr, ++srcP) {
|
||||
if (*srcP)
|
||||
dst->hLine(x + xCtr, y + yCtr, x + xCtr, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int BitmapFont::getSourceCharacterWidth(uint charIndex, const Graphics::Surface &src,
|
||||
const Common::Rect &charBounds) {
|
||||
if (charIndex == 0)
|
||||
// The space character is treated as half the width of bounding area
|
||||
return charBounds.width() / 2;
|
||||
|
||||
// Scan through the rows to find the right most pixel, getting the width from that
|
||||
int maxWidth = 0, rowX;
|
||||
for (int y = charBounds.top; y < charBounds.bottom; ++y) {
|
||||
rowX = 0;
|
||||
const byte *srcP = (const byte *)src.getBasePtr(charBounds.left, y);
|
||||
|
||||
for (int x = 0; x < charBounds.width(); ++x, ++srcP) {
|
||||
if (*srcP)
|
||||
rowX = x;
|
||||
}
|
||||
|
||||
maxWidth = MAX(maxWidth, MIN(rowX + 2, (int)charBounds.width()));
|
||||
}
|
||||
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
106
engines/glk/zcode/bitmap_font.h
Normal file
106
engines/glk/zcode/bitmap_font.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_BITMAP_FONT
|
||||
#define GLK_ZCODE_BITMAP_FONT
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Implements a font stored as a grid on a passed surface
|
||||
*/
|
||||
class BitmapFont : public Graphics::Font {
|
||||
private:
|
||||
Common::Array<Graphics::ManagedSurface> _chars;
|
||||
size_t _startingChar;
|
||||
Common::Point _size;
|
||||
protected:
|
||||
/**
|
||||
* Calculate a character width
|
||||
*/
|
||||
int getSourceCharacterWidth(uint charIndex, const Graphics::Surface &src,
|
||||
const Common::Rect &charBounds);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
BitmapFont(const Graphics::Surface &src, const Common::Point &size,
|
||||
uint srcWidth, uint srcHeight, unsigned char startingChar, bool isFixedWidth);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the font height
|
||||
*/
|
||||
int getFontHeight() const override { return _size.y; }
|
||||
|
||||
/**
|
||||
* Get the maximum character width
|
||||
*/
|
||||
int getMaxCharWidth() const override { return _size.x; }
|
||||
|
||||
/**
|
||||
* Get the width of the given character
|
||||
*/
|
||||
int getCharWidth(uint32 chr) const override { return _chars[chr - _startingChar].w; }
|
||||
|
||||
/**
|
||||
* Draw a character
|
||||
*/
|
||||
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Subclass for fixed width fonts
|
||||
*/
|
||||
class FixedWidthBitmapFont : public BitmapFont {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FixedWidthBitmapFont(const Graphics::Surface &src, const Common::Point &size,
|
||||
uint srcWidth = 8, uint srcHeight = 8, unsigned char startingChar = ' ') :
|
||||
BitmapFont(src, size, srcWidth, srcHeight, startingChar, true) {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Subclass for fixed width fonts
|
||||
*/
|
||||
class VariableWidthBitmapFont : public BitmapFont {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
VariableWidthBitmapFont(const Graphics::Surface &src, const Common::Point &size,
|
||||
uint srcWidth = 8, uint srcHeight = 8, unsigned char startingChar = ' ') :
|
||||
BitmapFont(src, size, srcWidth, srcHeight, startingChar, false) {}
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
187
engines/glk/zcode/config.cpp
Normal file
187
engines/glk/zcode/config.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/config.h"
|
||||
#include "glk/zcode/detection.h"
|
||||
#include "glk/glk.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
const Header::StoryEntry Header::RECORDS[26] = {
|
||||
{ SHERLOCK, 21, "871214" },
|
||||
{ SHERLOCK, 26, "880127" },
|
||||
{ BEYOND_ZORK, 47, "870915" },
|
||||
{ BEYOND_ZORK, 49, "870917" },
|
||||
{ BEYOND_ZORK, 51, "870923" },
|
||||
{ BEYOND_ZORK, 57, "871221" },
|
||||
{ ZORK_ZERO, 296, "881019" },
|
||||
{ ZORK_ZERO, 366, "890323" },
|
||||
{ ZORK_ZERO, 383, "890602" },
|
||||
{ ZORK_ZERO, 393, "890714" },
|
||||
{ SHOGUN, 292, "890314" },
|
||||
{ SHOGUN, 295, "890321" },
|
||||
{ SHOGUN, 311, "890510" },
|
||||
{ SHOGUN, 322, "890706" },
|
||||
{ ARTHUR, 54, "890606" },
|
||||
{ ARTHUR, 63, "890622" },
|
||||
{ ARTHUR, 74, "890714" },
|
||||
{ JOURNEY, 26, "890316" },
|
||||
{ JOURNEY, 30, "890322" },
|
||||
{ JOURNEY, 77, "890616" },
|
||||
{ JOURNEY, 83, "890706" },
|
||||
{ LURKING_HORROR, 203, "870506" },
|
||||
{ LURKING_HORROR, 219, "870912" },
|
||||
{ LURKING_HORROR, 221, "870918" },
|
||||
{ MILLIWAYS, 184, "890412" },
|
||||
{ UNKNOWN, 0, "------" }
|
||||
};
|
||||
|
||||
static uint getConfigBool(const Common::String &profileName, bool defaultVal = false) {
|
||||
return ConfMan.hasKey(profileName) ? ConfMan.getBool(profileName) : defaultVal;
|
||||
}
|
||||
|
||||
static uint getConfigInt(const Common::String &profileName, uint defaultVal, uint maxVal) {
|
||||
uint val = ConfMan.hasKey(profileName) ? ConfMan.getInt(profileName) : defaultVal;
|
||||
if (val > maxVal)
|
||||
error("Invalid value for configuration value %s", profileName.c_str());
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
Header::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_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
|
||||
h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
|
||||
h_strings_offset(0), h_default_background(0), h_default_foreground(0),
|
||||
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), _storyId(UNKNOWN) {
|
||||
Common::fill(&h_serial[0], &h_serial[6], '\0');
|
||||
Common::fill(&h_user_name[0], &h_user_name[8], '\0');
|
||||
|
||||
h_interpreter_number = getConfigInt("interpreter_number", INTERP_AMIGA, INTERP_TANDY);
|
||||
|
||||
|
||||
if (ConfMan.hasKey("username")) {
|
||||
Common::String username = ConfMan.get("username");
|
||||
strncpy((char *)h_user_name, username.c_str(), 7);
|
||||
}
|
||||
}
|
||||
|
||||
void Header::loadHeader(Common::SeekableReadStream &f) {
|
||||
f.seek(0);
|
||||
h_version = f.readByte();
|
||||
h_config = f.readByte();
|
||||
|
||||
if (h_version < V1 || h_version > V8)
|
||||
error("Unknown Z-code version");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
UserOptions::UserOptions() : _undo_slots(MAX_UNDO_SLOTS), _sound(true), _quetzal(true), _color_enabled(false),
|
||||
_err_report_mode(ERR_REPORT_ONCE), _ignore_errors(false), _expand_abbreviations(false), _tandyBit(false),
|
||||
_piracy(false), _script_cols(0), _left_margin(0), _right_margin(0), _defaultBackground(0), _defaultForeground(0) {
|
||||
}
|
||||
|
||||
void UserOptions::initialize(uint hVersion, uint storyId) {
|
||||
_err_report_mode = getConfigInt("err_report_mode", ERR_REPORT_ONCE, ERR_REPORT_FATAL);
|
||||
_ignore_errors = getConfigBool("ignore_errors");
|
||||
_expand_abbreviations = getConfigBool("expand_abbreviations");
|
||||
_tandyBit = getConfigBool("tandy_bit");
|
||||
_piracy = getConfigBool("piracy");
|
||||
_script_cols = getConfigInt("wrap_script_lines", 80, 999);
|
||||
_left_margin = getConfigInt("left_margin", 0, 999);
|
||||
_right_margin = getConfigInt("right_margin", 0, 999);
|
||||
|
||||
// Debugging flags
|
||||
_attribute_assignment = getConfigBool("attribute_assignment");
|
||||
_attribute_testing = getConfigBool("attribute_testing");
|
||||
_object_locating = getConfigBool("object_locating");
|
||||
_object_movement = getConfigBool("object_movement");
|
||||
|
||||
int defaultFg = hVersion == V6 ? 0 : 0xffffff;
|
||||
int defaultBg = hVersion == V6 ? 0xffffff : 0x80;
|
||||
if (storyId == BEYOND_ZORK)
|
||||
defaultBg = 0;
|
||||
|
||||
defaultFg = getConfigInt("foreground", defaultFg, 0xffffff);
|
||||
defaultBg = getConfigInt("background", defaultBg, 0xffffff);
|
||||
|
||||
Graphics::PixelFormat format = g_system->getScreenFormat();
|
||||
_defaultForeground = format.RGBToColor((defaultFg >> 16) & 0xff, (defaultFg >> 8) & 0xff, defaultFg & 0xff);
|
||||
_defaultBackground = format.RGBToColor((defaultBg >> 16) & 0xff, (defaultBg >> 8) & 0xff, defaultBg & 0xff);
|
||||
}
|
||||
|
||||
bool UserOptions::isInfocom() const {
|
||||
return g_vm->getOptions() & OPTION_INFOCOM;
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
240
engines/glk/zcode/config.h
Normal file
240
engines/glk/zcode/config.h
Normal file
@@ -0,0 +1,240 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_CONFIG
|
||||
#define GLK_ZCODE_CONFIG
|
||||
|
||||
#include "glk/zcode/frotz_types.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Configuration flags
|
||||
*/
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* There are four error reporting modes: never report errors;
|
||||
* report only the first time a given error type occurs;
|
||||
* report every time an error occurs;
|
||||
* or treat all errors as fatal errors, killing the interpreter.
|
||||
* I strongly recommend "report once" as the default. But you can compile in a
|
||||
* different default by changing the definition of ERR_DEFAULT_REPORT_MODE.
|
||||
*/
|
||||
enum ErrorReport {
|
||||
ERR_REPORT_NEVER = 0,
|
||||
ERR_REPORT_ONCE = 1,
|
||||
ERR_REPORT_ALWAYS = 2,
|
||||
ERR_REPORT_FATAL = 3,
|
||||
|
||||
ERR_DEFAULT_REPORT_MODE = ERR_REPORT_NEVER
|
||||
};
|
||||
|
||||
/**
|
||||
* Enumeration of the game header byte indexes
|
||||
*/
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* Header extension fields
|
||||
*/
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* User options
|
||||
*/
|
||||
struct UserOptions {
|
||||
bool _attribute_assignment;
|
||||
bool _attribute_testing;
|
||||
bool _object_locating;
|
||||
bool _object_movement;
|
||||
bool _expand_abbreviations;
|
||||
bool _ignore_errors;
|
||||
bool _piracy;
|
||||
bool _quetzal;
|
||||
bool _sound;
|
||||
bool _tandyBit;
|
||||
int _left_margin;
|
||||
int _right_margin;
|
||||
int _undo_slots;
|
||||
int _script_cols;
|
||||
int _err_report_mode;
|
||||
uint _defaultForeground;
|
||||
uint _defaultBackground;
|
||||
bool _color_enabled;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
UserOptions();
|
||||
|
||||
/**
|
||||
* Initializes the options
|
||||
*/
|
||||
void initialize(uint hVersion, uint storyId);
|
||||
|
||||
/**
|
||||
* Returns true if the game being played is one of the original Infocom releases
|
||||
*/
|
||||
bool isInfocom() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Story file header data
|
||||
*/
|
||||
struct Header {
|
||||
private:
|
||||
struct StoryEntry {
|
||||
Story _storyId;
|
||||
zword _release;
|
||||
char _serial[7];
|
||||
};
|
||||
static const StoryEntry RECORDS[26];
|
||||
public:
|
||||
zbyte h_version;
|
||||
zbyte h_config;
|
||||
zword h_release;
|
||||
zword h_resident_size;
|
||||
zword h_start_pc;
|
||||
zword h_dictionary;
|
||||
zword h_objects;
|
||||
zword h_globals;
|
||||
zword h_dynamic_size;
|
||||
zword h_flags;
|
||||
zbyte h_serial[6];
|
||||
zword h_abbreviations;
|
||||
zword h_file_size;
|
||||
zword h_checksum;
|
||||
zbyte h_interpreter_number;
|
||||
zbyte h_interpreter_version;
|
||||
zbyte h_screen_rows;
|
||||
zbyte h_screen_cols;
|
||||
zword h_screen_width;
|
||||
zword h_screen_height;
|
||||
zbyte h_font_height;
|
||||
zbyte h_font_width;
|
||||
zword h_functions_offset;
|
||||
zword h_strings_offset;
|
||||
zbyte h_default_background;
|
||||
zbyte h_default_foreground;
|
||||
zword h_terminating_keys;
|
||||
zword h_line_width;
|
||||
zbyte h_standard_high;
|
||||
zbyte h_standard_low;
|
||||
zword h_alphabet;
|
||||
zword h_extension_table;
|
||||
zbyte h_user_name[8];
|
||||
|
||||
zword hx_table_size;
|
||||
zword hx_mouse_x;
|
||||
zword hx_mouse_y;
|
||||
zword hx_unicode_table;
|
||||
zword hx_flags;
|
||||
zword hx_fore_colour;
|
||||
zword hx_back_colour;
|
||||
|
||||
Story _storyId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Header();
|
||||
|
||||
/**
|
||||
* Load the header
|
||||
*/
|
||||
void loadHeader(Common::SeekableReadStream &f);
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
188
engines/glk/zcode/detection.cpp
Normal file
188
engines/glk/zcode/detection.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/detection.h"
|
||||
#include "glk/zcode/detection_tables.h"
|
||||
#include "glk/zcode/quetzal.h"
|
||||
#include "glk/blorb.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/md5.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
void ZCodeMetaEngine::getSupportedGames(PlainGameList &games) {
|
||||
for (const PlainGameDescriptor *pd = INFOCOM_GAME_LIST; pd->gameId; ++pd)
|
||||
games.push_back(*pd);
|
||||
for (const PlainGameDescriptor *pd = ZCODE_GAME_LIST; pd->gameId; ++pd)
|
||||
games.push_back(*pd);
|
||||
}
|
||||
|
||||
const GlkDetectionEntry* ZCodeMetaEngine::getDetectionEntries() {
|
||||
static Common::Array<GlkDetectionEntry> entries;
|
||||
for (const FrotzGameDescription *entry = FROTZ_GAMES; entry->_gameId; ++entry) {
|
||||
GlkDetectionEntry detection = {
|
||||
entry->_gameId,
|
||||
entry->_extra,
|
||||
entry->_md5,
|
||||
entry->_filesize,
|
||||
entry->_language,
|
||||
Common::kPlatformUnknown
|
||||
};
|
||||
entries.push_back(detection);
|
||||
}
|
||||
|
||||
entries.push_back({nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformUnknown});
|
||||
|
||||
return entries.data();
|
||||
}
|
||||
|
||||
GameDescriptor ZCodeMetaEngine::findGame(const char *gameId) {
|
||||
for (const PlainGameDescriptor *pd = INFOCOM_GAME_LIST; pd->gameId; ++pd) {
|
||||
if (!strcmp(gameId, pd->gameId)) {
|
||||
GameDescriptor gd(*pd);
|
||||
gd._options |= OPTION_INFOCOM;
|
||||
|
||||
if (!strcmp(gameId, "questforexcalibur") ||
|
||||
!strcmp(gameId, "journey") ||
|
||||
!strcmp(gameId, "shogun") ||
|
||||
!strcmp(gameId, "zork0"))
|
||||
gd._supportLevel = kUnstableGame;
|
||||
|
||||
return gd;
|
||||
}
|
||||
}
|
||||
for (const PlainGameDescriptor *pd = ZCODE_GAME_LIST; pd->gameId; ++pd) {
|
||||
if (!strcmp(gameId, pd->gameId)) {
|
||||
GameDescriptor gd = *pd;
|
||||
/*
|
||||
* Tested against ScummVM 2.8.0git, following entries are confirmed not to be playable
|
||||
*/
|
||||
if (!strcmp(gameId, "bureaucrocy_zcode") ||
|
||||
!strcmp(gameId, "scopa") ||
|
||||
!strcmp(gameId, "sunburst"))
|
||||
gd._supportLevel = kUnstableGame;
|
||||
|
||||
return gd;
|
||||
}
|
||||
}
|
||||
|
||||
return GameDescriptor::empty();
|
||||
}
|
||||
|
||||
bool ZCodeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
|
||||
const char *const EXTENSIONS[] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8",
|
||||
".dat", ".data", ".zip", nullptr };
|
||||
|
||||
// Loop through the files of the folder
|
||||
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
||||
// Check for a recognised filename
|
||||
if (file->isDirectory())
|
||||
continue;
|
||||
Common::String filename = file->getName();
|
||||
bool hasExt = Blorb::hasBlorbExt(filename), isBlorb = false;
|
||||
for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
|
||||
hasExt = filename.hasSuffixIgnoreCase(*ext);
|
||||
if (!hasExt)
|
||||
continue;
|
||||
|
||||
// Open up the file and calculate the md5, and get the serial
|
||||
Common::File gameFile;
|
||||
if (!gameFile.open(*file))
|
||||
continue;
|
||||
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
|
||||
size_t filesize = gameFile.size();
|
||||
char serial[9] = "";
|
||||
bool emptyBlorb = false;
|
||||
gameFile.seek(0);
|
||||
isBlorb = Blorb::isBlorb(gameFile, ID_ZCOD);
|
||||
|
||||
if (!isBlorb) {
|
||||
if (Blorb::hasBlorbExt(filename)) {
|
||||
gameFile.close();
|
||||
continue;
|
||||
}
|
||||
gameFile.seek(18);
|
||||
Common::strcpy_s(&serial[0], sizeof(serial), "\"");
|
||||
gameFile.read(&serial[1], 6);
|
||||
Common::strcpy_s(&serial[7], sizeof(serial)-7, "\"");
|
||||
} else {
|
||||
Blorb b(*file, INTERPRETER_ZCODE);
|
||||
Common::SeekableReadStream *f = b.createReadStreamForMember("game");
|
||||
emptyBlorb = f == nullptr;
|
||||
|
||||
if (!emptyBlorb) {
|
||||
f->seek(18);
|
||||
Common::strcpy_s(&serial[0], sizeof(serial), "\"");
|
||||
f->read(&serial[1], 6);
|
||||
Common::strcpy_s(&serial[7], sizeof(serial) - 7, "\"");
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
gameFile.close();
|
||||
|
||||
// Check for known games. Note that there has been some variation in exact filesizes
|
||||
// for Infocom games due to padding at the end of files. So we match on md5s for the
|
||||
// first 5Kb, and only worry about filesize for more recent Blorb based Zcode games
|
||||
const FrotzGameDescription *p = FROTZ_GAMES;
|
||||
while (p->_gameId && p->_md5 && (md5 != p->_md5 ||
|
||||
(filesize != p->_filesize && isBlorb)))
|
||||
++p;
|
||||
|
||||
if (!p->_gameId) {
|
||||
// Generic .dat/.data/.zip files don't get reported as matches unless they have a known md5
|
||||
if (filename.hasSuffixIgnoreCase(".dat") || filename.hasSuffixIgnoreCase(".data") || filename.hasSuffixIgnoreCase(".zip") || emptyBlorb)
|
||||
continue;
|
||||
|
||||
const PlainGameDescriptor &desc = ZCODE_GAME_LIST[0];
|
||||
gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
|
||||
} else {
|
||||
GameDescriptor gameDesc = findGame(p->_gameId);
|
||||
DetectedGame gd = DetectedGame("glk", p->_gameId, gameDesc._description, p->_language, Common::kPlatformUnknown, p->_extra);
|
||||
gd.setGUIOptions(p->_guiOptions);
|
||||
|
||||
gd.addExtraEntry("filename", filename);
|
||||
gameList.push_back(gd);
|
||||
}
|
||||
}
|
||||
|
||||
return !gameList.empty();
|
||||
}
|
||||
|
||||
void ZCodeMetaEngine::detectClashes(Common::StringMap &map) {
|
||||
for (int idx = 0; idx < 2; ++idx) {
|
||||
for (const PlainGameDescriptor *pd = (idx == 0) ? INFOCOM_GAME_LIST : ZCODE_GAME_LIST; pd->gameId; ++pd) {
|
||||
if (map.contains(pd->gameId))
|
||||
error("Duplicate game Id found - %s", pd->gameId);
|
||||
map[pd->gameId] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
72
engines/glk/zcode/detection.h
Normal file
72
engines/glk/zcode/detection.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_DETECTION
|
||||
#define GLK_ZCODE_DETECTION
|
||||
|
||||
#include "common/fs.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "engines/game.h"
|
||||
#include "glk/streams.h"
|
||||
#include "glk/detection.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Game descriptor detection options
|
||||
*/
|
||||
enum DetectionOption {
|
||||
OPTION_INFOCOM = 1
|
||||
};
|
||||
|
||||
class ZCodeMetaEngine {
|
||||
public:
|
||||
/**
|
||||
* Get a list of supported games
|
||||
*/
|
||||
static void getSupportedGames(PlainGameList &games);
|
||||
|
||||
/**
|
||||
* Get the detection entries
|
||||
*/
|
||||
static const GlkDetectionEntry* getDetectionEntries();
|
||||
|
||||
/**
|
||||
* Returns a game description for the given game Id, if it's supported
|
||||
*/
|
||||
static GameDescriptor findGame(const char *gameId);
|
||||
|
||||
/**
|
||||
* Detect supported games
|
||||
*/
|
||||
static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
|
||||
|
||||
/**
|
||||
* Check for game Id clashes with other sub-engines
|
||||
*/
|
||||
static void detectClashes(Common::StringMap &map);
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
8212
engines/glk/zcode/detection_tables.h
Normal file
8212
engines/glk/zcode/detection_tables.h
Normal file
File diff suppressed because it is too large
Load Diff
223
engines/glk/zcode/frotz_types.h
Normal file
223
engines/glk/zcode/frotz_types.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_FROTZ_TYPES
|
||||
#define GLK_ZCODE_FROTZ_TYPES
|
||||
|
||||
#include "glk/glk_types.h"
|
||||
#include "common/algorithm.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#define MAX_UNDO_SLOTS 500
|
||||
#define STACK_SIZE 32768
|
||||
|
||||
#define lo(v) (v & 0xff)
|
||||
#define hi(v) (v >> 8)
|
||||
|
||||
/**
|
||||
* Character codes
|
||||
*/
|
||||
enum ZCodeKey {
|
||||
ZC_TIME_OUT = 0x00,
|
||||
ZC_NEW_STYLE = 0x01,
|
||||
ZC_NEW_FONT = 0x02,
|
||||
ZC_BACKSPACE = 0x08,
|
||||
ZC_INDENT = 0x09,
|
||||
ZC_GAP = 0x0b,
|
||||
ZC_RETURN = 0x0d,
|
||||
ZC_HKEY_MIN = 0x0e,
|
||||
ZC_HKEY_RECORD = 0x0e,
|
||||
ZC_HKEY_PLAYBACK = 0x0f,
|
||||
ZC_HKEY_SEED = 0x10,
|
||||
ZC_HKEY_UNDO = 0x11,
|
||||
ZC_HKEY_RESTART = 0x12,
|
||||
ZC_HKEY_QUIT = 0x13,
|
||||
ZC_HKEY_DEBUG = 0x14,
|
||||
ZC_HKEY_HELP = 0x15,
|
||||
ZC_HKEY_MAX = 0x15,
|
||||
ZC_ESCAPE = 0x1b,
|
||||
ZC_ASCII_MIN = 0x20,
|
||||
ZC_ASCII_MAX = 0x7e,
|
||||
ZC_BAD = 0x7f,
|
||||
ZC_ARROW_MIN = 0x81,
|
||||
ZC_ARROW_UP = 0x81,
|
||||
ZC_ARROW_DOWN = 0x82,
|
||||
ZC_ARROW_LEFT = 0x83,
|
||||
ZC_ARROW_RIGHT = 0x84,
|
||||
ZC_ARROW_MAX = 0x84,
|
||||
ZC_FKEY_MIN = 0x85,
|
||||
ZC_FKEY_MAX = 0x90,
|
||||
ZC_NUMPAD_MIN = 0x91,
|
||||
ZC_NUMPAD_MAX = 0x9a,
|
||||
ZC_SINGLE_CLICK = 0x9b,
|
||||
ZC_DOUBLE_CLICK = 0x9c,
|
||||
ZC_MENU_CLICK = 0x9d,
|
||||
ZC_LATIN1_MIN = 0xa0,
|
||||
ZC_LATIN1_MAX = 0xff
|
||||
};
|
||||
|
||||
enum Story {
|
||||
BEYOND_ZORK,
|
||||
SHERLOCK,
|
||||
ZORK_ZERO,
|
||||
SHOGUN,
|
||||
ARTHUR,
|
||||
JOURNEY,
|
||||
LURKING_HORROR,
|
||||
MILLIWAYS,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
enum Version {
|
||||
V1 = 1,
|
||||
V2 = 2,
|
||||
V3 = 3,
|
||||
V4 = 4,
|
||||
V5 = 5,
|
||||
V6 = 6,
|
||||
V7 = 7,
|
||||
V8 = 8,
|
||||
V9 = 9
|
||||
};
|
||||
|
||||
enum {
|
||||
TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency - V6
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
enum FrotzInterp {
|
||||
INTERP_DEFAULT = 0,
|
||||
INTERP_DEC_20 = 1,
|
||||
INTERP_APPLE_IIE = 2,
|
||||
INTERP_MACINTOSH = 3,
|
||||
INTERP_AMIGA = 4,
|
||||
INTERP_ATARI_ST = 5,
|
||||
INTERP_MSDOS = 6,
|
||||
INTERP_CBM_128 = 7,
|
||||
INTERP_CBM_64 = 8,
|
||||
INTERP_APPLE_IIC = 9,
|
||||
INTERP_APPLE_IIGS = 10,
|
||||
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 {
|
||||
PREVIOUS_FONT = 0,
|
||||
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_menu */
|
||||
|
||||
#define MENU_NEW 0
|
||||
#define MENU_ADD 1
|
||||
#define MENU_REMOVE 2
|
||||
|
||||
typedef byte zbyte;
|
||||
typedef uint32 zchar;
|
||||
typedef uint16 zword;
|
||||
|
||||
#define MAX_NESTING 16
|
||||
struct Redirect {
|
||||
zword _xSize;
|
||||
zword _table;
|
||||
zword _width;
|
||||
zword _total;
|
||||
|
||||
Redirect() : _xSize(0), _table(0), _width(0), _total(0) {}
|
||||
Redirect(zword xSize, zword table, zword width = 0, zword total = 0) :
|
||||
_xSize(xSize), _table(table), _width(width), _total(total) {}
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
686
engines/glk/zcode/glk_interface.cpp
Normal file
686
engines/glk/zcode/glk_interface.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/glk_interface.h"
|
||||
#include "glk/zcode/pics.h"
|
||||
#include "glk/zcode/sound_folder.h"
|
||||
#include "glk/conf.h"
|
||||
#include "glk/screen.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/compression/unzip.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) :
|
||||
GlkAPI(syst, gameDesc), _pics(nullptr), curr_status_ht(0), mach_status_ht(0), gos_status(nullptr),
|
||||
gos_linepending(0), gos_linebuf(nullptr), gos_linewin(nullptr), gos_channel(nullptr), mwin(0),
|
||||
mouse_x(0), mouse_y(0), fixforced(0), menu_selected(0), enable_wrapping(false), enable_scripting(false),
|
||||
enable_scrolling(false), enable_buffering(false), next_sample(0), next_volume(0), _soundLocked(false),
|
||||
_soundPlaying(false), _reverseVideo(false) {
|
||||
Common::fill(&statusline[0], &statusline[256], '\0');
|
||||
Common::fill(&zcolors[0], &zcolors[zcolor_NUMCOLORS], 0);
|
||||
}
|
||||
|
||||
GlkInterface::~GlkInterface() {
|
||||
delete _pics;
|
||||
}
|
||||
|
||||
void GlkInterface::initialize() {
|
||||
/* Setup options */
|
||||
UserOptions::initialize(h_version, _storyId);
|
||||
|
||||
/* Setup colors array */
|
||||
const int COLOR_MAP[zcolor_NUMCOLORS - 2] = {
|
||||
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
|
||||
};
|
||||
|
||||
zcolors[0] = zcolor_Current; // Current
|
||||
zcolors[1] = zcolor_Default; // Default
|
||||
for (int i = 2; i < zcolor_NUMCOLORS; ++i)
|
||||
zcolors[i] = zRGB(COLOR_MAP[i - 2]);
|
||||
|
||||
/*
|
||||
* Init glk stuff
|
||||
*/
|
||||
|
||||
// monor
|
||||
glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Weight, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Oblique, 0);
|
||||
|
||||
// monob
|
||||
glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Weight, 1);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Oblique, 0);
|
||||
|
||||
// monoi
|
||||
glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Weight, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Oblique, 1);
|
||||
|
||||
// monoz
|
||||
glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Weight, 1);
|
||||
glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Oblique, 1);
|
||||
|
||||
// propr
|
||||
glk_stylehint_set(wintype_TextBuffer, style_Normal, stylehint_Proportional, 1);
|
||||
glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Weight, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Oblique, 0);
|
||||
|
||||
// propb
|
||||
glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Proportional, 1);
|
||||
glk_stylehint_set(wintype_TextGrid, style_Header, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Weight, 1);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Oblique, 0);
|
||||
|
||||
// propi
|
||||
glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Proportional, 1);
|
||||
glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Weight, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1);
|
||||
|
||||
// propi
|
||||
glk_stylehint_set(wintype_TextBuffer, style_Note, stylehint_Proportional, 1);
|
||||
glk_stylehint_set(wintype_TextGrid, style_Note, stylehint_Proportional, 0);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Weight, 1);
|
||||
glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Oblique, 1);
|
||||
|
||||
/*
|
||||
* Get the screen size
|
||||
*/
|
||||
|
||||
gos_channel = nullptr;
|
||||
|
||||
h_screen_width = g_system->getWidth();
|
||||
h_screen_height = g_system->getHeight();
|
||||
h_font_width = g_conf->_monoInfo._cellW;
|
||||
h_font_height = g_conf->_monoInfo._cellH;
|
||||
h_screen_cols = h_screen_width / h_font_width;
|
||||
h_screen_rows = h_screen_height / h_font_height;
|
||||
|
||||
// Must be after screen dimensions are computed
|
||||
if (g_conf->_graphics) {
|
||||
if (_blorb)
|
||||
// Blorb file containers allow graphics
|
||||
h_flags |= GRAPHICS_FLAG;
|
||||
else if ((h_version == V6 || _storyId == BEYOND_ZORK) && initPictures())
|
||||
// Earlier Infocom game with picture files
|
||||
h_flags |= GRAPHICS_FLAG;
|
||||
}
|
||||
|
||||
// Use the ms-dos interpreter number for v6, because that's the
|
||||
// kind of graphics files we understand
|
||||
h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_AMIGA;
|
||||
h_interpreter_version = 'F';
|
||||
|
||||
// Set these per spec 8.3.2.
|
||||
h_default_foreground = WHITE_COLOUR;
|
||||
h_default_background = BLACK_COLOUR;
|
||||
|
||||
// Set up the foreground & background
|
||||
_color_enabled = ((h_version >= 5) && (h_flags & COLOUR_FLAG))
|
||||
|| (_defaultForeground != zcolor_Transparent) || (_defaultBackground != zcolor_Transparent);
|
||||
|
||||
if (_color_enabled) {
|
||||
h_config |= CONFIG_COLOUR;
|
||||
h_flags |= COLOUR_FLAG; // FIXME: beyond zork handling?
|
||||
|
||||
if (h_version == 6) {
|
||||
h_default_foreground = BLACK_COLOUR;
|
||||
h_default_background = WHITE_COLOUR;
|
||||
}
|
||||
|
||||
zcolors[h_default_foreground] = _defaultForeground;
|
||||
zcolors[h_default_background] = _defaultBackground;
|
||||
} else {
|
||||
if (h_flags & COLOUR_FLAG)
|
||||
h_flags &= ~COLOUR_FLAG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the windows
|
||||
*/
|
||||
if (_storyId == BEYOND_ZORK)
|
||||
showBeyondZorkTitle();
|
||||
|
||||
_wp.setup(h_version == 6);
|
||||
for (uint i = 0; i < _wp.size(); ++i) {
|
||||
_wp[i][TRUE_FG_COLOR] = zcolors[h_default_foreground];
|
||||
_wp[i][TRUE_BG_COLOR] = zcolors[h_default_background];
|
||||
}
|
||||
|
||||
/*
|
||||
* Icky magic bit setting
|
||||
*/
|
||||
|
||||
if (h_version == V3 && _tandyBit)
|
||||
h_config |= CONFIG_TANDY;
|
||||
|
||||
if (h_version == V3 && _wp._upper)
|
||||
h_config |= CONFIG_SPLITSCREEN;
|
||||
|
||||
if (h_version == V3 && !_wp._upper)
|
||||
h_config |= CONFIG_NOSTATUSLINE;
|
||||
|
||||
if (h_version >= V4)
|
||||
h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
|
||||
CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
|
||||
|
||||
if (h_version >= V5)
|
||||
h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
|
||||
|
||||
if ((h_version >= 5) && (h_flags & SOUND_FLAG))
|
||||
h_flags |= SOUND_FLAG;
|
||||
|
||||
if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
|
||||
h_flags |= OLD_SOUND_FLAG;
|
||||
|
||||
if ((h_version == 6) && (_sound != 0))
|
||||
h_config |= CONFIG_SOUND;
|
||||
|
||||
if (h_version >= V5 && (h_flags & UNDO_FLAG))
|
||||
if (_undo_slots == 0)
|
||||
h_flags &= ~UNDO_FLAG;
|
||||
|
||||
/*
|
||||
* Miscellaneous
|
||||
*/
|
||||
|
||||
// Add any sound folder or zip
|
||||
addSound();
|
||||
|
||||
// For Beyond Zork the Page Up/Down keys are remapped to scroll the description area,
|
||||
// since the arrow keys the original used are in use now for cycling prior commands
|
||||
if (_storyId == BEYOND_ZORK) {
|
||||
uint32 KEYCODES[2] = { keycode_PageUp, keycode_PageDown };
|
||||
glk_set_terminators_line_event(_wp._lower, KEYCODES, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void GlkInterface::addSound() {
|
||||
Common::FSNode gameDir(ConfMan.getPath("path"));
|
||||
SoundSubfolder::check(gameDir);
|
||||
SoundZip::check(gameDir, _storyId);
|
||||
}
|
||||
|
||||
bool GlkInterface::initPictures() {
|
||||
if (Pics::exists()) {
|
||||
_pics = new Pics();
|
||||
SearchMan.add("Pics", _pics, 99, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (h_version == V6 && _storyId != MILLIWAYS)
|
||||
error("Could not locate MG1 file");
|
||||
return false;
|
||||
}
|
||||
|
||||
int GlkInterface::os_char_width(zchar z) {
|
||||
return g_conf->_monoInfo._cellW;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
glk_schannel_play_ext(gos_channel, number, repeats, eos);
|
||||
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) {
|
||||
beep();
|
||||
}
|
||||
|
||||
bool GlkInterface::os_picture_data(int picture, uint *height, uint *width) {
|
||||
if (_pics && picture == 0) {
|
||||
*width = _pics->version();
|
||||
*height = _pics->size();
|
||||
return true;
|
||||
} else {
|
||||
uint fullWidth, fullHeight;
|
||||
bool result = glk_image_get_info(picture, &fullWidth, &fullHeight);
|
||||
|
||||
*width = fullWidth;
|
||||
*height = fullHeight;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
|
||||
static zbyte LURKING_REPEATS[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0xff,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01,
|
||||
0xff, 0x01, 0x01, 0xff, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
if (_storyId == LURKING_HORROR)
|
||||
repeats = LURKING_REPEATS[number];
|
||||
|
||||
os_start_sample(number, volume, repeats, eos);
|
||||
|
||||
_soundPlaying = true;
|
||||
}
|
||||
|
||||
void GlkInterface::start_next_sample() {
|
||||
if (next_sample != 0)
|
||||
start_sample(next_sample, next_volume, 0, 0);
|
||||
|
||||
next_sample = 0;
|
||||
next_volume = 0;
|
||||
}
|
||||
|
||||
void GlkInterface::gos_update_width() {
|
||||
uint width;
|
||||
if (_wp._upper) {
|
||||
glk_window_get_size(_wp._upper, &width, nullptr);
|
||||
h_screen_cols = width;
|
||||
SET_BYTE(H_SCREEN_COLS, width);
|
||||
|
||||
uint curx = _wp._upper[X_CURSOR];
|
||||
if (curx > width)
|
||||
_wp._upper.setCursor(Point(1, _wp._upper[Y_CURSOR]));
|
||||
}
|
||||
}
|
||||
|
||||
void GlkInterface::gos_update_height() {
|
||||
uint height_upper;
|
||||
uint height_lower;
|
||||
if (_wp.currWin()) {
|
||||
glk_window_get_size(_wp._upper, nullptr, &height_upper);
|
||||
glk_window_get_size(_wp._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() {
|
||||
uint height;
|
||||
if (_wp._upper && h_version != 6) {
|
||||
glk_window_get_size(_wp._upper, nullptr, &height);
|
||||
if ((uint)mach_status_ht != height) {
|
||||
glk_window_set_arrangement(glk_window_get_parent(_wp._upper),
|
||||
winmethod_Above | winmethod_Fixed, mach_status_ht, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GlkInterface::erase_window(zword w) {
|
||||
if (w == 0)
|
||||
_wp._lower.clear();
|
||||
|
||||
else if (_wp._upper) {
|
||||
//os_set_reverse_video(glk_window_get_stream(_wp._upper), true);
|
||||
|
||||
memset(statusline, ' ', sizeof statusline);
|
||||
_wp._upper.clear();
|
||||
reset_status_ht();
|
||||
curr_status_ht = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GlkInterface::split_window(zword lines) {
|
||||
if (!_wp._upper)
|
||||
return;
|
||||
|
||||
// The top line is always set for V1 to V3 games
|
||||
if (h_version < V4)
|
||||
lines++;
|
||||
|
||||
if ((!lines || lines > curr_status_ht) && h_version != 6) {
|
||||
uint height;
|
||||
|
||||
glk_window_get_size(_wp._upper, nullptr, &height);
|
||||
if (lines != height)
|
||||
glk_window_set_arrangement(glk_window_get_parent(_wp._upper),
|
||||
winmethod_Above | winmethod_Fixed, lines, nullptr);
|
||||
curr_status_ht = lines;
|
||||
}
|
||||
mach_status_ht = lines;
|
||||
|
||||
int curY = _wp._upper[Y_CURSOR];
|
||||
if (curY > lines)
|
||||
_wp._upper.setCursor(Point(1, 1));
|
||||
gos_update_width();
|
||||
|
||||
if (h_version == V3)
|
||||
_wp._upper.clear();
|
||||
if (h_version == V6) {
|
||||
_wp._upper.clear();
|
||||
_wp._lower.clear();
|
||||
_wp._background->fillRect(_defaultBackground, Rect(g_system->getWidth(), g_system->getHeight()));
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
uint32 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));
|
||||
|
||||
Point cursPos(_wp._upper[X_CURSOR], _wp._upper[Y_CURSOR]);
|
||||
_wp._upper.setCursor(Point(1, 1));
|
||||
glk_put_buffer_uni(buf, h_screen_cols);
|
||||
_wp._upper.setCursor(cursPos);
|
||||
}
|
||||
|
||||
void GlkInterface::gos_cancel_pending_line() {
|
||||
event_t ev;
|
||||
glk_cancel_line_event(gos_linewin, &ev);
|
||||
gos_linebuf[ev.val1] = '\0';
|
||||
gos_linepending = 0;
|
||||
}
|
||||
|
||||
void GlkInterface::showBeyondZorkTitle() {
|
||||
int saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
|
||||
|
||||
if (saveSlot == -1) {
|
||||
winid_t win = glk_window_open(nullptr, 0, 0, wintype_Graphics, 0);
|
||||
if (glk_image_draw_scaled(win, 1, 0, 0, g_vm->_screen->w, g_vm->_screen->h))
|
||||
_events->waitForPress();
|
||||
|
||||
glk_window_close(win, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void GlkInterface::os_draw_picture(int picture, const Common::Point &pos) {
|
||||
if (pos.x && pos.y) {
|
||||
_wp._background->bringToFront();
|
||||
Point pt(pos.x - 1, pos.y - 1);
|
||||
if (h_version < V5) {
|
||||
pt.x *= g_conf->_monoInfo._cellW;
|
||||
pt.y *= g_conf->_monoInfo._cellH;
|
||||
}
|
||||
|
||||
glk_image_draw(_wp._background, picture, pt.x, pt.y);
|
||||
} else {
|
||||
// Picture embedded within the lower text area
|
||||
_wp.currWin().imageDraw(picture, imagealign_MarginLeft, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int GlkInterface::os_peek_color() {
|
||||
if (_color_enabled) {
|
||||
return _defaultBackground;
|
||||
} else {
|
||||
return (_reverseVideo) ? h_default_foreground : h_default_background;
|
||||
}
|
||||
/*
|
||||
if (u_setup.color_enabled) { */
|
||||
#ifdef COLOR_SUPPORT
|
||||
short fg, bg;
|
||||
pair_content(PAIR_NUMBER(inch() & A_COLOR), &fg, &bg);
|
||||
switch(bg) {
|
||||
case COLOR_BLACK: return BLACK_COLOUR;
|
||||
case COLOR_RED: return RED_COLOUR;
|
||||
case COLOR_GREEN: return GREEN_COLOUR;
|
||||
case COLOR_YELLOW: return YELLOW_COLOUR;
|
||||
case COLOR_BLUE: return BLUE_COLOUR;
|
||||
case COLOR_MAGENTA: return MAGENTA_COLOUR;
|
||||
case COLOR_CYAN: return CYAN_COLOUR;
|
||||
case COLOR_WHITE: return WHITE_COLOUR;
|
||||
}
|
||||
return 0;
|
||||
#endif /* COLOR_SUPPORT */
|
||||
}
|
||||
|
||||
zchar GlkInterface::os_read_key(int timeout, bool show_cursor) {
|
||||
Window &win = _wp.currWin() ? _wp.currWin() : _wp._lower;
|
||||
uint key;
|
||||
|
||||
if (win) {
|
||||
// Get a keypress from a window
|
||||
if (gos_linepending)
|
||||
gos_cancel_pending_line();
|
||||
|
||||
glk_request_char_event_uni(win);
|
||||
if (timeout != 0)
|
||||
glk_request_timer_events(timeout * 100);
|
||||
|
||||
event_t ev;
|
||||
while (!shouldQuit()) {
|
||||
glk_select(&ev);
|
||||
if (ev.type == evtype_Arrange) {
|
||||
gos_update_height();
|
||||
gos_update_width();
|
||||
} else if (ev.type == evtype_Timer) {
|
||||
glk_cancel_char_event(win);
|
||||
glk_request_timer_events(0);
|
||||
return ZC_TIME_OUT;
|
||||
} else if (ev.type == evtype_CharInput)
|
||||
break;
|
||||
}
|
||||
if (shouldQuit())
|
||||
return 0;
|
||||
|
||||
glk_request_timer_events(0);
|
||||
|
||||
if (_wp._upper && mach_status_ht < curr_status_ht)
|
||||
reset_status_ht();
|
||||
curr_status_ht = 0;
|
||||
key = ev.val1;
|
||||
} else {
|
||||
// No active window, so get a raw keypress
|
||||
key = _events->getKeypress();
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case keycode_Escape: return ZC_ESCAPE;
|
||||
case keycode_PageUp: return ZC_ARROW_MIN;
|
||||
case keycode_PageDown: return ZC_ARROW_MAX;
|
||||
case keycode_Left: return ZC_ARROW_LEFT;
|
||||
case keycode_Right: return ZC_ARROW_RIGHT;
|
||||
case keycode_Up: return ZC_ARROW_UP;
|
||||
case keycode_Down: return ZC_ARROW_DOWN;
|
||||
case keycode_Return: return ZC_RETURN;
|
||||
case keycode_Delete: return ZC_BACKSPACE;
|
||||
case keycode_Tab: return ZC_INDENT;
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
zchar GlkInterface::os_read_line(int max, zchar *buf, int timeout, int width, int continued) {
|
||||
event_t ev;
|
||||
winid_t win = _wp.currWin() ? _wp.currWin() : _wp._lower;
|
||||
|
||||
if (!continued && gos_linepending)
|
||||
gos_cancel_pending_line();
|
||||
|
||||
if (!continued || !gos_linepending) {
|
||||
glk_request_line_event_uni(win, buf, max, os_string_length(buf));
|
||||
if (timeout != 0)
|
||||
glk_request_timer_events(timeout * 100);
|
||||
}
|
||||
|
||||
gos_linepending = 0;
|
||||
|
||||
while (!shouldQuit()) {
|
||||
glk_select(&ev);
|
||||
if (ev.type == evtype_Arrange) {
|
||||
gos_update_height();
|
||||
gos_update_width();
|
||||
} else if (ev.type == evtype_Timer) {
|
||||
gos_linewin = win;
|
||||
gos_linepending = 1;
|
||||
gos_linebuf = buf;
|
||||
return ZC_TIME_OUT;
|
||||
} else if (ev.type == evtype_LineInput) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shouldQuit())
|
||||
return 0;
|
||||
|
||||
glk_request_timer_events(0);
|
||||
buf[ev.val1] = '\0';
|
||||
|
||||
// If the upper status line area was expanded to show a text box/quotation, restore it back
|
||||
if (_wp._upper && mach_status_ht < curr_status_ht)
|
||||
reset_status_ht();
|
||||
curr_status_ht = 0;
|
||||
|
||||
if (ev.val2) {
|
||||
// Line terminator specified, so return it
|
||||
if (_storyId == BEYOND_ZORK && ev.val2 == keycode_PageUp)
|
||||
return ZC_ARROW_UP;
|
||||
else if (_storyId == BEYOND_ZORK && ev.val2 == keycode_PageDown)
|
||||
return ZC_ARROW_DOWN;
|
||||
|
||||
return ev.val2;
|
||||
}
|
||||
|
||||
return ZC_RETURN;
|
||||
}
|
||||
|
||||
uint GlkInterface::roundDiv(uint x, uint y) {
|
||||
uint quotient = x / y;
|
||||
uint dblremain = (x % y) << 1;
|
||||
|
||||
if ((dblremain > y) || ((dblremain == y) && (quotient & 1)))
|
||||
quotient++;
|
||||
return quotient;
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
259
engines/glk/zcode/glk_interface.h
Normal file
259
engines/glk/zcode/glk_interface.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_GLK_INTERFACE
|
||||
#define GLK_ZCODE_GLK_INTERFACE
|
||||
|
||||
#include "glk/glk_api.h"
|
||||
#include "glk/zcode/mem.h"
|
||||
#include "glk/zcode/windows.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#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) _screen->format.RGBToColor(zR(i), zG(i), zB(i))
|
||||
#define zcolor_NUMCOLORS (13)
|
||||
|
||||
enum SoundEffect {
|
||||
EFFECT_PREPARE = 1,
|
||||
EFFECT_PLAY = 2,
|
||||
EFFECT_STOP = 3,
|
||||
EFFECT_FINISH_WITH = 4
|
||||
};
|
||||
|
||||
enum RestartAction {
|
||||
RESTART_BEGIN = 0,
|
||||
RESTART_WPROP_SET = 1,
|
||||
RESTART_END = 2
|
||||
};
|
||||
|
||||
class Pics;
|
||||
|
||||
/**
|
||||
* Implements an intermediate interface on top of the GLK layer, providing screen
|
||||
* and sound effect handling
|
||||
*/
|
||||
class GlkInterface : public GlkAPI, public virtual UserOptions, public virtual Mem {
|
||||
private:
|
||||
bool _reverseVideo;
|
||||
public:
|
||||
Pics *_pics;
|
||||
zchar statusline[256];
|
||||
uint zcolors[zcolor_NUMCOLORS];
|
||||
int fixforced;
|
||||
|
||||
int curr_status_ht;
|
||||
int mach_status_ht;
|
||||
|
||||
Windows _wp;
|
||||
winid_t gos_status;
|
||||
int gos_linepending;
|
||||
zchar *gos_linebuf;
|
||||
winid_t gos_linewin;
|
||||
schanid_t gos_channel;
|
||||
|
||||
// Mouse data
|
||||
int mwin;
|
||||
int mouse_y;
|
||||
int mouse_x;
|
||||
int menu_selected;
|
||||
|
||||
// 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;
|
||||
private:
|
||||
/**
|
||||
* Loads the pictures file for Infocom V6 games
|
||||
*/
|
||||
bool initPictures();
|
||||
|
||||
/**
|
||||
* Displays the title screen for the game Beyond Zork
|
||||
*/
|
||||
void showBeyondZorkTitle();
|
||||
|
||||
/**
|
||||
* Add any Sound subfolder or sound zip file for access
|
||||
*/
|
||||
void addSound();
|
||||
|
||||
/**
|
||||
* Do a rounding division, rounding to even if fraction part is 1/2.
|
||||
*/
|
||||
uint roundDiv(uint x, uint y);
|
||||
protected:
|
||||
/**
|
||||
* Return the length of the character in screen units.
|
||||
*/
|
||||
int os_char_width(zchar z);
|
||||
|
||||
/**
|
||||
* Calculate the length of a word in screen units. Apart from letters,
|
||||
* the word may contain special codes:
|
||||
*
|
||||
* ZC_NEW_STYLE - next character is a new text style
|
||||
* ZC_NEW_FONT - next character is a new font
|
||||
*/
|
||||
int os_string_width(const zchar *s);
|
||||
|
||||
/**
|
||||
* Return the length of a string
|
||||
*/
|
||||
int os_string_length(zchar *s);
|
||||
|
||||
/**
|
||||
* Prepare a sample for playing
|
||||
*/
|
||||
void os_prepare_sample(int a);
|
||||
|
||||
/**
|
||||
* Signal that a given sample is finished with
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Stop playing a given sound number
|
||||
*/
|
||||
void os_stop_sample(int a);
|
||||
|
||||
/**
|
||||
* Make a beep sound
|
||||
*/
|
||||
void os_beep(int volume);
|
||||
|
||||
/**
|
||||
* Return true if the given picture is available. If so, write the
|
||||
* width and height of the picture into the appropriate variables.
|
||||
* Only when picture 0 is asked for, write the number of available
|
||||
* pictures and the release number instead.
|
||||
*/
|
||||
bool os_picture_data(int picture, uint *height, uint *width);
|
||||
|
||||
/**
|
||||
* Display a picture at the given coordinates. Top left is (1,1).
|
||||
*/
|
||||
void os_draw_picture(int picture, const Common::Point &pos);
|
||||
|
||||
/**
|
||||
* Return the colour of the pixel below the cursor. This is used by V6 games to print
|
||||
* text on top of pictures. The coulor need not be in the standard set of Z-machine colours.
|
||||
*/
|
||||
int os_peek_color();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Cancels any pending line
|
||||
*/
|
||||
void gos_cancel_pending_line();
|
||||
|
||||
/**
|
||||
* Called during game restarts
|
||||
*/
|
||||
void os_restart_game(RestartAction stage) {}
|
||||
|
||||
/**
|
||||
* Reads the mouse buttons
|
||||
*/
|
||||
zword os_read_mouse() {
|
||||
// Not implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_scrollback_char(zchar z) {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
void os_scrollback_erase(int amount) {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a keypress
|
||||
*/
|
||||
zchar os_read_key(int timeout, bool show_cursor);
|
||||
|
||||
/**
|
||||
* Waits for the user to type an input line
|
||||
*/
|
||||
zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued);
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~GlkInterface() override;
|
||||
|
||||
/**
|
||||
* Initialization
|
||||
*/
|
||||
void initialize();
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
42
engines/glk/zcode/infocom6x8.xbm
Normal file
42
engines/glk/zcode/infocom6x8.xbm
Normal file
@@ -0,0 +1,42 @@
|
||||
#define infocom6x8_width 192
|
||||
#define infocom6x8_height 24
|
||||
static unsigned char infocom6x8_bits[] = {
|
||||
0x40,0x50,0x28,0x44,0x22,0x08,0x44,0x90,0x00,0x00,0x00,0x20,0x86,0x60,0x18,
|
||||
0xc5,0xe3,0x3c,0x86,0x01,0x00,0x00,0x00,0x38,0x40,0x50,0x7c,0x1f,0x52,0x08,
|
||||
0x82,0x60,0x10,0x00,0x00,0x20,0xc9,0x90,0x24,0x45,0x10,0x20,0x49,0x02,0x00,
|
||||
0x04,0x10,0x44,0x40,0x00,0x28,0x05,0x21,0x04,0x01,0xf1,0x10,0x00,0x00,0x10,
|
||||
0x89,0x40,0x10,0xcf,0x71,0x10,0x46,0x12,0x08,0xc2,0x23,0x20,0x40,0x00,0x7c,
|
||||
0x9f,0x50,0x01,0x01,0x61,0x7c,0xc0,0x03,0x08,0x89,0x20,0x20,0x04,0x92,0x08,
|
||||
0x89,0x03,0x00,0x01,0x40,0x10,0x00,0x00,0x28,0x54,0x90,0x00,0x01,0x91,0x10,
|
||||
0x02,0x00,0x04,0x89,0x10,0x24,0x04,0x92,0x08,0x09,0x12,0x08,0xc2,0x23,0x00,
|
||||
0x40,0x00,0x00,0x5f,0x62,0x01,0x82,0x00,0x10,0x02,0x10,0x04,0xc6,0xf1,0x18,
|
||||
0xc4,0x61,0x08,0xc6,0x01,0x08,0x04,0x10,0x10,0x00,0x00,0x00,0x04,0x00,0x00,
|
||||
0x44,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x71,0x38,
|
||||
0xc7,0xf3,0x38,0xc9,0x81,0x24,0x41,0x94,0x18,0x87,0x71,0x38,0x5f,0x12,0x45,
|
||||
0x51,0xf4,0x0c,0xc1,0x40,0x00,0x49,0x92,0x04,0x49,0x10,0x04,0x89,0x80,0x14,
|
||||
0xc1,0xb6,0x24,0x49,0x92,0x04,0x44,0x12,0x45,0x4a,0x84,0x04,0x81,0xa0,0x00,
|
||||
0x4d,0x72,0x04,0xc9,0x71,0x04,0x89,0x80,0x0c,0x41,0xd5,0x24,0x49,0x92,0x18,
|
||||
0x44,0x12,0x45,0x84,0x42,0x04,0x82,0x10,0x01,0xcd,0x93,0x04,0x49,0x10,0x34,
|
||||
0x8f,0x80,0x14,0x41,0x94,0x24,0x47,0x72,0x20,0x44,0xa2,0x54,0x04,0x21,0x04,
|
||||
0x84,0x00,0x00,0x41,0x92,0x04,0x49,0x10,0x24,0x89,0x90,0x24,0x41,0x94,0x24,
|
||||
0x41,0x33,0x24,0x44,0xa2,0x6c,0x0a,0x11,0x04,0x88,0x00,0x00,0x4e,0x72,0x38,
|
||||
0xc7,0x13,0x38,0xc9,0x61,0x24,0x4f,0x94,0x18,0x81,0xd3,0x18,0x84,0x41,0x44,
|
||||
0x11,0xf1,0x0c,0xc8,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x00,0x04,0x60,0x00,
|
||||
0x41,0x20,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x10,
|
||||
0x41,0x00,0x00,0xc1,0x71,0x1c,0xc7,0x21,0x1c,0x07,0x00,0x14,0xc1,0x77,0x1c,
|
||||
0xc7,0x71,0x1c,0x47,0x51,0x54,0x45,0x71,0x08,0x81,0x00,0x00,0x02,0x51,0x04,
|
||||
0x45,0x71,0x14,0x45,0x20,0x14,0x41,0x55,0x14,0x45,0x11,0x04,0x42,0x51,0x54,
|
||||
0x45,0x41,0x08,0x81,0x20,0x00,0xc0,0x51,0x04,0xc5,0x21,0x14,0x45,0x20,0x0c,
|
||||
0x41,0x55,0x14,0x45,0x11,0x1c,0x42,0x51,0x54,0x42,0x21,0x04,0x00,0x51,0x01,
|
||||
0x40,0x51,0x04,0x45,0x20,0x14,0x45,0x20,0x14,0x41,0x55,0x14,0x45,0x11,0x10,
|
||||
0x42,0x51,0x54,0x45,0x11,0x08,0x81,0x80,0x00,0xc0,0x71,0x1c,0xc7,0x21,0x1c,
|
||||
0x45,0x20,0x14,0x41,0x55,0x1c,0xc7,0x11,0x1c,0xc2,0x21,0x7c,0xc5,0x71,0x08,
|
||||
0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x20,0x00,0x00,0x00,0x00,
|
||||
0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x41,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x1c,0x00,0x30,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,
|
||||
0xc0,0x01,0x00,0x00,0x00,0x00};
|
||||
55
engines/glk/zcode/infocom_graphics.xbm
Normal file
55
engines/glk/zcode/infocom_graphics.xbm
Normal file
@@ -0,0 +1,55 @@
|
||||
#define infocom_graphics_width 256
|
||||
#define infocom_graphics_height 24
|
||||
static unsigned char infocom_graphics_bits[] = {
|
||||
0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x00,0x10,0x08,0x10,0x00,0x10,0x08,0x08,
|
||||
0x00,0x00,0x10,0x08,0x01,0x80,0x10,0xff,0xff,0x00,0x1f,0xf8,0x10,0xff,0x1f,
|
||||
0xf8,0xf8,0x00,0x0c,0x30,0x40,0x02,0x00,0x00,0x00,0x10,0x08,0x10,0x00,0x10,
|
||||
0x08,0x08,0x00,0x00,0x10,0x08,0x02,0x40,0x10,0xff,0xff,0x00,0x1f,0xf8,0x10,
|
||||
0xff,0x1f,0xf8,0xf8,0x00,0x0e,0x70,0x20,0x04,0x00,0x00,0x00,0x10,0x08,0x10,
|
||||
0x00,0x10,0x08,0x08,0x00,0x00,0x10,0x08,0x04,0x20,0x10,0xff,0xff,0x00,0x1f,
|
||||
0xf8,0x10,0xff,0x1f,0xf8,0xf8,0x00,0xff,0xff,0x10,0x08,0x00,0x00,0xff,0x10,
|
||||
0x08,0xff,0x00,0x10,0x08,0x08,0xf8,0x1f,0x10,0x08,0xf8,0x1f,0x10,0xff,0xff,
|
||||
0xff,0x1f,0xf8,0xff,0xff,0x1f,0xf8,0xf8,0x00,0x0e,0x70,0x08,0x10,0x00,0xff,
|
||||
0x00,0x10,0x08,0x00,0xff,0xf0,0x0f,0xf8,0x08,0x10,0x1f,0xf8,0x08,0x10,0x1f,
|
||||
0xff,0xff,0xff,0x1f,0xf8,0xff,0xff,0xff,0xff,0xf8,0x00,0x0c,0x30,0x04,0x20,
|
||||
0x00,0x00,0x00,0x10,0x08,0x00,0x10,0x10,0x08,0x00,0x08,0x10,0x00,0x04,0x08,
|
||||
0x10,0x20,0xff,0x00,0xff,0x1f,0xf8,0xff,0x10,0x1f,0xf8,0x00,0x00,0x00,0x00,
|
||||
0x02,0x40,0x00,0x00,0x00,0x10,0x08,0x00,0x10,0x10,0x08,0x00,0x08,0x10,0x00,
|
||||
0x02,0x08,0x10,0x40,0xff,0x00,0xff,0x1f,0xf8,0xff,0x10,0x1f,0xf8,0x00,0x00,
|
||||
0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x10,0x08,0x00,0x10,0x10,0x08,0x00,0x08,
|
||||
0x10,0x00,0x01,0x08,0x10,0x80,0xff,0x00,0xff,0x1f,0xf8,0xff,0x10,0x1f,0xf8,
|
||||
0x00,0x00,0x00,0x1f,0xf8,0x01,0x80,0x1f,0x80,0x00,0x00,0x01,0xff,0x00,0x01,
|
||||
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0x10,0x18,
|
||||
0x00,0x18,0xff,0x00,0x00,0x1f,0xf8,0x02,0x40,0x1f,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x01,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x01,0x42,
|
||||
0x10,0x3c,0x18,0x3c,0x81,0x00,0x00,0x1f,0xf8,0x04,0x20,0x1f,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x01,0x80,0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff,0x80,
|
||||
0x01,0x24,0x10,0x7e,0x18,0x7e,0x81,0xf8,0x1f,0x1f,0xf8,0xf8,0x1f,0x1f,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,
|
||||
0xff,0x80,0x01,0x18,0x10,0x18,0x18,0x18,0x81,0xf8,0x1f,0x1f,0xf8,0xf8,0x1f,
|
||||
0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x01,0x03,0x07,0x0f,0x1f,
|
||||
0x3f,0x7f,0xff,0x80,0x01,0x18,0xff,0x18,0x18,0x18,0x81,0xf8,0x1f,0x00,0x04,
|
||||
0xf8,0x1f,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x01,0x03,0x07,
|
||||
0x0f,0x1f,0x3f,0x7f,0xff,0x80,0x01,0x24,0x10,0x18,0x7e,0x7e,0x81,0xf8,0x1f,
|
||||
0x00,0x02,0xf8,0x1f,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x01,0x42,0x10,0x18,0x3c,0x3c,0x81,
|
||||
0xf8,0x1f,0x00,0x01,0xf8,0x1f,0x80,0x00,0x80,0x01,0x00,0x00,0xff,0x01,0x80,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0x10,0x00,0x18,
|
||||
0x18,0xff,0x00,0xc6,0x1e,0x00,0xc6,0xc6,0x92,0x00,0xc6,0x0c,0x18,0x06,0x1c,
|
||||
0xee,0x18,0x8e,0x06,0x18,0x3e,0x06,0x18,0x0e,0x7e,0x0e,0xdb,0x1e,0x06,0xe7,
|
||||
0xff,0xe7,0xff,0x00,0x3c,0x6e,0x76,0x78,0xc6,0xee,0x4a,0xc6,0xca,0x0c,0x18,
|
||||
0x06,0x3c,0xd6,0x1b,0x56,0x06,0x18,0xc6,0xc6,0x3c,0x1e,0xc0,0x36,0x5a,0x26,
|
||||
0xc6,0xc3,0xe7,0xc3,0xc3,0x00,0x66,0x3a,0xc6,0xd8,0xee,0xd6,0x26,0x6c,0xf2,
|
||||
0x0c,0x7e,0x1e,0x6c,0xee,0x1e,0x26,0xc6,0x18,0xc6,0xe6,0x5a,0x36,0x7e,0xc6,
|
||||
0x3c,0x4a,0x66,0x81,0xe7,0x81,0x99,0x00,0x60,0x06,0x3e,0x18,0x92,0xd6,0x16,
|
||||
0x38,0xc6,0x0c,0x99,0x36,0x0c,0xc6,0x3c,0x8e,0xe6,0x18,0x3e,0xf6,0x99,0x66,
|
||||
0xc0,0x36,0x18,0x92,0x36,0xe7,0xe7,0xe7,0x9f,0x00,0x18,0x1e,0xc6,0x18,0xee,
|
||||
0xc6,0x0e,0x38,0x9e,0x0c,0x7e,0xe6,0x0c,0xc6,0x78,0x56,0xf6,0x3c,0x66,0xde,
|
||||
0x18,0xc6,0xc0,0x1e,0x18,0xf2,0x1e,0xe7,0xe7,0xe7,0xe7,0x00,0x00,0x66,0x76,
|
||||
0x1b,0xc6,0xc6,0x06,0x6c,0xa6,0x0c,0x18,0xc6,0x0c,0xc6,0xd8,0x26,0xde,0x5a,
|
||||
0xc6,0xce,0x18,0xc6,0xc0,0x06,0x18,0x92,0x06,0xe7,0x81,0x81,0xff,0x00,0x18,
|
||||
0x06,0x1e,0x1e,0xc6,0xc6,0x06,0xc6,0xc6,0x0c,0x18,0xc6,0x0c,0xc6,0x18,0x06,
|
||||
0xce,0xdb,0xc6,0xc0,0x18,0xc6,0x3e,0x06,0x18,0x92,0x06,0xe7,0xc3,0xc3,0xe7,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xe7,
|
||||
0xe7,0xff,0x00};
|
||||
309
engines/glk/zcode/mem.cpp
Normal file
309
engines/glk/zcode/mem.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/mem.h"
|
||||
#include "glk/zcode/zcode.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
Mem::Mem() : story_fp(nullptr), story_size(0), first_undo(nullptr), last_undo(nullptr),
|
||||
curr_undo(nullptr), undo_mem(nullptr), zmp(nullptr), pcp(nullptr), prev_zmp(nullptr),
|
||||
undo_diff(nullptr), undo_count(0), reserve_mem(0) {
|
||||
}
|
||||
|
||||
void Mem::initialize() {
|
||||
initializeStoryFile();
|
||||
loadGameHeader();
|
||||
loadMemory();
|
||||
initializeUndo();
|
||||
|
||||
// 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() {
|
||||
if (story_fp->size() < 64)
|
||||
error("This file is too small to be a Z-code file.");
|
||||
}
|
||||
|
||||
void Mem::loadGameHeader() {
|
||||
// Load header
|
||||
zmp = (byte *)malloc(64);
|
||||
story_fp->seek(0);
|
||||
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 = story_fp->size();
|
||||
}
|
||||
}
|
||||
|
||||
void Mem::loadMemory() {
|
||||
// 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;
|
||||
|
||||
if (story_fp->read(zmp + size, n) != n)
|
||||
error("Story file read error");
|
||||
}
|
||||
}
|
||||
|
||||
void Mem::initializeUndo() {
|
||||
byte *reserved = nullptr;
|
||||
|
||||
if (reserve_mem != 0) {
|
||||
if ((reserved = new byte[reserve_mem]) == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate h_dynamic_size bytes for previous dynamic zmp state
|
||||
// + 1.5 h_dynamic_size for Quetzal diff + 2.
|
||||
undo_mem = new zbyte[(h_dynamic_size * 5) / 2 + 2];
|
||||
if (undo_mem != nullptr) {
|
||||
prev_zmp = undo_mem;
|
||||
undo_diff = undo_mem + h_dynamic_size;
|
||||
memcpy(prev_zmp, zmp, h_dynamic_size);
|
||||
} else {
|
||||
_undo_slots = 0;
|
||||
}
|
||||
|
||||
if (reserve_mem)
|
||||
delete[] reserved;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void Mem::set_header_extension(int entry, zword val) {
|
||||
zword addr;
|
||||
|
||||
if (h_extension_table == 0 || entry > hx_table_size)
|
||||
return;
|
||||
|
||||
addr = h_extension_table + 2 * entry;
|
||||
SET_WORD(addr, val);
|
||||
}
|
||||
|
||||
void Mem::restart_header(void) {
|
||||
zword screen_x_size;
|
||||
zword screen_y_size;
|
||||
zbyte font_x_size;
|
||||
zbyte font_y_size;
|
||||
|
||||
int i;
|
||||
|
||||
SET_BYTE(H_CONFIG, h_config);
|
||||
SET_WORD(H_FLAGS, h_flags);
|
||||
|
||||
if (h_version >= V4) {
|
||||
SET_BYTE(H_INTERPRETER_NUMBER, h_interpreter_number);
|
||||
SET_BYTE(H_INTERPRETER_VERSION, h_interpreter_version);
|
||||
SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
|
||||
SET_BYTE(H_SCREEN_COLS, h_screen_cols);
|
||||
}
|
||||
|
||||
// It's less trouble to use font size 1x1 for V5 games, especially because of
|
||||
// a bug in the unreleased German version of "Zork 1"
|
||||
|
||||
if (h_version != V6) {
|
||||
screen_x_size = (zword)h_screen_cols;
|
||||
screen_y_size = (zword)h_screen_rows;
|
||||
font_x_size = 1;
|
||||
font_y_size = 1;
|
||||
} else {
|
||||
screen_x_size = h_screen_width;
|
||||
screen_y_size = h_screen_height;
|
||||
font_x_size = h_font_width;
|
||||
font_y_size = h_font_height;
|
||||
}
|
||||
|
||||
if (h_version >= V5) {
|
||||
SET_WORD(H_SCREEN_WIDTH, screen_x_size);
|
||||
SET_WORD(H_SCREEN_HEIGHT, screen_y_size);
|
||||
SET_BYTE(H_FONT_HEIGHT, font_y_size);
|
||||
SET_BYTE(H_FONT_WIDTH, font_x_size);
|
||||
SET_BYTE(H_DEFAULT_BACKGROUND, h_default_background);
|
||||
SET_BYTE(H_DEFAULT_FOREGROUND, h_default_foreground);
|
||||
}
|
||||
|
||||
if (h_version == V6)
|
||||
for (i = 0; i < 8; i++)
|
||||
storeb((zword)(H_USER_NAME + i), h_user_name[i]);
|
||||
|
||||
SET_BYTE(H_STANDARD_HIGH, h_standard_high);
|
||||
SET_BYTE(H_STANDARD_LOW, h_standard_low);
|
||||
|
||||
set_header_extension(HX_FLAGS, hx_flags);
|
||||
set_header_extension(HX_FORE_COLOUR, hx_fore_colour);
|
||||
set_header_extension(HX_BACK_COLOUR, hx_back_colour);
|
||||
}
|
||||
|
||||
void Mem::storeb(zword addr, zbyte value) {
|
||||
if (addr >= h_dynamic_size)
|
||||
runtimeError(ERR_STORE_RANGE);
|
||||
|
||||
if (addr == H_FLAGS + 1) {
|
||||
// flags register is modified
|
||||
|
||||
h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
|
||||
h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
|
||||
|
||||
flagsChanged(value);
|
||||
}
|
||||
|
||||
SET_BYTE(addr, value);
|
||||
}
|
||||
|
||||
void Mem::storew(zword addr, zword value) {
|
||||
storeb((zword)(addr + 0), hi(value));
|
||||
storeb((zword)(addr + 1), lo(value));
|
||||
}
|
||||
|
||||
void Mem::free_undo(int count) {
|
||||
undo_t *p;
|
||||
|
||||
if (count > undo_count)
|
||||
count = undo_count;
|
||||
while (count--) {
|
||||
p = first_undo;
|
||||
if (curr_undo == first_undo)
|
||||
curr_undo = curr_undo->next;
|
||||
first_undo = first_undo->next;
|
||||
free(p);
|
||||
undo_count--;
|
||||
}
|
||||
if (first_undo)
|
||||
first_undo->prev = nullptr;
|
||||
else
|
||||
last_undo = nullptr;
|
||||
}
|
||||
|
||||
void Mem::reset_memory() {
|
||||
story_fp = nullptr;
|
||||
|
||||
if (undo_mem) {
|
||||
free_undo(undo_count);
|
||||
delete[] undo_mem;
|
||||
}
|
||||
|
||||
undo_mem = nullptr;
|
||||
undo_count = 0;
|
||||
free(zmp);
|
||||
zmp = nullptr;
|
||||
}
|
||||
|
||||
long Mem::mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff) {
|
||||
unsigned size = mem_size;
|
||||
zbyte *p = diff;
|
||||
unsigned j;
|
||||
zbyte c = 0;
|
||||
|
||||
for (;;) {
|
||||
for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
|
||||
size--;
|
||||
if (size == 0) break;
|
||||
size--;
|
||||
if (j > 0x8000) {
|
||||
*p++ = 0;
|
||||
*p++ = 0xff;
|
||||
*p++ = 0xff;
|
||||
j -= 0x8000;
|
||||
}
|
||||
if (j > 0) {
|
||||
*p++ = 0;
|
||||
j--;
|
||||
if (j <= 0x7f) {
|
||||
*p++ = j;
|
||||
} else {
|
||||
*p++ = (j & 0x7f) | 0x80;
|
||||
*p++ = (j & 0x7f80) >> 7;
|
||||
}
|
||||
}
|
||||
|
||||
*p++ = c;
|
||||
*(b - 1) ^= c;
|
||||
}
|
||||
|
||||
return p - diff;
|
||||
}
|
||||
|
||||
void Mem::mem_undiff(zbyte *diff, long diff_length, zbyte *dest) {
|
||||
zbyte c;
|
||||
|
||||
while (diff_length) {
|
||||
c = *diff++;
|
||||
diff_length--;
|
||||
if (c == 0) {
|
||||
unsigned runlen;
|
||||
|
||||
if (!diff_length)
|
||||
return; // Incomplete run
|
||||
runlen = *diff++;
|
||||
diff_length--;
|
||||
if (runlen & 0x80) {
|
||||
if (!diff_length)
|
||||
return; // Incomplete extended run
|
||||
c = *diff++;
|
||||
diff_length--;
|
||||
runlen = (runlen & 0x7f) | (((unsigned)c) << 7);
|
||||
}
|
||||
|
||||
dest += runlen + 1;
|
||||
} else {
|
||||
*dest++ ^= c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
169
engines/glk/zcode/mem.h
Normal file
169
engines/glk/zcode/mem.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_MEM
|
||||
#define GLK_ZCODE_MEM
|
||||
|
||||
#include "glk/zcode/frotz_types.h"
|
||||
#include "glk/zcode/config.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#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]
|
||||
|
||||
typedef uint offset_t;
|
||||
|
||||
/**
|
||||
* Stores undo information
|
||||
*/
|
||||
struct undo_struct {
|
||||
undo_struct *next;
|
||||
undo_struct *prev;
|
||||
offset_t pc;
|
||||
long diff_size;
|
||||
zword frame_count;
|
||||
zword stack_size;
|
||||
zword frame_offset;
|
||||
// undo diff and stack data follow
|
||||
};
|
||||
typedef undo_struct undo_t;
|
||||
|
||||
/**
|
||||
* Handles the memory, header, and user options
|
||||
*/
|
||||
class Mem : public Header, public virtual UserOptions {
|
||||
protected:
|
||||
Common::SeekableReadStream *story_fp;
|
||||
uint story_size;
|
||||
byte *pcp;
|
||||
byte *zmp;
|
||||
|
||||
undo_t *first_undo, *last_undo, *curr_undo;
|
||||
zbyte *undo_mem, *prev_zmp, *undo_diff;
|
||||
int undo_count;
|
||||
int reserve_mem;
|
||||
private:
|
||||
/**
|
||||
* Handles setting the story file, parsing it if it's a Blorb file
|
||||
*/
|
||||
void initializeStoryFile();
|
||||
|
||||
/**
|
||||
* Handles loading the game header
|
||||
*/
|
||||
void loadGameHeader();
|
||||
|
||||
/**
|
||||
* Initializes memory and loads the story data
|
||||
*/
|
||||
void loadMemory();
|
||||
|
||||
/**
|
||||
* Setup undo data
|
||||
*/
|
||||
void initializeUndo();
|
||||
protected:
|
||||
/**
|
||||
* Read a value from the header extension (former mouse table).
|
||||
*/
|
||||
zword get_header_extension(int entry);
|
||||
|
||||
/**
|
||||
* Set an entry in the header extension (former mouse table).
|
||||
*/
|
||||
void set_header_extension(int entry, zword val);
|
||||
|
||||
/**
|
||||
* Set all header fields which hold information about the interpreter.
|
||||
*/
|
||||
void restart_header();
|
||||
|
||||
/**
|
||||
* Write a byte value to the dynamic Z-machine memory.
|
||||
*/
|
||||
void storeb(zword addr, zbyte value);
|
||||
|
||||
/**
|
||||
* Write a word value to the dynamic Z-machine memory.
|
||||
*/
|
||||
void storew(zword addr, zword value);
|
||||
|
||||
/**
|
||||
* Free count undo blocks from the beginning of the undo list
|
||||
*/
|
||||
void free_undo(int count);
|
||||
|
||||
/**
|
||||
* Generates a runtime error
|
||||
*/
|
||||
virtual void runtimeError(ErrorCode errNum) = 0;
|
||||
|
||||
/**
|
||||
* Called when the flags are changed
|
||||
*/
|
||||
virtual void flagsChanged(zbyte value) = 0;
|
||||
|
||||
/**
|
||||
* Close the story file and deallocate memory.
|
||||
*/
|
||||
void reset_memory();
|
||||
|
||||
/**
|
||||
* Set diff to a Quetzal-like difference between a and b,
|
||||
* copying a to b as we go. It is assumed that diff points to a
|
||||
* buffer which is large enough to hold the diff.
|
||||
* mem_size is the number of bytes to compare.
|
||||
* Returns the number of bytes copied to diff.
|
||||
*
|
||||
*/
|
||||
long mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff);
|
||||
|
||||
/**
|
||||
* Applies a quetzal-like diff to dest
|
||||
*/
|
||||
void mem_undiff(zbyte *diff, long diff_length, zbyte *dest);
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Mem();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Mem() {}
|
||||
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
void initialize();
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
190
engines/glk/zcode/pics.cpp
Normal file
190
engines/glk/zcode/pics.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/pics.h"
|
||||
#include "glk/zcode/pics_decoder.h"
|
||||
#include "glk/glk.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
enum {
|
||||
PIC_FILE_HEADER_FLAGS = 1,
|
||||
PIC_FILE_HEADER_NUM_IMAGES = 4,
|
||||
PIC_FILE_HEADER_ENTRY_SIZE = 8,
|
||||
PIC_FILE_HEADER_VERSION = 14
|
||||
};
|
||||
|
||||
Pics::Pics() : Common::Archive(), _filename(getFilename()) {
|
||||
Common::File f;
|
||||
if (!f.open(Common::Path(_filename)))
|
||||
error("Error reading Pics file");
|
||||
|
||||
_palette = new Common::Array<byte>();
|
||||
|
||||
Common::Array<uint> offsets;
|
||||
byte buffer[16];
|
||||
f.read(buffer, 16);
|
||||
_index.resize(READ_LE_UINT16(&buffer[PIC_FILE_HEADER_NUM_IMAGES]));
|
||||
_entrySize = buffer[PIC_FILE_HEADER_ENTRY_SIZE];
|
||||
_version = buffer[PIC_FILE_HEADER_FLAGS];
|
||||
assert(_entrySize >= 8 && _entrySize <= 14);
|
||||
|
||||
// Iterate through loading the index
|
||||
for (uint idx = 0; idx < _index.size(); ++idx) {
|
||||
Entry &e = _index[idx];
|
||||
f.read(buffer, _entrySize);
|
||||
|
||||
e._number = READ_LE_UINT16(buffer);
|
||||
e._width = READ_LE_UINT16(buffer + 2);
|
||||
e._height = READ_LE_UINT16(buffer + 4);
|
||||
e._flags = READ_LE_UINT16(buffer + 6);
|
||||
|
||||
if (_entrySize >= 11) {
|
||||
e._dataOffset = READ_BE_UINT32(buffer + 7) & 0xffffff;
|
||||
if (e._dataOffset)
|
||||
offsets.push_back(e._dataOffset);
|
||||
|
||||
if (_entrySize == 14) {
|
||||
e._paletteOffset = READ_BE_UINT32(buffer + 10) & 0xffffff;
|
||||
}
|
||||
}
|
||||
|
||||
if (e._dataOffset)
|
||||
e._filename = Common::String::format("pic%u.raw", e._number);
|
||||
else
|
||||
e._filename = Common::String::format("pic%u.rect", e._number);
|
||||
}
|
||||
|
||||
// Further processing of index to calculate data sizes
|
||||
Common::sort(offsets.begin(), offsets.end());
|
||||
|
||||
for (uint idx = 0; idx < _index.size(); ++idx) {
|
||||
Entry &e = _index[idx];
|
||||
if (!e._dataOffset)
|
||||
continue;
|
||||
|
||||
// Find the entry in the offsets array
|
||||
uint oidx = 0;
|
||||
while (oidx < offsets.size() && offsets[oidx] != e._dataOffset)
|
||||
++oidx;
|
||||
|
||||
// Set the size
|
||||
e._dataSize = (oidx == (offsets.size() - 1) ? f.size() : offsets[oidx + 1]) - e._dataOffset;
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
Pics::~Pics() {
|
||||
delete _palette;
|
||||
}
|
||||
|
||||
Common::String Pics::getFilename() {
|
||||
Common::String filename = g_vm->getFilename();
|
||||
while (filename.contains('.'))
|
||||
filename.deleteLastChar();
|
||||
|
||||
return filename + ".mg1";
|
||||
}
|
||||
|
||||
bool Pics::exists() {
|
||||
return Common::File::exists(Common::Path(getFilename()));
|
||||
}
|
||||
|
||||
bool Pics::hasFile(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
for (uint idx = 0; idx < _index.size(); ++idx) {
|
||||
if (_index[idx]._filename.equalsIgnoreCase(name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Pics::listMembers(Common::ArchiveMemberList &list) const {
|
||||
for (uint idx = 0; idx < _index.size(); ++idx) {
|
||||
list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(_index[idx]._filename, *this)));
|
||||
}
|
||||
|
||||
return (int)_index.size();
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr Pics::getMember(const Common::Path &path) const {
|
||||
if (!hasFile(path))
|
||||
return Common::ArchiveMemberPtr();
|
||||
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
PictureDecoder decoder;
|
||||
|
||||
for (uint idx = 0; idx < _index.size(); ++idx) {
|
||||
const Entry &e = _index[idx];
|
||||
if (e._filename.equalsIgnoreCase(name)) {
|
||||
Common::File f;
|
||||
Common::SeekableReadStream *dest;
|
||||
if (!f.open(path))
|
||||
error("Reading failed");
|
||||
|
||||
if (e._dataSize) {
|
||||
loadPalette(f, e, *_palette);
|
||||
|
||||
f.seek(e._dataOffset);
|
||||
Common::SeekableReadStream *src = f.readStream(e._dataSize);
|
||||
dest = decoder.decode(*src, e._flags, *_palette, kMCGA, e._width, e._height);
|
||||
delete src;
|
||||
} else {
|
||||
byte *rect = (byte *)malloc(2 * sizeof(uint32));
|
||||
WRITE_BE_UINT32(rect, e._width);
|
||||
WRITE_BE_UINT32(rect + 4, e._height);
|
||||
dest = new Common::MemoryReadStream(rect, 2 * sizeof(uint32), DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
f.close();
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Pics::loadPalette(Common::File &f, const Entry &e, Common::Array<byte> &palette) const {
|
||||
if (e._paletteOffset) {
|
||||
// Read in the image's palette
|
||||
assert(e._paletteOffset);
|
||||
f.seek(e._paletteOffset);
|
||||
_palette->resize(f.readByte() * 3);
|
||||
f.read(&(*_palette)[0], _palette->size());
|
||||
}
|
||||
|
||||
if (e._flags & 1) {
|
||||
byte *entry = &palette[(e._flags >> 12) * 3];
|
||||
Common::fill(entry, entry + 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
136
engines/glk/zcode/pics.h
Normal file
136
engines/glk/zcode/pics.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_PICS_H
|
||||
#define GLK_ZCODE_PICS_H
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
enum PicturesMode {
|
||||
kMONO = 0,
|
||||
kTEXT = 1,
|
||||
kCGA = 2,
|
||||
kMCGA = 3,
|
||||
kEGA = 4,
|
||||
kAmiga = 5
|
||||
};
|
||||
|
||||
/**
|
||||
* Infocom graphics file manager
|
||||
*/
|
||||
class Pics : public Common::Archive {
|
||||
/**
|
||||
* Describes a single index entry
|
||||
*/
|
||||
struct Entry {
|
||||
uint _number;
|
||||
size_t _width, _height;
|
||||
uint _flags;
|
||||
size_t _dataOffset;
|
||||
size_t _dataSize;
|
||||
size_t _paletteOffset;
|
||||
Common::String _filename;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Entry() : _number(0), _width(0), _height(0), _flags(0), _dataOffset(0), _dataSize(0),
|
||||
_paletteOffset(0) {}
|
||||
};
|
||||
private:
|
||||
Common::String _filename;
|
||||
Common::Array<Entry> _index; ///< list of entries
|
||||
uint _entrySize;
|
||||
uint _version;
|
||||
Common::Array<byte> *_palette;
|
||||
private:
|
||||
/**
|
||||
* Returns the filename for the pictures archive
|
||||
*/
|
||||
static Common::String getFilename();
|
||||
|
||||
/**
|
||||
* Read in the palette
|
||||
*/
|
||||
void loadPalette(Common::File &f, const Entry &e, Common::Array<byte> &palette) const;
|
||||
public:
|
||||
/**
|
||||
* Returns true if an mg1 file exists for the game
|
||||
*/
|
||||
static bool exists();
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Pics();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Pics() override;
|
||||
|
||||
/**
|
||||
* Return the number of entries in the file
|
||||
*/
|
||||
size_t size() const { return _index.size(); }
|
||||
|
||||
/**
|
||||
* Return the version of the file
|
||||
*/
|
||||
uint version() const { return _version; }
|
||||
|
||||
/**
|
||||
* Check if a member with the given name is present in the Archive.
|
||||
* Patterns are not allowed, as this is meant to be a quick File::exists()
|
||||
* replacement.
|
||||
*/
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Add all members of the Archive to list.
|
||||
* Must only append to list, and not remove elements from it.
|
||||
*
|
||||
* @return the number of names added to list
|
||||
*/
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
|
||||
/**
|
||||
* Returns a ArchiveMember representation of the given file.
|
||||
*/
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Create a stream bound to a member with the specified name in the
|
||||
* archive. If no member with this name exists, 0 is returned.
|
||||
* @return the newly created input stream
|
||||
*/
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
167
engines/glk/zcode/pics_decoder.cpp
Normal file
167
engines/glk/zcode/pics_decoder.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/pics_decoder.h"
|
||||
#include "glk/zcode/pics.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#define MAX_BIT 512 /* Must be less than or equal to CODE_TABLE_SIZE */
|
||||
#define CODE_SIZE 8
|
||||
#define CODE_TABLE_SIZE 4096
|
||||
#define PREFIX 0
|
||||
#define PIXEL 1
|
||||
|
||||
/**
|
||||
* Support class used for picture decompression
|
||||
*/
|
||||
class Compress {
|
||||
private:
|
||||
byte _codeBuffer[CODE_TABLE_SIZE];
|
||||
public:
|
||||
short _nextCode;
|
||||
short _sLen;
|
||||
short _sPtr;
|
||||
short _tLen;
|
||||
short _tPtr;
|
||||
|
||||
Compress() : _nextCode(0), _sLen(0), _sPtr(0), _tLen(0), _tPtr(0) {}
|
||||
|
||||
/**
|
||||
* Read a code
|
||||
*/
|
||||
short readCode(Common::ReadStream &src);
|
||||
};
|
||||
|
||||
static short MASK[16] = {
|
||||
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
|
||||
0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
|
||||
};
|
||||
|
||||
short Compress::readCode(Common::ReadStream &src) {
|
||||
short code, bsize, tlen, tptr;
|
||||
|
||||
code = 0;
|
||||
tlen = _tLen;
|
||||
tptr = 0;
|
||||
|
||||
while (tlen) {
|
||||
if (_sLen == 0) {
|
||||
if ((_sLen = src.read(_codeBuffer, MAX_BIT)) == 0) {
|
||||
error("fread");
|
||||
}
|
||||
_sLen *= 8;
|
||||
_sPtr = 0;
|
||||
}
|
||||
bsize = ((_sPtr + 8) & ~7) - _sPtr;
|
||||
bsize = (tlen > bsize) ? bsize : tlen;
|
||||
code |= (((uint)_codeBuffer[_sPtr >> 3] >> (_sPtr & 7)) & MASK[bsize]) << tptr;
|
||||
|
||||
tlen -= bsize;
|
||||
tptr += bsize;
|
||||
_sLen -= bsize;
|
||||
_sPtr += bsize;
|
||||
}
|
||||
|
||||
if ((_nextCode == MASK[_tLen]) && (_tLen < 12))
|
||||
_tLen++;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
PictureDecoder::PictureDecoder() {
|
||||
_tableVal = new byte[3 * 3840];
|
||||
_tableRef = (uint16 *)(_tableVal + 3840);
|
||||
}
|
||||
|
||||
PictureDecoder::~PictureDecoder() {
|
||||
delete[] _tableVal;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint flags,
|
||||
const Common::Array<byte> &palette, uint display, size_t width, size_t height) {
|
||||
Common::MemoryWriteStreamDynamic out(DisposeAfterUse::NO);
|
||||
short code_table[CODE_TABLE_SIZE][2];
|
||||
byte buffer[CODE_TABLE_SIZE];
|
||||
|
||||
// Write out dimensions
|
||||
out.writeUint16LE(width);
|
||||
out.writeUint16LE(height);
|
||||
|
||||
// Write out palette
|
||||
out.writeUint16LE(palette.size() / 3 + 2);
|
||||
for (int idx = 0; idx < 6; ++idx)
|
||||
out.writeByte((idx < 3) ? 0x77 : 0);
|
||||
if (!palette.empty())
|
||||
out.write(&palette[0], palette.size());
|
||||
|
||||
byte transparent = 0xff;
|
||||
if (flags & 1)
|
||||
transparent = flags >> 12;
|
||||
out.writeByte(transparent);
|
||||
|
||||
int i;
|
||||
short code, old = 0, first, clear_code;
|
||||
Compress comp;
|
||||
|
||||
clear_code = 1 << CODE_SIZE;
|
||||
comp._nextCode = clear_code + 2;
|
||||
comp._tLen = CODE_SIZE + 1;
|
||||
comp._tPtr = 0;
|
||||
|
||||
for (i = 0; i < CODE_TABLE_SIZE; i++) {
|
||||
code_table[i][PREFIX] = CODE_TABLE_SIZE;
|
||||
code_table[i][PIXEL] = i;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if ((code = comp.readCode(src)) == (clear_code + 1))
|
||||
break;
|
||||
if (code == clear_code) {
|
||||
comp._tLen = CODE_SIZE + 1;
|
||||
comp._nextCode = clear_code + 2;
|
||||
code = comp.readCode(src);
|
||||
} else {
|
||||
first = (code == comp._nextCode) ? old : code;
|
||||
while (code_table[first][PREFIX] != CODE_TABLE_SIZE)
|
||||
first = code_table[first][PREFIX];
|
||||
code_table[comp._nextCode][PREFIX] = old;
|
||||
code_table[comp._nextCode++][PIXEL] = code_table[first][PIXEL];
|
||||
}
|
||||
old = code;
|
||||
i = 0;
|
||||
do
|
||||
buffer[i++] = (unsigned char)code_table[code][PIXEL];
|
||||
while ((code = code_table[code][PREFIX]) != CODE_TABLE_SIZE);
|
||||
do
|
||||
out.writeByte(buffer[--i]);
|
||||
while (i > 0);
|
||||
}
|
||||
|
||||
return new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
60
engines/glk/zcode/pics_decoder.h
Normal file
60
engines/glk/zcode/pics_decoder.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_PICS_DECODER_H
|
||||
#define GLK_ZCODE_PICS_DECODER_H
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Decodes an Infocom encoded picture into a raw pixel stream that the outer
|
||||
* Glk engine is capable of then loading into a picture object
|
||||
*/
|
||||
class PictureDecoder {
|
||||
private:
|
||||
byte *_tableVal;
|
||||
uint16 *_tableRef;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
PictureDecoder();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~PictureDecoder();
|
||||
|
||||
/**
|
||||
* Decode method
|
||||
*/
|
||||
Common::SeekableReadStream *decode(Common::ReadStream &src, uint flags,
|
||||
const Common::Array<byte> &palette, uint display, size_t width, size_t height);
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
669
engines/glk/zcode/processor.cpp
Normal file
669
engines/glk/zcode/processor.cpp
Normal file
@@ -0,0 +1,669 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
#include "glk/zcode/zcode.h"
|
||||
#include "glk/conf.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
// 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::z_draw_picture,
|
||||
&Processor::z_picture_data,
|
||||
&Processor::z_erase_picture,
|
||||
&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::z_move_window,
|
||||
&Processor::z_window_size,
|
||||
&Processor::z_window_style,
|
||||
&Processor::z_get_wind_prop,
|
||||
&Processor::z_scroll_window,
|
||||
&Processor::z_pop_stack,
|
||||
&Processor::z_read_mouse,
|
||||
&Processor::z_mouse_window,
|
||||
&Processor::z_push_stack,
|
||||
&Processor::z_put_wind_prop,
|
||||
&Processor::z_print_form,
|
||||
&Processor::z_make_menu,
|
||||
&Processor::z_picture_table,
|
||||
&Processor::z_buffer_screen // spec 1.1
|
||||
};
|
||||
|
||||
Processor::Processor(OSystem *syst, const GlkGameDescription &gameDesc) :
|
||||
GlkInterface(syst, 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'), script_width(0),
|
||||
sfp(nullptr), rfp(nullptr), pfp(nullptr), ostream_screen(true), ostream_script(false),
|
||||
ostream_memory(false), ostream_record(false), istream_replay(false), message(false) {
|
||||
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
|
||||
};
|
||||
|
||||
op0_opcodes.resize(16);
|
||||
op1_opcodes.resize(16);
|
||||
Common::copy(&OP0_OPCODES[0], &OP0_OPCODES[16], &op0_opcodes[0]);
|
||||
Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], &op1_opcodes[0]);
|
||||
Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
|
||||
Common::fill(&zargs[0], &zargs[8], 0);
|
||||
Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
|
||||
Common::fill(&_errorCount[0], &_errorCount[ERR_NUM_ERRORS], 0);
|
||||
}
|
||||
|
||||
void Processor::initialize() {
|
||||
Mem::initialize();
|
||||
GlkInterface::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 (!shouldQuit() && !_finished);
|
||||
|
||||
_finished--;
|
||||
}
|
||||
|
||||
void Processor::call(zword routine, int argc, zword *args, int ct) {
|
||||
uint32 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 << (_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 (_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) {
|
||||
offset_t pc;
|
||||
int ct;
|
||||
|
||||
if (_sp > _fp)
|
||||
runtimeError(ERR_STK_UNDF);
|
||||
|
||||
_sp = _fp;
|
||||
|
||||
ct = *_sp++ >> (_quetzal ? 12 : 8);
|
||||
_frameCount--;
|
||||
_fp = _stack + 1 + *_sp++;
|
||||
pc = *_sp++;
|
||||
pc = ((offset_t)*_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) {
|
||||
offset_t 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, nullptr, 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(_quetzal ? _frameCount : (zword)(_fp - _stack));
|
||||
}
|
||||
|
||||
void Processor::z_throw() {
|
||||
if (_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() {
|
||||
offset_t pc;
|
||||
GET_PC(pc);
|
||||
|
||||
pc += (short)zargs[0] - 2;
|
||||
|
||||
if (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;
|
||||
default:
|
||||
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 ZCode
|
||||
} // End of namespace Glk
|
||||
1767
engines/glk/zcode/processor.h
Normal file
1767
engines/glk/zcode/processor.h
Normal file
File diff suppressed because it is too large
Load Diff
200
engines/glk/zcode/processor_buffer.cpp
Normal file
200
engines/glk/zcode/processor_buffer.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
const char *const Processor::ERR_MESSAGES[ERR_NUM_ERRORS] = {
|
||||
"Text buffer overflow",
|
||||
"Store out of dynamic memory",
|
||||
"Division by zero",
|
||||
"Illegal object",
|
||||
"Illegal attribute",
|
||||
"No such property",
|
||||
"Stack overflow",
|
||||
"Call to illegal address",
|
||||
"Call to non-routine",
|
||||
"Stack underflow",
|
||||
"Illegal opcode",
|
||||
"Bad stack frame",
|
||||
"Jump to illegal address",
|
||||
"Can't save while in interrupt",
|
||||
"Nesting stream #3 too deep",
|
||||
"Illegal window",
|
||||
"Illegal window property",
|
||||
"Print at illegal address",
|
||||
"Illegal dictionary word length",
|
||||
"@jin called with object 0",
|
||||
"@get_child called with object 0",
|
||||
"@get_parent called with object 0",
|
||||
"@get_sibling called with object 0",
|
||||
"@get_prop_addr called with object 0",
|
||||
"@get_prop called with object 0",
|
||||
"@put_prop called with object 0",
|
||||
"@clear_attr called with object 0",
|
||||
"@set_attr called with object 0",
|
||||
"@test_attr called with object 0",
|
||||
"@move_object called moving object 0",
|
||||
"@move_object called moving into object 0",
|
||||
"@remove_object called with object 0",
|
||||
"@get_next_prop called with object 0"
|
||||
};
|
||||
|
||||
void Processor::flush_buffer() {
|
||||
/* Make sure we stop when flush_buffer is called from flush_buffer.
|
||||
* Note that this is difficult to avoid as we might print a newline
|
||||
* during flush_buffer, which might cause a newline interrupt, that
|
||||
* might execute any arbitrary opcode, which might flush the buffer.
|
||||
*/
|
||||
if (_locked || bufferEmpty())
|
||||
return;
|
||||
|
||||
// Send the buffer to the output streams
|
||||
_buffer[_bufPos] = '\0';
|
||||
|
||||
_locked = true;
|
||||
stream_word(_buffer);
|
||||
_locked = false;
|
||||
|
||||
// Reset the buffer
|
||||
_bufPos = 0;
|
||||
_prevC = '\0';
|
||||
}
|
||||
|
||||
void Processor::print_char(zchar c) {
|
||||
static bool flag = false;
|
||||
|
||||
if (message || ostream_memory || enable_buffering) {
|
||||
if (!flag) {
|
||||
// Characters 0 and ZC_RETURN are special cases
|
||||
if (c == ZC_RETURN) {
|
||||
new_line();
|
||||
return;
|
||||
}
|
||||
if (c == 0)
|
||||
return;
|
||||
|
||||
// Flush the buffer before a whitespace or after a hyphen
|
||||
if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
|
||||
flush_buffer();
|
||||
|
||||
// Set the flag if this is part one of a style or font change
|
||||
if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
|
||||
flag = true;
|
||||
|
||||
// Remember the current character code
|
||||
_prevC = c;
|
||||
} else {
|
||||
flag = false;
|
||||
}
|
||||
|
||||
// Insert the character into the buffer
|
||||
_buffer[_bufPos++] = c;
|
||||
|
||||
if (_bufPos == TEXT_BUFFER_SIZE)
|
||||
error("Text buffer overflow");
|
||||
} else {
|
||||
stream_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::print_string(const char *s) {
|
||||
char c;
|
||||
|
||||
while ((c = *s++) != 0) {
|
||||
if (c == '\n')
|
||||
new_line();
|
||||
else
|
||||
print_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::print_string_uni(const uint32 *s) {
|
||||
uint32 c;
|
||||
while ((c = *s++) != 0) {
|
||||
if (c == '\n')
|
||||
new_line();
|
||||
else
|
||||
print_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::print_long(uint value, int base) {
|
||||
unsigned long i;
|
||||
char c;
|
||||
|
||||
for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base) {
|
||||
if (value >= i || i == 1) {
|
||||
c = (value / i) % base;
|
||||
print_char(c + (c <= 9 ? '0' : 'a' - 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::new_line() {
|
||||
flush_buffer();
|
||||
stream_new_line();
|
||||
}
|
||||
|
||||
void Processor::runtimeError(ErrorCode errNum) {
|
||||
int wasfirst;
|
||||
|
||||
if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
|
||||
return;
|
||||
|
||||
if (_err_report_mode == ERR_REPORT_FATAL
|
||||
|| (!_ignore_errors && errNum <= ERR_MAX_FATAL)) {
|
||||
flush_buffer();
|
||||
error("%s", ERR_MESSAGES[errNum - 1]);
|
||||
return;
|
||||
}
|
||||
|
||||
wasfirst = (_errorCount[errNum - 1] == 0);
|
||||
_errorCount[errNum - 1]++;
|
||||
|
||||
if ((_err_report_mode == ERR_REPORT_ALWAYS)
|
||||
|| (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
|
||||
offset_t pc;
|
||||
GET_PC(pc);
|
||||
print_string("Warning: ");
|
||||
print_string(ERR_MESSAGES[errNum - 1]);
|
||||
print_string(" (PC = ");
|
||||
print_long(pc, 16);
|
||||
print_char(')');
|
||||
|
||||
if (_err_report_mode == ERR_REPORT_ONCE) {
|
||||
print_string(" (will ignore further occurrences)");
|
||||
} else {
|
||||
print_string(" (occurrence ");
|
||||
print_long(_errorCount[errNum - 1], 10);
|
||||
print_char(')');
|
||||
}
|
||||
|
||||
new_line();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
222
engines/glk/zcode/processor_input.cpp
Normal file
222
engines/glk/zcode/processor_input.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#define INPUT_BUFFER_SIZE 200
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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 ZCode
|
||||
} // End of namespace Glk
|
||||
104
engines/glk/zcode/processor_maths.cpp
Normal file
104
engines/glk/zcode/processor_maths.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
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 ZCode
|
||||
} // End of namespace Glk
|
||||
217
engines/glk/zcode/processor_mem.cpp
Normal file
217
engines/glk/zcode/processor_mem.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
void Processor::flagsChanged(zbyte value) {
|
||||
if (value & SCRIPTING_FLAG) {
|
||||
if (!ostream_script)
|
||||
script_open();
|
||||
} else {
|
||||
if (ostream_script)
|
||||
script_close();
|
||||
}
|
||||
}
|
||||
|
||||
int Processor::save_undo() {
|
||||
long diff_size;
|
||||
zword stack_size;
|
||||
undo_t *p;
|
||||
|
||||
if (_undo_slots == 0)
|
||||
// undo feature unavailable
|
||||
return -1;
|
||||
|
||||
// save undo possible
|
||||
while (last_undo != curr_undo) {
|
||||
p = last_undo;
|
||||
last_undo = last_undo->prev;
|
||||
delete p;
|
||||
undo_count--;
|
||||
}
|
||||
if (last_undo)
|
||||
last_undo->next = nullptr;
|
||||
else
|
||||
first_undo = nullptr;
|
||||
|
||||
if (undo_count == _undo_slots)
|
||||
free_undo(1);
|
||||
|
||||
diff_size = mem_diff(zmp, prev_zmp, h_dynamic_size, undo_diff);
|
||||
stack_size = _stack + STACK_SIZE - _sp;
|
||||
do {
|
||||
p = (undo_t *) malloc(sizeof(undo_t) + diff_size + stack_size * sizeof(*_sp));
|
||||
if (p == nullptr)
|
||||
free_undo(1);
|
||||
} while (!p && undo_count);
|
||||
if (p == nullptr)
|
||||
return -1;
|
||||
|
||||
GET_PC(p->pc);
|
||||
p->frame_count = _frameCount;
|
||||
p->diff_size = diff_size;
|
||||
p->stack_size = stack_size;
|
||||
p->frame_offset = _fp - _stack;
|
||||
memcpy(p + 1, undo_diff, diff_size);
|
||||
memcpy((zbyte *)(p + 1) + diff_size, _sp, stack_size * sizeof(*_sp));
|
||||
|
||||
if (!first_undo) {
|
||||
p->prev = nullptr;
|
||||
first_undo = p;
|
||||
} else {
|
||||
last_undo->next = p;
|
||||
p->prev = last_undo;
|
||||
}
|
||||
|
||||
p->next = nullptr;
|
||||
curr_undo = last_undo = p;
|
||||
undo_count++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Processor::restore_undo(void) {
|
||||
if (_undo_slots == 0)
|
||||
// undo feature unavailable
|
||||
return -1;
|
||||
|
||||
if (curr_undo == nullptr)
|
||||
// no saved game state
|
||||
return 0;
|
||||
|
||||
// undo possible
|
||||
memcpy(zmp, prev_zmp, h_dynamic_size);
|
||||
SET_PC(curr_undo->pc);
|
||||
_sp = _stack + STACK_SIZE - curr_undo->stack_size;
|
||||
_fp = _stack + curr_undo->frame_offset;
|
||||
_frameCount = curr_undo->frame_count;
|
||||
mem_undiff((zbyte *)(curr_undo + 1), curr_undo->diff_size, prev_zmp);
|
||||
memcpy(_sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
|
||||
curr_undo->stack_size * sizeof(*_sp));
|
||||
|
||||
curr_undo = curr_undo->prev;
|
||||
|
||||
restart_header();
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* TOR: glkify -- this is for V6 only
|
||||
*/
|
||||
static zword get_max_width(zword win) { return 80; }
|
||||
|
||||
void Processor::memory_open(zword table, zword xsize, bool buffering) {
|
||||
if (_redirect.size() < MAX_NESTING) {
|
||||
if (!buffering)
|
||||
xsize = 0xffff;
|
||||
if (buffering && (short)xsize <= 0)
|
||||
xsize = get_max_width((zword)(-(short)xsize));
|
||||
|
||||
storew(table, 0);
|
||||
|
||||
_redirect.push(Redirect(xsize, table));
|
||||
ostream_memory = true;
|
||||
} else {
|
||||
runtimeError(ERR_STR3_NESTING);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::memory_new_line() {
|
||||
zword size;
|
||||
zword addr;
|
||||
|
||||
Redirect &r = _redirect.top();
|
||||
r._total += r._width;
|
||||
r._width = 0;
|
||||
|
||||
addr = r._table;
|
||||
|
||||
LOW_WORD(addr, size);
|
||||
addr += 2;
|
||||
|
||||
if (r._xSize != 0xffff) {
|
||||
r._table = addr + size;
|
||||
size = 0;
|
||||
} else {
|
||||
storeb((zword)(addr + (size++)), 13);
|
||||
}
|
||||
|
||||
storew(r._table, size);
|
||||
}
|
||||
|
||||
void Processor::memory_word(const zchar *s) {
|
||||
zword size;
|
||||
zword addr;
|
||||
zchar c;
|
||||
|
||||
Redirect &r = _redirect.top();
|
||||
if (h_version == V6) {
|
||||
int width = os_string_width(s);
|
||||
|
||||
if (r._xSize != 0xffff) {
|
||||
if (r._width + width > r._xSize) {
|
||||
|
||||
if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
|
||||
width = os_string_width(++s);
|
||||
|
||||
memory_new_line();
|
||||
}
|
||||
}
|
||||
|
||||
r._width += width;
|
||||
}
|
||||
|
||||
addr = r._table;
|
||||
|
||||
LOW_WORD(addr, size);
|
||||
addr += 2;
|
||||
|
||||
while ((c = *s++) != 0)
|
||||
storeb((zword)(addr + (size++)), translate_to_zscii(c));
|
||||
|
||||
storew(r._table, size);
|
||||
}
|
||||
|
||||
void Processor::memory_close(void) {
|
||||
if (!_redirect.empty()) {
|
||||
Redirect &r = _redirect.top();
|
||||
|
||||
if (r._xSize != 0xffff)
|
||||
memory_new_line();
|
||||
|
||||
if (h_version == V6) {
|
||||
h_line_width = (r._xSize != 0xffff) ? r._total : r._width;
|
||||
|
||||
SET_WORD(H_LINE_WIDTH, h_line_width);
|
||||
}
|
||||
|
||||
_redirect.pop();
|
||||
if (_redirect.empty())
|
||||
ostream_memory = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
731
engines/glk/zcode/processor_objects.cpp
Normal file
731
engines/glk/zcode/processor_objects.cpp
Normal file
@@ -0,0 +1,731 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#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 ZCode
|
||||
} // End of namespace Glk
|
||||
506
engines/glk/zcode/processor_screen.cpp
Normal file
506
engines/glk/zcode/processor_screen.cpp
Normal file
@@ -0,0 +1,506 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
#include "glk/zcode/zcode.h"
|
||||
#include "glk/conf.h"
|
||||
#include "glk/events.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
void Processor::screen_mssg_on() {
|
||||
Window &w = _wp.currWin();
|
||||
|
||||
if (w == _wp._lower) {
|
||||
w._oldStyle = w._currStyle;
|
||||
glk_set_style(style_Preformatted);
|
||||
glk_put_string("\n ");
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::screen_mssg_off() {
|
||||
Window &w = _wp.currWin();
|
||||
|
||||
if (w == _wp._lower) {
|
||||
glk_put_char('\n');
|
||||
w.setStyle(0);
|
||||
w.setStyle(w._oldStyle);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint32 zchar_runes[] = {
|
||||
// This mapping is based on the Amiga font in the Z-Machine
|
||||
// specification, with some liberties taken.
|
||||
|
||||
0x16AA, // RUNIC LETTER AC A
|
||||
0x16D2, // RUNIC LETTER BERKANAN BEORC BJARKAN B
|
||||
0x16C7, // RUNIC LETTER IWAZ EOH
|
||||
0x16D1, // RUNIC LETTER DAGAZ DAEG D
|
||||
0x16D6, // RUNIC LETTER EHWAZ EH E
|
||||
0x16A0, // RUNIC LETTER FEHU FEOH FE F
|
||||
0x16B7, // RUNIC LETTER GEBO GYFU G
|
||||
0x16BB, // RUNIC LETTER HAEGL H
|
||||
0x16C1, // RUNIC LETTER ISAZ IS ISS I
|
||||
0x16C4, // RUNIC LETTER GER
|
||||
0x16E6, // RUNIC LETTER LONG-BRANCH-YR
|
||||
0x16DA, // RUNIC LETTER LAUKAZ LAGU LOGR L
|
||||
0x16D7, // RUNIC LETTER MANNAZ MAN M
|
||||
0x16BE, // RUNIC LETTER NAUDIZ NYD NAUD N
|
||||
0x16A9, // RUNIC LETTER OS O
|
||||
0x16C8, // RUNIC LETTER PERTHO PEORTH P
|
||||
0x16B3, // RUNIC LETTER CEN
|
||||
0x16B1, // RUNIC LETTER RAIDO RAD REID R
|
||||
0x16CB, // RUNIC LETTER SIGEL LONG-BRANCH-SOL S
|
||||
0x16CF, // RUNIC LETTER TIWAZ TIR TYR T
|
||||
0x16A2, // RUNIC LETTER URUZ UR U
|
||||
0x16E0, // RUNIC LETTER EAR
|
||||
0x16B9, // RUNIC LETTER WUNJO WYNN W
|
||||
0x16C9, // RUNIC LETTER ALGIZ EOLHX
|
||||
0x16A5, // RUNIC LETTER W
|
||||
0x16DF // RUNIC LETTER OTHALAN ETHEL O
|
||||
};
|
||||
|
||||
uint32 Processor::zchar_to_unicode_rune(zchar c) {
|
||||
// There are only runic characters for a-z. Some versions of Beyond
|
||||
// Zork will render the conversation between Prince Foo and the black
|
||||
// rider in runic script, even though it contained upper case letters.
|
||||
// This produced an ugly mix of runes and map-drawing characters, etc.
|
||||
// which is probably why it was removed in later versions.
|
||||
//
|
||||
// Still, it's probably a good idea to convert the upper case letters
|
||||
// to lower case to get an appropriate rune. As far as I can tell, the
|
||||
// upper case letters are all used for drawing maps and progress bars.
|
||||
// I don't think they're ever intended for the lower window.
|
||||
//
|
||||
// Apart from the runes, the arrow glyphs could perhaps also be
|
||||
// sensibly converted to Unicode?
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return zchar_runes[c - 'a'];
|
||||
else if (c >= 'A' && c <= 'Z')
|
||||
return zchar_runes[c - 'A'];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Processor::screen_char(zchar c) {
|
||||
Window &w = _wp.currWin();
|
||||
w.ensureTextWindow();
|
||||
|
||||
if (h_version == V6)
|
||||
_wp.showTextWindows();
|
||||
|
||||
if (gos_linepending && (w == gos_linewin)) {
|
||||
gos_cancel_pending_line();
|
||||
if (_wp.currWin() == _wp._upper) {
|
||||
_wp._upper.setCursor(Point(1, _wp._upper[Y_CURSOR] + 1));
|
||||
}
|
||||
if (c == '\n')
|
||||
return;
|
||||
}
|
||||
|
||||
// check fixed flag in header, game can change it at whim
|
||||
int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0);
|
||||
int curfix = ((w._currStyle & FIXED_WIDTH_STYLE) != 0);
|
||||
if (forcefix && !curfix) {
|
||||
w.setStyle();
|
||||
fixforced = true;
|
||||
} else if (!forcefix && fixforced) {
|
||||
w.setStyle();
|
||||
fixforced = false;
|
||||
}
|
||||
|
||||
if (_wp._upper && _wp.currWin() == _wp._upper) {
|
||||
if (c == '\n' || c == ZC_RETURN) {
|
||||
glk_put_char('\n');
|
||||
_wp._upper.setCursor(Point(1, _wp._upper[Y_CURSOR] + 1));
|
||||
} else {
|
||||
int curx = _wp._upper[X_CURSOR], cury = _wp._upper[Y_CURSOR];
|
||||
|
||||
if (cury == 1) {
|
||||
if (curx <= (int)((sizeof statusline / sizeof(zchar)) - 1)) {
|
||||
statusline[curx - 1] = c;
|
||||
statusline[curx] = 0;
|
||||
}
|
||||
if (curx < h_screen_cols) {
|
||||
glk_put_char_uni(c);
|
||||
} else if (curx == h_screen_cols) {
|
||||
glk_put_char_uni(c);
|
||||
glk_window_move_cursor(_wp.currWin(), curx-1, cury-1);
|
||||
} else {
|
||||
smartstatusline();
|
||||
}
|
||||
|
||||
curx++;
|
||||
} else {
|
||||
if (curx < h_screen_cols) {
|
||||
glk_put_char_uni(c);
|
||||
} else if (curx == (h_screen_cols)) {
|
||||
glk_put_char_uni(c);
|
||||
glk_window_move_cursor(_wp.currWin(), curx-1, cury-1);
|
||||
}
|
||||
|
||||
curx++;
|
||||
}
|
||||
}
|
||||
} else if (w == _wp._lower) {
|
||||
if (c == ZC_RETURN)
|
||||
glk_put_char('\n');
|
||||
else {
|
||||
if (w._currFont == GRAPHICS_FONT) {
|
||||
uint32 runic_char = zchar_to_unicode_rune(c);
|
||||
if (runic_char != 0) {
|
||||
glk_set_style(style_User2);
|
||||
glk_put_char_uni(runic_char);
|
||||
glk_set_style(style_User1);
|
||||
} else
|
||||
glk_put_char_uni(c);
|
||||
} else
|
||||
glk_put_char_uni(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::screen_new_line() {
|
||||
screen_char('\n');
|
||||
}
|
||||
|
||||
void Processor::screen_word(const zchar *s) {
|
||||
zchar c;
|
||||
while ((c = *s++) != 0) {
|
||||
if (c == ZC_NEW_FONT)
|
||||
s++;
|
||||
else if (c == ZC_NEW_STYLE)
|
||||
s++;
|
||||
else
|
||||
screen_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::erase_screen(zword win) {
|
||||
if ((short)win == -1) {
|
||||
if (_wp._upper) {
|
||||
_wp._upper.updateColors();
|
||||
_wp._upper.clear();
|
||||
}
|
||||
|
||||
_wp._lower.clear();
|
||||
split_window(0);
|
||||
_wp.setWindow(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::erase_window(zword win) {
|
||||
if (h_version == V6 && win != _wp._cwin && h_interpreter_number != INTERP_AMIGA)
|
||||
_wp[win].updateColors();
|
||||
|
||||
_wp[win].clear();
|
||||
|
||||
if (h_version == V6 && win != _wp._cwin && h_interpreter_number != INTERP_AMIGA)
|
||||
_wp[_wp._cwin].updateColors();
|
||||
}
|
||||
|
||||
void Processor::z_buffer_mode() {
|
||||
// No implementation
|
||||
}
|
||||
|
||||
void Processor::z_buffer_screen() {
|
||||
store(0);
|
||||
}
|
||||
|
||||
void Processor::z_erase_line() {
|
||||
int i;
|
||||
|
||||
flush_buffer();
|
||||
|
||||
if (_wp._upper && _wp.currWin() == _wp._upper) {
|
||||
int curx = _wp[_wp._cwin][X_CURSOR], cury = _wp[_wp._cwin][Y_CURSOR];
|
||||
|
||||
for (i = 0; i < h_screen_cols + 1 - curx; i++)
|
||||
glk_put_char(' ');
|
||||
_wp._upper.setCursor(Point(curx, cury));
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::z_erase_window() {
|
||||
short w = (short)zargs[0];
|
||||
|
||||
flush_buffer();
|
||||
|
||||
if (w == -1 || w == -2)
|
||||
erase_screen(w);
|
||||
else
|
||||
erase_window(winarg0());
|
||||
}
|
||||
|
||||
void Processor::z_get_cursor() {
|
||||
zword y, x;
|
||||
|
||||
flush_buffer();
|
||||
|
||||
x = _wp[_wp._cwin][X_CURSOR];
|
||||
y = _wp[_wp._cwin][Y_CURSOR];
|
||||
|
||||
if (h_version != V6) {
|
||||
// convert to grid positions
|
||||
y = (y - 1) / h_font_height + 1;
|
||||
x = (x - 1) / h_font_width + 1;
|
||||
}
|
||||
|
||||
storew((zword)(zargs[0] + 0), y);
|
||||
storew((zword)(zargs[0] + 2), x);
|
||||
}
|
||||
|
||||
void Processor::z_print_table() {
|
||||
zword addr = zargs[0];
|
||||
int curx = _wp[_wp._cwin][X_CURSOR], cury = _wp[_wp._cwin][Y_CURSOR];
|
||||
zword xs = curx;
|
||||
int i, j;
|
||||
zbyte c;
|
||||
|
||||
// Supply default arguments
|
||||
if (zargc < 3)
|
||||
zargs[2] = 1;
|
||||
if (zargc < 4)
|
||||
zargs[3] = 0;
|
||||
|
||||
// Write text in width x height rectangle
|
||||
for (i = 0; i < zargs[2]; i++, curx = xs, cury++) {
|
||||
_wp[_wp._cwin].setCursor(Point(xs, cury));
|
||||
|
||||
for (j = 0; j < zargs[1]; j++) {
|
||||
LOW_BYTE(addr, c);
|
||||
addr++;
|
||||
|
||||
print_char(c);
|
||||
}
|
||||
|
||||
addr += zargs[3];
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
_wp[_wp._cwin].updateColors(zfore, zback);
|
||||
}
|
||||
|
||||
void Processor::z_set_colour() {
|
||||
int fg = (short)zargs[0];
|
||||
int bg = (short)zargs[1];
|
||||
zword win = (h_version == V6) ? winarg2() : 0;
|
||||
|
||||
if (win == 1 && h_version == V6)
|
||||
bg = zcolor_Transparent;
|
||||
|
||||
flush_buffer();
|
||||
|
||||
if (fg == -1)
|
||||
// Get color at cursor
|
||||
fg = os_peek_color();
|
||||
if (bg == -1)
|
||||
bg = zcolor_Transparent;
|
||||
|
||||
if (fg == 0)
|
||||
// keep current colour
|
||||
fg = _wp[win][TRUE_FG_COLOR];
|
||||
if (bg == 0)
|
||||
bg = _wp[win][TRUE_BG_COLOR];
|
||||
|
||||
if (fg == 1)
|
||||
fg = h_default_foreground;
|
||||
if (bg == 1)
|
||||
bg = h_default_background;
|
||||
|
||||
if (fg >= 0 && fg < zcolor_NUMCOLORS)
|
||||
fg = zcolors[fg];
|
||||
if (bg >= 0 && bg < zcolor_NUMCOLORS)
|
||||
bg = zcolors[bg];
|
||||
|
||||
if (h_version == V6 && h_interpreter_number == INTERP_AMIGA) {
|
||||
// Changing colours of window 0 affects the entire screen
|
||||
if (win == 0) {
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
int bg2 = _wp[i][TRUE_BG_COLOR];
|
||||
int fg2 = _wp[i][TRUE_FG_COLOR];
|
||||
|
||||
if (bg2 < 16)
|
||||
bg2 = (bg2 == (int)_wp[0][TRUE_BG_COLOR]) ? fg : bg;
|
||||
if (fg2 < 16)
|
||||
fg2 = (fg2 == (int)_wp[0][TRUE_FG_COLOR]) ? fg : bg;
|
||||
|
||||
_wp[i][TRUE_FG_COLOR] = fg2;
|
||||
_wp[i][TRUE_BG_COLOR] = bg2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_wp[win][TRUE_FG_COLOR] = fg;
|
||||
_wp[win][TRUE_BG_COLOR] = bg;
|
||||
|
||||
if (win == _wp._cwin || h_version != V6)
|
||||
_wp.currWin().updateColors(fg, bg);
|
||||
}
|
||||
|
||||
void Processor::z_set_font() {
|
||||
zword font = zargs[0];
|
||||
|
||||
store(_wp.currWin().setFont(font));
|
||||
}
|
||||
|
||||
void Processor::z_set_cursor() {
|
||||
int x = (int16)zargs[1], y = (int16)zargs[0];
|
||||
int win = (h_version == V6) ? winarg2() : _wp._cwin;
|
||||
|
||||
if (zargc < 3)
|
||||
zargs[2] = (zword)-3;
|
||||
|
||||
flush_buffer();
|
||||
_wp[win].setCursor(Point(x, y));
|
||||
|
||||
if (_wp.currWin() == _wp._upper && _wp[win][Y_CURSOR] > (uint)mach_status_ht) {
|
||||
mach_status_ht = _wp[win][Y_CURSOR];
|
||||
reset_status_ht();
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::z_set_text_style() {
|
||||
_wp[_wp._cwin].setStyle(zargs[0]);
|
||||
}
|
||||
|
||||
void Processor::z_set_window() {
|
||||
_wp.setWindow(zargs[0]);
|
||||
|
||||
if (_wp._cwin == 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 curx = _wp._upper[X_CURSOR];
|
||||
int 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 (!_wp._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.
|
||||
_wp.setWindow(1);
|
||||
|
||||
_wp._upper.setReverseVideo(true);
|
||||
_wp._upper.setCursor(Point(1, 1));
|
||||
|
||||
// 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
|
||||
_wp.setWindow(0);
|
||||
}
|
||||
|
||||
void Processor::z_split_window() {
|
||||
split_window(zargs[0]);
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
625
engines/glk/zcode/processor_streams.cpp
Normal file
625
engines/glk/zcode/processor_streams.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
#include "glk/zcode/quetzal.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
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 auxiliary 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 ZCode
|
||||
} // End of namespace Glk
|
||||
119
engines/glk/zcode/processor_table.cpp
Normal file
119
engines/glk/zcode/processor_table.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
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 ZCode
|
||||
} // End of namespace Glk
|
||||
909
engines/glk/zcode/processor_text.cpp
Normal file
909
engines/glk/zcode/processor_text.cpp
Normal file
@@ -0,0 +1,909 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
#include "common/ustr.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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::handleAbbreviations() {
|
||||
// Construct a unicode string containing the word
|
||||
int wordSize = 0;
|
||||
while (wordSize < (_resolution * 3) && _decoded[wordSize])
|
||||
++wordSize;
|
||||
Common::U32String word(_decoded, _decoded + wordSize);
|
||||
|
||||
// Check for standard abbreviations
|
||||
if (word == "g")
|
||||
word = "again";
|
||||
else if (word == "o")
|
||||
word = "oops";
|
||||
else if (word == "x")
|
||||
word = "examine";
|
||||
else if (word == "z")
|
||||
word = "wait";
|
||||
else
|
||||
return;
|
||||
|
||||
// Found abbreviation, so copy it's long form into buffer
|
||||
Common::copy(word.c_str(), word.c_str() + MIN((int)word.size() + 1, _resolution * 3), _decoded);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if ((from == 1) && isInfocom() && h_version < 5)
|
||||
handleAbbreviations();
|
||||
|
||||
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 ZCode
|
||||
} // End of namespace Glk
|
||||
198
engines/glk/zcode/processor_variables.cpp
Normal file
198
engines/glk/zcode/processor_variables.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
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 ZCode
|
||||
} // End of namespace Glk
|
||||
300
engines/glk/zcode/processor_windows.cpp
Normal file
300
engines/glk/zcode/processor_windows.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
static struct {
|
||||
Story story_id;
|
||||
int pic;
|
||||
int pic1;
|
||||
int pic2;
|
||||
} mapper[] = {
|
||||
{ ZORK_ZERO, 5, 497, 498 },
|
||||
{ ZORK_ZERO, 6, 501, 502 },
|
||||
{ ZORK_ZERO, 7, 499, 500 },
|
||||
{ ZORK_ZERO, 8, 503, 504 },
|
||||
{ ARTHUR, 54, 170, 171 },
|
||||
{ SHOGUN, 50, 61, 62 },
|
||||
{ UNKNOWN, 0, 0, 0 }
|
||||
};
|
||||
|
||||
void Processor::z_draw_picture() {
|
||||
zword pic = zargs[0];
|
||||
zword y = zargs[1];
|
||||
zword x = zargs[2];
|
||||
int i;
|
||||
|
||||
flush_buffer();
|
||||
|
||||
Window &win = _wp[_wp._cwin];
|
||||
if (_storyId == ZORK_ZERO && _wp._cwin == 0) {
|
||||
// WORKAROUND: Zork Zero has pictures for graphics embedded in the text with specific
|
||||
// co-prdinates. We need to reset it to 0,0 to flag it should be drawn at the cursor
|
||||
x = y = 0;
|
||||
} else {
|
||||
assert(x && y);
|
||||
x += win[X_POS] - 1;
|
||||
y += win[Y_POS] - 1;
|
||||
}
|
||||
|
||||
/* The following is necessary to make Amiga and Macintosh story
|
||||
* files work with MCGA graphics files. Some screen-filling
|
||||
* pictures of the original Amiga release like the borders of
|
||||
* Zork Zero were split into several MCGA pictures (left, right
|
||||
* and top borders). We pretend this has not happened.
|
||||
*/
|
||||
for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
|
||||
if (_storyId == mapper[i].story_id && pic == mapper[i].pic) {
|
||||
uint height1, width1;
|
||||
uint height2, width2;
|
||||
|
||||
int delta = 0;
|
||||
|
||||
os_picture_data(pic, &height1, &width1);
|
||||
os_picture_data(mapper[i].pic2, &height2, &width2);
|
||||
|
||||
if (_storyId == ARTHUR && pic == 54)
|
||||
delta = h_screen_width / 160;
|
||||
|
||||
assert(x && y);
|
||||
os_draw_picture(mapper[i].pic1, Point(x + delta, y + height1));
|
||||
os_draw_picture(mapper[i].pic2, Point(x + width1 - width2 - delta, y + height1));
|
||||
}
|
||||
}
|
||||
|
||||
os_draw_picture(pic, Point(x, y));
|
||||
|
||||
if (_storyId == SHOGUN && pic == 3) {
|
||||
uint height, width;
|
||||
|
||||
os_picture_data(59, &height, &width);
|
||||
os_draw_picture(59, Point(h_screen_width - width + 1, y));
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::z_picture_data() {
|
||||
zword pic = zargs[0];
|
||||
zword table = zargs[1];
|
||||
uint height, width;
|
||||
int i;
|
||||
|
||||
bool avail = os_picture_data(pic, &height, &width);
|
||||
|
||||
for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
|
||||
if (_storyId == mapper[i].story_id) {
|
||||
if (pic == mapper[i].pic) {
|
||||
uint height2, width2;
|
||||
|
||||
avail &= os_picture_data(mapper[i].pic1, &height2, &width2);
|
||||
avail &= os_picture_data(mapper[i].pic2, &height2, &width2);
|
||||
|
||||
height += height2;
|
||||
} else if (pic == mapper[i].pic1 || pic == mapper[i].pic2) {
|
||||
avail = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storew((zword)(table + 0), (zword)(height));
|
||||
storew((zword)(table + 2), (zword)(width));
|
||||
|
||||
branch(avail);
|
||||
}
|
||||
|
||||
void Processor::z_erase_picture() {
|
||||
#ifdef TODO
|
||||
int height, width;
|
||||
|
||||
zword y = zargs[1];
|
||||
zword x = zargs[2];
|
||||
|
||||
flush_buffer();
|
||||
|
||||
/* Do nothing if the background is transparent */
|
||||
|
||||
if (hi(cwp->colour) == TRANSPARENT_COLOUR)
|
||||
return;
|
||||
|
||||
if (y == 0) /* use cursor line if y-coordinate is 0 */
|
||||
y = cwp->y_cursor;
|
||||
if (x == 0) /* use cursor column if x-coordinate is 0 */
|
||||
x = cwp->x_cursor;
|
||||
|
||||
os_picture_data(zargs[0], &height, &width);
|
||||
|
||||
y += cwp->y_pos - 1;
|
||||
x += cwp->x_pos - 1;
|
||||
|
||||
os_erase_area(y, x, y + height - 1, x + width - 1, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Processor::z_set_margins() {
|
||||
#ifdef TODO
|
||||
zword win = winarg2();
|
||||
|
||||
flush_buffer();
|
||||
|
||||
wp[win].left = zargs[0];
|
||||
wp[win].right = zargs[1];
|
||||
|
||||
/* Protect the margins */
|
||||
|
||||
if (wp[win].x_cursor <= zargs[0] || wp[win].x_cursor > wp[win].x_size - zargs[1]) {
|
||||
|
||||
wp[win].x_cursor = zargs[0] + 1;
|
||||
|
||||
if (win == cwin)
|
||||
update_cursor();
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Processor::z_move_window(void) {
|
||||
flush_buffer();
|
||||
|
||||
zword win = winarg0();
|
||||
_wp[win].setPosition(Point(zargs[2], zargs[1]));
|
||||
}
|
||||
|
||||
void Processor::z_window_size() {
|
||||
flush_buffer();
|
||||
|
||||
zword win = winarg0();
|
||||
_wp[win].setSize(Point(zargs[2], zargs[1]));
|
||||
}
|
||||
|
||||
void Processor::z_window_style() {
|
||||
#ifdef TODO
|
||||
zword win = winarg0();
|
||||
zword flags = zargs[1];
|
||||
|
||||
flush_buffer();
|
||||
|
||||
/* Supply default arguments */
|
||||
|
||||
if (zargc < 3)
|
||||
zargs[2] = 0;
|
||||
|
||||
/* Set window style */
|
||||
|
||||
switch (zargs[2]) {
|
||||
case 0: wp[win].attribute = flags; break;
|
||||
case 1: wp[win].attribute |= flags; break;
|
||||
case 2: wp[win].attribute &= ~flags; break;
|
||||
case 3: wp[win].attribute ^= flags; break;
|
||||
}
|
||||
|
||||
if (cwin == win)
|
||||
update_attributes();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Processor::z_get_wind_prop() {
|
||||
flush_buffer();
|
||||
|
||||
zword win = winarg0();
|
||||
zword prop = zargs[1];
|
||||
|
||||
if (prop <= TRUE_BG_COLOR)
|
||||
store(_wp[win][(WindowProperty)prop]);
|
||||
else
|
||||
runtimeError(ERR_ILL_WIN_PROP);
|
||||
}
|
||||
|
||||
void Processor::z_put_wind_prop() {
|
||||
flush_buffer();
|
||||
|
||||
zword win = winarg0();
|
||||
WindowProperty prop = (WindowProperty)zargs[1];
|
||||
zword val = zargs[2];
|
||||
|
||||
if (prop >= TRUE_FG_COLOR)
|
||||
runtimeError(ERR_ILL_WIN_PROP);
|
||||
|
||||
_wp[win][prop] = val;
|
||||
}
|
||||
|
||||
void Processor::z_scroll_window() {
|
||||
#ifdef TODO
|
||||
zword win = winarg0();
|
||||
zword y, x;
|
||||
|
||||
flush_buffer();
|
||||
|
||||
/* Use the correct set of colours when scrolling the window */
|
||||
|
||||
if (win != cwin && !amiga_screen_model())
|
||||
os_set_colour(lo(wp[win].colour), hi(wp[win].colour));
|
||||
|
||||
y = wp[win].y_pos;
|
||||
x = wp[win].x_pos;
|
||||
|
||||
os_scroll_area(y,
|
||||
x,
|
||||
y + wp[win].y_size - 1,
|
||||
x + wp[win].x_size - 1,
|
||||
(short)zargs[1]);
|
||||
|
||||
if (win != cwin && !amiga_screen_model())
|
||||
os_set_colour(lo(cwp->colour), hi(cwp->colour));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Processor::z_mouse_window() {
|
||||
// No implementation - since ScummVM can run as a window, it's better
|
||||
// not to constrain the area the mouse can move
|
||||
}
|
||||
|
||||
void Processor::z_picture_table() {
|
||||
/* This opcode is used by Shogun and Zork Zero when the player
|
||||
* encounters built-in games such as Peggleboz. Nowadays it is
|
||||
* not very helpful to hold the picture data in memory because
|
||||
* even a small disk cache avoids re-loading of data.
|
||||
*/
|
||||
}
|
||||
|
||||
zword Processor::winarg0() {
|
||||
if (h_version == V6 && (short)zargs[0] == -3)
|
||||
return _wp._cwin;
|
||||
|
||||
if (zargs[0] >= ((h_version == V6) ? 8 : 2))
|
||||
runtimeError(ERR_ILL_WIN);
|
||||
|
||||
return zargs[0];
|
||||
}
|
||||
|
||||
zword Processor::winarg2() {
|
||||
if (zargc < 3 || (short)zargs[2] == -3)
|
||||
return _wp._cwin;
|
||||
|
||||
if (zargs[2] >= 8)
|
||||
runtimeError(ERR_ILL_WIN);
|
||||
|
||||
return zargs[2];
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
420
engines/glk/zcode/quetzal.cpp
Normal file
420
engines/glk/zcode/quetzal.cpp
Normal file
@@ -0,0 +1,420 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/quetzal.h"
|
||||
#include "glk/zcode/processor.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Various parsing states within restoration.
|
||||
*/
|
||||
enum ParseState {
|
||||
GOT_HEADER = 0x01,
|
||||
GOT_STACK = 0x02,
|
||||
GOT_MEMORY = 0x04,
|
||||
GOT_NONE = 0x00,
|
||||
GOT_ALL = 0x07,
|
||||
GOT_ERROR = 0x80
|
||||
};
|
||||
|
||||
#define WRITE_RUN(RUN) ws.writeByte(0); ws.writeByte((byte)(RUN))
|
||||
|
||||
bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::String &desc) {
|
||||
Processor &p = *proc;
|
||||
offset_t pc;
|
||||
zword i, j, n;
|
||||
zword nvars, nargs, nstk;
|
||||
zbyte var;
|
||||
int c;
|
||||
|
||||
// Reset Quetzal writer
|
||||
_writer.clear();
|
||||
|
||||
// Write `IFhd' chunk
|
||||
{
|
||||
Common::WriteStream &ws = _writer.add(ID_IFhd);
|
||||
pc = p.getPC();
|
||||
ws.writeUint16BE(p.h_release);
|
||||
ws.write(&p[H_SERIAL], 6);
|
||||
ws.writeUint16BE(p.h_checksum);
|
||||
|
||||
ws.writeByte((pc >> 16) & 0xff);
|
||||
ws.writeByte((pc >> 8) & 0xff);
|
||||
ws.writeByte(pc & 0xff);
|
||||
}
|
||||
|
||||
// Write `CMem' chunk.
|
||||
{
|
||||
Common::WriteStream &ws = _writer.add(ID_CMem);
|
||||
_storyFile->seek(0);
|
||||
|
||||
// j holds current run length.
|
||||
for (i = 0, j = 0; i < p.h_dynamic_size; ++i) {
|
||||
c = _storyFile->readByte();
|
||||
c ^= p[i];
|
||||
|
||||
if (c == 0) {
|
||||
// It's a run of equal bytes
|
||||
++j;
|
||||
} else {
|
||||
// Write out any run there may be.
|
||||
if (j > 0) {
|
||||
for (; j > 0x100; j -= 0x100) {
|
||||
WRITE_RUN(0xFF);
|
||||
}
|
||||
WRITE_RUN(j - 1);
|
||||
j = 0;
|
||||
}
|
||||
|
||||
// Any runs are now written. Write this (nonzero) byte
|
||||
ws.writeByte(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write `Stks' chunk. You are not expected to understand this. ;)
|
||||
{
|
||||
Common::WriteStream &ws = _writer.add(ID_Stks);
|
||||
|
||||
// We construct a list of frame indices, most recent first, in `frames'.
|
||||
// These indices are the offsets into the `stack' array of the word before
|
||||
// the first word pushed in each frame.
|
||||
frames[0] = p._sp - p._stack; // The frame we'd get by doing a call now.
|
||||
for (i = p._fp - p._stack + 4, n = 0; i < STACK_SIZE + 4; i = p._stack[i - 3] + 5)
|
||||
frames[++n] = i;
|
||||
|
||||
// All versions other than V6 can use evaluation stack outside a function
|
||||
// context. We write a faked stack frame (most fields zero) to cater for this.
|
||||
if (p.h_version != V6) {
|
||||
for (i = 0; i < 6; ++i)
|
||||
ws.writeByte(0);
|
||||
nstk = STACK_SIZE - frames[n];
|
||||
ws.writeUint16BE(nstk);
|
||||
for (j = STACK_SIZE - 1; j >= frames[n]; --j)
|
||||
ws.writeUint16BE(p._stack[j]);
|
||||
}
|
||||
|
||||
// Write out the rest of the stack frames.
|
||||
for (i = n; i > 0; --i) {
|
||||
zword *pf = p._stack + frames[i] - 4; // Points to call frame
|
||||
nvars = (pf[0] & 0x0F00) >> 8;
|
||||
nargs = pf[0] & 0x00FF;
|
||||
nstk = frames[i] - frames[i - 1] - nvars - 4;
|
||||
pc = ((uint)pf[3] << 9) | pf[2];
|
||||
|
||||
// Check type of call
|
||||
switch (pf[0] & 0xF000) {
|
||||
case 0x0000:
|
||||
// Function
|
||||
var = p[pc];
|
||||
pc = ((pc + 1) << 8) | nvars;
|
||||
break;
|
||||
|
||||
case 0x1000:
|
||||
// Procedure
|
||||
var = 0;
|
||||
pc = (pc << 8) | 0x10 | nvars; // Set procedure flag
|
||||
break;
|
||||
|
||||
default:
|
||||
p.runtimeError(ERR_SAVE_IN_INTER);
|
||||
return 0;
|
||||
}
|
||||
if (nargs != 0)
|
||||
nargs = (1 << nargs) - 1; // Make args into bitmap
|
||||
|
||||
// Write the main part of the frame...
|
||||
ws.writeUint32BE(pc);
|
||||
ws.writeByte(var);
|
||||
ws.writeByte(nargs);
|
||||
ws.writeUint16BE(nstk);
|
||||
|
||||
// Write the variables and eval stack
|
||||
for (j = 0, --pf; j<nvars + nstk; ++j, --pf)
|
||||
ws.writeUint16BE(*pf);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the save data out
|
||||
_writer.save(svf, desc, ID_IFZS);
|
||||
|
||||
// After all that, still nothing went wrong!
|
||||
return true;
|
||||
}
|
||||
|
||||
int Quetzal::restore(Common::SeekableReadStream *sv, Processor *proc) {
|
||||
Processor &p = *proc;
|
||||
uint tmpl, currlen;
|
||||
offset_t pc;
|
||||
zword tmpw;
|
||||
int fatal = 0; // Set to -1 when errors must be fatal.
|
||||
zbyte progress = GOT_NONE;
|
||||
int i, x, y;
|
||||
|
||||
// Load the savefile for reading
|
||||
if (!_reader.open(sv, ID_IFZS)) {
|
||||
p.print_string("This is not a saved game file!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read each chunk and process it
|
||||
for (QuetzalReader::Iterator it = _reader.begin(); it != _reader.end(); ++it) {
|
||||
Common::SeekableReadStream *s = it.getStream();
|
||||
currlen = (*it)._size;
|
||||
|
||||
switch ((*it)._id) {
|
||||
// `IFhd' header chunk; must be first in file
|
||||
case ID_IFhd:
|
||||
if (progress & GOT_HEADER) {
|
||||
p.print_string("Save file has two IFZS chunks!\n");
|
||||
return fatal;
|
||||
}
|
||||
progress |= GOT_HEADER;
|
||||
if (currlen < 13)
|
||||
return fatal;
|
||||
|
||||
tmpw = s->readUint16BE();
|
||||
if (tmpw != p.h_release)
|
||||
progress = GOT_ERROR;
|
||||
|
||||
for (int idx = H_SERIAL; idx < H_SERIAL + 6; ++idx) {
|
||||
x = s->readByte();
|
||||
if (x != p[idx])
|
||||
progress = GOT_ERROR;
|
||||
}
|
||||
|
||||
tmpw = s->readUint16BE();
|
||||
if (tmpw != p.h_checksum)
|
||||
progress = GOT_ERROR;
|
||||
|
||||
if (progress & GOT_ERROR) {
|
||||
p.print_string("File was not saved from this story!\n");
|
||||
return fatal;
|
||||
}
|
||||
|
||||
x = s->readByte();
|
||||
pc = (uint)x << 16;
|
||||
x = s->readByte();
|
||||
pc |= (uint)x << 8;
|
||||
x = s->readByte();
|
||||
pc |= (uint)x;
|
||||
|
||||
fatal = -1; // Setting PC means errors must be fatal
|
||||
p.setPC(pc);
|
||||
break;
|
||||
|
||||
// `Stks' stacks chunk; restoring this is quite complex. ;)
|
||||
case ID_Stks:
|
||||
if (progress & GOT_STACK) {
|
||||
p.print_string("File contains two stack chunks!\n");
|
||||
break;
|
||||
}
|
||||
progress |= GOT_STACK;
|
||||
|
||||
fatal = -1; // Setting SP means errors must be fatal
|
||||
p._sp = p._stack + STACK_SIZE;
|
||||
|
||||
// All versions other than V6 may use evaluation stack outside any function context.
|
||||
// As a result a faked function context will be present in the file here. We skip
|
||||
// this context, but load the associated stack onto the stack proper...
|
||||
if (p.h_version != V6) {
|
||||
if (currlen < 8)
|
||||
return fatal;
|
||||
|
||||
s->skip(6);
|
||||
tmpw = s->readUint16BE();
|
||||
|
||||
if (tmpw > STACK_SIZE) {
|
||||
p.print_string("Save-file has too much stack (and I can't cope).\n");
|
||||
return fatal;
|
||||
}
|
||||
|
||||
currlen -= 8;
|
||||
if (currlen < (uint)tmpw * 2)
|
||||
return fatal;
|
||||
for (i = 0; i < tmpw; ++i)
|
||||
*--p._sp = s->readUint16BE();
|
||||
currlen -= tmpw * 2;
|
||||
}
|
||||
|
||||
// We now proceed to load the main block of stack frames
|
||||
for (p._fp = p._stack + STACK_SIZE, p._frameCount = 0;
|
||||
currlen > 0; currlen -= 8, ++p._frameCount) {
|
||||
if (currlen < 8) return fatal;
|
||||
if (p._sp - p._stack < 4) {
|
||||
// No space for frame
|
||||
p.print_string("Save-file has too much stack (and I can't cope).\n");
|
||||
return fatal;
|
||||
}
|
||||
|
||||
// Read PC, procedure flag and formal param count
|
||||
tmpl = s->readUint32BE();
|
||||
y = (int)(tmpl & 0x0F); // Number of formals
|
||||
tmpw = y << 8;
|
||||
|
||||
// Read result variable
|
||||
x = s->readByte();
|
||||
|
||||
// Check the procedure flag...
|
||||
if (tmpl & 0x10) {
|
||||
tmpw |= 0x1000; // It's a procedure
|
||||
tmpl >>= 8; // Shift to get PC value
|
||||
} else {
|
||||
// Functions have type 0, so no need to or anything
|
||||
tmpl >>= 8; // Shift to get PC value
|
||||
--tmpl; // Point at result byte. */
|
||||
|
||||
// Sanity check on result variable...
|
||||
if (p[tmpl] != (zbyte)x) {
|
||||
p.print_string("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
|
||||
return fatal;
|
||||
}
|
||||
}
|
||||
|
||||
*--p._sp = (zword)(tmpl >> 9); // High part of PC
|
||||
*--p._sp = (zword)(tmpl & 0x1FF); // Low part of PC
|
||||
*--p._sp = (zword)(p._fp - p._stack - 1); // FP
|
||||
|
||||
// Read and process argument mask
|
||||
x = s->readByte();
|
||||
++x; // Should now be a power of 2
|
||||
for (i = 0; i<8; ++i)
|
||||
if (x & (1 << i))
|
||||
break;
|
||||
if (x ^ (1 << i)) {
|
||||
// Not a power of 2
|
||||
p.print_string("Save-file uses incomplete argument lists (which I can't handle)\n");
|
||||
return fatal;
|
||||
}
|
||||
|
||||
*--p._sp = tmpw | i;
|
||||
p._fp = p._sp; // FP for next frame
|
||||
|
||||
// Read amount of eval stack used
|
||||
tmpw = s->readUint16BE();
|
||||
|
||||
tmpw += y; // Amount of stack + number of locals
|
||||
if (p._sp - p._stack <= tmpw) {
|
||||
p.print_string("Save-file has too much stack (and I can't cope).\n");
|
||||
return fatal;
|
||||
}
|
||||
if (currlen < (uint)tmpw * 2)
|
||||
return fatal;
|
||||
|
||||
for (i = 0; i < tmpw; ++i)
|
||||
*--p._sp = s->readUint16BE();
|
||||
currlen -= tmpw * 2;
|
||||
}
|
||||
|
||||
// End of `Stks' processing...
|
||||
break;
|
||||
|
||||
// `CMem' compressed memory chunk; uncompress it
|
||||
case ID_CMem:
|
||||
if (!(progress & GOT_MEMORY)) {
|
||||
_storyFile->seek(0);
|
||||
|
||||
i = 0; // Bytes written to data area
|
||||
for (; currlen > 0; --currlen) {
|
||||
x = s->readByte();
|
||||
if (x == 0) {
|
||||
// Start of run
|
||||
// Check for bogus run
|
||||
if (currlen < 2) {
|
||||
p.print_string("File contains bogus `CMem' chunk.\n");
|
||||
s->skip(currlen);
|
||||
|
||||
currlen = 1;
|
||||
i = 0xFFFF;
|
||||
break; // Keep going; may be a `UMem' too
|
||||
}
|
||||
|
||||
// Copy story file to memory during the run
|
||||
--currlen;
|
||||
x = s->readByte();
|
||||
for (; x >= 0 && i < p.h_dynamic_size; --x, ++i)
|
||||
p[i] = _storyFile->readByte();
|
||||
} else {
|
||||
// Not a run
|
||||
y = _storyFile->readByte();
|
||||
p[i] = (zbyte)(x ^ y);
|
||||
++i;
|
||||
}
|
||||
|
||||
// Make sure we don't load too much
|
||||
if (i > p.h_dynamic_size) {
|
||||
p.print_string("warning: `CMem' chunk too long!\n");
|
||||
s->skip(currlen);
|
||||
break; // Keep going; there may be a `UMem' too
|
||||
}
|
||||
}
|
||||
|
||||
// If chunk is short, assume a run
|
||||
for (; i < p.h_dynamic_size; ++i)
|
||||
p[i] = _storyFile->readByte();
|
||||
|
||||
if (currlen == 0)
|
||||
progress |= GOT_MEMORY; // Only if succeeded
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// 'UMem' Uncompressed memory chunk
|
||||
case ID_UMem:
|
||||
if (!(progress & GOT_MEMORY)) {
|
||||
// Must be exactly the right size
|
||||
if (currlen == p.h_dynamic_size) {
|
||||
if (s->read(p.zmp, currlen) == currlen) {
|
||||
progress |= GOT_MEMORY; // Only on success
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
p.print_string("`UMem' chunk wrong size!\n");
|
||||
}
|
||||
|
||||
// Fall into default action (skip chunk) on errors
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
delete s;
|
||||
}
|
||||
|
||||
// We've reached the end of the file. For the restoration to have been a
|
||||
// success, we must have had one of each of the required chunks.
|
||||
if (!(progress & GOT_HEADER))
|
||||
p.print_string("error: no valid header (`IFhd') chunk in file.\n");
|
||||
if (!(progress & GOT_STACK))
|
||||
p.print_string("error: no valid stack (`Stks') chunk in file.\n");
|
||||
if (!(progress & GOT_MEMORY))
|
||||
p.print_string("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
|
||||
|
||||
return (progress == GOT_ALL ? 2 : fatal);
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
68
engines/glk/zcode/quetzal.h
Normal file
68
engines/glk/zcode/quetzal.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_QUETZAL
|
||||
#define GLK_ZCODE_QUETZAL
|
||||
|
||||
#include "glk/glk_types.h"
|
||||
#include "glk/quetzal.h"
|
||||
#include "glk/zcode/frotz_types.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
class Processor;
|
||||
|
||||
class Quetzal {
|
||||
private:
|
||||
Common::SeekableReadStream *_storyFile;
|
||||
QuetzalReader _reader;
|
||||
QuetzalWriter _writer;
|
||||
zword frames[STACK_SIZE / 4 + 1];
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Quetzal(Common::SeekableReadStream *storyFile) : _storyFile(storyFile) {}
|
||||
|
||||
/*
|
||||
* Save a game using Quetzal format.
|
||||
* @param svf Savegame file
|
||||
* @param proc Pointer to the Frotz processor
|
||||
* @param desc Savegame description
|
||||
* @returns Returns true if OK, false if failed
|
||||
*/
|
||||
bool save(Common::WriteStream *svf, Processor *proc, const Common::String &desc);
|
||||
|
||||
/**
|
||||
* Restore a saved game using Quetzal format
|
||||
* @param svf Savegame file
|
||||
* @param proc Pointer to the Frotz processor
|
||||
* @returns Return 2 if OK, 0 if an error occurred before any damage was done,
|
||||
* -1 on a fatal error
|
||||
*/
|
||||
int restore(Common::SeekableReadStream *svf, Processor *proc);
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
128
engines/glk/zcode/screen.cpp
Normal file
128
engines/glk/zcode/screen.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/screen.h"
|
||||
#include "glk/zcode/bitmap_font.h"
|
||||
#include "glk/zcode/zcode.h"
|
||||
#include "glk/conf.h"
|
||||
#include "common/file.h"
|
||||
#include "graphics/fonts/ttf.h"
|
||||
#include "image/xbm.h"
|
||||
|
||||
#include "glk/zcode/infocom6x8.xbm"
|
||||
#include "glk/zcode/infocom_graphics.xbm"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
FrotzScreen::FrotzScreen() : Glk::Screen() {
|
||||
g_conf->_tStyles[style_User1].font = CUSTOM;
|
||||
g_conf->_gStyles[style_User1].font = CUSTOM;
|
||||
g_conf->_tStyles[style_User2].font = CUSTOM2;
|
||||
}
|
||||
|
||||
void FrotzScreen::loadFonts(Common::Archive *archive) {
|
||||
// Get the zmachine version. At this point the header isn't loaded, so we have to do it manually
|
||||
g_vm->_gameFile.seek(0);
|
||||
byte version = g_vm->_gameFile.readByte();
|
||||
|
||||
if (version == 6) {
|
||||
loadVersion6Fonts(archive);
|
||||
} else {
|
||||
// Load the basic fonts
|
||||
Screen::loadFonts(archive);
|
||||
}
|
||||
|
||||
// Add character graphics and runic fonts
|
||||
loadExtraFonts(archive);
|
||||
}
|
||||
|
||||
void FrotzScreen::loadVersion6Fonts(Common::Archive *archive) {
|
||||
// Set the basic font properties
|
||||
MonoFontInfo &mi = g_conf->_monoInfo;
|
||||
PropFontInfo &pi = g_conf->_propInfo;
|
||||
mi._size = pi._size = 7;
|
||||
mi._aspect = pi._aspect = 1.0;
|
||||
pi._quotes = 0;
|
||||
pi._dashes = 0;
|
||||
pi._spaces = 0;
|
||||
pi._morePrompt = "[MORE]";
|
||||
pi._lineSeparation = 0;
|
||||
|
||||
g_vm->_defaultForeground = 0;
|
||||
g_vm->_defaultBackground = (int)zcolor_Transparent;
|
||||
g_conf->_tMarginX = 3;
|
||||
g_conf->_tMarginY = 3;
|
||||
|
||||
for (uint idx = 0; idx < style_NUMSTYLES; ++idx) {
|
||||
g_conf->_tStyles[idx].bg = g_conf->_tStylesDefault[idx].bg = zcolor_Transparent;
|
||||
g_conf->_gStyles[idx].bg = g_conf->_gStylesDefault[idx].bg = zcolor_Transparent;
|
||||
}
|
||||
|
||||
_fonts.resize(8);
|
||||
|
||||
// Load up the 8x8 Infocom font
|
||||
Image::XBMDecoder decoder;
|
||||
decoder.loadBits(infocom6x8_bits, infocom6x8_width, infocom6x8_height);
|
||||
|
||||
Common::Point fontSize(6, 8);
|
||||
|
||||
// Add normal fonts
|
||||
_fonts[MONOR] = new FixedWidthBitmapFont(*decoder.getSurface(), fontSize, 6, 8);
|
||||
_fonts[MONOB] = new FixedWidthBitmapFont(*decoder.getSurface(), fontSize, 6, 8);
|
||||
_fonts[PROPR] = new VariableWidthBitmapFont(*decoder.getSurface(), fontSize, 6, 8);
|
||||
_fonts[PROPB] = new VariableWidthBitmapFont(*decoder.getSurface(), fontSize, 6, 8);
|
||||
|
||||
// Create a new version of the font with every character unlined for the emphasized fonts
|
||||
const Graphics::Surface &norm = *decoder.getSurface();
|
||||
Graphics::ManagedSurface emph(norm.w, norm.h);
|
||||
emph.blitFrom(norm);
|
||||
|
||||
for (int y = 8 - 2; y < emph.h; y += 8) {
|
||||
byte *lineP = (byte *)emph.getBasePtr(0, y);
|
||||
Common::fill(lineP, lineP + emph.w, 1);
|
||||
}
|
||||
|
||||
// Add them to the font list
|
||||
_fonts[MONOI] = new FixedWidthBitmapFont(emph, fontSize, 6, 8);
|
||||
_fonts[MONOZ] = new FixedWidthBitmapFont(emph, fontSize, 6, 8);
|
||||
_fonts[PROPI] = new VariableWidthBitmapFont(emph, fontSize, 6, 8);
|
||||
_fonts[PROPZ] = new VariableWidthBitmapFont(emph, fontSize, 6, 8);
|
||||
}
|
||||
|
||||
void FrotzScreen::loadExtraFonts(Common::Archive *archive) {
|
||||
Image::XBMDecoder decoder;
|
||||
decoder.loadBits(infocom_graphics_bits, infocom_graphics_width, infocom_graphics_height);
|
||||
|
||||
Common::Point fontSize(_fonts[0]->getMaxCharWidth(), _fonts[0]->getFontHeight());
|
||||
_fonts.push_back(new FixedWidthBitmapFont(*decoder.getSurface(), fontSize));
|
||||
|
||||
// Add Runic font. It provides cleaner versions of the runic characters in the
|
||||
// character graphics font
|
||||
Common::File *f = new Common::File();
|
||||
if (!f->open("NotoSansRunic-Regular.ttf", *archive))
|
||||
error("Could not load font");
|
||||
|
||||
_fonts.push_back(Graphics::loadTTFFont(f, DisposeAfterUse::YES, g_conf->_propInfo._size, Graphics::kTTFSizeModeCharacter));
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
59
engines/glk/zcode/screen.h
Normal file
59
engines/glk/zcode/screen.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_FONTS
|
||||
#define GLK_ZCODE_FONTS
|
||||
|
||||
#include "glk/screen.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Derived screen class that adds in the Infocom character graphics font
|
||||
*/
|
||||
class FrotzScreen : public Glk::Screen {
|
||||
private:
|
||||
/**
|
||||
* Handles loading fonts for V6 games
|
||||
*/
|
||||
void loadVersion6Fonts(Common::Archive *archive);
|
||||
|
||||
/**
|
||||
* Handles loading the character graphics and runic fonts
|
||||
*/
|
||||
void loadExtraFonts(Common::Archive *archive);
|
||||
protected:
|
||||
/**
|
||||
* Load the fonts
|
||||
*/
|
||||
void loadFonts(Common::Archive *archive) override;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FrotzScreen();
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
146
engines/glk/zcode/sound_folder.cpp
Normal file
146
engines/glk/zcode/sound_folder.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/sound_folder.h"
|
||||
#include "common/file.h"
|
||||
#include "common/compression/unzip.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
void SoundSubfolder::check(const Common::FSNode &gameDir) {
|
||||
Common::FSNode sound = gameDir.getChild("sound");
|
||||
if (sound.isDirectory())
|
||||
SearchMan.add("sound", new SoundSubfolder(sound));
|
||||
}
|
||||
|
||||
SoundSubfolder::SoundSubfolder(const Common::FSNode &folder) : _folder(folder) {
|
||||
Common::FSList files;
|
||||
if (folder.getChildren(files, Common::FSNode::kListFilesOnly)) {
|
||||
for (uint idx = 0; idx < files.size(); ++idx) {
|
||||
Common::String filename = files[idx].getName();
|
||||
if (filename.hasSuffixIgnoreCase(".snd")) {
|
||||
int fileNum = atoi(filename.c_str() + filename.size() - 6);
|
||||
Common::Path newName(Common::String::format("sound%d.snd", fileNum));
|
||||
|
||||
_filenames[newName] = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundSubfolder::hasFile(const Common::Path &path) const {
|
||||
return _filenames.contains(path);
|
||||
}
|
||||
|
||||
int SoundSubfolder::listMembers(Common::ArchiveMemberList &list) const {
|
||||
int total = 0;
|
||||
for (FileMap::iterator i = _filenames.begin(); i != _filenames.end(); ++i) {
|
||||
list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember((*i)._key, *this)));
|
||||
++total;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr SoundSubfolder::getMember(const Common::Path &path) const {
|
||||
if (!hasFile(path))
|
||||
return Common::ArchiveMemberPtr();
|
||||
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *SoundSubfolder::createReadStreamForMember(const Common::Path &path) const {
|
||||
Common::File *f = new Common::File();
|
||||
if (_filenames.contains(path) && f->open(_folder.getChild(_filenames[path])))
|
||||
return f;
|
||||
|
||||
delete f;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void SoundZip::check(const Common::FSNode &gameDir, Story story) {
|
||||
if (story != LURKING_HORROR && story != SHERLOCK)
|
||||
return;
|
||||
Common::String zipName = (story == LURKING_HORROR) ? "lhsound.zip" : "shsound.zip";
|
||||
|
||||
// Check for the existence of the zip
|
||||
Common::FSNode zipNode = gameDir.getChild(zipName);
|
||||
if (!zipNode.exists())
|
||||
return;
|
||||
|
||||
SearchMan.add("sound", new SoundZip(Common::makeZipArchive(zipNode)));
|
||||
}
|
||||
|
||||
SoundZip::SoundZip(Common::Archive *zip) : _zip(zip) {
|
||||
Common::ArchiveMemberList files;
|
||||
zip->listMembers(files);
|
||||
|
||||
for (Common::ArchiveMemberList::iterator i = files.begin(); i != files.end(); ++i) {
|
||||
Common::Path filename = (*i)->getPathInArchive();
|
||||
Common::String basename(filename.baseName());
|
||||
if (basename.hasSuffixIgnoreCase(".snd")) {
|
||||
int fileNum = atoi(basename.c_str() + basename.size() - 6);
|
||||
Common::Path newName(Common::String::format("sound%d.snd", fileNum));
|
||||
|
||||
_filenames[newName] = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoundZip::~SoundZip() {
|
||||
delete _zip;
|
||||
}
|
||||
|
||||
bool SoundZip::hasFile(const Common::Path &path) const {
|
||||
return _filenames.contains(path);
|
||||
}
|
||||
|
||||
int SoundZip::listMembers(Common::ArchiveMemberList &list) const {
|
||||
int total = 0;
|
||||
|
||||
for (FileMap::iterator i = _filenames.begin(); i != _filenames.end(); ++i) {
|
||||
list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember((*i)._key, *this)));
|
||||
++total;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr SoundZip::getMember(const Common::Path &path) const {
|
||||
if (!hasFile(path))
|
||||
return Common::ArchiveMemberPtr();
|
||||
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
|
||||
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *SoundZip::createReadStreamForMember(const Common::Path &path) const {
|
||||
if (!_filenames.contains(path))
|
||||
return nullptr;
|
||||
|
||||
return _zip->createReadStreamForMember(_filenames[path]);
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
139
engines/glk/zcode/sound_folder.h
Normal file
139
engines/glk/zcode/sound_folder.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_SOUND_FOLDER_H
|
||||
#define GLK_ZCODE_SOUND_FOLDER_H
|
||||
|
||||
#include "glk/zcode/frotz_types.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
/**
|
||||
* Acts as an interface to an Infocom sound subfolder for the Lurking Horror or
|
||||
* Sherlock. Any file which ends with a number and '.snd' will be accessible as
|
||||
* 'sound<num>.snd' in the outer Glk layer
|
||||
*/
|
||||
class SoundSubfolder : public Common::Archive {
|
||||
private:
|
||||
Common::FSNode _folder;
|
||||
typedef Common::HashMap<Common::Path, Common::String, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> FileMap;
|
||||
FileMap _filenames;
|
||||
private:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
SoundSubfolder(const Common::FSNode &folder);
|
||||
public:
|
||||
/**
|
||||
* Checks for a sound subfolder, and if so, instantiates the class for it
|
||||
*/
|
||||
static void check(const Common::FSNode &gameDir);
|
||||
|
||||
/**
|
||||
* Check if a member with the given name is present in the Archive.
|
||||
* Patterns are not allowed, as this is meant to be a quick File::exists()
|
||||
* replacement.
|
||||
*/
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Add all members of the Archive to list.
|
||||
* Must only append to list, and not remove elements from it.
|
||||
*
|
||||
* @return the number of names added to list
|
||||
*/
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
|
||||
/**
|
||||
* Returns a ArchiveMember representation of the given file.
|
||||
*/
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Create a stream bound to a member with the specified name in the
|
||||
* archive. If no member with this name exists, 0 is returned.
|
||||
* @return the newly created input stream
|
||||
*/
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Acts as an interface to a Zip file from if-archive for the Lurking Horror or
|
||||
* Sherlock. Any file which ends with a number and '.snd' will be accessible as
|
||||
* 'sound<num>.snd' in the outer Glk layer
|
||||
*/
|
||||
class SoundZip : public Common::Archive {
|
||||
private:
|
||||
Common::Archive *_zip;
|
||||
typedef Common::HashMap<Common::Path, Common::Path, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> FileMap;
|
||||
FileMap _filenames;
|
||||
private:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
SoundZip(Common::Archive *zip);
|
||||
public:
|
||||
/**
|
||||
* Checks for a sound subfolder, and if so, instantiates the class for it
|
||||
*/
|
||||
static void check(const Common::FSNode &gameDir, Story story);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~SoundZip() override;
|
||||
|
||||
/**
|
||||
* Check if a member with the given name is present in the Archive.
|
||||
* Patterns are not allowed, as this is meant to be a quick File::exists()
|
||||
* replacement.
|
||||
*/
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Add all members of the Archive to list.
|
||||
* Must only append to list, and not remove elements from it.
|
||||
*
|
||||
* @return the number of names added to list
|
||||
*/
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
|
||||
/**
|
||||
* Returns a ArchiveMember representation of the given file.
|
||||
*/
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Create a stream bound to a member with the specified name in the
|
||||
* archive. If no member with this name exists, 0 is returned.
|
||||
* @return the newly created input stream
|
||||
*/
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
444
engines/glk/zcode/windows.cpp
Normal file
444
engines/glk/zcode/windows.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/windows.h"
|
||||
#include "glk/zcode/zcode.h"
|
||||
#include "glk/window_pair.h"
|
||||
#include "glk/window_graphics.h"
|
||||
#include "glk/window_text_buffer.h"
|
||||
#include "glk/window_text_grid.h"
|
||||
#include "glk/conf.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
Windows::Windows() : _lower(_windows[0]), _upper(_windows[1]), _background(nullptr), _cwin(0) {
|
||||
}
|
||||
|
||||
size_t Windows::size() const {
|
||||
return (g_vm->h_version < 6) ? 2 : 8;
|
||||
}
|
||||
|
||||
Window &Windows::operator[](uint idx) {
|
||||
assert(idx < size());
|
||||
return _windows[idx];
|
||||
}
|
||||
|
||||
void Windows::setup(bool isVersion6) {
|
||||
MonoFontInfo &mi = g_vm->_conf->_monoInfo;
|
||||
|
||||
if (isVersion6) {
|
||||
// For graphic games we have a background window covering the entire screen for greater
|
||||
// flexibility of wher we draw pictures, and the lower and upper areas sit on top of them
|
||||
_background = g_vm->glk_window_open(nullptr, 0, 0, wintype_Graphics, 0);
|
||||
_background->setBackgroundColor(0xffffff);
|
||||
|
||||
Window &w = _windows[0];
|
||||
w[X_SIZE] = g_vm->h_screen_width;
|
||||
w[Y_SIZE] = g_vm->h_screen_height;
|
||||
} else {
|
||||
_lower = g_vm->glk_window_open(nullptr, 0, 0, wintype_TextBuffer, 0);
|
||||
_upper = g_vm->glk_window_open(_lower, winmethod_Above | winmethod_Fixed, 0, wintype_TextGrid, 0);
|
||||
|
||||
_lower.update();
|
||||
_upper.update();
|
||||
g_vm->glk_set_window(_lower);
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < 8; ++idx) {
|
||||
Window &w = _windows[idx];
|
||||
w._windows = this;
|
||||
w._index = idx;
|
||||
w[FONT_NUMBER] = TEXT_FONT;
|
||||
w[FONT_SIZE] = (mi._cellH << 8) | mi._cellW;
|
||||
|
||||
|
||||
PropFontInfo &pi = g_conf->_propInfo;
|
||||
w._quotes = pi._quotes;
|
||||
w._dashes = pi._quotes;
|
||||
w._spaces = pi._spaces;
|
||||
}
|
||||
}
|
||||
|
||||
void Windows::setWindow(int win) {
|
||||
_cwin = win;
|
||||
|
||||
if (_windows[_cwin]._win)
|
||||
g_vm->glk_set_window(_windows[_cwin]._win);
|
||||
}
|
||||
|
||||
void Windows::showTextWindows() {
|
||||
// For v6, drawing graphics brings them to the front (such for title screens). So check for it
|
||||
const PairWindow *pairWin = dynamic_cast<const PairWindow *>(g_vm->glk_window_get_root());
|
||||
if (g_vm->h_version == V6 && pairWin && dynamic_cast<GraphicsWindow *>(pairWin->_children.back())) {
|
||||
// Yep, it's at the forefront. So since we're now drawing text, ensure all text windows are in front of it
|
||||
for (uint idx = 0; idx < size(); ++idx) {
|
||||
if (_windows[idx]) {
|
||||
winid_t win = _windows[idx];
|
||||
if (dynamic_cast<TextWindow *>(win))
|
||||
win->bringToFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Window::Window() : _windows(nullptr), _win(nullptr), _quotes(0), _dashes(0), _spaces(0), _index(-1),
|
||||
_currFont(TEXT_FONT), _prevFont(TEXT_FONT), _tempFont(TEXT_FONT), _currStyle(0), _oldStyle(0) {
|
||||
Common::fill(_properties, _properties + TRUE_BG_COLOR + 1, 0);
|
||||
_properties[Y_POS] = _properties[X_POS] = 1;
|
||||
_properties[Y_CURSOR] = _properties[X_CURSOR] = 1;
|
||||
_properties[FONT_NUMBER] = TEXT_FONT;
|
||||
_properties[FONT_SIZE] = (8 << 8) | 8;
|
||||
}
|
||||
|
||||
void Window::update() {
|
||||
assert(_win);
|
||||
int cellW = (g_vm->h_version < V5) ? g_vm->h_font_width : 1;
|
||||
int cellH = (g_vm->h_version < V5) ? g_vm->h_font_height : 1;
|
||||
|
||||
_properties[X_POS] = _win->_bbox.left / cellW + 1;
|
||||
_properties[Y_POS] = _win->_bbox.top / cellH + 1;
|
||||
_properties[X_SIZE] = _win->_bbox.width() / cellW;
|
||||
_properties[Y_SIZE] = _win->_bbox.height() / cellH;
|
||||
|
||||
Point pt = _win->getCursor();
|
||||
_properties[X_CURSOR] = (g_vm->h_version != V6) ? pt.x + 1 : pt.x / cellW + 1;
|
||||
_properties[Y_CURSOR] = (g_vm->h_version != V6) ? pt.y + 1 : pt.y / cellH + 1;
|
||||
|
||||
TextBufferWindow *win = dynamic_cast<TextBufferWindow *>(_win);
|
||||
_properties[LEFT_MARGIN] = (win ? win->_ladjw : 0) / cellW;
|
||||
_properties[RIGHT_MARGIN] = (win ? win->_radjw : 0) / cellW;
|
||||
_properties[FONT_SIZE] = (g_conf->_monoInfo._cellH << 8) | g_conf->_monoInfo._cellW;
|
||||
}
|
||||
|
||||
Window &Window::operator=(winid_t win) {
|
||||
_win = win;
|
||||
|
||||
// Set the screen colors
|
||||
if (win)
|
||||
win->_stream->setZColors(g_vm->_defaultForeground, g_vm->_defaultBackground);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Window::ensureTextWindow() {
|
||||
if (_win) {
|
||||
// There's a window present, so make sure it's textual
|
||||
if (!dynamic_cast<TextWindow *>(_win)) {
|
||||
g_vm->glk_window_close(_win);
|
||||
_win = nullptr;
|
||||
createGlkWindow();
|
||||
}
|
||||
} else {
|
||||
createGlkWindow();
|
||||
}
|
||||
|
||||
_windows->showTextWindows();
|
||||
}
|
||||
|
||||
void Window::setSize(const Point &newSize) {
|
||||
checkRepositionLower();
|
||||
|
||||
_properties[X_SIZE] = newSize.x;
|
||||
_properties[Y_SIZE] = newSize.y;
|
||||
|
||||
setSize();
|
||||
}
|
||||
|
||||
void Window::setSize() {
|
||||
if (_win) {
|
||||
Point newSize(_properties[X_SIZE], _properties[Y_SIZE]);
|
||||
if (g_vm->h_version < V5) {
|
||||
newSize.x *= g_conf->_monoInfo._cellW;
|
||||
newSize.y *= g_conf->_monoInfo._cellH;
|
||||
}
|
||||
|
||||
_win->setSize(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setPosition(const Point &newPos) {
|
||||
checkRepositionLower();
|
||||
|
||||
_properties[X_POS] = newPos.x;
|
||||
_properties[Y_POS] = newPos.y;
|
||||
|
||||
setPosition();
|
||||
}
|
||||
|
||||
void Window::setPosition() {
|
||||
if (_win) {
|
||||
Point newPos(_properties[X_POS] - 1, _properties[Y_POS] - 1);
|
||||
if (g_vm->h_version < V5) {
|
||||
newPos.x *= g_conf->_monoInfo._cellW;
|
||||
newPos.y *= g_conf->_monoInfo._cellH;
|
||||
}
|
||||
|
||||
_win->setPosition(newPos);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setCursor(const Point &newPos) {
|
||||
int x = newPos.x, y = newPos.y;
|
||||
|
||||
if (y < 0) {
|
||||
// Cursor on/off
|
||||
if (y == -2)
|
||||
g_vm->_events->showMouseCursor(true);
|
||||
else if (y == -1)
|
||||
g_vm->_events->showMouseCursor(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!x || !y) {
|
||||
update();
|
||||
|
||||
if (!x)
|
||||
x = _properties[X_CURSOR];
|
||||
if (!y)
|
||||
y = _properties[Y_CURSOR];
|
||||
}
|
||||
|
||||
_properties[X_CURSOR] = x;
|
||||
_properties[Y_CURSOR] = y;
|
||||
|
||||
setCursor();
|
||||
}
|
||||
|
||||
void Window::setCursor() {
|
||||
if (dynamic_cast<TextGridWindow *>(_win)) {
|
||||
g_vm->glk_window_move_cursor(_win, (_properties[X_CURSOR] - 1),
|
||||
(_properties[Y_CURSOR] - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void Window::clear() {
|
||||
if (_win)
|
||||
g_vm->glk_window_clear(_win);
|
||||
|
||||
if (_windows->_background) {
|
||||
Rect r = getBounds();
|
||||
_windows->_background->fillRect(g_conf->_windowColor, r);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::updateColors() {
|
||||
if (_win)
|
||||
_win->_stream->setZColors(_properties[TRUE_FG_COLOR], _properties[TRUE_BG_COLOR]);
|
||||
}
|
||||
|
||||
void Window::updateColors(uint fore, uint back) {
|
||||
_properties[TRUE_FG_COLOR] = fore;
|
||||
_properties[TRUE_BG_COLOR] = back;
|
||||
updateColors();
|
||||
}
|
||||
|
||||
uint Window::setFont(uint font) {
|
||||
int result = 0;
|
||||
|
||||
switch (font) {
|
||||
case PREVIOUS_FONT:
|
||||
// previous font
|
||||
_tempFont = _currFont;
|
||||
_currFont = _prevFont;
|
||||
_prevFont = _tempFont;
|
||||
setStyle();
|
||||
result = _currFont;
|
||||
break;
|
||||
|
||||
case TEXT_FONT:
|
||||
case GRAPHICS_FONT:
|
||||
case FIXED_WIDTH_FONT:
|
||||
_prevFont = _currFont;
|
||||
_currFont = font;
|
||||
setStyle();
|
||||
result = _prevFont;
|
||||
break;
|
||||
|
||||
case PICTURE_FONT: // picture font, undefined per 1.1
|
||||
default: // unavailable
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
PropFontInfo &pi = g_conf->_propInfo;
|
||||
if (_currFont == GRAPHICS_FONT) {
|
||||
_quotes = pi._quotes;
|
||||
_dashes = pi._dashes;
|
||||
_spaces = pi._spaces;
|
||||
pi._quotes = 0;
|
||||
pi._dashes = 0;
|
||||
pi._spaces = 0;
|
||||
} else {
|
||||
pi._quotes = _quotes;
|
||||
pi._dashes = _dashes;
|
||||
pi._spaces = _spaces;
|
||||
}
|
||||
|
||||
_properties[FONT_NUMBER] = font;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Window::setStyle(int style) {
|
||||
if (style == 0)
|
||||
_currStyle = 0;
|
||||
else if (style != -1)
|
||||
// not tickle time
|
||||
_currStyle |= style;
|
||||
|
||||
if (g_vm->h_flags & FIXED_FONT_FLAG || _currFont == FIXED_WIDTH_FONT || _currFont == GRAPHICS_FONT)
|
||||
style = _currStyle | FIXED_WIDTH_STYLE;
|
||||
else
|
||||
style = _currStyle;
|
||||
|
||||
if (g_vm->gos_linepending && _windows->currWin() == g_vm->gos_linewin)
|
||||
return;
|
||||
|
||||
_currStyle = style;
|
||||
updateStyle();
|
||||
}
|
||||
|
||||
void Window::updateStyle() {
|
||||
if (!_win)
|
||||
return;
|
||||
|
||||
uint style = _currStyle;
|
||||
if (style & REVERSE_STYLE)
|
||||
setReverseVideo(true);
|
||||
|
||||
if (style & FIXED_WIDTH_STYLE) {
|
||||
if (_currFont == GRAPHICS_FONT)
|
||||
_win->_stream->setStyle(style_User1); // character graphics
|
||||
else if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
|
||||
_win->_stream->setStyle(style_BlockQuote); // monoz
|
||||
else if (style & EMPHASIS_STYLE)
|
||||
_win->_stream->setStyle(style_Alert); // monoi
|
||||
else if (style & BOLDFACE_STYLE)
|
||||
_win->_stream->setStyle(style_Subheader); // monob
|
||||
else
|
||||
_win->_stream->setStyle(style_Preformatted); // monor
|
||||
|
||||
MonoFontInfo &fi = g_vm->_conf->_monoInfo;
|
||||
_properties[FONT_SIZE] = (fi._cellH << 8) | fi._cellW;
|
||||
} else {
|
||||
if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
|
||||
_win->_stream->setStyle(style_Note); // propz
|
||||
else if (style & EMPHASIS_STYLE)
|
||||
_win->_stream->setStyle(style_Emphasized); // propi
|
||||
else if (style & BOLDFACE_STYLE)
|
||||
_win->_stream->setStyle(style_Header); // propb
|
||||
else
|
||||
_win->_stream->setStyle(style_Normal); // propr
|
||||
|
||||
PropFontInfo &fi = g_vm->_conf->_propInfo;
|
||||
_properties[FONT_SIZE] = (fi._cellH << 8) | fi._cellW;
|
||||
}
|
||||
|
||||
if (_currStyle == 0)
|
||||
setReverseVideo(false);
|
||||
}
|
||||
|
||||
Rect Window::getBounds() const {
|
||||
if (_win)
|
||||
return _win->_bbox;
|
||||
|
||||
if (g_vm->h_version < V5)
|
||||
return Rect((_properties[X_POS] - 1) * g_vm->h_font_width, (_properties[Y_POS] - 1) * g_vm->h_font_height,
|
||||
(_properties[X_POS] - 1 + _properties[X_SIZE]) * g_vm->h_font_width,
|
||||
(_properties[Y_POS] - 1 + _properties[Y_SIZE]) * g_vm->h_font_height);
|
||||
|
||||
return Rect(_properties[X_POS] - 1, _properties[Y_POS] - 1, _properties[X_POS] - 1 + _properties[X_SIZE],
|
||||
_properties[Y_POS] - 1 + _properties[Y_SIZE]);
|
||||
}
|
||||
|
||||
void Window::setReverseVideo(bool reverse) {
|
||||
_win->_stream->setReverseVideo(reverse);
|
||||
}
|
||||
|
||||
void Window::createGlkWindow() {
|
||||
if (g_vm->h_version == V6)
|
||||
_windows->showTextWindows();
|
||||
|
||||
// Create a new window
|
||||
if (_index != 0 || (_currStyle & FIXED_WIDTH_STYLE)) {
|
||||
// Text grid window
|
||||
_win = g_vm->glk_window_open(g_vm->glk_window_get_root(),
|
||||
winmethod_Arbitrary | winmethod_Fixed, 0, wintype_TextGrid, 0);
|
||||
} else {
|
||||
// text buffer window
|
||||
_win = g_vm->glk_window_open(g_vm->glk_window_get_root(),
|
||||
winmethod_Arbitrary | winmethod_Fixed, 0, wintype_TextBuffer, 0);
|
||||
}
|
||||
|
||||
updateStyle();
|
||||
setSize();
|
||||
setPosition();
|
||||
setCursor();
|
||||
|
||||
g_vm->glk_set_window(_win);
|
||||
}
|
||||
|
||||
const uint &Window::getProperty(WindowProperty propType) {
|
||||
if (_win)
|
||||
update();
|
||||
|
||||
return _properties[propType];
|
||||
}
|
||||
|
||||
void Window::setProperty(WindowProperty propType, uint value) {
|
||||
switch (propType) {
|
||||
case TRUE_FG_COLOR:
|
||||
case TRUE_BG_COLOR:
|
||||
_properties[propType] = value;
|
||||
updateColors();
|
||||
break;
|
||||
|
||||
default:
|
||||
_properties[propType] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void Window::checkRepositionLower() {
|
||||
if (&_windows->_lower == this && _win) {
|
||||
PairWindow *parent = dynamic_cast<PairWindow *>(_win->_parent);
|
||||
if (!parent)
|
||||
error("Parent was not a pair window");
|
||||
|
||||
// Ensure the parent pair window is flagged as having children at arbitrary positions,
|
||||
// just in case it isn't already
|
||||
parent->_dir = winmethod_Arbitrary;
|
||||
}
|
||||
}
|
||||
|
||||
bool Window::imageDraw(uint image, ImageAlign align, int val) {
|
||||
ensureTextWindow();
|
||||
return g_vm->glk_image_draw(_win, image, align, val);
|
||||
}
|
||||
|
||||
bool Window::imageDrawScaled(uint image, int val1, int val2, uint width, uint height) {
|
||||
ensureTextWindow();
|
||||
return g_vm->glk_image_draw_scaled(_win, image, val1, val2, width, height);
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
297
engines/glk/zcode/windows.h
Normal file
297
engines/glk/zcode/windows.h
Normal file
@@ -0,0 +1,297 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_WINDOWS
|
||||
#define GLK_ZCODE_WINDOWS
|
||||
|
||||
#include "glk/windows.h"
|
||||
#include "glk/zcode/frotz_types.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
#include "glk/windows.h"
|
||||
#include "glk/utils.h"
|
||||
|
||||
enum WindowProperty {
|
||||
Y_POS = 0, X_POS = 1, Y_SIZE = 2, X_SIZE = 3, Y_CURSOR = 4, X_CURSOR = 5,
|
||||
LEFT_MARGIN = 6, RIGHT_MARGIN = 7, NEWLINE_INTERRUPT = 8, INTERRUPT_COUNTDOWN = 9,
|
||||
TEXT_STYLE = 10, COLOUR_DATA = 11, FONT_NUMBER = 12, FONT_SIZE = 13, ATTRIBUTES = 14,
|
||||
LINE_COUNT = 15, TRUE_FG_COLOR = 16, TRUE_BG_COLOR = 17
|
||||
};
|
||||
|
||||
class Windows;
|
||||
|
||||
/**
|
||||
* Represents one of the virtual windows
|
||||
*/
|
||||
class Window {
|
||||
friend class Windows;
|
||||
|
||||
/**
|
||||
* Stub class for accessing window properties via the square brackets operator
|
||||
*/
|
||||
class PropertyAccessor {
|
||||
private:
|
||||
Window *_owner;
|
||||
WindowProperty _prop;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
PropertyAccessor(Window *owner, WindowProperty prop) : _owner(owner), _prop(prop) {}
|
||||
|
||||
/**
|
||||
* Get
|
||||
*/
|
||||
operator uint() const {
|
||||
return _owner->getProperty(_prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set
|
||||
*/
|
||||
PropertyAccessor &operator=(uint val) {
|
||||
_owner->setProperty(_prop, val);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
private:
|
||||
Windows *_windows;
|
||||
int _index;
|
||||
winid_t _win;
|
||||
uint _properties[TRUE_BG_COLOR + 1];
|
||||
private:
|
||||
/**
|
||||
* Get a property value
|
||||
*/
|
||||
const uint &getProperty(WindowProperty propType);
|
||||
|
||||
/**
|
||||
* Set a property value
|
||||
*/
|
||||
void setProperty(WindowProperty propType, uint value);
|
||||
|
||||
/**
|
||||
* Called when trying to reposition or resize windows. Does special handling for the lower window
|
||||
*/
|
||||
void checkRepositionLower();
|
||||
|
||||
/**
|
||||
* Updates the local window properties based on an attached Glk window
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Creates a new Glk window to attach to the window
|
||||
*/
|
||||
void createGlkWindow();
|
||||
|
||||
/**
|
||||
* Updates the current font/style
|
||||
*/
|
||||
void updateStyle();
|
||||
|
||||
/**
|
||||
* Get the bounding area for the window
|
||||
*/
|
||||
Rect getBounds() const;
|
||||
public:
|
||||
int _currFont;
|
||||
int _prevFont;
|
||||
int _tempFont;
|
||||
int _currStyle;
|
||||
int _oldStyle;
|
||||
int _quotes;
|
||||
int _dashes;
|
||||
int _spaces;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Window();
|
||||
|
||||
/**
|
||||
* Assignment operator
|
||||
*/
|
||||
Window &operator=(winid_t win);
|
||||
|
||||
/**
|
||||
* Cast operator for getting a Glk window
|
||||
*/
|
||||
operator winid_t() const {
|
||||
assert(_win);
|
||||
return _win;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality operator
|
||||
*/
|
||||
inline bool operator==(const Window &rhs) { return this == &rhs; }
|
||||
|
||||
/**
|
||||
* Cast operator for testing if the window has a proper Glk window attached to it
|
||||
*/
|
||||
operator bool() const { return _win != nullptr; }
|
||||
|
||||
/**
|
||||
* Property accessor
|
||||
*/
|
||||
PropertyAccessor operator[](WindowProperty propType) { return PropertyAccessor(this, propType); }
|
||||
|
||||
/**
|
||||
* Ensures that the underlying window is a Glk text window
|
||||
*/
|
||||
void ensureTextWindow();
|
||||
|
||||
/**
|
||||
* Set the window size
|
||||
*/
|
||||
void setSize(const Point &newSize);
|
||||
|
||||
/**
|
||||
* Copys a window's size to the underlying Glk one, if present
|
||||
*/
|
||||
void setSize();
|
||||
|
||||
/**
|
||||
* Set the position of a window
|
||||
*/
|
||||
void setPosition(const Point &newPos);
|
||||
|
||||
/**
|
||||
* Copys a window's position to the underlying Glk one, if present
|
||||
*/
|
||||
void setPosition();
|
||||
|
||||
/**
|
||||
* Set the cursor position
|
||||
*/
|
||||
void setCursor(const Point &newPos);
|
||||
|
||||
/**
|
||||
* Copys a window's position to the underlying Glk one, if present
|
||||
*/
|
||||
void setCursor();
|
||||
|
||||
/**
|
||||
* Clear the window
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Update colors for the window
|
||||
*/
|
||||
void updateColors();
|
||||
|
||||
/**
|
||||
* Update colors for the window
|
||||
*/
|
||||
void updateColors(uint fore, uint back);
|
||||
|
||||
/**
|
||||
* Set the font
|
||||
*/
|
||||
uint setFont(uint font);
|
||||
|
||||
/**
|
||||
* Set the textstyle
|
||||
*/
|
||||
void setStyle(int style = -1);
|
||||
|
||||
/**
|
||||
* Set reverse video
|
||||
*/
|
||||
void setReverseVideo(bool reverse);
|
||||
|
||||
/**
|
||||
* Draw an image
|
||||
*/
|
||||
bool imageDraw(uint image, ImageAlign align, int val);
|
||||
|
||||
/**
|
||||
* Draw a scaled image
|
||||
*/
|
||||
bool imageDrawScaled(uint image, int val1, int val2, uint width, uint height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Windows manager
|
||||
*/
|
||||
class Windows {
|
||||
private:
|
||||
Window _windows[8];
|
||||
public:
|
||||
winid_t _background;
|
||||
Window &_lower;
|
||||
Window &_upper;
|
||||
int _cwin;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Windows();
|
||||
|
||||
/**
|
||||
* Returns the number of allowable windows
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
/**
|
||||
* Array access
|
||||
*/
|
||||
Window &operator[](uint idx);
|
||||
|
||||
/**
|
||||
* Setup the screen
|
||||
*/
|
||||
void setup(bool isVersion6);
|
||||
|
||||
/**
|
||||
* Set current window
|
||||
*/
|
||||
void setWindow(int win);
|
||||
|
||||
/**
|
||||
* Get the current window
|
||||
*/
|
||||
Window &currWin() {
|
||||
return _windows[_cwin];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current window pointer
|
||||
*/
|
||||
winid_t glkWin() const {
|
||||
assert(_windows[_cwin]._win);
|
||||
return _windows[_cwin]._win;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places any text windows in front of the background in V6 games
|
||||
*/
|
||||
void showTextWindows();
|
||||
};
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
151
engines/glk/zcode/zcode.cpp
Normal file
151
engines/glk/zcode/zcode.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/zcode/zcode.h"
|
||||
#include "glk/zcode/frotz_types.h"
|
||||
#include "glk/zcode/screen.h"
|
||||
#include "glk/zcode/quetzal.h"
|
||||
#include "engines/util.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
ZCode *g_vm;
|
||||
|
||||
ZCode::ZCode(OSystem *syst, const GlkGameDescription &gameDesc) :
|
||||
Processor(syst, gameDesc) {
|
||||
g_vm = this;
|
||||
}
|
||||
|
||||
ZCode::~ZCode() {
|
||||
reset_memory();
|
||||
}
|
||||
|
||||
void ZCode::initGraphicsMode() {
|
||||
_gameFile.seek(0);
|
||||
byte version = _gameFile.readByte();
|
||||
|
||||
if (version == 6) {
|
||||
// The V6 games have graphics that expect 320x200 mode
|
||||
Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
|
||||
initGraphics(320, 200, &pixelFormat);
|
||||
} else {
|
||||
GlkEngine::initGraphicsMode();
|
||||
}
|
||||
}
|
||||
|
||||
Screen *ZCode::createScreen() {
|
||||
return new FrotzScreen();
|
||||
}
|
||||
|
||||
void ZCode::runGame() {
|
||||
story_fp = &_gameFile;
|
||||
initialize();
|
||||
|
||||
// If save was selected from the launcher, handle loading it
|
||||
int saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
|
||||
if (saveSlot != -1) {
|
||||
int loadResult = loadGameState(saveSlot).getCode() == Common::kNoError ? 2 : -1;
|
||||
|
||||
if (h_version <= V3)
|
||||
branch(loadResult);
|
||||
else
|
||||
store(loadResult);
|
||||
}
|
||||
|
||||
// Game loop
|
||||
interpret();
|
||||
|
||||
if (!shouldQuit()) {
|
||||
flush_buffer();
|
||||
glk_exit();
|
||||
}
|
||||
}
|
||||
|
||||
void ZCode::initialize() {
|
||||
// Call process initialization
|
||||
Processor::initialize();
|
||||
|
||||
// Restart the game
|
||||
z_restart();
|
||||
}
|
||||
|
||||
Common::Error ZCode::loadGameState(int slot) {
|
||||
FileReference ref(slot, "", fileusage_SavedGame | fileusage_TextMode);
|
||||
|
||||
strid_t file = _streams->openFileStream(&ref, filemode_Read);
|
||||
if (file == nullptr)
|
||||
return Common::kReadingFailed;
|
||||
|
||||
Quetzal q(story_fp);
|
||||
bool success = q.restore(*file, this) == 2;
|
||||
|
||||
if (success) {
|
||||
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 {
|
||||
error("Error reading save file");
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::Error ZCode::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
Common::String msg;
|
||||
FileReference ref(slot, desc, fileusage_BinaryMode | fileusage_SavedGame);
|
||||
|
||||
strid_t file = _streams->openFileStream(&ref, filemode_Write);
|
||||
if (file == nullptr)
|
||||
return Common::kWritingFailed;
|
||||
|
||||
Quetzal q(story_fp);
|
||||
bool success = q.save(*file, this, desc);
|
||||
file->close();
|
||||
|
||||
if (!success)
|
||||
print_string_uni(_("Error writing save file\n").u32_str());
|
||||
|
||||
return Common::kNoError;
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
111
engines/glk/zcode/zcode.h
Normal file
111
engines/glk/zcode/zcode.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ZCODE_ZCODE
|
||||
#define GLK_ZCODE_ZCODE
|
||||
|
||||
#include "glk/zcode/processor.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace ZCode {
|
||||
|
||||
class FrotzScreen;
|
||||
|
||||
/**
|
||||
* Frotz interpreter for Z-code games
|
||||
*/
|
||||
class ZCode : public Processor {
|
||||
friend class FrotzScreen;
|
||||
protected:
|
||||
/**
|
||||
* Setup the video mode
|
||||
*/
|
||||
void initGraphicsMode() override;
|
||||
|
||||
/**
|
||||
* Create the screen class
|
||||
*/
|
||||
Screen *createScreen() override;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
ZCode(OSystem *syst, const GlkGameDescription &gameDesc);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~ZCode() override;
|
||||
|
||||
/**
|
||||
* Initialization
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Returns the running interpreter type
|
||||
*/
|
||||
InterpreterType getInterpreterType() const override { return INTERPRETER_ZCODE; }
|
||||
|
||||
/**
|
||||
* Execute the game
|
||||
*/
|
||||
void runGame() override;
|
||||
|
||||
/**
|
||||
* Indicates whether an autosave can currently be saved.
|
||||
*/
|
||||
bool canSaveAutosaveCurrently() override {
|
||||
/* ZCode saves also include the execution stack.
|
||||
* So I don't know how to do autosaves in the background
|
||||
* without ending up with an invalid stack state
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a savegame from a given slot
|
||||
*/
|
||||
Common::Error loadGameState(int slot) override;
|
||||
|
||||
/**
|
||||
* Save the game to a given slot
|
||||
*/
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
|
||||
/**
|
||||
* Loading method not used for Frotz sub-engine
|
||||
*/
|
||||
Common::Error readSaveData(Common::SeekableReadStream *rs) override { return Common::kReadingFailed; }
|
||||
|
||||
/**
|
||||
* Saving method not used for Frotz sub-engine
|
||||
*/
|
||||
Common::Error writeGameData(Common::WriteStream *ws) override { return Common::kWritingFailed; }
|
||||
|
||||
};
|
||||
|
||||
extern ZCode *g_vm;
|
||||
|
||||
} // End of namespace ZCode
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user