mirror of
				https://github.com/N64Recomp/N64ModernRuntime.git
				synced 2025-10-30 08:02:29 +00:00 
			
		
		
		
	Compare commits
	
		
			5 commits
		
	
	
		
			81b8761391
			...
			48a769d071
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						48a769d071 | ||
| 
							 | 
						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);
 | 
			
		||||
        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,13 +2079,10 @@ void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod,
 | 
			
		|||
        mod.disable_runtime_toggle();
 | 
			
		||||
    }
 | 
			
		||||
    for (const recomp::mods::Dependency& cur_dep : mod.manifest.dependencies) {
 | 
			
		||||
        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()) {
 | 
			
		||||
            errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			@ -2058,6 +2096,12 @@ void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod,
 | 
			
		|||
                // 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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
recomp::mods::CodeModLoadError recomp::mods::ModContext::init_mod_code(uint8_t* rdram, const std::unordered_map<uint32_t, uint16_t>& section_vrom_map, ModHandle& mod, int32_t load_address, bool hooks_available, uint32_t& ram_used, std::string& error_param) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2125,6 +2169,11 @@ 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];
 | 
			
		||||
        // 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;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            for (size_t i = 0; i < section.size; i++) {
 | 
			
		||||
                MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i];
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -2142,6 +2191,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::init_mod_code(uint8_t*
 | 
			
		|||
            // 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.
 | 
			
		||||
    for (size_t section_index = 0; section_index < mod_sections.size(); section_index++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -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()) {
 | 
			
		||||
            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()) {
 | 
			
		||||
                // 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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue