diff --git a/decode.c b/decode.c index 1f76a29..14e775c 100644 --- a/decode.c +++ b/decode.c @@ -28,3 +28,24 @@ void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *ds src += src_pitch - w; } } + +void decode_amiga_planar8(const uint8_t *src, int w, int h, int depth, uint8_t *dst, int dst_pitch, int dst_x, int dst_y) { + dst += dst_y * dst_pitch + dst_x; + const int plane_size = w * h; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + for (int i = 0; i < 8; ++i) { + int color = 0; + const int mask = 1 << (7 - i); + for (int bit = 0; bit < depth; ++bit) { + if (src[bit * plane_size] & mask) { + color |= 1 << bit; + } + } + dst[x * 8 + i] = color; + } + ++src; + } + dst += dst_pitch; + } +} diff --git a/decode.h b/decode.h index 07174f3..7ccd477 100644 --- a/decode.h +++ b/decode.h @@ -5,5 +5,6 @@ #include "intern.h" extern void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y); +extern void decode_amiga_planar8(const uint8_t *src, int w, int h, int depth, uint8_t *dst, int dst_pitch, int dst_x, int dst_y); #endif /* DECODE_H__ */ diff --git a/game.c b/game.c index f1fa7bf..a5e0c2f 100644 --- a/game.c +++ b/game.c @@ -17,7 +17,7 @@ void update_input() { void do_title_screen() { const uint32_t timestamp = g_sys.get_timestamp() + 20 * 1000; - load_img("pres.sqz"); + load_img(g_options.amiga_lbms ? "blues.lbm" : "pres.sqz"); fade_in_palette(); do { update_input(); @@ -28,7 +28,6 @@ void do_title_screen() { play_sound(SOUND_0); fade_out_palette(); g_sys.input.space = 0; - read_file("avtmag.sqv", g_res.avt_sqv); } static void check_cheat_code() { @@ -53,7 +52,7 @@ void do_select_player() { int frame2 = 1; const int color_rgb = 2; const int colors_count = 25; - load_img("choix.sqz"); + load_img(g_options.amiga_lbms ? "choix.lbm" : "choix.sqz"); screen_load_graphics(); screen_clear_sprites(); do { @@ -239,7 +238,7 @@ static void do_inter_screen_helper(int xpos, int ypos, int c) { static void do_inter_screen() { static const uint8_t xpos[] = { 0xFA, 0x50, 0xF0, 0xC8, 0x50, 0x50 }; static const uint8_t ypos[] = { 0xAA, 0x37, 0x28, 0x5F, 0xA5, 0xAA }; - load_img("inter.sqz"); + load_img(g_options.amiga_lbms ? "inter.lbm" : "inter.sqz"); g_vars.screen_h = 199; screen_clear_sprites(); if (g_vars.level > 1) { diff --git a/game.h b/game.h index 066cdd0..92002b9 100644 --- a/game.h +++ b/game.h @@ -42,6 +42,9 @@ struct options_t { int start_ypos16; bool amiga_copper_bars; bool amiga_colors; + bool amiga_sprites; + bool amiga_lbms; + bool amiga_data; }; extern struct options_t g_options; diff --git a/level.c b/level.c index 7c500c5..4dba431 100644 --- a/level.c +++ b/level.c @@ -29,7 +29,7 @@ static const uint16_t _colors_data[16 * MAX_LEVELS] = { 0x000,0x30b,0x747,0x446,0x006,0xfda,0x400,0xf87,0x37a,0x600,0xd63,0x000,0x000,0x800,0xdd0,0x98d }; -static const struct level_data_t { +static const struct { const char *ck1; const char *ck2; const char *sql; @@ -48,6 +48,18 @@ static const struct level_data_t { { "concert.ck1", "concert.ck2", "concert.sql", "concert.bin", "", "enemi6.sqv", level_xpos_concert, level_ypos_concert, 3 }, }; +static const struct { + const char *m; + const char *bin; +} _levels_amiga[MAX_LEVELS] = { + { "mag.m", "magasin.bin" }, + { "ent.m", "entrepo.bin" }, + { "pris.m", "prison.bin" }, + { "egou.m", "egout.bin" }, + { "ville.m", "ville.bin" }, + { 0, "concert.bin" }, +}; + static const char *_demo_filenames[] = { "demomag.ck1", "demomag.ck2", "demomag.sql" }; @@ -63,9 +75,19 @@ void load_level_data(int num) { load_ck(_levels[num].ck2, 0x8000); load_sql(_levels[num].sql); } - load_bin(_levels[num].bin); + if (g_options.amiga_data) { + load_bin(_levels_amiga[num].bin); + } else { + load_bin(_levels[num].bin); + } load_avt(_levels[num].avt, g_res.avt_sqv, 0); - load_sqv(_levels[num].sqv, g_res.tmp, SPRITES_COUNT); + if (g_options.amiga_sprites) { + char name[16]; + snprintf(name, sizeof(name), "ennemi%d", 1 + num); + load_spr(name, g_res.tmp, SPRITES_COUNT); + } else { + load_sqv(_levels[num].sqv, g_res.tmp, SPRITES_COUNT); + } memcpy(g_vars.level_xpos, _levels[num].xpos, MAX_OBJECTS * sizeof(int16_t)); memcpy(g_vars.level_ypos, _levels[num].ypos, MAX_OBJECTS * sizeof(int16_t)); if (g_vars.music_num != _levels[num].music) { diff --git a/main.c b/main.c index e0570bf..2e29017 100644 --- a/main.c +++ b/main.c @@ -23,6 +23,9 @@ int main(int argc, char *argv[]) { g_options.start_ypos16 = -1; g_options.amiga_copper_bars = true; g_options.amiga_colors = true; + // g_options.amiga_sprites = true; + // g_options.amiga_lbms = true; + // g_options.amiga_data = true; const char *data_path = DEFAULT_DATA_PATH; if (argc == 2) { struct stat st; diff --git a/resource.c b/resource.c index 0870be7..af1ac79 100644 --- a/resource.c +++ b/resource.c @@ -44,14 +44,7 @@ void res_init() { if (!g_res.snd) { print_warning("Failed to allocate sound buffer, %d bytes", SOUND_SIZE); } else { - int f = fio_open(filename, 1); - const int filesize = fio_size(f); - if (filesize != SOUND_SIZE) { - print_warning("Unexpected '%s' file size %d", filename, filesize); - } else if (fio_read(f, g_res.snd, SOUND_SIZE) != SOUND_SIZE) { - print_error("Failed to read %d bytes from file '%s'", filesize, filename); - } - fio_close(f); + read_file(filename, g_res.snd, SOUND_SIZE); } } if (fio_exists("demomag.sql")) { @@ -69,10 +62,12 @@ void res_fini() { free(g_res.snd); } -int read_file(const char *filename, uint8_t *dst) { +int read_file(const char *filename, uint8_t *dst, int size) { const int f = fio_open(filename, 1); const int filesize = fio_size(f); - if (fio_read(f, dst, filesize) != filesize) { + if (size > 0 && size != filesize) { + print_error("Unexpected '%s' file size %d (%d)", filename, filesize, size); + } else if (fio_read(f, dst, filesize) != filesize) { print_error("Failed to read %d bytes from file '%s'", filesize, filename); } fio_close(f); @@ -210,8 +205,7 @@ static const uint8_t *trigger_lookup_table3(uint8_t num) { void load_bin(const char *filename) { uint8_t bin[MAX_TRIGGERS * 10]; - const int size = read_file(filename, bin); - assert(size == MAX_TRIGGERS * 10); + read_file(filename, bin, MAX_TRIGGERS * 10); const uint8_t *p = bin; for (int i = 0; i < MAX_TRIGGERS; ++i) { struct trigger_t *t = &g_res.triggers[i]; @@ -247,7 +241,13 @@ void load_ck(const char *filename, uint16_t offset) { } void load_img(const char *filename) { - const int size = read_compressed_file(filename, g_res.tmp); + int size; + const char *ext = strrchr(filename, '.'); + if (ext && strcmp(ext + 1, "lbm") == 0) { + size = read_file(filename, g_res.tmp, 0); + } else { + size = read_compressed_file(filename, g_res.tmp); + } assert(size <= 32000); load_iff(g_res.tmp, size, g_res.tmp + 32000, 320); g_sys.set_screen_palette(g_res.palette, 16); @@ -255,21 +255,44 @@ void load_img(const char *filename) { memcpy(g_res.vga, g_res.tmp + 32000, 64000); } -void load_sqv(const char *filename, uint8_t *dst, int offset) { - read_compressed_file(filename, dst); - const uint8_t *ptr = dst; - const int count = READ_LE_UINT16(ptr); ptr += 6; - print_debug(DBG_RESOURCE, "sqv count %d", count); +static int load_spr_helper(int offset, const uint8_t *ptr, uint16_t (*read16)(const uint8_t *)) { + const int count = read16(ptr); ptr += 6; + print_debug(DBG_RESOURCE, "spr count %d", count); assert(offset + count <= MAX_SPR_FRAMES); for (int i = 0; i < count; ++i) { g_res.spr_frames[offset + i] = ptr; - const int h = READ_LE_UINT16(ptr - 4); - const int w = READ_LE_UINT16(ptr - 2); + const int h = read16(ptr - 4); + const int w = read16(ptr - 2); assert((w & 3) == 0); const int size = (w >> 1) * h + 4; print_debug(DBG_RESOURCE, "sprite %d, dim %d,%d size %d", i, w, h, size); ptr += size; } + return count; +} + +void load_spr(const char *filename, uint8_t *dst, int offset) { + read_file(filename, dst, 0); + const int count = load_spr_helper(offset, dst, READ_BE_UINT16); + g_res.spr_count = offset + count; + // convert to little endian + uint8_t *ptr = dst; + for (int i = 0; i < count; ++i) { + const int h = READ_BE_UINT16(ptr + 2); + ptr[2] = h; + ptr[3] = 0; + const int w = READ_BE_UINT16(ptr + 4); + ptr[4] = w; + ptr[5] = 0; + assert((w & 3) == 0); + const int size = (w >> 1) * h + 4; + ptr += size; + } +} + +void load_sqv(const char *filename, uint8_t *dst, int offset) { + read_compressed_file(filename, dst); + const int count = load_spr_helper(offset, dst, READ_LE_UINT16); g_res.spr_count = offset + count; } diff --git a/resource.h b/resource.h index 01970cc..e03b153 100644 --- a/resource.h +++ b/resource.h @@ -50,13 +50,14 @@ extern struct resource_data_t g_res; extern void res_init(); extern void res_fini(); -extern int read_file(const char *filename, uint8_t *dst); +extern int read_file(const char *filename, uint8_t *dst, int size); extern int read_compressed_file(const char *filename, uint8_t *dst); extern void load_avt(const char *filename, uint8_t *dst, int offset); extern void load_bin(const char *filename); extern void load_ck(const char *filename, uint16_t offset); extern void load_img(const char *filename); -extern void load_sqv(const char *filename, uint8_t *dst, int size); +extern void load_spr(const char *filename, uint8_t *dst, int offset); +extern void load_sqv(const char *filename, uint8_t *dst, int offset); extern void load_sql(const char *filename); extern uint8_t * lookup_sql(int x, int y); diff --git a/screen.c b/screen.c index 8f0f96a..53686a7 100644 --- a/screen.c +++ b/screen.c @@ -160,6 +160,7 @@ void screen_add_game_sprite4(int x, int y, int frame, int blinking_counter) { } static void decode_graphics(int spr_type, int start, int end) { + const bool amiga = g_options.amiga_sprites && start != 0; struct sys_rect_t r[MAX_SPR_FRAMES]; uint8_t *data = (uint8_t *)calloc(MAX_SPRITESHEET_W * MAX_SPRITESHEET_H, 1); if (data) { @@ -179,11 +180,19 @@ static void decode_graphics(int spr_type, int start, int end) { } current_x = 0; max_h = h; - decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); + if (amiga) { + decode_amiga_planar8(ptr, w / 8, h, 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 { - decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); + if (amiga) { + decode_amiga_planar8(ptr, w / 8, h, 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;