diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index 9f11854..d33cab4 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -16,6 +16,7 @@ namespace recomp { Sram, Flashram, AllowAll, // Allows all save types to work and reports eeprom size as 16kbit. + Custom, // Custom save type for recomp with a configurable size. }; struct GameEntry { @@ -25,6 +26,7 @@ namespace recomp { std::string mod_game_id; std::span cache_data; SaveType save_type = SaveType::None; + uint32_t custom_save_size = 0; bool is_enabled; gpr entrypoint_address; @@ -99,6 +101,7 @@ namespace recomp { bool eeprom_allowed(); bool sram_allowed(); bool flashram_allowed(); + bool custom_saving_allowed(); void start_game(const std::u8string& game_id); std::u8string current_game_id(); diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index f7f0705..fa8e11e 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -186,9 +186,12 @@ void save_clear(uint32_t start, uint32_t size, char value) { save_context.write_sempahore.signal(); } -size_t get_save_size(recomp::SaveType save_type) { +size_t get_save_size(recomp::SaveType save_type, uint32_t custom_save_size) { switch (save_type) { case recomp::SaveType::AllowAll: + return std::max(custom_save_size, 0x20000U); + case recomp::SaveType::Custom: + return custom_save_size; case recomp::SaveType::Flashram: return 0x20000; case recomp::SaveType::Sram: @@ -203,13 +206,38 @@ size_t get_save_size(recomp::SaveType save_type) { return 0; } -void ultramodern::init_saving(RDRAM_ARG1) { +void ultramodern::init_saving(RDRAM_ARG uint32_t custom_save_size) { std::filesystem::path save_file_path = get_save_file_path(); // Ensure the save file directory exists. std::filesystem::create_directories(save_file_path.parent_path()); - save_context.save_buffer.resize(get_save_size(recomp::get_save_type())); + recomp::SaveType save_type = recomp::get_save_type(); + if (save_type == recomp::SaveType::Custom) { + if (custom_save_size == 0) { + ultramodern::error_handling::message_box( + "The current game's GameEntry uses the \"Custom\" save type, but\n" + "the custom save size has not been specified.\n" + "\n" + "This can be done by setting \"custom_save_size\" in the\n" + "GameEntry passed to \"recomp::register_game\"." + ); + ULTRAMODERN_QUICK_EXIT(); + } + } + else if (save_type != recomp::SaveType::AllowAll) { + if (custom_save_size != 0) { + ultramodern::error_handling::message_box( + "The current game's GameEntry \"custom_save_size\" has been set, but\n" + "the current game does not use the \"Custom\" save type.\n" + "\n" + "Only the \"Custom\" save type has a configurable saving size.\n" + ); + ULTRAMODERN_QUICK_EXIT(); + } + } + + save_context.save_buffer.resize(get_save_size(save_type, custom_save_size)); // Read the save file if it exists. std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary); @@ -361,3 +389,31 @@ extern "C" void osEPiRawStartDma_recomp(RDRAM_ARG recomp_context * ctx) { ); ULTRAMODERN_QUICK_EXIT(); } + +// Custom recomp saving mechanism. + +// Called from the recompiled code as `void recomp_save_write(void* rdram_address, u32 offset, u32 count);` +extern "C" void recomp_save_write(uint8_t* rdram, recomp_context* ctx) { + if (!recomp::custom_saving_allowed()) { + ultramodern::error_handling::message_box("Attempted to use custom saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + int32_t rdram_address = ctx->r4; + uint32_t offset = ctx->r5; + uint32_t count = ctx->r6; + + save_write(rdram, rdram_address, offset, count); +} + +// Called from the recompiled code as `void recomp_save_read(void* rdram_address, u32 offset, u32 count);` +extern "C" void recomp_save_read(uint8_t* rdram, recomp_context* ctx) { + if (!recomp::custom_saving_allowed()) { + ultramodern::error_handling::message_box("Attempted to use custom saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + int32_t rdram_address = ctx->r4; + uint32_t offset = ctx->r5; + uint32_t count = ctx->r6; + + save_read(rdram, rdram_address, offset, count); +} diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 499749a..248e75f 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -550,7 +550,7 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) { recomp::init_heap(rdram, recomp::mod_rdram_start + mod_ram_used); save_type = game_entry.save_type; - ultramodern::init_saving(rdram); + ultramodern::init_saving(rdram, game_entry.custom_save_size); try { game_entry.entrypoint(rdram, context); @@ -591,6 +591,12 @@ bool recomp::flashram_allowed() { save_type == SaveType::AllowAll; } +bool recomp::custom_saving_allowed() { + return + save_type == SaveType::Custom || + save_type == SaveType::AllowAll; +} + void recomp::start( const recomp::Version& version, ultramodern::renderer::WindowHandle window_handle, diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index baed6f5..c343079 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -31,7 +31,7 @@ constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for // Initialization. void preinit(RDRAM_ARG renderer::WindowHandle window_handle); -void init_saving(RDRAM_ARG1); +void init_saving(RDRAM_ARG uint32_t custom_save_size); void init_events(RDRAM_ARG renderer::WindowHandle window_handle); void init_timers(RDRAM_ARG1); void init_thread_cleanup();