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 (#101)
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
		
			
				
	
				validate / ubuntu (x64, Release) (push) Waiting to run
				
			
		
			
				
	
				validate / ubuntu (arm64, Debug) (push) Waiting to run
				
			
		
			
				
	
				validate / ubuntu (arm64, Release) (push) Waiting to run
				
			
		
			
				
	
				validate / ubuntu (x64, Debug) (push) Waiting to run
				
			
		
			
				
	
				validate / windows (x64, Debug) (push) Waiting to run
				
			
		
			
				
	
				validate / windows (x64, Release) (push) Waiting to run
				
			
		
			
				
	
				validate / macos (arm64, Debug) (push) Waiting to run
				
			
		
			
				
	
				validate / macos (arm64, Release) (push) Waiting to run
				
			
		
			
				
	
				validate / macos (x64, Debug) (push) Waiting to run
				
			
		
			
				
	
				validate / macos (x64, Release) (push) Waiting to run
				
			
		
		
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	validate / ubuntu (x64, Release) (push) Waiting to run
				
			validate / ubuntu (arm64, Debug) (push) Waiting to run
				
			validate / ubuntu (arm64, Release) (push) Waiting to run
				
			validate / ubuntu (x64, Debug) (push) Waiting to run
				
			validate / windows (x64, Debug) (push) Waiting to run
				
			validate / windows (x64, Release) (push) Waiting to run
				
			validate / macos (arm64, Debug) (push) Waiting to run
				
			validate / macos (arm64, Release) (push) Waiting to run
				
			validate / macos (x64, Debug) (push) Waiting to run
				
			validate / macos (x64, Release) (push) Waiting to run
				
			This commit is contained in:
		
							parent
							
								
									cacb14fee5
								
							
						
					
					
						commit
						1f2a5838ab
					
				
					 5 changed files with 59 additions and 4 deletions
				
			
		|  | @ -109,6 +109,7 @@ namespace recomp { | |||
| 
 | ||||
|     void start_game(const std::u8string& game_id); | ||||
|     std::u8string current_game_id(); | ||||
|     std::string current_mod_game_id(); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -63,6 +63,16 @@ void recomp_get_mod_version(uint8_t* rdram, recomp_context* ctx, size_t mod_inde | |||
|     *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) { | ||||
|     gpr str_rdram = (gpr)_arg<0, PTR(char)>(rdram, ctx); | ||||
|     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_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_change_save_file", recomp_change_save_file); | ||||
|     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 { | ||||
|     std::vector<char> save_buffer; | ||||
|     std::thread saving_thread; | ||||
|     std::filesystem::path save_file_path; | ||||
|     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; | ||||
| } save_context; | ||||
| 
 | ||||
|  | @ -97,7 +102,15 @@ 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"); | ||||
|     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() { | ||||
|  | @ -143,6 +156,10 @@ void saving_thread_func(RDRAM_ARG1) { | |||
|         if (save_buffer_updated) { | ||||
|             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; | ||||
| } | ||||
| 
 | ||||
| void ultramodern::init_saving(RDRAM_ARG1) { | ||||
| void read_save_file() { | ||||
|     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()) { | ||||
|  | @ -224,10 +239,28 @@ void ultramodern::init_saving(RDRAM_ARG1) { | |||
|         // Otherwise clear the save file to all zeroes.
 | ||||
|         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}; | ||||
| } | ||||
| 
 | ||||
| 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() { | ||||
|     if (save_context.saving_thread.joinable()) { | ||||
|         save_context.saving_thread.join(); | ||||
|  |  | |||
|  | @ -490,6 +490,13 @@ std::u8string recomp::current_game_id() { | |||
|     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) { | ||||
|     std::lock_guard<std::mutex> lock(current_game_mutex); | ||||
|     current_game = game_id; | ||||
|  |  | |||
|  | @ -37,6 +37,9 @@ void init_events(RDRAM_ARG renderer::WindowHandle window_handle); | |||
| void init_timers(RDRAM_ARG1); | ||||
| void init_thread_cleanup(); | ||||
| 
 | ||||
| // Saving
 | ||||
| void change_save_file(const std::u8string& subfolder, const std::u8string& name); | ||||
| 
 | ||||
| // Thread queues.
 | ||||
| constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Wiseguy
						Wiseguy