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