diff options
author | Paul Gilbert | 2019-11-21 06:47:49 -0800 |
---|---|---|
committer | Paul Gilbert | 2019-11-22 18:49:07 -0800 |
commit | 9df3c85184b10a71be27209313efbc23974d1225 (patch) | |
tree | 251ff9a34997839b2567e76125350da9803be3f9 /engines/glk | |
parent | 9af0d1519444f6c82df24fb63be5d3def769f797 (diff) | |
download | scummvm-rg350-9df3c85184b10a71be27209313efbc23974d1225.tar.gz scummvm-rg350-9df3c85184b10a71be27209313efbc23974d1225.tar.bz2 scummvm-rg350-9df3c85184b10a71be27209313efbc23974d1225.zip |
GLK: MAGNETIC: Making functions all class methods, statics into class fields
Diffstat (limited to 'engines/glk')
-rw-r--r-- | engines/glk/magnetic/defs.h | 469 | ||||
-rw-r--r-- | engines/glk/magnetic/detection.cpp | 13 | ||||
-rw-r--r-- | engines/glk/magnetic/detection.h | 18 | ||||
-rw-r--r-- | engines/glk/magnetic/detection_tables.h | 80 | ||||
-rw-r--r-- | engines/glk/magnetic/emu.cpp | 294 | ||||
-rw-r--r-- | engines/glk/magnetic/glk.cpp | 2533 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic.cpp | 54 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic.h | 1285 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic_defs.h | 113 | ||||
-rw-r--r-- | engines/glk/magnetic/magnetic_types.h | 41 | ||||
-rw-r--r-- | engines/glk/magnetic/main.cpp | 111 |
11 files changed, 2316 insertions, 2695 deletions
diff --git a/engines/glk/magnetic/defs.h b/engines/glk/magnetic/defs.h deleted file mode 100644 index fc7233f59e..0000000000 --- a/engines/glk/magnetic/defs.h +++ /dev/null @@ -1,469 +0,0 @@ -/* 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 MAGNETIC_DEFS_H -#define MAGNETIC_DEFS_H - -#include "common/scummsys.h" -#include "glk/magnetic/magnetic_types.h" - -namespace Glk { -namespace Magnetic { - -/*****************************************************************************\ -* Type definitions for Magnetic -* -* Note: When running into trouble please ensure that these types have the -* correct number of bits on your system !!! -\*****************************************************************************/ - -typedef byte type8; -typedef int8 type8s; -typedef uint16 type16; -typedef int16 type16s; -typedef uint32 type32; -typedef int32 type32s; - -/****************************************************************************\ -* Compile time switches -\****************************************************************************/ - -/* Switch: SAVEMEM - Purpose: Magnetic loads a complete graphics file into memory by default. - Setting this switch you tell Magnetic to load images on request - (saving memory, wasting load time) - -#define SAVEMEM -*/ - -/* Switch: NO_ANIMATION - Purpose: By default Magnetic plays animated graphics. - Setting this switch to ignore animations, Magnetic shows the - static parts of the images anyway! - -#define NO_ANIMATION -*/ - -/****************************************************************************\ -* Abstract functions -* -* Note: These functions MUST be implemented by each port of Magnetic! -\****************************************************************************/ - -/****************************************************************************\ -* Function: ms_load_file -* -* Purpose: Load a save game file and restore game -* -* Parameters: type8s* name zero terminated string of filename -* typed by player -* type8* ptr pointer to data to save -* type16 size number of bytes to save -* -* Result: 0 is successful -* -* Note: You probably want to put in a file requester! -\****************************************************************************/ - -extern type8 ms_load_file(const char *name, type8 *ptr, type16 size); - -/****************************************************************************\ -* Function: ms_save_file -* -* Purpose: Save the current game to file -* -* Parameters: type8s* name zero terminated string of filename -* typed by player -* type8* ptr pointer to data to save -* type16 size number of bytes to save -* -* Result: 0 is successful -* -* Note: You probably want to put in a file requester! -\****************************************************************************/ - -extern type8 ms_save_file(const char *name, type8 *ptr, type16 size); - -/****************************************************************************\ -* Function: ms_statuschar -* -* Purpose: Output a single character to the status bar -* -* Parameters: type8 c character to be printed -* -* Note: All characters are printed as provided except for: -* 0x0A resets the x position to zero -* 0x09 moves the cursor to the right half of the bar, ie 'width-11' -\****************************************************************************/ - -extern void ms_statuschar(type8 c); - -/****************************************************************************\ -* Function: ms_putchar -* -* Purpose: Output a single character to the game/text windows -* -* Parameters: type8 c character to be printed -* -* Note: It is highly recommended to buffer the output, see also ms_flush() -\****************************************************************************/ - -extern void ms_putchar(type8 c); - -/****************************************************************************\ -* Function: ms_flush -* -* Purpose: Flush the output buffer (if applicable) -* -* Note: see also ms_putchar -\****************************************************************************/ - -extern void ms_flush(void); - -/****************************************************************************\ -* Function: ms_getchar -* -* Purpose: Read user input, buffered -* -* Parameters: type8 trans if not 0, translate any #undo -* input to a return code of 0 -* -* Return: One character -* -* Note: The first time it is called a string should be read and then given -* back one byte at a time (ie. one for each call) until a '\n' is -* reached (which will be the last byte sent back before it all restarts) -* Returning a zero means 'undo' and the rest of the line must then be -* ignored. -* Returning 1 means that the opcode should return immediately. This is -* needed to prevent possible corruption of the game's memory in -* interpreters which allow a new game to be loaded without restarting. -\****************************************************************************/ - -extern type8 ms_getchar(type8 trans); - -/****************************************************************************\ -* Function: ms_showpic -* -* Purpose: Displays or hides a picture -* -* Parameter: type32 c number of image to be displayed -* type8 mode mode == 0 means gfx off, -* mode == 1 gfx on thumbnails, -* mode == 2 gfx on normal. -* -* Note: For retrieving the raw data of a picture call ms_extract (see below) -\****************************************************************************/ - -extern void ms_showpic(type32 c, type8 mode); - -/****************************************************************************\ -* Function: ms_fatal -* -* Purpose: Handle fatal interpreter error -* -* Parameter: type8s* txt message -\****************************************************************************/ - -extern void ms_fatal(const char *txt); - -/****************************************************************************\ -* Magnetic core functions -* -* Note: These functions SHOULD be used somewhere in your port! -\****************************************************************************/ - -/****************************************************************************\ -* Function: ms_extract -* -* Purpose: Extract a picture and return a pointer to a raw bitmap -* -* Parameters: type32 c number of the picture -* type16* w width of picture -* type16* h height pf picture -* type16* pal array for the palette (16 colours) -* type8* is_anim 1 if animated picture, otherwise 0 -* OR null (!) -* -* Return: Pointer to bitmap data if successful, otherwise null (also if gfx -* are disabled!) -* -* Note: All pictures have 16 colours and the palette entries use 3-bit RGB -* encoded as 00000RRR0GGG0BBB, that is, bits 0-2 give the blue -* component, bits 4-6 the green and bits 8-10 the red. The bitmap is -* one byte per pixel, with each byte giving the pixel's index into the -* palette. The data is given starting from the upper left corner. The -* image buffer is reused when the next picture is requested, so take -* care! More information on animated pictures are below! -\****************************************************************************/ - -extern type8 *ms_extract(type32 c, type16 *w, type16 *h, type16 *pal, type8 *is_anim); - -/****************************************************************************\ -* 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. -\****************************************************************************/ - -/****************************************************************************\ -* Function: ms_animate -* -* Purpose: Generate the next frame of an animation -* -* Parameters: ms_position** positions array of ms_position structs -* type16* count size of array -* -* Return: 1 if animation continues, 0 if animation is finfished -* -* Note: The positions array holds size ms_positions structures. BEFORE calling -* ms_animate again, retrieve the frames for all the ms_positions -* structures with ms_get_anim_frame and display each one on the static -* main picture. -\****************************************************************************/ - -extern type8 ms_animate(struct ms_position **positions, type16 *count); - -/****************************************************************************\ -* Function: ms_get_anim_frame -* -* Purpose: Extracts the bitmap data of a single animation frame -* -* Parameters: type16s number number of frame (see ms_position struct) -* type16* width width of frame -* type16* height height of frame -* type8** mask pointer to masking data, might be NULL -* -* Return: 1 if animation continues, 0 if animation is finfished -* -* Note: The format of the frame is identical to the main pictures' returned by -* ms_extract. The mask has one-bit-per-pixel, determing how to handle the -* removal of the frame. -\****************************************************************************/ - -extern type8 *ms_get_anim_frame(type16s number, type16 *width, type16 *height, type8 **mask); - -/****************************************************************************\ -* Function: ms_anim_is_repeating -* -* Purpose: Detects whether an animation is repeating -* -* Return: True if repeating -\****************************************************************************/ - -extern type8 ms_anim_is_repeating(void); - -/****************************************************************************\ -* 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. -\****************************************************************************/ - -/****************************************************************************\ -* Function: ms_showhints -* Purpose: Show the player a hint -* -* Parameters: ms_hint* hints pointer to array of ms_hint structs -* -* Return: 0 on error, 1 on success -\****************************************************************************/ - -extern type8 ms_showhints(struct ms_hint *hints); - -/****************************************************************************\ -* Magnetic Windows sound support -* -* Wonderland contains music scores that are played when entering specific -* locations in the game. The music data are actually MIDI events and can be -* played through normal MIDI devices. The original game plays the MIDI score -* until the end, even if the location is changed while playing. The playback -* tempo is not included with the MIDI data. The ms_sndextract function -* returns a recommended tempo, however depending on the MIDI implementation -* and operating system, you might need to adapt it. -\****************************************************************************/ - -/****************************************************************************\ -* Function: ms_playmusic -* -* Purpose: Plays (or stops playing) a MIDI music score. -* -* Parameter: type8 * midi_data the MIDI data to play -* type32 length the length of the MIDI data -* type16 tempo the suggested tempo for playing -* -* Note: If midi_data is NULL, all that should happen is that any currently -* playing music is stopped. -* Note: The data returned contain a complete MIDI file header, so if pure -* memory processing is not applicable you can write the data to a -* temporary file and use external players or libraries. -\****************************************************************************/ - -extern void ms_playmusic(type8 *midi_data, type32 length, type16 tempo); - -/****************************************************************************\ -* Function: ms_init -* -* Purpose: Loads the interpreter with a game -* -* Parameters: type8s* name filename of story file -* type8s* gfxname filename of graphics file (optional) -* type8s* hntname filename of hints file (optional) -* type8s* sndname filename of music file (optional) -* -* Return: 0 = failure -* 1 = success (without graphics or graphics failed) -* 2 = success (with graphics) -* -* Note: You must call this function before starting the ms_rungame loop -\****************************************************************************/ - -extern type8 ms_init(const char *name, const char *gfxname, const char *hntname, const char *sndname); - -/****************************************************************************\ -* Function: ms_rungame -* -* Purpose: Executes an interpreter instruction -* -* Return: True if successful -* -* Note: You must call this function in a loop like this: -* while (running) {running=ms_rungame();} -\****************************************************************************/ - -extern type8 ms_rungame(void); - -/****************************************************************************\ -* Function: ms_freemen -* -* Purpose: Frees all allocated ressources -\****************************************************************************/ - -extern void ms_freemem(void); - -/****************************************************************************\ -* Function: ms_seed -* -* Purpose: Initializes the interpreter's random number generator with -* the given seed -* -* Parameter: type32 seed seed -\****************************************************************************/ - -extern void ms_seed(type32 seed); - -/****************************************************************************\ -* Function: ms_is_running -* -* Purpose: Detects if game is running -* -* Return: True, if game is currently running -\****************************************************************************/ - -extern type8 ms_is_running(void); - -/****************************************************************************\ -* Function: ms_is_magwin -* -* Purpose: Detects Magnetic Windows games (Wonderland, Collection) -* -* Return: True, if Magnetic Windows game -\****************************************************************************/ - -extern type8 ms_is_magwin(void); - -/****************************************************************************\ -* Function: ms_stop -* -* Purpose: Stops further processing of opcodes -\****************************************************************************/ - -extern void ms_stop(void); - -/****************************************************************************\ -* Function: ms_status -* -* Purpose: Dumps interperetr state to stderr, ie. registers -\****************************************************************************/ - -extern void ms_status(void); - -/****************************************************************************\ -* Function: ms_count -* -* Purpose: Returns the number of executed intructions -* -* Return: Instruction count -\****************************************************************************/ - -extern type32 ms_count(void); - -extern void write(const char *fmt, ...); - -extern void writeChar(char c); - -extern void gms_main(); - -} // End of namespace Magnetic -} // End of namespace Glk - -#endif diff --git a/engines/glk/magnetic/detection.cpp b/engines/glk/magnetic/detection.cpp index cc1714eaf1..54a0e7b928 100644 --- a/engines/glk/magnetic/detection.cpp +++ b/engines/glk/magnetic/detection.cpp @@ -75,7 +75,7 @@ bool MagneticMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames gameFile.close(); // Check for known games - const MagneticGameDescription *p = MAGNETIC_GAMES; + const GlkDetectionEntry *p = MAGNETIC_GAMES; while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize)) ++p; @@ -99,5 +99,16 @@ void MagneticMetaEngine::detectClashes(Common::StringMap &map) { } } +const gms_game_table_t *gms_gameid_lookup_game(uint32 undo_size, uint32 undo_pc) { + const gms_game_table_t *game; + + for (game = GMS_GAME_TABLE; game->name; game++) { + if (game->undo_size == undo_size && game->undo_pc == undo_pc) + break; + } + + return game->name ? game : nullptr; +} + } // End of namespace Magnetic } // End of namespace Glk diff --git a/engines/glk/magnetic/detection.h b/engines/glk/magnetic/detection.h index 28ea70ffc2..80a8b7f557 100644 --- a/engines/glk/magnetic/detection.h +++ b/engines/glk/magnetic/detection.h @@ -32,6 +32,18 @@ namespace Glk { namespace Magnetic { /** + * The following game database is built from Generic/games.txt, and is used + * to identify the game being run. Magnetic Scrolls games don't generally + * supply a status line, so this data can be used instead. + */ +struct gms_game_table_t { + const uint32 undo_size; ///< Header word at offset 0x22 + const uint32 undo_pc; ///< Header word at offset 0x26 + const char *const name; ///< Game title and platform +}; +typedef const gms_game_table_t *gms_game_tableref_t; + +/** * Meta engine for Magnetic interpreter */ class MagneticMetaEngine { @@ -57,6 +69,12 @@ public: static void detectClashes(Common::StringMap &map); }; +/** + * Look up and return the game table entry given a game's undo size and + * undo pc values. Returns the entry, or NULL if not found. + */ +extern const gms_game_table_t *gms_gameid_lookup_game(uint32 undo_size, uint32 undo_pc); + } // End of namespace Magnetic } // End of namespace Glk diff --git a/engines/glk/magnetic/detection_tables.h b/engines/glk/magnetic/detection_tables.h index 7476c34e0b..c0952d803d 100644 --- a/engines/glk/magnetic/detection_tables.h +++ b/engines/glk/magnetic/detection_tables.h @@ -27,17 +27,6 @@ namespace Glk { namespace Magnetic { -/** - * Game description - */ -struct MagneticGameDescription { - const char *const _gameId; - const char *const _extra; - const char *const _md5; - size_t _filesize; - Common::Language _language; -}; - const PlainGameDescriptor MAGNETIC_GAME_LIST[] = { { "magnetic", "Magnetic Scrolls Game" }, @@ -51,21 +40,62 @@ const PlainGameDescriptor MAGNETIC_GAME_LIST[] = { { nullptr, nullptr } }; -#define ENTRY0(ID, MD5, FILESIZE) { ID, nullptr, MD5, FILESIZE, Common::EN_ANY } -#define ENTRY1(ID, EXTRA, MD5, FILESIZE) { ID, EXTRA, MD5, FILESIZE, Common::EN_ANY } -#define TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY } +const GlkDetectionEntry MAGNETIC_GAMES[] = { + DT_ENTRY0("corruption", "313880cbe0f15bfa259ebaf228b4d0e9", 167466), + DT_ENTRY1("corruption", "Collection", "6fe35b357fa0311450d3a9c809e60ba8", 177185), + DT_ENTRY0("fish", "2efb8118f4cb9a36bb54646ce41a950e", 162858), + DT_ENTRY1("fish", "Collection", "cfe333306597d36c8aa3fc64f6be94ba", 172517), + DT_ENTRY0("guild", "bab78740d39ee5e058faf4912fdbf33d", 130858), + DT_ENTRY1("guild", "Collection", "36af907a4ec9db909148f308287586f1", 141766), + DT_ENTRY0("myth", "9c2a5272a9c0b1e173401ba4df32567a", 99370), + DT_ENTRY0("pawn", "4a7847980f9e942acd7aa51ea12a6586", 103466), + DT_ENTRY0("wonderland", "2cea8fccf42d570be8836416c2802613", 183916), + + DT_END_MARKER +}; + +const gms_game_table_t GMS_GAME_TABLE[] = { + {0x2100, 0x427e, "Corruption v1.11 (Amstrad CPC)"}, + {0x2100, 0x43a0, "Corruption v1.11 (Archimedes)"}, + {0x2100, 0x43a0, "Corruption v1.11 (DOS)"}, + {0x2100, 0x4336, "Corruption v1.11 (Commodore 64)"}, + {0x2100, 0x4222, "Corruption v1.11 (Spectrum +3)"}, + {0x2100, 0x4350, "Corruption v1.12 (Archimedes)"}, + {0x2500, 0x6624, "Corruption v1.12 (DOS, Magnetic Windows)"}, + + {0x2300, 0x3fa0, "Fish v1.02 (DOS)"}, + {0x2400, 0x4364, "Fish v1.03 (Spectrum +3)"}, + {0x2300, 0x3f72, "Fish v1.07 (Commodore 64)"}, + {0x2200, 0x3f9c, "Fish v1.08 (Archimedes)"}, + {0x2a00, 0x583a, "Fish v1.10 (DOS, Magnetic Windows)"}, + + {0x5000, 0x6c30, "Guild v1.0 (Amstrad CPC)"}, + {0x5000, 0x6cac, "Guild v1.0 (Commodore 64)"}, + {0x5000, 0x6d5c, "Guild v1.1 (DOS)"}, + {0x3300, 0x698a, "Guild v1.3 (Archimedes)"}, + {0x3200, 0x6772, "Guild v1.3 (Spectrum +3)"}, + {0x3400, 0x6528, "Guild v1.3 (DOS, Magnetic Windows)"}, + + {0x2b00, 0x488c, "Jinxter v1.05 (Commodore 64)"}, + {0x2c00, 0x4a08, "Jinxter v1.05 (DOS)"}, + {0x2c00, 0x487a, "Jinxter v1.05 (Spectrum +3)"}, + {0x2c00, 0x4a56, "Jinxter v1.10 (DOS)"}, + {0x2b00, 0x4924, "Jinxter v1.22 (Amstrad CPC)"}, + {0x2c00, 0x4960, "Jinxter v1.30 (Archimedes)"}, + + {0x1600, 0x3940, "Myth v1.0 (Commodore 64)"}, + {0x1500, 0x3a0a, "Myth v1.0 (DOS)"}, + + {0x3600, 0x42cc, "Pawn v2.3 (Amstrad CPC)"}, + {0x3600, 0x4420, "Pawn v2.3 (Archimedes)"}, + {0x3600, 0x3fb0, "Pawn v2.3 (Commodore 64)"}, + {0x3600, 0x4420, "Pawn v2.3 (DOS)"}, + {0x3900, 0x42e4, "Pawn v2.3 (Spectrum 128)"}, + {0x3900, 0x42f4, "Pawn v2.4 (Spectrum +3)"}, -const MagneticGameDescription MAGNETIC_GAMES[] = { - ENTRY0("corruption", "313880cbe0f15bfa259ebaf228b4d0e9", 167466), - ENTRY1("corruption", "Collection", "6fe35b357fa0311450d3a9c809e60ba8", 177185), - ENTRY0("fish", "2efb8118f4cb9a36bb54646ce41a950e", 162858), - ENTRY1("fish", "Collection", "cfe333306597d36c8aa3fc64f6be94ba", 172517), - ENTRY0("guild", "bab78740d39ee5e058faf4912fdbf33d", 130858), - ENTRY1("guild", "Collection", "36af907a4ec9db909148f308287586f1", 141766), - ENTRY0("myth", "9c2a5272a9c0b1e173401ba4df32567a", 99370), - ENTRY0("pawn", "4a7847980f9e942acd7aa51ea12a6586", 103466), - ENTRY0("wonderland", "2cea8fccf42d570be8836416c2802613", 183916), - TABLE_END_MARKER + {0x3900, 0x75f2, "Wonderland v1.21 (DOS, Magnetic Windows)"}, + {0x3900, 0x75f8, "Wonderland v1.27 (Archimedes)"}, + {0, 0, NULL} }; } // End of namespace Magnetic diff --git a/engines/glk/magnetic/emu.cpp b/engines/glk/magnetic/emu.cpp index d91b7e60d3..6dd0b11b21 100644 --- a/engines/glk/magnetic/emu.cpp +++ b/engines/glk/magnetic/emu.cpp @@ -20,57 +20,20 @@ * */ -#include "glk/magnetic/defs.h" +#include "glk/magnetic/magnetic_defs.h" #include "glk/magnetic/magnetic.h" #include "common/file.h" +#include "common/textconsole.h" namespace Glk { namespace Magnetic { -type32 dreg[8], areg[8], i_count, string_size, rseed = 0, pc, arg1i, mem_size; -type16 properties, fl_sub, fl_tab, fl_size, fp_tab, fp_size; -type8 zflag, nflag, cflag, vflag, byte1, byte2, regnr, admode, opsize; -type8 *arg1, *arg2, is_reversible, running = 0, tmparg[4] = {0, 0, 0, 0}; -type8 lastchar = 0, version = 0, sd = 0; -type8 *decode_table, *restart = 0, *code = 0, *string = 0, *string2 = 0; -type8 *string3 = 0, *dict = 0; -type8 quick_flag = 0, gfx_ver = 0, *gfx_buf = nullptr, *gfx_data = 0; -type8 *gfx2_hdr = 0, *gfx2_buf = nullptr; -const char *gfx2_name = nullptr; -type16 gfx2_hsize = 0; -Common::File *gfx_fp = nullptr; -type8 *snd_buf = nullptr, *snd_hdr = nullptr; -type16 snd_hsize = 0; -Common::File *snd_fp = nullptr; - +const char *const no_hints = "[Hints are not available.]\n"; +const char *const not_supported = "[This function is not supported.]\n"; const char *const undo_ok = "\n[Previous turn undone.]"; const char *const undo_fail = "\n[You can't \"undo\" what hasn't been done!]"; -type32 undo_regs[2][18], undo_pc, undo_size; -type8 *undo[2] = {0, 0}, undo_stat[2] = {0, 0}; -type16 gfxtable = 0, table_dist = 0; -type16 v4_id = 0, next_table = 1; - -#ifndef NO_ANIMATION - -struct picture anim_frame_table[MAX_ANIMS]; -type16 pos_table_size = 0; -type16 pos_table_count[MAX_POSITIONS]; -struct ms_position pos_table[MAX_POSITIONS][MAX_ANIMS]; -type8 *command_table = 0; -type16s command_index = -1; -struct lookup anim_table[MAX_POSITIONS]; -type16s pos_table_index = -1; -type16s pos_table_max = -1; -struct ms_position pos_array[MAX_FRAMES]; -type8 anim_repeat = 0; -#endif - -/* Hint support */ -struct ms_hint *hints = 0; -type8 *hint_contents = 0; -const char *const no_hints = "[Hints are not available.]\n"; -const char *const not_supported = "[This function is not supported.]\n"; +#define ms_fatal error #if defined(LOGEMU) || defined(LOGGFX) || defined(LOGHNT) FILE *dbg_log; @@ -79,14 +42,6 @@ FILE *dbg_log; #endif #endif -/* prototypes */ -type32 read_reg(int, int); -void write_reg(int, int, type32); - -#define MAX_STRING_SIZE 0xFF00 -#define MAX_PICTURE_SIZE 0xC800 -#define MAX_MUSIC_SIZE 0x4E20 - #ifdef LOGEMU void out(char *format, ...) { va_list a; @@ -108,7 +63,7 @@ void out2(char *format, ...) { #endif /* Convert virtual pointer to effective pointer */ -type8 *effective(type32 ptr) { +type8 *Magnetic::effective(type32 ptr) { if ((version < 4) && (mem_size == 0x10000)) return &(code[ptr & 0xffff]); if (ptr >= mem_size) { @@ -119,16 +74,7 @@ type8 *effective(type32 ptr) { return &(code[ptr]); } -type32 read_l(type8 *ptr) { - - return (type32)((type32) ptr[0] << 24 | (type32) ptr[1] << 16 | (type32) ptr[2] << 8 | (type32) ptr[3]); -} - -type16 read_w(type8 *ptr) { - return (type16)(ptr[0] << 8 | ptr[1]); -} - -void write_l(type8 *ptr, type32 val) { +void Magnetic::write_l(type8 *ptr, type32 val) { ptr[3] = (type8) val; val >>= 8; ptr[2] = (type8) val; @@ -138,32 +84,18 @@ void write_l(type8 *ptr, type32 val) { ptr[0] = (type8) val; } -void write_w(type8 *ptr, type16 val) { +void Magnetic::write_w(type8 *ptr, type16 val) { ptr[1] = (type8) val; val >>= 8; ptr[0] = (type8) val; } -type32 read_l2(type8 *ptr) { - return ((type32) ptr[1] << 24 | (type32) ptr[0] << 16 | (type32) ptr[3] << 8 | (type32) ptr[2]); -} - -type16 read_w2(type8 *ptr) { - return (type16)(ptr[1] << 8 | ptr[0]); -} - -/* Standard rand - for equal cross-platform behaviour */ - -void ms_seed(type32 seed) { - rseed = seed; -} - -type32 rand_emu(void) { +type32 Magnetic::rand_emu() { rseed = 1103515245L * rseed + 12345L; return rseed & 0x7fffffffL; } -void ms_freemem(void) { +void Magnetic::ms_freemem() { if (code) free(code); if (string) @@ -219,19 +151,7 @@ void ms_freemem(void) { snd_buf = nullptr; } -type8 ms_is_running(void) { - return running; -} - -type8 ms_is_magwin(void) { - return (version == 4) ? 1 : 0; -} - -void ms_stop(void) { - running = 0; -} - -type8 init_gfx1(type8 *header) { +type8 Magnetic::init_gfx1(type8 *header) { #ifdef SAVEMEM type32 i; #endif @@ -278,7 +198,7 @@ type8 init_gfx1(type8 *header) { return 2; } -type8 init_gfx2(type8 *header) { +type8 Magnetic::init_gfx2(type8 *header) { if (!(gfx_buf = (type8 *)malloc(MAX_PICTURE_SIZE))) { delete gfx_fp; gfx_fp = nullptr; @@ -309,7 +229,7 @@ type8 init_gfx2(type8 *header) { return 2; } -type8 init_snd(type8 *header) { +type8 Magnetic::init_snd(type8 *header) { if (!(snd_buf = (type8 *)malloc(MAX_MUSIC_SIZE))) { delete snd_fp; snd_fp = nullptr; @@ -339,9 +259,7 @@ type8 init_snd(type8 *header) { return 2; } -/* zero all registers and flags and load the game */ - -type8 ms_init(const char *name, const char *gfxname, const char *hntname, const char *sndname) { +type8 Magnetic::ms_init(const char *name, const char *gfxname, const char *hntname, const char *sndname) { Common::File fp; type8 header[42], header2[8], header3[4]; type32 i, dict_size, string2_size, code_size, dec; @@ -601,7 +519,7 @@ type8 ms_init(const char *name, const char *gfxname, const char *hntname, const return 1; } -type8 is_blank(type16 line, type16 width) { +type8 Magnetic::is_blank(type16 line, type16 width) { type32s i; for (i = line * width; i < (line + 1) * width; i++) @@ -610,8 +528,8 @@ type8 is_blank(type16 line, type16 width) { return 1; } -type8 *ms_extract1(type8 pic, type16 *w, type16 *h, type16 *pal) { - type8 *decodeTable, *data, bit, val, *buffer; +type8 *Magnetic::ms_extract1(type8 pic, type16 *w, type16 *h, type16 *pal) { + type8 *decodeTable, *data, bit, val, *buf; type16 tablesize, count; type32 i, j, upsize, offset; @@ -622,22 +540,22 @@ type8 *ms_extract1(type8 pic, type16 *w, type16 *h, type16 *pal) { if (fseek(gfx_fp, offset, SEEK_SET) < 0) return 0; datasize = read_l(gfx_data + 4 * (pic + 1)) - offset; - if (!(buffer = (type8 *)malloc(datasize))) + if (!(buf = (type8 *)malloc(datasize))) return 0; - if (fp.read(buffer, 1, datasize, gfx_fp) != datasize) + if (fp.read(buf, 1, datasize, gfx_fp) != datasize) return 0; #else - buffer = gfx_data + offset - 8; + buf = gfx_data + offset - 8; #endif for (i = 0; i < 16; i++) - pal[i] = read_w(buffer + 0x1c + 2 * i); - w[0] = (type16)(read_w(buffer + 4) - read_w(buffer + 2)); - h[0] = read_w(buffer + 6); + pal[i] = read_w(buf + 0x1c + 2 * i); + w[0] = (type16)(read_w(buf + 4) - read_w(buf + 2)); + h[0] = read_w(buf + 6); - tablesize = read_w(buffer + 0x3c); - //datasize = read_l(buffer + 0x3e); - decodeTable = buffer + 0x42; + tablesize = read_w(buf + 0x3c); + //datasize = read_l(buf + 0x3e); + decodeTable = buf + 0x42; data = decodeTable + tablesize * 2 + 2; upsize = h[0] * w[0]; @@ -667,14 +585,14 @@ type8 *ms_extract1(type8 pic, type16 *w, type16 *h, type16 *pal) { gfx_buf[j] ^= gfx_buf[j - w[0]]; #ifdef SAVEMEM - free(buffer); + free(buf); #endif for (; h[0] > 0 && is_blank((type16)(h[0] - 1), w[0]); h[0]--); for (i = 0; h[0] > 0 && is_blank((type16)i, w[0]); h[0]--, i++); return gfx_buf + i * w[0]; } -type16s find_name_in_header(const char *name, type8 upper) { +type16s Magnetic::find_name_in_header(const char *name, type8 upper) { type16s header_pos = 0; char pic_name[8]; type8 i; @@ -696,7 +614,7 @@ type16s find_name_in_header(const char *name, type8 upper) { return -1; } -void extract_frame(struct picture *pic) { +void Magnetic::extract_frame(struct picture *pic) { type32 i, x, y, bit_x, mask, ywb, yw, value, values[4]; if (pic->width * pic->height > MAX_PICTURE_SIZE) { @@ -727,7 +645,7 @@ void extract_frame(struct picture *pic) { } } -type8 *ms_extract2(const char *name, type16 *w, type16 *h, type16 *pal, type8 *is_anim) { +type8 *Magnetic::ms_extract2(const char *name, type16 *w, type16 *h, type16 *pal, type8 *is_anim) { struct picture main_pic; type32 offset = 0, length = 0, i; type16s header_pos = -1; @@ -885,7 +803,7 @@ type8 *ms_extract2(const char *name, type16 *w, type16 *h, type16 *pal, type8 *i return 0; } -type8 *ms_extract(type32 pic, type16 *w, type16 *h, type16 *pal, type8 *is_anim) { +type8 *Magnetic::ms_extract(type32 pic, type16 *w, type16 *h, type16 *pal, type8 *is_anim) { if (is_anim) *is_anim = 0; @@ -902,7 +820,7 @@ type8 *ms_extract(type32 pic, type16 *w, type16 *h, type16 *pal, type8 *is_anim) return 0; } -type8 ms_animate(struct ms_position **positions, type16 *count) { +type8 Magnetic::ms_animate(struct ms_position **positions, type16 *count) { #ifndef NO_ANIMATION type8 got_anim = 0; type16 i, j, ttable; @@ -1054,7 +972,7 @@ type8 ms_animate(struct ms_position **positions, type16 *count) { #endif } -type8 *ms_get_anim_frame(type16s number, type16 *width, type16 *height, type8 **mask) { +type8 *Magnetic::ms_get_anim_frame(type16s number, type16 *width, type16 *height, type8 **mask) { #ifndef NO_ANIMATION if (number >= 0) { extract_frame(anim_frame_table + number); @@ -1067,7 +985,7 @@ type8 *ms_get_anim_frame(type16s number, type16 *width, type16 *height, type8 ** return 0; } -type8 ms_anim_is_repeating(void) { +type8 Magnetic::ms_anim_is_repeating() const { #ifndef NO_ANIMATION return anim_repeat; #else @@ -1075,7 +993,7 @@ type8 ms_anim_is_repeating(void) { #endif } -type16s find_name_in_sndheader(const char *name) { +type16s Magnetic::find_name_in_sndheader(const char *name) { type16s header_pos = 0; while (header_pos < snd_hsize) { @@ -1088,7 +1006,7 @@ type16s find_name_in_sndheader(const char *name) { return -1; } -type8 *sound_extract(const char *name, type32 *length, type16 *tempo) { +type8 *Magnetic::sound_extract(const char *name, type32 *length, type16 *tempo) { type32 offset = 0; type16s header_pos = -1; @@ -1113,7 +1031,7 @@ type8 *sound_extract(const char *name, type32 *length, type16 *tempo) { return nullptr; } -void save_undo(void) { +void Magnetic::save_undo() { type8 *tmp, i; type32 tmp32; @@ -1139,7 +1057,7 @@ void save_undo(void) { undo_stat[1] = 1; } -type8 ms_undo(void) { +type8 Magnetic::ms_undo() { type8 i; ms_flush(); @@ -1158,7 +1076,7 @@ type8 ms_undo(void) { } #ifdef LOGEMU -void log_status(void) { +void Magnetic::log_status() { int j; fprintf(dbg_log, "\nD0:"); @@ -1172,7 +1090,7 @@ void log_status(void) { } #endif -void ms_status(void) { +void Magnetic::ms_status() { int j; Common::String s = "D0:"; @@ -1187,13 +1105,7 @@ void ms_status(void) { warning("%s", s.c_str()); } -type32 ms_count(void) { - return i_count; -} - -/* align register pointer for word/byte accesses */ - -type8 *reg_align(type8 *ptr, type8 size) { +type8 *Magnetic::reg_align(type8 *ptr, type8 size) { if (size == 1) ptr += 2; if (size == 0) @@ -1201,7 +1113,7 @@ type8 *reg_align(type8 *ptr, type8 size) { return ptr; } -type32 read_reg(int i, int s) { +type32 Magnetic::read_reg(int i, int s) { type8 *ptr; if (i > 15) { @@ -1223,7 +1135,7 @@ type32 read_reg(int i, int s) { } } -void write_reg(int i, int s, type32 val) { +void Magnetic::write_reg(int i, int s, type32 val) { type8 *ptr; if (i > 15) { @@ -1248,9 +1160,7 @@ void write_reg(int i, int s, type32 val) { } } -/* [35c4] */ - -void char_out(type8 c) { +void Magnetic::char_out(type8 c) { static type8 big = 0, period = 0, pipe = 0; if (c == 0xff) { @@ -1327,18 +1237,13 @@ void char_out(type8 c) { ms_putchar(c); } - -/* extract addressing mode information [1c6f] */ - -void set_info(type8 b) { +void Magnetic::set_info(type8 b) { regnr = (type8)(b & 0x07); admode = (type8)((b >> 3) & 0x07); opsize = (type8)(b >> 6); } -/* read a word and increase pc */ - -void read_word(void) { +void Magnetic::read_word() { type8 *epc; epc = effective(pc); @@ -1347,9 +1252,7 @@ void read_word(void) { pc += 2; } -/* get addressing mode and set arg1 [1c84] */ - -void set_arg1(void) { +void Magnetic::set_arg1() { type8 tmp[2], l1c; is_reversible = 1; @@ -1479,9 +1382,7 @@ void set_arg1(void) { arg1 = effective(arg1i); } -/* get addressing mode and set arg2 [1bc5] */ - -void set_arg2_nosize(int use_dx, type8 b) { +void Magnetic::set_arg2_nosize(int use_dx, type8 b) { if (use_dx) arg2 = (type8 *) dreg; else @@ -1489,14 +1390,12 @@ void set_arg2_nosize(int use_dx, type8 b) { arg2 += (b & 0x0e) << 1; } -void set_arg2(int use_dx, type8 b) { +void Magnetic::set_arg2(int use_dx, type8 b) { set_arg2_nosize(use_dx, b); arg2 = reg_align(arg2, opsize); } -/* [1b9e] */ - -void swap_args(void) { +void Magnetic::swap_args() { type8 *tmp; tmp = arg1; @@ -1504,16 +1403,12 @@ void swap_args(void) { arg2 = tmp; } -/* [1cdc] */ - -void push(type32 c) { +void Magnetic::push(type32 c) { write_reg(15, 2, read_reg(15, 2) - 4); write_l(effective(read_reg(15, 2)), c); } -/* [1cd1] */ - -type32 pop(void) { +type32 Magnetic::pop() { type32 c; c = read_l(effective(read_reg(15, 2))); @@ -1521,9 +1416,7 @@ type32 pop(void) { return c; } -/* check addressing mode and get argument [2e85] */ - -void get_arg(void) { +void Magnetic::get_arg() { #ifdef LOGEMU out(" %.4X", pc); #endif @@ -1537,7 +1430,7 @@ void get_arg(void) { set_arg1(); } -void set_flags(void) { +void Magnetic::set_flags() { type16 i; type32 j; @@ -1566,9 +1459,7 @@ void set_flags(void) { } } -/* [263a] */ - -int condition(type8 b) { +int Magnetic::condition(type8 b) { switch (b & 0x0f) { case 0: return 0xff; @@ -1604,9 +1495,7 @@ int condition(type8 b) { return 0x00; } -/* [26dc] */ - -void branch(type8 b) { +void Magnetic::branch(type8 b) { if (b == 0) pc += (type16s) read_w(effective(pc)); else @@ -1616,9 +1505,7 @@ void branch(type8 b) { #endif } -/* [2869] */ - -void do_add(type8 adda) { +void Magnetic::do_add(type8 adda) { if (adda) { if (opsize == 0) write_l(arg1, read_l(arg1) + (type8s) arg2[0]); @@ -1651,9 +1538,7 @@ void do_add(type8 adda) { } } -/* [2923] */ - -void do_sub(type8 suba) { +void Magnetic::do_sub(type8 suba) { if (suba) { if (opsize == 0) write_l(arg1, read_l(arg1) - (type8s) arg2[0]); @@ -1686,9 +1571,7 @@ void do_sub(type8 suba) { } } -/* [283b] */ - -void do_eor(void) { +void Magnetic::do_eor() { if (opsize == 0) arg1[0] ^= arg2[0]; if (opsize == 1) @@ -1699,9 +1582,7 @@ void do_eor(void) { set_flags(); } -/* [280d] */ - -void do_and(void) { +void Magnetic::do_and() { if (opsize == 0) arg1[0] &= arg2[0]; if (opsize == 1) @@ -1712,9 +1593,7 @@ void do_and(void) { set_flags(); } -/* [27df] */ - -void do_or(void) { +void Magnetic::do_or() { if (opsize == 0) arg1[0] |= arg2[0]; if (opsize == 1) @@ -1725,9 +1604,7 @@ void do_or(void) { set_flags(); /* [1c2b] */ } -/* [289f] */ - -void do_cmp(void) { +void Magnetic::do_cmp() { type8 *tmp; tmp = arg1; @@ -1741,9 +1618,7 @@ void do_cmp(void) { arg1 = tmp; } -/* [2973] */ - -void do_move(void) { +void Magnetic::do_move() { if (opsize == 0) arg1[0] = arg2[0]; @@ -1758,7 +1633,7 @@ void do_move(void) { } } -type8 do_btst(type8 a) { +type8 Magnetic::do_btst(type8 a) { a &= admode ? 0x7 : 0x1f; while (admode == 0 && a >= 8) { a -= 8; @@ -1770,9 +1645,7 @@ type8 do_btst(type8 a) { return a; } -/* bit operation entry point [307c] */ - -void do_bop(type8 b, type8 a) { +void Magnetic::do_bop(type8 b, type8 a) { #ifdef LOGEMU out("bop (%.2x,%.2x) ", (int) b, (int) a); #endif @@ -1802,7 +1675,7 @@ void do_bop(type8 b, type8 a) { } } -void check_btst(void) { +void Magnetic::check_btst() { #ifdef LOGEMU out("btst"); #endif @@ -1812,7 +1685,7 @@ void check_btst(void) { do_bop(byte2, arg2[0]); } -void check_lea(void) { +void Magnetic::check_lea() { #ifdef LOGEMU out("lea"); #endif @@ -1831,9 +1704,7 @@ void check_lea(void) { } } -/* [33cc] */ - -void check_movem(void) { +void Magnetic::check_movem() { type8 l1c; #ifdef LOGEMU @@ -1861,9 +1732,7 @@ void check_movem(void) { } } -/* [3357] */ - -void check_movem2(void) { +void Magnetic::check_movem2() { type8 l1c; #ifdef LOGEMU @@ -1891,10 +1760,7 @@ void check_movem2(void) { } } -/* [30e4] in Jinxter, ~540 lines of 6510 spaghetti-code */ -/* The mother of all bugs, but hey - no gotos used :-) */ - -void dict_lookup(void) { +void Magnetic::dict_lookup() { type16 dtab, doff, output, output_bak, bank, word, output2; type16 tmp16, i, obj_adj, adjlist, adjlist_bak; type8 c, c2, c3, flag, matchlen, longest, flag2; @@ -2066,9 +1932,7 @@ void dict_lookup(void) { write_reg(8 + 6, 1, read_reg(8 + 5, 1) + 1); } -/* A0=findproperties(D0) [2b86], properties_ptr=[2b78] A0FE */ - -void do_findprop(void) { +void Magnetic::do_findprop() { type16 tmp; if ((version > 2) && ((read_reg(0, 1) & 0x3fff) > fp_size)) { @@ -2086,7 +1950,7 @@ void do_findprop(void) { write_reg(8 + 0, 2, tmp * 14 + properties); } -void write_string(void) { +void Magnetic::write_string() { static type32 offset_bak; static type8 mask_bak; type8 c, b, mask; @@ -2142,7 +2006,7 @@ void write_string(void) { } } -void output_number(type16 number) { +void Magnetic::output_number(type16 number) { type16 tens = number / 10; if (tens > 0) @@ -2151,7 +2015,7 @@ void output_number(type16 number) { ms_putchar('0' + number); } -type16 output_text(const char *text) { +type16 Magnetic::output_text(const char *text) { type16 i; for (i = 0; text[i] != 0; i++) @@ -2159,7 +2023,7 @@ type16 output_text(const char *text) { return i; } -type16s hint_input(void) { +type16s Magnetic::hint_input() { type8 c1, c2, c3; output_text(">>"); @@ -2208,7 +2072,7 @@ type16s hint_input(void) { return 0; } -type16 show_hints_text(ms_hint *hintsData, type16 index) { +type16 Magnetic::show_hints_text(ms_hint *hintsData, type16 index) { type16 i = 0, j = 0; type16s input; ms_hint *hint = hintsData + index; @@ -2282,7 +2146,7 @@ type16 show_hints_text(ms_hint *hintsData, type16 index) { return 0; } -void do_line_a(void) { +void Magnetic::do_line_a() { type8 l1c; char *str; type16 ptr, ptr2, tmp16, dtype; @@ -2656,9 +2520,7 @@ void do_line_a(void) { } } -/* emulate an instruction [1b7e] */ - -type8 ms_rungame(void) { +type8 Magnetic::ms_rungame() { type8 l1c; type16 ptr; type32 tmp32; diff --git a/engines/glk/magnetic/glk.cpp b/engines/glk/magnetic/glk.cpp index 1801ef94ad..fec8691854 100644 --- a/engines/glk/magnetic/glk.cpp +++ b/engines/glk/magnetic/glk.cpp @@ -20,157 +20,253 @@ * */ -#include "glk/magnetic/defs.h" +#include "glk/magnetic/magnetic_defs.h" #include "glk/magnetic/magnetic.h" namespace Glk { namespace Magnetic { -/* - * True and false definitions -- usually defined in glkstart.h, but we need - * them early, so we'll define them here too. We also need NULL, but that's - * normally from stdio.h or one of it's cousins. - */ -#ifndef FALSE -# define FALSE false -#endif -#ifndef TRUE -# define TRUE true -#endif +const gms_command_t Magnetic::GMS_COMMAND_TABLE[14] = { + { &Magnetic::gms_command_summary, "summary", false, false }, + { &Magnetic::gms_command_undo, "undo", false, true }, + { &Magnetic::gms_command_script, "script", true, false }, + { &Magnetic::gms_command_inputlog, "inputlog", true, false }, + { &Magnetic::gms_command_readlog, "readlog", true, false }, + { &Magnetic::gms_command_abbreviations, "abbreviations", true, false }, + { &Magnetic::gms_command_graphics, "graphics", true, false }, + { &Magnetic::gms_command_gamma, "gamma", true, false }, + { &Magnetic::gms_command_animations, "animations", true, false }, + { &Magnetic::gms_command_prompts, "prompts", true, false }, + { &Magnetic::gms_command_version, "version", false, false }, + { &Magnetic::gms_command_commands, "commands", true, false }, + { &Magnetic::gms_command_help, "help", true, false }, + + { nullptr, nullptr, false, false} +}; + -#define BYTE_MAX 255 -#define CHAR_BIT 8 -#define UINT16_MAX 0xffff -#define INT32_MAX 0x7fffffff + +static gms_gamma_t GMS_GAMMA_TABLE[] = { + { "0.90", { 0, 29, 63, 99, 137, 175, 215, 255 }, true }, + { "0.95", { 0, 33, 68, 105, 141, 179, 217, 255 }, true }, + { "1.00", { 0, 36, 73, 109, 146, 182, 219, 255 }, false }, + { "1.05", { 0, 40, 77, 114, 150, 185, 220, 255 }, true }, + { "1.10", { 0, 43, 82, 118, 153, 188, 222, 255 }, true }, + { "1.15", { 0, 47, 86, 122, 157, 190, 223, 255 }, true }, + { "1.20", { 0, 50, 90, 126, 160, 193, 224, 255 }, true }, + { "1.25", { 0, 54, 94, 129, 163, 195, 225, 255 }, true }, + { "1.30", { 0, 57, 97, 133, 166, 197, 226, 255 }, true }, + { "1.35", { 0, 60, 101, 136, 168, 199, 227, 255 }, true }, + { "1.40", { 0, 64, 104, 139, 171, 201, 228, 255 }, true }, + { "1.45", { 0, 67, 107, 142, 173, 202, 229, 255 }, true }, + { "1.50", { 0, 70, 111, 145, 176, 204, 230, 255 }, true }, + { "1.55", { 0, 73, 114, 148, 178, 205, 231, 255 }, true }, + { "1.60", { 0, 76, 117, 150, 180, 207, 232, 255 }, true }, + { "1.65", { 0, 78, 119, 153, 182, 208, 232, 255 }, true }, + { "1.70", { 0, 81, 122, 155, 183, 209, 233, 255 }, true }, + { "1.75", { 0, 84, 125, 157, 185, 210, 233, 255 }, true }, + { "1.80", { 0, 87, 127, 159, 187, 212, 234, 255 }, true }, + { "1.85", { 0, 89, 130, 161, 188, 213, 235, 255 }, true }, + { "1.90", { 0, 92, 132, 163, 190, 214, 235, 255 }, true }, + { "1.95", { 0, 94, 134, 165, 191, 215, 236, 255 }, true }, + { "2.00", { 0, 96, 136, 167, 193, 216, 236, 255 }, true }, + { "2.05", { 0, 99, 138, 169, 194, 216, 237, 255 }, true }, + { "2.10", { 0, 101, 140, 170, 195, 217, 237, 255 }, true }, + { "2.15", { 0, 103, 142, 172, 197, 218, 237, 255 }, true }, + { "2.20", { 0, 105, 144, 173, 198, 219, 238, 255 }, true }, + { "2.25", { 0, 107, 146, 175, 199, 220, 238, 255 }, true }, + { "2.30", { 0, 109, 148, 176, 200, 220, 238, 255 }, true }, + { "2.35", { 0, 111, 150, 178, 201, 221, 239, 255 }, true }, + { "2.40", { 0, 113, 151, 179, 202, 222, 239, 255 }, true }, + { "2.45", { 0, 115, 153, 180, 203, 222, 239, 255 }, true }, + { "2.50", { 0, 117, 154, 182, 204, 223, 240, 255 }, true }, + { "2.55", { 0, 119, 156, 183, 205, 223, 240, 255 }, true }, + { "2.60", { 0, 121, 158, 184, 206, 224, 240, 255 }, true }, + { "2.65", { 0, 122, 159, 185, 206, 225, 241, 255 }, true }, + { "2.70", { 0, 124, 160, 186, 207, 225, 241, 255 }, true }, + { NULL, { 0, 0, 0, 0, 0, 0, 0, 0 }, false } +}; + +static gms_abbreviation_t GMS_ABBREVIATIONS[] = { + {'c', "close"}, {'g', "again"}, {'i', "inventory"}, + {'k', "attack"}, {'l', "look"}, {'p', "open"}, + {'q', "quit"}, {'r', "drop"}, {'t', "take"}, + {'x', "examine"}, {'y', "yes"}, {'z', "wait"}, + {'\0', NULL} +}; /*---------------------------------------------------------------------*/ -/* Module variables, miscellaneous other stuff */ +/* Module constants */ /*---------------------------------------------------------------------*/ +/* CRC table initialization polynomial. */ +static const glui32 GMS_CRC_POLYNOMIAL = 0xedb88320; + /* Glk Magnetic Scrolls port version number. */ static const glui32 GMS_PORT_VERSION = 0x00010601; +/* Magnetic Scrolls standard input prompt string. */ +static const char *const GMS_INPUT_PROMPT = ">"; + /* - * We use a maximum of five Glk windows, one for status, one for pictures, - * two for hints, and one for everything else. The status and pictures - * windows may be NULL, depending on user selections and the capabilities - * of the Glk library. The hints windows will normally be NULL, except - * when in the hints subsystem. + * Weighting values for calculating the luminance of a color. There are + * two commonly used sets of values for these -- 299,587,114, taken from + * NTSC (Never The Same Color) 1953 standards, and 212,716,72, which is the + * set that modern CRTs tend to match. The NTSC ones seem to give the best + * subjective results. + */ +static const gms_rgb_t GMS_LUMINANCE_WEIGHTS = { 299, 587, 114 }; + +/* + * Maximum number of regions to consider in a single repaint pass. A + * couple of hundred seems to strike the right balance between not too + * sluggardly picture updates, and responsiveness to input during graphics + * rendering, when combined with short timeouts. + */ +static const int GMS_REPAINT_LIMIT = 256; + +/* + * Graphics timeout; we like an update call after this period (ms). In + * practice, this timeout may actually be shorter than the time taken + * to reach the limit on repaint regions, but because Glk guarantees that + * user interactions (in this case, line events) take precedence over + * timeouts, this should be okay; we'll still see a game that responds to + * input each time the background repaint function yields. + * + * Setting this value is tricky. We'd like it to be the shortest possible + * consistent with getting other stuff done, say 10ms. However, Xglk has + * a granularity of 50ms on checking for timeouts, as it uses a 1/20s + * timeout on X select. This means that the shortest timeout we'll ever + * get from Xglk will be 50ms, so there's no point in setting this shorter + * than that. With luck, other Glk libraries will be more efficient than + * this, and can give us higher timer resolution; we'll set 50ms here, and + * hope that no other Glk library is worse. */ -static winid_t gms_main_window = NULL, - gms_status_window = NULL, - gms_graphics_window = NULL, - gms_hint_menu_window = NULL, - gms_hint_text_window = NULL; +static const glui32 GMS_GRAPHICS_TIMEOUT = 50; /* - * Transcript stream and input log. These are NULL if there is no current - * collection of these strings. + * Count of timeouts to wait in between animation paints, and to wait on + * repaint request. Waiting for 2 timeouts of around 50ms, gets us to the + * 100ms recommended animation frame rate. Waiting after a repaint smooths + * the display where the frame is being resized, by helping to avoid + * graphics output while more resize events are received; around 1/2 second + * seems okay. */ -static strid_t gms_transcript_stream = NULL, - gms_inputlog_stream = NULL; +static const int GMS_GRAPHICS_ANIMATION_WAIT = 2, +GMS_GRAPHICS_REPAINT_WAIT = 10; -/* Input read log stream, for reading back an input log. */ -static strid_t gms_readlog_stream = NULL; +/* Pixel size multiplier for image size scaling. */ +static const int GMS_GRAPHICS_PIXEL = 2; -/* Note about whether graphics is possible, or not. */ -static int gms_graphics_possible = TRUE; +/* Proportion of the display to use for graphics. */ +static const glui32 GMS_GRAPHICS_PROPORTION = 60; -/* Magnetic Scrolls standard input prompt string. */ -static const char *const GMS_INPUT_PROMPT = ">"; +/* + * Border and shading control. For cases where we can't detect the back- + * ground color of the main window, there's a default, white, background. + * Bordering is black, with a 1 pixel border, 2 pixel shading, and 8 steps + * of shading fade. + */ +static const glui32 GMS_GRAPHICS_DEFAULT_BACKGROUND = 0x00ffffff, + GMS_GRAPHICS_BORDER_COLOR = 0x00000000; +static const int GMS_GRAPHICS_BORDER = 1, + GMS_GRAPHICS_SHADING = 2, + GMS_GRAPHICS_SHADE_STEPS = 8; -/* Forward declaration of event wait function. */ -static void gms_event_wait(glui32 wait_type, event_t *event); +/* + * Guaranteed unused pixel value. This value is used to fill the on-screen + * buffer on new pictures or repaints, resulting in a full paint of all + * pixels since no off-screen, real picture, pixel will match it. + */ +static const int GMS_GRAPHICS_UNUSED_PIXEL = 0xff; +/* Default width used for non-windowing Glk status lines. */ +static const int GMS_DEFAULT_STATUS_WIDTH = 74; + +/* Success and fail return codes from hint functions. */ +static const type8 GMS_HINT_SUCCESS = 1, +GMS_HINT_ERROR = 0; + +/* Default window sizes for non-windowing Glk libraries. */ +static const glui32 GMS_HINT_DEFAULT_WIDTH = 72, +GMS_HINT_DEFAULT_HEIGHT = 25; + +/* + * Special hint nodes indicating the root hint node, and a value to signal + * quit from hints subsystem. + */ +static const type16 GMS_HINT_ROOT_NODE = 0, +GMS_HINTS_DONE = UINT16_MAX; + +/* Generic hint topic for the root hints node. */ +static const char *const GMS_GENERIC_TOPIC = "Hints Menu"; /*---------------------------------------------------------------------*/ /* Glk port utility functions */ /*---------------------------------------------------------------------*/ -/* - * gms_fatal() - * - * Fatal error handler. The function returns, expecting the caller to - * abort() or otherwise handle the error. - */ -static void gms_fatal(const char *string) { +void Magnetic::gms_fatal(const char *str) { /* * If the failure happens too early for us to have a window, print * the message to stderr. */ if (!gms_main_window) - error("\n\nINTERNAL ERROR: %s", string); + error("\n\nINTERNAL ERROR: %s", str); /* Cancel all possible pending window input events. */ - g_vm->glk_cancel_line_event(gms_main_window, NULL); - g_vm->glk_cancel_char_event(gms_main_window); + glk_cancel_line_event(gms_main_window, NULL); + glk_cancel_char_event(gms_main_window); if (gms_hint_menu_window) { - g_vm->glk_cancel_char_event(gms_hint_menu_window); - g_vm->glk_window_close(gms_hint_menu_window, NULL); + glk_cancel_char_event(gms_hint_menu_window); + glk_window_close(gms_hint_menu_window, NULL); } if (gms_hint_text_window) { - g_vm->glk_cancel_char_event(gms_hint_text_window); - g_vm->glk_window_close(gms_hint_text_window, NULL); + glk_cancel_char_event(gms_hint_text_window); + glk_window_close(gms_hint_text_window, NULL); } /* Print a message indicating the error. */ - g_vm->glk_set_window(gms_main_window); - g_vm->glk_set_style(style_Normal); - g_vm->glk_put_string("\n\nINTERNAL ERROR: "); - g_vm->glk_put_string(string); + glk_set_window(gms_main_window); + glk_set_style(style_Normal); + glk_put_string("\n\nINTERNAL ERROR: "); + glk_put_string(str); - g_vm->glk_put_string("\n\nPlease record the details of this error, try to" + glk_put_string("\n\nPlease record the details of this error, try to" " note down everything you did to cause it, and email" " this information to simon_baldwin@yahoo.com.\n\n"); } - -/* - * gms_malloc() - * gms_realloc() - * - * Non-failing malloc and realloc; call gms_fatal and exit if memory - * allocation fails. - */ -static void *gms_malloc(size_t size) { +void *Magnetic::gms_malloc(size_t size) { void *pointer; pointer = malloc(size); if (!pointer) { gms_fatal("GLK: Out of system memory"); - g_vm->glk_exit(); + glk_exit(); } return pointer; } -static void *gms_realloc(void *ptr, size_t size) { +void *Magnetic::gms_realloc(void *ptr, size_t size) { void *pointer; pointer = realloc(ptr, size); if (!pointer) { gms_fatal("GLK: Out of system memory"); - g_vm->glk_exit(); + glk_exit(); } return pointer; } - -/* - * gms_strncasecmp() - * gms_strcasecmp() - * - * Strncasecmp and strcasecmp are not ANSI functions, so here are local - * definitions to do the same jobs. - */ -static int gms_strncasecmp(const char *s1, const char *s2, size_t n) { +int Magnetic::gms_strncasecmp(const char *s1, const char *s2, size_t n) { size_t index; for (index = 0; index < n; index++) { int diff; - diff = g_vm->glk_char_to_lower(s1[index]) - g_vm->glk_char_to_lower(s2[index]); + diff = glk_char_to_lower(s1[index]) - glk_char_to_lower(s2[index]); if (diff < 0 || diff > 0) return diff < 0 ? -1 : 1; } @@ -178,7 +274,7 @@ static int gms_strncasecmp(const char *s1, const char *s2, size_t n) { return 0; } -static int gms_strcasecmp(const char *s1, const char *s2) { +int Magnetic::gms_strcasecmp(const char *s1, const char *s2) { size_t s1len, s2len; int result; @@ -196,22 +292,11 @@ static int gms_strcasecmp(const char *s1, const char *s2) { /* Glk port CRC functions */ /*---------------------------------------------------------------------*/ -/* CRC table initialization polynomial. */ -static const glui32 GMS_CRC_POLYNOMIAL = 0xedb88320; - - -/* - * gms_get_buffer_crc() - * - * Return the CRC of the bytes in buffer[0..length-1]. - * - * This algorithm is taken from the PNG specification, version 1.0. - */ -static glui32 gms_get_buffer_crc(const void *void_buffer, size_t length) { - static int is_initialized = FALSE; +glui32 Magnetic::gms_get_buffer_crc(const void *void_buffer, size_t length) { + static int is_initialized = false; static glui32 crc_table[BYTE_MAX + 1]; - const char *buffer = (const char *) void_buffer; + const char *buf = (const char *) void_buffer; glui32 crc; size_t index; @@ -227,7 +312,7 @@ static glui32 gms_get_buffer_crc(const void *void_buffer, size_t length) { crc_table[index] = crc; } - is_initialized = TRUE; + is_initialized = true; /* CRC lookup table self-test, after is_initialized set -- recursion. */ assert(gms_get_buffer_crc("123456789", 9) == 0xcbf43926); @@ -239,363 +324,45 @@ static glui32 gms_get_buffer_crc(const void *void_buffer, size_t length) { */ crc = 0xffffffff; for (index = 0; index < length; index++) - crc = crc_table[(crc ^ buffer[index]) & BYTE_MAX] ^ (crc >> CHAR_BIT); + crc = crc_table[(crc ^ buf[index]) & BYTE_MAX] ^ (crc >> CHAR_BIT); return crc ^ 0xffffffff; } - /*---------------------------------------------------------------------*/ /* Glk port game identification data and identification functions */ /*---------------------------------------------------------------------*/ -/* - * The game's name, suitable for printing out on a status line, or other - * location where game information is relevant. Set on game startup, by - * identifying the game from its text file header. - */ -static const char *gms_gameid_game_name = NULL; - - -/* - * The following game database is built from Generic/games.txt, and is used - * to identify the game being run. Magnetic Scrolls games don't generally - * supply a status line, so this data can be used instead. - */ -struct gms_game_table_t { - const type32 undo_size; /* Header word at offset 0x22 */ - const type32 undo_pc; /* Header word at offset 0x26 */ - const char *const name; /* Game title and platform */ -}; -typedef const gms_game_table_t *gms_game_tableref_t; - -static const gms_game_table_t GMS_GAME_TABLE[] = { - {0x2100, 0x427e, "Corruption v1.11 (Amstrad CPC)"}, - {0x2100, 0x43a0, "Corruption v1.11 (Archimedes)"}, - {0x2100, 0x43a0, "Corruption v1.11 (DOS)"}, - {0x2100, 0x4336, "Corruption v1.11 (Commodore 64)"}, - {0x2100, 0x4222, "Corruption v1.11 (Spectrum +3)"}, - {0x2100, 0x4350, "Corruption v1.12 (Archimedes)"}, - {0x2500, 0x6624, "Corruption v1.12 (DOS, Magnetic Windows)"}, - - {0x2300, 0x3fa0, "Fish v1.02 (DOS)"}, - {0x2400, 0x4364, "Fish v1.03 (Spectrum +3)"}, - {0x2300, 0x3f72, "Fish v1.07 (Commodore 64)"}, - {0x2200, 0x3f9c, "Fish v1.08 (Archimedes)"}, - {0x2a00, 0x583a, "Fish v1.10 (DOS, Magnetic Windows)"}, - - {0x5000, 0x6c30, "Guild v1.0 (Amstrad CPC)"}, - {0x5000, 0x6cac, "Guild v1.0 (Commodore 64)"}, - {0x5000, 0x6d5c, "Guild v1.1 (DOS)"}, - {0x3300, 0x698a, "Guild v1.3 (Archimedes)"}, - {0x3200, 0x6772, "Guild v1.3 (Spectrum +3)"}, - {0x3400, 0x6528, "Guild v1.3 (DOS, Magnetic Windows)"}, - - {0x2b00, 0x488c, "Jinxter v1.05 (Commodore 64)"}, - {0x2c00, 0x4a08, "Jinxter v1.05 (DOS)"}, - {0x2c00, 0x487a, "Jinxter v1.05 (Spectrum +3)"}, - {0x2c00, 0x4a56, "Jinxter v1.10 (DOS)"}, - {0x2b00, 0x4924, "Jinxter v1.22 (Amstrad CPC)"}, - {0x2c00, 0x4960, "Jinxter v1.30 (Archimedes)"}, - - {0x1600, 0x3940, "Myth v1.0 (Commodore 64)"}, - {0x1500, 0x3a0a, "Myth v1.0 (DOS)"}, - - {0x3600, 0x42cc, "Pawn v2.3 (Amstrad CPC)"}, - {0x3600, 0x4420, "Pawn v2.3 (Archimedes)"}, - {0x3600, 0x3fb0, "Pawn v2.3 (Commodore 64)"}, - {0x3600, 0x4420, "Pawn v2.3 (DOS)"}, - {0x3900, 0x42e4, "Pawn v2.3 (Spectrum 128)"}, - {0x3900, 0x42f4, "Pawn v2.4 (Spectrum +3)"}, - - {0x3900, 0x75f2, "Wonderland v1.21 (DOS, Magnetic Windows)"}, - {0x3900, 0x75f8, "Wonderland v1.27 (Archimedes)"}, - {0, 0, NULL} -}; - - -/* - * gms_gameid_lookup_game() - * - * Look up and return the game table entry given a game's undo size and - * undo pc values. Returns the entry, or NULL if not found. - */ -static gms_game_tableref_t gms_gameid_lookup_game(type32 undo_size, type32 undo_pc) { - gms_game_tableref_t game; - - for (game = GMS_GAME_TABLE; game->name; game++) { - if (game->undo_size == undo_size && game->undo_pc == undo_pc) - break; - } - - return game->name ? game : NULL; -} - - -/* - * gms_gameid_read_uint32() - * - * Endian-safe unsigned 32 bit integer read from game text file. Returns - * 0 on error, a known unused table value. - */ -static type32 gms_gameid_read_uint32(int offset, Common::SeekableReadStream *stream) { +type32 Magnetic::gms_gameid_read_uint32(int offset, Common::SeekableReadStream *stream) { if (!stream->seek(offset)) return 0; return stream->readUint32BE(); } - -/* - * gms_gameid_identify_game() - * - * Identify a game from its text file header, and cache the game's name for - * later queries. Sets the cache to NULL if not found. - */ -static void gms_gameid_identify_game(const Common::String &text_file) { +void Magnetic::gms_gameid_identify_game(const Common::String &text_file) { Common::File stream; if (!stream.open(text_file)) error("Error opening game file"); - type32 undo_size, undo_pc; + type32 game_size, game_pc; gms_game_tableref_t game; /* Read the game's signature undo size and undo pc values. */ - undo_size = gms_gameid_read_uint32(0x22, &stream); - undo_pc = gms_gameid_read_uint32(0x26, &stream); + game_size = gms_gameid_read_uint32(0x22, &stream); + game_pc = gms_gameid_read_uint32(0x26, &stream); /* Search for these values in the table, and set game name if found. */ - game = gms_gameid_lookup_game(undo_size, undo_pc); + game = gms_gameid_lookup_game(game_size, game_pc); gms_gameid_game_name = game ? game->name : NULL; } - -/* - * gms_gameid_get_game_name() - * - * Return the name of the game, or NULL if not identifiable. - */ -static const char *gms_gameid_get_game_name(void) { - return gms_gameid_game_name; -} - - /*---------------------------------------------------------------------*/ /* Glk port picture functions */ /*---------------------------------------------------------------------*/ -/* - * Color conversions lookup tables, and a word about gamma corrections. - * - * When uncorrected, some game pictures can look dark (Corruption, Won- - * derland), whereas others look just fine (Guild Of Thieves, Jinxter). - * - * The standard general-purpose gamma correction is around 2.1, with - * specific values, normally, of 2.5-2.7 for IBM PC systems, and 1.8 for - * Macintosh. However, applying even the low end of this range can make - * some pictures look washed out, yet improve others nicely. - * - * To try to solve this, here we'll set up a precalculated table with - * discrete gamma values. On displaying a picture, we'll try to find a - * gamma correction that seems to offer a reasonable level of contrast - * for the picture. - * - * Here's an AWK script to create the gamma table: - * - * BEGIN { max=255.0; step=max/7.0 - * for (gamma=0.9; gamma<=2.7; gamma+=0.05) { - * printf " {\"%2.2f\", {0, ", gamma - * for (i=1; i<8; i++) { - * printf "%3.0f", (((step*i / max) ^ (1.0/gamma)) * max) - * printf "%s", (i<7) ? ", " : "" - * } - * printf "}, " - * printf "%s },\n", (gamma>0.99 && gamma<1.01) ? "FALSE" : "TRUE " - * } } - * - */ -typedef const struct { - const char *const level; /* Gamma correction level. */ - const unsigned char table[8]; /* Color lookup table. */ - const int is_corrected; /* Flag if non-linear. */ -} gms_gamma_t; -typedef gms_gamma_t *gms_gammaref_t; - -static gms_gamma_t GMS_GAMMA_TABLE[] = { - {"0.90", {0, 29, 63, 99, 137, 175, 215, 255}, TRUE}, - {"0.95", {0, 33, 68, 105, 141, 179, 217, 255}, TRUE}, - {"1.00", {0, 36, 73, 109, 146, 182, 219, 255}, FALSE}, - {"1.05", {0, 40, 77, 114, 150, 185, 220, 255}, TRUE}, - {"1.10", {0, 43, 82, 118, 153, 188, 222, 255}, TRUE}, - {"1.15", {0, 47, 86, 122, 157, 190, 223, 255}, TRUE}, - {"1.20", {0, 50, 90, 126, 160, 193, 224, 255}, TRUE}, - {"1.25", {0, 54, 94, 129, 163, 195, 225, 255}, TRUE}, - {"1.30", {0, 57, 97, 133, 166, 197, 226, 255}, TRUE}, - {"1.35", {0, 60, 101, 136, 168, 199, 227, 255}, TRUE}, - {"1.40", {0, 64, 104, 139, 171, 201, 228, 255}, TRUE}, - {"1.45", {0, 67, 107, 142, 173, 202, 229, 255}, TRUE}, - {"1.50", {0, 70, 111, 145, 176, 204, 230, 255}, TRUE}, - {"1.55", {0, 73, 114, 148, 178, 205, 231, 255}, TRUE}, - {"1.60", {0, 76, 117, 150, 180, 207, 232, 255}, TRUE}, - {"1.65", {0, 78, 119, 153, 182, 208, 232, 255}, TRUE}, - {"1.70", {0, 81, 122, 155, 183, 209, 233, 255}, TRUE}, - {"1.75", {0, 84, 125, 157, 185, 210, 233, 255}, TRUE}, - {"1.80", {0, 87, 127, 159, 187, 212, 234, 255}, TRUE}, - {"1.85", {0, 89, 130, 161, 188, 213, 235, 255}, TRUE}, - {"1.90", {0, 92, 132, 163, 190, 214, 235, 255}, TRUE}, - {"1.95", {0, 94, 134, 165, 191, 215, 236, 255}, TRUE}, - {"2.00", {0, 96, 136, 167, 193, 216, 236, 255}, TRUE}, - {"2.05", {0, 99, 138, 169, 194, 216, 237, 255}, TRUE}, - {"2.10", {0, 101, 140, 170, 195, 217, 237, 255}, TRUE}, - {"2.15", {0, 103, 142, 172, 197, 218, 237, 255}, TRUE}, - {"2.20", {0, 105, 144, 173, 198, 219, 238, 255}, TRUE}, - {"2.25", {0, 107, 146, 175, 199, 220, 238, 255}, TRUE}, - {"2.30", {0, 109, 148, 176, 200, 220, 238, 255}, TRUE}, - {"2.35", {0, 111, 150, 178, 201, 221, 239, 255}, TRUE}, - {"2.40", {0, 113, 151, 179, 202, 222, 239, 255}, TRUE}, - {"2.45", {0, 115, 153, 180, 203, 222, 239, 255}, TRUE}, - {"2.50", {0, 117, 154, 182, 204, 223, 240, 255}, TRUE}, - {"2.55", {0, 119, 156, 183, 205, 223, 240, 255}, TRUE}, - {"2.60", {0, 121, 158, 184, 206, 224, 240, 255}, TRUE}, - {"2.65", {0, 122, 159, 185, 206, 225, 241, 255}, TRUE}, - {"2.70", {0, 124, 160, 186, 207, 225, 241, 255}, TRUE}, - {NULL, {0, 0, 0, 0, 0, 0, 0, 0}, FALSE} -}; - -/* R,G,B color triple definition. */ -typedef struct { - int red, green, blue; -} gms_rgb_t; -typedef gms_rgb_t *gms_rgbref_t; - -/* - * Weighting values for calculating the luminance of a color. There are - * two commonly used sets of values for these -- 299,587,114, taken from - * NTSC (Never The Same Color) 1953 standards, and 212,716,72, which is the - * set that modern CRTs tend to match. The NTSC ones seem to give the best - * subjective results. - */ -static const gms_rgb_t GMS_LUMINANCE_WEIGHTS = { 299, 587, 114 }; - -/* - * Maximum number of regions to consider in a single repaint pass. A - * couple of hundred seems to strike the right balance between not too - * sluggardly picture updates, and responsiveness to input during graphics - * rendering, when combined with short timeouts. - */ -static const int GMS_REPAINT_LIMIT = 256; - -/* - * Graphics timeout; we like an update call after this period (ms). In - * practice, this timeout may actually be shorter than the time taken - * to reach the limit on repaint regions, but because Glk guarantees that - * user interactions (in this case, line events) take precedence over - * timeouts, this should be okay; we'll still see a game that responds to - * input each time the background repaint function yields. - * - * Setting this value is tricky. We'd like it to be the shortest possible - * consistent with getting other stuff done, say 10ms. However, Xglk has - * a granularity of 50ms on checking for timeouts, as it uses a 1/20s - * timeout on X select. This means that the shortest timeout we'll ever - * get from Xglk will be 50ms, so there's no point in setting this shorter - * than that. With luck, other Glk libraries will be more efficient than - * this, and can give us higher timer resolution; we'll set 50ms here, and - * hope that no other Glk library is worse. - */ -static const glui32 GMS_GRAPHICS_TIMEOUT = 50; - -/* - * Count of timeouts to wait in between animation paints, and to wait on - * repaint request. Waiting for 2 timeouts of around 50ms, gets us to the - * 100ms recommended animation frame rate. Waiting after a repaint smooths - * the display where the frame is being resized, by helping to avoid - * graphics output while more resize events are received; around 1/2 second - * seems okay. - */ -static const int GMS_GRAPHICS_ANIMATION_WAIT = 2, - GMS_GRAPHICS_REPAINT_WAIT = 10; - -/* Pixel size multiplier for image size scaling. */ -static const int GMS_GRAPHICS_PIXEL = 2; - -/* Proportion of the display to use for graphics. */ -static const glui32 GMS_GRAPHICS_PROPORTION = 60; - -/* - * Border and shading control. For cases where we can't detect the back- - * ground color of the main window, there's a default, white, background. - * Bordering is black, with a 1 pixel border, 2 pixel shading, and 8 steps - * of shading fade. - */ -static const glui32 GMS_GRAPHICS_DEFAULT_BACKGROUND = 0x00ffffff, - GMS_GRAPHICS_BORDER_COLOR = 0x00000000; -static const int GMS_GRAPHICS_BORDER = 1, - GMS_GRAPHICS_SHADING = 2, - GMS_GRAPHICS_SHADE_STEPS = 8; - -/* - * Guaranteed unused pixel value. This value is used to fill the on-screen - * buffer on new pictures or repaints, resulting in a full paint of all - * pixels since no off-screen, real picture, pixel will match it. - */ -static const int GMS_GRAPHICS_UNUSED_PIXEL = 0xff; - -/* - * The current picture bitmap being displayed, its width, height, palette, - * animation flag, and picture id. - */ -enum { GMS_PALETTE_SIZE = 16 }; -static type8 *gms_graphics_bitmap = NULL; -static type16 gms_graphics_width = 0, - gms_graphics_height = 0, - gms_graphics_palette[GMS_PALETTE_SIZE]; /* = { 0, ... }; */ -static bool gms_graphics_animated = FALSE; -static type32 gms_graphics_picture = 0; - -/* - * Flags set on new picture, and on resize or arrange events, and a flag - * to indicate whether background repaint is stopped or active. - */ -static bool gms_graphics_new_picture = FALSE, - gms_graphics_repaint = FALSE, - gms_graphics_active = FALSE; - -/* Flag to try to monitor the state of interpreter graphics. */ -static bool gms_graphics_interpreter = FALSE; - -/* - * Pointer to the two graphics buffers, one the off-screen representation - * of pixels, and the other tracking on-screen data. These are temporary - * graphics malloc'ed memory, and should be free'd on exit. - */ -static type8 *gms_graphics_off_screen = NULL, - *gms_graphics_on_screen = NULL; - -/* - * Pointer to the current active gamma table entry. Because of the way - * it's queried, this may not be NULL, otherwise we risk a race, with - * admittedly a very low probability, with the updater. So, it's init- - * ialized instead to the gamma table. The real value in use is inserted - * on the first picture update timeout call for a new picture. - */ -static gms_gammaref_t gms_graphics_current_gamma = GMS_GAMMA_TABLE; - -/* - * The number of colors used in the palette by the current picture. This - * value is also at risk of a race with the updater, so it too has a mild - * lie for a default value. - */ -static int gms_graphics_color_count = GMS_PALETTE_SIZE; - - -/* - * gms_graphics_open() - * - * If it's not open, open the graphics window. Returns TRUE if graphics - * was successfully started, or already on. - */ -static int gms_graphics_open(void) { +int Magnetic::gms_graphics_open() { if (!gms_graphics_window) { - gms_graphics_window = g_vm->glk_window_open(gms_main_window, + gms_graphics_window = glk_window_open(gms_main_window, winmethod_Above | winmethod_Proportional, GMS_GRAPHICS_PROPORTION, @@ -605,86 +372,41 @@ static int gms_graphics_open(void) { return gms_graphics_window != NULL; } - -/* - * gms_graphics_close() - * - * If open, close the graphics window and set back to NULL. - */ -static void gms_graphics_close(void) { +void Magnetic::gms_graphics_close() { if (gms_graphics_window) { - g_vm->glk_window_close(gms_graphics_window, NULL); + glk_window_close(gms_graphics_window, NULL); gms_graphics_window = NULL; } } - -/* - * gms_graphics_start() - * - * If graphics enabled, start any background picture update processing. - */ -static void gms_graphics_start(void) { - if (g_vm->gms_graphics_enabled) { +void Magnetic::gms_graphics_start() { + if (gms_graphics_enabled) { /* If not running, start the updating "thread". */ if (!gms_graphics_active) { - g_vm->glk_request_timer_events(GMS_GRAPHICS_TIMEOUT); - gms_graphics_active = TRUE; + glk_request_timer_events(GMS_GRAPHICS_TIMEOUT); + gms_graphics_active = true; } } } - -/* - * gms_graphics_stop() - * - * Stop any background picture update processing. - */ -static void gms_graphics_stop(void) { +void Magnetic::gms_graphics_stop() { /* If running, stop the updating "thread". */ if (gms_graphics_active) { - g_vm->glk_request_timer_events(0); - gms_graphics_active = FALSE; + glk_request_timer_events(0); + gms_graphics_active = false; } } - -/* - * gms_graphics_are_displayed() - * - * Return TRUE if graphics are currently being displayed, FALSE otherwise. - */ -static int gms_graphics_are_displayed(void) { - return gms_graphics_window != NULL; -} - - -/* - * gms_graphics_paint() - * - * Set up a complete repaint of the current picture in the graphics window. - * This function should be called on the appropriate Glk window resize and - * arrange events. - */ -static void gms_graphics_paint(void) { - if (g_vm->gms_graphics_enabled && gms_graphics_are_displayed()) { +void Magnetic::gms_graphics_paint() { + if (gms_graphics_enabled && gms_graphics_are_displayed()) { /* Set the repaint flag, and start graphics. */ - gms_graphics_repaint = TRUE; + gms_graphics_repaint = true; gms_graphics_start(); } } - -/* - * gms_graphics_restart() - * - * Restart graphics as if the current picture is a new picture. This - * function should be called whenever graphics is re-enabled after being - * disabled, on change of gamma color correction policy, and on change - * of animation policy. - */ -static void gms_graphics_restart(void) { - if (g_vm->gms_graphics_enabled && gms_graphics_are_displayed()) { +void Magnetic::gms_graphics_restart() { + if (gms_graphics_enabled && gms_graphics_are_displayed()) { /* * If the picture is animated, we'll need to be able to re-get the * first animation frame so that the picture can be treated as if @@ -715,21 +437,13 @@ static void gms_graphics_restart(void) { } /* Set the new picture flag, and start graphics. */ - gms_graphics_new_picture = TRUE; + gms_graphics_new_picture = true; gms_graphics_start(); } } - -/* - * gms_graphics_count_colors() - * - * Analyze an image, and return the usage count of each palette color, and - * an overall count of how many colors out of the palette are used. NULL - * arguments indicate no interest in the return value. - */ -static void gms_graphics_count_colors(type8 bitmap[], type16 width, type16 height, - int *color_count, long color_usage[]) { +void Magnetic::gms_graphics_count_colors(type8 bitmap[], type16 width, type16 height, + int *color_count, long color_usage[]) { int x, y, count; long usage[GMS_PALETTE_SIZE], index_row; assert(bitmap); @@ -762,20 +476,8 @@ static void gms_graphics_count_colors(type8 bitmap[], type16 width, type16 heigh memcpy(color_usage, usage, sizeof(usage)); } - -/* - * gms_graphics_game_to_rgb_color() - * gms_graphics_split_color() - * gms_graphics_combine_color() - * gms_graphics_color_luminance() - * - * General graphics helper functions, to convert between Magnetic Scrolls - * and RGB color representations, and between RGB and Glk glui32 color - * representations, and to calculate color luminance. - */ -static void -gms_graphics_game_to_rgb_color(type16 color, gms_gammaref_t gamma, - gms_rgbref_t rgb_color) { +void Magnetic::gms_graphics_game_to_rgb_color(type16 color, gms_gammaref_t gamma, + gms_rgbref_t rgb_color) { assert(gamma && rgb_color); /* @@ -789,7 +491,7 @@ gms_graphics_game_to_rgb_color(type16 color, gms_gammaref_t gamma, rgb_color->blue = gamma->table[(color & 0x007)]; } -static void gms_graphics_split_color(glui32 color, gms_rgbref_t rgb_color) { +void Magnetic::gms_graphics_split_color(glui32 color, gms_rgbref_t rgb_color) { assert(rgb_color); rgb_color->red = (color >> 16) & 0xff; @@ -797,7 +499,7 @@ static void gms_graphics_split_color(glui32 color, gms_rgbref_t rgb_color) { rgb_color->blue = color & 0xff; } -static glui32 gms_graphics_combine_color(gms_rgbref_t rgb_color) { +glui32 Magnetic::gms_graphics_combine_color(gms_rgbref_t rgb_color) { glui32 color; assert(rgb_color); @@ -805,8 +507,8 @@ static glui32 gms_graphics_combine_color(gms_rgbref_t rgb_color) { return color; } -static int gms_graphics_color_luminance(gms_rgbref_t rgb_color) { - static int is_initialized = FALSE; +int Magnetic::gms_graphics_color_luminance(gms_rgbref_t rgb_color) { + static int is_initialized = false; static int weighting = 0; long luminance; @@ -816,7 +518,7 @@ static int gms_graphics_color_luminance(gms_rgbref_t rgb_color) { weighting = GMS_LUMINANCE_WEIGHTS.red + GMS_LUMINANCE_WEIGHTS.green + GMS_LUMINANCE_WEIGHTS.blue; - is_initialized = TRUE; + is_initialized = true; } /* Calculate the luminance and scale back by 1000 to 0-255 before return. */ @@ -828,16 +530,7 @@ static int gms_graphics_color_luminance(gms_rgbref_t rgb_color) { return (int)(luminance / weighting); } - -/* - * gms_graphics_compare_luminance() - * gms_graphics_constrast_variance() - * - * Calculate the contrast variance of the given palette and color usage, at - * the given gamma correction level. Helper functions for automatic gamma - * correction. - */ -static int gms_graphics_compare_luminance(const void *void_first, +int Magnetic::gms_graphics_compare_luminance(const void *void_first, const void *void_second) { long first = *(const long *)void_first; long second = *(const long *)void_second; @@ -845,7 +538,7 @@ static int gms_graphics_compare_luminance(const void *void_first, return first > second ? 1 : second > first ? -1 : 0; } -static long gms_graphics_contrast_variance(type16 palette[], +long Magnetic::gms_graphics_contrast_variance(type16 palette[], long color_usage[], gms_gammaref_t gamma) { int index, count, has_black, mean; long sum; @@ -854,7 +547,7 @@ static long gms_graphics_contrast_variance(type16 palette[], plus one extra for black */ /* Calculate the luminance energy of each palette color at this gamma. */ - has_black = FALSE; + has_black = false; for (index = 0, count = 0; index < GMS_PALETTE_SIZE; index++) { if (color_usage[index] > 0) { gms_rgb_t rgb_color; @@ -903,27 +596,7 @@ static long gms_graphics_contrast_variance(type16 palette[], return sum / (count - 1); } - -/* - * gms_graphics_equal_contrast_gamma() - * - * Try to find a gamma correction for the given palette and color usage that - * gives relatively equal contrast among the displayed colors. - * - * To do this, we search the gamma tables, computing color luminance for each - * color in the palette given this gamma. From luminances, we then compute - * the contrasts between the colors, and settle on the gamma correction that - * gives the most even and well-distributed picture contrast. We ignore - * colors not used in the palette. - * - * Note that we don't consider how often a palette color is used, only whether - * it's represented, or not. Some weighting might improve things, but the - * simple method seems to work adequately. In practice, as there are only 16 - * colors in a palette, most pictures use most colors in a relatively well - * distributed manner. This algorithm probably wouldn't work well on real - * photographs, though. - */ -static gms_gammaref_t gms_graphics_equal_contrast_gamma(type16 palette[], long color_usage[]) { +gms_gammaref_t Magnetic::gms_graphics_equal_contrast_gamma(type16 palette[], long color_usage[]) { gms_gammaref_t gamma, result; long lowest_variance; assert(palette && color_usage); @@ -952,25 +625,9 @@ static gms_gammaref_t gms_graphics_equal_contrast_gamma(type16 palette[], long c return result; } - -/* - * gms_graphics_select_gamma() - * - * Select a suitable gamma for the picture, based on the current gamma mode. - * - * The function returns either the linear gamma, a gamma value half way - * between linear and the gamma that gives the most even contrast, or just - * the gamma that gives the most even contrast. - * - * In the normal case, a value half way to the extreme case of making color - * contrast equal for all colors is, subjectively, a reasonable value to use. - * The problem cases are the darkest pictures, and selecting this value - * brightens them while at the same time not making them look overbright or - * too "sunny". - */ -static gms_gammaref_t gms_graphics_select_gamma(type8 bitmap[], +gms_gammaref_t Magnetic::gms_graphics_select_gamma(type8 bitmap[], type16 width, type16 height, type16 palette[]) { - static int is_initialized = FALSE; + static int is_initialized = false; static gms_gammaref_t linear_gamma = NULL; long color_usage[GMS_PALETTE_SIZE]; @@ -988,7 +645,7 @@ static gms_gammaref_t gms_graphics_select_gamma(type8 bitmap[], } } - is_initialized = TRUE; + is_initialized = true; } assert(linear_gamma); @@ -996,7 +653,7 @@ static gms_gammaref_t gms_graphics_select_gamma(type8 bitmap[], * Check to see if automated correction is turned off; if it is, return * the linear gamma. */ - if (g_vm->gms_gamma_mode == GAMMA_OFF) + if (gms_gamma_mode == GAMMA_OFF) return linear_gamma; /* @@ -1017,23 +674,15 @@ static gms_gammaref_t gms_graphics_select_gamma(type8 bitmap[], * For normal automated correction, return a gamma value half way between * the linear gamma and the equal contrast gamma. */ - if (g_vm->gms_gamma_mode == GAMMA_NORMAL) + if (gms_gamma_mode == GAMMA_NORMAL) return linear_gamma + (contrast_gamma - linear_gamma) / 2; /* Correction must be high; return the equal contrast gamma. */ - assert(g_vm->gms_gamma_mode == GAMMA_HIGH); + assert(gms_gamma_mode == GAMMA_HIGH); return contrast_gamma; } - -/* - * gms_graphics_clear_and_border() - * - * Clear the graphics window, and border and shade the area where the - * picture is going to be rendered. This attempts a small raised effect - * for the picture, in keeping with modern trends. - */ -static void gms_graphics_clear_and_border(winid_t glk_window, +void Magnetic::gms_graphics_clear_and_border(winid_t glk_window, int x_offset, int y_offset, int pixel_size, type16 width, type16 height) { glui32 background, fade_color, shading_color; gms_rgb_t rgb_background, rgb_border, rgb_fade; @@ -1046,7 +695,7 @@ static void gms_graphics_clear_and_border(winid_t glk_window, * background color). If we can get it, we'll match the graphics window * background to it. If we can't, we'll default the color to white. */ - if (!g_vm->glk_style_measure(gms_main_window, + if (!glk_style_measure(gms_main_window, style_Normal, stylehint_BackColor, &background)) { /* * Unable to get the main window background, so assume, and default @@ -1059,8 +708,8 @@ static void gms_graphics_clear_and_border(winid_t glk_window, * Set the graphics window background to match the main window background, * as best as we can tell, and clear the window. */ - g_vm->glk_window_set_background_color(glk_window, background); - g_vm->glk_window_clear(glk_window); + glk_window_set_background_color(glk_window, background); + glk_window_clear(glk_window); /* * For very small pictures, just border them, but don't try and do any @@ -1069,7 +718,7 @@ static void gms_graphics_clear_and_border(winid_t glk_window, if (width < 2 * GMS_GRAPHICS_SHADE_STEPS || height < 2 * GMS_GRAPHICS_SHADE_STEPS) { /* Paint a rectangle bigger than the picture by border pixels. */ - g_vm->glk_window_fill_rect(glk_window, + glk_window_fill_rect(glk_window, GMS_GRAPHICS_BORDER_COLOR, x_offset - GMS_GRAPHICS_BORDER, y_offset - GMS_GRAPHICS_BORDER, @@ -1084,7 +733,7 @@ static void gms_graphics_clear_and_border(winid_t glk_window, * shading pixels are later overwritten by the fading loop below. The * picture will sit over this rectangle. */ - g_vm->glk_window_fill_rect(glk_window, + glk_window_fill_rect(glk_window, GMS_GRAPHICS_BORDER_COLOR, x_offset - GMS_GRAPHICS_BORDER, y_offset - GMS_GRAPHICS_BORDER, @@ -1119,12 +768,12 @@ static void gms_graphics_clear_and_border(winid_t glk_window, shading_color = background; for (index = 0; index < GMS_GRAPHICS_SHADE_STEPS; index++) { /* Shade the two border areas with this color. */ - g_vm->glk_window_fill_rect(glk_window, shading_color, + glk_window_fill_rect(glk_window, shading_color, x_offset + width * pixel_size + GMS_GRAPHICS_BORDER, y_offset + index - GMS_GRAPHICS_BORDER, GMS_GRAPHICS_SHADING, 1); - g_vm->glk_window_fill_rect(glk_window, shading_color, + glk_window_fill_rect(glk_window, shading_color, x_offset + index - GMS_GRAPHICS_BORDER, y_offset + height * pixel_size + GMS_GRAPHICS_BORDER, @@ -1135,14 +784,7 @@ static void gms_graphics_clear_and_border(winid_t glk_window, } } - -/* - * gms_graphics_convert_palette() - * - * Convert a Magnetic Scrolls color palette to a Glk one, using the given - * gamma corrections. - */ -static void gms_graphics_convert_palette(type16 ms_palette[], gms_gammaref_t gamma, +void Magnetic::gms_graphics_convert_palette(type16 ms_palette[], gms_gammaref_t gamma, glui32 glk_palette[]) { int index; assert(ms_palette && gamma && glk_palette); @@ -1159,21 +801,13 @@ static void gms_graphics_convert_palette(type16 ms_palette[], gms_gammaref_t gam } } - -/* - * gms_graphics_position_picture() - * - * Given a picture width and height, return the x and y offsets to center - * this picture in the current graphics window. - */ -static void gms_graphics_position_picture(winid_t glk_window, - int pixel_size, type16 width, type16 height, - int *x_offset, int *y_offset) { +void Magnetic::gms_graphics_position_picture(winid_t glk_window, + int pixel_size, type16 width, type16 height, int *x_offset, int *y_offset) { glui32 window_width, window_height; assert(glk_window && x_offset && y_offset); /* Measure the current graphics window dimensions. */ - g_vm->glk_window_get_size(glk_window, &window_width, &window_height); + glk_window_get_size(glk_window, &window_width, &window_height); /* * Calculate and return an x and y offset to use on point plotting, so that @@ -1183,21 +817,9 @@ static void gms_graphics_position_picture(winid_t glk_window, *y_offset = ((int) window_height - height * pixel_size) / 2; } - -/* - * gms_graphics_apply_animation_frame() - * - * Apply a single animation frame to the given off-screen image buffer, using - * the frame bitmap, width, height and mask, the off-screen buffer, and the - * width and height of the main picture. - * - * Note that 'mask' may be NULL, implying that no frame pixel is transparent. - */ -static void gms_graphics_apply_animation_frame(type8 bitmap[], - type16 frame_width, type16 frame_height, - type8 mask[], int frame_x, int frame_y, - type8 off_screen[], type16 width, - type16 height) { +void Magnetic::gms_graphics_apply_animation_frame(type8 bitmap[], + type16 frame_width, type16 frame_height, type8 mask[], int frame_x, int frame_y, + type8 off_screen[], type16 width, type16 height) { int mask_width, x, y; type8 mask_hibit; long frame_row, buffer_row, mask_row; @@ -1273,18 +895,7 @@ static void gms_graphics_apply_animation_frame(type8 bitmap[], } } - -/* - * gms_graphics_animate() - * - * This function finds and applies the next set of animation frames to the - * given off-screen image buffer. It's handed the width and height of the - * main picture, and the off-screen buffer. - * - * It returns FALSE if at the end of animations, TRUE if more animations - * remain. - */ -static int gms_graphics_animate(type8 off_screen[], type16 width, type16 height) { +int Magnetic::gms_graphics_animate(type8 off_screen[], type16 width, type16 height) { struct ms_position *positions; type16 count; type8 status; @@ -1294,7 +905,7 @@ static int gms_graphics_animate(type8 off_screen[], type16 width, type16 height) /* Search for more animation frames, and return zero if none. */ status = ms_animate(&positions, &count); if (status == 0) - return FALSE; + return false; /* Apply each animation frame to the off-screen buffer. */ for (frame = 0; frame < count; frame++) { @@ -1316,26 +927,13 @@ static int gms_graphics_animate(type8 off_screen[], type16 width, type16 height) } } - /* Return TRUE since more animation frames remain. */ - return TRUE; + /* Return true since more animation frames remain. */ + return true; } #ifndef GARGLK -/* - * gms_graphics_is_vertex() - * - * Given a point, return TRUE if that point is the vertex of a fillable - * region. This is a helper function for layering pictures. When assign- - * ing layers, we want to weight the colors that have the most complex - * shapes, or the largest count of isolated areas, heavier than simpler - * areas. - * - * By painting the colors with the largest number of isolated areas or - * the most complex shapes first, we help to minimize the number of fill - * regions needed to render the complete picture. - */ -static int gms_graphics_is_vertex(type8 off_screen[], type16 width, type16 height, - int x, int y) { +int Magnetic::gms_graphics_is_vertex(type8 off_screen[], type16 width, type16 height, + int x, int y) { type8 pixel; int above, below, left, right; long index_row; @@ -1359,47 +957,14 @@ static int gms_graphics_is_vertex(type8 off_screen[], type16 width, type16 heigh right = (x == width - 1 || off_screen[index_row + x + 1] != pixel); /* - * Return TRUE if this pixel lies at the vertex of a rectangular, fillable, + * Return true if this pixel lies at the vertex of a rectangular, fillable, * area. That is, if two adjacent neighbors aren't the same color (or if * absent -- at the edge of the picture). */ return ((above || below) && (left || right)); } -/* - * gms_graphics_compare_layering_inverted() - * gms_graphics_assign_layers() - * - * Given two sets of image bitmaps, and a palette, this function will - * assign layers palette colors. - * - * Layers are assigned by first counting the number of vertices in the - * color plane, to get a measure of the complexity of shapes displayed in - * this color, and also the raw number of times each palette color is - * used. This is then sorted, so that layers are assigned to colors, with - * the lowest layer being the color with the most complex shapes, and - * within this (or where the count of vertices is zero, as it could be - * in some animation frames) the most used color. - * - * The function compares pixels in the two image bitmaps given, these - * being the off-screen and on-screen buffers, and generates counts only - * where these bitmaps differ. This ensures that only pixels not yet - * painted are included in layering. - * - * As well as assigning layers, this function returns a set of layer usage - * flags, to help the rendering loop to terminate as early as possible. - * - * By painting lower layers first, the paint can take in larger areas if - * it's permitted to include not-yet-validated higher levels. This helps - * minimize the amount of Glk areas fills needed to render a picture. - */ -typedef struct { - long complexity; /* Count of vertices for this color. */ - long usage; /* Color usage count. */ - int color; /* Color index into palette. */ -} gms_layering_t; - -static int gms_graphics_compare_layering_inverted(const void *void_first, +int Magnetic::gms_graphics_compare_layering_inverted(const void *void_first, const void *void_second) { gms_layering_t *first = (gms_layering_t *) void_first; gms_layering_t *second = (gms_layering_t *) void_second; @@ -1418,7 +983,7 @@ static int gms_graphics_compare_layering_inverted(const void *void_first, first->usage > second->usage ? -1 : 0; } -static void gms_graphics_assign_layers(type8 off_screen[], type8 on_screen[], +void Magnetic::gms_graphics_assign_layers(type8 off_screen[], type8 on_screen[], type16 width, type16 height, int layers[], long layer_usage[]) { int index, x, y; @@ -1473,36 +1038,7 @@ static void gms_graphics_assign_layers(type8 off_screen[], type8 on_screen[], } } -/* - * gms_graphics_paint_region() - * - * This is a partially optimized point plot. Given a point in the graphics - * bitmap, it tries to extend the point to a color region, and fill a number - * of pixels in a single Glk rectangle fill. The goal here is to reduce the - * number of Glk rectangle fills, which tend to be extremely inefficient - * operations for generalized point plotting. - * - * The extension works in image layers; each palette color is assigned a - * layer, and we paint each layer individually, starting at the lowest. So, - * the region is free to fill any invalidated pixel in a higher layer, and - * all pixels, invalidated or already validated, in the same layer. In - * practice, it is good enough to look for either invalidated pixels or pixels - * in the same layer, and construct a region as large as possible from these, - * then on marking points as validated, mark only those in the same layer as - * the initial point. - * - * The optimization here is not the best possible, but is reasonable. What - * we do is to try and stretch the region horizontally first, then vertically. - * In practice, we might find larger areas by stretching vertically and then - * horizontally, or by stretching both dimensions at the same time. In - * mitigation, the number of colors in a picture is small (16), and the - * aspect ratio of pictures makes them generally wider than they are tall. - * - * Once we've found the region, we render it with a single Glk rectangle fill, - * and mark all the pixels in this region that match the layer of the initial - * given point as validated. - */ -static void gms_graphics_paint_region(winid_t glk_window, glui32 palette[], int layers[], +void Magnetic::gms_graphics_paint_region(winid_t glk_window, glui32 palette[], int layers[], type8 off_screen[], type8 on_screen[], int x, int y, int x_offset, int y_offset, int pixel_size, type16 width, type16 height) { @@ -1571,7 +1107,7 @@ break_y_min: break_y_max: /* Fill the region using Glk's rectangle fill. */ - g_vm->glk_window_fill_rect(glk_window, palette[pixel], + glk_window_fill_rect(glk_window, palette[pixel], x_min * pixel_size + x_offset, y_min * pixel_size + y_offset, (x_max - x_min + 1) * pixel_size, @@ -1608,10 +1144,8 @@ break_y_max: } #endif -static void gms_graphics_paint_everything(winid_t glk_window, - glui32 palette[], - type8 off_screen[], - int x_offset, int y_offset, +void Magnetic::gms_graphics_paint_everything(winid_t glk_window, + glui32 palette[], type8 off_screen[], int x_offset, int y_offset, type16 width, type16 height) { type8 pixel; /* Reference pixel color */ int x, y; @@ -1619,7 +1153,7 @@ static void gms_graphics_paint_everything(winid_t glk_window, for (y = 0; y < height; y++) { for (x = 0; x < width; x ++) { pixel = off_screen[ y * width + x ]; - g_vm->glk_window_fill_rect(glk_window, + glk_window_fill_rect(glk_window, palette[ pixel ], x * GMS_GRAPHICS_PIXEL + x_offset, y * GMS_GRAPHICS_PIXEL + y_offset, @@ -1628,31 +1162,14 @@ static void gms_graphics_paint_everything(winid_t glk_window, } } -/* - * gms_graphics_timeout() - * - * This is a background function, called on Glk timeouts. Its job is to - * repaint some of the current graphics image. On successive calls, it - * does a part of the repaint, then yields to other processing. This is - * useful since the Glk primitive to plot points in graphical windows is - * extremely slow; this way, the repaint doesn't block game play. - * - * The function should be called on Glk timeout events. When the repaint - * is complete, the function will turn off Glk timers. - * - * The function uses double-buffering to track how much of the graphics - * buffer has been rendered. This helps to minimize the amount of point - * plots required, as only the differences between the two buffers need - * to be rendered. - */ -static void gms_graphics_timeout() { +void Magnetic::gms_graphics_timeout() { static glui32 palette[GMS_PALETTE_SIZE]; /* Precomputed Glk palette */ #ifndef GARGLK static int layers[GMS_PALETTE_SIZE]; /* Assigned image layers */ static long layer_usage[GMS_PALETTE_SIZE]; /* Image layer occupancies */ #endif - static int deferred_repaint = FALSE; /* Local delayed repaint flag */ + static int deferred_repaint = false; /* Local delayed repaint flag */ static int ignore_counter; /* Count of calls ignored */ static int x_offset, y_offset; /* Point plot offsets */ @@ -1683,8 +1200,8 @@ static void gms_graphics_timeout() { * we are receiving consecutive Glk arrange or redraw events. */ if (gms_graphics_repaint) { - deferred_repaint = TRUE; - gms_graphics_repaint = FALSE; + deferred_repaint = true; + gms_graphics_repaint = false; ignore_counter = GMS_GRAPHICS_REPAINT_WAIT - 1; return; } @@ -1816,8 +1333,8 @@ static void gms_graphics_timeout() { yield_counter = 0; /* Clear the new picture and deferred repaint flags. */ - gms_graphics_new_picture = FALSE; - deferred_repaint = FALSE; + gms_graphics_new_picture = false; + deferred_repaint = false; } #ifndef GARGLK @@ -1924,7 +1441,7 @@ static void gms_graphics_timeout() { * If animated, and if animations are enabled, handle further animation * frames, if any. */ - if (g_vm->gms_animation_enabled && gms_graphics_animated) { + if (gms_animation_enabled && gms_graphics_animated) { int more_animation; /* @@ -1999,31 +1516,7 @@ static void gms_graphics_timeout() { } } - -/* - * ms_showpic() - * - * Called by the main interpreter when it wants us to display a picture. - * The function gets the picture bitmap, palette, and dimensions, and - * saves them, and the picture id, in module variables for the background - * rendering function. - * - * The graphics window is opened if required, or closed if mode is zero. - * - * The function checks for changes of actual picture by calculating the - * CRC for picture data; this helps to prevent unnecessary repaints in - * cases where the interpreter passes us the same picture as we're already - * displaying. There is a less than 1 in 4,294,967,296 chance that a new - * picture will be missed. We'll live with that. - * - * Why use CRCs, rather than simply storing the values of picture passed in - * a static variable? Because some games, typically Magnetic Windows, use - * the picture argument as a form of string pointer, and can pass in the - * same value for several, perhaps all, game pictures. If we just checked - * for a change in the picture argument, we'd never see one. So we must - * instead look for changes in the real picture data. - */ -void ms_showpic(type32 picture, type8 mode) { +void Magnetic::ms_showpic(type32 picture, type8 mode) { static glui32 current_crc = 0; /* CRC of the current picture */ type8 *bitmap, animated; @@ -2034,13 +1527,13 @@ void ms_showpic(type32 picture, type8 mode) { /* See if the mode indicates no graphics. */ if (mode == 0) { /* Note that the interpreter turned graphics off. */ - gms_graphics_interpreter = FALSE; + gms_graphics_interpreter = false; /* * If we are currently displaying the graphics window, stop any update * "thread" and turn off graphics. */ - if (g_vm->gms_graphics_enabled && gms_graphics_are_displayed()) { + if (gms_graphics_enabled && gms_graphics_are_displayed()) { gms_graphics_stop(); gms_graphics_close(); } @@ -2050,7 +1543,7 @@ void ms_showpic(type32 picture, type8 mode) { } /* Note that the interpreter turned graphics on. */ - gms_graphics_interpreter = TRUE; + gms_graphics_interpreter = true; /* * Obtain the image details for the requested picture. The call returns @@ -2075,7 +1568,7 @@ void ms_showpic(type32 picture, type8 mode) { if (width == gms_graphics_width && height == gms_graphics_height && crc == current_crc - && g_vm->gms_graphics_enabled && gms_graphics_are_displayed()) + && gms_graphics_enabled && gms_graphics_are_displayed()) return; /* @@ -2106,39 +1599,19 @@ void ms_showpic(type32 picture, type8 mode) { * the picture details will simply stick around in module variables until * they are required. */ - if (g_vm->gms_graphics_enabled) { + if (gms_graphics_enabled) { /* * Ensure graphics on, then set the new picture flag and start the * updating "thread". */ if (gms_graphics_open()) { - gms_graphics_new_picture = TRUE; + gms_graphics_new_picture = true; gms_graphics_start(); } } } - -/* - * gms_graphics_picture_is_available() - * - * Return TRUE if the graphics module data is loaded with a usable picture, - * FALSE if there is no picture available to display. - */ -static int -gms_graphics_picture_is_available(void) { - return gms_graphics_bitmap != NULL; -} - - -/* - * gms_graphics_get_picture_details() - * - * Return the width, height, and animation flag of the currently loaded - * picture. The function returns FALSE if no picture is loaded, otherwise - * TRUE, with picture details in the return arguments. - */ -static int gms_graphics_get_picture_details(int *width, int *height, int *is_animated) { +int Magnetic::gms_graphics_get_picture_details(int *width, int *height, int *is_animated) { if (gms_graphics_picture_is_available()) { if (width) *width = gms_graphics_width; @@ -2147,30 +1620,15 @@ static int gms_graphics_get_picture_details(int *width, int *height, int *is_ani if (is_animated) *is_animated = gms_graphics_animated; - return TRUE; + return true; } - return FALSE; + return false; } - -/* - * gms_graphics_get_rendering_details() - * - * Returns the current level of applied gamma correction, as a string, the - * count of colors in the picture, and a flag indicating if graphics is - * active (busy). The function return FALSE if graphics is not enabled or - * if not being displayed, otherwise TRUE with the gamma, color count, and - * active flag in the return arguments. - * - * This function races with the graphics timeout, as it returns information - * set up by the first timeout following a new picture. There's a very, - * very small chance that it might win the race, in which case out-of-date - * gamma and color count values are returned. - */ -static int gms_graphics_get_rendering_details(const char **gamma, int *color_count, - int *is_active) { - if (g_vm->gms_graphics_enabled && gms_graphics_are_displayed()) { +int Magnetic::gms_graphics_get_rendering_details(const char **gamma, + int *color_count, int *is_active) { + if (gms_graphics_enabled && gms_graphics_are_displayed()) { /* * Return the string representing the gamma correction. If racing * with timeouts, we might return the gamma for the last picture. @@ -2192,31 +1650,17 @@ static int gms_graphics_get_rendering_details(const char **gamma, int *color_cou if (is_active) *is_active = gms_graphics_active; - return TRUE; + return true; } - return FALSE; + return false; } - -/* - * gms_graphics_interpreter_enabled() - * - * Return TRUE if it looks like interpreter graphics are turned on, FALSE - * otherwise. - */ -static int gms_graphics_interpreter_enabled(void) { +int Magnetic::gms_graphics_interpreter_enabled() { return gms_graphics_interpreter; } - -/* - * gms_graphics_cleanup() - * - * Free memory resources allocated by graphics functions. Called on game - * end. - */ -static void gms_graphics_cleanup(void) { +void Magnetic::gms_graphics_cleanup() { free(gms_graphics_bitmap); gms_graphics_bitmap = NULL; free(gms_graphics_off_screen); @@ -2224,40 +1668,16 @@ static void gms_graphics_cleanup(void) { free(gms_graphics_on_screen); gms_graphics_on_screen = NULL; - gms_graphics_animated = FALSE; + gms_graphics_animated = false; gms_graphics_picture = 0; } - /*---------------------------------------------------------------------*/ /* Glk port status line functions */ /*---------------------------------------------------------------------*/ -/* - * The interpreter feeds us status line characters one at a time, with Tab - * indicating right justify, and CR indicating the line is complete. To get - * this to fit with the Glk event and redraw model, here we'll buffer each - * completed status line, so we have a stable string to output when needed. - * It's also handy to have this buffer for Glk libraries that don't support - * separate windows. - */ -enum { GMS_STATBUFFER_LENGTH = 1024 }; -static char gms_status_buffer[GMS_STATBUFFER_LENGTH]; -static int gms_status_length = 0; - -/* Default width used for non-windowing Glk status lines. */ -static const int GMS_DEFAULT_STATUS_WIDTH = 74; - - -/* - * ms_statuschar() - * - * Receive one status character from the interpreter. Characters are - * buffered internally, and on CR, the buffer is copied to the main static - * status buffer for use by the status line printing function. - */ -void ms_statuschar(type8 c) { - static char buffer[GMS_STATBUFFER_LENGTH]; +void Magnetic::ms_statuschar(type8 c) { + static char buffer_[GMS_STATBUFFER_LENGTH]; static int length = 0; /* @@ -2266,39 +1686,31 @@ void ms_statuschar(type8 c) { * buffer the character. */ if (c == '\n') { - memcpy(gms_status_buffer, buffer, length); + memcpy(gms_status_buffer, buffer_, length); gms_status_length = length; length = 0; } else { - if (length < (int)sizeof(buffer)) - buffer[length++] = c; + if (length < (int)sizeof(buffer_)) + buffer_[length++] = c; } } - -/* - * gms_status_update() - * - * Update the information in the status window with the current contents of - * the completed status line buffer, or a default string if no completed - * status line. - */ -static void gms_status_update(void) { +void Magnetic::gms_status_update() { glui32 width, height; int index; assert(gms_status_window); - g_vm->glk_window_get_size(gms_status_window, &width, &height); + glk_window_get_size(gms_status_window, &width, &height); if (height > 0) { - g_vm->glk_window_clear(gms_status_window); - g_vm->glk_window_move_cursor(gms_status_window, 0, 0); - g_vm->glk_set_window(gms_status_window); + glk_window_clear(gms_status_window); + glk_window_move_cursor(gms_status_window, 0, 0); + glk_set_window(gms_status_window); - g_vm->glk_set_style(style_User1); + glk_set_style(style_User1); for (index = 0; index < (int)width; index++) - g_vm->glk_put_char(' '); - g_vm->glk_window_move_cursor(gms_status_window, 1, 0); + glk_put_char(' '); + glk_window_move_cursor(gms_status_window, 1, 0); if (gms_status_length > 0) { /* @@ -2308,9 +1720,9 @@ static void gms_status_update(void) { */ for (index = 0; index < gms_status_length; index++) { if (gms_status_buffer[index] == '\t') - g_vm->glk_window_move_cursor(gms_status_window, width - 11, 0); + glk_window_move_cursor(gms_status_window, width - 11, 0); else - g_vm->glk_put_char(gms_status_buffer[index]); + glk_put_char(gms_status_buffer[index]); } } else { const char *game_name; @@ -2322,23 +1734,15 @@ static void gms_status_update(void) { * in general, seem to use one. */ game_name = gms_gameid_get_game_name(); - g_vm->glk_put_string(game_name ? game_name : "ScummVM Magnetic version 2.3"); + glk_put_string(game_name ? game_name : "ScummVM Magnetic version 2.3"); } - g_vm->glk_set_window(gms_main_window); + glk_set_window(gms_main_window); } } - -/* - * gms_status_print() - * - * Print the current contents of the completed status line buffer out in the - * main window, if it has changed since the last call. This is for non- - * windowing Glk libraries. - */ -static void gms_status_print(void) { - static char buffer[GMS_STATBUFFER_LENGTH]; +void Magnetic::gms_status_print() { + static char buffer_[GMS_STATBUFFER_LENGTH]; static int length = 0; int index, column; @@ -2349,14 +1753,14 @@ static void gms_status_print(void) { */ if (gms_status_length == 0 || (gms_status_length == length - && strncmp(buffer, gms_status_buffer, length)) == 0) + && strncmp(buffer_, gms_status_buffer, length)) == 0) return; /* Set fixed width font to try to preserve status line formatting. */ - g_vm->glk_set_style(style_Preformatted); + glk_set_style(style_Preformatted); - /* Bracket, and output the status line buffer. */ - g_vm->glk_put_string("[ "); + /* Bracket, and output the status line buffer_. */ + glk_put_string("[ "); column = 1; for (index = 0; index < gms_status_length; index++) { /* @@ -2367,49 +1771,34 @@ static void gms_status_print(void) { */ if (gms_status_buffer[index] == '\t') { while (column <= GMS_DEFAULT_STATUS_WIDTH - 11) { - g_vm->glk_put_char(' '); + glk_put_char(' '); column++; } } else { - g_vm->glk_put_char(gms_status_buffer[index]); + glk_put_char(gms_status_buffer[index]); column++; } } while (column <= GMS_DEFAULT_STATUS_WIDTH) { - g_vm->glk_put_char(' '); + glk_put_char(' '); column++; } - g_vm->glk_put_string(" ]\n"); + glk_put_string(" ]\n"); - /* Save the details of the printed status buffer. */ - memcpy(buffer, gms_status_buffer, gms_status_length); + /* Save the details of the printed status buffer_. */ + memcpy(buffer_, gms_status_buffer, gms_status_length); length = gms_status_length; } - -/* - * gms_status_notify() - * - * Front end function for updating status. Either updates the status window - * or prints the status line to the main window. - */ -static void gms_status_notify(void) { +void Magnetic::gms_status_notify() { if (gms_status_window) gms_status_update(); else gms_status_print(); } - -/* - * gms_status_redraw() - * - * Redraw the contents of any status window with the buffered status string. - * This function should be called on the appropriate Glk window resize and - * arrange events. - */ -static void gms_status_redraw(void) { +void Magnetic::gms_status_redraw() { if (gms_status_window) { winid_t parent; @@ -2423,98 +1812,50 @@ static void gms_status_redraw(void) { * libraries other than Xglk, moreover, we're careful to activate it * only on resize and arrange events. */ - parent = g_vm->glk_window_get_parent(gms_status_window); - g_vm->glk_window_set_arrangement(parent, + parent = glk_window_get_parent(gms_status_window); + glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, 1, NULL); gms_status_update(); } } - /*---------------------------------------------------------------------*/ /* Glk port output functions */ /*---------------------------------------------------------------------*/ -/* - * Flag for if the user entered "help" as their last input, or if hints have - * been silenced as a result of already using a Glk command. - */ -static int gms_help_requested = FALSE, - gms_help_hints_silenced = FALSE; - -/* - * Output buffer. We receive characters one at a time, and it's a bit - * more efficient for everyone if we buffer them, and output a complete - * string on a flush call. - */ -static char *gms_output_buffer = NULL; -static int gms_output_allocation = 0, - gms_output_length = 0; - -/* - * Flag to indicate if the last buffer flushed looked like it ended in a - * ">" prompt. - */ -static int gms_output_prompt = FALSE; - - -/* - * gms_output_register_help_request() - * gms_output_silence_help_hints() - * gms_output_provide_help_hint() - * - * Register a request for help, and print a note of how to get Glk command - * help from the interpreter unless silenced. - */ -static void gms_output_register_help_request(void) { - gms_help_requested = TRUE; +void Magnetic::gms_output_register_help_request() { + gms_help_requested = true; } -static void gms_output_silence_help_hints(void) { - gms_help_hints_silenced = TRUE; +void Magnetic::gms_output_silence_help_hints() { + gms_help_hints_silenced = true; } -static void gms_output_provide_help_hint(void) { +void Magnetic::gms_output_provide_help_hint() { if (gms_help_requested && !gms_help_hints_silenced) { - g_vm->glk_set_style(style_Emphasized); - g_vm->glk_put_string("[Try 'glk help' for help on special interpreter" + glk_set_style(style_Emphasized); + glk_put_string("[Try 'glk help' for help on special interpreter" " commands]\n"); - gms_help_requested = FALSE; - g_vm->glk_set_style(style_Normal); + gms_help_requested = false; + glk_set_style(style_Normal); } } - -/* - * gms_game_prompted() - * - * Return TRUE if the last game output appears to have been a ">" prompt. - * Once called, the flag is reset to FALSE, and requires more game output - * to set it again. - */ -static int gms_game_prompted(void) { +int Magnetic::gms_game_prompted() { int result; result = gms_output_prompt; - gms_output_prompt = FALSE; + gms_output_prompt = false; return result; } - -/* - * gms_detect_game_prompt() - * - * See if the last non-newline-terminated line in the output buffer seems - * to be a prompt, and set the game prompted flag if it does, otherwise - * clear it. - */ -static void gms_detect_game_prompt(void) { +void Magnetic::gms_detect_game_prompt() { int index; - gms_output_prompt = FALSE; + gms_output_prompt = false; /* * Search for a prompt across any last unterminated buffered line; a prompt @@ -2523,34 +1864,20 @@ static void gms_detect_game_prompt(void) { for (index = gms_output_length - 1; index >= 0 && gms_output_buffer[index] != '\n'; index--) { if (gms_output_buffer[index] != ' ') { - gms_output_prompt = TRUE; + gms_output_prompt = true; break; } } } - -/* - * gms_output_delete() - * - * Delete all buffered output text. Free all malloc'ed buffer memory, and - * return the buffer variables to their initial values. - */ -static void gms_output_delete(void) { +void Magnetic::gms_output_delete() { free(gms_output_buffer); gms_output_buffer = NULL; gms_output_allocation = gms_output_length = 0; } - -/* - * gms_output_flush() - * - * Flush any buffered output text to the Glk main window, and clear the - * buffer. - */ -static void gms_output_flush(void) { - assert(g_vm->glk_stream_get_current()); +void Magnetic::gms_output_flush() { + assert(glk_stream_get_current()); if (gms_output_length > 0) { /* @@ -2559,7 +1886,7 @@ static void gms_output_flush(void) { * the game's prompt (if any). */ gms_detect_game_prompt(); - g_vm->glk_set_style(style_Normal); + glk_set_style(style_Normal); if (gms_output_prompt) { int index; @@ -2568,12 +1895,12 @@ static void gms_output_flush(void) { index >= 0 && gms_output_buffer[index] != '\n';) index--; - g_vm->glk_put_buffer(gms_output_buffer, index + 1); + glk_put_buffer(gms_output_buffer, index + 1); gms_output_provide_help_hint(); - g_vm->glk_put_buffer(gms_output_buffer + index + 1, + glk_put_buffer(gms_output_buffer + index + 1, gms_output_length - index - 1); } else { - g_vm->glk_put_buffer(gms_output_buffer, gms_output_length); + glk_put_buffer(gms_output_buffer, gms_output_length); gms_output_provide_help_hint(); } @@ -2581,13 +1908,7 @@ static void gms_output_flush(void) { } } - -/* - * ms_putchar() - * - * Buffer a character for eventual printing to the main window. - */ -void ms_putchar(type8 c) { +void Magnetic::ms_putchar(type8 c) { int bytes; assert(gms_output_length <= gms_output_allocation); @@ -2615,139 +1936,56 @@ void ms_putchar(type8 c) { gms_output_buffer[gms_output_length++] = c; } - -/* - * gms_styled_string() - * gms_styled_char() - * gms_standout_string() - * gms_standout_char() - * gms_normal_string() - * gms_normal_char() - * gms_header_string() - * gms_banner_string() - * - * Convenience functions to print strings in assorted styles. A standout - * string is one that hints that it's from the interpreter, not the game. - */ -static void gms_styled_string(glui32 style, const char *message) { +void Magnetic::gms_styled_string(glui32 style, const char *message) { assert(message); - g_vm->glk_set_style(style); - g_vm->glk_put_string(message); - g_vm->glk_set_style(style_Normal); + glk_set_style(style); + glk_put_string(message); + glk_set_style(style_Normal); } -static void gms_styled_char(glui32 style, char c) { - char buffer[2]; +void Magnetic::gms_styled_char(glui32 style, char c) { + char str[2]; - buffer[0] = c; - buffer[1] = '\0'; - gms_styled_string(style, buffer); + str[0] = c; + str[1] = '\0'; + gms_styled_string(style, str); } -static void gms_standout_string(const char *message) { +void Magnetic::gms_standout_string(const char *message) { gms_styled_string(style_Emphasized, message); } -#if 0 -static void gms_standout_char(char c) { - gms_styled_char(style_Emphasized, c); -} -#endif - -static void gms_normal_string(const char *message) { +void Magnetic::gms_normal_string(const char *message) { gms_styled_string(style_Normal, message); } -static void gms_normal_char(char c) { +void Magnetic::gms_normal_char(char c) { gms_styled_char(style_Normal, c); } -static void gms_header_string(const char *message) { +void Magnetic::gms_header_string(const char *message) { gms_styled_string(style_Header, message); } -static void gms_banner_string(const char *message) { +void Magnetic::gms_banner_string(const char *message) { gms_styled_string(style_Subheader, message); } -/* - * ms_flush() - * - * Handle a core interpreter call to flush the output buffer. Because Glk - * only flushes its buffers and displays text on g_vm->glk_select(), we can ignore - * these calls as long as we call gms_output_flush() when reading line input. - * - * Taking ms_flush() at face value can cause game text to appear before status - * line text where we are working with a non-windowing Glk, so it's best - * ignored where we can. - */ -void ms_flush(void) { +void Magnetic::ms_flush() { } - /*---------------------------------------------------------------------*/ /* Glk port hint functions */ /*---------------------------------------------------------------------*/ -/* Hint type definitions. */ -enum { - GMS_HINT_TYPE_FOLDER = 1, - GMS_HINT_TYPE_TEXT = 2 -}; - -/* Success and fail return codes from hint functions. */ -static const type8 GMS_HINT_SUCCESS = 1, - GMS_HINT_ERROR = 0; - -/* Default window sizes for non-windowing Glk libraries. */ -static const glui32 GMS_HINT_DEFAULT_WIDTH = 72, - GMS_HINT_DEFAULT_HEIGHT = 25; - -/* - * Special hint nodes indicating the root hint node, and a value to signal - * quit from hints subsystem. - */ -static const type16 GMS_HINT_ROOT_NODE = 0, - GMS_HINTS_DONE = UINT16_MAX; - -/* Generic hint topic for the root hints node. */ -static const char *const GMS_GENERIC_TOPIC = "Hints Menu"; - -/* - * Note of the interpreter's hints array. Note that keeping its address - * like this assumes that it's either static or heap in the interpreter. - */ -static struct ms_hint *gms_hints = NULL; - -/* Details of the current hint node on display from the hints array. */ -static type16 gms_current_hint_node = 0; - -/* - * Array of cursors for each hint. The cursor indicates the current hint - * position in a folder, and the last hint shown in text hints. Space - * is allocated as needed for a given set of hints, and needs to be freed - * on interpreter exit. - */ -static int *gms_hint_cursor = NULL; - - -/* - * gms_get_hint_max_node() - * - * Return the maximum hint node referred to by the tree under the given node. - * The result is the largest index found, or node, if greater. Because the - * interpreter doesn't supply it, we need to uncover it the hard way. The - * function is recursive, and since it is a tree search, assumes that hints - * is a tree, not a graph. - */ -static type16 gms_get_hint_max_node(const struct ms_hint hints[], type16 node) { +type16 Magnetic::gms_get_hint_max_node(const struct ms_hint hints_[], type16 node) { const struct ms_hint *hint; int index; type16 max_node; - assert(hints); + assert(hints_); - hint = hints + node; + hint = hints_ + node; max_node = node; switch (hint->nodetype) { @@ -2762,38 +2000,30 @@ static type16 gms_get_hint_max_node(const struct ms_hint hints[], type16 node) { for (index = 0; index < hint->elcount; index++) { type16 link_max; - link_max = gms_get_hint_max_node(hints, hint->links[index]); + link_max = gms_get_hint_max_node(hints_, hint->links[index]); if (link_max > max_node) max_node = link_max; } break; default: - gms_fatal("GLK: Invalid hints node type encountered"); - g_vm->glk_exit(); + gms_fatal("GLK: Invalid hints_ node type encountered"); + glk_exit(); } /* * Return the largest node reference found, capped to avoid overlapping the - * special end-hints value. + * special end-hints_ value. */ return max_node < GMS_HINTS_DONE ? max_node : GMS_HINTS_DONE - 1; } - -/* - * gms_get_hint_content() - * - * Return the content string for a given hint number within a given node. - * This counts over 'number' ASCII NULs in the node's content, returning - * the address of the string located this way. - */ -static const char *gms_get_hint_content(const struct ms_hint hints[], type16 node, int number) { +const char *Magnetic::gms_get_hint_content(const struct ms_hint hints_[], type16 node, int number) { const struct ms_hint *hint; int offset, index; - assert(hints); + assert(hints_); - hint = hints + node; + hint = hints_ + node; /* Run through content until 'number' strings found. */ offset = 0; @@ -2804,16 +2034,8 @@ static const char *gms_get_hint_content(const struct ms_hint hints[], type16 nod return hint->content + offset; } - -/* - * gms_get_hint_topic() - * - * Return the topic string for a given hint node. This is found by searching - * the parent node for a link to the node handed in. For the root node, the - * string is defaulted, since the root node has no parent. - */ -static const char *gms_get_hint_topic(const struct ms_hint hints[], type16 node) { - assert(hints); +const char *Magnetic::gms_get_hint_topic(const ms_hint hints_[], type16 node) { + assert(hints_); if (node == GMS_HINT_ROOT_NODE) { /* If the node is the root node, return a generic string. */ @@ -2827,12 +2049,12 @@ static const char *gms_get_hint_topic(const struct ms_hint hints[], type16 node) * Search the parent for a link to node, and use that as the hint topic; * NULL if none found. */ - parent = hints[node].parent; + parent = hints_[node].parent; topic = NULL; - for (index = 0; index < hints[parent].elcount; index++) { - if (hints[parent].links[index] == node) { - topic = gms_get_hint_content(hints, parent, index); + for (index = 0; index < hints_[parent].elcount; index++) { + if (hints_[parent].links[index] == node) { + topic = gms_get_hint_content(hints_, parent, index); break; } } @@ -2841,17 +2063,7 @@ static const char *gms_get_hint_topic(const struct ms_hint hints[], type16 node) } } - -/* - * gms_hint_open() - * - * If not already open, open the hints windows. Returns TRUE if the windows - * opened, or were already open. - * - * The function creates two hints windows -- a text grid on top, for menus, - * and a text buffer below for hints. - */ -static int gms_hint_open(void) { +int Magnetic::gms_hint_open() { if (!gms_hint_menu_window) { assert(!gms_hint_text_window); @@ -2859,91 +2071,64 @@ static int gms_hint_open(void) { * Open the hint menu window. The initial size is two lines, but we'll * change this later to suit the hint. */ - gms_hint_menu_window = g_vm->glk_window_open(gms_main_window, + gms_hint_menu_window = glk_window_open(gms_main_window, winmethod_Above | winmethod_Fixed, 2, wintype_TextGrid, 0); if (!gms_hint_menu_window) - return FALSE; + return false; /* * Now open the hints text window. This is set to be 100% of the size * of the main window, so should cover what remains of it completely. */ - gms_hint_text_window = g_vm->glk_window_open(gms_main_window, + gms_hint_text_window = glk_window_open(gms_main_window, winmethod_Above | winmethod_Proportional, 100, wintype_TextBuffer, 0); if (!gms_hint_text_window) { - g_vm->glk_window_close(gms_hint_menu_window, NULL); + glk_window_close(gms_hint_menu_window, NULL); gms_hint_menu_window = NULL; - return FALSE; + return false; } } - return TRUE; + return true; } - -/* - * gms_hint_close() - * - * If open, close the hints windows. - */ -static void gms_hint_close(void) { +void Magnetic::Magnetic::gms_hint_close() { if (gms_hint_menu_window) { assert(gms_hint_text_window); - g_vm->glk_window_close(gms_hint_menu_window, NULL); + glk_window_close(gms_hint_menu_window, NULL); gms_hint_menu_window = NULL; - g_vm->glk_window_close(gms_hint_text_window, NULL); + glk_window_close(gms_hint_text_window, NULL); gms_hint_text_window = NULL; } } - -/* - * gms_hint_windows_available() - * - * Return TRUE if hints windows are available. If they're not, the hints - * system will need to use alternative output methods. - */ -static int gms_hint_windows_available(void) { +int Magnetic::gms_hint_windows_available() { return (gms_hint_menu_window && gms_hint_text_window); } - -/* - * gms_hint_menu_print() - * gms_hint_menu_header() - * gms_hint_menu_justify() - * gms_hint_text_print() - * gms_hint_menutext_done() - * gms_hint_menutext_start() - * - * Output functions for writing hints. These functions will write to hints - * windows where available, and to the main window where not. When writing - * to hints windows, they also take care not to line wrap in the menu window. - * Limited formatting is available. - */ -static void gms_hint_menu_print(int line, int column, const char *string, - glui32 width, glui32 height) { - assert(string); +void Magnetic::gms_hint_menu_print(int line, int column, const char *string_, + glui32 width, glui32 height) { + assert(string_); /* Ignore the call if the text position is outside the window. */ if (!(line > (int)height || column > (int)width)) { if (gms_hint_windows_available()) { int posn, index; - g_vm->glk_window_move_cursor(gms_hint_menu_window, column, line); - g_vm->glk_set_window(gms_hint_menu_window); + glk_window_move_cursor(gms_hint_menu_window, column, line); + glk_set_window(gms_hint_menu_window); - /* Write until the end of the string, or the end of the window. */ + /* Write until the end of the string_, or the end of the window. */ for (posn = column, index = 0; - posn < (int)width && index < (int)strlen(string); posn++, index++) { - g_vm->glk_put_char(string[index]); + posn < (int)width && index < (int)strlen(string_); posn++, index++) { + glk_put_char(string_[index]); } - g_vm->glk_set_window(gms_main_window); + glk_set_window(gms_main_window); } else { static int current_line = 0; /* Retained line number */ static int current_column = 0; /* Retained col number */ @@ -2974,32 +2159,31 @@ static void gms_hint_menu_print(int line, int column, const char *string, gms_normal_char(' '); /* - * Write characters until the end of the string, or the end of the + * Write characters until the end of the string_, or the end of the * (self-imposed not-really-there) window. */ for (index = 0; - current_column < (int)width && index < (int)strlen(string); + current_column < (int)width && index < (int)strlen(string_); current_column++, index++) { - gms_normal_char(string[index]); + gms_normal_char(string_[index]); } } } } -static void gms_hint_menu_header(int line, const char *string, - glui32 width, glui32 height) { +void Magnetic::gms_hint_menu_header(int line, const char *string_, + glui32 width, glui32 height) { int posn, length; - assert(string); + assert(string_); /* Output the text in the approximate line center. */ - length = strlen(string); + length = strlen(string_); posn = length < (int)width ? (width - length) / 2 : 0; - gms_hint_menu_print(line, posn, string, width, height); + gms_hint_menu_print(line, posn, string_, width, height); } -static void gms_hint_menu_justify(int line, - const char *left_string, const char *right_string, - glui32 width, glui32 height) { +void Magnetic::gms_hint_menu_justify(int line, const char *left_string, + const char *right_string, glui32 width, glui32 height) { int posn, length; assert(left_string && right_string); @@ -3012,18 +2196,18 @@ static void gms_hint_menu_justify(int line, gms_hint_menu_print(line, posn, right_string, width, height); } -static void gms_hint_text_print(const char *string) { - assert(string); +void Magnetic::gms_hint_text_print(const char *string_) { + assert(string_); if (gms_hint_windows_available()) { - g_vm->glk_set_window(gms_hint_text_window); - g_vm->glk_put_string(string); - g_vm->glk_set_window(gms_main_window); + glk_set_window(gms_hint_text_window); + glk_put_string(string_); + glk_set_window(gms_main_window); } else - gms_normal_string(string); + gms_normal_string(string_); } -static void gms_hint_menutext_start(void) { +void Magnetic::gms_hint_menutext_start() { /* * Twiddle for non-windowing libraries; 'clear' the main window by writing * a null string at line 1, then a null string at line 0. This works @@ -3039,7 +2223,7 @@ static void gms_hint_menutext_start(void) { } } -static void gms_hint_menutext_done(void) { +void Magnetic::gms_hint_menutext_done() { /* * Twiddle for non-windowing libraries; 'clear' the main window by writing * an empty string to line zero. For windowing Glk libraries, this function @@ -3051,57 +2235,41 @@ static void gms_hint_menutext_done(void) { } } - -/* - * gms_hint_menutext_char_event() - * - * Request and return a character event from the hints windows. In practice, - * this means either of the hints windows if available, or the main window - * if not. - */ -static void gms_hint_menutext_char_event(event_t *event) { +void Magnetic::gms_hint_menutext_char_event(event_t *event) { assert(event); if (gms_hint_windows_available()) { - g_vm->glk_request_char_event(gms_hint_menu_window); - g_vm->glk_request_char_event(gms_hint_text_window); + glk_request_char_event(gms_hint_menu_window); + glk_request_char_event(gms_hint_text_window); gms_event_wait(evtype_CharInput, event); assert(event->window == gms_hint_menu_window || event->window == gms_hint_text_window); - g_vm->glk_cancel_char_event(gms_hint_menu_window); - g_vm->glk_cancel_char_event(gms_hint_text_window); + glk_cancel_char_event(gms_hint_menu_window); + glk_cancel_char_event(gms_hint_text_window); } else { - g_vm->glk_request_char_event(gms_main_window); + glk_request_char_event(gms_main_window); gms_event_wait(evtype_CharInput, event); } } - -/* - * gms_hint_arrange_windows() - * - * Arrange the hints windows so that the hint menu window has the requested - * number of lines. Returns the actual hint menu window width and height, - * or defaults if no hints windows are available. - */ -static void gms_hint_arrange_windows(int requested_lines, glui32 *width, glui32 *height) { +void Magnetic::gms_hint_arrange_windows(int requested_lines, glui32 *width, glui32 *height) { if (gms_hint_windows_available()) { winid_t parent; /* Resize the hint menu window to fit the current hint. */ - parent = g_vm->glk_window_get_parent(gms_hint_menu_window); - g_vm->glk_window_set_arrangement(parent, + parent = glk_window_get_parent(gms_hint_menu_window); + glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, requested_lines, NULL); /* Measure, and return the size of the hint menu window. */ - g_vm->glk_window_get_size(gms_hint_menu_window, width, height); + glk_window_get_size(gms_hint_menu_window, width, height); /* Clear both the hint menu and the hint text window. */ - g_vm->glk_window_clear(gms_hint_menu_window); - g_vm->glk_window_clear(gms_hint_text_window); + glk_window_clear(gms_hint_menu_window); + glk_window_clear(gms_hint_text_window); } else { /* * No hints windows, so default width and height. The hints output @@ -3114,17 +2282,11 @@ static void gms_hint_arrange_windows(int requested_lines, glui32 *width, glui32 } } - -/* - * gms_hint_display_folder() - * - * Update the hints windows for the given folder hint node. - */ -static void gms_hint_display_folder(const struct ms_hint hints[], - const int cursor[], type16 node) { +void Magnetic::gms_hint_display_folder(const ms_hint hints_[], + const int cursor[], type16 node) { glui32 width, height; int line, index; - assert(hints && cursor); + assert(hints_ && cursor); /* * Arrange windows to suit the hint folder. For a folder menu window we @@ -3132,12 +2294,12 @@ static void gms_hint_display_folder(const struct ms_hint hints[], * making a total of five additional lines. Width and height receive the * actual menu window dimensions. */ - gms_hint_arrange_windows(hints[node].elcount + 5, &width, &height); + gms_hint_arrange_windows(hints_[node].elcount + 5, &width, &height); /* Paint in the menu header. */ line = 0; gms_hint_menu_header(line++, - gms_get_hint_topic(hints, node), + gms_get_hint_topic(hints_, node), width, height); gms_hint_menu_justify(line++, " N = next subject ", " P = previous ", @@ -3153,12 +2315,12 @@ static void gms_hint_display_folder(const struct ms_hint hints[], * text for the selected hint is preceded by a '>' pointer. */ line++; - for (index = 0; index < hints[node].elcount; index++) { + for (index = 0; index < hints_[node].elcount; index++) { gms_hint_menu_print(line, 3, index == cursor[node] ? ">" : " ", width, height); gms_hint_menu_print(line++, 5, - gms_get_hint_content(hints, node, index), + gms_get_hint_content(hints_, node, index), width, height); } @@ -3170,21 +2332,15 @@ static void gms_hint_display_folder(const struct ms_hint hints[], gms_hint_menu_print(line, 0, " ", width, height); } - -/* - * gms_hint_display_text() - * - * Update the hints windows for the given text hint node. - */ -static void gms_hint_display_text(const struct ms_hint hints[], - const int cursor[], type16 node) { +void Magnetic::gms_hint_display_text(const ms_hint hints_[], + const int cursor[], type16 node) { glui32 width, height; int line, index; - assert(hints && cursor); + assert(hints_ && cursor); /* * Arrange windows to suit the hint text. For a hint menu, we use a simple - * two-line set of controls; everything else is in the hints text window. + * two-line set of controls; everything else is in the hints_ text window. * Width and height receive the actual menu window dimensions. */ gms_hint_arrange_windows(2, &width, &height); @@ -3192,66 +2348,52 @@ static void gms_hint_display_text(const struct ms_hint hints[], /* Paint in a short menu header. */ line = 0; gms_hint_menu_header(line++, - gms_get_hint_topic(hints, node), + gms_get_hint_topic(hints_, node), width, height); gms_hint_menu_justify(line++, " RETURN = read hint ", " Q = previous menu ", width, height); /* - * Output hints to the hints text window. Hints not yet exposed are + * Output hints_ to the hints_ text window. hints_ not yet exposed are * indicated by the cursor for the hint, and are displayed as a dash. */ gms_hint_text_print("\n"); - for (index = 0; index < hints[node].elcount; index++) { - char buffer[16]; + for (index = 0; index < hints_[node].elcount; index++) { + char buf[16]; - sprintf(buffer, "%3d. ", index + 1); - gms_hint_text_print(buffer); + sprintf(buf, "%3d. ", index + 1); + gms_hint_text_print(buf); gms_hint_text_print(index < cursor[node] - ? gms_get_hint_content(hints, node, index) : "-"); + ? gms_get_hint_content(hints_, node, index) : "-"); gms_hint_text_print("\n"); } } +void Magnetic::gms_hint_display(const ms_hint hints_[], const int cursor[], type16 node) { + assert(hints_ && cursor); -/* - * gms_hint_display() - * - * Display the given hint using the appropriate display function. - */ -static void gms_hint_display(const struct ms_hint hints[], - const int cursor[], type16 node) { - assert(hints && cursor); - - switch (hints[node].nodetype) { + switch (hints_[node].nodetype) { case GMS_HINT_TYPE_TEXT: - gms_hint_display_text(hints, cursor, node); + gms_hint_display_text(hints_, cursor, node); break; case GMS_HINT_TYPE_FOLDER: - gms_hint_display_folder(hints, cursor, node); + gms_hint_display_folder(hints_, cursor, node); break; default: - gms_fatal("GLK: Invalid hints node type encountered"); - g_vm->glk_exit(); + gms_fatal("GLK: Invalid hints_ node type encountered"); + glk_exit(); } } - -/* - * gms_hint_handle_folder() - * - * Handle a Glk keycode for the given folder hint. Return the next node to - * handle, or the special end-hints on Quit at the root node. - */ -static type16 gms_hint_handle_folder(const struct ms_hint hints[], - int cursor[], type16 node, glui32 keycode) { +type16 Magnetic::gms_hint_handle_folder(const ms_hint hints_[], + int cursor[], type16 node, glui32 keycode) { unsigned char response; type16 next_node; - assert(hints && cursor); + assert(hints_ && cursor); /* Convert key code into a single response character. */ switch (keycode) { @@ -3270,7 +2412,7 @@ static type16 gms_hint_handle_folder(const struct ms_hint hints[], response = 'Q'; break; default: - response = keycode <= BYTE_MAX ? g_vm->glk_char_to_upper(keycode) : 0; + response = keycode <= BYTE_MAX ? glk_char_to_upper(keycode) : 0; break; } @@ -3282,7 +2424,7 @@ static type16 gms_hint_handle_folder(const struct ms_hint hints[], switch (response) { case 'N': /* Advance the hint cursor, wrapping at the folder end. */ - if (cursor[node] < hints[node].elcount - 1) + if (cursor[node] < hints_[node].elcount - 1) cursor[node]++; else cursor[node] = 0; @@ -3293,18 +2435,18 @@ static type16 gms_hint_handle_folder(const struct ms_hint hints[], if (cursor[node] > 0) cursor[node]--; else - cursor[node] = hints[node].elcount - 1; + cursor[node] = hints_[node].elcount - 1; break; case '\n': /* The next node is the hint node at the cursor position. */ - next_node = hints[node].links[cursor[node]]; + next_node = hints_[node].links[cursor[node]]; break; case 'Q': /* If root, we're done; if not, next node is node's parent. */ next_node = node == GMS_HINT_ROOT_NODE - ? GMS_HINTS_DONE : hints[node].parent; + ? GMS_HINTS_DONE : hints_[node].parent; break; default: @@ -3314,18 +2456,11 @@ static type16 gms_hint_handle_folder(const struct ms_hint hints[], return next_node; } - -/* - * gms_hint_handle_text() - * - * Handle a Glk keycode for the given text hint. Return the next node to - * handle. - */ -static type16 gms_hint_handle_text(const struct ms_hint hints[], - int cursor[], type16 node, glui32 keycode) { +type16 Magnetic::gms_hint_handle_text(const ms_hint hints_[], + int cursor[], type16 node, glui32 keycode) { unsigned char response; type16 next_node; - assert(hints && cursor); + assert(hints_ && cursor); /* Convert key code into a single response character. */ switch (keycode) { @@ -3338,7 +2473,7 @@ static type16 gms_hint_handle_text(const struct ms_hint hints[], response = 'Q'; break; default: - response = keycode <= BYTE_MAX ? g_vm->glk_char_to_upper(keycode) : 0; + response = keycode <= BYTE_MAX ? glk_char_to_upper(keycode) : 0; break; } @@ -3350,13 +2485,13 @@ static type16 gms_hint_handle_text(const struct ms_hint hints[], switch (response) { case '\n': /* If not at end of the hint, advance the hint cursor. */ - if (cursor[node] < hints[node].elcount) + if (cursor[node] < hints_[node].elcount) cursor[node]++; break; case 'Q': /* Done with this hint node, so next node is its parent. */ - next_node = hints[node].parent; + next_node = hints_[node].parent; break; default: @@ -3366,71 +2501,50 @@ static type16 gms_hint_handle_text(const struct ms_hint hints[], return next_node; } - -/* - * gms_hint_handle() - * - * Handle a Glk keycode for the given hint using the appropriate handler - * function. Return the next node to handle. - */ -static type16 gms_hint_handle(const struct ms_hint hints[], - int cursor[], type16 node, glui32 keycode) { +type16 Magnetic::gms_hint_handle(const ms_hint hints_[], + int cursor[], type16 node, glui32 keycode) { type16 next_node; - assert(hints && cursor); + assert(hints_ && cursor); next_node = GMS_HINT_ROOT_NODE; - switch (hints[node].nodetype) { + switch (hints_[node].nodetype) { case GMS_HINT_TYPE_TEXT: - next_node = gms_hint_handle_text(hints, cursor, node, keycode); + next_node = gms_hint_handle_text(hints_, cursor, node, keycode); break; case GMS_HINT_TYPE_FOLDER: - next_node = gms_hint_handle_folder(hints, cursor, node, keycode); + next_node = gms_hint_handle_folder(hints_, cursor, node, keycode); break; default: - gms_fatal("GLK: Invalid hints node type encountered"); - g_vm->glk_exit(); + gms_fatal("GLK: Invalid hints_ node type encountered"); + glk_exit(); } return next_node; } - -/* - * ms_showhints() - * - * Start game hints. These are modal, though there's no overriding Glk - * reason why. It's just that this matches the way they're implemented by - * most Inform games. This may not be the best way of doing help, but at - * least it's likely to be familiar, and anything more ambitious may be - * beyond the current Glk capabilities. - * - * This function uses CRCs to detect any change of hints data. Normally, - * we'd expect none, at least within a given game run, but we can probably - * handle it okay if it happens. - */ -type8 ms_showhints(struct ms_hint *hints) { - static int is_initialized = FALSE; +type8 Magnetic::ms_showhints(ms_hint *hints_) { + static int is_initialized = false; static glui32 current_crc = 0; type16 hint_count; glui32 crc; - assert(hints); + assert(hints_); /* - * Find the number of hints in the array. To do this, we'll visit every + * Find the number of hints_ in the array. To do this, we'll visit every * node in a tree search, starting at the root, to locate the maximum node * number found, then add one to that. It's a pity that the interpreter * doesn't hand us this information directly. */ - hint_count = gms_get_hint_max_node(hints, GMS_HINT_ROOT_NODE) + 1; + hint_count = gms_get_hint_max_node(hints_, GMS_HINT_ROOT_NODE) + 1; /* - * Calculate a CRC for the hints array data. If the CRC has changed, or + * Calculate a CRC for the hints_ array data. If the CRC has changed, or * this is the first call, assign a new cursor array. */ - crc = gms_get_buffer_crc(hints, hint_count * sizeof(*hints)); + crc = gms_get_buffer_crc(hints_, hint_count * sizeof(*hints_)); if (crc != current_crc || !is_initialized) { int bytes; @@ -3441,22 +2555,22 @@ type8 ms_showhints(struct ms_hint *hints) { memset(gms_hint_cursor, 0, bytes); /* - * Retain the hints CRC, for later comparisons, and set is_initialized + * Retain the hints_ CRC, for later comparisons, and set is_initialized * flag. */ current_crc = crc; - is_initialized = TRUE; + is_initialized = true; } /* - * Save the hints array passed in. This is done here since even if the data + * Save the hints_ array passed in. This is done here since even if the data * remains the same (found by the CRC check above), the pointer to it might * have changed. */ - gms_hints = hints; + gms_hints = hints_; /* - * Try to create the hints windows. If they can't be created, perhaps + * Try to create the hints_ windows. If they can't be created, perhaps * because the Glk library doesn't support it, the output functions will * work around this. */ @@ -3464,8 +2578,8 @@ type8 ms_showhints(struct ms_hint *hints) { gms_hint_menutext_start(); /* - * Begin hints display at the root node, and navigate until the user exits - * hints. + * Begin hints_ display at the root node, and navigate until the user exits + * hints_. */ gms_current_hint_node = GMS_HINT_ROOT_NODE; while (gms_current_hint_node != GMS_HINTS_DONE) { @@ -3490,32 +2604,14 @@ type8 ms_showhints(struct ms_hint *hints) { return GMS_HINT_SUCCESS; } - -/* - * gms_hint_redraw() - * - * Update the hints windows for the current hint. This function should be - * called from the event handler on resize events, to repaint the hints - * display. It does nothing if no hints windows have been opened, since - * in this case, there's no resize action required -- either we're not in - * the hints subsystem, or hints are being displayed in the main game - * window, for whatever reason. - */ -static void gms_hint_redraw(void) { +void Magnetic::gms_hint_redraw() { if (gms_hint_windows_available()) { assert(gms_hints && gms_hint_cursor); gms_hint_display(gms_hints, gms_hint_cursor, gms_current_hint_node); } } - -/* - * gms_hints_cleanup() - * - * Free memory resources allocated by hints functions. Called on game - * end. - */ -static void gms_hints_cleanup(void) { +void Magnetic::gms_hints_cleanup() { free(gms_hint_cursor); gms_hint_cursor = NULL; @@ -3523,32 +2619,18 @@ static void gms_hints_cleanup(void) { gms_current_hint_node = 0; } - -void ms_playmusic(type8 *midi_data, type32 length, type16 tempo) { +void Magnetic::ms_playmusic(type8 *midi_data, type32 length, type16 tempo) { } - /*---------------------------------------------------------------------*/ /* Glk command escape functions */ /*---------------------------------------------------------------------*/ -/* - * gms_command_undo() - * - * Stub function for the undo command. The real work is to return the - * undo code to the input functions. - */ -static void gms_command_undo(const char *argument) { +void Magnetic::gms_command_undo(const char *argument) { assert(argument); } - -/* - * gms_command_script() - * - * Turn game output scripting (logging) on and off. - */ -static void gms_command_script(const char *argument) { +void Magnetic::gms_command_script(const char *argument) { assert(argument); if (gms_strcasecmp(argument, "on") == 0) { @@ -3559,7 +2641,7 @@ static void gms_command_script(const char *argument) { return; } - fileref = g_vm->glk_fileref_create_by_prompt(fileusage_Transcript + fileref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode, filemode_WriteAppend, 0); if (!fileref) { @@ -3567,15 +2649,15 @@ static void gms_command_script(const char *argument) { return; } - gms_transcript_stream = g_vm->glk_stream_open_file(fileref, + gms_transcript_stream = glk_stream_open_file(fileref, filemode_WriteAppend, 0); - g_vm->glk_fileref_destroy(fileref); + glk_fileref_destroy(fileref); if (!gms_transcript_stream) { gms_standout_string("Glk transcript failed.\n"); return; } - g_vm->glk_window_set_echo_stream(gms_main_window, gms_transcript_stream); + glk_window_set_echo_stream(gms_main_window, gms_transcript_stream); gms_normal_string("Glk transcript is now on.\n"); } @@ -3586,10 +2668,10 @@ static void gms_command_script(const char *argument) { return; } - g_vm->glk_stream_close(gms_transcript_stream, NULL); + glk_stream_close(gms_transcript_stream, NULL); gms_transcript_stream = NULL; - g_vm->glk_window_set_echo_stream(gms_main_window, NULL); + glk_window_set_echo_stream(gms_main_window, NULL); gms_normal_string("Glk transcript is now off.\n"); } @@ -3609,13 +2691,7 @@ static void gms_command_script(const char *argument) { } } - -/* - * gms_command_inputlog() - * - * Turn game input logging on and off. - */ -static void gms_command_inputlog(const char *argument) { +void Magnetic::gms_command_inputlog(const char *argument) { assert(argument); if (gms_strcasecmp(argument, "on") == 0) { @@ -3626,7 +2702,7 @@ static void gms_command_inputlog(const char *argument) { return; } - fileref = g_vm->glk_fileref_create_by_prompt(fileusage_InputRecord + fileref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_BinaryMode, filemode_WriteAppend, 0); if (!fileref) { @@ -3634,9 +2710,9 @@ static void gms_command_inputlog(const char *argument) { return; } - gms_inputlog_stream = g_vm->glk_stream_open_file(fileref, + gms_inputlog_stream = glk_stream_open_file(fileref, filemode_WriteAppend, 0); - g_vm->glk_fileref_destroy(fileref); + glk_fileref_destroy(fileref); if (!gms_inputlog_stream) { gms_standout_string("Glk input logging failed.\n"); return; @@ -3651,7 +2727,7 @@ static void gms_command_inputlog(const char *argument) { return; } - g_vm->glk_stream_close(gms_inputlog_stream, NULL); + glk_stream_close(gms_inputlog_stream, NULL); gms_inputlog_stream = NULL; gms_normal_string("Glk input log is now off.\n"); @@ -3672,13 +2748,7 @@ static void gms_command_inputlog(const char *argument) { } } - -/* - * gms_command_readlog() - * - * Set the game input log, to read input from a file. - */ -static void gms_command_readlog(const char *argument) { +void Magnetic::gms_command_readlog(const char *argument) { assert(argument); if (gms_strcasecmp(argument, "on") == 0) { @@ -3689,7 +2759,7 @@ static void gms_command_readlog(const char *argument) { return; } - fileref = g_vm->glk_fileref_create_by_prompt(fileusage_InputRecord + fileref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_BinaryMode, filemode_Read, 0); if (!fileref) { @@ -3697,14 +2767,14 @@ static void gms_command_readlog(const char *argument) { return; } - if (!g_vm->glk_fileref_does_file_exist(fileref)) { - g_vm->glk_fileref_destroy(fileref); + if (!glk_fileref_does_file_exist(fileref)) { + glk_fileref_destroy(fileref); gms_standout_string("Glk read log failed.\n"); return; } - gms_readlog_stream = g_vm->glk_stream_open_file(fileref, filemode_Read, 0); - g_vm->glk_fileref_destroy(fileref); + gms_readlog_stream = glk_stream_open_file(fileref, filemode_Read, 0); + glk_fileref_destroy(fileref); if (!gms_readlog_stream) { gms_standout_string("Glk read log failed.\n"); return; @@ -3719,7 +2789,7 @@ static void gms_command_readlog(const char *argument) { return; } - g_vm->glk_stream_close(gms_readlog_stream, NULL); + glk_stream_close(gms_readlog_stream, NULL); gms_readlog_stream = NULL; gms_normal_string("Glk read log is now off.\n"); @@ -3740,38 +2810,32 @@ static void gms_command_readlog(const char *argument) { } } - -/* - * gms_command_abbreviations() - * - * Turn abbreviation expansions on and off. - */ -static void gms_command_abbreviations(const char *argument) { +void Magnetic::gms_command_abbreviations(const char *argument) { assert(argument); if (gms_strcasecmp(argument, "on") == 0) { - if (g_vm->gms_abbreviations_enabled) { + if (gms_abbreviations_enabled) { gms_normal_string("Glk abbreviation expansions are already on.\n"); return; } - g_vm->gms_abbreviations_enabled = TRUE; + gms_abbreviations_enabled = true; gms_normal_string("Glk abbreviation expansions are now on.\n"); } else if (gms_strcasecmp(argument, "off") == 0) { - if (!g_vm->gms_abbreviations_enabled) { + if (!gms_abbreviations_enabled) { gms_normal_string("Glk abbreviation expansions are already off.\n"); return; } - g_vm->gms_abbreviations_enabled = FALSE; + gms_abbreviations_enabled = false; gms_normal_string("Glk abbreviation expansions are now off.\n"); } else if (strlen(argument) == 0) { gms_normal_string("Glk abbreviation expansions are "); - gms_normal_string(g_vm->gms_abbreviations_enabled ? "on" : "off"); + gms_normal_string(gms_abbreviations_enabled ? "on" : "off"); gms_normal_string(".\n"); } @@ -3784,15 +2848,7 @@ static void gms_command_abbreviations(const char *argument) { } } - -/* - * gms_command_graphics() - * - * Enable or disable graphics more permanently than is done by the main - * interpreter. Also, print out a few brief details about the graphics - * state of the program. - */ -static void gms_command_graphics(const char *argument) { +void Magnetic::gms_command_graphics(const char *argument) { assert(argument); if (!gms_graphics_possible) { @@ -3801,12 +2857,12 @@ static void gms_command_graphics(const char *argument) { } if (gms_strcasecmp(argument, "on") == 0) { - if (g_vm->gms_graphics_enabled) { + if (gms_graphics_enabled) { gms_normal_string("Glk graphics are already on.\n"); return; } - g_vm->gms_graphics_enabled = TRUE; + gms_graphics_enabled = true; /* If a picture is loaded, call the restart function to repaint it. */ if (gms_graphics_picture_is_available()) { @@ -3821,7 +2877,7 @@ static void gms_command_graphics(const char *argument) { } else if (gms_strcasecmp(argument, "off") == 0) { - if (!g_vm->gms_graphics_enabled) { + if (!gms_graphics_enabled) { gms_normal_string("Glk graphics are already off.\n"); return; } @@ -3830,7 +2886,7 @@ static void gms_command_graphics(const char *argument) { * Set graphics to disabled, and stop any graphics processing. Close * the graphics window. */ - g_vm->gms_graphics_enabled = FALSE; + gms_graphics_enabled = false; gms_graphics_stop(); gms_graphics_close(); @@ -3839,25 +2895,25 @@ static void gms_command_graphics(const char *argument) { else if (strlen(argument) == 0) { gms_normal_string("Glk graphics are available,"); - gms_normal_string(g_vm->gms_graphics_enabled + gms_normal_string(gms_graphics_enabled ? " and enabled.\n" : " but disabled.\n"); if (gms_graphics_picture_is_available()) { int width, height, is_animated; if (gms_graphics_get_picture_details(&width, &height, &is_animated)) { - char buffer[16]; + char buf[16]; gms_normal_string("There is "); gms_normal_string(is_animated ? "an animated" : "a"); gms_normal_string(" picture loaded, "); - sprintf(buffer, "%d", width); - gms_normal_string(buffer); + sprintf(buf, "%d", width); + gms_normal_string(buf); gms_normal_string(" by "); - sprintf(buffer, "%d", height); - gms_normal_string(buffer); + sprintf(buf, "%d", height); + gms_normal_string(buf); gms_normal_string(" pixels.\n"); } @@ -3866,22 +2922,22 @@ static void gms_command_graphics(const char *argument) { if (!gms_graphics_interpreter_enabled()) gms_normal_string("Interpreter graphics are disabled.\n"); - if (g_vm->gms_graphics_enabled && gms_graphics_are_displayed()) { + if (gms_graphics_enabled && gms_graphics_are_displayed()) { int color_count, is_active; const char *gamma; if (gms_graphics_get_rendering_details(&gamma, &color_count, &is_active)) { - char buffer[16]; + char buf[16]; gms_normal_string("Graphics are "); gms_normal_string(is_active ? "active, " : "displayed, "); - sprintf(buffer, "%d", color_count); - gms_normal_string(buffer); + sprintf(buf, "%d", color_count); + gms_normal_string(buf); gms_normal_string(" colours"); - if (g_vm->gms_gamma_mode == GAMMA_OFF) + if (gms_gamma_mode == GAMMA_OFF) gms_normal_string(", without gamma correction"); else { gms_normal_string(", with gamma "); @@ -3893,7 +2949,7 @@ static void gms_command_graphics(const char *argument) { gms_normal_string("Graphics are being displayed.\n"); } - if (g_vm->gms_graphics_enabled && !gms_graphics_are_displayed()) + if (gms_graphics_enabled && !gms_graphics_are_displayed()) gms_normal_string("Graphics are not being displayed.\n"); } @@ -3906,13 +2962,7 @@ static void gms_command_graphics(const char *argument) { } } - -/* - * gms_command_gamma() - * - * Enable or disable picture gamma corrections. - */ -static void gms_command_gamma(const char *argument) { +void Magnetic::gms_command_gamma(const char *argument) { assert(argument); if (!gms_graphics_possible) { @@ -3921,13 +2971,13 @@ static void gms_command_gamma(const char *argument) { } if (gms_strcasecmp(argument, "high") == 0) { - if (g_vm->gms_gamma_mode == GAMMA_HIGH) { + if (gms_gamma_mode == GAMMA_HIGH) { gms_normal_string("Glk automatic gamma correction mode is" " already 'high'.\n"); return; } - g_vm->gms_gamma_mode = GAMMA_HIGH; + gms_gamma_mode = GAMMA_HIGH; gms_graphics_restart(); gms_normal_string("Glk automatic gamma correction mode is" @@ -3936,13 +2986,13 @@ static void gms_command_gamma(const char *argument) { else if (gms_strcasecmp(argument, "normal") == 0 || gms_strcasecmp(argument, "on") == 0) { - if (g_vm->gms_gamma_mode == GAMMA_NORMAL) { + if (gms_gamma_mode == GAMMA_NORMAL) { gms_normal_string("Glk automatic gamma correction mode is" " already 'normal'.\n"); return; } - g_vm->gms_gamma_mode = GAMMA_NORMAL; + gms_gamma_mode = GAMMA_NORMAL; gms_graphics_restart(); gms_normal_string("Glk automatic gamma correction mode is" @@ -3951,13 +3001,13 @@ static void gms_command_gamma(const char *argument) { else if (gms_strcasecmp(argument, "none") == 0 || gms_strcasecmp(argument, "off") == 0) { - if (g_vm->gms_gamma_mode == GAMMA_OFF) { + if (gms_gamma_mode == GAMMA_OFF) { gms_normal_string("Glk automatic gamma correction mode is" " already 'off'.\n"); return; } - g_vm->gms_gamma_mode = GAMMA_OFF; + gms_gamma_mode = GAMMA_OFF; gms_graphics_restart(); gms_normal_string("Glk automatic gamma correction mode is" @@ -3966,7 +3016,7 @@ static void gms_command_gamma(const char *argument) { else if (strlen(argument) == 0) { gms_normal_string("Glk automatic gamma correction mode is '"); - switch (g_vm->gms_gamma_mode) { + switch (gms_gamma_mode) { case GAMMA_OFF: gms_normal_string("off"); break; @@ -3991,13 +3041,7 @@ static void gms_command_gamma(const char *argument) { } } - -/* - * gms_command_animations() - * - * Enable or disable picture animations. - */ -static void gms_command_animations(const char *argument) { +void Magnetic::gms_command_animations(const char *argument) { assert(argument); if (!gms_graphics_possible) { @@ -4008,7 +3052,7 @@ static void gms_command_animations(const char *argument) { if (gms_strcasecmp(argument, "on") == 0) { int is_animated; - if (g_vm->gms_animation_enabled) { + if (gms_animation_enabled) { gms_normal_string("Glk graphics animations are already on.\n"); return; } @@ -4018,7 +3062,7 @@ static void gms_command_animations(const char *argument) { * is animated; if it isn't, we can leave it displayed as is, since * changing animation mode doesn't affect this picture. */ - g_vm->gms_animation_enabled = TRUE; + gms_animation_enabled = true; if (gms_graphics_get_picture_details(NULL, NULL, &is_animated)) { if (is_animated) gms_graphics_restart(); @@ -4030,12 +3074,12 @@ static void gms_command_animations(const char *argument) { else if (gms_strcasecmp(argument, "off") == 0) { int is_animated; - if (!g_vm->gms_animation_enabled) { + if (!gms_animation_enabled) { gms_normal_string("Glk graphics animations are already off.\n"); return; } - g_vm->gms_animation_enabled = FALSE; + gms_animation_enabled = false; if (gms_graphics_get_picture_details(NULL, NULL, &is_animated)) { if (is_animated) gms_graphics_restart(); @@ -4046,7 +3090,7 @@ static void gms_command_animations(const char *argument) { else if (strlen(argument) == 0) { gms_normal_string("Glk graphics animations are "); - gms_normal_string(g_vm->gms_animation_enabled ? "on" : "off"); + gms_normal_string(gms_animation_enabled ? "on" : "off"); gms_normal_string(".\n"); } @@ -4059,22 +3103,16 @@ static void gms_command_animations(const char *argument) { } } - -/* - * gms_command_prompts() - * - * Turn the extra "> " prompt output on and off. - */ -static void gms_command_prompts(const char *argument) { +void Magnetic::gms_command_prompts(const char *argument) { assert(argument); if (gms_strcasecmp(argument, "on") == 0) { - if (g_vm->gms_prompt_enabled) { + if (gms_prompt_enabled) { gms_normal_string("Glk extra prompts are already on.\n"); return; } - g_vm->gms_prompt_enabled = TRUE; + gms_prompt_enabled = true; gms_normal_string("Glk extra prompts are now on.\n"); /* Check for a game prompt to clear the flag. */ @@ -4082,18 +3120,18 @@ static void gms_command_prompts(const char *argument) { } else if (gms_strcasecmp(argument, "off") == 0) { - if (!g_vm->gms_prompt_enabled) { + if (!gms_prompt_enabled) { gms_normal_string("Glk extra prompts are already off.\n"); return; } - g_vm->gms_prompt_enabled = FALSE; + gms_prompt_enabled = false; gms_normal_string("Glk extra prompts are now off.\n"); } else if (strlen(argument) == 0) { gms_normal_string("Glk extra prompts are "); - gms_normal_string(g_vm->gms_prompt_enabled ? "on" : "off"); + gms_normal_string(gms_prompt_enabled ? "on" : "off"); gms_normal_string(".\n"); } @@ -4106,46 +3144,29 @@ static void gms_command_prompts(const char *argument) { } } - -/* - * gms_command_print_version_number() - * gms_command_version() - * - * Print out the Glk library version number. - */ -static void gms_command_print_version_number(glui32 version) { - char buffer[64]; - - sprintf(buffer, "%lu.%lu.%lu", - (unsigned long) version >> 16, - (unsigned long)(version >> 8) & 0xff, - (unsigned long) version & 0xff); - gms_normal_string(buffer); +void Magnetic::gms_command_print_version_number(glui32 version_) { + Common::String str = Common::String::format("%lu.%lu.%lu", + (unsigned long)version_ >> 16, + (unsigned long)(version_ >> 8) & 0xff, + (unsigned long)version_ & 0xff); + gms_normal_string(str.c_str()); } -static void -gms_command_version(const char *argument) { - glui32 version; +void Magnetic::gms_command_version(const char *argument) { + glui32 version_; assert(argument); - gms_normal_string("This is version "); + gms_normal_string("This is version_ "); gms_command_print_version_number(GMS_PORT_VERSION); gms_normal_string(" of the Glk Magnetic port.\n"); - version = g_vm->glk_gestalt(gestalt_Version, 0); - gms_normal_string("The Glk library version is "); - gms_command_print_version_number(version); + version_ = glk_gestalt(gestalt_Version, 0); + gms_normal_string("The Glk library version_ is "); + gms_command_print_version_number(version_); gms_normal_string(".\n"); } - -/* - * gms_command_commands() - * - * Turn command escapes off. Once off, there's no way to turn them back on. - * Commands must be on already to enter this function. - */ -static void gms_command_commands(const char *argument) { +void Magnetic::gms_command_commands(const char *argument) { assert(argument); if (gms_strcasecmp(argument, "on") == 0) { @@ -4153,13 +3174,13 @@ static void gms_command_commands(const char *argument) { } else if (gms_strcasecmp(argument, "off") == 0) { - g_vm->gms_commands_enabled = FALSE; + gms_commands_enabled = false; gms_normal_string("Glk commands are now off.\n"); } else if (strlen(argument) == 0) { gms_normal_string("Glk commands are "); - gms_normal_string(g_vm->gms_commands_enabled ? "on" : "off"); + gms_normal_string(gms_commands_enabled ? "on" : "off"); gms_normal_string(".\n"); } @@ -4172,43 +3193,8 @@ static void gms_command_commands(const char *argument) { } } -/* Glk subcommands and handler functions. */ -typedef const struct { - const char *const command; /* Glk subcommand. */ - void (* const handler)(const char *argument); /* Subcommand handler. */ - const int takes_argument; /* Argument flag. */ - const int undo_return; /* "Undo" return value. */ -} gms_command_t; -typedef gms_command_t *gms_commandref_t; - -static void gms_command_summary(const char *argument); -static void gms_command_help(const char *argument); - -static gms_command_t GMS_COMMAND_TABLE[] = { - {"summary", gms_command_summary, FALSE, FALSE}, - {"undo", gms_command_undo, FALSE, TRUE}, - {"script", gms_command_script, TRUE, FALSE}, - {"inputlog", gms_command_inputlog, TRUE, FALSE}, - {"readlog", gms_command_readlog, TRUE, FALSE}, - {"abbreviations", gms_command_abbreviations, TRUE, FALSE}, - {"graphics", gms_command_graphics, TRUE, FALSE}, - {"gamma", gms_command_gamma, TRUE, FALSE}, - {"animations", gms_command_animations, TRUE, FALSE}, - {"prompts", gms_command_prompts, TRUE, FALSE}, - {"version", gms_command_version, FALSE, FALSE}, - {"commands", gms_command_commands, TRUE, FALSE}, - {"help", gms_command_help, TRUE, FALSE}, - {NULL, NULL, FALSE, FALSE} -}; - - -/* - * gms_command_summary() - * - * Report all current Glk settings. - */ -static void gms_command_summary(const char *argument) { - gms_commandref_t entry; +void Magnetic::gms_command_summary(const char *argument) { + const gms_command_t *entry; assert(argument); /* @@ -4216,29 +3202,23 @@ static void gms_command_summary(const char *argument) { * prompting each to print its current setting. */ for (entry = GMS_COMMAND_TABLE; entry->command; entry++) { - if (entry->handler == gms_command_summary - || entry->handler == gms_command_undo - || entry->handler == gms_command_help) + if (entry->handler == &Magnetic::gms_command_summary + || entry->handler == &Magnetic::gms_command_undo + || entry->handler == &Magnetic::gms_command_help) continue; - entry->handler(""); + (this->*entry->handler)(""); } } - -/* - * gms_command_help() - * - * Document the available Glk commands. - */ -static void gms_command_help(const char *command) { - gms_commandref_t entry, matched; +void Magnetic::gms_command_help(const char *command) { + const gms_command_t *entry, *matched; assert(command); if (strlen(command) == 0) { gms_normal_string("Glk commands are"); for (entry = GMS_COMMAND_TABLE; entry->command; entry++) { - gms_commandref_t next; + const gms_command_t *next; next = entry + 1; gms_normal_string(next->command ? " " : " and "); @@ -4277,17 +3257,17 @@ static void gms_command_help(const char *command) { return; } - if (matched->handler == gms_command_summary) { + if (matched->handler == &Magnetic::gms_command_summary) { gms_normal_string("Prints a summary of all the current Glk Magnetic" " settings.\n"); } - else if (matched->handler == gms_command_undo) { + else if (matched->handler == &Magnetic::gms_command_undo) { gms_normal_string("Undoes a single game turn.\n\nEquivalent to the" " standalone game 'undo' command.\n"); } - else if (matched->handler == gms_command_script) { + else if (matched->handler == &Magnetic::gms_command_script) { gms_normal_string("Logs the game's output to a file.\n\nUse "); gms_standout_string("glk script on"); gms_normal_string(" to begin logging game output, and "); @@ -4296,7 +3276,7 @@ static void gms_command_help(const char *command) { " when you turn scripts on.\n"); } - else if (matched->handler == gms_command_inputlog) { + else if (matched->handler == &Magnetic::gms_command_inputlog) { gms_normal_string("Records the commands you type into a game.\n\nUse "); gms_standout_string("glk inputlog on"); gms_normal_string(", to begin recording your commands, and "); @@ -4307,7 +3287,7 @@ static void gms_command_help(const char *command) { gms_normal_string(" command.\n"); } - else if (matched->handler == gms_command_readlog) { + else if (matched->handler == &Magnetic::gms_command_readlog) { gms_normal_string("Plays back commands recorded with "); gms_standout_string("glk inputlog on"); gms_normal_string(".\n\nUse "); @@ -4317,7 +3297,7 @@ static void gms_command_help(const char *command) { " text file created using any standard editor.\n"); } - else if (matched->handler == gms_command_abbreviations) { + else if (matched->handler == &Magnetic::gms_command_abbreviations) { gms_normal_string("Controls abbreviation expansion.\n\nGlk Magnetic" " automatically expands several standard single" " letter abbreviations for you; for example, \"x\"" @@ -4331,7 +3311,7 @@ static void gms_command_help(const char *command) { " single quote.\n"); } - else if (matched->handler == gms_command_graphics) { + else if (matched->handler == &Magnetic::gms_command_graphics) { gms_normal_string("Turns interpreter graphics on and off.\n\nUse "); gms_standout_string("glk graphics on"); gms_normal_string(" to enable interpreter graphics, and "); @@ -4346,7 +3326,7 @@ static void gms_command_help(const char *command) { " interpreter.\n"); } - else if (matched->handler == gms_command_gamma) { + else if (matched->handler == &Magnetic::gms_command_gamma) { gms_normal_string("Sets the level of automatic gamma correction applied" " to game graphics.\n\nUse "); gms_standout_string("glk gamma normal"); @@ -4359,7 +3339,7 @@ static void gms_command_help(const char *command) { gms_normal_string(" to turn off all automatic gamma correction.\n"); } - else if (matched->handler == gms_command_animations) { + else if (matched->handler == &Magnetic::gms_command_animations) { gms_normal_string("Turns graphic animations on and off.\n\nUse "); gms_standout_string("glk animation on"); gms_normal_string(" to enable animations, or "); @@ -4371,7 +3351,7 @@ static void gms_command_help(const char *command) { " game's pictures.\n"); } - else if (matched->handler == gms_command_prompts) { + else if (matched->handler == &Magnetic::gms_command_prompts) { gms_normal_string("Controls extra input prompting.\n\n" "Glk Magnetic can issue a replacement '>' input" " prompt if it detects that the game hasn't prompted" @@ -4382,12 +3362,12 @@ static void gms_command_help(const char *command) { gms_normal_string(" to turn it off.\n"); } - else if (matched->handler == gms_command_version) { + else if (matched->handler == &Magnetic::gms_command_version) { gms_normal_string("Prints the version numbers of the Glk library" " and the Glk Magnetic port.\n"); } - else if (matched->handler == gms_command_commands) { + else if (matched->handler == &Magnetic::gms_command_commands) { gms_normal_string("Turn off Glk commands.\n\nUse "); gms_standout_string("glk commands off"); gms_normal_string(" to disable all Glk commands, including this one." @@ -4395,7 +3375,7 @@ static void gms_command_help(const char *command) { " commands back on while inside the game.\n"); } - else if (matched->handler == gms_command_help) + else if (matched->handler == &Magnetic::gms_command_help) gms_command_help(""); else @@ -4403,32 +3383,22 @@ static void gms_command_help(const char *command) { " Sorry.\n"); } - -/* - * gms_command_escape() - * - * This function is handed each input line. If the line contains a specific - * Glk port command, handle it and return TRUE, otherwise return FALSE. - * - * On unambiguous returns, it will also set the value for undo_command to the - * table undo return value. - */ -static int gms_command_escape(const char *string, int *undo_command) { +int Magnetic::gms_command_escape(const char *string_, int *undo_command) { int posn; char *string_copy, *command, *argument; - assert(string && undo_command); + assert(string_ && undo_command); /* - * Return FALSE if the string doesn't begin with the Glk command escape + * Return false if the string doesn't begin with the Glk command escape * introducer. */ - posn = strspn(string, "\t "); - if (gms_strncasecmp(string + posn, "glk", strlen("glk")) != 0) - return FALSE; + posn = strspn(string_, "\t "); + if (gms_strncasecmp(string_ + posn, "glk", strlen("glk")) != 0) + return false; - /* Take a copy of the string, without any leading space or introducer. */ - string_copy = (char *)gms_malloc(strlen(string + posn) + 1 - strlen("glk")); - strcpy(string_copy, string + posn + strlen("glk")); + /* Take a copy of the string_, without any leading space or introducer. */ + string_copy = (char *)gms_malloc(strlen(string_ + posn) + 1 - strlen("glk")); + strcpy(string_copy, string_ + posn + strlen("glk")); /* * Find the subcommand; the first word in the string copy. Find its end, @@ -4455,11 +3425,11 @@ static int gms_command_escape(const char *string, int *undo_command) { * as "help". */ if (strlen(command) > 0) { - gms_commandref_t entry, matched; + const gms_command_t *entry, *matched; int matches; /* - * Search for the first unambiguous table command string matching + * Search for the first unambiguous table command string_ matching * the command passed in. */ matches = 0; @@ -4475,7 +3445,7 @@ static int gms_command_escape(const char *string, int *undo_command) { if (matches == 1) { if (!matched->undo_return) gms_normal_char('\n'); - matched->handler(argument); + (this->*(matched->handler))(argument); if (!matched->takes_argument && strlen(argument) > 0) { gms_normal_string("[The "); @@ -4501,91 +3471,48 @@ static int gms_command_escape(const char *string, int *undo_command) { gms_command_help(""); } - /* The string contained a Glk command; return TRUE. */ + /* The string_ contained a Glk command; return true. */ free(string_copy); - return TRUE; + return true; } - -/* - * gms_command_undo_special() - * - * This function makes a special case of the input line containing the single - * word "undo", treating it as if it is "glk undo". This makes life a bit - * more convenient for the player, since it's the same behavior that most - * other IF systems have. It returns TRUE if "undo" found, FALSE otherwise. - */ -static int gms_command_undo_special(const char *string) { +int Magnetic::gms_command_undo_special(const char *string_) { int posn, end; - assert(string); + assert(string_); - /* Find the start and end of the first string word. */ - posn = strspn(string, "\t "); - end = posn + strcspn(string + posn, "\t "); + /* Find the start and end of the first string_ word. */ + posn = strspn(string_, "\t "); + end = posn + strcspn(string_ + posn, "\t "); - /* See if string contains an "undo" request, with nothing following. */ + /* See if string_ contains an "undo" request, with nothing following. */ if (end - posn == (int)strlen("undo") - && gms_strncasecmp(string + posn, "undo", end - posn) == 0) { - posn = end + strspn(string + end, "\t "); - if (string[posn] == '\0') - return TRUE; + && gms_strncasecmp(string_ + posn, "undo", end - posn) == 0) { + posn = end + strspn(string_ + end, "\t "); + if (string_[posn] == '\0') + return true; } - return FALSE; + return false; } - /*---------------------------------------------------------------------*/ /* Glk port input functions */ /*---------------------------------------------------------------------*/ -/* - * Input buffer allocated for reading input lines. The buffer is filled - * from either an input log, if one is currently being read, or from Glk - * line input. We also need an "undo" notification flag. - */ -enum { GMS_INPUTBUFFER_LENGTH = 256 }; -static char gms_input_buffer[GMS_INPUTBUFFER_LENGTH]; -static int gms_input_length = 0, - gms_input_cursor = 0, - gms_undo_notification = FALSE; - -/* Table of single-character command abbreviations. */ -typedef const struct { - const char abbreviation; /* Abbreviation character. */ - const char *const expansion; /* Expansion string. */ -} gms_abbreviation_t; -typedef gms_abbreviation_t *gms_abbreviationref_t; - -static gms_abbreviation_t GMS_ABBREVIATIONS[] = { - {'c', "close"}, {'g', "again"}, {'i', "inventory"}, - {'k', "attack"}, {'l', "look"}, {'p', "open"}, - {'q', "quit"}, {'r', "drop"}, {'t', "take"}, - {'x', "examine"}, {'y', "yes"}, {'z', "wait"}, - {'\0', NULL} -}; - - -/* - * gms_expand_abbreviations() - * - * Expand a few common one-character abbreviations commonly found in other - * game systems, but not always normal in Magnetic Scrolls games. - */ -static void gms_expand_abbreviations(char *buffer, int size) { +void Magnetic::gms_expand_abbreviations(char *buffer_, int size) { char *command, abbreviation; const char *expansion; gms_abbreviationref_t entry; - assert(buffer); + assert(buffer_); /* Ignore anything that isn't a single letter command. */ - command = buffer + strspn(buffer, "\t "); + command = buffer_ + strspn(buffer_, "\t "); if (!(strlen(command) == 1 || (strlen(command) > 1 && Common::isSpace(command[1])))) return; /* Scan the abbreviations table for a match. */ - abbreviation = g_vm->glk_char_to_lower((unsigned char) command[0]); + abbreviation = glk_char_to_lower((unsigned char) command[0]); expansion = NULL; for (entry = GMS_ABBREVIATIONS; entry->expansion; entry++) { if (entry->abbreviation == abbreviation) { @@ -4599,7 +3526,7 @@ static void gms_expand_abbreviations(char *buffer, int size) { * expansion string. */ if (expansion) { - if ((int)(strlen(buffer) + strlen(expansion)) - 1 >= size) + if ((int)(strlen(buffer_) + strlen(expansion)) - 1 >= size) return; memmove(command + strlen(expansion) - 1, command, strlen(command) + 1); @@ -4615,20 +3542,7 @@ static void gms_expand_abbreviations(char *buffer, int size) { } } - -/* - * gms_buffer_input - * - * Read and buffer a line of input. If there is an input log active, then - * data is taken by reading this first. Otherwise, the function gets a - * line from Glk. - * - * It also makes special cases of some lines read from the user, either - * handling commands inside them directly, or expanding abbreviations as - * appropriate. This is not reflected in the buffer, which is adjusted as - * required before returning. - */ -static void gms_buffer_input(void) { +void Magnetic::gms_buffer_input() { event_t event; /* @@ -4645,7 +3559,7 @@ static void gms_buffer_input(void) { * To slightly improve things, if it looks like we didn't get a prompt from * the game, do our own. */ - if (g_vm->gms_prompt_enabled && !gms_game_prompted()) { + if (gms_prompt_enabled && !gms_game_prompted()) { gms_normal_char('\n'); gms_normal_string(GMS_INPUT_PROMPT); } @@ -4658,13 +3572,13 @@ static void gms_buffer_input(void) { glui32 chars; /* Get the next line from the log stream. */ - chars = g_vm->glk_get_line_stream(gms_readlog_stream, + chars = glk_get_line_stream(gms_readlog_stream, gms_input_buffer, sizeof(gms_input_buffer)); if (chars > 0) { /* Echo the line just read in input style. */ - g_vm->glk_set_style(style_Input); - g_vm->glk_put_buffer(gms_input_buffer, chars); - g_vm->glk_set_style(style_Normal); + glk_set_style(style_Input); + glk_put_buffer(gms_input_buffer, chars); + glk_set_style(style_Normal); /* Note how many characters buffered, and return. */ gms_input_length = chars; @@ -4675,7 +3589,7 @@ static void gms_buffer_input(void) { * We're at the end of the log stream. Close it, and then continue * on to request a line from Glk. */ - g_vm->glk_stream_close(gms_readlog_stream, NULL); + glk_stream_close(gms_readlog_stream, NULL); gms_readlog_stream = NULL; } @@ -4683,11 +3597,11 @@ static void gms_buffer_input(void) { * No input log being read, or we just hit the end of file on one. Revert * to normal line input; start by getting a new line from Glk. */ - g_vm->glk_request_line_event(gms_main_window, + glk_request_line_event(gms_main_window, gms_input_buffer, sizeof(gms_input_buffer) - 1, 0); gms_event_wait(evtype_LineInput, &event); - if (g_vm->shouldQuit()) { - g_vm->glk_cancel_line_event(gms_main_window, &event); + if (shouldQuit()) { + glk_cancel_line_event(gms_main_window, &event); return; } @@ -4699,15 +3613,15 @@ static void gms_buffer_input(void) { if (gms_command_undo_special(gms_input_buffer)) { /* Write the "undo" to any input log. */ if (gms_inputlog_stream) { - g_vm->glk_put_string_stream(gms_inputlog_stream, gms_input_buffer); - g_vm->glk_put_char_stream(gms_inputlog_stream, '\n'); + glk_put_string_stream(gms_inputlog_stream, gms_input_buffer); + glk_put_char_stream(gms_inputlog_stream, '\n'); } /* Overwrite buffer with an empty line if we saw "undo". */ gms_input_buffer[0] = '\n'; gms_input_length = 1; - gms_undo_notification = TRUE; + gms_undo_notification = true; return; } @@ -4715,7 +3629,7 @@ static void gms_buffer_input(void) { * If neither abbreviations nor local commands are enabled, use the data * read above without further massaging. */ - if (g_vm->gms_abbreviations_enabled || g_vm->gms_commands_enabled) { + if (gms_abbreviations_enabled || gms_commands_enabled) { char *command; /* @@ -4729,7 +3643,7 @@ static void gms_buffer_input(void) { memmove(command, command + 1, strlen(command)); } else { /* Check for, and expand, any abbreviated commands. */ - if (g_vm->gms_abbreviations_enabled) { + if (gms_abbreviations_enabled) { gms_expand_abbreviations(gms_input_buffer, sizeof(gms_input_buffer)); } @@ -4739,7 +3653,7 @@ static void gms_buffer_input(void) { * suppress the interpreter's use of this input for Glk commands * by overwriting the line with a single newline character. */ - if (g_vm->gms_commands_enabled) { + if (gms_commands_enabled) { int posn; posn = strspn(gms_input_buffer, "\t "); @@ -4768,8 +3682,8 @@ static void gms_buffer_input(void) { * special commands, nor any input read from a current open input log. */ if (gms_inputlog_stream) { - g_vm->glk_put_string_stream(gms_inputlog_stream, gms_input_buffer); - g_vm->glk_put_char_stream(gms_inputlog_stream, '\n'); + glk_put_string_stream(gms_inputlog_stream, gms_input_buffer); + glk_put_char_stream(gms_inputlog_stream, '\n'); } /* @@ -4784,15 +3698,7 @@ static void gms_buffer_input(void) { gms_input_length = strlen(gms_input_buffer); } - -/* - * ms_getchar() - * - * Return the single next character to the interpreter. This function - * extracts characters from the input buffer until empty, when it then - * tries to buffer more data. - */ -type8 ms_getchar(type8 trans) { +type8 Magnetic::ms_getchar(type8 trans) { /* See if we are at the end of the input buffer. */ if (gms_input_cursor == gms_input_length) { /* @@ -4802,7 +3708,7 @@ type8 ms_getchar(type8 trans) { gms_buffer_input(); gms_input_cursor = 0; - if (g_vm->shouldQuit()) + if (shouldQuit()) return '\0'; if (gms_undo_notification) { @@ -4810,7 +3716,7 @@ type8 ms_getchar(type8 trans) { * Clear the undo notification, and discard buffered input (usually * just the '\n' placed there when the undo command was recognized). */ - gms_undo_notification = FALSE; + gms_undo_notification = false; gms_input_length = 0; /* @@ -4826,59 +3732,15 @@ type8 ms_getchar(type8 trans) { return gms_input_buffer[gms_input_cursor++]; } -#if 0 -/* - * gms_confirm() - * - * Print a confirmation prompt, and read a single input character, taking - * only [YyNn] input. If the character is 'Y' or 'y', return TRUE. - */ -static int gms_confirm(const char *prompt) { - event_t event; - unsigned char response; - assert(prompt); - - /* - * Print the confirmation prompt, in a style that hints that it's from the - * interpreter, not the game. - */ - gms_standout_string(prompt); - - /* Wait for a single 'Y' or 'N' character response. */ - response = ' '; - do { - g_vm->glk_request_char_event(gms_main_window); - gms_event_wait(evtype_CharInput, &event); - - if (event.val1 <= BYTE_MAX) - response = g_vm->glk_char_to_upper(event.val1); - } while (!(response == 'Y' || response == 'N')); - - /* Echo the confirmation response, and a blank line. */ - g_vm->glk_set_style(style_Input); - g_vm->glk_put_string(response == 'Y' ? "Yes" : "No"); - g_vm->glk_set_style(style_Normal); - g_vm->glk_put_string("\n\n"); - - return response == 'Y'; -} -#endif - /*---------------------------------------------------------------------*/ /* Glk port event functions */ /*---------------------------------------------------------------------*/ -/* - * gms_event_wait() - * - * Process Glk events until one of the expected type arrives. Return - * the event of that type. - */ -static void gms_event_wait(glui32 wait_type, event_t *event) { +void Magnetic::gms_event_wait(glui32 wait_type, event_t *event) { assert(event); do { - g_vm->glk_select(event); + glk_select(event); switch (event->type) { case evtype_Arrange: @@ -4907,65 +3769,28 @@ static void gms_event_wait(glui32 wait_type, event_t *event) { /* Functions intercepted by link-time wrappers */ /*---------------------------------------------------------------------*/ -/* - * __wrap_toupper() - * __wrap_tolower() - * - * Wrapper functions around toupper() and tolower(). The Linux linker's - * --wrap option will convert calls to mumble() to __wrap_mumble() if we - * give it the right options. We'll use this feature to translate all - * toupper() and tolower() calls in the interpreter code into calls to - * Glk's versions of these functions. - * - * It's not critical that we do this. If a linker, say a non-Linux one, - * won't do --wrap, then just do without it. It's unlikely that there - * will be much noticeable difference. - */ -int __wrap_toupper(int ch) { +int Magnetic::__wrap_toupper(int ch) { unsigned char uch; - uch = g_vm->glk_char_to_upper((unsigned char) ch); + uch = glk_char_to_upper((unsigned char) ch); return (int) uch; } -int __wrap_tolower(int ch) { +int Magnetic::__wrap_tolower(int ch) { unsigned char lch; - lch = g_vm->glk_char_to_lower((unsigned char) ch); + lch = glk_char_to_lower((unsigned char) ch); return (int) lch; } - /*---------------------------------------------------------------------*/ /* main() and options parsing */ /*---------------------------------------------------------------------*/ -/* - * The following values need to be passed between the startup_code and main - * functions. - */ -static const char *gms_game_message = NULL; /* Error message. */ - - -/* - * gms_establish_filenames() - * - * Given a game name, try to establish three filenames from it - the main game - * text file, the (optional) graphics data file, and the (optional) hints - * file. Given an input "file" X, the function looks for X.MAG or X.mag for - * game data, X.GFX or X.gfx for graphics, and X.HNT or X.hnt for hints. - * If the input file already ends with .MAG, .GFX, or .HNT, the extension - * is stripped first. - * - * The function returns NULL for filenames not available. It's not fatal if - * the graphics filename or hints filename is NULL, but it is if the main game - * filename is NULL. Filenames are malloc'ed, and need to be freed by the - * caller. - */ -static void gms_establish_filenames(const char *name, char **text, char **graphics, char **hints) { +void Magnetic::gms_establish_filenames(const char *name, char **text, char **graphics, char **hints_) { char *base, *text_file, *graphics_file, *hints_file; Common::File stream; - assert(name && text && graphics && hints); + assert(name && text && graphics && hints_); /* Take a destroyable copy of the input filename. */ base = (char *)gms_malloc(strlen(name) + 1); @@ -4994,11 +3819,11 @@ static void gms_establish_filenames(const char *name, char **text, char **graphi if (!stream.open(text_file)) { /* * No access to a usable game text file. Return immediately, - * without looking for any associated graphics or hints files. + * without looking for any associated graphics or hints_ files. */ *text = NULL; *graphics = NULL; - *hints = NULL; + *hints_ = NULL; free(text_file); free(base); @@ -5030,7 +3855,7 @@ static void gms_establish_filenames(const char *name, char **text, char **graphi } stream.close(); - /* Now allocate space for the return hints file. */ + /* Now allocate space for the return hints_ file. */ hints_file = (char *)gms_malloc(strlen(base) + strlen(".HNT") + 1); /* As above, form a candidate graphics file, using a .HNT extension. */ @@ -5044,8 +3869,8 @@ static void gms_establish_filenames(const char *name, char **text, char **graphi if (!stream.open(hints_file)) { /* - * No access to any hints file. In this case, free memory and - * reset hints file to NULL. + * No access to any hints_ file. In this case, free memory and + * reset hints_ file to NULL. */ free(hints_file); hints_file = NULL; @@ -5053,35 +3878,35 @@ static void gms_establish_filenames(const char *name, char **text, char **graphi } stream.close(); - /* Return the text file, and graphics and hints, which may be NULL. */ + /* Return the text file, and graphics and hints_, which may be NULL. */ *text = text_file; *graphics = graphics_file; - *hints = hints_file; + *hints_ = hints_file; free(base); } -void gms_main() { +void Magnetic::gms_main() { char *text_file = NULL, *graphics_file = NULL, *hints_file = NULL; int ms_init_status, is_running; /* Create the main Glk window, and set its stream as current. */ - gms_main_window = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0); + gms_main_window = glk_window_open(0, 0, 0, wintype_TextBuffer, 0); if (!gms_main_window) { gms_fatal("GLK: Can't open main window"); - g_vm->glk_exit(); + glk_exit(); return; } - g_vm->glk_window_clear(gms_main_window); - g_vm->glk_set_window(gms_main_window); - g_vm->glk_set_style(style_Normal); + glk_window_clear(gms_main_window); + glk_set_window(gms_main_window); + glk_set_style(style_Normal); /* * Given the basic game name, try to come up with usable text, graphics, * and hints filenames. The graphics and hints files may be null, but the * text file may not. */ - Common::String gameFile = g_vm->getFilename(); + Common::String gameFile = getFilename(); gms_establish_filenames(gameFile.c_str(), &text_file, &graphics_file, &hints_file); /* Set the possibility of pictures depending on graphics file. */ @@ -5091,10 +3916,10 @@ void gms_main() { * the library can't offer both graphics and timers. We need timers to * create the background "thread" for picture updates. */ - gms_graphics_possible = g_vm->glk_gestalt(gestalt_Graphics, 0) - && g_vm->glk_gestalt(gestalt_Timer, 0); + gms_graphics_possible = glk_gestalt(gestalt_Graphics, 0) + && glk_gestalt(gestalt_Timer, 0); } else - gms_graphics_possible = FALSE; + gms_graphics_possible = false; /* @@ -5103,11 +3928,11 @@ void gms_main() { * been. If pictures are impossible, they can never be enabled. */ if (!gms_graphics_possible) - g_vm->gms_graphics_enabled = FALSE; + gms_graphics_enabled = false; /* Try to create a one-line status window. We can live without it. */ - g_vm->glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1); - gms_status_window = g_vm->glk_window_open(gms_main_window, + glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1); + gms_status_window = glk_window_open(gms_main_window, winmethod_Above | winmethod_Fixed, 1, wintype_TextGrid, 0); @@ -5124,7 +3949,7 @@ void gms_main() { /* Look for a complete failure to load the game. */ if (ms_init_status == 0) { if (gms_status_window) - g_vm->glk_window_close(gms_status_window, NULL); + glk_window_close(gms_status_window, NULL); gms_header_string("Glk Magnetic Error\n\n"); gms_normal_string("Can't load game '"); gms_normal_string(gameFile.c_str()); @@ -5140,7 +3965,7 @@ void gms_main() { free(graphics_file); free(hints_file); ms_freemem(); - g_vm->glk_exit(); + glk_exit(); } /* Try to identify the game from its text file header. */ @@ -5155,13 +3980,13 @@ void gms_main() { gms_standout_string("Error: Unable to open graphics file\n" "Continuing without pictures...\n\n"); - gms_graphics_possible = FALSE; + gms_graphics_possible = false; } - /* Run the game opcodes -- ms_rungame() returns FALSE on game end. */ + /* Run the game opcodes -- ms_rungame() returns false on game end. */ do { - is_running = ms_rungame() && !g_vm->shouldQuit(); - g_vm->glk_tick(); + is_running = ms_rungame() && !shouldQuit(); + glk_tick(); } while (is_running); /* Handle any updated status and pending buffered output. */ @@ -5182,15 +4007,15 @@ void gms_main() { /* Close any open transcript, input log, and/or read log. */ if (gms_transcript_stream) { - g_vm->glk_stream_close(gms_transcript_stream, NULL); + glk_stream_close(gms_transcript_stream, NULL); gms_transcript_stream = NULL; } if (gms_inputlog_stream) { - g_vm->glk_stream_close(gms_inputlog_stream, NULL); + glk_stream_close(gms_inputlog_stream, NULL); gms_inputlog_stream = NULL; } if (gms_readlog_stream) { - g_vm->glk_stream_close(gms_readlog_stream, NULL); + glk_stream_close(gms_readlog_stream, NULL); gms_readlog_stream = NULL; } @@ -5200,42 +4025,28 @@ void gms_main() { free(hints_file); } - /*---------------------------------------------------------------------*/ /* Linkage between Glk entry/exit calls and the Magnetic interpreter */ /*---------------------------------------------------------------------*/ -/* - * Safety flags, to ensure we always get startup before main, and that - * we only get a call to main once. - */ -static int gms_startup_called = FALSE, - gms_main_called = FALSE; - -/* - * glk_main() - * - * Main entry point for Glk. Here, all startup is done, and we call our - * function to run the game. - */ -void glk_main() { +void Magnetic::glk_main() { assert(gms_startup_called && !gms_main_called); - gms_main_called = TRUE; + gms_main_called = true; /* Call the interpreter main function. */ gms_main(); } -void write(const char *fmt, ...) { +void Magnetic::write(const char *fmt, ...) { va_list ap; va_start(ap, fmt); Common::String s = Common::String::format(fmt, ap); va_end(ap); - g_vm->glk_put_buffer(s.c_str(), s.size()); + glk_put_buffer(s.c_str(), s.size()); } -void writeChar(char c) { - g_vm->glk_put_char(c); +void Magnetic::writeChar(char c) { + glk_put_char(c); } } // End of namespace Magnetic diff --git a/engines/glk/magnetic/magnetic.cpp b/engines/glk/magnetic/magnetic.cpp index b4f1b2ea1b..f8ab15aab6 100644 --- a/engines/glk/magnetic/magnetic.cpp +++ b/engines/glk/magnetic/magnetic.cpp @@ -21,7 +21,7 @@ */ #include "glk/magnetic/magnetic.h" -#include "glk/magnetic/defs.h" +#include "glk/magnetic/magnetic_defs.h" #include "common/config-manager.h" namespace Glk { @@ -32,7 +32,57 @@ Magnetic *g_vm; Magnetic::Magnetic(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), gms_gamma_mode(GAMMA_NORMAL), gms_animation_enabled(true), gms_prompt_enabled(true), gms_abbreviations_enabled(true), gms_commands_enabled(true), - gms_graphics_enabled(false) { + gms_graphics_enabled(false), GMS_PORT_VERSION(0x00010601), + gms_main_window(nullptr), gms_status_window(nullptr), gms_graphics_window(nullptr), + gms_hint_menu_window(nullptr), gms_hint_text_window(nullptr), + gms_transcript_stream(nullptr), gms_readlog_stream(nullptr), + gms_inputlog_stream(nullptr), gms_graphics_possible(true), + GMS_INPUT_PROMPT(">"), gms_gameid_game_name(nullptr), gms_graphics_bitmap(nullptr), + gms_graphics_width(0), gms_graphics_height(0), gms_graphics_animated(false), + gms_graphics_picture(0), gms_graphics_new_picture(false), + gms_graphics_repaint(false), gms_graphics_active(false), + gms_graphics_interpreter(false), gms_graphics_off_screen(nullptr), + gms_graphics_on_screen(nullptr),// gms_graphics_current_gamma(Magnetic::GMS_GAMMA_TABLE), + gms_graphics_color_count(GMS_PALETTE_SIZE), gms_status_length(0), + gms_help_requested(false), gms_help_hints_silenced(false), + gms_output_buffer(nullptr), gms_output_allocation(0),gms_output_length(0), + gms_output_prompt(false), gms_hints(nullptr), gms_current_hint_node(0), + gms_hint_cursor(nullptr), gms_input_length(0), gms_input_cursor(0), + gms_undo_notification(false), gms_game_message(nullptr), gms_startup_called(false), + gms_main_called(false), gms_graphics_current_gamma(nullptr), + 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), running(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(nullptr), gfx2_buf(nullptr), + gfx2_name(nullptr), gfx2_hsize(0), gfx_fp(nullptr), snd_buf(nullptr), snd_hdr(nullptr), + snd_hsize(0), snd_fp(nullptr), undo_pc(0), undo_size(0), gfxtable(0), table_dist(0), + v4_id(0), next_table(1) +#ifndef NO_ANIMATION + , pos_table_size(0), command_table(nullptr), command_index(-1), + pos_table_index(-1), pos_table_max(-1), anim_repeat(0) +#endif + , hints(nullptr), hint_contents(nullptr), xpos(0), bufpos(0), log_on(0), + ms_gfx_enabled(0), log1(nullptr), log2(nullptr) { + + Common::fill(&gms_graphics_palette[0], &gms_graphics_palette[GMS_PALETTE_SIZE], 0); + Common::fill(&gms_status_buffer[0], &gms_status_buffer[GMS_STATBUFFER_LENGTH], '\0'); + Common::fill(&gms_input_buffer[0], &gms_input_buffer[GMS_INPUTBUFFER_LENGTH], '\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[3][0], 0); + undo[0] = undo[1] = nullptr; + undo_stat[0] = undo_stat[1] = 0; + Common::fill(&buffer[0], &buffer[80], 0); + Common::fill(&filename[0], &filename[256], 0); + +#ifndef NO_ANIMATION + Common::fill(&pos_table_count[0], &pos_table_count[MAX_POSITIONS], 0); +#endif + g_vm = this; } diff --git a/engines/glk/magnetic/magnetic.h b/engines/glk/magnetic/magnetic.h index 4e81e4f5e5..74d5b300da 100644 --- a/engines/glk/magnetic/magnetic.h +++ b/engines/glk/magnetic/magnetic.h @@ -28,28 +28,1307 @@ #include "common/scummsys.h" #include "glk/glk_api.h" #include "glk/magnetic/magnetic_types.h" +#include "glk/magnetic/magnetic_defs.h" +#include "glk/magnetic/detection.h" namespace Glk { namespace Magnetic { -enum GammaMode { - GAMMA_OFF, GAMMA_NORMAL, GAMMA_HIGH -}; +class Magnetic; + +typedef void (Magnetic::*CommandPtr)(const char *argument); + +/* Glk subcommands and handler functions. */ +struct gms_command_t { + CommandPtr handler; ///< Subcommand handler + const char *const command; ///< Glk subcommand + bool takes_argument; ///< Argument flag + bool undo_return; ///< "Undo" return value +} ; +typedef gms_command_t *gms_commandref_t; + /** * Magnetic game interpreter */ class Magnetic : public GlkAPI { public: + static const gms_command_t GMS_COMMAND_TABLE[14]; +private: GammaMode gms_gamma_mode; bool gms_animation_enabled, gms_prompt_enabled; bool gms_abbreviations_enabled, gms_commands_enabled; bool gms_graphics_enabled; + + // Glk Magnetic Scrolls port version number + const glui32 GMS_PORT_VERSION; + + /** + * We use a maximum of five Glk windows, one for status, one for pictures, + * two for hints, and one for everything else. The status and pictures + * windows may be NULL, depending on user selections and the capabilities + * of the Glk library. The hints windows will normally be NULL, except + * when in the hints subsystem. + */ + winid_t gms_main_window, gms_status_window, gms_graphics_window; + winid_t gms_hint_menu_window, gms_hint_text_window; + + /** + * Transcript stream and input log. These are NULL if there is no current + * collection of these strings. + */ + strid_t gms_transcript_stream, gms_inputlog_stream; + + // Input read log stream, for reading back an input log + strid_t gms_readlog_stream; + + /* Note about whether graphics is possible, or not. */ + bool gms_graphics_possible; + + /* Magnetic Scrolls standard input prompt string. */ + const char *const GMS_INPUT_PROMPT; + + /** + * The game's name, suitable for printing out on a status line, or other + * location where game information is relevant. Set on game startup, by + * identifying the game from its text file header. + */ + const char *gms_gameid_game_name; + + /* + * The current picture bitmap being displayed, its width, height, palette, + * animation flag, and picture id. + */ + type8 *gms_graphics_bitmap; + type16 gms_graphics_width, gms_graphics_height; + type16 gms_graphics_palette[GMS_PALETTE_SIZE]; /* = { 0, ... }; */ + bool gms_graphics_animated; + type32 gms_graphics_picture; + + /* + * Flags set on new picture, and on resize or arrange events, and a flag + * to indicate whether background repaint is stopped or active. + */ + bool gms_graphics_new_picture, gms_graphics_repaint; + bool gms_graphics_active; + + /* Flag to try to monitor the state of interpreter graphics. */ + bool gms_graphics_interpreter; + + /* + * Pointer to the two graphics buffers, one the off-screen representation + * of pixels, and the other tracking on-screen data. These are temporary + * graphics malloc'ed memory, and should be free'd on exit. + */ + type8 *gms_graphics_off_screen, *gms_graphics_on_screen; + + /* + * Pointer to the current active gamma table entry. Because of the way + * it's queried, this may not be NULL, otherwise we risk a race, with + * admittedly a very low probability, with the updater. So, it's init- + * ialized instead to the gamma table. The real value in use is inserted + * on the first picture update timeout call for a new picture. + */ + gms_gammaref_t gms_graphics_current_gamma; + + /* + * The number of colors used in the palette by the current picture. This + * value is also at risk of a race with the updater, so it too has a mild + * lie for a default value. + */ + int gms_graphics_color_count; + + /** + * The interpreter feeds us status line characters one at a time, with Tab + * indicating right justify, and CR indicating the line is complete. To get + * this to fit with the Glk event and redraw model, here we'll buffer each + * completed status line, so we have a stable string to output when needed. + * It's also handy to have this buffer for Glk libraries that don't support + * separate windows. + */ + char gms_status_buffer[GMS_STATBUFFER_LENGTH]; + int gms_status_length; + + /* + * Flag for if the user entered "help" as their last input, or if hints have + * been silenced as a result of already using a Glk command. + */ + int gms_help_requested, gms_help_hints_silenced; + + /* + * Output buffer. We receive characters one at a time, and it's a bit + * more efficient for everyone if we buffer them, and output a complete + * string on a flush call. + */ + char *gms_output_buffer; + int gms_output_allocation, gms_output_length; + + /* + * Flag to indicate if the last buffer flushed looked like it ended in a + * ">" prompt. + */ + int gms_output_prompt; + + /* + * Note of the interpreter's hints array. Note that keeping its address + * like this assumes that it's either static or heap in the interpreter. + */ + ms_hint *gms_hints; + + /* Details of the current hint node on display from the hints array. */ + type16 gms_current_hint_node; + + /* + * Array of cursors for each hint. The cursor indicates the current hint + * position in a folder, and the last hint shown in text hints. Space + * is allocated as needed for a given set of hints, and needs to be freed + * on interpreter exit. + */ + int *gms_hint_cursor; + + /* + * Input buffer allocated for reading input lines. The buffer is filled + * from either an input log, if one is currently being read, or from Glk + * line input. We also need an "undo" notification flag. + */ + char gms_input_buffer[GMS_INPUTBUFFER_LENGTH]; + int gms_input_length, gms_input_cursor, gms_undo_notification; + + /* + * The following values need to be passed between the startup_code and main + * functions. + */ + const char *gms_game_message; /* Error message. */ + + /* + * Safety flags, to ensure we always get startup before main, and that + * we only get a call to main once. + */ + int gms_startup_called, gms_main_called; +private: + type32 dreg[8], areg[8], i_count, string_size, rseed, pc, arg1i, mem_size; + type16 properties, fl_sub, fl_tab, fl_size, fp_tab, fp_size; + type8 zflag, nflag, cflag, vflag, byte1, byte2, regnr, admode, opsize; + type8 *arg1, *arg2, is_reversible, running, tmparg[4]; + type8 lastchar, version, sd; + type8 *decode_table, *restart, *code, *string, *string2; + type8 *string3, *dict; + type8 quick_flag, gfx_ver, *gfx_buf, *gfx_data; + type8 *gfx2_hdr, *gfx2_buf; + const char *gfx2_name; + type16 gfx2_hsize; + Common::File *gfx_fp; + type8 *snd_buf, *snd_hdr; + type16 snd_hsize; + Common::File *snd_fp; + + type32 undo_regs[2][18], undo_pc, undo_size; + type8 *undo[2], undo_stat[2]; + type16 gfxtable, table_dist; + type16 v4_id, next_table; + +#ifndef NO_ANIMATION + type16 pos_table_size; + type8 *command_table; + type16s command_index; + type16s pos_table_index; + type16s pos_table_max; + type8 anim_repeat; + type16 pos_table_count[MAX_POSITIONS]; + picture anim_frame_table[MAX_ANIMS]; + ms_position pos_table[MAX_POSITIONS][MAX_ANIMS]; + lookup anim_table[MAX_POSITIONS]; + ms_position pos_array[MAX_FRAMES]; +#endif + + /* Hint support */ + ms_hint *hints; + type8 *hint_contents; +private: + type8 buffer[80], xpos, bufpos, log_on, ms_gfx_enabled, filename[256]; + Common::DumpFile *log1, *log2; private: /** * Performs initialization */ void initialize(); + + /** + * Fatal error handler. The function returns, expecting the caller to + * abort() or otherwise handle the error. + */ + void gms_fatal(const char *string); + + /** + * Non-failing malloc. Calls error if memory allocation fails + */ + void *gms_malloc(size_t size); + + /** + * Non-failing realloc. Calls error if memory allocation fails + */ + void *gms_realloc(void *ptr, size_t size); + + /** + * Local comparison routine that doesn't have an ANSI standard + */ + int gms_strncasecmp(const char *s1, const char *s2, size_t n); + + /** + * Local comparison routine that doesn't have an ANSI standard + */ + int gms_strcasecmp(const char *s1, const char *s2); + + /** + * Return the CRC of the bytes in buffer[0..length-1]. + * + * This algorithm is taken from the PNG specification, version 1.0. + */ + glui32 gms_get_buffer_crc(const void *void_buffer, size_t length); + + /** + * Endian-safe unsigned 32 bit integer read from game text file. Returns + * 0 on error, a known unused table value. + */ + type32 gms_gameid_read_uint32(int offset, Common::SeekableReadStream *stream); + + /** + * Identify a game from its text file header, and cache the game's name for + * later queries. Sets the cache to NULL if not found. + */ + void gms_gameid_identify_game(const Common::String &text_file); + + /** + * Return the name of the game, or NULL if not identifiable. + */ + const char *gms_gameid_get_game_name() const { + return gms_gameid_game_name; + } + + /** + * If it's not open, open the graphics window. Returns TRUE if graphics + * was successfully started, or already on. + */ + int gms_graphics_open(); + + /** + * If open, close the graphics window and set back to NULL. + */ + void gms_graphics_close(); + + /** + * If graphics enabled, start any background picture update processing. + */ + void gms_graphics_start(); + + /** + * Stop any background picture update processing. + */ + void gms_graphics_stop(); + + /** + * Return TRUE if graphics are currently being displayed, FALSE otherwise. + */ + int gms_graphics_are_displayed() const { + return gms_graphics_window != nullptr; + } + + /** + * Set up a complete repaint of the current picture in the graphics window. + * This function should be called on the appropriate Glk window resize and + * arrange events. + */ + void gms_graphics_paint(); + + /** + * Restart graphics as if the current picture is a new picture. This + * function should be called whenever graphics is re-enabled after being + * disabled, on change of gamma color correction policy, and on change + * of animation policy. + */ + void gms_graphics_restart(); + + /** + * Analyze an image, and return the usage count of each palette color, and + * an overall count of how many colors out of the palette are used. NULL + * arguments indicate no interest in the return value. + */ + void gms_graphics_count_colors(type8 bitmap[], type16 width, type16 height, + int *color_count, long color_usage[]); + + /** + * General graphics color conversion + */ + void gms_graphics_game_to_rgb_color(type16 color, gms_gammaref_t gamma, + gms_rgbref_t rgb_color); + + /** + * General graphics color conversion + */ + void gms_graphics_split_color(glui32 color, gms_rgbref_t rgb_color); + + /** + * General graphics color conversion + */ + glui32 gms_graphics_combine_color(gms_rgbref_t rgb_color); + + /** + * General graphics color conversion + */ + int gms_graphics_color_luminance(gms_rgbref_t rgb_color); + + /** + * Calculate the contrast variance of the given palette and color usage, at + * the given gamma correction level. Helper functions for automatic gamma + * correction. + */ + static int gms_graphics_compare_luminance(const void *void_first, const void *void_second); + + /** + * Calculate the contrast variance of the given palette and color usage, at + * the given gamma correction level. Helper functions for automatic gamma + * correction. + */ + long gms_graphics_contrast_variance(type16 palette[], long color_usage[], + gms_gammaref_t gamma); + + /** + * Try to find a gamma correction for the given palette and color usage that + * gives relatively equal contrast among the displayed colors. + * + * To do this, we search the gamma tables, computing color luminance for each + * color in the palette given this gamma. From luminances, we then compute + * the contrasts between the colors, and settle on the gamma correction that + * gives the most even and well-distributed picture contrast. We ignore + * colors not used in the palette. + * + * Note that we don't consider how often a palette color is used, only whether + * it's represented, or not. Some weighting might improve things, but the + * simple method seems to work adequately. In practice, as there are only 16 + * colors in a palette, most pictures use most colors in a relatively well + * distributed manner. This algorithm probably wouldn't work well on real + * photographs, though. + */ + gms_gammaref_t gms_graphics_equal_contrast_gamma(type16 palette[], long color_usage[]); + + /** + * Select a suitable gamma for the picture, based on the current gamma mode. + * + * The function returns either the linear gamma, a gamma value half way + * between linear and the gamma that gives the most even contrast, or just + * the gamma that gives the most even contrast. + * + * In the normal case, a value half way to the extreme case of making color + * contrast equal for all colors is, subjectively, a reasonable value to use. + * The problem cases are the darkest pictures, and selecting this value + * brightens them while at the same time not making them look overbright or + * too "sunny". + */ + gms_gammaref_t gms_graphics_select_gamma(type8 bitmap[], type16 width, + type16 height, type16 palette[]); + + /** + * Clear the graphics window, and border and shade the area where the + * picture is going to be rendered. This attempts a small raised effect + * for the picture, in keeping with modern trends. + */ + void gms_graphics_clear_and_border(winid_t glk_window, + int x_offset, int y_offset, int pixel_size, type16 width, type16 height); + + /** + * Convert a Magnetic Scrolls color palette to a Glk one, using the given + * gamma corrections. + */ + void gms_graphics_convert_palette(type16 ms_palette[], gms_gammaref_t gamma, + glui32 glk_palette[]); + + /** + * Given a picture width and height, return the x and y offsets to center + * this picture in the current graphics window. + */ + void gms_graphics_position_picture(winid_t glk_window, + int pixel_size, type16 width, type16 height, + int *x_offset, int *y_offset); + + /** + * Apply a single animation frame to the given off-screen image buffer, using + * the frame bitmap, width, height and mask, the off-screen buffer, and the + * width and height of the main picture. + * + * Note that 'mask' may be NULL, implying that no frame pixel is transparent. + */ + void gms_graphics_apply_animation_frame(type8 bitmap[], + type16 frame_width, type16 frame_height, type8 mask[], + int frame_x, int frame_y, type8 off_screen[], type16 width, type16 height); + + /** + * This function finds and applies the next set of animation frames to the + * given off-screen image buffer. It's handed the width and height of the + * main picture, and the off-screen buffer. + * + * It returns FALSE if at the end of animations, TRUE if more animations + * remain. + */ + int gms_graphics_animate(type8 off_screen[], type16 width, type16 height); + +#ifndef GARGLK + /** + * Given a point, return TRUE if that point is the vertex of a fillable + * region. This is a helper function for layering pictures. When assign- + * ing layers, we want to weight the colors that have the most complex + * shapes, or the largest count of isolated areas, heavier than simpler + * areas. + * + * By painting the colors with the largest number of isolated areas or + * the most complex shapes first, we help to minimize the number of fill + * regions needed to render the complete picture. + */ + int gms_graphics_is_vertex(type8 off_screen[], type16 width, type16 height, + int x, int y); + + /** + * gms_graphics_compare_layering_inverted() + * gms_graphics_assign_layers() + * + * Given two sets of image bitmaps, and a palette, this function will + * assign layers palette colors. + * + * Layers are assigned by first counting the number of vertices in the + * color plane, to get a measure of the complexity of shapes displayed in + * this color, and also the raw number of times each palette color is + * used. This is then sorted, so that layers are assigned to colors, with + * the lowest layer being the color with the most complex shapes, and + * within this (or where the count of vertices is zero, as it could be + * in some animation frames) the most used color. + * + * The function compares pixels in the two image bitmaps given, these + * being the off-screen and on-screen buffers, and generates counts only + * where these bitmaps differ. This ensures that only pixels not yet + * painted are included in layering. + * + * As well as assigning layers, this function returns a set of layer usage + * flags, to help the rendering loop to terminate as early as possible. + * + * By painting lower layers first, the paint can take in larger areas if + * it's permitted to include not-yet-validated higher levels. This helps + * minimize the amount of Glk areas fills needed to render a picture. + */ + int gms_graphics_compare_layering_inverted(const void *void_first, + const void *void_second); + + void gms_graphics_assign_layers(type8 off_screen[], type8 on_screen[], + type16 width, type16 height, int layers[], long layer_usage[]); + + /** + * This is a partially optimized point plot. Given a point in the graphics + * bitmap, it tries to extend the point to a color region, and fill a number + * of pixels in a single Glk rectangle fill. The goal here is to reduce the + * number of Glk rectangle fills, which tend to be extremely inefficient + * operations for generalized point plotting. + * + * The extension works in image layers; each palette color is assigned a + * layer, and we paint each layer individually, starting at the lowest. So, + * the region is free to fill any invalidated pixel in a higher layer, and + * all pixels, invalidated or already validated, in the same layer. In + * practice, it is good enough to look for either invalidated pixels or pixels + * in the same layer, and construct a region as large as possible from these, + * then on marking points as validated, mark only those in the same layer as + * the initial point. + * + * The optimization here is not the best possible, but is reasonable. What + * we do is to try and stretch the region horizontally first, then vertically. + * In practice, we might find larger areas by stretching vertically and then + * horizontally, or by stretching both dimensions at the same time. In + * mitigation, the number of colors in a picture is small (16), and the + * aspect ratio of pictures makes them generally wider than they are tall. + * + * Once we've found the region, we render it with a single Glk rectangle fill, + * and mark all the pixels in this region that match the layer of the initial + * given point as validated. + */ + void gms_graphics_paint_region(winid_t glk_window, glui32 palette[], int layers[], + type8 off_screen[], type8 on_screen[], int x, int y, int x_offset, int y_offset, + int pixel_size, type16 width, type16 height); + #endif + + void gms_graphics_paint_everything(winid_t glk_window, + glui32 palette[], type8 off_screen[], int x_offset, int y_offset, + type16 width, type16 height); + + /** + * This is a background function, called on Glk timeouts. Its job is to + * repaint some of the current graphics image. On successive calls, it + * does a part of the repaint, then yields to other processing. This is + * useful since the Glk primitive to plot points in graphical windows is + * extremely slow; this way, the repaint doesn't block game play. + * + * The function should be called on Glk timeout events. When the repaint + * is complete, the function will turn off Glk timers. + * + * The function uses double-buffering to track how much of the graphics + * buffer has been rendered. This helps to minimize the amount of point + * plots required, as only the differences between the two buffers need + * to be rendered. + */ + void gms_graphics_timeout(); + + /** + * Called by the main interpreter when it wants us to display a picture. + * The function gets the picture bitmap, palette, and dimensions, and + * saves them, and the picture id, in module variables for the background + * rendering function. + * + * The graphics window is opened if required, or closed if mode is zero. + * + * The function checks for changes of actual picture by calculating the + * CRC for picture data; this helps to prevent unnecessary repaints in + * cases where the interpreter passes us the same picture as we're already + * displaying. There is a less than 1 in 4,294,967,296 chance that a new + * picture will be missed. We'll live with that. + * + * Why use CRCs, rather than simply storing the values of picture passed in + * a static variable? Because some games, typically Magnetic Windows, use + * the picture argument as a form of string pointer, and can pass in the + * same value for several, perhaps all, game pictures. If we just checked + * for a change in the picture argument, we'd never see one. So we must + * instead look for changes in the real picture data. + */ + void ms_showpic(type32 picture, type8 mode); + + /** + * Return TRUE if the graphics module data is loaded with a usable picture, + * FALSE if there is no picture available to display. + */ + int gms_graphics_picture_is_available() const { + return gms_graphics_bitmap != nullptr; + } + + /** + * Return the width, height, and animation flag of the currently loaded + * picture. The function returns FALSE if no picture is loaded, otherwise + * TRUE, with picture details in the return arguments. + */ + int gms_graphics_get_picture_details(int *width, int *height, int *is_animated); + + /** + * Returns the current level of applied gamma correction, as a string, the + * count of colors in the picture, and a flag indicating if graphics is + * active (busy). The function return FALSE if graphics is not enabled or + * if not being displayed, otherwise TRUE with the gamma, color count, and + * active flag in the return arguments. + * + * This function races with the graphics timeout, as it returns information + * set up by the first timeout following a new picture. There's a very, + * very small chance that it might win the race, in which case out-of-date + * gamma and color count values are returned. + */ + int gms_graphics_get_rendering_details(const char **gamma, int *color_count, + int *is_active); + + /** + * Return TRUE if it looks like interpreter graphics are turned on, FALSE + * otherwise. + */ + int gms_graphics_interpreter_enabled(); + + /* + * gms_graphics_cleanup() + * + * Free memory resources allocated by graphics functions. Called on game + * end. + */ + void gms_graphics_cleanup(); + + /*---------------------------------------------------------------------*/ + /* Glk port status line functions */ + /*---------------------------------------------------------------------*/ + + /** + * Receive one status character from the interpreter. Characters are + * buffered internally, and on CR, the buffer is copied to the main static + * status buffer for use by the status line printing function. + */ + void ms_statuschar(type8 c); + + /* + * Update the information in the status window with the current contents of + * the completed status line buffer, or a default string if no completed + * status line. + */ + void gms_status_update(); + + /** + * Print the current contents of the completed status line buffer out in the + * main window, if it has changed since the last call. This is for non- + * windowing Glk libraries. + */ + void gms_status_print(); + + /* + * gms_status_notify() + * + * Front end function for updating status. Either updates the status window + * or prints the status line to the main window. + */ + void gms_status_notify(); + + /* + * gms_status_redraw() + * + * Redraw the contents of any status window with the buffered status string. + * This function should be called on the appropriate Glk window resize and + * arrange events. + */ + void gms_status_redraw(); + + /*---------------------------------------------------------------------*/ + /* Glk port output functions */ + /*---------------------------------------------------------------------*/ + + /* + * gms_output_register_help_request() + * gms_output_silence_help_hints() + * gms_output_provide_help_hint() + * + * Register a request for help, and print a note of how to get Glk command + * help from the interpreter unless silenced. + */ + void gms_output_register_help_request(); + + void gms_output_silence_help_hints(); + + void gms_output_provide_help_hint(); + + /* + * gms_game_prompted() + * + * Return TRUE if the last game output appears to have been a ">" prompt. + * Once called, the flag is reset to FALSE, and requires more game output + * to set it again. + */ + int gms_game_prompted(); + + /* + * gms_detect_game_prompt() + * + * See if the last non-newline-terminated line in the output buffer seems + * to be a prompt, and set the game prompted flag if it does, otherwise + * clear it. + */ + void gms_detect_game_prompt(); + + /* + * gms_output_delete() + * + * Delete all buffered output text. Free all malloc'ed buffer memory, and + * return the buffer variables to their initial values. + */ + void gms_output_delete(); + + /* + * gms_output_flush() + * + * Flush any buffered output text to the Glk main window, and clear the + * buffer. + */ + void gms_output_flush(); + + /* + * ms_putchar() + * + * Buffer a character for eventual printing to the main window. + */ + void ms_putchar(type8 c); + + /* + * gms_styled_string() + * gms_styled_char() + * gms_standout_string() + * gms_standout_char() + * gms_normal_string() + * gms_normal_char() + * gms_header_string() + * gms_banner_string() + * + * Convenience functions to print strings in assorted styles. A standout + * string is one that hints that it's from the interpreter, not the game. + */ + void gms_styled_string(glui32 style, const char *message); + + void gms_styled_char(glui32 style, char c); + + void gms_standout_string(const char *message); + + void gms_normal_string(const char *message); + + void gms_normal_char(char c); + + void gms_header_string(const char *message); + + void gms_banner_string(const char *message); + + /** + * Handle a core interpreter call to flush the output buffer. Because Glk + * only flushes its buffers and displays text on g_vm->glk_select(), we can ignore + * these calls as long as we call gms_output_flush() when reading line input. + * + * Taking ms_flush() at face value can cause game text to appear before status + * line text where we are working with a non-windowing Glk, so it's best + * ignored where we can. + */ + void ms_flush(); + + + /*---------------------------------------------------------------------*/ + /* Glk port hint functions */ + /*---------------------------------------------------------------------*/ + + /** + * Return the maximum hint node referred to by the tree under the given node. + * The result is the largest index found, or node, if greater. Because the + * interpreter doesn't supply it, we need to uncover it the hard way. The + * function is recursive, and since it is a tree search, assumes that hints + * is a tree, not a graph. + */ + type16 gms_get_hint_max_node(const ms_hint hints_[], type16 node); + + /** + * Return the content string for a given hint number within a given node. + * This counts over 'number' ASCII NULs in the node's content, returning + * the address of the string located this way. + */ + const char *gms_get_hint_content(const ms_hint hints_[], type16 node, int number); + + /** + * Return the topic string for a given hint node. This is found by searching + * the parent node for a link to the node handed in. For the root node, the + * string is defaulted, since the root node has no parent. + */ + const char *gms_get_hint_topic(const ms_hint hints_[], type16 node); + + /** + * If not already open, open the hints windows. Returns TRUE if the windows + * opened, or were already open. + * + * The function creates two hints windows -- a text grid on top, for menus, + * and a text buffer below for hints. + */ + int gms_hint_open(); + + /** + * If open, close the hints windows. + */ + void gms_hint_close(); + + /** + * Return TRUE if hints windows are available. If they're not, the hints + * system will need to use alternative output methods. + */ + int gms_hint_windows_available(); + + /** + * gms_hint_menu_print() + * gms_hint_menu_header() + * gms_hint_menu_justify() + * gms_hint_text_print() + * gms_hint_menutext_done() + * gms_hint_menutext_start() + * + * Output functions for writing hints. These functions will write to hints + * windows where available, and to the main window where not. When writing + * to hints windows, they also take care not to line wrap in the menu window. + * Limited formatting is available. + */ + void gms_hint_menu_print(int line, int column, const char *string_, + glui32 width, glui32 height); + + void gms_hint_menu_header(int line, const char *string_, + glui32 width, glui32 height); + + void gms_hint_menu_justify(int line, const char *left_string, + const char *right_string, glui32 width, glui32 height); + + void gms_hint_text_print(const char *string_); + + void gms_hint_menutext_start(); + + void gms_hint_menutext_done(); + + /** + * Request and return a character event from the hints windows. In practice, + * this means either of the hints windows if available, or the main window + * if not. + */ + void gms_hint_menutext_char_event(event_t *event); + + /** + * Arrange the hints windows so that the hint menu window has the requested + * number of lines. Returns the actual hint menu window width and height, + * or defaults if no hints windows are available. + */ + void gms_hint_arrange_windows(int requested_lines, glui32 *width, glui32 *height); + + /** + * Update the hints windows for the given folder hint node. + */ + void gms_hint_display_folder(const struct ms_hint hints_[], + const int cursor[], type16 node); + + /** + * Update the hints windows for the given text hint node. + */ + void gms_hint_display_text(const struct ms_hint hints_[], + const int cursor[], type16 node); + + /** + * Display the given hint using the appropriate display function. + */ + void gms_hint_display(const struct ms_hint hints_[], + const int cursor[], type16 node); + + /** + * Handle a Glk keycode for the given folder hint. Return the next node to + * handle, or the special end-hints on Quit at the root node. + */ + type16 gms_hint_handle_folder(const ms_hint hints_[], + int cursor[], type16 node, glui32 keycode); + + /** + * Handle a Glk keycode for the given text hint. Return the next node to + * handle. + */ + type16 gms_hint_handle_text(const ms_hint hints[], + int cursor[], type16 node, glui32 keycode); + + /** + * Handle a Glk keycode for the given hint using the appropriate handler + * function. Return the next node to handle. + */ + type16 gms_hint_handle(const ms_hint hints_[], int cursor[], + type16 node, glui32 keycode); + + /** + * Start game hints. These are modal, though there's no overriding Glk + * reason why. It's just that this matches the way they're implemented by + * most Inform games. This may not be the best way of doing help, but at + * least it's likely to be familiar, and anything more ambitious may be + * beyond the current Glk capabilities. + * + * This function uses CRCs to detect any change of hints data. Normally, + * we'd expect none, at least within a given game run, but we can probably + * handle it okay if it happens. + */ + type8 ms_showhints(ms_hint *hints_); + + /** + * Update the hints windows for the current hint. This function should be + * called from the event handler on resize events, to repaint the hints + * display. It does nothing if no hints windows have been opened, since + * in this case, there's no resize action required -- either we're not in + * the hints subsystem, or hints are being displayed in the main game + * window, for whatever reason. + */ + void gms_hint_redraw(); + + /** + * Free memory resources allocated by hints functions. Called on game + * end. + */ + void gms_hints_cleanup(); + + void ms_playmusic(type8 *midi_data, type32 length, type16 tempo); + + /*---------------------------------------------------------------------*/ + /* Glk command escape functions */ + /*---------------------------------------------------------------------*/ + + /** + * Stub function for the undo command. The real work is to return the + * undo code to the input functions. + */ + void gms_command_undo(const char *argument); + + /** + * Turn game output scripting (logging) on and off. + */ + void gms_command_script(const char *argument); + + /** + * Turn game input logging on and off. + */ + void gms_command_inputlog(const char *argument); + + /** + * Set the game input log, to read input from a file. + */ + void gms_command_readlog(const char *argument); + + /** + * Turn abbreviation expansions on and off. + */ + void gms_command_abbreviations(const char *argument); + + /** + * Enable or disable graphics more permanently than is done by the main + * interpreter. Also, print out a few brief details about the graphics + * state of the program. + */ + void gms_command_graphics(const char *argument); + + /** + * Enable or disable picture gamma corrections. + */ + void gms_command_gamma(const char *argument); + + /** + * Enable or disable picture animations. + */ + void gms_command_animations(const char *argument); + + /** + * Turn the extra "> " prompt output on and off. + */ + void gms_command_prompts(const char *argument); + + /** + * gms_command_print_version_number() + * gms_command_version() + * + * Print out the Glk library version number. + */ + void gms_command_print_version_number(glui32 version); + + void gms_command_version(const char *argument); + + /** + * Turn command escapes off. Once off, there's no way to turn them back on. + * Commands must be on already to enter this function. + */ + void gms_command_commands(const char *argument); + + /** + * Report all current Glk settings. + */ + void gms_command_summary(const char *argument); + + /** + * Document the available Glk commands. + */ + void gms_command_help(const char *command); + + /** + * This function is handed each input line. If the line contains a specific + * Glk port command, handle it and return TRUE, otherwise return FALSE. + * + * On unambiguous returns, it will also set the value for undo_command to the + * table undo return value. + */ + int gms_command_escape(const char *string_, int *undo_command); + + /** + * This function makes a special case of the input line containing the single + * word "undo", treating it as if it is "glk undo". This makes life a bit + * more convenient for the player, since it's the same behavior that most + * other IF systems have. It returns TRUE if "undo" found, FALSE otherwise. + */ + int gms_command_undo_special(const char *string_); + + /*---------------------------------------------------------------------*/ + /* Glk port input functions */ + /*---------------------------------------------------------------------*/ + + /** + * Expand a few common one-character abbreviations commonly found in other + * game systems, but not always normal in Magnetic Scrolls games. + */ + void gms_expand_abbreviations(char *buffer_, int size); + + /** + * Read and buffer a line of input. If there is an input log active, then + * data is taken by reading this first. Otherwise, the function gets a + * line from Glk. + * + * It also makes special cases of some lines read from the user, either + * handling commands inside them directly, or expanding abbreviations as + * appropriate. This is not reflected in the buffer, which is adjusted as + * required before returning. + */ + void gms_buffer_input(); + + /** + * Return the single next character to the interpreter. This function + * extracts characters from the input buffer until empty, when it then + * tries to buffer more data. + */ + type8 ms_getchar(type8 trans); + + /*---------------------------------------------------------------------*/ + /* Glk port event functions */ + /*---------------------------------------------------------------------*/ + + /** + * Process Glk events until one of the expected type arrives. Return + * the event of that type. + */ + void gms_event_wait(glui32 wait_type, event_t *event); + + /*---------------------------------------------------------------------*/ + /* Functions intercepted by link-time wrappers */ + /*---------------------------------------------------------------------*/ + + /** + * __wrap_toupper() + * __wrap_tolower() + * + * Wrapper functions around toupper() and tolower(). The Linux linker's + * --wrap option will convert calls to mumble() to __wrap_mumble() if we + * give it the right options. We'll use this feature to translate all + * toupper() and tolower() calls in the interpreter code into calls to + * Glk's versions of these functions. + * + * It's not critical that we do this. If a linker, say a non-Linux one, + * won't do --wrap, then just do without it. It's unlikely that there + * will be much noticeable difference. + */ + int __wrap_toupper(int ch); + + int __wrap_tolower(int ch); + + /*---------------------------------------------------------------------*/ + /* main and options parsing */ + /*---------------------------------------------------------------------*/ + + /** + * Given a game name, try to establish three filenames from it - the main game + * text file, the (optional) graphics data file, and the (optional) hints + * file. Given an input "file" X, the function looks for X.MAG or X.mag for + * game data, X.GFX or X.gfx for graphics, and X.HNT or X.hnt for hints. + * If the input file already ends with .MAG, .GFX, or .HNT, the extension + * is stripped first. + * + * The function returns NULL for filenames not available. It's not fatal if + * the graphics filename or hints filename is NULL, but it is if the main game + * filename is NULL. Filenames are malloc'ed, and need to be freed by the + * caller. + */ + void gms_establish_filenames(const char *name, char **text, char **graphics, char **hints_); + + void gms_main(); + + /*---------------------------------------------------------------------*/ + /* Linkage between Glk entry/exit calls and the Magnetic interpreter */ + /*---------------------------------------------------------------------*/ + + /* + * glk_main() + * + * Main entry point for Glk. Here, all startup is done, and we call our + * function to run the game. + */ + void glk_main(); + + void write(const char *fmt, ...); + + void writeChar(char c); +private: + /* Convert virtual pointer to effective pointer */ + type8 *effective(type32 ptr); + + static type32 read_l(type8 *ptr) { + return (type32)((type32)ptr[0] << 24 | (type32)ptr[1] << 16 | (type32)ptr[2] << 8 | (type32)ptr[3]); + } + + static type16 read_w(type8 *ptr) { + return (type16)(ptr[0] << 8 | ptr[1]); + } + + static type32 read_l2(type8 *ptr) { + return ((type32)ptr[1] << 24 | (type32)ptr[0] << 16 | (type32)ptr[3] << 8 | (type32)ptr[2]); + } + + static type16 read_w2(type8 *ptr) { + return (type16)(ptr[1] << 8 | ptr[0]); + } + + static void write_l(type8 *ptr, type32 val); + + static void write_w(type8 *ptr, type16 val); + + /* Standard rand - for equal cross-platform behaviour */ + void ms_seed(type32 seed) { + rseed = seed; + } + + type32 rand_emu(); + + void ms_freemem(); + + type8 ms_is_running() const { + return running; + } + + type8 ms_is_magwin() const { + return (version == 4) ? 1 : 0; + } + + void ms_stop() { + running = 0; + } + + type8 init_gfx1(type8 *header); + + type8 init_gfx2(type8 *header); + + type8 init_snd(type8 *header); + + /* zero all registers and flags and load the game */ + type8 ms_init(const char *name, const char *gfxname, const char *hntname, const char *sndname); + + type8 is_blank(type16 line, type16 width); + + type8 *ms_extract1(type8 pic, type16 *w, type16 *h, type16 *pal); + + type16s find_name_in_header(const char *name, type8 upper); + + void extract_frame(struct picture *pic); + + type8 *ms_extract2(const char *name, type16 *w, type16 *h, type16 *pal, type8 *is_anim); + + type8 *ms_extract(type32 pic, type16 *w, type16 *h, type16 *pal, type8 *is_anim); + + type8 ms_animate(struct ms_position **positions, type16 *count); + + type8 *ms_get_anim_frame(type16s number, type16 *width, type16 *height, type8 **mask); + + type8 ms_anim_is_repeating() const; + + type16s find_name_in_sndheader(const char *name); + + type8 *sound_extract(const char *name, type32 *length, type16 *tempo); + + void save_undo(); + + type8 ms_undo(); + +#ifdef LOGEMU + void log_status(); +#endif + + void ms_status(); + + type32 ms_count() const { + return i_count; + } + + /* align register pointer for word/byte accesses */ + type8 *reg_align(type8 *ptr, type8 size); + + type32 read_reg(int i, int s); + + void write_reg(int i, int s, type32 val); + + /* [35c4] */ + void char_out(type8 c); + + /* extract addressing mode information [1c6f] */ + void set_info(type8 b); + + /* read a word and increase pc */ + void read_word(); + + /* get addressing mode and set arg1 [1c84] */ + void set_arg1(); + + /* get addressing mode and set arg2 [1bc5] */ + void set_arg2_nosize(int use_dx, type8 b); + + void set_arg2(int use_dx, type8 b); + + /* [1b9e] */ + void swap_args(); + + /* [1cdc] */ + void push(type32 c); + + /* [1cd1] */ + type32 pop(); + + /* check addressing mode and get argument [2e85] */ + void get_arg(); + + void set_flags(); + + /* [263a] */ + int condition(type8 b); + + /* [26dc] */ + void branch(type8 b); + + /* [2869] */ + void do_add(type8 adda); + + /* [2923] */ + void do_sub(type8 suba); + + /* [283b] */ + void do_eor(); + + /* [280d] */ + void do_and(); + + /* [27df] */ + void do_or(); + + /* [289f] */ + void do_cmp(); + + /* [2973] */ + void do_move(); + + type8 do_btst(type8 a); + + /* bit operation entry point [307c] */ + void do_bop(type8 b, type8 a); + + void check_btst(); + + void check_lea(); + + /* [33cc] */ + void check_movem(); + + /* [3357] */ + void check_movem2(); + + /* [30e4] in Jinxter, ~540 lines of 6510 spaghetti-code */ + /* The mother of all bugs, but hey - no gotos used :-) */ + void dict_lookup(); + + /* A0=findproperties(D0) [2b86], properties_ptr=[2b78] A0FE */ + void do_findprop(); + + void write_string(); + + void output_number(type16 number); + + type16 output_text(const char *text); + + type16s hint_input(); + + type16 show_hints_text(ms_hint *hintsData, type16 index); + + void do_line_a(); + + /* emulate an instruction [1b7e] */ + type8 ms_rungame(); +private: + type8 ms_load_file(const char *name, type8 *ptr, type16 size); + + type8 ms_save_file(const char *name, type8 *ptr, type16 size); + + void script_write(type8 c); + + void transcript_write(type8 c); public: /** * Constructor diff --git a/engines/glk/magnetic/magnetic_defs.h b/engines/glk/magnetic/magnetic_defs.h new file mode 100644 index 0000000000..f2b18c0539 --- /dev/null +++ b/engines/glk/magnetic/magnetic_defs.h @@ -0,0 +1,113 @@ +/* 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 MAGNETIC_DEFS_H +#define MAGNETIC_DEFS_H + +#include "common/scummsys.h" + +namespace Glk { +namespace Magnetic { + +/*****************************************************************************\ +* Type definitions for Magnetic +* +* Note: When running into trouble please ensure that these types have the +* correct number of bits on your system !!! +\*****************************************************************************/ + +typedef byte type8; +typedef int8 type8s; +typedef uint16 type16; +typedef int16 type16s; +typedef uint32 type32; +typedef int32 type32s; + +#ifndef BYTE_MAX +#define BYTE_MAX 255 +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif +#ifndef UINT16_MAX +#define UINT16_MAX 0xffff +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + +#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 + +/****************************************************************************\ +* Compile time switches +\****************************************************************************/ + +/* Switch: SAVEMEM + Purpose: Magnetic loads a complete graphics file into memory by default. + Setting this switch you tell Magnetic to load images on request + (saving memory, wasting load time) + +#define SAVEMEM +*/ + +/* Switch: NO_ANIMATION + Purpose: By default Magnetic plays animated graphics. + Setting this switch to ignore animations, Magnetic shows the + static parts of the images anyway! + +#define NO_ANIMATION +*/ + +/****************************************************************************\ +* Miscellaneous enums/types +* +\****************************************************************************/ + +enum { GMS_PALETTE_SIZE = 16 }; + +enum { GMS_INPUTBUFFER_LENGTH = 256 }; + +enum { GMS_STATBUFFER_LENGTH = 1024 }; + +enum GammaMode { + GAMMA_OFF, GAMMA_NORMAL, GAMMA_HIGH +}; + +/* Hint type definitions. */ +enum { + GMS_HINT_TYPE_FOLDER = 1, + GMS_HINT_TYPE_TEXT = 2 +}; + +} // End of namespace Magnetic +} // End of namespace Glk + +#endif diff --git a/engines/glk/magnetic/magnetic_types.h b/engines/glk/magnetic/magnetic_types.h index 5c8cad5077..0d18614c73 100644 --- a/engines/glk/magnetic/magnetic_types.h +++ b/engines/glk/magnetic/magnetic_types.h @@ -24,22 +24,11 @@ #define GLK_MAGNETIC_TYPES #include "common/scummsys.h" -#include "common/algorithm.h" +#include "glk/magnetic/magnetic_defs.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 { int16 flag; int16 count; @@ -131,6 +120,34 @@ struct ms_hint { } }; +struct gms_gamma_t { + const char *const level; ///< Gamma correction level + const unsigned char table[8]; ///< Color lookup table + const bool is_corrected; ///< Flag if non-linear +}; +typedef const gms_gamma_t *gms_gammaref_t; + +/* R,G,B color triple definition. */ +struct gms_rgb_t { + int red, green, blue; +}; +typedef gms_rgb_t *gms_rgbref_t; + +#ifndef GARGLK +struct gms_layering_t { + long complexity; /* Count of vertices for this color. */ + long usage; /* Color usage count. */ + int color; /* Color index into palette. */ +}; +#endif + +/* Table of single-character command abbreviations. */ +struct gms_abbreviation_t { + const char abbreviation; /* Abbreviation character. */ + const char *const expansion; /* Expansion string. */ +}; +typedef gms_abbreviation_t *gms_abbreviationref_t; + } // End of namespace Magnetic } // End of namespace Glk diff --git a/engines/glk/magnetic/main.cpp b/engines/glk/magnetic/main.cpp index 1f0128f36c..444aa0a930 100644 --- a/engines/glk/magnetic/main.cpp +++ b/engines/glk/magnetic/main.cpp @@ -20,7 +20,7 @@ * */ -#include "glk/magnetic/defs.h" +#include "glk/magnetic/magnetic.h" #include "common/file.h" #include "common/savefile.h" #include "common/system.h" @@ -30,10 +30,7 @@ namespace Magnetic { #define WIDTH 78 -static type8 buffer[80], xpos = 0, bufpos = 0, log_on = 0, ms_gfx_enabled, filename[256]; -static Common::DumpFile *log1 = 0, *log2 = 0; - -type8 ms_load_file(const char *name, type8 *ptr, type16 size) { +type8 Magnetic::ms_load_file(const char *name, type8 *ptr, type16 size) { assert(name); Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(name); if (!file) @@ -48,7 +45,7 @@ type8 ms_load_file(const char *name, type8 *ptr, type16 size) { return 0; } -type8 ms_save_file(const char *name, type8 *ptr, type16 size) { +type8 Magnetic::ms_save_file(const char *name, type8 *ptr, type16 size) { assert(name); Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(name); if (!file) @@ -64,113 +61,15 @@ type8 ms_save_file(const char *name, type8 *ptr, type16 size) { return 0; } -void script_write(type8 c) { +void Magnetic::script_write(type8 c) { if (log_on == 2) log1->writeByte(c); } -void transcript_write(type8 c) { +void Magnetic::transcript_write(type8 c) { if (log2) log2->writeByte(c); } -void ms_fatal(const char *txt) { - error("Fatal error: %s", txt); -} - -#if 0 -main(int argc, char **argv) { - type8 running, i, *gamename = 0, *gfxname = 0, *hintname = 0; - type32 dlimit, slimit; - - if (sizeof(type8) != 1 || sizeof(type16) != 2 || sizeof(type32) != 4) { - fprintf(stderr, - "You have incorrect typesizes, please edit the typedefs and recompile\n" - "or proceed on your own risk...\n"); - exit(1); - } - dlimit = slimit = 0xffffffff; - for (i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - switch (tolower(argv[i][1])) { - case 'd': - if (strlen(argv[i]) > 2) - dlimit = atoi(&argv[i][2]); - else - dlimit = 0; - break; - case 's': - if (strlen(argv[i]) > 2) - slimit = atoi(&argv[i][2]); - else - slimit = 655360; - break; - case 't': - if (!(log2 = fopen(&argv[i][2], "w"))) - printf("Failed to open \"%s\" for writing.\n", &argv[i][2]); - break; - case 'r': - if (log1 = fopen(&argv[i][2], "r")) - log_on = 1; - else - printf("Failed to open \"%s\" for reading.\n", &argv[i][2]); - break; - case 'w': - if (log1 = fopen(&argv[i][2], "w")) - log_on = 2; - else - printf("Failed to open \"%s\" for writing.\n", &argv[i][2]); - break; - default: - printf("Unknown option -%c, ignoring.\n", argv[i][1]); - break; - } - } else if (!gamename) - gamename = argv[i]; - else if (!gfxname) - gfxname = argv[i]; - else if (!hintname) - hintname = argv[i]; - } - if (!gamename) { - printf("Magnetic 2.3 - a Magnetic Scrolls interpreter\n\n"); - printf("Usage: %s [options] game [gfxfile] [hintfile]\n\n" - "Where the options are:\n" - " -dn activate register dump (after n instructions)\n" - " -rname read script file\n" - " -sn safety mode, exits automatically (after n instructions)\n" - " -tname write transcript file\n" - " -wname write script file\n\n" - "The interpreter commands are:\n" - " #undo undo - don't use it near are_you_sure prompts\n" - " #logoff turn off script writing\n\n", argv[0]); - exit(1); - } - - if (!(ms_gfx_enabled = ms_init(gamename, gfxname, hintname, 0))) { - printf("Couldn't start up game \"%s\".\n", gamename); - exit(1); - } - ms_gfx_enabled--; - running = 1; - while ((ms_count() < slimit) && running) { - if (ms_count() >= dlimit) - ms_status(); - running = ms_rungame(); - } - if (ms_count() == slimit) { - printf("\n\nSafety limit (%d) reached.\n", slimit); - ms_status(); - } - ms_freemem(); - if (log_on) - fclose(log1); - if (log2) - fclose(log2); - printf("\nExiting.\n"); - return 0; -} -#endif - } // End of namespace Magnetic } // End of namespace Glk |