diff --git a/Dockerfile b/Dockerfile index febcb6d..f791b48 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM ghcr.io/wiiu-env/devkitppc:20230621 -COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20230719 /artifacts $DEVKITPRO -COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:0.7.2-dev-20231105-45d17c5 /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.8.0-dev-20231216-104fdc3 /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/libwupsbackend:20230621 /artifacts $DEVKITPRO diff --git a/Makefile b/Makefile index 88d16a0..042b6fd 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,9 @@ SOURCES := source \ source/patcher \ source/plugin \ source/utils \ - source/utils/storage + source/utils/config \ + source/utils/storage \ + source/uitls/input DATA := data INCLUDES := source @@ -41,7 +43,7 @@ CFLAGS := -Wall -Wextra -Os -ffunction-sections -fdata-sections\ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -CXXFLAGS := $(CFLAGS) -std=c++20 -fno-exceptions -fno-rtti +CXXFLAGS := $(CFLAGS) -std=c++20 -fno-exceptions -fno-rtti ASFLAGS := -g $(ARCH) LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libmappedmemory.ld $(WUMSSPECS) @@ -56,7 +58,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g 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 diff --git a/source/PluginManagement.cpp b/source/PluginManagement.cpp index 3fa1ad9..ec4bdf6 100644 --- a/source/PluginManagement.cpp +++ b/source/PluginManagement.cpp @@ -181,6 +181,7 @@ bool PluginManagement::DoFunctionPatches(const std::vector> &plugins) { + CallHook(plugins, WUPS_LOADER_HOOK_INIT_CONFIG); CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED); CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE); CallHook(plugins, WUPS_LOADER_HOOK_INIT_PLUGIN); diff --git a/source/config/WUPSConfig.h b/source/config/WUPSConfig.h index ebc6494..e32b2ab 100644 --- a/source/config/WUPSConfig.h +++ b/source/config/WUPSConfig.h @@ -18,73 +18,14 @@ #pragma once #include "WUPSConfigCategory.h" -#include "utils/logger.h" #include #include #include -#include -class WUPSConfig { -public: - explicit WUPSConfig(std::string_view name) { - this->name = name; - } - - ~WUPSConfig() { - for (const auto &element : categories) { - delete element; +namespace WUPSConfigAPIBackend { + class WUPSConfig : public WUPSConfigCategory { + public: + explicit WUPSConfig(std::string_view name) : WUPSConfigCategory(name) { } - } - - /** - \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 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 &getCategories() { - return this->categories; - } - -private: - std::string name; - std::vector categories = {}; -}; + }; +} // namespace WUPSConfigAPIBackend diff --git a/source/config/WUPSConfigAPI.cpp b/source/config/WUPSConfigAPI.cpp new file mode 100644 index 0000000..c521638 --- /dev/null +++ b/source/config/WUPSConfigAPI.cpp @@ -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 +#include +#include +#include +#include +#include + +namespace WUPSConfigAPIBackend { + std::vector> sConfigs; + std::mutex sConfigsMutex; + + std::vector> sConfigCategories; + std::mutex sConfigCategoryMutex; + + std::vector> 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 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 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 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(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(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 item = nullptr; + if (options.version == WUPS_API_ITEM_OPTION_VERSION_V1) { + item = make_unique_nothrow(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(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 diff --git a/source/config/WUPSConfigAPI.h b/source/config/WUPSConfigAPI.h new file mode 100644 index 0000000..d34518f --- /dev/null +++ b/source/config/WUPSConfigAPI.h @@ -0,0 +1,231 @@ +#pragma once + +#include "WUPSConfig.h" +#include "WUPSConfigCategory.h" +#include "WUPSConfigItem.h" +#include + +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 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 The popped WUPSConfigCategory or nullptr if not found. + */ + std::unique_ptr 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 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 \ No newline at end of file diff --git a/source/config/WUPSConfigAPIDeprecated.cpp b/source/config/WUPSConfigAPIDeprecated.cpp new file mode 100644 index 0000000..a0efed7 --- /dev/null +++ b/source/config/WUPSConfigAPIDeprecated.cpp @@ -0,0 +1,231 @@ +#include "WUPSConfig.h" +#include "WUPSConfigAPI.h" +#include +#include + +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); \ No newline at end of file diff --git a/source/config/WUPSConfigCategory.h b/source/config/WUPSConfigCategory.h index 85117da..9aa586d 100644 --- a/source/config/WUPSConfigCategory.h +++ b/source/config/WUPSConfigCategory.h @@ -1,73 +1,95 @@ /**************************************************************************** - * Copyright (C) 2018-2021 Maschell - * - * 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 . - ****************************************************************************/ +* Copyright (C) 2018-2021 Maschell +* +* 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 . +****************************************************************************/ #pragma once #include "WUPSConfigItem.h" +#include #include #include #include -class WUPSConfigCategory { -public: - explicit WUPSConfigCategory(std::string_view name) { - this->name = name; - } - - ~WUPSConfigCategory() { - for (const auto &element : items) { - delete element; +namespace WUPSConfigAPIBackend { + class WUPSConfigCategory { + public: + explicit WUPSConfigCategory(std::string_view name) : mName(name) { } - } - /** - \return Returns the name of this WUPSConfigCategory + virtual ~WUPSConfigCategory() = default; + + /** + \return Returns the name of this WUPSConfigCategory **/ - [[nodiscard]] const std::string &getName() const { - return this->name; - } + [[nodiscard]] const std::string &getName() const { + return mName; + } - /** - \brief Adds a given WUPSConfigItem to this WUPSConfigCategory. - The item will be added to the end of the list. - This class holds responsibility for deleting the created instance. + /** + \brief Adds a given WUPSConfigItem to this WUPSConfigCategory. + The item will be added to the end of the list. + This class holds responsibility for deleting the created instance. - \param item: The item that will be added to this config. + \param item: The item 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 WUPSConfigItem instance. + \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 WUPSConfigItem instance. + **/ + [[nodiscard]] bool addItem(std::unique_ptr &item) { + if (item != nullptr) { + mItems.push_back(std::move(item)); + return true; + } + return false; + } + + /** + \return Returns a vector with all items. + **/ + [[nodiscard]] const std::vector> &getItems() const { + return mItems; + } + + /** + \return Returns a vector with all categories. **/ - [[nodiscard]] bool addItem(WUPSConfigItem *item) { - if (item != nullptr) { - items.push_back(item); + [[nodiscard]] const std::vector> &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 &category) { + mCategories.push_back(std::move(category)); return true; } - return false; - } - /** - \return Returns a vector with all items. - **/ - [[nodiscard]] const std::vector &getItems() const { - return this->items; - } - -private: - std::string name; - std::vector items{}; -}; + private: + std::string mName; + std::vector> mItems; + std::vector> mCategories; + }; +} // namespace WUPSConfigAPIBackend \ No newline at end of file diff --git a/source/config/WUPSConfigCategoryExport.cpp b/source/config/WUPSConfigCategoryExport.cpp deleted file mode 100644 index 30bf2c6..0000000 --- a/source/config/WUPSConfigCategoryExport.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "../utils/logger.h" -#include "WUPSConfigCategory.h" -#include -#include - -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(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(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(handle); - auto *item = reinterpret_cast(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(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(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);*/ \ No newline at end of file diff --git a/source/config/WUPSConfigExport.cpp b/source/config/WUPSConfigExport.cpp deleted file mode 100644 index a009e6c..0000000 --- a/source/config/WUPSConfigExport.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "../utils/logger.h" -#include "WUPSConfig.h" -#include -#include - -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(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(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(handle); - auto res = config->addCategory(std::string(categoryName)); - if (res.has_value()) { - if (out != nullptr) { - *out = reinterpret_cast(res.value()); - } else { - return -3; - } - return 0; - } - return -2; -} - -int32_t WUPSConfig_AddCategory(WUPSConfigHandle handle, WUPSConfigCategoryHandle category) { - auto *config = reinterpret_cast(handle); - auto res = config->addCategory(reinterpret_cast(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(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(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);*/ \ No newline at end of file diff --git a/source/config/WUPSConfigItem.h b/source/config/WUPSConfigItem.h index 8dadb2f..f10e78a 100644 --- a/source/config/WUPSConfigItem.h +++ b/source/config/WUPSConfigItem.h @@ -18,170 +18,54 @@ #pragma once #include +#include #include #include "utils/StringTools.h" #include "utils/logger.h" #include -class WUPSConfigItem { -public: - /** - Sets the display name of this WUPSConfigItem - This is the value which will be shown in the configuration menu. - **/ - virtual void setDisplayName(std::string_view _displayName) { - this->displayName = _displayName; - } - - /** - \return Returns the display name of this WUPSConfigItem - **/ - virtual const std::string &getDisplayName() { - return this->displayName; - } - - /** - Sets the config ID name of this WUPSConfigItem. - 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; - } - - /** - \return Returns the configID of this WUPSConfigItem. - **/ - [[nodiscard]] virtual const std::string &getConfigID() const { - return this->configID; - } - - /** - 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); - } +namespace WUPSConfigAPIBackend { + class WUPSConfigItem { + public: + explicit WUPSConfigItem(std::string displayName) : mDisplayName(std::move(displayName)) { } - DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED"); - return "NOT_IMPLEMENTED"; - } + virtual ~WUPSConfigItem() = default; - /** - Returns a string that displays the current value when selected. - This string is shown next to the display name when the cursor IS on this item - **/ - [[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); - } + [[nodiscard]] virtual std::string getCurrentValueDisplay() const = 0; + + [[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; } - 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; + virtual void setConfigId(const std::string &) {} + + virtual const std::string &getConfigId() { + return mStubConfigId; } - 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; + void setDisplayName(std::string displayName) { + mDisplayName = std::move(displayName); } - 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; - } + virtual void onInput(WUPSConfigSimplePadData) const {} - /** - Restores the default value - **/ - void restoreDefault() { - if (this->callbacks.restoreDefault != nullptr) { - this->callbacks.restoreDefault(context); - return; - } - DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED"); - } + virtual void onInputEx(WUPSConfigComplexPadData) const {} - /** - 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"); + protected: + std::string mDisplayName; + std::string mStubConfigId; }; - -private: - void *context; - std::string displayName; - std::string configID; - std::string defaultValue; - WUPSConfigCallbacks_t callbacks{}; -}; +} // namespace WUPSConfigAPIBackend diff --git a/source/config/WUPSConfigItemExport.cpp b/source/config/WUPSConfigItemExport.cpp deleted file mode 100644 index 62e6fcf..0000000 --- a/source/config/WUPSConfigItemExport.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "WUPSConfigItem.h" -#include -#include - -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(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(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(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(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(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); \ No newline at end of file diff --git a/source/config/WUPSConfigItemV1.cpp b/source/config/WUPSConfigItemV1.cpp new file mode 100644 index 0000000..0d64f2f --- /dev/null +++ b/source/config/WUPSConfigItemV1.cpp @@ -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 \ No newline at end of file diff --git a/source/config/WUPSConfigItemV1.h b/source/config/WUPSConfigItemV1.h new file mode 100644 index 0000000..49d5015 --- /dev/null +++ b/source/config/WUPSConfigItemV1.h @@ -0,0 +1,39 @@ +#pragma once +#include "WUPSConfigItem.h" +#include +#include + +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 \ No newline at end of file diff --git a/source/config/WUPSConfigItemV2.cpp b/source/config/WUPSConfigItemV2.cpp new file mode 100644 index 0000000..a45b1dd --- /dev/null +++ b/source/config/WUPSConfigItemV2.cpp @@ -0,0 +1,93 @@ +#include "WUPSConfigItemV2.h" +#include +#include +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 \ No newline at end of file diff --git a/source/config/WUPSConfigItemV2.h b/source/config/WUPSConfigItemV2.h new file mode 100644 index 0000000..46f0d01 --- /dev/null +++ b/source/config/WUPSConfigItemV2.h @@ -0,0 +1,35 @@ +#pragma once +#include "WUPSConfigItem.h" +#include +#include + +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 \ No newline at end of file diff --git a/source/globals.h b/source/globals.h index 5e8012b..dae304c 100644 --- a/source/globals.h +++ b/source/globals.h @@ -1,6 +1,6 @@ #pragma once #include "plugin/PluginContainer.h" -#include "utils/ConfigUtils.h" +#include "utils/config/ConfigUtils.h" #include "version.h" #include #include diff --git a/source/hooks.cpp b/source/hooks.cpp index 82f52ec..49d0f8b 100644 --- a/source/hooks.cpp +++ b/source/hooks.cpp @@ -20,8 +20,8 @@ static const char **hook_names = (const char *[]){ "WUPS_LOADER_HOOK_INIT_WRAPPER", "WUPS_LOADER_HOOK_FINI_WRAPPER", - "WUPS_LOADER_HOOK_GET_CONFIG", - "WUPS_LOADER_HOOK_CONFIG_CLOSED", + "WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED", + "WUPS_LOADER_HOOK_CONFIG_CLOSED_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_APPLICATION_REQUESTS_EXIT", "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> &plugins, wups_loader_hook_type_t 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_INIT_WRAPPER: case WUPS_LOADER_HOOK_FINI_WRAPPER: - case WUPS_LOADER_HOOK_GET_CONFIG: - case WUPS_LOADER_HOOK_CONFIG_CLOSED: + case WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED: + case WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED: case WUPS_LOADER_HOOK_INIT_PLUGIN: case WUPS_LOADER_HOOK_DEINIT_PLUGIN: case WUPS_LOADER_HOOK_APPLICATION_STARTS: @@ -75,7 +76,7 @@ void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type) break; case WUPS_LOADER_HOOK_INIT_STORAGE: 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{}; args.open_storage_ptr = &WUPSStorageDeprecated::StorageUtils::OpenStorage; 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 auto res = ((WUPSStorageError(*)(wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args); // clang-format on - if (res == WUPS_STORAGE_ERROR_INVALID_VERSION) { + if (res != WUPS_STORAGE_ERROR_SUCCESS) { // 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; } diff --git a/source/plugin/PluginConfigData.h b/source/plugin/PluginConfigData.h new file mode 100644 index 0000000..ae01464 --- /dev/null +++ b/source/plugin/PluginConfigData.h @@ -0,0 +1,56 @@ +#pragma once + +#include "config/WUPSConfigAPI.h" +#include +#include +#include + +class PluginConfigData { +public: + PluginConfigData(std::string_view name, + WUPSConfigAPI_MenuOpenedCallback openedCallback, + WUPSConfigAPI_MenuClosedCallback closedCallback) : mName(name), + mOpenedCallback(openedCallback), + mClosedCallback(closedCallback) { + } + + std::optional 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 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; +}; diff --git a/source/plugin/PluginContainer.h b/source/plugin/PluginContainer.h index 4945403..71c7f4a 100644 --- a/source/plugin/PluginContainer.h +++ b/source/plugin/PluginContainer.h @@ -17,11 +17,13 @@ #pragma once +#include "PluginConfigData.h" #include "PluginData.h" #include "PluginInformation.h" #include "PluginMetaInformation.h" #include #include +#include class PluginContainer { public: @@ -43,12 +45,21 @@ public: return mPluginData; } - uint32_t getHandle() { + uint32_t getHandle() const { return (uint32_t) this; } + [[nodiscard]] const std::optional &getConfigData() const { + return mPluginConfigData; + } + + void setConfigData(const PluginConfigData &pluginConfigData) { + mPluginConfigData = pluginConfigData; + } + private: const std::unique_ptr mMetaInformation; const std::unique_ptr mPluginInformation; const std::shared_ptr mPluginData; + std::optional mPluginConfigData; }; diff --git a/source/plugin/PluginMetaInformationFactory.cpp b/source/plugin/PluginMetaInformationFactory.cpp index 52c0ca9..22e774a 100644 --- a/source/plugin/PluginMetaInformationFactory.cpp +++ b/source/plugin/PluginMetaInformationFactory.cpp @@ -105,8 +105,8 @@ std::unique_ptr PluginMetaInformationFactory::loadPlugin( } else if (key == "wups") { if (value == "0.7.1") { pluginInfo->setWUPSVersion(0, 7, 1); - } else if (value == "0.7.2") { - pluginInfo->setWUPSVersion(0, 7, 2); + } else if (value == "0.8.0") { + pluginInfo->setWUPSVersion(0, 8, 0); } else { error = PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION; DEBUG_FUNCTION_LINE_ERR("Warning: Ignoring plugin - Unsupported WUPS version: %s.", value.c_str()); diff --git a/source/utils/ConfigUtils.cpp b/source/utils/ConfigUtils.cpp deleted file mode 100644 index 3cceec1..0000000 --- a/source/utils/ConfigUtils.cpp +++ /dev/null @@ -1,762 +0,0 @@ -#include "ConfigUtils.h" - -#include "../config/WUPSConfig.h" -#include "../globals.h" -#include "DrawUtils.h" -#include "hooks.h" -#include "logger.h" - -#include -#include -#include -#include -#include -#include -#include - -#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 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(((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 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(gStoredTVBuffer.mode), - gStoredTVBuffer.surface_format, gStoredTVBuffer.buffering_mode); - } - - if (gStoredDRCBuffer.buffer != nullptr) { - GX2SetDRCBuffer(gStoredDRCBuffer.buffer, gStoredDRCBuffer.buffer_size, static_cast(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(); -} diff --git a/source/utils/ConfigUtils.h b/source/utils/ConfigUtils.h deleted file mode 100644 index a15d62c..0000000 --- a/source/utils/ConfigUtils.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -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); -}; diff --git a/source/utils/config/CategoryRenderer.cpp b/source/utils/config/CategoryRenderer.cpp new file mode 100644 index 0000000..2aecd77 --- /dev/null +++ b/source/utils/config/CategoryRenderer.cpp @@ -0,0 +1,221 @@ +#include "CategoryRenderer.h" +#include "ConfigDefines.h" +#include "config/WUPSConfigCategory.h" +#include "utils/input/Input.h" +#include "utils/utils.h" +#include +#include + +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(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(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(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(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); +} diff --git a/source/utils/config/CategoryRenderer.h b/source/utils/config/CategoryRenderer.h new file mode 100644 index 0000000..55f4257 --- /dev/null +++ b/source/utils/config/CategoryRenderer.h @@ -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 + +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 mSubCategoryRenderer = {}; + const GeneralConfigInformation *mInfo = {}; + const WUPSConfigAPIBackend::WUPSConfigCategory *mCat = {}; + + std::vector> mItemRenderer = {}; + bool mIsItemMovementAllowed = true; + bool mFirstFrame = true; + bool mIsRoot = false; +}; diff --git a/source/utils/config/ConfigDefines.h b/source/utils/config/ConfigDefines.h new file mode 100644 index 0000000..79482c6 --- /dev/null +++ b/source/utils/config/ConfigDefines.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +#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, +}; \ No newline at end of file diff --git a/source/utils/config/ConfigDisplayItem.h b/source/utils/config/ConfigDisplayItem.h new file mode 100644 index 0000000..c8c0da3 --- /dev/null +++ b/source/utils/config/ConfigDisplayItem.h @@ -0,0 +1,26 @@ +#pragma once +#include "config/WUPSConfig.h" +#include + +struct GeneralConfigInformation { + std::string name; + std::string author; + std::string version; +}; + +class ConfigDisplayItem { +public: + ConfigDisplayItem(GeneralConfigInformation &info, std::unique_ptr 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 mConfig; + GeneralConfigInformation mInfo; +}; \ No newline at end of file diff --git a/source/utils/config/ConfigRenderer.cpp b/source/utils/config/ConfigRenderer.cpp new file mode 100644 index 0000000..742c104 --- /dev/null +++ b/source/utils/config/ConfigRenderer.cpp @@ -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(&(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> &categories) { + for (const auto &cat : categories) { + if (!cat->getCategories().empty()) { + CallOnCloseCallback(info, cat->getCategories()); + } + for (const auto &item : cat->getItems()) { + item->onCloseCallback(); + } + } +} diff --git a/source/utils/config/ConfigRenderer.h b/source/utils/config/ConfigRenderer.h new file mode 100644 index 0000000..5a0f27d --- /dev/null +++ b/source/utils/config/ConfigRenderer.h @@ -0,0 +1,42 @@ +#pragma once +#include "../DrawUtils.h" +#include "../input/Input.h" +#include "../logger.h" +#include "CategoryRenderer.h" +#include "globals.h" +#include +#include + +class ConfigRenderer { + +public: + explicit ConfigRenderer(std::vector &&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 mConfigs; + std::unique_ptr 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> &categories); +}; diff --git a/source/utils/config/ConfigRendererItem.h b/source/utils/config/ConfigRendererItem.h new file mode 100644 index 0000000..6529b37 --- /dev/null +++ b/source/utils/config/ConfigRendererItem.h @@ -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; +}; \ No newline at end of file diff --git a/source/utils/config/ConfigRendererItemCategory.h b/source/utils/config/ConfigRendererItemCategory.h new file mode 100644 index 0000000..3726cf4 --- /dev/null +++ b/source/utils/config/ConfigRendererItemCategory.h @@ -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; +}; \ No newline at end of file diff --git a/source/utils/config/ConfigRendererItemGeneric.h b/source/utils/config/ConfigRendererItemGeneric.h new file mode 100644 index 0000000..64e5d06 --- /dev/null +++ b/source/utils/config/ConfigRendererItemGeneric.h @@ -0,0 +1,36 @@ +#pragma once +#include "../DrawUtils.h" +#include "ConfigDefines.h" +#include + +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; + } +}; \ No newline at end of file diff --git a/source/utils/config/ConfigUtils.cpp b/source/utils/config/ConfigUtils.cpp new file mode 100644 index 0000000..21ff8d1 --- /dev/null +++ b/source/utils/config/ConfigUtils.cpp @@ -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 +#include +#include +#include +#include + +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 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 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("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(gStoredTVBuffer.mode), + gStoredTVBuffer.surface_format, gStoredTVBuffer.buffering_mode); + } + + if (gStoredDRCBuffer.buffer != nullptr) { + GX2SetDRCBuffer(gStoredDRCBuffer.buffer, gStoredDRCBuffer.buffer_size, static_cast(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(); +} diff --git a/source/utils/config/ConfigUtils.h b/source/utils/config/ConfigUtils.h new file mode 100644 index 0000000..6be5082 --- /dev/null +++ b/source/utils/config/ConfigUtils.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ConfigDefines.h" +#include "ConfigDisplayItem.h" +#include "config/WUPSConfig.h" + +#include "utils/input/Input.h" +#include +#include +#include + +#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); +}; diff --git a/source/utils/input/CombinedInput.h b/source/utils/input/CombinedInput.h new file mode 100644 index 0000000..adcfe77 --- /dev/null +++ b/source/utils/input/CombinedInput.h @@ -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 = {}; + } +}; \ No newline at end of file diff --git a/source/utils/input/Input.h b/source/utils/input/Input.h new file mode 100644 index 0000000..6b63934 --- /dev/null +++ b/source/utils/input/Input.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +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{}; +}; diff --git a/source/utils/input/VPADInput.h b/source/utils/input/VPADInput.h new file mode 100644 index 0000000..dc5dec9 --- /dev/null +++ b/source/utils/input/VPADInput.h @@ -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 . + ****************************************************************************/ + +#include "Input.h" +#include + +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 = {}; +}; \ No newline at end of file diff --git a/source/utils/input/WPADInput.h b/source/utils/input/WPADInput.h new file mode 100644 index 0000000..e509ca9 --- /dev/null +++ b/source/utils/input/WPADInput.h @@ -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 . + ****************************************************************************/ + +#include "Input.h" +#include +#include + +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; +}; \ No newline at end of file diff --git a/source/utils/logger.h b/source/utils/logger.h index 1b9f89b..4bf7101 100644 --- a/source/utils/logger.h +++ b/source/utils/logger.h @@ -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_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); diff --git a/source/utils/utils.h b/source/utils/utils.h index eaf0b5c..07fa875 100644 --- a/source/utils/utils.h +++ b/source/utils/utils.h @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -91,12 +92,44 @@ remove_first_if(Container &container, Predicate pred) { return false; } +template +typename std::enable_if>::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 bool remove_locked_first_if(std::mutex &mutex, Container &container, Predicate pred) { std::lock_guard lock(mutex); return remove_first_if(container, pred); } +template +T pop_locked_first_if(std::mutex &mutex, std::vector &container, Predicate pred) { + std::lock_guard 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(); OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr);