1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /* Based on Magnetic interpreter version 2.3 */
24 
25 #ifndef GLK_MAGNETIC_MAGNETIC
26 #define GLK_MAGNETIC_MAGNETIC
27 
28 #include "common/scummsys.h"
29 #include "glk/glk_api.h"
30 #include "glk/magnetic/magnetic_types.h"
31 #include "glk/magnetic/magnetic_defs.h"
32 #include "glk/magnetic/detection.h"
33 
34 namespace Glk {
35 namespace Magnetic {
36 
37 class Magnetic;
38 
39 typedef void (Magnetic::*CommandPtr)(const char *argument);
40 
41 /* Glk subcommands and handler functions. */
42 struct gms_command_t {
43 	CommandPtr handler;                 ///< Subcommand handler
44 	const char *const command;          ///< Glk subcommand
45 	bool takes_argument;                ///< Argument flag
46 	bool undo_return;                   ///< "Undo" return value
47 } ;
48 typedef gms_command_t *gms_commandref_t;
49 
50 
51 /**
52  * Magnetic game interpreter
53  */
54 class Magnetic : public GlkAPI {
55 public:
56 	static const gms_command_t GMS_COMMAND_TABLE[14];
57 	static const gms_gamma_t GMS_GAMMA_TABLE[38];
58 private:
59 	GammaMode gms_gamma_mode;
60 	bool gms_animation_enabled, gms_prompt_enabled;
61 	bool gms_abbreviations_enabled, gms_commands_enabled;
62 	bool gms_graphics_enabled;
63 
64 	// Glk Magnetic Scrolls port version number
65 	const glui32 GMS_PORT_VERSION;
66 
67 	/**
68 	 * We use a maximum of five Glk windows, one for status, one for pictures,
69 	 * two for hints, and one for everything else.  The status and pictures
70 	 * windows may be NULL, depending on user selections and the capabilities
71 	 * of the Glk library.  The hints windows will normally be NULL, except
72 	 * when in the hints subsystem.
73 	 */
74 	winid_t gms_main_window, gms_status_window, gms_graphics_window;
75 	winid_t gms_hint_menu_window, gms_hint_text_window;
76 
77 	/**
78 	 * Transcript stream and input log.  These are NULL if there is no current
79 	 * collection of these strings.
80 	 */
81 	strid_t gms_transcript_stream, gms_inputlog_stream;
82 
83 	// Input read log stream, for reading back an input log
84 	strid_t gms_readlog_stream;
85 
86 	/* Note about whether graphics is possible, or not. */
87 	bool gms_graphics_possible;
88 
89 	/* Magnetic Scrolls standard input prompt string. */
90 	const char *const GMS_INPUT_PROMPT;
91 
92 	/**
93 	 * The game's name, suitable for printing out on a status line, or other
94 	 * location where game information is relevant.  Set on game startup, by
95 	 * identifying the game from its text file header.
96 	 */
97 	const char *gms_gameid_game_name;
98 
99 	/*
100 	 * The current picture bitmap being displayed, its width, height, palette,
101 	 * animation flag, and picture id.
102 	 */
103 	type8 *gms_graphics_bitmap;
104 	type16 gms_graphics_width, gms_graphics_height;
105 	type16 gms_graphics_palette[GMS_PALETTE_SIZE]; /* = { 0, ... }; */
106 	bool gms_graphics_animated;
107 	type32 gms_graphics_picture;
108 
109 	/*
110 	 * Flags set on new picture, and on resize or arrange events, and a flag
111 	 * to indicate whether background repaint is stopped or active.
112 	 */
113 	bool gms_graphics_new_picture, gms_graphics_repaint;
114 	bool gms_graphics_active;
115 
116 	/* Flag to try to monitor the state of interpreter graphics. */
117 	bool gms_graphics_interpreter;
118 
119 	/*
120 	 * Pointer to the two graphics buffers, one the off-screen representation
121 	 * of pixels, and the other tracking on-screen data.  These are temporary
122 	 * graphics malloc'ed memory, and should be free'd on exit.
123 	 */
124 	type8 *gms_graphics_off_screen, *gms_graphics_on_screen;
125 
126 	/*
127 	 * Pointer to the current active gamma table entry.  Because of the way
128 	 * it's queried, this may not be NULL, otherwise we risk a race, with
129 	 * admittedly a very low probability, with the updater.  So, it's init-
130 	 * ialized instead to the gamma table.  The real value in use is inserted
131 	 * on the first picture update timeout call for a new picture.
132 	 */
133 	gms_gammaref_t gms_graphics_current_gamma;
134 
135 	/*
136 	 * The number of colors used in the palette by the current picture.  This
137 	 * value is also at risk of a race with the updater, so it too has a mild
138 	 * lie for a default value.
139 	 */
140 	int gms_graphics_color_count;
141 
142 	/**
143 	 * The interpreter feeds us status line characters one at a time, with Tab
144 	 * indicating right justify, and CR indicating the line is complete.  To get
145 	 * this to fit with the Glk event and redraw model, here we'll buffer each
146 	 * completed status line, so we have a stable string to output when needed.
147 	 * It's also handy to have this buffer for Glk libraries that don't support
148 	 * separate windows.
149 	 */
150 	char gms_status_buffer[GMS_STATBUFFER_LENGTH];
151 	int gms_status_length;
152 
153 	/*
154 	 * Flag for if the user entered "help" as their last input, or if hints have
155 	 * been silenced as a result of already using a Glk command.
156 	 */
157 	int gms_help_requested, gms_help_hints_silenced;
158 
159 	/*
160 	 * Output buffer.  We receive characters one at a time, and it's a bit
161 	 * more efficient for everyone if we buffer them, and output a complete
162 	 * string on a flush call.
163 	 */
164 	char *gms_output_buffer;
165 	int gms_output_allocation, gms_output_length;
166 
167 	/*
168 	 * Flag to indicate if the last buffer flushed looked like it ended in a
169 	 * ">" prompt.
170 	 */
171 	int gms_output_prompt;
172 
173 	/*
174 	 * Note of the interpreter's hints array.  Note that keeping its address
175 	 * like this assumes that it's either static or heap in the interpreter.
176 	 */
177 	ms_hint *gms_hints;
178 
179 	/* Details of the current hint node on display from the hints array. */
180 	type16 gms_current_hint_node;
181 
182 	/*
183 	 * Array of cursors for each hint.  The cursor indicates the current hint
184 	 * position in a folder, and the last hint shown in text hints.  Space
185 	 * is allocated as needed for a given set of hints, and needs to be freed
186 	 * on interpreter exit.
187 	 */
188 	int *gms_hint_cursor;
189 
190 	/*
191 	 * Input buffer allocated for reading input lines.  The buffer is filled
192 	 * from either an input log, if one is currently being read, or from Glk
193 	 * line input.  We also need an "undo" notification flag.
194 	 */
195 	char gms_input_buffer[GMS_INPUTBUFFER_LENGTH];
196 	int gms_input_length, gms_input_cursor, gms_undo_notification;
197 
198 	/*
199 	 * The following values need to be passed between the startup_code and main
200 	 * functions.
201 	 */
202 	const char *gms_game_message;  /* Error message. */
203 
204 	/*
205 	 * Safety flags, to ensure we always get startup before main, and that
206 	 * we only get a call to main once.
207 	 */
208 	int gms_startup_called, gms_main_called;
209 private:
210 	type32 dreg[8], areg[8], i_count, string_size, rseed, pc, arg1i, mem_size;
211 	type16 properties, fl_sub, fl_tab, fl_size, fp_tab, fp_size;
212 	type8 zflag, nflag, cflag, vflag, byte1, byte2, regnr, admode, opsize;
213 	type8 *arg1, *arg2, is_reversible, running, tmparg[4];
214 	type8 lastchar, version, sd;
215 	type8 *decode_table, *restart, *code, *string, *string2;
216 	type8 *string3, *dict;
217 	type8 quick_flag, gfx_ver, *gfx_buf, *gfx_data;
218 	type8 *gfx2_hdr, *gfx2_buf;
219 	const char *gfx2_name;
220 	type16 gfx2_hsize;
221 	Common::File *gfx_fp;
222 	type8 *snd_buf, *snd_hdr;
223 	type16 snd_hsize;
224 	Common::File *snd_fp;
225 
226 	type32 undo_regs[2][18], undo_pc, undo_size;
227 	type8 *undo[2], undo_stat[2];
228 	type16 gfxtable, table_dist;
229 	type16 v4_id, next_table;
230 
231 #ifndef NO_ANIMATION
232 	type16 pos_table_size;
233 	type8 *command_table;
234 	type16s command_index;
235 	type16s pos_table_index;
236 	type16s pos_table_max;
237 	type8 anim_repeat;
238 	type16 pos_table_count[MAX_POSITIONS];
239 	picture anim_frame_table[MAX_ANIMS];
240 	ms_position pos_table[MAX_POSITIONS][MAX_ANIMS];
241 	lookup anim_table[MAX_POSITIONS];
242 	ms_position pos_array[MAX_FRAMES];
243 #endif
244 
245 	/* Hint support */
246 	ms_hint *hints;
247 	type8 *hint_contents;
248 
249 	/**
250 	 * Weighting values for calculating the luminance of a color.  There are
251 	 * two commonly used sets of values for these -- 299,587,114, taken from
252 	 * NTSC (Never The Same Color) 1953 standards, and 212,716,72, which is the
253 	 * set that modern CRTs tend to match.  The NTSC ones seem to give the best
254 	 * subjective results.
255 	 */
256 	const gms_rgb_t GMS_LUMINANCE_WEIGHTS;
257 
258 	type8 *_saveData;
259 	size_t _saveSize;
260 private:
261 	type8 buffer[80], xpos, bufpos, log_on, ms_gfx_enabled, filename[256];
262 	Common::DumpFile *_log1, *_log2;
263 private:
264 	/* Method local statics in original code */
265 	glui32 crc_table[BYTE_MAX_VAL + 1];
266 	int luminance_weighting;
267 	gms_gammaref_t linear_gamma;
268 	uint32 pic_current_crc;			/* CRC of the current picture */
269 	uint32 hints_current_crc;		/* CRC of hints */
270 	bool hints_crc_initialized;
271 private:
272 	/**
273 	 * Performs initialization
274 	 */
275 	void initialize();
276 
277 	/**
278 	 * Initializes settings from the ScummVM configuration
279 	 */
280 	void initializeSettings();
281 
282 	/**
283 	 * Initializes the CRC table
284 	 */
285 	void initializeCRC();
286 
287 	/**
288 	 * Initializes the linear gamma entry
289 	 */
290 	void initializeLinearGamma();
291 
292 	/**
293 	 * Fatal error handler.  The function returns, expecting the caller to
294 	 * abort() or otherwise handle the error.
295 	 */
296 	void gms_fatal(const char *string);
297 
298 	/**
299 	 * Non-failing malloc. Calls error if memory allocation fails
300 	 */
301 	void *gms_malloc(size_t size);
302 
303 	/**
304 	 * Non-failing realloc. Calls error if memory allocation fails
305 	 */
306 	void *gms_realloc(void *ptr, size_t size);
307 
308 	/**
309 	 * Local comparison routine that doesn't have an ANSI standard
310 	 */
311 	int gms_strncasecmp(const char *s1, const char *s2, size_t n);
312 
313 	/**
314 	 * Local comparison routine that doesn't have an ANSI standard
315 	 */
316 	int gms_strcasecmp(const char *s1, const char *s2);
317 
318 	/**
319 	 * Return the CRC of the bytes in buffer[0..length-1].
320 	 *
321 	 * This algorithm is taken from the PNG specification, version 1.0.
322 	 */
323 	glui32 gms_get_buffer_crc(const void *void_buffer, size_t length);
324 
325 	/**
326 	 * Endian-safe unsigned 32 bit integer read from game text file.  Returns
327 	 * 0 on error, a known unused table value.
328 	 */
329 	type32 gms_gameid_read_uint32(int offset, Common::SeekableReadStream *stream);
330 
331 	/**
332 	 * Identify a game from its text file header, and cache the game's name for
333 	 * later queries.  Sets the cache to NULL if not found.
334 	 */
335 	void gms_gameid_identify_game(const Common::String &text_file);
336 
337 	/**
338 	 * Return the name of the game, or NULL if not identifiable.
339 	 */
gms_gameid_get_game_name()340 	const char *gms_gameid_get_game_name() const {
341 		return gms_gameid_game_name;
342 	}
343 
344 	/**
345 	 * If it's not open, open the graphics window.  Returns TRUE if graphics
346 	 * was successfully started, or already on.
347 	 */
348 	int gms_graphics_open();
349 
350 	/**
351 	 * If open, close the graphics window and set back to NULL.
352 	 */
353 	void gms_graphics_close();
354 
355 	/**
356 	 * If graphics enabled, start any background picture update processing.
357 	 */
358 	void gms_graphics_start();
359 
360 	/**
361 	 * Stop any background picture update processing.
362 	 */
363 	void gms_graphics_stop();
364 
365 	/**
366 	 * Return TRUE if graphics are currently being displayed, FALSE otherwise.
367 	 */
gms_graphics_are_displayed()368 	int gms_graphics_are_displayed() const {
369 		return gms_graphics_window != nullptr;
370 	}
371 
372 	/**
373 	 * Set up a complete repaint of the current picture in the graphics window.
374 	 * This function should be called on the appropriate Glk window resize and
375 	 * arrange events.
376 	 */
377 	void gms_graphics_paint();
378 
379 	/**
380 	 * Restart graphics as if the current picture is a new picture.  This
381 	 * function should be called whenever graphics is re-enabled after being
382 	 * disabled, on change of gamma color correction policy, and on change
383 	 * of animation policy.
384 	 */
385 	void gms_graphics_restart();
386 
387 	/**
388 	 * Analyze an image, and return the usage count of each palette color, and
389 	 * an overall count of how many colors out of the palette are used.  NULL
390 	 * arguments indicate no interest in the return value.
391 	 */
392 	void gms_graphics_count_colors(type8 bitmap[], type16 width, type16 height,
393 		int *color_count, long color_usage[]);
394 
395 	/**
396 	 * General graphics color conversion
397 	 */
398 	void gms_graphics_game_to_rgb_color(type16 color, gms_gammaref_t gamma,
399 		gms_rgbref_t rgb_color);
400 
401 	/**
402 	 * General graphics color conversion
403 	 */
404 	void gms_graphics_split_color(glui32 color, gms_rgbref_t rgb_color);
405 
406 	/**
407 	 * General graphics color conversion
408 	 */
409 	glui32 gms_graphics_combine_color(gms_rgbref_t rgb_color);
410 
411 	/**
412 	 * General graphics color conversion
413 	 */
414 	int gms_graphics_color_luminance(gms_rgbref_t rgb_color);
415 
416 	/**
417 	 * Calculate the contrast variance of the given palette and color usage, at
418 	 * the given gamma correction level.  Helper functions for automatic gamma
419 	 * correction.
420 	 */
421 	static int gms_graphics_compare_luminance(const void *void_first, const void *void_second);
422 
423 	/**
424 	 * Calculate the contrast variance of the given palette and color usage, at
425 	 * the given gamma correction level.  Helper functions for automatic gamma
426 	 * correction.
427 	 */
428 	long gms_graphics_contrast_variance(type16 palette[], long color_usage[],
429 		gms_gammaref_t gamma);
430 
431 	/**
432 	 * Try to find a gamma correction for the given palette and color usage that
433 	 * gives relatively equal contrast among the displayed colors.
434 	 *
435 	 * To do this, we search the gamma tables, computing color luminance for each
436 	 * color in the palette given this gamma.  From luminances, we then compute
437 	 * the contrasts between the colors, and settle on the gamma correction that
438 	 * gives the most even and well-distributed picture contrast.  We ignore
439 	 * colors not used in the palette.
440 	 *
441 	 * Note that we don't consider how often a palette color is used, only whether
442 	 * it's represented, or not.  Some weighting might improve things, but the
443 	 * simple method seems to work adequately.  In practice, as there are only 16
444 	 * colors in a palette, most pictures use most colors in a relatively well
445 	 * distributed manner.  This algorithm probably wouldn't work well on real
446 	 * photographs, though.
447 	 */
448 	gms_gammaref_t gms_graphics_equal_contrast_gamma(type16 palette[], long color_usage[]);
449 
450 	/**
451 	 * Select a suitable gamma for the picture, based on the current gamma mode.
452 	 *
453 	 * The function returns either the linear gamma, a gamma value half way
454 	 * between linear and the gamma that gives the most even contrast, or just
455 	 * the gamma that gives the most even contrast.
456 	 *
457 	 * In the normal case, a value half way to the extreme case of making color
458 	 * contrast equal for all colors is, subjectively, a reasonable value to use.
459 	 * The problem cases are the darkest pictures, and selecting this value
460 	 * brightens them while at the same time not making them look overbright or
461 	 * too "sunny".
462 	 */
463 	gms_gammaref_t gms_graphics_select_gamma(type8 bitmap[], type16 width,
464 		type16 height, type16 palette[]);
465 
466 	/**
467 	 * Clear the graphics window, and border and shade the area where the
468 	 * picture is going to be rendered.  This attempts a small raised effect
469 	 * for the picture, in keeping with modern trends.
470 	 */
471 	void gms_graphics_clear_and_border(winid_t glk_window,
472 		int x_offset, int y_offset, int pixel_size, type16 width, type16 height);
473 
474 	/**
475 	 * Convert a Magnetic Scrolls color palette to a Glk one, using the given
476 	 * gamma corrections.
477 	 */
478 	void gms_graphics_convert_palette(type16 ms_palette[], gms_gammaref_t gamma,
479 		glui32 glk_palette[]);
480 
481 	/**
482 	 * Given a picture width and height, return the x and y offsets to center
483 	 * this picture in the current graphics window.
484 	 */
485 	void gms_graphics_position_picture(winid_t glk_window,
486 		int pixel_size, type16 width, type16 height,
487 		int *x_offset, int *y_offset);
488 
489 	/**
490 	 * Apply a single animation frame to the given off-screen image buffer, using
491 	 * the frame bitmap, width, height and mask, the off-screen buffer, and the
492 	 * width and height of the main picture.
493 	 *
494 	 * Note that 'mask' may be NULL, implying that no frame pixel is transparent.
495 	 */
496 	void gms_graphics_apply_animation_frame(type8 bitmap[],
497 		type16 frame_width, type16 frame_height, type8 mask[],
498 		int frame_x, int frame_y, type8 off_screen[], type16 width, type16 height);
499 
500 	/**
501 	 * This function finds and applies the next set of animation frames to the
502 	 * given off-screen image buffer.  It's handed the width and height of the
503 	 * main picture, and the off-screen buffer.
504 	 *
505 	 * It returns FALSE if at the end of animations, TRUE if more animations
506 	 * remain.
507 	 */
508 	int gms_graphics_animate(type8 off_screen[], type16 width, type16 height);
509 
510 #ifndef GARGLK
511 	/**
512 	 * Given a point, return TRUE if that point is the vertex of a fillable
513 	 * region.  This is a helper function for layering pictures.  When assign-
514 	 * ing layers, we want to weight the colors that have the most complex
515 	 * shapes, or the largest count of isolated areas, heavier than simpler
516 	 * areas.
517 	 *
518 	 * By painting the colors with the largest number of isolated areas or
519 	 * the most complex shapes first, we help to minimize the number of fill
520 	 * regions needed to render the complete picture.
521 	 */
522 	int gms_graphics_is_vertex(type8 off_screen[], type16 width, type16 height,
523 		int x, int y);
524 
525 	/**
526 	 * gms_graphics_compare_layering_inverted()
527 	 * gms_graphics_assign_layers()
528 	 *
529 	 * Given two sets of image bitmaps, and a palette, this function will
530 	 * assign layers palette colors.
531 	 *
532 	 * Layers are assigned by first counting the number of vertices in the
533 	 * color plane, to get a measure of the complexity of shapes displayed in
534 	 * this color, and also the raw number of times each palette color is
535 	 * used.  This is then sorted, so that layers are assigned to colors, with
536 	 * the lowest layer being the color with the most complex shapes, and
537 	 * within this (or where the count of vertices is zero, as it could be
538 	 * in some animation frames) the most used color.
539 	 *
540 	 * The function compares pixels in the two image bitmaps given, these
541 	 * being the off-screen and on-screen buffers, and generates counts only
542 	 * where these bitmaps differ.  This ensures that only pixels not yet
543 	 * painted are included in layering.
544 	 *
545 	 * As well as assigning layers, this function returns a set of layer usage
546 	 * flags, to help the rendering loop to terminate as early as possible.
547 	 *
548 	 * By painting lower layers first, the paint can take in larger areas if
549 	 * it's permitted to include not-yet-validated higher levels.  This helps
550 	 * minimize the amount of Glk areas fills needed to render a picture.
551 	 */
552 	int gms_graphics_compare_layering_inverted(const void *void_first,
553 		const void *void_second);
554 
555 	void gms_graphics_assign_layers(type8 off_screen[], type8 on_screen[],
556 		type16 width, type16 height, int layers[], long layer_usage[]);
557 
558 	/**
559 	 * This is a partially optimized point plot.  Given a point in the graphics
560 	 * bitmap, it tries to extend the point to a color region, and fill a number
561 	 * of pixels in a single Glk rectangle fill.  The goal here is to reduce the
562 	 * number of Glk rectangle fills, which tend to be extremely inefficient
563 	 * operations for generalized point plotting.
564 	 *
565 	 * The extension works in image layers; each palette color is assigned a
566 	 * layer, and we paint each layer individually, starting at the lowest.  So,
567 	 * the region is free to fill any invalidated pixel in a higher layer, and
568 	 * all pixels, invalidated or already validated, in the same layer.  In
569 	 * practice, it is good enough to look for either invalidated pixels or pixels
570 	 * in the same layer, and construct a region as large as possible from these,
571 	 * then on marking points as validated, mark only those in the same layer as
572 	 * the initial point.
573 	 *
574 	 * The optimization here is not the best possible, but is reasonable.  What
575 	 * we do is to try and stretch the region horizontally first, then vertically.
576 	 * In practice, we might find larger areas by stretching vertically and then
577 	 * horizontally, or by stretching both dimensions at the same time.  In
578 	 * mitigation, the number of colors in a picture is small (16), and the
579 	 * aspect ratio of pictures makes them generally wider than they are tall.
580 	 *
581 	 * Once we've found the region, we render it with a single Glk rectangle fill,
582 	 * and mark all the pixels in this region that match the layer of the initial
583 	 * given point as validated.
584 	 */
585 	void gms_graphics_paint_region(winid_t glk_window, glui32 palette[], int layers[],
586 		type8 off_screen[], type8 on_screen[], int x, int y, int x_offset, int y_offset,
587 		int pixel_size, type16 width, type16 height);
588 	#endif
589 
590 	void gms_graphics_paint_everything(winid_t glk_window,
591 		glui32 palette[], type8 off_screen[], int x_offset, int y_offset,
592 		type16 width, type16 height);
593 
594 	/**
595 	 * This is a background function, called on Glk timeouts.  Its job is to
596 	 * repaint some of the current graphics image.  On successive calls, it
597 	 * does a part of the repaint, then yields to other processing.  This is
598 	 * useful since the Glk primitive to plot points in graphical windows is
599 	 * extremely slow; this way, the repaint doesn't block game play.
600 	 *
601 	 * The function should be called on Glk timeout events.  When the repaint
602 	 * is complete, the function will turn off Glk timers.
603 	 *
604 	 * The function uses double-buffering to track how much of the graphics
605 	 * buffer has been rendered.  This helps to minimize the amount of point
606 	 * plots required, as only the differences between the two buffers need
607 	 * to be rendered.
608 	 */
609 	void gms_graphics_timeout();
610 
611 	/**
612 	 * Called by the main interpreter when it wants us to display a picture.
613 	 * The function gets the picture bitmap, palette, and dimensions, and
614 	 * saves them, and the picture id, in module variables for the background
615 	 * rendering function.
616 	 *
617 	 * The graphics window is opened if required, or closed if mode is zero.
618 	 *
619 	 * The function checks for changes of actual picture by calculating the
620 	 * CRC for picture data; this helps to prevent unnecessary repaints in
621 	 * cases where the interpreter passes us the same picture as we're already
622 	 * displaying.  There is a less than 1 in 4,294,967,296 chance that a new
623 	 * picture will be missed.  We'll live with that.
624 	 *
625 	 * Why use CRCs, rather than simply storing the values of picture passed in
626 	 * a static variable?  Because some games, typically Magnetic Windows, use
627 	 * the picture argument as a form of string pointer, and can pass in the
628 	 * same value for several, perhaps all, game pictures.  If we just checked
629 	 * for a change in the picture argument, we'd never see one.  So we must
630 	 * instead look for changes in the real picture data.
631 	 */
632 	void ms_showpic(type32 picture, type8 mode);
633 
634 	/**
635 	 * Return TRUE if the graphics module data is loaded with a usable picture,
636 	 * FALSE if there is no picture available to display.
637 	 */
gms_graphics_picture_is_available()638 	int gms_graphics_picture_is_available() const {
639 		return gms_graphics_bitmap != nullptr;
640 	}
641 
642 	/**
643 	 * Return the width, height, and animation flag of the currently loaded
644 	 * picture.  The function returns FALSE if no picture is loaded, otherwise
645 	 * TRUE, with picture details in the return arguments.
646 	 */
647 	int gms_graphics_get_picture_details(int *width, int *height, int *is_animated);
648 
649 	/**
650 	 * Returns the current level of applied gamma correction, as a string, the
651 	 * count of colors in the picture, and a flag indicating if graphics is
652 	 * active (busy).  The function return FALSE if graphics is not enabled or
653 	 * if not being displayed, otherwise TRUE with the gamma, color count, and
654 	 * active flag in the return arguments.
655 	 *
656 	 * This function races with the graphics timeout, as it returns information
657 	 * set up by the first timeout following a new picture.  There's a very,
658 	 * very small chance that it might win the race, in which case out-of-date
659 	 * gamma and color count values are returned.
660 	 */
661 	int gms_graphics_get_rendering_details(const char **gamma, int *color_count,
662 		int *is_active);
663 
664 	/**
665 	 * Return TRUE if it looks like interpreter graphics are turned on, FALSE
666 	 * otherwise.
667 	 */
668 	int gms_graphics_interpreter_enabled();
669 
670 	/*
671 	 * gms_graphics_cleanup()
672 	 *
673 	 * Free memory resources allocated by graphics functions.  Called on game
674 	 * end.
675 	 */
676 	void gms_graphics_cleanup();
677 
678 	/*---------------------------------------------------------------------*/
679 	/*  Glk port status line functions                                     */
680 	/*---------------------------------------------------------------------*/
681 
682 	/**
683 	 * Receive one status character from the interpreter.  Characters are
684 	 * buffered internally, and on CR, the buffer is copied to the main static
685 	 * status buffer for use by the status line printing function.
686 	 */
687 	void ms_statuschar(type8 c);
688 
689 	/*
690 	 * Update the information in the status window with the current contents of
691 	 * the completed status line buffer, or a default string if no completed
692 	 * status line.
693 	 */
694 	void gms_status_update();
695 
696 	/**
697 	 * Print the current contents of the completed status line buffer out in the
698 	 * main window, if it has changed since the last call.  This is for non-
699 	 * windowing Glk libraries.
700 	 */
701 	void gms_status_print();
702 
703 	/*
704 	 * gms_status_notify()
705 	 *
706 	 * Front end function for updating status.  Either updates the status window
707 	 * or prints the status line to the main window.
708 	 */
709 	void gms_status_notify();
710 
711 	/*
712 	 * gms_status_redraw()
713 	 *
714 	 * Redraw the contents of any status window with the buffered status string.
715 	 * This function should be called on the appropriate Glk window resize and
716 	 * arrange events.
717 	 */
718 	void gms_status_redraw();
719 
720 	/*---------------------------------------------------------------------*/
721 	/*  Glk port output functions                                          */
722 	/*---------------------------------------------------------------------*/
723 
724 	/*
725 	 * gms_output_register_help_request()
726 	 * gms_output_silence_help_hints()
727 	 * gms_output_provide_help_hint()
728 	 *
729 	 * Register a request for help, and print a note of how to get Glk command
730 	 * help from the interpreter unless silenced.
731 	 */
732 	void gms_output_register_help_request();
733 
734 	void gms_output_silence_help_hints();
735 
736 	void gms_output_provide_help_hint();
737 
738 	/*
739 	 * gms_game_prompted()
740 	 *
741 	 * Return TRUE if the last game output appears to have been a ">" prompt.
742 	 * Once called, the flag is reset to FALSE, and requires more game output
743 	 * to set it again.
744 	 */
745 	int gms_game_prompted();
746 
747 	/*
748 	 * gms_detect_game_prompt()
749 	 *
750 	 * See if the last non-newline-terminated line in the output buffer seems
751 	 * to be a prompt, and set the game prompted flag if it does, otherwise
752 	 * clear it.
753 	 */
754 	void gms_detect_game_prompt();
755 
756 	/*
757 	 * gms_output_delete()
758 	 *
759 	 * Delete all buffered output text.  Free all malloc'ed buffer memory, and
760 	 * return the buffer variables to their initial values.
761 	 */
762 	void gms_output_delete();
763 
764 	/*
765 	 * gms_output_flush()
766 	 *
767 	 * Flush any buffered output text to the Glk main window, and clear the
768 	 * buffer.
769 	 */
770 	void gms_output_flush();
771 
772 	/*
773 	 * ms_putchar()
774 	 *
775 	 * Buffer a character for eventual printing to the main window.
776 	 */
777 	void ms_putchar(type8 c);
778 
779 	/*
780 	 * gms_styled_string()
781 	 * gms_styled_char()
782 	 * gms_standout_string()
783 	 * gms_standout_char()
784 	 * gms_normal_string()
785 	 * gms_normal_char()
786 	 * gms_header_string()
787 	 * gms_banner_string()
788 	 *
789 	 * Convenience functions to print strings in assorted styles.  A standout
790 	 * string is one that hints that it's from the interpreter, not the game.
791 	 */
792 	void gms_styled_string(glui32 style, const char *message);
793 
794 	void gms_styled_char(glui32 style, char c);
795 
796 	void gms_standout_string(const char *message);
797 
798 	void gms_normal_string(const char *message);
799 
800 	void gms_normal_char(char c);
801 
802 	void gms_header_string(const char *message);
803 
804 	void gms_banner_string(const char *message);
805 
806 	/**
807 	 * Handle a core interpreter call to flush the output buffer.  Because Glk
808 	 * only flushes its buffers and displays text on g_vm->glk_select(), we can ignore
809 	 * these calls as long as we call gms_output_flush() when reading line input.
810 	 *
811 	 * Taking ms_flush() at face value can cause game text to appear before status
812 	 * line text where we are working with a non-windowing Glk, so it's best
813 	 * ignored where we can.
814 	 */
815 	void ms_flush();
816 
817 
818 	/*---------------------------------------------------------------------*/
819 	/*  Glk port hint functions                                            */
820 	/*---------------------------------------------------------------------*/
821 
822 	/**
823 	 * Return the maximum hint node referred to by the tree under the given node.
824 	 * The result is the largest index found, or node, if greater.  Because the
825 	 * interpreter doesn't supply it, we need to uncover it the hard way.  The
826 	 * function is recursive, and since it is a tree search, assumes that hints
827 	 * is a tree, not a graph.
828 	 */
829 	type16 gms_get_hint_max_node(const ms_hint hints_[], type16 node);
830 
831 	/**
832 	 * Return the content string for a given hint number within a given node.
833 	 * This counts over 'number' ASCII NULs in the node's content, returning
834 	 * the address of the string located this way.
835 	 */
836 	const char *gms_get_hint_content(const ms_hint hints_[], type16 node, int number);
837 
838 	/**
839 	 * Return the topic string for a given hint node.  This is found by searching
840 	 * the parent node for a link to the node handed in.  For the root node, the
841 	 * string is defaulted, since the root node has no parent.
842 	 */
843 	const char *gms_get_hint_topic(const ms_hint hints_[], type16 node);
844 
845 	/**
846 	 * If not already open, open the hints windows.  Returns TRUE if the windows
847 	 * opened, or were already open.
848 	 *
849 	 * The function creates two hints windows -- a text grid on top, for menus,
850 	 * and a text buffer below for hints.
851 	 */
852 	int gms_hint_open();
853 
854 	/**
855 	 * If open, close the hints windows.
856 	 */
857 	void gms_hint_close();
858 
859 	/**
860 	 * Return TRUE if hints windows are available.  If they're not, the hints
861 	 * system will need to use alternative output methods.
862 	 */
863 	int gms_hint_windows_available();
864 
865 	/**
866 	 * gms_hint_menu_print()
867 	 * gms_hint_menu_header()
868 	 * gms_hint_menu_justify()
869 	 * gms_hint_text_print()
870 	 * gms_hint_menutext_done()
871 	 * gms_hint_menutext_start()
872 	 *
873 	 * Output functions for writing hints.  These functions will write to hints
874 	 * windows where available, and to the main window where not.  When writing
875 	 * to hints windows, they also take care not to line wrap in the menu window.
876 	 * Limited formatting is available.
877 	 */
878 	void gms_hint_menu_print(int line, int column, const char *string_,
879 		glui32 width, glui32 height);
880 
881 	void gms_hint_menu_header(int line, const char *string_,
882 		glui32 width, glui32 height);
883 
884 	void gms_hint_menu_justify(int line, const char *left_string,
885 		const char *right_string, glui32 width, glui32 height);
886 
887 	void gms_hint_text_print(const char *string_);
888 
889 	void gms_hint_menutext_start();
890 
891 	void gms_hint_menutext_done();
892 
893 	/**
894 	 * Request and return a character event from the hints windows.  In practice,
895 	 * this means either of the hints windows if available, or the main window
896 	 * if not.
897 	 */
898 	void gms_hint_menutext_char_event(event_t *event);
899 
900 	/**
901 	 * Arrange the hints windows so that the hint menu window has the requested
902 	 * number of lines.  Returns the actual hint menu window width and height,
903 	 * or defaults if no hints windows are available.
904 	 */
905 	void gms_hint_arrange_windows(int requested_lines, glui32 *width, glui32 *height);
906 
907 	/**
908 	 * Update the hints windows for the given folder hint node.
909 	 */
910 	void gms_hint_display_folder(const struct ms_hint hints_[],
911 		const int cursor[], type16 node);
912 
913 	/**
914 	 * Update the hints windows for the given text hint node.
915 	 */
916 	void gms_hint_display_text(const struct ms_hint hints_[],
917 		const int cursor[], type16 node);
918 
919 	/**
920 	 * Display the given hint using the appropriate display function.
921 	 */
922 	void gms_hint_display(const struct ms_hint hints_[],
923 		const int cursor[], type16 node);
924 
925 	/**
926 	 * Handle a Glk keycode for the given folder hint.  Return the next node to
927 	 * handle, or the special end-hints on Quit at the root node.
928 	 */
929 	type16 gms_hint_handle_folder(const ms_hint hints_[],
930 		int cursor[], type16 node, glui32 keycode);
931 
932 	/**
933 	 * Handle a Glk keycode for the given text hint.  Return the next node to
934 	 * handle.
935 	 */
936 	type16 gms_hint_handle_text(const ms_hint hints[],
937 		int cursor[], type16 node, glui32 keycode);
938 
939 	/**
940 	 * Handle a Glk keycode for the given hint using the appropriate handler
941 	 * function.  Return the next node to handle.
942 	 */
943 	type16 gms_hint_handle(const ms_hint hints_[], int cursor[],
944 		type16 node, glui32 keycode);
945 
946 	/**
947 	 * Start game hints.  These are modal, though there's no overriding Glk
948 	 * reason why.  It's just that this matches the way they're implemented by
949 	 * most Inform games.  This may not be the best way of doing help, but at
950 	 * least it's likely to be familiar, and anything more ambitious may be
951 	 * beyond the current Glk capabilities.
952 	 *
953 	 * This function uses CRCs to detect any change of hints data.  Normally,
954 	 * we'd expect none, at least within a given game run, but we can probably
955 	 * handle it okay if it happens.
956 	 */
957 	type8 ms_showhints(ms_hint *hints_);
958 
959 	/**
960 	 * Update the hints windows for the current hint.  This function should be
961 	 * called from the event handler on resize events, to repaint the hints
962 	 * display.  It does nothing if no hints windows have been opened, since
963 	 * in this case, there's no resize action required -- either we're not in
964 	 * the hints subsystem, or hints are being displayed in the main game
965 	 * window, for whatever reason.
966 	 */
967 	void gms_hint_redraw();
968 
969 	/**
970 	 * Free memory resources allocated by hints functions.  Called on game
971 	 * end.
972 	 */
973 	void gms_hints_cleanup();
974 
975 	void ms_playmusic(type8 *midi_data, type32 length, type16 tempo);
976 
977 	/*---------------------------------------------------------------------*/
978 	/*  Glk command escape functions                                       */
979 	/*---------------------------------------------------------------------*/
980 
981 	/**
982 	 * Stub function for the undo command.  The real work is to return the
983 	 * undo code to the input functions.
984 	 */
985 	void gms_command_undo(const char *argument);
986 
987 	/**
988 	 * Turn game output scripting (logging) on and off.
989 	 */
990 	void gms_command_script(const char *argument);
991 
992 	/**
993 	 * Turn game input logging on and off.
994 	 */
995 	void gms_command_inputlog(const char *argument);
996 
997 	/**
998 	 * Set the game input log, to read input from a file.
999 	 */
1000 	void gms_command_readlog(const char *argument);
1001 
1002 	/**
1003 	 * Turn abbreviation expansions on and off.
1004 	 */
1005 	void gms_command_abbreviations(const char *argument);
1006 
1007 	/**
1008 	 * Enable or disable graphics more permanently than is done by the main
1009 	 * interpreter.  Also, print out a few brief details about the graphics
1010 	 * state of the program.
1011 	 */
1012 	void gms_command_graphics(const char *argument);
1013 
1014 	/**
1015 	 * Enable or disable picture gamma corrections.
1016 	 */
1017 	void gms_command_gamma(const char *argument);
1018 
1019 	/**
1020 	 * Enable or disable picture animations.
1021 	 */
1022 	void gms_command_animations(const char *argument);
1023 
1024 	/**
1025 	 * Turn the extra "> " prompt output on and off.
1026 	 */
1027 	void gms_command_prompts(const char *argument);
1028 
1029 	/**
1030 	 * gms_command_print_version_number()
1031 	 * gms_command_version()
1032 	 *
1033 	 * Print out the Glk library version number.
1034 	 */
1035 	void gms_command_print_version_number(glui32 version);
1036 
1037 	void gms_command_version(const char *argument);
1038 
1039 	/**
1040 	 * Turn command escapes off.  Once off, there's no way to turn them back on.
1041 	 * Commands must be on already to enter this function.
1042 	 */
1043 	void gms_command_commands(const char *argument);
1044 
1045 	/**
1046 	 * Report all current Glk settings.
1047 	 */
1048 	void gms_command_summary(const char *argument);
1049 
1050 	/**
1051 	 * Document the available Glk commands.
1052 	 */
1053 	void gms_command_help(const char *command);
1054 
1055 	/**
1056 	 * This function is handed each input line.  If the line contains a specific
1057 	 * Glk port command, handle it and return TRUE, otherwise return FALSE.
1058 	 *
1059 	 * On unambiguous returns, it will also set the value for undo_command to the
1060 	 * table undo return value.
1061 	 */
1062 	int gms_command_escape(const char *string_, int *undo_command);
1063 
1064 	/**
1065 	 * This function makes a special case of the input line containing the single
1066 	 * word "undo", treating it as if it is "glk undo".  This makes life a bit
1067 	 * more convenient for the player, since it's the same behavior that most
1068 	 * other IF systems have.  It returns TRUE if "undo" found, FALSE otherwise.
1069 	 */
1070 	int gms_command_undo_special(const char *string_);
1071 
1072 	/*---------------------------------------------------------------------*/
1073 	/*  Glk port input functions                                           */
1074 	/*---------------------------------------------------------------------*/
1075 
1076 	/**
1077 	 * Expand a few common one-character abbreviations commonly found in other
1078 	 * game systems, but not always normal in Magnetic Scrolls games.
1079 	 */
1080 	void gms_expand_abbreviations(char *buffer_, int size);
1081 
1082 	/**
1083 	 * Read and buffer a line of input.  If there is an input log active, then
1084 	 * data is taken by reading this first.  Otherwise, the function gets a
1085 	 * line from Glk.
1086 	 *
1087 	 * It also makes special cases of some lines read from the user, either
1088 	 * handling commands inside them directly, or expanding abbreviations as
1089 	 * appropriate.  This is not reflected in the buffer, which is adjusted as
1090 	 * required before returning.
1091 	 */
1092 	void gms_buffer_input();
1093 
1094 	/**
1095 	 * Return the single next character to the interpreter.  This function
1096 	 * extracts characters from the input buffer until empty, when it then
1097 	 * tries to buffer more data.
1098 	 */
1099 	type8 ms_getchar(type8 trans);
1100 
1101 	/*---------------------------------------------------------------------*/
1102 	/*  Glk port event functions                                           */
1103 	/*---------------------------------------------------------------------*/
1104 
1105 	/**
1106 	 * Process Glk events until one of the expected type arrives.  Return
1107 	 * the event of that type.
1108 	 */
1109 	void gms_event_wait(glui32 wait_type, event_t *event);
1110 
1111 	/*---------------------------------------------------------------------*/
1112 	/*  Functions intercepted by link-time wrappers                        */
1113 	/*---------------------------------------------------------------------*/
1114 
1115 	/**
1116 	 * __wrap_toupper()
1117 	 * __wrap_tolower()
1118 	 *
1119 	 * Wrapper functions around toupper() and tolower().  The Linux linker's
1120 	 * --wrap option will convert calls to mumble() to __wrap_mumble() if we
1121 	 * give it the right options.  We'll use this feature to translate all
1122 	 * toupper() and tolower() calls in the interpreter code into calls to
1123 	 * Glk's versions of these functions.
1124 	 *
1125 	 * It's not critical that we do this.  If a linker, say a non-Linux one,
1126 	 * won't do --wrap, then just do without it.  It's unlikely that there
1127 	 * will be much noticeable difference.
1128 	 */
1129 	int __wrap_toupper(int ch);
1130 
1131 	int __wrap_tolower(int ch);
1132 
1133 	/*---------------------------------------------------------------------*/
1134 	/*  main and options parsing                                           */
1135 	/*---------------------------------------------------------------------*/
1136 
1137 	/**
1138 	 * Given a game name, try to establish three filenames from it - the main game
1139 	 * text file, the (optional) graphics data file, and the (optional) hints
1140 	 * file.  Given an input "file" X, the function looks for X.MAG or X.mag for
1141 	 * game data, X.GFX or X.gfx for graphics, and X.HNT or X.hnt for hints.
1142 	 * If the input file already ends with .MAG, .GFX, or .HNT, the extension
1143 	 * is stripped first.
1144 	 *
1145 	 * The function returns NULL for filenames not available.  It's not fatal if
1146 	 * the graphics filename or hints filename is NULL, but it is if the main game
1147 	 * filename is NULL.  Filenames are malloc'ed, and need to be freed by the
1148 	 * caller.
1149 	 */
1150 	void gms_establish_filenames(const char *name, char **text, char **graphics, char **hints_);
1151 
1152 	void gms_main();
1153 
1154 	/*---------------------------------------------------------------------*/
1155 	/*  Linkage between Glk entry/exit calls and the Magnetic interpreter  */
1156 	/*---------------------------------------------------------------------*/
1157 
1158 	/*
1159 	 * glk_main()
1160 	 *
1161 	 * Main entry point for Glk.  Here, all startup is done, and we call our
1162 	 * function to run the game.
1163 	 */
1164 	void glk_main();
1165 
1166 	void write(const char *fmt, ...);
1167 
1168 	void writeChar(char c);
1169 private:
1170 	/* Convert virtual pointer to effective pointer */
1171 	type8 *effective(type32 ptr);
1172 
read_l(type8 * ptr)1173 	static type32 read_l(type8 *ptr) {
1174 		return (type32)((type32)ptr[0] << 24 | (type32)ptr[1] << 16 | (type32)ptr[2] << 8 | (type32)ptr[3]);
1175 	}
1176 
read_w(type8 * ptr)1177 	static type16 read_w(type8 *ptr) {
1178 		return (type16)(ptr[0] << 8 | ptr[1]);
1179 	}
1180 
read_l2(type8 * ptr)1181 	static type32 read_l2(type8 *ptr) {
1182 		return ((type32)ptr[1] << 24 | (type32)ptr[0] << 16 | (type32)ptr[3] << 8 | (type32)ptr[2]);
1183 	}
1184 
read_w2(type8 * ptr)1185 	static type16 read_w2(type8 *ptr) {
1186 		return (type16)(ptr[1] << 8 | ptr[0]);
1187 	}
1188 
1189 	static void write_l(type8 *ptr, type32 val);
1190 
1191 	static void write_w(type8 *ptr, type16 val);
1192 
1193 	/* Standard rand - for equal cross-platform behaviour */
ms_seed(type32 seed)1194 	void ms_seed(type32 seed) {
1195 		rseed = seed;
1196 	}
1197 
1198 	type32 rand_emu();
1199 
1200 	void ms_freemem();
1201 
ms_is_running()1202 	type8 ms_is_running() const {
1203 		return running;
1204 	}
1205 
ms_is_magwin()1206 	type8 ms_is_magwin() const {
1207 		return (version == 4) ? 1 : 0;
1208 	}
1209 
ms_stop()1210 	void ms_stop() {
1211 		running = 0;
1212 	}
1213 
1214 	type8 init_gfx1(type8 *header);
1215 
1216 	type8 init_gfx2(type8 *header);
1217 
1218 	type8 init_snd(type8 *header);
1219 
1220 	/* zero all registers and flags and load the game */
1221 	type8 ms_init(const char *name, const char *gfxname, const char *hntname, const char *sndname);
1222 
1223 	type8 is_blank(type16 line, type16 width);
1224 
1225 	type8 *ms_extract1(type8 pic, type16 *w, type16 *h, type16 *pal);
1226 
1227 	type16s find_name_in_header(const char *name, type8 upper);
1228 
1229 	void extract_frame(struct picture *pic);
1230 
1231 	type8 *ms_extract2(const char *name, type16 *w, type16 *h, type16 *pal, type8 *is_anim);
1232 
1233 	type8 *ms_extract(type32 pic, type16 *w, type16 *h, type16 *pal, type8 *is_anim);
1234 
1235 	type8 ms_animate(struct ms_position **positions, type16 *count);
1236 
1237 	type8 *ms_get_anim_frame(type16s number, type16 *width, type16 *height, type8 **mask);
1238 
1239 	type8 ms_anim_is_repeating() const;
1240 
1241 	type16s find_name_in_sndheader(const char *name);
1242 
1243 	type8 *sound_extract(const char *name, type32 *length, type16 *tempo);
1244 
1245 	void save_undo();
1246 
1247 	type8 ms_undo();
1248 
1249 #ifdef LOGEMU
1250 	void log_status();
1251 #endif
1252 
1253 	void ms_status();
1254 
ms_count()1255 	type32 ms_count() const {
1256 		return i_count;
1257 	}
1258 
1259 	/* align register pointer for word/byte accesses */
1260 	type8 *reg_align(type8 *ptr, type8 size);
1261 
1262 	type32 read_reg(int i, int s);
1263 
1264 	void write_reg(int i, int s, type32 val);
1265 
1266 	/* [35c4] */
1267 	void char_out(type8 c);
1268 
1269 	/* extract addressing mode information [1c6f] */
1270 	void set_info(type8 b);
1271 
1272 	/* read a word and increase pc */
1273 	void read_word();
1274 
1275 	/* get addressing mode and set arg1 [1c84] */
1276 	void set_arg1();
1277 
1278 	/* get addressing mode and set arg2 [1bc5] */
1279 	void set_arg2_nosize(int use_dx, type8 b);
1280 
1281 	void set_arg2(int use_dx, type8 b);
1282 
1283 	/* [1b9e] */
1284 	void swap_args();
1285 
1286 	/* [1cdc] */
1287 	void push(type32 c);
1288 
1289 	/* [1cd1] */
1290 	type32 pop();
1291 
1292 	/* check addressing mode and get argument [2e85] */
1293 	void get_arg();
1294 
1295 	void set_flags();
1296 
1297 	/* [263a] */
1298 	int condition(type8 b);
1299 
1300 	/* [26dc] */
1301 	void branch(type8 b);
1302 
1303 	/* [2869] */
1304 	void do_add(type8 adda);
1305 
1306 	/* [2923] */
1307 	void do_sub(type8 suba);
1308 
1309 	/* [283b] */
1310 	void do_eor();
1311 
1312 	/* [280d] */
1313 	void do_and();
1314 
1315 	/* [27df] */
1316 	void do_or();
1317 
1318 	/* [289f] */
1319 	void do_cmp();
1320 
1321 	/* [2973] */
1322 	void do_move();
1323 
1324 	type8 do_btst(type8 a);
1325 
1326 	/* bit operation entry point [307c] */
1327 	void do_bop(type8 b, type8 a);
1328 
1329 	void check_btst();
1330 
1331 	void check_lea();
1332 
1333 	/* [33cc] */
1334 	void check_movem();
1335 
1336 	/* [3357] */
1337 	void check_movem2();
1338 
1339 	/* [30e4] in Jinxter, ~540 lines of 6510 spaghetti-code */
1340 	/* The mother of all bugs, but hey - no gotos used :-) */
1341 	void dict_lookup();
1342 
1343 	/* A0=findproperties(D0) [2b86], properties_ptr=[2b78] A0FE */
1344 	void do_findprop();
1345 
1346 	void write_string();
1347 
1348 	void output_number(type16 number);
1349 
1350 	type16 output_text(const char *text);
1351 
1352 	type16s hint_input();
1353 
1354 	type16 show_hints_text(ms_hint *hintsData, type16 index);
1355 
1356 	void do_line_a();
1357 
1358 	/* emulate an instruction [1b7e] */
1359 	type8 ms_rungame();
1360 private:
1361 	type8 ms_load_file(const char *name, type8 *ptr, type16 size);
1362 
1363 	type8 ms_save_file(const char *name, type8 *ptr, type16 size);
1364 
1365 	void script_write(type8 c);
1366 
1367 	void transcript_write(type8 c);
1368 public:
1369 	/**
1370 	 * Constructor
1371 	 */
1372 	Magnetic(OSystem *syst, const GlkGameDescription &gameDesc);
1373 
1374 	/**
1375 	 * Run the game
1376 	 */
1377 	void runGame() override;
1378 
1379 	/**
1380 	 * Returns the running interpreter type
1381 	 */
getInterpreterType()1382 	InterpreterType getInterpreterType() const override {
1383 		return INTERPRETER_MAGNETIC;
1384 	}
1385 
1386 	/**
1387 	 * The Magnetic engine currently doesn't support loading savegames from the GMM
1388 	 */
canLoadGameStateCurrently()1389 	bool canLoadGameStateCurrently() override {
1390 		return false;
1391 	}
1392 
1393 	/**
1394 	 * The Magnetic engine currently doesn't support saving games from the GMM
1395 	 */
canSaveGameStateCurrently()1396 	bool canSaveGameStateCurrently() override {
1397 		return false;
1398 	}
1399 
1400 	/**
1401 	 * Load a savegame from the passed Quetzal file chunk stream
1402 	 */
1403 	Common::Error readSaveData(Common::SeekableReadStream *rs) override;
1404 
1405 	/**
1406 	 * Save the game. The passed write stream represents access to the UMem chunk
1407 	 * in the Quetzal save file that will be created
1408 	 */
1409 	Common::Error writeGameData(Common::WriteStream *ws) override;
1410 };
1411 
1412 extern Magnetic *g_vm;
1413 
1414 } // End of namespace Magnetic
1415 } // End of namespace Glk
1416 
1417 #endif
1418