Implement new ConfigAPI and new Config Menu

This commit is contained in:
Maschell 2023-12-16 17:36:57 +01:00
parent b991d6e329
commit 921b5ce157
40 changed files with 2578 additions and 1319 deletions

View File

@ -1,7 +1,7 @@
FROM ghcr.io/wiiu-env/devkitppc:20230621 FROM ghcr.io/wiiu-env/devkitppc:20230621
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20230719 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:0.3.2-dev-20231203-2e5832b /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:0.7.2-dev-20231105-45d17c5 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:0.8.0-dev-20231216-104fdc3 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libmappedmemory:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libwupsbackend:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libwupsbackend:20230621 /artifacts $DEVKITPRO

View File

@ -29,7 +29,9 @@ SOURCES := source \
source/patcher \ source/patcher \
source/plugin \ source/plugin \
source/utils \ source/utils \
source/utils/storage source/utils/config \
source/utils/storage \
source/uitls/input
DATA := data DATA := data
INCLUDES := source INCLUDES := source
@ -56,7 +58,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif endif
LIBS := -lwums -lwut -lfunctionpatcher -lmappedmemory -lz -lnotifications LIBS := -lwums -lwups -lwut -lfunctionpatcher -lmappedmemory -lz -lnotifications
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level # list of directories containing libraries, this must be the top level

View File

@ -181,6 +181,7 @@ bool PluginManagement::DoFunctionPatches(const std::vector<std::unique_ptr<Plugi
} }
void PluginManagement::callInitHooks(const std::vector<std::unique_ptr<PluginContainer>> &plugins) { void PluginManagement::callInitHooks(const std::vector<std::unique_ptr<PluginContainer>> &plugins) {
CallHook(plugins, WUPS_LOADER_HOOK_INIT_CONFIG);
CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED); CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED);
CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE); CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE);
CallHook(plugins, WUPS_LOADER_HOOK_INIT_PLUGIN); CallHook(plugins, WUPS_LOADER_HOOK_INIT_PLUGIN);

View File

@ -18,73 +18,14 @@
#pragma once #pragma once
#include "WUPSConfigCategory.h" #include "WUPSConfigCategory.h"
#include "utils/logger.h"
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <wups/config.h>
class WUPSConfig { namespace WUPSConfigAPIBackend {
class WUPSConfig : public WUPSConfigCategory {
public: public:
explicit WUPSConfig(std::string_view name) { explicit WUPSConfig(std::string_view name) : WUPSConfigCategory(name) {
this->name = name;
} }
~WUPSConfig() {
for (const auto &element : categories) {
delete element;
}
}
/**
\return Returns the name of this WUPSConfig
**/
[[nodiscard]] const std::string &getName() const {
return this->name;
}
/**
\brief Creates a new WUPSCategory add its to this WUPSConfig.
The category will be added to the end of the list.
This class holds responsibility for deleting the created instance.
\param categoryName: The name of the category that will be created.
\return On success, the created and inserted category will be returned.
**/
std::optional<WUPSConfigCategory *> addCategory(std::string_view categoryName) {
auto curCat = new (std::nothrow) WUPSConfigCategory(categoryName);
if (curCat == nullptr) {
return {};
}
categories.push_back(curCat);
return curCat;
}
/**
\brief Adds a given WUPSConfigCategory to this WUPSConfig.
The category will be added to the end of the list.
This class holds responsibility for deleting the created instance.
\param category: The category that will be added to this config.
\return On success, the inserted category will be returned.
On error nullptr will be returned. In this case the caller still has the responsibility
for deleting the WUPSConfigCategory instance.
**/
WUPSConfigCategory *addCategory(WUPSConfigCategory *category) {
categories.push_back(category);
return category;
}
/**
\return Returns a vector with all categories.
**/
const std::vector<WUPSConfigCategory *> &getCategories() {
return this->categories;
}
private:
std::string name;
std::vector<WUPSConfigCategory *> categories = {};
}; };
} // namespace WUPSConfigAPIBackend

View File

@ -0,0 +1,324 @@
#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 "utils/logger.h"
#include "utils/utils.h"
#include <algorithm>
#include <memory>
#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);
auto itr = std::find_if(sConfigs.begin(), sConfigs.end(), [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()) {
auto res = GetCategoryByHandleRecursive(cat.get(), handle);
if (res) {
return res;
}
}
return nullptr;
}
WUPSConfigCategory *GetCategoryByHandle(WUPSConfigCategoryHandle handle, bool checkRecursive) {
std::lock_guard lock(sConfigCategoryMutex);
auto itr = std::find_if(sConfigCategories.begin(), sConfigCategories.end(), [handle](auto &cur) { return handle == cur.get(); });
if (itr == sConfigCategories.end()) {
if (checkRecursive) {
std::lock_guard config_lock(sConfigsMutex);
for (const auto &curConfig : sConfigs) {
auto *category = Intern::GetCategoryByHandleRecursive(curConfig.get(), handle);
if (category) {
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);
auto itr = std::find_if(sConfigItems.begin(), sConfigItems.end(), [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(uint32_t pluginIdentifier, WUPSConfigAPIOptions options, WUPSConfigAPI_MenuOpenedCallback openedCallback, 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;
}
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 to create root item.
std::lock_guard lock(sConfigsMutex);
if (std::any_of(sConfigs.begin(), sConfigs.end(), [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(WUPSConfigCategoryHandle parentHandle, 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(WUPSConfigCategoryHandle parentHandle, 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

View File

@ -0,0 +1,231 @@
#pragma once
#include "WUPSConfig.h"
#include "WUPSConfigCategory.h"
#include "WUPSConfigItem.h"
#include <wups/config.h>
namespace WUPSConfigAPIBackend {
namespace Intern {
/**
* @brief Retrieves a WUPSConfig pointer based on a given handle.
*
* This function searches for a WUPSConfig within a list of WUPSConfig objects based on a handle.
* If a matching handle is found, a pointer to the WUPSConfig object is returned. Otherwise, nullptr is returned.
*
* @param handle The handle used to identify the desired WUPSConfig object.
* @return A pointer to the WUPSConfig object with the matching handle, or nullptr if not found.
*
* @note This function locks the sConfigsMutex during the search operation to ensure thread safety.
*/
WUPSConfig *GetConfigByHandle(WUPSConfigHandle handle);
/**
* @brief Pop a WUPSConfig object from the sConfigs vector based on the provided handle.
*
* This function retrieves a WUPSConfig object from the sConfigs vector based on the given handle.
* It locks the sConfigsMutex to ensure thread safety during the operation.
*
* @param handle The handle of the WUPSConfig object to be retrieved.
* @return A std::unique_ptr to the WUPSConfig object if found, nullptr otherwise.
*/
std::unique_ptr<WUPSConfig> PopConfigByHandle(WUPSConfigHandle handle);
/**
* @brief Get a WUPSConfigCategory object by its handle
*
* This function searches for a WUPSConfigCategory object with the given handle.
* If the 'checkRecursive' flag is set to true, the function also searches recursively
* through all the WUPSConfig objects for the requested category.
*
* @param handle The handle of the desired WUPSConfigCategory
* @param checkRecursive Flag to indicate whether recursive search is required
* @return A pointer to the found WUPSConfigCategory object, or nullptr if not found
*/
WUPSConfigCategory *GetCategoryByHandle(WUPSConfigCategoryHandle handle, bool checkRecursive = false);
/**
* @brief Pop a WUPSConfigCategory from the list of categories by its handle.
*
* This function searches for a WUPSConfigCategory in the list of categories using the given handle.
* If a matching WUPSConfigCategory is found, it is removed from the list and returned as a unique_ptr.
* If no matching category is found, nullptr is returned.
*
* @param handle The handle of the WUPSConfigCategory to pop.
* @return std::unique_ptr<WUPSConfigCategory> The popped WUPSConfigCategory or nullptr if not found.
*/
std::unique_ptr<WUPSConfigCategory> PopCategoryByHandle(WUPSConfigCategoryHandle handle);
/**
* @brief Retrieves a WUPSConfigItem object by its handle.
*
* This function searches for a WUPSConfigItem object in the sConfigItems vector
* with a matching handle. It acquires a lock on the sConfigItemsMutex
* to ensure thread safety during the search operation. If a matching object is found,
* a pointer to the object is returned. Otherwise, nullptr is returned.
*
* @param handle The handle of the WUPSConfigItem to retrieve.
* @return A pointer to the WUPSConfigItem object with the specified handle, or nullptr if not found.
*/
WUPSConfigItem *GetItemByHandle(WUPSConfigItemHandle handle);
/**
* @brief Removes and returns an item from the configuration items list based on its handle.
*
* This function pops and returns an item from the `sConfigItems` vector based on its handle.
* The handle is used to match the item in the vector using a predicate. If a matching item is found,
* it is moved to the returned unique_ptr and removed from the vector.
*
* @param handle The handle of the item to be popped.
* @return A unique_ptr to the popped item. If no matching item is found, returns a nullptr.
*
* @note The function locks the `sConfigItemsMutex` mutex to ensure thread safety while performing the operation.
*
* @see WUPSConfigItem
* @see pop_locked_first_if
* @see sConfigItemsMutex
* @see sConfigItems
*/
std::unique_ptr<WUPSConfigItem> PopItemByHandle(WUPSConfigItemHandle handle);
/**
* @brief Creates a new configuration with the given name.
*
* This function creates a new configuration with the specified name. The configuration is allocated dynamically
* using `make_unique_nothrow` and added to the `sConfigs` vector.
*
* @param name The name of the configuration.
* @param out A pointer to a `WUPSConfigHandle` where the created configuration will be stored.
*
* @return A `WUPSConfigAPIStatus` indicating the status of the operation.
* - `WUPSCONFIG_API_RESULT_SUCCESS` if the configuration was created successfully.
* - `WUPSCONFIG_API_RESULT_INVALID_ARGUMENT` if the `out` or `name` parameter is null.
* - `WUPSCONFIG_API_RESULT_OUT_OF_MEMORY` if memory allocation fails.
*
* @note The caller is responsible for managing the lifetime of the created configuration and freeing the
* associated resources when they are no longer needed.
*/
WUPSConfigAPIStatus CreateConfig(const char *name, WUPSConfigHandle *out);
/**
* @brief Cleans all handles and clears the configuration data.
*
* This function acquires locks on three different mutexes: `sConfigsMutex`, `sConfigCategoryMutex`, and `sConfigItemsMutex`.
* It then clears the vectors `sConfigs`, `sConfigCategories`, and `sConfigItems`, effectively cleaning all handles and
* removing all configuration data.
*
* @note This function assumes that the mutexes and vectors are already defined and initialized.
*
* @see sConfigsMutex
* @see sConfigCategoryMutex
* @see sConfigItemsMutex
* @see sConfigs
* @see sConfigCategories
* @see sConfigItems
*/
void CleanAllHandles();
} // namespace Intern
namespace Category {
/**
* @brief Create a new WUPSConfigCategory.
*
* This function creates a new WUPSConfigCategory with the given options.
* The created category will be added to the global config categories list and a handle to the category will be
* returned.
*
* @param options The options to create the category.
* @param out[out] Pointer to store the handle to the newly created category.
* @return WUPSConfigAPIStatus The status of the operation.
* - WUPSCONFIG_API_RESULT_SUCCESS: Success.
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: Invalid parameter, `out` or `name` is NULL.
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION: Invalid category option version.
* - WUPSCONFIG_API_RESULT_OUT_OF_MEMORY: Failed to allocate WUPSConfigCategory.
*
* @note The caller is responsible for deleting the WUPSConfigCategory instance unless it has been transferred to
* another category
*/
WUPSConfigAPIStatus Create(WUPSConfigAPICreateCategoryOptions options, WUPSConfigCategoryHandle *out);
/**
* @brief Destroy a WUPSConfigCategory.
*
* This function destroys a WUPSConfigCategory identified by the given handle.
*
* @param handle The handle of the WUPSConfigCategory to destroy.
* @return WUPSConfigAPIStatus The status of the destroy operation.
* - WUPSCONFIG_API_RESULT_SUCCESS: If the WUPSConfigCategory was successfully destroyed.
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: If the handle was NULL.
* - WUPSCONFIG_API_RESULT_NOT_FOUND: If the WUPSConfigCategory was not found.
*/
WUPSConfigAPIStatus Destroy(WUPSConfigCategoryHandle handle);
/**
* @brief Adds a category to the WUPS configuration.
*
* This function adds a category to the WUPS configuration. The category is added as a child of
* the specified parent category. On success the ownership will be passed to the parent.
*
* @param parentHandle The handle of the parent category.
* @param categoryHandle The handle of the category to be added.
* @return The status of the operation. Possible values are:
* - WUPSCONFIG_API_RESULT_SUCCESS: The category was added successfully.
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: One or both of the input handles were NULL.
* - WUPSCONFIG_API_RESULT_NOT_FOUND: The parent category or the specified category was not found.
* - WUPSCONFIG_API_RESULT_UNKNOWN_ERROR: Failed to add the category to the parent.
*/
WUPSConfigAPIStatus AddCategory(WUPSConfigCategoryHandle parentHandle, WUPSConfigCategoryHandle categoryHandle);
/**
* @brief Adds an item to a WUPSConfigCategory.
*
* This function adds a WUPSConfigItem to a WUPSConfigCategory. The item will be added to the end of the list
* of items in the category. This function also holds the responsibility for deleting the created item instance.
*
* @param parentHandle The handle of the parent category.
* @param itemHandle The handle of the item to be added.
* @return Returns the status of the operation.
* - WUPSCONFIG_API_RESULT_SUCCESS: If the item was added successfully.
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: If either the parentHandle or itemHandle is nullptr.
* - WUPSCONFIG_API_RESULT_NOT_FOUND: If the parent category was not found.
* - WUPSCONFIG_API_RESULT_UNKNOWN_ERROR: If an unknown error occurred while adding the item.
*/
WUPSConfigAPIStatus AddItem(WUPSConfigCategoryHandle parentHandle, WUPSConfigItemHandle itemHandle);
} // namespace Category
namespace Item {
/**
* @brief Creates a new WUPSConfigItem and adds it to the list of config items.
*
* This function creates a new WUPSConfigItem object based on the provided options,
* and returns a handle to the newly created item.
*
* @param options The options for creating the item.
* @param out Pointer to store the handle to the newly created item.
* @return The status of the API call.
* - WUPSCONFIG_API_RESULT_SUCCESS if the item was created and added successfully.
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT if the 'out' parameter is nullptr.
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION if the options version is invalid.
* - WUPSCONFIG_API_RESULT_OUT_OF_MEMORY if memory allocation failed.
* @note The caller is responsible for deleting the WUPSConfigItem instance unless it has been transferred to
* a category
*/
WUPSConfigAPIStatus Create(WUPSConfigAPICreateItemOptions options, WUPSConfigItemHandle *out);
/**
* @brief Destroy a WUPSConfigItem.
*
* This function destroys a WUPSConfigItem identified by the given handle. It removes the memory allocated for the item.
*
* @param handle The handle of the WUPSConfigItem to destroy.
* @return The status of the operation, which can be one of the following values:
* - WUPSCONFIG_API_RESULT_SUCCESS: The WUPSConfigItem was destroyed successfully.
* - WUPSCONFIG_API_RESULT_NOT_FOUND: The WUPSConfigItem with the given handle was not found.
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The handle parameter was NULL.
*
* @see WUPSConfigItemHandle, WUPSConfigAPIStatus
*/
WUPSConfigAPIStatus Destroy(WUPSConfigItemHandle handle);
} // namespace Item
} // namespace WUPSConfigAPIBackend

View File

@ -0,0 +1,231 @@
#include "WUPSConfig.h"
#include "WUPSConfigAPI.h"
#include <wums.h>
#include <wups/config.h>
int32_t WUPSConfig_Create(void **out, const char *name) {
if (out == nullptr) {
return -1;
}
*out = nullptr;
WUPSConfigHandle outConfig;
if (WUPSConfigAPIBackend::Intern::CreateConfig(name, &outConfig) == WUPSCONFIG_API_RESULT_SUCCESS) {
*out = outConfig.handle;
return 0;
}
return -1;
}
int32_t WUPSConfig_Destroy(WUPSConfigHandle) {
return 0;
}
int32_t WUPSConfig_GetName(void *handle, char *out_buf, int32_t out_len) {
if (out_buf == nullptr) {
DEBUG_FUNCTION_LINE("Invalid param: \"out_buf\" was NULL");
return -1;
}
auto config = WUPSConfigAPIBackend::Intern::GetConfigByHandle(WUPSConfigHandle(handle));
if (!config) {
DEBUG_FUNCTION_LINE_WARN("Failed to find WUPSConfig by handle %08X", handle);
return -1;
}
snprintf(out_buf, out_len, "%s", config->getName().c_str());
return 0;
}
int32_t WUPSConfig_AddCategory(void *configHandle, void *categoryHandle) {
if (WUPSConfigAPIBackend::Category::AddCategory(WUPSConfigCategoryHandle(configHandle), WUPSConfigCategoryHandle(categoryHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
return -1;
}
return 0;
}
int32_t WUPSConfig_AddCategoryByName(void *handle, const char *categoryName, void **out) {
if (handle == nullptr || categoryName == nullptr || out == nullptr) {
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"categoryName\" or \"out\" was NULL");
return -1;
}
*out = nullptr;
WUPSConfigAPICreateCategoryOptions options = {
.version = WUPS_API_CATEGORY_OPTION_VERSION_V1,
.data = {
.v1 = {
.name = categoryName,
},
},
};
WUPSConfigCategoryHandle catHandle;
if (WUPSConfigAPIBackend::Category::Create(options, &catHandle) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to create category");
return -2;
}
if (WUPSConfigAPIBackend::Category::AddCategory(WUPSConfigCategoryHandle(handle), catHandle) < 0) {
WUPSConfigAPIBackend::Intern::PopCategoryByHandle(catHandle); // make sure to destroy created category.
return -3;
}
*out = catHandle.handle;
return 0;
}
WUMS_EXPORT_FUNCTION(WUPSConfig_Create);
WUMS_EXPORT_FUNCTION(WUPSConfig_Destroy);
WUMS_EXPORT_FUNCTION(WUPSConfig_GetName);
WUMS_EXPORT_FUNCTION(WUPSConfig_AddCategoryByName);
WUMS_EXPORT_FUNCTION(WUPSConfig_AddCategory);
int32_t WUPSConfigCategory_Create(void **out, const char *name) {
if (out == nullptr) {
return -1;
}
*out = nullptr;
WUPSConfigAPICreateCategoryOptions options = {
.version = WUPS_API_CATEGORY_OPTION_VERSION_V1,
.data = {
.v1 = {
.name = name,
},
},
};
WUPSConfigCategoryHandle outCat;
if (WUPSConfigAPIBackend::Category::Create(options, &outCat) != WUPSCONFIG_API_RESULT_SUCCESS) {
return -1;
}
*out = outCat.handle;
return 0;
}
int32_t WUPSConfigCategory_Destroy(void *handle) {
if (WUPSConfigAPIBackend::Category::Destroy(WUPSConfigCategoryHandle(handle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
return -1;
}
return 0;
}
int32_t WUPSConfigCategory_GetName(void *handle, char *out_buf, int32_t out_len) {
if (handle == nullptr || out_buf == nullptr || out_len == 0) {
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"out_buf\" was NULL");
return -1;
}
auto *category = WUPSConfigAPIBackend::Intern::GetCategoryByHandle(WUPSConfigCategoryHandle(handle), true);
if (!category) {
DEBUG_FUNCTION_LINE_WARN("Failed to find existing category for handle %08X", handle);
return -2;
}
snprintf(out_buf, out_len, "%s", category->getName().c_str());
return 0;
}
int32_t WUPSConfigCategory_AddItem(void *handle, void *item_Handle) {
if (WUPSConfigAPIBackend::Category::AddItem(WUPSConfigCategoryHandle(handle), WUPSConfigItemHandle(item_Handle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
return -1;
}
return 0;
}
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_Create);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_Destroy);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetName);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_AddItem);
int32_t WUPSConfigItem_Create(void **out, const char *configId, const char *displayName, WUPSConfigAPIItemCallbacksV1 callbacks, void *context) {
if (out == nullptr) {
return -1;
}
*out = nullptr;
WUPSConfigAPICreateItemOptions options = {
.version = WUPS_API_ITEM_OPTION_VERSION_V1,
.data = {
.v1 = {
.configId = configId,
.displayName = displayName,
.context = context,
.callbacks = callbacks,
},
},
};
WUPSConfigItemHandle outItem;
if (WUPSConfigAPIBackend::Item::Create(options, &outItem) != WUPSCONFIG_API_RESULT_SUCCESS) {
return -1;
}
*out = outItem.handle;
return 0;
}
int32_t WUPSConfigItem_Destroy(void *handle) {
if (WUPSConfigAPIBackend::Item::Destroy(WUPSConfigItemHandle(handle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
return -1;
}
return 0;
}
int32_t WUPSConfigItem_SetDisplayName(void *handle, const char *displayName) {
if (displayName == nullptr || handle == nullptr) {
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"displayName\" was NULL");
return -1;
}
auto *config = WUPSConfigAPIBackend::Intern::GetItemByHandle(WUPSConfigItemHandle(handle));
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to find item for handle %08X", handle);
return -2;
}
config->setDisplayName(displayName);
return 0;
}
int32_t WUPSConfigItem_GetDisplayName(void *handle, char *out_buf, int32_t out_len) {
if (handle == nullptr || out_buf == nullptr) {
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"out_buf\" was NULL");
return -1;
}
auto *config = WUPSConfigAPIBackend::Intern::GetItemByHandle(WUPSConfigItemHandle(handle));
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to find item for handle %08X", handle);
return -2;
}
snprintf(out_buf, out_len, "%s", config->getDisplayName().c_str());
return 0;
}
int32_t WUPSConfigItem_SetConfigID(void *handle, const char *configId) {
if (handle == nullptr || configId == nullptr) {
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"configId\" was NULL");
return -1;
}
auto *config = WUPSConfigAPIBackend::Intern::GetItemByHandle(WUPSConfigItemHandle(handle));
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to find item for handle %08X", handle);
return -2;
}
config->setConfigId(configId);
return 0;
}
int32_t WUPSConfigItem_GetConfigID(void *handle, char *out_buf, int32_t out_len) {
if (handle == nullptr || out_buf == nullptr) {
DEBUG_FUNCTION_LINE("Invalid param: \"handle\" or \"out_buf\" was NULL");
return -1;
}
auto *config = WUPSConfigAPIBackend::Intern::GetItemByHandle(WUPSConfigItemHandle(handle));
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to find item for handle %08X", handle);
return -2;
}
snprintf(out_buf, out_len, "%s", config->getConfigId().c_str());
return 0;
}
WUMS_EXPORT_FUNCTION(WUPSConfigItem_Create);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_Destroy);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_SetDisplayName);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_GetDisplayName);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_SetConfigID);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_GetConfigID);

View File

@ -18,27 +18,24 @@
#pragma once #pragma once
#include "WUPSConfigItem.h" #include "WUPSConfigItem.h"
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <wups/config.h> #include <wups/config.h>
namespace WUPSConfigAPIBackend {
class WUPSConfigCategory { class WUPSConfigCategory {
public: public:
explicit WUPSConfigCategory(std::string_view name) { explicit WUPSConfigCategory(std::string_view name) : mName(name) {
this->name = name;
} }
~WUPSConfigCategory() { virtual ~WUPSConfigCategory() = default;
for (const auto &element : items) {
delete element;
}
}
/** /**
\return Returns the name of this WUPSConfigCategory \return Returns the name of this WUPSConfigCategory
**/ **/
[[nodiscard]] const std::string &getName() const { [[nodiscard]] const std::string &getName() const {
return this->name; return mName;
} }
/** /**
@ -52,9 +49,9 @@ public:
On error false will be returned. In this case the caller still has the responsibility On error false will be returned. In this case the caller still has the responsibility
for deleting the WUPSConfigItem instance. for deleting the WUPSConfigItem instance.
**/ **/
[[nodiscard]] bool addItem(WUPSConfigItem *item) { [[nodiscard]] bool addItem(std::unique_ptr<WUPSConfigItem> &item) {
if (item != nullptr) { if (item != nullptr) {
items.push_back(item); mItems.push_back(std::move(item));
return true; return true;
} }
return false; return false;
@ -63,11 +60,36 @@ public:
/** /**
\return Returns a vector with all items. \return Returns a vector with all items.
**/ **/
[[nodiscard]] const std::vector<WUPSConfigItem *> &getItems() const { [[nodiscard]] const std::vector<std::unique_ptr<WUPSConfigItem>> &getItems() const {
return this->items; return mItems;
}
/**
\return Returns a vector with all categories.
**/
[[nodiscard]] const std::vector<std::unique_ptr<WUPSConfigCategory>> &getCategories() const {
return mCategories;
}
/**
\brief Adds a given WUPSConfigCategory to this WUPSConfigCategory.
The category will be added to the end of the list.
This class holds responsibility for deleting the created instance.
\param category: The category that will be added to this config.
\return On success, true will be returned.
On error false will be returned. In this case the caller still has the responsibility
for deleting the WUPSConfigCategory instance.
**/
[[nodiscard]] bool addCategory(std::unique_ptr<WUPSConfigCategory> &category) {
mCategories.push_back(std::move(category));
return true;
} }
private: private:
std::string name; std::string mName;
std::vector<WUPSConfigItem *> items{}; std::vector<std::unique_ptr<WUPSConfigItem>> mItems;
std::vector<std::unique_ptr<WUPSConfigCategory>> mCategories;
}; };
} // namespace WUPSConfigAPIBackend

View File

@ -1,81 +0,0 @@
#include "../utils/logger.h"
#include "WUPSConfigCategory.h"
#include <wums.h>
#include <wups/config.h>
int32_t WUPSConfigCategory_Create(WUPSConfigCategoryHandle *out, const char *name) {
if (name == nullptr || out == nullptr) {
return -1;
}
*out = (WUPSConfigCategoryHandle) new WUPSConfigCategory(name);
if (*out != 0) {
return 0;
}
return -2;
};
int32_t WUPSConfigCategory_Destroy(WUPSConfigCategoryHandle handle) {
if (handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigCategory *>(handle);
delete config;
return 0;
};
int32_t WUPSConfigCategory_GetName(WUPSConfigCategoryHandle handle, char *out_buf, int32_t out_len) {
if (out_buf == nullptr) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigCategory *>(handle);
snprintf(out_buf, out_len, "%s", config->getName().c_str());
return 0;
}
int32_t WUPSConfigCategory_AddItem(WUPSConfigCategoryHandle handle, WUPSConfigItemHandle item_Handle) {
if (handle == 0 || item_Handle == 0) {
return -1;
}
auto *category = reinterpret_cast<WUPSConfigCategory *>(handle);
auto *item = reinterpret_cast<WUPSConfigItem *>(item_Handle);
if (category->addItem(item)) {
return 0;
}
return -2;
}
/*
int32_t WUPSConfigCategory_GetItemCount(WUPSConfigCategoryHandle handle, int32_t *item_count) {
if (handle == 0 || item_count == nullptr) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigCategory *>(handle);
*item_count = config->getItems().size();
return 0;
}
int32_t WUPSConfigCategory_GetItems(WUPSConfigCategoryHandle handle, WUPSConfigItemHandle *items_out, int32_t items_out_size) {
if (handle == 0 || items_out == nullptr || items_out_size == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigCategory *>(handle);
auto items = config->getItems();
int32_t index = 0;
for (auto const &item: items) {
if (index >= items_out_size) {
break;
}
items_out[index] = (WUPSConfigItemHandle) item;
}
return 0;
}*/
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_Create);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_Destroy);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetName);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_AddItem);
/*
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetItemCount);
WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetItems);*/

View File

@ -1,96 +0,0 @@
#include "../utils/logger.h"
#include "WUPSConfig.h"
#include <wums.h>
#include <wups/config.h>
int32_t WUPSConfig_Create(WUPSConfigHandle *out, const char *name) {
if (name == nullptr || out == nullptr) {
return -1;
}
*out = (WUPSConfigHandle) new WUPSConfig(name);
if (*out != 0) {
return 0;
}
return -2;
};
int32_t WUPSConfig_Destroy(WUPSConfigHandle handle) {
if (handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfig *>(handle);
delete config;
return 0;
};
int32_t WUPSConfig_GetName(WUPSConfigHandle handle, char *out_buf, int32_t out_len) {
if (out_buf == nullptr) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfig *>(handle);
snprintf(out_buf, out_len, "%s", config->getName().c_str());
return 0;
}
int32_t WUPSConfig_AddCategoryByName(WUPSConfigHandle handle, const char *categoryName, WUPSConfigCategoryHandle *out) {
if (categoryName == nullptr) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfig *>(handle);
auto res = config->addCategory(std::string(categoryName));
if (res.has_value()) {
if (out != nullptr) {
*out = reinterpret_cast<WUPSConfigCategoryHandle>(res.value());
} else {
return -3;
}
return 0;
}
return -2;
}
int32_t WUPSConfig_AddCategory(WUPSConfigHandle handle, WUPSConfigCategoryHandle category) {
auto *config = reinterpret_cast<WUPSConfig *>(handle);
auto res = config->addCategory(reinterpret_cast<WUPSConfigCategory *>(category));
if (res == nullptr) {
return -1;
}
return 0;
}
/*
int32_t WUPSConfig_GetCategoryCount(WUPSConfigHandle handle, int32_t *category_count) {
if (category_count == nullptr) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfig *>(handle);
*category_count = config->getCategories().size();
return 0;
}
int32_t WUPSConfig_GetCategories(WUPSConfigHandle handle, WUPSConfigCategoryHandle *categories_out, int32_t categories_out_size) {
if (categories_out == nullptr || categories_out_size == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfig *>(handle);
auto cats = config->getCategories();
int32_t index = 0;
for (auto const &cat: cats) {
if (index >= categories_out_size) {
break;
}
categories_out[index] = (WUPSConfigCategoryHandle) cat;
}
return 0;
}*/
WUMS_EXPORT_FUNCTION(WUPSConfig_Create);
WUMS_EXPORT_FUNCTION(WUPSConfig_Destroy);
WUMS_EXPORT_FUNCTION(WUPSConfig_GetName);
WUMS_EXPORT_FUNCTION(WUPSConfig_AddCategoryByName);
WUMS_EXPORT_FUNCTION(WUPSConfig_AddCategory);
/*
WUMS_EXPORT_FUNCTION(WUPSConfig_GetCategoryCount);
WUMS_EXPORT_FUNCTION(WUPSConfig_GetCategories);*/

View File

@ -18,170 +18,54 @@
#pragma once #pragma once
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "utils/StringTools.h" #include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include <wups/config.h> #include <wups/config.h>
namespace WUPSConfigAPIBackend {
class WUPSConfigItem { class WUPSConfigItem {
public: public:
/** explicit WUPSConfigItem(std::string displayName) : mDisplayName(std::move(displayName)) {
Sets the display name of this WUPSConfigItem }
This is the value which will be shown in the configuration menu. virtual ~WUPSConfigItem() = default;
**/
virtual void setDisplayName(std::string_view _displayName) { [[nodiscard]] virtual std::string getCurrentValueDisplay() const = 0;
this->displayName = _displayName;
[[nodiscard]] virtual std::string getCurrentValueSelectedDisplay() const = 0;
virtual void onSelected(bool isSelected) const = 0;
virtual void onButtonPressed(WUPSConfigButtons) const {}
[[nodiscard]] virtual bool isMovementAllowed() const = 0;
virtual void restoreDefault() const = 0;
virtual void onCloseCallback() = 0;
[[nodiscard]] const std::string &getDisplayName() const {
return mDisplayName;
} }
/** virtual void setConfigId(const std::string &) {}
\return Returns the display name of this WUPSConfigItem
**/ virtual const std::string &getConfigId() {
virtual const std::string &getDisplayName() { return mStubConfigId;
return this->displayName;
} }
/** void setDisplayName(std::string displayName) {
Sets the config ID name of this WUPSConfigItem. mDisplayName = std::move(displayName);
This config ID is used to persist the configuration values and needs
to be unique in the context of this WUPSConfig.
Items in different categories are NOT allowed to have the config ID.
**/
virtual void setConfigID(std::string_view _configID) {
this->configID = _configID;
} }
/** virtual void onInput(WUPSConfigSimplePadData) const {}
\return Returns the configID of this WUPSConfigItem.
**/
[[nodiscard]] virtual const std::string &getConfigID() const {
return this->configID;
}
/** virtual void onInputEx(WUPSConfigComplexPadData) const {}
Returns a string that displays the current value.
This string is shown next to the display name when the cursor is NOT on this item
**/
[[nodiscard]] std::string getCurrentValueDisplay() const {
if (this->callbacks.getCurrentValueDisplay != nullptr) {
char buf[80];
int res = this->callbacks.getCurrentValueDisplay(context, buf, sizeof(buf));
if (res == 0) {
return buf;
} else {
return string_format("[ERROR %d]", res);
}
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return "NOT_IMPLEMENTED";
}
/** protected:
Returns a string that displays the current value when selected. std::string mDisplayName;
This string is shown next to the display name when the cursor IS on this item std::string mStubConfigId;
**/
[[nodiscard]] std::string getCurrentValueSelectedDisplay() const {
if (this->callbacks.getCurrentValueSelectedDisplay != nullptr) {
char buf[80];
int res = this->callbacks.getCurrentValueSelectedDisplay(context, buf, sizeof(buf));
if (res == 0) {
return buf;
} else {
return string_format("[ERROR %d]", res);
}
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return "NOT_IMPLEMENTED";
}
/**
Is called when the cursor enters or leaves the item.
When the cursor enters the item, "isSelected" will be true.
When the cursor leaves the item, "isSelected" will be false.
**/
void onSelected(bool isSelected) const {
if (this->callbacks.onSelected != nullptr) {
this->callbacks.onSelected(context, isSelected);
return;
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
}
/**
Is called when a button is pressed while the cursor on this item.
See the WUPSConfigButtons enum for possible values.
**/
void onButtonPressed(WUPSConfigButtons buttons) const {
if (this->callbacks.onButtonPressed != nullptr) {
this->callbacks.onButtonPressed(context, buttons);
return;
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
}
/**
When the cursor is on this item, the configuration menu asks this item
if it's allowed to leave it.
If it returns true, the item can be leaved.
It it returns false, leaves is not allowed.
**/
[[nodiscard]] bool isMovementAllowed() const {
if (this->callbacks.isMovementAllowed != nullptr) {
return this->callbacks.isMovementAllowed(context);
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return false;
}
/**
Restores the default value
**/
void restoreDefault() {
if (this->callbacks.restoreDefault != nullptr) {
this->callbacks.restoreDefault(context);
return;
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
}
/**
Call callback with with current value.
This function will be called whenever this item should call it's (optional) given
callback with the current value.
Returns true if a valid callback could be called
Returns false if no callback was called (e.g. callback was nullptr)
**/
bool callCallback() {
if (this->callbacks.callCallback != nullptr) {
return this->callbacks.callCallback(context);
}
return false;
}
bool isDirty() {
return defaultValue != getCurrentValueDisplay();
}
WUPSConfigItem(std::string_view _configID, std::string_view _displayName, WUPSConfigCallbacks_t callbacks, void *_context) {
this->configID = _configID;
this->displayName = _displayName;
this->context = _context;
this->callbacks = callbacks;
this->defaultValue = getCurrentValueDisplay();
}
virtual ~WUPSConfigItem() {
if (this->callbacks.onDelete != nullptr) {
this->callbacks.onDelete(context);
return;
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
};
private:
void *context;
std::string displayName;
std::string configID;
std::string defaultValue;
WUPSConfigCallbacks_t callbacks{};
}; };
} // namespace WUPSConfigAPIBackend

View File

@ -1,72 +0,0 @@
#include "WUPSConfigItem.h"
#include <wums.h>
#include <wups/config.h>
typedef uint32_t WUPSConfigItemHandle;
int32_t WUPSConfigItem_Create(WUPSConfigItemHandle *out, const char *configID, const char *displayName, WUPSConfigCallbacks_t callbacks, void *context) {
if (out == nullptr || configID == nullptr || displayName == nullptr) {
return -1;
}
*out = (WUPSConfigItemHandle) new WUPSConfigItem(configID, displayName, callbacks, context);
if (*out != 0) {
return 0;
}
return -2;
}
int32_t WUPSConfigItem_Destroy(WUPSConfigItemHandle handle) {
if (handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigItem *>(handle);
delete config;
return 0;
}
int32_t WUPSConfigItem_SetDisplayName(WUPSConfigItemHandle handle, const char *displayName) {
if (displayName == nullptr || handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigItem *>(handle);
config->setDisplayName(displayName);
return 0;
}
int32_t WUPSConfigItem_GetDisplayName(WUPSConfigItemHandle handle, char *out_buf, int32_t out_len) {
if (out_buf == nullptr || handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigItem *>(handle);
snprintf(out_buf, out_len, "%s", config->getDisplayName().c_str());
return 0;
}
int32_t WUPSConfigItem_SetConfigID(WUPSConfigItemHandle handle, const char *configID) {
if (configID == nullptr || handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigItem *>(handle);
config->setConfigID(configID);
return 0;
}
int32_t WUPSConfigItem_GetConfigID(WUPSConfigItemHandle handle, char *out_buf, int32_t out_len) {
if (out_buf == nullptr || handle == 0) {
return -1;
}
auto *config = reinterpret_cast<WUPSConfigItem *>(handle);
snprintf(out_buf, out_len, "%s", config->getConfigID().c_str());
return 0;
}
WUMS_EXPORT_FUNCTION(WUPSConfigItem_Create);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_Destroy);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_SetDisplayName);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_GetDisplayName);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_SetConfigID);
WUMS_EXPORT_FUNCTION(WUPSConfigItem_GetConfigID);

View File

@ -0,0 +1,97 @@
#include "WUPSConfigItemV1.h"
namespace WUPSConfigAPIBackend {
WUPSConfigItemV1::WUPSConfigItemV1(std::string_view configId, std::string_view _displayName, WUPSConfigAPIItemCallbacksV1 callbacks, void *context) : WUPSConfigItem(std::string(_displayName)) {
this->mConfigId = configId;
this->mContext = context;
this->mCallbacks = callbacks;
this->mDefaultValue = getCurrentValueDisplayImpl();
}
WUPSConfigItemV1::~WUPSConfigItemV1() {
if (this->mCallbacks.onDelete == nullptr) {
DEBUG_FUNCTION_LINE_WARN("onDelete callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onDelete(mContext);
}
std::string WUPSConfigItemV1::getCurrentValueDisplayImpl() const {
if (this->mCallbacks.getCurrentValueDisplay == nullptr) {
DEBUG_FUNCTION_LINE_ERR("getCurrentValueDisplay callback not implemented. [%s]", mDisplayName.c_str());
return "NOT_IMPLEMENTED";
}
char buf[80];
int res = this->mCallbacks.getCurrentValueDisplay(mContext, buf, sizeof(buf));
if (res != 0) {
return string_format("[ERROR %d]", res);
}
return buf;
}
std::string WUPSConfigItemV1::getCurrentValueDisplay() const {
return getCurrentValueDisplayImpl();
}
std::string WUPSConfigItemV1::getCurrentValueSelectedDisplay() const {
if (this->mCallbacks.getCurrentValueSelectedDisplay == nullptr) {
DEBUG_FUNCTION_LINE_ERR("getCurrentValueSelectedDisplay callback not implemented. [%s]", mDisplayName.c_str());
return "NOT_IMPLEMENTED";
}
char buf[80];
int res = this->mCallbacks.getCurrentValueSelectedDisplay(mContext, buf, sizeof(buf));
if (res != 0) {
return string_format("[ERROR %d]", res);
}
return buf;
}
void WUPSConfigItemV1::onSelected(bool isSelected) const {
if (this->mCallbacks.onSelected == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("onSelected callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onSelected(mContext, isSelected);
}
void WUPSConfigItemV1::onButtonPressed(WUPSConfigButtons buttons) const {
if (this->mCallbacks.onButtonPressed == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("onButtonPressed callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onButtonPressed(mContext, buttons);
}
bool WUPSConfigItemV1::isMovementAllowed() const {
if (this->mCallbacks.isMovementAllowed == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("isMovementAllowed callback not implemented. [%s]", mDisplayName.c_str());
return true;
}
return this->mCallbacks.isMovementAllowed(mContext);
}
void WUPSConfigItemV1::restoreDefault() const {
if (this->mCallbacks.restoreDefault == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("restoreDefault callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.restoreDefault(mContext);
}
void WUPSConfigItemV1::onCloseCallback() {
if (this->mCallbacks.callCallback == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("callCallback callback not implemented. [%s]", mDisplayName.c_str());
}
if (mDefaultValue != getCurrentValueDisplay()) {
this->mCallbacks.callCallback(mContext);
}
}
void WUPSConfigItemV1::setConfigId(const std::string &configId) {
mConfigId = configId;
}
const std::string &WUPSConfigItemV1::getConfigId() {
return mConfigId;
}
} // namespace WUPSConfigAPIBackend

View File

@ -0,0 +1,39 @@
#pragma once
#include "WUPSConfigItem.h"
#include <string>
#include <wups/config.h>
namespace WUPSConfigAPIBackend {
class WUPSConfigItemV1 : public WUPSConfigItem {
public:
WUPSConfigItemV1(std::string_view configId, std::string_view _displayName, WUPSConfigAPIItemCallbacksV1 callbacks, void *context);
~WUPSConfigItemV1() override;
[[nodiscard]] std::string getCurrentValueDisplayImpl() const;
[[nodiscard]] std::string getCurrentValueDisplay() const override;
[[nodiscard]] std::string getCurrentValueSelectedDisplay() const override;
void onSelected(bool isSelected) const override;
void onButtonPressed(WUPSConfigButtons buttons) const override;
[[nodiscard]] bool isMovementAllowed() const override;
void restoreDefault() const override;
void onCloseCallback() override;
void setConfigId(const std::string &configId) override;
const std::string &getConfigId() override;
private:
void *mContext;
std::string mConfigId;
std::string mDefaultValue;
WUPSConfigAPIItemCallbacksV1 mCallbacks{};
};
} // namespace WUPSConfigAPIBackend

View File

@ -0,0 +1,93 @@
#include "WUPSConfigItemV2.h"
#include <string>
#include <wups/config.h>
namespace WUPSConfigAPIBackend {
WUPSConfigItemV2::WUPSConfigItemV2(std::string_view displayName, WUPSConfigAPIItemCallbacksV2 callbacks, void *context) : WUPSConfigItem(std::string(displayName)) {
this->mDisplayName = displayName;
this->mContext = context;
this->mCallbacks = callbacks;
}
WUPSConfigItemV2::~WUPSConfigItemV2() {
if (this->mCallbacks.onDelete == nullptr) {
DEBUG_FUNCTION_LINE_WARN("onDelete callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onDelete(mContext);
}
void WUPSConfigItemV2::onSelected(bool isSelected) const {
if (this->mCallbacks.onSelected == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("onSelected callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onSelected(mContext, isSelected);
}
std::string WUPSConfigItemV2::getCurrentValueDisplay() const {
if (this->mCallbacks.getCurrentValueDisplay == nullptr) {
DEBUG_FUNCTION_LINE_ERR("getCurrentValueDisplay callback not implemented. [%s]", mDisplayName.c_str());
return "NOT_IMPLEMENTED";
}
char buf[80];
int res = this->mCallbacks.getCurrentValueDisplay(mContext, buf, sizeof(buf));
if (res != 0) {
return string_format("[ERROR %d]", res);
}
return buf;
}
std::string WUPSConfigItemV2::getCurrentValueSelectedDisplay() const {
if (this->mCallbacks.getCurrentValueSelectedDisplay == nullptr) {
DEBUG_FUNCTION_LINE_ERR("getCurrentValueSelectedDisplay callback not implemented. [%s]", mDisplayName.c_str());
return "NOT_IMPLEMENTED";
}
char buf[80];
int res = this->mCallbacks.getCurrentValueSelectedDisplay(mContext, buf, sizeof(buf));
if (res != 0) {
return string_format("[ERROR %d]", res);
}
return buf;
}
void WUPSConfigItemV2::onInput(WUPSConfigSimplePadData input) const {
if (this->mCallbacks.onInput == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("onInput callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onInput(mContext, input);
}
void WUPSConfigItemV2::onInputEx(WUPSConfigComplexPadData input) const {
if (this->mCallbacks.onInputEx == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("onInputEx callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onInputEx(mContext, input);
}
bool WUPSConfigItemV2::isMovementAllowed() const {
if (this->mCallbacks.isMovementAllowed == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("isMovementAllowed callback not implemented. [%s]", mDisplayName.c_str());
return true;
}
return this->mCallbacks.isMovementAllowed(mContext);
}
void WUPSConfigItemV2::restoreDefault() const {
if (this->mCallbacks.restoreDefault == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("restoreDefault callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.restoreDefault(mContext);
}
void WUPSConfigItemV2::onCloseCallback() {
if (this->mCallbacks.onCloseCallback == nullptr) {
DEBUG_FUNCTION_LINE_VERBOSE("onCloseCallback callback not implemented. [%s]", mDisplayName.c_str());
return;
}
this->mCallbacks.onCloseCallback(mContext);
}
} // namespace WUPSConfigAPIBackend

View File

@ -0,0 +1,35 @@
#pragma once
#include "WUPSConfigItem.h"
#include <string>
#include <wups/config.h>
namespace WUPSConfigAPIBackend {
class WUPSConfigItemV2 : public WUPSConfigItem {
public:
WUPSConfigItemV2(std::string_view displayName, WUPSConfigAPIItemCallbacksV2 callbacks, void *context);
~WUPSConfigItemV2() override;
[[nodiscard]] std::string getCurrentValueDisplay() const override;
[[nodiscard]] std::string getCurrentValueSelectedDisplay() const override;
void onSelected(bool isSelected) const override;
void onInput(WUPSConfigSimplePadData input) const override;
void onInputEx(WUPSConfigComplexPadData input) const override;
[[nodiscard]] bool isMovementAllowed() const override;
void restoreDefault() const override;
void onCloseCallback() override;
private:
void *mContext;
std::string mDefaultValue;
WUPSConfigAPIItemCallbacksV2 mCallbacks{};
};
} // namespace WUPSConfigAPIBackend

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "plugin/PluginContainer.h" #include "plugin/PluginContainer.h"
#include "utils/ConfigUtils.h" #include "utils/config/ConfigUtils.h"
#include "version.h" #include "version.h"
#include <coreinit/dynload.h> #include <coreinit/dynload.h>
#include <forward_list> #include <forward_list>

View File

@ -20,8 +20,8 @@ static const char **hook_names = (const char *[]){
"WUPS_LOADER_HOOK_INIT_WRAPPER", "WUPS_LOADER_HOOK_INIT_WRAPPER",
"WUPS_LOADER_HOOK_FINI_WRAPPER", "WUPS_LOADER_HOOK_FINI_WRAPPER",
"WUPS_LOADER_HOOK_GET_CONFIG", "WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED",
"WUPS_LOADER_HOOK_CONFIG_CLOSED", "WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED",
"WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED", "WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED",
@ -32,7 +32,8 @@ static const char **hook_names = (const char *[]){
"WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND", "WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND",
"WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT", "WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT",
"WUPS_LOADER_HOOK_APPLICATION_ENDS", "WUPS_LOADER_HOOK_APPLICATION_ENDS",
"WUPS_LOADER_HOOK_INIT_STORAGE"}; "WUPS_LOADER_HOOK_INIT_STORAGE",
"WUPS_LOADER_HOOK_INIT_CONFIG"};
void CallHook(const std::vector<std::unique_ptr<PluginContainer>> &plugins, wups_loader_hook_type_t hook_type) { void CallHook(const std::vector<std::unique_ptr<PluginContainer>> &plugins, wups_loader_hook_type_t hook_type) {
DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hook_names[hook_type], hook_type); DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hook_names[hook_type], hook_type);
@ -60,8 +61,8 @@ void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type)
case WUPS_LOADER_HOOK_FINI_WUT_SOCKETS: case WUPS_LOADER_HOOK_FINI_WUT_SOCKETS:
case WUPS_LOADER_HOOK_INIT_WRAPPER: case WUPS_LOADER_HOOK_INIT_WRAPPER:
case WUPS_LOADER_HOOK_FINI_WRAPPER: case WUPS_LOADER_HOOK_FINI_WRAPPER:
case WUPS_LOADER_HOOK_GET_CONFIG: case WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED:
case WUPS_LOADER_HOOK_CONFIG_CLOSED: case WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED:
case WUPS_LOADER_HOOK_INIT_PLUGIN: case WUPS_LOADER_HOOK_INIT_PLUGIN:
case WUPS_LOADER_HOOK_DEINIT_PLUGIN: case WUPS_LOADER_HOOK_DEINIT_PLUGIN:
case WUPS_LOADER_HOOK_APPLICATION_STARTS: case WUPS_LOADER_HOOK_APPLICATION_STARTS:
@ -75,7 +76,7 @@ void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type)
break; break;
case WUPS_LOADER_HOOK_INIT_STORAGE: case WUPS_LOADER_HOOK_INIT_STORAGE:
case WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED: { case WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED: {
if (plugin.getMetaInformation().getWUPSVersion() < WUPSVersion(0, 7, 2)) { if (plugin.getMetaInformation().getWUPSVersion() <= WUPSVersion(0, 7, 1)) {
WUPSStorageDeprecated::wups_loader_init_storage_args_t_ args{}; WUPSStorageDeprecated::wups_loader_init_storage_args_t_ args{};
args.open_storage_ptr = &WUPSStorageDeprecated::StorageUtils::OpenStorage; args.open_storage_ptr = &WUPSStorageDeprecated::StorageUtils::OpenStorage;
args.close_storage_ptr = &WUPSStorageDeprecated::StorageUtils::CloseStorage; args.close_storage_ptr = &WUPSStorageDeprecated::StorageUtils::CloseStorage;
@ -100,9 +101,19 @@ void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type)
// clang-format off // clang-format off
auto res = ((WUPSStorageError(*)(wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args); auto res = ((WUPSStorageError(*)(wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args);
// clang-format on // clang-format on
if (res == WUPS_STORAGE_ERROR_INVALID_VERSION) { if (res != WUPS_STORAGE_ERROR_SUCCESS) {
// TODO: More error handling? Notification? // TODO: More error handling? Notification?
DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_STORAGE failed for plugin %s: WUPS_STORAGE_ERROR_INVALID_VERSION", plugin.getMetaInformation().getName().c_str()); DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_STORAGE failed for plugin %s: %s", plugin.getMetaInformation().getName().c_str(), WUPS_GetStorageStatusStr(res));
}
break;
}
case WUPS_LOADER_HOOK_INIT_CONFIG: {
wups_loader_init_config_args_t args{.arg_version = 1, .plugin_identifier = plugin.getHandle()};
auto res = ((WUPSConfigAPIStatus(*)(wups_loader_init_config_args_t))((uint32_t *) func_ptr))(args);
// clang-format on
if (res != WUPSCONFIG_API_RESULT_SUCCESS) {
// TODO: More error handling? Notification?
DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_CONFIG failed for plugin %s: %s", plugin.getMetaInformation().getName().c_str(), WUPSConfigAPI_GetStatusStr(res));
} }
break; break;
} }

View File

@ -0,0 +1,56 @@
#pragma once
#include "config/WUPSConfigAPI.h"
#include <optional>
#include <string>
#include <wups/config_api.h>
class PluginConfigData {
public:
PluginConfigData(std::string_view name,
WUPSConfigAPI_MenuOpenedCallback openedCallback,
WUPSConfigAPI_MenuClosedCallback closedCallback) : mName(name),
mOpenedCallback(openedCallback),
mClosedCallback(closedCallback) {
}
std::optional<WUPSConfigHandle> createConfig() {
WUPSConfigHandle handle;
if (WUPSConfigAPIBackend::Intern::CreateConfig(mName.c_str(), &handle) == WUPSCONFIG_API_RESULT_SUCCESS) {
return handle;
}
return std::nullopt;
}
WUPSConfigAPIStatus CallMenuOpenendCallback(WUPSConfigHandle config) {
if (mOpenedCallback == nullptr) {
return WUPSCONFIG_API_RESULT_MISSING_CALLBACK;
}
if (mOpenedCallback(WUPSConfigCategoryHandle(config.handle)) != WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS) {
return WUPSCONFIG_API_RESULT_UNKNOWN_ERROR;
}
return WUPSCONFIG_API_RESULT_SUCCESS;
}
WUPSConfigAPIStatus CallMenuClosedCallback() {
if (mClosedCallback == nullptr) {
return WUPSCONFIG_API_RESULT_MISSING_CALLBACK;
}
mClosedCallback();
return WUPSCONFIG_API_RESULT_SUCCESS;
}
static std::optional<PluginConfigData> create(WUPSConfigAPIOptions options, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback) {
if (options.version != 1) {
return std::nullopt;
}
PluginConfigData result(options.data.v1.name, openedCallback, closedCallback);
return result;
}
private:
std::string mName;
WUPSConfigAPI_MenuOpenedCallback mOpenedCallback;
WUPSConfigAPI_MenuClosedCallback mClosedCallback;
};

View File

@ -17,11 +17,13 @@
#pragma once #pragma once
#include "PluginConfigData.h"
#include "PluginData.h" #include "PluginData.h"
#include "PluginInformation.h" #include "PluginInformation.h"
#include "PluginMetaInformation.h" #include "PluginMetaInformation.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <wups/config_api.h>
class PluginContainer { class PluginContainer {
public: public:
@ -43,12 +45,21 @@ public:
return mPluginData; return mPluginData;
} }
uint32_t getHandle() { uint32_t getHandle() const {
return (uint32_t) this; return (uint32_t) this;
} }
[[nodiscard]] const std::optional<PluginConfigData> &getConfigData() const {
return mPluginConfigData;
}
void setConfigData(const PluginConfigData &pluginConfigData) {
mPluginConfigData = pluginConfigData;
}
private: private:
const std::unique_ptr<PluginMetaInformation> mMetaInformation; const std::unique_ptr<PluginMetaInformation> mMetaInformation;
const std::unique_ptr<PluginInformation> mPluginInformation; const std::unique_ptr<PluginInformation> mPluginInformation;
const std::shared_ptr<PluginData> mPluginData; const std::shared_ptr<PluginData> mPluginData;
std::optional<PluginConfigData> mPluginConfigData;
}; };

View File

@ -105,8 +105,8 @@ std::unique_ptr<PluginMetaInformation> PluginMetaInformationFactory::loadPlugin(
} else if (key == "wups") { } else if (key == "wups") {
if (value == "0.7.1") { if (value == "0.7.1") {
pluginInfo->setWUPSVersion(0, 7, 1); pluginInfo->setWUPSVersion(0, 7, 1);
} else if (value == "0.7.2") { } else if (value == "0.8.0") {
pluginInfo->setWUPSVersion(0, 7, 2); pluginInfo->setWUPSVersion(0, 8, 0);
} else { } else {
error = PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION; error = PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION;
DEBUG_FUNCTION_LINE_ERR("Warning: Ignoring plugin - Unsupported WUPS version: %s.", value.c_str()); DEBUG_FUNCTION_LINE_ERR("Warning: Ignoring plugin - Unsupported WUPS version: %s.", value.c_str());

View File

@ -1,762 +0,0 @@
#include "ConfigUtils.h"
#include "../config/WUPSConfig.h"
#include "../globals.h"
#include "DrawUtils.h"
#include "hooks.h"
#include "logger.h"
#include <coreinit/screen.h>
#include <gx2/display.h>
#include <memory/mappedmemory.h>
#include <padscore/kpad.h>
#include <string>
#include <vector>
#include <vpad/input.h>
#define COLOR_BACKGROUND Color(238, 238, 238, 255)
#define COLOR_TEXT Color(51, 51, 51, 255)
#define COLOR_TEXT2 Color(72, 72, 72, 255)
#define COLOR_DISABLED Color(255, 0, 0, 255)
#define COLOR_BORDER Color(204, 204, 204, 255)
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4FF)
#define COLOR_WHITE Color(0xFFFFFFFF)
#define COLOR_BLACK Color(0, 0, 0, 255)
struct ConfigDisplayItem {
WUPSConfig *config{};
std::string name;
std::string author;
std::string version;
};
#define MAX_BUTTONS_ON_SCREEN 8
static uint32_t remapWiiMoteButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_BUTTON_LEFT)
conv_buttons |= VPAD_BUTTON_LEFT;
if (buttons & WPAD_BUTTON_RIGHT)
conv_buttons |= VPAD_BUTTON_RIGHT;
if (buttons & WPAD_BUTTON_DOWN)
conv_buttons |= VPAD_BUTTON_DOWN;
if (buttons & WPAD_BUTTON_UP)
conv_buttons |= VPAD_BUTTON_UP;
if (buttons & WPAD_BUTTON_PLUS)
conv_buttons |= VPAD_BUTTON_PLUS;
if (buttons & WPAD_BUTTON_B)
conv_buttons |= VPAD_BUTTON_B;
if (buttons & WPAD_BUTTON_A)
conv_buttons |= VPAD_BUTTON_A;
if (buttons & WPAD_BUTTON_MINUS)
conv_buttons |= VPAD_BUTTON_MINUS;
if (buttons & WPAD_BUTTON_HOME)
conv_buttons |= VPAD_BUTTON_HOME;
return conv_buttons;
}
static uint32_t remapClassicButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
conv_buttons |= VPAD_BUTTON_LEFT;
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
conv_buttons |= VPAD_BUTTON_RIGHT;
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
conv_buttons |= VPAD_BUTTON_DOWN;
if (buttons & WPAD_CLASSIC_BUTTON_UP)
conv_buttons |= VPAD_BUTTON_UP;
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
conv_buttons |= VPAD_BUTTON_PLUS;
if (buttons & WPAD_CLASSIC_BUTTON_X)
conv_buttons |= VPAD_BUTTON_X;
if (buttons & WPAD_CLASSIC_BUTTON_Y)
conv_buttons |= VPAD_BUTTON_Y;
if (buttons & WPAD_CLASSIC_BUTTON_B)
conv_buttons |= VPAD_BUTTON_B;
if (buttons & WPAD_CLASSIC_BUTTON_A)
conv_buttons |= VPAD_BUTTON_A;
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
conv_buttons |= VPAD_BUTTON_MINUS;
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
conv_buttons |= VPAD_BUTTON_HOME;
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
conv_buttons |= VPAD_BUTTON_ZR;
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
conv_buttons |= VPAD_BUTTON_ZL;
if (buttons & WPAD_CLASSIC_BUTTON_R)
conv_buttons |= VPAD_BUTTON_R;
if (buttons & WPAD_CLASSIC_BUTTON_L)
conv_buttons |= VPAD_BUTTON_L;
return conv_buttons;
}
void ConfigUtils::displayMenu() {
renderBasicScreen("Loading configs...");
std::vector<ConfigDisplayItem> configs;
for (auto &plugin : gLoadedPlugins) {
ConfigDisplayItem cfg;
cfg.name = plugin->getMetaInformation().getName();
cfg.author = plugin->getMetaInformation().getAuthor();
cfg.version = plugin->getMetaInformation().getVersion();
for (const auto &hook : plugin->getPluginInformation().getHookDataList()) {
if (hook->getType() == WUPS_LOADER_HOOK_GET_CONFIG /*WUPS_LOADER_HOOK_GET_CONFIG*/) {
if (hook->getFunctionPointer() == nullptr) {
break;
}
auto *cur_config = reinterpret_cast<WUPSConfig *>(((WUPSConfigHandle(*)())((uint32_t *) hook->getFunctionPointer()))());
if (cur_config == nullptr) {
break;
}
cfg.config = cur_config;
configs.push_back(cfg);
break;
}
}
}
ConfigDisplayItem *currentConfig = nullptr;
WUPSConfigCategory *currentCategory = nullptr;
uint32_t selectedConfig = 0;
uint32_t selectedCat = 0;
uint32_t selectedItem = 0;
uint32_t start = 0;
uint32_t end = MAX_BUTTONS_ON_SCREEN;
if (configs.size() < MAX_BUTTONS_ON_SCREEN) {
end = configs.size();
}
bool redraw = true;
uint32_t buttonsTriggered;
uint32_t buttonsReleased;
VPADStatus vpad_data{};
VPADReadError vpad_error;
KPADStatus kpad_data{};
KPADError kpad_error;
int32_t prevSelectedItem = -1;
bool isItemMovementAllowed = true;
while (true) {
buttonsTriggered = 0;
buttonsReleased = 0;
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &vpad_error);
if (vpad_error == VPAD_READ_SUCCESS) {
buttonsTriggered = vpad_data.trigger;
buttonsReleased = vpad_data.release;
}
for (int i = 0; i < 4; i++) {
if (KPADReadEx((KPADChan) i, &kpad_data, 1, &kpad_error) > 0) {
if (kpad_error == KPAD_ERROR_OK && kpad_data.extensionType != 0xFF) {
if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) {
buttonsTriggered |= remapWiiMoteButtons(kpad_data.trigger);
buttonsReleased |= remapWiiMoteButtons(kpad_data.release);
} else {
buttonsTriggered |= remapClassicButtons(kpad_data.classic.trigger);
buttonsReleased |= remapClassicButtons(kpad_data.classic.release);
}
}
}
}
if (buttonsReleased & VPAD_BUTTON_HOME) {
break;
}
if (configs.empty()) {
renderBasicScreen("No configurable plugins loaded");
continue;
}
if (!currentConfig || !currentConfig->config) {
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
if (selectedConfig < configs.size() - 1) {
selectedCat = 0;
selectedConfig++;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_UP) {
if (selectedConfig > 0) {
selectedCat = 0;
selectedConfig--;
redraw = true;
}
}
if (buttonsTriggered & VPAD_BUTTON_A) {
currentConfig = &configs[selectedConfig];
if (currentConfig == nullptr) {
break;
}
selectedItem = 0;
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
auto cats = currentConfig->config->getCategories();
if (cats.size() < MAX_BUTTONS_ON_SCREEN) {
end = cats.size();
}
redraw = true;
continue;
}
if (selectedConfig >= end) {
end = selectedConfig + 1;
start = end - MAX_BUTTONS_ON_SCREEN;
} else if (selectedConfig < start) {
start = selectedConfig;
end = start + MAX_BUTTONS_ON_SCREEN;
}
if (redraw) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
// draw buttons
uint32_t index = 8 + 24 + 8 + 4;
for (uint32_t i = start; i < end; i++) {
DrawUtils::setFontColor(COLOR_TEXT);
if (i == selectedConfig) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, index + 8 + 24, configs[i].name.c_str());
uint32_t sz = DrawUtils::getTextWidth(configs[i].name.c_str());
DrawUtils::setFontSize(12);
DrawUtils::print(16 * 2 + sz + 4, index + 8 + 24, configs[i].author.c_str());
DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, configs[i].version.c_str(), true);
index += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < configs.size()) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
redraw = false;
}
continue;
}
if (!currentCategory) {
auto cats = currentConfig->config->getCategories();
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
if (selectedCat < cats.size() - 1) {
selectedItem = 0;
prevSelectedItem = 0;
selectedCat++;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_UP) {
if (selectedCat > 0) {
selectedItem = 0;
prevSelectedItem = 0;
selectedCat--;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_A) {
if (!cats.empty() && selectedCat > cats.size() - 1) {
selectedCat = 0;
}
currentCategory = cats[selectedCat];
if (currentCategory == nullptr) {
DEBUG_FUNCTION_LINE_ERR("currentCategory was NULL");
break;
}
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
prevSelectedItem = -1;
auto items = currentCategory->getItems();
if (items.size() < MAX_BUTTONS_ON_SCREEN) {
end = items.size();
}
redraw = true;
continue;
} else if (buttonsTriggered & VPAD_BUTTON_B) {
currentConfig = nullptr;
currentCategory = nullptr;
start = 0;
selectedItem = 0;
prevSelectedItem = 0;
end = MAX_BUTTONS_ON_SCREEN;
if (configs.size() < MAX_BUTTONS_ON_SCREEN) {
end = configs.size();
}
redraw = true;
continue;
}
if (selectedCat >= end) {
end = selectedCat + 1;
start = end - MAX_BUTTONS_ON_SCREEN;
} else if (selectedCat < start) {
start = selectedCat;
end = start + MAX_BUTTONS_ON_SCREEN;
}
if (redraw) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
// draw buttons
uint32_t index = 8 + 24 + 8 + 4;
for (uint32_t i = start; i < end; i++) {
DrawUtils::setFontColor(COLOR_TEXT);
if (i == selectedCat) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, index + 8 + 24, cats[i]->getName().c_str());
index += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(currentConfig->config->getName(), 45).c_str());
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < cats.size()) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
redraw = false;
}
continue;
}
const std::vector<WUPSConfigItem *> config_items = currentCategory->getItems();
if (config_items.empty()) {
if (buttonsTriggered & VPAD_BUTTON_B) {
currentCategory = nullptr;
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
auto catSize = currentConfig->config->getCategories().size();
if (catSize < MAX_BUTTONS_ON_SCREEN) {
end = catSize;
}
redraw = true;
continue;
}
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth("This category has no items");
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), "This category has no items");
auto headline = string_format("%s - %s", currentConfig->config->getName().c_str(), currentCategory->getName().c_str());
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(headline, 45).c_str());
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
// draw scroll indicator
DrawUtils::setFontSize(24);
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
continue;
}
if (isItemMovementAllowed) {
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
if (selectedItem < config_items.size() - 1) {
selectedItem++;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_UP) {
if (selectedItem > 0) {
selectedItem--;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_B) {
currentCategory = nullptr;
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
auto catSize = currentConfig->config->getCategories().size();
if (catSize < MAX_BUTTONS_ON_SCREEN) {
end = catSize;
}
redraw = true;
continue;
}
}
WUPSConfigButtons pressedButtons = WUPS_CONFIG_BUTTON_NONE;
if (buttonsTriggered & VPAD_BUTTON_A) {
pressedButtons |= WUPS_CONFIG_BUTTON_A;
}
if (buttonsTriggered & VPAD_BUTTON_LEFT) {
pressedButtons |= WUPS_CONFIG_BUTTON_LEFT;
}
if (buttonsTriggered & VPAD_BUTTON_RIGHT) {
pressedButtons |= WUPS_CONFIG_BUTTON_RIGHT;
}
if (buttonsTriggered & VPAD_BUTTON_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_L;
}
if (buttonsTriggered & VPAD_BUTTON_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_R;
}
if (buttonsTriggered & VPAD_BUTTON_ZL) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZL;
}
if (buttonsTriggered & VPAD_BUTTON_ZR) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZR;
}
if (buttonsTriggered & VPAD_BUTTON_X) {
pressedButtons |= WUPS_CONFIG_BUTTON_Y;
}
if (buttonsTriggered & VPAD_BUTTON_Y) {
pressedButtons |= WUPS_CONFIG_BUTTON_X;
}
if (buttonsTriggered & VPAD_BUTTON_STICK_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_L;
}
if (buttonsTriggered & VPAD_BUTTON_STICK_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_R;
}
if (buttonsTriggered & VPAD_BUTTON_PLUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_PLUS;
}
if (buttonsTriggered & VPAD_BUTTON_MINUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_MINUS;
}
if (!isItemMovementAllowed) {
if (buttonsTriggered & VPAD_BUTTON_B) {
pressedButtons |= WUPS_CONFIG_BUTTON_B;
}
if (buttonsTriggered & VPAD_BUTTON_UP) {
pressedButtons |= WUPS_CONFIG_BUTTON_UP;
}
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
pressedButtons |= WUPS_CONFIG_BUTTON_UP;
}
}
if (pressedButtons != WUPS_CONFIG_BUTTON_NONE) {
redraw = true;
}
if (selectedItem >= end) {
end = selectedItem + 1;
start = selectedItem - MAX_BUTTONS_ON_SCREEN;
} else if (selectedItem < start) {
start = selectedItem;
end = start + MAX_BUTTONS_ON_SCREEN;
}
if (redraw) {
if (prevSelectedItem != (int32_t) selectedItem) {
if (prevSelectedItem >= 0) {
config_items[prevSelectedItem]->onSelected(false);
}
config_items[selectedItem]->onSelected(true);
prevSelectedItem = (int32_t) selectedItem;
}
if (pressedButtons != WUPS_CONFIG_BUTTON_NONE) {
config_items[selectedItem]->onButtonPressed(pressedButtons);
}
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
// draw buttons
uint32_t index = 8 + 24 + 8 + 4;
for (uint32_t i = start; i < end; i++) {
DrawUtils::setFontColor(COLOR_TEXT);
if (i == selectedItem) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, index + 8 + 24, config_items[i]->getDisplayName().c_str());
if (i == selectedItem) {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, config_items[i]->getCurrentValueSelectedDisplay().c_str(), true);
} else {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, config_items[i]->getCurrentValueDisplay().c_str(), true);
}
index += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
auto headline = string_format("%s - %s", currentConfig->config->getName().c_str(), currentCategory->getName().c_str());
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(headline, 45).c_str());
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 / \ue07e Toggle", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < config_items.size()) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
redraw = pressedButtons != WUPS_CONFIG_BUTTON_NONE;
isItemMovementAllowed = config_items[selectedItem]->isMovementAllowed();
}
}
for (const auto &element : configs) {
for (const auto &cat : element.config->getCategories()) {
for (const auto &item : cat->getItems()) {
if (item->isDirty()) {
item->callCallback();
}
}
}
}
CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_CONFIG_CLOSED);
for (const auto &element : configs) {
delete element.config;
}
}
#define __SetDCPitchReg ((void (*)(uint32_t, uint32_t))(0x101C400 + 0x1e714))
void ConfigUtils::openConfigMenu() {
bool wasHomeButtonMenuEnabled = OSIsHomeButtonMenuEnabled();
OSScreenInit();
uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
void *screenbuffer0 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size, 0x100);
void *screenbuffer1 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf1_size, 0x100);
// Fix the TV buffer pitch if a 1080p buffer is used.
if (screen_buf0_size == 0x00FD2000) {
__SetDCPitchReg(SCREEN_TV, 1920);
}
bool skipScreen0Free = false;
bool skipScreen1Free = false;
if (!screenbuffer0 || !screenbuffer1) {
if (screenbuffer0 == nullptr) {
if (gStoredTVBuffer.buffer_size >= screen_buf0_size) {
screenbuffer0 = gStoredTVBuffer.buffer;
skipScreen0Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedTVBuffer");
}
}
if (screenbuffer1 == nullptr) {
if (gStoredDRCBuffer.buffer_size >= screen_buf1_size) {
screenbuffer1 = gStoredDRCBuffer.buffer;
skipScreen1Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedDRCBuffer");
}
}
if (!screenbuffer0 || !screenbuffer1) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc buffers");
goto error_exit;
}
}
OSScreenSetBufferEx(SCREEN_TV, screenbuffer0);
OSScreenSetBufferEx(SCREEN_DRC, screenbuffer1);
OSScreenEnableEx(SCREEN_TV, 1);
OSScreenEnableEx(SCREEN_DRC, 1);
// Clear screens
OSScreenClearBufferEx(SCREEN_TV, 0);
OSScreenClearBufferEx(SCREEN_DRC, 0);
// Flip buffers
OSScreenFlipBuffersEx(SCREEN_TV);
OSScreenFlipBuffersEx(SCREEN_DRC);
DrawUtils::initBuffers(screenbuffer0, screen_buf0_size, screenbuffer1, screen_buf1_size);
if (!DrawUtils::initFont()) {
DEBUG_FUNCTION_LINE_ERR("Failed to init Font");
goto error_exit;
}
// disable the home button menu to prevent opening it when exiting
OSEnableHomeButtonMenu(false);
displayMenu();
OSEnableHomeButtonMenu(wasHomeButtonMenuEnabled);
DrawUtils::deinitFont();
error_exit:
if (gStoredTVBuffer.buffer != nullptr) {
GX2SetTVBuffer(gStoredTVBuffer.buffer, gStoredTVBuffer.buffer_size, static_cast<GX2TVRenderMode>(gStoredTVBuffer.mode),
gStoredTVBuffer.surface_format, gStoredTVBuffer.buffering_mode);
}
if (gStoredDRCBuffer.buffer != nullptr) {
GX2SetDRCBuffer(gStoredDRCBuffer.buffer, gStoredDRCBuffer.buffer_size, static_cast<GX2DrcRenderMode>(gStoredDRCBuffer.mode),
gStoredDRCBuffer.surface_format, gStoredDRCBuffer.buffering_mode);
}
if (!skipScreen0Free && screenbuffer0) {
MEMFreeToMappedMemory(screenbuffer0);
}
if (!skipScreen1Free && screenbuffer1) {
MEMFreeToMappedMemory(screenbuffer1);
}
}
void ConfigUtils::renderBasicScreen(std::string_view text) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth(text.data());
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), text.data());
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <gx2/enum.h>
#include <string>
struct StoredBuffer {
void *buffer;
uint32_t buffer_size;
uint32_t mode;
GX2SurfaceFormat surface_format;
GX2BufferingMode buffering_mode;
};
class ConfigUtils {
public:
static void openConfigMenu();
private:
static void displayMenu();
static void renderBasicScreen(std::string_view text);
};

View File

@ -0,0 +1,221 @@
#include "CategoryRenderer.h"
#include "ConfigDefines.h"
#include "config/WUPSConfigCategory.h"
#include "utils/input/Input.h"
#include "utils/utils.h"
#include <vector>
#include <wups/config.h>
CategoryRenderer::CategoryRenderer(const GeneralConfigInformation *info, const WUPSConfigAPIBackend::WUPSConfigCategory *cat, bool isRoot)
: mInfo(info), mCat(cat), mIsRoot(isRoot) {
for (uint32_t i = 0; i < cat->getCategories().size() + cat->getItems().size(); i++) {
if (i < cat->getCategories().size()) {
auto item = make_unique_nothrow<ConfigRendererItemCategory>(cat->getCategories()[i].get());
if (!item) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc ConfigRendererItemCategory");
OSFatal("WiiUPluginBackend::CategoryRenderer::CategoryRenderer() Failed to alloc ConfigRendererItemCategory");
}
mItemRenderer.push_back(std::move(item));
} else {
auto itemIndex = (int32_t) (i - cat->getCategories().size());
if (itemIndex < 0 || itemIndex >= (int32_t) cat->getItems().size()) {
DEBUG_FUNCTION_LINE_ERR("unexpected index");
OSFatal("WiiUPluginBackend::CategoryRenderer::CategoryRenderer() unexpected index");
}
auto item = make_unique_nothrow<ConfigRendererItem>(cat->getItems()[itemIndex].get());
if (!item) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc ConfigRendererItemCategory");
OSFatal("WiiUPluginBackend::CategoryRenderer::CategoryRenderer() Failed to alloc ConfigRendererItem");
}
mItemRenderer.push_back(std::move(item));
}
}
mCursorPos = 0;
if (!mItemRenderer.empty()) {
mItemRenderer[mCursorPos]->SetIsSelected(true);
}
}
CategoryRenderer::~CategoryRenderer() {
if (mCursorPos < (int32_t) mItemRenderer.size()) {
mItemRenderer[mCursorPos]->SetIsSelected(false);
}
}
ConfigSubState CategoryRenderer::Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData) {
switch (mState) {
case STATE_MAIN: {
auto res = UpdateStateMain(input, simpleInputData, complexInputData);
mFirstFrame = false;
return res;
}
case STATE_SUB: {
if (mSubCategoryRenderer) {
auto subResult = mSubCategoryRenderer->Update(input, simpleInputData, complexInputData);
if (subResult != SUB_STATE_RUNNING) {
mState = STATE_MAIN;
mFirstFrame = true;
return SUB_STATE_RUNNING;
}
return SUB_STATE_RUNNING;
} else {
DEBUG_FUNCTION_LINE_WARN("State is RENDERER_STATE_CAT but mCategoryRenderer is null. Resetting state.");
mState = STATE_MAIN;
mCursorPos = 0;
}
}
}
return SUB_STATE_ERROR;
}
ConfigSubState CategoryRenderer::UpdateStateMain(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData) {
if (mIsItemMovementAllowed && input.data.buttons_d & Input::eButtons::BUTTON_B) {
return SUB_STATE_RETURN;
}
if (mItemRenderer.empty()) {
return SUB_STATE_RUNNING;
}
auto totalElementSize = mItemRenderer.size();
int32_t prevSelectedItem = mCursorPos;
if (mIsItemMovementAllowed) {
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
mCursorPos++;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) {
mCursorPos--;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
if (mCursorPos < (int32_t) mCat->getCategories().size()) {
if (mCurrentOpen != mCursorPos) {
mSubCategoryRenderer.reset();
mSubCategoryRenderer = make_unique_nothrow<CategoryRenderer>(mInfo, mCat->getCategories()[mCursorPos].get(), false);
}
mCurrentOpen = mCursorPos;
mState = STATE_SUB;
}
}
}
if (mCursorPos < 0) {
mCursorPos = (int32_t) totalElementSize - 1;
} else if (mCursorPos > (int32_t) (totalElementSize - 1)) {
mCursorPos = 0;
}
// Adjust the render offset when reaching the boundaries
if (mCursorPos < mRenderOffset) {
mRenderOffset = mCursorPos;
} else if (mCursorPos >= mRenderOffset + MAX_BUTTONS_ON_SCREEN - 1) {
mRenderOffset = mCursorPos - MAX_BUTTONS_ON_SCREEN + 1;
}
bool posJustChanged = false;
if (prevSelectedItem != mCursorPos) {
mItemRenderer[prevSelectedItem]->SetIsSelected(false);
mItemRenderer[mCursorPos]->SetIsSelected(true);
posJustChanged = true;
}
if (!posJustChanged && !mFirstFrame) {
// WUPSConfigItemV2
mItemRenderer[mCursorPos]->OnInput(simpleInputData);
mItemRenderer[mCursorPos]->OnInputEx(complexInputData);
// WUPSConfigItemV1
if (input.data.buttons_d != 0) {
WUPSConfigButtons buttons = input.data.buttons_d;
buttons = ConfigUtils::convertInputs(buttons);
if (!mIsItemMovementAllowed) {
buttons &= ~MOVE_ITEM_INPUT_MASK;
}
mItemRenderer[mCursorPos]->OnButtonPressed(buttons);
}
}
mIsItemMovementAllowed = mItemRenderer[mCursorPos]->IsMovementAllowed();
return SUB_STATE_RUNNING;
}
void CategoryRenderer::Render() const {
switch (mState) {
case STATE_MAIN:
RenderStateMain();
break;
case STATE_SUB: {
if (mSubCategoryRenderer) {
mSubCategoryRenderer->Render();
} else {
DEBUG_FUNCTION_LINE_WARN("STATE_SUB but mSubCategoryRenderer is NULL");
}
break;
}
}
}
void CategoryRenderer::RenderStateMain() const {
if (mItemRenderer.empty()) {
DrawUtils::beginDraw();
RenderMainLayout();
std::string text(mIsRoot ? "This plugin can not be configured" : "This category is empty");
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth(text.c_str());
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), text.c_str());
DrawUtils::endDraw();
return;
}
auto totalElementSize = static_cast<int>(mItemRenderer.size());
// Calculate the range of items to display
int start = std::max(0, mRenderOffset);
int end = std::min(start + MAX_BUTTONS_ON_SCREEN, totalElementSize);
DrawUtils::beginDraw();
RenderMainLayout();
uint32_t yOffset = 8 + 24 + 8 + 4;
for (int32_t i = start; i < end; i++) {
bool isHighlighted = (i == mCursorPos);
mItemRenderer[i]->Draw(yOffset, isHighlighted);
yOffset += 42 + 8;
}
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < totalElementSize) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
DrawUtils::endDraw();
}
void CategoryRenderer::RenderMainLayout() const {
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(mInfo->name, 45).c_str());
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, mInfo->version.c_str(), true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "../DrawUtils.h"
#include "ConfigRendererItem.h"
#include "ConfigRendererItemCategory.h"
#include "ConfigRendererItemGeneric.h"
#include "ConfigUtils.h"
#include "config/WUPSConfigCategory.h"
#include "utils/input/Input.h"
#include <memory>
class CategoryRenderer {
public:
explicit CategoryRenderer(const GeneralConfigInformation *info, const WUPSConfigAPIBackend::WUPSConfigCategory *cat, bool isRoot);
~CategoryRenderer();
ConfigSubState Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData);
void Render() const;
private:
ConfigSubState UpdateStateMain(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData);
void RenderStateMain() const;
void RenderMainLayout() const;
enum State {
STATE_MAIN = 0,
STATE_SUB = 1,
};
State mState = STATE_MAIN;
int32_t mCurrentOpen = -1;
int32_t mCursorPos = 0;
int32_t mRenderOffset = 0;
std::unique_ptr<CategoryRenderer> mSubCategoryRenderer = {};
const GeneralConfigInformation *mInfo = {};
const WUPSConfigAPIBackend::WUPSConfigCategory *mCat = {};
std::vector<std::unique_ptr<ConfigRendererItemGeneric>> mItemRenderer = {};
bool mIsItemMovementAllowed = true;
bool mFirstFrame = true;
bool mIsRoot = false;
};

View File

@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <gx2/surface.h>
#include <string>
#define COLOR_BACKGROUND Color(238, 238, 238, 255)
#define COLOR_TEXT Color(51, 51, 51, 255)
#define COLOR_TEXT2 Color(72, 72, 72, 255)
#define COLOR_DISABLED Color(255, 0, 0, 255)
#define COLOR_BORDER Color(204, 204, 204, 255)
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4FF)
#define COLOR_WHITE Color(0xFFFFFFFF)
#define COLOR_BLACK Color(0, 0, 0, 255)
#define MAX_BUTTONS_ON_SCREEN 8
struct StoredBuffer {
void *buffer;
uint32_t buffer_size;
uint32_t mode;
GX2SurfaceFormat surface_format;
GX2BufferingMode buffering_mode;
};
enum ConfigSubState {
SUB_STATE_RUNNING = 0,
SUB_STATE_RETURN = 1,
SUB_STATE_ERROR = 2,
};

View File

@ -0,0 +1,26 @@
#pragma once
#include "config/WUPSConfig.h"
#include <memory>
struct GeneralConfigInformation {
std::string name;
std::string author;
std::string version;
};
class ConfigDisplayItem {
public:
ConfigDisplayItem(GeneralConfigInformation &info, std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config) : mConfig(std::move(config)), mInfo(std::move(info)) {
assert(mConfig);
}
[[nodiscard]] const GeneralConfigInformation &getConfigInformation() const {
return mInfo;
}
[[nodiscard]] const WUPSConfigAPIBackend::WUPSConfig &getConfig() const {
return *mConfig;
}
private:
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> mConfig;
GeneralConfigInformation mInfo;
};

View File

@ -0,0 +1,154 @@
#include "ConfigRenderer.h"
void ConfigRenderer::RenderStateMain() const {
auto totalElementSize = (int32_t) mConfigs.size();
// Calculate the range of items to display
int start = std::max(0, mRenderOffset);
int end = std::min(start + MAX_BUTTONS_ON_SCREEN, totalElementSize);
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
uint32_t yOffset = 8 + 24 + 8 + 4;
for (int32_t i = start; i < end; i++) {
drawConfigEntry(yOffset, mConfigs[i].getConfigInformation(), i == mCursorPos);
yOffset += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < totalElementSize) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
}
void ConfigRenderer::drawConfigEntry(uint32_t yOffset, const GeneralConfigInformation &configInformation, bool isHighlighted) const {
DrawUtils::setFontColor(COLOR_TEXT);
if (isHighlighted) {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, yOffset + 8 + 24, configInformation.name.c_str());
uint32_t sz = DrawUtils::getTextWidth(configInformation.name.c_str());
DrawUtils::setFontSize(12);
DrawUtils::print(16 * 2 + sz + 4, yOffset + 8 + 24, configInformation.author.c_str());
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, configInformation.version.c_str(), true);
}
ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) {
if (mConfigs.empty()) {
return SUB_STATE_ERROR;
}
auto totalElementSize = mConfigs.size();
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
mCursorPos++;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) {
mCursorPos--;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
if (mCursorPos != mCurrentOpen) {
mCategoryRenderer.reset();
mCategoryRenderer = make_unique_nothrow<CategoryRenderer>(&(mConfigs[mCursorPos].getConfigInformation()), &(mConfigs[mCursorPos].getConfig()), true);
}
mCurrentOpen = mCursorPos;
mState = STATE_SUB;
return SUB_STATE_RUNNING;
} else if (input.data.buttons_d & (Input::eButtons::BUTTON_B | Input::eButtons::BUTTON_HOME)) {
mCategoryRenderer.reset();
for (const auto &element : mConfigs) {
CallOnCloseCallback(element.getConfigInformation(), element.getConfig().getCategories());
}
return SUB_STATE_RETURN;
}
if (mCursorPos < 0) {
mCursorPos = (int32_t) totalElementSize - 1;
} else if (mCursorPos > (int32_t) (totalElementSize - 1)) {
mCursorPos = 0;
}
// Adjust the render offset when reaching the boundaries
if (mCursorPos < mRenderOffset) {
mRenderOffset = mCursorPos;
} else if (mCursorPos >= mRenderOffset + MAX_BUTTONS_ON_SCREEN - 1) {
mRenderOffset = mCursorPos - MAX_BUTTONS_ON_SCREEN + 1;
}
return SUB_STATE_RUNNING;
}
void ConfigRenderer::Render() const {
switch (mState) {
case STATE_MAIN:
RenderStateMain();
break;
case STATE_SUB: {
if (mCategoryRenderer) {
mCategoryRenderer->Render();
} else {
DEBUG_FUNCTION_LINE_WARN("render failed: state was RENDERER_STATE_CAT but mCategoryRenderer is NULL");
}
break;
}
}
}
ConfigSubState ConfigRenderer::Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData) {
switch (mState) {
case STATE_MAIN:
return UpdateStateMain(input);
case STATE_SUB: {
if (mCategoryRenderer) {
auto subResult = mCategoryRenderer->Update(input, simpleInputData, complexInputData);
if (subResult != SUB_STATE_RUNNING) {
mState = STATE_MAIN;
return SUB_STATE_RUNNING;
}
return SUB_STATE_RUNNING;
} else {
DEBUG_FUNCTION_LINE_WARN("State is RENDERER_STATE_CAT but mCategoryRenderer is null. Resetting state.");
mState = STATE_MAIN;
mCursorPos = 0;
}
}
}
return SUB_STATE_ERROR;
}
void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories) {
for (const auto &cat : categories) {
if (!cat->getCategories().empty()) {
CallOnCloseCallback(info, cat->getCategories());
}
for (const auto &item : cat->getItems()) {
item->onCloseCallback();
}
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "../DrawUtils.h"
#include "../input/Input.h"
#include "../logger.h"
#include "CategoryRenderer.h"
#include "globals.h"
#include <memory>
#include <vector>
class ConfigRenderer {
public:
explicit ConfigRenderer(std::vector<ConfigDisplayItem> &&vec) : mConfigs(std::move(vec)) {
}
~ConfigRenderer() = default;
ConfigSubState Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData);
void Render() const;
private:
ConfigSubState UpdateStateMain(const Input &input);
void RenderStateMain() const;
void drawConfigEntry(uint32_t yOffset, const GeneralConfigInformation &configInformation, bool isHighlighted) const;
enum State {
STATE_MAIN = 0,
STATE_SUB = 1,
};
std::vector<ConfigDisplayItem> mConfigs;
std::unique_ptr<CategoryRenderer> mCategoryRenderer = {};
State mState = STATE_MAIN;
int32_t mCursorPos = 0;
int32_t mRenderOffset = 0;
int32_t mCurrentOpen = -1;
void CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories);
};

View File

@ -0,0 +1,43 @@
#pragma once
#include "ConfigRendererItemGeneric.h"
#include "config/WUPSConfigItem.h"
class ConfigRendererItem : public ConfigRendererItemGeneric {
public:
explicit ConfigRendererItem(const WUPSConfigAPIBackend::WUPSConfigItem *item) : mItem(item) {
assert(item);
}
void Draw(uint32_t yOffset, bool isHighlighted) const override {
assert(mItem);
drawGenericBoxAndText(yOffset, mItem->getDisplayName(), isHighlighted);
DrawUtils::setFontSize(24);
if (isHighlighted) {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, mItem->getCurrentValueSelectedDisplay().c_str(), true);
} else {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, mItem->getCurrentValueDisplay().c_str(), true);
}
}
void SetIsSelected(bool isSelected) override {
mItem->onSelected(isSelected);
}
void OnButtonPressed(WUPSConfigButtons buttons) override {
mItem->onButtonPressed(buttons);
}
[[nodiscard]] bool IsMovementAllowed() const override {
return mItem->isMovementAllowed();
}
void OnInput(WUPSConfigSimplePadData input) override {
mItem->onInput(input);
}
void OnInputEx(WUPSConfigComplexPadData input) override {
mItem->onInputEx(input);
}
private:
const WUPSConfigAPIBackend::WUPSConfigItem *mItem;
};

View File

@ -0,0 +1,17 @@
#pragma once
#include "ConfigRendererItemGeneric.h"
#include "config/WUPSConfigCategory.h"
class ConfigRendererItemCategory : public ConfigRendererItemGeneric {
public:
explicit ConfigRendererItemCategory(const WUPSConfigAPIBackend::WUPSConfigCategory *category) : mCategory(category) {
assert(category);
}
void Draw(uint32_t yOffset, bool isHighlighted) const override {
drawGenericBoxAndText(yOffset, mCategory->getName(), isHighlighted);
}
private:
const WUPSConfigAPIBackend::WUPSConfigCategory *mCategory;
};

View File

@ -0,0 +1,36 @@
#pragma once
#include "../DrawUtils.h"
#include "ConfigDefines.h"
#include <wups/config.h>
class ConfigRendererItemGeneric {
public:
virtual ~ConfigRendererItemGeneric() = default;
virtual void drawGenericBoxAndText(uint32_t yOffset, const std::string &displayName, bool isHighlighted) const {
if (isHighlighted) {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::setFontColor(COLOR_TEXT);
DrawUtils::print(16 * 2, yOffset + 8 + 24, displayName.c_str());
}
virtual void Draw(uint32_t yOffset, bool isHighlighted) const = 0;
virtual void SetIsSelected(bool) {
}
virtual void OnButtonPressed(WUPSConfigButtons) {
}
virtual void OnInput(WUPSConfigSimplePadData) {
}
virtual void OnInputEx(WUPSConfigComplexPadData) {
}
[[nodiscard]] virtual bool IsMovementAllowed() const {
return true;
}
};

View File

@ -0,0 +1,308 @@
#include "ConfigUtils.h"
#include "../../globals.h"
#include "../DrawUtils.h"
#include "../logger.h"
#include "ConfigRenderer.h"
#include "config/WUPSConfigAPI.h"
#include "hooks.h"
#include "utils/input/CombinedInput.h"
#include "utils/input/VPADInput.h"
#include "utils/input/WPADInput.h"
#include <coreinit/screen.h>
#include <gx2/display.h>
#include <memory/mappedmemory.h>
#include <string>
#include <vector>
WUPS_CONFIG_SIMPLE_INPUT ConfigUtils::convertInputs(uint32_t buttons) {
WUPSConfigButtons pressedButtons = WUPS_CONFIG_BUTTON_NONE;
if (buttons & Input::eButtons::BUTTON_A) {
pressedButtons |= WUPS_CONFIG_BUTTON_A;
}
if (buttons & Input::eButtons::BUTTON_LEFT) {
pressedButtons |= WUPS_CONFIG_BUTTON_LEFT;
}
if (buttons & Input::eButtons::BUTTON_RIGHT) {
pressedButtons |= WUPS_CONFIG_BUTTON_RIGHT;
}
if (buttons & Input::eButtons::BUTTON_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_L;
}
if (buttons & Input::eButtons::BUTTON_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_R;
}
if (buttons & Input::eButtons::BUTTON_ZL) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZL;
}
if (buttons & Input::eButtons::BUTTON_ZR) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZR;
}
if (buttons & Input::eButtons::BUTTON_X) {
pressedButtons |= WUPS_CONFIG_BUTTON_X;
}
if (buttons & Input::eButtons::BUTTON_Y) {
pressedButtons |= WUPS_CONFIG_BUTTON_Y;
}
if (buttons & Input::eButtons::BUTTON_STICK_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_L;
}
if (buttons & Input::eButtons::BUTTON_STICK_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_R;
}
if (buttons & Input::eButtons::BUTTON_PLUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_PLUS;
}
if (buttons & Input::eButtons::BUTTON_MINUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_MINUS;
}
if (buttons & Input::eButtons::BUTTON_B) {
pressedButtons |= WUPS_CONFIG_BUTTON_B;
}
if (buttons & Input::eButtons::BUTTON_UP) {
pressedButtons |= WUPS_CONFIG_BUTTON_UP;
}
if (buttons & Input::eButtons::BUTTON_DOWN) {
pressedButtons |= WUPS_CONFIG_BUTTON_DOWN;
}
return (WUPS_CONFIG_SIMPLE_INPUT) pressedButtons;
}
void ConfigUtils::displayMenu() {
renderBasicScreen("Loading configs...");
std::vector<ConfigDisplayItem> configs;
for (auto &plugin : gLoadedPlugins) {
GeneralConfigInformation info;
info.name = plugin->getMetaInformation().getName();
info.author = plugin->getMetaInformation().getAuthor();
info.version = plugin->getMetaInformation().getVersion();
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config;
auto configData = plugin->getConfigData();
if (configData) {
auto configHandleOpt = configData->createConfig();
if (configHandleOpt) {
WUPSConfigAPIStatus callbackResult = configData->CallMenuOpenendCallback(configHandleOpt.value());
config = WUPSConfigAPIBackend::Intern::PopConfigByHandle(configHandleOpt.value());
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to get config for handle: %08X", configHandleOpt.value().handle);
} else if (callbackResult != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Callback failed for %s: %s", info.name.c_str(), WUPSConfigAPI_GetStatusStr(callbackResult));
config.reset();
}
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to create config for plugin: \"%s\"", info.name.c_str());
}
} else {
for (const auto &hook : plugin->getPluginInformation().getHookDataList()) {
if (hook->getType() == WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED) {
if (hook->getFunctionPointer() == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Hook had invalid ptr");
break;
}
auto cur_config_handle = ((void *(*) ())((uint32_t *) hook->getFunctionPointer()))();
if (cur_config_handle == nullptr) {
DEBUG_FUNCTION_LINE_WARN("Hook returned empty handle");
break;
}
config = WUPSConfigAPIBackend::Intern::PopConfigByHandle(WUPSConfigHandle(cur_config_handle));
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to find config for handle: %08X", cur_config_handle);
}
break;
}
}
}
if (!config) {
config = make_unique_nothrow<WUPSConfigAPIBackend::WUPSConfig>("DUMMY");
}
configs.emplace_back(info, std::move(config));
}
ConfigRenderer renderer(std::move(configs));
configs.clear();
CombinedInput baseInput;
VPadInput vpadInput;
WPADInput wpadInputs[4] = {
WPAD_CHAN_0,
WPAD_CHAN_1,
WPAD_CHAN_2,
WPAD_CHAN_3};
auto startTime = OSGetTime();
while (true) {
baseInput.reset();
if (vpadInput.update(1280, 720)) {
baseInput.combine(vpadInput);
}
for (auto &wpadInput : wpadInputs) {
if (wpadInput.update(1280, 720)) {
baseInput.combine(wpadInput);
}
}
baseInput.process();
WUPSConfigSimplePadData simpleData;
simpleData.buttons_d = convertInputs(baseInput.data.buttons_d);
simpleData.buttons_r = convertInputs(baseInput.data.buttons_r);
simpleData.buttons_h = convertInputs(baseInput.data.buttons_h);
simpleData.x = baseInput.data.x;
simpleData.y = baseInput.data.y;
simpleData.touched = baseInput.data.touched;
simpleData.validPointer = baseInput.data.validPointer;
WUPSConfigComplexPadData complexData;
complexData.vpad.data = vpadInput.vpad;
complexData.vpad.tpCalib = vpadInput.tpCalib;
complexData.vpad.vpadError = vpadInput.vpadError;
for (int i = 0; i < 4; i++) {
complexData.kpad.kpadError[i] = wpadInputs[i].kpadError;
complexData.kpad.data[i] = wpadInputs[i].kpad;
}
auto subState = renderer.Update(baseInput, simpleData, complexData);
if (subState != SUB_STATE_RUNNING) {
break;
}
renderer.Render();
auto diffTime = OSTicksToMicroseconds(OSGetTime() - startTime);
if (diffTime < 16000) {
OSSleepTicks(OSMicrosecondsToTicks(16000 - diffTime));
}
}
for (auto &plugin : gLoadedPlugins) {
auto configData = plugin->getConfigData();
if (configData) {
configData->CallMenuClosedCallback();
} else {
CallHook(*plugin, WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED);
}
}
WUPSConfigAPIBackend::Intern::CleanAllHandles();
}
#define __SetDCPitchReg ((void (*)(uint32_t, uint32_t))(0x101C400 + 0x1e714))
void ConfigUtils::openConfigMenu() {
bool wasHomeButtonMenuEnabled = OSIsHomeButtonMenuEnabled();
OSScreenInit();
uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
void *screenbuffer0 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size, 0x100);
void *screenbuffer1 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf1_size, 0x100);
// Fix the TV buffer pitch if a 1080p buffer is used.
if (screen_buf0_size == 0x00FD2000) {
__SetDCPitchReg(SCREEN_TV, 1920);
}
bool skipScreen0Free = false;
bool skipScreen1Free = false;
if (!screenbuffer0 || !screenbuffer1) {
if (screenbuffer0 == nullptr) {
if (gStoredTVBuffer.buffer_size >= screen_buf0_size) {
screenbuffer0 = gStoredTVBuffer.buffer;
skipScreen0Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedTVBuffer");
}
}
if (screenbuffer1 == nullptr) {
if (gStoredDRCBuffer.buffer_size >= screen_buf1_size) {
screenbuffer1 = gStoredDRCBuffer.buffer;
skipScreen1Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedDRCBuffer");
}
}
if (!screenbuffer0 || !screenbuffer1) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc buffers");
goto error_exit;
}
}
OSScreenSetBufferEx(SCREEN_TV, screenbuffer0);
OSScreenSetBufferEx(SCREEN_DRC, screenbuffer1);
OSScreenEnableEx(SCREEN_TV, 1);
OSScreenEnableEx(SCREEN_DRC, 1);
// Clear screens
OSScreenClearBufferEx(SCREEN_TV, 0);
OSScreenClearBufferEx(SCREEN_DRC, 0);
// Flip buffers
OSScreenFlipBuffersEx(SCREEN_TV);
OSScreenFlipBuffersEx(SCREEN_DRC);
DrawUtils::initBuffers(screenbuffer0, screen_buf0_size, screenbuffer1, screen_buf1_size);
if (!DrawUtils::initFont()) {
DEBUG_FUNCTION_LINE_ERR("Failed to init Font");
goto error_exit;
}
// disable the home button menu to prevent opening it when exiting
OSEnableHomeButtonMenu(false);
displayMenu();
OSEnableHomeButtonMenu(wasHomeButtonMenuEnabled);
DrawUtils::deinitFont();
error_exit:
if (gStoredTVBuffer.buffer != nullptr) {
GX2SetTVBuffer(gStoredTVBuffer.buffer, gStoredTVBuffer.buffer_size, static_cast<GX2TVRenderMode>(gStoredTVBuffer.mode),
gStoredTVBuffer.surface_format, gStoredTVBuffer.buffering_mode);
}
if (gStoredDRCBuffer.buffer != nullptr) {
GX2SetDRCBuffer(gStoredDRCBuffer.buffer, gStoredDRCBuffer.buffer_size, static_cast<GX2DrcRenderMode>(gStoredDRCBuffer.mode),
gStoredDRCBuffer.surface_format, gStoredDRCBuffer.buffering_mode);
}
if (!skipScreen0Free && screenbuffer0) {
MEMFreeToMappedMemory(screenbuffer0);
}
if (!skipScreen1Free && screenbuffer1) {
MEMFreeToMappedMemory(screenbuffer1);
}
}
void ConfigUtils::renderBasicScreen(std::string_view text) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth(text.data());
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), text.data());
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "ConfigDefines.h"
#include "ConfigDisplayItem.h"
#include "config/WUPSConfig.h"
#include "utils/input/Input.h"
#include <gx2/enum.h>
#include <memory>
#include <string>
#define MOVE_ITEM_INPUT_MASK (WUPS_CONFIG_BUTTON_B | WUPS_CONFIG_BUTTON_DOWN | WUPS_CONFIG_BUTTON_UP)
class ConfigUtils {
public:
static void openConfigMenu();
static WUPS_CONFIG_SIMPLE_INPUT convertInputs(uint32_t buttons);
private:
static void displayMenu();
static void renderBasicScreen(std::string_view text);
};

View File

@ -0,0 +1,27 @@
#pragma once
#include "Input.h"
class CombinedInput : public Input {
public:
void combine(const Input &b) {
data.buttons_h |= b.data.buttons_h;
if (!data.touched) {
data.touched = b.data.touched;
}
if (!data.validPointer) {
data.validPointer = b.data.validPointer;
data.pointerAngle = b.data.pointerAngle;
data.x = b.data.x;
data.y = b.data.y;
}
}
void process() {
data.buttons_d |= (data.buttons_h & (~lastData.buttons_h));
data.buttons_r |= (lastData.buttons_h & (~data.buttons_h));
lastData.buttons_h = data.buttons_h;
}
void reset() {
data = {};
}
};

View File

@ -0,0 +1,62 @@
#pragma once
#include <cstdint>
#include <cstring>
class Input {
public:
//!Constructor
Input() = default;
//!Destructor
virtual ~Input() = default;
enum eButtons {
BUTTON_NONE = 0x0000,
VPAD_TOUCH = 0x80000000,
BUTTON_STICK_L = 0x80000,
BUTTON_STICK_R = 0x40000,
BUTTON_Z = 0x20000,
BUTTON_C = 0x10000,
BUTTON_A = 0x8000,
BUTTON_B = 0x4000,
BUTTON_X = 0x2000,
BUTTON_Y = 0x1000,
BUTTON_1 = BUTTON_Y,
BUTTON_2 = BUTTON_X,
BUTTON_LEFT = 0x0800,
BUTTON_RIGHT = 0x0400,
BUTTON_UP = 0x0200,
BUTTON_DOWN = 0x0100,
BUTTON_ZL = 0x0080,
BUTTON_ZR = 0x0040,
BUTTON_L = 0x0020,
BUTTON_R = 0x0010,
BUTTON_PLUS = 0x0008,
BUTTON_MINUS = 0x0004,
BUTTON_HOME = 0x0002,
BUTTON_SYNC = 0x0001,
STICK_R_LEFT = 0x04000000,
STICK_R_RIGHT = 0x02000000,
STICK_R_UP = 0x01000000,
STICK_R_DOWN = 0x00800000,
STICK_L_LEFT = 0x40000000,
STICK_L_RIGHT = 0x20000000,
STICK_L_UP = 0x10000000,
STICK_L_DOWN = 0x08000000
};
typedef struct {
uint32_t buttons_h;
uint32_t buttons_d;
uint32_t buttons_r;
bool validPointer;
bool touched;
float pointerAngle;
int32_t x;
int32_t y;
} PadData;
PadData data{};
PadData lastData{};
};

View File

@ -0,0 +1,57 @@
#pragma once
/****************************************************************************
* Copyright (C) 2015 Dimok
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "Input.h"
#include <vpad/input.h>
class VPadInput : public Input {
public:
//!Constructor
VPadInput() = default;
//!Destructor
~VPadInput() override = default;
bool update(int32_t width, int32_t height) {
lastData = data;
data = {};
vpadError = VPAD_READ_NO_SAMPLES;
if (VPADRead(VPAD_CHAN_0, &vpad, 1, &vpadError) > 0 && vpadError == VPAD_READ_SUCCESS) {
data.buttons_r = vpad.release;
data.buttons_h = vpad.hold;
data.buttons_d = vpad.trigger;
data.validPointer = !vpad.tpNormal.validity;
data.touched = vpad.tpNormal.touched;
VPADGetTPCalibratedPoint(VPAD_CHAN_0, &tpCalib, &vpad.tpFiltered1);
//! calculate the screen offsets
data.x = -(width >> 1) + (int32_t) (((float) tpCalib.x / 1280.0f) * (float) width);
data.y = -(height >> 1) + (int32_t) (float) height - (((float) tpCalib.y / 720.0f) * (float) height);
return true;
}
return false;
}
VPADStatus vpad = {};
VPADReadError vpadError = {};
VPADTouchData tpCalib = {};
};

View File

@ -0,0 +1,187 @@
#pragma once
/****************************************************************************
* Copyright (C) 2015 Dimok
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "Input.h"
#include <padscore/kpad.h>
#include <padscore/wpad.h>
class WPADInput : public Input {
public:
WPADInput(KPADChan channel) {
this->channel = channel;
}
~WPADInput() override {}
uint32_t remapWiiMoteButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_BUTTON_LEFT)
conv_buttons |= Input::BUTTON_LEFT;
if (buttons & WPAD_BUTTON_RIGHT)
conv_buttons |= Input::BUTTON_RIGHT;
if (buttons & WPAD_BUTTON_DOWN)
conv_buttons |= Input::BUTTON_DOWN;
if (buttons & WPAD_BUTTON_UP)
conv_buttons |= Input::BUTTON_UP;
if (buttons & WPAD_BUTTON_PLUS)
conv_buttons |= Input::BUTTON_PLUS;
if (buttons & WPAD_BUTTON_2)
conv_buttons |= Input::BUTTON_2;
if (buttons & WPAD_BUTTON_1)
conv_buttons |= Input::BUTTON_1;
if (buttons & WPAD_BUTTON_B)
conv_buttons |= Input::BUTTON_B;
if (buttons & WPAD_BUTTON_A)
conv_buttons |= Input::BUTTON_A;
if (buttons & WPAD_BUTTON_MINUS)
conv_buttons |= Input::BUTTON_MINUS;
if (buttons & WPAD_BUTTON_Z)
conv_buttons |= Input::BUTTON_Z;
if (buttons & WPAD_BUTTON_C)
conv_buttons |= Input::BUTTON_C;
if (buttons & WPAD_BUTTON_HOME)
conv_buttons |= Input::BUTTON_HOME;
return conv_buttons;
}
uint32_t remapClassicButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
conv_buttons |= Input::BUTTON_LEFT;
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
conv_buttons |= Input::BUTTON_RIGHT;
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
conv_buttons |= Input::BUTTON_DOWN;
if (buttons & WPAD_CLASSIC_BUTTON_UP)
conv_buttons |= Input::BUTTON_UP;
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
conv_buttons |= Input::BUTTON_PLUS;
if (buttons & WPAD_CLASSIC_BUTTON_X)
conv_buttons |= Input::BUTTON_X;
if (buttons & WPAD_CLASSIC_BUTTON_Y)
conv_buttons |= Input::BUTTON_Y;
if (buttons & WPAD_CLASSIC_BUTTON_B)
conv_buttons |= Input::BUTTON_B;
if (buttons & WPAD_CLASSIC_BUTTON_A)
conv_buttons |= Input::BUTTON_A;
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
conv_buttons |= Input::BUTTON_MINUS;
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
conv_buttons |= Input::BUTTON_HOME;
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
conv_buttons |= Input::BUTTON_ZR;
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
conv_buttons |= Input::BUTTON_ZL;
if (buttons & WPAD_CLASSIC_BUTTON_R)
conv_buttons |= Input::BUTTON_R;
if (buttons & WPAD_CLASSIC_BUTTON_L)
conv_buttons |= Input::BUTTON_L;
return conv_buttons;
}
bool update(int32_t width, int32_t height) {
lastData = data;
kpadError = KPAD_ERROR_UNINITIALIZED;
data = {};
WPADExtensionType type;
if (WPADProbe(channel, &type) != 0) {
data.buttons_h = 0;
return false;
}
if (KPADReadEx(channel, &kpad, 1, &kpadError) <= 0) {
return false;
}
if (kpadError != KPAD_ERROR_OK || kpad.extensionType == 0xFF) {
return false;
}
if (kpad.extensionType == WPAD_EXT_CORE || kpad.extensionType == WPAD_EXT_NUNCHUK) {
data.buttons_r = remapWiiMoteButtons(kpad.release);
data.buttons_h = remapWiiMoteButtons(kpad.hold);
data.buttons_d = remapWiiMoteButtons(kpad.trigger);
} else {
data.buttons_r = remapClassicButtons(kpad.classic.release);
data.buttons_h = remapClassicButtons(kpad.classic.hold);
data.buttons_d = remapClassicButtons(kpad.classic.trigger);
}
data.validPointer = (kpad.posValid == 1 || kpad.posValid == 2) &&
(kpad.pos.x >= -1.0f && kpad.pos.x <= 1.0f) &&
(kpad.pos.y >= -1.0f && kpad.pos.y <= 1.0f);
//! calculate the screen offsets if pointer is valid else leave old value
if (data.validPointer) {
data.x = (width >> 1) * kpad.pos.x;
data.y = (height >> 1) * (-kpad.pos.y);
if (kpad.angle.y > 0.0f) {
data.pointerAngle = (-kpad.angle.x + 1.0f) * 0.5f * 180.0f;
} else {
data.pointerAngle = (kpad.angle.x + 1.0f) * 0.5f * 180.0f - 180.0f;
}
}
return true;
}
static void init() {
KPADInit();
WPADEnableURCC(1);
}
static void close() {
}
KPADStatus kpad = {};
KPADError kpadError = KPAD_ERROR_UNINITIALIZED;
KPADChan channel;
};

View File

@ -55,7 +55,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) while (0) #define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##INFO ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS); #define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);

View File

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <vector>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -91,12 +92,44 @@ remove_first_if(Container &container, Predicate pred) {
return false; return false;
} }
template<typename Container, typename Predicate>
typename std::enable_if<std::is_same<Container, std::vector<typename Container::value_type>>::value, bool>::type
remove_first_if(Container &container, Predicate pred) {
auto it = container.begin();
while (it != container.end()) {
if (pred(*it)) {
container.erase(it);
return true;
}
++it;
}
return false;
}
template<typename Container, typename Predicate> template<typename Container, typename Predicate>
bool remove_locked_first_if(std::mutex &mutex, Container &container, Predicate pred) { bool remove_locked_first_if(std::mutex &mutex, Container &container, Predicate pred) {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
return remove_first_if(container, pred); return remove_first_if(container, pred);
} }
template<typename T, typename Predicate>
T pop_locked_first_if(std::mutex &mutex, std::vector<T> &container, Predicate pred) {
std::lock_guard<std::mutex> lock(mutex);
T result;
auto it = container.begin();
while (it != container.end()) {
if (pred(*it)) {
result = std::move(*it);
container.erase(it);
return result;
}
++it;
}
return result;
}
std::string getPluginPath(); std::string getPluginPath();
OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr); OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr);