Add initial support for persisting a lsit inactive plugins

This commit is contained in:
Maschell 2024-08-04 20:59:07 +02:00
parent ef1296c8dd
commit 176d663598
12 changed files with 233 additions and 56 deletions

View File

@ -6,6 +6,7 @@
#include "patcher/hooks_patcher_static.h"
#include "plugin/PluginDataFactory.h"
#include "plugin/PluginMetaInformationFactory.h"
#include "utils/WUPSBackendSettings.h"
#include "utils/utils.h"
#include <coreinit/debug.h>
#include <notifications/notifications.h>
@ -107,7 +108,10 @@ WUMS_APPLICATION_STARTS() {
DEBUG_FUNCTION_LINE("Load plugins from %s", pluginPath.c_str());
auto pluginData = PluginDataFactory::loadDir(pluginPath);
WUPSBackendSettings::LoadSettings();
auto &inactiveList = WUPSBackendSettings::GetInactivePluginFilenames();
auto pluginData = PluginDataFactory::loadDir(pluginPath, inactiveList);
gLoadedPlugins = PluginManagement::loadPlugins(pluginData, gTrampData);
initNeeded = true;

View File

@ -24,8 +24,9 @@
#include <dirent.h>
#include <forward_list>
#include <memory>
#include <vector>
std::vector<PluginLoadWrapper> PluginDataFactory::loadDir(std::string_view path) {
std::vector<PluginLoadWrapper> PluginDataFactory::loadDir(std::string_view path, const std::vector<std::string> &inactivePluginsFilenames) {
std::vector<PluginLoadWrapper> result;
struct dirent *dp;
DIR *dfd;
@ -53,15 +54,9 @@ std::vector<PluginLoadWrapper> PluginDataFactory::loadDir(std::string_view path)
DEBUG_FUNCTION_LINE("Loading plugin: %s", full_file_path.c_str());
auto pluginData = load(full_file_path);
if (pluginData) {
// 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::find(inactivePluginsFilenames.begin(), inactivePluginsFilenames.end(), dp->d_name) != inactivePluginsFilenames.end()) {
shouldBeLoadedAndLinked = false;
}
result.emplace_back(std::move(pluginData), shouldBeLoadedAndLinked);
} else {

View File

@ -29,7 +29,7 @@
class PluginDataFactory {
public:
static std::vector<PluginLoadWrapper> loadDir(std::string_view path);
static std::vector<PluginLoadWrapper> loadDir(std::string_view path, const std::vector<std::string> &inactivePluginsFilenames);
static std::unique_ptr<PluginData> load(std::string_view path);

View File

@ -0,0 +1,77 @@
#include "WUPSBackendSettings.h"
#include "fs/CFile.hpp"
#include "fs/FSUtils.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <span>
#include <string>
#include <vector>
namespace WUPSBackendSettings {
namespace {
std::vector<std::string> 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<std::string> filenames) {
sInactivePlugins.clear();
for (const auto &filename : filenames) {
sInactivePlugins.emplace_back(filename);
}
}
const std::vector<std::string> &GetInactivePluginFilenames() {
return sInactivePlugins;
}
} // namespace WUPSBackendSettings

View File

@ -0,0 +1,15 @@
#pragma once
#include <span>
#include <string>
#include <vector>
namespace WUPSBackendSettings {
bool LoadSettings();
bool SaveSettings();
void SetInactivePluginFilenames(std::span<std::string> filenames);
const std::vector<std::string> &GetInactivePluginFilenames();
}; // namespace WUPSBackendSettings

View File

@ -25,7 +25,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,
};

View File

@ -101,18 +101,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;
@ -134,7 +132,7 @@ ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) {
for (auto &cur : mConfigs) {
cur.resetIsActivePlugin();
}
mNeedRedraw = true;
mNeedRedraw = true;
mSetActivePluginsMode = false;
return SUB_STATE_RUNNING;
} else {
@ -233,6 +231,18 @@ void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, c
}
}
bool ConfigRenderer::GetActivePluginsIfChanged(std::vector<PluginLoadWrapper> &result) {
if (mActivePluginsDirty) {
std::vector<std::string> 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()) {

View File

@ -30,6 +30,8 @@ public:
void ResetNeedsRedraw();
bool GetActivePluginsIfChanged(std::vector<PluginLoadWrapper> &result);
private:
ConfigSubState UpdateStateMain(const Input &input);

View File

@ -6,9 +6,12 @@
#include "ConfigRenderer.h"
#include "config/WUPSConfigAPI.h"
#include "hooks.h"
#include "utils/WUPSBackendSettings.h"
#include "utils/input/CombinedInput.h"
#include "utils/input/VPADInput.h"
#include "utils/input/WPADInput.h"
#include <coreinit/title.h>
#include <sysapp/title.h>
#include <avm/tv.h>
#include <coreinit/screen.h>
@ -16,6 +19,7 @@
#include <memory/mappedmemory.h>
#include <ranges>
#include <string>
#include <sysapp/launch.h>
#include <vector>
WUPS_CONFIG_SIMPLE_INPUT ConfigUtils::convertInputs(uint32_t buttons) {
@ -218,16 +222,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<PluginLoadWrapper> 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<std::string> 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);
}
}

View File

@ -174,25 +174,10 @@ namespace StorageUtils {
WUPSStorageError LoadFromFile(std::string_view plugin_id, nlohmann::json &outJson) {
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 = (uint8_t *) memalign(0x40, ROUNDUP(file.size() + 1, 0x40));
if (!json_data) {
return WUPS_STORAGE_ERROR_MALLOC_FAILED;
}
WUPSStorageError result = WUPS_STORAGE_ERROR_SUCCESS;
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 = 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) {

View File

@ -1,14 +1,19 @@
#include "utils.h"
#include "fs/CFile.hpp"
#include "globals.h"
#include "json.hpp"
#include "logger.h"
#include <algorithm>
#include <coreinit/ios.h>
#include <string>
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];
memset(environmentPath, 0, sizeof(environmentPath));
@ -17,15 +22,32 @@ std::string getPluginPath() {
if (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, size_t size) {
char ascii[17];
@ -88,4 +110,26 @@ void CustomDynLoadFree(void *addr) {
if (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;
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "json.hpp"
#include <algorithm>
#include <coreinit/dynload.h>
#include <cstdint>
@ -132,6 +133,10 @@ T pop_locked_first_if(std::mutex &mutex, std::vector<T> &container, Predicate pr
std::string getPluginPath();
std::string getModulePath();
OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr);
void CustomDynLoadFree(void *addr);
void CustomDynLoadFree(void *addr);
bool ParseJsonFromFile(const std::string &filePath, nlohmann::json &outJson);