From 30bc1533c9df3449853a2d749c8d44d780860124 Mon Sep 17 00:00:00 2001 From: Angie Date: Fri, 21 Jun 2024 19:35:00 -0400 Subject: [PATCH 1/3] move init_saving and join_saving_thread to librecomp namespace --- librecomp/include/librecomp/game.hpp | 3 ++- librecomp/include/librecomp/save.hpp | 13 +++++++++++++ librecomp/src/pi.cpp | 13 ++++++++----- librecomp/src/recomp.cpp | 6 ++++-- ultramodern/include/ultramodern/ultramodern.hpp | 2 -- 5 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 librecomp/include/librecomp/save.hpp diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index 4c2b304..fe74e38 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -4,9 +4,10 @@ #include #include +#include "ultramodern/ultramodern.hpp" + #include "recomp.h" #include "rsp.hpp" -#include namespace recomp { struct GameEntry { diff --git a/librecomp/include/librecomp/save.hpp b/librecomp/include/librecomp/save.hpp new file mode 100644 index 0000000..e03f62f --- /dev/null +++ b/librecomp/include/librecomp/save.hpp @@ -0,0 +1,13 @@ +#ifndef __LIBRECOMP_SAVE_HPP__ +#define __LIBRECOMP_SAVE_HPP__ + +#include + +namespace recomp { + namespace save { + void init(uint8_t *rdram); + void join_thread(); + } +} + +#endif diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index 855211c..d527832 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -7,8 +7,11 @@ #include "recomp.h" #include "game.hpp" #include "files.hpp" -#include -#include + +#include "ultramodern/ultra64.h" +#include "ultramodern/ultramodern.hpp" + +#include "librecomp/save.hpp" static std::vector rom; @@ -153,7 +156,7 @@ void save_write_ptr(const void* in, uint32_t offset, uint32_t count) { std::lock_guard lock { save_context.save_buffer_mutex }; memcpy(&save_context.save_buffer[offset], in, count); } - + save_context.write_sempahore.signal(); } @@ -184,7 +187,7 @@ void save_clear(uint32_t start, uint32_t size, char value) { save_context.write_sempahore.signal(); } -void ultramodern::init_saving(RDRAM_ARG1) { +void recomp::save::init(uint8_t *rdram) { std::filesystem::path save_file_path = get_save_file_path(); // Ensure the save file directory exists. @@ -203,7 +206,7 @@ void ultramodern::init_saving(RDRAM_ARG1) { save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM}; } -void ultramodern::join_saving_thread() { +void recomp::save::join_thread() { if (save_context.saving_thread.joinable()) { save_context.saving_thread.join(); } diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 75f8a05..9616b81 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -18,6 +18,8 @@ #include "ultramodern/ultramodern.hpp" #include "ultramodern/error_handling.hpp" +#include "librecomp/save.hpp" + #ifdef _MSC_VER inline uint32_t byteswap(uint32_t val) { return _byteswap_ulong(val); @@ -434,7 +436,7 @@ void recomp::start( ultramodern::error_handling::message_box("Error opening stored ROM! Please restart this program."); } - ultramodern::init_saving(rdram); + recomp::save::init(rdram); auto find_it = game_roms.find(current_game.value()); const recomp::GameEntry& game_entry = find_it->second; @@ -469,5 +471,5 @@ void recomp::start( game_thread.join(); ultramodern::join_event_threads(); ultramodern::join_thread_cleaner_thread(); - ultramodern::join_saving_thread(); + recomp::save::join_thread(); } diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index b144eaa..be2372c 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -36,7 +36,6 @@ 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_events(RDRAM_ARG renderer::WindowHandle window_handle); void init_timers(RDRAM_ARG1); void init_thread_cleanup(); @@ -124,7 +123,6 @@ bool is_game_started(); void quit(); void join_event_threads(); void join_thread_cleaner_thread(); -void join_saving_thread(); void set_audio_callbacks(const audio_callbacks_t& callbacks); From da86be4b8ae1387afcaf42fb175782884c35de10 Mon Sep 17 00:00:00 2001 From: Angie Date: Fri, 21 Jun 2024 19:41:34 -0400 Subject: [PATCH 2/3] Move the other save functions to recomp namespace --- librecomp/include/librecomp/save.hpp | 9 ++++++++- librecomp/src/flash.cpp | 24 +++++++++++------------- librecomp/src/pi.cpp | 12 ++++++------ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/librecomp/include/librecomp/save.hpp b/librecomp/include/librecomp/save.hpp index e03f62f..1c3427c 100644 --- a/librecomp/include/librecomp/save.hpp +++ b/librecomp/include/librecomp/save.hpp @@ -3,10 +3,17 @@ #include +#include "ultramodern/ultra64.h" + namespace recomp { namespace save { - void init(uint8_t *rdram); + void init(RDRAM_ARG1); void join_thread(); + + void write_ptr(const void* in, uint32_t offset, uint32_t count); + void write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); + void read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); + void clear(uint32_t start, uint32_t size, char value); } } diff --git a/librecomp/src/flash.cpp b/librecomp/src/flash.cpp index 43407e8..440a9e7 100644 --- a/librecomp/src/flash.cpp +++ b/librecomp/src/flash.cpp @@ -1,8 +1,11 @@ #include #include -#include -#include + +#include "ultramodern/ultra64.h" +#include "ultramodern/ultramodern.hpp" + #include "recomp.h" +#include "librecomp/save.hpp" // TODO move this out into ultramodern code @@ -13,11 +16,6 @@ constexpr uint32_t page_count = flash_size / page_size; constexpr uint32_t sector_size = page_size * pages_per_sector; constexpr uint32_t sector_count = flash_size / sector_size; -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); -void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); -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) { @@ -44,13 +42,13 @@ extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx) } extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) { - save_clear(0, ultramodern::save_size, 0xFF); + recomp::save::clear(0, ultramodern::save_size, 0xFF); ctx->r2 = 0; } extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) { - save_clear(0, ultramodern::save_size, 0xFF); + recomp::save::clear(0, ultramodern::save_size, 0xFF); ctx->r2 = 0; } @@ -65,7 +63,7 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) return; } - save_clear(page_num * page_size, page_size, 0xFF); + recomp::save::clear(page_num * page_size, page_size, 0xFF); ctx->r2 = 0; } @@ -80,7 +78,7 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context return; } - save_clear(page_num * page_size, page_size, 0xFF); + recomp::save::clear(page_num * page_size, page_size, 0xFF); ctx->r2 = 0; } @@ -111,7 +109,7 @@ extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx) uint32_t page_num = ctx->r4; // Copy the write buffer into the save file - save_write_ptr(write_buffer.data(), page_num * page_size, page_size); + recomp::save::write_ptr(write_buffer.data(), page_num * page_size, page_size); ctx->r2 = 0; } @@ -128,7 +126,7 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) { uint32_t count = n_pages * page_size; // Read from the save file into the provided buffer - save_read(PASS_RDRAM dramAddr, offset, count); + recomp::save::read(PASS_RDRAM dramAddr, offset, count); // Send the message indicating read completion osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK); diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index d527832..57df11d 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -151,7 +151,7 @@ void saving_thread_func(RDRAM_ARG1) { } } -void save_write_ptr(const void* in, uint32_t offset, uint32_t count) { +void recomp::save::write_ptr(const void* in, uint32_t offset, uint32_t count) { { std::lock_guard lock { save_context.save_buffer_mutex }; memcpy(&save_context.save_buffer[offset], in, count); @@ -160,7 +160,7 @@ void save_write_ptr(const void* in, uint32_t offset, uint32_t count) { save_context.write_sempahore.signal(); } -void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { +void recomp::save::write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { { std::lock_guard lock { save_context.save_buffer_mutex }; for (uint32_t i = 0; i < count; i++) { @@ -171,14 +171,14 @@ void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t cou save_context.write_sempahore.signal(); } -void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { +void recomp::save::read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { std::lock_guard lock { save_context.save_buffer_mutex }; for (size_t i = 0; i < count; i++) { MEM_B(i, rdram_address) = save_context.save_buffer[offset + i]; } } -void save_clear(uint32_t start, uint32_t size, char value) { +void recomp::save::clear(uint32_t start, uint32_t size, char value) { { std::lock_guard lock { save_context.save_buffer_mutex }; std::fill_n(save_context.save_buffer.begin() + start, size, value); @@ -224,7 +224,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); } else if (physical_addr >= sram_base) { // read sram - save_read(rdram, rdram_address, physical_addr - sram_base, size); + recomp::save::read(rdram, rdram_address, physical_addr - sram_base, size); // Send a message to the mq to indicate that the transfer completed osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); @@ -237,7 +237,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ throw std::runtime_error("ROM DMA write unimplemented"); } else if (physical_addr >= sram_base) { // write sram - save_write(rdram, rdram_address, physical_addr - sram_base, size); + recomp::save::write(rdram, rdram_address, physical_addr - sram_base, size); // Send a message to the mq to indicate that the transfer completed osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); From 49c40ee6870999bb6d3d972576584916e7dc70f5 Mon Sep 17 00:00:00 2001 From: Angie Date: Fri, 21 Jun 2024 19:52:43 -0400 Subject: [PATCH 3/3] move save stuff to save.cpp --- librecomp/CMakeLists.txt | 1 + librecomp/src/pi.cpp | 122 -------------------------------- librecomp/src/save.cpp | 146 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 122 deletions(-) create mode 100644 librecomp/src/save.cpp diff --git a/librecomp/CMakeLists.txt b/librecomp/CMakeLists.txt index 7318386..3e5a9f3 100644 --- a/librecomp/CMakeLists.txt +++ b/librecomp/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(librecomp STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/print.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/recomp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/rsp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/save.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/sp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_stubs.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_translation.cpp" diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index 57df11d..951a44f 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -90,128 +90,6 @@ void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr) MEM_B(3, ram_address) = *rom_addr++; } -struct { - std::array save_buffer; - std::thread saving_thread; - moodycamel::LightweightSemaphore write_sempahore; - std::mutex save_buffer_mutex; -} save_context; - -const std::u8string save_folder = u8"saves"; - -extern std::filesystem::path config_path; - -std::filesystem::path get_save_file_path() { - return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin"); -} - -void update_save_file() { - bool saving_failed = false; - { - std::ofstream save_file = recomp::open_output_file_with_backup(get_save_file_path(), std::ios_base::binary); - - if (save_file.good()) { - std::lock_guard lock{ save_context.save_buffer_mutex }; - save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size()); - } - else { - saving_failed = true; - } - } - if (!saving_failed) { - saving_failed = !recomp::finalize_output_file_with_backup(get_save_file_path()); - } - if (saving_failed) { - ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions and whether the save folder has been moved to Dropbox or similar, as this can cause issues."); - } -} - -extern std::atomic_bool exited; - -void saving_thread_func(RDRAM_ARG1) { - while (!exited) { - bool save_buffer_updated = false; - // Repeatedly wait for a new action to be sent. - constexpr int64_t wait_time_microseconds = 10000; - constexpr int max_actions = 128; - int num_actions = 0; - - // Wait up to the given timeout for a write to come in. Allow multiple writes to coalesce together into a single save. - // Cap the number of coalesced writes to guarantee that the save buffer eventually gets written out to the file even if the game - // is constantly sending writes. - while (save_context.write_sempahore.wait(wait_time_microseconds) && num_actions < max_actions) { - save_buffer_updated = true; - num_actions++; - } - - // If an action came through that affected the save file, save the updated contents. - if (save_buffer_updated) { - update_save_file(); - } - } -} - -void recomp::save::write_ptr(const void* in, uint32_t offset, uint32_t count) { - { - std::lock_guard lock { save_context.save_buffer_mutex }; - memcpy(&save_context.save_buffer[offset], in, count); - } - - save_context.write_sempahore.signal(); -} - -void recomp::save::write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { - { - std::lock_guard lock { save_context.save_buffer_mutex }; - for (uint32_t i = 0; i < count; i++) { - save_context.save_buffer[offset + i] = MEM_B(i, rdram_address); - } - } - - save_context.write_sempahore.signal(); -} - -void recomp::save::read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { - std::lock_guard lock { save_context.save_buffer_mutex }; - for (size_t i = 0; i < count; i++) { - MEM_B(i, rdram_address) = save_context.save_buffer[offset + i]; - } -} - -void recomp::save::clear(uint32_t start, uint32_t size, char value) { - { - std::lock_guard lock { save_context.save_buffer_mutex }; - std::fill_n(save_context.save_buffer.begin() + start, size, value); - } - - save_context.write_sempahore.signal(); -} - -void recomp::save::init(uint8_t *rdram) { - 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()); - - // 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()) { - save_file.read(save_context.save_buffer.data(), save_context.save_buffer.size()); - } - else { - // Otherwise clear the save file to all zeroes. - save_context.save_buffer.fill(0); - } - - save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM}; -} - -void recomp::save::join_thread() { - if (save_context.saving_thread.joinable()) { - save_context.saving_thread.join(); - } -} - void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_addr, uint32_t size, uint32_t direction) { // TODO asynchronous transfer // TODO implement unaligned DMA correctly diff --git a/librecomp/src/save.cpp b/librecomp/src/save.cpp new file mode 100644 index 0000000..80b3a68 --- /dev/null +++ b/librecomp/src/save.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ultramodern/error_handling.hpp" + +#include "librecomp/files.hpp" +#include "librecomp/game.hpp" +#include "librecomp/recomp.h" +#include "librecomp/save.hpp" + + +struct { + std::array save_buffer; + std::thread saving_thread; + moodycamel::LightweightSemaphore write_sempahore; + std::mutex save_buffer_mutex; +} save_context; + +const std::u8string save_folder = u8"saves"; + +extern std::filesystem::path config_path; + +std::filesystem::path get_save_file_path() { + return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin"); +} + +void update_save_file() { + bool saving_failed = false; + { + std::ofstream save_file = recomp::open_output_file_with_backup(get_save_file_path(), std::ios_base::binary); + + if (save_file.good()) { + std::lock_guard lock{ save_context.save_buffer_mutex }; + save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size()); + } + else { + saving_failed = true; + } + } + if (!saving_failed) { + saving_failed = !recomp::finalize_output_file_with_backup(get_save_file_path()); + } + if (saving_failed) { + ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions and whether the save folder has been moved to Dropbox or similar, as this can cause issues."); + } +} + +extern std::atomic_bool exited; + +void saving_thread_func(RDRAM_ARG1) { + while (!exited) { + bool save_buffer_updated = false; + // Repeatedly wait for a new action to be sent. + constexpr int64_t wait_time_microseconds = 10000; + constexpr int max_actions = 128; + int num_actions = 0; + + // Wait up to the given timeout for a write to come in. Allow multiple writes to coalesce together into a single save. + // Cap the number of coalesced writes to guarantee that the save buffer eventually gets written out to the file even if the game + // is constantly sending writes. + while (save_context.write_sempahore.wait(wait_time_microseconds) && num_actions < max_actions) { + save_buffer_updated = true; + num_actions++; + } + + // If an action came through that affected the save file, save the updated contents. + if (save_buffer_updated) { + update_save_file(); + } + } +} + +void recomp::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); + } + + save_context.write_sempahore.signal(); +} + +void recomp::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 (uint32_t i = 0; i < count; i++) { + save_context.save_buffer[offset + i] = MEM_B(i, rdram_address); + } + } + + save_context.write_sempahore.signal(); +} + +void recomp::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 (size_t i = 0; i < count; i++) { + MEM_B(i, rdram_address) = save_context.save_buffer[offset + i]; + } +} + +void recomp::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); + } + + save_context.write_sempahore.signal(); +} + +void recomp::save::init(uint8_t *rdram) { + 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()); + + // 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()) { + save_file.read(save_context.save_buffer.data(), save_context.save_buffer.size()); + } + else { + // Otherwise clear the save file to all zeroes. + save_context.save_buffer.fill(0); + } + + save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM}; +} + +void recomp::save::join_thread() { + if (save_context.saving_thread.joinable()) { + save_context.saving_thread.join(); + } +}