diff options
-rw-r--r-- | engines/glk/magnetic/emu.cpp | 267 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic.cpp | 24 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic.h | 100 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic_types.h | 136 | ||||
-rw-r--r-- | engines/glk/module.mk | 1 |
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 \ |