1 /*
2 SDLPoP, a port/conversion of the DOS game Prince of Persia.
3 Copyright (C) 2013-2021 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 <https://www.gnu.org/licenses/>.
17
18 The authors of this program may be contacted at https://forum.princed.org
19 */
20
21 #include "common.h"
22 #include <time.h>
23 #include <errno.h>
24
25 #ifdef _WIN32
26 #include <windows.h>
27 #include <wchar.h>
28 #else
29 #include "dirent.h"
30 #endif
31
32 // Most functions in this file are different from those in the original game.
33
sdlperror(const char * header)34 void sdlperror(const char* header) {
35 const char* error = SDL_GetError();
36 printf("%s: %s\n",header,error);
37 //quit(1);
38 }
39
40 char exe_dir[POP_MAX_PATH] = ".";
41 bool found_exe_dir = false;
42
find_exe_dir()43 void find_exe_dir() {
44 if (found_exe_dir) return;
45 snprintf_check(exe_dir, sizeof(exe_dir), "%s", g_argv[0]);
46 char* last_slash = NULL;
47 char* pos = exe_dir;
48 for (char c = *pos; c != '\0'; c = *(++pos)) {
49 if (c == '/' || c == '\\') {
50 last_slash = pos;
51 }
52 }
53 if (last_slash != NULL) {
54 *last_slash = '\0';
55 }
56 found_exe_dir = true;
57 }
58
file_exists(const char * filename)59 bool file_exists(const char* filename) {
60 return (access(filename, F_OK) != -1);
61 }
62
locate_file_(const char * filename,char * path_buffer,int buffer_size)63 const char* locate_file_(const char* filename, char* path_buffer, int buffer_size) {
64 if(file_exists(filename)) {
65 return filename;
66 } else {
67 // If failed, it may be that SDLPoP is being run from the wrong different working directory.
68 // We can try to rescue the situation by loading from the directory of the executable.
69 find_exe_dir();
70 snprintf_check(path_buffer, buffer_size, "%s/%s", exe_dir, filename);
71 return (const char*) path_buffer;
72 }
73 }
74
75 #ifdef _WIN32
76 // These macros are from the SDL2 source. (src/core/windows/SDL_windows.h)
77 // The pointers returned by these macros must be freed with SDL_free().
78 #define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR))
79 #define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
80
81 // This hack is needed because SDL uses UTF-8 everywhere (even in argv!), but fopen on Windows uses whatever code page is currently set.
fopen_UTF8(const char * filename_UTF8,const char * mode_UTF8)82 FILE* fopen_UTF8(const char* filename_UTF8, const char* mode_UTF8) {
83 WCHAR* filename_UTF16 = WIN_UTF8ToString(filename_UTF8);
84 WCHAR* mode_UTF16 = WIN_UTF8ToString(mode_UTF8);
85 FILE* result = _wfopen(filename_UTF16, mode_UTF16);
86 SDL_free(mode_UTF16);
87 SDL_free(filename_UTF16);
88 return result;
89 }
90
chdir_UTF8(const char * path_UTF8)91 int chdir_UTF8(const char* path_UTF8) {
92 WCHAR* path_UTF16 = WIN_UTF8ToString(path_UTF8);
93 int result = _wchdir(path_UTF16);
94 SDL_free(path_UTF16);
95 return result;
96 }
97
mkdir_UTF8(const char * path_UTF8)98 int mkdir_UTF8(const char* path_UTF8) {
99 WCHAR* path_UTF16 = WIN_UTF8ToString(path_UTF8);
100 int result = _wmkdir(path_UTF16);
101 SDL_free(path_UTF16);
102 return result;
103 }
104
access_UTF8(const char * filename_UTF8,int mode)105 int access_UTF8(const char* filename_UTF8, int mode) {
106 WCHAR* filename_UTF16 = WIN_UTF8ToString(filename_UTF8);
107 int result = _waccess(filename_UTF16, mode);
108 SDL_free(filename_UTF16);
109 return result;
110 }
111
stat_UTF8(const char * filename_UTF8,struct stat * _Stat)112 int stat_UTF8(const char *filename_UTF8, struct stat *_Stat) {
113 WCHAR* filename_UTF16 = WIN_UTF8ToString(filename_UTF8);
114 // There is a _wstat() function as well, but it expects the second argument to be a different type than stat().
115 int result = wstat(filename_UTF16, _Stat);
116 SDL_free(filename_UTF16);
117 return result;
118 }
119
120 #endif //_WIN32
121
122 // OS abstraction for listing directory contents
123 // Directory listing using dirent.h is available using MinGW on Windows, but not using MSVC (need to use Win32 API).
124 // - Under GNU/Linux, etc (or if compiling with MinGW on Windows), we can use dirent.h
125 // - Under Windows, we'd like to directly call the Win32 API. (Note: MSVC does not include dirent.h)
126 // NOTE: If we are using MinGW, we'll opt to use the Win32 API as well: dirent.h would just wrap Win32 anyway!
127
128 #ifdef _WIN32
129 struct directory_listing_type {
130 WIN32_FIND_DATAW find_data;
131 HANDLE search_handle;
132 char* current_filename_UTF8;
133 };
134
create_directory_listing_and_find_first_file(const char * directory,const char * extension)135 directory_listing_type* create_directory_listing_and_find_first_file(const char* directory, const char* extension) {
136 directory_listing_type* directory_listing = calloc(1, sizeof(directory_listing_type));
137 char search_pattern[POP_MAX_PATH];
138 snprintf_check(search_pattern, POP_MAX_PATH, "%s/*.%s", directory, extension);
139 WCHAR* search_pattern_UTF16 = WIN_UTF8ToString(search_pattern);
140 directory_listing->search_handle = FindFirstFileW( search_pattern_UTF16, &directory_listing->find_data );
141 SDL_free(search_pattern_UTF16);
142 if (directory_listing->search_handle != INVALID_HANDLE_VALUE) {
143 return directory_listing;
144 } else {
145 free(directory_listing);
146 return NULL;
147 }
148 }
149
get_current_filename_from_directory_listing(directory_listing_type * data)150 char* get_current_filename_from_directory_listing(directory_listing_type* data) {
151 SDL_free(data->current_filename_UTF8);
152 data->current_filename_UTF8 = NULL;
153 data->current_filename_UTF8 = WIN_StringToUTF8(data->find_data.cFileName);
154 return data->current_filename_UTF8;
155 }
156
find_next_file(directory_listing_type * data)157 bool find_next_file(directory_listing_type* data) {
158 return (bool) FindNextFileW( data->search_handle, &data->find_data );
159 }
160
close_directory_listing(directory_listing_type * data)161 void close_directory_listing(directory_listing_type* data) {
162 FindClose(data->search_handle);
163 SDL_free(data->current_filename_UTF8);
164 data->current_filename_UTF8 = NULL;
165 free(data);
166 }
167
168 #else // use dirent.h API for listing files
169
170 struct directory_listing_type {
171 DIR* dp;
172 char* found_filename;
173 const char* extension;
174 };
175
create_directory_listing_and_find_first_file(const char * directory,const char * extension)176 directory_listing_type* create_directory_listing_and_find_first_file(const char* directory, const char* extension) {
177 directory_listing_type* data = calloc(1, sizeof(directory_listing_type));
178 bool ok = false;
179 data->dp = opendir(directory);
180 if (data->dp != NULL) {
181 struct dirent* ep;
182 while ((ep = readdir(data->dp))) {
183 char *ext = strrchr(ep->d_name, '.');
184 if (ext != NULL && strcasecmp(ext+1, extension) == 0) {
185 data->found_filename = ep->d_name;
186 data->extension = extension;
187 ok = true;
188 break;
189 }
190 }
191 }
192 if (ok) {
193 return data;
194 } else {
195 free(data);
196 return NULL;
197 }
198 }
199
get_current_filename_from_directory_listing(directory_listing_type * data)200 char* get_current_filename_from_directory_listing(directory_listing_type* data) {
201 return data->found_filename;
202 }
203
find_next_file(directory_listing_type * data)204 bool find_next_file(directory_listing_type* data) {
205 bool ok = false;
206 struct dirent* ep;
207 while ((ep = readdir(data->dp))) {
208 char *ext = strrchr(ep->d_name, '.');
209 if (ext != NULL && strcasecmp(ext+1, data->extension) == 0) {
210 data->found_filename = ep->d_name;
211 ok = true;
212 break;
213 }
214 }
215 return ok;
216 }
217
close_directory_listing(directory_listing_type * data)218 void close_directory_listing(directory_listing_type *data) {
219 closedir(data->dp);
220 free(data);
221 }
222
223 #endif //_WIN32
224
225 dat_type* dat_chain_ptr = NULL;
226
227 char last_text_input;
228
229 // seg009:000D
read_key()230 int __pascal far read_key() {
231 // stub
232 int key = last_key_scancode;
233 last_key_scancode = 0;
234 return key;
235 }
236
237 // seg009:019A
clear_kbd_buf()238 void __pascal far clear_kbd_buf() {
239 // stub
240 last_key_scancode = 0;
241 last_text_input = 0;
242 }
243
244 // seg009:040A
prandom(word max)245 word __pascal far prandom(word max) {
246 if (!seed_was_init) {
247 // init from current time
248 random_seed = time(NULL);
249 seed_was_init = 1;
250 }
251 random_seed = random_seed * 214013 + 2531011;
252 return (random_seed >> 16) % (max + 1);
253 }
254
255 // seg009:0467
round_xpos_to_byte(int xpos,int round_direction)256 int __pascal far round_xpos_to_byte(int xpos,int round_direction) {
257 // stub
258 return xpos;
259 }
260
261 // seg009:0C7A
quit(int exit_code)262 void __pascal far quit(int exit_code) {
263 restore_stuff();
264 exit(exit_code);
265 }
266
267 // seg009:0C90
restore_stuff()268 void __pascal far restore_stuff() {
269 SDL_Quit();
270 }
271
272 // seg009:0E33
key_test_quit()273 int __pascal far key_test_quit() {
274 word key;
275 key = read_key();
276 if (key == (SDL_SCANCODE_Q | WITH_CTRL)) { // Ctrl+Q
277
278 #ifdef USE_REPLAY
279 if (recording) save_recorded_replay_dialog();
280 #endif
281 #ifdef USE_MENU
282 if (is_menu_shown) menu_was_closed();
283 #endif
284
285 quit(0);
286 }
287 return key;
288 }
289
290 // seg009:0E54
check_param(const char * param)291 const char* __pascal far check_param(const char *param) {
292 // stub
293 short arg_index;
294 for (arg_index = 1; arg_index < g_argc; ++arg_index) {
295
296 char* curr_arg = g_argv[arg_index];
297
298 // Filenames (e.g. replays) should never be a valid 'normal' param so we should skip these to prevent conflicts.
299 // We can lazily distinguish filenames from non-filenames by checking whether they have a dot in them.
300 // (Assumption: all relevant files, e.g. replay files, have some file extension anyway)
301 if (strchr(curr_arg, '.') != NULL) {
302 continue;
303 }
304
305 // List of params that expect a specifier ('sub-') arg directly after it (e.g. the mod's name, after "mod" arg)
306 // Such sub-args may conflict with the normal params (so, we should 'skip over' them)
307 static const char params_with_one_subparam[][16] = { "mod", "validate", /*...*/ };
308
309 bool curr_arg_has_one_subparam = false;
310 int i;
311 for (i = 0; i < COUNT(params_with_one_subparam); ++i) {
312 if (strncasecmp(curr_arg, params_with_one_subparam[i], strlen(params_with_one_subparam[i])) == 0) {
313 curr_arg_has_one_subparam = true;
314 break;
315 }
316 }
317
318 if (curr_arg_has_one_subparam) {
319 // Found an arg that has one sub-param, so we want to:
320 // 1: skip over the next arg (if we are NOT checking for this specific param)
321 // 2: return a pointer below to the SUB-arg (if we ARE checking for this specific param)
322 ++arg_index;
323 if (!(arg_index < g_argc)) return NULL; // not enough arguments
324 }
325
326 if (/*strnicmp*/strncasecmp(curr_arg, param, strlen(param)) == 0) {
327 return g_argv[arg_index];
328 }
329 }
330 return NULL;
331 }
332
333 // seg009:0EDF
pop_wait(int timer_index,int time)334 int __pascal far pop_wait(int timer_index,int time) {
335 start_timer(timer_index, time);
336 return do_wait(timer_index);
337 }
338
open_dat_from_root_or_data_dir(const char * filename)339 static FILE* open_dat_from_root_or_data_dir(const char* filename) {
340 FILE* fp = NULL;
341 fp = fopen(filename, "rb");
342
343 // if failed, try if the DAT file can be opened in the data/ directory, instead of the main folder
344 if (fp == NULL) {
345 char data_path[POP_MAX_PATH];
346 snprintf_check(data_path, sizeof(data_path), "/usr/local/share/sdlpop/%s", filename);
347
348 if (!file_exists(data_path)) {
349 find_exe_dir();
350 snprintf_check(data_path, sizeof(data_path), "%s/data/%s", exe_dir, filename);
351 }
352
353 // verify that this is a regular file and not a directory (otherwise, don't open)
354 struct stat path_stat;
355 stat(data_path, &path_stat);
356 if (S_ISREG(path_stat.st_mode)) {
357 fp = fopen(data_path, "rb");
358 }
359 }
360 return fp;
361 }
362
363 int __pascal far showmessage(char far *text,int arg_4,void far *arg_0);
364
365 // seg009:0F58
open_dat(const char * filename,int drive)366 dat_type *__pascal open_dat(const char *filename,int drive) {
367 FILE* fp = NULL;
368 if (!use_custom_levelset) {
369 fp = open_dat_from_root_or_data_dir(filename);
370 }
371 else {
372 if (!skip_mod_data_files) {
373 char filename_mod[POP_MAX_PATH];
374 // before checking the root directory, first try mods/MODNAME/
375 snprintf_check(filename_mod, sizeof(filename_mod), "%s/%s", mod_data_path, filename);
376 fp = fopen(filename_mod, "rb");
377 }
378 if (fp == NULL && !skip_normal_data_files) {
379 fp = open_dat_from_root_or_data_dir(filename);
380 }
381 }
382 dat_header_type dat_header;
383 dat_table_type* dat_table = NULL;
384
385 dat_type* pointer = (dat_type*) calloc(1, sizeof(dat_type));
386 snprintf_check(pointer->filename, sizeof(pointer->filename), "%s", filename);
387 pointer->next_dat = dat_chain_ptr;
388 dat_chain_ptr = pointer;
389
390 if (fp != NULL) {
391 if (fread(&dat_header, 6, 1, fp) != 1)
392 goto failed;
393 dat_table = (dat_table_type*) malloc(dat_header.table_size);
394 if (dat_table == NULL ||
395 fseek(fp, dat_header.table_offset, SEEK_SET) ||
396 fread(dat_table, dat_header.table_size, 1, fp) != 1)
397 goto failed;
398 pointer->handle = fp;
399 pointer->dat_table = dat_table;
400 } else {
401 /* // showmessage will crash if we call if before certain things are initialized!
402 // There is no DAT file, verify whether the corresponding directory exists.
403 char filename_no_ext[POP_MAX_PATH];
404 // strip the .DAT file extension from the filename (use folders simply named TITLE, KID, VPALACE, etc.)
405 strncpy(filename_no_ext, pointer->filename, sizeof(filename_no_ext));
406 size_t len = strlen(filename_no_ext);
407 if (len >= 5 && filename_no_ext[len-4] == '.') {
408 filename_no_ext[len-4] = '\0'; // terminate, so ".DAT" is deleted from the filename
409 }
410 char filename[POP_MAX_PATH];
411 snprintf_check(filename,sizeof(filename),"data/%s",filename_no_ext);
412 const char* data_path = locate_file(filename);
413 struct stat path_stat;
414 int result = stat(data_path, &path_stat);
415 if (result != 0 || !S_ISDIR(path_stat.st_mode)) {
416 char error_message[256];
417 snprintf_check(error_message, sizeof(error_message), "Cannot find a required data file: %s\nPress any key to quit.", filename);
418 if (onscreen_surface_ != NULL && copyprot_dialog != NULL) { // otherwise showmessage will crash
419 showmessage(error_message, 1, &key_test_quit);
420 quit(1);
421 }
422 }
423 */
424 }
425 out:
426 // stub
427 return pointer;
428 failed:
429 perror(filename);
430 if (fp)
431 fclose(fp);
432 if (dat_table)
433 free(dat_table);
434 goto out;
435 }
436
437 // seg009:9CAC
set_loaded_palette(dat_pal_type far * palette_ptr)438 void __pascal far set_loaded_palette(dat_pal_type far *palette_ptr) {
439 int dest_row, dest_index, source_row;
440 for (dest_row = dest_index = source_row = 0; dest_row < 16; ++dest_row, dest_index += 0x10) {
441 if (palette_ptr->row_bits & (1 << dest_row)) {
442 set_pal_arr(dest_index, 16, palette_ptr->vga + source_row*0x10, 1);
443 ++source_row;
444 }
445 }
446 }
447
448 // data:3356
449 word chtab_palette_bits = 1;
450
451 // seg009:104E
load_sprites_from_file(int resource,int palette_bits,int quit_on_error)452 chtab_type* __pascal load_sprites_from_file(int resource,int palette_bits, int quit_on_error) {
453 int i;
454 int n_images = 0;
455 //int has_palette_bits = 1;
456 chtab_type* chtab = NULL;
457 dat_shpl_type* shpl = (dat_shpl_type*) load_from_opendats_alloc(resource, "pal", NULL, NULL);
458 if (shpl == NULL) {
459 printf("Can't load sprites from resource %d.\n", resource);
460 if (quit_on_error) {
461 char error_message[256];
462 // Unfortunately we don't know at this point which data file is missing. So we use the name of the last opened DAT file.
463 // It's also possible that the DAT file exists and it just doesn't contain the needed resource.
464 snprintf_check(error_message, sizeof(error_message), "Cannot find a required data file: %s\nMake sure that the data/ folder exists.\nPress any key to quit.", dat_chain_ptr->filename);
465 showmessage(error_message, 1, &key_test_quit);
466 quit(1);
467 }
468 return NULL;
469 }
470
471 dat_pal_type* pal_ptr = &shpl->palette;
472 if (graphics_mode == gmMcgaVga) {
473 if (palette_bits == 0) {
474 /*
475 palette_bits = add_palette_bits(pal_ptr->n_colors);
476 if (palette_bits == 0) {
477 quit(1);
478 }
479 */
480 } else {
481 chtab_palette_bits |= palette_bits;
482 //has_palette_bits = 0;
483 }
484 pal_ptr->row_bits = palette_bits;
485 }
486
487 n_images = shpl->n_images;
488 size_t alloc_size = sizeof(chtab_type) + sizeof(void far *) * n_images;
489 chtab = (chtab_type*) malloc(alloc_size);
490 memset(chtab, 0, alloc_size);
491 chtab->n_images = n_images;
492 for (i = 1; i <= n_images; i++) {
493 SDL_Surface* image = load_image(resource + i, pal_ptr);
494 // if (image == NULL) printf(" failed");
495 if (image != NULL) {
496
497 if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
498 sdlperror("load_sprites_from_file: SDL_SetAlpha");
499 quit(1);
500 }
501
502 /*
503 if (SDL_SetColorKey(image, SDL_SRCCOLORKEY, 0) != 0) {
504 sdlperror("load_sprites_from_file: SDL_SetColorKey");
505 quit(1);
506 }
507 */
508 }
509 // printf("\n");
510 chtab->images[i-1] = image;
511 }
512 set_loaded_palette(pal_ptr);
513 return chtab;
514 }
515
516 // seg009:11A8
free_chtab(chtab_type * chtab_ptr)517 void __pascal far free_chtab(chtab_type *chtab_ptr) {
518 image_type far* curr_image;
519 word id;
520 word n_images;
521 if (graphics_mode == gmMcgaVga && chtab_ptr->has_palette_bits) {
522 chtab_palette_bits &= ~ chtab_ptr->chtab_palette_bits;
523 }
524 n_images = chtab_ptr->n_images;
525 for (id = 0; id < n_images; ++id) {
526 curr_image = chtab_ptr->images[id];
527 if (curr_image) {
528 SDL_FreeSurface(curr_image);
529 }
530 }
531 free_near(chtab_ptr);
532 }
533
534 // seg009:8CE6
decompress_rle_lr(byte far * destination,const byte far * source,int dest_length)535 void __pascal far decompress_rle_lr(byte far *destination,const byte far *source,int dest_length) {
536 const byte* src_pos = source;
537 byte* dest_pos = destination;
538 short rem_length = dest_length;
539 while (rem_length) {
540 sbyte count = *(src_pos++);
541 if (count >= 0) { // copy
542 ++count;
543 do {
544 *(dest_pos++) = *(src_pos++);
545 --rem_length;
546 } while (--count && rem_length);
547 } else { // repeat
548 byte al = *(src_pos++);
549 count = -count;
550 do {
551 *(dest_pos++) = al;
552 --rem_length;
553 } while (--count && rem_length);
554 }
555 }
556 }
557
558 // seg009:8D1C
decompress_rle_ud(byte far * destination,const byte far * source,int dest_length,int width,int height)559 void __pascal far decompress_rle_ud(byte far *destination,const byte far *source,int dest_length,int width,int height) {
560 short rem_height = height;
561 const byte* src_pos = source;
562 byte* dest_pos = destination;
563 short rem_length = dest_length;
564 --dest_length;
565 --width;
566 while (rem_length) {
567 sbyte count = *(src_pos++);
568 if (count >= 0) { // copy
569 ++count;
570 do {
571 *(dest_pos++) = *(src_pos++);
572 dest_pos += width;
573 if (--rem_height == 0) {
574 dest_pos -= dest_length;
575 rem_height = height;
576 }
577 --rem_length;
578 } while (--count && rem_length);
579 } else { // repeat
580 byte al = *(src_pos++);
581 count = -count;
582 do {
583 *(dest_pos++) = al;
584 dest_pos += width;
585 if (--rem_height == 0) {
586 dest_pos -= dest_length;
587 rem_height = height;
588 }
589 --rem_length;
590 } while (--count && rem_length);
591 }
592 }
593 }
594
595 // seg009:90FA
decompress_lzg_lr(byte far * dest,const byte far * source,int dest_length)596 byte far* __pascal far decompress_lzg_lr(byte far *dest,const byte far *source,int dest_length) {
597 byte* window = (byte*) malloc_near(0x400);
598 if (window == NULL) return NULL;
599 memset(window, 0, 0x400);
600 byte* window_pos = window + 0x400 - 0x42; // bx
601 short remaining = dest_length; // cx
602 byte* window_end = window + 0x400; // dx
603 const byte* source_pos = source;
604 byte* dest_pos = dest;
605 word mask = 0;
606 do {
607 mask >>= 1;
608 if ((mask & 0xFF00) == 0) {
609 mask = *(source_pos++) | 0xFF00;
610 }
611 if (mask & 1) {
612 *(window_pos++) = *(dest_pos++) = *(source_pos++);
613 if (window_pos >= window_end) window_pos = window;
614 --remaining;
615 } else {
616 word copy_info = *(source_pos++);
617 copy_info = (copy_info << 8) | *(source_pos++);
618 byte* copy_source = window + (copy_info & 0x3FF);
619 byte copy_length = (copy_info >> 10) + 3;
620 do {
621 *(window_pos++) = *(dest_pos++) = *(copy_source++);
622 if (copy_source >= window_end) copy_source = window;
623 if (window_pos >= window_end) window_pos = window;
624 } while (--remaining && --copy_length);
625 }
626 } while (remaining);
627 // end:
628 free(window);
629 return dest;
630 }
631
632 // seg009:91AD
decompress_lzg_ud(byte far * dest,const byte far * source,int dest_length,int stride,int height)633 byte far* __pascal far decompress_lzg_ud(byte far *dest,const byte far *source,int dest_length,int stride,int height) {
634 byte* window = (byte*) malloc_near(0x400);
635 if (window == NULL) return NULL;
636 memset(window, 0, 0x400);
637 byte* window_pos = window + 0x400 - 0x42; // bx
638 short remaining = height; // cx
639 byte* window_end = window + 0x400; // dx
640 const byte* source_pos = source;
641 byte* dest_pos = dest;
642 word mask = 0;
643 short var_6 = dest_length - 1;
644 do {
645 mask >>= 1;
646 if ((mask & 0xFF00) == 0) {
647 mask = *(source_pos++) | 0xFF00;
648 }
649 if (mask & 1) {
650 *(window_pos++) = *dest_pos = *(source_pos++);
651 dest_pos += stride;
652 if (--remaining == 0) {
653 dest_pos -= var_6;
654 remaining = height;
655 }
656 if (window_pos >= window_end) window_pos = window;
657 --dest_length;
658 } else {
659 word copy_info = *(source_pos++);
660 copy_info = (copy_info << 8) | *(source_pos++);
661 byte* copy_source = window + (copy_info & 0x3FF);
662 byte copy_length = (copy_info >> 10) + 3;
663 do {
664 *(window_pos++) = *dest_pos = *(copy_source++);
665 dest_pos += stride;
666 if (--remaining == 0) {
667 dest_pos -= var_6;
668 remaining = height;
669 }
670 if (copy_source >= window_end) copy_source = window;
671 if (window_pos >= window_end) window_pos = window;
672 } while (--dest_length && --copy_length);
673 }
674 } while (dest_length);
675 // end:
676 free(window);
677 return dest;
678 }
679
680 // seg009:938E
decompr_img(byte far * dest,const image_data_type far * source,int decomp_size,int cmeth,int stride)681 void __pascal far decompr_img(byte far *dest,const image_data_type far *source,int decomp_size,int cmeth, int stride) {
682 switch (cmeth) {
683 case 0: // RAW left-to-right
684 memcpy_far(dest, &source->data, decomp_size);
685 break;
686 case 1: // RLE left-to-right
687 decompress_rle_lr(dest, source->data, decomp_size);
688 break;
689 case 2: // RLE up-to-down
690 decompress_rle_ud(dest, source->data, decomp_size, stride, source->height);
691 break;
692 case 3: // LZG left-to-right
693 decompress_lzg_lr(dest, source->data, decomp_size);
694 break;
695 case 4: // LZG up-to-down
696 decompress_lzg_ud(dest, source->data, decomp_size, stride, source->height);
697 break;
698 }
699 }
700
calc_stride(image_data_type * image_data)701 int calc_stride(image_data_type* image_data) {
702 int width = image_data->width;
703 int flags = image_data->flags;
704 int depth = ((flags >> 12) & 7) + 1;
705 return (depth * width + 7) / 8;
706 }
707
conv_to_8bpp(byte * in_data,int width,int height,int stride,int depth)708 byte* conv_to_8bpp(byte* in_data, int width, int height, int stride, int depth) {
709 byte* out_data = (byte*) malloc(width * height);
710 int y, x_pixel, x_byte, pixel_in_byte;
711 int pixels_per_byte = 8 / depth;
712 int mask = (1 << depth) - 1;
713 for (y = 0; y < height; ++y) {
714 byte* in_pos = in_data + y*stride;
715 byte* out_pos = out_data + y*width;
716 for (x_pixel = x_byte = 0; x_byte < stride; ++x_byte) {
717 byte v = *in_pos;
718 int shift = 8;
719 for (pixel_in_byte = 0; pixel_in_byte < pixels_per_byte && x_pixel < width; ++pixel_in_byte, ++x_pixel) {
720 shift -= depth;
721 *out_pos = (v >> shift) & mask;
722 ++out_pos;
723 }
724 ++in_pos;
725 }
726 }
727 return out_data;
728 }
729
decode_image(image_data_type * image_data,dat_pal_type * palette)730 image_type* decode_image(image_data_type* image_data, dat_pal_type* palette) {
731 int height = image_data->height;
732 if (height == 0) return NULL;
733 int width = image_data->width;
734 int flags = image_data->flags;
735 int depth = ((flags >> 12) & 7) + 1;
736 int cmeth = (flags >> 8) & 0x0F;
737 int stride = calc_stride(image_data);
738 int dest_size = stride * height;
739 byte* dest = (byte*) malloc(dest_size);
740 memset(dest, 0, dest_size);
741 decompr_img(dest, image_data, dest_size, cmeth, stride);
742 byte* image_8bpp = conv_to_8bpp(dest, width, height, stride, depth);
743 free(dest); dest = NULL;
744 image_type* image = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
745 if (image == NULL) {
746 sdlperror("decode_image: SDL_CreateRGBSurface");
747 quit(1);
748 }
749 if (SDL_LockSurface(image) != 0) {
750 sdlperror("decode_image: SDL_LockSurface");
751 }
752 int y;
753 for (y = 0; y < height; ++y) {
754 // fill image with data
755 memcpy((byte*)image->pixels + y*image->pitch, image_8bpp + y*width, width);
756 }
757 SDL_UnlockSurface(image);
758
759 free(image_8bpp); image_8bpp = NULL;
760 SDL_Color colors[16];
761 int i;
762 for (i = 0; i < 16; ++i) {
763 colors[i].r = palette->vga[i].r << 2;
764 colors[i].g = palette->vga[i].g << 2;
765 colors[i].b = palette->vga[i].b << 2;
766 colors[i].a = SDL_ALPHA_OPAQUE; // SDL2's SDL_Color has a fourth alpha component
767 }
768 // Force 0th color to be black for non-transparent blitters. (hitpoints, shadow)
769 // This is needed to remove the colored rectangles around hitpoints and the shadow, when using Brain's SNES graphics for example.
770 colors[0].r = 0;
771 colors[0].g = 0;
772 colors[0].b = 0;
773 colors[0].a = SDL_ALPHA_TRANSPARENT;
774 SDL_SetPaletteColors(image->format->palette, colors, 0, 16); // SDL_SetColors = deprecated
775 return image;
776 }
777
778 // seg009:121A
load_image(int resource_id,dat_pal_type * palette)779 image_type* far __pascal far load_image(int resource_id, dat_pal_type* palette) {
780 // stub
781 data_location result;
782 int size;
783 void* image_data = load_from_opendats_alloc(resource_id, "png", &result, &size);
784 image_type* image = NULL;
785 switch (result) {
786 case data_none:
787 return NULL;
788 break;
789 case data_DAT: { // DAT
790 image = decode_image((image_data_type*) image_data, palette);
791 } break;
792 case data_directory: { // directory
793 SDL_RWops* rw = SDL_RWFromConstMem(image_data, size);
794 if (rw == NULL) {
795 sdlperror("load_image: SDL_RWFromConstMem");
796 return NULL;
797 }
798 image = IMG_Load_RW(rw, 0);
799 if (image == NULL) {
800 printf("load_image: IMG_Load_RW: %s\n", IMG_GetError());
801 }
802 if (SDL_RWclose(rw) != 0) {
803 sdlperror("load_image: SDL_RWclose");
804 }
805 } break;
806 }
807 if (image_data != NULL) free(image_data);
808
809
810 if (image != NULL) {
811 // should immediately start using the onscreen pixel format, so conversion will not be needed
812
813 if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) { //sdl 1.2: SDL_SRCCOLORKEY
814 sdlperror("load_image: SDL_SetColorKey");
815 quit(1);
816 }
817 // printf("bpp = %d\n", image->format->BitsPerPixel);
818 if (SDL_SetSurfaceAlphaMod(image, 0) != 0) { //sdl 1.2: SDL_SetAlpha removed
819 sdlperror("load_image: SDL_SetAlpha");
820 quit(1);
821 }
822 // image_type* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
823 // if (!colored_image) {
824 // sdlperror("load_image: SDL_ConvertSurfaceFormat");
825 // quit(1);
826 // }
827 // SDL_FreeSurface(image);
828 // image = colored_image;
829 }
830 return image;
831 }
832
833 // seg009:13C4
draw_image_transp(image_type far * image,image_type far * mask,int xpos,int ypos)834 void __pascal far draw_image_transp(image_type far *image,image_type far *mask,int xpos,int ypos) {
835 if (graphics_mode == gmMcgaVga) {
836 draw_image_transp_vga(image, xpos, ypos);
837 } else {
838 // ...
839 }
840 }
841
842 // seg009:157E
set_joy_mode()843 int __pascal far set_joy_mode() {
844 // stub
845 if (SDL_NumJoysticks() < 1) {
846 is_joyst_mode = 0;
847 } else {
848 if (gamecontrollerdb_file[0] != '\0') {
849 SDL_GameControllerAddMappingsFromFile(gamecontrollerdb_file);
850 }
851
852 if (SDL_IsGameController(0)) {
853 sdl_controller_ = SDL_GameControllerOpen(0);
854 if (sdl_controller_ == NULL) {
855 is_joyst_mode = 0;
856 } else {
857 is_joyst_mode = 1;
858 }
859 }
860 // We have a joystick connected, but it's NOT compatible with the SDL_GameController
861 // interface, so we resort to the classic SDL_Joystick interface instead
862 else {
863 sdl_joystick_ = SDL_JoystickOpen(0);
864 is_joyst_mode = 1;
865 using_sdl_joystick_interface = 1;
866 }
867 }
868 if (enable_controller_rumble && is_joyst_mode) {
869 sdl_haptic = SDL_HapticOpen(0);
870 SDL_HapticRumbleInit(sdl_haptic); // initialize the device for simple rumble
871 } else {
872 sdl_haptic = NULL;
873 }
874
875 is_keyboard_mode = !is_joyst_mode;
876 return is_joyst_mode;
877 }
878
879 // seg009:178B
make_offscreen_buffer(const rect_type far * rect)880 surface_type far *__pascal make_offscreen_buffer(const rect_type far *rect) {
881 // stub
882 #ifndef USE_ALPHA
883 // Bit order matches onscreen buffer, good for fading.
884 return SDL_CreateRGBSurface(0, rect->right, rect->bottom, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0); //RGB888 (little endian)
885 #else
886 return SDL_CreateRGBSurface(0, rect->right, rect->bottom, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24);
887 #endif
888 //return surface;
889 }
890
891 // seg009:17BD
free_surface(surface_type * surface)892 void __pascal far free_surface(surface_type *surface) {
893 SDL_FreeSurface(surface);
894 }
895
896 // seg009:17EA
free_peel(peel_type * peel_ptr)897 void __pascal far free_peel(peel_type *peel_ptr) {
898 SDL_FreeSurface(peel_ptr->peel);
899 free(peel_ptr);
900 }
901
902 // seg009:182F
set_hc_pal()903 void __pascal far set_hc_pal() {
904 // stub
905 if (graphics_mode == gmMcgaVga) {
906 set_pal_arr(0, 16, custom->vga_palette, 1);
907 } else {
908 // ...
909 }
910 }
911
912 // seg009:2446
flip_not_ega(byte far * memory,int height,int stride)913 void __pascal far flip_not_ega(byte far *memory,int height,int stride) {
914 byte* row_buffer = (byte*) malloc(stride);
915 byte* top_ptr;
916 byte* bottom_ptr;
917 bottom_ptr = top_ptr = memory;
918 bottom_ptr += (height - 1) * stride;
919 short rem_rows = height >> 1;
920 do {
921 memcpy(row_buffer, top_ptr, stride);
922 memcpy(top_ptr, bottom_ptr, stride);
923 memcpy(bottom_ptr, row_buffer, stride);
924 top_ptr += stride;
925 bottom_ptr -= stride;
926 --rem_rows;
927 } while (rem_rows);
928 free(row_buffer);
929 }
930
931 // seg009:19B1
flip_screen(surface_type far * surface)932 void __pascal far flip_screen(surface_type far *surface) {
933 // stub
934 if (graphics_mode != gmEga) {
935 if (SDL_LockSurface(surface) != 0) {
936 sdlperror("flip_screen: SDL_LockSurface");
937 quit(1);
938 }
939 flip_not_ega((byte*) surface->pixels, surface->h, surface->pitch);
940 SDL_UnlockSurface(surface);
941 } else {
942 // ...
943 }
944 }
945
946 #ifndef USE_FADE
947 // seg009:19EF
fade_in_2(surface_type near * source_surface,int which_rows)948 void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
949 // stub
950 method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
951 }
952
953 // seg009:1CC9
fade_out_2(int rows)954 void __pascal far fade_out_2(int rows) {
955 // stub
956 }
957 #endif // USE_FADE
958
959 // seg009:2288
draw_image_transp_vga(image_type far * image,int xpos,int ypos)960 void __pascal far draw_image_transp_vga(image_type far *image,int xpos,int ypos) {
961 // stub
962 method_6_blit_img_to_scr(image, xpos, ypos, blitters_10h_transp);
963 }
964
965 #ifdef USE_TEXT
966
967
968 /*const*/ byte hc_font_data[] = {
969 0x20,0x83,0x07,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0xD2,0x00,0xD8,0x00,0xE5,0x00,
970 0xEE,0x00,0xFA,0x00,0x07,0x01,0x14,0x01,0x21,0x01,0x2A,0x01,0x37,0x01,0x44,0x01,
971 0x50,0x01,0x5C,0x01,0x6A,0x01,0x74,0x01,0x81,0x01,0x8E,0x01,0x9B,0x01,0xA8,0x01,
972 0xB5,0x01,0xC2,0x01,0xCF,0x01,0xDC,0x01,0xE9,0x01,0xF6,0x01,0x03,0x02,0x10,0x02,
973 0x1C,0x02,0x2A,0x02,0x37,0x02,0x42,0x02,0x4F,0x02,0x5C,0x02,0x69,0x02,0x76,0x02,
974 0x83,0x02,0x90,0x02,0x9D,0x02,0xAA,0x02,0xB7,0x02,0xC4,0x02,0xD1,0x02,0xDE,0x02,
975 0xEB,0x02,0xF8,0x02,0x05,0x03,0x12,0x03,0x1F,0x03,0x2C,0x03,0x39,0x03,0x46,0x03,
976 0x53,0x03,0x60,0x03,0x6D,0x03,0x7A,0x03,0x87,0x03,0x94,0x03,0xA1,0x03,0xAE,0x03,
977 0xBB,0x03,0xC8,0x03,0xD5,0x03,0xE2,0x03,0xEB,0x03,0xF9,0x03,0x02,0x04,0x0F,0x04,
978 0x1C,0x04,0x29,0x04,0x36,0x04,0x43,0x04,0x50,0x04,0x5F,0x04,0x6C,0x04,0x79,0x04,
979 0x88,0x04,0x95,0x04,0xA2,0x04,0xAF,0x04,0xBC,0x04,0xC9,0x04,0xD8,0x04,0xE7,0x04,
980 0xF4,0x04,0x01,0x05,0x0E,0x05,0x1B,0x05,0x28,0x05,0x35,0x05,0x42,0x05,0x51,0x05,
981 0x5E,0x05,0x6B,0x05,0x78,0x05,0x85,0x05,0x8D,0x05,0x9A,0x05,0xA7,0x05,0xBB,0x05,
982 0xD9,0x05,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x02,0x00,0x01,0x00,0xC0,0xC0,
983 0xC0,0xC0,0xC0,0x00,0xC0,0x03,0x00,0x05,0x00,0x01,0x00,0xD8,0xD8,0xD8,0x06,0x00,
984 0x07,0x00,0x01,0x00,0x00,0x6C,0xFE,0x6C,0xFE,0x6C,0x07,0x00,0x07,0x00,0x01,0x00,
985 0x10,0x7C,0xD0,0x7C,0x16,0x7C,0x10,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xC6,0x0C,
986 0x18,0x30,0x63,0xC3,0x07,0x00,0x08,0x00,0x01,0x00,0x38,0x6C,0x38,0x7A,0xCC,0xCE,
987 0x7B,0x03,0x00,0x03,0x00,0x01,0x00,0x60,0x60,0xC0,0x07,0x00,0x04,0x00,0x01,0x00,
988 0x30,0x60,0xC0,0xC0,0xC0,0x60,0x30,0x07,0x00,0x04,0x00,0x01,0x00,0xC0,0x60,0x30,
989 0x30,0x30,0x60,0xC0,0x06,0x00,0x07,0x00,0x01,0x00,0x00,0x6C,0x38,0xFE,0x38,0x6C,
990 0x06,0x00,0x06,0x00,0x01,0x00,0x00,0x30,0x30,0xFC,0x30,0x30,0x08,0x00,0x03,0x00,
991 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x04,0x00,0x04,0x00,0x01,0x00,
992 0x00,0x00,0x00,0xF0,0x07,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,
993 0xC0,0x07,0x00,0x08,0x00,0x01,0x00,0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x07,0x00,
994 0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,
995 0x00,0x30,0x70,0xF0,0x30,0x30,0x30,0xFC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,
996 0x0C,0x18,0x30,0x60,0xFC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0x0C,0x18,0x0C,
997 0xCC,0x78,0x07,0x00,0x07,0x00,0x01,0x00,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x07,
998 0x00,0x06,0x00,0x01,0x00,0xF8,0xC0,0xC0,0xF8,0x0C,0x0C,0xF8,0x07,0x00,0x06,0x00,
999 0x01,0x00,0x78,0xC0,0xC0,0xF8,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0xFC,
1000 0x0C,0x18,0x30,0x30,0x30,0x30,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0x78,
1001 0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,
1002 0x06,0x00,0x02,0x00,0x01,0x00,0x00,0xC0,0xC0,0x00,0xC0,0xC0,0x08,0x00,0x03,0x00,
1003 0x01,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0xC0,0x07,0x00,0x05,0x00,0x01,0x00,
1004 0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x05,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0xF0,
1005 0x00,0xF0,0x07,0x00,0x05,0x00,0x01,0x00,0xC0,0x60,0x30,0x18,0x30,0x60,0xC0,0x07,
1006 0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0x0C,0x18,0x30,0x00,0x30,0x07,0x00,0x06,0x00,
1007 0x01,0x00,0x78,0xCC,0xDC,0xDC,0xD8,0xC0,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x78,
1008 0xCC,0xCC,0xFC,0xCC,0xCC,0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xF8,
1009 0xCC,0xCC,0xF8,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,0xC0,0xC0,0xCC,0x78,
1010 0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0xCC,0xF8,0x07,0x00,0x05,
1011 0x00,0x01,0x00,0xF8,0xC0,0xC0,0xF0,0xC0,0xC0,0xF8,0x07,0x00,0x05,0x00,0x01,0x00,
1012 0xF8,0xC0,0xC0,0xF0,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,
1013 0xDC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xCC,0xFC,0xCC,0xCC,
1014 0xCC,0x07,0x00,0x04,0x00,0x01,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0xF0,0x07,0x00,
1015 0x06,0x00,0x01,0x00,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0x78,0x07,0x00,0x07,0x00,0x01,
1016 0x00,0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x07,0x00,0x05,0x00,0x01,0x00,0xC0,0xC0,
1017 0xC0,0xC0,0xC0,0xC0,0xF8,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xE7,0xFF,0xDB,0xC3,
1018 0xC3,0xC3,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xEC,0xFC,0xDC,0xCC,0xCC,0x07,
1019 0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,
1020 0x01,0x00,0xF8,0xCC,0xCC,0xF8,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x78,
1021 0xCC,0xCC,0xCC,0xCC,0xD8,0x6C,0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xF8,
1022 0xD8,0xCC,0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,0x78,0x0C,0xCC,0x78,
1023 0x07,0x00,0x06,0x00,0x01,0x00,0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x07,0x00,0x06,
1024 0x00,0x01,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,0x00,
1025 0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xC3,0xC3,
1026 0xDB,0xFF,0xE7,0xC3,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0x78,0x30,0x78,0xCC,
1027 0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x07,0x00,
1028 0x08,0x00,0x01,0x00,0xFF,0x06,0x0C,0x18,0x30,0x60,0xFF,0x07,0x00,0x04,0x00,0x01,
1029 0x00,0xF0,0xC0,0xC0,0xC0,0xC0,0xC0,0xF0,0x07,0x00,0x08,0x00,0x01,0x00,0xC0,0x60,
1030 0x30,0x18,0x0C,0x06,0x03,0x07,0x00,0x04,0x00,0x01,0x00,0xF0,0x30,0x30,0x30,0x30,
1031 0x30,0xF0,0x03,0x00,0x06,0x00,0x01,0x00,0x30,0x78,0xCC,0x08,0x00,0x06,0x00,0x01,
1032 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x04,0x00,0x01,0x00,0xC0,
1033 0x60,0x30,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0x7C,0x07,
1034 0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xF8,0x07,0x00,0x06,0x00,
1035 0x01,0x00,0x00,0x00,0x78,0xCC,0xC0,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x0C,
1036 0x0C,0x7C,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xCC,
1037 0xFC,0xC0,0x7C,0x07,0x00,0x05,0x00,0x01,0x00,0x38,0x60,0xF8,0x60,0x60,0x60,0x60,
1038 0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x7C,0x0C,0x78,0x07,
1039 0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xCC,0x07,0x00,0x02,0x00,
1040 0x01,0x00,0xC0,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0x09,0x00,0x04,0x00,0x01,0x00,0x30,
1041 0x00,0x30,0x30,0x30,0x30,0x30,0x30,0xE0,0x07,0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,
1042 0xCC,0xD8,0xF0,0xD8,0xCC,0x07,0x00,0x02,0x00,0x01,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,
1043 0xC0,0xC0,0x07,0x00,0x08,0x00,0x01,0x00,0x00,0x00,0xFE,0xDB,0xDB,0xDB,0xDB,0x07,
1044 0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0x07,0x00,0x06,0x00,
1045 0x01,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x78,0x09,0x00,0x06,0x00,0x01,0x00,0x00,
1046 0x00,0xF8,0xCC,0xCC,0xCC,0xF8,0xC0,0xC0,0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,
1047 0x78,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,
1048 0xCC,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xC0,0x78,0x0C,
1049 0xF8,0x07,0x00,0x05,0x00,0x01,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x38,0x07,0x00,
1050 0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,
1051 0x00,0x00,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x07,0x00,0x08,0x00,0x01,0x00,0x00,0x00,
1052 0xC3,0xC3,0xDB,0xFF,0x66,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0x78,0x30,
1053 0x78,0xCC,0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,
1054 0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xFC,0x18,0x30,0x60,0xFC,0x07,0x00,
1055 0x04,0x00,0x01,0x00,0x30,0x60,0x60,0xC0,0x60,0x60,0x30,0x07,0x00,0x02,0x00,0x01,
1056 0x00,0xC0,0xC0,0xC0,0x00,0xC0,0xC0,0xC0,0x07,0x00,0x04,0x00,0x01,0x00,0xC0,0x60,
1057 0x60,0x30,0x60,0x60,0xC0,0x02,0x00,0x07,0x00,0x01,0x00,0x76,0xDC,0x07,0x00,0x07,
1058 0x00,0x01,0x00,0x00,0x00,0x70,0xC4,0xCC,0x8C,0x38,0x07,0x00,0x07,0x00,0x01,0x00,
1059 0x00,0x06,0x0C,0xD8,0xF0,0xE0,0xC0,0x08,0x00,0x10,0x00,0x02,0x00,0x7F,0xFE,0xCD,
1060 0xC7,0xB5,0xEF,0xB5,0xEF,0x85,0xEF,0xB5,0xEF,0xB4,0x6F,0x08,0x00,0x13,0x00,0x03,
1061 0x00,0x7F,0xFF,0xC0,0xCC,0x46,0xE0,0xB6,0xDA,0xE0,0xBE,0xDA,0xE0,0xBE,0xC6,0xE0,
1062 0xB6,0xDA,0xE0,0xCE,0xDA,0x20,0x7F,0xFF,0xC0,0x08,0x00,0x11,0x00,0x03,0x00,0x7F,
1063 0xFF,0x00,0xC6,0x73,0x80,0xDD,0xAD,0x80,0xCE,0xEF,0x80,0xDF,0x6F,0x80,0xDD,0xAD,
1064 0x80,0xC6,0x73,0x80,0x7F,0xFF,0x00
1065 };
1066
load_font_character_offsets(rawfont_type * data)1067 static void load_font_character_offsets(rawfont_type* data) {
1068 int n_chars = data->last_char - data->first_char + 1;
1069 byte* pos = (byte*) &data->offsets[n_chars];
1070 for (int index = 0; index < n_chars; ++index) {
1071 data->offsets[index] = (word) (pos - (byte*) data);
1072 image_data_type* image_data = (image_data_type*) pos;
1073 int image_bytes = image_data->height * calc_stride(image_data);
1074 pos = (byte*) &image_data->data + image_bytes;
1075 }
1076 }
1077
load_font_from_data(rawfont_type * data)1078 font_type load_font_from_data(/*const*/ rawfont_type* data) {
1079 font_type font;
1080 font.first_char = data->first_char;
1081 font.last_char = data->last_char;
1082 font.height_above_baseline = data->height_above_baseline;
1083 font.height_below_baseline = data->height_below_baseline;
1084 font.space_between_lines = data->space_between_lines;
1085 font.space_between_chars = data->space_between_chars;
1086 int n_chars = font.last_char - font.first_char + 1;
1087 // Allow loading a font even if the offsets for each character image were not supplied in the raw data.
1088 if (data->offsets[0] == 0) {
1089 load_font_character_offsets(data);
1090 }
1091 chtab_type* chtab = malloc(sizeof(chtab_type) + sizeof(image_type* far) * n_chars);
1092 int chr,index;
1093 // Make a dummy palette for decode_image().
1094 dat_pal_type dat_pal;
1095 memset(&dat_pal, 0, sizeof(dat_pal));
1096 dat_pal.vga[1].r = dat_pal.vga[1].g = dat_pal.vga[1].b = 0x3F; // white
1097 for (index = 0, chr = data->first_char; chr <= data->last_char; ++index, ++chr) {
1098 /*const*/ image_data_type* image_data = (/*const*/ image_data_type*)((/*const*/ byte*)data + data->offsets[index]);
1099 //image_data->flags=0;
1100 if (image_data->height == 0) image_data->height = 1; // HACK: decode_image() returns NULL if height==0.
1101 image_type* image;
1102 chtab->images[index] = image = decode_image(image_data, &dat_pal);
1103 if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
1104 sdlperror("load_font_from_data: SDL_SetColorKey");
1105 quit(1);
1106 }
1107 }
1108 font.chtab = chtab;
1109 return font;
1110 }
1111
1112 // Small font data (hardcoded), defined in menu.c
1113 extern byte hc_small_font_data[];
1114
load_font()1115 void load_font() {
1116 // Try to load font from a file.
1117 dat_type* dathandle = open_dat("font", 0);
1118 hc_font.chtab = load_sprites_from_file(1000, 1<<1, 0);
1119 close_dat(dathandle);
1120 if (hc_font.chtab == NULL) {
1121 // Use built-in font.
1122 hc_font = load_font_from_data((/*const*/ rawfont_type*)hc_font_data);
1123 }
1124
1125 #ifdef USE_MENU
1126 hc_small_font = load_font_from_data((rawfont_type*)hc_small_font_data);
1127 #endif
1128
1129 }
1130
1131 // seg009:35C5
get_char_width(byte character)1132 int __pascal far get_char_width(byte character) {
1133 font_type* font = textstate.ptr_font;
1134 int width = 0;
1135 if (character <= font->last_char && character >= font->first_char) {
1136 image_type* image = font->chtab->images[character - font->first_char];
1137 if (image != NULL) {
1138 width += image->w; //char_ptrs[character - font->first_char]->width;
1139 if (width) width += font->space_between_chars;
1140 }
1141 }
1142 return width;
1143 }
1144
1145 // seg009:3E99
find_linebreak(const char far * text,int length,int break_width,int x_align)1146 int __pascal far find_linebreak(const char far *text,int length,int break_width,int x_align) {
1147 short curr_line_width; // in pixels
1148 short last_break_pos; // in characters
1149 int curr_char_pos = 0;
1150 last_break_pos = 0;
1151 curr_line_width = 0;
1152 const char* text_pos = text;
1153 while (curr_char_pos < length) {
1154 curr_line_width += get_char_width(*text_pos);
1155 if (curr_line_width <= break_width) {
1156 ++curr_char_pos;
1157 char curr_char = *(text_pos++);
1158 if (curr_char == '\n') {
1159 return curr_char_pos;
1160 }
1161 if (curr_char == '-' ||
1162 (x_align <= 0 && (curr_char == ' ' || *text_pos == ' ')) ||
1163 (*text_pos == ' ' && curr_char == ' ')
1164 ) {
1165 // May break here.
1166 last_break_pos = curr_char_pos;
1167 }
1168 } else {
1169 if (last_break_pos == 0) {
1170 // If the first word is wider than break_width then break it.
1171 return curr_char_pos;
1172 } else {
1173 // Otherwise break at the last space.
1174 return last_break_pos;
1175 }
1176 }
1177 }
1178 return curr_char_pos;
1179 }
1180
1181 // seg009:403F
get_line_width(const char far * text,int length)1182 int __pascal far get_line_width(const char far *text,int length) {
1183 int width = 0;
1184 const char* text_pos = text;
1185 while (--length >= 0) {
1186 width += get_char_width(*(text_pos++));
1187 }
1188 return width;
1189 }
1190
1191 // seg009:3706
draw_text_character(byte character)1192 int __pascal far draw_text_character(byte character) {
1193 //printf("going to do draw_text_character...\n");
1194 font_type* font = textstate.ptr_font;
1195 int width = 0;
1196 if (character <= font->last_char && character >= font->first_char) {
1197 image_type* image = font->chtab->images[character - font->first_char]; //char_ptrs[character - font->first_char];
1198 if (image != NULL) {
1199 method_3_blit_mono(image, textstate.current_x, textstate.current_y - font->height_above_baseline, textstate.textblit, textstate.textcolor);
1200 width = font->space_between_chars + image->w;
1201 }
1202 }
1203 textstate.current_x += width;
1204 return width;
1205 }
1206
1207 // seg009:377F
draw_text_line(const char far * text,int length)1208 int __pascal far draw_text_line(const char far *text,int length) {
1209 //hide_cursor();
1210 int width = 0;
1211 const char* text_pos = text;
1212 while (--length >= 0) {
1213 width += draw_text_character(*(text_pos++));
1214 }
1215 //show_cursor();
1216 return width;
1217 }
1218
1219 // seg009:3755
draw_cstring(const char far * string)1220 int __pascal far draw_cstring(const char far *string) {
1221 //hide_cursor();
1222 int width = 0;
1223 const char* text_pos = string;
1224 while (*text_pos) {
1225 width += draw_text_character(*(text_pos++));
1226 }
1227 //show_cursor();
1228 return width;
1229 }
1230
1231 // seg009:3F01
draw_text(const rect_type far * rect_ptr,int x_align,int y_align,const char far * text,int length)1232 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) {
1233 //printf("going to do draw_text()...\n");
1234 short rect_top;
1235 short rect_height;
1236 short rect_width;
1237 //textinfo_type var_C;
1238 short num_lines;
1239 short font_line_distance;
1240 //hide_cursor();
1241 //get_textinfo(&var_C);
1242 set_clip_rect(rect_ptr);
1243 rect_width = rect_ptr->right - rect_ptr->left;
1244 rect_top = rect_ptr->top;
1245 rect_height = rect_ptr->bottom - rect_ptr->top;
1246 num_lines = 0;
1247 int rem_length = length;
1248 const char* line_start = text;
1249 #define MAX_LINES 100
1250 const char* line_starts[MAX_LINES];
1251 int line_lengths[MAX_LINES];
1252 do {
1253 int line_length = find_linebreak(line_start, rem_length, rect_width, x_align);
1254 if (line_length == 0) break;
1255 if (num_lines >= MAX_LINES) {
1256 //... ERROR!
1257 printf("draw_text(): Too many lines!\n");
1258 quit(1);
1259 }
1260 line_starts[num_lines] = line_start;
1261 line_lengths[num_lines] = line_length;
1262 ++num_lines;
1263 line_start += line_length;
1264 rem_length -= line_length;
1265 } while(rem_length);
1266 font_type* font = textstate.ptr_font;
1267 font_line_distance = font->height_above_baseline + font->height_below_baseline + font->space_between_lines;
1268 int text_height = font_line_distance * num_lines - font->space_between_lines;
1269 int text_top = rect_top;
1270 if (y_align >= 0) {
1271 if (y_align <= 0) {
1272 // middle
1273 // The +1 is for simulating SHR + ADC/SBB.
1274 text_top += (rect_height+1)/2 - (text_height+1)/2;
1275 } else {
1276 // bottom
1277 text_top += rect_height - text_height;
1278 }
1279 }
1280 textstate.current_y = text_top + font->height_above_baseline;
1281 int i;
1282 for (i = 0; i < num_lines; ++i) {
1283 const char* line_pos = line_starts[i];
1284 int line_length = line_lengths[i];
1285 if (x_align < 0 &&
1286 *line_pos == ' ' &&
1287 i != 0 &&
1288 *(line_pos-1) != '\n'
1289 ) {
1290 // Skip over space if it's not at the beginning of a line.
1291 ++line_pos;
1292 --line_length;
1293 if (line_length != 0 &&
1294 *line_pos == ' ' &&
1295 *(line_pos-2) == '.'
1296 ) {
1297 // Skip over second space after point.
1298 ++line_pos;
1299 --line_length;
1300 }
1301 }
1302 int line_width = get_line_width(line_pos,line_length);
1303 int text_left = rect_ptr->left;
1304 if (x_align >= 0) {
1305 if (x_align <= 0) {
1306 // center
1307 text_left += rect_width/2 - line_width/2;
1308 } else {
1309 // right
1310 text_left += rect_width - line_width;
1311 }
1312 }
1313 textstate.current_x = text_left;
1314 //printf("going to draw text line...\n");
1315 draw_text_line(line_pos,line_length);
1316 textstate.current_y += font_line_distance;
1317 }
1318 reset_clip_rect();
1319 //set_textinfo(...);
1320 //show_cursor();
1321 return rect_ptr;
1322 }
1323
1324 // seg009:3E4F
show_text(const rect_type far * rect_ptr,int x_align,int y_align,const char far * text)1325 void __pascal far show_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text) {
1326 // stub
1327 //printf("show_text: %s\n",text);
1328 draw_text(rect_ptr, x_align, y_align, text, strlen(text));
1329 }
1330
1331 // seg009:04FF
show_text_with_color(const rect_type far * rect_ptr,int x_align,int y_align,const char far * text,int color)1332 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) {
1333 short saved_textcolor;
1334 saved_textcolor = textstate.textcolor;
1335 textstate.textcolor = color;
1336 show_text(rect_ptr, x_align, y_align, text);
1337 textstate.textcolor = saved_textcolor;
1338 }
1339
1340 // seg009:3A91
set_curr_pos(int xpos,int ypos)1341 void __pascal far set_curr_pos(int xpos,int ypos) {
1342 textstate.current_x = xpos;
1343 textstate.current_y = ypos;
1344 }
1345
1346 // seg009:145A
init_copyprot_dialog()1347 void __pascal far init_copyprot_dialog() {
1348 copyprot_dialog = make_dialog_info(&dialog_settings, &dialog_rect_1, &dialog_rect_1, NULL);
1349 copyprot_dialog->peel = read_peel_from_screen(©prot_dialog->peel_rect);
1350 }
1351
1352 // seg009:0838
showmessage(char far * text,int arg_4,void far * arg_0)1353 int __pascal far showmessage(char far *text,int arg_4,void far *arg_0) {
1354 word key;
1355 rect_type rect;
1356 //font_type* saved_font_ptr;
1357 //surface_type* old_target;
1358 //old_target = current_target_surface;
1359 //current_target_surface = onscreen_surface_;
1360 // In the disassembly there is some messing with the current_target_surface and font (?)
1361 // However, this does not seem to be strictly necessary
1362 if (NULL == offscreen_surface) offscreen_surface = make_offscreen_buffer(&screen_rect); // In case we get an error before there is an offsceen buffer
1363 method_1_blit_rect(offscreen_surface, onscreen_surface_, ©prot_dialog->peel_rect, ©prot_dialog->peel_rect, 0);
1364 draw_dialog_frame(copyprot_dialog);
1365 //saved_font_ptr = textstate.ptr_font;
1366 //saved_font_ptr = current_target_surface->ptr_font;
1367 //current_target_surface->ptr_font = ptr_font;
1368 shrink2_rect(&rect, ©prot_dialog->text_rect, 2, 1);
1369 show_text_with_color(&rect, 0, 0, text, color_15_brightwhite);
1370 //textstate.ptr_font = saved_font_ptr;
1371 //current_target_surface->ptr_font = saved_font_ptr;
1372 clear_kbd_buf();
1373 do {
1374 idle();
1375 key = key_test_quit(); // Press any key to continue...
1376 } while(key == 0);
1377 //restore_dialog_peel_2(copyprot_dialog->peel);
1378 //current_target_surface = old_target;
1379 need_full_redraw = 1; // lazy: instead of neatly restoring only the relevant part, just redraw the whole screen
1380 return key;
1381 }
1382
1383 // seg009:08FB
make_dialog_info(dialog_settings_type * settings,rect_type * dialog_rect,rect_type * text_rect,peel_type * dialog_peel)1384 dialog_type * __pascal far make_dialog_info(dialog_settings_type *settings, rect_type *dialog_rect,
1385 rect_type *text_rect, peel_type *dialog_peel) {
1386 dialog_type* dialog_info;
1387 dialog_info = malloc_near(sizeof(dialog_type));
1388 dialog_info->settings = settings;
1389 dialog_info->has_peel = 0;
1390 dialog_info->peel = dialog_peel;
1391 if (text_rect != NULL)
1392 dialog_info->text_rect = *text_rect;
1393 calc_dialog_peel_rect(dialog_info);
1394 if (text_rect != NULL) { // does not seem to be quite right; see seg009:0948 (?)
1395 read_dialog_peel(dialog_info);
1396 }
1397 return dialog_info;
1398 }
1399
1400 // seg009:0BE7
calc_dialog_peel_rect(dialog_type * dialog)1401 void __pascal far calc_dialog_peel_rect(dialog_type*dialog) {
1402 dialog_settings_type* settings;
1403 settings = dialog->settings;
1404 dialog->peel_rect.left = dialog->text_rect.left - settings->left_border;
1405 dialog->peel_rect.top = dialog->text_rect.top - settings->top_border;
1406 dialog->peel_rect.right = dialog->text_rect.right + settings->right_border + settings->shadow_right;
1407 dialog->peel_rect.bottom = dialog->text_rect.bottom + settings->bottom_border + settings->shadow_bottom;
1408 }
1409
1410 // seg009:0BB0
read_dialog_peel(dialog_type * dialog)1411 void __pascal far read_dialog_peel(dialog_type *dialog) {
1412 if (dialog->has_peel) {
1413 if (dialog->peel == NULL) {
1414 dialog->peel = read_peel_from_screen(&dialog->peel_rect);
1415 }
1416 dialog->has_peel = 1;
1417 draw_dialog_frame(dialog);
1418 }
1419 }
1420
1421 // seg009:09DE
draw_dialog_frame(dialog_type * dialog)1422 void __pascal far draw_dialog_frame(dialog_type *dialog) {
1423 dialog->settings->method_2_frame(dialog);
1424 }
1425
1426 // A pointer to this function is the first field of dialog_settings (data:2944)
1427 // Perhaps used when replacing a dialog's text with another text (?)
1428 // seg009:096F
add_dialog_rect(dialog_type * dialog)1429 void __pascal far add_dialog_rect(dialog_type *dialog) {
1430 draw_rect(&dialog->text_rect, color_0_black);
1431 }
1432
1433 // seg009:09F0
dialog_method_2_frame(dialog_type * dialog)1434 void __pascal far dialog_method_2_frame(dialog_type *dialog) {
1435 rect_type rect;
1436 short shadow_right = dialog->settings->shadow_right;
1437 short shadow_bottom = dialog->settings->shadow_bottom;
1438 short bottom_border = dialog->settings->bottom_border;
1439 short outer_border = dialog->settings->outer_border;
1440 short peel_top = dialog->peel_rect.top;
1441 short peel_left = dialog->peel_rect.left;
1442 short peel_bottom = dialog->peel_rect.bottom;
1443 short peel_right = dialog->peel_rect.right;
1444 short text_top = dialog->text_rect.top;
1445 short text_left = dialog->text_rect.left;
1446 short text_bottom = dialog->text_rect.bottom;
1447 short text_right = dialog->text_rect.right;
1448 // Draw outer border
1449 rect = (rect_type) { peel_top, peel_left, peel_bottom - shadow_bottom, peel_right - shadow_right };
1450 draw_rect(&rect, color_0_black);
1451 // Draw shadow (right)
1452 rect = (rect_type) { text_top, peel_right - shadow_right, peel_bottom, peel_right };
1453 draw_rect(&rect, get_text_color(0, color_8_darkgray /*dialog's shadow*/, 0));
1454 // Draw shadow (bottom)
1455 rect = (rect_type) { peel_bottom - shadow_bottom, text_left, peel_bottom, peel_right };
1456 draw_rect(&rect, get_text_color(0, color_8_darkgray /*dialog's shadow*/, 0));
1457 // Draw inner border (left)
1458 rect = (rect_type) { peel_top + outer_border, peel_left + outer_border, text_bottom, text_left };
1459 draw_rect(&rect, color_15_brightwhite);
1460 // Draw inner border (top)
1461 rect = (rect_type) { peel_top + outer_border, text_left, text_top, text_right + dialog->settings->right_border - outer_border };
1462 draw_rect(&rect, color_15_brightwhite);
1463 // Draw inner border (right)
1464 rect.top = text_top;
1465 rect.left = text_right;
1466 rect.bottom = text_bottom + bottom_border - outer_border; // (rect.right stays the same)
1467 draw_rect(&rect, color_15_brightwhite);
1468 // Draw inner border (bottom)
1469 rect = (rect_type) { text_bottom, peel_left + outer_border, text_bottom + bottom_border - outer_border, text_right };
1470 draw_rect(&rect, color_15_brightwhite);
1471 }
1472
1473 // seg009:0C44
show_dialog(const char * text)1474 void __pascal far show_dialog(const char *text) {
1475 char string[256];
1476 snprintf(string, sizeof(string), "%s\n\nPress any key to continue.", text);
1477 showmessage(string, 1, &key_test_quit);
1478 }
1479
1480 // seg009:0791
get_text_center_y(const rect_type far * rect)1481 int __pascal far get_text_center_y(const rect_type far *rect) {
1482 const font_type far* font;
1483 short empty_height; // height of empty space above+below the line of text
1484 font = &hc_font;//current_target_surface->ptr_font;
1485 empty_height = rect->bottom - font->height_above_baseline - font->height_below_baseline - rect->top;
1486 return ((empty_height - empty_height % 2) >> 1) + font->height_above_baseline + empty_height % 2 + rect->top;
1487 }
1488
1489 // seg009:3E77
get_cstring_width(const char far * text)1490 int __pascal far get_cstring_width(const char far *text) {
1491 int width = 0;
1492 const char* text_pos = text;
1493 char curr_char;
1494 while (0 != (curr_char = *(text_pos++))) {
1495 width += get_char_width(curr_char);
1496 }
1497 return width;
1498 }
1499
1500 // seg009:0767
draw_text_cursor(int xpos,int ypos,int color)1501 void __pascal far draw_text_cursor(int xpos,int ypos,int color) {
1502 set_curr_pos(xpos, ypos);
1503 /*current_target_surface->*/textstate.textcolor = color;
1504 draw_text_character('_');
1505 //restore_curr_color();
1506 textstate.textcolor = 15;
1507 }
1508
1509 // seg009:053C
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)1510 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) {
1511 short length;
1512 word key;
1513 short cursor_visible;
1514 short current_xpos;
1515 short ypos;
1516 short init_length;
1517 length = 0;
1518 cursor_visible = 0;
1519 draw_rect(rect, bgcolor);
1520 init_length = strlen(initial);
1521 if (has_initial) {
1522 strcpy(buffer, initial);
1523 length = init_length;
1524 }
1525 current_xpos = rect->left + arg_4;
1526 ypos = get_text_center_y(rect);
1527 set_curr_pos(current_xpos, ypos);
1528 /*current_target_surface->*/textstate.textcolor = color;
1529 draw_cstring(initial);
1530 //restore_curr_pos?();
1531 current_xpos += get_cstring_width(initial) + (init_length != 0) * arg_4;
1532 do {
1533 key = 0;
1534 do {
1535 if (cursor_visible) {
1536 draw_text_cursor(current_xpos, ypos, color);
1537 } else {
1538 draw_text_cursor(current_xpos, ypos, bgcolor);
1539 }
1540 cursor_visible = !cursor_visible;
1541 start_timer(timer_0, 6);
1542 if (key) {
1543 if (cursor_visible) {
1544 draw_text_cursor(current_xpos, ypos, color);
1545 cursor_visible = !cursor_visible;
1546 }
1547 if (key == SDL_SCANCODE_RETURN) { // Enter
1548 buffer[length] = 0;
1549 return length;
1550 } else break;
1551 }
1552 while (!has_timer_stopped(timer_0) && (key = key_test_quit()) == 0) idle();
1553 } while (1);
1554 // Only use the printable ASCII chars (UTF-8 encoding)
1555 char entered_char = last_text_input <= 0x7E ? last_text_input : 0;
1556 clear_kbd_buf();
1557
1558 if (key == SDL_SCANCODE_ESCAPE) { // Esc
1559 draw_rect(rect, bgcolor);
1560 buffer[0] = 0;
1561 return -1;
1562 }
1563 if (length != 0 && (key == SDL_SCANCODE_BACKSPACE ||
1564 key == SDL_SCANCODE_DELETE)) { // Backspace, Delete
1565 --length;
1566 draw_text_cursor(current_xpos, ypos, bgcolor);
1567 current_xpos -= get_char_width(buffer[length]);
1568 set_curr_pos(current_xpos, ypos);
1569 /*current_target_surface->*/textstate.textcolor = bgcolor;
1570 draw_text_character(buffer[length]);
1571 //restore_curr_pos?();
1572 draw_text_cursor(current_xpos, ypos, color);
1573 }
1574 else if (entered_char >= 0x20 && entered_char <= 0x7E && length < max_length) {
1575 // Would the new character make the cursor go past the right side of the rect?
1576 if (get_char_width('_') + get_char_width(entered_char) + current_xpos < rect->right) {
1577 draw_text_cursor(current_xpos, ypos, bgcolor);
1578 set_curr_pos(current_xpos, ypos);
1579 /*current_target_surface->*/textstate.textcolor = color;
1580 current_xpos += draw_text_character(buffer[length++] = entered_char);
1581 }
1582 }
1583 } while(1);
1584 }
1585
1586 #else // USE_TEXT
1587
1588 // seg009:3706
draw_text_character(byte character)1589 int __pascal far draw_text_character(byte character) {
1590 // stub
1591 printf("draw_text_character: %c\n",character);
1592 return 0;
1593 }
1594
1595 // seg009:3E4F
show_text(const rect_type far * rect_ptr,int x_align,int y_align,const char far * text)1596 void __pascal far show_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text) {
1597 // stub
1598 printf("show_text: %s\n",text);
1599 }
1600
1601 // seg009:04FF
show_text_with_color(const rect_type far * rect_ptr,int x_align,int y_align,const char far * text,int color)1602 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) {
1603 //short saved_textcolor;
1604 //saved_textcolor = textstate.textcolor;
1605 //textstate.textcolor = color;
1606 show_text(rect_ptr, x_align, y_align, text);
1607 //textstate.textcolor = saved_textcolor;
1608 }
1609
1610 // seg009:3A91
set_curr_pos(int xpos,int ypos)1611 void __pascal far set_curr_pos(int xpos,int ypos) {
1612 // stub
1613 }
1614
1615 // seg009:0C44
show_dialog(const char * text)1616 void __pascal far show_dialog(const char *text) {
1617 // stub
1618 puts(text);
1619 }
1620
1621 // seg009:053C
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)1622 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) {
1623 // stub
1624 strncpy(buffer, "dummy input text", max_length);
1625 return strlen(buffer);
1626 }
1627
showmessage(char far * text,int arg_4,void far * arg_0)1628 int __pascal far showmessage(char far *text,int arg_4,void far *arg_0) {
1629 // stub
1630 puts(text);
1631 return 0;
1632 }
1633
init_copyprot_dialog()1634 void __pascal far init_copyprot_dialog() {
1635 // stub
1636 }
1637
draw_dialog_frame(dialog_type * dialog)1638 void __pascal far draw_dialog_frame(dialog_type *dialog) {
1639 // stub
1640 }
1641
add_dialog_rect(dialog_type * dialog)1642 void __pascal far add_dialog_rect(dialog_type *dialog) {
1643 // stub
1644 }
1645
dialog_method_2_frame(dialog_type * dialog)1646 void __pascal far dialog_method_2_frame(dialog_type *dialog) {
1647 // stub
1648 }
1649
1650 #endif // USE_TEXT
1651
1652 // seg009:37E8
draw_rect(const rect_type far * rect,int color)1653 void __pascal far draw_rect(const rect_type far *rect,int color) {
1654 method_5_rect(rect, blitters_0_no_transp, color);
1655 }
1656
1657 // seg009:3985
rect_sthg(surface_type * surface,const rect_type far * rect)1658 surface_type far *__pascal rect_sthg(surface_type *surface,const rect_type far *rect) {
1659 // stub
1660 return surface;
1661 }
1662
1663 // seg009:39CE
shrink2_rect(rect_type far * target_rect,const rect_type far * source_rect,int delta_x,int delta_y)1664 rect_type far *__pascal shrink2_rect(rect_type far *target_rect,const rect_type far *source_rect,int delta_x,int delta_y) {
1665 target_rect->top = source_rect->top + delta_y;
1666 target_rect->left = source_rect->left + delta_x;
1667 target_rect->bottom = source_rect->bottom - delta_y;
1668 target_rect->right = source_rect->right - delta_x;
1669 return target_rect;
1670 }
1671
1672 // seg009:3BBA
restore_peel(peel_type * peel_ptr)1673 void __pascal far restore_peel(peel_type* peel_ptr) {
1674 //printf("restoring peel at (x=%d, y=%d)\n", peel_ptr.rect.left, peel_ptr.rect.top); // debug
1675 method_6_blit_img_to_scr(peel_ptr->peel, peel_ptr->rect.left, peel_ptr->rect.top, /*0x10*/0);
1676 free_peel(peel_ptr);
1677 //SDL_FreeSurface(peel_ptr.peel);
1678 }
1679
1680 // seg009:3BE9
read_peel_from_screen(const rect_type far * rect)1681 peel_type* __pascal far read_peel_from_screen(const rect_type far *rect) {
1682 // stub
1683 peel_type* result;
1684 result = calloc(1, sizeof(peel_type));
1685 //memset(&result, 0, sizeof(result));
1686 result->rect = *rect;
1687 #ifndef USE_ALPHA
1688 SDL_Surface* peel_surface = SDL_CreateRGBSurface(0, rect->right - rect->left, rect->bottom - rect->top,
1689 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
1690 #else
1691 SDL_Surface* peel_surface = SDL_CreateRGBSurface(0, rect->right - rect->left, rect->bottom - rect->top, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24);
1692 #endif
1693 if (peel_surface == NULL) {
1694 sdlperror("read_peel_from_screen: SDL_CreateRGBSurface");
1695 quit(1);
1696 }
1697 result->peel = peel_surface;
1698 rect_type target_rect = {0, 0, rect->right - rect->left, rect->bottom - rect->top};
1699 method_1_blit_rect(result->peel, current_target_surface, &target_rect, rect, 0);
1700 return result;
1701 }
1702
1703 // seg009:3D95
intersect_rect(rect_type far * output,const rect_type far * input1,const rect_type far * input2)1704 int __pascal far intersect_rect(rect_type far *output,const rect_type far *input1,const rect_type far *input2) {
1705 short left = MAX(input1->left, input2->left);
1706 short right = MIN(input1->right, input2->right);
1707 if (left < right) {
1708 output->left = left;
1709 output->right = right;
1710 short top = MAX(input1->top, input2->top);
1711 short bottom = MIN(input1->bottom, input2->bottom);
1712 if (top < bottom) {
1713 output->top = top;
1714 output->bottom = bottom;
1715 return 1;
1716 }
1717 }
1718 memset(output, 0, sizeof(rect_type));
1719 return 0;
1720 }
1721
1722 // seg009:4063
union_rect(rect_type far * output,const rect_type far * input1,const rect_type far * input2)1723 rect_type far * __pascal far union_rect(rect_type far *output,const rect_type far *input1,const rect_type far *input2) {
1724 short top = MIN(input1->top, input2->top);
1725 short left = MIN(input1->left, input2->left);
1726 short bottom = MAX(input1->bottom, input2->bottom);
1727 short right = MAX(input1->right, input2->right);
1728 output->top = top;
1729 output->left = left;
1730 output->bottom = bottom;
1731 output->right = right;
1732 return output;
1733 }
1734
1735 enum userevents {
1736 userevent_SOUND,
1737 userevent_TIMER,
1738 };
1739
1740 short speaker_playing = 0;
1741 short digi_playing = 0;
1742 short midi_playing = 0;
1743 short ogg_playing = 0;
1744
1745 // The currently playing sound buffer for the PC speaker.
1746 speaker_type* current_speaker_sound;
1747 // Index for which note is currently playing.
1748 int speaker_note_index;
1749 // Tracks how long the last (partially played) speaker note has been playing (for the audio callback).
1750 int current_speaker_note_samples_already_emitted;
1751
speaker_sound_stop()1752 void __pascal far speaker_sound_stop() {
1753 if (!speaker_playing) return;
1754 SDL_LockAudio();
1755 speaker_playing = 0;
1756 current_speaker_sound = NULL;
1757 speaker_note_index = 0;
1758 current_speaker_note_samples_already_emitted = 0;
1759 SDL_UnlockAudio();
1760 }
1761
1762 // The current buffer, holds the resampled sound data.
1763 byte* digi_buffer = NULL;
1764 // The current position in digi_buffer.
1765 byte* digi_remaining_pos = NULL;
1766 // The remaining length.
1767 int digi_remaining_length = 0;
1768
1769 // The properties of the audio device.
1770 SDL_AudioSpec* digi_audiospec = NULL;
1771 // The desired samplerate. Everything will be resampled to this.
1772 const int digi_samplerate = 44100;
1773
stop_digi()1774 void stop_digi() {
1775 // SDL_PauseAudio(1);
1776 if (!digi_playing) return;
1777 SDL_LockAudio();
1778 digi_playing = 0;
1779 /*
1780 // if (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) {
1781 SDL_PauseAudio(1);
1782 SDL_CloseAudio();
1783 // }
1784 if (digi_audiospec != NULL) {
1785 free(digi_audiospec);
1786 digi_audiospec = NULL;
1787 }
1788 */
1789 digi_buffer = NULL;
1790 digi_remaining_length = 0;
1791 digi_remaining_pos = NULL;
1792 SDL_UnlockAudio();
1793 }
1794
1795 // Decoder for the currently playing OGG sound. (This also holds the playback position.)
1796 stb_vorbis* ogg_decoder;
1797
stop_ogg()1798 void stop_ogg() {
1799 SDL_PauseAudio(1);
1800 if (!ogg_playing) return;
1801 ogg_playing = 0;
1802 SDL_LockAudio();
1803 ogg_decoder = NULL;
1804 SDL_UnlockAudio();
1805 }
1806
1807 // seg009:7214
stop_sounds()1808 void __pascal far stop_sounds() {
1809 // stub
1810 stop_digi();
1811 stop_midi();
1812 speaker_sound_stop();
1813 stop_ogg();
1814 }
1815
1816 short square_wave_state = 4000; // If the amplitude is too high, the speaker sounds will be really loud!
1817 float square_wave_samples_since_last_flip;
1818
generate_square_wave(byte * stream,float note_freq,int samples)1819 void generate_square_wave(byte* stream, float note_freq, int samples) {
1820 int channels = digi_audiospec->channels;
1821 float half_period_in_samples = (digi_audiospec->freq / note_freq) * 0.5f;
1822
1823 int samples_left = samples;
1824 while (samples_left > 0) {
1825 if (square_wave_samples_since_last_flip > half_period_in_samples) {
1826 // Produce a square wave by flipping the signal.
1827 square_wave_state = ~square_wave_state;
1828 // Note(Falcury): not completely sure that this is the right way to prevent glitches in the sound...
1829 // Because I can still hear some hiccups, e.g. in the music. Especially when switching between notes.
1830 square_wave_samples_since_last_flip -= half_period_in_samples;
1831 } else {
1832 int samples_until_next_flip = (int)(half_period_in_samples - square_wave_samples_since_last_flip);
1833 ++samples_until_next_flip; // round up.
1834
1835 int samples_to_emit = MIN(samples_until_next_flip, samples_left);
1836 for (int i = 0; i < samples_to_emit * channels; ++i) {
1837 *(short*)stream = square_wave_state;
1838 stream += sizeof(short);
1839 }
1840 samples_left -= samples_to_emit;
1841 square_wave_samples_since_last_flip += samples_to_emit;
1842 }
1843 }
1844 }
1845
speaker_callback(void * userdata,Uint8 * stream,int len)1846 void speaker_callback(void *userdata, Uint8 *stream, int len) {
1847 int output_channels = digi_audiospec->channels;
1848 int bytes_per_sample = sizeof(short) * output_channels;
1849 int samples_requested = len / bytes_per_sample;
1850
1851 if (current_speaker_sound == NULL) return;
1852 word tempo = current_speaker_sound->tempo;
1853
1854 int total_samples_left = samples_requested;
1855 while (total_samples_left > 0) {
1856 note_type* note = current_speaker_sound->notes + speaker_note_index;
1857 if (note->frequency == 0x12 /*end*/) {
1858 speaker_playing = 0;
1859 current_speaker_sound = NULL;
1860 speaker_note_index = 0;
1861 SDL_Event event;
1862 memset(&event, 0, sizeof(event));
1863 event.type = SDL_USEREVENT;
1864 event.user.code = userevent_SOUND;
1865 SDL_PushEvent(&event);
1866 return;
1867 }
1868
1869 int note_length_in_samples = (note->length * digi_audiospec->freq) / tempo;
1870 int note_samples_to_emit = MIN(note_length_in_samples - current_speaker_note_samples_already_emitted, total_samples_left);
1871 total_samples_left -= note_samples_to_emit;
1872 size_t copy_len = (size_t)note_samples_to_emit * bytes_per_sample;
1873 if (note->frequency <= 0x01 /*rest*/) {
1874 memset(stream, digi_audiospec->silence, copy_len);
1875 } else {
1876 generate_square_wave(stream, (float)note->frequency, note_samples_to_emit);
1877 }
1878 stream += copy_len;
1879
1880 int note_samples_emitted = current_speaker_note_samples_already_emitted + note_samples_to_emit;
1881 if (note_samples_emitted < note_length_in_samples) {
1882 current_speaker_note_samples_already_emitted += note_samples_to_emit;
1883 } else {
1884 ++speaker_note_index;
1885 current_speaker_note_samples_already_emitted = 0;
1886 }
1887 }
1888 }
1889
1890 // seg009:7640
play_speaker_sound(sound_buffer_type far * buffer)1891 void __pascal far play_speaker_sound(sound_buffer_type far *buffer) {
1892 speaker_sound_stop();
1893 stop_sounds();
1894 current_speaker_sound = &buffer->speaker;
1895 speaker_note_index = 0;
1896 speaker_playing = 1;
1897 SDL_PauseAudio(0);
1898 }
1899
digi_callback(void * userdata,Uint8 * stream,int len)1900 void digi_callback(void *userdata, Uint8 *stream, int len) {
1901 // Don't go over the end of either the input or the output buffer.
1902 size_t copy_len = MIN(len, digi_remaining_length);
1903 //printf("digi_callback(): copy_len = %d\n", copy_len);
1904 //printf("digi_callback(): len = %d\n", len);
1905 if (is_sound_on) {
1906 // Copy the next part of the input of the output.
1907 memcpy(stream, digi_remaining_pos, copy_len);
1908 // In case the sound does not fill the buffer: fill the rest of the buffer with silence.
1909 memset(stream + copy_len, digi_audiospec->silence, len - copy_len);
1910 } else {
1911 // If sound is off: Mute the sound but keep track of where we are.
1912 memset(stream, digi_audiospec->silence, len);
1913 }
1914 // If the sound ended, push an event.
1915 if (digi_playing && digi_remaining_length == 0) {
1916 //printf("digi_callback(): sound ended\n");
1917 SDL_Event event;
1918 memset(&event, 0, sizeof(event));
1919 event.type = SDL_USEREVENT;
1920 event.user.code = userevent_SOUND;
1921 digi_playing = 0;
1922 SDL_PushEvent(&event);
1923 }
1924 // Advance the pointer.
1925 digi_remaining_length -= copy_len;
1926 digi_remaining_pos += copy_len;
1927 }
1928
ogg_callback(void * userdata,Uint8 * stream,int len)1929 void ogg_callback(void *userdata, Uint8 *stream, int len) {
1930 int output_channels = digi_audiospec->channels;
1931 int bytes_per_sample = sizeof(short) * output_channels;
1932 int samples_requested = len / bytes_per_sample;
1933
1934 int samples_filled;
1935 if (is_sound_on) {
1936 samples_filled = stb_vorbis_get_samples_short_interleaved(ogg_decoder, output_channels,
1937 (short*) stream, len / sizeof(short));
1938 if (samples_filled < samples_requested) {
1939 // In case the sound does not fill the buffer: fill the rest of the buffer with silence.
1940 int bytes_filled = samples_filled * bytes_per_sample;
1941 int remaining_bytes = (samples_requested - samples_filled) * bytes_per_sample;
1942 memset(stream + bytes_filled, digi_audiospec->silence, remaining_bytes);
1943 }
1944 } else {
1945 // If sound is off: Mute the sound, but keep track of where we are.
1946 memset(stream, digi_audiospec->silence, len);
1947 // Let the decoder run normally (to advance the position), but discard the result.
1948 byte* discarded_samples = alloca(len);
1949 samples_filled = stb_vorbis_get_samples_short_interleaved(ogg_decoder, output_channels,
1950 (short*) discarded_samples, len / sizeof(short));
1951 }
1952 // Push an event if the sound has ended.
1953 if (samples_filled == 0) {
1954 //printf("ogg_callback(): sound ended\n");
1955 SDL_Event event;
1956 memset(&event, 0, sizeof(event));
1957 event.type = SDL_USEREVENT;
1958 event.user.code = userevent_SOUND;
1959 ogg_playing = 0;
1960 SDL_PushEvent(&event);
1961 }
1962 }
1963
1964 #ifdef USE_FAST_FORWARD
1965 int audio_speed = 1; // =1 normally, >1 during fast forwarding
1966 #endif
1967
audio_callback(void * userdata,Uint8 * stream_orig,int len_orig)1968 void audio_callback(void* userdata, Uint8* stream_orig, int len_orig) {
1969
1970 Uint8* stream;
1971 int len;
1972 #ifdef USE_FAST_FORWARD
1973 if (audio_speed > 1) {
1974 len = len_orig * audio_speed;
1975 stream = malloc(len);
1976 } else
1977 #endif
1978 {
1979 len = len_orig;
1980 stream = stream_orig;
1981 }
1982
1983 memset(stream, digi_audiospec->silence, len);
1984 if (digi_playing) {
1985 digi_callback(userdata, stream, len);
1986 } else if (speaker_playing) {
1987 speaker_callback(userdata, stream, len);
1988 }
1989 // Note: music sounds and digi sounds are allowed to play simultaneously (will be blended together)
1990 // I.e., digi sounds and music will not cut each other short.
1991 if (midi_playing) {
1992 midi_callback(userdata, stream, len);
1993 } else if (ogg_playing) {
1994 ogg_callback(userdata, stream, len);
1995 }
1996
1997 #ifdef USE_FAST_FORWARD
1998 if (audio_speed > 1) {
1999
2000 #ifdef FAST_FORWARD_MUTE
2001 memset(stream_orig, digi_audiospec->silence, len_orig);
2002 #else
2003 #ifdef FAST_FORWARD_RESAMPLE_SOUND
2004 static SDL_AudioCVT cvt;
2005 static bool cvt_initialized = false;
2006 if (!cvt_initialized) {
2007 SDL_BuildAudioCVT(&cvt,
2008 digi_audiospec->format, digi_audiospec->channels, digi_audiospec->freq * audio_speed,
2009 digi_audiospec->format, digi_audiospec->channels, digi_audiospec->freq);
2010 cvt_initialized = true;
2011 }
2012 //realloc(stream, len * cvt.len_mult);
2013 //cvt.buf = stream;
2014 cvt.len = len;
2015 cvt.buf = malloc(cvt.len * cvt.len_mult);
2016 memcpy(cvt.buf, stream, cvt.len);
2017 //printf("cvt.needed = %d\n", cvt.needed);
2018 //printf("cvt.len_mult = %d\n", cvt.len_mult);
2019 //printf("cvt.len_ratio = %lf\n", cvt.len_ratio);
2020 SDL_ConvertAudio(&cvt);
2021
2022 memcpy(stream_orig, cvt.buf, len_orig);
2023 free(cvt.buf);
2024 cvt.buf = NULL;
2025 #else
2026 // Hack: use the beginning of the buffer instead of resampling.
2027 memcpy(stream_orig, stream, len_orig);
2028 #endif
2029 #endif
2030
2031 free(stream);
2032 }
2033 #endif
2034
2035 }
2036
2037 int digi_unavailable = 0;
init_digi()2038 void init_digi() {
2039 if (digi_unavailable) return;
2040 if (digi_audiospec != NULL) return;
2041 // Open the audio device. Called once.
2042 //printf("init_digi(): called\n");
2043
2044 SDL_AudioFormat desired_audioformat;
2045 SDL_version version;
2046 SDL_GetVersion(&version);
2047 //printf("SDL Version = %d.%d.%d\n", version.major, version.minor, version.patch);
2048 if (version.major <= 2 && version.minor <= 0 && version.patch <= 3) {
2049 // In versions before 2.0.4, 16-bit audio samples don't work properly (the sound becomes garbled).
2050 // See: https://bugzilla.libsdl.org/show_bug.cgi?id=2389
2051 // Workaround: set the audio format to 8-bit, if we are linking against an older SDL2 version.
2052 desired_audioformat = AUDIO_U8;
2053 printf("Your SDL.dll is older than 2.0.4. Using 8-bit audio format to work around resampling bug.");
2054 } else {
2055 desired_audioformat = AUDIO_S16SYS;
2056 }
2057
2058 SDL_AudioSpec *desired;
2059 desired = (SDL_AudioSpec *)malloc(sizeof(SDL_AudioSpec));
2060 memset(desired, 0, sizeof(SDL_AudioSpec));
2061 desired->freq = digi_samplerate; //buffer->digi.sample_rate;
2062 desired->format = desired_audioformat;
2063 desired->channels = 2;
2064 desired->samples = 1024;
2065 desired->callback = audio_callback;
2066 desired->userdata = NULL;
2067 if (SDL_OpenAudio(desired, NULL) != 0) {
2068 sdlperror("init_digi: SDL_OpenAudio");
2069 //quit(1);
2070 digi_unavailable = 1;
2071 return;
2072 }
2073 //SDL_PauseAudio(0);
2074 digi_audiospec = desired;
2075 }
2076
2077 const int sound_channel = 0;
2078 const int max_sound_id = 58;
2079
load_sound_names()2080 void load_sound_names() {
2081 const char* names_path = locate_file("/usr/local/share/sdlpop/music/names.txt");
2082 if (sound_names != NULL) return;
2083 FILE* fp = fopen(names_path,"rt");
2084 if (fp==NULL) return;
2085 sound_names = (char**) calloc(sizeof(char*) * max_sound_id, 1);
2086 while (!feof(fp)) {
2087 int index;
2088 char name[POP_MAX_PATH];
2089 if (fscanf(fp, "%d=%255s\n", &index, /*sizeof(name)-1,*/ name) != 2) {
2090 perror(names_path);
2091 continue;
2092 }
2093 //if (feof(fp)) break;
2094 //printf("sound_names[%d] = %s\n",index,name);
2095 if (index >= 0 && index < max_sound_id) {
2096 sound_names[index] = strdup(name);
2097 }
2098 }
2099 fclose(fp);
2100 }
2101
sound_name(int index)2102 char* sound_name(int index) {
2103 if (sound_names != NULL && index >= 0 && index < max_sound_id) {
2104 return sound_names[index];
2105 } else {
2106 return NULL;
2107 }
2108 }
2109
2110 sound_buffer_type* convert_digi_sound(sound_buffer_type* digi_buffer);
2111
load_sound(int index)2112 sound_buffer_type* load_sound(int index) {
2113 sound_buffer_type* result = NULL;
2114 //printf("load_sound(%d)\n", index);
2115 init_digi();
2116 if (enable_music && !digi_unavailable && result == NULL && index >= 0 && index < max_sound_id) {
2117 //printf("Trying to load from music folder\n");
2118
2119 //load_sound_names(); // Moved to load_sounds()
2120 if (sound_names != NULL && sound_name(index) != NULL) {
2121 //printf("Loading from music folder\n");
2122 do {
2123 FILE* fp = NULL;
2124 char filename[POP_MAX_PATH];
2125 if (!skip_mod_data_files) {
2126 // before checking the root directory, first try mods/MODNAME/
2127 snprintf_check(filename, sizeof(filename), "%s/music/%s.ogg", mod_data_path, sound_name(index));
2128 fp = fopen(filename, "rb");
2129 }
2130 if (fp == NULL && !skip_normal_data_files) {
2131 snprintf_check(filename, sizeof(filename), "/usr/local/share/sdlpop/music/%s.ogg", sound_name(index));
2132 fp = fopen(locate_file(filename), "rb");
2133 }
2134 if (fp == NULL) {
2135 break;
2136 }
2137 // Read the entire file (undecoded) into memory.
2138 struct stat info;
2139 if (fstat(fileno(fp), &info))
2140 break;
2141 size_t file_size = (size_t) MAX(0, info.st_size);
2142 byte* file_contents = malloc(file_size);
2143 if (fread(file_contents, 1, file_size, fp) != file_size) {
2144 free(file_contents);
2145 fclose(fp);
2146 break;
2147 }
2148 fclose(fp);
2149
2150 // Decoding the entire file immediately would make the loading time much longer.
2151 // However, we can also create the decoder now, and only use it when we are actually playing the file.
2152 // (In the audio callback, we'll decode chunks of samples to the output stream, as needed).
2153 stb_vorbis* decoder = stb_vorbis_open_memory(file_contents, file_size, NULL, NULL);
2154 if (decoder == NULL) {
2155 free(file_contents);
2156 break;
2157 }
2158 result = malloc(sizeof(sound_buffer_type));
2159 result->type = sound_ogg;
2160 result->ogg.total_length = stb_vorbis_stream_length_in_samples(decoder) * sizeof(short);
2161 result->ogg.file_contents = file_contents; // Remember in case we want to free the sound later.
2162 result->ogg.decoder = decoder;
2163 } while(0); // do once (breakable block)
2164 } else {
2165 //printf("sound_names = %p\n", sound_names);
2166 //printf("sound_names[%d] = %p\n", index, sound_name(index));
2167 }
2168 }
2169 if (result == NULL) {
2170 //printf("Trying to load from DAT\n");
2171 result = (sound_buffer_type*) load_from_opendats_alloc(index + 10000, "bin", NULL, NULL);
2172 }
2173 if (result != NULL && (result->type & 7) == sound_digi) {
2174 sound_buffer_type* converted = convert_digi_sound(result);
2175 free(result);
2176 result = converted;
2177 }
2178 if (result == NULL && !skip_normal_data_files) {
2179 fprintf(stderr, "Failed to load sound %d '%s'\n", index, sound_name(index));
2180 }
2181 return result;
2182 }
2183
play_ogg_sound(sound_buffer_type * buffer)2184 void play_ogg_sound(sound_buffer_type *buffer) {
2185 init_digi();
2186 if (digi_unavailable) return;
2187 stop_sounds();
2188
2189 // Need to rewind the music, or else the decoder might continue where it left off, the last time this sound played.
2190 stb_vorbis_seek_start(buffer->ogg.decoder);
2191
2192 SDL_LockAudio();
2193 ogg_decoder = buffer->ogg.decoder;
2194 SDL_UnlockAudio();
2195 SDL_PauseAudio(0);
2196
2197 ogg_playing = 1;
2198 }
2199
2200 int wave_version = -1;
2201
2202 typedef struct waveinfo_type {
2203 int sample_rate, sample_size, sample_count;
2204 byte* samples;
2205 } waveinfo_type;
2206
determine_wave_version(sound_buffer_type * buffer,waveinfo_type * waveinfo)2207 bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo) {
2208 int version = wave_version;
2209 if (version == -1) {
2210 // Determine the version of the wave data.
2211 version = 0;
2212 if (buffer->digi.sample_size == 8) version += 1;
2213 if (buffer->digi_new.sample_size == 8) version += 2;
2214 if (version == 1 || version == 2) wave_version = version;
2215 }
2216
2217 switch (version) {
2218 case 1: // 1.0 and 1.1
2219 waveinfo->sample_rate = buffer->digi.sample_rate;
2220 waveinfo->sample_size = buffer->digi.sample_size;
2221 waveinfo->sample_count = buffer->digi.sample_count;
2222 waveinfo->samples = buffer->digi.samples;
2223 return true;
2224 case 2: // 1.3 and 1.4 (and PoP2)
2225 waveinfo->sample_rate = buffer->digi_new.sample_rate;
2226 waveinfo->sample_size = buffer->digi_new.sample_size;
2227 waveinfo->sample_count = buffer->digi_new.sample_count;
2228 waveinfo->samples = buffer->digi_new.samples;
2229 return true;
2230 case 3: // ambiguous
2231 printf("Warning: Ambiguous wave version.\n");
2232 return false;
2233 default: // case 0, unknown
2234 printf("Warning: Can't determine wave version.\n");
2235 return false;
2236 }
2237 }
2238
convert_digi_sound(sound_buffer_type * digi_buffer)2239 sound_buffer_type* convert_digi_sound(sound_buffer_type* digi_buffer) {
2240 init_digi();
2241 if (digi_unavailable) return NULL;
2242 waveinfo_type waveinfo;
2243 if (false == determine_wave_version(digi_buffer, &waveinfo)) return NULL;
2244
2245 float freq_ratio = (float)waveinfo.sample_rate / (float)digi_audiospec->freq;
2246
2247 int source_length = waveinfo.sample_count;
2248 int expanded_frames = source_length * digi_audiospec->freq / waveinfo.sample_rate;
2249 int expanded_length = expanded_frames * 2 * sizeof(short);
2250 sound_buffer_type* converted_buffer = malloc(sizeof(sound_buffer_type) + expanded_length);
2251
2252 converted_buffer->type = sound_digi_converted;
2253 converted_buffer->converted.length = expanded_length;
2254
2255 byte* source = waveinfo.samples;
2256 short* dest = converted_buffer->converted.samples;
2257
2258 for (int i = 0; i < expanded_frames; ++i) {
2259 float src_frame_float = i * freq_ratio;
2260 int src_frame_0 = (int) src_frame_float; // truncation
2261
2262 int sample_0 = (source[src_frame_0] | (source[src_frame_0] << 8)) - 32768;
2263 short interpolated_sample;
2264 if (src_frame_0 >= waveinfo.sample_count-1) {
2265 interpolated_sample = (short)sample_0;
2266 } else {
2267 int src_frame_1 = src_frame_0 + 1;
2268 float alpha = src_frame_float - src_frame_0;
2269 int sample_1 = (source[src_frame_1] | (source[src_frame_1] << 8)) - 32768;
2270 interpolated_sample = (short)((1.0f - alpha) * sample_0 + alpha * sample_1);
2271 }
2272 for (int channel = 0; channel < digi_audiospec->channels; ++channel) {
2273 *dest++ = interpolated_sample;
2274 }
2275 }
2276
2277 return converted_buffer;
2278 }
2279
2280 // seg009:74F0
play_digi_sound(sound_buffer_type far * buffer)2281 void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
2282 //if (!is_sound_on) return;
2283 init_digi();
2284 if (digi_unavailable) return;
2285 stop_digi();
2286 // stop_sounds();
2287 //printf("play_digi_sound(): called\n");
2288 if ((buffer->type & 7) != sound_digi_converted) {
2289 printf("Tried to play unconverted digi sound.\n");
2290 return;
2291 }
2292 SDL_LockAudio();
2293 digi_buffer = (byte*) buffer->converted.samples;
2294 digi_playing = 1;
2295 digi_remaining_length = buffer->converted.length;
2296 digi_remaining_pos = digi_buffer;
2297 SDL_UnlockAudio();
2298 SDL_PauseAudio(0);
2299 }
2300
free_sound(sound_buffer_type far * buffer)2301 void free_sound(sound_buffer_type far *buffer) {
2302 if (buffer == NULL) return;
2303 if (buffer->type == sound_ogg) {
2304 stb_vorbis_close(buffer->ogg.decoder);
2305 free(buffer->ogg.file_contents);
2306 }
2307 free(buffer);
2308 }
2309
2310 // seg009:7220
play_sound_from_buffer(sound_buffer_type far * buffer)2311 void __pascal far play_sound_from_buffer(sound_buffer_type far *buffer) {
2312
2313 #ifdef USE_REPLAY
2314 if (replaying && skipping_replay) return;
2315 #endif
2316
2317 // stub
2318 if (buffer == NULL) {
2319 printf("Tried to play NULL sound.\n");
2320 //quit(1);
2321 return;
2322 }
2323 switch (buffer->type & 7) {
2324 case sound_speaker:
2325 play_speaker_sound(buffer);
2326 break;
2327 case sound_digi_converted:
2328 case sound_digi:
2329 play_digi_sound(buffer);
2330 break;
2331 case sound_midi:
2332 play_midi_sound(buffer);
2333 break;
2334 case sound_ogg:
2335 play_ogg_sound(buffer);
2336 break;
2337 default:
2338 printf("Tried to play unimplemented sound type %d.\n", buffer->type);
2339 quit(1);
2340 break;
2341 }
2342 }
2343
turn_music_on_off(byte new_state)2344 void turn_music_on_off(byte new_state) {
2345 enable_music = new_state;
2346 turn_sound_on_off(is_sound_on);
2347 }
2348
2349 // seg009:7273
turn_sound_on_off(byte new_state)2350 void __pascal far turn_sound_on_off(byte new_state) {
2351 // stub
2352 is_sound_on = new_state;
2353 //if (!is_sound_on) stop_sounds();
2354 }
2355
2356 // seg009:7299
check_sound_playing()2357 int __pascal far check_sound_playing() {
2358 return speaker_playing || digi_playing || midi_playing || ogg_playing;
2359 }
2360
apply_aspect_ratio()2361 void apply_aspect_ratio() {
2362 // Allow us to use a consistent set of screen co-ordinates, even if the screen size changes
2363 if (use_correct_aspect_ratio) {
2364 SDL_RenderSetLogicalSize(renderer_, 320 * 5, 200 * 6); // 4:3
2365 } else {
2366 SDL_RenderSetLogicalSize(renderer_, 320, 200); // 16:10
2367 }
2368 window_resized();
2369 }
2370
window_resized()2371 void window_resized() {
2372 #if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
2373 if (use_integer_scaling) {
2374 int window_width, window_height;
2375 // On high-DPI screens, this is what we need instead of SDL_GetWindowSize().
2376 //SDL_GL_GetDrawableSize(window_, &window_width, &window_height);
2377 SDL_GetRendererOutputSize(renderer_, &window_width, &window_height);
2378 int render_width, render_height;
2379 SDL_RenderGetLogicalSize(renderer_, &render_width, &render_height);
2380 // Disable integer scaling if it would result in downscaling.
2381 // Because then the only suitable integer scaling factor is zero, i.e. the picture disappears.
2382 SDL_bool makes_sense = (window_width >= render_width && window_height >= render_height);
2383 SDL_RenderSetIntegerScale(renderer_, makes_sense);
2384 }
2385 #endif
2386 }
2387
init_overlay()2388 void init_overlay() {
2389 static bool initialized = false;
2390 if (!initialized) {
2391 overlay_surface = SDL_CreateRGBSurface(0, 320, 200, 32, 0xFF, 0xFF << 8, 0xFF << 16, 0xFF << 24) ;
2392 merged_surface = SDL_CreateRGBSurface(0, 320, 200, 24, 0xFF, 0xFF << 8, 0xFF << 16, 0) ;
2393 initialized = true;
2394 }
2395 }
2396
2397 SDL_Surface* onscreen_surface_2x;
2398
init_scaling()2399 void init_scaling() {
2400 // Don't crash in validate mode.
2401 if (renderer_ == NULL) return;
2402
2403 if (texture_sharp == NULL) {
2404 texture_sharp = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 320, 200);
2405 }
2406 if (scaling_type == 1) {
2407 if (!is_renderer_targettexture_supported && onscreen_surface_2x == NULL) {
2408 onscreen_surface_2x = SDL_CreateRGBSurface(0, 320*2, 200*2, 24, 0xFF, 0xFF << 8, 0xFF << 16, 0) ;
2409 }
2410 if (texture_fuzzy == NULL) {
2411 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
2412 int access = is_renderer_targettexture_supported ? SDL_TEXTUREACCESS_TARGET : SDL_TEXTUREACCESS_STREAMING;
2413 texture_fuzzy = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGB24, access, 320*2, 200*2);
2414 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
2415 }
2416 target_texture = texture_fuzzy;
2417 } else if (scaling_type == 2) {
2418 if (texture_blurry == NULL) {
2419 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
2420 texture_blurry = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 320, 200);
2421 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
2422 }
2423 target_texture = texture_blurry;
2424 } else {
2425 target_texture = texture_sharp;
2426 }
2427 if (target_texture == NULL) {
2428 sdlperror("init_scaling: SDL_CreateTexture");
2429 quit(1);
2430 }
2431 }
2432
2433 // seg009:38ED
set_gr_mode(byte grmode)2434 void __pascal far set_gr_mode(byte grmode) {
2435 #ifdef SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING
2436 SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
2437 #endif
2438 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE |
2439 SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC ) != 0) {
2440 sdlperror("set_gr_mode: SDL_Init");
2441 quit(1);
2442 }
2443
2444 //SDL_EnableUNICODE(1); //deprecated
2445 Uint32 flags = 0;
2446 if (!start_fullscreen) start_fullscreen = check_param("full") != NULL;
2447 if (start_fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
2448 flags |= SDL_WINDOW_RESIZABLE;
2449 flags |= SDL_WINDOW_ALLOW_HIGHDPI; // for Retina displays
2450
2451 // Should use different default window dimensions when using 4:3 aspect ratio
2452 if (use_correct_aspect_ratio && pop_window_width == 640 && pop_window_height == 400) {
2453 pop_window_height = 480;
2454 }
2455
2456 #if _WIN32
2457 // Tell Windows that the application is DPI aware, to prevent unwanted bitmap stretching.
2458 // SetProcessDPIAware() is only available on Windows Vista and later, so we need to load it dynamically.
2459 BOOL WINAPI (*SetProcessDPIAware)();
2460 HMODULE user32dll = LoadLibraryA("User32.dll");
2461 if (user32dll) {
2462 SetProcessDPIAware = GetProcAddress(user32dll, "SetProcessDPIAware");
2463 if (SetProcessDPIAware) {
2464 SetProcessDPIAware();
2465 }
2466 FreeLibrary(user32dll);
2467 }
2468 #endif
2469
2470 #ifdef USE_REPLAY
2471 if (!is_validate_mode) // run without a window if validating a replay
2472 #endif
2473 window_ = SDL_CreateWindow(WINDOW_TITLE,
2474 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
2475 pop_window_width, pop_window_height, flags);
2476 // Make absolutely sure that VSync will be off, to prevent timer issues.
2477 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "0");
2478 #ifdef USE_HW_ACCELERATION
2479 const Uint32 RENDER_BACKEND = SDL_RENDERER_ACCELERATED;
2480 #else
2481 const Uint32 RENDER_BACKEND = SDL_RENDERER_SOFTWARE;
2482 #endif
2483 renderer_ = SDL_CreateRenderer(window_, -1 , RENDER_BACKEND | SDL_RENDERER_TARGETTEXTURE);
2484 SDL_RendererInfo renderer_info;
2485 if (SDL_GetRendererInfo(renderer_, &renderer_info) == 0) {
2486 if (renderer_info.flags & SDL_RENDERER_TARGETTEXTURE) {
2487 is_renderer_targettexture_supported = true;
2488 }
2489 }
2490 if (use_integer_scaling) {
2491 #if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
2492 SDL_RenderSetIntegerScale(renderer_, SDL_TRUE);
2493 #else
2494 printf("Warning: You need to compile with SDL 2.0.5 or newer for the use_integer_scaling option.\n");
2495 #endif
2496 }
2497
2498 SDL_Surface* icon = IMG_Load(locate_file("/usr/local/share/sdlpop/icon.png"));
2499 if (icon == NULL) {
2500 sdlperror("set_gr_mode: Could not load icon");
2501 } else {
2502 SDL_SetWindowIcon(window_, icon);
2503 }
2504
2505 apply_aspect_ratio();
2506 window_resized();
2507
2508 /* Migration to SDL2: everything is still blitted to onscreen_surface_, however:
2509 * SDL2 renders textures to the screen instead of surfaces; so, every screen
2510 * update causes the onscreen_surface_ to be copied into the target_texture, which is
2511 * subsequently displayed.
2512 * The function handling the screen updates is update_screen()
2513 * */
2514 onscreen_surface_ = SDL_CreateRGBSurface(0, 320, 200, 24, 0xFF, 0xFF << 8, 0xFF << 16, 0);
2515 if (onscreen_surface_ == NULL) {
2516 sdlperror("set_gr_mode: SDL_CreateRGBSurface");
2517 quit(1);
2518 }
2519 init_overlay();
2520 init_scaling();
2521 if (start_fullscreen) {
2522 SDL_ShowCursor(SDL_DISABLE);
2523 }
2524
2525
2526 //SDL_WM_SetCaption(WINDOW_TITLE, NULL);
2527 // if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) != 0) { //deprecated
2528 // sdlperror("set_gr_mode: SDL_EnableKeyRepeat");
2529 // quit(1);
2530 // }
2531 graphics_mode = gmMcgaVga;
2532 #ifdef USE_TEXT
2533 load_font();
2534 #endif
2535 }
2536
get_final_surface()2537 SDL_Surface* get_final_surface() {
2538 if (!is_overlay_displayed) {
2539 return onscreen_surface_;
2540 } else {
2541 return merged_surface;
2542 }
2543 }
2544
draw_overlay()2545 void draw_overlay() {
2546 int overlay = 0;
2547 is_overlay_displayed = false;
2548 #ifdef USE_DEBUG_CHEATS
2549 if (is_timer_displayed && start_level > 0) overlay = 1; // Timer overlay
2550 else if (fixes->fix_quicksave_during_feather &&
2551 is_feather_timer_displayed &&
2552 start_level > 0 &&
2553 is_feather_fall > 0) {
2554 overlay = 3; // Feather timer overlay
2555 }
2556 #endif
2557 #ifdef USE_MENU
2558 // Menu overlay - not drawn here directly, only copied from the overlay surface.
2559 if (is_paused && is_menu_shown) overlay = 2;
2560 #endif
2561 if (overlay != 0) {
2562 is_overlay_displayed = true;
2563 surface_type* saved_target_surface = current_target_surface;
2564 current_target_surface = overlay_surface;
2565 rect_type drawn_rect;
2566 if (overlay == 1) {
2567 #ifdef USE_DEBUG_CHEATS
2568 char timer_text[32];
2569 if (rem_min < 0) {
2570 snprintf(timer_text, sizeof(timer_text), "%02d:%02d:%02d",
2571 -(rem_min + 1), (719 - rem_tick) / 12, (719 - rem_tick) % 12);
2572 } else {
2573 snprintf(timer_text, sizeof(timer_text), "%02d:%02d:%02d",
2574 rem_min - 1, rem_tick / 12, rem_tick % 12);
2575 }
2576 int expected_numeric_chars = 6;
2577 int extra_numeric_chars = MAX(0, (int)strnlen(timer_text, sizeof(timer_text)) - 8);
2578 int line_width = 5 + (expected_numeric_chars + extra_numeric_chars) * 9;
2579
2580 rect_type timer_box_rect = {0, 0, 11, 2 + line_width};
2581 rect_type timer_text_rect = {2, 2, 10, 100};
2582 draw_rect_with_alpha(&timer_box_rect, color_0_black, 128);
2583 show_text(&timer_text_rect, -1, -1, timer_text);
2584
2585 #ifdef USE_REPLAY
2586 // During playback, display the number of ticks since start, if the timer is shown (debug cheats: T).
2587 if (replaying) {
2588 char ticks_text[12];
2589 snprintf(ticks_text, sizeof(ticks_text), "T: %d", curr_tick);
2590 rect_type ticks_box_rect = timer_box_rect;
2591 ticks_box_rect.top += 12;
2592 ticks_box_rect.bottom += 12;
2593 rect_type ticks_text_rect = timer_text_rect;
2594 ticks_text_rect.top += 12;
2595 ticks_text_rect.bottom += 12;
2596
2597 draw_rect_with_alpha(&ticks_box_rect, color_0_black, 128);
2598 show_text(&ticks_text_rect, -1, -1, ticks_text);
2599
2600 timer_box_rect.bottom += 12;
2601 }
2602 #endif
2603
2604 drawn_rect = timer_box_rect; // Only need to blit this bit to the merged_surface.
2605 #endif
2606 } else if (overlay == 3) { // Feather timer
2607 #ifdef USE_DEBUG_CHEATS
2608 char timer_text[32];
2609 int ticks_per_sec = get_ticks_per_sec(timer_1);
2610 snprintf(timer_text, sizeof(timer_text), "%02d:%02d", is_feather_fall / ticks_per_sec, is_feather_fall % ticks_per_sec);
2611 int expected_numeric_chars = 6;
2612 int extra_numeric_chars = MAX(0, (int)strnlen(timer_text, sizeof(timer_text)) - 8);
2613 int line_width = 5 + (expected_numeric_chars + extra_numeric_chars) * 9;
2614
2615 rect_type timer_box_rect = {0, 0, 11, 2 + line_width};
2616 rect_type timer_text_rect = {2, 2, 10, 100};
2617 draw_rect_with_alpha(&timer_box_rect, color_0_black, 128);
2618 show_text_with_color(&timer_text_rect, -1, -1, timer_text, color_10_brightgreen);
2619
2620 drawn_rect = timer_box_rect; // Only need to blit this bit to the merged_surface.
2621 #endif
2622 } else {
2623 drawn_rect = screen_rect; // We'll blit the whole contents of overlay_surface to the merged_surface.
2624 }
2625 SDL_Rect sdl_rect;
2626 rect_to_sdlrect(&drawn_rect, &sdl_rect);
2627 SDL_BlitSurface(onscreen_surface_, NULL, merged_surface, NULL);
2628 SDL_BlitSurface(overlay_surface, &sdl_rect, merged_surface, &sdl_rect);
2629 current_target_surface = saved_target_surface;
2630 }
2631 }
2632
update_screen()2633 void update_screen() {
2634 draw_overlay();
2635 SDL_Surface* surface = get_final_surface();
2636 init_scaling();
2637 if (scaling_type == 1) {
2638 // Make "fuzzy pixels" like DOSBox does:
2639 // First scale to double size with nearest-neighbor scaling, then scale to full screen with smooth scaling.
2640 // The result is not as blurry as if we did only a smooth scaling, but not as sharp as if we did only nearest-neighbor scaling.
2641 if (is_renderer_targettexture_supported) {
2642 SDL_UpdateTexture(texture_sharp, NULL, surface->pixels, surface->pitch);
2643 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
2644 SDL_SetRenderTarget(renderer_, target_texture);
2645 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
2646 SDL_RenderClear(renderer_);
2647 SDL_RenderCopy(renderer_, texture_sharp, NULL, NULL);
2648 SDL_SetRenderTarget(renderer_, NULL);
2649 } else {
2650 SDL_BlitScaled(surface, NULL, onscreen_surface_2x, NULL);
2651 surface = onscreen_surface_2x;
2652 SDL_UpdateTexture(target_texture, NULL, surface->pixels, surface->pitch);
2653 }
2654 } else {
2655 SDL_UpdateTexture(target_texture, NULL, surface->pixels, surface->pitch);
2656 }
2657 SDL_RenderClear(renderer_);
2658 SDL_RenderCopy(renderer_, target_texture, NULL, NULL);
2659 SDL_RenderPresent(renderer_);
2660 }
2661
2662 // seg009:9289
set_pal_arr(int start,int count,const rgb_type far * array,int vsync)2663 void __pascal far set_pal_arr(int start,int count,const rgb_type far *array,int vsync) {
2664 // stub
2665 int i;
2666 for (i = 0; i < count; ++i) {
2667 if (array) {
2668 set_pal(start + i, array[i].r, array[i].g, array[i].b, vsync);
2669 } else {
2670 set_pal(start + i, 0, 0, 0, vsync);
2671 }
2672 }
2673 }
2674
2675 rgb_type palette[256];
2676
2677 // seg009:92DF
set_pal(int index,int red,int green,int blue,int vsync)2678 void __pascal far set_pal(int index,int red,int green,int blue,int vsync) {
2679 // stub
2680 //palette[index] = ((red&0x3F)<<2)|((green&0x3F)<<2<<8)|((blue&0x3F)<<2<<16);
2681 palette[index].r = red;
2682 palette[index].g = green;
2683 palette[index].b = blue;
2684 }
2685
2686 // seg009:969C
add_palette_bits(byte n_colors)2687 int __pascal far add_palette_bits(byte n_colors) {
2688 // stub
2689 return 0;
2690 }
2691
2692 // seg009:9C36
find_first_pal_row(int which_rows_mask)2693 int __pascal far find_first_pal_row(int which_rows_mask) {
2694 word which_row = 0;
2695 word row_mask = 1;
2696 do {
2697 if (row_mask & which_rows_mask) {
2698 return which_row;
2699 }
2700 ++which_row;
2701 row_mask <<= 1;
2702 } while (which_row < 16);
2703 return 0;
2704 }
2705
2706 // seg009:9C6C
get_text_color(int cga_color,int low_half,int high_half_mask)2707 int __pascal far get_text_color(int cga_color,int low_half,int high_half_mask) {
2708 if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
2709 return cga_color;
2710 } else if (graphics_mode == gmMcgaVga && high_half_mask != 0) {
2711 return (find_first_pal_row(high_half_mask) << 4) + low_half;
2712 } else {
2713 return low_half;
2714 }
2715 }
2716
load_from_opendats_metadata(int resource_id,const char * extension,FILE ** out_fp,data_location * result,byte * checksum,int * size,dat_type ** out_pointer)2717 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) {
2718 char image_filename[POP_MAX_PATH];
2719 FILE* fp = NULL;
2720 dat_type* pointer;
2721 *result = data_none;
2722 // Go through all open DAT files.
2723 for (pointer = dat_chain_ptr; fp == NULL && pointer != NULL; pointer = pointer->next_dat) {
2724 *out_pointer = pointer;
2725 if (pointer->handle != NULL) {
2726 // If it's an actual DAT file:
2727 fp = pointer->handle;
2728 dat_table_type* dat_table = pointer->dat_table;
2729 int i;
2730 for (i = 0; i < dat_table->res_count; ++i) {
2731 if (dat_table->entries[i].id == resource_id) {
2732 break;
2733 }
2734 }
2735 if (i < dat_table->res_count) {
2736 // found
2737 *result = data_DAT;
2738 *size = dat_table->entries[i].size;
2739 if (fseek(fp, dat_table->entries[i].offset, SEEK_SET) ||
2740 fread(checksum, 1, 1, fp) != 1) {
2741 perror(pointer->filename);
2742 fp = NULL;
2743 }
2744 } else {
2745 // not found
2746 fp = NULL;
2747 }
2748 } else {
2749 // If it's a directory:
2750 char filename_no_ext[POP_MAX_PATH];
2751 // strip the .DAT file extension from the filename (use folders simply named TITLE, KID, VPALACE, etc.)
2752 strncpy(filename_no_ext, pointer->filename, sizeof(filename_no_ext));
2753 size_t len = strlen(filename_no_ext);
2754 if (len >= 5 && filename_no_ext[len-4] == '.') {
2755 filename_no_ext[len-4] = '\0'; // terminate, so ".DAT" is deleted from the filename
2756 }
2757 snprintf_check(image_filename,sizeof(image_filename),"/usr/local/share/sdlpop/%s/res%d.%s",filename_no_ext, resource_id, extension);
2758 if (!use_custom_levelset) {
2759 //printf("loading (binary) %s",image_filename);
2760 fp = fopen(locate_file(image_filename), "rb");
2761 }
2762 else {
2763 if (!skip_mod_data_files) {
2764 char image_filename_mod[POP_MAX_PATH];
2765 // before checking data/, first try mods/MODNAME/data/
2766 snprintf_check(image_filename_mod, sizeof(image_filename_mod), "%s/%s", mod_data_path, image_filename);
2767 //printf("loading (binary) %s",image_filename_mod);
2768 fp = fopen(locate_file(image_filename_mod), "rb");
2769 }
2770 if (fp == NULL && !skip_normal_data_files) {
2771 fp = fopen(locate_file(image_filename), "rb");
2772 }
2773 }
2774
2775 if (fp != NULL) {
2776 struct stat buf;
2777 if (fstat(fileno(fp), &buf) == 0) {
2778 *result = data_directory;
2779 *size = buf.st_size;
2780 } else {
2781 perror(image_filename);
2782 fclose(fp);
2783 fp = NULL;
2784 }
2785 }
2786 }
2787 }
2788 *out_fp = fp;
2789 if (fp == NULL) {
2790 *result = data_none;
2791 // printf(" FAILED\n");
2792 //return NULL;
2793 }
2794 //...
2795 }
2796
2797 // seg009:9F34
close_dat(dat_type far * pointer)2798 void __pascal far close_dat(dat_type far *pointer) {
2799 dat_type** prev = &dat_chain_ptr;
2800 dat_type* curr = dat_chain_ptr;
2801 while (curr != NULL) {
2802 if (curr == pointer) {
2803 *prev = curr->next_dat;
2804 if (curr->handle) fclose(curr->handle);
2805 if (curr->dat_table) free(curr->dat_table);
2806 free(curr);
2807 return;
2808 }
2809 curr = curr->next_dat;
2810 prev = &((*prev)->next_dat);
2811 }
2812 // stub
2813 }
2814
2815 // seg009:9F80
load_from_opendats_alloc(int resource,const char * extension,data_location * out_result,int * out_size)2816 void far *__pascal load_from_opendats_alloc(int resource, const char* extension, data_location* out_result, int* out_size) {
2817 // stub
2818 //printf("id = %d\n",resource);
2819 dat_type* pointer;
2820 data_location result;
2821 byte checksum;
2822 int size;
2823 FILE* fp = NULL;
2824 load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
2825 if (out_result != NULL) *out_result = result;
2826 if (out_size != NULL) *out_size = size;
2827 if (result == data_none) return NULL;
2828 void* area = malloc(size);
2829 //read(fd, area, size);
2830 if (fread(area, size, 1, fp) != 1) {
2831 fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n",
2832 __func__, pointer->filename, resource,
2833 size, strerror(errno));
2834 free(area);
2835 area = NULL;
2836 }
2837 if (result == data_directory) fclose(fp);
2838 /* XXX: check checksum */
2839 return area;
2840 }
2841
2842 // seg009:A172
load_from_opendats_to_area(int resource,void far * area,int length,const char * extension)2843 int __pascal far load_from_opendats_to_area(int resource,void far *area,int length, const char* extension) {
2844 // stub
2845 //return 0;
2846 dat_type* pointer;
2847 data_location result;
2848 byte checksum;
2849 int size;
2850 FILE* fp = NULL;
2851 load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
2852 if (result == data_none) return 0;
2853 if (fread(area, MIN(size, length), 1, fp) != 1) {
2854 fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n",
2855 __func__, pointer->filename, resource,
2856 size, strerror(errno));
2857 memset(area, 0, MIN(size, length));
2858 }
2859 if (result == data_directory) fclose(fp);
2860 /* XXX: check checksum */
2861 return 0;
2862 }
2863
2864 // SDL-specific implementations
2865
rect_to_sdlrect(const rect_type * rect,SDL_Rect * sdlrect)2866 void rect_to_sdlrect(const rect_type* rect, SDL_Rect* sdlrect) {
2867 sdlrect->x = rect->left;
2868 sdlrect->y = rect->top;
2869 sdlrect->w = rect->right - rect->left;
2870 sdlrect->h = rect->bottom - rect->top;
2871 }
2872
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)2873 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) {
2874 SDL_Rect src_rect;
2875 rect_to_sdlrect(source_rect, &src_rect);
2876 SDL_Rect dest_rect;
2877 rect_to_sdlrect(target_rect, &dest_rect);
2878
2879 if (blit == blitters_0_no_transp) {
2880 // Disable transparency.
2881 if (SDL_SetColorKey(source_surface, 0, 0) != 0) {
2882 sdlperror("method_1_blit_rect: SDL_SetColorKey");
2883 quit(1);
2884 }
2885 } else {
2886 // Enable transparency.
2887 if (SDL_SetColorKey(source_surface, SDL_TRUE, 0) != 0) {
2888 sdlperror("method_1_blit_rect: SDL_SetColorKey");
2889 quit(1);
2890 }
2891 }
2892 if (SDL_BlitSurface(source_surface, &src_rect, target_surface, &dest_rect) != 0) {
2893 sdlperror("method_1_blit_rect: SDL_BlitSurface");
2894 quit(1);
2895 }
2896 }
2897
method_3_blit_mono(image_type far * image,int xpos,int ypos,int blitter,byte color)2898 image_type far * __pascal far method_3_blit_mono(image_type far *image,int xpos,int ypos,int blitter,byte color) {
2899 int w = image->w;
2900 int h = image->h;
2901 if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
2902 sdlperror("method_3_blit_mono: SDL_SetColorKey");
2903 quit(1);
2904 }
2905 SDL_Surface* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
2906
2907 SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_NONE);
2908 /* Causes problems with SDL 2.0.5 (see #105)
2909 if (SDL_SetColorKey(colored_image, SDL_TRUE, 0) != 0) {
2910 sdlperror("method_3_blit_mono: SDL_SetColorKey");
2911 quit(1);
2912 }
2913 */
2914
2915 if (SDL_LockSurface(colored_image) != 0) {
2916 sdlperror("method_3_blit_mono: SDL_LockSurface");
2917 quit(1);
2918 }
2919
2920 int y,x;
2921 rgb_type palette_color = palette[color];
2922 uint32_t rgb_color = SDL_MapRGB(colored_image->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) & 0xFFFFFF;
2923 int stride = colored_image->pitch;
2924 for (y = 0; y < h; ++y) {
2925 uint32_t* pixel_ptr = (uint32_t*) ((byte*)colored_image->pixels + stride * y);
2926 for (x = 0; x < w; ++x) {
2927 // set RGB but leave alpha
2928 *pixel_ptr = (*pixel_ptr & 0xFF000000) | rgb_color;
2929 //printf("pixel x=%d, y=%d, color = 0x%8x\n", x, y, *pixel_ptr);
2930 ++pixel_ptr;
2931 }
2932 }
2933 SDL_UnlockSurface(colored_image);
2934
2935 SDL_Rect src_rect = {0, 0, image->w, image->h};
2936 SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
2937
2938 SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_BLEND);
2939 SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_BLEND);
2940 SDL_SetSurfaceAlphaMod(colored_image, 255);
2941 if (SDL_BlitSurface(colored_image, &src_rect, current_target_surface, &dest_rect) != 0) {
2942 sdlperror("method_3_blit_mono: SDL_BlitSurface");
2943 quit(1);
2944 }
2945 SDL_FreeSurface(colored_image);
2946
2947 return image;
2948 }
2949
2950 // Workaround for a bug in SDL2 (before v2.0.4):
2951 // https://bugzilla.libsdl.org/show_bug.cgi?id=2986
2952 // SDL_FillRect onto a 24-bit surface swaps Red and Blue component
2953
2954 bool RGB24_bug_checked = false;
2955 bool RGB24_bug_affected;
2956
RGB24_bug_check()2957 bool RGB24_bug_check() {
2958 if (!RGB24_bug_checked) {
2959 // Check if the bug occurs in this version of SDL.
2960 SDL_Surface* test_surface = SDL_CreateRGBSurface(0, 1, 1, 24, 0, 0, 0, 0);
2961 if (NULL == test_surface) sdlperror("SDL_CreateSurface in RGB24_bug_check");
2962 // Fill with red.
2963 SDL_FillRect(test_surface, NULL, SDL_MapRGB(test_surface->format, 0xFF, 0, 0));
2964 if (0 != SDL_LockSurface(test_surface)) sdlperror("SDL_LockSurface in RGB24_bug_check");
2965 // Read red component of pixel.
2966 RGB24_bug_affected = (*(Uint32*)test_surface->pixels & test_surface->format->Rmask) == 0;
2967 SDL_UnlockSurface(test_surface);
2968 SDL_FreeSurface(test_surface);
2969 RGB24_bug_checked = true;
2970 }
2971 return RGB24_bug_affected;
2972 }
2973
safe_SDL_FillRect(SDL_Surface * dst,const SDL_Rect * rect,Uint32 color)2974 int safe_SDL_FillRect(SDL_Surface* dst, const SDL_Rect* rect, Uint32 color) {
2975 if (dst->format->BitsPerPixel == 24 && RGB24_bug_check()) {
2976 // In the buggy version, SDL_FillRect swaps R and B, so we swap it once more.
2977 color = ((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16);
2978 }
2979 return SDL_FillRect(dst, rect, color);
2980 }
2981 // End of workaround.
2982
method_5_rect(const rect_type far * rect,int blit,byte color)2983 const rect_type far * __pascal far method_5_rect(const rect_type far *rect,int blit,byte color) {
2984 SDL_Rect dest_rect;
2985 rect_to_sdlrect(rect, &dest_rect);
2986 rgb_type palette_color = palette[color];
2987 #ifndef USE_ALPHA
2988 uint32_t rgb_color = SDL_MapRGBA(current_target_surface->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2, 0xFF);
2989 #else
2990 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);
2991 #endif
2992 if (safe_SDL_FillRect(current_target_surface, &dest_rect, rgb_color) != 0) {
2993 sdlperror("method_5_rect: SDL_FillRect");
2994 quit(1);
2995 }
2996 return rect;
2997 }
2998
draw_rect_with_alpha(const rect_type * rect,byte color,byte alpha)2999 void draw_rect_with_alpha(const rect_type* rect, byte color, byte alpha) {
3000 SDL_Rect dest_rect;
3001 rect_to_sdlrect(rect, &dest_rect);
3002 rgb_type palette_color = palette[color];
3003 uint32_t rgb_color = SDL_MapRGBA(overlay_surface->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2, alpha);
3004 if (safe_SDL_FillRect(current_target_surface, &dest_rect, rgb_color) != 0) {
3005 sdlperror("draw_rect_with_alpha: SDL_FillRect");
3006 quit(1);
3007 }
3008 }
3009
draw_rect_contours(const rect_type * rect,byte color)3010 void draw_rect_contours(const rect_type* rect, byte color) {
3011 // TODO: handle 24 bit surfaces? (currently, 32 bit surface is assumed)
3012 if (current_target_surface->format->BitsPerPixel != 32) {
3013 printf("draw_rect_contours: not implemented for %d bit surfaces\n", current_target_surface->format->BitsPerPixel);
3014 return;
3015 }
3016 SDL_Rect dest_rect;
3017 rect_to_sdlrect(rect, &dest_rect);
3018 rgb_type palette_color = palette[color];
3019 uint32_t rgb_color = SDL_MapRGBA(overlay_surface->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2, 0xFF);
3020 if (SDL_LockSurface(current_target_surface) != 0) {
3021 sdlperror("draw_rect_contours: SDL_LockSurface");
3022 quit(1);
3023 }
3024 int bytes_per_pixel = current_target_surface->format->BytesPerPixel;
3025 int pitch = current_target_surface->pitch;
3026 byte* pixels = current_target_surface->pixels;
3027 int xmin = MIN(dest_rect.x, current_target_surface->w);
3028 int xmax = MIN(dest_rect.x + dest_rect.w, current_target_surface->w);
3029 int ymin = MIN(dest_rect.y, current_target_surface->h);
3030 int ymax = MIN(dest_rect.y + dest_rect.h, current_target_surface->h);
3031 byte* row = pixels + ymin*pitch;
3032 uint32_t* pixel = (uint32_t*) (row + xmin*bytes_per_pixel);
3033 for (int x = xmin; x < xmax; ++x) {
3034 *pixel++ = rgb_color;
3035 }
3036 for (int y = ymin+1; y < ymax-1; ++y) {
3037 row += pitch;
3038 *(uint32_t*)(row + xmin*bytes_per_pixel) = rgb_color;
3039 *(uint32_t*)(row + (xmax-1)*bytes_per_pixel) = rgb_color;
3040 }
3041 pixel = (uint32_t*) (pixels + (ymax-1)*pitch + xmin*bytes_per_pixel);
3042 for (int x = xmin; x < xmax; ++x) {
3043 *pixel++ = rgb_color;
3044 }
3045
3046 SDL_UnlockSurface(current_target_surface);
3047 }
3048
blit_xor(SDL_Surface * target_surface,SDL_Rect * dest_rect,SDL_Surface * image,SDL_Rect * src_rect)3049 void blit_xor(SDL_Surface* target_surface, SDL_Rect* dest_rect, SDL_Surface* image, SDL_Rect* src_rect) {
3050 if (dest_rect->w != src_rect->w || dest_rect->h != src_rect->h) {
3051 printf("blit_xor: dest_rect and src_rect have different sizes\n");
3052 quit(1);
3053 }
3054 SDL_Surface* helper_surface = SDL_CreateRGBSurface(0, dest_rect->w, dest_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
3055 if (helper_surface == NULL) {
3056 sdlperror("blit_xor: SDL_CreateRGBSurface");
3057 quit(1);
3058 }
3059 SDL_Surface* image_24 = SDL_ConvertSurface(image, helper_surface->format, 0);
3060 //SDL_CreateRGBSurface(0, src_rect->w, src_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
3061 if (image_24 == NULL) {
3062 sdlperror("blit_xor: SDL_CreateRGBSurface");
3063 quit(1);
3064 }
3065 SDL_Rect dest_rect2 = *src_rect;
3066 // Read what is currently where we want to draw the new image.
3067 if (SDL_BlitSurface(target_surface, dest_rect, helper_surface, &dest_rect2) != 0) {
3068 sdlperror("blit_xor: SDL_BlitSurface");
3069 quit(1);
3070 }
3071 if (SDL_LockSurface(image_24) != 0) {
3072 sdlperror("blit_xor: SDL_LockSurface");
3073 quit(1);
3074 }
3075 if (SDL_LockSurface(helper_surface) != 0) {
3076 sdlperror("blit_xor: SDL_LockSurface");
3077 quit(1);
3078 }
3079 int size = helper_surface->h * helper_surface->pitch;
3080 int i;
3081 byte *p_src = (byte*) image_24->pixels;
3082 byte *p_dest = (byte*) helper_surface->pixels;
3083
3084 // Xor the old area with the image.
3085 for (i = 0; i < size; ++i) {
3086 *p_dest ^= *p_src;
3087 ++p_src; ++p_dest;
3088 }
3089 SDL_UnlockSurface(image_24);
3090 SDL_UnlockSurface(helper_surface);
3091 // Put the new area in place of the old one.
3092 if (SDL_BlitSurface(helper_surface, src_rect, target_surface, dest_rect) != 0) {
3093 sdlperror("blit_xor: SDL_BlitSurface 2065");
3094 quit(1);
3095 }
3096 SDL_FreeSurface(image_24);
3097 SDL_FreeSurface(helper_surface);
3098 }
3099
3100 #ifdef USE_COLORED_TORCHES
draw_colored_torch(int color,SDL_Surface * image,int xpos,int ypos)3101 void draw_colored_torch(int color, SDL_Surface* image, int xpos, int ypos) {
3102 if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
3103 sdlperror("draw_colored_torch: SDL_SetColorKey");
3104 quit(1);
3105 }
3106
3107 SDL_Surface* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
3108 SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_NONE);
3109
3110 if (SDL_LockSurface(colored_image) != 0) {
3111 sdlperror("draw_colored_torch: SDL_LockSurface");
3112 quit(1);
3113 }
3114
3115 int w = colored_image->w;
3116 int h = colored_image->h;
3117 int y,x;
3118 int iRed = ((color >> 4) & 3) * 85;
3119 int iGreen = ((color >> 2) & 3) * 85;
3120 int iBlue = ((color >> 0) & 3) * 85;
3121 uint32_t old_color = SDL_MapRGB(colored_image->format, 0xFC, 0x84, 0x00) & 0xFFFFFF; // the orange in the flame
3122 uint32_t new_color = SDL_MapRGB(colored_image->format, iRed, iGreen, iBlue) & 0xFFFFFF;
3123 int stride = colored_image->pitch;
3124 for (y = 0; y < h; ++y) {
3125 uint32_t* pixel_ptr = (uint32_t*) ((byte*)colored_image->pixels + stride * y);
3126 for (x = 0; x < w; ++x) {
3127 if ((*pixel_ptr & 0xFFFFFF) == old_color) {
3128 // set RGB but leave alpha
3129 *pixel_ptr = (*pixel_ptr & 0xFF000000) | new_color;
3130 }
3131 ++pixel_ptr;
3132 }
3133 }
3134 SDL_UnlockSurface(colored_image);
3135
3136 method_6_blit_img_to_scr(colored_image, xpos, ypos, blitters_0_no_transp);
3137 SDL_FreeSurface(colored_image);
3138 }
3139 #endif
3140
method_6_blit_img_to_scr(image_type far * image,int xpos,int ypos,int blit)3141 image_type far * __pascal far method_6_blit_img_to_scr(image_type far *image,int xpos,int ypos,int blit) {
3142 if (image == NULL) {
3143 printf("method_6_blit_img_to_scr: image == NULL\n");
3144 //quit(1);
3145 return NULL;
3146 }
3147
3148 if (blit == blitters_9_black) {
3149 method_3_blit_mono(image, xpos, ypos, blitters_9_black, 0);
3150 return image;
3151 }
3152
3153 SDL_Rect src_rect = {0, 0, image->w, image->h};
3154 SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
3155
3156 if (blit == blitters_3_xor) {
3157 blit_xor(current_target_surface, &dest_rect, image, &src_rect);
3158 return image;
3159 }
3160
3161 #ifdef USE_COLORED_TORCHES
3162 if (blit >= blitters_colored_flame && blit <= blitters_colored_flame_last) {
3163 draw_colored_torch(blit - blitters_colored_flame, image, xpos, ypos);
3164 return image;
3165 }
3166 #endif
3167
3168 SDL_SetSurfaceBlendMode(image, SDL_BLENDMODE_NONE);
3169 SDL_SetSurfaceAlphaMod(image, 255);
3170
3171 if (blit == blitters_0_no_transp) {
3172 SDL_SetColorKey(image, SDL_FALSE, 0);
3173 }
3174 else {
3175 SDL_SetColorKey(image, SDL_TRUE, 0);
3176 }
3177 if (SDL_BlitSurface(image, &src_rect, current_target_surface, &dest_rect) != 0) {
3178 sdlperror("method_6_blit_img_to_scr: SDL_BlitSurface 2247");
3179 quit(1);
3180 }
3181
3182 if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
3183 sdlperror("method_6_blit_img_to_scr: SDL_SetAlpha");
3184 quit(1);
3185 }
3186 return image;
3187 }
3188
3189 #ifndef USE_COMPAT_TIMER
3190 int fps = 60;
3191 float milliseconds_per_tick = (1000.0f / 60.0f);
3192 Uint64 timer_last_counter[NUM_TIMERS];
3193 #endif
3194 int wait_time[NUM_TIMERS];
3195
3196
3197 #ifdef USE_COMPAT_TIMER
timer_callback(Uint32 interval,void * param)3198 Uint32 timer_callback(Uint32 interval, void *param) {
3199 SDL_Event event;
3200 memset(&event, 0, sizeof(event));
3201 event.type = SDL_USEREVENT;
3202 event.user.code = userevent_TIMER;
3203 event.user.data1 = param;
3204 SDL_PushEvent(&event);
3205 return interval;
3206 }
3207 #endif
3208
reset_timer(int timer_index)3209 void reset_timer(int timer_index) {
3210 #ifndef USE_COMPAT_TIMER
3211 timer_last_counter[timer_index] = SDL_GetPerformanceCounter();
3212 #endif
3213 }
3214
get_ticks_per_sec(int timer_index)3215 double get_ticks_per_sec(int timer_index) {
3216 return (double) fps / wait_time[timer_index];
3217 }
3218
recalculate_feather_fall_timer(double previous_ticks_per_second,double ticks_per_second)3219 void recalculate_feather_fall_timer(double previous_ticks_per_second, double ticks_per_second) {
3220 if (is_feather_fall <= MAX(previous_ticks_per_second, ticks_per_second) ||
3221 previous_ticks_per_second == ticks_per_second) {
3222 return;
3223 }
3224 // there are more ticks per second in base mode vs fight mode so
3225 // feather fall length needs to be recalculated
3226 is_feather_fall = is_feather_fall / previous_ticks_per_second * ticks_per_second;
3227 }
3228
set_timer_length(int timer_index,int length)3229 void set_timer_length(int timer_index, int length) {
3230 if (!fixes->fix_quicksave_during_feather) {
3231 wait_time[timer_index] = length;
3232 return;
3233 }
3234 if (is_feather_fall == 0 ||
3235 wait_time[timer_index] < custom->base_speed ||
3236 wait_time[timer_index] > custom->fight_speed) {
3237 wait_time[timer_index] = length;
3238 return;
3239 }
3240 double previous_ticks_per_second, ticks_per_second;
3241 previous_ticks_per_second = get_ticks_per_sec(timer_index);
3242 wait_time[timer_index] = length;
3243 ticks_per_second = get_ticks_per_sec(timer_index);
3244 recalculate_feather_fall_timer(previous_ticks_per_second, ticks_per_second);
3245 }
3246
start_timer(int timer_index,int length)3247 void __pascal start_timer(int timer_index, int length) {
3248 #ifdef USE_REPLAY
3249 if (replaying && skipping_replay) return;
3250 #endif
3251 #ifndef USE_COMPAT_TIMER
3252 timer_last_counter[timer_index] = SDL_GetPerformanceCounter();
3253 #endif
3254 wait_time[timer_index] = length;
3255 }
3256
toggle_fullscreen()3257 void toggle_fullscreen() {
3258 uint32_t flags = SDL_GetWindowFlags(window_);
3259 if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
3260 SDL_SetWindowFullscreen(window_, 0);
3261 SDL_ShowCursor(SDL_ENABLE);
3262 }
3263 else {
3264 SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
3265 SDL_ShowCursor(SDL_DISABLE);
3266 }
3267 }
3268
3269 bool ignore_tab = false;
3270
process_events()3271 void process_events() {
3272 // Process all events in the queue.
3273 // Previously, this procedure would wait for *one* event and process it, then return.
3274 // Much like the x86 HLT instruction.
3275 // (We still want to process all events in the queue. For instance, there might be
3276 // simultaneous SDL2 KEYDOWN and TEXTINPUT events.)
3277 SDL_Event event;
3278 while (SDL_PollEvent(&event) == 1) { // while there are still events to be processed
3279 switch (event.type) {
3280 case SDL_KEYDOWN:
3281 {
3282 int modifier = event.key.keysym.mod;
3283 int scancode = event.key.keysym.scancode;
3284
3285 // Handle these separately, so they won't interrupt things that are usually interrupted by a keypress. (pause, cutscene)
3286 #ifdef USE_FAST_FORWARD
3287 if (scancode == SDL_SCANCODE_GRAVE) {
3288 init_timer(BASE_FPS * FAST_FORWARD_RATIO); // fast-forward on
3289 audio_speed = FAST_FORWARD_RATIO;
3290 break;
3291 }
3292 #endif
3293 #ifdef USE_SCREENSHOT
3294 if (scancode == SDL_SCANCODE_F12) {
3295 if (modifier & KMOD_SHIFT) {
3296 save_level_screenshot((modifier & KMOD_CTRL) != 0);
3297 } else {
3298 save_screenshot();
3299 }
3300 } else
3301 #endif
3302 #ifdef USE_MENU
3303 if (escape_key_suppressed &&
3304 (scancode == SDL_SCANCODE_BACKSPACE || (enable_pause_menu && scancode == SDL_SCANCODE_ESCAPE))
3305 ) {
3306 break; // Prevent repeated keystrokes opening/closing the menu as long as the key is held down.
3307 } else
3308 #endif
3309 if ((modifier & KMOD_ALT) &&
3310 scancode == SDL_SCANCODE_RETURN)
3311 {
3312 // Only if the Enter key was pressed down right now.
3313 if (key_states[scancode] == 0) {
3314 // Alt+Enter: toggle fullscreen mode
3315 toggle_fullscreen();
3316 key_states[scancode] = 1;
3317 }
3318 } else {
3319 key_states[scancode] = 1;
3320 switch (scancode) {
3321 // Keys that are ignored by themselves:
3322 case SDL_SCANCODE_LCTRL:
3323 case SDL_SCANCODE_LSHIFT:
3324 case SDL_SCANCODE_LALT:
3325 case SDL_SCANCODE_LGUI:
3326 case SDL_SCANCODE_RCTRL:
3327 case SDL_SCANCODE_RSHIFT:
3328 case SDL_SCANCODE_RALT:
3329 case SDL_SCANCODE_RGUI:
3330 case SDL_SCANCODE_CAPSLOCK:
3331 case SDL_SCANCODE_SCROLLLOCK:
3332 case SDL_SCANCODE_NUMLOCKCLEAR:
3333 case SDL_SCANCODE_APPLICATION:
3334 case SDL_SCANCODE_PRINTSCREEN:
3335 case SDL_SCANCODE_VOLUMEUP:
3336 case SDL_SCANCODE_VOLUMEDOWN:
3337 // Why are there two mute key codes?
3338 case SDL_SCANCODE_MUTE:
3339 case SDL_SCANCODE_AUDIOMUTE:
3340 case SDL_SCANCODE_PAUSE:
3341 break;
3342
3343 default:
3344 // If Alt is held down from Alt+Tab: ignore it until it's released.
3345 if (scancode == SDL_SCANCODE_TAB && ignore_tab) break;
3346
3347 last_key_scancode = scancode;
3348 if (modifier & KMOD_SHIFT) last_key_scancode |= WITH_SHIFT;
3349 if (modifier & KMOD_CTRL ) last_key_scancode |= WITH_CTRL ;
3350 if (modifier & KMOD_ALT ) last_key_scancode |= WITH_ALT ;
3351 }
3352
3353 #ifdef USE_AUTO_INPUT_MODE
3354 switch (scancode) {
3355 // Keys that are used for keyboard control:
3356 case SDL_SCANCODE_LSHIFT:
3357 case SDL_SCANCODE_RSHIFT:
3358 case SDL_SCANCODE_LEFT:
3359 case SDL_SCANCODE_RIGHT:
3360 case SDL_SCANCODE_UP:
3361 case SDL_SCANCODE_DOWN:
3362 case SDL_SCANCODE_CLEAR:
3363 case SDL_SCANCODE_HOME:
3364 case SDL_SCANCODE_PAGEUP:
3365 case SDL_SCANCODE_KP_2:
3366 case SDL_SCANCODE_KP_4:
3367 case SDL_SCANCODE_KP_5:
3368 case SDL_SCANCODE_KP_6:
3369 case SDL_SCANCODE_KP_7:
3370 case SDL_SCANCODE_KP_8:
3371 case SDL_SCANCODE_KP_9:
3372 if (!is_keyboard_mode) {
3373 is_keyboard_mode = 1;
3374 is_joyst_mode = 0;
3375 }
3376 }
3377 #endif
3378 }
3379 break;
3380 }
3381 case SDL_KEYUP:
3382 // If Alt was held down from Alt+Tab but now it's released: stop ignoring Tab.
3383 if (event.key.keysym.scancode == SDL_SCANCODE_TAB && ignore_tab) ignore_tab = false;
3384
3385 #ifdef USE_FAST_FORWARD
3386 if (event.key.keysym.scancode == SDL_SCANCODE_GRAVE) {
3387 init_timer(BASE_FPS); // fast-forward off
3388 audio_speed = 1;
3389 break;
3390 }
3391 #endif
3392
3393 key_states[event.key.keysym.scancode] = 0;
3394 #ifdef USE_MENU
3395 // Prevent repeated keystrokes opening/closing the menu as long as the key is held down.
3396 if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
3397 escape_key_suppressed = false;
3398 }
3399 #endif
3400 break;
3401 case SDL_CONTROLLERAXISMOTION:
3402 if (event.caxis.axis < 6) {
3403 joy_axis[event.caxis.axis] = event.caxis.value;
3404
3405 #ifdef USE_AUTO_INPUT_MODE
3406 if (!is_joyst_mode && (event.caxis.value >= joystick_threshold || event.caxis.value <= -joystick_threshold)) {
3407 is_joyst_mode = 1;
3408 is_keyboard_mode = 0;
3409 }
3410 #endif
3411 }
3412 break;
3413 case SDL_CONTROLLERBUTTONDOWN:
3414 #ifdef USE_AUTO_INPUT_MODE
3415 if (!is_joyst_mode) {
3416 is_joyst_mode = 1;
3417 is_keyboard_mode = 0;
3418 }
3419 #endif
3420 switch (event.cbutton.button)
3421 {
3422 case SDL_CONTROLLER_BUTTON_DPAD_LEFT: joy_hat_states[0] = -1; break; // left
3423 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 1; break; // right
3424 case SDL_CONTROLLER_BUTTON_DPAD_UP: joy_hat_states[1] = -1; break; // up
3425 case SDL_CONTROLLER_BUTTON_DPAD_DOWN: joy_hat_states[1] = 1; break; // down
3426
3427 case SDL_CONTROLLER_BUTTON_A: joy_AY_buttons_state = 1; break; /*** A (down) ***/
3428 case SDL_CONTROLLER_BUTTON_Y: joy_AY_buttons_state = -1; break; /*** Y (up) ***/
3429 case SDL_CONTROLLER_BUTTON_X: joy_X_button_state = 1; break; /*** X (Shift) ***/
3430 case SDL_CONTROLLER_BUTTON_B: joy_B_button_state = 1; break; /*** B (unused) ***/
3431
3432 case SDL_CONTROLLER_BUTTON_START:
3433 case SDL_CONTROLLER_BUTTON_BACK:
3434 #ifdef USE_MENU
3435 last_key_scancode = SDL_SCANCODE_BACKSPACE; /*** bring up pause menu ***/
3436 #else
3437 last_key_scancode = SDL_SCANCODE_ESCAPE; /*** back (pause game) ***/
3438 #endif
3439 break;
3440
3441 default: break;
3442 }
3443 break;
3444 case SDL_CONTROLLERBUTTONUP:
3445 switch (event.cbutton.button)
3446 {
3447 case SDL_CONTROLLER_BUTTON_DPAD_LEFT: joy_hat_states[0] = 0; break; // left
3448 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 0; break; // right
3449 case SDL_CONTROLLER_BUTTON_DPAD_UP: joy_hat_states[1] = 0; break; // up
3450 case SDL_CONTROLLER_BUTTON_DPAD_DOWN: joy_hat_states[1] = 0; break; // down
3451
3452 case SDL_CONTROLLER_BUTTON_A: joy_AY_buttons_state = 0; break; /*** A (down) ***/
3453 case SDL_CONTROLLER_BUTTON_Y: joy_AY_buttons_state = 0; break; /*** Y (up) ***/
3454 case SDL_CONTROLLER_BUTTON_X: joy_X_button_state = 0; break; /*** X (Shift) ***/
3455 case SDL_CONTROLLER_BUTTON_B: joy_B_button_state = 0; break; /*** B (unused) ***/
3456
3457 default: break;
3458 }
3459 break;
3460 case SDL_JOYBUTTONDOWN:
3461 case SDL_JOYBUTTONUP:
3462 case SDL_JOYAXISMOTION:
3463 // Only handle the event if the joystick is incompatible with the SDL_GameController interface.
3464 // (Otherwise it will interfere with the normal action of the SDL_GameController API.)
3465 if (!using_sdl_joystick_interface) {
3466 break;
3467 }
3468 if (event.type == SDL_JOYAXISMOTION) {
3469 if (event.jaxis.axis == SDL_JOYSTICK_X_AXIS) {
3470 joy_axis[SDL_CONTROLLER_AXIS_LEFTX] = event.jaxis.value;
3471 }
3472 else if (event.jaxis.axis == SDL_JOYSTICK_Y_AXIS) {
3473 joy_axis[SDL_CONTROLLER_AXIS_LEFTY] = event.jaxis.value;
3474 }
3475 // Disregard SDL_JOYAXISMOTION events within joystick 'dead zone'
3476 int joy_x = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
3477 int joy_y = joy_axis[SDL_CONTROLLER_AXIS_LEFTY];
3478 if ((dword)(joy_x*joy_x) + (dword)(joy_y*joy_y) < (dword)(joystick_threshold*joystick_threshold)) {
3479 break;
3480 }
3481 }
3482 #ifdef USE_AUTO_INPUT_MODE
3483 if (!is_joyst_mode) {
3484 is_joyst_mode = 1;
3485 is_keyboard_mode = 0;
3486 }
3487 #endif
3488 if (event.type == SDL_JOYBUTTONDOWN) {
3489 if (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y) joy_AY_buttons_state = -1; // Y (up)
3490 else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X) joy_X_button_state = -1; // X (Shift)
3491 }
3492 else if (event.type == SDL_JOYBUTTONUP) {
3493 if (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y) joy_AY_buttons_state = 0; // Y (up)
3494 else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X) joy_X_button_state = 0; // X (Shift)
3495 }
3496 break;
3497
3498 case SDL_TEXTINPUT:
3499 last_text_input = event.text.text[0]; // UTF-8 formatted char text input
3500 break;
3501 case SDL_WINDOWEVENT:
3502 // In case the user switches away while holding a key: do as if all keys were released.
3503 // (DOSBox does the same.)
3504
3505 /* // not implemented in SDL2 for now
3506 *
3507 if ((event.active.state & SDL_APPINPUTFOCUS) && event.active.gain == 0) {
3508 memset(key_states, 0, sizeof(key_states));
3509 }
3510 // Note: event.active.state can contain multiple flags or'ed.
3511 // 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.
3512 if ((event.active.state & SDL_APPACTIVE) && event.active.gain == 1) {
3513 update_screen();
3514 }
3515 */
3516 switch (event.window.event) {
3517 case SDL_WINDOWEVENT_SIZE_CHANGED:
3518 window_resized();
3519 // fallthrough!
3520 //case SDL_WINDOWEVENT_MOVED:
3521 //case SDL_WINDOWEVENT_RESTORED:
3522 case SDL_WINDOWEVENT_EXPOSED:
3523 update_screen();
3524 break;
3525
3526 case SDL_WINDOWEVENT_FOCUS_GAINED:
3527 // Fix for this bug: When playing back a recording, Alt+Tabbing back to SDLPoP stops the replay if Alt is released before Tab.
3528 { // If Alt is held down from Alt+Tab: ignore it until it's released.
3529 const Uint8 *state = SDL_GetKeyboardState(NULL);
3530 if (state[SDL_SCANCODE_TAB]) ignore_tab = true;
3531 }
3532 break;
3533 }
3534 break;
3535 case SDL_USEREVENT:
3536 if (event.user.code == userevent_TIMER /*&& event.user.data1 == (void*)timer_index*/) {
3537 #ifdef USE_COMPAT_TIMER
3538 int index;
3539 for (index = 0; index < NUM_TIMERS; ++index) {
3540 if (wait_time[index] > 0) --wait_time[index];
3541 }
3542 #endif
3543 } else if (event.user.code == userevent_SOUND) {
3544 //sound_timer = 0;
3545 //stop_sounds();
3546 }
3547 break;
3548 #ifdef USE_MENU
3549 case SDL_MOUSEBUTTONDOWN:
3550 switch(event.button.button) {
3551 case SDL_BUTTON_LEFT:
3552 if (!is_menu_shown) {
3553 last_key_scancode = SDL_SCANCODE_BACKSPACE;
3554 } else {
3555 mouse_clicked = true;
3556 }
3557 break;
3558 case SDL_BUTTON_RIGHT:
3559 case SDL_BUTTON_X1: // 'Back' button (on mice that have these extra buttons).
3560 mouse_button_clicked_right = true;
3561 break;
3562 default: break;
3563 }
3564
3565 break;
3566 case SDL_MOUSEWHEEL:
3567 if (is_menu_shown) {
3568 menu_control_scroll_y = -event.wheel.y;
3569 }
3570 break;
3571 #endif
3572 case SDL_QUIT:
3573 #ifdef USE_MENU
3574 if (is_menu_shown) {
3575 menu_was_closed();
3576 }
3577 #endif
3578 quit(0);
3579 break;
3580 }
3581 }
3582 }
3583
idle()3584 void idle() {
3585 process_events();
3586 update_screen();
3587 }
3588
do_simple_wait(int timer_index)3589 void __pascal do_simple_wait(int timer_index) {
3590 #ifdef USE_REPLAY
3591 if ((replaying && skipping_replay) || is_validate_mode) return;
3592 #endif
3593 update_screen();
3594 while (! has_timer_stopped(timer_index)) {
3595 SDL_Delay(1);
3596 process_events();
3597 }
3598 }
3599
3600 word word_1D63A = 1;
do_wait(int timer_index)3601 int __pascal do_wait(int timer_index) {
3602 #ifdef USE_REPLAY
3603 if ((replaying && skipping_replay) || is_validate_mode) return 0;
3604 #endif
3605 update_screen();
3606 while (! has_timer_stopped(timer_index)) {
3607 SDL_Delay(1);
3608 process_events();
3609 int key = do_paused();
3610 if (key != 0 && (word_1D63A != 0 || key == 0x1B)) return 1;
3611 }
3612 return 0;
3613 }
3614
3615 #ifdef USE_COMPAT_TIMER
3616 SDL_TimerID global_timer = NULL;
3617 #endif
3618 // seg009:78E9
init_timer(int frequency)3619 void __pascal far init_timer(int frequency) {
3620 perf_frequency = SDL_GetPerformanceFrequency();
3621 #ifndef USE_COMPAT_TIMER
3622 fps = frequency;
3623 milliseconds_per_tick = 1000.0f / (float)fps;
3624 perf_counters_per_tick = perf_frequency / fps;
3625 milliseconds_per_counter = 1000.0f / perf_frequency;
3626 #else
3627 if (global_timer != 0) {
3628 if (!SDL_RemoveTimer(global_timer)) {
3629 sdlperror("init_timer: SDL_RemoveTimer");
3630 }
3631 }
3632 global_timer = SDL_AddTimer(1000/frequency, timer_callback, NULL);
3633 if (global_timer == 0) {
3634 sdlperror("init_timer: SDL_AddTimer");
3635 quit(1);
3636 }
3637 #endif
3638 }
3639
3640 // seg009:35F6
set_clip_rect(const rect_type far * rect)3641 void __pascal far set_clip_rect(const rect_type far *rect) {
3642 SDL_Rect clip_rect;
3643 rect_to_sdlrect(rect, &clip_rect);
3644 SDL_SetClipRect(current_target_surface, &clip_rect);
3645 }
3646
3647 // seg009:365C
reset_clip_rect()3648 void __pascal far reset_clip_rect() {
3649 SDL_SetClipRect(current_target_surface, NULL);
3650 }
3651
3652 // seg009:1983
set_bg_attr(int vga_pal_index,int hc_pal_index)3653 void __pascal far set_bg_attr(int vga_pal_index,int hc_pal_index) {
3654 // stub
3655 #ifdef USE_FLASH
3656 //palette[vga_pal_index] = vga_palette[hc_pal_index];
3657 if (!enable_flash) return;
3658 if (vga_pal_index == 0) {
3659 /*
3660 if (SDL_SetAlpha(offscreen_surface, SDL_SRCALPHA, 0) != 0) {
3661 sdlperror("set_bg_attr: SDL_SetAlpha");
3662 quit(1);
3663 }
3664 */
3665 // Make the black pixels transparent.
3666 if (SDL_SetColorKey(offscreen_surface, SDL_TRUE, 0) != 0) { // SDL_SRCCOLORKEY old
3667 sdlperror("set_bg_attr: SDL_SetColorKey");
3668 quit(1);
3669 }
3670 SDL_Rect rect = {0,0,0,0};
3671 rect.w = offscreen_surface->w;
3672 rect.h = offscreen_surface->h;
3673 rgb_type palette_color = palette[hc_pal_index];
3674 uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) /*& 0xFFFFFF*/;
3675 //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
3676 // First clear the screen with the color of the flash.
3677 if (safe_SDL_FillRect(onscreen_surface_, &rect, rgb_color) != 0) {
3678 sdlperror("set_bg_attr: SDL_FillRect");
3679 quit(1);
3680 }
3681 //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
3682 if (upside_down) {
3683 flip_screen(offscreen_surface);
3684 }
3685 // Then draw the offscreen image onto it.
3686 if (SDL_BlitSurface(offscreen_surface, &rect, onscreen_surface_, &rect) != 0) {
3687 sdlperror("set_bg_attr: SDL_BlitSurface");
3688 quit(1);
3689 }
3690 #ifdef USE_LIGHTING
3691 if (hc_pal_index == 0) update_lighting(&rect_top);
3692 #endif
3693 if (upside_down) {
3694 flip_screen(offscreen_surface);
3695 }
3696 // And show it!
3697 // update_screen();
3698 // Give some time to show the flash.
3699 //SDL_Flip(onscreen_surface_);
3700 // if (hc_pal_index != 0) SDL_Delay(2*(1000/60));
3701 //SDL_Flip(onscreen_surface_);
3702 /*
3703 if (SDL_SetAlpha(offscreen_surface, 0, 0) != 0) {
3704 sdlperror("set_bg_attr: SDL_SetAlpha");
3705 quit(1);
3706 }
3707 */
3708 if (SDL_SetColorKey(offscreen_surface, 0, 0) != 0) {
3709 sdlperror("set_bg_attr: SDL_SetColorKey");
3710 quit(1);
3711 }
3712 }
3713 #endif // USE_FLASH
3714 }
3715
3716 // seg009:07EB
offset4_rect_add(rect_type far * dest,const rect_type far * source,int d_left,int d_top,int d_right,int d_bottom)3717 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) {
3718 *dest = *source;
3719 dest->left += d_left;
3720 dest->top += d_top;
3721 dest->right += d_right;
3722 dest->bottom += d_bottom;
3723 return dest;
3724 }
3725
3726 // seg009:3AA5
offset2_rect(rect_type far * dest,const rect_type far * source,int delta_x,int delta_y)3727 rect_type far *__pascal offset2_rect(rect_type far *dest,const rect_type far *source,int delta_x,int delta_y) {
3728 dest->top = source->top + delta_y;
3729 dest->left = source->left + delta_x;
3730 dest->bottom = source->bottom + delta_y;
3731 dest->right = source->right + delta_x;
3732 return dest;
3733 }
3734
3735 #ifdef USE_FADE
3736 // seg009:19EF
fade_in_2(surface_type near * source_surface,int which_rows)3737 void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
3738 palette_fade_type far* palette_buffer;
3739 if (graphics_mode == gmMcgaVga) {
3740 palette_buffer = make_pal_buffer_fadein(source_surface, which_rows, 2);
3741 while (fade_in_frame(palette_buffer) == 0) {
3742 process_events(); // modified
3743 do_paused();
3744 }
3745 pal_restore_free_fadein(palette_buffer);
3746 } else {
3747 // ...
3748 }
3749 }
3750
3751 // seg009:1A51
make_pal_buffer_fadein(surface_type * source_surface,int which_rows,int wait_time)3752 palette_fade_type far *__pascal make_pal_buffer_fadein(surface_type *source_surface,int which_rows,int wait_time) {
3753 palette_fade_type far* palette_buffer;
3754 word curr_row;
3755 word var_8;
3756 word curr_row_mask;
3757 palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
3758 palette_buffer->which_rows = which_rows;
3759 palette_buffer->wait_time = wait_time;
3760 palette_buffer->fade_pos = 0x40;
3761 palette_buffer->proc_restore_free = &pal_restore_free_fadein;
3762 palette_buffer->proc_fade_frame = &fade_in_frame;
3763 read_palette_256(palette_buffer->original_pal);
3764 memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
3765 var_8 = 0;
3766 for (curr_row = 0, curr_row_mask = 1; curr_row < 0x10; ++curr_row, curr_row_mask<<=1) {
3767 if (which_rows & curr_row_mask) {
3768 memset_far(palette_buffer->faded_pal + (curr_row<<4), 0, sizeof(rgb_type[0x10]));
3769 set_pal_arr(curr_row<<4, 0x10, NULL, (var_8++&3)==0);
3770 }
3771 }
3772 //method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
3773 // for RGB
3774 //method_5_rect(&screen_rect, 0, 0);
3775 return palette_buffer;
3776 }
3777
3778 // seg009:1B64
pal_restore_free_fadein(palette_fade_type far * palette_buffer)3779 void __pascal far pal_restore_free_fadein(palette_fade_type far *palette_buffer) {
3780 set_pal_256(palette_buffer->original_pal);
3781 free_far(palette_buffer);
3782 // for RGB
3783 method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
3784 }
3785
3786 // seg009:1B88
fade_in_frame(palette_fade_type far * palette_buffer)3787 int __pascal far fade_in_frame(palette_fade_type far *palette_buffer) {
3788 rgb_type* faded_pal_ptr;
3789 word start;
3790 word column;
3791 rgb_type* original_pal_ptr;
3792 word current_row_mask;
3793 // void* var_12;
3794 /**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
3795
3796 //printf("start ticks = %u\n",SDL_GetTicks());
3797 --palette_buffer->fade_pos;
3798 for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
3799 if (palette_buffer->which_rows & current_row_mask) {
3800 //var_12 = palette_buffer->
3801 original_pal_ptr = palette_buffer->original_pal + start;
3802 faded_pal_ptr = palette_buffer->faded_pal + start;
3803 for (column = 0; column<0x10; ++column) {
3804 if (original_pal_ptr[column].r > palette_buffer->fade_pos) {
3805 ++faded_pal_ptr[column].r;
3806 }
3807 if (original_pal_ptr[column].g > palette_buffer->fade_pos) {
3808 ++faded_pal_ptr[column].g;
3809 }
3810 if (original_pal_ptr[column].b > palette_buffer->fade_pos) {
3811 ++faded_pal_ptr[column].b;
3812 }
3813 }
3814 }
3815 }
3816 column = 0;
3817 for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
3818 if (palette_buffer->which_rows & current_row_mask) {
3819 set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
3820 }
3821 }
3822
3823 int h = offscreen_surface->h;
3824 if (SDL_LockSurface(onscreen_surface_) != 0) {
3825 sdlperror("fade_in_frame: SDL_LockSurface");
3826 quit(1);
3827 }
3828 if (SDL_LockSurface(offscreen_surface) != 0) {
3829 sdlperror("fade_in_frame: SDL_LockSurface");
3830 quit(1);
3831 }
3832 int y,x;
3833 int on_stride = onscreen_surface_->pitch;
3834 int off_stride = offscreen_surface->pitch;
3835 int fade_pos = palette_buffer->fade_pos;
3836 for (y = 0; y < h; ++y) {
3837 byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
3838 byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
3839 for (x = 0; x < on_stride; ++x) {
3840 //if (*off_pixel_ptr > palette_buffer->fade_pos) *pixel_ptr += 4;
3841 int v = *off_pixel_ptr - fade_pos*4;
3842 if (v<0) v=0;
3843 *on_pixel_ptr = v;
3844 ++on_pixel_ptr; ++off_pixel_ptr;
3845 }
3846 }
3847 SDL_UnlockSurface(onscreen_surface_);
3848 SDL_UnlockSurface(offscreen_surface);
3849
3850 //SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0); // debug
3851
3852 /**/do_simple_wait(1); // can interrupt fading of cutscene
3853 //do_wait(timer_1); // can interrupt fading of main title
3854 //printf("end ticks = %u\n",SDL_GetTicks());
3855 return palette_buffer->fade_pos == 0;
3856 }
3857
3858 // seg009:1CC9
fade_out_2(int rows)3859 void __pascal far fade_out_2(int rows) {
3860 palette_fade_type far *palette_buffer;
3861 if (graphics_mode == gmMcgaVga) {
3862 palette_buffer = make_pal_buffer_fadeout(rows, 2);
3863 while (fade_out_frame(palette_buffer) == 0) {
3864 process_events(); // modified
3865 do_paused();
3866 }
3867 pal_restore_free_fadeout(palette_buffer);
3868 } else {
3869 // ...
3870 }
3871 }
3872
3873 // seg009:1D28
make_pal_buffer_fadeout(int which_rows,int wait_time)3874 palette_fade_type far *__pascal make_pal_buffer_fadeout(int which_rows,int wait_time) {
3875 palette_fade_type far *palette_buffer;
3876 palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
3877 palette_buffer->which_rows = which_rows;
3878 palette_buffer->wait_time = wait_time;
3879 palette_buffer->fade_pos = 00; // modified
3880 palette_buffer->proc_restore_free = &pal_restore_free_fadeout;
3881 palette_buffer->proc_fade_frame = &fade_out_frame;
3882 read_palette_256(palette_buffer->original_pal);
3883 memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
3884 // for RGB
3885 method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
3886 return palette_buffer;
3887 }
3888
3889 // seg009:1DAF
pal_restore_free_fadeout(palette_fade_type far * palette_buffer)3890 void __pascal far pal_restore_free_fadeout(palette_fade_type far *palette_buffer) {
3891 surface_type* surface;
3892 surface = current_target_surface;
3893 current_target_surface = onscreen_surface_;
3894 draw_rect(&screen_rect, 0);
3895 current_target_surface = surface;
3896 set_pal_256(palette_buffer->original_pal);
3897 free_far(palette_buffer);
3898 // for RGB
3899 method_5_rect(&screen_rect, 0, 0);
3900 }
3901
3902 // seg009:1DF7
fade_out_frame(palette_fade_type far * palette_buffer)3903 int __pascal far fade_out_frame(palette_fade_type far *palette_buffer) {
3904 rgb_type* faded_pal_ptr;
3905 word start;
3906 word var_8;
3907 word column;
3908 word current_row_mask;
3909 byte* curr_color_ptr;
3910 var_8 = 1;
3911 ++palette_buffer->fade_pos; // modified
3912 /**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
3913 for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
3914 if (palette_buffer->which_rows & current_row_mask) {
3915 //var_12 = palette_buffer->
3916 //original_pal_ptr = palette_buffer->original_pal + start;
3917 faded_pal_ptr = palette_buffer->faded_pal + start;
3918 for (column = 0; column<0x10; ++column) {
3919 curr_color_ptr = &faded_pal_ptr[column].r;
3920 if (*curr_color_ptr != 0) {
3921 --*curr_color_ptr;
3922 var_8 = 0;
3923 }
3924 curr_color_ptr = &faded_pal_ptr[column].g;
3925 if (*curr_color_ptr != 0) {
3926 --*curr_color_ptr;
3927 var_8 = 0;
3928 }
3929 curr_color_ptr = &faded_pal_ptr[column].b;
3930 if (*curr_color_ptr != 0) {
3931 --*curr_color_ptr;
3932 var_8 = 0;
3933 }
3934 }
3935 }
3936 }
3937 column = 0;
3938 for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
3939 if (palette_buffer->which_rows & current_row_mask) {
3940 set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
3941 }
3942 }
3943
3944 int h = offscreen_surface->h;
3945 if (SDL_LockSurface(onscreen_surface_) != 0) {
3946 sdlperror("fade_out_frame: SDL_LockSurface");
3947 quit(1);
3948 }
3949 if (SDL_LockSurface(offscreen_surface) != 0) {
3950 sdlperror("fade_out_frame: SDL_LockSurface");
3951 quit(1);
3952 }
3953 int y,x;
3954 int on_stride = onscreen_surface_->pitch;
3955 int off_stride = offscreen_surface->pitch;
3956 int fade_pos = palette_buffer->fade_pos;
3957 for (y = 0; y < h; ++y) {
3958 byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
3959 byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
3960 for (x = 0; x < on_stride; ++x) {
3961 //if (*pixel_ptr >= 4) *pixel_ptr -= 4;
3962 int v = *off_pixel_ptr - fade_pos*4;
3963 if (v<0) v=0;
3964 *on_pixel_ptr = v;
3965 ++on_pixel_ptr; ++off_pixel_ptr;
3966 }
3967 }
3968 SDL_UnlockSurface(onscreen_surface_);
3969 SDL_UnlockSurface(offscreen_surface);
3970
3971 do_simple_wait(timer_1); // can interrupt fading of cutscene
3972 return var_8;
3973 }
3974
3975 // seg009:1F28
read_palette_256(rgb_type far * target)3976 void __pascal far read_palette_256(rgb_type far *target) {
3977 int i;
3978 for (i = 0; i < 256; ++i) {
3979 target[i] = palette[i];
3980 }
3981 }
3982
3983 // seg009:1F5E
set_pal_256(rgb_type far * source)3984 void __pascal far set_pal_256(rgb_type far *source) {
3985 int i;
3986 for (i = 0; i < 256; ++i) {
3987 palette[i] = source[i];
3988 }
3989 }
3990 #endif // USE_FADE
3991
set_chtab_palette(chtab_type * chtab,byte * colors,int n_colors)3992 void set_chtab_palette(chtab_type* chtab, byte* colors, int n_colors) {
3993 if (chtab != NULL) {
3994 SDL_Color* scolors = (SDL_Color*) malloc(n_colors*sizeof(SDL_Color));
3995 int i;
3996 //printf("scolors\n",i);
3997 for (i = 0; i < n_colors; ++i) {
3998 //printf("i=%d\n",i);
3999 scolors[i].r = *colors << 2; ++colors;
4000 scolors[i].g = *colors << 2; ++colors;
4001 scolors[i].b = *colors << 2; ++colors;
4002 scolors[i].a = SDL_ALPHA_OPAQUE; // the SDL2 SDL_Color struct has an alpha component
4003 }
4004
4005 // Color 0 of the palette data is not used, it is replaced by the background color.
4006 // Needed for correct alternate colors (v1.3) of level 8.
4007 scolors[0].r = scolors[0].g = scolors[0].b = 0;
4008 scolors[0].a = SDL_ALPHA_TRANSPARENT;
4009
4010 //printf("setcolors\n",i);
4011 for (i = 0; i < chtab->n_images; ++i) {
4012 //printf("i=%d\n",i);
4013 image_type* current_image = chtab->images[i];
4014 if (current_image != NULL) {
4015
4016 int n_colors_to_be_set = n_colors;
4017 SDL_Palette* current_palette = current_image->format->palette;
4018
4019 // one of the guard images (i=25) is only a single transparent pixel
4020 // this caused SDL_SetPaletteColors to fail, I think because that palette contains only 2 colors
4021 if (current_palette->ncolors < n_colors_to_be_set)
4022 n_colors_to_be_set = current_palette->ncolors;
4023 if (SDL_SetPaletteColors(current_palette, scolors, 0, n_colors_to_be_set) != 0) {
4024 sdlperror("set_chtab_palette: SDL_SetPaletteColors");
4025 quit(1);
4026 }
4027 }
4028 }
4029 free(scolors);
4030 }
4031 }
4032
has_timer_stopped(int timer_index)4033 int has_timer_stopped(int timer_index) {
4034 #ifdef USE_COMPAT_TIMER
4035 return wait_time[timer_index] == 0;
4036 #else
4037 #ifdef USE_REPLAY
4038 if ((replaying && skipping_replay) || is_validate_mode) return true;
4039 #endif
4040 Uint64 current_counter = SDL_GetPerformanceCounter();
4041 int ticks_elapsed = (int)((current_counter / perf_counters_per_tick) - (timer_last_counter[timer_index] / perf_counters_per_tick));
4042 int overshoot = ticks_elapsed - wait_time[timer_index];
4043 if (overshoot >= 0) {
4044 // float milliseconds_elapsed = (current_counter - timer_last_counter[timer_index]) * milliseconds_per_counter;
4045 // printf("timer %d: frametime (ms) = %5.1f fps = %.1f timer ticks elapsed = %d\n", timer_index, milliseconds_elapsed, 1000.0f / milliseconds_elapsed, ticks_elapsed);
4046 if (overshoot > 0 && overshoot <= 3) {
4047 current_counter -= overshoot * perf_counters_per_tick;
4048 }
4049 timer_last_counter[timer_index] = current_counter;
4050 return true;
4051 } else {
4052 return false;
4053 }
4054 #endif
4055 }
4056