Import blues from 116fbc7a

This commit is contained in:
Gregory Montoir 2018-07-16 20:43:34 +08:00
parent a9b55449cd
commit 4859c25ba0
10 changed files with 231 additions and 71 deletions

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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);
}
slot = -1;
} else {
fseek(file_slot->fp, 0, SEEK_END);

6
game.c
View File

@ -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();
}

39
level.c
View File

@ -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) {

20
main.c
View File

@ -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();

View File

@ -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;

View File

@ -5,7 +5,7 @@
#include "sys.h"
#include "util.h"
#define MAX_SPRITESHEET_W 1024
#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);

13
sys.h
View File

@ -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);

View File

@ -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;
}