mirror of
				https://github.com/N64Recomp/N64ModernRuntime.git
				synced 2025-10-30 08:02:29 +00:00 
			
		
		
		
	Compare commits
	
		
			5 commits
		
	
	
		
			94d5b17ab5
			...
			626cb4682d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 626cb4682d | ||
|   | cea072b59b | ||
|   | 83891b4231 | ||
|   | df547d2c06 | ||
|   | bd1dde8774 | 
					 7 changed files with 224 additions and 54 deletions
				
			
		|  | @ -1 +1 @@ | ||||||
| Subproject commit 8781eb44acbf55cb6a109d2aa5529aadb95a419d | Subproject commit c1a6dc93bfa5977de0ea256562058be4f1b73353 | ||||||
|  | @ -96,6 +96,7 @@ namespace recomp { | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         std::string error_to_string(ModLoadError); |         std::string error_to_string(ModLoadError); | ||||||
|  |         void unmet_dependency_handler(uint8_t* rdram, recomp_context* ctx, uintptr_t arg); | ||||||
| 
 | 
 | ||||||
|         enum class CodeModLoadError { |         enum class CodeModLoadError { | ||||||
|             Good, |             Good, | ||||||
|  | @ -133,6 +134,19 @@ namespace recomp { | ||||||
|             String |             String | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  |         enum class DependencyStatus { | ||||||
|  |             // Do not change these values as they're exposed in the mod API!
 | ||||||
|  | 
 | ||||||
|  |             // The dependency was found and the version requirement was met.
 | ||||||
|  |             Found = 0, | ||||||
|  |             // The ID given is not a dependency of the mod in question.
 | ||||||
|  |             InvalidDependency = 1, | ||||||
|  |             // The dependency was not found.
 | ||||||
|  |             NotFound = 2, | ||||||
|  |             // The dependency was found, but the version requirement was not met.
 | ||||||
|  |             WrongVersion = 3 | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         struct ModFileHandle { |         struct ModFileHandle { | ||||||
|             virtual ~ModFileHandle() = default; |             virtual ~ModFileHandle() = default; | ||||||
|             virtual std::vector<char> read_file(const std::string& filepath, bool& exists) const = 0; |             virtual std::vector<char> read_file(const std::string& filepath, bool& exists) const = 0; | ||||||
|  | @ -171,6 +185,7 @@ namespace recomp { | ||||||
|         struct Dependency { |         struct Dependency { | ||||||
|             std::string mod_id; |             std::string mod_id; | ||||||
|             Version version; |             Version version; | ||||||
|  |             bool optional; | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         struct ConfigOptionEnum { |         struct ConfigOptionEnum { | ||||||
|  | @ -363,6 +378,10 @@ namespace recomp { | ||||||
|             bool register_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest); |             bool register_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest); | ||||||
|             ModContentTypeId get_code_content_type() const { return code_content_type_id; } |             ModContentTypeId get_code_content_type() const { return code_content_type_id; } | ||||||
|             bool is_content_runtime_toggleable(ModContentTypeId content_type) const; |             bool is_content_runtime_toggleable(ModContentTypeId content_type) const; | ||||||
|  |             std::string get_mod_display_name(size_t mod_index) const; | ||||||
|  |             std::filesystem::path get_mod_path(size_t mod_index) const; | ||||||
|  |             std::pair<std::string, std::string> get_mod_import_info(size_t mod_index, size_t import_index) const; | ||||||
|  |             DependencyStatus is_dependency_met(size_t mod_index, const std::string& dependency_id) const; | ||||||
|         private: |         private: | ||||||
|             ModOpenError open_mod_from_manifest(ModManifest &manifest, std::string &error_param, const std::vector<ModContentTypeId> &supported_content_types, bool requires_manifest); |             ModOpenError open_mod_from_manifest(ModManifest &manifest, std::string &error_param, const std::vector<ModContentTypeId> &supported_content_types, bool requires_manifest); | ||||||
|             ModOpenError open_mod_from_path(const std::filesystem::path& mod_path, std::string& error_param, const std::vector<ModContentTypeId>& supported_content_types, bool requires_manifest); |             ModOpenError open_mod_from_path(const std::filesystem::path& mod_path, std::string& error_param, const std::vector<ModContentTypeId>& supported_content_types, bool requires_manifest); | ||||||
|  | @ -621,6 +640,10 @@ namespace recomp { | ||||||
|         size_t get_mod_order_index(size_t mod_index); |         size_t get_mod_order_index(size_t mod_index); | ||||||
|         ModContentTypeId register_mod_content_type(const ModContentType& type); |         ModContentTypeId register_mod_content_type(const ModContentType& type); | ||||||
|         bool register_mod_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest); |         bool register_mod_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest); | ||||||
|  |         std::string get_mod_display_name(size_t mod_index); | ||||||
|  |         std::filesystem::path get_mod_path(size_t mod_index); | ||||||
|  |         std::pair<std::string, std::string> get_mod_import_info(size_t mod_index, size_t import_index); | ||||||
|  |         DependencyStatus is_dependency_met(size_t mod_index, const std::string& dependency_id); | ||||||
| 
 | 
 | ||||||
|         void register_config_exports(); |         void register_config_exports(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -96,6 +96,18 @@ void recomp_get_mod_folder_path(uint8_t* rdram, recomp_context* ctx) { | ||||||
|     return_string(rdram, ctx, std::filesystem::absolute(mod_folder_path).u8string());     |     return_string(rdram, ctx, std::filesystem::absolute(mod_folder_path).u8string());     | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void recomp_get_mod_file_path(uint8_t* rdram, recomp_context* ctx, size_t mod_index) { | ||||||
|  |     std::filesystem::path mod_file_path = recomp::mods::get_mod_path(mod_index); | ||||||
|  | 
 | ||||||
|  |     return_string(rdram, ctx, std::filesystem::absolute(mod_file_path).u8string());  | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void recomp_is_dependency_met(uint8_t* rdram, recomp_context* ctx, size_t mod_index) { | ||||||
|  |     std::string dependency_id = _arg_string<0>(rdram, ctx); | ||||||
|  |     recomp::mods::DependencyStatus status = recomp::mods::is_dependency_met(mod_index, dependency_id); | ||||||
|  |     _return(ctx, static_cast<uint32_t>(status)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void recomp::mods::register_config_exports() { | void recomp::mods::register_config_exports() { | ||||||
|     recomp::overlays::register_ext_base_export("recomp_get_config_u32", recomp_get_config_u32); |     recomp::overlays::register_ext_base_export("recomp_get_config_u32", recomp_get_config_u32); | ||||||
|     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); | ||||||
|  | @ -105,4 +117,6 @@ void recomp::mods::register_config_exports() { | ||||||
|     recomp::overlays::register_ext_base_export("recomp_change_save_file", recomp_change_save_file); |     recomp::overlays::register_ext_base_export("recomp_change_save_file", recomp_change_save_file); | ||||||
|     recomp::overlays::register_base_export("recomp_get_save_file_path", recomp_get_save_file_path); |     recomp::overlays::register_base_export("recomp_get_save_file_path", recomp_get_save_file_path); | ||||||
|     recomp::overlays::register_base_export("recomp_get_mod_folder_path", recomp_get_mod_folder_path); |     recomp::overlays::register_base_export("recomp_get_mod_folder_path", recomp_get_mod_folder_path); | ||||||
|  |     recomp::overlays::register_ext_base_export("recomp_get_mod_file_path", recomp_get_mod_file_path); | ||||||
|  |     recomp::overlays::register_ext_base_export("recomp_is_dependency_met", recomp_is_dependency_met); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -181,6 +181,7 @@ const std::string authors_key = "authors"; | ||||||
| const std::string minimum_recomp_version_key = "minimum_recomp_version"; | const std::string minimum_recomp_version_key = "minimum_recomp_version"; | ||||||
| const std::string enabled_by_default_key = "enabled_by_default"; | const std::string enabled_by_default_key = "enabled_by_default"; | ||||||
| const std::string dependencies_key = "dependencies"; | const std::string dependencies_key = "dependencies"; | ||||||
|  | const std::string optional_dependencies_key = "optional_dependencies"; | ||||||
| const std::string native_libraries_key = "native_libraries"; | const std::string native_libraries_key = "native_libraries"; | ||||||
| const std::string config_schema_key = "config_schema"; | const std::string config_schema_key = "config_schema"; | ||||||
| 
 | 
 | ||||||
|  | @ -602,6 +603,26 @@ recomp::mods::ModOpenError recomp::mods::parse_manifest(ModManifest& ret, const | ||||||
|             error_param = dep_string; |             error_param = dep_string; | ||||||
|             return ModOpenError::InvalidDependencyString; |             return ModOpenError::InvalidDependencyString; | ||||||
|         } |         } | ||||||
|  |         cur_dep.optional = false; | ||||||
|  | 
 | ||||||
|  |         size_t dependency_index = ret.dependencies.size(); | ||||||
|  |         ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index); | ||||||
|  |         ret.dependencies.emplace_back(std::move(cur_dep)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Optional dependencies (optional)
 | ||||||
|  |     std::vector<std::string> optional_dep_strings{}; | ||||||
|  |     current_error = try_get_vec<json::string_t>(optional_dep_strings, manifest_json, optional_dependencies_key, false, error_param); | ||||||
|  |     if (current_error != ModOpenError::Good) { | ||||||
|  |         return current_error; | ||||||
|  |     } | ||||||
|  |     for (const std::string& dep_string : optional_dep_strings) { | ||||||
|  |         Dependency cur_dep; | ||||||
|  |         if (!parse_dependency(dep_string, cur_dep)) { | ||||||
|  |             error_param = dep_string; | ||||||
|  |             return ModOpenError::InvalidDependencyString; | ||||||
|  |         } | ||||||
|  |         cur_dep.optional = true; | ||||||
| 
 | 
 | ||||||
|         size_t dependency_index = ret.dependencies.size(); |         size_t dependency_index = ret.dependencies.size(); | ||||||
|         ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index); |         ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index); | ||||||
|  |  | ||||||
|  | @ -505,18 +505,20 @@ recomp::mods::LiveRecompilerCodeHandle::LiveRecompilerCodeHandle( | ||||||
|     N64Recomp::LiveGenerator generator{ context.functions.size(), recompiler_inputs }; |     N64Recomp::LiveGenerator generator{ context.functions.size(), recompiler_inputs }; | ||||||
|     std::vector<std::vector<uint32_t>> dummy_static_funcs{}; |     std::vector<std::vector<uint32_t>> dummy_static_funcs{}; | ||||||
| 
 | 
 | ||||||
|  |     bool errored = false; | ||||||
|  | 
 | ||||||
|     for (size_t func_index = 0; func_index < context.functions.size(); func_index++) { |     for (size_t func_index = 0; func_index < context.functions.size(); func_index++) { | ||||||
|         std::ostringstream dummy_ostream{}; |         std::ostringstream dummy_ostream{}; | ||||||
| 
 | 
 | ||||||
|         if (!N64Recomp::recompile_function_live(generator, context, func_index, dummy_ostream, dummy_static_funcs, true)) { |         if (!N64Recomp::recompile_function_live(generator, context, func_index, dummy_ostream, dummy_static_funcs, true)) { | ||||||
|             is_good = false; |             errored = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Generate the code.
 |     // Generate the code.
 | ||||||
|     recompiler_output = std::make_unique<N64Recomp::LiveGeneratorOutput>(generator.finish()); |     recompiler_output = std::make_unique<N64Recomp::LiveGeneratorOutput>(generator.finish()); | ||||||
|     is_good = recompiler_output->good; |     is_good = !errored && recompiler_output->good; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void recomp::mods::LiveRecompilerCodeHandle::set_imported_function(size_t import_index, GenericFunction func) { | void recomp::mods::LiveRecompilerCodeHandle::set_imported_function(size_t import_index, GenericFunction func) { | ||||||
|  | @ -975,6 +977,45 @@ bool recomp::mods::ModContext::register_container_type(const std::string& extens | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::string recomp::mods::ModContext::get_mod_display_name(size_t mod_index) const { | ||||||
|  |     return opened_mods[mod_index].manifest.display_name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::filesystem::path recomp::mods::ModContext::get_mod_path(size_t mod_index) const { | ||||||
|  |     return opened_mods[mod_index].manifest.mod_root_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::pair<std::string, std::string> recomp::mods::ModContext::get_mod_import_info(size_t mod_index, size_t import_index) const { | ||||||
|  |     const ModHandle& mod = opened_mods[mod_index]; | ||||||
|  |     const N64Recomp::ImportSymbol& imported_func = mod.recompiler_context->import_symbols[import_index]; | ||||||
|  |     const std::string& dependency_id = mod.recompiler_context->dependencies[imported_func.dependency_index]; | ||||||
|  | 
 | ||||||
|  |     return std::make_pair<std::string, std::string>(std::string{ dependency_id }, std::string{ imported_func.base.name }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | recomp::mods::DependencyStatus recomp::mods::ModContext::is_dependency_met(size_t mod_index, const std::string& dependency_id) const { | ||||||
|  |     const ModHandle& mod = opened_mods[mod_index]; | ||||||
|  | 
 | ||||||
|  |     auto find_dep = mod.manifest.dependencies_by_id.find(dependency_id); | ||||||
|  |     if (find_dep == mod.manifest.dependencies_by_id.end()) { | ||||||
|  |         return DependencyStatus::InvalidDependency; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto find_dep_mod = loaded_mods_by_id.find(dependency_id); | ||||||
|  |     if (find_dep_mod == loaded_mods_by_id.end()) { | ||||||
|  |         return DependencyStatus::NotFound; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const Dependency& dep = mod.manifest.dependencies[find_dep->second]; | ||||||
|  |     const ModHandle& dep_mod = opened_mods[find_dep_mod->second]; | ||||||
|  | 
 | ||||||
|  |     if (dep_mod.manifest.version < dep.version) { | ||||||
|  |         return DependencyStatus::WrongVersion; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return DependencyStatus::Found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool recomp::mods::ModContext::is_content_runtime_toggleable(ModContentTypeId content_type) const { | bool recomp::mods::ModContext::is_content_runtime_toggleable(ModContentTypeId content_type) const { | ||||||
|     assert(content_type.value < content_types.size()); |     assert(content_type.value < content_types.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -1026,7 +1067,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable | ||||||
|                 if (mod_from_stack_it != opened_mods_by_id.end()) { |                 if (mod_from_stack_it != opened_mods_by_id.end()) { | ||||||
|                     const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; |                     const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; | ||||||
|                     for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { |                     for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { | ||||||
|                         if (!auto_enabled_mods.contains(dependency.mod_id)) { |                         if (!dependency.optional && !auto_enabled_mods.contains(dependency.mod_id)) { | ||||||
|                             auto_enabled_mods.emplace(dependency.mod_id); |                             auto_enabled_mods.emplace(dependency.mod_id); | ||||||
|                             mod_stack.emplace_back(dependency.mod_id); |                             mod_stack.emplace_back(dependency.mod_id); | ||||||
| 
 | 
 | ||||||
|  | @ -1071,7 +1112,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable | ||||||
|                     if (mod_from_stack_it != opened_mods_by_id.end()) { |                     if (mod_from_stack_it != opened_mods_by_id.end()) { | ||||||
|                         const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; |                         const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; | ||||||
|                         for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { |                         for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { | ||||||
|                             if (!new_auto_enabled_mods.contains(dependency.mod_id)) { |                             if (!dependency.optional && !new_auto_enabled_mods.contains(dependency.mod_id)) { | ||||||
|                                 new_auto_enabled_mods.emplace(dependency.mod_id); |                                 new_auto_enabled_mods.emplace(dependency.mod_id); | ||||||
|                                 mod_stack.emplace_back(dependency.mod_id); |                                 mod_stack.emplace_back(dependency.mod_id); | ||||||
|                             } |                             } | ||||||
|  | @ -2038,25 +2079,28 @@ void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod, | ||||||
|         mod.disable_runtime_toggle(); |         mod.disable_runtime_toggle(); | ||||||
|     } |     } | ||||||
|     for (const recomp::mods::Dependency& cur_dep : mod.manifest.dependencies) { |     for (const recomp::mods::Dependency& cur_dep : mod.manifest.dependencies) { | ||||||
|         // Look for the dependency in the loaded mod mapping.
 |         if (!cur_dep.optional) { | ||||||
|         auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep.mod_id); |             // Look for the dependency in the loaded mod mapping.
 | ||||||
|         if (find_loaded_dep_it == loaded_mods_by_id.end()) { |             auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep.mod_id); | ||||||
|             errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id); |             if (find_loaded_dep_it != loaded_mods_by_id.end()) { | ||||||
|             continue; |                 ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second]; | ||||||
|         } |                 if (cur_dep.version > dep_mod.manifest.version) | ||||||
|  |                 { | ||||||
|  |                     std::stringstream error_param_stream{}; | ||||||
|  |                     error_param_stream << "requires mod \"" << cur_dep.mod_id << "\" " << | ||||||
|  |                         (int)cur_dep.version.major << "." << (int)cur_dep.version.minor << "." << (int)cur_dep.version.patch << ", got " << | ||||||
|  |                         (int)dep_mod.manifest.version.major << "." << (int)dep_mod.manifest.version.minor << "." << (int)dep_mod.manifest.version.patch << ""; | ||||||
|  |                     errors.emplace_back(ModLoadError::WrongDependencyVersion, error_param_stream.str()); | ||||||
|  |                 } | ||||||
| 
 | 
 | ||||||
|         ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second]; |                 // Prevent the dependency from being toggled at runtime, as it's required for this mod.
 | ||||||
|         if (cur_dep.version > dep_mod.manifest.version) |                 dep_mod.disable_runtime_toggle(); | ||||||
|         { |             } | ||||||
|             std::stringstream error_param_stream{}; |             // Add an error for this mod if the dependency isn't optional.
 | ||||||
|             error_param_stream << "requires mod \"" << cur_dep.mod_id << "\" " << |             else { | ||||||
|                 (int)cur_dep.version.major << "." << (int)cur_dep.version.minor << "." << (int)cur_dep.version.patch << ", got " << |                 errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id); | ||||||
|                 (int)dep_mod.manifest.version.major << "." << (int)dep_mod.manifest.version.minor << "." << (int)dep_mod.manifest.version.patch << ""; |             } | ||||||
|             errors.emplace_back(ModLoadError::WrongDependencyVersion, error_param_stream.str()); |  | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         // Prevent the dependency from being toggled at runtime, as it's required for this mod.
 |  | ||||||
|         dep_mod.disable_runtime_toggle(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2125,22 +2169,28 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::init_mod_code(uint8_t* | ||||||
|     int32_t cur_section_addr = load_address; |     int32_t cur_section_addr = load_address; | ||||||
|     for (size_t section_index = 0; section_index < mod_sections.size(); section_index++) { |     for (size_t section_index = 0; section_index < mod_sections.size(); section_index++) { | ||||||
|         const auto& section = mod_sections[section_index]; |         const auto& section = mod_sections[section_index]; | ||||||
|         for (size_t i = 0; i < section.size; i++) { |         // Do not load fixed address sections into mod memory. Use their address as-is.
 | ||||||
|             MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i]; |         if (section.fixed_address) { | ||||||
|  |             mod.section_load_addresses[section_index] = section.ram_addr; | ||||||
|         } |         } | ||||||
|         mod.section_load_addresses[section_index] = cur_section_addr; |         else { | ||||||
|         // Calculate the bss section's address based on the size of this section.
 |             for (size_t i = 0; i < section.size; i++) { | ||||||
|         cur_section_addr += section.size; |                 MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i]; | ||||||
|         // Zero the bss section.
 |             } | ||||||
|         for (size_t i = 0; i < section.bss_size; i++) { |             mod.section_load_addresses[section_index] = cur_section_addr; | ||||||
|             MEM_B(i, (gpr)cur_section_addr) = 0; |             // Calculate the bss section's address based on the size of this section.
 | ||||||
|  |             cur_section_addr += section.size; | ||||||
|  |             // Zero the bss section.
 | ||||||
|  |             for (size_t i = 0; i < section.bss_size; i++) { | ||||||
|  |                 MEM_B(i, (gpr)cur_section_addr) = 0; | ||||||
|  |             } | ||||||
|  |             // Calculate the next section's address based on the size of the bss section.
 | ||||||
|  |             cur_section_addr += section.bss_size; | ||||||
|  |             // Align the next section's address to 16 bytes.
 | ||||||
|  |             cur_section_addr = (cur_section_addr + 15) & ~15; | ||||||
|  |             // Add some empty space between mods to act as a buffer for misbehaving mods that have out of bounds accesses.
 | ||||||
|  |             cur_section_addr += 0x400; | ||||||
|         } |         } | ||||||
|         // Calculate the next section's address based on the size of the bss section.
 |  | ||||||
|         cur_section_addr += section.bss_size; |  | ||||||
|         // Align the next section's address to 16 bytes.
 |  | ||||||
|         cur_section_addr = (cur_section_addr + 15) & ~15; |  | ||||||
|         // Add some empty space between mods to act as a buffer for misbehaving mods that have out of bounds accesses.
 |  | ||||||
|         cur_section_addr += 0x400; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Iterate over each section again after loading them to perform R_MIPS_32 relocations.
 |     // Iterate over each section again after loading them to perform R_MIPS_32 relocations.
 | ||||||
|  | @ -2203,7 +2253,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | ||||||
|     std::unordered_map<size_t, size_t> entry_func_hooks{}; |     std::unordered_map<size_t, size_t> entry_func_hooks{}; | ||||||
|     std::unordered_map<size_t, size_t> return_func_hooks{}; |     std::unordered_map<size_t, size_t> return_func_hooks{}; | ||||||
| 
 | 
 | ||||||
|     // Scan the replacements and check for any 
 |     // Scan the replacements to handle hooks on the replaced functions.
 | ||||||
|     for (const auto& replacement : mod.recompiler_context->replacements) { |     for (const auto& replacement : mod.recompiler_context->replacements) { | ||||||
|         // Check if there's a hook slot for the entry of this function.
 |         // Check if there's a hook slot for the entry of this function.
 | ||||||
|         HookDefinition entry_def { |         HookDefinition entry_def { | ||||||
|  | @ -2362,14 +2412,24 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             auto find_mod_it = loaded_mods_by_id.find(dependency_id); |             auto find_mod_it = loaded_mods_by_id.find(dependency_id); | ||||||
|             if (find_mod_it == loaded_mods_by_id.end()) { |             if (find_mod_it != loaded_mods_by_id.end()) { | ||||||
|                 error_param = "Failed to find import dependency while loading code: " + dependency_id; |                 const auto& dependency = opened_mods[find_mod_it->second]; | ||||||
|                 // This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
 |                 did_find_func = dependency.get_export_function(imported_func.base.name, func_handle); | ||||||
|                 // is validated against the manifest's. 
 |             } | ||||||
|                 return CodeModLoadError::InternalError; |             else { | ||||||
|  |                 auto find_optional_it = mod.manifest.dependencies_by_id.find(dependency_id); | ||||||
|  |                 if (find_optional_it != mod.manifest.dependencies_by_id.end()) { | ||||||
|  |                     uintptr_t shim_argument = ((import_index & 0xFFFFFFFFu) << 32) | (mod_index & 0xFFFFFFFFu); | ||||||
|  |                     func_handle = shim_functions.emplace_back(std::make_unique<N64Recomp::ShimFunction>(unmet_dependency_handler, shim_argument)).get()->get_func(); | ||||||
|  |                     did_find_func = true; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     error_param = "Failed to find import dependency while loading code: " + dependency_id; | ||||||
|  |                     // This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
 | ||||||
|  |                     // is validated against the manifest's. 
 | ||||||
|  |                     return CodeModLoadError::InternalError; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             const auto& dependency = opened_mods[find_mod_it->second]; |  | ||||||
|             did_find_func = dependency.get_export_function(imported_func.base.name, func_handle); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!did_find_func) { |         if (!did_find_func) { | ||||||
|  | @ -2387,6 +2447,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci | ||||||
|         GenericFunction func = mod.code_handle->get_function_handle(callback.function_index); |         GenericFunction func = mod.code_handle->get_function_handle(callback.function_index); | ||||||
|         size_t event_index = 0; |         size_t event_index = 0; | ||||||
|         bool did_find_event = false; |         bool did_find_event = false; | ||||||
|  |         bool optional = false; | ||||||
| 
 | 
 | ||||||
|         if (dependency_id == N64Recomp::DependencyBaseRecomp) { |         if (dependency_id == N64Recomp::DependencyBaseRecomp) { | ||||||
|             event_index = recomp::overlays::get_base_event_index(dependency_event.event_name); |             event_index = recomp::overlays::get_base_event_index(dependency_event.event_name); | ||||||
|  | @ -2398,23 +2459,40 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci | ||||||
|             did_find_event = mod.get_global_event_index(dependency_event.event_name, event_index); |             did_find_event = mod.get_global_event_index(dependency_event.event_name, event_index); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|  |             // Check if the dependency is optional.
 | ||||||
|             auto find_mod_it = loaded_mods_by_id.find(dependency_id); |             auto find_mod_it = loaded_mods_by_id.find(dependency_id); | ||||||
|             if (find_mod_it == loaded_mods_by_id.end()) { |             if (find_mod_it == loaded_mods_by_id.end()) { | ||||||
|                 error_param = "Failed to find callback dependency while loading code: " + dependency_id; |                 // Get the manifest's version of the dependency.
 | ||||||
|                 // This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
 |                 auto find_manifest_dep = mod.manifest.dependencies_by_id.find(dependency_id); | ||||||
|                 // is validated against the manifest's. 
 |                 // This should always be found, but just in case validate that the find was successful.
 | ||||||
|                 return CodeModLoadError::InternalError; |                 // This will get treated as an error if it wasn't found in the manifest.
 | ||||||
|  |                 if (find_manifest_dep != mod.manifest.dependencies_by_id.end()) { | ||||||
|  |                     const auto& manifest_dep = mod.manifest.dependencies[find_manifest_dep->second]; | ||||||
|  |                     if (manifest_dep.optional) { | ||||||
|  |                         optional = true; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (!optional) { | ||||||
|  |                     error_param = "Failed to find callback dependency while loading code: " + dependency_id; | ||||||
|  |                     // This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
 | ||||||
|  |                     // is validated against the manifest's. 
 | ||||||
|  |                     return CodeModLoadError::InternalError; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 const auto& dependency_mod = opened_mods[find_mod_it->second]; | ||||||
|  |                 did_find_event = dependency_mod.get_global_event_index(dependency_event.event_name, event_index); | ||||||
|             } |             } | ||||||
|             const auto& dependency_mod = opened_mods[find_mod_it->second]; |  | ||||||
|             did_find_event = dependency_mod.get_global_event_index(dependency_event.event_name, event_index); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!did_find_event) { |         if (did_find_event) { | ||||||
|  |             recomp::mods::register_event_callback(event_index, mod_index, func); | ||||||
|  |         } | ||||||
|  |         else if (!optional) { | ||||||
|             error_param = dependency_id + ":" + dependency_event.event_name; |             error_param = dependency_id + ":" + dependency_event.event_name; | ||||||
|             return CodeModLoadError::InvalidCallbackEvent; |             return CodeModLoadError::InvalidCallbackEvent; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         recomp::mods::register_event_callback(event_index, mod_index, func); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Register hooks.
 |     // Register hooks.
 | ||||||
|  | @ -2503,3 +2581,18 @@ void recomp::mods::ModContext::unload_mods() { | ||||||
|     num_events = recomp::overlays::num_base_events(); |     num_events = recomp::overlays::num_base_events(); | ||||||
|     active_game = (size_t)-1; |     active_game = (size_t)-1; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void recomp::mods::unmet_dependency_handler(uint8_t* rdram, recomp_context* ctx, uintptr_t arg) { | ||||||
|  |     size_t caller_mod_index = (arg >>  0) & uint64_t(0xFFFFFFFF); | ||||||
|  |     size_t import_index =     (arg >> 32) & uint64_t(0xFFFFFFFF); | ||||||
|  | 
 | ||||||
|  |     std::string mod_name = recomp::mods::get_mod_display_name(caller_mod_index); | ||||||
|  |     std::pair<std::string, std::string> import_info = recomp::mods::get_mod_import_info(caller_mod_index, import_index); | ||||||
|  |      | ||||||
|  |     ultramodern::error_handling::message_box( | ||||||
|  |         ( | ||||||
|  |             "Fatal error in mod \"" + mod_name + "\": Called function \"" + import_info.second + "\" in unmet optional dependency \"" + import_info.first + "\".\n" | ||||||
|  |         ).c_str() | ||||||
|  |     ); | ||||||
|  |     ULTRAMODERN_QUICK_EXIT(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -129,6 +129,26 @@ bool recomp::mods::register_mod_container_type(const std::string& extension, con | ||||||
|     return mod_context->register_container_type(extension, content_types, requires_manifest); |     return mod_context->register_container_type(extension, content_types, requires_manifest); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::string recomp::mods::get_mod_display_name(size_t mod_index) { | ||||||
|  |     std::lock_guard mod_lock{ mod_context_mutex }; | ||||||
|  |     return mod_context->get_mod_display_name(mod_index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::filesystem::path recomp::mods::get_mod_path(size_t mod_index) { | ||||||
|  |     std::lock_guard mod_lock{ mod_context_mutex }; | ||||||
|  |     return mod_context->get_mod_path(mod_index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::pair<std::string, std::string> recomp::mods::get_mod_import_info(size_t mod_index, size_t import_index) { | ||||||
|  |     std::lock_guard mod_lock{ mod_context_mutex }; | ||||||
|  |     return mod_context->get_mod_import_info(mod_index, import_index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | recomp::mods::DependencyStatus recomp::mods::is_dependency_met(size_t mod_index, const std::string& dependency_id) { | ||||||
|  |     std::lock_guard mod_lock{ mod_context_mutex }; | ||||||
|  |     return mod_context->is_dependency_met(mod_index, dependency_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool check_hash(const std::vector<uint8_t>& rom_data, uint64_t expected_hash) { | bool check_hash(const std::vector<uint8_t>& rom_data, uint64_t expected_hash) { | ||||||
|     uint64_t calculated_hash = XXH3_64bits(rom_data.data(), rom_data.size()); |     uint64_t calculated_hash = XXH3_64bits(rom_data.data(), rom_data.size()); | ||||||
|     return calculated_hash == expected_hash; |     return calculated_hash == expected_hash; | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ | ||||||
| #elif defined(__ANDROID__) | #elif defined(__ANDROID__) | ||||||
| #   include "android/native_window.h" | #   include "android/native_window.h" | ||||||
| #elif defined(__linux__) | #elif defined(__linux__) | ||||||
| #   include "X11/Xlib.h" |  | ||||||
| #   undef None | #   undef None | ||||||
| #   undef Status | #   undef Status | ||||||
| #   undef LockMask | #   undef LockMask | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue