Skip to content

Commit 1f2a583

Browse files
authored
Added a mechanism to swap save files at runtime and a corresponding mod API export (#101)
1 parent cacb14f commit 1f2a583

File tree

5 files changed

+59
-4
lines changed

5 files changed

+59
-4
lines changed

librecomp/include/librecomp/game.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ namespace recomp {
109109

110110
void start_game(const std::u8string& game_id);
111111
std::u8string current_game_id();
112+
std::string current_mod_game_id();
112113
}
113114

114115
#endif

librecomp/src/mod_config_api.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ void recomp_get_mod_version(uint8_t* rdram, recomp_context* ctx, size_t mod_inde
6363
*patch_out = version.patch;
6464
}
6565

66+
void recomp_change_save_file(uint8_t* rdram, recomp_context* ctx, size_t mod_index) {
67+
std::string name = _arg_string<0>(rdram, ctx);
68+
std::u8string name_u8 = std::u8string{reinterpret_cast<const char8_t*>(name.data()), name.size()};
69+
70+
std::string mod_id = recomp::mods::get_mod_id(mod_index);
71+
std::u8string mod_id_u8 = std::u8string{reinterpret_cast<const char8_t*>(mod_id.data()), mod_id.size()};
72+
73+
ultramodern::change_save_file(mod_id_u8, name_u8);
74+
}
75+
6676
void recomp_free_config_string(uint8_t* rdram, recomp_context* ctx) {
6777
gpr str_rdram = (gpr)_arg<0, PTR(char)>(rdram, ctx);
6878
gpr offset = str_rdram - 0xFFFFFFFF80000000ULL;
@@ -75,5 +85,6 @@ void recomp::mods::register_config_exports() {
7585
recomp::overlays::register_ext_base_export("recomp_get_config_double", recomp_get_config_double);
7686
recomp::overlays::register_ext_base_export("recomp_get_config_string", recomp_get_config_string);
7787
recomp::overlays::register_ext_base_export("recomp_get_mod_version", recomp_get_mod_version);
88+
recomp::overlays::register_ext_base_export("recomp_change_save_file", recomp_change_save_file);
7889
recomp::overlays::register_base_export("recomp_free_config_string", recomp_free_config_string);
7990
}

librecomp/src/pi.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr)
8888
struct {
8989
std::vector<char> save_buffer;
9090
std::thread saving_thread;
91+
std::filesystem::path save_file_path;
9192
moodycamel::LightweightSemaphore write_sempahore;
93+
// Used to tell the saving thread that a file swap is pending.
94+
moodycamel::LightweightSemaphore swap_file_pending_sempahore;
95+
// Used to tell the consumer thread that the saving thread is ready for a file swap.
96+
moodycamel::LightweightSemaphore swap_file_ready_sempahore;
9297
std::mutex save_buffer_mutex;
9398
} save_context;
9499

@@ -97,7 +102,15 @@ const std::u8string save_folder = u8"saves";
97102
extern std::filesystem::path config_path;
98103

99104
std::filesystem::path get_save_file_path() {
100-
return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin");
105+
return save_context.save_file_path;
106+
}
107+
108+
void set_save_file_path(const std::u8string& subfolder, const std::u8string& name) {
109+
std::filesystem::path save_folder_path = config_path / save_folder;
110+
if (!subfolder.empty()) {
111+
save_folder_path = save_folder_path / subfolder;
112+
}
113+
save_context.save_file_path = save_folder_path / (name + u8".bin");
101114
}
102115

103116
void update_save_file() {
@@ -143,6 +156,10 @@ void saving_thread_func(RDRAM_ARG1) {
143156
if (save_buffer_updated) {
144157
update_save_file();
145158
}
159+
160+
if (save_context.swap_file_pending_sempahore.tryWait()) {
161+
save_context.swap_file_ready_sempahore.signal();
162+
}
146163
}
147164
}
148165

@@ -207,14 +224,12 @@ size_t get_save_size(recomp::SaveType save_type) {
207224
return 0;
208225
}
209226

210-
void ultramodern::init_saving(RDRAM_ARG1) {
227+
void read_save_file() {
211228
std::filesystem::path save_file_path = get_save_file_path();
212229

213230
// Ensure the save file directory exists.
214231
std::filesystem::create_directories(save_file_path.parent_path());
215232

216-
save_context.save_buffer.resize(get_save_size(recomp::get_save_type()));
217-
218233
// Read the save file if it exists.
219234
std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary);
220235
if (save_file.good()) {
@@ -224,10 +239,28 @@ void ultramodern::init_saving(RDRAM_ARG1) {
224239
// Otherwise clear the save file to all zeroes.
225240
std::fill(save_context.save_buffer.begin(), save_context.save_buffer.end(), 0);
226241
}
242+
}
243+
244+
void ultramodern::init_saving(RDRAM_ARG1) {
245+
set_save_file_path(u8"", recomp::current_game_id());
246+
247+
save_context.save_buffer.resize(get_save_size(recomp::get_save_type()));
248+
249+
read_save_file();
227250

228251
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
229252
}
230253

254+
void ultramodern::change_save_file(const std::u8string& subfolder, const std::u8string& name) {
255+
// Tell the saving thread that a file swap is pending.
256+
save_context.swap_file_pending_sempahore.signal();
257+
// Wait until the saving thread indicates it's ready to swap files.
258+
save_context.swap_file_ready_sempahore.wait();
259+
// Perform the save file swap.
260+
set_save_file_path(subfolder, name);
261+
read_save_file();
262+
}
263+
231264
void ultramodern::join_saving_thread() {
232265
if (save_context.saving_thread.joinable()) {
233266
save_context.saving_thread.join();

librecomp/src/recomp.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,13 @@ std::u8string recomp::current_game_id() {
490490
return current_game.value();
491491
};
492492

493+
std::string recomp::current_mod_game_id() {
494+
auto find_it = game_roms.find(current_game_id());
495+
const recomp::GameEntry& game_entry = find_it->second;
496+
497+
return game_entry.mod_game_id;
498+
}
499+
493500
void recomp::start_game(const std::u8string& game_id) {
494501
std::lock_guard<std::mutex> lock(current_game_mutex);
495502
current_game = game_id;

ultramodern/include/ultramodern/ultramodern.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
3737
void init_timers(RDRAM_ARG1);
3838
void init_thread_cleanup();
3939

40+
// Saving
41+
void change_save_file(const std::u8string& subfolder, const std::u8string& name);
42+
4043
// Thread queues.
4144
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
4245

0 commit comments

Comments
 (0)