From acde4d666decd864fe642a34cab1331f50753f53 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 4 Aug 2024 20:59:07 +0200 Subject: [PATCH] Add initial support for persisting a lsit inactive plugins --- source/main.cpp | 5 +- source/plugin/PluginDataFactory.cpp | 15 ++--- source/plugin/PluginDataFactory.h | 2 +- source/utils/WUPSBackendSettings.cpp | 77 ++++++++++++++++++++++++++ source/utils/WUPSBackendSettings.h | 15 +++++ source/utils/config/ConfigDefines.h | 7 ++- source/utils/config/ConfigRenderer.cpp | 28 +++++++--- source/utils/config/ConfigRenderer.h | 5 +- source/utils/config/ConfigUtils.cpp | 55 +++++++++++++++--- source/utils/storage/StorageUtils.cpp | 20 +------ source/utils/utils.cpp | 56 +++++++++++++++++-- source/utils/utils.h | 7 ++- 12 files changed, 236 insertions(+), 56 deletions(-) create mode 100644 source/utils/WUPSBackendSettings.cpp create mode 100644 source/utils/WUPSBackendSettings.h diff --git a/source/main.cpp b/source/main.cpp index ee61093..366db18 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -5,6 +5,7 @@ #include "hooks.h" #include "patcher/hooks_patcher_static.h" #include "plugin/PluginDataFactory.h" +#include "utils/WUPSBackendSettings.h" #include "utils/logger.h" #include "utils/utils.h" #include "version.h" @@ -112,7 +113,9 @@ WUMS_APPLICATION_STARTS() { DEBUG_FUNCTION_LINE("Load plugins from %s", pluginPath.c_str()); - const auto pluginData = PluginDataFactory::loadDir(pluginPath); + WUPSBackendSettings::LoadSettings(); + auto &inactiveList = WUPSBackendSettings::GetInactivePluginFilenames(); + const auto pluginData = PluginDataFactory::loadDir(pluginPath, inactiveList); newLoadedPlugins = PluginManagement::loadPlugins(pluginData, gTrampData); } diff --git a/source/plugin/PluginDataFactory.cpp b/source/plugin/PluginDataFactory.cpp index 6be82be..1f07c32 100644 --- a/source/plugin/PluginDataFactory.cpp +++ b/source/plugin/PluginDataFactory.cpp @@ -26,7 +26,8 @@ #include #include -std::vector PluginDataFactory::loadDir(std::string_view path) { + +std::vector PluginDataFactory::loadDir(std::string_view path, const std::vector &inactivePluginsFilenames) { std::vector result; dirent *dp; DIR *dfd; @@ -54,15 +55,9 @@ std::vector PluginDataFactory::loadDir(std::string_view path) DEBUG_FUNCTION_LINE("Loading plugin: %s", full_file_path.c_str()); if (auto pluginData = load(full_file_path)) { - // TODO: This is only for testing. Remove me!c - bool shouldBeLoadedAndLinked = false; - if (full_file_path.ends_with("AromaBasePlugin.wps") || - full_file_path.ends_with("drc_region_free.wps") || - full_file_path.ends_with("regionfree.wps") || - full_file_path.ends_with("ftpiiu.wps") || - full_file_path.ends_with("wiiload.wps") || - full_file_path.ends_with("homebrew_on_menu.wps")) { - shouldBeLoadedAndLinked = true; + bool shouldBeLoadedAndLinked = true; + if (std::ranges::find(inactivePluginsFilenames, dp->d_name) != inactivePluginsFilenames.end()) { + shouldBeLoadedAndLinked = false; } result.emplace_back(std::move(pluginData), shouldBeLoadedAndLinked); } else { diff --git a/source/plugin/PluginDataFactory.h b/source/plugin/PluginDataFactory.h index fbefd01..2e3880c 100644 --- a/source/plugin/PluginDataFactory.h +++ b/source/plugin/PluginDataFactory.h @@ -25,7 +25,7 @@ class PluginDataFactory { public: - static std::vector loadDir(std::string_view path); + static std::vector loadDir(std::string_view path, const std::vector &inactivePluginsFilenames); static std::unique_ptr load(std::string_view path); diff --git a/source/utils/WUPSBackendSettings.cpp b/source/utils/WUPSBackendSettings.cpp new file mode 100644 index 0000000..6699701 --- /dev/null +++ b/source/utils/WUPSBackendSettings.cpp @@ -0,0 +1,77 @@ +#include "WUPSBackendSettings.h" +#include "fs/CFile.hpp" +#include "fs/FSUtils.h" +#include "utils/logger.h" +#include "utils/utils.h" +#include +#include +#include + +namespace WUPSBackendSettings { + namespace { + std::vector sInactivePlugins; + } + +#define INACTIVE_PLUGINS_KEY "inactive_plugins" + + bool LoadSettings() { + nlohmann::json j = nlohmann::json::object(); + std::string folderPath = getModulePath() + "/configs/"; + std::string filePath = folderPath + "wupsbackend.json"; + + if (!ParseJsonFromFile(filePath, j)) { + sInactivePlugins.clear(); + return false; + } + + if (j.contains(INACTIVE_PLUGINS_KEY) && j[INACTIVE_PLUGINS_KEY].is_array()) { + for (auto &cur : j[INACTIVE_PLUGINS_KEY]) { + if (cur.is_string()) { + sInactivePlugins.push_back(cur); + } + } + } + + return true; + } + + bool SaveSettings() { + std::string folderPath = getModulePath() + "/configs/"; + std::string filePath = folderPath + "wupsbackend.json"; + if (!FSUtils::CreateSubfolder(folderPath)) { + return false; + } + + CFile file(filePath, CFile::WriteOnly); + if (!file.isOpen()) { + DEBUG_FUNCTION_LINE_ERR("Cannot create file %s", filePath.c_str()); + return false; + } + + nlohmann::json j = nlohmann::json::object(); + j[INACTIVE_PLUGINS_KEY] = sInactivePlugins; + + std::string jsonString = j.dump(4, ' ', false, nlohmann::json::error_handler_t::ignore); + auto writeResult = file.write((const uint8_t *) jsonString.c_str(), jsonString.size()); + + file.close(); + + if (writeResult != (int32_t) jsonString.size()) { + return false; + } + + return true; + } + + void SetInactivePluginFilenames(std::span filenames) { + sInactivePlugins.clear(); + for (const auto &filename : filenames) { + sInactivePlugins.emplace_back(filename); + } + } + + const std::vector &GetInactivePluginFilenames() { + return sInactivePlugins; + } + +} // namespace WUPSBackendSettings \ No newline at end of file diff --git a/source/utils/WUPSBackendSettings.h b/source/utils/WUPSBackendSettings.h new file mode 100644 index 0000000..a15dcfb --- /dev/null +++ b/source/utils/WUPSBackendSettings.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace WUPSBackendSettings { + bool LoadSettings(); + + bool SaveSettings(); + + void SetInactivePluginFilenames(std::span filenames); + + const std::vector &GetInactivePluginFilenames(); +}; // namespace WUPSBackendSettings diff --git a/source/utils/config/ConfigDefines.h b/source/utils/config/ConfigDefines.h index 2731081..ee60f36 100644 --- a/source/utils/config/ConfigDefines.h +++ b/source/utils/config/ConfigDefines.h @@ -23,7 +23,8 @@ struct StoredBuffer { }; enum ConfigSubState { - SUB_STATE_RUNNING = 0, - SUB_STATE_RETURN = 1, - SUB_STATE_ERROR = 2, + SUB_STATE_RUNNING = 0, + SUB_STATE_RETURN = 1, + SUB_STATE_RETURN_WITH_PLUGIN_RELOAD = 1, + SUB_STATE_ERROR = 2, }; \ No newline at end of file diff --git a/source/utils/config/ConfigRenderer.cpp b/source/utils/config/ConfigRenderer.cpp index 26a8d66..78a2d3f 100644 --- a/source/utils/config/ConfigRenderer.cpp +++ b/source/utils/config/ConfigRenderer.cpp @@ -90,18 +90,16 @@ ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) { mCursorPos--; } else if (input.data.buttons_d & Input::eButtons::BUTTON_PLUS) { if (mSetActivePluginsMode) { - if (mActivePluginsDirty) { - for (const auto &cur : mConfigs) { - gLoadOnNextLaunch.emplace_back(cur.getConfigInformation().pluginData, cur.isActivePlugin()); - } - _SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr); - } mNeedRedraw = true; mCategoryRenderer.reset(); - return SUB_STATE_RETURN; + return SUB_STATE_RETURN_WITH_PLUGIN_RELOAD; } } else if (input.data.buttons_d & Input::eButtons::BUTTON_X) { - mSetActivePluginsMode = !mSetActivePluginsMode; + if (!mSetActivePluginsMode) { + mSetActivePluginsMode = true; + mNeedRedraw = true; + return SUB_STATE_RUNNING; + } } else if (input.data.buttons_d & Input::eButtons::BUTTON_A) { if (mSetActivePluginsMode) { mActivePluginsDirty = true; @@ -123,7 +121,7 @@ ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) { for (auto &cur : mConfigs) { cur.resetIsActivePlugin(); } - mNeedRedraw = true; + mNeedRedraw = true; mSetActivePluginsMode = false; return SUB_STATE_RUNNING; } else { @@ -252,6 +250,18 @@ void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, c } } +bool ConfigRenderer::GetActivePluginsIfChanged(std::vector &result) { + if (mActivePluginsDirty) { + std::vector inactive_plugins; + result.clear(); + for (const auto &cur : mConfigs) { + result.emplace_back(cur.getConfigInformation().pluginData, cur.isActivePlugin()); + } + return true; + } + return false; +} + void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const WUPSConfigAPIBackend::WUPSConfig &config) { CallOnCloseCallback(info, config.getCategories()); for (const auto &item : config.getItems()) { diff --git a/source/utils/config/ConfigRenderer.h b/source/utils/config/ConfigRenderer.h index 91014e3..14881a7 100644 --- a/source/utils/config/ConfigRenderer.h +++ b/source/utils/config/ConfigRenderer.h @@ -3,12 +3,13 @@ #include "CategoryRenderer.h" #include "ConfigDefines.h" #include "ConfigDisplayItem.h" +#include "plugin/PluginLoadWrapper.h" #include "utils/input/Input.h" #include +#include #include #include -#include #include class ConfigRenderer { @@ -26,6 +27,8 @@ public: void ResetNeedsRedraw(); + bool GetActivePluginsIfChanged(std::vector &result); + private: ConfigSubState UpdateStateMain(const Input &input); diff --git a/source/utils/config/ConfigUtils.cpp b/source/utils/config/ConfigUtils.cpp index ea89ae3..380e53e 100644 --- a/source/utils/config/ConfigUtils.cpp +++ b/source/utils/config/ConfigUtils.cpp @@ -2,7 +2,9 @@ #include "ConfigRenderer.h" #include "config/WUPSConfigAPI.h" #include "hooks.h" +#include "plugin/PluginLoadWrapper.h" #include "utils/DrawUtils.h" +#include "utils/WUPSBackendSettings.h" #include "utils/dc.h" #include "utils/input/CombinedInput.h" #include "utils/input/Input.h" @@ -13,9 +15,11 @@ #include "version.h" #include +#include #include #include #include +#include #include #include @@ -214,16 +218,51 @@ void ConfigUtils::displayMenu() { } startTime = OSGetTime(); - renderBasicScreen("Saving configs..."); - for (const auto &plugin : gLoadedPlugins) { - const auto configData = plugin.getConfigData(); - if (configData) { - if (configData->CallMenuClosedCallback() == WUPSCONFIG_API_RESULT_MISSING_CALLBACK) { - DEBUG_FUNCTION_LINE_WARN("CallMenuClosedCallback is missing for %s", plugin.getMetaInformation().getName().c_str()); + DEBUG_FUNCTION_LINE_INFO("."); + std::vector newActivePluginsList; + + if (renderer.GetActivePluginsIfChanged(newActivePluginsList)) { + startTime = OSGetTime(); + renderBasicScreen("Applying changes, app will now restart..."); + + // Get list of inactive plugins to save them in the config + // Note: this does only consider plugin loaded from the sd card. + std::vector newInactivePluginsList; + for (const auto &cur : newActivePluginsList) { + if (!cur.isLoadAndLink()) { + auto &source = cur.getPluginData()->getSource(); + // TODO: Make sure to only use plugins from the actual plugin directory? + // atm nothing (to my knowledge) is using the WUPS API to load any plugin from a path though + if (source.ends_with(".wps")) { + std::size_t found = source.find_last_of("/\\"); + std::string filename = source.substr(found + 1); + newInactivePluginsList.push_back(filename); + } + } + } + gLoadOnNextLaunch = newActivePluginsList; + WUPSBackendSettings::SetInactivePluginFilenames(newInactivePluginsList); + WUPSBackendSettings::SaveSettings(); + + _SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr); + // Make sure to wait at least 2 seconds so user can read the screen and + // are aware the app will restart now. + auto diffTime = OSTicksToMilliseconds(OSGetTime() - startTime); + if (diffTime < 2000) { + OSSleepTicks(OSTicksToMilliseconds(2000 - diffTime)); + } + } else { + renderBasicScreen("Saving configs..."); + for (const auto &plugin : gLoadedPlugins) { + const auto configData = plugin.getConfigData(); + if (configData) { + if (configData->CallMenuClosedCallback() == WUPSCONFIG_API_RESULT_MISSING_CALLBACK) { + DEBUG_FUNCTION_LINE_WARN("CallMenuClosedCallback is missing for %s", plugin.getMetaInformation().getName().c_str()); + } + } else { + CallHook(plugin, WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED); } - } else { - CallHook(plugin, WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED); } } diff --git a/source/utils/storage/StorageUtils.cpp b/source/utils/storage/StorageUtils.cpp index 0e62964..d7f7eb1 100644 --- a/source/utils/storage/StorageUtils.cpp +++ b/source/utils/storage/StorageUtils.cpp @@ -174,24 +174,10 @@ namespace StorageUtils { WUPSStorageError LoadFromFile(std::string_view plugin_id, nlohmann::json &outJson) { const std::string filePath = getPluginPath() + "/config/" + plugin_id.data() + ".json"; - CFile file(filePath, CFile::ReadOnly); - if (!file.isOpen() || file.size() == 0) { - return WUPS_STORAGE_ERROR_NOT_FOUND; + if (ParseJsonFromFile(filePath, outJson)) { + return WUPS_STORAGE_ERROR_SUCCESS; } - auto *json_data = static_cast(memalign(0x40, ROUNDUP(file.size() + 1, 0x40))); - if (!json_data) { - return WUPS_STORAGE_ERROR_MALLOC_FAILED; - } - WUPSStorageError result = WUPS_STORAGE_ERROR_SUCCESS; - if (const uint64_t readRes = file.read(json_data, file.size()); readRes == file.size()) { - json_data[file.size()] = '\0'; - outJson = nlohmann::json::parse(json_data, nullptr, false); - } else { - result = WUPS_STORAGE_ERROR_IO_ERROR; - } - file.close(); - free(json_data); - return result; + return WUPS_STORAGE_ERROR_IO_ERROR; } WUPSStorageError LoadFromFile(std::string_view plugin_id, StorageItemRoot &rootItem) { diff --git a/source/utils/utils.cpp b/source/utils/utils.cpp index d94ecb8..b9040c1 100644 --- a/source/utils/utils.cpp +++ b/source/utils/utils.cpp @@ -1,30 +1,54 @@ #include "utils.h" +#include "fs/CFile.hpp" #include "globals.h" +#include "json.hpp" #include "logger.h" + #include #include #include #include +#include static std::string sPluginPath; -std::string getPluginPath() { - if (!sPluginPath.empty()) { - return sPluginPath; +static std::string sModulePath; +static std::string sEnvironmentPath; + +std::string getEnvironmentPath() { + if (!sEnvironmentPath.empty()) { + return sEnvironmentPath; } char environmentPath[0x100] = {}; if (const auto handle = IOS_Open("/dev/mcp", IOS_OPEN_READ); handle >= 0) { int in = 0xF9; // IPC_CUSTOM_COPY_ENVIRONMENT_PATH if (IOS_Ioctl(handle, 100, &in, sizeof(in), environmentPath, sizeof(environmentPath)) != IOS_ERROR_OK) { - return "fs:/vol/external01/wiiu/plugins"; + return "fs:/vol/external01/wiiu/environments/aroma"; } IOS_Close(handle); } - sPluginPath = std::string(environmentPath).append("/plugins"); + sEnvironmentPath = environmentPath; + return sEnvironmentPath; +} +std::string getPluginPath() { + if (!sPluginPath.empty()) { + return sPluginPath; + } + + sPluginPath = getEnvironmentPath().append("/plugins"); return sPluginPath; } +std::string getModulePath() { + if (!sModulePath.empty()) { + return sModulePath; + } + + sModulePath = getEnvironmentPath().append("/modules"); + return sModulePath; +} + // https://gist.github.com/ccbrown/9722406 void dumpHex(const void *data, const size_t size) { char ascii[17]; @@ -86,4 +110,26 @@ void CustomDynLoadFree(void *addr) { if (const auto it = std::ranges::find(gAllocatedAddresses, addr); it != gAllocatedAddresses.end()) { gAllocatedAddresses.erase(it); } +} + +bool ParseJsonFromFile(const std::string &filePath, nlohmann::json &outJson) { + CFile file(filePath, CFile::ReadOnly); + if (!file.isOpen() || file.size() == 0) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + auto *json_data = (uint8_t *) memalign(0x40, ROUNDUP(file.size() + 1, 0x40)); + if (!json_data) { + return WUPS_STORAGE_ERROR_MALLOC_FAILED; + } + bool result = true; + uint64_t readRes = file.read(json_data, file.size()); + if (readRes == file.size()) { + json_data[file.size()] = '\0'; + outJson = nlohmann::json::parse(json_data, nullptr, false); + } else { + result = false; + } + file.close(); + free(json_data); + return result; } \ No newline at end of file diff --git a/source/utils/utils.h b/source/utils/utils.h index f71d163..a949f9c 100644 --- a/source/utils/utils.h +++ b/source/utils/utils.h @@ -1,5 +1,6 @@ #pragma once +#include "json.hpp" #include #include #include @@ -137,6 +138,10 @@ void append_move_all_values(Container &dest, Container &src) { std::string getPluginPath(); +std::string getModulePath(); + OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr); -void CustomDynLoadFree(void *addr); \ No newline at end of file +void CustomDynLoadFree(void *addr); + +bool ParseJsonFromFile(const std::string &filePath, nlohmann::json &outJson); \ No newline at end of file