@@ -975,6 +975,45 @@ bool recomp::mods::ModContext::register_container_type(const std::string& extens
975975 return true ;
976976}
977977
978+ std::string recomp::mods::ModContext::get_mod_display_name (size_t mod_index) const {
979+ return opened_mods[mod_index].manifest .display_name ;
980+ }
981+
982+ std::filesystem::path recomp::mods::ModContext::get_mod_path (size_t mod_index) const {
983+ return opened_mods[mod_index].manifest .mod_root_path ;
984+ }
985+
986+ std::pair<std::string, std::string> recomp::mods::ModContext::get_mod_import_info (size_t mod_index, size_t import_index) const {
987+ const ModHandle& mod = opened_mods[mod_index];
988+ const N64Recomp::ImportSymbol& imported_func = mod.recompiler_context ->import_symbols [import_index];
989+ const std::string& dependency_id = mod.recompiler_context ->dependencies [imported_func.dependency_index ];
990+
991+ return std::make_pair<std::string, std::string>(std::string{ dependency_id }, std::string{ imported_func.base .name });
992+ }
993+
994+ recomp::mods::DependencyStatus recomp::mods::ModContext::is_dependency_met (size_t mod_index, const std::string& dependency_id) const {
995+ const ModHandle& mod = opened_mods[mod_index];
996+
997+ auto find_dep = mod.manifest .dependencies_by_id .find (dependency_id);
998+ if (find_dep == mod.manifest .dependencies_by_id .end ()) {
999+ return DependencyStatus::InvalidDependency;
1000+ }
1001+
1002+ auto find_dep_mod = loaded_mods_by_id.find (dependency_id);
1003+ if (find_dep_mod == loaded_mods_by_id.end ()) {
1004+ return DependencyStatus::NotFound;
1005+ }
1006+
1007+ const Dependency& dep = mod.manifest .dependencies [find_dep->second ];
1008+ const ModHandle& dep_mod = opened_mods[find_dep_mod->second ];
1009+
1010+ if (dep_mod.manifest .version < dep.version ) {
1011+ return DependencyStatus::WrongVersion;
1012+ }
1013+
1014+ return DependencyStatus::Found;
1015+ }
1016+
9781017bool recomp::mods::ModContext::is_content_runtime_toggleable (ModContentTypeId content_type) const {
9791018 assert (content_type.value < content_types.size ());
9801019
@@ -1026,7 +1065,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable
10261065 if (mod_from_stack_it != opened_mods_by_id.end ()) {
10271066 const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second ];
10281067 for (const Dependency &dependency : mod_from_stack_handle.manifest .dependencies ) {
1029- if (!auto_enabled_mods.contains (dependency.mod_id )) {
1068+ if (!dependency. optional && ! auto_enabled_mods.contains (dependency.mod_id )) {
10301069 auto_enabled_mods.emplace (dependency.mod_id );
10311070 mod_stack.emplace_back (dependency.mod_id );
10321071
@@ -1071,7 +1110,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable
10711110 if (mod_from_stack_it != opened_mods_by_id.end ()) {
10721111 const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second ];
10731112 for (const Dependency &dependency : mod_from_stack_handle.manifest .dependencies ) {
1074- if (!new_auto_enabled_mods.contains (dependency.mod_id )) {
1113+ if (!dependency. optional && ! new_auto_enabled_mods.contains (dependency.mod_id )) {
10751114 new_auto_enabled_mods.emplace (dependency.mod_id );
10761115 mod_stack.emplace_back (dependency.mod_id );
10771116 }
@@ -2038,25 +2077,28 @@ void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod,
20382077 mod.disable_runtime_toggle ();
20392078 }
20402079 for (const recomp::mods::Dependency& cur_dep : mod.manifest .dependencies ) {
2041- // Look for the dependency in the loaded mod mapping.
2042- auto find_loaded_dep_it = loaded_mods_by_id.find (cur_dep.mod_id );
2043- if (find_loaded_dep_it == loaded_mods_by_id.end ()) {
2044- errors.emplace_back (ModLoadError::MissingDependency, cur_dep.mod_id );
2045- continue ;
2046- }
2080+ if (!cur_dep.optional ) {
2081+ // Look for the dependency in the loaded mod mapping.
2082+ auto find_loaded_dep_it = loaded_mods_by_id.find (cur_dep.mod_id );
2083+ if (find_loaded_dep_it != loaded_mods_by_id.end ()) {
2084+ ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second ];
2085+ if (cur_dep.version > dep_mod.manifest .version )
2086+ {
2087+ std::stringstream error_param_stream{};
2088+ error_param_stream << " requires mod \" " << cur_dep.mod_id << " \" " <<
2089+ (int )cur_dep.version .major << " ." << (int )cur_dep.version .minor << " ." << (int )cur_dep.version .patch << " , got " <<
2090+ (int )dep_mod.manifest .version .major << " ." << (int )dep_mod.manifest .version .minor << " ." << (int )dep_mod.manifest .version .patch << " " ;
2091+ errors.emplace_back (ModLoadError::WrongDependencyVersion, error_param_stream.str ());
2092+ }
20472093
2048- ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second ];
2049- if (cur_dep.version > dep_mod.manifest .version )
2050- {
2051- std::stringstream error_param_stream{};
2052- error_param_stream << " requires mod \" " << cur_dep.mod_id << " \" " <<
2053- (int )cur_dep.version .major << " ." << (int )cur_dep.version .minor << " ." << (int )cur_dep.version .patch << " , got " <<
2054- (int )dep_mod.manifest .version .major << " ." << (int )dep_mod.manifest .version .minor << " ." << (int )dep_mod.manifest .version .patch << " " ;
2055- errors.emplace_back (ModLoadError::WrongDependencyVersion, error_param_stream.str ());
2094+ // Prevent the dependency from being toggled at runtime, as it's required for this mod.
2095+ dep_mod.disable_runtime_toggle ();
2096+ }
2097+ // Add an error for this mod if the dependency isn't optional.
2098+ else {
2099+ errors.emplace_back (ModLoadError::MissingDependency, cur_dep.mod_id );
2100+ }
20562101 }
2057-
2058- // Prevent the dependency from being toggled at runtime, as it's required for this mod.
2059- dep_mod.disable_runtime_toggle ();
20602102 }
20612103}
20622104
@@ -2362,14 +2404,24 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci
23622404 }
23632405 else {
23642406 auto find_mod_it = loaded_mods_by_id.find (dependency_id);
2365- if (find_mod_it == loaded_mods_by_id.end ()) {
2366- error_param = " Failed to find import dependency while loading code: " + dependency_id;
2367- // This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
2368- // is validated against the manifest's.
2369- return CodeModLoadError::InternalError;
2407+ if (find_mod_it != loaded_mods_by_id.end ()) {
2408+ const auto & dependency = opened_mods[find_mod_it->second ];
2409+ did_find_func = dependency.get_export_function (imported_func.base .name , func_handle);
2410+ }
2411+ else {
2412+ auto find_optional_it = mod.manifest .dependencies_by_id .find (dependency_id);
2413+ if (find_optional_it != mod.manifest .dependencies_by_id .end ()) {
2414+ uintptr_t shim_argument = ((import_index & 0xFFFFFFFFu ) << 32 ) | (mod_index & 0xFFFFFFFFu );
2415+ func_handle = shim_functions.emplace_back (std::make_unique<N64Recomp::ShimFunction>(unmet_dependency_handler, shim_argument)).get ()->get_func ();
2416+ did_find_func = true ;
2417+ }
2418+ else {
2419+ error_param = " Failed to find import dependency while loading code: " + dependency_id;
2420+ // This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
2421+ // is validated against the manifest's.
2422+ return CodeModLoadError::InternalError;
2423+ }
23702424 }
2371- const auto & dependency = opened_mods[find_mod_it->second ];
2372- did_find_func = dependency.get_export_function (imported_func.base .name , func_handle);
23732425 }
23742426
23752427 if (!did_find_func) {
@@ -2503,3 +2555,18 @@ void recomp::mods::ModContext::unload_mods() {
25032555 num_events = recomp::overlays::num_base_events ();
25042556 active_game = (size_t )-1 ;
25052557}
2558+
2559+ void recomp::mods::unmet_dependency_handler (uint8_t * rdram, recomp_context* ctx, uintptr_t arg) {
2560+ size_t caller_mod_index = (arg >> 0 ) & uint64_t (0xFFFFFFFF );
2561+ size_t import_index = (arg >> 32 ) & uint64_t (0xFFFFFFFF );
2562+
2563+ std::string mod_name = recomp::mods::get_mod_display_name (caller_mod_index);
2564+ std::pair<std::string, std::string> import_info = recomp::mods::get_mod_import_info (caller_mod_index, import_index);
2565+
2566+ ultramodern::error_handling::message_box (
2567+ (
2568+ " Fatal error in mod \" " + mod_name + " \" : Called function \" " + import_info.second + " \" in unmet optional dependency \" " + import_info.first + " \" .\n "
2569+ ).c_str ()
2570+ );
2571+ ULTRAMODERN_QUICK_EXIT ();
2572+ }
0 commit comments