mirror of
https://github.com/wiiu-env/WiiUPluginLoaderBackend.git
synced 2024-12-24 12:01:49 +01:00
329 lines
16 KiB
C++
329 lines
16 KiB
C++
#include "WUPSConfigAPI.h"
|
|
#include "WUPSConfig.h"
|
|
#include "WUPSConfigCategory.h"
|
|
#include "WUPSConfigItem.h"
|
|
#include "WUPSConfigItemV1.h"
|
|
#include "WUPSConfigItemV2.h"
|
|
#include "globals.h"
|
|
#include "plugin/PluginConfigData.h"
|
|
#include "plugin/PluginContainer.h"
|
|
#include "utils/logger.h"
|
|
#include "utils/utils.h"
|
|
|
|
#include <memory>
|
|
#include <ranges>
|
|
#include <vector>
|
|
#include <wums/exports.h>
|
|
#include <wups/config.h>
|
|
#include <wups/config_api.h>
|
|
|
|
namespace WUPSConfigAPIBackend {
|
|
|
|
std::vector<std::unique_ptr<WUPSConfig>> sConfigs;
|
|
std::mutex sConfigsMutex;
|
|
|
|
std::vector<std::unique_ptr<WUPSConfigCategory>> sConfigCategories;
|
|
std::mutex sConfigCategoryMutex;
|
|
|
|
std::vector<std::unique_ptr<WUPSConfigItem>> sConfigItems;
|
|
std::mutex sConfigItemsMutex;
|
|
|
|
namespace Intern {
|
|
WUPSConfig *GetConfigByHandle(WUPSConfigHandle handle) {
|
|
std::lock_guard lock(sConfigsMutex);
|
|
const auto itr = std::ranges::find_if(sConfigs, [&handle](auto &cur) { return handle == cur.get(); });
|
|
if (itr == sConfigs.end()) {
|
|
return nullptr;
|
|
}
|
|
return itr->get();
|
|
}
|
|
|
|
std::unique_ptr<WUPSConfig> PopConfigByHandle(WUPSConfigHandle handle) {
|
|
return pop_locked_first_if(sConfigsMutex, sConfigs, [&handle](auto &cur) { return handle == cur.get(); });
|
|
}
|
|
|
|
static WUPSConfigCategory *GetCategoryByHandleRecursive(WUPSConfigCategory *category, WUPSConfigCategoryHandle handle) {
|
|
if (handle == category) {
|
|
return category;
|
|
}
|
|
for (const auto &cat : category->getCategories()) {
|
|
if (const auto res = GetCategoryByHandleRecursive(cat.get(), handle)) {
|
|
return res;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
WUPSConfigCategory *GetCategoryByHandle(WUPSConfigCategoryHandle handle, const bool checkRecursive) {
|
|
std::lock_guard lock(sConfigCategoryMutex);
|
|
const auto itr = std::ranges::find_if(sConfigCategories, [&handle](auto &cur) { return handle == cur.get(); });
|
|
if (itr == sConfigCategories.end()) {
|
|
if (checkRecursive) {
|
|
std::lock_guard config_lock(sConfigsMutex);
|
|
for (const auto &curConfig : sConfigs) {
|
|
if (auto *category = GetCategoryByHandleRecursive(curConfig.get(), handle)) {
|
|
return category;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
return itr->get();
|
|
}
|
|
|
|
|
|
std::unique_ptr<WUPSConfigCategory> PopCategoryByHandle(WUPSConfigCategoryHandle handle) {
|
|
return pop_locked_first_if(sConfigCategoryMutex, sConfigCategories, [&handle](auto &cur) { return handle == cur.get(); });
|
|
}
|
|
|
|
WUPSConfigItem *GetItemByHandle(WUPSConfigItemHandle handle) {
|
|
std::lock_guard lock(sConfigItemsMutex);
|
|
const auto itr = std::ranges::find_if(sConfigItems, [&handle](auto &cur) { return handle == cur.get(); });
|
|
if (itr == sConfigItems.end()) {
|
|
return nullptr;
|
|
}
|
|
return itr->get();
|
|
}
|
|
|
|
std::unique_ptr<WUPSConfigItem> PopItemByHandle(WUPSConfigItemHandle handle) {
|
|
return pop_locked_first_if(sConfigItemsMutex, sConfigItems, [&handle](auto &cur) { return handle == cur.get(); });
|
|
}
|
|
|
|
WUPSConfigAPIStatus CreateConfig(const char *name, WUPSConfigHandle *out) {
|
|
if (out == nullptr || name == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"out\" or \"name\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
auto config = make_unique_nothrow<WUPSConfig>(name);
|
|
if (!config) {
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to allocate WUPSConfig");
|
|
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
|
}
|
|
std::lock_guard lock(sConfigsMutex);
|
|
*out = WUPSConfigHandle(config.get());
|
|
sConfigs.push_back(std::move(config));
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
|
|
void CleanAllHandles() {
|
|
std::lock_guard lock(sConfigsMutex);
|
|
std::lock_guard lock1(sConfigCategoryMutex);
|
|
std::lock_guard lock2(sConfigItemsMutex);
|
|
sConfigs.clear();
|
|
sConfigCategories.clear();
|
|
sConfigItems.clear();
|
|
}
|
|
} // namespace Intern
|
|
|
|
/**
|
|
* @brief Initialize the WUPSConfigAPI with extended functionality.
|
|
*
|
|
* This function initializes the WUPSConfigAPI with extended functionality by associating a plugin
|
|
* identified by `pluginIdentifier` with the provided options and callback functions. The `options`
|
|
* parameter specifies the configuration options for the plugin, while `openedCallback` and
|
|
* `closedCallback` specify the callback functions to be invoked when the plugin menu is opened
|
|
* and closed, respectively.
|
|
*
|
|
* @param pluginIdentifier The identifier of the plugin to be associated with the WUPSConfigAPI.
|
|
* @param options The configuration options for the plugin.
|
|
* @param openedCallback The callback function to be invoked when the plugin menu is opened.
|
|
* @param closedCallback The callback function to be invoked when the plugin menu is closed.
|
|
*
|
|
* @return The status of the initialization process. Possible return values are:
|
|
* - WUPSCONFIG_API_RESULT_SUCCESS: The initialization was successful.
|
|
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The `openedCallback` or `closedCallback` parameter is nullptr.
|
|
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION: The specified `options.version` is not supported.
|
|
* - WUPSCONFIG_API_RESULT_NOT_FOUND: The plugin with the given identifier was not found.
|
|
*/
|
|
WUPSConfigAPIStatus InitEx(const uint32_t pluginIdentifier,
|
|
const WUPSConfigAPIOptions options,
|
|
const WUPSConfigAPI_MenuOpenedCallback openedCallback,
|
|
const WUPSConfigAPI_MenuClosedCallback closedCallback) {
|
|
if (openedCallback == nullptr || closedCallback == nullptr) {
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
for (auto &cur : gLoadedPlugins) {
|
|
if (cur.getHandle() == pluginIdentifier) {
|
|
if (options.version != 1) {
|
|
return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION;
|
|
}
|
|
const auto configDat = PluginConfigData::create(options, openedCallback, closedCallback);
|
|
if (!configDat) {
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to create config data for %08X", pluginIdentifier);
|
|
return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION;
|
|
}
|
|
cur.setConfigData(configDat.value());
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
}
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
|
|
namespace Category {
|
|
WUPSConfigAPIStatus Create(WUPSConfigAPICreateCategoryOptions options, WUPSConfigCategoryHandle *out) {
|
|
if (out == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"out\" or \"name\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
if (options.version != WUPS_API_CATEGORY_OPTION_VERSION_V1) {
|
|
DEBUG_FUNCTION_LINE_WARN("Invalid category option version: expected %08X but got %08X", WUPS_API_CATEGORY_OPTION_VERSION_V1, options.version);
|
|
return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION;
|
|
}
|
|
auto category = make_unique_nothrow<WUPSConfigCategory>(options.data.v1.name);
|
|
if (!category) {
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to allocate WUPSConfigCategory");
|
|
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
|
}
|
|
|
|
std::lock_guard lock(sConfigCategoryMutex);
|
|
*out = WUPSConfigCategoryHandle(category.get());
|
|
sConfigCategories.push_back(std::move(category));
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
|
|
WUPSConfigAPIStatus Destroy(WUPSConfigCategoryHandle handle) {
|
|
if (handle == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!remove_locked_first_if(sConfigCategoryMutex, sConfigCategories, [&handle](auto &cur) { return handle == cur.get(); })) {
|
|
{
|
|
// Ignore any attempts to destroy the root item.
|
|
std::lock_guard lock(sConfigsMutex);
|
|
if (std::ranges::any_of(sConfigs, [&handle](auto &cur) { return handle == cur.get(); })) {
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
}
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to destroy WUPSConfigCategory (for handle: \"%08X\")", handle.handle);
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
|
|
WUPSConfigAPIStatus AddCategory(const WUPSConfigCategoryHandle parentHandle, const WUPSConfigCategoryHandle categoryHandle) {
|
|
if (parentHandle == nullptr || categoryHandle == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"parentHandle\" or \"categoryHandle\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
std::lock_guard lockConfigs(sConfigsMutex);
|
|
std::lock_guard lockCats(sConfigCategoryMutex);
|
|
WUPSConfigCategory *parentCat = Intern::GetConfigByHandle(WUPSConfigHandle(parentHandle.handle));
|
|
if (!parentCat) {
|
|
parentCat = Intern::GetCategoryByHandle(parentHandle);
|
|
if (!parentCat) {
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to find parent for handle %08X", parentHandle.handle);
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
auto category = Intern::PopCategoryByHandle(categoryHandle);
|
|
if (!category) {
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to find category. parentHandle: %08X categoryHandle: %08X", parentHandle.handle, categoryHandle.handle);
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
|
|
if (!parentCat->addCategory(category)) {
|
|
sConfigCategories.push_back(std::move(category));
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to add category to parent. parentHandle: %08X categoryHandle: %08X", parentHandle.handle, categoryHandle.handle);
|
|
return WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; // TODO!!!
|
|
}
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
|
|
WUPSConfigAPIStatus AddItem(const WUPSConfigCategoryHandle parentHandle, const WUPSConfigItemHandle itemHandle) {
|
|
if (parentHandle == nullptr || itemHandle == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"item_Handle\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::lock_guard lockConfigs(sConfigsMutex);
|
|
std::lock_guard lockCats(sConfigCategoryMutex);
|
|
WUPSConfigCategory *parentCat = Intern::GetConfigByHandle(WUPSConfigHandle(parentHandle.handle));
|
|
if (!parentCat) {
|
|
parentCat = Intern::GetCategoryByHandle(parentHandle, true);
|
|
if (!parentCat) {
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to find parent for handle %08X", parentHandle.handle);
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
auto item = Intern::PopItemByHandle(itemHandle);
|
|
if (!item) {
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to get item for handle %08X", itemHandle.handle);
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
|
|
if (!parentCat->addItem(item)) {
|
|
std::lock_guard lockItems(sConfigItemsMutex);
|
|
sConfigItems.push_back(std::move(item));
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to add item %08X to category %08X", itemHandle.handle, parentHandle.handle);
|
|
return WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; // TODO
|
|
}
|
|
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
} // namespace Category
|
|
|
|
namespace Item {
|
|
WUPSConfigAPIStatus Create(WUPSConfigAPICreateItemOptions options, WUPSConfigItemHandle *out) {
|
|
if (out == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"out\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::unique_ptr<WUPSConfigItem> item = nullptr;
|
|
if (options.version == WUPS_API_ITEM_OPTION_VERSION_V1) {
|
|
item = make_unique_nothrow<WUPSConfigItemV1>(options.data.v1.configId, options.data.v1.displayName, options.data.v1.callbacks, options.data.v1.context);
|
|
} else if (options.version == WUPS_API_ITEM_OPTION_VERSION_V2) {
|
|
item = make_unique_nothrow<WUPSConfigItemV2>(options.data.v2.displayName, options.data.v2.callbacks, options.data.v2.context);
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_WARN("Invalid item option version: expected %08X or %08X but got %08X", WUPS_API_ITEM_OPTION_VERSION_V1, WUPS_API_ITEM_OPTION_VERSION_V2, options.version);
|
|
return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION;
|
|
}
|
|
|
|
if (!item) {
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to allocate WUPSConfigItem");
|
|
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
|
}
|
|
std::lock_guard lock(sConfigItemsMutex);
|
|
*out = WUPSConfigItemHandle(item.get());
|
|
sConfigItems.push_back(std::move(item));
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
|
|
WUPSConfigAPIStatus Destroy(WUPSConfigItemHandle handle) {
|
|
if (handle == nullptr) {
|
|
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" was NULL");
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!remove_locked_first_if(sConfigItemsMutex, sConfigItems, [&handle](auto &cur) { return cur.get() == handle; })) {
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to destroy WUPSConfigItem (handle: \"%08X\")", handle);
|
|
return WUPSCONFIG_API_RESULT_NOT_FOUND;
|
|
}
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
} // namespace Item
|
|
|
|
WUPSConfigAPIStatus WUPSConfigAPI_GetVersion(WUPSConfigAPIVersion *out) {
|
|
if (out == nullptr) {
|
|
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
*out = 1;
|
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
}
|
|
|
|
WUMS_EXPORT_FUNCTION(WUPSConfigAPI_GetVersion);
|
|
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::InitEx, WUPSConfigAPI_InitEx);
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::Category::Create, WUPSConfigAPI_Category_CreateEx);
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::Category::Destroy, WUPSConfigAPI_Category_Destroy);
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::Category::AddCategory, WUPSConfigAPI_Category_AddCategory);
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::Category::AddItem, WUPSConfigAPI_Category_AddItem);
|
|
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::Item::Create, WUPSConfigAPI_Item_CreateEx);
|
|
WUMS_EXPORT_FUNCTION_EX(WUPSConfigAPIBackend::Item::Destroy, WUPSConfigAPI_Item_Destroy);
|
|
} // namespace WUPSConfigAPIBackend
|