diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b12eb1c..57de4c8 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -30,6 +30,7 @@ jobs: - name: clang-format run: | docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src + docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cpp/src docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 build-examples: runs-on: ubuntu-22.04 @@ -40,8 +41,10 @@ jobs: run: | docker build . -f Dockerfile.buildexamples -t builder cd ./plugins/example_plugin - docker run --rm -v ${PWD}:/project builder make - cd ../storage_test_plugin + docker run --rm -v ${PWD}:/project builder make + cd ../example_plugin_cpp + docker run --rm -v ${PWD}:/project builder make + cd ../storage_test_plugin docker run --rm -v ${PWD}:/project builder make - uses: actions/upload-artifact@master with: diff --git a/.github/workflows/push_image.yml b/.github/workflows/push_image.yml index f72a249..adb1b00 100644 --- a/.github/workflows/push_image.yml +++ b/.github/workflows/push_image.yml @@ -33,6 +33,8 @@ jobs: - name: clang-format run: | docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 + docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src + docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cpp/src build-examples: runs-on: ubuntu-22.04 needs: clang-format-examples @@ -42,8 +44,10 @@ jobs: run: | docker build . -f Dockerfile.buildexamples -t builder cd ./plugins/example_plugin - docker run --rm -v ${PWD}:/project builder make - cd ../storage_test_plugin + docker run --rm -v ${PWD}:/project builder make + cd ./plugins/example_plugin_cpp + docker run --rm -v ${PWD}:/project builder make + cd ../storage_test_plugin docker run --rm -v ${PWD}:/project builder make build-and-push-image: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index a47f768..7f694c6 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,8 @@ TOPDIR ?= $(CURDIR) include $(TOPDIR)/share/wups_rules export WUPS_MAJOR := 0 -export WUPS_MINOR := 7 -export WUPS_PATCH := 2 +export WUPS_MINOR := 8 +export WUPS_PATCH := 0 VERSION := $(WUPS_MAJOR).$(WUPS_MINOR).$(WUPS_PATCH) @@ -23,15 +23,14 @@ INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -CFLAGS := -g -Wall -Werror -save-temps \ +CFLAGS := -g -O2 -Wall -Werror -save-temps \ -ffunction-sections -fdata-sections \ - -fno-exceptions -fno-rtti \ $(MACHDEP) \ $(BUILD_CFLAGS) CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ -CXXFLAGS := $(CFLAGS) -std=gnu++20 +CXXFLAGS := $(CFLAGS) -std=c++20 ASFLAGS := -g $(MACHDEP) diff --git a/README.MD b/README.MD index 171ba08..69f9b5c 100644 --- a/README.MD +++ b/README.MD @@ -28,4 +28,4 @@ It's highly recommended to pin the version to the **latest date** instead of usi ## Format the code via docker -`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./include ./libraries ./plugins/example_plugin/src ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 -i` +`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./include ./libraries ./plugins/example_plugin/src ./plugins/example_plugin_cpp/src ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 -i` diff --git a/include/wups.h b/include/wups.h index 6e7f493..a3d968a 100644 --- a/include/wups.h +++ b/include/wups.h @@ -32,12 +32,4 @@ #include "wups/hooks.h" #include "wups/meta.h" #include "wups/storage.h" -#ifdef DEBUG -#include -#endif - -#ifdef DEBUG -#define WUPS_DEBUG_REPORT(fmt, ...) OSReport(fmt, ##__VA_ARGS__) -#else -#define WUPS_DEBUG_REPORT(fmt, ...) -#endif +#include "wups/wups_debug.h" diff --git a/include/wups/config.h b/include/wups/config.h index 9491d36..d7e6a85 100644 --- a/include/wups/config.h +++ b/include/wups/config.h @@ -17,26 +17,54 @@ #pragma once +#include #include +#include -#define WUPS_CONFIG_BUTTON_NONE 0 -#define WUPS_CONFIG_BUTTON_LEFT (1 << 0) -#define WUPS_CONFIG_BUTTON_RIGHT (1 << 1) -#define WUPS_CONFIG_BUTTON_UP (1 << 2) -#define WUPS_CONFIG_BUTTON_DOWN (1 << 3) -#define WUPS_CONFIG_BUTTON_A (1 << 4) -#define WUPS_CONFIG_BUTTON_B (1 << 5) -#define WUPS_CONFIG_BUTTON_ZL (1 << 6) -#define WUPS_CONFIG_BUTTON_ZR (1 << 7) -#define WUPS_CONFIG_BUTTON_L (1 << 8) -#define WUPS_CONFIG_BUTTON_R (1 << 9) -#define WUPS_CONFIG_BUTTON_X (1 << 10) -#define WUPS_CONFIG_BUTTON_Y (1 << 11) -#define WUPS_CONFIG_BUTTON_STICK_L (1 << 12) -#define WUPS_CONFIG_BUTTON_STICK_R (1 << 13) -#define WUPS_CONFIG_BUTTON_PLUS (1 << 14) -#define WUPS_CONFIG_BUTTON_MINUS (1 << 15) -typedef int32_t WUPSConfigButtons; +typedef uint32_t WUPSConfigButtons; + +typedef enum WUPS_CONFIG_SIMPLE_INPUT { + WUPS_CONFIG_BUTTON_NONE = 0, + WUPS_CONFIG_BUTTON_LEFT = (1 << 0), + WUPS_CONFIG_BUTTON_RIGHT = (1 << 1), + WUPS_CONFIG_BUTTON_UP = (1 << 2), + WUPS_CONFIG_BUTTON_DOWN = (1 << 3), + WUPS_CONFIG_BUTTON_A = (1 << 4), + WUPS_CONFIG_BUTTON_B = (1 << 5), + WUPS_CONFIG_BUTTON_ZL = (1 << 6), + WUPS_CONFIG_BUTTON_ZR = (1 << 7), + WUPS_CONFIG_BUTTON_L = (1 << 8), + WUPS_CONFIG_BUTTON_R = (1 << 9), + WUPS_CONFIG_BUTTON_X = (1 << 10), + WUPS_CONFIG_BUTTON_Y = (1 << 11), + WUPS_CONFIG_BUTTON_STICK_L = (1 << 12), + WUPS_CONFIG_BUTTON_STICK_R = (1 << 13), + WUPS_CONFIG_BUTTON_PLUS = (1 << 14), + WUPS_CONFIG_BUTTON_MINUS = (1 << 15), +} WUPS_CONFIG_SIMPLE_INPUT; + +typedef struct { + WUPS_CONFIG_SIMPLE_INPUT buttons_h; + WUPS_CONFIG_SIMPLE_INPUT buttons_d; + WUPS_CONFIG_SIMPLE_INPUT buttons_r; + bool validPointer; + bool touched; + float pointerAngle; + int32_t x; + int32_t y; +} WUPSConfigSimplePadData; + +typedef struct { + struct { + VPADReadError vpadError; + VPADTouchData tpCalib; + VPADStatus data; + } vpad; + struct { + KPADError kpadError[4]; + KPADStatus data[4]; + } kpad; +} WUPSConfigComplexPadData; typedef enum WUPSConfigAPIStatus { WUPSCONFIG_API_RESULT_SUCCESS = 0, @@ -76,17 +104,111 @@ typedef struct { void (*onDelete)(void *context); } WUPSConfigAPIItemCallbacksV1; +typedef struct { + /** + * Set the string which is displayed for an item + * @param context The context which has been passed to the item during creating + * @param out_buf Buffer where the string should be written to + * @param out_size Size of out_buf + * + * \result non-zero result indicates an error. + */ + int32_t (*getCurrentValueDisplay)(void *context, char *out_buf, int32_t out_size); + + /** + * Set the string which is displayed for an item when the cursor is on is item + * @param context The context which has been passed to the item during creating + * @param out_buf Buffer where the string should be written to + * @param out_size Size of out_buf + * + * \result non-zero result indicates an error. + */ + int32_t (*getCurrentValueSelectedDisplay)(void *context, char *out_buf, int32_t out_size); + + /** + * Called when the cursor enters or leaves this item + * @param context The context which has been passed to the item during creating + * @param isSelected True if the item cursor is now pointing to this item, \n + * False if it's not pointing to this item anymore + */ + void (*onSelected)(void *context, bool isSelected); + + /** + * Called when the current value of this item should be set to the default value + * @param context The context which has been passed to the item during creating + */ + void (*restoreDefault)(void *context); + + /** + * Determines if movement to different item is allowed. + * @param context The context which has been passed to the item during creating + * \return True if it should be not possible to select a different item or exit the current category \n + * False if it should be possible to select a different item or exit the current category + */ + bool (*isMovementAllowed)(void *context); + + /** + * Called when the config menu has been closed + * @param context The context which has been passed to the item during creating + */ + void (*onCloseCallback)(void *context); + + /** + * This function is called on each frame and provides information about the current inputs. + * The inputs are simplified and all 5 possible controller inputs (from Gamepad and up to 4 Wiimotes/Pro Controller) + * are unified in this single unified struct. + * + * @param context The context which has been passed to the item during creating + * @param input Simplified version of the current inputs + * + * \note To get the full input for all possible controllers see "onInputEx" + * + * @see onInputEx + */ + void (*onInput)(void *context, WUPSConfigSimplePadData input); + + /** + * This function is called on each frame and provides information about the current inputs. + * The structs contains information for current individual Gampepad and Wiimote/Pro Contoller inputs. + * + * @param context The context which has been passed to the item during creating + * @param input current input for all possibles controller + * + * \note To get a simplified input callback that combines all controller into a single struct see "onInput" + * + * @see onInput + */ + void (*onInputEx)(void *context, WUPSConfigComplexPadData input); + + /** + * This function is called when the item is about to be deleted. It can be used to free any alloated memory. + * + * @param context The context which has been passed to the item during creating + */ + void (*onDelete)(void *context); +} WUPSConfigAPIItemCallbacksV2; + #define WUPS_API_ITEM_OPTION_VERSION_V1 1 +#define WUPS_API_ITEM_OPTION_VERSION_V2 2 + typedef struct WUPSConfigAPIItemOptionsV1 { + const char *configId; const char *displayName; void *context; WUPSConfigAPIItemCallbacksV1 callbacks; } WUPSConfigAPIItemOptionsV1; +typedef struct WUPSConfigAPIItemOptionsV2 { + const char *displayName; + void *context; + WUPSConfigAPIItemCallbacksV2 callbacks; +} WUPSConfigAPIItemOptionsV2; + typedef struct WUPSConfigAPICreateItemOptions { uint32_t version; union { WUPSConfigAPIItemOptionsV1 v1; + WUPSConfigAPIItemOptionsV2 v2; } data; } WUPSConfigAPICreateItemOptions; diff --git a/include/wups/config/WUPSConfigCategory.h b/include/wups/config/WUPSConfigCategory.h new file mode 100644 index 0000000..22d3db1 --- /dev/null +++ b/include/wups/config/WUPSConfigCategory.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __cplusplus + +#include "WUPSConfigItem.h" +#include +#include +#include + +class WUPSConfigCategory { +public: + explicit WUPSConfigCategory(WUPSConfigCategoryHandle handle) noexcept; + virtual ~WUPSConfigCategory(); + + WUPSConfigCategory(const WUPSConfigCategory &) = delete; + + WUPSConfigCategory(WUPSConfigCategory &&src) noexcept : mHandle(src.mHandle) { + src.mHandle = {}; + } + + WUPSConfigCategory &operator=(WUPSConfigCategory &&src) noexcept { + if (this != &src) { + src.mHandle = {}; + } + return *this; + } + + static std::optional Create(std::string_view name, WUPSConfigAPIStatus &error) noexcept; + + static WUPSConfigCategory Create(std::string_view name); + + bool add(WUPSConfigCategory &&cat, WUPSConfigAPIStatus &error) noexcept; + + void add(WUPSConfigCategory &&cat); + + bool add(WUPSConfigItem &&item, WUPSConfigAPIStatus &error) noexcept; + + void add(WUPSConfigItem &&item); + + [[nodiscard]] const WUPSConfigCategoryHandle &getHandle() const { + return mHandle; + } + + void release() { + mHandle = {}; + } + +private: + WUPSConfigCategoryHandle mHandle = {}; +}; +#endif \ No newline at end of file diff --git a/include/wups/config/WUPSConfigItem.h b/include/wups/config/WUPSConfigItem.h new file mode 100644 index 0000000..c51ba3d --- /dev/null +++ b/include/wups/config/WUPSConfigItem.h @@ -0,0 +1,41 @@ +#pragma once + +#ifdef __cplusplus + +#include "wups/config.h" +#include + +class WUPSConfigItem { +protected: + explicit WUPSConfigItem(WUPSConfigItemHandle itemHandle) : mHandle(itemHandle) { + } + +public: + virtual ~WUPSConfigItem(); + + WUPSConfigItem(const WUPSConfigItem &) = delete; + + WUPSConfigItem(WUPSConfigItem &&src) noexcept : mHandle(src.mHandle) { + src.mHandle = {}; + } + + WUPSConfigItem &operator=(WUPSConfigItem &&src) noexcept { + if (this != &src) { + src.mHandle = {}; + } + return *this; + } + + [[nodiscard]] const WUPSConfigItemHandle &getHandle() const { + return mHandle; + } + + void release() { + mHandle = {}; + } + +private: + WUPSConfigItemHandle mHandle = {}; +}; + +#endif diff --git a/include/wups/config/WUPSConfigItemBoolean.h b/include/wups/config/WUPSConfigItemBoolean.h index 877dd9d..942956a 100644 --- a/include/wups/config/WUPSConfigItemBoolean.h +++ b/include/wups/config/WUPSConfigItemBoolean.h @@ -1,25 +1,132 @@ -#include +#pragma once + +#include "WUPSConfigItem.h" +#include #ifdef __cplusplus extern "C" { #endif typedef struct ConfigItemBoolean { - char *configId; WUPSConfigItemHandle handle; + const char *identifier; bool defaultValue; + bool valueAtCreation; bool value; char trueValue[32]; char falseValue[32]; - void *callback; + void *valueChangedCallback; } ConfigItemBoolean; typedef void (*BooleanValueChangedCallback)(ConfigItemBoolean *, bool); -bool WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback); -bool WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback, const char *trueValue, - const char *falseValue); +WUPSConfigAPIStatus +WUPSConfigItemBoolean_CreateEx(const char *identifier, + const char *displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + const char *trueValue, + const char *falseValue, + WUPSConfigItemHandle *outHandle); + + +/** + * @brief Adds a boolean configuration item to the specified category. + * + * This function adds a boolean configuration item to the given category. The item is displayed with a specified display name. + * The default value and current value of the item are set to the provided values. A callback function is called whenever + * the value of the item changes. + * + * @param cat The handle of the category to add the item to. + * @param identifier Optional identifier for the item. Can be NULL. + * @param displayName The display name of the item. + * @param defaultValue The default value of the item. + * @param currentValue The current value of the item. + * @param callback A callback function that will be called when the config menu closes and the value of the item has been changed. + * @return True if the item was added successfully, false otherwise. + */ +WUPSConfigAPIStatus +WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat, + const char *identifier, + const char *displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback); + +/** + * @brief Adds a boolean configuration item to the specified category. + * + * This function adds a boolean configuration item to the given category. The item is displayed with a specified display name. + * The default value and current value of the item are set to the provided values. A callback function is called whenever + * the value of the item changes. + * + * @param cat The handle of the category to add the item to. + * @param identifier Optional identifier for the item. Can be NULL. + * @param displayName The display name of the item. + * @param defaultValue The default value of the item. + * @param currentValue The current value of the item. + * @param callback A callback function that will be called when the config menu closes and the value of the item has been changed. + * @param trueValue The string representation of the true value. + * @param falseValue The string representation of the false value. + * @return True if the item was successfully added to the category, false otherwise. + */ +WUPSConfigAPIStatus +WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat, + const char *identifier, + const char *displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + const char *trueValue, + const char *falseValue); #ifdef __cplusplus } +#endif + +#ifdef __cplusplus + +#include "WUPSConfigItem.h" +#include +#include +#include +#include + +class WUPSConfigItemBoolean : public WUPSConfigItem { +public: + static std::optional CreateEx(std::optional identifier, + std::string_view displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + std::string_view trueValue, + std::string_view falseValue, + WUPSConfigAPIStatus &err) noexcept; + + static WUPSConfigItemBoolean CreateEx(std::optional identifier, + std::string_view displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + std::string_view trueValue, + std::string_view falseValue); + + static std::optional Create(std::optional identifier, + std::string_view displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + WUPSConfigAPIStatus &err) noexcept; + + static WUPSConfigItemBoolean Create(std::optional identifier, + std::string_view displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback); + +private: + explicit WUPSConfigItemBoolean(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) { + } +}; #endif \ No newline at end of file diff --git a/include/wups/config/WUPSConfigItemIntegerRange.h b/include/wups/config/WUPSConfigItemIntegerRange.h index 1d610f3..51cced2 100644 --- a/include/wups/config/WUPSConfigItemIntegerRange.h +++ b/include/wups/config/WUPSConfigItemIntegerRange.h @@ -1,25 +1,92 @@ -#include +#pragma once + +#include #ifdef __cplusplus extern "C" { #endif typedef struct ConfigItemIntegerRange { - char *configId; WUPSConfigItemHandle handle; + const char *identifier; int defaultValue; int value; + int valueAtCreation; int minValue; int maxValue; - void *callback; + void *valueChangedCallback; } ConfigItemIntegerRange; typedef void (*IntegerRangeValueChangedCallback)(ConfigItemIntegerRange *, int32_t); -bool WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, - int32_t defaultValue, int32_t minValue, int32_t maxValue, - IntegerRangeValueChangedCallback callback); +WUPSConfigAPIStatus +WUPSConfigItemIntegerRange_Create(const char *identifier, + const char *displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback callback, + WUPSConfigItemHandle *outHandle); + +/** + * \brief Adds an integer range configuration item to a category. + * + * This function creates a new ConfigItemIntegerRange item and adds it to the specified category. + * The item represents an integer value within a specified range, and allows the user to modify the value. + * + * \param cat The category handle to which the item should be added. + * \param identifier Optional identifier for the item. Can be NULL. + * \param displayName The display name for the item. + * \param defaultValue The default value for the item. + * \param currentValue The current value for the item. + * \param minValue The minimum value allowed for the item. + * \param maxValue The maximum value allowed for the item. + * \param callback A callback function that will be called when the config menu closes and the value of the item has been changed. + * + * \return Returns true if the item was successfully added to the category, false otherwise. + * + * @note The defaultValue and currentValue must in the specified range. + */ +WUPSConfigAPIStatus +WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat, + const char *identifier, + const char *displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback callback); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif + + +#ifdef __cplusplus +#include "WUPSConfigItem.h" +#include +#include +#include +#include +#include + +class WUPSConfigItemIntegerRange : public WUPSConfigItem { + +public: + static std::optional Create( + std::optional identifier, + std::string_view displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback valuesChangedCallback, + WUPSConfigAPIStatus &err) noexcept; + + static WUPSConfigItemIntegerRange Create( + std::optional identifier, + std::string_view displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback valuesChangedCallback); + +private: + explicit WUPSConfigItemIntegerRange(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) { + } +}; +#endif diff --git a/include/wups/config/WUPSConfigItemMultipleValues.h b/include/wups/config/WUPSConfigItemMultipleValues.h index 76c5fc0..d0c2533 100644 --- a/include/wups/config/WUPSConfigItemMultipleValues.h +++ b/include/wups/config/WUPSConfigItemMultipleValues.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include @@ -8,24 +10,107 @@ extern "C" { typedef struct ConfigItemMultipleValuesPair { uint32_t value; - char *valueName; + const char *valueName; } ConfigItemMultipleValuesPair; typedef struct ConfigItemMultipleValues { - char *configId; WUPSConfigItemHandle handle; + const char *identifier; int32_t defaultValueIndex; int32_t valueIndex; - void *callback; + int32_t valueIndexAtCreation; ConfigItemMultipleValuesPair *values; int valueCount; + void *valueChangedCallback; } ConfigItemMultipleValues; typedef void (*MultipleValuesChangedCallback)(ConfigItemMultipleValues *, uint32_t); -bool WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, int defaultValueIndex, ConfigItemMultipleValuesPair *possibleValues, - int pairCount, MultipleValuesChangedCallback callback); + +WUPSConfigAPIStatus +WUPSConfigItemMultipleValues_Create(const char *identifier, const char *displayName, + int defaultValueIndex, int currentValueIndex, + ConfigItemMultipleValuesPair *possibleValues, int pairCount, + MultipleValuesChangedCallback callback, + WUPSConfigItemHandle *outHandle); + +/** + * @brief Add a multiple values configuration item to a category. + * + * This function adds a multiple values configuration item to a specified category. + * The item will be displayed in the configuration menu with the provided display name. + * + * @param cat The handle of the category where the item should be added. + * @param identifier The identifier of the item. It is used to uniquely identify the item. + * @param displayName The display name of the item. It will be shown in the configuration menu. + * @param defaultValueIndex The index of the default value in the array of possible values. + * @param currentValueIndex The index of the current value in the array of possible values. + * @param possibleValues An array of possible values for the item. + * @param pairCount The number of pairs (value and value name) in the possibleValues array. + * @param callback A callback function that will be called when the config menu closes and the value of the item has been changed. + * + * @return true if the item was successfully added to the category, false otherwise. + * + * @note The defaultValueIndex and currentValueIndex must be valid for the given pairCount. + */ +WUPSConfigAPIStatus +WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *identifier, const char *displayName, + int defaultValueIndex, int currentValueIndex, + ConfigItemMultipleValuesPair *possibleValues, int pairCount, + MultipleValuesChangedCallback callback); #ifdef __cplusplus } +#endif + + +#ifdef __cplusplus + +#include "WUPSConfigItem.h" +#include +#include +#include +#include +#include + +class WUPSConfigItemMultipleValues : public WUPSConfigItem { + +public: + struct ValuePair { + uint32_t value; + std::string_view name; + }; + + static std::optional CreateFromIndex(std::optional identifier, + std::string_view displayName, + int defaultValueIndex, int currentValueIndex, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback, + WUPSConfigAPIStatus &err) noexcept; + + static WUPSConfigItemMultipleValues CreateFromIndex(std::optional identifier, + std::string_view displayName, + int defaultValueIndex, int currentValueIndex, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback); + + static std::optional CreateFromValue( + std::optional identifier, + std::string_view displayName, + uint32_t defaultValue, uint32_t currentValue, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback, + WUPSConfigAPIStatus &err) noexcept; + + static WUPSConfigItemMultipleValues CreateFromValue( + std::optional identifier, + std::string_view displayName, + int32_t defaultValue, int32_t currentValue, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback); + +private: + explicit WUPSConfigItemMultipleValues(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) { + } +}; #endif \ No newline at end of file diff --git a/include/wups/config/WUPSConfigItemStub.h b/include/wups/config/WUPSConfigItemStub.h index a2141c1..ecff758 100644 --- a/include/wups/config/WUPSConfigItemStub.h +++ b/include/wups/config/WUPSConfigItemStub.h @@ -1,4 +1,6 @@ -#include +#pragma once + +#include #ifdef __cplusplus extern "C" { @@ -8,8 +10,39 @@ typedef struct ConfigItemStub { WUPSConfigItemHandle handle; } ConfigItemStub; -bool WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName); +WUPSConfigAPIStatus WUPSConfigItemStub_Create(const char *displayName, + WUPSConfigItemHandle *outHandle); + +/** + * @brief Adds a stub item to a category to display information + * + * @param cat The handle of the category to which the item should be added. + * @param displayName The display name of the item. + * @return true if the item was added successfully, false otherwise. + */ +WUPSConfigAPIStatus +WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *displayName); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif +#ifdef __cplusplus + +#include "WUPSConfigItem.h" +#include +#include +#include +#include + +class WUPSConfigItemStub : public WUPSConfigItem { +public: + static std::optional Create(std::string_view displayName, + WUPSConfigAPIStatus &err) noexcept; + + static WUPSConfigItemStub Create(std::string_view displayName); + +private: + explicit WUPSConfigItemStub(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) { + } +}; +#endif diff --git a/include/wups/config_api.h b/include/wups/config_api.h index 25ffba9..855b308 100644 --- a/include/wups/config_api.h +++ b/include/wups/config_api.h @@ -1,6 +1,8 @@ #pragma once #include "config.h" +#include "config/WUPSConfigCategory.h" +#include "config/WUPSConfigItem.h" #include #include @@ -11,14 +13,71 @@ extern "C" { typedef WUPSConfigAPICallbackStatus (*WUPSConfigAPI_MenuOpenedCallback)(WUPSConfigCategoryHandle root); typedef void (*WUPSConfigAPI_MenuClosedCallback)(); +/** + * @brief Initialize the WUPSConfigAPI with extended options. For internal use. + * @see WUPSConfigAPI_Init instead + */ WUPSConfigAPIStatus WUPSConfigAPI_InitEx(uint32_t pluginIdentifier, WUPSConfigAPIOptions, WUPSConfigAPI_MenuOpenedCallback, WUPSConfigAPI_MenuClosedCallback); +/** + * @brief Initializes the WUPSConfigAPI with the given options, opened callback, and closed callback. + * + * This function initializes the WUPSConfigAPI with the provided options, opened callback, and closed callback. + * If the initialization is successful, it returns WUPSCONFIG_API_RESULT_SUCCESS. Otherwise, it returns + * an appropriate error code indicating the reason for failure. + * + * @param optionsV1 The options for the WUPSConfigAPI. It contains the name of the plugin. + * @param openedCallback The callback function to be called when the menu is opened. + * @param closedCallback The callback function to be called when the menu is closed. + * + * @return WUPSConfigAPIStatus WUPSCONFIG_API_RESULT_SUCCESS if initialization is successful, + * otherwise an appropriate error code. + * - 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 + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The API has not been initialized. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported. + */ WUPSConfigAPIStatus WUPSConfigAPI_Init(WUPSConfigAPIOptionsV1 optionsV1, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback); +/** + * @brief Retrieves the version of the WUPSConfigAPI library. + * + * This function retrieves the version of the WUPSConfigAPI library and stores it in the provided output variable. + * + * @param[out] outVariable Pointer to a WUPSConfigAPIVersion variable to store the library version. + * @return Returns the status of the API call. + * - WUPSCONFIG_API_RESULT_SUCCESS: The version was retrieved successfully. + * - WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND: The module containing the WUPSConfigAPI_GetVersion function was not found. + * - WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT: The WUPSConfigAPI_GetVersion function was not found in the module. + */ WUPSConfigAPIStatus WUPSConfigAPI_GetVersion(WUPSConfigAPIVersion *outVariable); +/** + * @brief Create a new category with extended options. Internal use. + * @see WUPSConfigAPI_Category_Create + **/ WUPSConfigAPIStatus WUPSConfigAPI_Category_CreateEx(WUPSConfigAPICreateCategoryOptions options, WUPSConfigCategoryHandle *out); +/** + * @brief Create a new category. + * + * This function creates a new category using the given options. The options struct contains the category name. + * If the category creation is successful, the category handle is returned through the *out parameter. + * + * @param options The options for creating the category. + * @param[out] out A pointer to a WUPSConfigCategoryHandle variable that will receive the category handle. + * @return WUPSConfigAPIStatus The status of the category creation operation. + * - WUPSCONFIG_API_RESULT_SUCCESS: Success. + * - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: Invalid parameter, `out` or `name` is NULL. + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The API has not been initialized. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported. + * + * @note The function internally calls WUPSConfigAPI_Category_CreateEx(). + * @note The caller is responsible for deleting the WUPSConfigCategoryHandle instance unless it has been transferred to + * a different category + */ static inline WUPSConfigAPIStatus WUPSConfigAPI_Category_Create(WUPSConfigAPICreateCategoryOptionsV1 options, WUPSConfigCategoryHandle *out) { WUPSConfigAPICreateCategoryOptions optionsWrapper = { .version = WUPS_API_CATEGORY_OPTION_VERSION_V1, @@ -27,24 +86,126 @@ static inline WUPSConfigAPIStatus WUPSConfigAPI_Category_Create(WUPSConfigAPICre return WUPSConfigAPI_Category_CreateEx(optionsWrapper, out); } +/** + * @brief Destroys a WUPSConfigCategoryHandle. + * + * This function is used to destroy a WUPSConfigCategoryHandle object. + * + * @param handle The WUPSConfigCategoryHandle to be destroyed. + * @return WUPSConfigAPIStatus Returns the status of the API call: + * - WUPSCONFIG_API_RESULT_SUCCESS: The WUPSConfigCategoryHandle was successfully destroyed. + * - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The handle is nullptr. + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The API has not been initialized. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported. + * + * @note The caller is responsible for deleting the WUPSConfigCategoryHandle instance unless it has been transferred to + * a different category + */ WUPSConfigAPIStatus WUPSConfigAPI_Category_Destroy(WUPSConfigCategoryHandle handle); +/** + * @brief Adds a category to the parent category. + * + * This function is used to add a category to an existing parent category. + * + * @param parentHandle The handle to the parent category. + * @param categoryHandle The handle to the category to be added. + * + * @return WUPSConfigAPIStatus The status code indicating the result of the operation. + * Possible values are: + * - WUPSCONFIG_API_RESULT_SUCCESS: The category was added successfully. + * - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The parentHandle or categoryHandle is null. + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The library is not initialized. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported. + * - WUPSCONFIG_API_RESULT_UNKNOWN_ERROR: An unknown error occurred. + * + * @note On success the ownership of the category will be transferred to the Category and the categoryHandle WUPSConfigCategoryHandle + * will be invalid. + */ WUPSConfigAPIStatus WUPSConfigAPI_Category_AddCategory(WUPSConfigCategoryHandle parentHandle, WUPSConfigCategoryHandle categoryHandle); +/** + * @brief Adds an item to the given category. + * + * This function adds the specified item to the parent category. The parent + * category and item handles must be valid and non-zero. + * + * @param parentHandle The handle of the parent category. + * @param[out] itemHandle The handle of the item to be added. + * @return WUPSConfigAPIStatus The status code indicating the result 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 invalid. + * - 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. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The provided command is not supported. + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The library is not initialized properly. + * @see WUPSConfigCategoryHandle + * @see WUPSConfigItemHandle + * @see WUPSConfigAPIStatus + * + * @note On success the ownership of the item will be transferred to the Category and the itemHandle WUPSConfigItemHandle + * will be invalid. + */ WUPSConfigAPIStatus WUPSConfigAPI_Category_AddItem(WUPSConfigCategoryHandle parentHandle, WUPSConfigItemHandle itemHandle); +/** + * @brief Create a WUPSConfigAPI item with extended options. Internal use. + * @see WUPSConfigAPI_Item_Create + **/ WUPSConfigAPIStatus WUPSConfigAPI_Item_CreateEx(WUPSConfigAPICreateItemOptions options, WUPSConfigItemHandle *out); -static inline WUPSConfigAPIStatus WUPSConfigAPI_Item_Create(WUPSConfigAPIItemOptionsV1 options, WUPSConfigItemHandle *out) { +/** + * @brief Creates a new configuration item using the provided options. + * + * This function creates a new configuration item using the provided options. + * + * @param options The options for creating the configuration item. + * @param[out] out The handle to the created configuration item. + * @return The status of the API call. Possible values are: + * - 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. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The provided command is not supported. + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The library is not initialized properly. + * + * @note The caller is responsible for deleting the WUPSConfigItem instance unless it has been transferred to + * a category + */ +static inline WUPSConfigAPIStatus WUPSConfigAPI_Item_Create(WUPSConfigAPIItemOptionsV2 options, WUPSConfigItemHandle *out) { WUPSConfigAPICreateItemOptions itemOptions = { - .version = WUPS_API_ITEM_OPTION_VERSION_V1, - .data = {.v1 = options}, + .version = WUPS_API_ITEM_OPTION_VERSION_V2, + .data = {.v2 = options}, }; return WUPSConfigAPI_Item_CreateEx(itemOptions, out); } +/** + * @brief Destroy a WUPSConfigItemHandle. + * + * This function destroys a WUPSConfigItemHandle. It can only be called if the WUPSConfig API is initialized and the handle is valid. + * A item must not be destroyed if it's added to a WUPSConfigCategory + * + * @param handle The handle to be destroyed. + * @return WUPSConfigAPIStatus The status code indicating the result of the operation: + * - WUPSCONFIG_API_RESULT_SUCCESS: If the handle was successfully destroyed. + * - WUPSCONFIG_API_RESULT_NOT_FOUND: The WUPSConfigItem with the given handle was not found. + * - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: If the handle is invalid. + * - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: If the WUPSConfig API is not initialized. + * - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: If the destroy command is not supported by the API or the API version is too low. + */ WUPSConfigAPIStatus WUPSConfigAPI_Item_Destroy(WUPSConfigItemHandle handle); +/** + * @brief Converts a WUPSConfigAPIStatus value to a corresponding string representation. + * + * This function takes a WUPSConfigAPIStatus value and returns a string representation of the value. + * The string representation is determined based on the given status value using a switch statement. + * If the status value is not recognized, a default string "WUPSCONFIG_API_RESULT_UNKNOWN_ERROR" is returned. + * + * @param status The WUPSConfigAPIStatus value to convert to string. + * @return The string representation of the given WUPSConfigAPIStatus value. + */ const char *WUPSConfigAPI_GetStatusStr(WUPSConfigAPIStatus status); #ifdef __cplusplus diff --git a/include/wups/hooks.h b/include/wups/hooks.h index 68752df..8484254 100644 --- a/include/wups/hooks.h +++ b/include/wups/hooks.h @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ - #pragma once #include "common.h" diff --git a/include/wups/meta.h b/include/wups/meta.h index 3cd6bec..4f501d2 100644 --- a/include/wups/meta.h +++ b/include/wups/meta.h @@ -38,7 +38,7 @@ extern "C" { #endif -#define WUPS_VERSION_STR "0.7.2" +#define WUPS_VERSION_STR "0.8.0" #define WUPS_PLUGIN_NAME(__plugin_name) \ WUPS_META(name, __plugin_name); \ WUPS_META(wups, WUPS_VERSION_STR); \ diff --git a/include/wups/storage.h b/include/wups/storage.h index 996863a..587ea3d 100644 --- a/include/wups/storage.h +++ b/include/wups/storage.h @@ -102,9 +102,6 @@ WUPSStorageError WUPS_GetFloat(wups_storage_item parent, const char *key, float WUPSStorageError WUPS_GetDouble(wups_storage_item parent, const char *key, double *outValue); WUPSStorageError WUPS_GetBinary(wups_storage_item parent, const char *key, void *outData, uint32_t maxSize, uint32_t *outSize); -/** - * Return the size of a stored string or binary item. - */ WUPSStorageError WUPS_GetItemSize(wups_storage_item parent, const char *key, uint32_t *outSize); #ifdef __cplusplus diff --git a/include/wups/wups_debug.h b/include/wups/wups_debug.h new file mode 100644 index 0000000..c966fa8 --- /dev/null +++ b/include/wups/wups_debug.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef DEBUG +#include +#endif + +#ifdef DEBUG +#define WUPS_DEBUG_REPORT(fmt, ...) OSReport(fmt, ##__VA_ARGS__) +#else +#define WUPS_DEBUG_REPORT(fmt, ...) +#endif \ No newline at end of file diff --git a/libraries/libwups/WUPSConfigCategory.cpp b/libraries/libwups/WUPSConfigCategory.cpp new file mode 100644 index 0000000..896338a --- /dev/null +++ b/libraries/libwups/WUPSConfigCategory.cpp @@ -0,0 +1,67 @@ +#include +#include + +WUPSConfigCategory::WUPSConfigCategory(WUPSConfigCategoryHandle handle) noexcept : mHandle(handle) { +} + +WUPSConfigCategory::~WUPSConfigCategory() { + if (mHandle.handle != nullptr) { + WUPSConfigAPI_Category_Destroy(mHandle); + } +} + +std::optional WUPSConfigCategory::Create(std::string_view name, WUPSConfigAPIStatus &error) noexcept { + WUPSConfigCategoryHandle catHandle; + if ((error = WUPSConfigAPI_Category_Create({.name = name.data()}, &catHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return std::nullopt; + } + return WUPSConfigCategory(catHandle); +} + +WUPSConfigCategory WUPSConfigCategory::Create(std::string_view name) { + WUPSConfigAPIStatus error; + auto res = Create(name, error); + if (!res) { + throw std::runtime_error{std::string("Failed to create category: ").append(name)}; + } + return std::move(*res); +} + +bool WUPSConfigCategory::add(WUPSConfigCategory &&cat, WUPSConfigAPIStatus &error) noexcept { + if (mHandle.handle == nullptr || cat.getHandle().handle == nullptr) { + OSReport("mHandle %08X item %08X\n", mHandle.handle, cat.getHandle().handle); + return false; + } + if ((error = WUPSConfigAPI_Category_AddCategory(mHandle, cat.getHandle())) != WUPSCONFIG_API_RESULT_SUCCESS) { + return false; + } + cat.release(); + return true; +} + +void WUPSConfigCategory::add(WUPSConfigCategory &&cat) { + WUPSConfigAPIStatus err; + if (!add(std::move(cat), err)) { + throw std::runtime_error{"Failed to add category to category"}; + } +} + +bool WUPSConfigCategory::add(WUPSConfigItem &&item, WUPSConfigAPIStatus &error) noexcept { + if (mHandle.handle == nullptr || item.getHandle().handle == nullptr) { + OSReport("mHandle %08X item %08X\n", mHandle.handle, item.getHandle().handle); + error = WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; + return false; + } + if ((error = WUPSConfigAPI_Category_AddItem(mHandle, item.getHandle())) != WUPSCONFIG_API_RESULT_SUCCESS) { + return false; + } + item.release(); + return true; +} + +void WUPSConfigCategory::add(WUPSConfigItem &&item) { + WUPSConfigAPIStatus err; + if (!add(std::move(item), err)) { + throw std::runtime_error{"Failed to add item to category"}; + } +} \ No newline at end of file diff --git a/libraries/libwups/WUPSConfigItem.cpp b/libraries/libwups/WUPSConfigItem.cpp new file mode 100644 index 0000000..33272a5 --- /dev/null +++ b/libraries/libwups/WUPSConfigItem.cpp @@ -0,0 +1,8 @@ +#include +#include + +WUPSConfigItem::~WUPSConfigItem() { + if (mHandle.handle != nullptr) { + WUPSConfigAPI_Item_Destroy(mHandle); + } +} \ No newline at end of file diff --git a/libraries/libwups/WUPSConfigItemBoolean.cpp b/libraries/libwups/WUPSConfigItemBoolean.cpp index f24637c..fec1675 100644 --- a/libraries/libwups/WUPSConfigItemBoolean.cpp +++ b/libraries/libwups/WUPSConfigItemBoolean.cpp @@ -2,43 +2,36 @@ #include #include #include -#include +#include -int32_t WUPSConfigItemBoolean_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { +static void WUPSConfigItemBoolean_onCloseCallback(void *context) { auto *item = (ConfigItemBoolean *) context; - snprintf(out_buf, out_size, " %s", item->value ? item->trueValue : item->falseValue); - return 0; + if (item->valueAtCreation != item->value && item->valueChangedCallback != nullptr) { + ((BooleanValueChangedCallback) (item->valueChangedCallback))(item, item->value); + } } -void toggleValue(ConfigItemBoolean *item) { +static inline void toggleValue(ConfigItemBoolean *item) { item->value = !item->value; } -bool WUPSConfigItemBoolean_callCallback(void *context) { - auto *item = (ConfigItemBoolean *) context; - if (item->callback != nullptr) { - ((BooleanValueChangedCallback) (item->callback))(item, item->value); - return true; - } - return false; -} - -void WUPSConfigItemBoolean_onButtonPressed(void *context, WUPSConfigButtons buttons) { +static void WUPSConfigItemBoolean_onInput(void *context, WUPSConfigSimplePadData input) { auto *item = (ConfigItemBoolean *) context; - if ((buttons & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A) { + if ((input.buttons_d & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A) { toggleValue(item); - } else if (buttons & WUPS_CONFIG_BUTTON_LEFT && !item->value) { + } else if (input.buttons_d & WUPS_CONFIG_BUTTON_LEFT && !item->value) { toggleValue(item); - } else if ((buttons & WUPS_CONFIG_BUTTON_RIGHT) && item->value) { + } else if ((input.buttons_d & WUPS_CONFIG_BUTTON_RIGHT) && item->value) { toggleValue(item); } } - -bool WUPSConfigItemBoolean_isMovementAllowed(void *context) { - return true; +static int32_t WUPSConfigItemBoolean_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { + auto *item = (ConfigItemBoolean *) context; + snprintf(out_buf, out_size, " %s", item->value ? item->trueValue : item->falseValue); + return 0; } -int32_t WUPSConfigItemBoolean_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { +static int32_t WUPSConfigItemBoolean_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { auto *item = (ConfigItemBoolean *) context; if (item->value) { snprintf(out_buf, out_size, " %s >", item->trueValue); @@ -48,77 +41,114 @@ int32_t WUPSConfigItemBoolean_getCurrentValueSelectedDisplay(void *context, char return 0; } -void WUPSConfigItemBoolean_restoreDefault(void *context) { +static void WUPSConfigItemBoolean_restoreDefault(void *context) { auto *item = (ConfigItemBoolean *) context; item->value = item->defaultValue; } -void WUPSConfigItemBoolean_onSelected(void *context, bool isSelected) { -} - static void WUPSConfigItemBoolean_Cleanup(ConfigItemBoolean *item) { if (!item) { return; } - free(item->configId); + free((void *) item->identifier); free(item); } -void WUPSConfigItemBoolean_onDelete(void *context) { +static void WUPSConfigItemBoolean_onDelete(void *context) { WUPSConfigItemBoolean_Cleanup((ConfigItemBoolean *) context); } -extern "C" bool -WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback, const char *trueValue, - const char *falseValue) { - if (cat == nullptr) { - return false; +extern "C" WUPSConfigAPIStatus +WUPSConfigItemBoolean_CreateEx(const char *identifier, + const char *displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + const char *trueValue, + const char *falseValue, + WUPSConfigItemHandle *outHandle) { + if (outHandle == nullptr) { + return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; } auto *item = (ConfigItemBoolean *) malloc(sizeof(ConfigItemBoolean)); if (item == nullptr) { - return false; + return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY; } - if (configId != nullptr) { - item->configId = strdup(configId); + if (identifier != nullptr) { + item->identifier = strdup(identifier); } else { - item->configId = nullptr; + item->identifier = nullptr; } - item->defaultValue = defaultValue; - item->value = defaultValue; - item->callback = (void *) callback; + item->defaultValue = defaultValue; + item->value = currentValue; + item->valueAtCreation = currentValue; + item->valueChangedCallback = (void *) callback; snprintf(item->trueValue, sizeof(item->trueValue), "%s", trueValue); snprintf(item->falseValue, sizeof(item->falseValue), "%s", falseValue); - WUPSConfigAPIItemCallbacksV1 callbacks = { + WUPSConfigAPIItemCallbacksV2 callbacks = { .getCurrentValueDisplay = &WUPSConfigItemBoolean_getCurrentValueDisplay, .getCurrentValueSelectedDisplay = &WUPSConfigItemBoolean_getCurrentValueSelectedDisplay, - .onSelected = &WUPSConfigItemBoolean_onSelected, + .onSelected = nullptr, .restoreDefault = &WUPSConfigItemBoolean_restoreDefault, - .isMovementAllowed = &WUPSConfigItemBoolean_isMovementAllowed, - .callCallback = &WUPSConfigItemBoolean_callCallback, - .onButtonPressed = &WUPSConfigItemBoolean_onButtonPressed, + .isMovementAllowed = nullptr, + .onCloseCallback = &WUPSConfigItemBoolean_onCloseCallback, + .onInput = &WUPSConfigItemBoolean_onInput, + .onInputEx = nullptr, .onDelete = &WUPSConfigItemBoolean_onDelete}; - WUPSConfigAPIItemOptionsV1 options = { + WUPSConfigAPIItemOptionsV2 options = { .displayName = displayName, .context = item, .callbacks = callbacks, }; - if (WUPSConfigAPI_Item_Create(options, &item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPIStatus err; + if ((err = WUPSConfigAPI_Item_Create(options, &item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) { WUPSConfigItemBoolean_Cleanup(item); - return false; + return err; + } + + *outHandle = item->handle; + return WUPSCONFIG_API_RESULT_SUCCESS; +} + +extern "C" WUPSConfigAPIStatus +WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat, + const char *identifier, + const char *displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback, + const char *trueValue, + const char *falseValue) { + WUPSConfigItemHandle itemHandle; + WUPSConfigAPIStatus res; + if ((res = WUPSConfigItemBoolean_CreateEx(identifier, + displayName, + defaultValue, currentValue, + callback, + trueValue, falseValue, + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return res; } - if (WUPSConfigAPI_Category_AddItem(cat, item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { - WUPSConfigAPI_Item_Destroy(item->handle); - return false; + if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + + WUPSConfigAPI_Item_Destroy(itemHandle); + return res; } - return true; + return WUPSCONFIG_API_RESULT_SUCCESS; } -extern "C" bool WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback) { - return WUPSConfigItemBoolean_AddToCategoryEx(cat, configID, displayName, defaultValue, callback, "true", "false"); +extern "C" WUPSConfigAPIStatus +WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat, + const char *identifier, + const char *displayName, + bool defaultValue, + bool currentValue, + BooleanValueChangedCallback callback) { + return WUPSConfigItemBoolean_AddToCategoryEx(cat, identifier, displayName, defaultValue, currentValue, callback, "true", "false"); } diff --git a/libraries/libwups/WUPSConfigItemBooleanCpp.cpp b/libraries/libwups/WUPSConfigItemBooleanCpp.cpp new file mode 100644 index 0000000..654c830 --- /dev/null +++ b/libraries/libwups/WUPSConfigItemBooleanCpp.cpp @@ -0,0 +1,36 @@ +#include "wups/config/WUPSConfigItemBoolean.h" + +std::optional WUPSConfigItemBoolean::CreateEx(std::optional identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback, std::string_view trueValue, std::string_view falseValue, WUPSConfigAPIStatus &err) noexcept { + WUPSConfigItemHandle itemHandle; + if ((err = WUPSConfigItemBoolean_CreateEx(identifier ? identifier->data() : nullptr, + displayName.data(), + defaultValue, currentValue, + callback, + trueValue.data(), + falseValue.data(), + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return std::nullopt; + } + return WUPSConfigItemBoolean(itemHandle); +} +WUPSConfigItemBoolean WUPSConfigItemBoolean::CreateEx(std::optional identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback, std::string_view trueValue, std::string_view falseValue) { + WUPSConfigAPIStatus err; + auto result = CreateEx(std::move(identifier), displayName, defaultValue, currentValue, callback, trueValue, falseValue, err); + if (!result) { + throw std::runtime_error(std::string("Failed to create WUPSConfigItemBoolean: ").append(WUPSConfigAPI_GetStatusStr(err))); + } + return std::move(*result); +} + +std::optional WUPSConfigItemBoolean::Create(std::optional identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback, WUPSConfigAPIStatus &err) noexcept { + return CreateEx(std::move(identifier), displayName, defaultValue, currentValue, callback, "true", "false", err); +} + +WUPSConfigItemBoolean WUPSConfigItemBoolean::Create(std::optional identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback) { + WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; + auto res = Create(std::move(identifier), displayName, defaultValue, currentValue, callback, err); + if (!res) { + throw std::runtime_error(std::string("Failed to create WUPSConfigItemBoolean: ").append(WUPSConfigAPI_GetStatusStr(err))); + } + return std::move(*res); +} diff --git a/libraries/libwups/WUPSConfigItemIntegerRange.cpp b/libraries/libwups/WUPSConfigItemIntegerRange.cpp index e6ff09c..6ee95e1 100644 --- a/libraries/libwups/WUPSConfigItemIntegerRange.cpp +++ b/libraries/libwups/WUPSConfigItemIntegerRange.cpp @@ -2,32 +2,25 @@ #include #include #include -#include +#include -int32_t WUPSConfigItemIntegerRange_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { - auto *item = (ConfigItemIntegerRange *) context; - snprintf(out_buf, out_size, "%d", item->value); - return 0; -} - -bool WUPSConfigItemIntegerRange_callCallback(void *context) { +void WUPSConfigItemIntegerRange_onCloseCallback(void *context) { auto *item = (ConfigItemIntegerRange *) context; - if (item->callback != nullptr) { - ((IntegerRangeValueChangedCallback) item->callback)(item, item->value); - return true; + if (item->valueAtCreation != item->value && item->valueChangedCallback != nullptr) { + ((IntegerRangeValueChangedCallback) item->valueChangedCallback)(item, item->value); } - return false; } -void WUPSConfigItemIntegerRange_onButtonPressed(void *context, WUPSConfigButtons buttons) { +void WUPSConfigItemIntegerRange_onInput(void *context, WUPSConfigSimplePadData input) { auto *item = (ConfigItemIntegerRange *) context; - if (buttons & WUPS_CONFIG_BUTTON_LEFT) { + + if (input.buttons_d & WUPS_CONFIG_BUTTON_LEFT) { item->value--; - } else if ((buttons & WUPS_CONFIG_BUTTON_RIGHT)) { + } else if ((input.buttons_d & WUPS_CONFIG_BUTTON_RIGHT)) { item->value++; - } else if ((buttons & WUPS_CONFIG_BUTTON_L)) { + } else if ((input.buttons_d & WUPS_CONFIG_BUTTON_L)) { item->value = item->value - 50; - } else if ((buttons & WUPS_CONFIG_BUTTON_R)) { + } else if ((input.buttons_d & WUPS_CONFIG_BUTTON_R)) { item->value = item->value + 50; } @@ -38,8 +31,10 @@ void WUPSConfigItemIntegerRange_onButtonPressed(void *context, WUPSConfigButtons } } -bool WUPSConfigItemIntegerRange_isMovementAllowed(void *context) { - return true; +int32_t WUPSConfigItemIntegerRange_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { + auto *item = (ConfigItemIntegerRange *) context; + snprintf(out_buf, out_size, " %d", item->value); + return 0; } int32_t WUPSConfigItemIntegerRange_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { @@ -59,14 +54,11 @@ void WUPSConfigItemIntegerRange_restoreDefault(void *context) { item->value = item->defaultValue; } -void WUPSConfigItemIntegerRange_onSelected(void *context, bool isSelected) { -} - static void WUPSConfigItemIntegerRange_Cleanup(ConfigItemIntegerRange *item) { if (!item) { return; } - free(item->configId); + free((void *) item->identifier); free(item); } @@ -74,52 +66,87 @@ void WUPSConfigItemIntegerRange_onDelete(void *context) { WUPSConfigItemIntegerRange_Cleanup((ConfigItemIntegerRange *) context); } -extern "C" bool WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, int32_t defaultValue, int32_t minValue, int32_t maxValue, - IntegerRangeValueChangedCallback callback) { - if (cat == 0) { - return false; +extern "C" WUPSConfigAPIStatus +WUPSConfigItemIntegerRange_Create(const char *identifier, + const char *displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback callback, + WUPSConfigItemHandle *outHandle) { + if (outHandle == nullptr) { + return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; + } + if (maxValue < minValue || defaultValue < minValue || defaultValue > maxValue || currentValue < minValue || currentValue > maxValue) { + return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; } + *outHandle = {}; auto *item = (ConfigItemIntegerRange *) malloc(sizeof(ConfigItemIntegerRange)); if (item == nullptr) { - return false; + return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY; } - if (configId != nullptr) { - item->configId = strdup(configId); + if (identifier != nullptr) { + item->identifier = strdup(identifier); } else { - item->configId = nullptr; + item->identifier = nullptr; } - item->defaultValue = defaultValue; - item->value = defaultValue; - item->minValue = minValue; - item->maxValue = maxValue; - item->callback = (void *) callback; + item->defaultValue = defaultValue; + item->value = currentValue; + item->valueAtCreation = currentValue; + item->minValue = minValue; + item->maxValue = maxValue; + item->valueChangedCallback = (void *) callback; - WUPSConfigAPIItemCallbacksV1 callbacks = { + WUPSConfigAPIItemCallbacksV2 callbacks = { .getCurrentValueDisplay = &WUPSConfigItemIntegerRange_getCurrentValueDisplay, .getCurrentValueSelectedDisplay = &WUPSConfigItemIntegerRange_getCurrentValueSelectedDisplay, - .onSelected = &WUPSConfigItemIntegerRange_onSelected, + .onSelected = nullptr, .restoreDefault = &WUPSConfigItemIntegerRange_restoreDefault, - .isMovementAllowed = &WUPSConfigItemIntegerRange_isMovementAllowed, - .callCallback = &WUPSConfigItemIntegerRange_callCallback, - .onButtonPressed = &WUPSConfigItemIntegerRange_onButtonPressed, - .onDelete = &WUPSConfigItemIntegerRange_onDelete}; + .isMovementAllowed = nullptr, + .onCloseCallback = &WUPSConfigItemIntegerRange_onCloseCallback, + .onInput = &WUPSConfigItemIntegerRange_onInput, + .onInputEx = nullptr, + .onDelete = &WUPSConfigItemIntegerRange_onDelete, + }; - WUPSConfigAPIItemOptionsV1 options = { + WUPSConfigAPIItemOptionsV2 options = { .displayName = displayName, .context = item, .callbacks = callbacks, }; - if (WUPSConfigAPI_Item_Create(options, &(item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPIStatus err; + if ((err = WUPSConfigAPI_Item_Create(options, &(item->handle))) != WUPSCONFIG_API_RESULT_SUCCESS) { WUPSConfigItemIntegerRange_Cleanup(item); - return false; - }; + return err; + } + *outHandle = item->handle; - if (WUPSConfigAPI_Category_AddItem(cat, item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { - WUPSConfigAPI_Item_Destroy(item->handle); - return false; + return WUPSCONFIG_API_RESULT_SUCCESS; +} + +extern "C" WUPSConfigAPIStatus +WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat, + const char *identifier, + const char *displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback callback) { + WUPSConfigItemHandle itemHandle; + WUPSConfigAPIStatus res; + if ((res = WUPSConfigItemIntegerRange_Create(identifier, + displayName, + defaultValue, currentValue, + minValue, maxValue, + callback, + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return res; } - return true; -} \ No newline at end of file + + if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPI_Item_Destroy(itemHandle); + return res; + } + return WUPSCONFIG_API_RESULT_SUCCESS; +} diff --git a/libraries/libwups/WUPSConfigItemIntegerRangeCpp.cpp b/libraries/libwups/WUPSConfigItemIntegerRangeCpp.cpp new file mode 100644 index 0000000..cfe1bfc --- /dev/null +++ b/libraries/libwups/WUPSConfigItemIntegerRangeCpp.cpp @@ -0,0 +1,34 @@ +#include "wups/config/WUPSConfigItemIntegerRange.h" + +std::optional WUPSConfigItemIntegerRange::Create( + std::optional identifier, + std::string_view displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback valuesChangedCallback, + WUPSConfigAPIStatus &err) noexcept { + WUPSConfigItemHandle itemHandle; + if ((err = WUPSConfigItemIntegerRange_Create(identifier ? identifier->c_str() : nullptr, + displayName.data(), + defaultValue, currentValue, + minValue, maxValue, + valuesChangedCallback, + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return std::nullopt; + } + return WUPSConfigItemIntegerRange(itemHandle); +} + +WUPSConfigItemIntegerRange WUPSConfigItemIntegerRange::Create( + std::optional identifier, + std::string_view displayName, + int32_t defaultValue, int32_t currentValue, + int32_t minValue, int32_t maxValue, + IntegerRangeValueChangedCallback valuesChangedCallback) { + WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; + auto result = Create(std::move(identifier), displayName, defaultValue, currentValue, minValue, maxValue, valuesChangedCallback, err); + if (!result) { + throw std::runtime_error(std::string("Failed to create WUPSConfigItemIntegerRange: ").append(WUPSConfigAPI_GetStatusStr(err))); + } + return std::move(*result); +} \ No newline at end of file diff --git a/libraries/libwups/WUPSConfigItemMultipleValues.cpp b/libraries/libwups/WUPSConfigItemMultipleValues.cpp index 0683928..7d51806 100644 --- a/libraries/libwups/WUPSConfigItemMultipleValues.cpp +++ b/libraries/libwups/WUPSConfigItemMultipleValues.cpp @@ -2,9 +2,9 @@ #include #include #include -#include +#include -int32_t WUPSConfigItemMultipleValues_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { +static int32_t WUPSConfigItemMultipleValues_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { auto *item = (ConfigItemMultipleValues *) context; if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) { @@ -17,20 +17,20 @@ int32_t WUPSConfigItemMultipleValues_getCurrentValueDisplay(void *context, char return -1; } -bool WUPSConfigItemMultipleValues_callCallback(void *context) { +static void WUPSConfigItemMultipleValues_onCloseCallback(void *context) { auto *item = (ConfigItemMultipleValues *) context; - if (item->callback != nullptr && item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) { - ((MultipleValuesChangedCallback) (item->callback))(item, item->values[item->valueIndex].value); - return true; + if (item->valueIndexAtCreation != item->valueIndex && item->valueChangedCallback != nullptr) { + if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) { + ((MultipleValuesChangedCallback) (item->valueChangedCallback))(item, item->values[item->valueIndex].value); + } } - return false; } -void WUPSConfigItemMultipleValues_onButtonPressed(void *context, WUPSConfigButtons buttons) { +static void WUPSConfigItemMultipleValues_onInput(void *context, WUPSConfigSimplePadData input) { auto *item = (ConfigItemMultipleValues *) context; - if (buttons & WUPS_CONFIG_BUTTON_LEFT) { + if (input.buttons_d & WUPS_CONFIG_BUTTON_LEFT) { item->valueIndex--; - } else if (buttons & WUPS_CONFIG_BUTTON_RIGHT) { + } else if (input.buttons_d & WUPS_CONFIG_BUTTON_RIGHT) { item->valueIndex++; } if (item->valueIndex < 0) { @@ -40,11 +40,7 @@ void WUPSConfigItemMultipleValues_onButtonPressed(void *context, WUPSConfigButto } } -bool WUPSConfigItemMultipleValues_isMovementAllowed(void *context) { - return true; -} - -int32_t WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { +static int32_t WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { auto *item = (ConfigItemMultipleValues *) context; if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) { if (item->valueCount == 1) { @@ -62,94 +58,119 @@ int32_t WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay(void *contex return 0; } -void WUPSConfigItemMultipleValues_restoreDefault(void *context) { +static void WUPSConfigItemMultipleValues_restoreDefault(void *context) { auto *item = (ConfigItemMultipleValues *) context; item->valueIndex = item->defaultValueIndex; } -void WUPSConfigItemMultipleValues_onSelected(void *context, bool isSelected) { -} - static void WUPSConfigItemMultipleValues_Cleanup(ConfigItemMultipleValues *item) { if (!item) { return; } for (int i = 0; i < item->valueCount; ++i) { - free(item->values[i].valueName); + free((void *) item->values[i].valueName); } - free(item->configId); - free(item->values); + free((void *) item->identifier); + free(item->values); free(item); } -void WUPSConfigItemMultipleValues_onDelete(void *context) { +static void WUPSConfigItemMultipleValues_onDelete(void *context) { auto *item = (ConfigItemMultipleValues *) context; - WUPSConfigItemMultipleValues_Cleanup(item); } -extern "C" bool -WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, - int32_t defaultValueIndex, ConfigItemMultipleValuesPair *possibleValues, - int pairCount, MultipleValuesChangedCallback callback) { - if (cat == 0 || displayName == nullptr || possibleValues == nullptr || pairCount < 0) { - return false; +extern "C" WUPSConfigAPIStatus +WUPSConfigItemMultipleValues_Create(const char *identifier, const char *displayName, + int32_t defaultValueIndex, int currentValueIndex, + ConfigItemMultipleValuesPair *possibleValues, + int pairCount, MultipleValuesChangedCallback callback, + WUPSConfigItemHandle *outHandle) { + if (outHandle == nullptr || displayName == nullptr || possibleValues == nullptr || pairCount < 0 || + defaultValueIndex >= pairCount || currentValueIndex >= pairCount) { + return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; } + *outHandle = {}; auto *item = (ConfigItemMultipleValues *) malloc(sizeof(ConfigItemMultipleValues)); if (item == nullptr) { - return false; + return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY; } + *item = {}; - auto *values = (ConfigItemMultipleValuesPair *) malloc(sizeof(ConfigItemMultipleValuesPair) * pairCount); + item->values = (ConfigItemMultipleValuesPair *) malloc(sizeof(ConfigItemMultipleValuesPair) * pairCount); + if (!item->values) { + WUPSConfigItemMultipleValues_Cleanup(item); + return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY; + } + + if (identifier != nullptr) { + item->identifier = strdup(identifier); + } else { + item->identifier = nullptr; + } for (int i = 0; i < pairCount; ++i) { - values[i].value = possibleValues[i].value; + item->values[i].value = possibleValues[i].value; if (possibleValues[i].valueName == nullptr) { - values[i].valueName = nullptr; + item->values[i].valueName = nullptr; continue; } - values[i].valueName = strdup(possibleValues[i].valueName); + item->values[i].valueName = strdup(possibleValues[i].valueName); } - item->valueCount = pairCount; - item->values = values; - item->valueIndex = defaultValueIndex; - item->defaultValueIndex = defaultValueIndex; - item->callback = (void *) callback; + item->valueCount = pairCount; + item->defaultValueIndex = defaultValueIndex; + item->valueIndex = currentValueIndex; + item->valueIndexAtCreation = currentValueIndex; + item->valueChangedCallback = (void *) callback; - if (configId != nullptr) { - item->configId = strdup(configId); - } else { - item->configId = nullptr; - } - - WUPSConfigAPIItemCallbacksV1 callbacks = { + WUPSConfigAPIItemCallbacksV2 callbacks = { .getCurrentValueDisplay = &WUPSConfigItemMultipleValues_getCurrentValueDisplay, .getCurrentValueSelectedDisplay = &WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay, - .onSelected = &WUPSConfigItemMultipleValues_onSelected, + .onSelected = nullptr, .restoreDefault = &WUPSConfigItemMultipleValues_restoreDefault, - .isMovementAllowed = &WUPSConfigItemMultipleValues_isMovementAllowed, - .callCallback = &WUPSConfigItemMultipleValues_callCallback, - .onButtonPressed = &WUPSConfigItemMultipleValues_onButtonPressed, + .isMovementAllowed = nullptr, + .onCloseCallback = &WUPSConfigItemMultipleValues_onCloseCallback, + .onInput = &WUPSConfigItemMultipleValues_onInput, + .onInputEx = nullptr, .onDelete = &WUPSConfigItemMultipleValues_onDelete}; - WUPSConfigAPIItemOptionsV1 options = { + WUPSConfigAPIItemOptionsV2 options = { .displayName = displayName, .context = item, .callbacks = callbacks, }; - if (WUPSConfigAPI_Item_Create(options, &item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPIStatus err; + if ((err = WUPSConfigAPI_Item_Create(options, &item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) { WUPSConfigItemMultipleValues_Cleanup(item); - return false; + return err; } + *outHandle = item->handle; + return WUPSCONFIG_API_RESULT_SUCCESS; +} - if (WUPSConfigAPI_Category_AddItem(cat, item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { - WUPSConfigAPI_Item_Destroy(item->handle); - return false; +extern "C" WUPSConfigAPIStatus +WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *identifier, const char *displayName, + int32_t defaultValueIndex, int currentValueIndex, + ConfigItemMultipleValuesPair *possibleValues, int pairCount, + MultipleValuesChangedCallback callback) { + WUPSConfigItemHandle itemHandle; + WUPSConfigAPIStatus res; + if ((res = WUPSConfigItemMultipleValues_Create(identifier, + displayName, + defaultValueIndex, currentValueIndex, + possibleValues, pairCount, + callback, + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return res; } - return true; -} \ No newline at end of file + if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPI_Item_Destroy(itemHandle); + return res; + } + return WUPSCONFIG_API_RESULT_SUCCESS; +} diff --git a/libraries/libwups/WUPSConfigItemMultipleValuesCpp.cpp b/libraries/libwups/WUPSConfigItemMultipleValuesCpp.cpp new file mode 100644 index 0000000..3ef19d1 --- /dev/null +++ b/libraries/libwups/WUPSConfigItemMultipleValuesCpp.cpp @@ -0,0 +1,102 @@ +#include "wups/config/WUPSConfigItemMultipleValues.h" + + +std::optional WUPSConfigItemMultipleValues::CreateFromIndex( + std::optional identifier, + std::string_view displayName, + int defaultValueIndex, int currentValueIndex, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback, + WUPSConfigAPIStatus &err) noexcept { + auto *values = (ConfigItemMultipleValuesPair *) malloc(possibleValues.size_bytes()); + if (!values) { + err = WUPSCONFIG_API_RESULT_OUT_OF_MEMORY; + return std::nullopt; + } + int i = 0; + for (const auto &cur : possibleValues) { + values[i].value = cur.value; + values[i].valueName = cur.name.data(); + i++; + } + WUPSConfigItemHandle itemHandle; + err = WUPSConfigItemMultipleValues_Create( + identifier ? identifier->c_str() : nullptr, + displayName.data(), + defaultValueIndex, currentValueIndex, + values, (int32_t) possibleValues.size(), + valuesChangedCallback, + &itemHandle); + free(values); + if (err != WUPSCONFIG_API_RESULT_SUCCESS) { + return std::nullopt; + } + return WUPSConfigItemMultipleValues(itemHandle); +} + +WUPSConfigItemMultipleValues WUPSConfigItemMultipleValues::CreateFromIndex( + std::optional identifier, + std::string_view displayName, + int defaultValueIndex, int currentValueIndex, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback) { + WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; + auto result = CreateFromIndex(std::move(identifier), displayName, defaultValueIndex, currentValueIndex, possibleValues, valuesChangedCallback, err); + if (!result) { + throw std::runtime_error(std::string("Failed to create WUPSConfigItemMultipleValues: ").append(WUPSConfigAPI_GetStatusStr(err))); + } + return std::move(*result); +} + +std::optional WUPSConfigItemMultipleValues::CreateFromValue( + std::optional identifier, + std::string_view displayName, + uint32_t defaultValue, uint32_t currentValue, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback, + WUPSConfigAPIStatus &err) noexcept { + int defaultIndex = -1; + int currentValueIndex = -1; + int i = 0; + for (const auto &cur : possibleValues) { + if (defaultIndex != -1 && currentValueIndex != -1) { + break; + } + if (cur.value == defaultValue) { + currentValueIndex = i; + } + if (cur.value == currentValue) { + defaultIndex = i; + } + i++; + } + if (defaultIndex == -1 || currentValueIndex == -1) { + err = WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; + return std::nullopt; + } + + return WUPSConfigItemMultipleValues::CreateFromIndex(std::move(identifier), + displayName, + defaultIndex, currentValueIndex, + possibleValues, + valuesChangedCallback, + err); +} + +WUPSConfigItemMultipleValues WUPSConfigItemMultipleValues::CreateFromValue( + std::optional identifier, std::string_view displayName, + int32_t defaultValue, int32_t currentValue, + const std::span &possibleValues, + MultipleValuesChangedCallback valuesChangedCallback) { + WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; + auto result = CreateFromValue(std::move(identifier), + displayName, + defaultValue, currentValue, + possibleValues, + valuesChangedCallback, + err); + if (!result) { + throw std::runtime_error(std::string("Failed to create WUPSConfigItemMultipleValues (\"").append(displayName).append("\":").append(WUPSConfigAPI_GetStatusStr(err))); + } + return std::move(*result); +} diff --git a/libraries/libwups/WUPSConfigItemStub.cpp b/libraries/libwups/WUPSConfigItemStub.cpp index cef399e..c8f977d 100644 --- a/libraries/libwups/WUPSConfigItemStub.cpp +++ b/libraries/libwups/WUPSConfigItemStub.cpp @@ -1,84 +1,67 @@ #include "wups/config/WUPSConfigItemStub.h" -#include #include #include -#include -int32_t WUPSConfigItemStub_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { +static int32_t WUPSConfigItemStub_getEmptyTextValue(void *context, char *out_buf, int32_t out_size) { memset(out_buf, 0, out_size); return 0; } -bool WUPSConfigItemStub_callCallback(void *context) { - return false; -} - -void WUPSConfigItemStub_onButtonPressed(void *context, WUPSConfigButtons buttons) { -} - -bool WUPSConfigItemStub_isMovementAllowed(void *context) { - return true; -} - -int32_t WUPSConfigItemStub_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { - memset(out_buf, 0, out_size); - return 0; -} - -void WUPSConfigItemStub_restoreDefault(void *context) { -} - -void WUPSConfigItemStub_onSelected(void *context, bool isSelected) { -} - -static void WUPSConfigItemStub_Cleanup(ConfigItemStub *item) { +static void WUPSConfigItemStub_Cleanup(void *item) { free(item); } -void WUPSConfigItemStub_onDelete(void *context) { - WUPSConfigItemStub_Cleanup((ConfigItemStub *) context); -} - - -extern "C" bool -WUPSConfigItemStub_AddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName) { - if (cat == 0) { - return false; +extern "C" WUPSConfigAPIStatus +WUPSConfigItemStub_Create(const char *displayName, WUPSConfigItemHandle *outHandle) { + if (displayName == nullptr || outHandle == nullptr) { + return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT; } auto *item = (ConfigItemStub *) malloc(sizeof(ConfigItemStub)); if (item == nullptr) { - return false; + return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY; } - WUPSConfigAPIItemCallbacksV1 callbacks = { - .getCurrentValueDisplay = &WUPSConfigItemStub_getCurrentValueDisplay, - .getCurrentValueSelectedDisplay = &WUPSConfigItemStub_getCurrentValueSelectedDisplay, - .onSelected = &WUPSConfigItemStub_onSelected, - .restoreDefault = &WUPSConfigItemStub_restoreDefault, - .isMovementAllowed = &WUPSConfigItemStub_isMovementAllowed, - .callCallback = &WUPSConfigItemStub_callCallback, - .onButtonPressed = &WUPSConfigItemStub_onButtonPressed, - .onDelete = &WUPSConfigItemStub_onDelete}; + WUPSConfigAPIItemCallbacksV2 callbacks = { + .getCurrentValueDisplay = &WUPSConfigItemStub_getEmptyTextValue, + .getCurrentValueSelectedDisplay = &WUPSConfigItemStub_getEmptyTextValue, + .onSelected = nullptr, + .restoreDefault = nullptr, + .isMovementAllowed = nullptr, + .onCloseCallback = nullptr, + .onInput = nullptr, + .onInputEx = nullptr, + .onDelete = &WUPSConfigItemStub_Cleanup, + }; - WUPSConfigAPIItemOptionsV1 options = { + WUPSConfigAPIItemOptionsV2 options = { .displayName = displayName, .context = item, .callbacks = callbacks, }; - if (WUPSConfigAPI_Item_Create(options, &item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPIStatus err; + if ((err = WUPSConfigAPI_Item_Create(options, &item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) { WUPSConfigItemStub_Cleanup(item); - return false; + return err; } - if (WUPSConfigAPI_Category_AddItem(cat, item->handle) != WUPSCONFIG_API_RESULT_SUCCESS) { - WUPSConfigAPI_Item_Destroy(item->handle); - return false; - } - return true; -} + *outHandle = item->handle; -extern "C" bool WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName) { - return WUPSConfigItemStub_AddToCategoryEx(cat, configID, displayName); + return WUPSCONFIG_API_RESULT_SUCCESS; } + +extern "C" WUPSConfigAPIStatus +WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *displayName) { + WUPSConfigItemHandle itemHandle; + WUPSConfigAPIStatus res; + if ((res = WUPSConfigItemStub_Create(displayName, + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return res; + } + if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + WUPSConfigAPI_Item_Destroy(itemHandle); + return res; + } + return WUPSCONFIG_API_RESULT_SUCCESS; +} \ No newline at end of file diff --git a/libraries/libwups/WUPSConfigItemStubCpp.cpp b/libraries/libwups/WUPSConfigItemStubCpp.cpp new file mode 100644 index 0000000..1279712 --- /dev/null +++ b/libraries/libwups/WUPSConfigItemStubCpp.cpp @@ -0,0 +1,20 @@ +#include "wups/config/WUPSConfigItemStub.h" + +std::optional WUPSConfigItemStub::Create(std::string_view displayName, + WUPSConfigAPIStatus &err) noexcept { + WUPSConfigItemHandle itemHandle; + if ((err = WUPSConfigItemStub_Create(displayName.data(), + &itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) { + return std::nullopt; + } + return WUPSConfigItemStub(itemHandle); +} + +WUPSConfigItemStub WUPSConfigItemStub::Create(std::string_view displayName) { + WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR; + auto result = Create(displayName, err); + if (!result) { + throw std::runtime_error(std::string("Failed to create WUPSConfigItemStub: ").append(WUPSConfigAPI_GetStatusStr(err))); + } + return std::move(*result); +} diff --git a/libraries/libwups/config_api.cpp b/libraries/libwups/config_api.cpp index d5c5c25..1fdbc76 100644 --- a/libraries/libwups/config_api.cpp +++ b/libraries/libwups/config_api.cpp @@ -1,5 +1,5 @@ #include "wups/config_api.h" -#include "wups.h" +#include "wups/wups_debug.h" #include #include diff --git a/plugins/example_plugin/src/main.c b/plugins/example_plugin/src/main.c index 2a62259..766d0b3 100644 --- a/plugins/example_plugin/src/main.c +++ b/plugins/example_plugin/src/main.c @@ -1,10 +1,12 @@ #include "utils/logger.h" #include #include +#include #include #include #include #include +#include /** Mandatory plugin information. @@ -56,7 +58,7 @@ WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle ro } // Add a new item to this settings category - if (!WUPSConfigItemBoolean_AddToCategory(settingsCategory, LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls", logFSOpen, &logFSOpenChanged)) { + if (WUPSConfigItemBoolean_AddToCategory(settingsCategory, LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls", true, logFSOpen, &logFSOpenChanged) != WUPSCONFIG_API_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Failed to add item to category"); return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; } @@ -81,7 +83,7 @@ WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle ro DEBUG_FUNCTION_LINE_ERR("Failed to create categoryLevel1"); return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; } - if (!WUPSConfigItemBoolean_AddToCategory(categoryLevel2, "stubInsideCategory", "This is stub item inside a nested category", false, NULL)) { + if (WUPSConfigItemBoolean_AddToCategory(categoryLevel2, "stubInsideCategory", "This is stub item inside a nested category", false, false, NULL) != WUPSCONFIG_API_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Failed to add stub item to root category"); return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; } @@ -98,10 +100,9 @@ WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle ro return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; } } - { // We can also directly add items to the root category - if (!WUPSConfigItemBoolean_AddToCategory(root, "stub0", "This is stub item without category", false, NULL)) { + if (WUPSConfigItemStub_AddToCategory(root, "This is stub item without category") != WUPSCONFIG_API_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Failed to add stub item to root category"); return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; } @@ -117,11 +118,11 @@ WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle ro values[i].value = i; values[i].valueName = str; } - bool multValuesRes = WUPSConfigItemMultipleValues_AddToCategory(root, "multival", "Multiple values", 0, values, numOfElements, NULL); + WUPSConfigAPIStatus multValuesRes = WUPSConfigItemMultipleValues_AddToCategory(root, "multival", "Multiple values", 0, 0, values, numOfElements, NULL); for (int i = 0; i < sizeof(values) / sizeof(values[0]); i++) { - free(values[i].valueName); + free((void *) values[i].valueName); } - if (!multValuesRes) { + if (multValuesRes != WUPSCONFIG_API_RESULT_SUCCESS) { return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; } } diff --git a/plugins/example_plugin_cpp/.clang-format b/plugins/example_plugin_cpp/.clang-format new file mode 100644 index 0000000..56cc685 --- /dev/null +++ b/plugins/example_plugin_cpp/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/plugins/example_plugin_cpp/Dockerfile b/plugins/example_plugin_cpp/Dockerfile new file mode 100644 index 0000000..b9bbafb --- /dev/null +++ b/plugins/example_plugin_cpp/Dockerfile @@ -0,0 +1,5 @@ +FROM ghcr.io/wiiu-env/devkitppc:20230218 + +COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230215 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/plugins/example_plugin_cpp/Makefile b/plugins/example_plugin_cpp/Makefile new file mode 100644 index 0000000..1860d5c --- /dev/null +++ b/plugins/example_plugin_cpp/Makefile @@ -0,0 +1,135 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wups/share/wups_rules + +WUT_ROOT := $(DEVKITPRO)/wut +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := ExamplePluginCPP +BUILD := build +SOURCES := src src/utils +DATA := data +INCLUDES := src + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ + +CXXFLAGS := $(CFLAGS) -std=c++20 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS) + +LIBS := -lwups -lwut + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUT_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD)) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wps + +$(OUTPUT).wps : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/plugins/example_plugin_cpp/README.md b/plugins/example_plugin_cpp/README.md new file mode 100644 index 0000000..b68d161 --- /dev/null +++ b/plugins/example_plugin_cpp/README.md @@ -0,0 +1,57 @@ +# Example plugin + +This is just a simple example plugin which can be used as a template. +The plugin logs the FSOpenFile calls via UDP (**Only when build via `make DEBUG=1`**). + +The logging can be enabled/disabled via the WUPS Config menu (press L, DPAD Down and Minus on the GamePad, Pro Controller or Classic Controller). + +## Installation + +(`[ENVIRONMENT]` is a placeholder for the actual environment name.) + +1. Copy the file `ExamplePlugin.wps` into `sd:/wiiu/environments/[ENVIRONMENT]/plugins`. +2. Requires the [WiiUPluginLoaderBackend](https://github.com/wiiu-env/WiiUPluginLoaderBackend) in `sd:/wiiu/environments/[ENVIRONMENT]/modules`. + +Start the environment (e.g Aroma) and the backend should load the plugin. + +## Building + +For building you need: + +- [wups](https://github.com/Maschell/WiiUPluginSystem) +- [wut](https://github.com/devkitpro/wut) + +Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself. + +Then you should be able to compile via `make` (with no logging) or `make DEBUG=1` (with logging). + +## Buildflags + +### Logging + +Building via `make` only logs errors (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`. + +`make` Logs errors only (via OSReport). +`make DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule). +`make DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule). + +If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging. + +## Building using the Dockerfile + +It's possible to use a docker image for building. This way you don't need anything installed on your host system. + +``` +# Build docker image (only needed once) +docker build . -t example-plugin-builder + +# make +docker run -it --rm -v ${PWD}:/project example-plugin-builder make DEBUG=1 + +# make clean +docker run -it --rm -v ${PWD}:/project example-plugin-builder make clean +``` + +## Format the code via docker + +`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src -i` \ No newline at end of file diff --git a/plugins/example_plugin_cpp/src/main.cpp b/plugins/example_plugin_cpp/src/main.cpp new file mode 100644 index 0000000..af62e94 --- /dev/null +++ b/plugins/example_plugin_cpp/src/main.cpp @@ -0,0 +1,306 @@ +#include "utils/logger.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Mandatory plugin information. + If not set correctly, the loader will refuse to use the plugin. +**/ +WUPS_PLUGIN_NAME("Example plugin C++"); +WUPS_PLUGIN_DESCRIPTION("This is just an example plugin written in C++"); +WUPS_PLUGIN_VERSION("v1.0"); +WUPS_PLUGIN_AUTHOR("Maschell"); +WUPS_PLUGIN_LICENSE("BSD"); + +#define LOG_FS_OPEN_CONFIG_ID "logFSOpen" +#define OTHER_EXAMPLE_BOOL_CONFIG_ID "otherBoolItem" +#define OTHER_EXAMPLE2_BOOL_CONFIG_ID "other2BoolItem" +#define INTEGER_RANGE_EXAMPLE_CONFIG_ID "intRangeExample" +#define MULTIPLE_VALUES_EXAMPLE_CONFIG_ID "multValueExample" + +/** + All of this defines can be used in ANY file. + It's possible to split it up into multiple files. + +**/ + +WUPS_USE_WUT_DEVOPTAB(); // Use the wut devoptabs +WUPS_USE_STORAGE("example_plugin_cpp"); // Unique id for the storage api + +enum ExampleOptions { + EXAMPLE_OPTION_1 = 0, + EXAMPLE_OPTION_2 = 1, + EXAMPLE_OPTION_3 = 2, +}; + +#define LOF_FS_OPEN_DEFAULT_VALUE true +#define INTEGER_RANGE_DEFAULT_VALUE 10 +#define MULTIPLE_VALUES_DEFAULT_VALUE EXAMPLE_OPTION_2 + +bool sLogFSOpen = LOF_FS_OPEN_DEFAULT_VALUE; +int sIntegerRangeValue = INTEGER_RANGE_DEFAULT_VALUE; +ExampleOptions sExampleOptionValue = MULTIPLE_VALUES_DEFAULT_VALUE; + +/** + * Callback that will be called if the config has been changed + */ +void boolItemChanged(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE_INFO("New value in boolItemChanged: %d", newValue); + if (std::string_view(LOG_FS_OPEN_CONFIG_ID) == item->identifier) { + sLogFSOpen = newValue; + // If the value has changed, we store it in the storage. + WUPS_StoreInt(nullptr, item->identifier, newValue); + } else if (std::string_view(OTHER_EXAMPLE_BOOL_CONFIG_ID) == item->identifier) { + DEBUG_FUNCTION_LINE_ERR("Other bool value has changed to %d", newValue); + } else if (std::string_view(OTHER_EXAMPLE2_BOOL_CONFIG_ID) == item->identifier) { + DEBUG_FUNCTION_LINE_ERR("Other2 bool value has changed to %d", newValue); + } +} + +void integerRangeItemChanged(ConfigItemIntegerRange *item, int newValue) { + DEBUG_FUNCTION_LINE_INFO("New value in integerRangeItemChanged: %d", newValue); + // If the value has changed, we store it in the storage. + if (std::string_view(LOG_FS_OPEN_CONFIG_ID) == item->identifier) { + sIntegerRangeValue = newValue; + // If the value has changed, we store it in the storage. + WUPS_StoreInt(nullptr, item->identifier, newValue); + } +} + +void multipleValueItemChanged(ConfigItemIntegerRange *item, uint32_t newValue) { + DEBUG_FUNCTION_LINE_INFO("New value in multipleValueItemChanged: %d", newValue); + // If the value has changed, we store it in the storage. + if (std::string_view(MULTIPLE_VALUES_EXAMPLE_CONFIG_ID) == item->identifier) { + sExampleOptionValue = (ExampleOptions) newValue; + // If the value has changed, we store it in the storage. + WUPS_StoreInt(nullptr, item->identifier, sExampleOptionValue); + } +} + +WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) { + // We open the storage, so we can persist the configuration the user did. + if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("Failed to open storage"); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + // To use the C++ API, we create new WUPSConfigCategory from the root handle! + WUPSConfigCategory root = WUPSConfigCategory(rootHandle); + + // The functions of the Config API come in two variants: One that throws an exception, and another one which doesn't + // To use the Config API without exception see the example below this try/catch block. + try { + // Then we can simply create a new category + auto functionPatchesCat = WUPSConfigCategory::Create("function patches"); + + // Add a boolean item to this newly created category + functionPatchesCat.add(WUPSConfigItemBoolean::Create(LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls", + LOF_FS_OPEN_DEFAULT_VALUE, sLogFSOpen, + boolItemChanged)); + + // And finally move that category to the root category. + // Note: "functionPatchesCat" can NOT be changed after adding it to root. + root.add(std::move(functionPatchesCat)); + + // We can also add items directly to root! + root.add(WUPSConfigItemBoolean::Create(OTHER_EXAMPLE_BOOL_CONFIG_ID, "Just another bool item", + false, false, + boolItemChanged)); + + // You can also add an item which just displays any text. + root.add(WUPSConfigItemStub::Create("This item is just displaying some text")); + + // It's also possible to create and item to select an integer from a range. + root.add(WUPSConfigItemIntegerRange::Create(INTEGER_RANGE_EXAMPLE_CONFIG_ID, "Item for selecting an integer between 0 and 50", + INTEGER_RANGE_DEFAULT_VALUE, sIntegerRangeValue, + 0, 50, + &integerRangeItemChanged)); + + + // To select value from an enum WUPSConfigItemMultipleValues fits the best. + constexpr WUPSConfigItemMultipleValues::ValuePair possibleValues[] = { + {EXAMPLE_OPTION_1, "Option 1"}, + {EXAMPLE_OPTION_2, "Option 2"}, + {EXAMPLE_OPTION_3, "Option 3"}, + }; + + // It comes in two variants. + // - "WUPSConfigItemMultipleValues::CreateFromValue" will take a default and current **value** + // - "WUPSConfigItemMultipleValues::CreateFromIndex" will take a default and current **index** + root.add(WUPSConfigItemMultipleValues::CreateFromValue(MULTIPLE_VALUES_EXAMPLE_CONFIG_ID, "Select an option!", + MULTIPLE_VALUES_DEFAULT_VALUE, sExampleOptionValue, + possibleValues, + nullptr)); + + // It's also possible to have nested categories + auto nc1 = WUPSConfigCategory::Create("Category inside root"); + auto nc2 = WUPSConfigCategory::Create("Category inside subcategory 1"); + auto nc3 = WUPSConfigCategory::Create("Category inside subcategory 2"); + + nc3.add(WUPSConfigItemStub::Create("Item inside subcategory 3")); + nc2.add(WUPSConfigItemStub::Create("Item inside subcategory 2")); + nc1.add(WUPSConfigItemStub::Create("Item inside subcategory 1")); + + nc2.add(std::move(nc3)); + nc1.add(std::move(nc2)); + root.add(std::move(nc1)); + } catch (std::exception &e) { + DEBUG_FUNCTION_LINE_ERR("Creating config menu failed: %s", e.what()); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + // In case we don't like exception, we can use the API as well. + // If we add a "WUPSConfigAPIStatus" reference to the API calls, the function won't throw an exception. + // Instead it will return std::optionals and write the result into the WUPSConfigAPIStatus. + WUPSConfigAPIStatus err; + auto categoryOpt = WUPSConfigCategory::Create("Just another Category", err); + if (!categoryOpt) { + DEBUG_FUNCTION_LINE_ERR("Failed to create category: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + auto boolItemOpt = WUPSConfigItemBoolean::Create(OTHER_EXAMPLE2_BOOL_CONFIG_ID, "Just another bool item", + false, false, + boolItemChanged, + err); + if (!boolItemOpt) { + DEBUG_FUNCTION_LINE_ERR("Failed to create bool item: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + // Add bool item to category + if (!categoryOpt->add(std::move(*boolItemOpt), err)) { + DEBUG_FUNCTION_LINE_ERR("Failed to add bool item to category: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + // Add category to root. + if (!root.add(std::move(*categoryOpt), err)) { + DEBUG_FUNCTION_LINE_ERR("Failed to add category to root: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS; +} + +void ConfigMenuClosedCallback() { + // Save all changes + if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to close storage"); + } +} + +/** + Gets called ONCE when the plugin was loaded. +**/ +INITIALIZE_PLUGIN() { + // Logging only works when compiled with `make DEBUG=1`. See the README for more information. + initLogging(); + DEBUG_FUNCTION_LINE("INITIALIZE_PLUGIN of example_plugin!"); + + WUPSConfigAPIOptionsV1 configOptions = {.name = "example_plugin"}; + if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to init config api"); + } + + // Open storage to read values + WUPSStorageError storageRes = WUPS_OpenStorage(); + if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); + } else { + // Try to get value from storage + if ((storageRes = WUPS_GetBool(nullptr, LOG_FS_OPEN_CONFIG_ID, &sLogFSOpen)) == WUPS_STORAGE_ERROR_NOT_FOUND) { + // Add the value to the storage if it's missing. + if (WUPS_StoreBool(nullptr, LOG_FS_OPEN_CONFIG_ID, sLogFSOpen) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("Failed to store bool"); + } + } else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); + } + + // Close storage + if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("Failed to close storage"); + } + } + deinitLogging(); +} + +/** + Gets called when the plugin will be unloaded. +**/ +DEINITIALIZE_PLUGIN() { + DEBUG_FUNCTION_LINE("DEINITIALIZE_PLUGIN of example_plugin!"); +} + +/** + Gets called when an application starts. +**/ +ON_APPLICATION_START() { + initLogging(); + + DEBUG_FUNCTION_LINE("ON_APPLICATION_START of example_plugin!"); +} + +/** + * Gets called when an application actually ends + */ +ON_APPLICATION_ENDS() { + deinitLogging(); +} + +/** + Gets called when an application request to exit. +**/ +ON_APPLICATION_REQUESTS_EXIT() { + DEBUG_FUNCTION_LINE_INFO("ON_APPLICATION_REQUESTS_EXIT of example_plugin!"); +} + +/** + This defines a function replacement. + It allows to replace the system function with an own function. + So whenever a game / application calls an overridden function, your function gets called instead. + + Currently it's only possible to override functions that are loaded from .rpl files of OSv10 (00050010-1000400A). + + Signature of this macro: + DECL_FUNCTION( RETURN_TYPE, ARBITRARY_NAME_OF_FUNCTION , ARGS_SEPERATED_BY_COMMA){ + //Your code goes here. + } + + Within this macro, two more function get declare you can use. + my_ARBITRARY_NAME_OF_FUNCTION and real_ARBITRARY_NAME_OF_FUNCTION + + RETURN_TYPE my_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA): + is just name of the function that gets declared in this macro. + It has the same effect as calling the overridden function directly. + + RETURN_TYPE real_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA): + is the name of the function, that leads to function that was overridden. + Use this to call the original function that will be overridden. + CAUTION: Other plugins may already have manipulated the return value or arguments. + + Use this macro for each function you want to override +**/ +DECL_FUNCTION(int, FSOpenFile, FSClient *pClient, FSCmdBlock *pCmd, const char *path, const char *mode, int *handle, int error) { + int result = real_FSOpenFile(pClient, pCmd, path, mode, handle, error); + if (sLogFSOpen) { + DEBUG_FUNCTION_LINE_INFO("FSOpenFile called for folder %s! Result %d", path, result); + } + return result; +} + +/** +This tells the loader which functions from which library (.rpl) should be replaced with which function from this file. +The list of possible libraries can be found in the wiki. (In general it's WUPS_LOADER_LIBRARY_ + the name of the RPL in caps lock) + +WUPS_MUST_REPLACE(FUNCTION_NAME_IN_THIS_FILE, NAME_OF_LIB_WHICH_CONTAINS_THIS_FUNCTION, NAME_OF_FUNCTION_TO_OVERRIDE) + +Define this for each function you want to override. +**/ +WUPS_MUST_REPLACE(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile); \ No newline at end of file diff --git a/plugins/example_plugin_cpp/src/utils/logger.c b/plugins/example_plugin_cpp/src/utils/logger.c new file mode 100644 index 0000000..f700806 --- /dev/null +++ b/plugins/example_plugin_cpp/src/utils/logger.c @@ -0,0 +1,36 @@ +#ifdef DEBUG +#include +#include +#include +#include + +uint32_t moduleLogInit = false; +uint32_t cafeLogInit = false; +uint32_t udpLogInit = false; +#endif // DEBUG + +void initLogging() { +#ifdef DEBUG + if (!(moduleLogInit = WHBLogModuleInit())) { + cafeLogInit = WHBLogCafeInit(); + udpLogInit = WHBLogUdpInit(); + } +#endif // DEBUG +} + +void deinitLogging() { +#ifdef DEBUG + if (moduleLogInit) { + WHBLogModuleDeinit(); + moduleLogInit = false; + } + if (cafeLogInit) { + WHBLogCafeDeinit(); + cafeLogInit = false; + } + if (udpLogInit) { + WHBLogUdpDeinit(); + udpLogInit = false; + } +#endif // DEBUG +} \ No newline at end of file diff --git a/plugins/example_plugin_cpp/src/utils/logger.h b/plugins/example_plugin_cpp/src/utils/logger.h new file mode 100644 index 0000000..5f195ba --- /dev/null +++ b/plugins/example_plugin_cpp/src/utils/logger.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_APP_TYPE "P" +#define LOG_APP_NAME "ExamplePlugin" + +#define __FILENAME__ ({ \ + const char *__filename = __FILE__; \ + const char *__pos = strrchr(__filename, '/'); \ + if (!__pos) __pos = strrchr(__filename, '\\'); \ + __pos ? __pos + 1 : __filename; \ +}) + +#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS) + +#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS) + +#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \ + do { \ + LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \ + } while (0) + +#ifdef DEBUG + +#ifdef VERBOSE_DEBUG +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS); +#else +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) +#endif + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS); + +#else + +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) + +#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...) 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); + +#endif + +void initLogging(); + +void deinitLogging(); + +#ifdef __cplusplus +} +#endif