From 4859c25ba00f6e54a7dccfd086224cb8d869d712 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Mon, 16 Jul 2018 20:43:34 +0800 Subject: [PATCH] Import blues from 116fbc7a --- Makefile | 2 +- decode.c | 8 +-- fileio.c | 3 +- game.c | 6 +- level.c | 39 +++++++----- main.c | 20 ++++++ opcodes.c | 6 +- screen.c | 30 ++++----- sys.h | 13 ++-- sys_sdl2.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++------ 10 files changed, 231 insertions(+), 71 deletions(-) diff --git a/Makefile b/Makefile index 492d264..c5af10c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ SDL_CFLAGS := `sdl2-config --cflags` -SDL_LIBS := `sdl2-config --libs` -lSDL2_mixer +SDL_LIBS := `sdl2-config --libs` SRCS := decode.c fileio.c game.c level.c main.c opcodes.c resource.c screen.c sound.c staticres.c sys_sdl2.c triggers.c unpack.c util.c OBJS := $(SRCS:.c=.o) diff --git a/decode.c b/decode.c index acba802..6de03f3 100644 --- a/decode.c +++ b/decode.c @@ -6,9 +6,9 @@ void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *ds src_pitch /= 8; assert((w & 7) == 0); w /= 8; + dst += dst_y * dst_pitch + dst_x; const int bitplane_size = w * h; for (int y = 0; y < h; ++y) { - const int y_offset = dst_y + y; for (int x = 0; x < w; ++x) { for (int i = 0; i < 8; ++i) { int color = 0; @@ -18,14 +18,12 @@ void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *ds color |= 1 << (3 - bit); } } - if (color != 0) { - const int x_offset = dst_x + (x * 8 + i); - dst[y_offset * dst_pitch + x_offset] = color; - } + dst[x * 8 + i] = color; } ++src; } src += src_pitch - w; + dst += dst_pitch; } } diff --git a/fileio.c b/fileio.c index 280ff3e..38b0650 100644 --- a/fileio.c +++ b/fileio.c @@ -55,8 +55,9 @@ int fio_open(const char *filename, int error_flag) { if (!file_slot->fp) { if (error_flag) { print_error("Unable to open file '%s'", filename); + } else { + print_warning("Unable to open file '%s'", filename); } - print_warning("Unable to open file '%s'", filename); slot = -1; } else { fseek(file_slot->fp, 0, SEEK_END); diff --git a/game.c b/game.c index 8491903..7b3a645 100644 --- a/game.c +++ b/game.c @@ -282,12 +282,12 @@ static void do_inter_screen() { } void game_main() { + play_music(0); g_vars.screen_w = GAME_SCREEN_W; g_vars.screen_h = GAME_SCREEN_H; g_vars.screen_draw_offset = 0; screen_flip(); screen_init(); - g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, "Blues Brothers"); g_vars.start_level = 0; load_sqv("sprite.sqv", g_res.spr_sqv, 0); if (g_options.amiga_sprites) { @@ -303,7 +303,7 @@ void game_main() { g_sys.set_palette_amiga(palette, 16); } do_title_screen(); - while (g_sys.input.quit == 0) { + while (!g_sys.input.quit) { if (!g_vars.level_completed_flag) { // _level_cheat_code = 0; g_vars.game_over_flag = 0; @@ -316,7 +316,7 @@ void game_main() { } screen_unk5(); } - while (g_sys.input.quit == 0 && g_vars.play_level_flag) { + while (!g_sys.input.quit && g_vars.play_level_flag) { if (!g_vars.game_over_flag) { do_inter_screen(); } diff --git a/level.c b/level.c index 41e7a27..64216d8 100644 --- a/level.c +++ b/level.c @@ -58,16 +58,12 @@ static const struct { { "concert.blk", "concert.tbl", 0, "concert.bin", "ennemi6" }, }; -static const char *_demo_filenames[] = { - "demomag.ck1", "demomag.ck2", "demomag.sql" -}; - void load_level_data(int num) { print_debug(DBG_GAME, "load_level_data num %d", num); if (num == 0 && (g_res.flags & RESOURCE_FLAGS_DEMO)) { - load_ck(_demo_filenames[0], 0x6000); - load_ck(_demo_filenames[1], 0x8000); - load_sql(_demo_filenames[2]); + load_ck("demomag.ck1", 0x6000); + load_ck("demomag.ck2", 0x8000); + load_sql("demomag.sql"); } else { load_ck(_levels[num].ck1, 0x6000); load_ck(_levels[num].ck2, 0x8000); @@ -96,9 +92,18 @@ void load_level_data(int num) { } static void init_level() { + static const uint16_t restart_xpos[] = { 720, 720, 912, 912, 336, 352, 288, 304, 960, 976, 64, 80 }; + static const uint16_t restart_ypos[] = { 496, 496, 752, 752, 816, 816, 304, 304, 592, 592, 352, 352 }; + if (g_vars.play_demo_flag) { + g_vars.objects[OBJECT_NUM_PLAYER1].unk60 = 0; + } int xpos = g_options.start_xpos16; if (xpos < 0) { - xpos = (g_vars.level_xpos[OBJECT_NUM_PLAYER1] >> 4) - 10; + xpos = g_vars.level_xpos[OBJECT_NUM_PLAYER1]; + if (!g_vars.two_players_flag && (g_vars.objects[OBJECT_NUM_PLAYER1].unk60 || g_vars.objects[OBJECT_NUM_PLAYER2].unk60)) { + xpos = restart_xpos[g_vars.level * 2]; + } + xpos = (xpos >> 4) - 10; } else { g_vars.level_xpos[OBJECT_NUM_PLAYER1] = (xpos << 4) + 10; } @@ -109,7 +114,11 @@ static void init_level() { int ypos = g_options.start_ypos16; if (ypos < 0) { - ypos = (g_vars.level_ypos[OBJECT_NUM_PLAYER1] >> 4) - 6; + ypos = g_vars.level_ypos[OBJECT_NUM_PLAYER1]; + if (!g_vars.two_players_flag && (g_vars.objects[OBJECT_NUM_PLAYER1].unk60 || g_vars.objects[OBJECT_NUM_PLAYER2].unk60)) { + ypos = restart_ypos[g_vars.level * 2]; + } + ypos = (ypos >> 4) - 6; } else { g_vars.level_ypos[OBJECT_NUM_PLAYER1] = (ypos << 4) + 6; } @@ -158,7 +167,8 @@ static void init_level() { obj->xpos = g_vars.level_xpos[i]; obj->ypos = g_vars.level_ypos[i]; } else { - print_warning("init_level: obj #%d unk60 %d", i, obj->unk60); + obj->xpos = restart_xpos[g_vars.level * 2 + obj->type]; + obj->ypos = restart_ypos[g_vars.level * 2 + obj->type]; } print_debug(DBG_GAME, "init_level obj #%d pos %d,%d", i, obj->xpos, obj->ypos); } else { @@ -2153,9 +2163,9 @@ static void draw_foreground_tiles() { } void do_level() { - const int w = TILEMAP_SCREEN_W / 16; - for (int i = 0; i < 256; ++i) { - g_vars.screen_tile_lut[i] = (i / w) * 640 + (i % w); + static const int W = 320 / 16; + for (int tile_num = 0; tile_num < 256; ++tile_num) { + g_vars.screen_tile_lut[tile_num] = (tile_num / W) * 640 + (tile_num % W); } g_vars.screen_tilemap_w = level_dim[g_vars.level * 2]; g_vars.screen_tilemap_h = level_dim[g_vars.level * 2 + 1]; @@ -2174,7 +2184,6 @@ void do_level() { if (g_options.amiga_colors) { g_sys.set_palette_amiga(_colors_data + g_vars.level * 16, 0); } - // _time_sync_ptr[1] = 1: g_vars.inp_keyboard[0xB9] = 0; // SPACE g_vars.screen_draw_offset = TILEMAP_OFFSET_Y * 40; g_vars.update_objects_counter = 0; @@ -2228,7 +2237,7 @@ void do_level() { screen_clear_sprites(); // _draw_last_sprite_flag = 1; - } while (g_sys.input.quit == 0 && !g_vars.quit_level_flag); + } while (!g_sys.input.quit && !g_vars.quit_level_flag); g_vars.screen_draw_offset -= TILEMAP_OFFSET_Y * 40; screen_unk5(); if (g_options.amiga_copper_bars) { diff --git a/main.c b/main.c index 0de9ca9..07f6837 100644 --- a/main.c +++ b/main.c @@ -12,6 +12,10 @@ struct options_t g_options; static const char *DEFAULT_DATA_PATH = "."; +static const int DEFAULT_SCALE_FACTOR = 2; + +static const char *DEFAULT_SCALE_FILTER = 0; // nearest pixel sampling + static const char *USAGE = "Usage: %s [OPTIONS]...\n" " --datapath=PATH Path to data files (default '.')\n" @@ -26,6 +30,9 @@ int main(int argc, char *argv[]) { // g_options.amiga_sprites = true; // g_options.amiga_data = true; const char *data_path = DEFAULT_DATA_PATH; + int scale_factor = DEFAULT_SCALE_FACTOR; + const char *scale_filter = DEFAULT_SCALE_FILTER; + bool fullscreen = false; if (argc == 2) { struct stat st; if (stat(argv[1], &st) == 0 && S_ISDIR(st.st_mode)) { @@ -39,6 +46,9 @@ int main(int argc, char *argv[]) { { "debug", required_argument, 0, 3 }, { "cheats", required_argument, 0, 4 }, { "startpos", required_argument, 0, 5 }, + { "fullscreen", no_argument, 0, 6 }, + { "scale", required_argument, 0, 7 }, + { "filter", required_argument, 0, 8 }, { 0, 0, 0, 0 }, }; int index; @@ -62,6 +72,15 @@ int main(int argc, char *argv[]) { case 5: sscanf(optarg, "%dx%d", &g_options.start_xpos16, &g_options.start_ypos16); break; + case 6: + fullscreen = true; + break; + case 7: + scale_factor = atoi(optarg); + break; + case 8: + scale_filter = strdup(optarg); + break; default: fprintf(stdout, USAGE, argv[0]); return -1; @@ -70,6 +89,7 @@ int main(int argc, char *argv[]) { fio_init(data_path); res_init(); g_sys.init(); + g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, "Blues Brothers", scale_factor, scale_filter, fullscreen); sound_init(SYS_AUDIO_FREQ); game_main(); sound_fini(); diff --git a/opcodes.c b/opcodes.c index 846fc2d..f3a98b1 100644 --- a/opcodes.c +++ b/opcodes.c @@ -300,9 +300,13 @@ static void object_func_op13(struct object_t *obj) { obj->special_anim = 1; } +static void object_func_op14_helper(int x1, int y1, int x2, int y2, int color) { + print_warning("object_func_op14_helper: unimplemented"); +} + static void object_func_op14(struct object_t *obj) { obj->special_anim = 2; - // sub_1AD3B(obj->xpos, level_ypos_egou[obj->unk5D] - _screen_tilemap_yorigin, obj->xpos, obj->ypos - 5, 3); + object_func_op14_helper(obj->xpos, level_ypos_egou[obj->unk5D] - g_vars.screen_tilemap_yorigin, obj->xpos, obj->ypos - 5, 3); if (obj->elevator_direction == 1) { if (obj->moving_direction < 25) { ++obj->moving_direction; diff --git a/screen.c b/screen.c index dde3a3f..d104a48 100644 --- a/screen.c +++ b/screen.c @@ -5,8 +5,8 @@ #include "sys.h" #include "util.h" -#define MAX_SPRITESHEET_W 1024 -#define MAX_SPRITESHEET_H 512 +#define MAX_SPRITESHEET_W 512 +#define MAX_SPRITESHEET_H 512 void screen_init() { } @@ -183,28 +183,24 @@ static void decode_graphics(int spr_type, int start, int end) { } current_x = 0; max_h = h; - if (g_options.amiga_sprites) { - decode_amiga_planar8(ptr, w / 8, h, (start == 0) ? 3 : 4, data, MAX_SPRITESHEET_W, current_x, current_y); - } else { - decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); - } - r[j].x = current_x; - r[j].y = current_y; } else { - if (g_options.amiga_sprites) { - decode_amiga_planar8(ptr, w / 8, h, (start == 0) ? 3 : 4, data, MAX_SPRITESHEET_W, current_x, current_y); - } else { - decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); - } - r[j].x = current_x; - r[j].y = current_y; - current_x += w; if (h > max_h) { max_h = h; } } + if (g_options.amiga_sprites) { + decode_amiga_planar8(ptr, w / 8, h, (start == 0) ? 3 : 4, data, MAX_SPRITESHEET_W, current_x, current_y); + } else { + decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); + } + r[j].x = current_x; + r[j].y = current_y; r[j].w = w; r[j].h = h; + current_x += w; + if (h > max_h) { + max_h = h; + } } assert(max_w <= MAX_SPRITESHEET_W); assert(current_y + max_h <= MAX_SPRITESHEET_H); diff --git a/sys.h b/sys.h index 8e444bd..83e1f53 100644 --- a/sys.h +++ b/sys.h @@ -12,13 +12,10 @@ #define SYS_AUDIO_FREQ 22050 struct input_t { - char quit; - char escape; - char space; - char direction; - char functions[12]; - char digits[10]; - char alphabet[26]; + uint8_t direction; + bool quit; + bool escape; + bool space; }; typedef void (*sys_audio_cb)(void *, uint8_t *data, int len); @@ -32,7 +29,7 @@ struct sys_t { struct input_t input; int (*init)(); void (*fini)(); - void (*set_screen_size)(int w, int h, const char *caption); + void (*set_screen_size)(int w, int h, const char *caption, int scale, const char *filter, bool fullscreen); void (*set_screen_palette)(const uint8_t *colors, int); void (*set_palette_amiga)(const uint16_t *colors, int offset); void (*set_copper_bars)(const uint16_t *data); diff --git a/sys_sdl2.c b/sys_sdl2.c index fda3f1e..70595f6 100644 --- a/sys_sdl2.c +++ b/sys_sdl2.c @@ -5,7 +5,6 @@ #define COPPER_BARS_H 80 -static const int SCALE = 2; static const int FADE_STEPS = 16; struct spritesheet_t { @@ -37,9 +36,10 @@ static uint32_t *_screen_buffer; static struct input_t *_input = &g_sys.input; static int _copper_color; static uint32_t _copper_palette[COPPER_BARS_H]; +static SDL_GameController *_controller; static int sdl2_init() { - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); SDL_ShowCursor(SDL_DISABLE); _screen_w = _screen_h = 0; _window = 0; @@ -49,10 +49,32 @@ static int sdl2_init() { memset(_screen_palette, 0, sizeof(_screen_palette)); _screen_buffer = 0; _copper_color = -1; + SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); + _controller = 0; + const int count = SDL_NumJoysticks(); + if (count > 0) { + for (int i = 0; i < count; ++i) { + if (SDL_IsGameController(i)) { + _controller = SDL_GameControllerOpen(i); + if (_controller) { + fprintf(stdout, "Using controller '%s'\n", SDL_GameControllerName(_controller)); + break; + } + } + } + } return 0; } static void sdl2_fini() { + if (_fmt) { + SDL_FreeFormat(_fmt); + _fmt = 0; + } + if (_texture) { + SDL_DestroyTexture(_texture); + _texture = 0; + } if (_renderer) { SDL_DestroyRenderer(_renderer); _renderer = 0; @@ -61,27 +83,31 @@ static void sdl2_fini() { SDL_DestroyWindow(_window); _window = 0; } - if (_texture) { - SDL_DestroyTexture(_texture); - _texture = 0; - } - if (_fmt) { - SDL_FreeFormat(_fmt); - _fmt = 0; - } free(_screen_buffer); + if (_controller) { + SDL_GameControllerClose(_controller); + _controller = 0; + } SDL_Quit(); } -static void sdl2_set_screen_size(int w, int h, const char *caption) { +static void sdl2_set_screen_size(int w, int h, const char *caption, int scale, const char *filter, bool fullscreen) { assert(_screen_w == 0 && _screen_h == 0); // abort if called more than once _screen_w = w; _screen_h = h; - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); /* nearest pixel sampling */ - const int window_w = w * SCALE; - const int window_h = h * SCALE; // * 4 / 3; - _window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, SDL_WINDOW_RESIZABLE); + if (!filter || strcmp(filter, "nearest") == 0) { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + } else if (strcmp(filter, "linear") == 0) { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + } else { + print_warning("Unhandled filter '%s'", filter); + } + const int window_w = w * scale; + const int window_h = h * scale; + const int flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; + _window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, flags); _renderer = SDL_CreateRenderer(_window, -1, 0); + SDL_RenderSetLogicalSize(_renderer, w, h); print_debug(DBG_SYSTEM, "set_screen_size %d,%d", _screen_w, _screen_h); _screen_buffer = (uint32_t *)calloc(_screen_w * _screen_h, sizeof(uint32_t)); if (!_screen_buffer) { @@ -190,10 +216,10 @@ static void sdl2_update_screen(const uint8_t *p, int present) { struct sprite_t *spr = &_sprites[i]; struct spritesheet_t *sheet = &_spritesheets[spr->sheet]; SDL_Rect r; - r.x = spr->x * SCALE; - r.y = spr->y * SCALE; - r.w = sheet->r[spr->num].w * SCALE; - r.h = sheet->r[spr->num].h * SCALE; + r.x = spr->x; + r.y = spr->y; + r.w = sheet->r[spr->num].w; + r.h = sheet->r[spr->num].h; if (!spr->xflip) { SDL_RenderCopy(_renderer, sheet->texture, &sheet->r[spr->num], &r); } else { @@ -205,7 +231,7 @@ static void sdl2_update_screen(const uint8_t *p, int present) { } } -static void handle_keyevent(int keysym, int keydown) { +static void handle_keyevent(int keysym, bool keydown) { switch (keysym) { case SDLK_LEFT: if (keydown) { @@ -239,6 +265,85 @@ static void handle_keyevent(int keysym, int keydown) { case SDLK_SPACE: _input->space = keydown; break; + case SDLK_ESCAPE: + _input->escape = keydown; + break; + } +} + +static void handle_controlleraxis(int axis, int value) { + static const int THRESHOLD = 3200; + switch (axis) { + case SDL_CONTROLLER_AXIS_LEFTX: + case SDL_CONTROLLER_AXIS_RIGHTX: + if (value < -THRESHOLD) { + _input->direction |= INPUT_DIRECTION_LEFT; + } else { + _input->direction &= ~INPUT_DIRECTION_LEFT; + } + if (value > THRESHOLD) { + _input->direction |= INPUT_DIRECTION_RIGHT; + } else { + _input->direction &= ~INPUT_DIRECTION_RIGHT; + } + break; + case SDL_CONTROLLER_AXIS_LEFTY: + case SDL_CONTROLLER_AXIS_RIGHTY: + if (value < -THRESHOLD) { + _input->direction |= INPUT_DIRECTION_UP; + } else { + _input->direction &= ~INPUT_DIRECTION_UP; + } + if (value > THRESHOLD) { + _input->direction |= INPUT_DIRECTION_DOWN; + } else { + _input->direction &= ~INPUT_DIRECTION_DOWN; + } + break; + } +} + +static void handle_controllerbutton(int button, bool pressed) { + switch (button) { + case SDL_CONTROLLER_BUTTON_A: + case SDL_CONTROLLER_BUTTON_B: + case SDL_CONTROLLER_BUTTON_X: + case SDL_CONTROLLER_BUTTON_Y: + _input->space = pressed; + break; + case SDL_CONTROLLER_BUTTON_BACK: + _input->escape = pressed; + break; + case SDL_CONTROLLER_BUTTON_START: + break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + if (pressed) { + _input->direction |= INPUT_DIRECTION_UP; + } else { + _input->direction &= ~INPUT_DIRECTION_UP; + } + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + if (pressed) { + _input->direction |= INPUT_DIRECTION_DOWN; + } else { + _input->direction &= ~INPUT_DIRECTION_DOWN; + } + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + if (pressed) { + _input->direction |= INPUT_DIRECTION_LEFT; + } else { + _input->direction &= ~INPUT_DIRECTION_LEFT; + } + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + if (pressed) { + _input->direction |= INPUT_DIRECTION_RIGHT; + } else { + _input->direction &= ~INPUT_DIRECTION_RIGHT; + } + break; } } @@ -253,6 +358,36 @@ static int handle_event(const SDL_Event *ev) { case SDL_KEYDOWN: handle_keyevent(ev->key.keysym.sym, 1); break; + case SDL_CONTROLLERDEVICEADDED: + if (!_controller) { + _controller = SDL_GameControllerOpen(ev->cdevice.which); + if (_controller) { + fprintf(stdout, "Using controller '%s'\n", SDL_GameControllerName(_controller)); + } + } + break; + case SDL_CONTROLLERDEVICEREMOVED: + if (_controller == SDL_GameControllerFromInstanceID(ev->cdevice.which)) { + fprintf(stdout, "Removed controller '%s'\n", SDL_GameControllerName(_controller)); + SDL_GameControllerClose(_controller); + _controller = 0; + } + break; + case SDL_CONTROLLERBUTTONUP: + if (_controller) { + handle_controllerbutton(ev->cbutton.button, 0); + } + break; + case SDL_CONTROLLERBUTTONDOWN: + if (_controller) { + handle_controllerbutton(ev->cbutton.button, 1); + } + break; + case SDL_CONTROLLERAXISMOTION: + if (_controller) { + handle_controlleraxis(ev->caxis.axis, ev->caxis.value); + } + break; default: return -1; }