mirror of
				https://github.com/N64Recomp/N64ModernRuntime.git
				synced 2025-10-30 08:02:29 +00:00 
			
		
		
		
	Compare commits
	
		
			6 commits
		
	
	
		
			94d5b17ab5
			...
			626cb4682d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 626cb4682d | ||
|   | cea072b59b | ||
|   | 83891b4231 | ||
|   | df547d2c06 | ||
|   | bd1dde8774 | ||
|   | 4b32a19531 | 
					 8 changed files with 225 additions and 55 deletions
				
			
		|  | @ -1 +1 @@ | |||
| Subproject commit 8781eb44acbf55cb6a109d2aa5529aadb95a419d | ||||
| Subproject commit c1a6dc93bfa5977de0ea256562058be4f1b73353 | ||||
|  | @ -96,6 +96,7 @@ namespace recomp { | |||
|         }; | ||||
| 
 | ||||
|         std::string error_to_string(ModLoadError); | ||||
|         void unmet_dependency_handler(uint8_t* rdram, recomp_context* ctx, uintptr_t arg); | ||||
| 
 | ||||
|         enum class CodeModLoadError { | ||||
|             Good, | ||||
|  | @ -133,6 +134,19 @@ namespace recomp { | |||
|             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 { | ||||
|             virtual ~ModFileHandle() = default; | ||||
|             virtual std::vector<char> read_file(const std::string& filepath, bool& exists) const = 0; | ||||
|  | @ -171,6 +185,7 @@ namespace recomp { | |||
|         struct Dependency { | ||||
|             std::string mod_id; | ||||
|             Version version; | ||||
|             bool optional; | ||||
|         }; | ||||
| 
 | ||||
|         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); | ||||
|             ModContentTypeId get_code_content_type() const { return code_content_type_id; } | ||||
|             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: | ||||
|             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); | ||||
|  | @ -621,6 +640,10 @@ namespace recomp { | |||
|         size_t get_mod_order_index(size_t mod_index); | ||||
|         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); | ||||
|         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(); | ||||
|     } | ||||
|  |  | |||
|  | @ -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());     | ||||
| } | ||||
| 
 | ||||
| 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() { | ||||
|     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); | ||||
|  | @ -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_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_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 enabled_by_default_key = "enabled_by_default"; | ||||
| 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 config_schema_key = "config_schema"; | ||||
| 
 | ||||
|  | @ -602,6 +603,26 @@ recomp::mods::ModOpenError recomp::mods::parse_manifest(ModManifest& ret, const | |||
|             error_param = dep_string; | ||||
|             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(); | ||||
|         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 }; | ||||
|     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++) { | ||||
|         std::ostringstream dummy_ostream{}; | ||||
| 
 | ||||
|         if (!N64Recomp::recompile_function_live(generator, context, func_index, dummy_ostream, dummy_static_funcs, true)) { | ||||
|             is_good = false; | ||||
|             errored = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Generate the code.
 | ||||
|     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) { | ||||
|  | @ -975,6 +977,45 @@ bool recomp::mods::ModContext::register_container_type(const std::string& extens | |||
|     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 { | ||||
|     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()) { | ||||
|                     const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; | ||||
|                     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); | ||||
|                             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()) { | ||||
|                         const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; | ||||
|                         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); | ||||
|                                 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(); | ||||
|     } | ||||
|     for (const recomp::mods::Dependency& cur_dep : mod.manifest.dependencies) { | ||||
|         // Look for the dependency in the loaded mod mapping.
 | ||||
|         auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep.mod_id); | ||||
|         if (find_loaded_dep_it == loaded_mods_by_id.end()) { | ||||
|             errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id); | ||||
|             continue; | ||||
|         } | ||||
|         if (!cur_dep.optional) { | ||||
|             // Look for the dependency in the loaded mod mapping.
 | ||||
|             auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep.mod_id); | ||||
|             if (find_loaded_dep_it != loaded_mods_by_id.end()) { | ||||
|                 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]; | ||||
|         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()); | ||||
|                 // Prevent the dependency from being toggled at runtime, as it's required for this mod.
 | ||||
|                 dep_mod.disable_runtime_toggle(); | ||||
|             } | ||||
|             // Add an error for this mod if the dependency isn't optional.
 | ||||
|             else { | ||||
|                 errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // 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; | ||||
|     for (size_t section_index = 0; section_index < mod_sections.size(); section_index++) { | ||||
|         const auto& section = mod_sections[section_index]; | ||||
|         for (size_t i = 0; i < section.size; i++) { | ||||
|             MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i]; | ||||
|         // Do not load fixed address sections into mod memory. Use their address as-is.
 | ||||
|         if (section.fixed_address) { | ||||
|             mod.section_load_addresses[section_index] = section.ram_addr; | ||||
|         } | ||||
|         mod.section_load_addresses[section_index] = cur_section_addr; | ||||
|         // 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; | ||||
|         else { | ||||
|             for (size_t i = 0; i < section.size; i++) { | ||||
|                 MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i]; | ||||
|             } | ||||
|             mod.section_load_addresses[section_index] = cur_section_addr; | ||||
|             // 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.
 | ||||
|  | @ -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> 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) { | ||||
|         // Check if there's a hook slot for the entry of this function.
 | ||||
|         HookDefinition entry_def { | ||||
|  | @ -2362,14 +2412,24 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci | |||
|         } | ||||
|         else { | ||||
|             auto find_mod_it = loaded_mods_by_id.find(dependency_id); | ||||
|             if (find_mod_it == loaded_mods_by_id.end()) { | ||||
|                 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; | ||||
|             if (find_mod_it != loaded_mods_by_id.end()) { | ||||
|                 const auto& dependency = opened_mods[find_mod_it->second]; | ||||
|                 did_find_func = dependency.get_export_function(imported_func.base.name, func_handle); | ||||
|             } | ||||
|             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) { | ||||
|  | @ -2387,6 +2447,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci | |||
|         GenericFunction func = mod.code_handle->get_function_handle(callback.function_index); | ||||
|         size_t event_index = 0; | ||||
|         bool did_find_event = false; | ||||
|         bool optional = false; | ||||
| 
 | ||||
|         if (dependency_id == N64Recomp::DependencyBaseRecomp) { | ||||
|             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); | ||||
|         } | ||||
|         else { | ||||
|             // Check if the dependency is optional.
 | ||||
|             auto find_mod_it = loaded_mods_by_id.find(dependency_id); | ||||
|             if (find_mod_it == loaded_mods_by_id.end()) { | ||||
|                 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; | ||||
|                 // Get the manifest's version of the dependency.
 | ||||
|                 auto find_manifest_dep = mod.manifest.dependencies_by_id.find(dependency_id); | ||||
|                 // This should always be found, but just in case validate that the find was successful.
 | ||||
|                 // 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; | ||||
|             return CodeModLoadError::InvalidCallbackEvent; | ||||
|         } | ||||
| 
 | ||||
|         recomp::mods::register_event_callback(event_index, mod_index, func); | ||||
|     } | ||||
| 
 | ||||
|     // Register hooks.
 | ||||
|  | @ -2503,3 +2581,18 @@ void recomp::mods::ModContext::unload_mods() { | |||
|     num_events = recomp::overlays::num_base_events(); | ||||
|     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); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     uint64_t calculated_hash = XXH3_64bits(rom_data.data(), rom_data.size()); | ||||
|     return calculated_hash == expected_hash; | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ | |||
| #elif defined(__ANDROID__) | ||||
| #   include "android/native_window.h" | ||||
| #elif defined(__linux__) | ||||
| #   include "X11/Xlib.h" | ||||
| #   undef None | ||||
| #   undef Status | ||||
| #   undef LockMask | ||||
|  |  | |||
|  | @ -160,7 +160,7 @@ void vi_thread_func() { | |||
|                 else { | ||||
|                     set_dummy_vi(); | ||||
|                     static bool swap = false; | ||||
|                     uint32_t vi_origin = 0x400 + 0x280; // Skip initial RDRAM contents and add the usual origin offset
 | ||||
|                     uint32_t vi_origin = 0x80700000; // Skip initial RDRAM contents
 | ||||
|                     // Offset by one FB every other frame so RT64 continues drawing
 | ||||
|                     if (swap) { | ||||
|                         vi_origin += 0x25800; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue