/*
SDLPoP, a port/conversion of the DOS game Prince of Persia.
Copyright (C) 2013-2018 Dávid Nagy
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
The authors of this program may be contacted at http://forum.princed.org
*/
#include "common.h"
#include <time.h>
#include <errno.h>
// Most functions in this file are different from those in the original game.
void sdlperror(const char* header) {
const char* error = SDL_GetError();
printf("%s: %s\n",header
,error
);
//quit(1);
}
char exe_dir[POP_MAX_PATH] = ".";
bool found_exe_dir = false;
void find_exe_dir() {
if (found_exe_dir) return;
#if 0
strncpy(exe_dir
, g_argv
[0], sizeof(exe_dir
));
char* last_slash = NULL;
char* pos = exe_dir;
for (char c = *pos; c != '\0'; c = *(++pos)) {
if (c == '/' || c == '\\') {
last_slash = pos;
}
}
if (last_slash != NULL) {
*last_slash = '\0';
}
#else
extern int _NSGetExecutablePath (char* buf, uint32_t* bufsize);
uint32_t bufsize = sizeof (exe_dir);
char *pos;
_NSGetExecutablePath (exe_dir, &bufsize);
if (pos != NULL) *pos = 0;
if (pos != NULL) *pos = 0;
strcat (exe_dir
, "/Resources");
#endif
found_exe_dir = true;
}
static inline bool file_exists(const char* filename) {
return (access(filename, F_OK) != -1);
}
const char* locate_file_(const char* filename, char* path_buffer, int buffer_size) {
if(file_exists(filename)) {
return filename;
} else {
// If failed, it may be that SDLPoP is being run from the wrong different working directory.
// We can try to rescue the situation by loading from the directory of the executable.
find_exe_dir();
snprintf(path_buffer
, buffer_size
, "%s/%s", exe_dir
, filename
);
return (const char*) path_buffer;
}
}
dat_type* dat_chain_ptr = NULL;
int last_key_scancode;
char last_text_input;
// seg009:000D
int __pascal far read_key() {
// stub
int key = last_key_scancode;
last_key_scancode = 0;
return key;
}
// seg009:019A
void __pascal far clear_kbd_buf() {
// stub
last_key_scancode = 0;
last_text_input = 0;
}
// seg009:040A
word __pascal far prandom(word max) {
if (!seed_was_init) {
// init from current time
random_seed
= time(NULL
);
seed_was_init = 1;
}
random_seed = random_seed * 214013 + 2531011;
return (random_seed >> 16) % (max + 1);
}
// seg009:0467
int __pascal far round_xpos_to_byte(int xpos,int round_direction) {
// stub
return xpos;
}
// seg009:0C7A
void __pascal far quit(int exit_code) {
restore_stuff();
}
// seg009:0C90
void __pascal far restore_stuff() {
SDL_Quit();
}
// seg009:0E33
int __pascal far key_test_quit() {
word key;
key = read_key();
if (key == (SDL_SCANCODE_Q | WITH_CTRL)) { // ctrl-q
#ifdef USE_REPLAY
if (recording) save_recorded_replay();
#endif
quit(0);
}
return key;
}
// seg009:0E54
const char* __pascal far check_param(const char *param) {
// stub
short arg_index;
for (arg_index = 1; arg_index < g_argc; ++arg_index) {
char* curr_arg = g_argv[arg_index];
// Filenames (e.g. replays) should never be a valid 'normal' param so we should skip these to prevent conflicts.
// We can lazily distinguish filenames from non-filenames by checking whether they have a dot in them.
// (Assumption: all relevant files, e.g. replay files, have some file extension anyway)
if (strchr(curr_arg
, '.') != NULL
) {
continue;
}
// List of params that expect a specifier ('sub-') arg directly after it (e.g. the mod's name, after "mod" arg)
// Such sub-args may conflict with the normal params (so, we should 'skip over' them)
static const char params_with_one_subparam[][16] = { "mod", "validate", /*...*/ };
bool curr_arg_has_one_subparam = false;
int i;
for (i = 0; i < COUNT(params_with_one_subparam); ++i) {
if (strncasecmp
(curr_arg
, params_with_one_subparam
[i
], strlen(params_with_one_subparam
[i
])) == 0) {
curr_arg_has_one_subparam = true;
break;
}
}
if (curr_arg_has_one_subparam) {
// Found an arg that has one sub-param, so we want to:
// 1: skip over the next arg (if we are NOT checking for this specific param)
// 2: return a pointer below to the SUB-arg (if we ARE checking for this specific param)
++arg_index;
if (!(arg_index < g_argc)) return NULL; // not enough arguments
}
if (/*strnicmp*/strncasecmp
(curr_arg
, param
, strlen(param
)) == 0) {
return g_argv[arg_index];
}
}
return NULL;
}
// seg009:0EDF
int __pascal far pop_wait
(int timer_index
,int time) {
start_timer
(timer_index
, time);
return do_wait(timer_index);
}
// S_ISREG may not be defined under MSVC
#if defined(_MSC_VER) && !defined(S_ISREG)
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#endif
static FILE* open_dat_from_root_or_data_dir(const char* filename) {
FILE* fp = NULL;
fp
= fopen(filename
, "rb");
// if failed, try if the DAT file can be opened in the data/ directory, instead of the main folder
if (fp == NULL) {
char data_path[POP_MAX_PATH];
snprintf(data_path
, sizeof(data_path
), "data/%s", filename
);
if (!file_exists(data_path)) {
find_exe_dir();
snprintf(data_path
, sizeof(data_path
), "%s/data/%s", exe_dir
, filename
);
}
// verify that this is a regular file and not a directory (otherwise, don't open)
struct stat path_stat;
stat(data_path, &path_stat);
if (S_ISREG(path_stat.st_mode)) {
fp
= fopen(data_path
, "rb");
}
}
return fp;
}
// seg009:0F58
dat_type *__pascal open_dat(const char *filename,int drive) {
FILE* fp = NULL;
if (!use_custom_levelset) {
fp = open_dat_from_root_or_data_dir(filename);
}
else {
char filename_mod[POP_MAX_PATH];
// before checking the root directory, first try mods/MODNAME/
snprintf(filename_mod
, sizeof(filename_mod
), "mods/%s/%s", levelset_name
, filename
);
fp
= fopen(filename_mod
, "rb");
if (fp == NULL) {
fp = open_dat_from_root_or_data_dir(filename);
}
}
dat_header_type dat_header;
dat_table_type* dat_table = NULL;
dat_type
* pointer
= (dat_type
*) calloc(1, sizeof(dat_type
));
strncpy(pointer
->filename
, filename
, sizeof(pointer
->filename
));
pointer->next_dat = dat_chain_ptr;
dat_chain_ptr = pointer;
if (fp != NULL) {
if (fread(&dat_header
, 6, 1, fp
) != 1)
goto failed;
dat_table
= (dat_table_type
*) malloc(dat_header.
table_size);
if (dat_table == NULL ||
fseek(fp
, dat_header.
table_offset, SEEK_SET
) ||
fread(dat_table
, dat_header.
table_size, 1, fp
) != 1)
goto failed;
pointer->handle = fp;
pointer->dat_table = dat_table;
}
out:
// stub
return pointer;
failed:
if (fp)
if (dat_table)
goto out;
}
// seg009:9CAC
void __pascal far set_loaded_palette(dat_pal_type far *palette_ptr) {
int dest_row, dest_index, source_row;
for (dest_row = dest_index = source_row = 0; dest_row < 16; ++dest_row, dest_index += 0x10) {
if (palette_ptr->row_bits & (1 << dest_row)) {
set_pal_arr(dest_index, 16, palette_ptr->vga + source_row*0x10, 1);
++source_row;
}
}
}
// data:3356
word chtab_palette_bits = 1;
// seg009:104E
chtab_type* __pascal load_sprites_from_file(int resource,int palette_bits, int quit_on_error) {
int i;
int n_images = 0;
//int has_palette_bits = 1;
chtab_type* chtab = NULL;
dat_shpl_type* shpl = (dat_shpl_type*) load_from_opendats_alloc(resource, "pal", NULL, NULL);
if (shpl == NULL) {
printf("Can't load sprites from resource %d.\n", resource
);
//if (quit_on_error) quit(1);
return NULL;
}
dat_pal_type* pal_ptr = &shpl->palette;
if (graphics_mode == gmMcgaVga) {
if (palette_bits == 0) {
/*
palette_bits = add_palette_bits(pal_ptr->n_colors);
if (palette_bits == 0) {
quit(1);
}
*/
} else {
chtab_palette_bits |= palette_bits;
//has_palette_bits = 0;
}
pal_ptr->row_bits = palette_bits;
}
n_images = shpl->n_images;
size_t alloc_size = sizeof(chtab_type) + sizeof(void far *) * n_images;
chtab
= (chtab_type
*) malloc(alloc_size
);
chtab->n_images = n_images;
for (i = 1; i <= n_images; i++) {
SDL_Surface* image = load_image(resource + i, pal_ptr);
// if (image == NULL) printf(" failed");
if (image != NULL) {
if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
sdlperror("SDL_SetAlpha");
quit(1);
}
/*
if (SDL_SetColorKey(image, SDL_SRCCOLORKEY, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
*/
}
// printf("\n");
chtab->images[i-1] = image;
}
set_loaded_palette(pal_ptr);
return chtab;
}
// seg009:11A8
void __pascal far free_chtab(chtab_type *chtab_ptr) {
image_type far* curr_image;
word id;
word n_images;
if (graphics_mode == gmMcgaVga && chtab_ptr->has_palette_bits) {
chtab_palette_bits &= ~ chtab_ptr->chtab_palette_bits;
}
n_images = chtab_ptr->n_images;
for (id = 0; id < n_images; ++id) {
curr_image = chtab_ptr->images[id];
if (curr_image) {
SDL_FreeSurface(curr_image);
}
}
free_near(chtab_ptr);
}
// seg009:8CE6
void __pascal far decompress_rle_lr(byte far *destination,const byte far *source,int dest_length) {
const byte* src_pos = source;
byte* dest_pos = destination;
short rem_length = dest_length;
while (rem_length) {
sbyte count = *(src_pos++);
if (count >= 0) { // copy
++count;
do {
*(dest_pos++) = *(src_pos++);
--rem_length;
} while (--count && rem_length);
} else { // repeat
byte al = *(src_pos++);
count = -count;
do {
*(dest_pos++) = al;
--rem_length;
} while (--count && rem_length);
}
}
}
// seg009:8D1C
void __pascal far decompress_rle_ud(byte far *destination,const byte far *source,int dest_length,int width,int height) {
short rem_height = height;
const byte* src_pos = source;
byte* dest_pos = destination;
short rem_length = dest_length;
--dest_length;
--width;
while (rem_length) {
sbyte count = *(src_pos++);
if (count >= 0) { // copy
++count;
do {
*(dest_pos++) = *(src_pos++);
dest_pos += width;
if (--rem_height == 0) {
dest_pos -= dest_length;
rem_height = height;
}
--rem_length;
} while (--count && rem_length);
} else { // repeat
byte al = *(src_pos++);
count = -count;
do {
*(dest_pos++) = al;
dest_pos += width;
if (--rem_height == 0) {
dest_pos -= dest_length;
rem_height = height;
}
--rem_length;
} while (--count && rem_length);
}
}
}
// seg009:90FA
byte far* __pascal far decompress_lzg_lr(byte far *dest,const byte far *source,int dest_length) {
byte* window = (byte*) malloc_near(0x400);
if (window == NULL) return NULL;
byte* window_pos = window + 0x400 - 0x42; // bx
short remaining = dest_length; // cx
byte* window_end = window + 0x400; // dx
const byte* source_pos = source;
byte* dest_pos = dest;
word mask = 0;
do {
mask >>= 1;
if ((mask & 0xFF00) == 0) {
mask = *(source_pos++) | 0xFF00;
}
if (mask & 1) {
*(window_pos++) = *(dest_pos++) = *(source_pos++);
if (window_pos >= window_end) window_pos = window;
--remaining;
} else {
word copy_info = *(source_pos++);
copy_info = (copy_info << 8) | *(source_pos++);
byte* copy_source = window + (copy_info & 0x3FF);
byte copy_length = (copy_info >> 10) + 3;
do {
*(window_pos++) = *(dest_pos++) = *(copy_source++);
if (copy_source >= window_end) copy_source = window;
if (window_pos >= window_end) window_pos = window;
} while (--remaining && --copy_length);
}
} while (remaining);
// end:
return dest;
}
// seg009:91AD
byte far* __pascal far decompress_lzg_ud(byte far *dest,const byte far *source,int dest_length,int stride,int height) {
byte* window = (byte*) malloc_near(0x400);
if (window == NULL) return NULL;
byte* window_pos = window + 0x400 - 0x42; // bx
short remaining = height; // cx
byte* window_end = window + 0x400; // dx
const byte* source_pos = source;
byte* dest_pos = dest;
word mask = 0;
short var_6 = dest_length - 1;
do {
mask >>= 1;
if ((mask & 0xFF00) == 0) {
mask = *(source_pos++) | 0xFF00;
}
if (mask & 1) {
*(window_pos++) = *dest_pos = *(source_pos++);
dest_pos += stride;
if (--remaining == 0) {
dest_pos -= var_6;
remaining = height;
}
if (window_pos >= window_end) window_pos = window;
--dest_length;
} else {
word copy_info = *(source_pos++);
copy_info = (copy_info << 8) | *(source_pos++);
byte* copy_source = window + (copy_info & 0x3FF);
byte copy_length = (copy_info >> 10) + 3;
do {
*(window_pos++) = *dest_pos = *(copy_source++);
dest_pos += stride;
if (--remaining == 0) {
dest_pos -= var_6;
remaining = height;
}
if (copy_source >= window_end) copy_source = window;
if (window_pos >= window_end) window_pos = window;
} while (--dest_length && --copy_length);
}
} while (dest_length);
// end:
return dest;
}
// seg009:938E
void __pascal far decompr_img(byte far *dest,const image_data_type far *source,int decomp_size,int cmeth, int stride) {
switch (cmeth) {
case 0: // RAW left-to-right
memcpy_far(dest, &source->data, decomp_size);
break;
case 1: // RLE left-to-right
decompress_rle_lr(dest, source->data, decomp_size);
break;
case 2: // RLE up-to-down
decompress_rle_ud(dest, source->data, decomp_size, stride, source->height);
break;
case 3: // LZG left-to-right
decompress_lzg_lr(dest, source->data, decomp_size);
break;
case 4: // LZG up-to-down
decompress_lzg_ud(dest, source->data, decomp_size, stride, source->height);
break;
}
}
int calc_stride(image_data_type* image_data) {
int width = image_data->width;
int flags = image_data->flags;
int depth = ((flags >> 12) & 3) + 1;
return (depth * width + 7) / 8;
}
byte* conv_to_8bpp(byte* in_data, int width, int height, int stride, int depth) {
byte
* out_data
= (byte
*) malloc(width
* height
);
int y, x_pixel, x_byte, pixel_in_byte;
int pixels_per_byte = 8 / depth;
int mask = (1 << depth) - 1;
for (y = 0; y < height; ++y) {
byte* in_pos = in_data + y*stride;
byte* out_pos = out_data + y*width;
for (x_pixel = x_byte = 0; x_byte < stride; ++x_byte) {
byte v = *in_pos;
int shift = 8;
for (pixel_in_byte = 0; pixel_in_byte < pixels_per_byte && x_pixel < width; ++pixel_in_byte, ++x_pixel) {
shift -= depth;
*out_pos = (v >> shift) & mask;
++out_pos;
}
++in_pos;
}
}
return out_data;
}
image_type* decode_image(image_data_type* image_data, dat_pal_type* palette) {
int height = image_data->height;
if (height == 0) return NULL;
int width = image_data->width;
int flags = image_data->flags;
int depth = ((flags >> 12) & 3) + 1;
int cmeth = (flags >> 8) & 0x0F;
int stride = calc_stride(image_data);
int dest_size = stride * height;
byte
* dest
= (byte
*) malloc(dest_size
);
decompr_img(dest, image_data, dest_size, cmeth, stride);
byte* image_8bpp = conv_to_8bpp(dest, width, height, stride, depth);
image_type* image = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
if (image == NULL) {
sdlperror("SDL_CreateRGBSurface");
quit(1);
}
if (SDL_LockSurface(image) != 0) {
sdlperror("SDL_LockSurface");
}
int y;
for (y = 0; y < height; ++y) {
// fill image with data
memcpy((byte
*)image
->pixels
+ y
*image
->pitch
, image_8bpp
+ y
*width
, width
);
}
SDL_UnlockSurface(image);
free(image_8bpp
); image_8bpp
= NULL
;
SDL_Color colors[16];
int i;
for (i = 0; i < 16; ++i) {
colors[i].r = palette->vga[i].r << 2;
colors[i].g = palette->vga[i].g << 2;
colors[i].b = palette->vga[i].b << 2;
colors[i].a = SDL_ALPHA_OPAQUE; // SDL2's SDL_Color has a fourth alpha component
}
colors[0].a = SDL_ALPHA_TRANSPARENT;
SDL_SetPaletteColors(image->format->palette, colors, 0, 16); // SDL_SetColors = deprecated
return image;
}
// seg009:121A
image_type* far __pascal far load_image(int resource_id, dat_pal_type* palette) {
// stub
data_location result;
int size;
void* image_data = load_from_opendats_alloc(resource_id, "png", &result, &size);
image_type* image = NULL;
switch (result) {
case data_none:
return NULL;
break;
case data_DAT: { // DAT
image = decode_image((image_data_type*) image_data, palette);
} break;
case data_directory: { // directory
SDL_RWops* rw = SDL_RWFromConstMem(image_data, size);
if (rw == NULL) {
sdlperror("SDL_RWFromConstMem");
return NULL;
}
image = IMG_LoadPNG_RW(rw);
if (SDL_RWclose(rw) != 0) {
sdlperror("SDL_RWclose");
}
} break;
}
if (image_data
!= NULL
) free(image_data
);
if (image != NULL) {
// should immediately start using the onscreen pixel format, so conversion will not be needed
if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) { //sdl 1.2: SDL_SRCCOLORKEY
sdlperror("SDL_SetColorKey");
quit(1);
}
// printf("bpp = %d\n", image->format->BitsPerPixel);
if (SDL_SetSurfaceAlphaMod(image, 0) != 0) { //sdl 1.2: SDL_SetAlpha removed
sdlperror("SDL_SetAlpha");
quit(1);
}
// image_type* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
// if (!colored_image) {
// sdlperror("SDL_ConvertSurfaceFormat");
// quit(1);
// }
// SDL_FreeSurface(image);
// image = colored_image;
}
return image;
}
// seg009:13C4
void __pascal far draw_image_transp(image_type far *image,image_type far *mask,int xpos,int ypos) {
if (graphics_mode == gmMcgaVga) {
draw_image_transp_vga(image, xpos, ypos);
} else {
// ...
}
}
// seg009:157E
int __pascal far set_joy_mode() {
// stub
if (SDL_NumJoysticks() < 1) {
is_joyst_mode = 0;
} else {
if (SDL_IsGameController(0)) {
sdl_controller_ = SDL_GameControllerOpen(0);
if (sdl_controller_ == NULL) {
is_joyst_mode = 0;
} else {
is_joyst_mode = 1;
}
}
// We have a joystick connected, but it's NOT compatible with the SDL_GameController
// interface, so we resort to the classic SDL_Joystick interface instead
else {
sdl_joystick_ = SDL_JoystickOpen(0);
is_joyst_mode = 1;
using_sdl_joystick_interface = 1;
}
}
if (enable_controller_rumble && is_joyst_mode) {
sdl_haptic = SDL_HapticOpen(0);
SDL_HapticRumbleInit(sdl_haptic); // initialize the device for simple rumble
} else {
sdl_haptic = NULL;
}
is_keyboard_mode = !is_joyst_mode;
return is_joyst_mode;
}
// seg009:178B
surface_type far *__pascal make_offscreen_buffer(const rect_type far *rect) {
// stub
#ifndef USE_ALPHA
// Bit order matches onscreen buffer, good for fading.
return SDL_CreateRGBSurface(0, rect->right, rect->bottom, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0); //RGB888 (little endian)
#else
return SDL_CreateRGBSurface(0, rect->right, rect->bottom, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24);
#endif
//return surface;
}
// seg009:17BD
void __pascal far free_surface(surface_type *surface) {
SDL_FreeSurface(surface);
}
// seg009:17EA
void __pascal far free_peel(peel_type *peel_ptr) {
SDL_FreeSurface(peel_ptr->peel);
}
// seg009:182F
void __pascal far set_hc_pal() {
// stub
if (graphics_mode == gmMcgaVga) {
set_pal_arr(0, 16, vga_palette, 1);
} else {
// ...
}
}
// seg009:2446
void __pascal far flip_not_ega(byte far *memory,int height,int stride) {
byte
* row_buffer
= (byte
*) malloc(stride
);
byte* top_ptr;
byte* bottom_ptr;
bottom_ptr = top_ptr = memory;
bottom_ptr += (height - 1) * stride;
short rem_rows = height >> 1;
do {
memcpy(row_buffer
, top_ptr
, stride
);
memcpy(top_ptr
, bottom_ptr
, stride
);
memcpy(bottom_ptr
, row_buffer
, stride
);
top_ptr += stride;
bottom_ptr -= stride;
--rem_rows;
} while (rem_rows);
}
// seg009:19B1
void __pascal far flip_screen(surface_type far *surface) {
// stub
if (graphics_mode != gmEga) {
if (SDL_LockSurface(surface) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
flip_not_ega((byte*) surface->pixels, surface->h, surface->pitch);
SDL_UnlockSurface(surface);
} else {
// ...
}
}
#ifndef USE_FADE
// seg009:19EF
void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
// stub
method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
request_screen_update();
}
// seg009:1CC9
void __pascal far fade_out_2(int rows) {
// stub
}
#endif // USE_FADE
// seg009:2288
void __pascal far draw_image_transp_vga(image_type far *image,int xpos,int ypos) {
// stub
method_6_blit_img_to_scr(image, xpos, ypos, blitters_10h_transp);
}
#ifdef USE_TEXT
font_type hc_font = {0x01,0xFF, 7,2,1,1, NULL};
textstate_type textstate = {0,0,0,15,&hc_font};
/*const*/ byte hc_font_data[] = {
0x20,0x83,0x07,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0xD2,0x00,0xD8,0x00,0xE5,0x00,
0xEE,0x00,0xFA,0x00,0x07,0x01,0x14,0x01,0x21,0x01,0x2A,0x01,0x37,0x01,0x44,0x01,
0x50,0x01,0x5C,0x01,0x6A,0x01,0x74,0x01,0x81,0x01,0x8E,0x01,0x9B,0x01,0xA8,0x01,
0xB5,0x01,0xC2,0x01,0xCF,0x01,0xDC,0x01,0xE9,0x01,0xF6,0x01,0x03,0x02,0x10,0x02,
0x1C,0x02,0x2A,0x02,0x37,0x02,0x42,0x02,0x4F,0x02,0x5C,0x02,0x69,0x02,0x76,0x02,
0x83,0x02,0x90,0x02,0x9D,0x02,0xAA,0x02,0xB7,0x02,0xC4,0x02,0xD1,0x02,0xDE,0x02,
0xEB,0x02,0xF8,0x02,0x05,0x03,0x12,0x03,0x1F,0x03,0x2C,0x03,0x39,0x03,0x46,0x03,
0x53,0x03,0x60,0x03,0x6D,0x03,0x7A,0x03,0x87,0x03,0x94,0x03,0xA1,0x03,0xAE,0x03,
0xBB,0x03,0xC8,0x03,0xD5,0x03,0xE2,0x03,0xEB,0x03,0xF9,0x03,0x02,0x04,0x0F,0x04,
0x1C,0x04,0x29,0x04,0x36,0x04,0x43,0x04,0x50,0x04,0x5F,0x04,0x6C,0x04,0x79,0x04,
0x88,0x04,0x95,0x04,0xA2,0x04,0xAF,0x04,0xBC,0x04,0xC9,0x04,0xD8,0x04,0xE7,0x04,
0xF4,0x04,0x01,0x05,0x0E,0x05,0x1B,0x05,0x28,0x05,0x35,0x05,0x42,0x05,0x51,0x05,
0x5E,0x05,0x6B,0x05,0x78,0x05,0x85,0x05,0x8D,0x05,0x9A,0x05,0xA7,0x05,0xBB,0x05,
0xD9,0x05,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x02,0x00,0x01,0x00,0xC0,0xC0,
0xC0,0xC0,0xC0,0x00,0xC0,0x03,0x00,0x05,0x00,0x01,0x00,0xD8,0xD8,0xD8,0x06,0x00,
0x07,0x00,0x01,0x00,0x00,0x6C,0xFE,0x6C,0xFE,0x6C,0x07,0x00,0x07,0x00,0x01,0x00,
0x10,0x7C,0xD0,0x7C,0x16,0x7C,0x10,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xC6,0x0C,
0x18,0x30,0x63,0xC3,0x07,0x00,0x08,0x00,0x01,0x00,0x38,0x6C,0x38,0x7A,0xCC,0xCE,
0x7B,0x03,0x00,0x03,0x00,0x01,0x00,0x60,0x60,0xC0,0x07,0x00,0x04,0x00,0x01,0x00,
0x30,0x60,0xC0,0xC0,0xC0,0x60,0x30,0x07,0x00,0x04,0x00,0x01,0x00,0xC0,0x60,0x30,
0x30,0x30,0x60,0xC0,0x06,0x00,0x07,0x00,0x01,0x00,0x00,0x6C,0x38,0xFE,0x38,0x6C,
0x06,0x00,0x06,0x00,0x01,0x00,0x00,0x30,0x30,0xFC,0x30,0x30,0x08,0x00,0x03,0x00,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x04,0x00,0x04,0x00,0x01,0x00,
0x00,0x00,0x00,0xF0,0x07,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,
0xC0,0x07,0x00,0x08,0x00,0x01,0x00,0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x07,0x00,
0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,
0x00,0x30,0x70,0xF0,0x30,0x30,0x30,0xFC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,
0x0C,0x18,0x30,0x60,0xFC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0x0C,0x18,0x0C,
0xCC,0x78,0x07,0x00,0x07,0x00,0x01,0x00,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x07,
0x00,0x06,0x00,0x01,0x00,0xF8,0xC0,0xC0,0xF8,0x0C,0x0C,0xF8,0x07,0x00,0x06,0x00,
0x01,0x00,0x78,0xC0,0xC0,0xF8,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0xFC,
0x0C,0x18,0x30,0x30,0x30,0x30,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0x78,
0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,
0x06,0x00,0x02,0x00,0x01,0x00,0x00,0xC0,0xC0,0x00,0xC0,0xC0,0x08,0x00,0x03,0x00,
0x01,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0xC0,0x07,0x00,0x05,0x00,0x01,0x00,
0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x05,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0xF0,
0x00,0xF0,0x07,0x00,0x05,0x00,0x01,0x00,0xC0,0x60,0x30,0x18,0x30,0x60,0xC0,0x07,
0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0x0C,0x18,0x30,0x00,0x30,0x07,0x00,0x06,0x00,
0x01,0x00,0x78,0xCC,0xDC,0xDC,0xD8,0xC0,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x78,
0xCC,0xCC,0xFC,0xCC,0xCC,0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xF8,
0xCC,0xCC,0xF8,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,0xC0,0xC0,0xCC,0x78,
0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0xCC,0xF8,0x07,0x00,0x05,
0x00,0x01,0x00,0xF8,0xC0,0xC0,0xF0,0xC0,0xC0,0xF8,0x07,0x00,0x05,0x00,0x01,0x00,
0xF8,0xC0,0xC0,0xF0,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,
0xDC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xCC,0xFC,0xCC,0xCC,
0xCC,0x07,0x00,0x04,0x00,0x01,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0xF0,0x07,0x00,
0x06,0x00,0x01,0x00,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0x78,0x07,0x00,0x07,0x00,0x01,
0x00,0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x07,0x00,0x05,0x00,0x01,0x00,0xC0,0xC0,
0xC0,0xC0,0xC0,0xC0,0xF8,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xE7,0xFF,0xDB,0xC3,
0xC3,0xC3,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xEC,0xFC,0xDC,0xCC,0xCC,0x07,
0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x07,0x00,0x06,0x00,
0x01,0x00,0xF8,0xCC,0xCC,0xF8,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x78,
0xCC,0xCC,0xCC,0xCC,0xD8,0x6C,0x07,0x00,0x06,0x00,0x01,0x00,0xF8,0xCC,0xCC,0xF8,
0xD8,0xCC,0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0x78,0xCC,0xC0,0x78,0x0C,0xCC,0x78,
0x07,0x00,0x06,0x00,0x01,0x00,0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x07,0x00,0x06,
0x00,0x01,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,0x00,
0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x07,0x00,0x08,0x00,0x01,0x00,0xC3,0xC3,0xC3,
0xDB,0xFF,0xE7,0xC3,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0x78,0x30,0x78,0xCC,
0xCC,0x07,0x00,0x06,0x00,0x01,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x07,0x00,
0x08,0x00,0x01,0x00,0xFF,0x06,0x0C,0x18,0x30,0x60,0xFF,0x07,0x00,0x04,0x00,0x01,
0x00,0xF0,0xC0,0xC0,0xC0,0xC0,0xC0,0xF0,0x07,0x00,0x08,0x00,0x01,0x00,0xC0,0x60,
0x30,0x18,0x0C,0x06,0x03,0x07,0x00,0x04,0x00,0x01,0x00,0xF0,0x30,0x30,0x30,0x30,
0x30,0xF0,0x03,0x00,0x06,0x00,0x01,0x00,0x30,0x78,0xCC,0x08,0x00,0x06,0x00,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x04,0x00,0x01,0x00,0xC0,
0x60,0x30,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0x7C,0x07,
0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xF8,0x07,0x00,0x06,0x00,
0x01,0x00,0x00,0x00,0x78,0xCC,0xC0,0xCC,0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x0C,
0x0C,0x7C,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xCC,
0xFC,0xC0,0x7C,0x07,0x00,0x05,0x00,0x01,0x00,0x38,0x60,0xF8,0x60,0x60,0x60,0x60,
0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x7C,0x0C,0x78,0x07,
0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xCC,0x07,0x00,0x02,0x00,
0x01,0x00,0xC0,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0x09,0x00,0x04,0x00,0x01,0x00,0x30,
0x00,0x30,0x30,0x30,0x30,0x30,0x30,0xE0,0x07,0x00,0x06,0x00,0x01,0x00,0xC0,0xC0,
0xCC,0xD8,0xF0,0xD8,0xCC,0x07,0x00,0x02,0x00,0x01,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,
0xC0,0xC0,0x07,0x00,0x08,0x00,0x01,0x00,0x00,0x00,0xFE,0xDB,0xDB,0xDB,0xDB,0x07,
0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0x07,0x00,0x06,0x00,
0x01,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x78,0x09,0x00,0x06,0x00,0x01,0x00,0x00,
0x00,0xF8,0xCC,0xCC,0xCC,0xF8,0xC0,0xC0,0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,
0x78,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,
0xCC,0xC0,0xC0,0xC0,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x78,0xC0,0x78,0x0C,
0xF8,0x07,0x00,0x05,0x00,0x01,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x38,0x07,0x00,
0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x7C,0x07,0x00,0x06,0x00,0x01,
0x00,0x00,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x07,0x00,0x08,0x00,0x01,0x00,0x00,0x00,
0xC3,0xC3,0xDB,0xFF,0x66,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0x78,0x30,
0x78,0xCC,0x09,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,
0x78,0x07,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0xFC,0x18,0x30,0x60,0xFC,0x07,0x00,
0x04,0x00,0x01,0x00,0x30,0x60,0x60,0xC0,0x60,0x60,0x30,0x07,0x00,0x02,0x00,0x01,
0x00,0xC0,0xC0,0xC0,0x00,0xC0,0xC0,0xC0,0x07,0x00,0x04,0x00,0x01,0x00,0xC0,0x60,
0x60,0x30,0x60,0x60,0xC0,0x02,0x00,0x07,0x00,0x01,0x00,0x76,0xDC,0x07,0x00,0x07,
0x00,0x01,0x00,0x00,0x00,0x70,0xC4,0xCC,0x8C,0x38,0x07,0x00,0x07,0x00,0x01,0x00,
0x00,0x06,0x0C,0xD8,0xF0,0xE0,0xC0,0x08,0x00,0x10,0x00,0x02,0x00,0x7F,0xFE,0xCD,
0xC7,0xB5,0xEF,0xB5,0xEF,0x85,0xEF,0xB5,0xEF,0xB4,0x6F,0x08,0x00,0x13,0x00,0x03,
0x00,0x7F,0xFF,0xC0,0xCC,0x46,0xE0,0xB6,0xDA,0xE0,0xBE,0xDA,0xE0,0xBE,0xC6,0xE0,
0xB6,0xDA,0xE0,0xCE,0xDA,0x20,0x7F,0xFF,0xC0,0x08,0x00,0x11,0x00,0x03,0x00,0x7F,
0xFF,0x00,0xC6,0x73,0x80,0xDD,0xAD,0x80,0xCE,0xEF,0x80,0xDF,0x6F,0x80,0xDD,0xAD,
0x80,0xC6,0x73,0x80,0x7F,0xFF,0x00
};
font_type load_font_from_data(/*const*/ rawfont_type* data) {
font_type font;
font.first_char = data->first_char;
font.last_char = data->last_char;
font.height_above_baseline = data->height_above_baseline;
font.height_below_baseline = data->height_below_baseline;
font.space_between_lines = data->space_between_lines;
font.space_between_chars = data->space_between_chars;
int n_chars = font.last_char - font.first_char + 1;
chtab_type
* chtab
= malloc(sizeof(chtab_type
) + sizeof(image_type
* far
) * n_chars
);
int chr,index;
// Make a dummy palette for decode_image().
dat_pal_type dat_pal;
memset(&dat_pal
, 0, sizeof(dat_pal
));
dat_pal.vga[1].r = dat_pal.vga[1].g = dat_pal.vga[1].b = 0x3F; // white
for (index = 0, chr = data->first_char; chr <= data->last_char; ++index, ++chr) {
/*const*/ image_data_type* image_data = (/*const*/ image_data_type*)((/*const*/ byte*)data + data->offsets[index]);
//image_data->flags=0;
if (image_data->height == 0) image_data->height = 1; // HACK: decode_image() returns NULL if height==0.
image_type* image;
chtab->images[index] = image = decode_image(image_data, &dat_pal);
if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
}
font.chtab = chtab;
return font;
}
void load_font() {
// Try to load font from a file.
dat_type* dathandle = open_dat("font", 0);
hc_font.chtab = load_sprites_from_file(1000, 1<<1, 0);
close_dat(dathandle);
if (hc_font.chtab == NULL) {
// Use built-in font.
hc_font = load_font_from_data((/*const*/ rawfont_type*)hc_font_data);
}
}
// seg009:35C5
int __pascal far get_char_width(byte character) {
font_type* font = textstate.ptr_font;
int width = 0;
if (character <= font->last_char && character >= font->first_char) {
image_type* image = font->chtab->images[character - font->first_char];
if (image != NULL) {
width += image->w; //char_ptrs[character - font->first_char]->width;
if (width) width += font->space_between_chars;
}
}
return width;
}
// seg009:3E99
int __pascal far find_linebreak(const char far *text,int length,int break_width,int x_align) {
short curr_line_width; // in pixels
short last_break_pos; // in characters
int curr_char_pos = 0;
last_break_pos = 0;
curr_line_width = 0;
const char* text_pos = text;
while (curr_char_pos < length) {
curr_line_width += get_char_width(*text_pos);
if (curr_line_width <= break_width) {
++curr_char_pos;
char curr_char = *(text_pos++);
if (curr_char == '\n') {
return curr_char_pos;
}
if (curr_char == '-' ||
(x_align <= 0 && (curr_char == ' ' || *text_pos == ' ')) ||
(*text_pos == ' ' && curr_char == ' ')
) {
// May break here.
last_break_pos = curr_char_pos;
}
} else {
if (last_break_pos == 0) {
// If the first word is wider than break_width then break it.
return curr_char_pos;
} else {
// Otherwise break at the last space.
return last_break_pos;
}
}
}
return curr_char_pos;
}
// seg009:403F
int __pascal far get_line_width(const char far *text,int length) {
int width = 0;
const char* text_pos = text;
while (--length >= 0) {
width += get_char_width(*(text_pos++));
}
return width;
}
// seg009:3706
int __pascal far draw_text_character(byte character) {
//printf("going to do draw_text_character...\n");
font_type* font = textstate.ptr_font;
int width = 0;
if (character <= font->last_char && character >= font->first_char) {
image_type* image = font->chtab->images[character - font->first_char]; //char_ptrs[character - font->first_char];
if (image != NULL) {
method_3_blit_mono(image, textstate.current_x, textstate.current_y - font->height_above_baseline, textstate.textblit, textstate.textcolor);
width = font->space_between_chars + image->w;
}
}
textstate.current_x += width;
return width;
}
// seg009:377F
int __pascal far draw_text_line(const char far *text,int length) {
//hide_cursor();
int width = 0;
const char* text_pos = text;
while (--length >= 0) {
width += draw_text_character(*(text_pos++));
}
//show_cursor();
return width;
}
// seg009:3755
int __pascal far draw_cstring(const char far *string) {
//hide_cursor();
int width = 0;
const char* text_pos = string;
while (*text_pos) {
width += draw_text_character(*(text_pos++));
}
//show_cursor();
request_screen_update();
return width;
}
// seg009:3F01
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) {
//printf("going to do draw_text()...\n");
short rect_top;
short rect_height;
short rect_width;
//textinfo_type var_C;
short num_lines;
short font_line_distance;
//hide_cursor();
//get_textinfo(&var_C);
set_clip_rect(rect_ptr);
rect_width = rect_ptr->right - rect_ptr->left;
rect_top = rect_ptr->top;
rect_height = rect_ptr->bottom - rect_ptr->top;
num_lines = 0;
int rem_length = length;
const char* line_start = text;
#define MAX_LINES 100
const char* line_starts[MAX_LINES];
int line_lengths[MAX_LINES];
do {
int line_length = find_linebreak(line_start, rem_length, rect_width, x_align);
if (line_length == 0) break;
if (num_lines >= MAX_LINES) {
//... ERROR!
printf("draw_text(): Too many lines!\n");
quit(1);
}
line_starts[num_lines] = line_start;
line_lengths[num_lines] = line_length;
++num_lines;
line_start += line_length;
rem_length -= line_length;
} while(rem_length);
font_type* font = textstate.ptr_font;
font_line_distance = font->height_above_baseline + font->height_below_baseline + font->space_between_lines;
int text_height = font_line_distance * num_lines - font->space_between_lines;
int text_top = rect_top;
if (y_align >= 0) {
if (y_align <= 0) {
// middle
// The +1 is for simulating SHR + ADC/SBB.
text_top += (rect_height+1)/2 - (text_height+1)/2;
} else {
// bottom
text_top += rect_height - text_height;
}
}
textstate.current_y = text_top + font->height_above_baseline;
int i;
for (i = 0; i < num_lines; ++i) {
const char* line_pos = line_starts[i];
int line_length = line_lengths[i];
if (x_align < 0 &&
*line_pos == ' ' &&
i != 0 &&
*(line_pos-1) != '\n'
) {
// Skip over space if it's not at the beginning of a line.
++line_pos;
--line_length;
if (line_length != 0 &&
*line_pos == ' ' &&
*(line_pos-2) == '.'
) {
// Skip over second space after point.
++line_pos;
--line_length;
}
}
int line_width = get_line_width(line_pos,line_length);
int text_left = rect_ptr->left;
if (x_align >= 0) {
if (x_align <= 0) {
// center
text_left += rect_width/2 - line_width/2;
} else {
// right
text_left += rect_width - line_width;
}
}
textstate.current_x = text_left;
//printf("going to draw text line...\n");
draw_text_line(line_pos,line_length);
textstate.current_y += font_line_distance;
}
reset_clip_rect();
//set_textinfo(...);
//show_cursor();
return rect_ptr;
}
// seg009:3E4F
void __pascal far show_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text) {
// stub
//printf("show_text: %s\n",text);
int temp = screen_updates_suspended;
screen_updates_suspended = 1;
draw_text
(rect_ptr
, x_align
, y_align
, text
, strlen(text
));
screen_updates_suspended = temp;
request_screen_update();
}
// seg009:04FF
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) {
short saved_textcolor;
saved_textcolor = textstate.textcolor;
textstate.textcolor = color;
show_text(rect_ptr, x_align, y_align, text);
textstate.textcolor = saved_textcolor;
}
// seg009:3A91
void __pascal far set_curr_pos(int xpos,int ypos) {
textstate.current_x = xpos;
textstate.current_y = ypos;
}
// seg009:145A
void __pascal far init_copyprot_dialog() {
copyprot_dialog = make_dialog_info(&dialog_settings, &dialog_rect_1, &dialog_rect_1, NULL);
copyprot_dialog->peel = read_peel_from_screen(©prot_dialog->peel_rect);
}
// seg009:0838
int __pascal far showmessage(char far *text,int arg_4,void far *arg_0) {
word key;
rect_type rect;
//font_type* saved_font_ptr;
//surface_type* old_target;
//old_target = current_target_surface;
//current_target_surface = onscreen_surface_;
// In the disassembly there is some messing with the current_target_surface and font (?)
// However, this does not seem to be strictly necessary
method_1_blit_rect(offscreen_surface, onscreen_surface_, ©prot_dialog->peel_rect, ©prot_dialog->peel_rect, 0);
draw_dialog_frame(copyprot_dialog);
//saved_font_ptr = textstate.ptr_font;
//saved_font_ptr = current_target_surface->ptr_font;
//current_target_surface->ptr_font = ptr_font;
shrink2_rect(&rect, ©prot_dialog->text_rect, 2, 1);
show_text_with_color(&rect, 0, 0, text, color_15_brightwhite);
screen_updates_suspended = 0;
request_screen_update();
//textstate.ptr_font = saved_font_ptr;
//current_target_surface->ptr_font = saved_font_ptr;
clear_kbd_buf();
do {
idle();
key = key_test_quit(); // Press any key to continue...
} while(key == 0);
//restore_dialog_peel_2(copyprot_dialog->peel);
//current_target_surface = old_target;
need_full_redraw = 1; // lazy: instead of neatly restoring only the relevant part, just redraw the whole screen
return key;
}
// seg009:08FB
dialog_type * __pascal far make_dialog_info(dialog_settings_type *settings, rect_type *dialog_rect,
rect_type *text_rect, peel_type *dialog_peel) {
dialog_type* dialog_info;
dialog_info = malloc_near(sizeof(dialog_type));
dialog_info->settings = settings;
dialog_info->has_peel = 0;
dialog_info->peel = dialog_peel;
if (text_rect != NULL)
dialog_info->text_rect = *text_rect;
calc_dialog_peel_rect(dialog_info);
if (text_rect != NULL) { // does not seem to be quite right; see seg009:0948 (?)
read_dialog_peel(dialog_info);
}
return dialog_info;
}
// seg009:0BE7
void __pascal far calc_dialog_peel_rect(dialog_type*dialog) {
dialog_settings_type* settings;
settings = dialog->settings;
dialog->peel_rect.left = dialog->text_rect.left - settings->left_border;
dialog->peel_rect.top = dialog->text_rect.top - settings->top_border;
dialog->peel_rect.right = dialog->text_rect.right + settings->right_border + settings->shadow_right;
dialog->peel_rect.bottom = dialog->text_rect.bottom + settings->bottom_border + settings->shadow_bottom;
}
// seg009:0BB0
void __pascal far read_dialog_peel(dialog_type *dialog) {
if (dialog->has_peel) {
if (dialog->peel == NULL) {
dialog->peel = read_peel_from_screen(&dialog->peel_rect);
}
dialog->has_peel = 1;
draw_dialog_frame(dialog);
}
}
// seg009:09DE
void __pascal far draw_dialog_frame(dialog_type *dialog) {
dialog->settings->method_2_frame(dialog);
}
// A pointer to this function is the first field of dialog_settings (data:2944)
// Perhaps used when replacing a dialog's text with another text (?)
// seg009:096F
void __pascal far add_dialog_rect(dialog_type *dialog) {
draw_rect(&dialog->text_rect, color_0_black);
}
// seg009:09F0
void __pascal far dialog_method_2_frame(dialog_type *dialog) {
rect_type rect;
short shadow_right = dialog->settings->shadow_right;
short shadow_bottom = dialog->settings->shadow_bottom;
short bottom_border = dialog->settings->bottom_border;
short outer_border = dialog->settings->outer_border;
short peel_top = dialog->peel_rect.top;
short peel_left = dialog->peel_rect.left;
short peel_bottom = dialog->peel_rect.bottom;
short peel_right = dialog->peel_rect.right;
short text_top = dialog->text_rect.top;
short text_left = dialog->text_rect.left;
short text_bottom = dialog->text_rect.bottom;
short text_right = dialog->text_rect.right;
// Draw outer border
rect = (rect_type) { peel_top, peel_left, peel_bottom - shadow_bottom, peel_right - shadow_right };
draw_rect(&rect, color_0_black);
// Draw shadow (right)
rect = (rect_type) { text_top, peel_right - shadow_right, peel_bottom, peel_right };
draw_rect(&rect, get_text_color(0, color_8_darkgray /*dialog's shadow*/, 0));
// Draw shadow (bottom)
rect = (rect_type) { peel_bottom - shadow_bottom, text_left, peel_bottom, peel_right };
draw_rect(&rect, get_text_color(0, color_8_darkgray /*dialog's shadow*/, 0));
// Draw inner border (left)
rect = (rect_type) { peel_top + outer_border, peel_left + outer_border, text_bottom, text_left };
draw_rect(&rect, color_15_brightwhite);
// Draw inner border (top)
rect = (rect_type) { peel_top + outer_border, text_left, text_top, text_right + dialog->settings->right_border - outer_border };
draw_rect(&rect, color_15_brightwhite);
// Draw inner border (right)
rect.top = text_top;
rect.left = text_right;
rect.bottom = text_bottom + bottom_border - outer_border; // (rect.right stays the same)
draw_rect(&rect, color_15_brightwhite);
// Draw inner border (bottom)
rect = (rect_type) { text_bottom, peel_left + outer_border, text_bottom + bottom_border - outer_border, text_right };
draw_rect(&rect, color_15_brightwhite);
}
// seg009:0C44
void __pascal far show_dialog(const char *text) {
char string[256];
snprintf(string
, sizeof(string
), "%s\n\nPress any key to continue.", text
);
showmessage(string, 1, &key_test_quit);
}
// seg009:0791
int __pascal far get_text_center_y(const rect_type far *rect) {
const font_type far* font;
short empty_height; // height of empty space above+below the line of text
font = &hc_font;//current_target_surface->ptr_font;
empty_height = rect->bottom - font->height_above_baseline - font->height_below_baseline - rect->top;
return ((empty_height - empty_height % 2) >> 1) + font->height_above_baseline + empty_height % 2 + rect->top;
}
// seg009:3E77
int __pascal far get_cstring_width(const char far *text) {
int width = 0;
const char* text_pos = text;
char curr_char;
while (0 != (curr_char = *(text_pos++))) {
width += get_char_width(curr_char);
}
return width;
}
// seg009:0767
void __pascal far draw_text_cursor(int xpos,int ypos,int color) {
set_curr_pos(xpos, ypos);
/*current_target_surface->*/textstate.textcolor = color;
draw_text_character('_');
//restore_curr_color();
textstate.textcolor = 15;
}
// seg009:053C
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) {
short length;
word key;
short cursor_visible;
short current_xpos;
short ypos;
short init_length;
length = 0;
cursor_visible = 0;
draw_rect(rect, bgcolor);
init_length
= strlen(initial
);
if (has_initial) {
length = init_length;
}
current_xpos = rect->left + arg_4;
ypos = get_text_center_y(rect);
set_curr_pos(current_xpos, ypos);
/*current_target_surface->*/textstate.textcolor = color;
draw_cstring(initial);
//restore_curr_pos?();
current_xpos += get_cstring_width(initial) + (init_length != 0) * arg_4;
do {
key = 0;
do {
if (cursor_visible) {
draw_text_cursor(current_xpos, ypos, color);
} else {
draw_text_cursor(current_xpos, ypos, bgcolor);
}
cursor_visible = !cursor_visible;
start_timer(timer_0, 6);
if (key) {
if (cursor_visible) {
draw_text_cursor(current_xpos, ypos, color);
cursor_visible = !cursor_visible;
}
if (key == SDL_SCANCODE_RETURN) { // enter
buffer[length] = 0;
return length;
} else break;
}
request_screen_update();
// while (!timer_stopped[0] && (key = key_test_quit()) == 0) idle();
while (!has_timer_stopped(0) && (key = key_test_quit()) == 0) idle();
} while (1);
// Only use the printable ASCII chars (UTF-8 encoding)
char entered_char = last_text_input <= 0x7E ? last_text_input : 0;
clear_kbd_buf();
if (key == SDL_SCANCODE_ESCAPE) { // esc
draw_rect(rect, bgcolor);
buffer[0] = 0;
return -1;
}
if (length != 0 && (key == SDL_SCANCODE_BACKSPACE ||
key == SDL_SCANCODE_DELETE)) { // backspace, delete
--length;
draw_text_cursor(current_xpos, ypos, bgcolor);
current_xpos -= get_char_width(buffer[length]);
set_curr_pos(current_xpos, ypos);
/*current_target_surface->*/textstate.textcolor = bgcolor;
draw_text_character(buffer[length]);
//restore_curr_pos?();
draw_text_cursor(current_xpos, ypos, color);
}
else if (entered_char >= 0x20 && entered_char <= 0x7E && length < max_length) {
// Would the new character make the cursor go past the right side of the rect?
if (get_char_width('_') + get_char_width(entered_char) + current_xpos < rect->right) {
draw_text_cursor(current_xpos, ypos, bgcolor);
set_curr_pos(current_xpos, ypos);
/*current_target_surface->*/textstate.textcolor = color;
current_xpos += draw_text_character(buffer[length++] = entered_char);
}
}
request_screen_update();
} while(1);
}
#else // USE_TEXT
// seg009:3706
int __pascal far draw_text_character(byte character) {
// stub
printf("draw_text_character: %c\n",character
);
return 0;
}
// seg009:3E4F
void __pascal far show_text(const rect_type far *rect_ptr,int x_align,int y_align,const char far *text) {
// stub
printf("show_text: %s\n",text
);
}
// seg009:04FF
void __pascal far show_text_with_color(const rect_type far *rect_ptr,int x_align,int y_align,char far *text,int color) {
//short saved_textcolor;
//saved_textcolor = textstate.textcolor;
//textstate.textcolor = color;
show_text(rect_ptr, x_align, y_align, text);
//textstate.textcolor = saved_textcolor;
}
// seg009:3A91
void __pascal far set_curr_pos(int xpos,int ypos) {
// stub
}
// seg009:0C44
void __pascal far show_dialog(char *text) {
// stub
}
// seg009:053C
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) {
// stub
strncpy(buffer
, "dummy input text", max_length
);
}
#endif // USE_TEXT
// seg009:37E8
void __pascal far draw_rect(const rect_type far *rect,int color) {
method_5_rect(rect, blitters_0_no_transp, color);
}
// seg009:3985
surface_type far *__pascal rect_sthg(surface_type *surface,const rect_type far *rect) {
// stub
return surface;
}
// seg009:39CE
rect_type far *__pascal shrink2_rect(rect_type far *target_rect,const rect_type far *source_rect,int delta_x,int delta_y) {
target_rect->top = source_rect->top + delta_y;
target_rect->left = source_rect->left + delta_x;
target_rect->bottom = source_rect->bottom - delta_y;
target_rect->right = source_rect->right - delta_x;
return target_rect;
}
// seg009:3BBA
void __pascal far restore_peel(peel_type* peel_ptr) {
//printf("restoring peel at (x=%d, y=%d)\n", peel_ptr.rect.left, peel_ptr.rect.top); // debug
method_6_blit_img_to_scr(peel_ptr->peel, peel_ptr->rect.left, peel_ptr->rect.top, /*0x10*/0);
free_peel(peel_ptr);
//SDL_FreeSurface(peel_ptr.peel);
}
// seg009:3BE9
peel_type* __pascal far read_peel_from_screen(const rect_type far *rect) {
// stub
peel_type* result;
result
= calloc(1, sizeof(peel_type
));
//memset(&result, 0, sizeof(result));
result->rect = *rect;
#ifndef USE_ALPHA
SDL_Surface* peel_surface = SDL_CreateRGBSurface(0, rect->right - rect->left, rect->bottom - rect->top,
24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
#else
SDL_Surface* peel_surface = SDL_CreateRGBSurface(0, rect->right - rect->left, rect->bottom - rect->top, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24);
#endif
if (peel_surface == NULL) {
sdlperror("SDL_CreateRGBSurface");
quit(1);
}
result->peel = peel_surface;
rect_type target_rect = {0, 0, rect->right - rect->left, rect->bottom - rect->top};
method_1_blit_rect(result->peel, current_target_surface, &target_rect, rect, 0);
return result;
}
// seg009:3D95
int __pascal far intersect_rect(rect_type far *output,const rect_type far *input1,const rect_type far *input2) {
short left = MAX(input1->left, input2->left);
short right = MIN(input1->right, input2->right);
if (left < right) {
output->left = left;
output->right = right;
short top = MAX(input1->top, input2->top);
short bottom = MIN(input1->bottom, input2->bottom);
if (top < bottom) {
output->top = top;
output->bottom = bottom;
return 1;
}
}
memset(output
, 0, sizeof(rect_type
));
return 0;
}
// seg009:4063
rect_type far * __pascal far union_rect(rect_type far *output,const rect_type far *input1,const rect_type far *input2) {
short top = MIN(input1->top, input2->top);
short left = MIN(input1->left, input2->left);
short bottom = MAX(input1->bottom, input2->bottom);
short right = MAX(input1->right, input2->right);
output->top = top;
output->left = left;
output->bottom = bottom;
output->right = right;
return output;
}
enum userevents {
userevent_SOUND,
userevent_TIMER,
};
SDL_TimerID sound_timer = 0;
short speaker_playing = 0;
short digi_playing = 0;
short midi_playing = 0;
void __pascal far speaker_sound_stop() {
// stub
speaker_playing = 0;
if (sound_timer != 0) {
if (!SDL_RemoveTimer(sound_timer)) {
sdlperror("SDL_RemoveTimer in speaker_sound_stop");
//quit(1);
}
sound_timer = 0;
}
}
// The current buffer, holds the resampled sound data.
byte* digi_buffer = NULL;
// The current position in digi_buffer.
byte* digi_remaining_pos = NULL;
// The remaining length.
size_t digi_remaining_length = 0;
// The properties of the audio device.
SDL_AudioSpec* digi_audiospec = NULL;
// The desired samplerate. Everything will be resampled to this.
const int digi_samplerate = 44100;
void stop_digi() {
#ifndef USE_MIXER
SDL_PauseAudio(1);
if (!digi_playing) return;
SDL_LockAudio();
digi_playing = 0;
/*
// if (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) {
SDL_PauseAudio(1);
SDL_CloseAudio();
// }
if (digi_audiospec != NULL) {
free(digi_audiospec);
digi_audiospec = NULL;
}
*/
if (digi_buffer != NULL) {
digi_buffer = NULL;
}
digi_remaining_length = 0;
digi_remaining_pos = NULL;
SDL_UnlockAudio();
#else
Mix_HaltChannel(-1);
Mix_HaltMusic();
digi_playing = 0;
#endif
}
// seg009:7214
void __pascal far stop_sounds() {
// stub
stop_digi();
// stop_midi();
speaker_sound_stop();
}
Uint32 speaker_callback(Uint32 interval, void *param) {
SDL_Event event;
memset(&event
, 0, sizeof(event
));
event.type = SDL_USEREVENT;
event.user.code = userevent_SOUND;
event.user.data1 = param;
if (!SDL_RemoveTimer(sound_timer)) {
sdlperror("SDL_RemoveTimer in speaker_callback");
//quit(1);
}
sound_timer = 0;
speaker_playing = 0;
// First remove the timer, then allow the other thread to continue.
SDL_PushEvent(&event);
return 0;
}
// seg009:7640
void __pascal far play_speaker_sound(sound_buffer_type far *buffer) {
// stub
//speaker_sound_stop();
stop_sounds();
int length = 0;
int index;
for (index = 0; buffer->speaker.notes[index].frequency != 0x12; ++index) {
length += buffer->speaker.notes[index].length;
}
int time_ms = length*1000 / buffer->speaker.tempo;
//printf("length = %d ms\n", time_ms);
sound_timer = SDL_AddTimer(time_ms, speaker_callback, NULL);
if (sound_timer == 0) {
sdlperror("SDL_AddTimer");
quit(1);
}
speaker_playing = 1;
}
#ifndef USE_MIXER
void digi_callback(void *userdata, Uint8 *stream, int len) {
// Don't go over the end of either the input or the output buffer.
size_t copy_len = MIN(len, digi_remaining_length);
//printf("digi_callback(): copy_len = %d\n", copy_len);
//printf("digi_callback(): len = %d\n", len);
if (is_sound_on) {
// Copy the next part of the input of the output.
memcpy(stream
, digi_remaining_pos
, copy_len
);
// In case the sound does not fill the buffer: fill the rest of the buffer with silence.
memset(stream
+ copy_len
, digi_audiospec
->silence
, len
- copy_len
);
} else {
// If sound is off: Mute the sound but keep track of where we are.
memset(stream
, digi_audiospec
->silence
, len
);
}
// If the sound ended, push an event.
if (digi_playing && digi_remaining_length == 0) {
//printf("digi_callback(): sound ended\n");
SDL_Event event;
memset(&event
, 0, sizeof(event
));
event.type = SDL_USEREVENT;
event.user.code = userevent_SOUND;
digi_playing = 0;
SDL_PushEvent(&event);
}
// Advance the pointer.
digi_remaining_length -= copy_len;
digi_remaining_pos += copy_len;
}
#endif
#ifdef USE_MIXER
void channel_finished(int channel) {
digi_playing = 0;
//printf("Finished channel %d\n", channel);
SDL_Event event;
memset(&event
, 0, sizeof(event
));
event.type = SDL_USEREVENT;
event.user.code = userevent_SOUND;
SDL_PushEvent(&event);
}
void music_finished(void) {
channel_finished(-1);
}
#endif
int digi_unavailable = 0;
void init_digi() {
if (digi_unavailable) return;
if (digi_audiospec != NULL) return;
// Open the audio device. Called once.
//fprintf(stderr, "init_digi(): called\n");
SDL_AudioFormat desired_audioformat;
SDL_version version;
SDL_GetVersion(&version);
//printf("SDL Version = %d.%d.%d\n", version.major, version.minor, version.patch);
if (version.major <= 2 && version.minor <= 0 && version.patch <= 3) {
// In versions before 2.0.4, 16-bit audio samples don't work properly (the sound becomes garbled).
// See: https://bugzilla.libsdl.org/show_bug.cgi?id=2389
// Workaround: set the audio format to 8-bit, if we are linking against an older SDL2 version.
desired_audioformat = AUDIO_U8;
printf("Your SDL.dll is older than 2.0.4. Using 8-bit audio format to work around resampling bug.");
} else {
desired_audioformat = AUDIO_S16SYS;
}
SDL_AudioSpec *desired;
desired
= (SDL_AudioSpec
*)malloc(sizeof(SDL_AudioSpec
));
memset(desired
, 0, sizeof(SDL_AudioSpec
));
desired->freq = digi_samplerate; //buffer->digi.sample_rate;
desired->format = desired_audioformat;
desired->channels = 2;
desired->samples = 1024;
#ifndef USE_MIXER
desired->callback = digi_callback;
desired->userdata = NULL;
if (SDL_OpenAudio(desired, NULL) != 0) {
sdlperror("SDL_OpenAudio");
//quit(1);
digi_unavailable = 1;
return;
}
//SDL_PauseAudio(0);
#else
if (Mix_OpenAudio(desired->freq, desired->format, desired->channels, desired->samples) != 0) {
sdlperror("Mix_OpenAudio");
digi_unavailable = 1;
return;
}
Mix_AllocateChannels(1);
Mix_ChannelFinished(channel_finished);
Mix_HookMusicFinished(music_finished);
//printf ("init_digi(): SETTING SOUND AND MUSIC VOLUME\n");
Mix_Volume (-1, is_sound_on ? mixer_volume : 0);
Mix_VolumeMusic (is_sound_on ? mixer_volume : 0);
#endif
digi_audiospec = desired;
}
#ifdef USE_MIXER
const int sound_channel = 0;
const int max_sound_id = 58;
char** sound_names = NULL;
void load_sound_names() {
const char* names_path = locate_file("data/music/names.txt");
if (sound_names != NULL) return;
FILE
* fp
= fopen(names_path
,"rt");
if (fp==NULL) return;
sound_names
= (char**) calloc(sizeof(char*) * max_sound_id
, 1);
int index;
char name[POP_MAX_PATH];
if (fscanf(fp
, "%d=%255s\n", &index
, /*sizeof(name)-1,*/ name
) != 2) {
continue;
}
//if (feof(fp)) break;
//printf("sound_names[%d] = %s\n",index,name);
if (index >= 0 && index < max_sound_id) {
sound_names[index] = strdup(name);
}
}
}
char* sound_name(int index) {
if (sound_names != NULL && index >= 0 && index < max_sound_id) {
return sound_names[index];
} else {
return NULL;
}
}
void convert_digi_sound(sound_buffer_type *buffer);
#endif
sound_buffer_type* load_sound(int index) {
sound_buffer_type* result = NULL;
#ifdef USE_MIXER
//printf("load_sound(%d)\n", index);
init_digi();
if (!digi_unavailable && result == NULL && index >= 0 && index < max_sound_id) {
//printf("Trying to load from music folder\n");
//load_sound_names(); // Moved to load_sounds()
if (sound_names != NULL && sound_name(index) != NULL) {
//printf("Loading from music folder\n");
const char* exts[]={"ogg","mp3","flac","wav"};
int i;
for (i = 0; i < COUNT(exts); ++i) {
char filename[POP_MAX_PATH];
const char* ext=exts[i];
snprintf(filename
, sizeof(filename
), "data/music/%s.%s", sound_name
(index
), ext
);
const char* located_filename = locate_file(filename);
// Skip nonexistent files:
if (!file_exists(located_filename))
continue;
//printf("Trying to load %s\n", filename);
Mix_Music* music = Mix_LoadMUS(located_filename);
if (music == NULL) {
sdlperror(located_filename);
//sdlperror("Mix_LoadWAV");
continue;
}
//printf("Loaded sound from %s\n", filename);
result
= malloc(sizeof(sound_buffer_type
));
result->type = sound_music;
result->music = music;
break;
}
} else {
//printf("sound_names = %p\n", sound_names);
//printf("sound_names[%d] = %p\n", index, sound_name(index));
}
}
#endif
if (result == NULL) {
//printf("Trying to load from DAT\n");
result = (sound_buffer_type*) load_from_opendats_alloc(index + 10000, "bin", NULL, NULL);
}
#ifdef USE_MIXER
if (result == NULL) {
fprintf(stderr
, "Failed to load sound %d '%s'\n", index
, sound_name
(index
));
} else {
// Convert waves to mixer chunks in advance.
if ((result->type & 7) == sound_digi) {
convert_digi_sound(result);
}
}
#endif
return result;
}
#ifdef USE_MIXER
void __pascal far play_chunk_sound(sound_buffer_type far *buffer) {
//if (!is_sound_on) return;
init_digi();
if (digi_unavailable) return;
stop_sounds();
//printf("playing chunk sound %p\n", buffer);
//printf ("play_chunk_sound(): SETTING VOLUME\n");
// Mix_Volume (sound_channel, is_sound_on ? mixer_volume : 0);
if (Mix_PlayChannel(sound_channel, buffer->chunk, 0) == -1) {
sdlperror("Mix_PlayChannel");
}
digi_playing = 1;
}
void __pascal far play_music_sound(sound_buffer_type far *buffer) {
init_digi();
if (digi_unavailable) return;
stop_sounds();
//printf ("play_music_sound(): SETTING MUSIC VOLUME\n");
// Mix_VolumeMusic (is_sound_on ? mixer_volume : 0);
if (Mix_PlayMusic(buffer->music, 0) == -1) {
sdlperror("Mix_PlayMusic");
}
digi_playing = 1;
}
Uint32 fourcc(char* string) {
return *(Uint32*)string;
}
#endif
int wave_version = -1;
typedef struct waveinfo_type {
int sample_rate, sample_size, sample_count;
byte* samples;
} waveinfo_type;
bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo);
bool determine_wave_version(sound_buffer_type *buffer, waveinfo_type* waveinfo) {
int version = wave_version;
if (version == -1) {
// Determine the version of the wave data.
version = 0;
if (buffer->digi.sample_size == 8) version += 1;
if (buffer->digi_new.sample_size == 8) version += 2;
if (version == 1 || version == 2) wave_version = version;
}
switch (version) {
case 1: // 1.0 and 1.1
waveinfo->sample_rate = buffer->digi.sample_rate;
waveinfo->sample_size = buffer->digi.sample_size;
waveinfo->sample_count = buffer->digi.sample_count;
waveinfo->samples = buffer->digi.samples;
return true;
case 2: // 1.3 and 1.4 (and PoP2)
waveinfo->sample_rate = buffer->digi_new.sample_rate;
waveinfo->sample_size = buffer->digi_new.sample_size;
waveinfo->sample_count = buffer->digi_new.sample_count;
waveinfo->samples = buffer->digi_new.samples;
return true;
case 3: // ambiguous
printf("Warning: Ambiguous wave version.\n");
return false;
default: // case 0, unknown
printf("Warning: Can't determine wave version.\n");
return false;
}
}
#ifndef USE_MIXER
// seg009:74F0
void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
//if (!is_sound_on) return;
init_digi();
if (digi_unavailable) return;
//stop_digi();
stop_sounds();
//printf("play_digi_sound(): called\n");
waveinfo_type waveinfo;
if (false == determine_wave_version(buffer, &waveinfo)) return;
SDL_AudioCVT cvt;
int result = SDL_BuildAudioCVT(&cvt,
AUDIO_U8, 1, waveinfo.sample_rate,
digi_audiospec->format, digi_audiospec->channels, digi_audiospec->freq
);
// The case of result == 0 is undocumented, but it may occur.
if (result != 1 && result != 0) {
sdlperror("SDL_BuildAudioCVT");
printf("(returned %d)\n", result
);
quit(1);
}
int dlen = waveinfo.sample_count; // if format is AUDIO_U8
cvt.
buf = (Uint8
*) malloc(dlen
* cvt.
len_mult);
memcpy(cvt.
buf, waveinfo.
samples, dlen
);
cvt.len = dlen;
if (SDL_ConvertAudio(&cvt) != 0) {
sdlperror("SDL_ConvertAudio");
quit(1);
}
SDL_LockAudio();
digi_buffer = cvt.buf;
digi_playing = 1;
// digi_remaining_length = sample_count;
// digi_remaining_pos = samples;
digi_remaining_length = cvt.len_cvt;
digi_remaining_pos = digi_buffer;
SDL_UnlockAudio();
SDL_PauseAudio(0);
}
#else
void __pascal far play_digi_sound(sound_buffer_type far *buffer) {
printf("Warning: Tried to play a digi sound without converting it to a mixer chunk first!\n");
}
void convert_digi_sound(sound_buffer_type *buffer) {
waveinfo_type waveinfo;
if (false == determine_wave_version(buffer, &waveinfo)) return;
// Convert the DAT sound to WAV, so the Mixer can load it.
int size = waveinfo.sample_count;
int rounded_size = (size+1)&(~1);
int alloc_size = sizeof(WAV_header_type) + rounded_size;
WAV_header_type
* wav_data
= malloc(alloc_size
);
wav_data->ChunkID = fourcc("RIFF");
wav_data->ChunkSize = 36 + rounded_size;
wav_data->Format = fourcc("WAVE");
wav_data->Subchunk1ID = fourcc("fmt ");
wav_data->Subchunk1Size = 16;
wav_data->AudioFormat = 1; // PCM
wav_data->NumChannels = 1; // Mono
wav_data->SampleRate = waveinfo.sample_rate;
wav_data->BitsPerSample = waveinfo.sample_size;
wav_data->ByteRate = wav_data->SampleRate * wav_data->NumChannels * wav_data->BitsPerSample/8;
wav_data->BlockAlign = wav_data->NumChannels * wav_data->BitsPerSample/8;
wav_data->Subchunk2ID = fourcc("data");
wav_data->Subchunk2Size = size;
memcpy(wav_data
->Data
, waveinfo.
samples, size
);
SDL_RWops* rw = SDL_RWFromConstMem(wav_data, alloc_size);
Mix_Chunk *chunk = Mix_LoadWAV_RW(rw, 1);
if (chunk == NULL) {
FILE
* fp
= fopen("dump.wav","wb");
fwrite(wav_data
,alloc_size
,1,fp
);
}
if (chunk == NULL) {
sdlperror("Mix_LoadWAV_RW");
return;
}
buffer->type = sound_chunk;
buffer->chunk = chunk;
}
#endif
void free_sound(sound_buffer_type far *buffer) {
if (buffer == NULL) return;
#ifdef USE_MIXER
if (buffer->type == sound_chunk) {
Mix_FreeChunk(buffer->chunk);
}
if (buffer->type == sound_music) {
Mix_FreeMusic(buffer->music);
}
#endif
}
// seg009:7220
void __pascal far play_sound_from_buffer(sound_buffer_type far *buffer) {
#ifdef USE_REPLAY
if (replaying && skipping_replay) return;
#endif
// stub
if (buffer == NULL) {
printf("Tried to play NULL sound.\n");
//quit(1);
return;
}
switch (buffer->type & 7) {
case sound_speaker:
play_speaker_sound(buffer);
break;
case sound_digi:
play_digi_sound(buffer);
break;
#ifdef USE_MIXER
case sound_chunk:
play_chunk_sound(buffer);
break;
case sound_music:
play_music_sound(buffer);
break;
#endif
default:
printf("Tried to play unimplemented sound type %d.\n", buffer
->type
);
quit(1);
break;
}
}
// seg009:7273
void __pascal far turn_sound_on_off(byte new_state) {
// stub
is_sound_on = new_state;
//if (!is_sound_on) stop_sounds();
#ifdef USE_MIXER
init_digi();
if (digi_unavailable) return;
//printf ("turn_sound_on_off(): SETTING SOUND AND MUSIC VOLUME\n");
Mix_Volume(-1, is_sound_on ? mixer_volume : 0);
Mix_VolumeMusic(is_sound_on ? mixer_volume : 0);
#endif
}
// seg009:7299
int __pascal far check_sound_playing() {
return speaker_playing || digi_playing || midi_playing;
}
void window_resized() {
#if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
if (use_integer_scaling) {
int window_width, window_height;
SDL_GetWindowSize(window_, &window_width, &window_height);
int render_width, render_height;
SDL_RenderGetLogicalSize(renderer_, &render_width, &render_height);
// Disable integer scaling if it would result in downscaling.
// Because then the only suitable integer scaling factor is zero, i.e. the picture disappears.
SDL_bool makes_sense = (window_width >= render_width && window_height >= render_height);
SDL_RenderSetIntegerScale(renderer_, makes_sense);
}
#endif
}
void xbrz_scale (size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, int yFirst, int yLast, bool has_alpha_channel);
static void *hqx_pixels32 = NULL;
static void *hqx_resized_pixels32 = NULL;
static void *hqx_resized_pixels24 = NULL;
// seg009:38ED
void __pascal far set_gr_mode(byte grmode) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE |
SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC ) != 0) {
sdlperror("SDL_Init");
quit(1);
}
//SDL_EnableUNICODE(1); //deprecated
Uint32 flags = 0;
if (!start_fullscreen) start_fullscreen = check_param("full") != NULL;
if (start_fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
flags |= SDL_WINDOW_RESIZABLE;
// Should use different default window dimensions when using 4:3 aspect ratio
if (use_correct_aspect_ratio && pop_window_width == 640 && pop_window_height == 400) {
pop_window_height = 480;
}
#ifdef USE_REPLAY
if (!is_validate_mode) // run without a window if validating a replay
#endif
window_ = SDL_CreateWindow(WINDOW_TITLE,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
pop_window_width, pop_window_height, flags);
renderer_ = SDL_CreateRenderer(window_, -1 , SDL_RENDERER_ACCELERATED );
if (use_integer_scaling) {
#if SDL_VERSION_ATLEAST(2,0,5) // SDL_RenderSetIntegerScale
SDL_RenderSetIntegerScale(renderer_, SDL_TRUE);
#else
printf("Warning: You need to compile with SDL 2.0.5 or newer for the use_integer_scaling option.\n");
#endif
}
SDL_Surface* icon = IMG_Load(locate_file("data/icon.png"));
if (icon == NULL) {
sdlperror("Could not load icon");
} else {
SDL_SetWindowIcon(window_, icon);
}
// Allow us to use a consistent set of screen co-ordinates, even if the screen size changes
if (use_correct_aspect_ratio) {
SDL_RenderSetLogicalSize(renderer_, 320*5, 200*6);
} else {
SDL_RenderSetLogicalSize(renderer_, 320, 200);
}
window_resized();
/* Migration to SDL2: everything is still blitted to onscreen_surface_, however:
* SDL2 renders textures to the screen instead of surfaces; so for now, every screen
* update causes the onscreen_surface_ to be copied into sdl_texture_, which is
* subsequently displayed; awaits a better refactoring!
* The function handling the screen updates is request_screen_update()
* */
onscreen_surface_ = SDL_CreateRGBSurface(0, 320, 200, 24, 0xFF, 0xFF << 8, 0xFF << 16, 0) ;
hqx_pixels32
= (void *) malloc (320 * 200 * 4);
if (hqx_pixels32 == NULL) { sdlperror ("hqx original 32bpp image buffer allocation failure"); quit (1); }
hqx_resized_pixels32
= (void *) realloc (hqx_resized_pixels32
, 6 * 320 * 6 * 200 * 4);
if (hqx_resized_pixels32 == NULL) { sdlperror ("hqx resized 32bpp image buffer allocation failure"); quit (1); }
hqx_resized_pixels24
= (void *) realloc (hqx_resized_pixels24
, 6 * 320 * 6 * 200 * 3);
if (hqx_resized_pixels24 == NULL) { sdlperror ("hqx resized 24bpp image buffer allocation failure"); quit (1); }
sdl_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 320 * filter_level, 200 * filter_level);
screen_updates_suspended = 0;
if (onscreen_surface_ == NULL) {
sdlperror("SDL_SetVideoMode");
quit(1);
}
if (start_fullscreen) {
SDL_ShowCursor(SDL_DISABLE);
}
//SDL_WM_SetCaption(WINDOW_TITLE, NULL);
// if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) != 0) { //deprecated
// sdlperror("SDL_EnableKeyRepeat");
// quit(1);
// }
graphics_mode = gmMcgaVga;
#ifdef USE_TEXT
load_font();
#endif
SDL_PumpEvents(); // Pierre-Marie Baty -- SDL black window fix on macOS 10.14+
SDL_SetWindowSize(window_, pop_window_width, pop_window_height);
}
void request_screen_update() {
#ifdef USE_REPLAY
if (replaying && skipping_replay) return;
#endif
if (!screen_updates_suspended) {
if (filter_level > 1)
{
uint8_t *hqx_24bpp_ptr;
uint8_t *hqx_32bpp_ptr;
hqx_24bpp_ptr = onscreen_surface_->pixels; hqx_32bpp_ptr = hqx_pixels32; for (int hqx_pixel_index = 0; hqx_pixel_index < 320 * 200; hqx_pixel_index++) { *hqx_32bpp_ptr++ = *hqx_24bpp_ptr++; *hqx_32bpp_ptr++ = *hqx_24bpp_ptr++; *hqx_32bpp_ptr++ = *hqx_24bpp_ptr++; *hqx_32bpp_ptr++ = 0xFF; }
xbrz_scale (filter_level, hqx_pixels32, hqx_resized_pixels32, 320, 200, 0, 200, false);
hqx_32bpp_ptr = hqx_resized_pixels32; hqx_24bpp_ptr = hqx_resized_pixels24; for (int hqx_pixel_index = 0; hqx_pixel_index < filter_level * 320 * filter_level * 200; hqx_pixel_index++) { *hqx_24bpp_ptr++ = *hqx_32bpp_ptr++; *hqx_24bpp_ptr++ = *hqx_32bpp_ptr++; *hqx_24bpp_ptr++ = *hqx_32bpp_ptr++; hqx_32bpp_ptr++; }
SDL_UpdateTexture(sdl_texture_, NULL, hqx_resized_pixels24, onscreen_surface_->pitch * filter_level);
}
else
SDL_UpdateTexture(sdl_texture_, NULL, onscreen_surface_->pixels, onscreen_surface_->pitch);
SDL_RenderClear(renderer_);
SDL_RenderCopy(renderer_, sdl_texture_, NULL, NULL);
SDL_RenderPresent(renderer_);
}
}
// seg009:9289
void __pascal far set_pal_arr(int start,int count,const rgb_type far *array,int vsync) {
// stub
int i;
for (i = 0; i < count; ++i) {
if (array) {
set_pal(start + i, array[i].r, array[i].g, array[i].b, vsync);
} else {
set_pal(start + i, 0, 0, 0, vsync);
}
}
}
rgb_type palette[256];
// seg009:92DF
void __pascal far set_pal(int index,int red,int green,int blue,int vsync) {
// stub
//palette[index] = ((red&0x3F)<<2)|((green&0x3F)<<2<<8)|((blue&0x3F)<<2<<16);
palette[index].r = red;
palette[index].g = green;
palette[index].b = blue;
}
// seg009:969C
int __pascal far add_palette_bits(byte n_colors) {
// stub
return 0;
}
// seg009:9C36
int __pascal far find_first_pal_row(int which_rows_mask) {
word which_row = 0;
word row_mask = 1;
do {
if (row_mask & which_rows_mask) {
return which_row;
}
++which_row;
row_mask <<= 1;
} while (which_row < 16);
return 0;
}
// seg009:9C6C
int __pascal far get_text_color(int cga_color,int low_half,int high_half_mask) {
if (graphics_mode == gmCga || graphics_mode == gmHgaHerc) {
return cga_color;
} else if (graphics_mode == gmMcgaVga && high_half_mask != 0) {
return (find_first_pal_row(high_half_mask) << 4) + low_half;
} else {
return low_half;
}
}
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) {
char image_filename[POP_MAX_PATH];
FILE* fp = NULL;
dat_type* pointer;
*result = data_none;
// Go through all open DAT files.
for (pointer = dat_chain_ptr; fp == NULL && pointer != NULL; pointer = pointer->next_dat) {
*out_pointer = pointer;
if (pointer->handle != NULL) {
// If it's an actual DAT file:
fp = pointer->handle;
dat_table_type* dat_table = pointer->dat_table;
int i;
for (i = 0; i < dat_table->res_count; ++i) {
if (dat_table->entries[i].id == resource_id) {
break;
}
}
if (i < dat_table->res_count) {
// found
*result = data_DAT;
*size = dat_table->entries[i].size;
if (fseek(fp
, dat_table
->entries
[i
].
offset, SEEK_SET
) ||
fread(checksum
, 1, 1, fp
) != 1) {
fp = NULL;
}
} else {
// not found
fp = NULL;
}
} else {
// If it's a directory:
char filename_no_ext[POP_MAX_PATH];
// strip the .DAT file extension from the filename (use folders simply named TITLE, KID, VPALACE, etc.)
strncpy(filename_no_ext
, pointer
->filename
, sizeof(filename_no_ext
));
size_t len
= strlen(filename_no_ext
);
if (len >= 5 && filename_no_ext[len-4] == '.') {
filename_no_ext[len-4] = '\0'; // terminate, so ".DAT" is deleted from the filename
}
snprintf(image_filename
,sizeof(image_filename
),"data/%s/res%d.%s",filename_no_ext
, resource_id
, extension
);
if (!use_custom_levelset) {
//printf("loading (binary) %s",image_filename);
fp
= fopen(locate_file
(image_filename
), "rb");
}
else {
char image_filename_mod[POP_MAX_PATH];
// before checking data/, first try mods/MODNAME/data/
snprintf(image_filename_mod
, sizeof(image_filename_mod
), "mods/%s/%s", levelset_name
, image_filename
);
//printf("loading (binary) %s",image_filename_mod);
fp
= fopen(locate_file
(image_filename_mod
), "rb");
if (fp == NULL) {
fp
= fopen(locate_file
(image_filename
), "rb");
}
}
if (fp != NULL) {
struct stat buf;
if (fstat(fileno(fp), &buf) == 0) {
*result = data_directory;
*size = buf.st_size;
} else {
fp = NULL;
}
}
}
}
*out_fp = fp;
if (fp == NULL) {
*result = data_none;
// printf(" FAILED\n");
//return NULL;
}
//...
}
// seg009:9F34
void __pascal far close_dat(dat_type far *pointer) {
dat_type** prev = &dat_chain_ptr;
dat_type* curr = dat_chain_ptr;
while (curr != NULL) {
if (curr == pointer) {
*prev = curr->next_dat;
if (curr
->handle
) fclose(curr
->handle
);
if (curr
->dat_table
) free(curr
->dat_table
);
return;
}
curr = curr->next_dat;
prev = &((*prev)->next_dat);
}
// stub
}
// seg009:9F80
void far *__pascal load_from_opendats_alloc(int resource, const char* extension, data_location* out_result, int* out_size) {
// stub
//printf("id = %d\n",resource);
dat_type* pointer;
data_location result;
byte checksum;
int size;
FILE* fp = NULL;
load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
if (out_result != NULL) *out_result = result;
if (out_size != NULL) *out_size = size;
if (result == data_none) return NULL;
//read(fd, area, size);
if (fread(area
, size
, 1, fp
) != 1) {
fprintf(stderr
, "%s: %s, resource %d, size %d, failed: %s\n",
__func__, pointer->filename, resource,
area = NULL;
}
if (result
== data_directory
) fclose(fp
);
/* XXX: check checksum */
return area;
}
// seg009:A172
int __pascal far load_from_opendats_to_area(int resource,void far *area,int length, const char* extension) {
// stub
//return 0;
dat_type* pointer;
data_location result;
byte checksum;
int size;
FILE* fp = NULL;
load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer);
if (result == data_none) return 0;
if (fread(area
, MIN
(size
, length
), 1, fp
) != 1) {
fprintf(stderr
, "%s: %s, resource %d, size %d, failed: %s\n",
__func__, pointer->filename, resource,
memset(area
, 0, MIN
(size
, length
));
}
if (result
== data_directory
) fclose(fp
);
/* XXX: check checksum */
return 0;
}
// SDL-specific implementations
void rect_to_sdlrect(const rect_type* rect, SDL_Rect* sdlrect) {
sdlrect->x = rect->left;
sdlrect->y = rect->top;
sdlrect->w = rect->right - rect->left;
sdlrect->h = rect->bottom - rect->top;
}
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) {
SDL_Rect src_rect;
rect_to_sdlrect(source_rect, &src_rect);
SDL_Rect dest_rect;
rect_to_sdlrect(target_rect, &dest_rect);
if (blit == blitters_0_no_transp) {
// Disable transparency.
if (SDL_SetColorKey(source_surface, 0, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
} else {
// Enable transparency.
if (SDL_SetColorKey(source_surface, SDL_TRUE, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
}
if (SDL_BlitSurface(source_surface, &src_rect, target_surface, &dest_rect) != 0) {
sdlperror("SDL_BlitSurface");
quit(1);
}
if (target_surface == onscreen_surface_) {
request_screen_update();
}
}
image_type far * __pascal far method_3_blit_mono(image_type far *image,int xpos,int ypos,int blitter,byte color) {
int w = image->w;
int h = image->h;
if (SDL_SetColorKey(image, SDL_TRUE, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
SDL_Surface* colored_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_NONE);
/* Causes problems with SDL 2.0.5 (see #105)
if (SDL_SetColorKey(colored_image, SDL_TRUE, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
*/
if (SDL_LockSurface(colored_image) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
int y,x;
rgb_type palette_color = palette[color];
uint32_t rgb_color = SDL_MapRGB(colored_image->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) & 0xFFFFFF;
int stride = colored_image->pitch;
for (y = 0; y < h; ++y) {
uint32_t* pixel_ptr = (uint32_t*) ((byte*)colored_image->pixels + stride * y);
for (x = 0; x < w; ++x) {
// set RGB but leave alpha
*pixel_ptr = (*pixel_ptr & 0xFF000000) | rgb_color;
//printf("pixel x=%d, y=%d, color = 0x%8x\n", x, y, *pixel_ptr);
++pixel_ptr;
}
}
SDL_UnlockSurface(colored_image);
SDL_Rect src_rect = {0, 0, image->w, image->h};
SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
SDL_SetSurfaceBlendMode(colored_image, SDL_BLENDMODE_BLEND);
SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_BLEND);
SDL_SetSurfaceAlphaMod(colored_image, 255);
if (SDL_BlitSurface(colored_image, &src_rect, current_target_surface, &dest_rect) != 0) {
sdlperror("SDL_BlitSurface");
quit(1);
}
SDL_FreeSurface(colored_image);
return image;
}
// Workaround for a bug in SDL2 (before v2.0.4):
// https://bugzilla.libsdl.org/show_bug.cgi?id=2986
// SDL_FillRect onto a 24-bit surface swaps Red and Blue component
bool RGB24_bug_checked = false;
bool RGB24_bug_affected;
bool RGB24_bug_check() {
if (!RGB24_bug_checked) {
// Check if the bug occurs in this version of SDL.
SDL_Surface* test_surface = SDL_CreateRGBSurface(0, 1, 1, 24, 0, 0, 0, 0);
if (NULL == test_surface) sdlperror("SDL_CreateSurface in RGB24_bug_check");
// Fill with red.
SDL_FillRect(test_surface, NULL, SDL_MapRGB(test_surface->format, 0xFF, 0, 0));
if (0 != SDL_LockSurface(test_surface)) sdlperror("SDL_LockSurface in RGB24_bug_check");
// Read red component of pixel.
RGB24_bug_affected = (*(Uint32*)test_surface->pixels & test_surface->format->Rmask) == 0;
SDL_UnlockSurface(test_surface);
SDL_FreeSurface(test_surface);
RGB24_bug_checked = true;
}
return RGB24_bug_affected;
}
int safe_SDL_FillRect(SDL_Surface* dst, const SDL_Rect* rect, Uint32 color) {
if (dst->format->BitsPerPixel == 24 && RGB24_bug_check()) {
// In the buggy version, SDL_FillRect swaps R and B, so we swap it once more.
color = ((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16);
}
return SDL_FillRect(dst, rect, color);
}
// End of workaround.
const rect_type far * __pascal far method_5_rect(const rect_type far *rect,int blit,byte color) {
SDL_Rect dest_rect;
rect_to_sdlrect(rect, &dest_rect);
rgb_type palette_color = palette[color];
#ifndef USE_ALPHA
uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2);
#else
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);
#endif
if (safe_SDL_FillRect(current_target_surface, &dest_rect, rgb_color) != 0) {
sdlperror("SDL_FillRect");
quit(1);
}
if (current_target_surface == onscreen_surface_) {
request_screen_update();
}
return rect;
}
void blit_xor(SDL_Surface* target_surface, SDL_Rect* dest_rect, SDL_Surface* image, SDL_Rect* src_rect) {
if (dest_rect->w != src_rect->w || dest_rect->h != src_rect->h) {
printf("blit_xor: dest_rect and src_rect have different sizes\n");
quit(1);
}
SDL_Surface* helper_surface = SDL_CreateRGBSurface(0, dest_rect->w, dest_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
if (helper_surface == NULL) {
sdlperror("SDL_CreateRGBSurface");
quit(1);
}
SDL_Surface* image_24 = SDL_ConvertSurface(image, helper_surface->format, 0);
//SDL_CreateRGBSurface(0, src_rect->w, src_rect->h, 24, 0xFF, 0xFF<<8, 0xFF<<16, 0);
if (image_24 == NULL) {
sdlperror("SDL_CreateRGBSurface");
quit(1);
}
SDL_Rect dest_rect2 = *src_rect;
// Read what is currently where we want to draw the new image.
if (SDL_BlitSurface(target_surface, dest_rect, helper_surface, &dest_rect2) != 0) {
sdlperror("SDL_BlitSurface");
quit(1);
}
if (SDL_LockSurface(image_24) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
if (SDL_LockSurface(helper_surface) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
int size = helper_surface->h * helper_surface->pitch;
int i;
byte *p_src = (byte*) image_24->pixels;
byte *p_dest = (byte*) helper_surface->pixels;
// Xor the old area with the image.
for (i = 0; i < size; ++i) {
*p_dest ^= *p_src;
++p_src; ++p_dest;
}
SDL_UnlockSurface(image_24);
SDL_UnlockSurface(helper_surface);
// Put the new area in place of the old one.
if (SDL_BlitSurface(helper_surface, src_rect, target_surface, dest_rect) != 0) {
sdlperror("SDL_BlitSurface 2065");
quit(1);
}
SDL_FreeSurface(image_24);
SDL_FreeSurface(helper_surface);
if (target_surface == onscreen_surface_) {
request_screen_update();
}
}
image_type far * __pascal far method_6_blit_img_to_scr(image_type far *image,int xpos,int ypos,int blit) {
if (image == NULL) {
printf("method_6_blit_img_to_scr: image == NULL\n");
//quit(1);
return NULL;
}
if (blit == blitters_9_black) {
method_3_blit_mono(image, xpos, ypos, blitters_9_black, 0);
return image;
}
SDL_Rect src_rect = {0, 0, image->w, image->h};
SDL_Rect dest_rect = {xpos, ypos, image->w, image->h};
if (blit == blitters_3_xor) {
blit_xor(current_target_surface, &dest_rect, image, &src_rect);
return image;
}
SDL_SetSurfaceBlendMode(image, SDL_BLENDMODE_NONE);
SDL_SetSurfaceBlendMode(current_target_surface, SDL_BLENDMODE_NONE);
SDL_SetSurfaceAlphaMod(image, 255);
if (blit == blitters_0_no_transp) {
SDL_SetColorKey(image, SDL_FALSE, 0);
}
else {
SDL_SetColorKey(image, SDL_TRUE, 0);
}
if (SDL_BlitSurface(image, &src_rect, current_target_surface, &dest_rect) != 0) {
sdlperror("SDL_BlitSurface 2247");
quit(1);
}
if (SDL_SetSurfaceAlphaMod(image, 0) != 0) {
sdlperror("SDL_SetAlpha");
quit(1);
}
// if (current_target_surface == onscreen_surface_)
// request_screen_update();
return image;
}
#ifndef USE_COMPAT_TIMER
int fps = 60;
SDL_TimerID timer_handles[2] = {0,0};
int timer_stopped[2] = {1,1};
#else
int wait_time[2] = {0,0};
#endif
void remove_timer(int timer_index) {
#ifndef USE_COMPAT_TIMER
if (timer_handles[timer_index]) {
if (!SDL_RemoveTimer(timer_handles[timer_index])) {
printf("timer_handles[%d] = %d\n", timer_index
, timer_handles
[timer_index
]);
sdlperror("SDL_RemoveTimer in remove_timer");
//quit(1);
}
timer_handles[timer_index] = 0;
}
#endif
}
int target_time;
Uint32 timer_callback(Uint32 interval, void *param) {
int now = SDL_GetTicks();
// let the timer finish 5 ms earlier to allow for overhead before the next frame is displayed
// this is somewhat ugly and may cause the game to run slightly too fast on fast systems (not tested)
int residual_wait_time = target_time - now - 5;
if (residual_wait_time > 0 && residual_wait_time <= 40) SDL_Delay(residual_wait_time);
SDL_Event event;
memset(&event
, 0, sizeof(event
));
event.type = SDL_USEREVENT;
event.user.code = userevent_TIMER;
event.user.data1 = param;
int timer_index = (uintptr_t)param;
remove_timer(timer_index);
// First remove the timer, then allow the other thread to continue.
SDL_PushEvent(&event);
#ifndef USE_COMPAT_TIMER
return 0;
#else
return interval;
#endif
}
void __pascal start_timer(int timer_index, int length) {
#ifdef USE_REPLAY
if (replaying && skipping_replay) return;
#endif
#ifndef USE_COMPAT_TIMER
if (timer_handles[timer_index]) {
remove_timer(timer_index);
timer_handles[timer_index] = 0;
}
timer_stopped[timer_index] = 0; // length<=0;
if (length <= 0) {
// We need the timer to finish *after* keypresses.
// So idle() is called at least once in do_wait(), and keypresses while fading are not ignored.
timer_callback(0, (void*)(uintptr_t)timer_index);
return;
}
int now = SDL_GetTicks();
double frametime = 1000.0 / 60.0;
// double frametime = (timer_index == 1) ? 16.60 : 1000.0 / 60.0;
int target_length = (int) (length * frametime);
// subtract 40ms to allow for variable lag; correct for this when the timer ends
target_time = now + target_length;
int modified_length = target_length - 40;
SDL_TimerID timer = SDL_AddTimer(modified_length, timer_callback, (void*)(uintptr_t)timer_index);
if (timer == 0) {
sdlperror("SDL_AddTimer");
quit(1);
}
timer_handles[timer_index] = timer;
#else
wait_time[timer_index] = length;
#endif
}
void toggle_fullscreen() {
uint32_t flags = SDL_GetWindowFlags(window_);
if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
SDL_SetWindowFullscreen(window_, 0);
SDL_ShowCursor(SDL_ENABLE);
}
else {
SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_ShowCursor(SDL_DISABLE);
}
}
void idle() {
// Wait for *one* event and process it, then return.
// Much like the x86 HLT instruction.
SDL_Event event;
if (SDL_WaitEvent(&event) == 0) {
sdlperror("SDL_WaitEvent");
quit(1);
}
// We still want to process all events in the queue
// For instance, there may be simultaneous SDL2 KEYDOWN and TEXTINPUT events
do { // while there are still events to be processed
switch (event.type) {
case SDL_KEYDOWN:
{
int modifier = event.key.keysym.mod;
int scancode = event.key.keysym.scancode;
// Handle these separately, so they won't interrupt things that are usually interrupted by a keypress. (pause, cutscene)
#ifdef USE_SCREENSHOT
if (scancode == SDL_SCANCODE_F12) {
if (modifier & KMOD_SHIFT) {
save_level_screenshot((modifier & KMOD_CTRL) != 0);
} else {
save_screenshot();
}
} else
#endif
if ((modifier & KMOD_ALT) &&
scancode == SDL_SCANCODE_RETURN)
{
// Only if the Enter key was pressed down right now.
if (key_states[scancode] == 0) {
// Alt-Enter: toggle fullscreen mode
toggle_fullscreen();
key_states[scancode] = 1;
}
} else {
key_states[scancode] = 1;
switch (scancode) {
// Keys that are ignored by themselves:
case SDL_SCANCODE_LCTRL:
case SDL_SCANCODE_LSHIFT:
case SDL_SCANCODE_LALT:
case SDL_SCANCODE_LGUI:
case SDL_SCANCODE_RCTRL:
case SDL_SCANCODE_RSHIFT:
case SDL_SCANCODE_RALT:
case SDL_SCANCODE_RGUI:
case SDL_SCANCODE_CAPSLOCK:
case SDL_SCANCODE_SCROLLLOCK:
case SDL_SCANCODE_NUMLOCKCLEAR:
case SDL_SCANCODE_APPLICATION:
case SDL_SCANCODE_PRINTSCREEN:
case SDL_SCANCODE_PAUSE:
break;
default:
last_key_scancode = scancode;
if (modifier & KMOD_SHIFT) last_key_scancode |= WITH_SHIFT;
if (modifier & KMOD_CTRL ) last_key_scancode |= WITH_CTRL ;
if (modifier & KMOD_ALT ) last_key_scancode |= WITH_ALT ;
}
#ifdef USE_AUTO_INPUT_MODE
switch (scancode) {
// Keys that are used for keyboard control:
case SDL_SCANCODE_LSHIFT:
case SDL_SCANCODE_RSHIFT:
case SDL_SCANCODE_LEFT:
case SDL_SCANCODE_RIGHT:
case SDL_SCANCODE_UP:
case SDL_SCANCODE_DOWN:
case SDL_SCANCODE_CLEAR:
case SDL_SCANCODE_HOME:
case SDL_SCANCODE_PAGEUP:
case SDL_SCANCODE_KP_2:
case SDL_SCANCODE_KP_4:
case SDL_SCANCODE_KP_5:
case SDL_SCANCODE_KP_6:
case SDL_SCANCODE_KP_7:
case SDL_SCANCODE_KP_8:
case SDL_SCANCODE_KP_9:
if (!is_keyboard_mode) {
is_keyboard_mode = 1;
is_joyst_mode = 0;
}
}
#endif
}
break;
}
case SDL_KEYUP:
key_states[event.key.keysym.scancode] = 0;
break;
case SDL_CONTROLLERAXISMOTION:
if (event.caxis.axis < 6) {
joy_axis[event.caxis.axis] = event.caxis.value;
#ifdef USE_AUTO_INPUT_MODE
if (!is_joyst_mode && (event.caxis.value >= joystick_threshold || event.caxis.value <= -joystick_threshold)) {
is_joyst_mode = 1;
is_keyboard_mode = 0;
}
#endif
}
break;
case SDL_CONTROLLERBUTTONDOWN:
#ifdef USE_AUTO_INPUT_MODE
if (!is_joyst_mode) {
is_joyst_mode = 1;
is_keyboard_mode = 0;
}
#endif
switch (event.cbutton.button)
{
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: joy_hat_states[0] = -1; break; // left
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 1; break; // right
case SDL_CONTROLLER_BUTTON_DPAD_UP: joy_hat_states[1] = -1; break; // up
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: joy_hat_states[1] = 1; break; // down
case SDL_CONTROLLER_BUTTON_A: joy_AY_buttons_state = 1; break; /*** A (down) ***/
case SDL_CONTROLLER_BUTTON_Y: joy_AY_buttons_state = -1; break; /*** Y (up) ***/
case SDL_CONTROLLER_BUTTON_X: joy_X_button_state = 1; break; /*** X (shift) ***/
case SDL_CONTROLLER_BUTTON_B: joy_B_button_state = 1; break; /*** B (unused) ***/
case SDL_CONTROLLER_BUTTON_START:
last_key_scancode = SDL_SCANCODE_R | WITH_CTRL; /*** start (restart game) ***/
break;
case SDL_CONTROLLER_BUTTON_BACK:
last_key_scancode = SDL_SCANCODE_A | WITH_CTRL; /*** back (restart level) ***/
break;
default: break;
}
break;
case SDL_CONTROLLERBUTTONUP:
switch (event.cbutton.button)
{
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: joy_hat_states[0] = 0; break; // left
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: joy_hat_states[0] = 0; break; // right
case SDL_CONTROLLER_BUTTON_DPAD_UP: joy_hat_states[1] = 0; break; // up
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: joy_hat_states[1] = 0; break; // down
case SDL_CONTROLLER_BUTTON_A: joy_AY_buttons_state = 0; break; /*** A (down) ***/
case SDL_CONTROLLER_BUTTON_Y: joy_AY_buttons_state = 0; break; /*** Y (up) ***/
case SDL_CONTROLLER_BUTTON_X: joy_X_button_state = 0; break; /*** X (shift) ***/
case SDL_CONTROLLER_BUTTON_B: joy_B_button_state = 0; break; /*** B (unused) ***/
default: break;
}
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
case SDL_JOYAXISMOTION:
// Only handle the event if the joystick is incompatible with the SDL_GameController interface.
// (Otherwise it will interfere with the normal action of the SDL_GameController API.)
if (!using_sdl_joystick_interface) {
break;
}
if (event.type == SDL_JOYAXISMOTION) {
if (event.jaxis.axis == SDL_JOYSTICK_X_AXIS) {
joy_axis[SDL_CONTROLLER_AXIS_LEFTX] = event.jaxis.value;
}
else if (event.jaxis.axis == SDL_JOYSTICK_Y_AXIS) {
joy_axis[SDL_CONTROLLER_AXIS_LEFTY] = event.jaxis.value;
}
// Disregard SDL_JOYAXISMOTION events within joystick 'dead zone'
int joy_x = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
int joy_y = joy_axis[SDL_CONTROLLER_AXIS_LEFTX];
if ((dword)(joy_x*joy_x) + (dword)(joy_y*joy_y) < (dword)(joystick_threshold*joystick_threshold)) {
break;
}
}
#ifdef USE_AUTO_INPUT_MODE
if (!is_joyst_mode) {
is_joyst_mode = 1;
is_keyboard_mode = 0;
}
#endif
if (event.type == SDL_JOYBUTTONDOWN) {
if (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y) joy_AY_buttons_state = -1; // Y (up)
else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X) joy_X_button_state = -1; // X (shift)
}
else if (event.type == SDL_JOYBUTTONUP) {
if (event.jbutton.button == SDL_JOYSTICK_BUTTON_Y) joy_AY_buttons_state = 0; // Y (up)
else if (event.jbutton.button == SDL_JOYSTICK_BUTTON_X) joy_X_button_state = 0; // X (shift)
}
break;
case SDL_TEXTINPUT:
last_text_input = event.text.text[0]; // UTF-8 formatted char text input
break;
case SDL_WINDOWEVENT:
// In case the user switches away while holding a key: do as if all keys were released.
// (DOSBox does the same.)
/* // not implemented in SDL2 for now
*
if ((event.active.state & SDL_APPINPUTFOCUS) && event.active.gain == 0) {
memset(key_states, 0, sizeof(key_states));
}
// Note: event.active.state can contain multiple flags or'ed.
// 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.
if ((event.active.state & SDL_APPACTIVE) && event.active.gain == 1) {
request_screen_update();
}
*/
switch (event.window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED:
window_resized();
//case SDL_WINDOWEVENT_MOVED:
//case SDL_WINDOWEVENT_RESTORED:
case SDL_WINDOWEVENT_EXPOSED:
request_screen_update();
break;
}
break;
case SDL_USEREVENT:
if (event.user.code == userevent_TIMER /*&& event.user.data1 == (void*)timer_index*/) {
#ifndef USE_COMPAT_TIMER
int timer_index = (uintptr_t) event.user.data1;
timer_stopped[timer_index] = 1;
//printf("timer_index = %d\n", timer_index);
// 2014-08-27: According to the output of the next line, handle is always NULL.
// 2014-08-28: Except when you interrupt fading of the cutscene.
//printf("timer_handles[timer_index] = %p\n", timer_handles[timer_index]);
// 2014-08-27: However, this line will change something: it makes the game too fast. Weird...
// 2014-08-28: Wait, now it doesn't...
//timer_handles[timer_index] = 0;
#else
int index;
for (index = 0; index < 2; ++index) {
if (wait_time[index] > 0) --wait_time[index];
}
#endif
} else if (event.user.code == userevent_SOUND) {
//sound_timer = 0;
#ifndef USE_MIXER
//stop_sounds();
#endif
}
break;
case SDL_QUIT:
quit(0);
break;
}
} while (SDL_PollEvent(&event) == 1);
}
word word_1D63A = 1;
// seg009:0EA9
int __pascal do_wait(int timer_index) {
while (! has_timer_stopped(timer_index)) {
idle();
int key = do_paused();
if (key != 0 && (word_1D63A != 0 || key == 0x1B)) return 1;
}
return 0;
}
void __pascal do_simple_wait(int timer_index) {
#ifdef USE_REPLAY
if ((replaying && skipping_replay) || is_validate_mode) return;
#endif
while (! has_timer_stopped(timer_index)) {
idle();
}
}
#ifdef USE_COMPAT_TIMER
SDL_TimerID global_timer = NULL;
#endif
// seg009:78E9
void __pascal far init_timer(int frequency) {
#ifndef USE_COMPAT_TIMER
fps = frequency;
#else
if (global_timer != 0) {
if (!SDL_RemoveTimer(global_timer)) {
sdlperror("SDL_RemoveTimer");
}
}
global_timer = SDL_AddTimer(1000/frequency, timer_callback, NULL);
if (global_timer == 0) {
sdlperror("SDL_AddTimer");
quit(1);
}
#endif
}
// seg009:35F6
void __pascal far set_clip_rect(const rect_type far *rect) {
SDL_Rect clip_rect;
rect_to_sdlrect(rect, &clip_rect);
SDL_SetClipRect(current_target_surface, &clip_rect);
}
// seg009:365C
void __pascal far reset_clip_rect() {
SDL_SetClipRect(current_target_surface, NULL);
}
// seg009:1983
void __pascal far set_bg_attr(int vga_pal_index,int hc_pal_index) {
// stub
#ifdef USE_FLASH
//palette[vga_pal_index] = vga_palette[hc_pal_index];
if (!enable_flash) return;
if (vga_pal_index == 0) {
/*
if (SDL_SetAlpha(offscreen_surface, SDL_SRCALPHA, 0) != 0) {
sdlperror("SDL_SetAlpha");
quit(1);
}
*/
// Make the black pixels transparent.
if (SDL_SetColorKey(offscreen_surface, SDL_TRUE, 0) != 0) { // SDL_SRCCOLORKEY old
sdlperror("SDL_SetColorKey");
quit(1);
}
SDL_Rect rect = {0,0,0,0};
rect.w = offscreen_surface->w;
rect.h = offscreen_surface->h;
rgb_type palette_color = palette[hc_pal_index];
uint32_t rgb_color = SDL_MapRGB(onscreen_surface_->format, palette_color.r<<2, palette_color.g<<2, palette_color.b<<2) /*& 0xFFFFFF*/;
//SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
// First clear the screen with the color of the flash.
if (safe_SDL_FillRect(onscreen_surface_, &rect, rgb_color) != 0) {
sdlperror("SDL_FillRect");
quit(1);
}
//SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0);
if (upside_down) {
flip_screen(offscreen_surface);
}
// Then draw the offscreen image onto it.
if (SDL_BlitSurface(offscreen_surface, &rect, onscreen_surface_, &rect) != 0) {
sdlperror("SDL_BlitSurface");
quit(1);
}
#ifdef USE_LIGHTING
if (hc_pal_index == 0) update_lighting(&rect_top);
#endif
if (upside_down) {
flip_screen(offscreen_surface);
}
// And show it!
request_screen_update();
// Give some time to show the flash.
//SDL_Flip(onscreen_surface_);
// if (hc_pal_index != 0) SDL_Delay(2*(1000/60));
//SDL_Flip(onscreen_surface_);
/*
if (SDL_SetAlpha(offscreen_surface, 0, 0) != 0) {
sdlperror("SDL_SetAlpha");
quit(1);
}
*/
if (SDL_SetColorKey(offscreen_surface, 0, 0) != 0) {
sdlperror("SDL_SetColorKey");
quit(1);
}
}
#endif // USE_FLASH
}
// seg009:07EB
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) {
*dest = *source;
dest->left += d_left;
dest->top += d_top;
dest->right += d_right;
dest->bottom += d_bottom;
return dest;
}
// seg009:3AA5
rect_type far *__pascal offset2_rect(rect_type far *dest,const rect_type far *source,int delta_x,int delta_y) {
dest->top = source->top + delta_y;
dest->left = source->left + delta_x;
dest->bottom = source->bottom + delta_y;
dest->right = source->right + delta_x;
return dest;
}
#ifdef USE_FADE
// seg009:19EF
void __pascal far fade_in_2(surface_type near *source_surface,int which_rows) {
palette_fade_type far* palette_buffer;
if (graphics_mode == gmMcgaVga) {
palette_buffer = make_pal_buffer_fadein(source_surface, which_rows, 2);
while (fade_in_frame(palette_buffer) == 0) {
pop_wait(timer_1, 0); // modified
}
pal_restore_free_fadein(palette_buffer);
} else {
// ...
}
}
// seg009:1A51
palette_fade_type far *__pascal make_pal_buffer_fadein(surface_type *source_surface,int which_rows,int wait_time) {
palette_fade_type far* palette_buffer;
word curr_row;
word var_8;
word curr_row_mask;
palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
palette_buffer->which_rows = which_rows;
palette_buffer->wait_time = wait_time;
palette_buffer->fade_pos = 0x40;
palette_buffer->proc_restore_free = &pal_restore_free_fadein;
palette_buffer->proc_fade_frame = &fade_in_frame;
read_palette_256(palette_buffer->original_pal);
memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
var_8 = 0;
for (curr_row = 0, curr_row_mask = 1; curr_row < 0x10; ++curr_row, curr_row_mask<<=1) {
if (which_rows & curr_row_mask) {
memset_far(palette_buffer->faded_pal + (curr_row<<4), 0, sizeof(rgb_type[0x10]));
set_pal_arr(curr_row<<4, 0x10, NULL, (var_8++&3)==0);
}
}
//method_1_blit_rect(onscreen_surface_, source_surface, &screen_rect, &screen_rect, 0);
// for RGB
//method_5_rect(&screen_rect, 0, 0);
return palette_buffer;
}
// seg009:1B64
void __pascal far pal_restore_free_fadein(palette_fade_type far *palette_buffer) {
set_pal_256(palette_buffer->original_pal);
free_far(palette_buffer);
// for RGB
method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
}
// seg009:1B88
int __pascal far fade_in_frame(palette_fade_type far *palette_buffer) {
rgb_type* faded_pal_ptr;
word start;
word column;
rgb_type* original_pal_ptr;
word current_row_mask;
// void* var_12;
/**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
//printf("start ticks = %u\n",SDL_GetTicks());
--palette_buffer->fade_pos;
for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
if (palette_buffer->which_rows & current_row_mask) {
//var_12 = palette_buffer->
original_pal_ptr = palette_buffer->original_pal + start;
faded_pal_ptr = palette_buffer->faded_pal + start;
for (column = 0; column<0x10; ++column) {
if (original_pal_ptr[column].r > palette_buffer->fade_pos) {
++faded_pal_ptr[column].r;
}
if (original_pal_ptr[column].g > palette_buffer->fade_pos) {
++faded_pal_ptr[column].g;
}
if (original_pal_ptr[column].b > palette_buffer->fade_pos) {
++faded_pal_ptr[column].b;
}
}
}
}
column = 0;
for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
if (palette_buffer->which_rows & current_row_mask) {
set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
}
}
int h = offscreen_surface->h;
if (SDL_LockSurface(onscreen_surface_) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
if (SDL_LockSurface(offscreen_surface) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
int y,x;
int on_stride = onscreen_surface_->pitch;
int off_stride = offscreen_surface->pitch;
int fade_pos = palette_buffer->fade_pos;
for (y = 0; y < h; ++y) {
byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
for (x = 0; x < on_stride; ++x) {
//if (*off_pixel_ptr > palette_buffer->fade_pos) *pixel_ptr += 4;
int v = *off_pixel_ptr - fade_pos*4;
if (v<0) v=0;
*on_pixel_ptr = v;
++on_pixel_ptr; ++off_pixel_ptr;
}
}
SDL_UnlockSurface(onscreen_surface_);
SDL_UnlockSurface(offscreen_surface);
//SDL_UpdateRect(onscreen_surface_, 0, 0, 0, 0); // debug
request_screen_update();
/**/do_simple_wait(1); // can interrupt fading of cutscene
//do_wait(timer_1); // can interrupt fading of main title
//printf("end ticks = %u\n",SDL_GetTicks());
return palette_buffer->fade_pos == 0;
}
// seg009:1CC9
void __pascal far fade_out_2(int rows) {
palette_fade_type far *palette_buffer;
if (graphics_mode == gmMcgaVga) {
palette_buffer = make_pal_buffer_fadeout(rows, 2);
while (fade_out_frame(palette_buffer) == 0) {
pop_wait(timer_1, 0); // modified
}
pal_restore_free_fadeout(palette_buffer);
} else {
// ...
}
}
// seg009:1D28
palette_fade_type far *__pascal make_pal_buffer_fadeout(int which_rows,int wait_time) {
palette_fade_type far *palette_buffer;
palette_buffer = (palette_fade_type*) malloc_far(sizeof(palette_fade_type));
palette_buffer->which_rows = which_rows;
palette_buffer->wait_time = wait_time;
palette_buffer->fade_pos = 00; // modified
palette_buffer->proc_restore_free = &pal_restore_free_fadeout;
palette_buffer->proc_fade_frame = &fade_out_frame;
read_palette_256(palette_buffer->original_pal);
memcpy_far(palette_buffer->faded_pal, palette_buffer->original_pal, sizeof(palette_buffer->faded_pal));
// for RGB
method_1_blit_rect(onscreen_surface_, offscreen_surface, &screen_rect, &screen_rect, 0);
return palette_buffer;
}
// seg009:1DAF
void __pascal far pal_restore_free_fadeout(palette_fade_type far *palette_buffer) {
surface_type* surface;
surface = current_target_surface;
current_target_surface = onscreen_surface_;
draw_rect(&screen_rect, 0);
current_target_surface = surface;
set_pal_256(palette_buffer->original_pal);
free_far(palette_buffer);
// for RGB
method_5_rect(&screen_rect, 0, 0);
}
// seg009:1DF7
int __pascal far fade_out_frame(palette_fade_type far *palette_buffer) {
rgb_type* faded_pal_ptr;
word start;
word var_8;
word column;
word current_row_mask;
byte* curr_color_ptr;
var_8 = 1;
++palette_buffer->fade_pos; // modified
/**/start_timer(timer_1, palette_buffer->wait_time); // too slow?
for (start=0,current_row_mask=1; start<0x100; start+=0x10, current_row_mask<<=1) {
if (palette_buffer->which_rows & current_row_mask) {
//var_12 = palette_buffer->
//original_pal_ptr = palette_buffer->original_pal + start;
faded_pal_ptr = palette_buffer->faded_pal + start;
for (column = 0; column<0x10; ++column) {
curr_color_ptr = &faded_pal_ptr[column].r;
if (*curr_color_ptr != 0) {
--*curr_color_ptr;
var_8 = 0;
}
curr_color_ptr = &faded_pal_ptr[column].g;
if (*curr_color_ptr != 0) {
--*curr_color_ptr;
var_8 = 0;
}
curr_color_ptr = &faded_pal_ptr[column].b;
if (*curr_color_ptr != 0) {
--*curr_color_ptr;
var_8 = 0;
}
}
}
}
column = 0;
for (start = 0, current_row_mask = 1; start<0x100; start+=0x10, current_row_mask<<=1) {
if (palette_buffer->which_rows & current_row_mask) {
set_pal_arr(start, 0x10, palette_buffer->faded_pal + start, (column++&3)==0);
}
}
int h = offscreen_surface->h;
if (SDL_LockSurface(onscreen_surface_) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
if (SDL_LockSurface(offscreen_surface) != 0) {
sdlperror("SDL_LockSurface");
quit(1);
}
int y,x;
int on_stride = onscreen_surface_->pitch;
int off_stride = offscreen_surface->pitch;
int fade_pos = palette_buffer->fade_pos;
for (y = 0; y < h; ++y) {
byte* on_pixel_ptr = (byte*)onscreen_surface_->pixels + on_stride * y;
byte* off_pixel_ptr = (byte*)offscreen_surface->pixels + off_stride * y;
for (x = 0; x < on_stride; ++x) {
//if (*pixel_ptr >= 4) *pixel_ptr -= 4;
int v = *off_pixel_ptr - fade_pos*4;
if (v<0) v=0;
*on_pixel_ptr = v;
++on_pixel_ptr; ++off_pixel_ptr;
}
}
SDL_UnlockSurface(onscreen_surface_);
SDL_UnlockSurface(offscreen_surface);
request_screen_update();
/**/do_simple_wait(1); // can interrupt fading of cutscene
//do_wait(timer_1); // can interrupt fading of main title
return var_8;
}
// seg009:1F28
void __pascal far read_palette_256(rgb_type far *target) {
int i;
for (i = 0; i < 256; ++i) {
target[i] = palette[i];
}
}
// seg009:1F5E
void __pascal far set_pal_256(rgb_type far *source) {
int i;
for (i = 0; i < 256; ++i) {
palette[i] = source[i];
}
}
#endif // USE_FADE
void set_chtab_palette(chtab_type* chtab, byte* colors, int n_colors) {
if (chtab != NULL) {
SDL_Color
* scolors
= (SDL_Color
*) malloc(n_colors
*sizeof(SDL_Color
));
int i;
//printf("scolors\n",i);
for (i = 0; i < n_colors; ++i) {
//printf("i=%d\n",i);
scolors[i].r = *colors << 2; ++colors;
scolors[i].g = *colors << 2; ++colors;
scolors[i].b = *colors << 2; ++colors;
scolors[i].a = SDL_ALPHA_OPAQUE; // the SDL2 SDL_Color struct has an alpha component
}
// Color 0 of the palette data is not used, it is replaced by the background color.
// Needed for correct alternate colors (v1.3) of level 8.
scolors[0].r = scolors[0].g = scolors[0].b = 0;
//printf("setcolors\n",i);
for (i = 0; i < chtab->n_images; ++i) {
//printf("i=%d\n",i);
image_type* current_image = chtab->images[i];
if (current_image != NULL) {
int n_colors_to_be_set = n_colors;
SDL_Palette* current_palette = current_image->format->palette;
// one of the guard images (i=25) is only a single transparent pixel
// this caused SDL_SetPaletteColors to fail, I think because that palette contains only 2 colors
if (current_palette->ncolors < n_colors_to_be_set)
n_colors_to_be_set = current_palette->ncolors;
if (SDL_SetPaletteColors(current_palette, scolors, 0, n_colors_to_be_set) != 0) {
sdlperror("SDL_SetPaletteColors");
quit(1);
}
}
}
}
}
int has_timer_stopped(int index) {
#ifdef USE_COMPAT_TIMER
return wait_time[index] == 0;
#else
return timer_stopped[index];
#endif
}