Subversion Repositories Games.Prince of Persia

Rev

Rev 7 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2. SDLPoP, a port/conversion of the DOS game Prince of Persia.
  3. Copyright (C) 2013-2018  Dávid Nagy
  4.  
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  
  18. The authors of this program may be contacted at http://forum.princed.org
  19. */
  20.  
  21. #include "common.h"
  22. #include <time.h>
  23. #include <errno.h>
  24.  
  25. // Most functions in this file are different from those in the original game.
  26.  
  27. void sdlperror(const char* header) {
  28.         const char* error = SDL_GetError();
  29.         printf("%s: %s\n",header,error);
  30.         //quit(1);
  31. }
  32.  
  33. char exe_dir[POP_MAX_PATH] = ".";
  34. bool found_exe_dir = false;
  35.  
  36. void find_exe_dir() {
  37.         if (found_exe_dir) return;
  38. #if 0
  39.         strncpy(exe_dir, g_argv[0], sizeof(exe_dir));
  40.         char* last_slash = NULL;
  41.         char* pos = exe_dir;
  42.         for (char c = *pos; c != '\0'; c = *(++pos)) {
  43.                 if (c == '/' || c == '\\') {
  44.                         last_slash = pos;
  45.                 }
  46.         }
  47.         if (last_slash != NULL) {
  48.                 *last_slash = '\0';
  49.         }
  50. #else
  51.    extern int _NSGetExecutablePath (char* buf, uint32_t* bufsize);
  52.    uint32_t bufsize = sizeof (exe_dir);
  53.    char *pos;
  54.    _NSGetExecutablePath (exe_dir, &bufsize);
  55.    pos = strrchr (exe_dir, '/');
  56.    if (pos != NULL) *pos = 0;
  57.    pos = strrchr (exe_dir, '/');
  58.    if (pos != NULL) *pos = 0;
  59.    strcat (exe_dir, "/Resources");
  60. #endif
  61.         found_exe_dir = true;
  62. }
  63.  
  64. static inline bool file_exists(const char* filename) {
  65.     return (access(filename, F_OK) != -1);
  66. }
  67.  
  68. const char* locate_file_(const char* filename, char* path_buffer, int buffer_size) {
  69.         if(file_exists(filename)) {
  70.                 return filename;
  71.         } else {
  72.                 // If failed, it may be that SDLPoP is being run from the wrong different working directory.
  73.                 // We can try to rescue the situation by loading from the directory of the executable.
  74.                 find_exe_dir();
  75.         snprintf(path_buffer, buffer_size, "%s/%s", exe_dir, filename);
  76.         return (const char*) path_buffer;
  77.         }
  78. }
  79.  
  80.  
  81.  
  82. dat_type* dat_chain_ptr = NULL;
  83.  
  84. int last_key_scancode;
  85. char last_text_input;
  86.  
  87. // seg009:000D
  88. int __pascal far read_key() {
  89.         // stub
  90.         int key = last_key_scancode;
  91.         last_key_scancode = 0;
  92.         return key;
  93. }
  94.  
  95. // seg009:019A
  96. void __pascal far clear_kbd_buf() {
  97.         // stub
  98.         last_key_scancode = 0;
  99.         last_text_input = 0;
  100. }
  101.  
  102. // seg009:040A
  103. word __pascal far prandom(word max) {
  104.         if (!seed_was_init) {
  105.                 // init from current time
  106.                 random_seed = time(NULL);
  107.                 seed_was_init = 1;
  108.         }
  109.         random_seed = random_seed * 214013 + 2531011;
  110.         return (random_seed >> 16) % (max + 1);
  111. }
  112.  
  113. // seg009:0467
  114. int __pascal far round_xpos_to_byte(int xpos,int round_direction) {
  115.         // stub
  116.         return xpos;
  117. }
  118.  
  119. // seg009:0C7A
  120. void __pascal far quit(int exit_code) {
  121.         restore_stuff();
  122.         exit(exit_code);
  123. }
  124.  
  125. // seg009:0C90
  126. void __pascal far restore_stuff() {
  127.         SDL_Quit();
  128. }
  129.  
  130. // seg009:0E33
  131. int __pascal far key_test_quit() {
  132.         word key;
  133.         key = read_key();
  134.         if (key == (SDL_SCANCODE_Q | WITH_CTRL)) { // ctrl-q
  135.  
  136.                 #ifdef USE_REPLAY
  137.                 if (recording) save_recorded_replay();
  138.                 #endif
  139.  
  140.                 quit(0);
  141.         }
  142.         return key;
  143. }
  144.  
  145. // seg009:0E54
  146. const char* __pascal far check_param(const char *param) {
  147.         // stub
  148.         short arg_index;
  149.         for (arg_index = 1; arg_index < g_argc; ++arg_index) {
  150.  
  151.                 char* curr_arg = g_argv[arg_index];
  152.  
  153.                 // Filenames (e.g. replays) should never be a valid 'normal' param so we should skip these to prevent conflicts.
  154.                 // We can lazily distinguish filenames from non-filenames by checking whether they have a dot in them.
  155.                 // (Assumption: all relevant files, e.g. replay files, have some file extension anyway)
  156.                 if (strchr(curr_arg, '.') != NULL) {
  157.                         continue;
  158.                 }
  159.  
  160.                 // List of params that expect a specifier ('sub-') arg directly after it (e.g. the mod's name, after "mod" arg)
  161.                 // Such sub-args may conflict with the normal params (so, we should 'skip over' them)
  162.                 static const char params_with_one_subparam[][16] = { "mod", "validate", /*...*/ };
  163.  
  164.                 bool curr_arg_has_one_subparam = false;
  165.                 int i;
  166.                 for (i = 0; i < COUNT(params_with_one_subparam); ++i) {
  167.                         if (strncasecmp(curr_arg, params_with_one_subparam[i], strlen(params_with_one_subparam[i])) == 0) {
  168.                                 curr_arg_has_one_subparam = true;
  169.                                 break;
  170.                         }
  171.                 }
  172.  
  173.                 if (curr_arg_has_one_subparam) {
  174.                         // Found an arg that has one sub-param, so we want to:
  175.                         // 1: skip over the next arg                (if we are NOT checking for this specific param)
  176.                         // 2: return a pointer below to the SUB-arg (if we ARE checking for this specific param)
  177.                         ++arg_index;
  178.                         if (!(arg_index < g_argc)) return NULL; // not enough arguments
  179.                 }
  180.  
  181.                 if (/*strnicmp*/strncasecmp(curr_arg, param, strlen(param)) == 0) {
  182.                         return g_argv[arg_index];
  183.                 }
  184.         }
  185.         return NULL;
  186. }
  187.  
  188. // seg009:0EDF
  189. int __pascal far pop_wait(int timer_index,int time) {
  190.         start_timer(timer_index, time);
  191.         return do_wait(timer_index);
  192. }
  193.  
  194. // S_ISREG may not be defined under MSVC
  195. #if defined(_MSC_VER) && !defined(S_ISREG)
  196. #define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
  197. #endif
  198.  
  199. static FILE* open_dat_from_root_or_data_dir(const char* filename) {
  200.         FILE* fp = NULL;
  201.         fp = fopen(filename, "rb");
  202.  
  203.         // if failed, try if the DAT file can be opened in the data/ directory, instead of the main folder
  204.         if (fp == NULL) {
  205.                 char data_path[POP_MAX_PATH];
  206.                 snprintf(data_path, sizeof(data_path), "data/%s", filename);
  207.  
  208.         if (!file_exists(data_path)) {
  209.             find_exe_dir();
  210.             snprintf(data_path, sizeof(data_path), "%s/data/%s", exe_dir, filename);
  211.         }
  212.  
  213.                 // verify that this is a regular file and not a directory (otherwise, don't open)
  214.                 struct stat path_stat;
  215.                 stat(data_path, &path_stat);
  216.                 if (S_ISREG(path_stat.st_mode)) {
  217.                         fp = fopen(data_path, "rb");
  218.                 }
  219.         }
  220.         return fp;
  221. }
  222.  
  223. // seg009:0F58
  224. dat_type *__pascal open_dat(const char *filename,int drive) {
  225.         FILE* fp = NULL;
  226.         if (!use_custom_levelset) {
  227.                 fp = open_dat_from_root_or_data_dir(filename);
  228.         }
  229.         else {
  230.                 char filename_mod[POP_MAX_PATH];
  231.                 // before checking the root directory, first try mods/MODNAME/
  232.                 snprintf(filename_mod, sizeof(filename_mod), "mods/%s/%s", levelset_name, filename);
  233.                 fp = fopen(filename_mod, "rb");
  234.                 if (fp == NULL) {
  235.                         fp = open_dat_from_root_or_data_dir(filename);
  236.                 }
  237.         }
  238.         dat_header_type dat_header;
  239.         dat_table_type* dat_table = NULL;
  240.  
  241.         dat_type* pointer = (dat_type*) calloc(1, sizeof(dat_type));
  242.         strncpy(pointer->filename, filename, sizeof(pointer->filename));
  243.         pointer->next_dat = dat_chain_ptr;
  244.         dat_chain_ptr = pointer;
  245.  
  246.         if (fp != NULL) {
  247.                 if (fread(&dat_header, 6, 1, fp) != 1)
  248.                         goto failed;
  249.                 dat_table = (dat_table_type*) malloc(dat_header.table_size);
  250.                 if (dat_table == NULL ||
  251.                     fseek(fp, dat_header.table_offset, SEEK_SET) ||
  252.                     fread(dat_table, dat_header.table_size, 1, fp) != 1)
  253.                         goto failed;
  254.                 pointer->handle = fp;
  255.                 pointer->dat_table = dat_table;
  256.         }
  257. out:
  258.         // stub
  259.         return pointer;
  260. failed:
  261.         perror(filename);
  262.         if (fp)
  263.                 fclose(fp);
  264.         if (dat_table)
  265.                 free(dat_table);
  266.         goto out;
  267. }
  268.  
  269. // seg009:9CAC
  270. void __pascal far set_loaded_palette(dat_pal_type far *palette_ptr) {
  271.         int dest_row, dest_index, source_row;
  272.         for (dest_row = dest_index = source_row = 0; dest_row < 16; ++dest_row, dest_index += 0x10) {
  273.                 if (palette_ptr->row_bits & (1 << dest_row)) {
  274.                         set_pal_arr(dest_index, 16, palette_ptr->vga + source_row*0x10, 1);
  275.                         ++source_row;
  276.                 }
  277.         }
  278. }
  279.  
  280. // data:3356
  281. word chtab_palette_bits = 1;
  282.  
  283. // seg009:104E
  284. chtab_type* __pascal load_sprites_from_file(int resource,int palette_bits, int quit_on_error) {
  285.         int i;
  286.         int n_images = 0;
  287.         //int has_palette_bits = 1;
  288.         chtab_type* chtab = NULL;
  289.         dat_shpl_type* shpl = (dat_shpl_type*) load_from_opendats_alloc(resource, "pal", NULL, NULL);
  290.         if (shpl == NULL) {
  291.                 printf("Can't load sprites from resource %d.\n", resource);
  292.                 //if (quit_on_error) quit(1);
  293.                 return NULL;
  294.         }
  295.  
  296.         dat_pal_type* pal_ptr = &shpl->palette;
  297.         if (graphics_mode == gmMcgaVga) {
  298.                 if (palette_bits == 0) {
  299.                         /*
  300.                         palette_bits = add_palette_bits(pal_ptr->n_colors);
  301.                         if (palette_bits == 0) {
  302.                                 quit(1);
  303.                         }
  304.                         */
  305.                 } else {
  306.                         chtab_palette_bits |= palette_bits;
  307.                         //has_palette_bits = 0;
  308.                 }
  309.                 pal_ptr->row_bits = palette_bits;
  310.         }
  311.  
  312.         n_images = shpl->n_images;
  313.         size_t alloc_size = sizeof(chtab_type) + sizeof(void far *) * n_images;
  314.         chtab = (chtab_type*) malloc(alloc_size);
  315.         memset(chtab, 0, alloc_size);
  316.         chtab->n_images = n_images;
  317.         for (i = 1; i <= n_images; i++) {
  318.                 SDL_Surface* image = load_image(resource + i, pal_ptr);
  319. //              if (image == NULL) printf(" failed");
  320.                 if (image != NULL) {
  321.  
  322.                         if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
  323.                                 sdlperror("SDL_SetAlpha");
  324.                                 quit(1);
  325.                         }
  326.  
  327.                         /*
  328.                         if (SDL_SetColorKey(image, SDL_SRCCOLORKEY, 0) != 0) {
  329.                                 sdlperror("SDL_SetColorKey");
  330.                                 quit(1);
  331.                         }
  332.                         */
  333.                 }
  334. //              printf("\n");
  335.                 chtab->images[i-1] = image;
  336.         }
  337.         set_loaded_palette(pal_ptr);
  338.         return chtab;
  339. }
  340.  
  341. // seg009:11A8
  342. void __pascal far free_chtab(chtab_type *chtab_ptr) {
  343.         image_type far* curr_image;
  344.         word id;
  345.         word n_images;
  346.         if (graphics_mode == gmMcgaVga && chtab_ptr->has_palette_bits) {
  347.                 chtab_palette_bits &= ~ chtab_ptr->chtab_palette_bits;
  348.         }
  349.         n_images = chtab_ptr->n_images;
  350.         for (id = 0; id < n_images; ++id) {
  351.                 curr_image = chtab_ptr->images[id];
  352.                 if (curr_image) {
  353.                         SDL_FreeSurface(curr_image);
  354.                 }
  355.         }
  356.         free_near(chtab_ptr);
  357. }
  358.  
  359. // seg009:8CE6
  360. void __pascal far decompress_rle_lr(byte far *destination,const byte far *source,int dest_length) {
  361.         const byte* src_pos = source;
  362.         byte* dest_pos = destination;
  363.         short rem_length = dest_length;
  364.         while (rem_length) {
  365.                 sbyte count = *(src_pos++);
  366.                 if (count >= 0) { // copy
  367.                         ++count;
  368.                         do {
  369.                                 *(dest_pos++) = *(src_pos++);
  370.                                 --rem_length;
  371.                         } while (--count && rem_length);
  372.                 } else { // repeat
  373.                         byte al = *(src_pos++);
  374.                         count = -count;
  375.                         do {
  376.                                 *(dest_pos++) = al;
  377.                                 --rem_length;
  378.                         } while (--count && rem_length);
  379.                 }
  380.         }
  381. }
  382.  
  383. // seg009:8D1C
  384. void __pascal far decompress_rle_ud(byte far *destination,const byte far *source,int dest_length,int width,int height) {
  385.         short rem_height = height;
  386.         const byte* src_pos = source;
  387.         byte* dest_pos = destination;
  388.         short rem_length = dest_length;
  389.         --dest_length;
  390.         --width;
  391.         while (rem_length) {
  392.                 sbyte count = *(src_pos++);
  393.                 if (count >= 0) { // copy
  394.                         ++count;
  395.                         do {
  396.                                 *(dest_pos++) = *(src_pos++);
  397.                                 dest_pos += width;
  398.                                 if (--rem_height == 0) {
  399.                                         dest_pos -= dest_length;
  400.                                         rem_height = height;
  401.                                 }
  402.                                 --rem_length;
  403.                         } while (--count && rem_length);
  404.                 } else { // repeat
  405.                         byte al = *(src_pos++);
  406.                         count = -count;
  407.                         do {
  408.                                 *(dest_pos++) = al;
  409.                                 dest_pos += width;
  410.                                 if (--rem_height == 0) {
  411.                                         dest_pos -= dest_length;
  412.                                         rem_height = height;
  413.                                 }
  414.                                 --rem_length;
  415.                         } while (--count && rem_length);
  416.                 }
  417.         }
  418. }
  419.  
  420. // seg009:90FA
  421. byte far* __pascal far decompress_lzg_lr(byte far *dest,const byte far *source,int dest_length) {
  422.         byte* window = (byte*) malloc_near(0x400);
  423.         if (window == NULL) return NULL;
  424.         memset(window, 0, 0x400);
  425.         byte* window_pos = window + 0x400 - 0x42; // bx
  426.         short remaining = dest_length; // cx
  427.         byte* window_end = window + 0x400; // dx
  428.         const byte* source_pos = source;
  429.         byte* dest_pos = dest;
  430.         word mask = 0;
  431.         do {
  432.                 mask >>= 1;
  433.                 if ((mask & 0xFF00) == 0) {
  434.                         mask = *(source_pos++) | 0xFF00;
  435.                 }
  436.                 if (mask & 1) {
  437.                         *(window_pos++) = *(dest_pos++) = *(source_pos++);
  438.                         if (window_pos >= window_end) window_pos = window;
  439.                         --remaining;
  440.                 } else {
  441.                         word copy_info = *(source_pos++);
  442.                         copy_info = (copy_info << 8) | *(source_pos++);
  443.                         byte* copy_source = window + (copy_info & 0x3FF);
  444.                         byte copy_length = (copy_info >> 10) + 3;
  445.                         do {
  446.                                 *(window_pos++) = *(dest_pos++) = *(copy_source++);
  447.                                 if (copy_source >= window_end) copy_source = window;
  448.                                 if (window_pos >= window_end) window_pos = window;
  449.                         } while (--remaining && --copy_length);
  450.                 }
  451.         } while (remaining);
  452. //      end:
  453.         free(window);
  454.         return dest;
  455. }
  456.  
  457. // seg009:91AD
  458. byte far* __pascal far decompress_lzg_ud(byte far *dest,const byte far *source,int dest_length,int stride,int height) {
  459.         byte* window = (byte*) malloc_near(0x400);
  460.         if (window == NULL) return NULL;
  461.         memset(window, 0, 0x400);
  462.         byte* window_pos = window + 0x400 - 0x42; // bx
  463.         short remaining = height; // cx
  464.         byte* window_end = window + 0x400; // dx
  465.         const byte* source_pos = source;
  466.         byte* dest_pos = dest;
  467.         word mask = 0;
  468.         short var_6 = dest_length - 1;
  469.         do {
  470.                 mask >>= 1;
  471.                 if ((mask & 0xFF00) == 0) {
  472.                         mask = *(source_pos++) | 0xFF00;
  473.                 }
  474.                 if (mask & 1) {
  475.                         *(window_pos++) = *dest_pos = *(source_pos++);
  476.                         dest_pos += stride;
  477.                         if (--remaining == 0) {
  478.                                 dest_pos -= var_6;
  479.                                 remaining = height;
  480.                         }
  481.                         if (window_pos >= window_end) window_pos = window;
  482.                         --dest_length;
  483.                 } else {
  484.                         word copy_info = *(source_pos++);
  485.                         copy_info = (copy_info << 8) | *(source_pos++);
  486.                         byte* copy_source = window + (copy_info & 0x3FF);
  487.                         byte copy_length = (copy_info >> 10) + 3;
  488.                         do {
  489.                                 *(window_pos++) = *dest_pos = *(copy_source++);
  490.                                 dest_pos += stride;
  491.                                 if (--remaining == 0) {
  492.                                         dest_pos -= var_6;
  493.                                         remaining = height;
  494.                                 }
  495.                                 if (copy_source >= window_end) copy_source = window;
  496.                                 if (window_pos >= window_end) window_pos = window;
  497.                         } while (--dest_length && --copy_length);
  498.                 }
  499.         } while (dest_length);
  500. //      end:
  501.         free(window);
  502.         return dest;
  503. }
  504.  
  505. // seg009:938E
  506. void __pascal far decompr_img(byte far *dest,const image_data_type far *source,int decomp_size,int cmeth, int stride) {
  507.         switch (cmeth) {
  508.                 case 0: // RAW left-to-right
  509.                         memcpy_far(dest, &source->data, decomp_size);
  510.                 break;
  511.                 case 1: // RLE left-to-right
  512.                         decompress_rle_lr(dest, source->data, decomp_size);
  513.                 break;
  514.                 case 2: // RLE up-to-down
  515.                         decompress_rle_ud(dest, source->data, decomp_size, stride, source->height);
  516.                 break;
  517.                 case 3: // LZG left-to-right
  518.                         decompress_lzg_lr(dest, source->data, decomp_size);
  519.                 break;
  520.                 case 4: // LZG up-to-down
  521.                         decompress_lzg_ud(dest, source->data, decomp_size, stride, source->height);
  522.                 break;
  523.         }
  524. }
  525.  
  526. int calc_stride(image_data_type* image_data) {
  527.         int width = image_data->width;
  528.         int flags = image_data->flags;
  529.         int depth = ((flags >> 12) & 3) + 1;
  530.         return (depth * width + 7) / 8;
  531. }
  532.  
  533. byte* conv_to_8bpp(byte* in_data, int width, int height, int stride, int depth) {
  534.         byte* out_data = (byte*) malloc(width * height);
  535.         int y, x_pixel, x_byte, pixel_in_byte;
  536.         int pixels_per_byte = 8 / depth;
  537.         int mask = (1 << depth) - 1;
  538.         for (y = 0; y < height; ++y) {
  539.                 byte* in_pos = in_data + y*stride;
  540.                 byte* out_pos = out_data + y*width;
  541.                 for (x_pixel = x_byte = 0; x_byte < stride; ++x_byte) {
  542.                         byte v = *in_pos;
  543.                         int shift = 8;
  544.                         for (pixel_in_byte = 0; pixel_in_byte < pixels_per_byte && x_pixel < width; ++pixel_in_byte, ++x_pixel) {
  545.                                 shift -= depth;
  546.                                 *out_pos = (v >> shift) & mask;
  547.                                 ++out_pos;
  548.                         }
  549.                         ++in_pos;
  550.                 }
  551.         }
  552.         return out_data;
  553. }
  554.  
  555. image_type* decode_image(image_data_type* image_data, dat_pal_type* palette) {
  556.         int height = image_data->height;
  557.         if (height == 0) return NULL;
  558.         int width = image_data->width;
  559.         int flags = image_data->flags;
  560.         int depth = ((flags >> 12) & 3) + 1;
  561.         int cmeth = (flags >> 8) & 0x0F;
  562.         int stride = calc_stride(image_data);
  563.         int dest_size = stride * height;
  564.         byte* dest = (byte*) malloc(dest_size);
  565.         memset(dest, 0, dest_size);
  566.         decompr_img(dest, image_data, dest_size, cmeth, stride);
  567.         byte* image_8bpp = conv_to_8bpp(dest, width, height, stride, depth);
  568.         free(dest); dest = NULL;
  569.         image_type* image = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
  570.         if (image == NULL) {
  571.                 sdlperror("SDL_CreateRGBSurface");
  572.                 quit(1);
  573.         }
  574.         if (SDL_LockSurface(image) != 0) {
  575.                 sdlperror("SDL_LockSurface");
  576.         }
  577.         int y;
  578.         for (y = 0; y < height; ++y) {
  579.                 // fill image with data
  580.                 memcpy((byte*)image->pixels + y*image->pitch, image_8bpp + y*width, width);
  581.         }
  582.         SDL_UnlockSurface(image);
  583.  
  584.         free(image_8bpp); image_8bpp = NULL;
  585.         SDL_Color colors[16];
  586.         int i;
  587.         for (i = 0; i < 16; ++i) {
  588.                 colors[i].r = palette->vga[i].r << 2;
  589.                 colors[i].g = palette->vga[i].g << 2;
  590.                 colors[i].b = palette->vga[i].b << 2;
  591.                 colors[i].a = SDL_ALPHA_OPAQUE;   // SDL2's SDL_Color has a fourth alpha component
  592.         }
  593.         colors[0].a = SDL_ALPHA_TRANSPARENT;
  594.         SDL_SetPaletteColors(image->format->palette, colors, 0, 16); // SDL_SetColors = deprecated
  595.         return image;
  596. }
  597.  
  598. // seg009:121A
  599. image_type* far __pascal far load_image(int resource_id, dat_pal_type* palette) {
  600.         // stub
  601.         data_location result;
  602.         int size;
  603.         void* image_data = load_from_opendats_alloc(resource_id, "png", &result, &size);
  604.         image_type* image = NULL;
  605.         switch (result) {
  606.                 case data_none:
  607.                         return NULL;
  608.                 break;
  609.                 case data_DAT: { // DAT
  610.                         image = decode_image((image_data_type*) image_data, palette);
  611.                 } break;
  612.                 case data_directory: { // directory
  613.                         SDL_RWops* rw = SDL_RWFromConstMem(image_data, size);
  614.                         if (rw == NULL) {
  615.                                 sdlperror("SDL_RWFromConstMem");
  616.                                 return NULL;
  617.                         }
  618.                         image = IMG_LoadPNG_RW(rw);
  619.                         if (SDL_RWclose(rw) != 0) {
  620.                                 sdlperror("SDL_RWclose");
  621.                         }
  622.                 } break;
  623.         }
  624.         if (image_data != NULL) free(image_data);
  625.  
  626.  
  627.         if (image != NULL) {
  628.                 // should immediately start using the onscreen pixel format, so conversion will not be needed
  629.  
  630.                 if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) { //sdl 1.2: SDL_SRCCOLORKEY
  631.                         sdlperror("SDL_SetColorKey");
  632.                         quit(1);
  633.                 }
  634. //              printf("bpp = %d\n", image->format->BitsPerPixel);
  635.                 if (SDL_SetSurfaceAlphaMod(image, 0) != 0) { //sdl 1.2: SDL_SetAlpha removed
  636.                         sdlperror("SDL_SetAlpha");
  637.                         quit(1);
  638.                 }
  639. //              image_type* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
  640. //              if (!colored_image) {
  641. //                      sdlperror("SDL_ConvertSurfaceFormat");
  642. //                      quit(1);
  643. //              }
  644. //              SDL_FreeSurface(image);
  645. //              image = colored_image;
  646.         }
  647.         return image;
  648. }
  649.  
  650. // seg009:13C4
  651. void __pascal far draw_image_transp(image_type far *image,image_type far *mask,int xpos,int ypos) {
  652.         if (graphics_mode == gmMcgaVga) {
  653.                 draw_image_transp_vga(image, xpos, ypos);
  654.         } else {
  655.                 // ...
  656.         }
  657. }
  658.  
  659. // seg009:157E
  660. int __pascal far set_joy_mode() {
  661.         // stub
  662.         if (SDL_NumJoysticks() < 1) {
  663.                 is_joyst_mode = 0;
  664.         } else {
  665.                 if (SDL_IsGameController(0)) {
  666.                         sdl_controller_ = SDL_GameControllerOpen(0);
  667.                         if (sdl_controller_ == NULL) {
  668.                                 is_joyst_mode = 0;
  669.                         } else {
  670.                                 is_joyst_mode = 1;
  671.                         }
  672.                 }
  673.                 // We have a joystick connected, but it's NOT compatible with the SDL_GameController
  674.                 // interface, so we resort to the classic SDL_Joystick interface instead
  675.                 else {
  676.                         sdl_joystick_ = SDL_JoystickOpen(0);
  677.                         is_joyst_mode = 1;
  678.                         using_sdl_joystick_interface = 1;
  679.                 }
  680.         }
  681.         if (enable_controller_rumble && is_joyst_mode) {
  682.                 sdl_haptic = SDL_HapticOpen(0);
  683.                 SDL_HapticRumbleInit(sdl_haptic); // initialize the device for simple rumble
  684.         } else {
  685.                 sdl_haptic = NULL;
  686.         }
  687.  
  688.         is_keyboard_mode = !is_joyst_mode;
  689.         return is_joyst_mode;
  690. }
  691.  
  692. // seg009:178B
  693. surface_type far *__pascal make_offscreen_buffer(const rect_type far *rect) {
  694.         // stub
  695. #ifndef USE_ALPHA
  696.         // Bit order matches onscreen buffer, good for fading.
  697.         return SDL_CreateRGBSurface(0, rect->right, rect->bottom, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0); //RGB888 (little endian)
  698. #else
  699.         return SDL_CreateRGBSurface(0, rect->right, rect->bottom, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24);
  700. #endif
  701.         //return surface;
  702. }
  703.  
  704. // seg009:17BD
  705. void __pascal far free_surface(surface_type *surface) {
  706.         SDL_FreeSurface(surface);
  707. }
  708.  
  709. // seg009:17EA
  710. void __pascal far free_peel(peel_type *peel_ptr) {
  711.         SDL_FreeSurface(peel_ptr->peel);
  712.         free(peel_ptr);
  713. }
  714.  
  715. // seg009:182F
  716. void __pascal far set_hc_pal() {
  717.         // stub
  718.         if (graphics_mode == gmMcgaVga) {
  719.                 set_pal_arr(0, 16, vga_palette, 1);
  720.         } else {
  721.                 // ...
  722.         }
  723. }
  724.  
  725. // seg009:2446
  726. void __pascal far flip_not_ega(byte far *memory,int height,int stride) {
  727.         byte* row_buffer = (byte*) malloc(stride);
  728.         byte* top_ptr;
  729.         byte* bottom_ptr;
  730.         bottom_ptr = top_ptr = memory;
  731.         bottom_ptr += (height - 1) * stride;
  732.         short rem_rows = height >> 1;
  733.         do {
  734.                 memcpy(row_buffer, top_ptr, stride);
  735.                 memcpy(top_ptr, bottom_ptr, stride);
  736.                 memcpy(bottom_ptr, row_buffer, stride);
  737.                 top_ptr += stride;
  738.                 bottom_ptr -= stride;
  739.                 --rem_rows;
  740.         } while (rem_rows);
  741.         free(row_buffer);
  742. }
  743.  
  744. // seg009:19B1
  745. void __pascal far flip_screen(surface_type far *surface) {
  746.         // stub
  747.         if (graphics_mode != gmEga) {
  748.                 if (SDL_LockSurface(surface) != 0) {
  749.                         sdlperror("SDL_LockSurface");
  750.                         quit(1);
  751.                 }
  752.                 flip_not_ega((byte*) surface->pixels, surface->h, surface->pitch);
  753.                 SDL_UnlockSurface(surface);
  754.         } else {
  755.                 // ...
  756.         }
  757. }
  758.  
  759. #ifndef USE_FADE
  760. // seg009:19EF
  761. void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
  762.         // stub
  763.         method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
  764.         request_screen_update();
  765. }
  766.  
  767. // seg009:1CC9
  768. void __pascal far fade_out_2(int rows) {
  769.         // stub
  770. }
  771. #endif // USE_FADE
  772.  
  773. // seg009:2288
  774. void __pascal far draw_image_transp_vga(image_type far *image,int xpos,int ypos) {
  775.         // stub
  776.         method_6_blit_img_to_scr(image, xpos, ypos, blitters_10h_transp);
  777. }
  778.  
  779. #ifdef USE_TEXT
  780.  
  781. font_type hc_font = {0x01,0xFF, 7,2,1,1, NULL};
  782. textstate_type textstate = {0,0,0,15,&hc_font};
  783.  
  784. /*const*/ byte hc_font_data[] = {
  785. 0x20,0x83,0x07,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0xD2,0x00,0xD8,0x00,0xE5,0x00,
  786. 0xEE,0x00,0xFA,0x00,0x07,0x01,0x14,0x01,0x21,0x01,0x2A,0x01,0x37,0x01,0x44,0x01,
  787. 0x50,0x01,0x5C,0x01,0x6A,0x01,0x74,0x01,0x81,0x01,0x8E,0x01,0x9B,0x01,0xA8,0x01,
  788. 0xB5,0x01,0xC2,0x01,0xCF,0x01,0xDC,0x01,0xE9,0x01,0xF6,0x01,0x03,0x02,0x10,0x02,
  789. 0x1C,0x02,0x2A,0x02,0x37,0x02,0x42,0x02,0x4F,0x02,0x5C,0x02,0x69,0x02,0x76,0x02,
  790. 0x83,0x02,0x90,0x02,0x9D,0x02,0xAA,0x02,0xB7,0x02,0xC4,0x02,0xD1,0x02,0xDE,0x02,
  791. 0xEB,0x02,0xF8,0x02,0x05,0x03,0x12,0x03,0x1F,0x03,0x2C,0x03,0x39,0x03,0x46,0x03,
  792. 0x53,0x03,0x60,0x03,0x6D,0x03,0x7A,0x03,0x87,0x03,0x94,0x03,0xA1,0x03,0xAE,0x03,
  793. 0xBB,0x03,0xC8,0x03,0xD5,0x03,0xE2,0x03,0xEB,0x03,0xF9,0x03,0x02,0x04,0x0F,0x04,
  794. 0x1C,0x04,0x29,0x04,0x36,0x04,0x43,0x04,0x50,0x04,0x5F,0x04,0x6C,0x04,0x79,0x04,
  795. 0x88,0x04,0x95,0x04,0xA2,0x04,0xAF,0x04,0xBC,0x04,0xC9,0x04,0xD8,0x04,0xE7,0x04,
  796. 0xF4,0x04,0x01,0x05,0x0E,0x05,0x1B,0x05,0x28,0x05,0x35,0x05,0x42,0x05,0x51,0x05,
  797. 0x5E,0x05,0x6B,0x05,0x78,0x05,0x85,0x05,0x8D,0x05,0x9A,0x05,0xA7,0x05,0xBB,0x05,
  798. 0xD9,0x05,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x02,0x00,0x01,0x00,0xC0,0xC0,
  799. 0xC0,0xC0,0xC0,0x00,0xC0,0x03,0x00,0x05,0x00,0x01,0x00,0xD8,0xD8,0xD8,0x06,0x00,
  800. 0x07,0x00,0x01,0x00,0x00,0x6C,0xFE,0x6C,0xFE,0x6C,0x07,0x00,0x07,0x00,0x01,0x00,
  801. 0x10,0x7C,0xD0,0x7C,0x16,0x7C,0x10,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xC6,0x0C,
  802. 0x18,0x30,0x63,0xC3,0x07,0x00,0x08,0x00,0x01,0x00,0x38,0x6C,0x38,0x7A,0xCC,0xCE,
  803. 0x7B,0x03,0x00,0x03,0x00,0x01,0x00,0x60,0x60,0xC0,0x07,0x00,0x04,0x00,0x01,0x00,
  804. 0x30,0x60,0xC0,0xC0,0xC0,0x60,0x30,0x07,0x00,0x04,0x00,0x01,0x00,0xC0,0x60,0x30,
  805. 0x30,0x30,0x60,0xC0,0x06,0x00,0x07,0x00,0x01,0x00,0x00,0x6C,0x38,0xFE,0x38,0x6C,
  806. 0x06,0x00,0x06,0x00,0x01,0x00,0x00,0x30,0x30,0xFC,0x30,0x30,0x08,0x00,0x03,0x00,
  807. 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x04,0x00,0x04,0x00,0x01,0x00,
  808. 0x00,0x00,0x00,0xF0,0x07,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,
  809. 0xC0,0x07,0x00,0x08,0x00,0x01,0x00,0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x07,0x00,
  810. 0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,
  811. 0x00,0x30,0x70,0xF0,0x30,0x30,0x30,0xFC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,
  812. 0x0C,0x18,0x30,0x60,0xFC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0x0C,0x18,0x0C,
  813. 0xCC,0x78,0x07,0x00,0x07,0x00,0x01,0x00,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x07,
  814. 0x00,0x06,0x00,0x01,0x00,0xF8,0xC0,0xC0,0xF8,0x0C,0x0C,0xF8,0x07,0x00,0x06,0x00,
  815. 0x01,0x00,0x78,0xC0,0xC0,0xF8,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0xFC,
  816. 0x0C,0x18,0x30,0x30,0x30,0x30,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0x78,
  817. 0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,
  818. 0x06,0x00,0x02,0x00,0x01,0x00,0x00,0xC0,0xC0,0x00,0xC0,0xC0,0x08,0x00,0x03,0x00,
  819. 0x01,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0xC0,0x07,0x00,0x05,0x00,0x01,0x00,
  820. 0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x05,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0xF0,
  821. 0x00,0xF0,0x07,0x00,0x05,0x00,0x01,0x00,0xC0,0x60,0x30,0x18,0x30,0x60,0xC0,0x07,
  822. 0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0x0C,0x18,0x30,0x00,0x30,0x07,0x00,0x06,0x00,
  823. 0x01,0x00,0x78,0xCC,0xDC,0xDC,0xD8,0xC0,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x78,
  824. 0xCC,0xCC,0xFC,0xCC,0xCC,0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xF8,
  825. 0xCC,0xCC,0xF8,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,0xC0,0xC0,0xCC,0x78,
  826. 0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0xCC,0xF8,0x07,0x00,0x05,
  827. 0x00,0x01,0x00,0xF8,0xC0,0xC0,0xF0,0xC0,0xC0,0xF8,0x07,0x00,0x05,0x00,0x01,0x00,
  828. 0xF8,0xC0,0xC0,0xF0,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,
  829. 0xDC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xCC,0xFC,0xCC,0xCC,
  830. 0xCC,0x07,0x00,0x04,0x00,0x01,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0xF0,0x07,0x00,
  831. 0x06,0x00,0x01,0x00,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0x78,0x07,0x00,0x07,0x00,0x01,
  832. 0x00,0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x07,0x00,0x05,0x00,0x01,0x00,0xC0,0xC0,
  833. 0xC0,0xC0,0xC0,0xC0,0xF8,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xE7,0xFF,0xDB,0xC3,
  834. 0xC3,0xC3,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xEC,0xFC,0xDC,0xCC,0xCC,0x07,
  835. 0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,
  836. 0x01,0x00,0xF8,0xCC,0xCC,0xF8,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x78,
  837. 0xCC,0xCC,0xCC,0xCC,0xD8,0x6C,0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xF8,
  838. 0xD8,0xCC,0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,0x78,0x0C,0xCC,0x78,
  839. 0x07,0x00,0x06,0x00,0x01,0x00,0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x07,0x00,0x06,
  840. 0x00,0x01,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,0x00,
  841. 0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xC3,0xC3,
  842. 0xDB,0xFF,0xE7,0xC3,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0x78,0x30,0x78,0xCC,
  843. 0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x07,0x00,
  844. 0x08,0x00,0x01,0x00,0xFF,0x06,0x0C,0x18,0x30,0x60,0xFF,0x07,0x00,0x04,0x00,0x01,
  845. 0x00,0xF0,0xC0,0xC0,0xC0,0xC0,0xC0,0xF0,0x07,0x00,0x08,0x00,0x01,0x00,0xC0,0x60,
  846. 0x30,0x18,0x0C,0x06,0x03,0x07,0x00,0x04,0x00,0x01,0x00,0xF0,0x30,0x30,0x30,0x30,
  847. 0x30,0xF0,0x03,0x00,0x06,0x00,0x01,0x00,0x30,0x78,0xCC,0x08,0x00,0x06,0x00,0x01,
  848. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x04,0x00,0x01,0x00,0xC0,
  849. 0x60,0x30,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0x7C,0x07,
  850. 0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xF8,0x07,0x00,0x06,0x00,
  851. 0x01,0x00,0x00,0x00,0x78,0xCC,0xC0,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x0C,
  852. 0x0C,0x7C,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xCC,
  853. 0xFC,0xC0,0x7C,0x07,0x00,0x05,0x00,0x01,0x00,0x38,0x60,0xF8,0x60,0x60,0x60,0x60,
  854. 0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x7C,0x0C,0x78,0x07,
  855. 0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xCC,0x07,0x00,0x02,0x00,
  856. 0x01,0x00,0xC0,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0x09,0x00,0x04,0x00,0x01,0x00,0x30,
  857. 0x00,0x30,0x30,0x30,0x30,0x30,0x30,0xE0,0x07,0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,
  858. 0xCC,0xD8,0xF0,0xD8,0xCC,0x07,0x00,0x02,0x00,0x01,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,
  859. 0xC0,0xC0,0x07,0x00,0x08,0x00,0x01,0x00,0x00,0x00,0xFE,0xDB,0xDB,0xDB,0xDB,0x07,
  860. 0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0x07,0x00,0x06,0x00,
  861. 0x01,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x78,0x09,0x00,0x06,0x00,0x01,0x00,0x00,
  862. 0x00,0xF8,0xCC,0xCC,0xCC,0xF8,0xC0,0xC0,0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,
  863. 0x78,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,
  864. 0xCC,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xC0,0x78,0x0C,
  865. 0xF8,0x07,0x00,0x05,0x00,0x01,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x38,0x07,0x00,
  866. 0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,
  867. 0x00,0x00,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x07,0x00,0x08,0x00,0x01,0x00,0x00,0x00,
  868. 0xC3,0xC3,0xDB,0xFF,0x66,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0x78,0x30,
  869. 0x78,0xCC,0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,
  870. 0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xFC,0x18,0x30,0x60,0xFC,0x07,0x00,
  871. 0x04,0x00,0x01,0x00,0x30,0x60,0x60,0xC0,0x60,0x60,0x30,0x07,0x00,0x02,0x00,0x01,
  872. 0x00,0xC0,0xC0,0xC0,0x00,0xC0,0xC0,0xC0,0x07,0x00,0x04,0x00,0x01,0x00,0xC0,0x60,
  873. 0x60,0x30,0x60,0x60,0xC0,0x02,0x00,0x07,0x00,0x01,0x00,0x76,0xDC,0x07,0x00,0x07,
  874. 0x00,0x01,0x00,0x00,0x00,0x70,0xC4,0xCC,0x8C,0x38,0x07,0x00,0x07,0x00,0x01,0x00,
  875. 0x00,0x06,0x0C,0xD8,0xF0,0xE0,0xC0,0x08,0x00,0x10,0x00,0x02,0x00,0x7F,0xFE,0xCD,
  876. 0xC7,0xB5,0xEF,0xB5,0xEF,0x85,0xEF,0xB5,0xEF,0xB4,0x6F,0x08,0x00,0x13,0x00,0x03,
  877. 0x00,0x7F,0xFF,0xC0,0xCC,0x46,0xE0,0xB6,0xDA,0xE0,0xBE,0xDA,0xE0,0xBE,0xC6,0xE0,
  878. 0xB6,0xDA,0xE0,0xCE,0xDA,0x20,0x7F,0xFF,0xC0,0x08,0x00,0x11,0x00,0x03,0x00,0x7F,
  879. 0xFF,0x00,0xC6,0x73,0x80,0xDD,0xAD,0x80,0xCE,0xEF,0x80,0xDF,0x6F,0x80,0xDD,0xAD,
  880. 0x80,0xC6,0x73,0x80,0x7F,0xFF,0x00
  881. };
  882.  
  883. font_type load_font_from_data(/*const*/ rawfont_type* data) {
  884.         font_type font;
  885.         font.first_char = data->first_char;
  886.         font.last_char = data->last_char;
  887.         font.height_above_baseline = data->height_above_baseline;
  888.         font.height_below_baseline = data->height_below_baseline;
  889.         font.space_between_lines = data->space_between_lines;
  890.         font.space_between_chars = data->space_between_chars;
  891.         int n_chars = font.last_char - font.first_char + 1;
  892.         chtab_type* chtab = malloc(sizeof(chtab_type) + sizeof(image_type* far) * n_chars);
  893.         int chr,index;
  894.         // Make a dummy palette for decode_image().
  895.         dat_pal_type dat_pal;
  896.         memset(&dat_pal, 0, sizeof(dat_pal));
  897.         dat_pal.vga[1].r = dat_pal.vga[1].g = dat_pal.vga[1].b = 0x3F; // white
  898.         for (index = 0, chr = data->first_char; chr <= data->last_char; ++index, ++chr) {
  899.                 /*const*/ image_data_type* image_data = (/*const*/ image_data_type*)((/*const*/ byte*)data + data->offsets[index]);
  900.                 //image_data->flags=0;
  901.                 if (image_data->height == 0) image_data->height = 1; // HACK: decode_image() returns NULL if height==0.
  902.                 image_type* image;
  903.                 chtab->images[index] = image = decode_image(image_data, &dat_pal);
  904.                 if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
  905.                         sdlperror("SDL_SetColorKey");
  906.                         quit(1);
  907.                 }
  908.         }
  909.         font.chtab = chtab;
  910.         return font;
  911. }
  912.  
  913. void load_font() {
  914.         // Try to load font from a file.
  915.         dat_type* dathandle = open_dat("font", 0);
  916.         hc_font.chtab = load_sprites_from_file(1000, 1<<1, 0);
  917.         close_dat(dathandle);
  918.         if (hc_font.chtab == NULL) {
  919.                 // Use built-in font.
  920.                 hc_font = load_font_from_data((/*const*/ rawfont_type*)hc_font_data);
  921.         }
  922. }
  923.  
  924. // seg009:35C5
  925. int __pascal far get_char_width(byte character) {
  926.         font_type* font = textstate.ptr_font;
  927.         int width = 0;
  928.         if (character <= font->last_char && character >= font->first_char) {
  929.                 image_type* image = font->chtab->images[character - font->first_char];
  930.                 if (image != NULL) {
  931.                         width += image->w; //char_ptrs[character - font->first_char]->width;
  932.                         if (width) width += font->space_between_chars;
  933.                 }
  934.         }
  935.         return width;
  936. }
  937.  
  938. // seg009:3E99
  939. int __pascal far find_linebreak(const char far *text,int length,int break_width,int x_align) {
  940.         short curr_line_width; // in pixels
  941.         short last_break_pos; // in characters
  942.         int curr_char_pos = 0;
  943.         last_break_pos = 0;
  944.         curr_line_width = 0;
  945.         const char* text_pos = text;
  946.         while (curr_char_pos < length) {
  947.                 curr_line_width += get_char_width(*text_pos);
  948.                 if (curr_line_width <= break_width) {
  949.                         ++curr_char_pos;
  950.                         char curr_char = *(text_pos++);
  951.                         if (curr_char == '\n') {
  952.                                 return curr_char_pos;
  953.                         }
  954.                         if (curr_char == '-' ||
  955.                                 (x_align <= 0 && (curr_char == ' ' || *text_pos == ' ')) ||
  956.                                 (*text_pos == ' ' && curr_char == ' ')
  957.                         ) {
  958.                                 // May break here.
  959.                                 last_break_pos = curr_char_pos;
  960.                         }
  961.                 } else {
  962.                         if (last_break_pos == 0) {
  963.                                 // If the first word is wider than break_width then break it.
  964.                                 return curr_char_pos;
  965.                         } else {
  966.                                 // Otherwise break at the last space.
  967.                                 return last_break_pos;
  968.                         }
  969.                 }
  970.         }
  971.         return curr_char_pos;
  972. }
  973.  
  974. // seg009:403F
  975. int __pascal far get_line_width(const char far *text,int length) {
  976.         int width = 0;
  977.         const char* text_pos = text;
  978.         while (--length >= 0) {
  979.                 width += get_char_width(*(text_pos++));
  980.         }
  981.         return width;
  982. }
  983.  
  984. // seg009:3706
  985. int __pascal far draw_text_character(byte character) {
  986.         //printf("going to do draw_text_character...\n");
  987.         font_type* font = textstate.ptr_font;
  988.         int width = 0;
  989.         if (character <= font->last_char && character >= font->first_char) {
  990.                 image_type* image = font->chtab->images[character - font->first_char]; //char_ptrs[character - font->first_char];
  991.                 if (image != NULL) {
  992.                         method_3_blit_mono(image, textstate.current_x, textstate.current_y - font->height_above_baseline, textstate.textblit, textstate.textcolor);
  993.                         width = font->space_between_chars + image->w;
  994.                 }
  995.         }
  996.         textstate.current_x += width;
  997.         return width;
  998. }
  999.  
  1000. // seg009:377F
  1001. int __pascal far draw_text_line(const char far *text,int length) {
  1002.         //hide_cursor();
  1003.         int width = 0;
  1004.         const char* text_pos = text;
  1005.         while (--length >= 0) {
  1006.                 width += draw_text_character(*(text_pos++));
  1007.         }
  1008.         //show_cursor();
  1009.         return width;
  1010. }
  1011.  
  1012. // seg009:3755
  1013. int __pascal far draw_cstring(const char far *string) {
  1014.         //hide_cursor();
  1015.         int width = 0;
  1016.         const char* text_pos = string;
  1017.         while (*text_pos) {
  1018.                 width += draw_text_character(*(text_pos++));
  1019.         }
  1020.         //show_cursor();
  1021.         request_screen_update();
  1022.         return width;
  1023. }
  1024.  
  1025. // seg009:3F01
  1026. const rect_type far *__pascal draw_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text,int length) {
  1027.         //printf("going to do draw_text()...\n");
  1028.         short rect_top;
  1029.         short rect_height;
  1030.         short rect_width;
  1031.         //textinfo_type var_C;
  1032.         short num_lines;
  1033.         short font_line_distance;
  1034.         //hide_cursor();
  1035.         //get_textinfo(&var_C);
  1036.         set_clip_rect(rect_ptr);
  1037.         rect_width = rect_ptr->right - rect_ptr->left;
  1038.         rect_top = rect_ptr->top;
  1039.         rect_height = rect_ptr->bottom - rect_ptr->top;
  1040.         num_lines = 0;
  1041.         int rem_length = length;
  1042.         const char* line_start = text;
  1043.         #define MAX_LINES 100
  1044.         const char* line_starts[MAX_LINES];
  1045.         int line_lengths[MAX_LINES];
  1046.         do {
  1047.                 int line_length = find_linebreak(line_start, rem_length, rect_width, x_align);
  1048.                 if (line_length == 0) break;
  1049.                 if (num_lines >= MAX_LINES) {
  1050.                         //... ERROR!
  1051.                         printf("draw_text(): Too many lines!\n");
  1052.                         quit(1);
  1053.                 }
  1054.                 line_starts[num_lines] = line_start;
  1055.                 line_lengths[num_lines] = line_length;
  1056.                 ++num_lines;
  1057.                 line_start += line_length;
  1058.                 rem_length -= line_length;
  1059.         } while(rem_length);
  1060.         font_type* font = textstate.ptr_font;
  1061.         font_line_distance = font->height_above_baseline + font->height_below_baseline + font->space_between_lines;
  1062.         int text_height = font_line_distance * num_lines - font->space_between_lines;
  1063.         int text_top = rect_top;
  1064.         if (y_align >= 0) {
  1065.                 if (y_align <= 0) {
  1066.                         // middle
  1067.                         // The +1 is for simulating SHR + ADC/SBB.
  1068.                         text_top += (rect_height+1)/2 - (text_height+1)/2;
  1069.                 } else {
  1070.                         // bottom
  1071.                         text_top += rect_height - text_height;
  1072.                 }
  1073.         }
  1074.         textstate.current_y = text_top + font->height_above_baseline;
  1075.         int i;
  1076.         for (i = 0; i < num_lines; ++i) {
  1077.                 const char* line_pos = line_starts[i];
  1078.                 int line_length = line_lengths[i];
  1079.                 if (x_align < 0 &&
  1080.                         *line_pos == ' ' &&
  1081.                         i != 0 &&
  1082.                         *(line_pos-1) != '\n'
  1083.                 ) {
  1084.                         // Skip over space if it's not at the beginning of a line.
  1085.                         ++line_pos;
  1086.                         --line_length;
  1087.                         if (line_length != 0 &&
  1088.                                 *line_pos == ' ' &&
  1089.                                 *(line_pos-2) == '.'
  1090.                         ) {
  1091.                                 // Skip over second space after point.
  1092.                                 ++line_pos;
  1093.                                 --line_length;
  1094.                         }
  1095.                 }
  1096.                 int line_width = get_line_width(line_pos,line_length);
  1097.                 int text_left = rect_ptr->left;
  1098.                 if (x_align >= 0) {
  1099.                         if (x_align <= 0) {
  1100.                                 // center
  1101.                                 text_left += rect_width/2 - line_width/2;
  1102.                         } else {
  1103.                                 // right
  1104.                                 text_left += rect_width - line_width;
  1105.                         }
  1106.                 }
  1107.                 textstate.current_x = text_left;
  1108.                 //printf("going to draw text line...\n");
  1109.                 draw_text_line(line_pos,line_length);
  1110.                 textstate.current_y += font_line_distance;
  1111.         }
  1112.         reset_clip_rect();
  1113.         //set_textinfo(...);
  1114.         //show_cursor();
  1115.         return rect_ptr;
  1116. }
  1117.  
  1118. // seg009:3E4F
  1119. void __pascal far show_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text) {
  1120.         // stub
  1121.         //printf("show_text: %s\n",text);
  1122.         int temp = screen_updates_suspended;
  1123.         screen_updates_suspended = 1;
  1124.         draw_text(rect_ptr, x_align, y_align, text, strlen(text));
  1125.         screen_updates_suspended = temp;
  1126.         request_screen_update();
  1127. }
  1128.  
  1129. // seg009:04FF
  1130. void __pascal far show_text_with_color(const rect_type far *rect_ptr,int x_align,int y_align, const char far *text,int color) {
  1131.         short saved_textcolor;
  1132.         saved_textcolor = textstate.textcolor;
  1133.         textstate.textcolor = color;
  1134.         show_text(rect_ptr, x_align, y_align, text);
  1135.         textstate.textcolor = saved_textcolor;
  1136. }
  1137.  
  1138. // seg009:3A91
  1139. void __pascal far set_curr_pos(int xpos,int ypos) {
  1140.         textstate.current_x = xpos;
  1141.         textstate.current_y = ypos;
  1142. }
  1143.  
  1144. // seg009:145A
  1145. void __pascal far init_copyprot_dialog() {
  1146.         copyprot_dialog = make_dialog_info(&dialog_settings, &dialog_rect_1, &dialog_rect_1, NULL);
  1147.         copyprot_dialog->peel = read_peel_from_screen(&copyprot_dialog->peel_rect);
  1148. }
  1149.  
  1150. // seg009:0838
  1151. int __pascal far showmessage(char far *text,int arg_4,void far *arg_0) {
  1152.         word key;
  1153.         rect_type rect;
  1154.         //font_type* saved_font_ptr;
  1155.         //surface_type* old_target;
  1156.         //old_target = current_target_surface;
  1157.         //current_target_surface = onscreen_surface_;
  1158.         // In the disassembly there is some messing with the current_target_surface and font (?)
  1159.         // However, this does not seem to be strictly necessary
  1160.         method_1_blit_rect(offscreen_surface, onscreen_surface_, &copyprot_dialog->peel_rect, &copyprot_dialog->peel_rect, 0);
  1161.         draw_dialog_frame(copyprot_dialog);
  1162.         //saved_font_ptr = textstate.ptr_font;
  1163.         //saved_font_ptr = current_target_surface->ptr_font;
  1164.         //current_target_surface->ptr_font = ptr_font;
  1165.         shrink2_rect(&rect, &copyprot_dialog->text_rect, 2, 1);
  1166.         show_text_with_color(&rect, 0, 0, text, color_15_brightwhite);
  1167.         screen_updates_suspended = 0;
  1168.         request_screen_update();
  1169.         //textstate.ptr_font = saved_font_ptr;
  1170.         //current_target_surface->ptr_font = saved_font_ptr;
  1171.         clear_kbd_buf();
  1172.         do {
  1173.                 idle();
  1174.                 key = key_test_quit(); // Press any key to continue...
  1175.         } while(key == 0);
  1176.         //restore_dialog_peel_2(copyprot_dialog->peel);
  1177.         //current_target_surface = old_target;
  1178.         need_full_redraw = 1; // lazy: instead of neatly restoring only the relevant part, just redraw the whole screen
  1179.         return key;
  1180. }
  1181.  
  1182. // seg009:08FB
  1183. dialog_type * __pascal far make_dialog_info(dialog_settings_type *settings, rect_type *dialog_rect,
  1184.                                             rect_type *text_rect, peel_type *dialog_peel) {
  1185.         dialog_type* dialog_info;
  1186.         dialog_info = malloc_near(sizeof(dialog_type));
  1187.         dialog_info->settings = settings;
  1188.         dialog_info->has_peel = 0;
  1189.         dialog_info->peel = dialog_peel;
  1190.         if (text_rect != NULL)
  1191.                 dialog_info->text_rect = *text_rect;
  1192.         calc_dialog_peel_rect(dialog_info);
  1193.         if (text_rect != NULL) {        // does not seem to be quite right; see seg009:0948 (?)
  1194.                 read_dialog_peel(dialog_info);
  1195.         }
  1196.         return dialog_info;
  1197. }
  1198.  
  1199. // seg009:0BE7
  1200. void __pascal far calc_dialog_peel_rect(dialog_type*dialog) {
  1201.         dialog_settings_type* settings;
  1202.         settings = dialog->settings;
  1203.         dialog->peel_rect.left = dialog->text_rect.left - settings->left_border;
  1204.         dialog->peel_rect.top = dialog->text_rect.top - settings->top_border;
  1205.         dialog->peel_rect.right = dialog->text_rect.right + settings->right_border + settings->shadow_right;
  1206.         dialog->peel_rect.bottom = dialog->text_rect.bottom + settings->bottom_border + settings->shadow_bottom;
  1207. }
  1208.  
  1209. // seg009:0BB0
  1210. void __pascal far read_dialog_peel(dialog_type *dialog) {
  1211.         if (dialog->has_peel) {
  1212.                 if (dialog->peel == NULL) {
  1213.                         dialog->peel = read_peel_from_screen(&dialog->peel_rect);
  1214.                 }
  1215.                 dialog->has_peel = 1;
  1216.                 draw_dialog_frame(dialog);
  1217.         }
  1218. }
  1219.  
  1220. // seg009:09DE
  1221. void __pascal far draw_dialog_frame(dialog_type *dialog) {
  1222.         dialog->settings->method_2_frame(dialog);
  1223. }
  1224.  
  1225. // A pointer to this function is the first field of dialog_settings (data:2944)
  1226. // Perhaps used when replacing a dialog's text with another text (?)
  1227. // seg009:096F
  1228. void __pascal far add_dialog_rect(dialog_type *dialog) {
  1229.         draw_rect(&dialog->text_rect, color_0_black);
  1230. }
  1231.  
  1232. // seg009:09F0
  1233. void __pascal far dialog_method_2_frame(dialog_type *dialog) {
  1234.         rect_type rect;
  1235.         short shadow_right = dialog->settings->shadow_right;
  1236.         short shadow_bottom = dialog->settings->shadow_bottom;
  1237.         short bottom_border = dialog->settings->bottom_border;
  1238.         short outer_border = dialog->settings->outer_border;
  1239.         short peel_top = dialog->peel_rect.top;
  1240.         short peel_left = dialog->peel_rect.left;
  1241.         short peel_bottom = dialog->peel_rect.bottom;
  1242.         short peel_right = dialog->peel_rect.right;
  1243.         short text_top = dialog->text_rect.top;
  1244.         short text_left = dialog->text_rect.left;
  1245.         short text_bottom = dialog->text_rect.bottom;
  1246.         short text_right = dialog->text_rect.right;
  1247.         // Draw outer border
  1248.         rect = (rect_type) { peel_top, peel_left, peel_bottom - shadow_bottom, peel_right - shadow_right };
  1249.         draw_rect(&rect, color_0_black);
  1250.         // Draw shadow (right)
  1251.         rect = (rect_type) { text_top, peel_right - shadow_right, peel_bottom, peel_right };
  1252.         draw_rect(&rect, get_text_color(0, color_8_darkgray /*dialog's shadow*/, 0));
  1253.         // Draw shadow (bottom)
  1254.         rect = (rect_type) { peel_bottom - shadow_bottom, text_left, peel_bottom, peel_right };
  1255.         draw_rect(&rect, get_text_color(0, color_8_darkgray /*dialog's shadow*/, 0));
  1256.         // Draw inner border (left)
  1257.         rect = (rect_type) { peel_top + outer_border, peel_left + outer_border, text_bottom, text_left };
  1258.         draw_rect(&rect, color_15_brightwhite);
  1259.         // Draw inner border (top)
  1260.         rect = (rect_type) { peel_top + outer_border, text_left, text_top, text_right + dialog->settings->right_border - outer_border };
  1261.         draw_rect(&rect, color_15_brightwhite);
  1262.         // Draw inner border (right)
  1263.         rect.top = text_top;
  1264.         rect.left =  text_right;
  1265.         rect.bottom = text_bottom + bottom_border - outer_border;           // (rect.right stays the same)
  1266.         draw_rect(&rect, color_15_brightwhite);
  1267.         // Draw inner border (bottom)
  1268.         rect = (rect_type) { text_bottom, peel_left + outer_border, text_bottom + bottom_border - outer_border, text_right };
  1269.         draw_rect(&rect, color_15_brightwhite);
  1270. }
  1271.  
  1272. // seg009:0C44
  1273. void __pascal far show_dialog(const char *text) {
  1274.         char string[256];
  1275.         snprintf(string, sizeof(string), "%s\n\nPress any key to continue.", text);
  1276.         showmessage(string, 1, &key_test_quit);
  1277. }
  1278.  
  1279. // seg009:0791
  1280. int __pascal far get_text_center_y(const rect_type far *rect) {
  1281.         const font_type far* font;
  1282.         short empty_height; // height of empty space above+below the line of text
  1283.         font = &hc_font;//current_target_surface->ptr_font;
  1284.         empty_height = rect->bottom - font->height_above_baseline - font->height_below_baseline - rect->top;
  1285.         return ((empty_height - empty_height % 2) >> 1) + font->height_above_baseline + empty_height % 2 + rect->top;
  1286. }
  1287.  
  1288. // seg009:3E77
  1289. int __pascal far get_cstring_width(const char far *text) {
  1290.         int width = 0;
  1291.         const char* text_pos = text;
  1292.         char curr_char;
  1293.         while (0 != (curr_char = *(text_pos++))) {
  1294.                 width += get_char_width(curr_char);
  1295.         }
  1296.         return width;
  1297. }
  1298.  
  1299. // seg009:0767
  1300. void __pascal far draw_text_cursor(int xpos,int ypos,int color) {
  1301.         set_curr_pos(xpos, ypos);
  1302.         /*current_target_surface->*/textstate.textcolor = color;
  1303.         draw_text_character('_');
  1304.         //restore_curr_color();
  1305.         textstate.textcolor = 15;
  1306. }
  1307.  
  1308. // seg009:053C
  1309. int __pascal far input_str(const rect_type far *rect,char *buffer,int max_length,const char *initial,int has_initial,int arg_4,int color,int bgcolor) {
  1310.         short length;
  1311.         word key;
  1312.         short cursor_visible;
  1313.         short current_xpos;
  1314.         short ypos;
  1315.         short init_length;
  1316.         length = 0;
  1317.         cursor_visible = 0;
  1318.         draw_rect(rect, bgcolor);
  1319.         init_length = strlen(initial);
  1320.         if (has_initial) {
  1321.                 strcpy(buffer, initial);
  1322.                 length = init_length;
  1323.         }
  1324.         current_xpos = rect->left + arg_4;
  1325.         ypos = get_text_center_y(rect);
  1326.         set_curr_pos(current_xpos, ypos);
  1327.         /*current_target_surface->*/textstate.textcolor = color;
  1328.         draw_cstring(initial);
  1329.         //restore_curr_pos?();
  1330.         current_xpos += get_cstring_width(initial) + (init_length != 0) * arg_4;
  1331.         do {
  1332.                 key = 0;
  1333.                 do {
  1334.                         if (cursor_visible) {
  1335.                                 draw_text_cursor(current_xpos, ypos, color);
  1336.                         } else {
  1337.                                 draw_text_cursor(current_xpos, ypos, bgcolor);
  1338.                         }
  1339.                         cursor_visible = !cursor_visible;
  1340.                         start_timer(timer_0, 6);
  1341.                         if (key) {
  1342.                                 if (cursor_visible) {
  1343.                                         draw_text_cursor(current_xpos, ypos, color);
  1344.                                         cursor_visible = !cursor_visible;
  1345.                                 }
  1346.                                 if (key == SDL_SCANCODE_RETURN) { // enter
  1347.                                         buffer[length] = 0;
  1348.                                         return length;
  1349.                                 } else break;
  1350.                         }
  1351.                         request_screen_update();
  1352. //                      while (!timer_stopped[0] && (key = key_test_quit()) == 0) idle();
  1353.                         while (!has_timer_stopped(0) && (key = key_test_quit()) == 0) idle();
  1354.                 } while (1);
  1355.                 // Only use the printable ASCII chars (UTF-8 encoding)
  1356.                 char entered_char = last_text_input <= 0x7E ? last_text_input : 0;
  1357.                 clear_kbd_buf();
  1358.  
  1359.                 if (key == SDL_SCANCODE_ESCAPE) { // esc
  1360.                         draw_rect(rect, bgcolor);
  1361.                         buffer[0] = 0;
  1362.                         return -1;
  1363.                 }
  1364.                 if (length != 0 && (key == SDL_SCANCODE_BACKSPACE ||
  1365.                                 key == SDL_SCANCODE_DELETE)) { // backspace, delete
  1366.                         --length;
  1367.                         draw_text_cursor(current_xpos, ypos, bgcolor);
  1368.                         current_xpos -= get_char_width(buffer[length]);
  1369.                         set_curr_pos(current_xpos, ypos);
  1370.                         /*current_target_surface->*/textstate.textcolor = bgcolor;
  1371.                         draw_text_character(buffer[length]);
  1372.                         //restore_curr_pos?();
  1373.                         draw_text_cursor(current_xpos, ypos, color);
  1374.                 }
  1375.                 else if (entered_char >= 0x20 && entered_char <= 0x7E && length < max_length) {
  1376.                         // Would the new character make the cursor go past the right side of the rect?
  1377.                         if (get_char_width('_') + get_char_width(entered_char) + current_xpos < rect->right) {
  1378.                                 draw_text_cursor(current_xpos, ypos, bgcolor);
  1379.                                 set_curr_pos(current_xpos, ypos);
  1380.                                 /*current_target_surface->*/textstate.textcolor = color;
  1381.                                 current_xpos += draw_text_character(buffer[length++] = entered_char);
  1382.                         }
  1383.                 }
  1384.                 request_screen_update();
  1385.         } while(1);
  1386. }
  1387.  
  1388. #else // USE_TEXT
  1389.  
  1390. // seg009:3706
  1391. int __pascal far draw_text_character(byte character) {
  1392.         // stub
  1393.         printf("draw_text_character: %c\n",character);
  1394.         return 0;
  1395. }
  1396.  
  1397. // seg009:3E4F
  1398. void __pascal far show_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text) {
  1399.         // stub
  1400.         printf("show_text: %s\n",text);
  1401. }
  1402.  
  1403. // seg009:04FF
  1404. void __pascal far show_text_with_color(const rect_type far *rect_ptr,int x_align,int y_align,char far *text,int color) {
  1405.         //short saved_textcolor;
  1406.         //saved_textcolor = textstate.textcolor;
  1407.         //textstate.textcolor = color;
  1408.         show_text(rect_ptr, x_align, y_align, text);
  1409.         //textstate.textcolor = saved_textcolor;
  1410. }
  1411.  
  1412. // seg009:3A91
  1413. void __pascal far set_curr_pos(int xpos,int ypos) {
  1414.         // stub
  1415. }
  1416.  
  1417. // seg009:0C44
  1418. void __pascal far show_dialog(char *text) {
  1419.         // stub
  1420.         puts(text);
  1421. }
  1422.  
  1423. // seg009:053C
  1424. int __pascal far input_str(const rect_type far *rect,char *buffer,int max_length,const char *initial,int has_initial,int arg_4,int color,int bgcolor) {
  1425.         // stub
  1426.         strncpy(buffer, "dummy input text", max_length);
  1427.         return strlen(buffer);
  1428. }
  1429.  
  1430. #endif // USE_TEXT
  1431.  
  1432. // seg009:37E8
  1433. void __pascal far draw_rect(const rect_type far *rect,int color) {
  1434.         method_5_rect(rect, blitters_0_no_transp, color);
  1435. }
  1436.  
  1437. // seg009:3985
  1438. surface_type far *__pascal rect_sthg(surface_type *surface,const rect_type far *rect) {
  1439.         // stub
  1440.         return surface;
  1441. }
  1442.  
  1443. // seg009:39CE
  1444. rect_type far *__pascal shrink2_rect(rect_type far *target_rect,const rect_type far *source_rect,int delta_x,int delta_y) {
  1445.         target_rect->top    = source_rect->top    + delta_y;
  1446.         target_rect->left   = source_rect->left   + delta_x;
  1447.         target_rect->bottom = source_rect->bottom - delta_y;
  1448.         target_rect->right  = source_rect->right  - delta_x;
  1449.         return target_rect;
  1450. }
  1451.  
  1452. // seg009:3BBA
  1453. void __pascal far restore_peel(peel_type* peel_ptr) {
  1454.         //printf("restoring peel at (x=%d, y=%d)\n", peel_ptr.rect.left, peel_ptr.rect.top); // debug
  1455.         method_6_blit_img_to_scr(peel_ptr->peel, peel_ptr->rect.left, peel_ptr->rect.top, /*0x10*/0);
  1456.         free_peel(peel_ptr);
  1457.         //SDL_FreeSurface(peel_ptr.peel);
  1458. }
  1459.  
  1460. // seg009:3BE9
  1461. peel_type* __pascal far read_peel_from_screen(const rect_type far *rect) {
  1462.         // stub
  1463.         peel_type* result;
  1464.         result = calloc(1, sizeof(peel_type));
  1465.         //memset(&result, 0, sizeof(result));
  1466.         result->rect = *rect;
  1467. #ifndef USE_ALPHA
  1468.         SDL_Surface* peel_surface = SDL_CreateRGBSurface(0, rect->right - rect->left, rect->bottom - rect->top,
  1469.                                                          24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
  1470. #else
  1471.         SDL_Surface* peel_surface = SDL_CreateRGBSurface(0, rect->right - rect->left, rect->bottom - rect->top, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24);
  1472. #endif
  1473.         if (peel_surface == NULL) {
  1474.                 sdlperror("SDL_CreateRGBSurface");
  1475.                 quit(1);
  1476.         }
  1477.         result->peel = peel_surface;
  1478.         rect_type target_rect = {0, 0, rect->right - rect->left, rect->bottom - rect->top};
  1479.         method_1_blit_rect(result->peel, current_target_surface, &target_rect, rect, 0);
  1480.         return result;
  1481. }
  1482.  
  1483. // seg009:3D95
  1484. int __pascal far intersect_rect(rect_type far *output,const rect_type far *input1,const rect_type far *input2) {
  1485.         short left = MAX(input1->left, input2->left);
  1486.         short right = MIN(input1->right, input2->right);
  1487.         if (left < right) {
  1488.                 output->left = left;
  1489.                 output->right = right;
  1490.                 short top = MAX(input1->top, input2->top);
  1491.                 short bottom = MIN(input1->bottom, input2->bottom);
  1492.                 if (top < bottom) {
  1493.                         output->top = top;
  1494.                         output->bottom = bottom;
  1495.                         return 1;
  1496.                 }
  1497.         }
  1498.         memset(output, 0, sizeof(rect_type));
  1499.         return 0;
  1500. }
  1501.  
  1502. // seg009:4063
  1503. rect_type far * __pascal far union_rect(rect_type far *output,const rect_type far *input1,const rect_type far *input2) {
  1504.         short top = MIN(input1->top, input2->top);
  1505.         short left = MIN(input1->left, input2->left);
  1506.         short bottom = MAX(input1->bottom, input2->bottom);
  1507.         short right = MAX(input1->right, input2->right);
  1508.         output->top = top;
  1509.         output->left = left;
  1510.         output->bottom = bottom;
  1511.         output->right = right;
  1512.         return output;
  1513. }
  1514.  
  1515. enum userevents {
  1516.         userevent_SOUND,
  1517.         userevent_TIMER,
  1518. };
  1519.  
  1520. SDL_TimerID sound_timer = 0;
  1521. short speaker_playing = 0;
  1522. short digi_playing = 0;
  1523. short midi_playing = 0;
  1524.  
  1525. void __pascal far speaker_sound_stop() {
  1526.         // stub
  1527.         speaker_playing = 0;
  1528.         if (sound_timer != 0) {
  1529.                 if (!SDL_RemoveTimer(sound_timer)) {
  1530.                         sdlperror("SDL_RemoveTimer in speaker_sound_stop");
  1531.                         //quit(1);
  1532.                 }
  1533.                 sound_timer = 0;
  1534.         }
  1535. }
  1536.  
  1537. // The current buffer, holds the resampled sound data.
  1538. byte* digi_buffer = NULL;
  1539. // The current position in digi_buffer.
  1540. byte* digi_remaining_pos = NULL;
  1541. // The remaining length.
  1542. size_t digi_remaining_length = 0;
  1543.  
  1544. // The properties of the audio device.
  1545. SDL_AudioSpec* digi_audiospec = NULL;
  1546. // The desired samplerate. Everything will be resampled to this.
  1547. const int digi_samplerate = 44100;
  1548.  
  1549. void stop_digi() {
  1550. #ifndef USE_MIXER
  1551.         SDL_PauseAudio(1);
  1552.         if (!digi_playing) return;
  1553.         SDL_LockAudio();
  1554.         digi_playing = 0;
  1555.         /*
  1556. //      if (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) {
  1557.                 SDL_PauseAudio(1);
  1558.                 SDL_CloseAudio();
  1559. //      }
  1560.         if (digi_audiospec != NULL) {
  1561.                 free(digi_audiospec);
  1562.                 digi_audiospec = NULL;
  1563.         }
  1564.         */
  1565.         if (digi_buffer != NULL) {
  1566.                 free(digi_buffer);
  1567.                 digi_buffer = NULL;
  1568.         }
  1569.         digi_remaining_length = 0;
  1570.         digi_remaining_pos = NULL;
  1571.         SDL_UnlockAudio();
  1572. #else
  1573.         Mix_HaltChannel(-1);
  1574.         Mix_HaltMusic();
  1575.         digi_playing = 0;
  1576. #endif
  1577. }
  1578.  
  1579. // seg009:7214
  1580. void __pascal far stop_sounds() {
  1581.         // stub
  1582.         stop_digi();
  1583.         // stop_midi();
  1584.         speaker_sound_stop();
  1585. }
  1586.  
  1587. Uint32 speaker_callback(Uint32 interval, void *param) {
  1588.         SDL_Event event;
  1589.         memset(&event, 0, sizeof(event));
  1590.         event.type = SDL_USEREVENT;
  1591.         event.user.code = userevent_SOUND;
  1592.         event.user.data1 = param;
  1593.         if (!SDL_RemoveTimer(sound_timer)) {
  1594.                 sdlperror("SDL_RemoveTimer in speaker_callback");
  1595.                 //quit(1);
  1596.         }
  1597.         sound_timer = 0;
  1598.         speaker_playing = 0;
  1599.         // First remove the timer, then allow the other thread to continue.
  1600.         SDL_PushEvent(&event);
  1601.         return 0;
  1602. }
  1603.  
  1604. // seg009:7640
  1605. void __pascal far play_speaker_sound(sound_buffer_type far *buffer) {
  1606.         // stub
  1607.         //speaker_sound_stop();
  1608.         stop_sounds();
  1609.         int length = 0;
  1610.         int index;
  1611.         for (index = 0; buffer->speaker.notes[index].frequency != 0x12; ++index) {
  1612.                 length += buffer->speaker.notes[index].length;
  1613.         }
  1614.         int time_ms = length*1000 / buffer->speaker.tempo;
  1615.         //printf("length = %d ms\n", time_ms);
  1616.         sound_timer = SDL_AddTimer(time_ms, speaker_callback, NULL);
  1617.         if (sound_timer == 0) {
  1618.                 sdlperror("SDL_AddTimer");
  1619.                 quit(1);
  1620.         }
  1621.         speaker_playing = 1;
  1622. }
  1623.  
  1624. #ifndef USE_MIXER
  1625. void digi_callback(void *userdata, Uint8 *stream, int len) {
  1626.         // Don't go over the end of either the input or the output buffer.
  1627.         size_t copy_len = MIN(len, digi_remaining_length);
  1628.         //printf("digi_callback(): copy_len = %d\n", copy_len);
  1629.         //printf("digi_callback(): len = %d\n", len);
  1630.         if (is_sound_on) {
  1631.                 // Copy the next part of the input of the output.
  1632.                 memcpy(stream, digi_remaining_pos, copy_len);
  1633.                 // In case the sound does not fill the buffer: fill the rest of the buffer with silence.
  1634.                 memset(stream + copy_len, digi_audiospec->silence, len - copy_len);
  1635.         } else {
  1636.                 // If sound is off: Mute the sound but keep track of where we are.
  1637.                 memset(stream, digi_audiospec->silence, len);
  1638.         }
  1639.         // If the sound ended, push an event.
  1640.         if (digi_playing && digi_remaining_length == 0) {
  1641.                 //printf("digi_callback(): sound ended\n");
  1642.                 SDL_Event event;
  1643.                 memset(&event, 0, sizeof(event));
  1644.                 event.type = SDL_USEREVENT;
  1645.                 event.user.code = userevent_SOUND;
  1646.                 digi_playing = 0;
  1647.                 SDL_PushEvent(&event);
  1648.         }
  1649.         // Advance the pointer.
  1650.         digi_remaining_length -= copy_len;
  1651.         digi_remaining_pos += copy_len;
  1652. }
  1653. #endif
  1654.  
  1655. #ifdef USE_MIXER
  1656. void channel_finished(int channel) {
  1657.         digi_playing = 0;
  1658.         //printf("Finished channel %d\n", channel);
  1659.         SDL_Event event;
  1660.         memset(&event, 0, sizeof(event));
  1661.         event.type = SDL_USEREVENT;
  1662.         event.user.code = userevent_SOUND;
  1663.         SDL_PushEvent(&event);
  1664. }
  1665. void music_finished(void) {
  1666.         channel_finished(-1);
  1667. }
  1668. #endif
  1669.  
  1670. int digi_unavailable = 0;
  1671. void init_digi() {
  1672.         if (digi_unavailable) return;
  1673.         if (digi_audiospec != NULL) return;
  1674.         // Open the audio device. Called once.
  1675.         //fprintf(stderr, "init_digi(): called\n");
  1676.  
  1677.         SDL_AudioFormat desired_audioformat;
  1678.         SDL_version version;
  1679.         SDL_GetVersion(&version);
  1680.         //printf("SDL Version = %d.%d.%d\n", version.major, version.minor, version.patch);
  1681.         if (version.major <= 2 && version.minor <= 0 && version.patch <= 3) {
  1682.                 // In versions before 2.0.4, 16-bit audio samples don't work properly (the sound becomes garbled).
  1683.                 // See: https://bugzilla.libsdl.org/show_bug.cgi?id=2389
  1684.                 // Workaround: set the audio format to 8-bit, if we are linking against an older SDL2 version.
  1685.                 desired_audioformat = AUDIO_U8;
  1686.                 printf("Your SDL.dll is older than 2.0.4. Using 8-bit audio format to work around resampling bug.");
  1687.         } else {
  1688.                 desired_audioformat = AUDIO_S16SYS;
  1689.         }
  1690.  
  1691.         SDL_AudioSpec *desired;
  1692.         desired = (SDL_AudioSpec *)malloc(sizeof(SDL_AudioSpec));
  1693.         memset(desired, 0, sizeof(SDL_AudioSpec));
  1694.         desired->freq = digi_samplerate; //buffer->digi.sample_rate;
  1695.         desired->format = desired_audioformat;
  1696.         desired->channels = 2;
  1697.         desired->samples = 1024;
  1698. #ifndef USE_MIXER
  1699.         desired->callback = digi_callback;
  1700.         desired->userdata = NULL;
  1701.         if (SDL_OpenAudio(desired, NULL) != 0) {
  1702.                 sdlperror("SDL_OpenAudio");
  1703.                 //quit(1);
  1704.                 digi_unavailable = 1;
  1705.                 return;
  1706.         }
  1707.         //SDL_PauseAudio(0);
  1708. #else
  1709.         if (Mix_OpenAudio(desired->freq, desired->format, desired->channels, desired->samples) != 0) {
  1710.                 sdlperror("Mix_OpenAudio");
  1711.                 digi_unavailable = 1;
  1712.                 return;
  1713.         }
  1714.         Mix_AllocateChannels(1);
  1715.         Mix_ChannelFinished(channel_finished);
  1716.         Mix_HookMusicFinished(music_finished);
  1717. //printf ("init_digi(): SETTING SOUND AND MUSIC VOLUME\n");
  1718.    Mix_Volume (-1, is_sound_on ? mixer_volume : 0);
  1719.    Mix_VolumeMusic (is_sound_on ? mixer_volume : 0);
  1720. #endif
  1721.         digi_audiospec = desired;
  1722. }
  1723.  
  1724. #ifdef USE_MIXER
  1725. const int sound_channel = 0;
  1726. const int max_sound_id = 58;
  1727. char** sound_names = NULL;
  1728.  
  1729. void load_sound_names() {
  1730.         const char* names_path = locate_file("data/music/names.txt");
  1731.         if (sound_names != NULL) return;
  1732.         FILE* fp = fopen(names_path,"rt");
  1733.         if (fp==NULL) return;
  1734.         sound_names = (char**) calloc(sizeof(char*) * max_sound_id, 1);
  1735.         while (!feof(fp)) {
  1736.                 int index;
  1737.                 char name[POP_MAX_PATH];
  1738.                 if (fscanf(fp, "%d=%255s\n", &index, /*sizeof(name)-1,*/ name) != 2) {
  1739.                         perror(names_path);
  1740.                         continue;
  1741.                 }
  1742.                 //if (feof(fp)) break;
  1743.                 //printf("sound_names[%d] = %s\n",index,name);
  1744.                 if (index >= 0 && index < max_sound_id) {
  1745.                         sound_names[index] = strdup(name);
  1746.                 }
  1747.         }
  1748.         fclose(fp);
  1749. }
  1750.  
  1751. char* sound_name(int index) {
  1752.         if (sound_names != NULL && index >= 0 && index < max_sound_id) {
  1753.                 return sound_names[index];
  1754.         } else {
  1755.                 return NULL;
  1756.         }
  1757. }
  1758.  
  1759. void convert_digi_sound(sound_buffer_type *buffer);
  1760. #endif
  1761.  
  1762. sound_buffer_type* load_sound(int index) {
  1763.         sound_buffer_type* result = NULL;
  1764. #ifdef USE_MIXER
  1765.         //printf("load_sound(%d)\n", index);
  1766.         init_digi();
  1767.         if (!digi_unavailable && result == NULL && index >= 0 && index < max_sound_id) {
  1768.                 //printf("Trying to load from music folder\n");
  1769.  
  1770.                 //load_sound_names();  // Moved to load_sounds()
  1771.                 if (sound_names != NULL && sound_name(index) != NULL) {
  1772.                         //printf("Loading from music folder\n");
  1773.                         const char* exts[]={"ogg","mp3","flac","wav"};
  1774.                         int i;
  1775.                         for (i = 0; i < COUNT(exts); ++i) {
  1776.                                 char filename[POP_MAX_PATH];
  1777.                                 const char* ext=exts[i];
  1778.  
  1779.                                 snprintf(filename, sizeof(filename), "data/music/%s.%s", sound_name(index), ext);
  1780.                 const char* located_filename = locate_file(filename);
  1781.                                 // Skip nonexistent files:
  1782.                                 if (!file_exists(located_filename))
  1783.                                         continue;
  1784.                                 //printf("Trying to load %s\n", filename);
  1785.                                 Mix_Music* music = Mix_LoadMUS(located_filename);
  1786.                                 if (music == NULL) {
  1787.                                         sdlperror(located_filename);
  1788.                                         //sdlperror("Mix_LoadWAV");
  1789.                                         continue;
  1790.                                 }
  1791.                                 //printf("Loaded sound from %s\n", filename);
  1792.                                 result = malloc(sizeof(sound_buffer_type));
  1793.                                 result->type = sound_music;
  1794.                                 result->music = music;
  1795.                                 break;
  1796.                         }
  1797.                 } else {
  1798.                         //printf("sound_names = %p\n", sound_names);
  1799.                         //printf("sound_names[%d] = %p\n", index, sound_name(index));
  1800.                 }
  1801.         }
  1802. #endif
  1803.         if (result == NULL) {
  1804.                 //printf("Trying to load from DAT\n");
  1805.                 result = (sound_buffer_type*) load_from_opendats_alloc(index + 10000, "bin", NULL, NULL);
  1806.         }
  1807. #ifdef USE_MIXER
  1808.         if (result == NULL) {
  1809.                 fprintf(stderr, "Failed to load sound %d '%s'\n", index, sound_name(index));
  1810.         } else {
  1811.                 // Convert waves to mixer chunks in advance.
  1812.                 if ((result->type & 7) == sound_digi) {
  1813.                         convert_digi_sound(result);
  1814.                 }
  1815.         }
  1816. #endif
  1817.         return result;
  1818. }
  1819.  
  1820. #ifdef USE_MIXER
  1821. void __pascal far play_chunk_sound(sound_buffer_type far *buffer) {
  1822.         //if (!is_sound_on) return;
  1823.         init_digi();
  1824.         if (digi_unavailable) return;
  1825.         stop_sounds();
  1826.         //printf("playing chunk sound %p\n", buffer);
  1827. //printf ("play_chunk_sound(): SETTING VOLUME\n");
  1828. //   Mix_Volume (sound_channel, is_sound_on ? mixer_volume : 0);
  1829.    if (Mix_PlayChannel(sound_channel, buffer->chunk, 0) == -1) {
  1830.                 sdlperror("Mix_PlayChannel");
  1831.         }
  1832.         digi_playing = 1;
  1833. }
  1834.  
  1835. void __pascal far play_music_sound(sound_buffer_type far *buffer) {
  1836.         init_digi();
  1837.         if (digi_unavailable) return;
  1838.         stop_sounds();
  1839. //printf ("play_music_sound(): SETTING MUSIC VOLUME\n");
  1840. //   Mix_VolumeMusic (is_sound_on ? mixer_volume : 0);
  1841.    if (Mix_PlayMusic(buffer->music, 0) == -1) {
  1842.                 sdlperror("Mix_PlayMusic");
  1843.         }
  1844.         digi_playing = 1;
  1845. }
  1846.  
  1847. Uint32 fourcc(char* string) {
  1848.         return *(Uint32*)string;
  1849. }
  1850. #endif
  1851.  
  1852. int wave_version = -1;
  1853.  
  1854. typedef struct waveinfo_type {
  1855.         int sample_rate, sample_size, sample_count;
  1856.         byte* samples;
  1857. } waveinfo_type;
  1858.  
  1859. bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo);
  1860.  
  1861. bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo) {
  1862.         int version = wave_version;
  1863.         if (version == -1) {
  1864.                 // Determine the version of the wave data.
  1865.                 version = 0;
  1866.                 if (buffer->digi.sample_size == 8) version += 1;
  1867.                 if (buffer->digi_new.sample_size == 8) version += 2;
  1868.                 if (version == 1 || version == 2) wave_version = version;
  1869.         }
  1870.  
  1871.         switch (version) {
  1872.                 case 1: // 1.0 and 1.1
  1873.                         waveinfo->sample_rate = buffer->digi.sample_rate;
  1874.                         waveinfo->sample_size = buffer->digi.sample_size;
  1875.                         waveinfo->sample_count = buffer->digi.sample_count;
  1876.                         waveinfo->samples = buffer->digi.samples;
  1877.                         return true;
  1878.                 case 2: // 1.3 and 1.4 (and PoP2)
  1879.                         waveinfo->sample_rate = buffer->digi_new.sample_rate;
  1880.                         waveinfo->sample_size = buffer->digi_new.sample_size;
  1881.                         waveinfo->sample_count = buffer->digi_new.sample_count;
  1882.                         waveinfo->samples = buffer->digi_new.samples;
  1883.                         return true;
  1884.                 case 3: // ambiguous
  1885.                         printf("Warning: Ambiguous wave version.\n");
  1886.                         return false;
  1887.                 default: // case 0, unknown
  1888.                         printf("Warning: Can't determine wave version.\n");
  1889.                         return false;
  1890.         }
  1891. }
  1892.  
  1893. #ifndef USE_MIXER
  1894. // seg009:74F0
  1895. void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
  1896.         //if (!is_sound_on) return;
  1897.         init_digi();
  1898.         if (digi_unavailable) return;
  1899.         //stop_digi();
  1900.         stop_sounds();
  1901.         //printf("play_digi_sound(): called\n");
  1902.  
  1903.         waveinfo_type waveinfo;
  1904.         if (false == determine_wave_version(buffer, &waveinfo)) return;
  1905.  
  1906.         SDL_AudioCVT cvt;
  1907.         memset(&cvt, 0, sizeof(cvt));
  1908.         int result = SDL_BuildAudioCVT(&cvt,
  1909.                 AUDIO_U8, 1, waveinfo.sample_rate,
  1910.                 digi_audiospec->format, digi_audiospec->channels, digi_audiospec->freq
  1911.         );
  1912.         // The case of result == 0 is undocumented, but it may occur.
  1913.         if (result != 1 && result != 0) {
  1914.                 sdlperror("SDL_BuildAudioCVT");
  1915.                 printf("(returned %d)\n", result);
  1916.                 quit(1);
  1917.         }
  1918.         int dlen = waveinfo.sample_count; // if format is AUDIO_U8
  1919.         cvt.buf = (Uint8*) malloc(dlen * cvt.len_mult);
  1920.         memcpy(cvt.buf, waveinfo.samples, dlen);
  1921.         cvt.len = dlen;
  1922.         if (SDL_ConvertAudio(&cvt) != 0) {
  1923.                 sdlperror("SDL_ConvertAudio");
  1924.                 quit(1);
  1925.         }
  1926.  
  1927.         SDL_LockAudio();
  1928.         digi_buffer = cvt.buf;
  1929.         digi_playing = 1;
  1930. //      digi_remaining_length = sample_count;
  1931. //      digi_remaining_pos = samples;
  1932.         digi_remaining_length = cvt.len_cvt;
  1933.         digi_remaining_pos = digi_buffer;
  1934.         SDL_UnlockAudio();
  1935.         SDL_PauseAudio(0);
  1936. }
  1937. #else
  1938. void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
  1939.         printf("Warning: Tried to play a digi sound without converting it to a mixer chunk first!\n");
  1940. }
  1941.  
  1942. void convert_digi_sound(sound_buffer_type *buffer) {
  1943.         waveinfo_type waveinfo;
  1944.         if (false == determine_wave_version(buffer, &waveinfo)) return;
  1945.  
  1946.         // Convert the DAT sound to WAV, so the Mixer can load it.
  1947.         int size = waveinfo.sample_count;
  1948.         int rounded_size = (size+1)&(~1);
  1949.         int alloc_size = sizeof(WAV_header_type) + rounded_size;
  1950.         WAV_header_type* wav_data = malloc(alloc_size);
  1951.         wav_data->ChunkID = fourcc("RIFF");
  1952.         wav_data->ChunkSize = 36 + rounded_size;
  1953.         wav_data->Format = fourcc("WAVE");
  1954.         wav_data->Subchunk1ID = fourcc("fmt ");
  1955.         wav_data->Subchunk1Size = 16;
  1956.         wav_data->AudioFormat = 1; // PCM
  1957.         wav_data->NumChannels = 1; // Mono
  1958.         wav_data->SampleRate = waveinfo.sample_rate;
  1959.         wav_data->BitsPerSample = waveinfo.sample_size;
  1960.         wav_data->ByteRate = wav_data->SampleRate * wav_data->NumChannels * wav_data->BitsPerSample/8;
  1961.         wav_data->BlockAlign = wav_data->NumChannels * wav_data->BitsPerSample/8;
  1962.         wav_data->Subchunk2ID = fourcc("data");
  1963.         wav_data->Subchunk2Size = size;
  1964.         memcpy(wav_data->Data, waveinfo.samples, size);
  1965.         SDL_RWops* rw = SDL_RWFromConstMem(wav_data, alloc_size);
  1966.         Mix_Chunk *chunk = Mix_LoadWAV_RW(rw, 1);
  1967.         if (chunk == NULL) {
  1968.                 FILE* fp = fopen("dump.wav","wb");
  1969.                 fwrite(wav_data,alloc_size,1,fp);
  1970.                 fclose(fp);
  1971.         }
  1972.         free(wav_data);
  1973.         if (chunk == NULL) {
  1974.                 sdlperror("Mix_LoadWAV_RW");
  1975.                 return;
  1976.         }
  1977.         buffer->type = sound_chunk;
  1978.         buffer->chunk = chunk;
  1979. }
  1980. #endif
  1981.  
  1982. void free_sound(sound_buffer_type far *buffer) {
  1983.         if (buffer == NULL) return;
  1984. #ifdef USE_MIXER
  1985.         if (buffer->type == sound_chunk) {
  1986.                 Mix_FreeChunk(buffer->chunk);
  1987.         }
  1988.         if (buffer->type == sound_music) {
  1989.                 Mix_FreeMusic(buffer->music);
  1990.         }
  1991. #endif
  1992.         free(buffer);
  1993. }
  1994.  
  1995. // seg009:7220
  1996. void __pascal far play_sound_from_buffer(sound_buffer_type far *buffer) {
  1997.  
  1998. #ifdef USE_REPLAY
  1999.         if (replaying && skipping_replay) return;
  2000. #endif
  2001.  
  2002.         // stub
  2003.         if (buffer == NULL) {
  2004.                 printf("Tried to play NULL sound.\n");
  2005.                 //quit(1);
  2006.                 return;
  2007.         }
  2008.         switch (buffer->type & 7) {
  2009.                 case sound_speaker:
  2010.                         play_speaker_sound(buffer);
  2011.                 break;
  2012.                 case sound_digi:
  2013.                         play_digi_sound(buffer);
  2014.                 break;
  2015. #ifdef USE_MIXER
  2016.                 case sound_chunk:
  2017.                         play_chunk_sound(buffer);
  2018.                 break;
  2019.                 case sound_music:
  2020.                         play_music_sound(buffer);
  2021.                 break;
  2022. #endif
  2023.                 default:
  2024.                         printf("Tried to play unimplemented sound type %d.\n", buffer->type);
  2025.                         quit(1);
  2026.                 break;
  2027.         }
  2028. }
  2029.  
  2030. // seg009:7273
  2031. void __pascal far turn_sound_on_off(byte new_state) {
  2032.         // stub
  2033.         is_sound_on = new_state;
  2034.         //if (!is_sound_on) stop_sounds();
  2035. #ifdef USE_MIXER
  2036.         init_digi();
  2037.         if (digi_unavailable) return;
  2038. //printf ("turn_sound_on_off(): SETTING SOUND AND MUSIC VOLUME\n");
  2039.         Mix_Volume(-1, is_sound_on ? mixer_volume : 0);
  2040.         Mix_VolumeMusic(is_sound_on ? mixer_volume : 0);
  2041. #endif
  2042. }
  2043.  
  2044. // seg009:7299
  2045. int __pascal far check_sound_playing() {
  2046.         return speaker_playing || digi_playing || midi_playing;
  2047. }
  2048.  
  2049. void window_resized() {
  2050. #if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
  2051.         if (use_integer_scaling) {
  2052.                 int window_width, window_height;
  2053.                 SDL_GetWindowSize(window_, &window_width, &window_height);
  2054.                 int render_width, render_height;
  2055.                 SDL_RenderGetLogicalSize(renderer_, &render_width, &render_height);
  2056.                 // Disable integer scaling if it would result in downscaling.
  2057.                 // Because then the only suitable integer scaling factor is zero, i.e. the picture disappears.
  2058.                 SDL_bool makes_sense = (window_width >= render_width && window_height >= render_height);
  2059.                 SDL_RenderSetIntegerScale(renderer_, makes_sense);
  2060.         }
  2061. #endif
  2062. }
  2063.  
  2064. void xbrz_scale (size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, int yFirst, int yLast, bool has_alpha_channel);
  2065. static void *hqx_pixels32 = NULL;
  2066. static void *hqx_resized_pixels32 = NULL;
  2067. static void *hqx_resized_pixels24 = NULL;
  2068.  
  2069. // seg009:38ED
  2070. void __pascal far set_gr_mode(byte grmode) {
  2071.         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE |
  2072.                      SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC ) != 0) {
  2073.                 sdlperror("SDL_Init");
  2074.                 quit(1);
  2075.         }
  2076.  
  2077.         //SDL_EnableUNICODE(1); //deprecated
  2078.         Uint32 flags = 0;
  2079.         if (!start_fullscreen) start_fullscreen = check_param("full") != NULL;
  2080.         if (start_fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  2081.         flags |= SDL_WINDOW_RESIZABLE;
  2082.  
  2083.         // Should use different default window dimensions when using 4:3 aspect ratio
  2084.         if (use_correct_aspect_ratio && pop_window_width == 640 && pop_window_height == 400) {
  2085.                 pop_window_height = 480;
  2086.         }
  2087.  
  2088. #ifdef USE_REPLAY
  2089.         if (!is_validate_mode) // run without a window if validating a replay
  2090. #endif
  2091.         window_ = SDL_CreateWindow(WINDOW_TITLE,
  2092.                                    SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  2093.                                    pop_window_width, pop_window_height, flags);
  2094.         renderer_ = SDL_CreateRenderer(window_, -1 , SDL_RENDERER_ACCELERATED );
  2095.         if (use_integer_scaling) {
  2096. #if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
  2097.                 SDL_RenderSetIntegerScale(renderer_, SDL_TRUE);
  2098. #else
  2099.                 printf("Warning: You need to compile with SDL 2.0.5 or newer for the use_integer_scaling option.\n");
  2100. #endif
  2101.         }
  2102.  
  2103.         SDL_Surface* icon = IMG_Load(locate_file("data/icon.png"));
  2104.         if (icon == NULL) {
  2105.                 sdlperror("Could not load icon");
  2106.         } else {
  2107.                 SDL_SetWindowIcon(window_, icon);
  2108.         }
  2109.  
  2110.         // Allow us to use a consistent set of screen co-ordinates, even if the screen size changes
  2111.         if (use_correct_aspect_ratio) {
  2112.                 SDL_RenderSetLogicalSize(renderer_, 320*5, 200*6);
  2113.         } else {
  2114.                 SDL_RenderSetLogicalSize(renderer_, 320, 200);
  2115.         }
  2116.  
  2117.         window_resized();
  2118.  
  2119.         /* Migration to SDL2: everything is still blitted to onscreen_surface_, however:
  2120.          * SDL2 renders textures to the screen instead of surfaces; so for now, every screen
  2121.          * update causes the onscreen_surface_ to be copied into sdl_texture_, which is
  2122.          * subsequently displayed; awaits a better refactoring!
  2123.          * The function handling the screen updates is request_screen_update()
  2124.          * */
  2125.         onscreen_surface_ = SDL_CreateRGBSurface(0, 320, 200, 24, 0xFF, 0xFF << 8, 0xFF << 16, 0) ;
  2126.  
  2127.         hqx_pixels32 = (void *) malloc (320 * 200 * 4);
  2128.         if (hqx_pixels32 == NULL) { sdlperror ("hqx original 32bpp image buffer allocation failure"); quit (1); }
  2129.         hqx_resized_pixels32 = (void *) realloc (hqx_resized_pixels32, 6 * 320 * 6 * 200 * 4);
  2130.         if (hqx_resized_pixels32 == NULL) { sdlperror ("hqx resized 32bpp image buffer allocation failure"); quit (1); }
  2131.         hqx_resized_pixels24 = (void *) realloc (hqx_resized_pixels24, 6 * 320 * 6 * 200 * 3);
  2132.         if (hqx_resized_pixels24 == NULL) { sdlperror ("hqx resized 24bpp image buffer allocation failure"); quit (1); }
  2133.  
  2134.         sdl_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 320 * filter_level, 200 * filter_level);
  2135.         screen_updates_suspended = 0;
  2136.  
  2137.         if (onscreen_surface_ == NULL) {
  2138.                 sdlperror("SDL_SetVideoMode");
  2139.                 quit(1);
  2140.         }
  2141.         if (start_fullscreen) {
  2142.                 SDL_ShowCursor(SDL_DISABLE);
  2143.         }
  2144.  
  2145.  
  2146.         //SDL_WM_SetCaption(WINDOW_TITLE, NULL);
  2147. //      if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) != 0) {  //deprecated
  2148. //              sdlperror("SDL_EnableKeyRepeat");
  2149. //              quit(1);
  2150. //      }
  2151.         graphics_mode = gmMcgaVga;
  2152. #ifdef USE_TEXT
  2153.         load_font();
  2154. #endif
  2155.  
  2156.     SDL_PumpEvents(); // Pierre-Marie Baty -- SDL black window fix on macOS 10.14+
  2157.     SDL_SetWindowSize(window_, pop_window_width, pop_window_height);
  2158. }
  2159.  
  2160. void request_screen_update() {
  2161.  
  2162. #ifdef USE_REPLAY
  2163.         if (replaying && skipping_replay) return;
  2164. #endif
  2165.         if (!screen_updates_suspended) {
  2166.                 if (filter_level > 1)
  2167.                 {
  2168.                         uint8_t *hqx_24bpp_ptr;
  2169.                         uint8_t *hqx_32bpp_ptr;
  2170.  
  2171.                         hqx_24bpp_ptr = onscreen_surface_->pixels; hqx_32bpp_ptr = hqx_pixels32; for (int hqx_pixel_index = 0; hqx_pixel_index < 320 * 200; hqx_pixel_index++) { *hqx_32bpp_ptr++ = *hqx_24bpp_ptr++; *hqx_32bpp_ptr++ = *hqx_24bpp_ptr++; *hqx_32bpp_ptr++ = *hqx_24bpp_ptr++; *hqx_32bpp_ptr++ = 0xFF; }
  2172.                         xbrz_scale (filter_level, hqx_pixels32, hqx_resized_pixels32, 320, 200, 0, 200, false);
  2173.                         hqx_32bpp_ptr = hqx_resized_pixels32; hqx_24bpp_ptr = hqx_resized_pixels24; for (int hqx_pixel_index = 0; hqx_pixel_index < filter_level * 320 * filter_level * 200; hqx_pixel_index++) { *hqx_24bpp_ptr++ = *hqx_32bpp_ptr++; *hqx_24bpp_ptr++ = *hqx_32bpp_ptr++; *hqx_24bpp_ptr++ = *hqx_32bpp_ptr++; hqx_32bpp_ptr++; }
  2174.  
  2175.                         SDL_UpdateTexture(sdl_texture_, NULL, hqx_resized_pixels24, onscreen_surface_->pitch * filter_level);
  2176.                 }
  2177.                 else
  2178.                         SDL_UpdateTexture(sdl_texture_, NULL, onscreen_surface_->pixels, onscreen_surface_->pitch);
  2179.  
  2180.                 SDL_RenderClear(renderer_);
  2181.                 SDL_RenderCopy(renderer_, sdl_texture_, NULL, NULL);
  2182.                 SDL_RenderPresent(renderer_);
  2183.         }
  2184. }
  2185.  
  2186. // seg009:9289
  2187. void __pascal far set_pal_arr(int start,int count,const rgb_type far *array,int vsync) {
  2188.         // stub
  2189.         int i;
  2190.         for (i = 0; i < count; ++i) {
  2191.                 if (array) {
  2192.                         set_pal(start + i, array[i].r, array[i].g, array[i].b, vsync);
  2193.                 } else {
  2194.                         set_pal(start + i, 0, 0, 0, vsync);
  2195.                 }
  2196.         }
  2197. }
  2198.  
  2199. rgb_type palette[256];
  2200.  
  2201. // seg009:92DF
  2202. void __pascal far set_pal(int index,int red,int green,int blue,int vsync) {
  2203.         // stub
  2204.         //palette[index] = ((red&0x3F)<<2)|((green&0x3F)<<2<<8)|((blue&0x3F)<<2<<16);
  2205.         palette[index].r = red;
  2206.         palette[index].g = green;
  2207.         palette[index].b = blue;
  2208. }
  2209.  
  2210. // seg009:969C
  2211. int __pascal far add_palette_bits(byte n_colors) {
  2212.         // stub
  2213.         return 0;
  2214. }
  2215.  
  2216. // seg009:9C36
  2217. int __pascal far find_first_pal_row(int which_rows_mask) {
  2218.         word which_row = 0;
  2219.         word row_mask = 1;
  2220.         do {
  2221.                 if (row_mask & which_rows_mask) {
  2222.                         return which_row;
  2223.                 }
  2224.                 ++which_row;
  2225.                 row_mask <<= 1;
  2226.         } while (which_row < 16);
  2227.         return 0;
  2228. }
  2229.  
  2230. // seg009:9C6C
  2231. int __pascal far get_text_color(int cga_color,int low_half,int high_half_mask) {
  2232.         if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
  2233.                 return cga_color;
  2234.         } else if (graphics_mode == gmMcgaVga && high_half_mask != 0) {
  2235.                 return (find_first_pal_row(high_half_mask) << 4) + low_half;
  2236.         } else {
  2237.                 return low_half;
  2238.         }
  2239. }
  2240.  
  2241. void load_from_opendats_metadata(int resource_id, const char* extension, FILE** out_fp, data_location* result, byte* checksum, int* size, dat_type** out_pointer) {
  2242.         char image_filename[POP_MAX_PATH];
  2243.         FILE* fp = NULL;
  2244.         dat_type* pointer;
  2245.         *result = data_none;
  2246.         // Go through all open DAT files.
  2247.         for (pointer = dat_chain_ptr; fp == NULL && pointer != NULL; pointer = pointer->next_dat) {
  2248.                 *out_pointer = pointer;
  2249.                 if (pointer->handle != NULL) {
  2250.                         // If it's an actual DAT file:
  2251.                         fp = pointer->handle;
  2252.                         dat_table_type* dat_table = pointer->dat_table;
  2253.                         int i;
  2254.                         for (i = 0; i < dat_table->res_count; ++i) {
  2255.                                 if (dat_table->entries[i].id == resource_id) {
  2256.                                         break;
  2257.                                 }
  2258.                         }
  2259.                         if (i < dat_table->res_count) {
  2260.                                 // found
  2261.                                 *result = data_DAT;
  2262.                                 *size = dat_table->entries[i].size;
  2263.                                 if (fseek(fp, dat_table->entries[i].offset, SEEK_SET) ||
  2264.                                     fread(checksum, 1, 1, fp) != 1) {
  2265.                                         perror(pointer->filename);
  2266.                                         fp = NULL;
  2267.                                 }
  2268.                         } else {
  2269.                                 // not found
  2270.                                 fp = NULL;
  2271.                         }
  2272.                 } else {
  2273.                         // If it's a directory:
  2274.                         char filename_no_ext[POP_MAX_PATH];
  2275.                         // strip the .DAT file extension from the filename (use folders simply named TITLE, KID, VPALACE, etc.)
  2276.                         strncpy(filename_no_ext, pointer->filename, sizeof(filename_no_ext));
  2277.                         size_t len = strlen(filename_no_ext);
  2278.                         if (len >= 5 && filename_no_ext[len-4] == '.') {
  2279.                                 filename_no_ext[len-4] = '\0'; // terminate, so ".DAT" is deleted from the filename
  2280.                         }
  2281.                         snprintf(image_filename,sizeof(image_filename),"data/%s/res%d.%s",filename_no_ext, resource_id, extension);
  2282.                         if (!use_custom_levelset) {
  2283.                                 //printf("loading (binary) %s",image_filename);
  2284.                                 fp = fopen(locate_file(image_filename), "rb");
  2285.                         }
  2286.                         else {
  2287.                                 char image_filename_mod[POP_MAX_PATH];
  2288.                                 // before checking data/, first try mods/MODNAME/data/
  2289.                                 snprintf(image_filename_mod, sizeof(image_filename_mod), "mods/%s/%s", levelset_name, image_filename);
  2290.                                 //printf("loading (binary) %s",image_filename_mod);
  2291.                                 fp = fopen(locate_file(image_filename_mod), "rb");
  2292.                                 if (fp == NULL) {
  2293.                                         fp = fopen(locate_file(image_filename), "rb");
  2294.                                 }
  2295.                         }
  2296.  
  2297.                         if (fp != NULL) {
  2298.                                 struct stat buf;
  2299.                                 if (fstat(fileno(fp), &buf) == 0) {
  2300.                                         *result = data_directory;
  2301.                                         *size = buf.st_size;
  2302.                                 } else {
  2303.                                         perror(image_filename);
  2304.                                         fclose(fp);
  2305.                                         fp = NULL;
  2306.                                 }
  2307.                         }
  2308.                 }
  2309.         }
  2310.         *out_fp = fp;
  2311.         if (fp == NULL) {
  2312.                 *result = data_none;
  2313. //              printf(" FAILED\n");
  2314.                 //return NULL;
  2315.         }
  2316.         //...
  2317. }
  2318.  
  2319. // seg009:9F34
  2320. void __pascal far close_dat(dat_type far *pointer) {
  2321.         dat_type** prev = &dat_chain_ptr;
  2322.         dat_type* curr = dat_chain_ptr;
  2323.         while (curr != NULL) {
  2324.                 if (curr == pointer) {
  2325.                         *prev = curr->next_dat;
  2326.                         if (curr->handle) fclose(curr->handle);
  2327.                         if (curr->dat_table) free(curr->dat_table);
  2328.                         free(curr);
  2329.                         return;
  2330.                 }
  2331.                 curr = curr->next_dat;
  2332.                 prev = &((*prev)->next_dat);
  2333.         }
  2334.         // stub
  2335. }
  2336.  
  2337. // seg009:9F80
  2338. void far *__pascal load_from_opendats_alloc(int resource, const char* extension, data_location* out_result, int* out_size) {
  2339.         // stub
  2340.         //printf("id = %d\n",resource);
  2341.         dat_type* pointer;
  2342.         data_location result;
  2343.         byte checksum;
  2344.         int size;
  2345.         FILE* fp = NULL;
  2346.         load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
  2347.         if (out_result != NULL) *out_result = result;
  2348.         if (out_size != NULL) *out_size = size;
  2349.         if (result == data_none) return NULL;
  2350.         void* area = malloc(size);
  2351.         //read(fd, area, size);
  2352.         if (fread(area, size, 1, fp) != 1) {
  2353.                 fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n",
  2354.                         __func__, pointer->filename, resource,
  2355.                         size, strerror(errno));
  2356.                 free(area);
  2357.                 area = NULL;
  2358.         }
  2359.         if (result == data_directory) fclose(fp);
  2360.         /* XXX: check checksum */
  2361.         return area;
  2362. }
  2363.  
  2364. // seg009:A172
  2365. int __pascal far load_from_opendats_to_area(int resource,void far *area,int length, const char* extension) {
  2366.         // stub
  2367.         //return 0;
  2368.         dat_type* pointer;
  2369.         data_location result;
  2370.         byte checksum;
  2371.         int size;
  2372.         FILE* fp = NULL;
  2373.         load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
  2374.         if (result == data_none) return 0;
  2375.         if (fread(area, MIN(size, length), 1, fp) != 1) {
  2376.                 fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n",
  2377.                         __func__, pointer->filename, resource,
  2378.                         size, strerror(errno));
  2379.                 memset(area, 0, MIN(size, length));
  2380.         }
  2381.         if (result == data_directory) fclose(fp);
  2382.         /* XXX: check checksum */
  2383.         return 0;
  2384. }
  2385.  
  2386. // SDL-specific implementations
  2387.  
  2388. void rect_to_sdlrect(const rect_type* rect, SDL_Rect* sdlrect) {
  2389.         sdlrect->x = rect->left;
  2390.         sdlrect->y = rect->top;
  2391.         sdlrect->w = rect->right - rect->left;
  2392.         sdlrect->h = rect->bottom - rect->top;
  2393. }
  2394.  
  2395. void __pascal far method_1_blit_rect(surface_type near *target_surface,surface_type near *source_surface,const rect_type far *target_rect, const rect_type far *source_rect,int blit) {
  2396.         SDL_Rect src_rect;
  2397.         rect_to_sdlrect(source_rect, &src_rect);
  2398.         SDL_Rect dest_rect;
  2399.         rect_to_sdlrect(target_rect, &dest_rect);
  2400.  
  2401.         if (blit == blitters_0_no_transp) {
  2402.                 // Disable transparency.
  2403.                 if (SDL_SetColorKey(source_surface, 0, 0) != 0) {
  2404.                         sdlperror("SDL_SetColorKey");
  2405.                         quit(1);
  2406.                 }
  2407.         } else {
  2408.                 // Enable transparency.
  2409.                 if (SDL_SetColorKey(source_surface, SDL_TRUE, 0) != 0) {
  2410.                         sdlperror("SDL_SetColorKey");
  2411.                         quit(1);
  2412.                 }
  2413.         }
  2414.         if (SDL_BlitSurface(source_surface, &src_rect, target_surface, &dest_rect) != 0) {
  2415.                 sdlperror("SDL_BlitSurface");
  2416.                 quit(1);
  2417.         }
  2418.         if (target_surface == onscreen_surface_) {
  2419.                 request_screen_update();
  2420.         }
  2421. }
  2422.  
  2423. image_type far * __pascal far method_3_blit_mono(image_type far *image,int xpos,int ypos,int blitter,byte color) {
  2424.         int w = image->w;
  2425.         int h = image->h;
  2426.         if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
  2427.                 sdlperror("SDL_SetColorKey");
  2428.                 quit(1);
  2429.         }
  2430.         SDL_Surface* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
  2431.  
  2432.         SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_NONE);
  2433.         /* Causes problems with SDL 2.0.5 (see #105)
  2434.         if (SDL_SetColorKey(colored_image, SDL_TRUE, 0) != 0) {
  2435.                 sdlperror("SDL_SetColorKey");
  2436.                 quit(1);
  2437.         }
  2438.         */
  2439.  
  2440.         if (SDL_LockSurface(colored_image) != 0) {
  2441.                 sdlperror("SDL_LockSurface");
  2442.                 quit(1);
  2443.         }
  2444.  
  2445.         int y,x;
  2446.         rgb_type palette_color = palette[color];
  2447.         uint32_t rgb_color = SDL_MapRGB(colored_image->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) & 0xFFFFFF;
  2448.         int stride = colored_image->pitch;
  2449.         for (y = 0; y < h; ++y) {
  2450.                 uint32_t* pixel_ptr = (uint32_t*) ((byte*)colored_image->pixels + stride * y);
  2451.                 for (x = 0; x < w; ++x) {
  2452.                         // set RGB but leave alpha
  2453.                         *pixel_ptr = (*pixel_ptr & 0xFF000000) | rgb_color;
  2454.                         //printf("pixel x=%d, y=%d, color = 0x%8x\n", x, y, *pixel_ptr);
  2455.                         ++pixel_ptr;
  2456.                 }
  2457.         }
  2458.         SDL_UnlockSurface(colored_image);
  2459.  
  2460.         SDL_Rect src_rect = {0, 0, image->w, image->h};
  2461.         SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
  2462.  
  2463.         SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_BLEND);
  2464.         SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_BLEND);
  2465.         SDL_SetSurfaceAlphaMod(colored_image, 255);
  2466.         if (SDL_BlitSurface(colored_image, &src_rect, current_target_surface, &dest_rect) != 0) {
  2467.                 sdlperror("SDL_BlitSurface");
  2468.                 quit(1);
  2469.         }
  2470.         SDL_FreeSurface(colored_image);
  2471.  
  2472.         return image;
  2473. }
  2474.  
  2475. // Workaround for a bug in SDL2 (before v2.0.4):
  2476. // https://bugzilla.libsdl.org/show_bug.cgi?id=2986
  2477. // SDL_FillRect onto a 24-bit surface swaps Red and Blue component
  2478.  
  2479. bool RGB24_bug_checked = false;
  2480. bool RGB24_bug_affected;
  2481.  
  2482. bool RGB24_bug_check() {
  2483.         if (!RGB24_bug_checked) {
  2484.                 // Check if the bug occurs in this version of SDL.
  2485.                 SDL_Surface* test_surface = SDL_CreateRGBSurface(0, 1, 1, 24, 0, 0, 0, 0);
  2486.                 if (NULL == test_surface) sdlperror("SDL_CreateSurface in RGB24_bug_check");
  2487.                 // Fill with red.
  2488.                 SDL_FillRect(test_surface, NULL, SDL_MapRGB(test_surface->format, 0xFF, 0, 0));
  2489.                 if (0 != SDL_LockSurface(test_surface)) sdlperror("SDL_LockSurface in RGB24_bug_check");
  2490.                 // Read red component of pixel.
  2491.                 RGB24_bug_affected = (*(Uint32*)test_surface->pixels & test_surface->format->Rmask) == 0;
  2492.                 SDL_UnlockSurface(test_surface);
  2493.                 SDL_FreeSurface(test_surface);
  2494.                 RGB24_bug_checked = true;
  2495.         }
  2496.         return RGB24_bug_affected;
  2497. }
  2498.  
  2499. int safe_SDL_FillRect(SDL_Surface* dst, const SDL_Rect* rect, Uint32 color) {
  2500.         if (dst->format->BitsPerPixel == 24 && RGB24_bug_check()) {
  2501.                 // In the buggy version, SDL_FillRect swaps R and B, so we swap it once more.
  2502.                 color = ((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16);
  2503.         }
  2504.         return SDL_FillRect(dst, rect, color);
  2505. }
  2506. // End of workaround.
  2507.  
  2508. const rect_type far * __pascal far method_5_rect(const rect_type far *rect,int blit,byte color) {
  2509.         SDL_Rect dest_rect;
  2510.         rect_to_sdlrect(rect, &dest_rect);
  2511.         rgb_type palette_color = palette[color];
  2512. #ifndef USE_ALPHA
  2513.         uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2);
  2514. #else
  2515.         uint32_t rgb_color = SDL_MapRGBA(current_target_surface->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2, color == 0 ? SDL_ALPHA_TRANSPARENT : SDL_ALPHA_OPAQUE);
  2516. #endif
  2517.         if (safe_SDL_FillRect(current_target_surface, &dest_rect, rgb_color) != 0) {
  2518.                 sdlperror("SDL_FillRect");
  2519.                 quit(1);
  2520.         }
  2521.         if (current_target_surface == onscreen_surface_) {
  2522.                 request_screen_update();
  2523.         }
  2524.         return rect;
  2525. }
  2526.  
  2527. void blit_xor(SDL_Surface* target_surface, SDL_Rect* dest_rect, SDL_Surface* image, SDL_Rect* src_rect) {
  2528.         if (dest_rect->w != src_rect->w || dest_rect->h != src_rect->h) {
  2529.                 printf("blit_xor: dest_rect and src_rect have different sizes\n");
  2530.                 quit(1);
  2531.         }
  2532.         SDL_Surface* helper_surface = SDL_CreateRGBSurface(0, dest_rect->w, dest_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
  2533.         if (helper_surface == NULL) {
  2534.                 sdlperror("SDL_CreateRGBSurface");
  2535.                 quit(1);
  2536.         }
  2537.         SDL_Surface* image_24 = SDL_ConvertSurface(image, helper_surface->format, 0);
  2538.         //SDL_CreateRGBSurface(0, src_rect->w, src_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
  2539.         if (image_24 == NULL) {
  2540.                 sdlperror("SDL_CreateRGBSurface");
  2541.                 quit(1);
  2542.         }
  2543.         SDL_Rect dest_rect2 = *src_rect;
  2544.         // Read what is currently where we want to draw the new image.
  2545.         if (SDL_BlitSurface(target_surface, dest_rect, helper_surface, &dest_rect2) != 0) {
  2546.                 sdlperror("SDL_BlitSurface");
  2547.                 quit(1);
  2548.         }
  2549.         if (SDL_LockSurface(image_24) != 0) {
  2550.                 sdlperror("SDL_LockSurface");
  2551.                 quit(1);
  2552.         }
  2553.         if (SDL_LockSurface(helper_surface) != 0) {
  2554.                 sdlperror("SDL_LockSurface");
  2555.                 quit(1);
  2556.         }
  2557.         int size = helper_surface->h * helper_surface->pitch;
  2558.         int i;
  2559.         byte *p_src = (byte*) image_24->pixels;
  2560.         byte *p_dest = (byte*) helper_surface->pixels;
  2561.  
  2562.         // Xor the old area with the image.
  2563.         for (i = 0; i < size; ++i) {
  2564.                 *p_dest ^= *p_src;
  2565.                 ++p_src; ++p_dest;
  2566.         }
  2567.         SDL_UnlockSurface(image_24);
  2568.         SDL_UnlockSurface(helper_surface);
  2569.         // Put the new area in place of the old one.
  2570.         if (SDL_BlitSurface(helper_surface, src_rect, target_surface, dest_rect) != 0) {
  2571.                 sdlperror("SDL_BlitSurface 2065");
  2572.                 quit(1);
  2573.         }
  2574.         SDL_FreeSurface(image_24);
  2575.         SDL_FreeSurface(helper_surface);
  2576.         if (target_surface == onscreen_surface_) {
  2577.                 request_screen_update();
  2578.         }
  2579. }
  2580.  
  2581. image_type far * __pascal far method_6_blit_img_to_scr(image_type far *image,int xpos,int ypos,int blit) {
  2582.         if (image == NULL) {
  2583.                 printf("method_6_blit_img_to_scr: image == NULL\n");
  2584.                 //quit(1);
  2585.                 return NULL;
  2586.         }
  2587.  
  2588.         if (blit == blitters_9_black) {
  2589.                 method_3_blit_mono(image, xpos, ypos, blitters_9_black, 0);
  2590.                 return image;
  2591.         }
  2592.  
  2593.         SDL_Rect src_rect = {0, 0, image->w, image->h};
  2594.         SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
  2595.  
  2596.         if (blit == blitters_3_xor) {
  2597.                 blit_xor(current_target_surface, &dest_rect, image, &src_rect);
  2598.                 return image;
  2599.         }
  2600.         SDL_SetSurfaceBlendMode(image, SDL_BLENDMODE_NONE);
  2601.         SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_NONE);
  2602.         SDL_SetSurfaceAlphaMod(image, 255);
  2603.  
  2604.         if (blit == blitters_0_no_transp) {
  2605.                 SDL_SetColorKey(image, SDL_FALSE, 0);
  2606.         }
  2607.         else {
  2608.                 SDL_SetColorKey(image, SDL_TRUE, 0);
  2609.         }
  2610.         if (SDL_BlitSurface(image, &src_rect, current_target_surface, &dest_rect) != 0) {
  2611.                 sdlperror("SDL_BlitSurface 2247");
  2612.                 quit(1);
  2613.         }
  2614.  
  2615.         if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
  2616.                 sdlperror("SDL_SetAlpha");
  2617.                 quit(1);
  2618.         }
  2619. //      if (current_target_surface == onscreen_surface_)
  2620. //              request_screen_update();
  2621.  
  2622.         return image;
  2623. }
  2624.  
  2625. #ifndef USE_COMPAT_TIMER
  2626. int fps = 60;
  2627. SDL_TimerID timer_handles[2] = {0,0};
  2628. int timer_stopped[2] = {1,1};
  2629. #else
  2630. int wait_time[2] = {0,0};
  2631. #endif
  2632.  
  2633. void remove_timer(int timer_index) {
  2634. #ifndef USE_COMPAT_TIMER
  2635.         if (timer_handles[timer_index]) {
  2636.                 if (!SDL_RemoveTimer(timer_handles[timer_index])) {
  2637.                         printf("timer_handles[%d] = %d\n", timer_index, timer_handles[timer_index]);
  2638.                         sdlperror("SDL_RemoveTimer in remove_timer");
  2639.                         //quit(1);
  2640.                 }
  2641.                 timer_handles[timer_index] = 0;
  2642.         }
  2643. #endif
  2644. }
  2645.  
  2646. int target_time;
  2647.  
  2648. Uint32 timer_callback(Uint32 interval, void *param) {
  2649.         int now = SDL_GetTicks();
  2650.  
  2651.         // let the timer finish 5 ms earlier to allow for overhead before the next frame is displayed
  2652.         // this is somewhat ugly and may cause the game to run slightly too fast on fast systems (not tested)
  2653.         int residual_wait_time = target_time - now - 5;
  2654.         if (residual_wait_time > 0 && residual_wait_time <= 40) SDL_Delay(residual_wait_time);
  2655.  
  2656.         SDL_Event event;
  2657.         memset(&event, 0, sizeof(event));
  2658.         event.type = SDL_USEREVENT;
  2659.         event.user.code = userevent_TIMER;
  2660.         event.user.data1 = param;
  2661.         int timer_index = (uintptr_t)param;
  2662.         remove_timer(timer_index);
  2663.         // First remove the timer, then allow the other thread to continue.
  2664.         SDL_PushEvent(&event);
  2665.  
  2666.  
  2667. #ifndef USE_COMPAT_TIMER
  2668.         return 0;
  2669. #else
  2670.         return interval;
  2671. #endif
  2672. }
  2673.  
  2674. void __pascal start_timer(int timer_index, int length) {
  2675. #ifdef USE_REPLAY
  2676.         if (replaying && skipping_replay) return;
  2677. #endif
  2678. #ifndef USE_COMPAT_TIMER
  2679.         if (timer_handles[timer_index]) {
  2680.                 remove_timer(timer_index);
  2681.                 timer_handles[timer_index] = 0;
  2682.         }
  2683.         timer_stopped[timer_index] = 0; // length<=0;
  2684.         if (length <= 0) {
  2685.                 // We need the timer to finish *after* keypresses.
  2686.                 // So idle() is called at least once in do_wait(), and keypresses while fading are not ignored.
  2687.                 timer_callback(0, (void*)(uintptr_t)timer_index);
  2688.                 return;
  2689.         }
  2690.  
  2691.         int now = SDL_GetTicks();
  2692.         double frametime = 1000.0 / 60.0;
  2693. //      double frametime = (timer_index == 1) ? 16.60 : 1000.0 / 60.0;
  2694.         int target_length = (int) (length * frametime);
  2695.  
  2696.         // subtract 40ms to allow for variable lag; correct for this when the timer ends
  2697.         target_time = now + target_length;
  2698.         int modified_length = target_length - 40;
  2699.  
  2700.         SDL_TimerID timer = SDL_AddTimer(modified_length, timer_callback, (void*)(uintptr_t)timer_index);
  2701.  
  2702.         if (timer == 0) {
  2703.                 sdlperror("SDL_AddTimer");
  2704.                 quit(1);
  2705.         }
  2706.         timer_handles[timer_index] = timer;
  2707. #else
  2708.         wait_time[timer_index] = length;
  2709. #endif
  2710. }
  2711.  
  2712. void toggle_fullscreen() {
  2713.         uint32_t flags = SDL_GetWindowFlags(window_);
  2714.         if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
  2715.                 SDL_SetWindowFullscreen(window_, 0);
  2716.                 SDL_ShowCursor(SDL_ENABLE);
  2717.         }
  2718.         else {
  2719.                 SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
  2720.                 SDL_ShowCursor(SDL_DISABLE);
  2721.         }
  2722. }
  2723.  
  2724. void idle() {
  2725.         // Wait for *one* event and process it, then return.
  2726.         // Much like the x86 HLT instruction.
  2727.         SDL_Event event;
  2728.         if (SDL_WaitEvent(&event) == 0) {
  2729.                 sdlperror("SDL_WaitEvent");
  2730.                 quit(1);
  2731.         }
  2732.         // We still want to process all events in the queue
  2733.         // For instance, there may be simultaneous SDL2 KEYDOWN and TEXTINPUT events
  2734.         do { // while there are still events to be processed
  2735.                 switch (event.type) {
  2736.                         case SDL_KEYDOWN:
  2737.                         {
  2738.                                 int modifier = event.key.keysym.mod;
  2739.                                 int scancode = event.key.keysym.scancode;
  2740.  
  2741.                                 // Handle these separately, so they won't interrupt things that are usually interrupted by a keypress. (pause, cutscene)
  2742. #ifdef USE_SCREENSHOT
  2743.                                 if (scancode == SDL_SCANCODE_F12) {
  2744.                                         if (modifier & KMOD_SHIFT) {
  2745.                                                 save_level_screenshot((modifier & KMOD_CTRL) != 0);
  2746.                                         } else {
  2747.                                                 save_screenshot();
  2748.                                         }
  2749.                                 } else
  2750. #endif
  2751.                                 if ((modifier & KMOD_ALT) &&
  2752.                                         scancode == SDL_SCANCODE_RETURN)
  2753.                                 {
  2754.                                         // Only if the Enter key was pressed down right now.
  2755.                                         if (key_states[scancode] == 0) {
  2756.                                                 // Alt-Enter: toggle fullscreen mode
  2757.                                                 toggle_fullscreen();
  2758.                                                 key_states[scancode] = 1;
  2759.                                         }
  2760.                                 } else {
  2761.                                         key_states[scancode] = 1;
  2762.                                         switch (scancode) {
  2763.                                                 // Keys that are ignored by themselves:
  2764.                                                 case SDL_SCANCODE_LCTRL:
  2765.                                                 case SDL_SCANCODE_LSHIFT:
  2766.                                                 case SDL_SCANCODE_LALT:
  2767.                                                 case SDL_SCANCODE_LGUI:
  2768.                                                 case SDL_SCANCODE_RCTRL:
  2769.                                                 case SDL_SCANCODE_RSHIFT:
  2770.                                                 case SDL_SCANCODE_RALT:
  2771.                                                 case SDL_SCANCODE_RGUI:
  2772.                                                 case SDL_SCANCODE_CAPSLOCK:
  2773.                                                 case SDL_SCANCODE_SCROLLLOCK:
  2774.                                                 case SDL_SCANCODE_NUMLOCKCLEAR:
  2775.                                                 case SDL_SCANCODE_APPLICATION:
  2776.                                                 case SDL_SCANCODE_PRINTSCREEN:
  2777.                                                 case SDL_SCANCODE_PAUSE:
  2778.                                                         break;
  2779.                                                 default:
  2780.                                                         last_key_scancode = scancode;
  2781.                                                         if (modifier & KMOD_SHIFT) last_key_scancode |= WITH_SHIFT;
  2782.                                                         if (modifier & KMOD_CTRL ) last_key_scancode |= WITH_CTRL ;
  2783.                                                         if (modifier & KMOD_ALT  ) last_key_scancode |= WITH_ALT  ;
  2784.                                         }
  2785.  
  2786. #ifdef USE_AUTO_INPUT_MODE
  2787.                                         switch (scancode) {
  2788.                                                 // Keys that are used for keyboard control:
  2789.                                                 case SDL_SCANCODE_LSHIFT:
  2790.                                                 case SDL_SCANCODE_RSHIFT:
  2791.                                                 case SDL_SCANCODE_LEFT:
  2792.                                                 case SDL_SCANCODE_RIGHT:
  2793.                                                 case SDL_SCANCODE_UP:
  2794.                                                 case SDL_SCANCODE_DOWN:
  2795.                                                 case SDL_SCANCODE_CLEAR:
  2796.                                                 case SDL_SCANCODE_HOME:
  2797.                                                 case SDL_SCANCODE_PAGEUP:
  2798.                                                 case SDL_SCANCODE_KP_2:
  2799.                                                 case SDL_SCANCODE_KP_4:
  2800.                                                 case SDL_SCANCODE_KP_5:
  2801.                                                 case SDL_SCANCODE_KP_6:
  2802.                                                 case SDL_SCANCODE_KP_7:
  2803.                                                 case SDL_SCANCODE_KP_8:
  2804.                                                 case SDL_SCANCODE_KP_9:
  2805.                                                         if (!is_keyboard_mode) {
  2806.                                                                 is_keyboard_mode = 1;
  2807.                                                                 is_joyst_mode = 0;
  2808.                                                         }
  2809.                                         }
  2810. #endif
  2811.                                 }
  2812.                                 break;
  2813.                         }
  2814.                         case SDL_KEYUP:
  2815.                                 key_states[event.key.keysym.scancode] = 0;
  2816.                                 break;
  2817.                         case SDL_CONTROLLERAXISMOTION:
  2818.                                 if (event.caxis.axis < 6) {
  2819.                                         joy_axis[event.caxis.axis] = event.caxis.value;
  2820.  
  2821. #ifdef USE_AUTO_INPUT_MODE
  2822.                                         if (!is_joyst_mode && (event.caxis.value >= joystick_threshold || event.caxis.value <= -joystick_threshold)) {
  2823.                                                 is_joyst_mode = 1;
  2824.                                                 is_keyboard_mode = 0;
  2825.                                         }
  2826. #endif
  2827.                                 }
  2828.                                 break;
  2829.                         case SDL_CONTROLLERBUTTONDOWN:
  2830. #ifdef USE_AUTO_INPUT_MODE
  2831.                                 if (!is_joyst_mode) {
  2832.                                         is_joyst_mode = 1;
  2833.                                         is_keyboard_mode = 0;
  2834.                                 }
  2835. #endif
  2836.                                 switch (event.cbutton.button)
  2837.                                 {
  2838.                                         case SDL_CONTROLLER_BUTTON_DPAD_LEFT:  joy_hat_states[0] = -1; break; // left
  2839.                                         case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 1;  break; // right
  2840.                                         case SDL_CONTROLLER_BUTTON_DPAD_UP:    joy_hat_states[1] = -1; break; // up
  2841.                                         case SDL_CONTROLLER_BUTTON_DPAD_DOWN:  joy_hat_states[1] = 1;  break; // down
  2842.  
  2843.                                         case SDL_CONTROLLER_BUTTON_A:          joy_AY_buttons_state = 1;  break; /*** A (down) ***/
  2844.                                         case SDL_CONTROLLER_BUTTON_Y:          joy_AY_buttons_state = -1; break; /*** Y (up) ***/
  2845.                                         case SDL_CONTROLLER_BUTTON_X:          joy_X_button_state = 1;    break; /*** X (shift) ***/
  2846.                                         case SDL_CONTROLLER_BUTTON_B:          joy_B_button_state = 1;    break; /*** B (unused) ***/
  2847.  
  2848.                                         case SDL_CONTROLLER_BUTTON_START:
  2849.                                                 last_key_scancode = SDL_SCANCODE_R | WITH_CTRL; /*** start (restart game) ***/
  2850.                                                 break;
  2851.  
  2852.                                         case SDL_CONTROLLER_BUTTON_BACK:
  2853.                                                 last_key_scancode = SDL_SCANCODE_A | WITH_CTRL;  /*** back (restart level) ***/
  2854.                                                 break;
  2855.  
  2856.                                         default: break;
  2857.                                 }
  2858.                                 break;
  2859.                         case SDL_CONTROLLERBUTTONUP:
  2860.                                 switch (event.cbutton.button)
  2861.                                 {
  2862.                                         case SDL_CONTROLLER_BUTTON_DPAD_LEFT:  joy_hat_states[0] = 0; break; // left
  2863.                                         case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 0; break; // right
  2864.                                         case SDL_CONTROLLER_BUTTON_DPAD_UP:    joy_hat_states[1] = 0; break; // up
  2865.                                         case SDL_CONTROLLER_BUTTON_DPAD_DOWN:  joy_hat_states[1] = 0; break; // down
  2866.  
  2867.                                         case SDL_CONTROLLER_BUTTON_A:          joy_AY_buttons_state = 0; break; /*** A (down) ***/
  2868.                                         case SDL_CONTROLLER_BUTTON_Y:          joy_AY_buttons_state = 0; break; /*** Y (up) ***/
  2869.                                         case SDL_CONTROLLER_BUTTON_X:          joy_X_button_state = 0;   break; /*** X (shift) ***/
  2870.                                         case SDL_CONTROLLER_BUTTON_B:          joy_B_button_state = 0;   break; /*** B (unused) ***/
  2871.  
  2872.                                         default: break;
  2873.                                 }
  2874.                                 break;
  2875.                         case SDL_JOYBUTTONDOWN:
  2876.                         case SDL_JOYBUTTONUP:
  2877.                         case SDL_JOYAXISMOTION:
  2878.                                 // Only handle the event if the joystick is incompatible with the SDL_GameController interface.
  2879.                                 // (Otherwise it will interfere with the normal action of the SDL_GameController API.)
  2880.                                 if (!using_sdl_joystick_interface) {
  2881.                                         break;
  2882.                                 }
  2883.                                 if (event.type == SDL_JOYAXISMOTION) {
  2884.                                         if (event.jaxis.axis == SDL_JOYSTICK_X_AXIS) {
  2885.                                                 joy_axis[SDL_CONTROLLER_AXIS_LEFTX] = event.jaxis.value;
  2886.                                         }
  2887.                                         else if (event.jaxis.axis == SDL_JOYSTICK_Y_AXIS) {
  2888.                                                 joy_axis[SDL_CONTROLLER_AXIS_LEFTY] = event.jaxis.value;
  2889.                                         }
  2890.                                         // Disregard SDL_JOYAXISMOTION events within joystick 'dead zone'
  2891.                                         int joy_x = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
  2892.                                         int joy_y = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
  2893.                                         if ((dword)(joy_x*joy_x) + (dword)(joy_y*joy_y) < (dword)(joystick_threshold*joystick_threshold)) {
  2894.                                                 break;
  2895.                                         }
  2896.  
  2897.                                 }
  2898. #ifdef USE_AUTO_INPUT_MODE
  2899.                                 if (!is_joyst_mode) {
  2900.                                         is_joyst_mode = 1;
  2901.                                         is_keyboard_mode = 0;
  2902.                                 }
  2903. #endif
  2904.                                 if (event.type == SDL_JOYBUTTONDOWN) {
  2905.                                         if      (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y)   joy_AY_buttons_state = -1; // Y (up)
  2906.                                         else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X)   joy_X_button_state = -1;   // X (shift)
  2907.                                 }
  2908.                                 else if (event.type == SDL_JOYBUTTONUP) {
  2909.                                         if      (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y)   joy_AY_buttons_state = 0;  // Y (up)
  2910.                                         else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X)   joy_X_button_state = 0;    // X (shift)
  2911.                                 }
  2912.                                 break;
  2913.  
  2914.                         case SDL_TEXTINPUT:
  2915.                                 last_text_input = event.text.text[0]; // UTF-8 formatted char text input
  2916.                                 break;
  2917.                         case SDL_WINDOWEVENT:
  2918.                                 // In case the user switches away while holding a key: do as if all keys were released.
  2919.                                 // (DOSBox does the same.)
  2920.  
  2921. /* // not implemented in SDL2 for now
  2922.  *
  2923.                         if ((event.active.state & SDL_APPINPUTFOCUS) && event.active.gain == 0) {
  2924.                                 memset(key_states, 0, sizeof(key_states));
  2925.                         }
  2926.                         // Note: event.active.state can contain multiple flags or'ed.
  2927.                         // If the game is in full screen, and I switch away (alt-tab) and back, most of the screen will be black, until it is redrawn.
  2928.                         if ((event.active.state & SDL_APPACTIVE) && event.active.gain == 1) {
  2929.                                 request_screen_update();
  2930.                         }
  2931. */
  2932.                                 switch (event.window.event) {
  2933.                                         case SDL_WINDOWEVENT_SIZE_CHANGED:
  2934.                                                 window_resized();
  2935.                                         //case SDL_WINDOWEVENT_MOVED:
  2936.                                         //case SDL_WINDOWEVENT_RESTORED:
  2937.                                         case SDL_WINDOWEVENT_EXPOSED:
  2938.                                                 request_screen_update();
  2939.                                                 break;
  2940.                                 }
  2941.                                 break;
  2942.                         case SDL_USEREVENT:
  2943.                                 if (event.user.code == userevent_TIMER /*&& event.user.data1 == (void*)timer_index*/) {
  2944. #ifndef USE_COMPAT_TIMER
  2945.                                         int timer_index = (uintptr_t) event.user.data1;
  2946.                                         timer_stopped[timer_index] = 1;
  2947.                                         //printf("timer_index = %d\n", timer_index);
  2948.                                         // 2014-08-27: According to the output of the next line, handle is always NULL.
  2949.                                         // 2014-08-28: Except when you interrupt fading of the cutscene.
  2950.                                         //printf("timer_handles[timer_index] = %p\n", timer_handles[timer_index]);
  2951.                                         // 2014-08-27: However, this line will change something: it makes the game too fast. Weird...
  2952.                                         // 2014-08-28: Wait, now it doesn't...
  2953.                                         //timer_handles[timer_index] = 0;
  2954. #else
  2955.                                 int index;
  2956.                                 for (index = 0; index < 2; ++index) {
  2957.                                         if (wait_time[index] > 0) --wait_time[index];
  2958.                                 }
  2959. #endif
  2960.                                 } else if (event.user.code == userevent_SOUND) {
  2961.                                         //sound_timer = 0;
  2962. #ifndef USE_MIXER
  2963.                                 //stop_sounds();
  2964. #endif
  2965.                                 }
  2966.                                 break;
  2967.                         case SDL_QUIT:
  2968.                                 quit(0);
  2969.                                 break;
  2970.                 }
  2971.         } while (SDL_PollEvent(&event) == 1);
  2972. }
  2973.  
  2974. word word_1D63A = 1;
  2975. // seg009:0EA9
  2976. int __pascal do_wait(int timer_index) {
  2977.         while (! has_timer_stopped(timer_index)) {
  2978.                 idle();
  2979.                 int key = do_paused();
  2980.                 if (key != 0 && (word_1D63A != 0 || key == 0x1B)) return 1;
  2981.         }
  2982.         return 0;
  2983. }
  2984.  
  2985. void __pascal do_simple_wait(int timer_index) {
  2986. #ifdef USE_REPLAY
  2987.         if ((replaying && skipping_replay) || is_validate_mode) return;
  2988. #endif
  2989.  
  2990.         while (! has_timer_stopped(timer_index)) {
  2991.                 idle();
  2992.         }
  2993. }
  2994.  
  2995. #ifdef USE_COMPAT_TIMER
  2996. SDL_TimerID global_timer = NULL;
  2997. #endif
  2998. // seg009:78E9
  2999. void __pascal far init_timer(int frequency) {
  3000. #ifndef USE_COMPAT_TIMER
  3001.         fps = frequency;
  3002. #else
  3003.         if (global_timer != 0) {
  3004.                 if (!SDL_RemoveTimer(global_timer)) {
  3005.                         sdlperror("SDL_RemoveTimer");
  3006.                 }
  3007.         }
  3008.         global_timer = SDL_AddTimer(1000/frequency, timer_callback, NULL);
  3009.         if (global_timer == 0) {
  3010.                 sdlperror("SDL_AddTimer");
  3011.                 quit(1);
  3012.         }
  3013. #endif
  3014. }
  3015.  
  3016. // seg009:35F6
  3017. void __pascal far set_clip_rect(const rect_type far *rect) {
  3018.         SDL_Rect clip_rect;
  3019.         rect_to_sdlrect(rect, &clip_rect);
  3020.         SDL_SetClipRect(current_target_surface, &clip_rect);
  3021. }
  3022.  
  3023. // seg009:365C
  3024. void __pascal far reset_clip_rect() {
  3025.         SDL_SetClipRect(current_target_surface, NULL);
  3026. }
  3027.  
  3028. // seg009:1983
  3029. void __pascal far set_bg_attr(int vga_pal_index,int hc_pal_index) {
  3030.         // stub
  3031. #ifdef USE_FLASH
  3032.         //palette[vga_pal_index] = vga_palette[hc_pal_index];
  3033.         if (!enable_flash) return;
  3034.         if (vga_pal_index == 0) {
  3035.                 /*
  3036.                 if (SDL_SetAlpha(offscreen_surface, SDL_SRCALPHA, 0) != 0) {
  3037.                         sdlperror("SDL_SetAlpha");
  3038.                         quit(1);
  3039.                 }
  3040.                 */
  3041.                 // Make the black pixels transparent.
  3042.                 if (SDL_SetColorKey(offscreen_surface, SDL_TRUE, 0) != 0) {     // SDL_SRCCOLORKEY old
  3043.                         sdlperror("SDL_SetColorKey");
  3044.                         quit(1);
  3045.                 }
  3046.                 SDL_Rect rect = {0,0,0,0};
  3047.                 rect.w = offscreen_surface->w;
  3048.                 rect.h = offscreen_surface->h;
  3049.                 rgb_type palette_color = palette[hc_pal_index];
  3050.                 uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) /*& 0xFFFFFF*/;
  3051.                 //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
  3052.                 // First clear the screen with the color of the flash.
  3053.                 if (safe_SDL_FillRect(onscreen_surface_, &rect, rgb_color) != 0) {
  3054.                         sdlperror("SDL_FillRect");
  3055.                         quit(1);
  3056.                 }
  3057.                 //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
  3058.                 if (upside_down) {
  3059.                         flip_screen(offscreen_surface);
  3060.                 }
  3061.                 // Then draw the offscreen image onto it.
  3062.                 if (SDL_BlitSurface(offscreen_surface, &rect, onscreen_surface_, &rect) != 0) {
  3063.                         sdlperror("SDL_BlitSurface");
  3064.                         quit(1);
  3065.                 }
  3066. #ifdef USE_LIGHTING
  3067.                 if (hc_pal_index == 0) update_lighting(&rect_top);
  3068. #endif
  3069.                 if (upside_down) {
  3070.                         flip_screen(offscreen_surface);
  3071.                 }
  3072.                 // And show it!
  3073.                 request_screen_update();
  3074.                 // Give some time to show the flash.
  3075.                 //SDL_Flip(onscreen_surface_);
  3076. //              if (hc_pal_index != 0) SDL_Delay(2*(1000/60));
  3077.                 //SDL_Flip(onscreen_surface_);
  3078.                 /*
  3079.                 if (SDL_SetAlpha(offscreen_surface, 0, 0) != 0) {
  3080.                         sdlperror("SDL_SetAlpha");
  3081.                         quit(1);
  3082.                 }
  3083.                 */
  3084.                 if (SDL_SetColorKey(offscreen_surface, 0, 0) != 0) {
  3085.                         sdlperror("SDL_SetColorKey");
  3086.                         quit(1);
  3087.                 }
  3088.         }
  3089. #endif // USE_FLASH
  3090. }
  3091.  
  3092. // seg009:07EB
  3093. rect_type far *__pascal offset4_rect_add(rect_type far *dest,const rect_type far *source,int d_left,int d_top,int d_right,int d_bottom) {
  3094.         *dest = *source;
  3095.         dest->left += d_left;
  3096.         dest->top += d_top;
  3097.         dest->right += d_right;
  3098.         dest->bottom += d_bottom;
  3099.         return dest;
  3100. }
  3101.  
  3102. // seg009:3AA5
  3103. rect_type far *__pascal offset2_rect(rect_type far *dest,const rect_type far *source,int delta_x,int delta_y) {
  3104.         dest->top    = source->top    + delta_y;
  3105.         dest->left   = source->left   + delta_x;
  3106.         dest->bottom = source->bottom + delta_y;
  3107.         dest->right  = source->right  + delta_x;
  3108.         return dest;
  3109. }
  3110.  
  3111. #ifdef USE_FADE
  3112. // seg009:19EF
  3113. void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
  3114.         palette_fade_type far* palette_buffer;
  3115.         if (graphics_mode == gmMcgaVga) {
  3116.                 palette_buffer = make_pal_buffer_fadein(source_surface, which_rows, 2);
  3117.                 while (fade_in_frame(palette_buffer) == 0) {
  3118.                         pop_wait(timer_1, 0); // modified
  3119.                 }
  3120.                 pal_restore_free_fadein(palette_buffer);
  3121.         } else {
  3122.                 // ...
  3123.         }
  3124. }
  3125.  
  3126. // seg009:1A51
  3127. palette_fade_type far *__pascal make_pal_buffer_fadein(surface_type *source_surface,int which_rows,int wait_time) {
  3128.         palette_fade_type far* palette_buffer;
  3129.         word curr_row;
  3130.         word var_8;
  3131.         word curr_row_mask;
  3132.         palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
  3133.         palette_buffer->which_rows = which_rows;
  3134.         palette_buffer->wait_time = wait_time;
  3135.         palette_buffer->fade_pos = 0x40;
  3136.         palette_buffer->proc_restore_free = &pal_restore_free_fadein;
  3137.         palette_buffer->proc_fade_frame = &fade_in_frame;
  3138.         read_palette_256(palette_buffer->original_pal);
  3139.         memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
  3140.         var_8 = 0;
  3141.         for (curr_row = 0, curr_row_mask = 1; curr_row < 0x10; ++curr_row, curr_row_mask<<=1) {
  3142.                 if (which_rows & curr_row_mask) {
  3143.                         memset_far(palette_buffer->faded_pal + (curr_row<<4), 0, sizeof(rgb_type[0x10]));
  3144.                         set_pal_arr(curr_row<<4, 0x10, NULL, (var_8++&3)==0);
  3145.                 }
  3146.         }
  3147.         //method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
  3148.         // for RGB
  3149.         //method_5_rect(&screen_rect, 0, 0);
  3150.         return palette_buffer;
  3151. }
  3152.  
  3153. // seg009:1B64
  3154. void __pascal far pal_restore_free_fadein(palette_fade_type far *palette_buffer) {
  3155.         set_pal_256(palette_buffer->original_pal);
  3156.         free_far(palette_buffer);
  3157.         // for RGB
  3158.         method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
  3159. }
  3160.  
  3161. // seg009:1B88
  3162. int __pascal far fade_in_frame(palette_fade_type far *palette_buffer) {
  3163.         rgb_type* faded_pal_ptr;
  3164.         word start;
  3165.         word column;
  3166.         rgb_type* original_pal_ptr;
  3167.         word current_row_mask;
  3168. //      void* var_12;
  3169.         /**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
  3170.         //printf("start ticks = %u\n",SDL_GetTicks());
  3171.         --palette_buffer->fade_pos;
  3172.         for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
  3173.                 if (palette_buffer->which_rows & current_row_mask) {
  3174.                         //var_12 = palette_buffer->
  3175.                         original_pal_ptr = palette_buffer->original_pal + start;
  3176.                         faded_pal_ptr = palette_buffer->faded_pal + start;
  3177.                         for (column = 0; column<0x10; ++column) {
  3178.                                 if (original_pal_ptr[column].r > palette_buffer->fade_pos) {
  3179.                                         ++faded_pal_ptr[column].r;
  3180.                                 }
  3181.                                 if (original_pal_ptr[column].g > palette_buffer->fade_pos) {
  3182.                                         ++faded_pal_ptr[column].g;
  3183.                                 }
  3184.                                 if (original_pal_ptr[column].b > palette_buffer->fade_pos) {
  3185.                                         ++faded_pal_ptr[column].b;
  3186.                                 }
  3187.                         }
  3188.                 }
  3189.         }
  3190.         column = 0;
  3191.         for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
  3192.                 if (palette_buffer->which_rows & current_row_mask) {
  3193.                         set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
  3194.                 }
  3195.         }
  3196.  
  3197.         int h = offscreen_surface->h;
  3198.         if (SDL_LockSurface(onscreen_surface_) != 0) {
  3199.                 sdlperror("SDL_LockSurface");
  3200.                 quit(1);
  3201.         }
  3202.         if (SDL_LockSurface(offscreen_surface) != 0) {
  3203.                 sdlperror("SDL_LockSurface");
  3204.                 quit(1);
  3205.         }
  3206.         int y,x;
  3207.         int on_stride = onscreen_surface_->pitch;
  3208.         int off_stride = offscreen_surface->pitch;
  3209.         int fade_pos = palette_buffer->fade_pos;
  3210.         for (y = 0; y < h; ++y) {
  3211.                 byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
  3212.                 byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
  3213.                 for (x = 0; x < on_stride; ++x) {
  3214.                         //if (*off_pixel_ptr > palette_buffer->fade_pos) *pixel_ptr += 4;
  3215.                         int v = *off_pixel_ptr - fade_pos*4;
  3216.                         if (v<0) v=0;
  3217.                         *on_pixel_ptr = v;
  3218.                         ++on_pixel_ptr; ++off_pixel_ptr;
  3219.                 }
  3220.         }
  3221.         SDL_UnlockSurface(onscreen_surface_);
  3222.         SDL_UnlockSurface(offscreen_surface);
  3223.  
  3224.         //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0); // debug
  3225.         request_screen_update();
  3226.  
  3227.         /**/do_simple_wait(1); // can interrupt fading of cutscene
  3228.         //do_wait(timer_1); // can interrupt fading of main title
  3229.         //printf("end ticks = %u\n",SDL_GetTicks());
  3230.         return palette_buffer->fade_pos == 0;
  3231. }
  3232.  
  3233. // seg009:1CC9
  3234. void __pascal far fade_out_2(int rows) {
  3235.         palette_fade_type far *palette_buffer;
  3236.         if (graphics_mode == gmMcgaVga) {
  3237.                 palette_buffer = make_pal_buffer_fadeout(rows, 2);
  3238.                 while (fade_out_frame(palette_buffer) == 0) {
  3239.                         pop_wait(timer_1, 0); // modified
  3240.                 }
  3241.                 pal_restore_free_fadeout(palette_buffer);
  3242.         } else {
  3243.                 // ...
  3244.         }
  3245. }
  3246.  
  3247. // seg009:1D28
  3248. palette_fade_type far *__pascal make_pal_buffer_fadeout(int which_rows,int wait_time) {
  3249.         palette_fade_type far *palette_buffer;
  3250.         palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
  3251.         palette_buffer->which_rows = which_rows;
  3252.         palette_buffer->wait_time = wait_time;
  3253.         palette_buffer->fade_pos = 00; // modified
  3254.         palette_buffer->proc_restore_free = &pal_restore_free_fadeout;
  3255.         palette_buffer->proc_fade_frame = &fade_out_frame;
  3256.         read_palette_256(palette_buffer->original_pal);
  3257.         memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
  3258.         // for RGB
  3259.         method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
  3260.         return palette_buffer;
  3261. }
  3262.  
  3263. // seg009:1DAF
  3264. void __pascal far pal_restore_free_fadeout(palette_fade_type far *palette_buffer) {
  3265.         surface_type* surface;
  3266.         surface = current_target_surface;
  3267.         current_target_surface = onscreen_surface_;
  3268.         draw_rect(&screen_rect, 0);
  3269.         current_target_surface = surface;
  3270.         set_pal_256(palette_buffer->original_pal);
  3271.         free_far(palette_buffer);
  3272.         // for RGB
  3273.         method_5_rect(&screen_rect, 0, 0);
  3274. }
  3275.  
  3276. // seg009:1DF7
  3277. int __pascal far fade_out_frame(palette_fade_type far *palette_buffer) {
  3278.         rgb_type* faded_pal_ptr;
  3279.         word start;
  3280.         word var_8;
  3281.         word column;
  3282.         word current_row_mask;
  3283.         byte* curr_color_ptr;
  3284.         var_8 = 1;
  3285.         ++palette_buffer->fade_pos; // modified
  3286.         /**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
  3287.         for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
  3288.                 if (palette_buffer->which_rows & current_row_mask) {
  3289.                         //var_12 = palette_buffer->
  3290.                         //original_pal_ptr = palette_buffer->original_pal + start;
  3291.                         faded_pal_ptr = palette_buffer->faded_pal + start;
  3292.                         for (column = 0; column<0x10; ++column) {
  3293.                                 curr_color_ptr = &faded_pal_ptr[column].r;
  3294.                                 if (*curr_color_ptr != 0) {
  3295.                                         --*curr_color_ptr;
  3296.                                         var_8 = 0;
  3297.                                 }
  3298.                                 curr_color_ptr = &faded_pal_ptr[column].g;
  3299.                                 if (*curr_color_ptr != 0) {
  3300.                                         --*curr_color_ptr;
  3301.                                         var_8 = 0;
  3302.                                 }
  3303.                                 curr_color_ptr = &faded_pal_ptr[column].b;
  3304.                                 if (*curr_color_ptr != 0) {
  3305.                                         --*curr_color_ptr;
  3306.                                         var_8 = 0;
  3307.                                 }
  3308.                         }
  3309.                 }
  3310.         }
  3311.         column = 0;
  3312.         for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
  3313.                 if (palette_buffer->which_rows & current_row_mask) {
  3314.                         set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
  3315.                 }
  3316.         }
  3317.  
  3318.         int h = offscreen_surface->h;
  3319.         if (SDL_LockSurface(onscreen_surface_) != 0) {
  3320.                 sdlperror("SDL_LockSurface");
  3321.                 quit(1);
  3322.         }
  3323.         if (SDL_LockSurface(offscreen_surface) != 0) {
  3324.                 sdlperror("SDL_LockSurface");
  3325.                 quit(1);
  3326.         }
  3327.         int y,x;
  3328.         int on_stride = onscreen_surface_->pitch;
  3329.         int off_stride = offscreen_surface->pitch;
  3330.         int fade_pos = palette_buffer->fade_pos;
  3331.         for (y = 0; y < h; ++y) {
  3332.                 byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
  3333.                 byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
  3334.                 for (x = 0; x < on_stride; ++x) {
  3335.                         //if (*pixel_ptr >= 4) *pixel_ptr -= 4;
  3336.                         int v = *off_pixel_ptr - fade_pos*4;
  3337.                         if (v<0) v=0;
  3338.                         *on_pixel_ptr = v;
  3339.                         ++on_pixel_ptr; ++off_pixel_ptr;
  3340.                 }
  3341.         }
  3342.         SDL_UnlockSurface(onscreen_surface_);
  3343.         SDL_UnlockSurface(offscreen_surface);
  3344.  
  3345.         request_screen_update();
  3346.  
  3347.         /**/do_simple_wait(1); // can interrupt fading of cutscene
  3348.         //do_wait(timer_1); // can interrupt fading of main title
  3349.         return var_8;
  3350. }
  3351.  
  3352. // seg009:1F28
  3353. void __pascal far read_palette_256(rgb_type far *target) {
  3354.         int i;
  3355.         for (i = 0; i < 256; ++i) {
  3356.                 target[i] = palette[i];
  3357.         }
  3358. }
  3359.  
  3360. // seg009:1F5E
  3361. void __pascal far set_pal_256(rgb_type far *source) {
  3362.         int i;
  3363.         for (i = 0; i < 256; ++i) {
  3364.                 palette[i] = source[i];
  3365.         }
  3366. }
  3367. #endif // USE_FADE
  3368.  
  3369. void set_chtab_palette(chtab_type* chtab, byte* colors, int n_colors) {
  3370.         if (chtab != NULL) {
  3371.                 SDL_Color* scolors = (SDL_Color*) malloc(n_colors*sizeof(SDL_Color));
  3372.                 int i;
  3373.                 //printf("scolors\n",i);
  3374.                 for (i = 0; i < n_colors; ++i) {
  3375.                         //printf("i=%d\n",i);
  3376.                         scolors[i].r = *colors << 2; ++colors;
  3377.                         scolors[i].g = *colors << 2; ++colors;
  3378.                         scolors[i].b = *colors << 2; ++colors;
  3379.                         scolors[i].a = SDL_ALPHA_OPAQUE; // the SDL2 SDL_Color struct has an alpha component
  3380.                 }
  3381.  
  3382.                 // Color 0 of the palette data is not used, it is replaced by the background color.
  3383.                 // Needed for correct alternate colors (v1.3) of level 8.
  3384.                 scolors[0].r = scolors[0].g = scolors[0].b = 0;
  3385.  
  3386.                 //printf("setcolors\n",i);
  3387.                 for (i = 0; i < chtab->n_images; ++i) {
  3388.                         //printf("i=%d\n",i);
  3389.                         image_type* current_image = chtab->images[i];
  3390.                         if (current_image != NULL) {
  3391.  
  3392.                                 int n_colors_to_be_set = n_colors;
  3393.                                 SDL_Palette* current_palette = current_image->format->palette;
  3394.  
  3395.                                 // one of the guard images (i=25) is only a single transparent pixel
  3396.                                 // this caused SDL_SetPaletteColors to fail, I think because that palette contains only 2 colors
  3397.                                 if (current_palette->ncolors < n_colors_to_be_set)
  3398.                                         n_colors_to_be_set = current_palette->ncolors;
  3399.                                 if (SDL_SetPaletteColors(current_palette, scolors, 0, n_colors_to_be_set) != 0) {
  3400.                                         sdlperror("SDL_SetPaletteColors");
  3401.                                         quit(1);
  3402.                                 }
  3403.                         }
  3404.                 }
  3405.                 free(scolors);
  3406.         }
  3407. }
  3408.  
  3409. int has_timer_stopped(int index) {
  3410. #ifdef USE_COMPAT_TIMER
  3411.         return wait_time[index] == 0;
  3412. #else
  3413.         return timer_stopped[index];
  3414. #endif
  3415. }
  3416.