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(&copyprot_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_, &copyprot_dialog->peel_rect, &copyprot_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, &copyprot_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