aboutsummaryrefslogtreecommitdiff
path: root/engines/glk
diff options
context:
space:
mode:
authorPaul Gilbert2019-11-21 06:47:49 -0800
committerPaul Gilbert2019-11-22 18:49:07 -0800
commit9df3c85184b10a71be27209313efbc23974d1225 (patch)
tree251ff9a34997839b2567e76125350da9803be3f9 /engines/glk
parent9af0d1519444f6c82df24fb63be5d3def769f797 (diff)
downloadscummvm-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.h469
-rw-r--r--engines/glk/magnetic/detection.cpp13
-rw-r--r--engines/glk/magnetic/detection.h18
-rw-r--r--engines/glk/magnetic/detection_tables.h80
-rw-r--r--engines/glk/magnetic/emu.cpp294
-rw-r--r--engines/glk/magnetic/glk.cpp2533
-rw-r--r--engines/glk/magnetic/magnetic.cpp54
-rw-r--r--engines/glk/magnetic/magnetic.h1285
-rw-r--r--engines/glk/magnetic/magnetic_defs.h113
-rw-r--r--engines/glk/magnetic/magnetic_types.h41
-rw-r--r--engines/glk/magnetic/main.cpp111
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