From 532127292473c87dc6ba65c74dcaf237d0835b14 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 2 Feb 2025 12:50:53 +0100 Subject: [PATCH] Unload unactive plugin when wiiloading a plugin with the same name and author --- source/PluginManagement.cpp | 3 ++ source/config/WUPSConfigAPI.cpp | 3 ++ source/hooks.cpp | 5 ++- source/main.cpp | 79 +++++++++++++++++++++++++++++++-- source/utils/exports.cpp | 11 +++-- 5 files changed, 93 insertions(+), 8 deletions(-) diff --git a/source/PluginManagement.cpp b/source/PluginManagement.cpp index 95d39d3..6702a25 100644 --- a/source/PluginManagement.cpp +++ b/source/PluginManagement.cpp @@ -172,6 +172,9 @@ bool PluginManagement::doRelocations(const std::vector &plugins bool PluginManagement::RestoreFunctionPatches(std::vector &plugins) { for (auto &cur : std::ranges::reverse_view(plugins)) { + if (!cur.isLinkedAndLoaded()) { + continue; + } for (auto &curFunction : std::ranges::reverse_view(cur.getPluginLinkInformation().getFunctionDataList())) { if (!curFunction.RemovePatch()) { DEBUG_FUNCTION_LINE_ERR("Failed to remove function patch for: plugin %s", cur.getMetaInformation().getName().c_str()); diff --git a/source/config/WUPSConfigAPI.cpp b/source/config/WUPSConfigAPI.cpp index 6168c88..46c1845 100644 --- a/source/config/WUPSConfigAPI.cpp +++ b/source/config/WUPSConfigAPI.cpp @@ -146,6 +146,9 @@ namespace WUPSConfigAPIBackend { return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; } for (auto &cur : gLoadedPlugins) { + if (!cur.isLinkedAndLoaded()) { + continue; + } if (cur.getHandle() == pluginIdentifier) { if (options.version != 1) { return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION; diff --git a/source/hooks.cpp b/source/hooks.cpp index 765a1b2..25dac04 100644 --- a/source/hooks.cpp +++ b/source/hooks.cpp @@ -60,7 +60,10 @@ void CallHook(const std::vector &plugins, const wups_loader_hoo } } -void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type) { +void CallHook(const PluginContainer &plugin, const wups_loader_hook_type_t hook_type) { + if (!plugin.isLinkedAndLoaded()) { + return; + } for (const auto &hook : plugin.getPluginLinkInformation().getHookDataList()) { if (hook.getType() == hook_type) { DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s for plugin %s [%d]", hook_names[hook.getType()], plugin.getMetaInformation().getName().c_str(), hook_type); diff --git a/source/main.cpp b/source/main.cpp index 75eff9c..6e9b057 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -9,6 +9,7 @@ #include "plugin/PluginData.h" #include "plugin/PluginDataFactory.h" #include "plugin/PluginLoadWrapper.h" +#include "plugin/PluginMetaInformationFactory.h" #include "plugin/RelocationData.h" #include "plugin/SectionInfo.h" #include "utils/DrawUtils.h" @@ -197,15 +198,81 @@ WUMS_APPLICATION_STARTS() { DEBUG_FUNCTION_LINE_INFO("Got new list of plugins to load"); std::vector pluginsToKeep; std::vector toBeLoaded; + std::vector filteredLoadOnNextLaunch; + + { + struct MetaInfoShort { + MetaInfoShort() = default; + MetaInfoShort(const PluginMetaInformation &metaInfo) : name(metaInfo.getName()), author(metaInfo.getAuthor()) {} + bool operator==(const MetaInfoShort &other) const { + return name == other.name && author == other.author; + } + + std::string name; + std::string author; + }; + // Build a meta info cache for + std::map pluginMetaInformationCache; + for (const auto &pluginLoadWrapper : gLoadOnNextLaunch) { + const auto &pluginData = pluginLoadWrapper.getPluginData(); + // Skip already cached entries + if (pluginMetaInformationCache.contains(pluginData->getHandle())) { + DEBUG_FUNCTION_LINE_ERR("Skip parsing meta information for %08X because it's already cached", pluginData->getHandle()); + continue; + } + // First to get a copy of meta information from the already loaded plugins + if (const auto it = std::ranges::find_if(gLoadedPlugins, [&pluginLoadWrapper](const PluginContainer &plugin) { + return plugin.getPluginDataCopy()->getHandle() == pluginLoadWrapper.getPluginData()->getHandle(); + }); + it != gLoadedPlugins.end()) { + pluginMetaInformationCache[pluginData->getHandle()] = it->getMetaInformation(); + continue; + } + // If this fails, we parse try to parse it instead + PluginParseErrors err; + if (const auto metaInfoOpt = PluginMetaInformationFactory::loadPlugin(*pluginData, err)) { + pluginMetaInformationCache[pluginData->getHandle()] = *metaInfoOpt; + } else { + DEBUG_FUNCTION_LINE_WARN("Failed to parse meta data for plugin data handle %08X", pluginData->getHandle()); + } + } + + // Check if we want to link a plugin that's currently unloaded + // E.g. if you disable a plugin from the config menu and then wiiload it, the disabled plugin copy should be unloaded + for (const auto &pluginLoadWrapper : gLoadOnNextLaunch) { + if (!pluginLoadWrapper.isLoadAndLink()) { + const auto unloadedMetaInfoIt = pluginMetaInformationCache.find(pluginLoadWrapper.getPluginData()->getHandle()); + if (unloadedMetaInfoIt == pluginMetaInformationCache.end()) { + DEBUG_FUNCTION_LINE_WARN("Failed to find meta information for plugin data handle %08X", pluginLoadWrapper.getPluginData()->getHandle()); + continue; + } + if (const auto it = std::ranges::find_if(gLoadOnNextLaunch, [&pluginLoadWrapper, &pluginMetaInformationCache, &unloadedMetaInfoIt](const PluginLoadWrapper &plugin) { + const bool differentPluginData = plugin.getPluginData()->getHandle() != pluginLoadWrapper.getPluginData()->getHandle(); + const bool otherWillBeLinked = plugin.isLoadAndLink(); + bool sameAuthorAndName = false; + if (const auto otherMetaInfoIt = pluginMetaInformationCache.find(plugin.getPluginData()->getHandle()); otherMetaInfoIt != pluginMetaInformationCache.end()) { + const auto &otherMetaInfo = otherMetaInfoIt->second; + const auto &unloadedMetaInfo = unloadedMetaInfoIt->second; + sameAuthorAndName = otherMetaInfo == unloadedMetaInfo; + } + return differentPluginData && otherWillBeLinked && sameAuthorAndName; + }); + it != gLoadOnNextLaunch.end()) { + continue; + } + } + filteredLoadOnNextLaunch.push_back(pluginLoadWrapper); + } + } // Check which plugins are already loaded and which needs to be - for (const auto &pluginLoadWrapper : gLoadOnNextLaunch) { + for (const auto &pluginLoadWrapper : filteredLoadOnNextLaunch) { const auto &pluginNeedsNoReloadFn = [&pluginLoadWrapper](const PluginContainer &container) { return (container.getPluginDataCopy()->getHandle() == pluginLoadWrapper.getPluginData()->getHandle()) && (container.isLinkedAndLoaded() == pluginLoadWrapper.isLoadAndLink()); }; // Check if the plugin data is already loaded - if (auto it = std::ranges::find_if(gLoadedPlugins, pluginNeedsNoReloadFn); + if (const auto it = std::ranges::find_if(gLoadedPlugins, pluginNeedsNoReloadFn); it != gLoadedPlugins.end()) { pluginsToKeep.push_back(std::move(*it)); gLoadedPlugins.erase(it); @@ -255,7 +322,7 @@ WUMS_APPLICATION_STARTS() { CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_INIT_WRAPPER, needsInitsCheck); for (auto &plugin : gLoadedPlugins) { - if (plugin.isInitDone()) { continue; } + if (plugin.isInitDone() && !plugin.isLinkedAndLoaded()) { continue; } if (const WUPSStorageError err = plugin.OpenStorage(); err != WUPS_STORAGE_ERROR_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Failed to open storage for plugin: %s. (%s)", plugin.getMetaInformation().getName().c_str(), WUPSStorageAPI_GetStatusStr(err)); } @@ -275,6 +342,10 @@ void CleanupPlugins(std::vector &&pluginsToDeinit) { const auto saved_reent = currentThread->reserved[4]; const auto saved_cleanupCallback = currentThread->cleanupCallback; + for (const auto &pluginContainer : pluginsToDeinit) { + DEBUG_FUNCTION_LINE_INFO("De-init plugin %s from %s. PluginData: %08X", pluginContainer.getMetaInformation().getName().c_str(), pluginContainer.getMetaInformation().getAuthor().c_str(), pluginContainer.getPluginDataCopy()->getHandle()); + } + currentThread->reserved[4] = 0; CallHook(pluginsToDeinit, WUPS_LOADER_HOOK_DEINIT_PLUGIN); @@ -292,7 +363,7 @@ void CleanupPlugins(std::vector &&pluginsToDeinit) { PluginManagement::RestoreFunctionPatches(pluginsToDeinit); for (auto &plugin : pluginsToDeinit) { - if (!plugin.isInitDone()) { continue; } + if (!plugin.isInitDone() || !plugin.isLinkedAndLoaded()) { continue; } if (const WUPSStorageError err = plugin.CloseStorage(); err != WUPS_STORAGE_ERROR_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Failed to close storage for plugin: %s", plugin.getMetaInformation().getName().c_str()); } diff --git a/source/utils/exports.cpp b/source/utils/exports.cpp index 08dcd47..a8ac206 100644 --- a/source/utils/exports.cpp +++ b/source/utils/exports.cpp @@ -49,8 +49,7 @@ extern "C" PluginBackendApiErrorType WUPSLoadAndLinkByDataHandle(const wups_back } } - // TODO: What happens when we wiiload a new version of an inactive plugin? Do we consider that a problem? - // add all loaded plugins that are not active as inactive + // Keep inactive plugins loaded. Duplicates will be eliminated by comparing name/author for (const auto &plugin : gLoadedPlugins) { if (!plugin.isLinkedAndLoaded()) { gLoadOnNextLaunch.emplace_back(plugin.getPluginDataCopy(), false); @@ -178,6 +177,9 @@ extern "C" PluginBackendApiErrorType WUPSGetPluginDataForContainerHandles(const const auto handle = plugin_container_handle_list[i]; bool found = false; for (const auto &curContainer : gLoadedPlugins) { + if (!curContainer.isLinkedAndLoaded()) { + continue; + } if (curContainer.getHandle() == handle) { auto pluginData = curContainer.getPluginDataCopy(); plugin_data_list[i] = pluginData->getHandle(); @@ -202,6 +204,9 @@ extern "C" PluginBackendApiErrorType WUPSGetMetaInformation(const wups_backend_p auto handle = plugin_container_handle_list[i]; bool found = false; for (const auto &curContainer : gLoadedPlugins) { + if (!curContainer.isLinkedAndLoaded()) { + continue; + } if (curContainer.getHandle() == handle) { const auto &metaInfo = curContainer.getMetaInformation(); @@ -278,7 +283,7 @@ extern "C" PluginBackendApiErrorType WUPSGetNumberOfLoadedPlugins(uint32_t *outC if (outCount == nullptr) { return PLUGIN_BACKEND_API_ERROR_INVALID_ARG; } - *outCount = std::count_if(gLoadedPlugins.begin(), gLoadedPlugins.end(), [](const auto &cur) { return cur.isLinkedAndLoaded(); }); + *outCount = std::ranges::count_if(gLoadedPlugins, [](const auto &cur) { return cur.isLinkedAndLoaded(); }); return PLUGIN_BACKEND_API_ERROR_NONE; }