aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/glk/magnetic/emu.cpp267
-rw-r--r--engines/glk/magnetic/magnetic.cpp24
-rw-r--r--engines/glk/magnetic/magnetic.h100
-rw-r--r--engines/glk/magnetic/magnetic_types.h136
-rw-r--r--engines/glk/module.mk1
5 files changed, 524 insertions, 4 deletions
diff --git a/engines/glk/magnetic/emu.cpp b/engines/glk/magnetic/emu.cpp
new file mode 100644
index 0000000000..b3d6445ed3
--- /dev/null
+++ b/engines/glk/magnetic/emu.cpp
@@ -0,0 +1,267 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/magnetic/magnetic.h"
+
+namespace Glk {
+namespace Magnetic {
+
+static const char *no_hints = "[Hints are not available.]\n";
+static const char *not_supported = "[This function is not supported.]\n";
+
+int Magnetic::ms_init(bool restarting) {
+ byte header[42], header2[8];
+ uint32 i, dict_size, string2_size, code_size, dec;
+
+ ms_stop();
+
+ if (restarting) {
+ if (!restart)
+ return 0;
+ else {
+ memcpy(code, restart, undo_size);
+ undo_stat[0] = undo_stat[1] = 0;
+ ms_showpic(0, 0);
+ }
+ } else {
+ undo_stat[0] = undo_stat[1] = 0;
+
+ if (_gameFile.read(header, 42) != 42 || READ_BE_UINT32(header) != MKTAG('M', 'a', 'S', 'c')
+ || READ_LE_UINT32(header + 8) != 42)
+ return 0;
+
+ ms_freemem();
+ version = header[13];
+ code_size = READ_LE_UINT32(header + 14);
+ string_size = READ_LE_UINT32(header + 18);
+ string2_size = READ_LE_UINT32(header + 22);
+ dict_size = READ_LE_UINT32(header + 26);
+ undo_size = READ_LE_UINT32(header + 34);
+ undo_pc = READ_LE_UINT32(header + 38);
+
+ if ((version < 4) && (code_size < 65536))
+ mem_size = 65536;
+ else
+ mem_size = code_size;
+
+ sd = (byte)((dict_size != 0L) ? 1 : 0); // if (sd) => separate dict
+
+ if (!(code = new byte[mem_size]) || !(string2 = new byte[string2_size]) ||
+ !(restart = new byte[undo_size]) || (sd &&
+ !(dict = new byte[dict_size]))) {
+ ms_freemem();
+ return 0;
+ }
+ if (string_size > MAX_STRING_SIZE) {
+ if (!(string = new byte[MAX_STRING_SIZE]) ||
+ !(string3 = new byte[string_size - MAX_STRING_SIZE])) {
+ ms_freemem();
+ return 0;
+ }
+ } else {
+ if (!(string = new byte[string_size])) {
+ ms_freemem();
+ return 0;
+ }
+ }
+ if (!(undo[0] = new byte[undo_size]) || !(undo[1] = new byte[undo_size])) {
+ ms_freemem();
+ return 0;
+ }
+ if (_gameFile.read(code, code_size) != code_size) {
+ ms_freemem();
+ return 0;
+ }
+
+ memcpy(restart, code, undo_size); // fast restarts
+ if (string_size > MAX_STRING_SIZE) {
+ if (_gameFile.read(string, MAX_STRING_SIZE) != MAX_STRING_SIZE) {
+ ms_freemem();
+ return 0;
+ }
+ if (_gameFile.read(string3, string_size - MAX_STRING_SIZE) != (string_size - MAX_STRING_SIZE)) {
+ ms_freemem();
+ return 0;
+ }
+ } else {
+ if (_gameFile.read(string, string_size) != string_size) {
+ ms_freemem();
+ return 0;
+ }
+ }
+ if (_gameFile.read(string2, string2_size) != string2_size) {
+ ms_freemem();
+ return 0;
+ }
+ if (sd && _gameFile.read(dict, dict_size) != dict_size) {
+ ms_freemem();
+ return 0;
+ }
+
+ dec = READ_LE_UINT32(header + 30);
+ if (dec >= string_size) {
+ decode_table = string2 + dec - string_size;
+ } else {
+ if (dec >= MAX_STRING_SIZE)
+ decode_table = string3 + dec - MAX_STRING_SIZE;
+ else
+ decode_table = string + dec;
+ }
+ }
+
+ for (i = 0; i < 8; i++)
+ dreg[i] = areg[i] = 0;
+ write_reg(8 + 7, 2, 0xfffe); // Stack-pointer, -2 due to MS-DOS segments
+ pc = 0;
+ zflag = nflag = cflag = vflag = 0;
+ i_count = 0;
+ running = 1;
+
+ if (restarting)
+ return (byte)(gfx_buf ? 2 : 1); // Restarted
+
+ if (version == 4) {
+ // Try loading a hint file
+ if (_hintFile.isOpen()) {
+ _hintFile.seek(0);
+
+ if (_hintFile.readUint32BE() == MKTAG('M', 'a', 'H', 't')) {
+ byte buf[8];
+ uint16 i, j, blkcnt, elcnt, ntype, elsize, conidx;
+
+ // Allocate memory for hints
+ hints = new ms_hint[MAX_HINTS];
+ hint_contents = new byte[MAX_HCONTENTS];
+
+ if ((hints != 0) && (hint_contents != 0)) {
+ // Read number of blocks
+ blkcnt = _hintFile.readUint16LE();
+
+ conidx = 0;
+ for (i = 0; i < blkcnt; i++) {
+ // Read number of elements
+ elcnt = _hintFile.readUint16LE();
+ hints[i].elcount = elcnt;
+
+ // Read node type
+ ntype = _hintFile.readUint16LE();
+ hints[i].nodetype = ntype;
+ hints[i].content = hint_contents + conidx;
+
+ for (j = 0; j < elcnt; j++) {
+ elsize = _hintFile.readUint16LE();
+ if (_hintFile.read(hint_contents + conidx, elsize) != elsize || _hintFile.eos())
+ return 0;
+ hint_contents[conidx + elsize - 1] = '\0';
+
+ conidx += elsize;
+ }
+
+ // Do we need a jump table?
+ if (ntype == 1) {
+ for (j = 0; j < elcnt; j++) {
+ hints[i].links[j] = _hintFile.readUint16LE();
+ }
+ }
+
+ // Read the parent block
+ hints[i].parent = _hintFile.readUint16LE();
+
+ }
+ } else {
+ delete[] hints;
+ delete[] hint_contents;
+ hints = nullptr;
+ hint_contents = nullptr;
+ }
+ }
+ }
+
+ // Try loading a music file
+ if (_sndFile.isOpen() && _sndFile.size() >= 8) {
+ _sndFile.seek(0);
+
+ if (_sndFile.readUint32BE() != MKTAG('M', 'a', 'S', 'd'))
+ return 0;
+
+ init_snd(_sndFile.readUint32LE());
+ }
+ }
+
+ if (!_gfxFile.isOpen() || _gfxFile.size() < 8)
+ return 1;
+ _gfxFile.seek(0);
+ _gfxFile.read(header2, 8);
+
+ if (version < 4 && READ_BE_UINT32(header2) == MKTAG('M', 'a', 'P', 'i'))
+ return init_gfx1(header2);
+ else if (version == 4 && READ_BE_UINT32(header2) == MKTAG('M', 'a', 'P', '2'))
+ return init_gfx2(header2);
+
+ return 1;
+}
+
+void Magnetic::ms_freemem() {
+ delete[] code;
+ delete[] string;
+ delete[] string2;
+ delete[] string3;
+ delete[] dict;
+ delete[] undo[0];
+ delete[] undo[1];
+ delete[] restart;
+ code = string = string2 = string3 = dict = nullptr;
+ undo[0] = undo[1] = restart = nullptr;
+
+ delete[] gfx_data;
+ delete[] gfx_buf;
+ delete[] gfx2_hdr;
+ delete[] gfx2_buf;
+ gfx_data = gfx_buf = gfx2_hdr = gfx2_buf = 0;
+
+ gfx_fp.close();
+
+ gfx2_name.clear();
+ gfx_ver = 0;
+ gfxtable = table_dist = 0;
+
+ pos_table_size = 0;
+ command_index = 0;
+ anim_repeat = 0;
+ pos_table_index = -1;
+ pos_table_max = -1;
+
+ lastchar = 0;
+ delete[] hints;
+ delete[] hint_contents;
+ hints = nullptr;
+ hint_contents = nullptr;
+
+ delete[] snd_hdr;
+ delete[] snd_buf;
+ snd_hdr = nullptr;
+ snd_hsize = 0;
+ snd_buf = nullptr;
+}
+
+} // End of namespace Magnetic
+} // End of namespace Glk
diff --git a/engines/glk/magnetic/magnetic.cpp b/engines/glk/magnetic/magnetic.cpp
index 9bfdc4825c..c658b8c5fd 100644
--- a/engines/glk/magnetic/magnetic.cpp
+++ b/engines/glk/magnetic/magnetic.cpp
@@ -28,7 +28,27 @@ namespace Glk {
namespace Magnetic {
Magnetic::Magnetic(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
- vm_exited_cleanly(false) {
+
+ vm_exited_cleanly(false), dlimit(0xffffffff), slimit(0xffffffff), log_on(0),
+ i_count(0), string_size(0), rseed(0), pc(0), arg1i(0), mem_size(0), properties(0), fl_sub(0),
+ fl_tab(0), fl_size(0), fp_tab(0), fp_size(0), zflag(0), nflag(0), cflag(0), vflag(0), byte1(0),
+ byte2(0), regnr(0), admode(0), opsize(0), arg1(nullptr), arg2(nullptr), is_reversible(0),
+ lastchar(0), version(0), sd(0), decode_table(nullptr), restart(nullptr), code(nullptr),
+ string(nullptr), string2(nullptr), string3(nullptr), dict(nullptr), quick_flag(0), gfx_ver(0),
+ gfx_buf(nullptr), gfx_data(nullptr), gfx2_hdr(0), gfx2_buf(nullptr), gfx2_hsize(0),
+ snd_buf(nullptr), snd_hdr(nullptr), snd_hsize(0), undo_pc(0), undo_size(0),
+ gfxtable(0), table_dist(0), v4_id(0), next_table(1), pos_table_size(0),
+ command_table(nullptr), command_index(-1), pos_table_index(-1), pos_table_max(-1),
+ anim_repeat(0) {
+
+ // Emu fields
+ undo[0] = undo[1] = nullptr;
+ undo_stat[0] = undo_stat[1] = 0;
+ Common::fill(&dreg[0], &dreg[8], 0);
+ Common::fill(&areg[0], &areg[8], 0);
+ Common::fill(&tmparg[0], &tmparg[4], 0);
+ Common::fill(&undo_regs[0][0], &undo_regs[2][18], 0),
+ Common::fill(&pos_table_count[0], &pos_table_count[MAX_POSITIONS], 0);
}
void Magnetic::runGame() {
@@ -60,7 +80,7 @@ bool Magnetic::is_gamefile_valid() {
}
// We support version 2.0 through 3.1.*
- uint version = _gameFile.readUint32BE();
+ version = _gameFile.readUint32BE();
if (version < 0x20000) {
GUIErrorMessage(_("This Glulx file is too old a version to execute."));
return false;
diff --git a/engines/glk/magnetic/magnetic.h b/engines/glk/magnetic/magnetic.h
index 024ef51e1e..a791ca806c 100644
--- a/engines/glk/magnetic/magnetic.h
+++ b/engines/glk/magnetic/magnetic.h
@@ -20,11 +20,12 @@
*
*/
-#ifndef GLK_GLULXE
-#define GLK_GLULXE
+#ifndef GLK_MAGNETIC_MAGNETIC
+#define GLK_MAGNETIC_MAGNETIC
#include "common/scummsys.h"
#include "glk/glk_api.h"
+#include "glk/magnetic/magnetic_types.h"
namespace Glk {
namespace Magnetic {
@@ -34,12 +35,107 @@ namespace Magnetic {
*/
class Magnetic : public GlkAPI {
public:
+ Common::File _hintFile;
+ Common::File _gfxFile;
+ Common::File _sndFile;
bool vm_exited_cleanly;
+ uint dlimit, slimit;
+ int log_on;
+
+ // Emu fields
+ uint32 dreg[8], areg[8], i_count, string_size, rseed, pc, arg1i, mem_size;
+ uint16 properties, fl_sub, fl_tab, fl_size, fp_tab, fp_size;
+ byte zflag, nflag, cflag, vflag, byte1, byte2, regnr, admode, opsize;
+ byte *arg1, *arg2, is_reversible, running, tmparg[4];
+ byte lastchar, version, sd;
+ byte *decode_table, *restart, *code, *string, *string2;
+ byte *string3, *dict;
+ byte quick_flag, gfx_ver, *gfx_buf, *gfx_data;
+ byte *gfx2_hdr, *gfx2_buf;
+ Common::String gfx2_name;
+ uint16 gfx2_hsize;
+ byte *snd_buf, *snd_hdr;
+ uint16 snd_hsize;
+ Common::File gfx_fp;
+ uint32 undo_regs[2][18], undo_pc, undo_size;
+ byte *undo[2], undo_stat[2];
+ uint16 gfxtable, table_dist;
+ uint16 v4_id, next_table;
+
+ ms_hint *hints;
+ byte *hint_contents;
+ picture anim_frame_table[MAX_ANIMS];
+ uint16 pos_table_size;
+ uint16 pos_table_count[MAX_POSITIONS];
+ ms_position pos_table[MAX_POSITIONS][MAX_ANIMS];
+ byte *command_table;
+ int command_index;
+ lookup anim_table[MAX_POSITIONS];
+ int pos_table_index;
+ int pos_table_max;
+ ms_position pos_array[MAX_FRAMES];
+ byte anim_repeat;
+
+
private:
/**
* Validates the game file, and if it's invalid, displays an error dialog
*/
bool is_gamefile_valid();
+
+ /**
+ * Loads the interpreter with a game
+ * @return 0 = failure, 1 = success(without graphics or graphics failed),
+ * 2 = success(with graphics)
+ */
+ int ms_init(bool restarting = false);
+
+ /**
+ * Stops further processing of opcodes
+ */
+ void ms_stop() { running = false; }
+
+ /**
+ * Detects if game is running
+ */
+ bool ms_is_running() const { return running; }
+
+ /**
+ * Frees all allocated ressources
+ */
+ void ms_freemem();
+
+ // Graphics
+
+ /**
+ * Displays or hides a picture
+ * @param c number of image to be displayed
+ * @param mode 0 = means gfx off, 1 gfx on thumbnails, 2 gfx on normal
+ *
+ * @remarks For retrieving the raw data of a picture call ms_extract
+ */
+ void ms_showpic(int c, byte mode) {
+ // TODO
+ }
+
+ void write_reg(int i, int s, uint32 val) {
+ // TODO
+ }
+
+ // Sound
+
+ void init_snd(uint param) {
+ // TODO
+ }
+
+ byte init_gfx1(byte *header) {
+ // TODO
+ return 0;
+ }
+ byte init_gfx2(byte *header) {
+ // TODO
+ return 0;
+ }
public:
/**
* Constructor
diff --git a/engines/glk/magnetic/magnetic_types.h b/engines/glk/magnetic/magnetic_types.h
new file mode 100644
index 0000000000..b9933a3f95
--- /dev/null
+++ b/engines/glk/magnetic/magnetic_types.h
@@ -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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_MAGNETIC_TYPES
+#define GLK_MAGNETIC_TYPES
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Magnetic {
+
+#define MAX_HINTS 260
+#define MAX_HCONTENTS 30000
+
+#define MAX_POSITIONS 20
+#define MAX_ANIMS 200
+#define MAX_FRAMES 20
+#define MAX_STRING_SIZE 0xFF00
+#define MAX_PICTURE_SIZE 0xC800
+#define MAX_MUSIC_SIZE 0x4E20
+#define MAX_HITEMS 25
+
+struct lookup {
+ uint16 flag;
+ uint16 count;
+
+ lookup() : flag(0), count(0) {}
+};
+
+struct picture {
+ byte * data;
+ uint32 data_size;
+ uint16 width;
+ uint16 height;
+ uint16 wbytes;
+ uint16 plane_step;
+ byte *mask;
+
+ picture() : data(nullptr), data_size(0), width(0), height(0), wbytes(0), plane_step(0),
+ mask(nullptr) {}
+};
+
+/**
+ * Magnetic animated pictures support
+ *
+ * Note: Some of the pictures for Wonderland and the Collection Volume 1 games
+ * are animations. To detect these, pass a pointer to a type8 as the is_anim
+ * argument to ms_extract().
+ *
+ * There are two types of animated images, however almost all images are type1.
+ * A type1 image consists of four main elements:
+ * 1) A static picture which is loaded straight at the beginning
+ * 2) A set of frames with a mask. These frames are just "small pictures", which
+ * are coded like the normal static pictures. The image mask determines
+ * how the frame is removed after it has been displayed. A mask is exactly
+ * 1/8 the size of the image and holds 1 bit per pixel, saying "remove pixel"
+ * or leave pixel set when frame gets removed. It might be a good idea to check
+ * your system documentation for masking operations as your system might be
+ * able to use this mask data directly.
+ * 3) Positioning tables. These hold animation sequences consisting of commands
+ * like "Draw frame 12 at (123,456)"
+ * 4) A playback script, which determines how to use the positioning tables.
+ * These scripts are handled inside Magnetic, so no need to worry about.
+ * However, details can be found in the ms_animate() function.
+ *
+ * A type2 image is like a type1 image, but it does not have a static
+ * picture, nor does it have frame masking. It just consists of frames.
+ *
+ * How to support animations?
+ * After getting is_anim == 1 you should call ms_animate() immediately, and at
+ * regular intervals until ms_animate() returns 0. An appropriate interval
+ * between calls is about 100 milliseconds.
+ * Each call to ms_animate() will fill in the arguments with the address
+ * and size of an array of ms_position structures (see below), each of
+ * which holds an an animation frame number and x and y co-ordinates. To
+ * display the animation, decode all the animation frames (discussed below)
+ * from a single call to ms_animate() and display each one over the main picture.
+ * If your port does not support animations, define NO_ANIMATION.
+ */
+struct ms_position {
+ int16 x, y;
+ int16 number;
+
+ ms_position() : x(0), y(0), number(0) {}
+};
+
+/**
+ * Magnetic Windows hint support
+ *
+ * The windowed Magnetic Scolls games included online hints. To add support
+ * for the hints to your magnetic port, you should implement the ms_showhints
+ * function. It retrieves a pointer to an array of ms_hint structs
+ * The root element is always hints[0]. The elcount determines the number
+ * of items in this topic. You probably want to display those in some kind
+ * of list interface. The content pointer points to the actual description of
+ * the items, separated by '\0' terminators. The nodetype is 1 if the items are
+ * "folders" and 2 if the items are hints. Hints should be displayed one after
+ * another. For "folder" items, the links array holds the index of the hint in
+ * the array which is to be displayed on selection. One hint block has exactly
+ * one type. The parent element determines the "back" target.
+ */
+struct ms_hint {
+ uint16 elcount;
+ uint16 nodetype;
+ byte *content;
+ uint16 links[MAX_HITEMS];
+ uint16 parent;
+
+ ms_hint() : elcount(0), nodetype(0), content(nullptr), parent(0) {
+ Common::fill(&links[0], &links[MAX_HITEMS], 0);
+ }
+};
+
+} // End of namespace Magnetic
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index be66cafd41..4c546ff4de 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -72,6 +72,7 @@ MODULE_OBJS := \
glulxe/string.o \
glulxe/vm.o \
magnetic/detection.o \
+ magnetic/emu.o \
magnetic/magnetic.o \
scott/detection.o \
scott/scott.o \