Subversion Repositories Games.Prince of Persia

Rev

Rev 1 | Rev 6 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
        //printf("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
#endif
1718
        digi_audiospec = desired;
1719
}
1720
 
1721
#ifdef USE_MIXER
1722
const int sound_channel = 0;
1723
const int max_sound_id = 58;
1724
char** sound_names = NULL;
1725
 
1726
void load_sound_names() {
1727
        const char* names_path = locate_file("data/music/names.txt");
1728
        if (sound_names != NULL) return;
1729
        FILE* fp = fopen(names_path,"rt");
1730
        if (fp==NULL) return;
1731
        sound_names = (char**) calloc(sizeof(char*) * max_sound_id, 1);
1732
        while (!feof(fp)) {
1733
                int index;
1734
                char name[POP_MAX_PATH];
1735
                if (fscanf(fp, "%d=%255s\n", &index, /*sizeof(name)-1,*/ name) != 2) {
1736
                        perror(names_path);
1737
                        continue;
1738
                }
1739
                //if (feof(fp)) break;
1740
                //printf("sound_names[%d] = %s\n",index,name);
1741
                if (index >= 0 && index < max_sound_id) {
1742
                        sound_names[index] = strdup(name);
1743
                }
1744
        }
1745
        fclose(fp);
1746
}
1747
 
1748
char* sound_name(int index) {
1749
        if (sound_names != NULL && index >= 0 && index < max_sound_id) {
1750
                return sound_names[index];
1751
        } else {
1752
                return NULL;
1753
        }
1754
}
1755
 
1756
void convert_digi_sound(sound_buffer_type *buffer);
1757
#endif
1758
 
1759
sound_buffer_type* load_sound(int index) {
1760
        sound_buffer_type* result = NULL;
1761
#ifdef USE_MIXER
1762
        //printf("load_sound(%d)\n", index);
1763
        init_digi();
1764
        if (!digi_unavailable && result == NULL && index >= 0 && index < max_sound_id) {
1765
                //printf("Trying to load from music folder\n");
1766
 
1767
                //load_sound_names();  // Moved to load_sounds()
1768
                if (sound_names != NULL && sound_name(index) != NULL) {
1769
                        //printf("Loading from music folder\n");
1770
                        const char* exts[]={"ogg","mp3","flac","wav"};
1771
                        int i;
1772
                        for (i = 0; i < COUNT(exts); ++i) {
1773
                                char filename[POP_MAX_PATH];
1774
                                const char* ext=exts[i];
1775
 
1776
                                snprintf(filename, sizeof(filename), "data/music/%s.%s", sound_name(index), ext);
1777
                const char* located_filename = locate_file(filename);
1778
                                // Skip nonexistent files:
1779
                                if (!file_exists(located_filename))
1780
                                        continue;
1781
                                //printf("Trying to load %s\n", filename);
1782
                                Mix_Music* music = Mix_LoadMUS(located_filename);
1783
                                if (music == NULL) {
1784
                                        sdlperror(located_filename);
1785
                                        //sdlperror("Mix_LoadWAV");
1786
                                        continue;
1787
                                }
1788
                                //printf("Loaded sound from %s\n", filename);
1789
                                result = malloc(sizeof(sound_buffer_type));
1790
                                result->type = sound_music;
1791
                                result->music = music;
1792
                                break;
1793
                        }
1794
                } else {
1795
                        //printf("sound_names = %p\n", sound_names);
1796
                        //printf("sound_names[%d] = %p\n", index, sound_name(index));
1797
                }
1798
        }
1799
#endif
1800
        if (result == NULL) {
1801
                //printf("Trying to load from DAT\n");
1802
                result = (sound_buffer_type*) load_from_opendats_alloc(index + 10000, "bin", NULL, NULL);
1803
        }
1804
#ifdef USE_MIXER
1805
        if (result == NULL) {
1806
                fprintf(stderr, "Failed to load sound %d '%s'\n", index, sound_name(index));
1807
        } else {
1808
                // Convert waves to mixer chunks in advance.
1809
                if ((result->type & 7) == sound_digi) {
1810
                        convert_digi_sound(result);
1811
                }
1812
        }
1813
#endif
1814
        return result;
1815
}
1816
 
1817
#ifdef USE_MIXER
1818
void __pascal far play_chunk_sound(sound_buffer_type far *buffer) {
1819
        //if (!is_sound_on) return;
1820
        init_digi();
1821
        if (digi_unavailable) return;
1822
        stop_sounds();
1823
        //printf("playing chunk sound %p\n", buffer);
1824
        if (Mix_PlayChannel(sound_channel, buffer->chunk, 0) == -1) {
1825
                sdlperror("Mix_PlayChannel");
1826
        }
1827
        digi_playing = 1;
1828
}
1829
 
1830
void __pascal far play_music_sound(sound_buffer_type far *buffer) {
1831
        init_digi();
1832
        if (digi_unavailable) return;
1833
        stop_sounds();
1834
        if (Mix_PlayMusic(buffer->music, 0) == -1) {
1835
                sdlperror("Mix_PlayMusic");
1836
        }
1837
        digi_playing = 1;
1838
}
1839
 
1840
Uint32 fourcc(char* string) {
1841
        return *(Uint32*)string;
1842
}
1843
#endif
1844
 
1845
int wave_version = -1;
1846
 
1847
typedef struct waveinfo_type {
1848
        int sample_rate, sample_size, sample_count;
1849
        byte* samples;
1850
} waveinfo_type;
1851
 
1852
bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo);
1853
 
1854
bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo) {
1855
        int version = wave_version;
1856
        if (version == -1) {
1857
                // Determine the version of the wave data.
1858
                version = 0;
1859
                if (buffer->digi.sample_size == 8) version += 1;
1860
                if (buffer->digi_new.sample_size == 8) version += 2;
1861
                if (version == 1 || version == 2) wave_version = version;
1862
        }
1863
 
1864
        switch (version) {
1865
                case 1: // 1.0 and 1.1
1866
                        waveinfo->sample_rate = buffer->digi.sample_rate;
1867
                        waveinfo->sample_size = buffer->digi.sample_size;
1868
                        waveinfo->sample_count = buffer->digi.sample_count;
1869
                        waveinfo->samples = buffer->digi.samples;
1870
                        return true;
1871
                case 2: // 1.3 and 1.4 (and PoP2)
1872
                        waveinfo->sample_rate = buffer->digi_new.sample_rate;
1873
                        waveinfo->sample_size = buffer->digi_new.sample_size;
1874
                        waveinfo->sample_count = buffer->digi_new.sample_count;
1875
                        waveinfo->samples = buffer->digi_new.samples;
1876
                        return true;
1877
                case 3: // ambiguous
1878
                        printf("Warning: Ambiguous wave version.\n");
1879
                        return false;
1880
                default: // case 0, unknown
1881
                        printf("Warning: Can't determine wave version.\n");
1882
                        return false;
1883
        }
1884
}
1885
 
1886
#ifndef USE_MIXER
1887
// seg009:74F0
1888
void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
1889
        //if (!is_sound_on) return;
1890
        init_digi();
1891
        if (digi_unavailable) return;
1892
        //stop_digi();
1893
        stop_sounds();
1894
        //printf("play_digi_sound(): called\n");
1895
 
1896
        waveinfo_type waveinfo;
1897
        if (false == determine_wave_version(buffer, &waveinfo)) return;
1898
 
1899
        SDL_AudioCVT cvt;
1900
        memset(&cvt, 0, sizeof(cvt));
1901
        int result = SDL_BuildAudioCVT(&cvt,
1902
                AUDIO_U8, 1, waveinfo.sample_rate,
1903
                digi_audiospec->format, digi_audiospec->channels, digi_audiospec->freq
1904
        );
1905
        // The case of result == 0 is undocumented, but it may occur.
1906
        if (result != 1 && result != 0) {
1907
                sdlperror("SDL_BuildAudioCVT");
1908
                printf("(returned %d)\n", result);
1909
                quit(1);
1910
        }
1911
        int dlen = waveinfo.sample_count; // if format is AUDIO_U8
1912
        cvt.buf = (Uint8*) malloc(dlen * cvt.len_mult);
1913
        memcpy(cvt.buf, waveinfo.samples, dlen);
1914
        cvt.len = dlen;
1915
        if (SDL_ConvertAudio(&cvt) != 0) {
1916
                sdlperror("SDL_ConvertAudio");
1917
                quit(1);
1918
        }
1919
 
1920
        SDL_LockAudio();
1921
        digi_buffer = cvt.buf;
1922
        digi_playing = 1;
1923
//      digi_remaining_length = sample_count;
1924
//      digi_remaining_pos = samples;
1925
        digi_remaining_length = cvt.len_cvt;
1926
        digi_remaining_pos = digi_buffer;
1927
        SDL_UnlockAudio();
1928
        SDL_PauseAudio(0);
1929
}
1930
#else
1931
void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
1932
        printf("Warning: Tried to play a digi sound without converting it to a mixer chunk first!\n");
1933
}
1934
 
1935
void convert_digi_sound(sound_buffer_type *buffer) {
1936
        waveinfo_type waveinfo;
1937
        if (false == determine_wave_version(buffer, &waveinfo)) return;
1938
 
1939
        // Convert the DAT sound to WAV, so the Mixer can load it.
1940
        int size = waveinfo.sample_count;
1941
        int rounded_size = (size+1)&(~1);
1942
        int alloc_size = sizeof(WAV_header_type) + rounded_size;
1943
        WAV_header_type* wav_data = malloc(alloc_size);
1944
        wav_data->ChunkID = fourcc("RIFF");
1945
        wav_data->ChunkSize = 36 + rounded_size;
1946
        wav_data->Format = fourcc("WAVE");
1947
        wav_data->Subchunk1ID = fourcc("fmt ");
1948
        wav_data->Subchunk1Size = 16;
1949
        wav_data->AudioFormat = 1; // PCM
1950
        wav_data->NumChannels = 1; // Mono
1951
        wav_data->SampleRate = waveinfo.sample_rate;
1952
        wav_data->BitsPerSample = waveinfo.sample_size;
1953
        wav_data->ByteRate = wav_data->SampleRate * wav_data->NumChannels * wav_data->BitsPerSample/8;
1954
        wav_data->BlockAlign = wav_data->NumChannels * wav_data->BitsPerSample/8;
1955
        wav_data->Subchunk2ID = fourcc("data");
1956
        wav_data->Subchunk2Size = size;
1957
        memcpy(wav_data->Data, waveinfo.samples, size);
1958
        SDL_RWops* rw = SDL_RWFromConstMem(wav_data, alloc_size);
1959
        Mix_Chunk *chunk = Mix_LoadWAV_RW(rw, 1);
1960
        if (chunk == NULL) {
1961
                FILE* fp = fopen("dump.wav","wb");
1962
                fwrite(wav_data,alloc_size,1,fp);
1963
                fclose(fp);
1964
        }
1965
        free(wav_data);
1966
        if (chunk == NULL) {
1967
                sdlperror("Mix_LoadWAV_RW");
1968
                return;
1969
        }
1970
        buffer->type = sound_chunk;
1971
        buffer->chunk = chunk;
1972
}
1973
#endif
1974
 
1975
void free_sound(sound_buffer_type far *buffer) {
1976
        if (buffer == NULL) return;
1977
#ifdef USE_MIXER
1978
        if (buffer->type == sound_chunk) {
1979
                Mix_FreeChunk(buffer->chunk);
1980
        }
1981
        if (buffer->type == sound_music) {
1982
                Mix_FreeMusic(buffer->music);
1983
        }
1984
#endif
1985
        free(buffer);
1986
}
1987
 
1988
// seg009:7220
1989
void __pascal far play_sound_from_buffer(sound_buffer_type far *buffer) {
1990
 
1991
#ifdef USE_REPLAY
1992
        if (replaying && skipping_replay) return;
1993
#endif
1994
 
1995
        // stub
1996
        if (buffer == NULL) {
1997
                printf("Tried to play NULL sound.\n");
1998
                //quit(1);
1999
                return;
2000
        }
2001
        switch (buffer->type & 7) {
2002
                case sound_speaker:
2003
                        play_speaker_sound(buffer);
2004
                break;
2005
                case sound_digi:
2006
                        play_digi_sound(buffer);
2007
                break;
2008
#ifdef USE_MIXER
2009
                case sound_chunk:
2010
                        play_chunk_sound(buffer);
2011
                break;
2012
                case sound_music:
2013
                        play_music_sound(buffer);
2014
                break;
2015
#endif
2016
                default:
2017
                        printf("Tried to play unimplemented sound type %d.\n", buffer->type);
2018
                        quit(1);
2019
                break;
2020
        }
2021
}
2022
 
2023
// seg009:7273
2024
void __pascal far turn_sound_on_off(byte new_state) {
2025
        // stub
2026
        is_sound_on = new_state;
2027
        //if (!is_sound_on) stop_sounds();
2028
#ifdef USE_MIXER
2029
        init_digi();
2030
        if (digi_unavailable) return;
2031
        Mix_Volume(-1, is_sound_on ? MIX_MAX_VOLUME : 0);
2032
        Mix_VolumeMusic(is_sound_on ? MIX_MAX_VOLUME : 0);
2033
#endif
2034
}
2035
 
2036
// seg009:7299
2037
int __pascal far check_sound_playing() {
2038
        return speaker_playing || digi_playing || midi_playing;
2039
}
2040
 
2041
void window_resized() {
2042
#if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
2043
        if (use_integer_scaling) {
2044
                int window_width, window_height;
2045
                SDL_GetWindowSize(window_, &window_width, &window_height);
2046
                int render_width, render_height;
2047
                SDL_RenderGetLogicalSize(renderer_, &render_width, &render_height);
2048
                // Disable integer scaling if it would result in downscaling.
2049
                // Because then the only suitable integer scaling factor is zero, i.e. the picture disappears.
2050
                SDL_bool makes_sense = (window_width >= render_width && window_height >= render_height);
2051
                SDL_RenderSetIntegerScale(renderer_, makes_sense);
2052
        }
2053
#endif
2054
}
2055
 
2 pmbaty 2056
void xbrz_scale24 (size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, int yFirst, int yLast);
1 pmbaty 2057
static void *hqx_pixels32 = NULL;
2058
static void *hqx_resized_pixels32 = NULL;
2059
static void *hqx_resized_pixels24 = NULL;
2 pmbaty 2060
int filter_level = 1;
1 pmbaty 2061
 
2062
// seg009:38ED
2063
void __pascal far set_gr_mode(byte grmode) {
2064
        if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE |
2065
                     SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC ) != 0) {
2066
                sdlperror("SDL_Init");
2067
                quit(1);
2068
        }
2069
 
2070
        //SDL_EnableUNICODE(1); //deprecated
2071
        Uint32 flags = 0;
2072
        if (!start_fullscreen) start_fullscreen = check_param("full") != NULL;
2073
        if (start_fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
2074
        flags |= SDL_WINDOW_RESIZABLE;
2075
 
2076
        // Should use different default window dimensions when using 4:3 aspect ratio
2077
        if (use_correct_aspect_ratio && pop_window_width == 640 && pop_window_height == 400) {
2078
                pop_window_height = 480;
2079
        }
2080
 
2081
#ifdef USE_REPLAY
2082
        if (!is_validate_mode) // run without a window if validating a replay
2083
#endif
2084
        window_ = SDL_CreateWindow(WINDOW_TITLE,
2085
                                   SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
2086
                                   pop_window_width, pop_window_height, flags);
2087
        renderer_ = SDL_CreateRenderer(window_, -1 , SDL_RENDERER_ACCELERATED );
2088
        if (use_integer_scaling) {
2089
#if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
2090
                SDL_RenderSetIntegerScale(renderer_, SDL_TRUE);
2091
#else
2092
                printf("Warning: You need to compile with SDL 2.0.5 or newer for the use_integer_scaling option.\n");
2093
#endif
2094
        }
2095
 
2096
        SDL_Surface* icon = IMG_Load(locate_file("data/icon.png"));
2097
        if (icon == NULL) {
2098
                sdlperror("Could not load icon");
2099
        } else {
2100
                SDL_SetWindowIcon(window_, icon);
2101
        }
2102
 
2103
        // Allow us to use a consistent set of screen co-ordinates, even if the screen size changes
2104
        if (use_correct_aspect_ratio) {
2105
                SDL_RenderSetLogicalSize(renderer_, 320*5, 200*6);
2106
        } else {
2107
                SDL_RenderSetLogicalSize(renderer_, 320, 200);
2108
        }
2109
 
2110
        window_resized();
2111
 
2112
        /* Migration to SDL2: everything is still blitted to onscreen_surface_, however:
2113
         * SDL2 renders textures to the screen instead of surfaces; so for now, every screen
2114
         * update causes the onscreen_surface_ to be copied into sdl_texture_, which is
2115
         * subsequently displayed; awaits a better refactoring!
2116
         * The function handling the screen updates is request_screen_update()
2117
         * */
2118
        onscreen_surface_ = SDL_CreateRGBSurface(0, 320, 200, 24, 0xFF, 0xFF << 8, 0xFF << 16, 0) ;
2119
 
2120
        hqx_pixels32 = (void *) malloc (320 * 200 * 4);
2121
        if (hqx_pixels32 == NULL) { sdlperror ("hqx original 32bpp image buffer allocation failure"); quit (1); }
2 pmbaty 2122
        hqx_resized_pixels32 = (void *) realloc (hqx_resized_pixels32, 6 * 320 * 6 * 200 * 4);
1 pmbaty 2123
        if (hqx_resized_pixels32 == NULL) { sdlperror ("hqx resized 32bpp image buffer allocation failure"); quit (1); }
2 pmbaty 2124
        hqx_resized_pixels24 = (void *) realloc (hqx_resized_pixels24, 6 * 320 * 6 * 200 * 3);
1 pmbaty 2125
        if (hqx_resized_pixels24 == NULL) { sdlperror ("hqx resized 24bpp image buffer allocation failure"); quit (1); }
2126
 
2 pmbaty 2127
        sdl_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 320 * filter_level, 200 * filter_level);
1 pmbaty 2128
        screen_updates_suspended = 0;
2129
 
2130
        if (onscreen_surface_ == NULL) {
2131
                sdlperror("SDL_SetVideoMode");
2132
                quit(1);
2133
        }
2134
        if (start_fullscreen) {
2135
                SDL_ShowCursor(SDL_DISABLE);
2136
        }
2137
 
2138
 
2139
        //SDL_WM_SetCaption(WINDOW_TITLE, NULL);
2140
//      if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) != 0) {  //deprecated
2141
//              sdlperror("SDL_EnableKeyRepeat");
2142
//              quit(1);
2143
//      }
2144
        graphics_mode = gmMcgaVga;
2145
#ifdef USE_TEXT
2146
        load_font();
2147
#endif
2148
}
2149
 
2150
void request_screen_update() {
2151
 
2152
#ifdef USE_REPLAY
2153
        if (replaying && skipping_replay) return;
2154
#endif
2155
        if (!screen_updates_suspended) {
2 pmbaty 2156
                if (filter_level > 1)
1 pmbaty 2157
                {
2158
                        uint8_t *hqx_24bpp_ptr;
2159
                        uint8_t *hqx_32bpp_ptr;
2160
 
2161
                        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; }
2 pmbaty 2162
                        xbrz_scale24 (filter_level, hqx_pixels32, hqx_resized_pixels32, 320, 200, 0, 200);
2163
                        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++; }
1 pmbaty 2164
 
2 pmbaty 2165
                        SDL_UpdateTexture(sdl_texture_, NULL, hqx_resized_pixels24, onscreen_surface_->pitch * filter_level);
1 pmbaty 2166
                }
2167
                else
2168
                        SDL_UpdateTexture(sdl_texture_, NULL, onscreen_surface_->pixels, onscreen_surface_->pitch);
2169
 
2170
                SDL_RenderClear(renderer_);
2171
                SDL_RenderCopy(renderer_, sdl_texture_, NULL, NULL);
2172
                SDL_RenderPresent(renderer_);
2173
        }
2174
}
2175
 
2176
// seg009:9289
2177
void __pascal far set_pal_arr(int start,int count,const rgb_type far *array,int vsync) {
2178
        // stub
2179
        int i;
2180
        for (i = 0; i < count; ++i) {
2181
                if (array) {
2182
                        set_pal(start + i, array[i].r, array[i].g, array[i].b, vsync);
2183
                } else {
2184
                        set_pal(start + i, 0, 0, 0, vsync);
2185
                }
2186
        }
2187
}
2188
 
2189
rgb_type palette[256];
2190
 
2191
// seg009:92DF
2192
void __pascal far set_pal(int index,int red,int green,int blue,int vsync) {
2193
        // stub
2194
        //palette[index] = ((red&0x3F)<<2)|((green&0x3F)<<2<<8)|((blue&0x3F)<<2<<16);
2195
        palette[index].r = red;
2196
        palette[index].g = green;
2197
        palette[index].b = blue;
2198
}
2199
 
2200
// seg009:969C
2201
int __pascal far add_palette_bits(byte n_colors) {
2202
        // stub
2203
        return 0;
2204
}
2205
 
2206
// seg009:9C36
2207
int __pascal far find_first_pal_row(int which_rows_mask) {
2208
        word which_row = 0;
2209
        word row_mask = 1;
2210
        do {
2211
                if (row_mask & which_rows_mask) {
2212
                        return which_row;
2213
                }
2214
                ++which_row;
2215
                row_mask <<= 1;
2216
        } while (which_row < 16);
2217
        return 0;
2218
}
2219
 
2220
// seg009:9C6C
2221
int __pascal far get_text_color(int cga_color,int low_half,int high_half_mask) {
2222
        if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
2223
                return cga_color;
2224
        } else if (graphics_mode == gmMcgaVga && high_half_mask != 0) {
2225
                return (find_first_pal_row(high_half_mask) << 4) + low_half;
2226
        } else {
2227
                return low_half;
2228
        }
2229
}
2230
 
2231
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) {
2232
        char image_filename[POP_MAX_PATH];
2233
        FILE* fp = NULL;
2234
        dat_type* pointer;
2235
        *result = data_none;
2236
        // Go through all open DAT files.
2237
        for (pointer = dat_chain_ptr; fp == NULL && pointer != NULL; pointer = pointer->next_dat) {
2238
                *out_pointer = pointer;
2239
                if (pointer->handle != NULL) {
2240
                        // If it's an actual DAT file:
2241
                        fp = pointer->handle;
2242
                        dat_table_type* dat_table = pointer->dat_table;
2243
                        int i;
2244
                        for (i = 0; i < dat_table->res_count; ++i) {
2245
                                if (dat_table->entries[i].id == resource_id) {
2246
                                        break;
2247
                                }
2248
                        }
2249
                        if (i < dat_table->res_count) {
2250
                                // found
2251
                                *result = data_DAT;
2252
                                *size = dat_table->entries[i].size;
2253
                                if (fseek(fp, dat_table->entries[i].offset, SEEK_SET) ||
2254
                                    fread(checksum, 1, 1, fp) != 1) {
2255
                                        perror(pointer->filename);
2256
                                        fp = NULL;
2257
                                }
2258
                        } else {
2259
                                // not found
2260
                                fp = NULL;
2261
                        }
2262
                } else {
2263
                        // If it's a directory:
2264
                        char filename_no_ext[POP_MAX_PATH];
2265
                        // strip the .DAT file extension from the filename (use folders simply named TITLE, KID, VPALACE, etc.)
2266
                        strncpy(filename_no_ext, pointer->filename, sizeof(filename_no_ext));
2267
                        size_t len = strlen(filename_no_ext);
2268
                        if (len >= 5 && filename_no_ext[len-4] == '.') {
2269
                                filename_no_ext[len-4] = '\0'; // terminate, so ".DAT" is deleted from the filename
2270
                        }
2271
                        snprintf(image_filename,sizeof(image_filename),"data/%s/res%d.%s",filename_no_ext, resource_id, extension);
2272
                        if (!use_custom_levelset) {
2273
                                //printf("loading (binary) %s",image_filename);
2274
                                fp = fopen(locate_file(image_filename), "rb");
2275
                        }
2276
                        else {
2277
                                char image_filename_mod[POP_MAX_PATH];
2278
                                // before checking data/, first try mods/MODNAME/data/
2279
                                snprintf(image_filename_mod, sizeof(image_filename_mod), "mods/%s/%s", levelset_name, image_filename);
2280
                                //printf("loading (binary) %s",image_filename_mod);
2281
                                fp = fopen(locate_file(image_filename_mod), "rb");
2282
                                if (fp == NULL) {
2283
                                        fp = fopen(locate_file(image_filename), "rb");
2284
                                }
2285
                        }
2286
 
2287
                        if (fp != NULL) {
2288
                                struct stat buf;
2289
                                if (fstat(fileno(fp), &buf) == 0) {
2290
                                        *result = data_directory;
2291
                                        *size = buf.st_size;
2292
                                } else {
2293
                                        perror(image_filename);
2294
                                        fclose(fp);
2295
                                        fp = NULL;
2296
                                }
2297
                        }
2298
                }
2299
        }
2300
        *out_fp = fp;
2301
        if (fp == NULL) {
2302
                *result = data_none;
2303
//              printf(" FAILED\n");
2304
                //return NULL;
2305
        }
2306
        //...
2307
}
2308
 
2309
// seg009:9F34
2310
void __pascal far close_dat(dat_type far *pointer) {
2311
        dat_type** prev = &dat_chain_ptr;
2312
        dat_type* curr = dat_chain_ptr;
2313
        while (curr != NULL) {
2314
                if (curr == pointer) {
2315
                        *prev = curr->next_dat;
2316
                        if (curr->handle) fclose(curr->handle);
2317
                        if (curr->dat_table) free(curr->dat_table);
2318
                        free(curr);
2319
                        return;
2320
                }
2321
                curr = curr->next_dat;
2322
                prev = &((*prev)->next_dat);
2323
        }
2324
        // stub
2325
}
2326
 
2327
// seg009:9F80
2328
void far *__pascal load_from_opendats_alloc(int resource, const char* extension, data_location* out_result, int* out_size) {
2329
        // stub
2330
        //printf("id = %d\n",resource);
2331
        dat_type* pointer;
2332
        data_location result;
2333
        byte checksum;
2334
        int size;
2335
        FILE* fp = NULL;
2336
        load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
2337
        if (out_result != NULL) *out_result = result;
2338
        if (out_size != NULL) *out_size = size;
2339
        if (result == data_none) return NULL;
2340
        void* area = malloc(size);
2341
        //read(fd, area, size);
2342
        if (fread(area, size, 1, fp) != 1) {
2343
                fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n",
2344
                        __func__, pointer->filename, resource,
2345
                        size, strerror(errno));
2346
                free(area);
2347
                area = NULL;
2348
        }
2349
        if (result == data_directory) fclose(fp);
2350
        /* XXX: check checksum */
2351
        return area;
2352
}
2353
 
2354
// seg009:A172
2355
int __pascal far load_from_opendats_to_area(int resource,void far *area,int length, const char* extension) {
2356
        // stub
2357
        //return 0;
2358
        dat_type* pointer;
2359
        data_location result;
2360
        byte checksum;
2361
        int size;
2362
        FILE* fp = NULL;
2363
        load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
2364
        if (result == data_none) return 0;
2365
        if (fread(area, MIN(size, length), 1, fp) != 1) {
2366
                fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n",
2367
                        __func__, pointer->filename, resource,
2368
                        size, strerror(errno));
2369
                memset(area, 0, MIN(size, length));
2370
        }
2371
        if (result == data_directory) fclose(fp);
2372
        /* XXX: check checksum */
2373
        return 0;
2374
}
2375
 
2376
// SDL-specific implementations
2377
 
2378
void rect_to_sdlrect(const rect_type* rect, SDL_Rect* sdlrect) {
2379
        sdlrect->x = rect->left;
2380
        sdlrect->y = rect->top;
2381
        sdlrect->w = rect->right - rect->left;
2382
        sdlrect->h = rect->bottom - rect->top;
2383
}
2384
 
2385
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) {
2386
        SDL_Rect src_rect;
2387
        rect_to_sdlrect(source_rect, &src_rect);
2388
        SDL_Rect dest_rect;
2389
        rect_to_sdlrect(target_rect, &dest_rect);
2390
 
2391
        if (blit == blitters_0_no_transp) {
2392
                // Disable transparency.
2393
                if (SDL_SetColorKey(source_surface, 0, 0) != 0) {
2394
                        sdlperror("SDL_SetColorKey");
2395
                        quit(1);
2396
                }
2397
        } else {
2398
                // Enable transparency.
2399
                if (SDL_SetColorKey(source_surface, SDL_TRUE, 0) != 0) {
2400
                        sdlperror("SDL_SetColorKey");
2401
                        quit(1);
2402
                }
2403
        }
2404
        if (SDL_BlitSurface(source_surface, &src_rect, target_surface, &dest_rect) != 0) {
2405
                sdlperror("SDL_BlitSurface");
2406
                quit(1);
2407
        }
2408
        if (target_surface == onscreen_surface_) {
2409
                request_screen_update();
2410
        }
2411
}
2412
 
2413
image_type far * __pascal far method_3_blit_mono(image_type far *image,int xpos,int ypos,int blitter,byte color) {
2414
        int w = image->w;
2415
        int h = image->h;
2416
        if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
2417
                sdlperror("SDL_SetColorKey");
2418
                quit(1);
2419
        }
2420
        SDL_Surface* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
2421
 
2422
        SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_NONE);
2423
        /* Causes problems with SDL 2.0.5 (see #105)
2424
        if (SDL_SetColorKey(colored_image, SDL_TRUE, 0) != 0) {
2425
                sdlperror("SDL_SetColorKey");
2426
                quit(1);
2427
        }
2428
        */
2429
 
2430
        if (SDL_LockSurface(colored_image) != 0) {
2431
                sdlperror("SDL_LockSurface");
2432
                quit(1);
2433
        }
2434
 
2435
        int y,x;
2436
        rgb_type palette_color = palette[color];
2437
        uint32_t rgb_color = SDL_MapRGB(colored_image->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) & 0xFFFFFF;
2438
        int stride = colored_image->pitch;
2439
        for (y = 0; y < h; ++y) {
2440
                uint32_t* pixel_ptr = (uint32_t*) ((byte*)colored_image->pixels + stride * y);
2441
                for (x = 0; x < w; ++x) {
2442
                        // set RGB but leave alpha
2443
                        *pixel_ptr = (*pixel_ptr & 0xFF000000) | rgb_color;
2444
                        //printf("pixel x=%d, y=%d, color = 0x%8x\n", x, y, *pixel_ptr);
2445
                        ++pixel_ptr;
2446
                }
2447
        }
2448
        SDL_UnlockSurface(colored_image);
2449
 
2450
        SDL_Rect src_rect = {0, 0, image->w, image->h};
2451
        SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
2452
 
2453
        SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_BLEND);
2454
        SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_BLEND);
2455
        SDL_SetSurfaceAlphaMod(colored_image, 255);
2456
        if (SDL_BlitSurface(colored_image, &src_rect, current_target_surface, &dest_rect) != 0) {
2457
                sdlperror("SDL_BlitSurface");
2458
                quit(1);
2459
        }
2460
        SDL_FreeSurface(colored_image);
2461
 
2462
        return image;
2463
}
2464
 
2465
// Workaround for a bug in SDL2 (before v2.0.4):
2466
// https://bugzilla.libsdl.org/show_bug.cgi?id=2986
2467
// SDL_FillRect onto a 24-bit surface swaps Red and Blue component
2468
 
2469
bool RGB24_bug_checked = false;
2470
bool RGB24_bug_affected;
2471
 
2472
bool RGB24_bug_check() {
2473
        if (!RGB24_bug_checked) {
2474
                // Check if the bug occurs in this version of SDL.
2475
                SDL_Surface* test_surface = SDL_CreateRGBSurface(0, 1, 1, 24, 0, 0, 0, 0);
2476
                if (NULL == test_surface) sdlperror("SDL_CreateSurface in RGB24_bug_check");
2477
                // Fill with red.
2478
                SDL_FillRect(test_surface, NULL, SDL_MapRGB(test_surface->format, 0xFF, 0, 0));
2479
                if (0 != SDL_LockSurface(test_surface)) sdlperror("SDL_LockSurface in RGB24_bug_check");
2480
                // Read red component of pixel.
2481
                RGB24_bug_affected = (*(Uint32*)test_surface->pixels & test_surface->format->Rmask) == 0;
2482
                SDL_UnlockSurface(test_surface);
2483
                SDL_FreeSurface(test_surface);
2484
                RGB24_bug_checked = true;
2485
        }
2486
        return RGB24_bug_affected;
2487
}
2488
 
2489
int safe_SDL_FillRect(SDL_Surface* dst, const SDL_Rect* rect, Uint32 color) {
2490
        if (dst->format->BitsPerPixel == 24 && RGB24_bug_check()) {
2491
                // In the buggy version, SDL_FillRect swaps R and B, so we swap it once more.
2492
                color = ((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16);
2493
        }
2494
        return SDL_FillRect(dst, rect, color);
2495
}
2496
// End of workaround.
2497
 
2498
const rect_type far * __pascal far method_5_rect(const rect_type far *rect,int blit,byte color) {
2499
        SDL_Rect dest_rect;
2500
        rect_to_sdlrect(rect, &dest_rect);
2501
        rgb_type palette_color = palette[color];
2502
#ifndef USE_ALPHA
2503
        uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2);
2504
#else
2505
        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);
2506
#endif
2507
        if (safe_SDL_FillRect(current_target_surface, &dest_rect, rgb_color) != 0) {
2508
                sdlperror("SDL_FillRect");
2509
                quit(1);
2510
        }
2511
        if (current_target_surface == onscreen_surface_) {
2512
                request_screen_update();
2513
        }
2514
        return rect;
2515
}
2516
 
2517
void blit_xor(SDL_Surface* target_surface, SDL_Rect* dest_rect, SDL_Surface* image, SDL_Rect* src_rect) {
2518
        if (dest_rect->w != src_rect->w || dest_rect->h != src_rect->h) {
2519
                printf("blit_xor: dest_rect and src_rect have different sizes\n");
2520
                quit(1);
2521
        }
2522
        SDL_Surface* helper_surface = SDL_CreateRGBSurface(0, dest_rect->w, dest_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
2523
        if (helper_surface == NULL) {
2524
                sdlperror("SDL_CreateRGBSurface");
2525
                quit(1);
2526
        }
2527
        SDL_Surface* image_24 = SDL_ConvertSurface(image, helper_surface->format, 0);
2528
        //SDL_CreateRGBSurface(0, src_rect->w, src_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
2529
        if (image_24 == NULL) {
2530
                sdlperror("SDL_CreateRGBSurface");
2531
                quit(1);
2532
        }
2533
        SDL_Rect dest_rect2 = *src_rect;
2534
        // Read what is currently where we want to draw the new image.
2535
        if (SDL_BlitSurface(target_surface, dest_rect, helper_surface, &dest_rect2) != 0) {
2536
                sdlperror("SDL_BlitSurface");
2537
                quit(1);
2538
        }
2539
        if (SDL_LockSurface(image_24) != 0) {
2540
                sdlperror("SDL_LockSurface");
2541
                quit(1);
2542
        }
2543
        if (SDL_LockSurface(helper_surface) != 0) {
2544
                sdlperror("SDL_LockSurface");
2545
                quit(1);
2546
        }
2547
        int size = helper_surface->h * helper_surface->pitch;
2548
        int i;
2549
        byte *p_src = (byte*) image_24->pixels;
2550
        byte *p_dest = (byte*) helper_surface->pixels;
2551
 
2552
        // Xor the old area with the image.
2553
        for (i = 0; i < size; ++i) {
2554
                *p_dest ^= *p_src;
2555
                ++p_src; ++p_dest;
2556
        }
2557
        SDL_UnlockSurface(image_24);
2558
        SDL_UnlockSurface(helper_surface);
2559
        // Put the new area in place of the old one.
2560
        if (SDL_BlitSurface(helper_surface, src_rect, target_surface, dest_rect) != 0) {
2561
                sdlperror("SDL_BlitSurface 2065");
2562
                quit(1);
2563
        }
2564
        SDL_FreeSurface(image_24);
2565
        SDL_FreeSurface(helper_surface);
2566
        if (target_surface == onscreen_surface_) {
2567
                request_screen_update();
2568
        }
2569
}
2570
 
2571
image_type far * __pascal far method_6_blit_img_to_scr(image_type far *image,int xpos,int ypos,int blit) {
2572
        if (image == NULL) {
2573
                printf("method_6_blit_img_to_scr: image == NULL\n");
2574
                //quit(1);
2575
                return NULL;
2576
        }
2577
 
2578
        if (blit == blitters_9_black) {
2579
                method_3_blit_mono(image, xpos, ypos, blitters_9_black, 0);
2580
                return image;
2581
        }
2582
 
2583
        SDL_Rect src_rect = {0, 0, image->w, image->h};
2584
        SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
2585
 
2586
        if (blit == blitters_3_xor) {
2587
                blit_xor(current_target_surface, &dest_rect, image, &src_rect);
2588
                return image;
2589
        }
2590
        SDL_SetSurfaceBlendMode(image, SDL_BLENDMODE_NONE);
2591
        SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_NONE);
2592
        SDL_SetSurfaceAlphaMod(image, 255);
2593
 
2594
        if (blit == blitters_0_no_transp) {
2595
                SDL_SetColorKey(image, SDL_FALSE, 0);
2596
        }
2597
        else {
2598
                SDL_SetColorKey(image, SDL_TRUE, 0);
2599
        }
2600
        if (SDL_BlitSurface(image, &src_rect, current_target_surface, &dest_rect) != 0) {
2601
                sdlperror("SDL_BlitSurface 2247");
2602
                quit(1);
2603
        }
2604
 
2605
        if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
2606
                sdlperror("SDL_SetAlpha");
2607
                quit(1);
2608
        }
2609
//      if (current_target_surface == onscreen_surface_)
2610
//              request_screen_update();
2611
 
2612
        return image;
2613
}
2614
 
2615
#ifndef USE_COMPAT_TIMER
2616
int fps = 60;
2617
SDL_TimerID timer_handles[2] = {0,0};
2618
int timer_stopped[2] = {1,1};
2619
#else
2620
int wait_time[2] = {0,0};
2621
#endif
2622
 
2623
void remove_timer(int timer_index) {
2624
#ifndef USE_COMPAT_TIMER
2625
        if (timer_handles[timer_index]) {
2626
                if (!SDL_RemoveTimer(timer_handles[timer_index])) {
2627
                        printf("timer_handles[%d] = %d\n", timer_index, timer_handles[timer_index]);
2628
                        sdlperror("SDL_RemoveTimer in remove_timer");
2629
                        //quit(1);
2630
                }
2631
                timer_handles[timer_index] = 0;
2632
        }
2633
#endif
2634
}
2635
 
2636
int target_time;
2637
 
2638
Uint32 timer_callback(Uint32 interval, void *param) {
2639
        int now = SDL_GetTicks();
2640
 
2641
        // let the timer finish 5 ms earlier to allow for overhead before the next frame is displayed
2642
        // this is somewhat ugly and may cause the game to run slightly too fast on fast systems (not tested)
2643
        int residual_wait_time = target_time - now - 5;
2644
        if (residual_wait_time > 0 && residual_wait_time <= 40) SDL_Delay(residual_wait_time);
2645
 
2646
        SDL_Event event;
2647
        memset(&event, 0, sizeof(event));
2648
        event.type = SDL_USEREVENT;
2649
        event.user.code = userevent_TIMER;
2650
        event.user.data1 = param;
2651
        int timer_index = (uintptr_t)param;
2652
        remove_timer(timer_index);
2653
        // First remove the timer, then allow the other thread to continue.
2654
        SDL_PushEvent(&event);
2655
 
2656
 
2657
#ifndef USE_COMPAT_TIMER
2658
        return 0;
2659
#else
2660
        return interval;
2661
#endif
2662
}
2663
 
2664
void __pascal start_timer(int timer_index, int length) {
2665
#ifdef USE_REPLAY
2666
        if (replaying && skipping_replay) return;
2667
#endif
2668
#ifndef USE_COMPAT_TIMER
2669
        if (timer_handles[timer_index]) {
2670
                remove_timer(timer_index);
2671
                timer_handles[timer_index] = 0;
2672
        }
2673
        timer_stopped[timer_index] = 0; // length<=0;
2674
        if (length <= 0) {
2675
                // We need the timer to finish *after* keypresses.
2676
                // So idle() is called at least once in do_wait(), and keypresses while fading are not ignored.
2677
                timer_callback(0, (void*)(uintptr_t)timer_index);
2678
                return;
2679
        }
2680
 
2681
        int now = SDL_GetTicks();
2682
        double frametime = 1000.0 / 60.0;
2683
//      double frametime = (timer_index == 1) ? 16.60 : 1000.0 / 60.0;
2684
        int target_length = (int) (length * frametime);
2685
 
2686
        // subtract 40ms to allow for variable lag; correct for this when the timer ends
2687
        target_time = now + target_length;
2688
        int modified_length = target_length - 40;
2689
 
2690
        SDL_TimerID timer = SDL_AddTimer(modified_length, timer_callback, (void*)(uintptr_t)timer_index);
2691
 
2692
        if (timer == 0) {
2693
                sdlperror("SDL_AddTimer");
2694
                quit(1);
2695
        }
2696
        timer_handles[timer_index] = timer;
2697
#else
2698
        wait_time[timer_index] = length;
2699
#endif
2700
}
2701
 
2702
void toggle_fullscreen() {
2703
        uint32_t flags = SDL_GetWindowFlags(window_);
2704
        if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
2705
                SDL_SetWindowFullscreen(window_, 0);
2706
                SDL_ShowCursor(SDL_ENABLE);
2707
        }
2708
        else {
2709
                SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
2710
                SDL_ShowCursor(SDL_DISABLE);
2711
        }
2712
}
2713
 
2714
void idle() {
2715
        // Wait for *one* event and process it, then return.
2716
        // Much like the x86 HLT instruction.
2717
        SDL_Event event;
2718
        if (SDL_WaitEvent(&event) == 0) {
2719
                sdlperror("SDL_WaitEvent");
2720
                quit(1);
2721
        }
2722
        // We still want to process all events in the queue
2723
        // For instance, there may be simultaneous SDL2 KEYDOWN and TEXTINPUT events
2724
        do { // while there are still events to be processed
2725
                switch (event.type) {
2726
                        case SDL_KEYDOWN:
2727
                        {
2728
                                int modifier = event.key.keysym.mod;
2729
                                int scancode = event.key.keysym.scancode;
2730
 
2731
                                // Handle these separately, so they won't interrupt things that are usually interrupted by a keypress. (pause, cutscene)
2732
#ifdef USE_SCREENSHOT
2733
                                if (scancode == SDL_SCANCODE_F12) {
2734
                                        if (modifier & KMOD_SHIFT) {
2735
                                                save_level_screenshot((modifier & KMOD_CTRL) != 0);
2736
                                        } else {
2737
                                                save_screenshot();
2738
                                        }
2739
                                } else
2740
#endif
2741
                                if ((modifier & KMOD_ALT) &&
2742
                                        scancode == SDL_SCANCODE_RETURN)
2743
                                {
2744
                                        // Only if the Enter key was pressed down right now.
2745
                                        if (key_states[scancode] == 0) {
2746
                                                // Alt-Enter: toggle fullscreen mode
2747
                                                toggle_fullscreen();
2748
                                                key_states[scancode] = 1;
2749
                                        }
2750
                                } else {
2751
                                        key_states[scancode] = 1;
2752
                                        switch (scancode) {
2753
                                                // Keys that are ignored by themselves:
2754
                                                case SDL_SCANCODE_LCTRL:
2755
                                                case SDL_SCANCODE_LSHIFT:
2756
                                                case SDL_SCANCODE_LALT:
2757
                                                case SDL_SCANCODE_LGUI:
2758
                                                case SDL_SCANCODE_RCTRL:
2759
                                                case SDL_SCANCODE_RSHIFT:
2760
                                                case SDL_SCANCODE_RALT:
2761
                                                case SDL_SCANCODE_RGUI:
2762
                                                case SDL_SCANCODE_CAPSLOCK:
2763
                                                case SDL_SCANCODE_SCROLLLOCK:
2764
                                                case SDL_SCANCODE_NUMLOCKCLEAR:
2765
                                                case SDL_SCANCODE_APPLICATION:
2766
                                                case SDL_SCANCODE_PRINTSCREEN:
2767
                                                case SDL_SCANCODE_PAUSE:
2768
                                                        break;
2769
                                                default:
2770
                                                        last_key_scancode = scancode;
2771
                                                        if (modifier & KMOD_SHIFT) last_key_scancode |= WITH_SHIFT;
2772
                                                        if (modifier & KMOD_CTRL ) last_key_scancode |= WITH_CTRL ;
2773
                                                        if (modifier & KMOD_ALT  ) last_key_scancode |= WITH_ALT  ;
2774
                                        }
2775
 
2776
#ifdef USE_AUTO_INPUT_MODE
2777
                                        switch (scancode) {
2778
                                                // Keys that are used for keyboard control:
2779
                                                case SDL_SCANCODE_LSHIFT:
2780
                                                case SDL_SCANCODE_RSHIFT:
2781
                                                case SDL_SCANCODE_LEFT:
2782
                                                case SDL_SCANCODE_RIGHT:
2783
                                                case SDL_SCANCODE_UP:
2784
                                                case SDL_SCANCODE_DOWN:
2785
                                                case SDL_SCANCODE_CLEAR:
2786
                                                case SDL_SCANCODE_HOME:
2787
                                                case SDL_SCANCODE_PAGEUP:
2788
                                                case SDL_SCANCODE_KP_2:
2789
                                                case SDL_SCANCODE_KP_4:
2790
                                                case SDL_SCANCODE_KP_5:
2791
                                                case SDL_SCANCODE_KP_6:
2792
                                                case SDL_SCANCODE_KP_7:
2793
                                                case SDL_SCANCODE_KP_8:
2794
                                                case SDL_SCANCODE_KP_9:
2795
                                                        if (!is_keyboard_mode) {
2796
                                                                is_keyboard_mode = 1;
2797
                                                                is_joyst_mode = 0;
2798
                                                        }
2799
                                        }
2800
#endif
2801
                                }
2802
                                break;
2803
                        }
2804
                        case SDL_KEYUP:
2805
                                key_states[event.key.keysym.scancode] = 0;
2806
                                break;
2807
                        case SDL_CONTROLLERAXISMOTION:
2808
                                if (event.caxis.axis < 6) {
2809
                                        joy_axis[event.caxis.axis] = event.caxis.value;
2810
 
2811
#ifdef USE_AUTO_INPUT_MODE
2812
                                        if (!is_joyst_mode && (event.caxis.value >= joystick_threshold || event.caxis.value <= -joystick_threshold)) {
2813
                                                is_joyst_mode = 1;
2814
                                                is_keyboard_mode = 0;
2815
                                        }
2816
#endif
2817
                                }
2818
                                break;
2819
                        case SDL_CONTROLLERBUTTONDOWN:
2820
#ifdef USE_AUTO_INPUT_MODE
2821
                                if (!is_joyst_mode) {
2822
                                        is_joyst_mode = 1;
2823
                                        is_keyboard_mode = 0;
2824
                                }
2825
#endif
2826
                                switch (event.cbutton.button)
2827
                                {
2828
                                        case SDL_CONTROLLER_BUTTON_DPAD_LEFT:  joy_hat_states[0] = -1; break; // left
2829
                                        case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 1;  break; // right
2830
                                        case SDL_CONTROLLER_BUTTON_DPAD_UP:    joy_hat_states[1] = -1; break; // up
2831
                                        case SDL_CONTROLLER_BUTTON_DPAD_DOWN:  joy_hat_states[1] = 1;  break; // down
2832
 
2833
                                        case SDL_CONTROLLER_BUTTON_A:          joy_AY_buttons_state = 1;  break; /*** A (down) ***/
2834
                                        case SDL_CONTROLLER_BUTTON_Y:          joy_AY_buttons_state = -1; break; /*** Y (up) ***/
2835
                                        case SDL_CONTROLLER_BUTTON_X:          joy_X_button_state = 1;    break; /*** X (shift) ***/
2836
                                        case SDL_CONTROLLER_BUTTON_B:          joy_B_button_state = 1;    break; /*** B (unused) ***/
2837
 
2838
                                        case SDL_CONTROLLER_BUTTON_START:
2839
                                                last_key_scancode = SDL_SCANCODE_R | WITH_CTRL; /*** start (restart game) ***/
2840
                                                break;
2841
 
2842
                                        case SDL_CONTROLLER_BUTTON_BACK:
2843
                                                last_key_scancode = SDL_SCANCODE_A | WITH_CTRL;  /*** back (restart level) ***/
2844
                                                break;
2845
 
2846
                                        default: break;
2847
                                }
2848
                                break;
2849
                        case SDL_CONTROLLERBUTTONUP:
2850
                                switch (event.cbutton.button)
2851
                                {
2852
                                        case SDL_CONTROLLER_BUTTON_DPAD_LEFT:  joy_hat_states[0] = 0; break; // left
2853
                                        case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 0; break; // right
2854
                                        case SDL_CONTROLLER_BUTTON_DPAD_UP:    joy_hat_states[1] = 0; break; // up
2855
                                        case SDL_CONTROLLER_BUTTON_DPAD_DOWN:  joy_hat_states[1] = 0; break; // down
2856
 
2857
                                        case SDL_CONTROLLER_BUTTON_A:          joy_AY_buttons_state = 0; break; /*** A (down) ***/
2858
                                        case SDL_CONTROLLER_BUTTON_Y:          joy_AY_buttons_state = 0; break; /*** Y (up) ***/
2859
                                        case SDL_CONTROLLER_BUTTON_X:          joy_X_button_state = 0;   break; /*** X (shift) ***/
2860
                                        case SDL_CONTROLLER_BUTTON_B:          joy_B_button_state = 0;   break; /*** B (unused) ***/
2861
 
2862
                                        default: break;
2863
                                }
2864
                                break;
2865
                        case SDL_JOYBUTTONDOWN:
2866
                        case SDL_JOYBUTTONUP:
2867
                        case SDL_JOYAXISMOTION:
2868
                                // Only handle the event if the joystick is incompatible with the SDL_GameController interface.
2869
                                // (Otherwise it will interfere with the normal action of the SDL_GameController API.)
2870
                                if (!using_sdl_joystick_interface) {
2871
                                        break;
2872
                                }
2873
                                if (event.type == SDL_JOYAXISMOTION) {
2874
                                        if (event.jaxis.axis == SDL_JOYSTICK_X_AXIS) {
2875
                                                joy_axis[SDL_CONTROLLER_AXIS_LEFTX] = event.jaxis.value;
2876
                                        }
2877
                                        else if (event.jaxis.axis == SDL_JOYSTICK_Y_AXIS) {
2878
                                                joy_axis[SDL_CONTROLLER_AXIS_LEFTY] = event.jaxis.value;
2879
                                        }
2880
                                        // Disregard SDL_JOYAXISMOTION events within joystick 'dead zone'
2881
                                        int joy_x = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
2882
                                        int joy_y = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
2883
                                        if ((dword)(joy_x*joy_x) + (dword)(joy_y*joy_y) < (dword)(joystick_threshold*joystick_threshold)) {
2884
                                                break;
2885
                                        }
2886
 
2887
                                }
2888
#ifdef USE_AUTO_INPUT_MODE
2889
                                if (!is_joyst_mode) {
2890
                                        is_joyst_mode = 1;
2891
                                        is_keyboard_mode = 0;
2892
                                }
2893
#endif
2894
                                if (event.type == SDL_JOYBUTTONDOWN) {
2895
                                        if      (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y)   joy_AY_buttons_state = -1; // Y (up)
2896
                                        else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X)   joy_X_button_state = -1;   // X (shift)
2897
                                }
2898
                                else if (event.type == SDL_JOYBUTTONUP) {
2899
                                        if      (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y)   joy_AY_buttons_state = 0;  // Y (up)
2900
                                        else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X)   joy_X_button_state = 0;    // X (shift)
2901
                                }
2902
                                break;
2903
 
2904
                        case SDL_TEXTINPUT:
2905
                                last_text_input = event.text.text[0]; // UTF-8 formatted char text input
2906
                                break;
2907
                        case SDL_WINDOWEVENT:
2908
                                // In case the user switches away while holding a key: do as if all keys were released.
2909
                                // (DOSBox does the same.)
2910
 
2911
/* // not implemented in SDL2 for now
2912
 *
2913
                        if ((event.active.state & SDL_APPINPUTFOCUS) && event.active.gain == 0) {
2914
                                memset(key_states, 0, sizeof(key_states));
2915
                        }
2916
                        // Note: event.active.state can contain multiple flags or'ed.
2917
                        // 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.
2918
                        if ((event.active.state & SDL_APPACTIVE) && event.active.gain == 1) {
2919
                                request_screen_update();
2920
                        }
2921
*/
2922
                                switch (event.window.event) {
2923
                                        case SDL_WINDOWEVENT_SIZE_CHANGED:
2924
                                                window_resized();
2925
                                        //case SDL_WINDOWEVENT_MOVED:
2926
                                        //case SDL_WINDOWEVENT_RESTORED:
2927
                                        case SDL_WINDOWEVENT_EXPOSED:
2928
                                                request_screen_update();
2929
                                                break;
2930
                                }
2931
                                break;
2932
                        case SDL_USEREVENT:
2933
                                if (event.user.code == userevent_TIMER /*&& event.user.data1 == (void*)timer_index*/) {
2934
#ifndef USE_COMPAT_TIMER
2935
                                        int timer_index = (uintptr_t) event.user.data1;
2936
                                        timer_stopped[timer_index] = 1;
2937
                                        //printf("timer_index = %d\n", timer_index);
2938
                                        // 2014-08-27: According to the output of the next line, handle is always NULL.
2939
                                        // 2014-08-28: Except when you interrupt fading of the cutscene.
2940
                                        //printf("timer_handles[timer_index] = %p\n", timer_handles[timer_index]);
2941
                                        // 2014-08-27: However, this line will change something: it makes the game too fast. Weird...
2942
                                        // 2014-08-28: Wait, now it doesn't...
2943
                                        //timer_handles[timer_index] = 0;
2944
#else
2945
                                int index;
2946
                                for (index = 0; index < 2; ++index) {
2947
                                        if (wait_time[index] > 0) --wait_time[index];
2948
                                }
2949
#endif
2950
                                } else if (event.user.code == userevent_SOUND) {
2951
                                        //sound_timer = 0;
2952
#ifndef USE_MIXER
2953
                                //stop_sounds();
2954
#endif
2955
                                }
2956
                                break;
2957
                        case SDL_QUIT:
2958
                                quit(0);
2959
                                break;
2960
                }
2961
        } while (SDL_PollEvent(&event) == 1);
2962
}
2963
 
2964
word word_1D63A = 1;
2965
// seg009:0EA9
2966
int __pascal do_wait(int timer_index) {
2967
        while (! has_timer_stopped(timer_index)) {
2968
                idle();
2969
                int key = do_paused();
2970
                if (key != 0 && (word_1D63A != 0 || key == 0x1B)) return 1;
2971
        }
2972
        return 0;
2973
}
2974
 
2975
void __pascal do_simple_wait(int timer_index) {
2976
#ifdef USE_REPLAY
2977
        if ((replaying && skipping_replay) || is_validate_mode) return;
2978
#endif
2979
 
2980
        while (! has_timer_stopped(timer_index)) {
2981
                idle();
2982
        }
2983
}
2984
 
2985
#ifdef USE_COMPAT_TIMER
2986
SDL_TimerID global_timer = NULL;
2987
#endif
2988
// seg009:78E9
2989
void __pascal far init_timer(int frequency) {
2990
#ifndef USE_COMPAT_TIMER
2991
        fps = frequency;
2992
#else
2993
        if (global_timer != 0) {
2994
                if (!SDL_RemoveTimer(global_timer)) {
2995
                        sdlperror("SDL_RemoveTimer");
2996
                }
2997
        }
2998
        global_timer = SDL_AddTimer(1000/frequency, timer_callback, NULL);
2999
        if (global_timer == 0) {
3000
                sdlperror("SDL_AddTimer");
3001
                quit(1);
3002
        }
3003
#endif
3004
}
3005
 
3006
// seg009:35F6
3007
void __pascal far set_clip_rect(const rect_type far *rect) {
3008
        SDL_Rect clip_rect;
3009
        rect_to_sdlrect(rect, &clip_rect);
3010
        SDL_SetClipRect(current_target_surface, &clip_rect);
3011
}
3012
 
3013
// seg009:365C
3014
void __pascal far reset_clip_rect() {
3015
        SDL_SetClipRect(current_target_surface, NULL);
3016
}
3017
 
3018
// seg009:1983
3019
void __pascal far set_bg_attr(int vga_pal_index,int hc_pal_index) {
3020
        // stub
3021
#ifdef USE_FLASH
3022
        //palette[vga_pal_index] = vga_palette[hc_pal_index];
3023
        if (!enable_flash) return;
3024
        if (vga_pal_index == 0) {
3025
                /*
3026
                if (SDL_SetAlpha(offscreen_surface, SDL_SRCALPHA, 0) != 0) {
3027
                        sdlperror("SDL_SetAlpha");
3028
                        quit(1);
3029
                }
3030
                */
3031
                // Make the black pixels transparent.
3032
                if (SDL_SetColorKey(offscreen_surface, SDL_TRUE, 0) != 0) {     // SDL_SRCCOLORKEY old
3033
                        sdlperror("SDL_SetColorKey");
3034
                        quit(1);
3035
                }
3036
                SDL_Rect rect = {0,0,0,0};
3037
                rect.w = offscreen_surface->w;
3038
                rect.h = offscreen_surface->h;
3039
                rgb_type palette_color = palette[hc_pal_index];
3040
                uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) /*& 0xFFFFFF*/;
3041
                //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
3042
                // First clear the screen with the color of the flash.
3043
                if (safe_SDL_FillRect(onscreen_surface_, &rect, rgb_color) != 0) {
3044
                        sdlperror("SDL_FillRect");
3045
                        quit(1);
3046
                }
3047
                //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
3048
                if (upside_down) {
3049
                        flip_screen(offscreen_surface);
3050
                }
3051
                // Then draw the offscreen image onto it.
3052
                if (SDL_BlitSurface(offscreen_surface, &rect, onscreen_surface_, &rect) != 0) {
3053
                        sdlperror("SDL_BlitSurface");
3054
                        quit(1);
3055
                }
3056
#ifdef USE_LIGHTING
3057
                if (hc_pal_index == 0) update_lighting(&rect_top);
3058
#endif
3059
                if (upside_down) {
3060
                        flip_screen(offscreen_surface);
3061
                }
3062
                // And show it!
3063
                request_screen_update();
3064
                // Give some time to show the flash.
3065
                //SDL_Flip(onscreen_surface_);
3066
//              if (hc_pal_index != 0) SDL_Delay(2*(1000/60));
3067
                //SDL_Flip(onscreen_surface_);
3068
                /*
3069
                if (SDL_SetAlpha(offscreen_surface, 0, 0) != 0) {
3070
                        sdlperror("SDL_SetAlpha");
3071
                        quit(1);
3072
                }
3073
                */
3074
                if (SDL_SetColorKey(offscreen_surface, 0, 0) != 0) {
3075
                        sdlperror("SDL_SetColorKey");
3076
                        quit(1);
3077
                }
3078
        }
3079
#endif // USE_FLASH
3080
}
3081
 
3082
// seg009:07EB
3083
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) {
3084
        *dest = *source;
3085
        dest->left += d_left;
3086
        dest->top += d_top;
3087
        dest->right += d_right;
3088
        dest->bottom += d_bottom;
3089
        return dest;
3090
}
3091
 
3092
// seg009:3AA5
3093
rect_type far *__pascal offset2_rect(rect_type far *dest,const rect_type far *source,int delta_x,int delta_y) {
3094
        dest->top    = source->top    + delta_y;
3095
        dest->left   = source->left   + delta_x;
3096
        dest->bottom = source->bottom + delta_y;
3097
        dest->right  = source->right  + delta_x;
3098
        return dest;
3099
}
3100
 
3101
#ifdef USE_FADE
3102
// seg009:19EF
3103
void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
3104
        palette_fade_type far* palette_buffer;
3105
        if (graphics_mode == gmMcgaVga) {
3106
                palette_buffer = make_pal_buffer_fadein(source_surface, which_rows, 2);
3107
                while (fade_in_frame(palette_buffer) == 0) {
3108
                        pop_wait(timer_1, 0); // modified
3109
                }
3110
                pal_restore_free_fadein(palette_buffer);
3111
        } else {
3112
                // ...
3113
        }
3114
}
3115
 
3116
// seg009:1A51
3117
palette_fade_type far *__pascal make_pal_buffer_fadein(surface_type *source_surface,int which_rows,int wait_time) {
3118
        palette_fade_type far* palette_buffer;
3119
        word curr_row;
3120
        word var_8;
3121
        word curr_row_mask;
3122
        palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
3123
        palette_buffer->which_rows = which_rows;
3124
        palette_buffer->wait_time = wait_time;
3125
        palette_buffer->fade_pos = 0x40;
3126
        palette_buffer->proc_restore_free = &pal_restore_free_fadein;
3127
        palette_buffer->proc_fade_frame = &fade_in_frame;
3128
        read_palette_256(palette_buffer->original_pal);
3129
        memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
3130
        var_8 = 0;
3131
        for (curr_row = 0, curr_row_mask = 1; curr_row < 0x10; ++curr_row, curr_row_mask<<=1) {
3132
                if (which_rows & curr_row_mask) {
3133
                        memset_far(palette_buffer->faded_pal + (curr_row<<4), 0, sizeof(rgb_type[0x10]));
3134
                        set_pal_arr(curr_row<<4, 0x10, NULL, (var_8++&3)==0);
3135
                }
3136
        }
3137
        //method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
3138
        // for RGB
3139
        //method_5_rect(&screen_rect, 0, 0);
3140
        return palette_buffer;
3141
}
3142
 
3143
// seg009:1B64
3144
void __pascal far pal_restore_free_fadein(palette_fade_type far *palette_buffer) {
3145
        set_pal_256(palette_buffer->original_pal);
3146
        free_far(palette_buffer);
3147
        // for RGB
3148
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
3149
}
3150
 
3151
// seg009:1B88
3152
int __pascal far fade_in_frame(palette_fade_type far *palette_buffer) {
3153
        rgb_type* faded_pal_ptr;
3154
        word start;
3155
        word column;
3156
        rgb_type* original_pal_ptr;
3157
        word current_row_mask;
3158
//      void* var_12;
3159
        /**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
3160
        //printf("start ticks = %u\n",SDL_GetTicks());
3161
        --palette_buffer->fade_pos;
3162
        for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
3163
                if (palette_buffer->which_rows & current_row_mask) {
3164
                        //var_12 = palette_buffer->
3165
                        original_pal_ptr = palette_buffer->original_pal + start;
3166
                        faded_pal_ptr = palette_buffer->faded_pal + start;
3167
                        for (column = 0; column<0x10; ++column) {
3168
                                if (original_pal_ptr[column].r > palette_buffer->fade_pos) {
3169
                                        ++faded_pal_ptr[column].r;
3170
                                }
3171
                                if (original_pal_ptr[column].g > palette_buffer->fade_pos) {
3172
                                        ++faded_pal_ptr[column].g;
3173
                                }
3174
                                if (original_pal_ptr[column].b > palette_buffer->fade_pos) {
3175
                                        ++faded_pal_ptr[column].b;
3176
                                }
3177
                        }
3178
                }
3179
        }
3180
        column = 0;
3181
        for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
3182
                if (palette_buffer->which_rows & current_row_mask) {
3183
                        set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
3184
                }
3185
        }
3186
 
3187
        int h = offscreen_surface->h;
3188
        if (SDL_LockSurface(onscreen_surface_) != 0) {
3189
                sdlperror("SDL_LockSurface");
3190
                quit(1);
3191
        }
3192
        if (SDL_LockSurface(offscreen_surface) != 0) {
3193
                sdlperror("SDL_LockSurface");
3194
                quit(1);
3195
        }
3196
        int y,x;
3197
        int on_stride = onscreen_surface_->pitch;
3198
        int off_stride = offscreen_surface->pitch;
3199
        int fade_pos = palette_buffer->fade_pos;
3200
        for (y = 0; y < h; ++y) {
3201
                byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
3202
                byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
3203
                for (x = 0; x < on_stride; ++x) {
3204
                        //if (*off_pixel_ptr > palette_buffer->fade_pos) *pixel_ptr += 4;
3205
                        int v = *off_pixel_ptr - fade_pos*4;
3206
                        if (v<0) v=0;
3207
                        *on_pixel_ptr = v;
3208
                        ++on_pixel_ptr; ++off_pixel_ptr;
3209
                }
3210
        }
3211
        SDL_UnlockSurface(onscreen_surface_);
3212
        SDL_UnlockSurface(offscreen_surface);
3213
 
3214
        //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0); // debug
3215
        request_screen_update();
3216
 
3217
        /**/do_simple_wait(1); // can interrupt fading of cutscene
3218
        //do_wait(timer_1); // can interrupt fading of main title
3219
        //printf("end ticks = %u\n",SDL_GetTicks());
3220
        return palette_buffer->fade_pos == 0;
3221
}
3222
 
3223
// seg009:1CC9
3224
void __pascal far fade_out_2(int rows) {
3225
        palette_fade_type far *palette_buffer;
3226
        if (graphics_mode == gmMcgaVga) {
3227
                palette_buffer = make_pal_buffer_fadeout(rows, 2);
3228
                while (fade_out_frame(palette_buffer) == 0) {
3229
                        pop_wait(timer_1, 0); // modified
3230
                }
3231
                pal_restore_free_fadeout(palette_buffer);
3232
        } else {
3233
                // ...
3234
        }
3235
}
3236
 
3237
// seg009:1D28
3238
palette_fade_type far *__pascal make_pal_buffer_fadeout(int which_rows,int wait_time) {
3239
        palette_fade_type far *palette_buffer;
3240
        palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
3241
        palette_buffer->which_rows = which_rows;
3242
        palette_buffer->wait_time = wait_time;
3243
        palette_buffer->fade_pos = 00; // modified
3244
        palette_buffer->proc_restore_free = &pal_restore_free_fadeout;
3245
        palette_buffer->proc_fade_frame = &fade_out_frame;
3246
        read_palette_256(palette_buffer->original_pal);
3247
        memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
3248
        // for RGB
3249
        method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
3250
        return palette_buffer;
3251
}
3252
 
3253
// seg009:1DAF
3254
void __pascal far pal_restore_free_fadeout(palette_fade_type far *palette_buffer) {
3255
        surface_type* surface;
3256
        surface = current_target_surface;
3257
        current_target_surface = onscreen_surface_;
3258
        draw_rect(&screen_rect, 0);
3259
        current_target_surface = surface;
3260
        set_pal_256(palette_buffer->original_pal);
3261
        free_far(palette_buffer);
3262
        // for RGB
3263
        method_5_rect(&screen_rect, 0, 0);
3264
}
3265
 
3266
// seg009:1DF7
3267
int __pascal far fade_out_frame(palette_fade_type far *palette_buffer) {
3268
        rgb_type* faded_pal_ptr;
3269
        word start;
3270
        word var_8;
3271
        word column;
3272
        word current_row_mask;
3273
        byte* curr_color_ptr;
3274
        var_8 = 1;
3275
        ++palette_buffer->fade_pos; // modified
3276
        /**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
3277
        for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
3278
                if (palette_buffer->which_rows & current_row_mask) {
3279
                        //var_12 = palette_buffer->
3280
                        //original_pal_ptr = palette_buffer->original_pal + start;
3281
                        faded_pal_ptr = palette_buffer->faded_pal + start;
3282
                        for (column = 0; column<0x10; ++column) {
3283
                                curr_color_ptr = &faded_pal_ptr[column].r;
3284
                                if (*curr_color_ptr != 0) {
3285
                                        --*curr_color_ptr;
3286
                                        var_8 = 0;
3287
                                }
3288
                                curr_color_ptr = &faded_pal_ptr[column].g;
3289
                                if (*curr_color_ptr != 0) {
3290
                                        --*curr_color_ptr;
3291
                                        var_8 = 0;
3292
                                }
3293
                                curr_color_ptr = &faded_pal_ptr[column].b;
3294
                                if (*curr_color_ptr != 0) {
3295
                                        --*curr_color_ptr;
3296
                                        var_8 = 0;
3297
                                }
3298
                        }
3299
                }
3300
        }
3301
        column = 0;
3302
        for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
3303
                if (palette_buffer->which_rows & current_row_mask) {
3304
                        set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
3305
                }
3306
        }
3307
 
3308
        int h = offscreen_surface->h;
3309
        if (SDL_LockSurface(onscreen_surface_) != 0) {
3310
                sdlperror("SDL_LockSurface");
3311
                quit(1);
3312
        }
3313
        if (SDL_LockSurface(offscreen_surface) != 0) {
3314
                sdlperror("SDL_LockSurface");
3315
                quit(1);
3316
        }
3317
        int y,x;
3318
        int on_stride = onscreen_surface_->pitch;
3319
        int off_stride = offscreen_surface->pitch;
3320
        int fade_pos = palette_buffer->fade_pos;
3321
        for (y = 0; y < h; ++y) {
3322
                byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
3323
                byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
3324
                for (x = 0; x < on_stride; ++x) {
3325
                        //if (*pixel_ptr >= 4) *pixel_ptr -= 4;
3326
                        int v = *off_pixel_ptr - fade_pos*4;
3327
                        if (v<0) v=0;
3328
                        *on_pixel_ptr = v;
3329
                        ++on_pixel_ptr; ++off_pixel_ptr;
3330
                }
3331
        }
3332
        SDL_UnlockSurface(onscreen_surface_);
3333
        SDL_UnlockSurface(offscreen_surface);
3334
 
3335
        request_screen_update();
3336
 
3337
        /**/do_simple_wait(1); // can interrupt fading of cutscene
3338
        //do_wait(timer_1); // can interrupt fading of main title
3339
        return var_8;
3340
}
3341
 
3342
// seg009:1F28
3343
void __pascal far read_palette_256(rgb_type far *target) {
3344
        int i;
3345
        for (i = 0; i < 256; ++i) {
3346
                target[i] = palette[i];
3347
        }
3348
}
3349
 
3350
// seg009:1F5E
3351
void __pascal far set_pal_256(rgb_type far *source) {
3352
        int i;
3353
        for (i = 0; i < 256; ++i) {
3354
                palette[i] = source[i];
3355
        }
3356
}
3357
#endif // USE_FADE
3358
 
3359
void set_chtab_palette(chtab_type* chtab, byte* colors, int n_colors) {
3360
        if (chtab != NULL) {
3361
                SDL_Color* scolors = (SDL_Color*) malloc(n_colors*sizeof(SDL_Color));
3362
                int i;
3363
                //printf("scolors\n",i);
3364
                for (i = 0; i < n_colors; ++i) {
3365
                        //printf("i=%d\n",i);
3366
                        scolors[i].r = *colors << 2; ++colors;
3367
                        scolors[i].g = *colors << 2; ++colors;
3368
                        scolors[i].b = *colors << 2; ++colors;
3369
                        scolors[i].a = SDL_ALPHA_OPAQUE; // the SDL2 SDL_Color struct has an alpha component
3370
                }
3371
 
3372
                // Color 0 of the palette data is not used, it is replaced by the background color.
3373
                // Needed for correct alternate colors (v1.3) of level 8.
3374
                scolors[0].r = scolors[0].g = scolors[0].b = 0;
3375
 
3376
                //printf("setcolors\n",i);
3377
                for (i = 0; i < chtab->n_images; ++i) {
3378
                        //printf("i=%d\n",i);
3379
                        image_type* current_image = chtab->images[i];
3380
                        if (current_image != NULL) {
3381
 
3382
                                int n_colors_to_be_set = n_colors;
3383
                                SDL_Palette* current_palette = current_image->format->palette;
3384
 
3385
                                // one of the guard images (i=25) is only a single transparent pixel
3386
                                // this caused SDL_SetPaletteColors to fail, I think because that palette contains only 2 colors
3387
                                if (current_palette->ncolors < n_colors_to_be_set)
3388
                                        n_colors_to_be_set = current_palette->ncolors;
3389
                                if (SDL_SetPaletteColors(current_palette, scolors, 0, n_colors_to_be_set) != 0) {
3390
                                        sdlperror("SDL_SetPaletteColors");
3391
                                        quit(1);
3392
                                }
3393
                        }
3394
                }
3395
                free(scolors);
3396
        }
3397
}
3398
 
3399
int has_timer_stopped(int index) {
3400
#ifdef USE_COMPAT_TIMER
3401
        return wait_time[index] == 0;
3402
#else
3403
        return timer_stopped[index];
3404
#endif
3405
}