mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2025-10-30 08:02:29 +00:00
Added a mechanism to swap save files at runtime and a corresponding mod API export
This commit is contained in:
parent
cacb14fee5
commit
b4c8f622ca
5 changed files with 59 additions and 4 deletions
|
|
@ -109,6 +109,7 @@ namespace recomp {
|
||||||
|
|
||||||
void start_game(const std::u8string& game_id);
|
void start_game(const std::u8string& game_id);
|
||||||
std::u8string current_game_id();
|
std::u8string current_game_id();
|
||||||
|
std::string current_mod_game_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,16 @@ void recomp_get_mod_version(uint8_t* rdram, recomp_context* ctx, size_t mod_inde
|
||||||
*patch_out = version.patch;
|
*patch_out = version.patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void recomp_change_save_file(uint8_t* rdram, recomp_context* ctx, size_t mod_index) {
|
||||||
|
std::string name = _arg_string<0>(rdram, ctx);
|
||||||
|
std::u8string name_u8 = std::u8string{reinterpret_cast<const char8_t*>(name.data()), name.size()};
|
||||||
|
|
||||||
|
std::string mod_id = recomp::mods::get_mod_id(mod_index);
|
||||||
|
std::u8string mod_id_u8 = std::u8string{reinterpret_cast<const char8_t*>(mod_id.data()), mod_id.size()};
|
||||||
|
|
||||||
|
ultramodern::change_save_file(mod_id_u8, name_u8);
|
||||||
|
}
|
||||||
|
|
||||||
void recomp_free_config_string(uint8_t* rdram, recomp_context* ctx) {
|
void recomp_free_config_string(uint8_t* rdram, recomp_context* ctx) {
|
||||||
gpr str_rdram = (gpr)_arg<0, PTR(char)>(rdram, ctx);
|
gpr str_rdram = (gpr)_arg<0, PTR(char)>(rdram, ctx);
|
||||||
gpr offset = str_rdram - 0xFFFFFFFF80000000ULL;
|
gpr offset = str_rdram - 0xFFFFFFFF80000000ULL;
|
||||||
|
|
@ -75,5 +85,6 @@ void recomp::mods::register_config_exports() {
|
||||||
recomp::overlays::register_ext_base_export("recomp_get_config_double", recomp_get_config_double);
|
recomp::overlays::register_ext_base_export("recomp_get_config_double", recomp_get_config_double);
|
||||||
recomp::overlays::register_ext_base_export("recomp_get_config_string", recomp_get_config_string);
|
recomp::overlays::register_ext_base_export("recomp_get_config_string", recomp_get_config_string);
|
||||||
recomp::overlays::register_ext_base_export("recomp_get_mod_version", recomp_get_mod_version);
|
recomp::overlays::register_ext_base_export("recomp_get_mod_version", recomp_get_mod_version);
|
||||||
|
recomp::overlays::register_ext_base_export("recomp_change_save_file", recomp_change_save_file);
|
||||||
recomp::overlays::register_base_export("recomp_free_config_string", recomp_free_config_string);
|
recomp::overlays::register_base_export("recomp_free_config_string", recomp_free_config_string);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,12 @@ void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr)
|
||||||
struct {
|
struct {
|
||||||
std::vector<char> save_buffer;
|
std::vector<char> save_buffer;
|
||||||
std::thread saving_thread;
|
std::thread saving_thread;
|
||||||
|
std::filesystem::path save_file_path;
|
||||||
moodycamel::LightweightSemaphore write_sempahore;
|
moodycamel::LightweightSemaphore write_sempahore;
|
||||||
|
// Used to tell the saving thread that a file swap is pending.
|
||||||
|
moodycamel::LightweightSemaphore swap_file_pending_sempahore;
|
||||||
|
// Used to tell the consumer thread that the saving thread is ready for a file swap.
|
||||||
|
moodycamel::LightweightSemaphore swap_file_ready_sempahore;
|
||||||
std::mutex save_buffer_mutex;
|
std::mutex save_buffer_mutex;
|
||||||
} save_context;
|
} save_context;
|
||||||
|
|
||||||
|
|
@ -97,7 +102,15 @@ const std::u8string save_folder = u8"saves";
|
||||||
extern std::filesystem::path config_path;
|
extern std::filesystem::path config_path;
|
||||||
|
|
||||||
std::filesystem::path get_save_file_path() {
|
std::filesystem::path get_save_file_path() {
|
||||||
return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin");
|
return save_context.save_file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_save_file_path(const std::u8string& subfolder, const std::u8string& name) {
|
||||||
|
std::filesystem::path save_folder_path = config_path / save_folder;
|
||||||
|
if (!subfolder.empty()) {
|
||||||
|
save_folder_path = save_folder_path / subfolder;
|
||||||
|
}
|
||||||
|
save_context.save_file_path = save_folder_path / (name + u8".bin");
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_save_file() {
|
void update_save_file() {
|
||||||
|
|
@ -143,6 +156,10 @@ void saving_thread_func(RDRAM_ARG1) {
|
||||||
if (save_buffer_updated) {
|
if (save_buffer_updated) {
|
||||||
update_save_file();
|
update_save_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (save_context.swap_file_pending_sempahore.tryWait()) {
|
||||||
|
save_context.swap_file_ready_sempahore.signal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,14 +224,12 @@ size_t get_save_size(recomp::SaveType save_type) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ultramodern::init_saving(RDRAM_ARG1) {
|
void read_save_file() {
|
||||||
std::filesystem::path save_file_path = get_save_file_path();
|
std::filesystem::path save_file_path = get_save_file_path();
|
||||||
|
|
||||||
// Ensure the save file directory exists.
|
// Ensure the save file directory exists.
|
||||||
std::filesystem::create_directories(save_file_path.parent_path());
|
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.
|
// Read the save file if it exists.
|
||||||
std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary);
|
std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary);
|
||||||
if (save_file.good()) {
|
if (save_file.good()) {
|
||||||
|
|
@ -224,10 +239,28 @@ void ultramodern::init_saving(RDRAM_ARG1) {
|
||||||
// Otherwise clear the save file to all zeroes.
|
// Otherwise clear the save file to all zeroes.
|
||||||
std::fill(save_context.save_buffer.begin(), save_context.save_buffer.end(), 0);
|
std::fill(save_context.save_buffer.begin(), save_context.save_buffer.end(), 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ultramodern::init_saving(RDRAM_ARG1) {
|
||||||
|
set_save_file_path(u8"", recomp::current_game_id());
|
||||||
|
|
||||||
|
save_context.save_buffer.resize(get_save_size(recomp::get_save_type()));
|
||||||
|
|
||||||
|
read_save_file();
|
||||||
|
|
||||||
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
|
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ultramodern::change_save_file(const std::u8string& subfolder, const std::u8string& name) {
|
||||||
|
// Tell the saving thread that a file swap is pending.
|
||||||
|
save_context.swap_file_pending_sempahore.signal();
|
||||||
|
// Wait until the saving thread indicates it's ready to swap files.
|
||||||
|
save_context.swap_file_ready_sempahore.wait();
|
||||||
|
// Perform the save file swap.
|
||||||
|
set_save_file_path(subfolder, name);
|
||||||
|
read_save_file();
|
||||||
|
}
|
||||||
|
|
||||||
void ultramodern::join_saving_thread() {
|
void ultramodern::join_saving_thread() {
|
||||||
if (save_context.saving_thread.joinable()) {
|
if (save_context.saving_thread.joinable()) {
|
||||||
save_context.saving_thread.join();
|
save_context.saving_thread.join();
|
||||||
|
|
|
||||||
|
|
@ -490,6 +490,13 @@ std::u8string recomp::current_game_id() {
|
||||||
return current_game.value();
|
return current_game.value();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string recomp::current_mod_game_id() {
|
||||||
|
auto find_it = game_roms.find(current_game_id());
|
||||||
|
const recomp::GameEntry& game_entry = find_it->second;
|
||||||
|
|
||||||
|
return game_entry.mod_game_id;
|
||||||
|
}
|
||||||
|
|
||||||
void recomp::start_game(const std::u8string& game_id) {
|
void recomp::start_game(const std::u8string& game_id) {
|
||||||
std::lock_guard<std::mutex> lock(current_game_mutex);
|
std::lock_guard<std::mutex> lock(current_game_mutex);
|
||||||
current_game = game_id;
|
current_game = game_id;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||||
void init_timers(RDRAM_ARG1);
|
void init_timers(RDRAM_ARG1);
|
||||||
void init_thread_cleanup();
|
void init_thread_cleanup();
|
||||||
|
|
||||||
|
// Saving
|
||||||
|
void change_save_file(const std::u8string& subfolder, const std::u8string& name);
|
||||||
|
|
||||||
// Thread queues.
|
// Thread queues.
|
||||||
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
|
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue