From 1b7e9c9aa6251441923bcc8ba6aa1eee1ed7c4ae Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Thu, 7 Nov 2024 06:05:05 -0800 Subject: [PATCH] Incremental loading for BK files --- src/formats/bk.c | 128 ++++++++++++++++++++++---------------- src/formats/bk.h | 13 ++++ src/formats/error.c | 2 + src/formats/error.h | 1 + src/game/game_state.c | 46 ++++++++++---- src/game/protos/scene.c | 60 +++++++++++++++++- src/game/protos/scene.h | 3 + src/resources/bk.c | 20 +++++- src/resources/bk.h | 2 + src/resources/bk_loader.c | 55 +++++++++++++++- src/resources/bk_loader.h | 15 +++++ 11 files changed, 275 insertions(+), 70 deletions(-) diff --git a/src/formats/bk.c b/src/formats/bk.c index 1eceed561..6299ce7c7 100644 --- a/src/formats/bk.c +++ b/src/formats/bk.c @@ -92,9 +92,82 @@ void sd_bk_postprocess(sd_bk_file *bk) { } } +int sd_bk_load_incremental(sd_bk_file *bk, sd_reader *r) { + int ret = SD_SUCCESS; + switch(bk->load_state) { + case BK_LOAD_INIT: + // Header + bk->file_id = sd_read_udword(r); + bk->unknown_a = sd_read_ubyte(r); + uint16_t img_w = sd_read_uword(r); + uint16_t img_h = sd_read_uword(r); + + // Allocate backgroun image + bk->background = omf_calloc(1, sizeof(sd_vga_image)); + if((ret = sd_vga_image_create(bk->background, img_w, img_h)) != SD_SUCCESS) { + return ret; + } + bk->load_state = BK_LOAD_ANIMS; + return SD_AGAIN; + case BK_LOAD_ANIMS: + sd_skip(r, 4); // offset of next animation + int animno = sd_read_ubyte(r); + if(animno >= MAX_BK_ANIMS) { + bk->load_state = BK_LOAD_BACKGROUND; + return SD_AGAIN; + } + if(!sd_reader_ok(r)) { + // TODO is this an error? + return SD_INVALID_INPUT; + } + + // Initialize animation + bk->anims[animno] = omf_calloc(1, sizeof(sd_bk_anim)); + if((ret = sd_bk_anim_create(bk->anims[animno])) != SD_SUCCESS) { + return ret; + } + if((ret = sd_bk_anim_load(r, bk->anims[animno])) != SD_SUCCESS) { + return ret; + } + return SD_AGAIN; + case BK_LOAD_BACKGROUND: + // Read background image + sd_read_buf(r, bk->background->data, bk->background->len); + // Read palatte count + bk->palette_count = sd_read_ubyte(r); + bk->load_state = BK_LOAD_PALETTES; + return SD_AGAIN; + case BK_LOAD_PALETTES: + for(uint8_t i = 0; i < bk->palette_count; i++) { + if(bk->palettes[i] == NULL) { + bk->palettes[i] = omf_malloc(sizeof(vga_palette)); + if((ret = palette_load(r, bk->palettes[i])) != SD_SUCCESS) { + return ret; + } + bk->remaps[i] = omf_malloc(sizeof(vga_remap_tables)); + if((ret = palette_remaps_load(r, bk->remaps[i])) != SD_SUCCESS) { + return ret; + } + return SD_AGAIN; + } + } + // Read soundtable + sd_read_buf(r, bk->soundtable, 30); + bk->load_state = BK_LOAD_POSTPROCESS; + return SD_AGAIN; + case BK_LOAD_POSTPROCESS: + // Fix missing sprites + sd_bk_postprocess(bk); + bk->load_state = BK_LOAD_DONE; + return SD_SUCCESS; + case BK_LOAD_DONE: + return SD_SUCCESS; + } + // Should never happen + return SD_INVALID_INPUT; +} + int sd_bk_load(sd_bk_file *bk, const char *filename) { - uint16_t img_w, img_h; - uint8_t animno = 0; sd_reader *r; int ret = SD_SUCCESS; @@ -108,58 +181,9 @@ int sd_bk_load(sd_bk_file *bk, const char *filename) { return SD_FILE_OPEN_ERROR; } - // Header - bk->file_id = sd_read_udword(r); - bk->unknown_a = sd_read_ubyte(r); - img_w = sd_read_uword(r); - img_h = sd_read_uword(r); - - // Read animations - while(1) { - sd_skip(r, 4); // offset of next animation - animno = sd_read_ubyte(r); - if(animno >= MAX_BK_ANIMS || !sd_reader_ok(r)) { - break; - } - - // Initialize animation - bk->anims[animno] = omf_calloc(1, sizeof(sd_bk_anim)); - if((ret = sd_bk_anim_create(bk->anims[animno])) != SD_SUCCESS) { - goto exit_0; - } - if((ret = sd_bk_anim_load(r, bk->anims[animno])) != SD_SUCCESS) { - goto exit_0; - } - } - - // Read background image - bk->background = omf_calloc(1, sizeof(sd_vga_image)); - if((ret = sd_vga_image_create(bk->background, img_w, img_h)) != SD_SUCCESS) { - goto exit_0; + while((ret = sd_bk_load_incremental(bk, r)) == SD_AGAIN) { } - int bsize = img_w * img_h; - sd_read_buf(r, bk->background->data, bsize); - - // Read palettes and their remaps - bk->palette_count = sd_read_ubyte(r); - for(uint8_t i = 0; i < bk->palette_count; i++) { - bk->palettes[i] = omf_malloc(sizeof(vga_palette)); - if((ret = palette_load(r, bk->palettes[i])) != SD_SUCCESS) { - goto exit_0; - } - bk->remaps[i] = omf_malloc(sizeof(vga_remap_tables)); - if((ret = palette_remaps_load(r, bk->remaps[i])) != SD_SUCCESS) { - goto exit_0; - } - } - - // Read soundtable - sd_read_buf(r, bk->soundtable, 30); - - // Fix missing sprites - sd_bk_postprocess(bk); -exit_0: sd_reader_close(r); return ret; } diff --git a/src/formats/bk.h b/src/formats/bk.h index e44cd9be5..5c363e6cd 100644 --- a/src/formats/bk.h +++ b/src/formats/bk.h @@ -18,11 +18,22 @@ #define MAX_BK_ANIMS 50 ///< Amount of animations in the BK file. This is fixed! #define MAX_BK_PALETTES 8 ///< Maximum amount of palettes allowed in BK file. +typedef enum +{ + BK_LOAD_INIT = 0, + BK_LOAD_ANIMS, + BK_LOAD_BACKGROUND, + BK_LOAD_PALETTES, + BK_LOAD_POSTPROCESS, + BK_LOAD_DONE, +} bk_load_state; + /*! \brief BK file information * * Contains information about an OMF:2097 scene. Eg. arenas, menus, intro, etc. */ typedef struct { + bk_load_state load_state; uint32_t file_id; ///< File ID uint8_t unknown_a; ///< Unknown value uint8_t palette_count; ///< Number of palettes in the BK file @@ -183,6 +194,8 @@ int sd_bk_pop_palette(sd_bk_file *bk); */ vga_palette *sd_bk_get_palette(const sd_bk_file *bk, int index); +int sd_bk_load_incremental(sd_bk_file *bk, sd_reader *r); + /*! \brief Load .BK file * * Loads the given BK file to memory. The structure must be initialized with sd_bk_create() diff --git a/src/formats/error.c b/src/formats/error.c index 8c24e9889..59749ed44 100644 --- a/src/formats/error.c +++ b/src/formats/error.c @@ -29,6 +29,8 @@ const char *sd_get_error(enum SD_ERRORCODE error_code) { return "File could not be unlinked"; case SD_FAILURE: return "General failure"; + case SD_AGAIN: + return "Function not finished, call it again"; } abort(); } diff --git a/src/formats/error.h b/src/formats/error.h index ae479aaf0..5910dd7cd 100644 --- a/src/formats/error.h +++ b/src/formats/error.h @@ -28,6 +28,7 @@ enum SD_ERRORCODE SD_FILE_READ_ERROR, ///< File could not be read SD_FILE_UNLINK_ERROR, ///< File could not be unlinked SD_FAILURE, ///< General failure + SD_AGAIN, ///< Function isn't done, needs to be called again }; /*! \brief Get text error for error ID diff --git a/src/game/game_state.c b/src/game/game_state.c index 33f2586fa..bb31abe7d 100644 --- a/src/game/game_state.c +++ b/src/game/game_state.c @@ -542,8 +542,24 @@ void game_state_debug(game_state *gs) { int game_load_new(game_state *gs, int scene_id) { // Free old scene - scene_free(gs->sc); - omf_free(gs->sc); + // scene_free(gs->sc); + // omf_free(gs->sc); + + // Initialize new scene with BK data etc. + // gs->sc = omf_calloc(1, sizeof(scene)); + int ret = scene_create_incremental(gs->sc, gs, scene_id); + + if(ret == SD_AGAIN) { + return SD_AGAIN; + } + + if(ret) { + log_error("Error while loading scene %d: %d.", scene_id, ret); + goto error_0; + } + + // Free texture items, we are going to create new ones. + video_signal_scene_change(); // Remove old objects render_obj *robj; @@ -557,19 +573,9 @@ int game_load_new(game_state *gs, int scene_id) { } } - // Free texture items, we are going to create new ones. - video_signal_scene_change(); - gs->this_id = scene_id; gs->next_id = scene_id; - // Initialize new scene with BK data etc. - gs->sc = omf_calloc(1, sizeof(scene)); - if(scene_create(gs->sc, gs, scene_id)) { - log_error("Error while loading scene %d.", scene_id); - goto error_0; - } - // Load scene specifics switch(scene_id) { case SCENE_OPENOMF: @@ -844,6 +850,9 @@ void game_state_merge_sounds(game_state *old, game_state *new) { // This function is always called with the same interval, and game speed does not affect it void game_state_static_tick(game_state *gs, bool replay) { + uint64_t ticks = SDL_GetTicks64(); + uint64_t limit = game_state_ms_per_dyntick(gs) / 2; + // Set scene crossfade values if(gs->this_wait_ticks > 0) { gs->this_wait_ticks--; @@ -862,11 +871,22 @@ void game_state_static_tick(game_state *gs, bool replay) { } // Load up new scene - if(game_load_new(gs, gs->next_id)) { + int ret; + while((ret = game_load_new(gs, gs->next_id)) == SD_AGAIN) { + uint64_t now = SDL_GetTicks64(); + if(now - ticks > limit) { + // taking too long for the dynamic tick time + break; + } + } + if(ret > 0 && ret != SD_AGAIN) { log_error("Error while loading new scene! bailing."); gs->run = 0; return; } + if(ret == SD_AGAIN) { + return; + } if(settings_get()->video.crossfade_on) { gs->this_wait_ticks = FRAME_WAIT_TICKS; } else { diff --git a/src/game/protos/scene.c b/src/game/protos/scene.c index 25be41c3c..613a03471 100644 --- a/src/game/protos/scene.c +++ b/src/game/protos/scene.c @@ -2,7 +2,6 @@ #include "game/game_player.h" #include "game/game_state_type.h" #include "resources/af_loader.h" -#include "resources/bk_loader.h" #include "resources/ids.h" #include "utils/allocator.h" #include "utils/log.h" @@ -64,6 +63,64 @@ int scene_create(scene *scene, game_state *gs, int scene_id) { return 0; } +// Loads BK file etc. +int scene_create_incremental(scene *scene, game_state *gs, int scene_id) { + if(scene_id == SCENE_NONE) { + return 1; + } + + // Load BK + int resource_id = scene_to_resource(scene_id); + if(!scene->next_bk_data) { + scene->next_bk_data = omf_calloc(1, sizeof(bk_inc)); + } + int ret = load_bk_file_incremental(scene->next_bk_data, resource_id); + if(ret != SD_SUCCESS && ret != SD_AGAIN) { + log_error("Unable to load scene %s (%s)!", scene_get_name(scene_id), get_resource_name(resource_id)); + return ret; + } else if(ret == SD_AGAIN) { + // not done loading yet + return SD_AGAIN; + } + scene_free(scene); + scene->bk_data = scene->next_bk_data->bk; + + log_debug("setting bk_data %p", scene->bk_data); + omf_free(scene->next_bk_data); + scene->id = scene_id; + scene->gs = gs; + scene->af_data[0] = NULL; + scene->af_data[1] = NULL; + scene->static_ticks_since_start = 0; + + // Init functions + scene->userdata = NULL; + scene->free = NULL; + scene->event = NULL; + scene->render = NULL; + scene->render_overlay = NULL; + scene->dynamic_tick = NULL; + scene->static_tick = NULL; + scene->input_poll = NULL; + scene->startup = NULL; + scene->prio_override = NULL; + + // Set base palette + vga_state_set_base_palette_from(bk_get_palette(scene->bk_data, 0)); + vga_state_set_remaps_from(bk_get_remaps(scene->bk_data, 0)); + + // Set menu colors to the correct position + palette_set_menu_colors(); + + // Index 0 is always black. + vga_color c = {0, 0, 0}; + vga_state_set_base_palette_index(0, &c); + + // All done. + log_debug("Loaded scene %s (%s).", scene_get_name(scene_id), get_resource_name(resource_id)); + return 0; +} + int scene_load_har(scene *scene, int player_id) { game_player *player = game_state_get_player(scene->gs, player_id); if(scene->af_data[player_id]) { @@ -223,6 +280,7 @@ void scene_free(scene *scene) { if(scene->free != NULL) { scene->free(scene); } + log_debug("freeing %p", scene->bk_data); bk_free(scene->bk_data); omf_free(scene->bk_data); if(scene->af_data[0]) { diff --git a/src/game/protos/scene.h b/src/game/protos/scene.h index 945988f0b..820bad3b4 100644 --- a/src/game/protos/scene.h +++ b/src/game/protos/scene.h @@ -9,6 +9,7 @@ #include "game/utils/serial.h" #include "game/utils/ticktimer.h" #include "resources/bk.h" +#include "resources/bk_loader.h" #include "video/surface.h" #include @@ -31,6 +32,7 @@ typedef void (*scene_clone_free_cb)(scene *scene); struct scene_t { game_state *gs; int id; + bk_inc *next_bk_data; bk *bk_data; af *af_data[2]; void *userdata; @@ -52,6 +54,7 @@ struct scene_t { }; int scene_create(scene *scene, game_state *gs, int scene_id); +int scene_create_incremental(scene *scene, game_state *gs, int scene_id); int scene_load_har(scene *scene, int player_id); void scene_init(scene *scene); void scene_free(scene *scene); diff --git a/src/resources/bk.c b/src/resources/bk.c index 6a89c14d0..487803108 100644 --- a/src/resources/bk.c +++ b/src/resources/bk.c @@ -1,9 +1,10 @@ #include "formats/bk.h" +#include "formats/error.h" #include "resources/bk.h" #include "utils/allocator.h" #include -void bk_create(bk *b, void *src) { +void bk_create_inc(bk *b, void *src) { sd_bk_file *sdbk = (sd_bk_file *)src; // File ID @@ -26,15 +27,28 @@ void bk_create(bk *b, void *src) { // Array for sprites, since we know we will fill most slots. array_create(&b->sprites); - // Copy info structs hashmap_create(&b->infos); +} + +void bk_create(bk *b, void *src) { + bk_create_inc(b, src); + + // Copy info structs + while(SD_AGAIN == bk_convert_inc(b, src)) { + } +} + +int bk_convert_inc(bk *b, void *src) { + sd_bk_file *sdbk = (sd_bk_file *)src; bk_info tmp_bk_info; for(int i = 0; i < 50; i++) { - if(sdbk->anims[i] != NULL) { + if(sdbk->anims[i] != NULL && hashmap_get_int(&b->infos, i, (void **)&tmp_bk_info, NULL)) { bk_info_create(&tmp_bk_info, &b->sprites, (void *)sdbk->anims[i], i); hashmap_put_int(&b->infos, i, &tmp_bk_info, sizeof(bk_info)); + return SD_AGAIN; } } + return SD_SUCCESS; } bk_info *bk_get_info(bk *b, int id) { diff --git a/src/resources/bk.h b/src/resources/bk.h index a766aa173..4c8dd1c5f 100644 --- a/src/resources/bk.h +++ b/src/resources/bk.h @@ -16,7 +16,9 @@ typedef struct bk_t { char sound_translation_table[30]; } bk; +void bk_create_inc(bk *b, void *src); void bk_create(bk *b, void *src); +int bk_convert_inc(bk *b, void *src); bk_info *bk_get_info(bk *b, int id); vga_palette *bk_get_palette(bk *b, int id); vga_remap_tables *bk_get_remaps(bk *b, int id); diff --git a/src/resources/bk_loader.c b/src/resources/bk_loader.c index f1e437cde..733fdf89b 100644 --- a/src/resources/bk_loader.c +++ b/src/resources/bk_loader.c @@ -1,7 +1,8 @@ #include "resources/bk_loader.h" #include "formats/bk.h" -#include "formats/error.h" #include "resources/pathmanager.h" +#include "utils/allocator.h" +#include "utils/log.h" int load_bk_file(bk *b, int id) { // Get directory + filename @@ -22,3 +23,55 @@ int load_bk_file(bk *b, int id) { sd_bk_free(&tmp); return 0; } + +int bk_inc_create(bk_inc *b, int id) { + // Get directory + filename + const char *filename = pm_get_resource_path(id); + + // Initialize reader + if(!(b->r = sd_reader_open(filename))) { + return SD_FILE_OPEN_ERROR; + } + + if(sd_bk_create(&b->sd_bk) != SD_SUCCESS) { + sd_bk_free(&b->sd_bk); + sd_reader_close(b->r); + return 1; + } + + b->state = 1; + return SD_AGAIN; +} + +int load_bk_file_incremental(bk_inc *b, int id) { + int ret = SD_SUCCESS; + switch(b->state) { + case 0: + return bk_inc_create(b, id); + case 1: + ret = sd_bk_load_incremental(&b->sd_bk, b->r); + if(ret == SD_SUCCESS) { + b->state = 2; + return SD_AGAIN; + } + if(ret != SD_AGAIN) { + sd_reader_close(b->r); + } + return ret; + case 2: + b->bk = omf_calloc(1, sizeof(bk)); + log_debug("creating BK %p", b->bk); + bk_create_inc(b->bk, &b->sd_bk); + b->state = 3; + return SD_AGAIN; + case 3: + ret = bk_convert_inc(b->bk, &b->sd_bk); + if(ret != SD_AGAIN) { + sd_bk_free(&b->sd_bk); + sd_reader_close(b->r); + b->state = 4; + } + return ret; + } + return ret; +} diff --git a/src/resources/bk_loader.h b/src/resources/bk_loader.h index 42c6f1fa6..46412d64d 100644 --- a/src/resources/bk_loader.h +++ b/src/resources/bk_loader.h @@ -1,8 +1,23 @@ #ifndef BK_LOADER_H #define BK_LOADER_H +#include "formats/bk.h" +#include "formats/error.h" #include "resources/bk.h" +/*typedef enum { + BK_LOADER_INIT, + BK_LOADER_LOAD_BK,*/ + +typedef struct { + int id; + sd_reader *r; + int state; + sd_bk_file sd_bk; + bk *bk; +} bk_inc; + int load_bk_file(bk *b, int id); +int load_bk_file_incremental(bk_inc *b, int id); #endif // BK_LOADER_H