From 1662d6de163d5242ed033355b09d0aef70be08b3 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 26 Oct 2024 21:50:19 -0400 Subject: [PATCH 1/2] Added save type selection and validation --- librecomp/include/librecomp/game.hpp | 16 ++++++- librecomp/src/eep.cpp | 31 ++++++++++++- librecomp/src/flash.cpp | 66 ++++++++++++++++++++++++++++ librecomp/src/pi.cpp | 39 +++++++++++++++- librecomp/src/recomp.cpp | 28 +++++++++++- 5 files changed, 175 insertions(+), 5 deletions(-) diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index 8712fc6..c7b7c48 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -65,6 +65,15 @@ namespace recomp { void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr); const Version& get_project_version(); + enum class SaveType { + None, + Eep4k, + Eep16k, + Sram, + Flashram, + AllowAll, // Allows all save types to work and reports eeprom size as 16kbit. + }; + /** * The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`): * - `rsp_callbacks` @@ -73,7 +82,7 @@ namespace recomp { * It must be called only once and it must be called before `ultramodern::preinit`. */ void start( - uint32_t rdram_size, + SaveType save_type, const Version& project_version, ultramodern::renderer::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, @@ -86,6 +95,11 @@ namespace recomp { const ultramodern::threads::callbacks_t& threads_callbacks ); + SaveType get_save_type(); + bool eeprom_allowed(); + bool sram_allowed(); + bool flashram_allowed(); + void start_game(const std::u8string& game_id); std::u8string current_game_id(); } diff --git a/librecomp/src/eep.cpp b/librecomp/src/eep.cpp index f386d67..59bf31f 100644 --- a/librecomp/src/eep.cpp +++ b/librecomp/src/eep.cpp @@ -1,4 +1,5 @@ #include "librecomp/recomp.h" +#include "librecomp/game.hpp" #include "ultramodern/ultra64.h" @@ -12,10 +13,23 @@ constexpr int eep16_size = 16384; constexpr int eep16_block_count = eep16_size / eeprom_block_size; extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = 0x02; // EEP16K + switch (recomp::get_save_type()) { + case recomp::SaveType::AllowAll: + case recomp::SaveType::Eep16k: + ctx->r2 = 0x02; // EEPROM_TYPE_16K + case recomp::SaveType::Eep4k: + ctx->r2 = 0x01; // EEPROM_TYPE_4K + default: + ctx->r2 = 0x00; + } } extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) { + if (!recomp::eeprom_allowed()) { + ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint8_t eep_address = ctx->r5; gpr buffer = ctx->r6; int32_t nbytes = eeprom_block_size; @@ -29,6 +43,11 @@ extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) { } extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) { + if (!recomp::eeprom_allowed()) { + ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint8_t eep_address = ctx->r5; gpr buffer = ctx->r6; int32_t nbytes = ctx->r7; @@ -42,6 +61,11 @@ extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) { } extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) { + if (!recomp::eeprom_allowed()) { + ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint8_t eep_address = ctx->r5; gpr buffer = ctx->r6; int32_t nbytes = eeprom_block_size; @@ -55,6 +79,11 @@ extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) { } extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) { + if (!recomp::eeprom_allowed()) { + ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint8_t eep_address = ctx->r5; gpr buffer = ctx->r6; int32_t nbytes = ctx->r7; diff --git a/librecomp/src/flash.cpp b/librecomp/src/flash.cpp index 4bbb79e..af3c1a7 100644 --- a/librecomp/src/flash.cpp +++ b/librecomp/src/flash.cpp @@ -4,6 +4,7 @@ #include #include "librecomp/recomp.h" #include "librecomp/addresses.hpp" +#include "librecomp/game.hpp" // TODO move this out into ultramodern code @@ -22,16 +23,31 @@ void save_clear(uint32_t start, uint32_t size, char value); std::array write_buffer; extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + ctx->r2 = recomp::flash_handle; } extern "C" void osFlashReadStatus_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + PTR(u8) flash_status = ctx->r4; MEM_B(0, flash_status) = 0; } extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + PTR(u32) flash_type = ctx->r4; PTR(u32) flash_maker = ctx->r5; @@ -41,16 +57,31 @@ extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) { } extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + // Nothing to do here. } extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + save_clear(0, ultramodern::save_size, 0xFF); ctx->r2 = 0; } extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + save_clear(0, ultramodern::save_size, 0xFF); ctx->r2 = 0; @@ -58,6 +89,11 @@ extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * // This function is named sector but really means page. extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint32_t page_num = (uint32_t)ctx->r4; // Prevent out of bounds erase @@ -73,6 +109,11 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) // Same naming issue as above. extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint32_t page_num = (uint32_t)ctx->r4; // Prevent out of bounds erase @@ -87,11 +128,21 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context } extern "C" void osFlashCheckEraseEnd_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + // All erases are blocking in this implementation, so this should always return OK. ctx->r2 = 0; // FLASH_STATUS_ERASE_OK } extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4); int32_t pri = ctx->r5; PTR(void) dramAddr = ctx->r6; @@ -109,6 +160,11 @@ extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) } extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + uint32_t page_num = ctx->r4; // Copy the write buffer into the save file @@ -118,6 +174,11 @@ extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx) } extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4); int32_t pri = ctx->r5; uint32_t page_num = ctx->r6; @@ -138,5 +199,10 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) { } extern "C" void osFlashChange_recomp(uint8_t * rdram, recomp_context * ctx) { + if (!recomp::flashram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } + assert(false); } diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index 00ab67e..f7f0705 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -82,7 +82,7 @@ void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr) } struct { - std::array save_buffer; + std::vector save_buffer; std::thread saving_thread; moodycamel::LightweightSemaphore write_sempahore; std::mutex save_buffer_mutex; @@ -143,6 +143,8 @@ void saving_thread_func(RDRAM_ARG1) { } void save_write_ptr(const void* in, uint32_t offset, uint32_t count) { + assert(offset + count <= save_context.save_buffer.size()); + { std::lock_guard lock { save_context.save_buffer_mutex }; memcpy(&save_context.save_buffer[offset], in, count); @@ -152,6 +154,8 @@ void save_write_ptr(const void* in, uint32_t offset, uint32_t count) { } void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { + assert(offset + count <= save_context.save_buffer.size()); + { std::lock_guard lock { save_context.save_buffer_mutex }; for (gpr i = 0; i < count; i++) { @@ -163,6 +167,8 @@ void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t cou } void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { + assert(offset + count <= save_context.save_buffer.size()); + std::lock_guard lock { save_context.save_buffer_mutex }; for (gpr i = 0; i < count; i++) { MEM_B(i, rdram_address) = save_context.save_buffer[offset + i]; @@ -170,6 +176,8 @@ void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t coun } void save_clear(uint32_t start, uint32_t size, char value) { + assert(start + size < save_context.save_buffer.size()); + { std::lock_guard lock { save_context.save_buffer_mutex }; std::fill_n(save_context.save_buffer.begin() + start, size, value); @@ -178,12 +186,31 @@ 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) { + switch (save_type) { + case recomp::SaveType::AllowAll: + case recomp::SaveType::Flashram: + return 0x20000; + case recomp::SaveType::Sram: + return 0x8000; + case recomp::SaveType::Eep16k: + return 0x800; + case recomp::SaveType::Eep4k: + return 0x200; + case recomp::SaveType::None: + return 0; + } + return 0; +} + void ultramodern::init_saving(RDRAM_ARG1) { 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())); + // Read the save file if it exists. std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary); if (save_file.good()) { @@ -191,7 +218,7 @@ void ultramodern::init_saving(RDRAM_ARG1) { } else { // Otherwise clear the save file to all zeroes. - save_context.save_buffer.fill(0); + std::fill(save_context.save_buffer.begin(), save_context.save_buffer.end(), 0); } save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM}; @@ -214,6 +241,10 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ // Send a message to the mq to indicate that the transfer completed osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); } else if (physical_addr >= recomp::sram_base) { + if (!recomp::sram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use SRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } // read sram save_read(rdram, rdram_address, physical_addr - recomp::sram_base, size); @@ -227,6 +258,10 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ // write cart rom throw std::runtime_error("ROM DMA write unimplemented"); } else if (physical_addr >= recomp::sram_base) { + if (!recomp::sram_allowed()) { + ultramodern::error_handling::message_box("Attempted to use SRAM saving with other save type"); + ULTRAMODERN_QUICK_EXIT(); + } // write sram save_write(rdram, rdram_address, physical_addr - recomp::sram_base, size); diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index e3509da..9e8a3dd 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -566,8 +566,33 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) { } } +static recomp::SaveType _save_type = recomp::SaveType::None; + +recomp::SaveType recomp::get_save_type() { + return _save_type; +} + +bool recomp::eeprom_allowed() { + return + _save_type == SaveType::Eep4k || + _save_type == SaveType::Eep16k || + _save_type == SaveType::AllowAll; +} + +bool recomp::sram_allowed() { + return + _save_type == SaveType::Sram || + _save_type == SaveType::AllowAll; +} + +bool recomp::flashram_allowed() { + return + _save_type == SaveType::Flashram || + _save_type == SaveType::AllowAll; +} + void recomp::start( - uint32_t rdram_size, + SaveType save_type, const recomp::Version& version, ultramodern::renderer::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, @@ -579,6 +604,7 @@ void recomp::start( const ultramodern::error_handling::callbacks_t& error_handling_callbacks, const ultramodern::threads::callbacks_t& threads_callbacks ) { + _save_type = save_type; project_version = version; recomp::check_all_stored_roms(); From 4c756c4ffb866e8b2b00ee1be763055e1f2cb664 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 26 Oct 2024 22:27:16 -0400 Subject: [PATCH 2/2] Made save type be a per-game setting --- librecomp/include/librecomp/game.hpp | 20 ++++++++++---------- librecomp/src/recomp.cpp | 23 +++++++++++------------ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index c7b7c48..9f11854 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -9,12 +9,22 @@ #include namespace recomp { + enum class SaveType { + None, + Eep4k, + Eep16k, + Sram, + Flashram, + AllowAll, // Allows all save types to work and reports eeprom size as 16kbit. + }; + struct GameEntry { uint64_t rom_hash; std::string internal_name; std::u8string game_id; std::string mod_game_id; std::span cache_data; + SaveType save_type = SaveType::None; bool is_enabled; gpr entrypoint_address; @@ -65,15 +75,6 @@ namespace recomp { void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr); const Version& get_project_version(); - enum class SaveType { - None, - Eep4k, - Eep16k, - Sram, - Flashram, - AllowAll, // Allows all save types to work and reports eeprom size as 16kbit. - }; - /** * The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`): * - `rsp_callbacks` @@ -82,7 +83,6 @@ namespace recomp { * It must be called only once and it must be called before `ultramodern::preinit`. */ void start( - SaveType save_type, const Version& project_version, ultramodern::renderer::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 9e8a3dd..f7caffc 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -66,6 +66,8 @@ std::unordered_map game_roms {}; std::unique_ptr mod_context = std::make_unique(); // The project's version. recomp::Version project_version; +// The current game's save type. +recomp::SaveType save_type = recomp::SaveType::None; std::u8string recomp::GameEntry::stored_filename() const { return game_id + u8".z64"; @@ -547,6 +549,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::load_shader_cache(game_entry.cache_data); @@ -566,33 +569,30 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) { } } -static recomp::SaveType _save_type = recomp::SaveType::None; - recomp::SaveType recomp::get_save_type() { - return _save_type; + return save_type; } bool recomp::eeprom_allowed() { return - _save_type == SaveType::Eep4k || - _save_type == SaveType::Eep16k || - _save_type == SaveType::AllowAll; + save_type == SaveType::Eep4k || + save_type == SaveType::Eep16k || + save_type == SaveType::AllowAll; } bool recomp::sram_allowed() { return - _save_type == SaveType::Sram || - _save_type == SaveType::AllowAll; + save_type == SaveType::Sram || + save_type == SaveType::AllowAll; } bool recomp::flashram_allowed() { return - _save_type == SaveType::Flashram || - _save_type == SaveType::AllowAll; + save_type == SaveType::Flashram || + save_type == SaveType::AllowAll; } void recomp::start( - SaveType save_type, const recomp::Version& version, ultramodern::renderer::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, @@ -604,7 +604,6 @@ void recomp::start( const ultramodern::error_handling::callbacks_t& error_handling_callbacks, const ultramodern::threads::callbacks_t& threads_callbacks ) { - _save_type = save_type; project_version = version; recomp::check_all_stored_roms();