Subversion Repositories Games.Prince of Persia

Rev

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