diff --git a/Dockerfile b/Dockerfile
index 0abe4b9..ab7d5b7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,9 @@
-FROM wiiuenv/devkitppc:20210917
+FROM wiiuenv/devkitppc:20210920
-COPY --from=wiiuenv/wiiumodulesystem:20210917 /artifacts $DEVKITPRO
-COPY --from=wiiuenv/wiiupluginsystem:20210917 /artifacts $DEVKITPRO
-COPY --from=wiiuenv/libfunctionpatcher:20210109 /artifacts $DEVKITPRO
+COPY --from=wiiuenv/wiiumodulesystem:20210924 /artifacts $DEVKITPRO
+COPY --from=wiiuenv/wiiupluginsystem:20210924 /artifacts $DEVKITPRO
+COPY --from=wiiuenv/libfunctionpatcher:20210924 /artifacts $DEVKITPRO
+COPY --from=wiiuenv/libmappedmemory:20210924 /artifacts $DEVKITPRO
+COPY --from=wiiuenv/libwupsbackend:20210924 /artifacts $DEVKITPRO
WORKDIR project
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 187502b..9c052c6 100644
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,8 @@ WUT_ROOT := $(DEVKITPRO)/wut
TARGET := PluginBackend
BUILD := build
SOURCES := source \
+ source/fs \
+ source/config \
source/elfio \
source/patcher \
source/plugin \
@@ -41,9 +43,9 @@ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
-LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS)
+LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld -T$(WUMS_ROOT)/share/libmappedmemory.ld $(WUMSSPECS)
-LIBS := -lwums -lwut -lwups -lfunctionpatcher -lz
+LIBS := -lwums -lwut -lwups -lfunctionpatcher -lmappedmemory -lfreetype -lbz2 -lpng -lz
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
@@ -93,7 +95,7 @@ export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
- -I$(CURDIR)/$(BUILD)
+ -I$(CURDIR)/$(BUILD) -I$(DEVKITPRO)/portlibs/ppc/include/freetype2
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
diff --git a/source/PluginManagement.cpp b/source/PluginManagement.cpp
index c06566d..cf841d2 100644
--- a/source/PluginManagement.cpp
+++ b/source/PluginManagement.cpp
@@ -155,6 +155,7 @@ void PluginManagement::unloadPlugins(plugin_information_t *gPluginInformation, M
}
void PluginManagement::callInitHooks(plugin_information_t *pluginInformation) {
+ CallHook(pluginInformation, WUPS_LOADER_HOOK_INIT_STORAGE);
CallHook(pluginInformation, WUPS_LOADER_HOOK_INIT_PLUGIN);
DEBUG_FUNCTION_LINE_VERBOSE("Done calling init hooks");
}
diff --git a/source/common/plugin_defines.h b/source/common/plugin_defines.h
index 56ff17c..a158149 100644
--- a/source/common/plugin_defines.h
+++ b/source/common/plugin_defines.h
@@ -56,6 +56,7 @@ struct plugin_meta_info_t {
char license[MAXIMUM_PLUGIN_META_FIELD_LENGTH] = "";
char buildTimestamp[MAXIMUM_PLUGIN_META_FIELD_LENGTH] = "";
char descripion[MAXIMUM_PLUGIN_DESCRIPTION_LENGTH] = "";
+ char id[MAXIMUM_PLUGIN_META_FIELD_LENGTH] = "";
uint32_t size;
};
diff --git a/source/config/WUPSConfig.h b/source/config/WUPSConfig.h
new file mode 100644
index 0000000..9b4b47e
--- /dev/null
+++ b/source/config/WUPSConfig.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+ * Copyright (C) 2018-2021 Maschell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ ****************************************************************************/
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "WUPSConfigCategory.h"
+#include "utils/logger.h"
+
+class WUPSConfig {
+public:
+ explicit WUPSConfig(const std::string &name) {
+ this->name = name;
+ }
+
+ ~WUPSConfig() {
+ for (auto &element : categories) {
+ delete element;
+ }
+ }
+
+ /**
+ \return Returns the name of this WUPSConfig
+ **/
+ std::string getName() {
+ return this->name;
+ }
+
+ /**
+ \brief Creates a new WUPSCategory add its to this WUPSConfig.
+ The category will be added to the end of the list.
+ This class holds responsibility for deleting the created instance.
+
+ \param categoryName: The name of the category that will be created.
+
+ \return On success, the created and inserted category will be returned.
+ **/
+ std::optional addCategory(const std::string &categoryName) {
+ auto curCat = new WUPSConfigCategory(categoryName);
+ if (curCat == nullptr) {
+ return {};
+ }
+ categories.push_back(curCat);
+ return curCat;
+ }
+
+ /**
+ \brief Adds a given WUPSConfigCategory to this WUPSConfig.
+ The category will be added to the end of the list.
+ This class holds responsibility for deleting the created instance.
+
+ \param category: The category that will be added to this config.
+
+ \return On success, the inserted category will be returned.
+ On error NULL will be returned. In this case the caller still has the responsibility
+ for deleting the WUPSConfigCategory instance.
+ **/
+ WUPSConfigCategory *addCategory(WUPSConfigCategory *category) {
+ categories.push_back(category);
+ return category;
+ }
+
+ /**
+ \return Returns a vector with all categories.
+ **/
+ const std::vector &getCategories() {
+ return this->categories;
+ }
+
+private:
+ std::string name;
+ std::vector categories = {};
+};
diff --git a/source/config/WUPSConfigCategory.h b/source/config/WUPSConfigCategory.h
new file mode 100644
index 0000000..71b09c3
--- /dev/null
+++ b/source/config/WUPSConfigCategory.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+ * Copyright (C) 2018-2021 Maschell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ ****************************************************************************/
+
+#pragma once
+
+#include
+#include
+#include
+#include "WUPSConfigItem.h"
+
+class WUPSConfigCategory {
+public:
+ explicit WUPSConfigCategory(const std::string &name) {
+ this->name = name;
+ }
+
+ ~WUPSConfigCategory() {
+ for (auto &element : items) {
+ delete element;
+ }
+ }
+
+ /**
+ \return Returns the name of this WUPSConfigCategory
+ **/
+ [[nodiscard]] const std::string &getName() const {
+ return this->name;
+ }
+
+ /**
+ \brief Adds a given WUPSConfigItem to this WUPSConfigCategory.
+ The item will be added to the end of the list.
+ This class holds responsibility for deleting the created instance.
+
+ \param item: The item that will be added to this config.
+
+ \return On success, true will be returned.
+ On error false will be returned. In this case the caller still has the responsibility
+ for deleting the WUPSConfigItem instance.
+ **/
+ [[nodiscard]] bool addItem(WUPSConfigItem *item) {
+ if(item != nullptr){
+ items.push_back(item);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ \return Returns a vector with all items.
+ **/
+ [[nodiscard]] const std::vector &getItems() const {
+ return this->items;
+ }
+
+private:
+ std::string name;
+ std::vector items{};
+};
diff --git a/source/config/WUPSConfigCategoryExport.cpp b/source/config/WUPSConfigCategoryExport.cpp
new file mode 100644
index 0000000..cc0bb12
--- /dev/null
+++ b/source/config/WUPSConfigCategoryExport.cpp
@@ -0,0 +1,81 @@
+#include
+#include
+#include "WUPSConfigCategory.h"
+#include "../utils/logger.h"
+
+int32_t WUPSConfigCategory_Create(WUPSConfigCategoryHandle *out, const char *name) {
+ if (name == nullptr || out == nullptr) {
+ return -1;
+ }
+
+ *out = (WUPSConfigCategoryHandle) new WUPSConfigCategory(name);
+ if (*out != 0) {
+ return 0;
+ }
+ return -2;
+};
+
+int32_t WUPSConfigCategory_Destroy(WUPSConfigCategoryHandle handle) {
+ if (handle == 0) {
+ return -1;
+ }
+
+ auto *config = reinterpret_cast(handle);
+ delete config;
+ return 0;
+};
+
+int32_t WUPSConfigCategory_GetName(WUPSConfigCategoryHandle handle, char *out_buf, int32_t out_len) {
+ if (out_buf == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ snprintf(out_buf, out_len, "%s", config->getName().c_str());
+ return 0;
+}
+
+int32_t WUPSConfigCategory_AddItem(WUPSConfigCategoryHandle handle, WUPSConfigItemHandle item_Handle) {
+ if (handle == 0 || item_Handle == 0) {
+ return -1;
+ }
+ auto *category = reinterpret_cast(handle);
+ auto *item = reinterpret_cast(item_Handle);
+ if (category->addItem(item)) {
+ return 0;
+ }
+ return -2;
+}
+/*
+int32_t WUPSConfigCategory_GetItemCount(WUPSConfigCategoryHandle handle, int32_t *item_count) {
+ if (handle == 0 || item_count == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ *item_count = config->getItems().size();
+ return 0;
+}
+
+int32_t WUPSConfigCategory_GetItems(WUPSConfigCategoryHandle handle, WUPSConfigItemHandle *items_out, int32_t items_out_size) {
+ if (handle == 0 || items_out == nullptr || items_out_size == 0) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ auto items = config->getItems();
+ int32_t index = 0;
+ for (auto const &item: items) {
+ if (index >= items_out_size) {
+ break;
+ }
+ items_out[index] = (WUPSConfigItemHandle) item;
+ }
+
+ return 0;
+}*/
+
+WUMS_EXPORT_FUNCTION(WUPSConfigCategory_Create);
+WUMS_EXPORT_FUNCTION(WUPSConfigCategory_Destroy);
+WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetName);
+WUMS_EXPORT_FUNCTION(WUPSConfigCategory_AddItem);
+/*
+WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetItemCount);
+WUMS_EXPORT_FUNCTION(WUPSConfigCategory_GetItems);*/
\ No newline at end of file
diff --git a/source/config/WUPSConfigExport.cpp b/source/config/WUPSConfigExport.cpp
new file mode 100644
index 0000000..b14f3d1
--- /dev/null
+++ b/source/config/WUPSConfigExport.cpp
@@ -0,0 +1,97 @@
+#include
+#include
+#include "WUPSConfig.h"
+#include "../utils/logger.h"
+
+int32_t WUPSConfig_Create(WUPSConfigHandle *out, const char *name) {
+ if (name == nullptr || out == nullptr) {
+ return -1;
+ }
+
+ *out = (WUPSConfigHandle) new WUPSConfig(name);
+ if (*out != 0) {
+ return 0;
+ }
+ return -2;
+};
+
+int32_t WUPSConfig_Destroy(WUPSConfigHandle handle) {
+ if (handle == 0) {
+ return -1;
+ }
+
+ auto *config = reinterpret_cast(handle);
+ delete config;
+ return 0;
+};
+
+int32_t WUPSConfig_GetName(WUPSConfigHandle handle, char *out_buf, int32_t out_len) {
+ if (out_buf == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ snprintf(out_buf, out_len, "%s", config->getName().c_str());
+ DEBUG_FUNCTION_LINE("%s", out_buf);
+ return 0;
+}
+
+int32_t WUPSConfig_AddCategoryByName(WUPSConfigHandle handle, const char *categoryName, WUPSConfigCategoryHandle *out) {
+ if (categoryName == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ auto res = config->addCategory(std::string(categoryName));
+ if (res.has_value()) {
+ if (out != nullptr) {
+ *out = reinterpret_cast(res.value());
+ }else{
+ return -3;
+ }
+ return 0;
+ }
+ return -2;
+}
+
+int32_t WUPSConfig_AddCategory(WUPSConfigHandle handle, WUPSConfigCategoryHandle category) {
+ auto *config = reinterpret_cast(handle);
+ auto res = config->addCategory(reinterpret_cast(category));
+ if (res == nullptr) {
+ return -1;
+ }
+ return 0;
+}
+/*
+int32_t WUPSConfig_GetCategoryCount(WUPSConfigHandle handle, int32_t *category_count) {
+ if (category_count == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ *category_count = config->getCategories().size();
+ return 0;
+}
+
+int32_t WUPSConfig_GetCategories(WUPSConfigHandle handle, WUPSConfigCategoryHandle *categories_out, int32_t categories_out_size) {
+ if (categories_out == nullptr || categories_out_size == 0) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ auto cats = config->getCategories();
+ int32_t index = 0;
+ for (auto const &cat: cats) {
+ if (index >= categories_out_size) {
+ break;
+ }
+ categories_out[index] = (WUPSConfigCategoryHandle) cat;
+ }
+
+ return 0;
+}*/
+
+WUMS_EXPORT_FUNCTION(WUPSConfig_Create);
+WUMS_EXPORT_FUNCTION(WUPSConfig_Destroy);
+WUMS_EXPORT_FUNCTION(WUPSConfig_GetName);
+WUMS_EXPORT_FUNCTION(WUPSConfig_AddCategoryByName);
+WUMS_EXPORT_FUNCTION(WUPSConfig_AddCategory);
+/*
+WUMS_EXPORT_FUNCTION(WUPSConfig_GetCategoryCount);
+WUMS_EXPORT_FUNCTION(WUPSConfig_GetCategories);*/
\ No newline at end of file
diff --git a/source/config/WUPSConfigItem.h b/source/config/WUPSConfigItem.h
new file mode 100644
index 0000000..85873cf
--- /dev/null
+++ b/source/config/WUPSConfigItem.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+ * Copyright (C) 2018 Maschell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ ****************************************************************************/
+
+#pragma once
+
+#include
+#include
+
+#include
+#include "utils/StringTools.h"
+#include "utils/logger.h"
+
+class WUPSConfigItem {
+public:
+ /**
+ Sets the display name of this WUPSConfigItem
+ This is the value which will be shown in the configuration menu.
+ **/
+ virtual void setDisplayName(const std::string &_displayName) {
+ this->displayName = _displayName;
+ }
+
+ /**
+ \return Returns the display name of this WUPSConfigItem
+ **/
+ virtual const std::string &getDisplayName() {
+ return this->displayName;
+ }
+
+ /**
+ Sets the config ID name of this WUPSConfigItem.
+ This config ID is used to persist the configuration values and needs
+ to be unique in the context of this WUPSConfig.
+ Items in different categories are NOT allowed to have the config ID.
+ **/
+ virtual void setConfigID(const std::string &_configID) {
+ this->configID = _configID;
+ }
+
+ /**
+ \return Returns the configID of this WUPSConfigItem.
+ **/
+ [[nodiscard]] virtual const std::string &getConfigID() const {
+ return this->configID;
+ }
+
+ /**
+ Returns a string that displays the current value.
+ This string is shown next to the display name when the cursor is NOT on this item
+ **/
+ [[nodiscard]] std::string getCurrentValueDisplay() const {
+ if (this->callbacks.getCurrentValueDisplay != nullptr) {
+ char buf[256];
+ int res = this->callbacks.getCurrentValueDisplay(context, buf, sizeof(buf));
+ if (res == 0) {
+ return buf;
+ } else {
+ return StringTools::strfmt("[ERROR %d]", res);
+ }
+ }
+ return "NOT_IMPLEMENTED";
+ }
+
+ /**
+ Returns a string that displays the current value when selected.
+ This string is shown next to the display name when the cursor IS on this item
+ **/
+ [[nodiscard]] std::string getCurrentValueSelectedDisplay() const {
+ if (this->callbacks.getCurrentValueSelectedDisplay != nullptr) {
+ char buf[256];
+ int res = this->callbacks.getCurrentValueSelectedDisplay(context, buf, sizeof(buf));
+ if (res == 0) {
+ return buf;
+ } else {
+ return StringTools::strfmt("[ERROR %d]", res);
+ }
+ }
+ return "NOT_IMPLEMENTED";
+ }
+
+ /**
+ Is called when the cursor enters or leaves the item.
+ When the cursor enters the item, "isSelected" will be true.
+ When the cursor leaves the item, "isSelected" will be false.
+ **/
+ void onSelected(bool isSelected) const {
+ if (this->callbacks.onSelected != nullptr) {
+ this->callbacks.onSelected(context, isSelected);
+ }
+ }
+
+ /**
+ Is called when a button is pressed while the cursor on this item.
+ See the WUPSConfigButtons enum for possible values.
+ **/
+ void onButtonPressed(WUPSConfigButtons buttons) const {
+ if (this->callbacks.onButtonPressed != nullptr) {
+ this->callbacks.onButtonPressed(context, buttons);
+ }
+ }
+
+ /**
+ When the cursor is on this item, the configuration menu asks this item
+ if it's allowed to leave it.
+ If it returns true, the item can be leaved.
+ It it returns false, leaves is not allowed.
+ **/
+ [[nodiscard]] bool isMovementAllowed() const {
+ if (this->callbacks.isMovementAllowed != nullptr) {
+ return this->callbacks.isMovementAllowed(context);
+ }
+ return false;
+ }
+
+ /**
+ Restores the default value
+ **/
+ void restoreDefault() {
+ if (this->callbacks.restoreDefault != nullptr) {
+ this->callbacks.restoreDefault(context);
+ }
+ }
+
+ /**
+ Call callback with with current value.
+ This function will be called whenever this item should call it's (optional) given
+ callback with the current value.
+ Returns true if a valid callback could be called
+ Returns false if no callback was called (e.g. callback was NULL)
+ **/
+ bool callCallback() {
+ if (this->callbacks.callCallback != nullptr) {
+ return this->callbacks.callCallback(context);
+ }
+ return false;
+ }
+
+ bool isDirty() {
+ return defaultValue != getCurrentValueDisplay();
+ }
+
+ WUPSConfigItem(const std::string &_configID, const std::string &_displayName, WUPSConfigCallbacks_t callbacks, void *_context) {
+ this->configID = _configID;
+ this->displayName = _displayName;
+ this->context = _context;
+ this->callbacks = callbacks;
+ this->defaultValue = getCurrentValueDisplay();
+ }
+
+ virtual ~WUPSConfigItem() {
+ if (this->callbacks.onDelete != nullptr) {
+ this->callbacks.onDelete(context);
+ }
+ };
+
+private:
+ void *context;
+ std::string displayName;
+ std::string configID;
+ std::string defaultValue;
+ WUPSConfigCallbacks_t callbacks{};
+};
diff --git a/source/config/WUPSConfigItemExport.cpp b/source/config/WUPSConfigItemExport.cpp
new file mode 100644
index 0000000..798cb32
--- /dev/null
+++ b/source/config/WUPSConfigItemExport.cpp
@@ -0,0 +1,72 @@
+#include
+#include
+#include "WUPSConfigItem.h"
+
+typedef uint32_t WUPSConfigItemHandle;
+
+int32_t WUPSConfigItem_Create(WUPSConfigItemHandle *out, const char *configID, const char *displayName, WUPSConfigCallbacks_t callbacks, void* context) {
+ if (configID == nullptr || displayName == nullptr) {
+ return -1;
+ }
+
+ *out = (WUPSConfigItemHandle) new WUPSConfigItem(configID, displayName, callbacks, context);
+ if (*out != 0) {
+ return 0;
+ }
+ return -2;
+};
+
+int32_t WUPSConfigItem_Destroy(WUPSConfigItemHandle handle) {
+ if (handle == 0) {
+ return -1;
+ }
+
+ auto *config = reinterpret_cast(handle);
+ delete config;
+ return 0;
+};
+
+int32_t WUPSConfigItem_SetDisplayName(WUPSConfigItemHandle handle, const char *displayName) {
+ if (displayName == nullptr) {
+ return -1;
+ }
+
+ auto *config = reinterpret_cast(handle);
+ config->setDisplayName(displayName);
+ return 0;
+};
+
+int32_t WUPSConfigItem_GetDisplayName(WUPSConfigItemHandle handle, char *out_buf, int32_t out_len) {
+ if (out_buf == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ snprintf(out_buf, out_len, "%s", config->getDisplayName().c_str());
+ return 0;
+}
+
+int32_t WUPSConfigItem_SetConfigID(WUPSConfigItemHandle handle, const char *configID) {
+ if (configID == nullptr) {
+ return -1;
+ }
+
+ auto *config = reinterpret_cast(handle);
+ config->setConfigID(configID);
+ return 0;
+};
+
+int32_t WUPSConfigItem_GetConfigID(WUPSConfigItemHandle handle, char *out_buf, int32_t out_len) {
+ if (out_buf == nullptr) {
+ return -1;
+ }
+ auto *config = reinterpret_cast(handle);
+ snprintf(out_buf, out_len, "%s", config->getConfigID().c_str());
+ return 0;
+}
+
+WUMS_EXPORT_FUNCTION(WUPSConfigItem_Create);
+WUMS_EXPORT_FUNCTION(WUPSConfigItem_Destroy);
+WUMS_EXPORT_FUNCTION(WUPSConfigItem_SetDisplayName);
+WUMS_EXPORT_FUNCTION(WUPSConfigItem_GetDisplayName);
+WUMS_EXPORT_FUNCTION(WUPSConfigItem_SetConfigID);
+WUMS_EXPORT_FUNCTION(WUPSConfigItem_GetConfigID);
\ No newline at end of file
diff --git a/source/fs/CFile.cpp b/source/fs/CFile.cpp
new file mode 100644
index 0000000..c8e78d2
--- /dev/null
+++ b/source/fs/CFile.cpp
@@ -0,0 +1,175 @@
+
+#include
+#include
+#include
+#include
+#include
+
+CFile::CFile() {
+ iFd = -1;
+ mem_file = NULL;
+ filesize = 0;
+ pos = 0;
+}
+
+CFile::CFile(const std::string &filepath, eOpenTypes mode) {
+ iFd = -1;
+ this->open(filepath, mode);
+}
+
+CFile::CFile(const uint8_t *mem, int32_t size) {
+ iFd = -1;
+ this->open(mem, size);
+}
+
+CFile::~CFile() {
+ this->close();
+}
+
+int32_t CFile::open(const std::string &filepath, eOpenTypes mode) {
+ this->close();
+ int32_t openMode = 0;
+
+ // This depend on the devoptab implementation.
+ // see https://github.com/devkitPro/wut/blob/master/libraries/wutdevoptab/devoptab_fs_open.c#L21 fpr reference
+
+ switch (mode) {
+ default:
+ case ReadOnly: // file must exist
+ openMode = O_RDONLY;
+ break;
+ case WriteOnly: // file will be created / zerod
+ openMode = O_TRUNC | O_CREAT | O_WRONLY;
+ break;
+ case ReadWrite: // file must exist
+ openMode = O_RDWR;
+ break;
+ case Append: // append to file, file will be created if missing. write only
+ openMode = O_CREAT | O_APPEND | O_WRONLY;
+ break;
+ }
+
+ //! Using fopen works only on the first launch as expected
+ //! on the second launch it causes issues because we don't overwrite
+ //! the .data sections which is needed for a normal application to re-init
+ //! this will be added with launching as RPX
+ iFd = ::open(filepath.c_str(), openMode);
+ if (iFd < 0)
+ return iFd;
+
+
+ filesize = ::lseek(iFd, 0, SEEK_END);
+ ::lseek(iFd, 0, SEEK_SET);
+
+ return 0;
+}
+
+int32_t CFile::open(const uint8_t *mem, int32_t size) {
+ this->close();
+
+ mem_file = mem;
+ filesize = size;
+
+ return 0;
+}
+
+void CFile::close() {
+ if (iFd >= 0)
+ ::close(iFd);
+
+ iFd = -1;
+ mem_file = NULL;
+ filesize = 0;
+ pos = 0;
+}
+
+int32_t CFile::read(uint8_t *ptr, size_t size) {
+ if (iFd >= 0) {
+ int32_t ret = ::read(iFd, ptr, size);
+ if (ret > 0)
+ pos += ret;
+ return ret;
+ }
+
+ int32_t readsize = size;
+
+ if (readsize > (int64_t) (filesize - pos))
+ readsize = filesize - pos;
+
+ if (readsize <= 0)
+ return readsize;
+
+ if (mem_file != NULL) {
+ memcpy(ptr, mem_file + pos, readsize);
+ pos += readsize;
+ return readsize;
+ }
+
+ return -1;
+}
+
+int32_t CFile::write(const uint8_t *ptr, size_t size) {
+ if (iFd >= 0) {
+ size_t done = 0;
+ while (done < size) {
+ int32_t ret = ::write(iFd, ptr, size - done);
+ if (ret <= 0)
+ return ret;
+
+ ptr += ret;
+ done += ret;
+ pos += ret;
+ }
+ return done;
+ }
+
+ return -1;
+}
+
+int32_t CFile::seek(long int offset, int32_t origin) {
+ int32_t ret = 0;
+ int64_t newPos = pos;
+
+ if (origin == SEEK_SET) {
+ newPos = offset;
+ } else if (origin == SEEK_CUR) {
+ newPos += offset;
+ } else if (origin == SEEK_END) {
+ newPos = filesize + offset;
+ }
+
+ if (newPos < 0) {
+ pos = 0;
+ } else {
+ pos = newPos;
+ }
+
+ if (iFd >= 0)
+ ret = ::lseek(iFd, pos, SEEK_SET);
+
+ if (mem_file != NULL) {
+ if (pos > filesize) {
+ pos = filesize;
+ }
+ }
+
+ return ret;
+}
+
+int32_t CFile::fwrite(const char *format, ...) {
+ char tmp[512];
+ tmp[0] = 0;
+ int32_t result = -1;
+
+ va_list va;
+ va_start(va, format);
+ if ((vsprintf(tmp, format, va) >= 0)) {
+ result = this->write((uint8_t *) tmp, strlen(tmp));
+ }
+ va_end(va);
+
+
+ return result;
+}
+
+
diff --git a/source/fs/CFile.hpp b/source/fs/CFile.hpp
new file mode 100644
index 0000000..6c0421b
--- /dev/null
+++ b/source/fs/CFile.hpp
@@ -0,0 +1,71 @@
+#ifndef CFILE_HPP_
+#define CFILE_HPP_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+class CFile {
+public:
+ enum eOpenTypes {
+ ReadOnly,
+ WriteOnly,
+ ReadWrite,
+ Append
+ };
+
+ CFile();
+
+ CFile(const std::string &filepath, eOpenTypes mode);
+
+ CFile(const uint8_t *memory, int32_t memsize);
+
+ virtual ~CFile();
+
+ int32_t open(const std::string &filepath, eOpenTypes mode);
+
+ int32_t open(const uint8_t *memory, int32_t memsize);
+
+ BOOL isOpen() const {
+ if (iFd >= 0)
+ return true;
+
+ if (mem_file)
+ return true;
+
+ return false;
+ }
+
+ void close();
+
+ int32_t read(uint8_t *ptr, size_t size);
+
+ int32_t write(const uint8_t *ptr, size_t size);
+
+ int32_t fwrite(const char *format, ...);
+
+ int32_t seek(long int offset, int32_t origin);
+
+ uint64_t tell() {
+ return pos;
+ };
+
+ uint64_t size() {
+ return filesize;
+ };
+
+ void rewind() {
+ this->seek(0, SEEK_SET);
+ };
+
+protected:
+ int32_t iFd;
+ const uint8_t *mem_file;
+ uint64_t filesize;
+ uint64_t pos;
+};
+
+#endif
diff --git a/source/fs/DirList.cpp b/source/fs/DirList.cpp
new file mode 100644
index 0000000..ab2fea4
--- /dev/null
+++ b/source/fs/DirList.cpp
@@ -0,0 +1,218 @@
+/****************************************************************************
+ * Copyright (C) 2010
+ * by Dimok
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any
+ * damages arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose, including commercial applications, and to alter it and
+ * redistribute it freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you
+ * must not claim that you wrote the original software. If you use
+ * this software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and
+ * must not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ * DirList Class
+ * for WiiXplorer 2010
+ ***************************************************************************/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+DirList::DirList() {
+ Flags = 0;
+ Filter = 0;
+ Depth = 0;
+}
+
+DirList::DirList(const std::string &path, const char *filter, uint32_t flags, uint32_t maxDepth) {
+ this->LoadPath(path, filter, flags, maxDepth);
+ this->SortList();
+}
+
+DirList::~DirList() {
+ ClearList();
+}
+
+BOOL DirList::LoadPath(const std::string &folder, const char *filter, uint32_t flags, uint32_t maxDepth) {
+ if (folder.empty())
+ return false;
+
+ Flags = flags;
+ Filter = filter;
+ Depth = maxDepth;
+
+ std::string folderpath(folder);
+ uint32_t length = folderpath.size();
+
+ //! clear path of double slashes
+ StringTools::RemoveDoubleSlashs(folderpath);
+
+ //! remove last slash if exists
+ if (length > 0 && folderpath[length - 1] == '/')
+ folderpath.erase(length - 1);
+
+ //! add root slash if missing
+ if (folderpath.find('/') == std::string::npos) {
+ folderpath += '/';
+ }
+
+ return InternalLoadPath(folderpath);
+}
+
+BOOL DirList::InternalLoadPath(std::string &folderpath) {
+ if (folderpath.size() < 3)
+ return false;
+
+ struct dirent *dirent = NULL;
+ DIR *dir = NULL;
+
+ dir = opendir(folderpath.c_str());
+ if (dir == NULL)
+ return false;
+
+ while ((dirent = readdir(dir)) != 0) {
+ BOOL isDir = dirent->d_type & DT_DIR;
+ const char *filename = dirent->d_name;
+
+ if (isDir) {
+ if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
+ continue;
+
+ if ((Flags & CheckSubfolders) && (Depth > 0)) {
+ int32_t length = folderpath.size();
+ if (length > 2 && folderpath[length - 1] != '/') {
+ folderpath += '/';
+ }
+ folderpath += filename;
+
+ Depth--;
+ InternalLoadPath(folderpath);
+ folderpath.erase(length);
+ Depth++;
+ }
+
+ if (!(Flags & Dirs))
+ continue;
+ } else if (!(Flags & Files)) {
+ continue;
+ }
+
+ if (Filter) {
+ char *fileext = strrchr(filename, '.');
+ if (!fileext)
+ continue;
+
+ if (StringTools::strtokcmp(fileext, Filter, ",") == 0)
+ AddEntrie(folderpath, filename, isDir);
+ } else {
+ AddEntrie(folderpath, filename, isDir);
+ }
+ }
+ closedir(dir);
+
+ return true;
+}
+
+void DirList::AddEntrie(const std::string &filepath, const char *filename, BOOL isDir) {
+ if (!filename)
+ return;
+
+ int32_t pos = FileInfo.size();
+
+ FileInfo.resize(pos + 1);
+
+ FileInfo[pos].FilePath = (char *) malloc(filepath.size() + strlen(filename) + 2);
+ if (!FileInfo[pos].FilePath) {
+ FileInfo.resize(pos);
+ return;
+ }
+
+ sprintf(FileInfo[pos].FilePath, "%s/%s", filepath.c_str(), filename);
+ FileInfo[pos].isDir = isDir;
+}
+
+void DirList::ClearList() {
+ for (uint32_t i = 0; i < FileInfo.size(); ++i) {
+ if (FileInfo[i].FilePath) {
+ free(FileInfo[i].FilePath);
+ FileInfo[i].FilePath = NULL;
+ }
+ }
+
+ FileInfo.clear();
+ std::vector().swap(FileInfo);
+}
+
+const char *DirList::GetFilename(int32_t ind) const {
+ if (!valid(ind))
+ return "";
+
+ return StringTools::FullpathToFilename(FileInfo[ind].FilePath);
+}
+
+static BOOL SortCallback(const DirEntry &f1, const DirEntry &f2) {
+ if (f1.isDir && !(f2.isDir))
+ return true;
+ if (!(f1.isDir) && f2.isDir)
+ return false;
+
+ if (f1.FilePath && !f2.FilePath)
+ return true;
+ if (!f1.FilePath)
+ return false;
+
+ if (strcasecmp(f1.FilePath, f2.FilePath) > 0)
+ return false;
+
+ return true;
+}
+
+void DirList::SortList() {
+ if (FileInfo.size() > 1)
+ std::sort(FileInfo.begin(), FileInfo.end(), SortCallback);
+}
+
+void DirList::SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)) {
+ if (FileInfo.size() > 1)
+ std::sort(FileInfo.begin(), FileInfo.end(), SortFunc);
+}
+
+uint64_t DirList::GetFilesize(int32_t index) const {
+ struct stat st;
+ const char *path = GetFilepath(index);
+
+ if (!path || stat(path, &st) != 0)
+ return 0;
+
+ return st.st_size;
+}
+
+int32_t DirList::GetFileIndex(const char *filename) const {
+ if (!filename)
+ return -1;
+
+ for (uint32_t i = 0; i < FileInfo.size(); ++i) {
+ if (strcasecmp(GetFilename(i), filename) == 0)
+ return i;
+ }
+
+ return -1;
+}
diff --git a/source/fs/DirList.h b/source/fs/DirList.h
new file mode 100644
index 0000000..08b3fbc
--- /dev/null
+++ b/source/fs/DirList.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+ * Copyright (C) 2010
+ * by Dimok
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any
+ * damages arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose, including commercial applications, and to alter it and
+ * redistribute it freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you
+ * must not claim that you wrote the original software. If you use
+ * this software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and
+ * must not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ * DirList Class
+ * for WiiXplorer 2010
+ ***************************************************************************/
+#ifndef ___DIRLIST_H_
+#define ___DIRLIST_H_
+
+#include
+#include
+#include
+
+typedef struct {
+ char *FilePath;
+ BOOL isDir;
+} DirEntry;
+
+class DirList {
+public:
+ //!Constructor
+ DirList(void);
+
+ //!\param path Path from where to load the filelist of all files
+ //!\param filter A fileext that needs to be filtered
+ //!\param flags search/filter flags from the enum
+ DirList(const std::string &path, const char *filter = NULL, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff);
+
+ //!Destructor
+ virtual ~DirList();
+
+ //! Load all the files from a directory
+ BOOL LoadPath(const std::string &path, const char *filter = NULL, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff);
+
+ //! Get a filename of the list
+ //!\param list index
+ const char *GetFilename(int32_t index) const;
+
+ //! Get the a filepath of the list
+ //!\param list index
+ const char *GetFilepath(int32_t index) const {
+ if (!valid(index))
+ return "";
+ else
+ return FileInfo[index].FilePath;
+ }
+
+ //! Get the a filesize of the list
+ //!\param list index
+ uint64_t GetFilesize(int32_t index) const;
+
+ //! Is index a dir or a file
+ //!\param list index
+ BOOL IsDir(int32_t index) const {
+ if (!valid(index))
+ return false;
+ return FileInfo[index].isDir;
+ };
+
+ //! Get the filecount of the whole list
+ int32_t GetFilecount() const {
+ return FileInfo.size();
+ };
+
+ //! Sort list by filepath
+ void SortList();
+
+ //! Custom sort command for custom sort functions definitions
+ void SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b));
+
+ //! Get the index of the specified filename
+ int32_t GetFileIndex(const char *filename) const;
+
+ //! Enum for search/filter flags
+ enum {
+ Files = 0x01,
+ Dirs = 0x02,
+ CheckSubfolders = 0x08,
+ };
+protected:
+ // Internal parser
+ BOOL InternalLoadPath(std::string &path);
+
+ //!Add a list entrie
+ void AddEntrie(const std::string &filepath, const char *filename, BOOL isDir);
+
+ //! Clear the list
+ void ClearList();
+
+ //! Check if valid pos is requested
+ inline BOOL valid(uint32_t pos) const {
+ return (pos < FileInfo.size());
+ };
+
+ uint32_t Flags;
+ uint32_t Depth;
+ const char *Filter;
+ std::vector FileInfo;
+};
+
+#endif
diff --git a/source/fs/FSUtils.cpp b/source/fs/FSUtils.cpp
new file mode 100644
index 0000000..c51a254
--- /dev/null
+++ b/source/fs/FSUtils.cpp
@@ -0,0 +1,142 @@
+#include
+#include
+#include
+#include
+#include
+#include "fs/FSUtils.h"
+#include "fs/CFile.hpp"
+#include "utils/logger.h"
+
+int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) {
+ //! always initialze input
+ *inbuffer = NULL;
+ if (size)
+ *size = 0;
+
+ int32_t iFd = open(filepath, O_RDONLY);
+ if (iFd < 0)
+ return -1;
+
+ uint32_t filesize = lseek(iFd, 0, SEEK_END);
+ lseek(iFd, 0, SEEK_SET);
+
+ uint8_t *buffer = (uint8_t *) malloc(filesize);
+ if (buffer == NULL) {
+ close(iFd);
+ return -2;
+ }
+
+ uint32_t blocksize = 0x4000;
+ uint32_t done = 0;
+ int32_t readBytes = 0;
+
+ while (done < filesize) {
+ if (done + blocksize > filesize) {
+ blocksize = filesize - done;
+ }
+ readBytes = read(iFd, buffer + done, blocksize);
+ if (readBytes <= 0)
+ break;
+ done += readBytes;
+ }
+
+ close(iFd);
+
+ if (done != filesize) {
+ free(buffer);
+ buffer = NULL;
+ return -3;
+ }
+
+ *inbuffer = buffer;
+
+ //! sign is optional input
+ if (size) {
+ *size = filesize;
+ }
+
+ return filesize;
+}
+
+int32_t FSUtils::CheckFile(const char *filepath) {
+ if (!filepath)
+ return 0;
+
+ struct stat filestat;
+
+ char dirnoslash[strlen(filepath) + 2];
+ snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath);
+
+ while (dirnoslash[strlen(dirnoslash) - 1] == '/')
+ dirnoslash[strlen(dirnoslash) - 1] = '\0';
+
+ char *notRoot = strrchr(dirnoslash, '/');
+ if (!notRoot) {
+ strcat(dirnoslash, "/");
+ }
+
+ if (stat(dirnoslash, &filestat) == 0)
+ return 1;
+
+ return 0;
+}
+
+int32_t FSUtils::CreateSubfolder(const char *fullpath) {
+ if (!fullpath)
+ return 0;
+
+ int32_t result = 0;
+
+ char dirnoslash[strlen(fullpath) + 1];
+ strcpy(dirnoslash, fullpath);
+
+ int32_t pos = strlen(dirnoslash) - 1;
+ while (dirnoslash[pos] == '/') {
+ dirnoslash[pos] = '\0';
+ pos--;
+ }
+
+ if (CheckFile(dirnoslash)) {
+ return 1;
+ } else {
+ char parentpath[strlen(dirnoslash) + 2];
+ strcpy(parentpath, dirnoslash);
+ char *ptr = strrchr(parentpath, '/');
+
+ if (!ptr) {
+ //!Device root directory (must be with '/')
+ strcat(parentpath, "/");
+ struct stat filestat;
+ if (stat(parentpath, &filestat) == 0)
+ return 1;
+
+ return 0;
+ }
+
+ ptr++;
+ ptr[0] = '\0';
+
+ result = CreateSubfolder(parentpath);
+ }
+
+ if (!result)
+ return 0;
+
+ if (mkdir(dirnoslash, 0777) == -1) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int32_t FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) {
+ CFile file(path, CFile::WriteOnly);
+ if (!file.isOpen()) {
+ DEBUG_FUNCTION_LINE("Failed to open %s\n", path);
+ return 0;
+ }
+ int32_t written = file.write((const uint8_t *) buffer, size);
+ file.close();
+ return written;
+}
+
diff --git a/source/fs/FSUtils.h b/source/fs/FSUtils.h
new file mode 100644
index 0000000..7899406
--- /dev/null
+++ b/source/fs/FSUtils.h
@@ -0,0 +1,18 @@
+#ifndef __FS_UTILS_H_
+#define __FS_UTILS_H_
+
+#include
+
+class FSUtils {
+public:
+ static int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size);
+
+ //! todo: C++ class
+ static int32_t CreateSubfolder(const char *fullpath);
+
+ static int32_t CheckFile(const char *filepath);
+
+ static int32_t saveBufferToFile(const char *path, void *buffer, uint32_t size);
+};
+
+#endif // __FS_UTILS_H_
diff --git a/source/globals.cpp b/source/globals.cpp
index 05559fe..2d9a160 100644
--- a/source/globals.cpp
+++ b/source/globals.cpp
@@ -9,4 +9,7 @@ relocation_trampolin_entry_t *gTrampolineData __attribute__((section(".data")))
uint32_t gPluginDataHeapSize = 0;
uint32_t gPluginInformationHeapSize = 0;
-uint32_t gTrampolineDataSize = 0;
\ No newline at end of file
+uint32_t gTrampolineDataSize = 0;
+
+StoredBuffer storedTVBuffer{};
+StoredBuffer storedDRCBuffer{};
diff --git a/source/globals.h b/source/globals.h
index 957fa1b..d50542c 100644
--- a/source/globals.h
+++ b/source/globals.h
@@ -4,6 +4,7 @@
#include "plugin/PluginContainer.h"
#include "common/plugin_defines.h"
+#include "utils/ConfigUtils.h"
extern plugin_information_t *gPluginInformation;
extern MEMHeapHandle gPluginDataHeap;
@@ -14,6 +15,8 @@ extern plugin_information_on_reload_t gLinkOnReload;
extern module_information_t *gModuleData;
extern relocation_trampolin_entry_t *gTrampolineData;
extern uint32_t gTrampolineDataSize;
+extern StoredBuffer storedTVBuffer;
+extern StoredBuffer storedDRCBuffer;
#define PLUGIN_DATA_HEAP_SIZE (8 * 1024 * 1024)
#define NUMBER_OF_TRAMPS 1024
\ No newline at end of file
diff --git a/source/hooks.cpp b/source/hooks.cpp
index 54ae3d4..abbda32 100644
--- a/source/hooks.cpp
+++ b/source/hooks.cpp
@@ -1,5 +1,6 @@
#include "hooks.h"
#include "utils/logger.h"
+#include "utils/StorageUtils.h"
void CallHook(plugin_information_t *pluginInformation, wups_loader_hook_type_t hook_type) {
CallHookEx(pluginInformation, hook_type, -1);
@@ -31,6 +32,11 @@ static const char **hook_names = (const char *[]) {
"WUPS_LOADER_HOOK_INIT_WUT_SOCKETS",
"WUPS_LOADER_HOOK_FINI_WUT_SOCKETS",
+ "WUPS_LOADER_HOOK_GET_CONFIG",
+ "WUPS_LOADER_HOOK_CONFIG_CLOSED",
+
+ "WUPS_LOADER_HOOK_INIT_STORAGE",
+
"WUPS_LOADER_HOOK_INIT_PLUGIN",
"WUPS_LOADER_HOOK_DEINIT_PLUGIN",
"WUPS_LOADER_HOOK_APPLICATION_STARTS",
@@ -38,13 +44,10 @@ static const char **hook_names = (const char *[]) {
"WUPS_LOADER_HOOK_RELEASE_FOREGROUND",
"WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND",
"WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT",
- "WUPS_LOADER_HOOK_APPLICATION_ENDS",
- "WUPS_LOADER_HOOK_VSYNC"};
+ "WUPS_LOADER_HOOK_APPLICATION_ENDS"};
void CallHookEx(plugin_information_t *pluginInformation, wups_loader_hook_type_t hook_type, int32_t plugin_index_needed) {
- if(hook_type != WUPS_LOADER_HOOK_VSYNC) {
- DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hook_names[hook_type], hook_type);
- }
+ DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hook_names[hook_type], hook_type);
for (int32_t plugin_index = 0; plugin_index < pluginInformation->number_used_plugins; plugin_index++) {
plugin_information_single_t *plugin_data = &pluginInformation->plugin_data[plugin_index];
if (plugin_index_needed != -1 && plugin_index_needed != plugin_index) {
@@ -56,9 +59,7 @@ void CallHookEx(plugin_information_t *pluginInformation, wups_loader_hook_type_t
for (uint32_t j = 0; j < plugin_data->info.number_used_hooks; j++) {
replacement_data_hook_t *hook_data = &plugin_data->info.hooks[j];
if (hook_data->type == hook_type) {
- if(hook_data->type != WUPS_LOADER_HOOK_VSYNC){
- DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s for plugin %s [%d]", hook_names[hook_data->type], plugin_data->meta.name, hook_type);
- }
+ DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s for plugin %s [%d]", hook_names[hook_data->type], plugin_data->meta.name, hook_type);
void *func_ptr = hook_data->func_pointer;
if (func_ptr != nullptr) {
//DEBUG_FUNCTION_LINE("function pointer is %08x",func_ptr);
@@ -78,11 +79,18 @@ void CallHookEx(plugin_information_t *pluginInformation, wups_loader_hook_type_t
hook_type == WUPS_LOADER_HOOK_FINI_WUT_DEVOPTAB ||
hook_type == WUPS_LOADER_HOOK_INIT_WUT_SOCKETS ||
hook_type == WUPS_LOADER_HOOK_FINI_WUT_SOCKETS ||
+ hook_type == WUPS_LOADER_HOOK_GET_CONFIG ||
+ hook_type == WUPS_LOADER_HOOK_CONFIG_CLOSED ||
hook_type == WUPS_LOADER_HOOK_RELEASE_FOREGROUND ||
- hook_type == WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND ||
- hook_type == WUPS_LOADER_HOOK_VSYNC
+ hook_type == WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND
){
((void (*)()) ((uint32_t *) func_ptr))();
+ } else if (hook_type == WUPS_LOADER_HOOK_INIT_STORAGE) {
+ wups_loader_init_storage_args_t args;
+ args.open_storage_ptr = &StorageUtils::OpenStorage;
+ args.close_storage_ptr = &StorageUtils::CloseStorage;
+ args.plugin_id = plugin_data->meta.id;
+ ((void (*)(wups_loader_init_storage_args_t)) ((uint32_t *) func_ptr))(args);
} else {
DEBUG_FUNCTION_LINE("######################################");
DEBUG_FUNCTION_LINE("Hook is not implemented %s [%d]", hook_names[hook_type], hook_type);
diff --git a/source/main.cpp b/source/main.cpp
index 2590048..e27332b 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -15,7 +15,7 @@
WUMS_MODULE_EXPORT_NAME("homebrew_wupsbackend");
-WUMS_USE_WUT_DEVOPTAB()
+WUMS_USE_WUT_DEVOPTAB();
WUMS_INITIALIZE(args) {
WHBLogUdpInit();
diff --git a/source/patcher/hooks_patcher_static.cpp b/source/patcher/hooks_patcher_static.cpp
index fc5ddb7..058c7e4 100644
--- a/source/patcher/hooks_patcher_static.cpp
+++ b/source/patcher/hooks_patcher_static.cpp
@@ -1,17 +1,47 @@
#include "hooks_patcher_static.h"
-#include
#include
#include
+#include
#include
#include
-#include "../utils/logger.h"
+#include "../utils/ConfigUtils.h"
#include "../globals.h"
#include "../hooks.h"
-DECL_FUNCTION(void, GX2WaitForVsync, void) {
- CallHook(gPluginInformation, WUPS_LOADER_HOOK_VSYNC);
- real_GX2WaitForVsync();
+uint8_t vpadPressCooldown = 0xFF;
+bool configMenuOpened = false;
+bool wantsToOpenConfigMenu = false;
+
+DECL_FUNCTION(void, GX2SwapScanBuffers, void) {
+ real_GX2SwapScanBuffers();
+
+ if (wantsToOpenConfigMenu && !configMenuOpened) {
+ configMenuOpened = true;
+ ConfigUtils::openConfigMenu();
+ configMenuOpened = false;
+ wantsToOpenConfigMenu = false;
+ }
+}
+
+DECL_FUNCTION(void, GX2SetTVBuffer, void *buffer, uint32_t buffer_size, int32_t tv_render_mode, GX2SurfaceFormat format, GX2BufferingMode buffering_mode) {
+ storedTVBuffer.buffer = buffer;
+ storedTVBuffer.buffer_size = buffer_size;
+ storedTVBuffer.mode = tv_render_mode;
+ storedTVBuffer.surface_format = format;
+ storedTVBuffer.buffering_mode = buffering_mode;
+
+ return real_GX2SetTVBuffer(buffer,buffer_size,tv_render_mode,format,buffering_mode);
+}
+
+DECL_FUNCTION(void, GX2SetDRCBuffer, void *buffer, uint32_t buffer_size, uint32_t drc_mode, GX2SurfaceFormat surface_format, GX2BufferingMode buffering_mode) {
+ storedDRCBuffer.buffer = buffer;
+ storedDRCBuffer.buffer_size = buffer_size;
+ storedDRCBuffer.mode = drc_mode;
+ storedDRCBuffer.surface_format = surface_format;
+ storedDRCBuffer.buffering_mode = buffering_mode;
+
+ return real_GX2SetDRCBuffer(buffer,buffer_size,drc_mode,surface_format,buffering_mode);
}
static uint32_t lastData0 = 0;
@@ -39,10 +69,46 @@ DECL_FUNCTION(void, OSReleaseForeground) {
real_OSReleaseForeground();
}
+DECL_FUNCTION(int32_t, VPADRead, int32_t chan, VPADStatus *buffer, uint32_t buffer_size, int32_t *error) {
+ int32_t result = real_VPADRead(chan, buffer, buffer_size, error);
+
+ if (result > 0 && (buffer[0].hold == (VPAD_BUTTON_L | VPAD_BUTTON_DOWN | VPAD_BUTTON_MINUS)) && vpadPressCooldown == 0 && !configMenuOpened) {
+ wantsToOpenConfigMenu = true;
+ vpadPressCooldown = 0x3C;
+ }
+
+ if (vpadPressCooldown > 0) {
+ vpadPressCooldown--;
+ }
+ return result;
+}
+
+DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) {
+ real_WPADRead(chan, data);
+
+ if (!configMenuOpened && data[0].err == 0) {
+ if (data[0].extensionType == WPAD_EXT_CORE || data[0].extensionType == WPAD_EXT_NUNCHUK) {
+ // button data is in the first 2 bytes for wiimotes
+ if (((uint16_t*)data)[0] == (WPAD_BUTTON_B | WPAD_BUTTON_DOWN | WPAD_BUTTON_MINUS)) {
+ wantsToOpenConfigMenu = true;
+ }
+ } else {
+ // TODO does this work for classic controllers?
+ if (data[0].buttons == (WPAD_CLASSIC_BUTTON_L | WPAD_CLASSIC_BUTTON_DOWN | WPAD_CLASSIC_BUTTON_MINUS)) {
+ wantsToOpenConfigMenu = true;
+ }
+ }
+ }
+}
+
function_replacement_data_t method_hooks_hooks_static[] __attribute__((section(".data"))) = {
- REPLACE_FUNCTION(GX2WaitForVsync, LIBRARY_GX2, GX2WaitForVsync),
- REPLACE_FUNCTION(OSReceiveMessage, LIBRARY_COREINIT, OSReceiveMessage),
- REPLACE_FUNCTION(OSReleaseForeground, LIBRARY_COREINIT, OSReleaseForeground)
+ REPLACE_FUNCTION(GX2SwapScanBuffers, LIBRARY_GX2, GX2SwapScanBuffers),
+ REPLACE_FUNCTION(GX2SetTVBuffer, LIBRARY_GX2, GX2SetTVBuffer),
+ REPLACE_FUNCTION(GX2SetDRCBuffer, LIBRARY_GX2, GX2SetDRCBuffer),
+ REPLACE_FUNCTION(OSReceiveMessage, LIBRARY_COREINIT, OSReceiveMessage),
+ REPLACE_FUNCTION(OSReleaseForeground, LIBRARY_COREINIT, OSReleaseForeground),
+ REPLACE_FUNCTION(VPADRead, LIBRARY_VPAD, VPADRead),
+ REPLACE_FUNCTION(WPADRead, LIBRARY_PADSCORE, WPADRead),
};
uint32_t method_hooks_size_hooks_static __attribute__((section(".data"))) = sizeof(method_hooks_hooks_static) / sizeof(function_replacement_data_t);
\ No newline at end of file
diff --git a/source/plugin/PluginContainerPersistence.cpp b/source/plugin/PluginContainerPersistence.cpp
index aa07e1c..1475f1e 100644
--- a/source/plugin/PluginContainerPersistence.cpp
+++ b/source/plugin/PluginContainerPersistence.cpp
@@ -48,7 +48,7 @@ bool PluginContainerPersistence::savePlugin(plugin_information_t *pluginInformat
strncpy(plugin_meta_data->license, pluginMetaInfo.getLicense().c_str(), MAXIMUM_PLUGIN_META_FIELD_LENGTH - 1);
if (pluginMetaInfo.getBuildTimestamp().size() >= MAXIMUM_PLUGIN_META_FIELD_LENGTH) {
- DEBUG_FUNCTION_LINE("Warning: build timestampt will be truncated.");
+ DEBUG_FUNCTION_LINE("Warning: build timestamp will be truncated.");
}
strncpy(plugin_meta_data->buildTimestamp, pluginMetaInfo.getBuildTimestamp().c_str(), MAXIMUM_PLUGIN_META_FIELD_LENGTH - 1);
@@ -58,6 +58,11 @@ bool PluginContainerPersistence::savePlugin(plugin_information_t *pluginInformat
}
strncpy(plugin_meta_data->descripion, pluginMetaInfo.getDescription().c_str(), MAXIMUM_PLUGIN_DESCRIPTION_LENGTH - 1);
+ if (pluginMetaInfo.getId().size() >= MAXIMUM_PLUGIN_META_FIELD_LENGTH) {
+ DEBUG_FUNCTION_LINE("Warning: plugin id will be truncated.");
+ }
+ strncpy(plugin_meta_data->id, pluginMetaInfo.getId().c_str(), MAXIMUM_PLUGIN_META_FIELD_LENGTH - 1);
+
plugin_meta_data->size = pluginMetaInfo.getSize();
auto pluginInfo = plugin.getPluginInformation();
diff --git a/source/plugin/PluginMetaInformation.cpp b/source/plugin/PluginMetaInformation.cpp
index 7640621..053a8b5 100644
--- a/source/plugin/PluginMetaInformation.cpp
+++ b/source/plugin/PluginMetaInformation.cpp
@@ -8,4 +8,5 @@ PluginMetaInformation::PluginMetaInformation(const PluginMetaInformation &other)
this->buildtimestamp = other.buildtimestamp;
this->description = other.description;
this->size = other.size;
+ this->id = other.id;
}
\ No newline at end of file
diff --git a/source/plugin/PluginMetaInformation.h b/source/plugin/PluginMetaInformation.h
index 604c4d6..6a26f95 100644
--- a/source/plugin/PluginMetaInformation.h
+++ b/source/plugin/PluginMetaInformation.h
@@ -52,6 +52,10 @@ class PluginMetaInformation {
return this->size;
}
+ [[nodiscard]] const std::string getId() const {
+ return this->id;
+ }
+
private:
PluginMetaInformation() = default;
@@ -83,12 +87,17 @@ class PluginMetaInformation {
this->size = _size;
}
+ void setId(const std::string &id) {
+ this->id = id;
+ }
+
std::string name;
std::string author;
std::string version;
std::string license;
std::string buildtimestamp;
std::string description;
+ std::string id;
size_t size{};
friend class PluginMetaInformationFactory;
diff --git a/source/plugin/PluginMetaInformationFactory.cpp b/source/plugin/PluginMetaInformationFactory.cpp
index db52972..90929b8 100644
--- a/source/plugin/PluginMetaInformationFactory.cpp
+++ b/source/plugin/PluginMetaInformationFactory.cpp
@@ -81,7 +81,7 @@ std::optional PluginMetaInformationFactory::loadPlugin(co
}
// Get meta information and check WUPS version:
- if (psec->get_name().compare(".wups.meta") == 0) {
+ if (psec->get_name() == ".wups.meta") {
const void *sectionData = psec->get_data();
uint32_t sectionSize = psec->get_size();
@@ -92,7 +92,7 @@ std::optional PluginMetaInformationFactory::loadPlugin(co
continue;
}
- auto firstFound = std::string(curEntry).find_first_of("=");
+ auto firstFound = std::string(curEntry).find_first_of('=');
if (firstFound != std::string::npos) {
curEntry[firstFound] = '\0';
std::string key(curEntry);
@@ -110,8 +110,10 @@ std::optional PluginMetaInformationFactory::loadPlugin(co
pluginInfo.setBuildTimestamp(value);
} else if (key.compare("description") == 0) {
pluginInfo.setDescription(value);
+ } else if (key.compare("id") == 0) {
+ pluginInfo.setId(value);
} else if (key.compare("wups") == 0) {
- if (value.compare("0.5") != 0) {
+ if (value.compare("0.6") != 0) {
DEBUG_FUNCTION_LINE("Warning: Ignoring plugin - Unsupported WUPS version: %s.", value.c_str());
return std::nullopt;
}
diff --git a/source/utils/ConfigUtils.cpp b/source/utils/ConfigUtils.cpp
new file mode 100644
index 0000000..cc37b95
--- /dev/null
+++ b/source/utils/ConfigUtils.cpp
@@ -0,0 +1,679 @@
+#include "ConfigUtils.h"
+
+#include "logger.h"
+#include "../config/WUPSConfig.h"
+#include "../globals.h"
+#include "DrawUtils.h"
+#include "StringTools.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define COLOR_BACKGROUND Color(238, 238, 238, 255)
+#define COLOR_TEXT Color(51, 51, 51, 255)
+#define COLOR_TEXT2 Color(72, 72, 72, 255)
+#define COLOR_DISABLED Color(255, 0, 0, 255)
+#define COLOR_BORDER Color(204, 204, 204, 255)
+#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4FF)
+#define COLOR_WHITE Color(0xFFFFFFFF)
+#define COLOR_BLACK Color(0, 0, 0, 255)
+
+struct ConfigDisplayItem {
+ WUPSConfig* config;
+ std::string name;
+ std::string author;
+ std::string version;
+ bool enabled;
+};
+
+#define MAX_BUTTONS_ON_SCREEN 8
+
+static uint32_t remapWiiMoteButtons(uint32_t buttons)
+{
+ uint32_t conv_buttons = 0;
+
+ if(buttons & WPAD_BUTTON_LEFT)
+ conv_buttons |= VPAD_BUTTON_LEFT;
+
+ if(buttons & WPAD_BUTTON_RIGHT)
+ conv_buttons |= VPAD_BUTTON_RIGHT;
+
+ if(buttons & WPAD_BUTTON_DOWN)
+ conv_buttons |= VPAD_BUTTON_DOWN;
+
+ if(buttons & WPAD_BUTTON_UP)
+ conv_buttons |= VPAD_BUTTON_UP;
+
+ if(buttons & WPAD_BUTTON_PLUS)
+ conv_buttons |= VPAD_BUTTON_PLUS;
+
+ if(buttons & WPAD_BUTTON_B)
+ conv_buttons |= VPAD_BUTTON_B;
+
+ if(buttons & WPAD_BUTTON_A)
+ conv_buttons |= VPAD_BUTTON_A;
+
+ if(buttons & WPAD_BUTTON_MINUS)
+ conv_buttons |= VPAD_BUTTON_MINUS;
+
+ if(buttons & WPAD_BUTTON_HOME)
+ conv_buttons |= VPAD_BUTTON_HOME;
+
+ return conv_buttons;
+}
+
+static uint32_t remapClassicButtons(uint32_t buttons)
+{
+ uint32_t conv_buttons = 0;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_LEFT)
+ conv_buttons |= VPAD_BUTTON_LEFT;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_RIGHT)
+ conv_buttons |= VPAD_BUTTON_RIGHT;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_DOWN)
+ conv_buttons |= VPAD_BUTTON_DOWN;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_UP)
+ conv_buttons |= VPAD_BUTTON_UP;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_PLUS)
+ conv_buttons |= VPAD_BUTTON_PLUS;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_X)
+ conv_buttons |= VPAD_BUTTON_X;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_Y)
+ conv_buttons |= VPAD_BUTTON_Y;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_B)
+ conv_buttons |= VPAD_BUTTON_B;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_A)
+ conv_buttons |= VPAD_BUTTON_A;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_MINUS)
+ conv_buttons |= VPAD_BUTTON_MINUS;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_HOME)
+ conv_buttons |= VPAD_BUTTON_HOME;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_ZR)
+ conv_buttons |= VPAD_BUTTON_ZR;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_ZL)
+ conv_buttons |= VPAD_BUTTON_ZL;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_R)
+ conv_buttons |= VPAD_BUTTON_R;
+
+ if(buttons & WPAD_CLASSIC_BUTTON_L)
+ conv_buttons |= VPAD_BUTTON_L;
+
+ return conv_buttons;
+}
+
+void ConfigUtils::displayMenu() {
+ std::vector configs;
+ for (int32_t plugin_index = 0; plugin_index < gPluginInformation->number_used_plugins; plugin_index++) {
+ plugin_information_single_t *plugin_data = &gPluginInformation->plugin_data[plugin_index];
+ if (plugin_data == nullptr) {
+ continue;
+ }
+ ConfigDisplayItem cfg;
+ cfg.name = std::string(plugin_data->meta.name);
+ cfg.author = std::string(plugin_data->meta.author);
+ cfg.version = std::string(plugin_data->meta.version);
+ cfg.enabled = true;
+
+ for (uint32_t j = 0; j < plugin_data->info.number_used_hooks; j++) {
+ replacement_data_hook_t *hook_data = &plugin_data->info.hooks[j];
+ if (hook_data->type == WUPS_LOADER_HOOK_GET_CONFIG/*WUPS_LOADER_HOOK_GET_CONFIG*/) {
+ if (hook_data->func_pointer == nullptr) {
+ break;
+ }
+ WUPSConfig *cur_config = reinterpret_cast(((WUPSConfigHandle (*)()) ((uint32_t *) hook_data->func_pointer))());
+ if(cur_config == nullptr){
+ break;
+ }
+
+ //if(cur_config > 0x8000000);
+ //DCFlushRange(&cur_config, sizeof(WUPSConfig*));
+ //DCFlushRange(cur_config, sizeof(WUPSConfig));
+ cfg.config = cur_config;
+
+ DEBUG_FUNCTION_LINE("name %s author %s version %s enabled %d config %08X",cfg.name.c_str(),cfg.author.c_str(),cfg.version.c_str(),cfg.enabled, cfg.config)
+ configs.push_back(cfg);
+ break;
+ }
+ }
+ }
+
+ ConfigDisplayItem* currentConfig = nullptr;
+ WUPSConfigCategory* currentCategory = nullptr;
+
+ uint32_t selectedBtn = 0;
+ uint32_t start = 0;
+ uint32_t end = MAX_BUTTONS_ON_SCREEN;
+ if (configs.size() < MAX_BUTTONS_ON_SCREEN) {
+ end = configs.size();
+ }
+
+ bool redraw = true;
+ uint32_t buttonsTriggered;
+ uint32_t buttonsReleased;
+
+ VPADStatus vpad_data{};
+ VPADReadError vpad_error;
+ KPADStatus kpad_data{};
+ int32_t kpad_error;
+
+ while (true) {
+ buttonsTriggered = 0;
+ buttonsReleased = 0;
+
+ VPADRead(VPAD_CHAN_0, &vpad_data, 1, &vpad_error);
+ if (vpad_error == VPAD_READ_SUCCESS) {
+ buttonsTriggered = vpad_data.trigger;
+ buttonsReleased = vpad_data.release;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if (KPADReadEx((KPADChan)i, &kpad_data, 1, &kpad_error) > 0) {
+ if (kpad_error == KPAD_ERROR_OK) {
+ if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) {
+ buttonsTriggered |= remapWiiMoteButtons(kpad_data.trigger);
+ buttonsReleased |= remapWiiMoteButtons(kpad_data.release);
+ } else {
+ buttonsTriggered |= remapClassicButtons(kpad_data.classic.trigger);
+ buttonsReleased |= remapClassicButtons(kpad_data.classic.release);
+ }
+ }
+ }
+ }
+
+ if (buttonsTriggered & VPAD_BUTTON_HOME) {
+
+ break;
+ }
+
+ if (!currentConfig || !currentConfig->config) {
+ if (buttonsTriggered & VPAD_BUTTON_DOWN) {
+ if (selectedBtn < configs.size() - 1) {
+ selectedBtn++;
+ redraw = true;
+ }
+ }
+ else if (buttonsTriggered & VPAD_BUTTON_UP) {
+ if (selectedBtn > 0) {
+ selectedBtn--;
+ redraw = true;
+ }
+ }
+ if (buttonsTriggered & VPAD_BUTTON_X) {
+ configs[selectedBtn].enabled = !configs[selectedBtn].enabled;
+ redraw = true;
+ }
+ else if (buttonsTriggered & VPAD_BUTTON_A) {
+ currentConfig = &configs[selectedBtn];
+ if(currentConfig == nullptr){
+ DEBUG_FUNCTION_LINE("BYEBYE");
+ break;
+ }
+
+ selectedBtn = 0;
+ start = 0;
+ end = MAX_BUTTONS_ON_SCREEN;
+
+ auto cats = currentConfig->config->getCategories();
+ if (cats.size() < MAX_BUTTONS_ON_SCREEN) {
+ end = cats.size();
+ }
+
+ redraw = true;
+ continue;
+ }
+
+ if (selectedBtn >= end) {
+ end = selectedBtn + 1;
+ start = end - MAX_BUTTONS_ON_SCREEN;
+ }
+ else if (selectedBtn < start) {
+ start = selectedBtn;
+ end = start + MAX_BUTTONS_ON_SCREEN;
+ }
+
+ if (redraw) {
+ DrawUtils::beginDraw();
+ DrawUtils::clear(COLOR_BACKGROUND);
+
+ // draw buttons
+ uint32_t index = 8 + 24 + 8 + 4;
+ for (uint32_t i = start; i < end; i++) {
+ if (configs[i].enabled) {
+ DrawUtils::setFontColor(COLOR_TEXT);
+ } else {
+ DrawUtils::setFontColor(COLOR_DISABLED);
+ }
+
+ if (i == selectedBtn) {
+ DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
+ } else {
+ DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 2, configs[i].enabled ? COLOR_BORDER : COLOR_DISABLED);
+ }
+
+ DrawUtils::setFontSize(24);
+ DrawUtils::print(16*2, index + 8 + 24, configs[i].name.c_str());
+ uint32_t sz = DrawUtils::getTextWidth(configs[i].name.c_str());
+ DrawUtils::setFontSize(12);
+ DrawUtils::print(16*2 + sz + 4, index + 8 + 24, configs[i].author.c_str());
+ DrawUtils::print(SCREEN_WIDTH - 16*2, index + 8 + 24, configs[i].version.c_str(), true);
+ index += 42 + 8;
+ }
+
+ DrawUtils::setFontColor(COLOR_TEXT);
+
+ // draw top bar
+ DrawUtils::setFontSize(24);
+ DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
+ DrawUtils::setFontSize(18);
+ DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, "v1.0", true);
+ DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
+
+ // draw bottom bar
+ DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8*2, 3, COLOR_BLACK);
+ DrawUtils::setFontSize(18);
+ DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
+ if (configs[selectedBtn].enabled) {
+ DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue002 Disable / \ue000 Select", true);
+ } else {
+ DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue002 Enable / \ue000 Select", true);
+ }
+
+ // draw scroll indicator
+ DrawUtils::setFontSize(24);
+ if (end < configs.size()) {
+ DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
+ }
+ if (start > 0) {
+ DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
+ }
+
+ // draw home button
+ DrawUtils::setFontSize(18);
+ const char* exitHint = "\ue044 Exit";
+ DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 8, exitHint, true);
+
+ DrawUtils::endDraw();
+ redraw = false;
+ }
+
+ continue;
+ }
+
+ if(!currentCategory){
+ auto cats = currentConfig->config->getCategories();
+
+ if (buttonsTriggered & VPAD_BUTTON_DOWN) {
+ if (selectedBtn < cats.size() - 1) {
+ selectedBtn++;
+ redraw = true;
+ }
+ }
+ else if (buttonsTriggered & VPAD_BUTTON_UP) {
+ if (selectedBtn > 0) {
+ selectedBtn--;
+ redraw = true;
+ }
+ }
+ else if (buttonsTriggered & VPAD_BUTTON_A) {
+ currentCategory = cats[selectedBtn];
+ if(currentCategory == nullptr){
+ DEBUG_FUNCTION_LINE("BYEBYE");
+ break;
+ }
+
+ selectedBtn = 0;
+ start = 0;
+ end = MAX_BUTTONS_ON_SCREEN;
+
+ auto items = currentCategory->getItems();
+ if (items.size() < MAX_BUTTONS_ON_SCREEN) {
+ end = items.size();
+ }
+
+ redraw = true;
+ continue;
+ } else if (buttonsTriggered & VPAD_BUTTON_B) {
+ currentConfig = nullptr;
+ currentCategory = nullptr;
+ selectedBtn = 0;
+ start = 0;
+ end = MAX_BUTTONS_ON_SCREEN;
+ if (configs.size() < MAX_BUTTONS_ON_SCREEN) {
+ end = configs.size();
+ }
+ redraw = true;
+ continue;
+ }
+
+ if (selectedBtn >= end) {
+ end = selectedBtn + 1;
+ start = end - MAX_BUTTONS_ON_SCREEN;
+ }
+ else if (selectedBtn < start) {
+ start = selectedBtn;
+ end = start + MAX_BUTTONS_ON_SCREEN;
+ }
+
+ if (redraw) {
+ DrawUtils::beginDraw();
+ DrawUtils::clear(COLOR_BACKGROUND);
+
+ // draw buttons
+ uint32_t index = 8 + 24 + 8 + 4;
+ for (uint32_t i = start; i < end; i++) {
+ DrawUtils::setFontColor(COLOR_TEXT);
+
+ if (i == selectedBtn) {
+ DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
+ } else {
+ DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 2, COLOR_BORDER);
+ }
+
+ DrawUtils::setFontSize(24);
+ DrawUtils::print(16*2, index + 8 + 24, cats[i]->getName().c_str());
+ index += 42 + 8;
+ }
+
+ DrawUtils::setFontColor(COLOR_TEXT);
+
+ // draw top bar
+ DrawUtils::setFontSize(24);
+ DrawUtils::print(16, 6 + 24, currentConfig->config->getName().c_str());
+ DrawUtils::setFontSize(18);
+ DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
+ DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
+
+ // draw bottom bar
+ DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8*2, 3, COLOR_BLACK);
+ DrawUtils::setFontSize(18);
+ DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
+ if (configs[selectedBtn].enabled) {
+ DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue002 Disable / \ue000 Select", true);
+ } else {
+ DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue002 Enable / \ue000 Select", true);
+ }
+
+ // draw scroll indicator
+ DrawUtils::setFontSize(24);
+ if (end < configs.size()) {
+ DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
+ }
+ if (start > 0) {
+ DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
+ }
+
+ // draw home button
+ DrawUtils::setFontSize(18);
+ const char* exitHint = "\ue044 Exit";
+ DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 8, exitHint, true);
+
+ DrawUtils::endDraw();
+ redraw = false;
+ }
+
+ continue;
+ }
+
+ const std::vector config_items = currentCategory->getItems();
+
+ if (buttonsTriggered & VPAD_BUTTON_DOWN) {
+ if (selectedBtn < config_items.size() - 1) {
+ selectedBtn++;
+ redraw = true;
+ }
+ }
+ else if (buttonsTriggered & VPAD_BUTTON_UP) {
+ if (selectedBtn > 0) {
+ selectedBtn--;
+ redraw = true;
+ }
+ }
+ else if (buttonsTriggered & VPAD_BUTTON_B) {
+ currentCategory = nullptr;
+ selectedBtn = 0;
+ start = 0;
+ end = MAX_BUTTONS_ON_SCREEN;
+ auto catSize = currentConfig->config->getCategories().size();
+ if (catSize < MAX_BUTTONS_ON_SCREEN) {
+ end = catSize;
+ }
+ redraw = true;
+ continue;
+ }
+
+ WUPSConfigButtons pressedButtons = WUPS_CONFIG_BUTTON_NONE;
+ if (buttonsTriggered & VPAD_BUTTON_A) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_A;
+ }
+ if (buttonsTriggered & VPAD_BUTTON_LEFT) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_LEFT;
+ }
+ if (buttonsTriggered & VPAD_BUTTON_RIGHT) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_RIGHT;
+ }
+ if (buttonsTriggered & VPAD_BUTTON_L) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_L;
+ }
+ if (buttonsTriggered & VPAD_BUTTON_R) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_R;
+ }
+ if (buttonsTriggered & VPAD_BUTTON_ZL) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_ZL;
+ }
+ if (buttonsTriggered & VPAD_BUTTON_ZR) {
+ pressedButtons |= WUPS_CONFIG_BUTTON_ZR;
+ }
+ if (pressedButtons != WUPS_CONFIG_BUTTON_NONE) {
+ redraw = true;
+ }
+
+ if (selectedBtn >= end) {
+ end = selectedBtn + 1;
+ start = end - MAX_BUTTONS_ON_SCREEN;
+ }
+ else if (selectedBtn < start) {
+ start = selectedBtn;
+ end = start + MAX_BUTTONS_ON_SCREEN;
+ }
+
+ if (redraw) {
+ DrawUtils::beginDraw();
+ DrawUtils::clear(COLOR_BACKGROUND);
+
+ // draw buttons
+ uint32_t index = 8 + 24 + 8 + 4;
+ for (uint32_t i = start; i < end; i++) {
+ DrawUtils::setFontColor(COLOR_TEXT);
+
+ if (i == selectedBtn) {
+ DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
+ } else {
+ DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 2, COLOR_BORDER);
+ }
+
+ DrawUtils::setFontSize(24);
+ DrawUtils::print(16*2, index + 8 + 24, config_items[i]->getDisplayName().c_str());
+ if (i == selectedBtn) {
+ if (pressedButtons != WUPS_CONFIG_BUTTON_NONE) {
+ config_items[i]->onButtonPressed(pressedButtons);
+ }
+ DrawUtils::print(SCREEN_WIDTH - 16*2, index + 8 + 24, config_items[i]->getCurrentValueSelectedDisplay().c_str(), true);
+ }
+ else {
+ DrawUtils::print(SCREEN_WIDTH - 16*2, index + 8 + 24, config_items[i]->getCurrentValueDisplay().c_str(), true);
+ }
+ index += 42 + 8;
+ }
+
+ DrawUtils::setFontColor(COLOR_TEXT);
+
+ std::string headline;
+ StringTools::strprintf(headline, "%s - %s",currentConfig->config->getName().c_str(), currentCategory->getName().c_str());
+ // draw top bar
+ DrawUtils::setFontSize(24);
+ DrawUtils::print(16, 6 + 24, headline.c_str());
+ DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
+ DrawUtils::setFontSize(18);
+ DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
+
+ // draw bottom bar
+ DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8*2, 3, COLOR_BLACK);
+ DrawUtils::setFontSize(18);
+ DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
+ DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue001 Back", true);
+
+ // draw scroll indicator
+ DrawUtils::setFontSize(24);
+ if (end < configs.size()) {
+ DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
+ }
+ if (start > 0) {
+ DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
+ }
+
+ // draw home button
+ DrawUtils::setFontSize(18);
+ const char* exitHint = "\ue044 Exit";
+ DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 8, exitHint, true);
+
+ DrawUtils::endDraw();
+ redraw = false;
+ }
+ }
+
+ for (const auto& element : configs) {
+ for (const auto& cat : element.config->getCategories()) {
+ for (const auto& item : cat->getItems()) {
+ if(item->isDirty()){
+ item->callCallback();
+ }
+ }
+ }
+ }
+
+ for (int32_t plugin_index = 0; plugin_index < gPluginInformation->number_used_plugins; plugin_index++) {
+ plugin_information_single_t *plugin_data = &gPluginInformation->plugin_data[plugin_index];
+ if (plugin_data == nullptr) {
+ continue;
+ }
+
+ for (uint32_t j = 0; j < plugin_data->info.number_used_hooks; j++) {
+ replacement_data_hook_t *hook_data = &plugin_data->info.hooks[j];
+ if (hook_data->type == WUPS_LOADER_HOOK_CONFIG_CLOSED) {
+ if (hook_data->func_pointer == nullptr) {
+ break;
+ }
+ ((void (*)()) ((uint32_t *) hook_data->func_pointer))();
+ break;
+ }
+ }
+ }
+
+ for (const auto& element : configs) {
+ DEBUG_FUNCTION_LINE("Delete %08X", element.config);
+ delete element.config;
+ }
+}
+
+void ConfigUtils::openConfigMenu()
+{
+ bool wasHomeButtonMenuEnabled = OSIsHomeButtonMenuEnabled();
+
+ OSScreenInit();
+
+ uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
+ uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
+ void* screenbuffer0 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size, 0x100);
+ void* screenbuffer1 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf1_size, 0x100);
+
+ bool skipScreen0Free = false;
+ bool skipScreen1Free = false;
+
+ if (!screenbuffer0 || !screenbuffer1) {
+ if(screenbuffer0 == nullptr){
+ if(storedTVBuffer.buffer_size >= screen_buf0_size){
+ screenbuffer0 = storedTVBuffer.buffer;
+ skipScreen0Free = true;
+ DEBUG_FUNCTION_LINE("Use storedTVBuffer");
+ }
+ }
+ if(screenbuffer1 == nullptr){
+ if(storedDRCBuffer.buffer_size >= screen_buf1_size){
+ screenbuffer1 = storedDRCBuffer.buffer;
+ skipScreen1Free = true;
+ DEBUG_FUNCTION_LINE("Use storedDRCBuffer");
+ }
+ }
+ if (!screenbuffer0 || !screenbuffer1) {
+ DEBUG_FUNCTION_LINE("Failed to alloc buffers");
+ goto error_exit;
+ }
+ }
+
+ OSScreenSetBufferEx(SCREEN_TV, screenbuffer0);
+ OSScreenSetBufferEx(SCREEN_DRC, screenbuffer1);
+
+ OSScreenEnableEx(SCREEN_TV, 1);
+ OSScreenEnableEx(SCREEN_DRC, 1);
+
+ // Clear screens
+ OSScreenClearBufferEx(SCREEN_TV, 0);
+ OSScreenClearBufferEx(SCREEN_DRC, 0);
+
+ // Flip buffers
+ OSScreenFlipBuffersEx(SCREEN_TV);
+ OSScreenFlipBuffersEx(SCREEN_DRC);
+
+ DrawUtils::initBuffers(screenbuffer0, screen_buf0_size, screenbuffer1, screen_buf1_size);
+ DrawUtils::initFont();
+
+ // disable the home button menu to prevent opening it when exiting
+ OSEnableHomeButtonMenu(false);
+
+ displayMenu();
+
+ OSEnableHomeButtonMenu(wasHomeButtonMenuEnabled);
+
+ DrawUtils::deinitFont();
+
+ error_exit:
+
+ if(storedTVBuffer.buffer != nullptr) {
+ GX2SetTVBuffer(storedTVBuffer.buffer, storedTVBuffer.buffer_size, static_cast(storedTVBuffer.mode),
+ storedTVBuffer.surface_format, storedTVBuffer.buffering_mode);
+ }
+
+ if(storedDRCBuffer.buffer != nullptr) {
+ GX2SetDRCBuffer(storedDRCBuffer.buffer, storedDRCBuffer.buffer_size, static_cast(storedDRCBuffer.mode),
+ storedDRCBuffer.surface_format, storedDRCBuffer.buffering_mode);
+ }
+ if (!skipScreen0Free && screenbuffer0) {
+ MEMFreeToMappedMemory(screenbuffer0);
+ screenbuffer0 = nullptr;
+ }
+
+ if (!skipScreen1Free && screenbuffer1) {
+ MEMFreeToMappedMemory(screenbuffer1);
+ screenbuffer1 = nullptr;
+ }
+}
diff --git a/source/utils/ConfigUtils.h b/source/utils/ConfigUtils.h
new file mode 100644
index 0000000..dd3c427
--- /dev/null
+++ b/source/utils/ConfigUtils.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+
+struct StoredBuffer {
+ void* buffer;
+ uint32_t buffer_size;
+ uint32_t mode;
+ GX2SurfaceFormat surface_format;
+ GX2BufferingMode buffering_mode;
+};
+
+class ConfigUtils {
+public:
+ static void openConfigMenu();
+
+private:
+ static void displayMenu();
+};
diff --git a/source/utils/DrawUtils.cpp b/source/utils/DrawUtils.cpp
new file mode 100644
index 0000000..928125d
--- /dev/null
+++ b/source/utils/DrawUtils.cpp
@@ -0,0 +1,306 @@
+#include "DrawUtils.h"
+
+#include
+#include
+#include
+#include
+#include FT_FREETYPE_H
+
+// buffer width
+#define TV_WIDTH 0x500
+#define DRC_WIDTH 0x380
+
+bool DrawUtils::isBackBuffer;
+
+uint8_t *DrawUtils::tvBuffer = nullptr;
+uint32_t DrawUtils::tvSize = 0;
+uint8_t *DrawUtils::drcBuffer = nullptr;
+uint32_t DrawUtils::drcSize = 0;
+
+// Don't put those into the clase or we have to include ft everywhere
+static FT_Library ft_lib = nullptr;
+static FT_Face ft_face = nullptr;
+static Color font_col(0xFFFFFFFF);
+
+void DrawUtils::initBuffers(void *tvBuffer_, uint32_t tvSize_, void *drcBuffer_, uint32_t drcSize_) {
+ DrawUtils::tvBuffer = (uint8_t *) tvBuffer_;
+ DrawUtils::tvSize = tvSize_;
+ DrawUtils::drcBuffer = (uint8_t *) drcBuffer_;
+ DrawUtils::drcSize = drcSize_;
+}
+
+void DrawUtils::beginDraw() {
+ uint32_t pixel = *(uint32_t *) tvBuffer;
+
+ // check which buffer is currently used
+ OSScreenPutPixelEx(SCREEN_TV, 0, 0, 0xABCDEF90);
+ if (*(uint32_t *) tvBuffer == 0xABCDEF90) {
+ isBackBuffer = false;
+ } else {
+ isBackBuffer = true;
+ }
+
+ // restore the pixel we used for checking
+ *(uint32_t *) tvBuffer = pixel;
+}
+
+void DrawUtils::endDraw() {
+ // OSScreenFlipBuffersEx already flushes the cache?
+ // DCFlushRange(tvBuffer, tvSize);
+ // DCFlushRange(drcBuffer, drcSize);
+
+ OSScreenFlipBuffersEx(SCREEN_DRC);
+ OSScreenFlipBuffersEx(SCREEN_TV);
+}
+
+void DrawUtils::clear(Color col) {
+ OSScreenClearBufferEx(SCREEN_TV, col.color);
+ OSScreenClearBufferEx(SCREEN_DRC, col.color);
+}
+
+void DrawUtils::drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ float opacity = a / 255.0f;
+
+ // put pixel in the drc buffer
+ uint32_t i = (x + y * DRC_WIDTH) * 4;
+ if (i + 3 < drcSize / 2) {
+ if (isBackBuffer) {
+ i += drcSize / 2;
+ }
+ if (a == 0xFF) {
+ drcBuffer[i] = r;
+ drcBuffer[i + 1] = g;
+ drcBuffer[i + 2] = b;
+ } else {
+ drcBuffer[i] = r * opacity + drcBuffer[i] * (1 - opacity);
+ drcBuffer[i + 1] = g * opacity + drcBuffer[i + 1] * (1 - opacity);
+ drcBuffer[i + 2] = b * opacity + drcBuffer[i + 2] * (1 - opacity);
+ }
+ }
+
+ // scale and put pixel in the tv buffer
+ for (uint32_t yy = (y * 1.5); yy < ((y * 1.5) + 1); yy++) {
+ for (uint32_t xx = (x * 1.5); xx < ((x * 1.5) + 1); xx++) {
+ uint32_t i = (xx + yy * TV_WIDTH) * 4;
+ if (i + 3 < tvSize / 2) {
+ if (isBackBuffer) {
+ i += tvSize / 2;
+ }
+ if (a == 0xFF) {
+ tvBuffer[i] = r;
+ tvBuffer[i + 1] = g;
+ tvBuffer[i + 2] = b;
+ } else {
+ tvBuffer[i] = r * opacity + tvBuffer[i] * (1 - opacity);
+ tvBuffer[i + 1] = g * opacity + tvBuffer[i + 1] * (1 - opacity);
+ tvBuffer[i + 2] = b * opacity + tvBuffer[i + 2] * (1 - opacity);
+ }
+ }
+ }
+ }
+}
+
+void DrawUtils::drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col) {
+ for (uint32_t yy = y; yy < y + h; yy++) {
+ for (uint32_t xx = x; xx < x + w; xx++) {
+ drawPixel(xx, yy, col);
+ }
+ }
+}
+
+void DrawUtils::drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col) {
+ drawRectFilled(x, y, w, borderSize, col);
+ drawRectFilled(x, y + h - borderSize, w, borderSize, col);
+ drawRectFilled(x, y, borderSize, h, col);
+ drawRectFilled(x + w - borderSize, y, borderSize, h, col);
+}
+
+void DrawUtils::drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t *data) {
+ if (data[0] != 'B' || data[1] != 'M') {
+ // invalid header
+ return;
+ }
+
+ uint32_t dataPos = __builtin_bswap32(*(uint32_t *) &(data[0x0A]));
+ uint32_t width = __builtin_bswap32(*(uint32_t *) &(data[0x12]));
+ uint32_t height = __builtin_bswap32(*(uint32_t *) &(data[0x16]));
+
+ if (dataPos == 0) {
+ dataPos = 54;
+ }
+
+ data += dataPos;
+
+ // TODO flip image since bitmaps are stored upside down
+
+ for (uint32_t yy = y; yy < y + target_height; yy++) {
+ for (uint32_t xx = x; xx < x + target_width; xx++) {
+ uint32_t i = (((xx - x) * width / target_width) + ((yy - y) * height / target_height) * width) * 3;
+ drawPixel(xx, yy, data[i + 2], data[i + 1], data[i], 0xFF);
+ }
+ }
+}
+
+static void png_read_data(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) {
+ void **data = (void **) png_get_io_ptr(png_ptr);
+
+ memcpy(outBytes, *data, byteCountToRead);
+ *((uint8_t **) data) += byteCountToRead;
+}
+
+void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t *data) {
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (png_ptr == nullptr) {
+ return;
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == nullptr) {
+ png_destroy_read_struct(&png_ptr, nullptr, nullptr);
+ return;
+ }
+
+ png_set_read_fn(png_ptr, (void *) &data, png_read_data);
+
+ png_read_info(png_ptr, info_ptr);
+
+ uint32_t width = 0;
+ uint32_t height = 0;
+ int bitDepth = 0;
+ int colorType = -1;
+ uint32_t retval = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, nullptr, nullptr, nullptr);
+ if (retval != 1) {
+ return;
+ }
+
+ uint32_t bytesPerRow = png_get_rowbytes(png_ptr, info_ptr);
+ auto *rowData = new uint8_t[bytesPerRow];
+
+ for (uint32_t yy = y; yy < y + height; yy++) {
+ png_read_row(png_ptr, (png_bytep) rowData, nullptr);
+
+ for (uint32_t xx = x; xx < x + width; xx++) {
+ if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
+ uint32_t i = (xx - x) * 4;
+ drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], rowData[i + 3]);
+ } else if (colorType == PNG_COLOR_TYPE_RGB) {
+ uint32_t i = (xx - x) * 3;
+ drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], 0xFF);
+ }
+ }
+ }
+
+ delete[] rowData;
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+}
+
+void DrawUtils::initFont() {
+ void *font = nullptr;
+ uint32_t size = 0;
+ OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size);
+
+ if (font && size) {
+ FT_Init_FreeType(&ft_lib);
+ FT_New_Memory_Face(ft_lib, (FT_Byte *) font, size, 0, &ft_face);
+ }
+}
+
+void DrawUtils::deinitFont() {
+ FT_Done_Face(ft_face);
+ FT_Done_FreeType(ft_lib);
+}
+
+void DrawUtils::setFontSize(uint32_t size) {
+ FT_Set_Pixel_Sizes(ft_face, 0, size);
+}
+
+void DrawUtils::setFontColor(Color col) {
+ font_col = col;
+}
+
+static void draw_freetype_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) {
+ FT_Int i, j, p, q;
+ FT_Int x_max = x + bitmap->width;
+ FT_Int y_max = y + bitmap->rows;
+
+ for (i = x, p = 0; i < x_max; i++, p++) {
+ for (j = y, q = 0; j < y_max; j++, q++) {
+ if (i < 0 || j < 0 || i >= SCREEN_WIDTH || j >= SCREEN_HEIGHT) {
+ continue;
+ }
+
+ float opacity = bitmap->buffer[q * bitmap->pitch + p] / 255.0f;
+ DrawUtils::drawPixel(i, j, font_col.r, font_col.g, font_col.b, font_col.a * opacity);
+ }
+ }
+}
+
+void DrawUtils::print(uint32_t x, uint32_t y, const char *string, bool alignRight) {
+ auto *buffer = new wchar_t[strlen(string) + 1];
+
+ size_t num = mbstowcs(buffer, string, strlen(string));
+ if (num > 0) {
+ buffer[num] = 0;
+ } else {
+ wchar_t *tmp = buffer;
+ while ((*tmp++ = *string++));
+ }
+
+ print(x, y, buffer, alignRight);
+ delete[] buffer;
+}
+
+void DrawUtils::print(uint32_t x, uint32_t y, const wchar_t *string, bool alignRight) {
+ FT_GlyphSlot slot = ft_face->glyph;
+ FT_Vector pen = {(int) x, (int) y};
+
+ if (alignRight) {
+ pen.x -= getTextWidth(string);
+ }
+
+ for (; *string; string++) {
+ uint32_t charcode = *string;
+
+ if (charcode == '\n') {
+ pen.y += ft_face->size->metrics.height >> 6;
+ pen.x = x;
+ continue;
+ }
+
+ FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, charcode), FT_LOAD_DEFAULT);
+ FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
+
+ draw_freetype_bitmap(&slot->bitmap, pen.x + slot->bitmap_left, pen.y - slot->bitmap_top);
+ pen.x += slot->advance.x >> 6;
+ }
+}
+
+uint32_t DrawUtils::getTextWidth(const char *string) {
+ auto *buffer = new wchar_t[strlen(string) + 1];
+
+ size_t num = mbstowcs(buffer, string, strlen(string));
+ if (num > 0) {
+ buffer[num] = 0;
+ } else {
+ wchar_t *tmp = buffer;
+ while ((*tmp++ = *string++));
+ }
+
+ uint32_t width = getTextWidth(buffer);
+ delete[] buffer;
+
+ return width;
+}
+
+uint32_t DrawUtils::getTextWidth(const wchar_t *string) {
+ FT_GlyphSlot slot = ft_face->glyph;
+ uint32_t width = 0;
+
+ for (; *string; string++) {
+ FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, *string), FT_LOAD_BITMAP_METRICS_ONLY);
+
+ width += slot->advance.x >> 6;
+ }
+
+ return width;
+}
diff --git a/source/utils/DrawUtils.h b/source/utils/DrawUtils.h
new file mode 100644
index 0000000..36d0851
--- /dev/null
+++ b/source/utils/DrawUtils.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include
+
+// visible screen sizes
+#define SCREEN_WIDTH 854
+#define SCREEN_HEIGHT 480
+
+union Color {
+ explicit Color(uint32_t color) {
+ this->color = color;
+ }
+ Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ this->r = r; this->g = g; this->b = b; this->a = a;
+ }
+
+ uint32_t color{};
+ struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+ };
+};
+
+class DrawUtils {
+public:
+ static void initBuffers(void* tvBuffer, uint32_t tvSize, void* drcBuffer, uint32_t drcSize);
+ static void beginDraw();
+ static void endDraw();
+ static void clear(Color col);
+ static void drawPixel(uint32_t x, uint32_t y, Color col) { drawPixel(x, y, col.r, col.g, col.b, col.a); }
+ static void drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+
+ static void drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col);
+ static void drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col);
+
+ static void drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t* data);
+ static void drawPNG(uint32_t x, uint32_t y, const uint8_t* data);
+
+ static void initFont();
+ static void deinitFont();
+ static void setFontSize(uint32_t size);
+ static void setFontColor(Color col);
+ static void print(uint32_t x, uint32_t y, const char* string, bool alignRight = false);
+ static void print(uint32_t x, uint32_t y, const wchar_t* string, bool alignRight = false);
+ static uint32_t getTextWidth(const char* string);
+ static uint32_t getTextWidth(const wchar_t* string);
+
+private:
+ static bool isBackBuffer;
+
+ static uint8_t* tvBuffer;
+ static uint32_t tvSize;
+ static uint8_t* drcBuffer;
+ static uint32_t drcSize;
+};
diff --git a/source/utils/StorageUtils.cpp b/source/utils/StorageUtils.cpp
new file mode 100644
index 0000000..ac51e90
--- /dev/null
+++ b/source/utils/StorageUtils.cpp
@@ -0,0 +1,133 @@
+#include "StorageUtils.h"
+#include
+
+#include "utils/logger.h"
+#include "utils/json.hpp"
+#include "fs/CFile.hpp"
+#include "fs/FSUtils.h"
+
+static void processJson(wups_storage_item_t* items, nlohmann::json json) {
+ if (items == nullptr) {
+ return;
+ }
+
+ items->data = (wups_storage_item_t*) malloc(json.size() * sizeof(wups_storage_item_t));
+ items->data_size = json.size();
+
+ uint32_t index = 0;
+ for (auto it = json.begin(); it != json.end(); ++it) {
+ wups_storage_item_t* item = &((wups_storage_item_t*) items->data)[index];
+ item->type = WUPS_STORAGE_TYPE_INVALID;
+ item->pending_delete = false;
+ item->data = nullptr;
+ item->key = nullptr;
+
+ item->key = (char*) malloc(it.key().size() + 1);
+ strcpy(item->key, it.key().c_str());
+
+ if (it.value().is_string()) {
+ item->type = WUPS_STORAGE_TYPE_STRING;
+ uint32_t size = it.value().get().size() + 1;
+ item->data = malloc(size);
+ item->data_size = size;
+ strcpy((char*) item->data, it.value().get().c_str());
+ } else if (it.value().is_number_integer()) {
+ item->type = WUPS_STORAGE_TYPE_INT;
+ item->data = malloc(sizeof(int32_t));
+ item->data_size = sizeof(int32_t);
+ *(int32_t*) item->data = it.value().get();
+ } else if (it.value().is_object()) {
+ if (it.value().size() > 0) {
+ item->type = WUPS_STORAGE_TYPE_ITEM;
+ processJson(item, it.value());
+ }
+ } else {
+ DEBUG_FUNCTION_LINE("Unknown type %s for value %s", it.value().type_name(), it.key().c_str());
+ }
+ index++;
+ }
+}
+
+int StorageUtils::OpenStorage(const char* plugin_id, wups_storage_item_t* items) {
+ if (!plugin_id || !items) {
+ return WUPS_STORAGE_ERROR_INVALID_BACKEND_PARAMS;
+ }
+
+ std::string filePath = std::string("fs:/vol/external01/wiiu/plugins/config/") + plugin_id + ".json";
+
+ nlohmann::json j;
+ CFile file(filePath, CFile::ReadOnly);
+ if (file.isOpen() && file.size() > 0) {
+ uint8_t* json_data = new uint8_t[file.size() + 1];
+ json_data[file.size()] = '\0';
+
+ file.read(json_data, file.size());
+ file.close();
+
+ j = nlohmann::json::parse(json_data, nullptr, false);
+ delete[] json_data;
+
+ if (j == nlohmann::detail::value_t::discarded || j.empty() || !j.is_object()) {
+ return WUPS_STORAGE_ERROR_INVALID_JSON;
+ }
+ } else { // empty or no config exists yet
+ DEBUG_FUNCTION_LINE("open failed");
+ return WUPS_STORAGE_ERROR_SUCCESS;
+ }
+
+ processJson(items, j["storageitems"]);
+
+ return WUPS_STORAGE_ERROR_SUCCESS;
+}
+
+static nlohmann::json processItems(wups_storage_item_t* items) {
+ nlohmann::json json;
+
+ if (!items) {
+ return json;
+ }
+
+ for (uint32_t i = 0; i < items->data_size; i++) {
+ wups_storage_item_t* item = &((wups_storage_item_t*) items->data)[i];
+
+ if (item->pending_delete || item->type == WUPS_STORAGE_TYPE_INVALID || !item->data || !item->key) {
+ continue;
+ }
+
+ if (item->type == WUPS_STORAGE_TYPE_STRING) {
+ json[item->key] = (const char*) item->data;
+ } else if (item->type == WUPS_STORAGE_TYPE_INT) {
+ json[item->key] = *(int32_t*) item->data;
+ } else if (item->type == WUPS_STORAGE_TYPE_ITEM) {
+ json[item->key] = processItems(item);
+ } else {
+ DEBUG_FUNCTION_LINE("Saving type %d not implemented", item->type);
+ }
+ }
+ return json;
+}
+
+int StorageUtils::CloseStorage(const char* plugin_id, wups_storage_item_t* items) {
+ if (!plugin_id || !items) {
+ return WUPS_STORAGE_ERROR_INVALID_BACKEND_PARAMS;
+ }
+
+ std::string folderPath = "fs:/vol/external01/wiiu/plugins/config/";
+ std::string filePath = folderPath + plugin_id + ".json";
+
+ FSUtils::CreateSubfolder(folderPath.c_str());
+
+ CFile file(filePath, CFile::WriteOnly);
+ if (!file.isOpen()) {
+ DEBUG_FUNCTION_LINE("Cannot create file %s", filePath.c_str());
+ return WUPS_STORAGE_ERROR_IO;
+ };
+
+ nlohmann::json j;
+ j["storageitems"] = processItems(items);
+
+ std::string jsonString = j.dump(4);
+ file.write((const uint8_t*) jsonString.c_str(), jsonString.size());
+ file.close();
+ return WUPS_STORAGE_ERROR_SUCCESS;
+}
diff --git a/source/utils/StorageUtils.h b/source/utils/StorageUtils.h
new file mode 100644
index 0000000..7cbdbd2
--- /dev/null
+++ b/source/utils/StorageUtils.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+class StorageUtils {
+public:
+ static int OpenStorage(const char* plugin_id, wups_storage_item_t* items);
+ static int CloseStorage(const char* plugin_id, wups_storage_item_t* items);
+};
diff --git a/source/utils/exports.cpp b/source/utils/exports.cpp
index 5e09918..c366e9c 100644
--- a/source/utils/exports.cpp
+++ b/source/utils/exports.cpp
@@ -16,10 +16,11 @@ void fillPluginInformation(plugin_information *out, PluginMetaInformation *metaI
strncpy(out->name, metaInformation->getName().c_str(), 255);
strncpy(out->license, metaInformation->getLicense().c_str(), 255);
strncpy(out->version, metaInformation->getVersion().c_str(), 255);
+ strncpy(out->id, metaInformation->getId().c_str(), 255);
out->size = metaInformation->getSize();
}
-extern "C" int32_t WUPSLoadAndLinkByDataHandle(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size) {
+extern "C" PluginBackendApiErrorType WUPSLoadAndLinkByDataHandle(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size) {
gLinkOnReload.number_used_plugins = 0;
if (plugin_data_handle_list != nullptr && plugin_data_handle_list_size != 0) {
for (uint32_t i = 0; i < plugin_data_handle_list_size; i++) {
@@ -35,12 +36,12 @@ extern "C" int32_t WUPSLoadAndLinkByDataHandle(const plugin_data_handle *plugin_
}
DCFlushRange(&gLinkOnReload, sizeof(gLinkOnReload));
} else {
- return ERROR_INVALID_ARG;
+ return PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
}
- return ERROR_NONE;
+ return PLUGIN_BACKEND_API_ERROR_NONE;
}
-extern "C" int32_t WUPSDeletePluginContainer(const plugin_container_handle *handle_list, uint32_t handle_list_size) {
+extern "C" PluginBackendApiErrorType WUPSDeletePluginContainer(const plugin_container_handle *handle_list, uint32_t handle_list_size) {
if (handle_list != nullptr && handle_list_size != 0) {
for (uint32_t i = 0; i < handle_list_size; i++) {
auto handle = handle_list[i];
@@ -49,10 +50,10 @@ extern "C" int32_t WUPSDeletePluginContainer(const plugin_container_handle *hand
delete pluginContainer;
}
}
- return ERROR_NONE;
+ return PLUGIN_BACKEND_API_ERROR_NONE;
}
-extern "C" int32_t WUPSDeletePluginData(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size) {
+extern "C" PluginBackendApiErrorType WUPSDeletePluginData(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size) {
if (plugin_data_handle_list != nullptr && plugin_data_handle_list_size != 0) {
for (uint32_t i = 0; i < plugin_data_handle_list_size; i++) {
auto handle = plugin_data_handle_list[i];
@@ -61,10 +62,10 @@ extern "C" int32_t WUPSDeletePluginData(const plugin_data_handle *plugin_data_ha
delete pluginData;
}
}
- return ERROR_NONE;
+ return PLUGIN_BACKEND_API_ERROR_NONE;
}
-extern "C" int32_t WUPSLoadPluginAsData(GetPluginInformationInputType inputType, const char *path, char *buffer, size_t size, plugin_data_handle *out) {
+extern "C" PluginBackendApiErrorType WUPSLoadPluginAsData(GetPluginInformationInputType inputType, const char *path, char *buffer, size_t size, plugin_data_handle *out) {
std::optional pluginData;
if (inputType == PLUGIN_INFORMATION_INPUT_TYPE_PATH && path != nullptr) {
pluginData = PluginDataFactory::load(path, gPluginDataHeap);
@@ -73,35 +74,35 @@ extern "C" int32_t WUPSLoadPluginAsData(GetPluginInformationInputType inputType,
memcpy(&data[0], buffer, size);
pluginData = PluginDataFactory::load(data, gPluginDataHeap);
} else {
- return ERROR_INVALID_ARG;
+ return PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
}
if (!pluginData) {
DEBUG_FUNCTION_LINE("Failed to alloc plugin data");
- return ERROR_FAILED_ALLOC;
+ return PLUGIN_BACKEND_API_ERROR_FAILED_ALLOC;
}
if (out == nullptr) {
DEBUG_FUNCTION_LINE("out was NULL");
- return ERROR_INVALID_ARG;
+ return PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
} else {
auto *pluginDataHandle = new PluginData(pluginData.value());
DEBUG_FUNCTION_LINE("Saving plugin data handle: %08X", pluginDataHandle);
*out = (uint32_t) pluginDataHandle;
}
- return ERROR_NONE;
+ return PLUGIN_BACKEND_API_ERROR_NONE;
}
-extern "C" int32_t WUPSLoadPluginAsDataByPath(plugin_data_handle *output, const char *path) {
+extern "C" PluginBackendApiErrorType WUPSLoadPluginAsDataByPath(plugin_data_handle *output, const char *path) {
return WUPSLoadPluginAsData(PLUGIN_INFORMATION_INPUT_TYPE_PATH, path, nullptr, 0, output);
}
-extern "C" int32_t WUPSLoadPluginAsDataByBuffer(plugin_data_handle *output, char *buffer, size_t size) {
+extern "C" PluginBackendApiErrorType WUPSLoadPluginAsDataByBuffer(plugin_data_handle *output, char *buffer, size_t size) {
return WUPSLoadPluginAsData(PLUGIN_INFORMATION_INPUT_TYPE_BUFFER, nullptr, buffer, size, output);
}
-extern "C" int32_t WUPSGetPluginMetaInformation(GetPluginInformationInputType inputType, const char* path, char *buffer, size_t size, plugin_information *output) {
+extern "C" PluginBackendApiErrorType WUPSGetPluginMetaInformation(GetPluginInformationInputType inputType, const char* path, char *buffer, size_t size, plugin_information *output) {
std::optional pluginInfo;
if (inputType == PLUGIN_INFORMATION_INPUT_TYPE_PATH && path != nullptr) {
std::string pathStr(path);
@@ -111,34 +112,34 @@ extern "C" int32_t WUPSGetPluginMetaInformation(GetPluginInformationInputType in
DEBUG_FUNCTION_LINE("PLUGIN_INFORMATION_INPUT_TYPE_BUFFER %08X %d", buffer, size);
pluginInfo = PluginMetaInformationFactory::loadPlugin(buffer, size);
} else {
- return ERROR_INVALID_ARG;
+ return PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
}
if (!pluginInfo) {
DEBUG_FUNCTION_LINE("Failed to load plugin meta information");
- return ERROR_FILE_NOT_FOUND;
+ return PLUGIN_BACKEND_API_ERROR_FILE_NOT_FOUND;
}
DEBUG_FUNCTION_LINE("Loaded plugin meta information");
if (output == nullptr) {
- return ERROR_INVALID_ARG;
+ return PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
} else {
fillPluginInformation(output, &pluginInfo.value());
}
- return ERROR_NONE;
+ return PLUGIN_BACKEND_API_ERROR_NONE;
}
-extern "C" int32_t WUPSGetPluginMetaInformationByPath(plugin_information *output, const char *path) {
+extern "C" PluginBackendApiErrorType WUPSGetPluginMetaInformationByPath(plugin_information *output, const char *path) {
return WUPSGetPluginMetaInformation(PLUGIN_INFORMATION_INPUT_TYPE_PATH, path, nullptr, 0, output);
}
-extern "C" int32_t WUPSGetPluginMetaInformationByBuffer(plugin_information *output, char *buffer, size_t size) {
+extern "C" PluginBackendApiErrorType WUPSGetPluginMetaInformationByBuffer(plugin_information *output, char *buffer, size_t size) {
return WUPSGetPluginMetaInformation(PLUGIN_INFORMATION_INPUT_TYPE_BUFFER, nullptr, buffer, size, output);
}
-extern "C" int32_t WUPSGetPluginDataForContainerHandles(const plugin_container_handle *plugin_container_handle_list, plugin_data_handle *plugin_data_list, uint32_t buffer_size) {
- int res = ERROR_NONE;
+extern "C" PluginBackendApiErrorType WUPSGetPluginDataForContainerHandles(const plugin_container_handle *plugin_container_handle_list, plugin_data_handle *plugin_data_list, uint32_t buffer_size) {
+ PluginBackendApiErrorType res = PLUGIN_BACKEND_API_ERROR_NONE;
if (plugin_container_handle_list != nullptr && buffer_size != 0) {
for (uint32_t i = 0; i < buffer_size; i++) {
auto handle = plugin_container_handle_list[i];
@@ -148,18 +149,19 @@ extern "C" int32_t WUPSGetPluginDataForContainerHandles(const plugin_container_h
plugin_data_list[i] = (uint32_t) pluginData;
}
} else {
- res = ERROR_INVALID_ARG;
+ res = PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
}
return res;
}
-extern "C" int32_t WUPSGetMetaInformation(const plugin_container_handle *plugin_container_handle_list, plugin_information *plugin_information_list, uint32_t buffer_size) {
- int res = ERROR_NONE;
+extern "C" PluginBackendApiErrorType WUPSGetMetaInformation(const plugin_container_handle *plugin_container_handle_list, plugin_information *plugin_information_list, uint32_t buffer_size) {
+ PluginBackendApiErrorType res = PLUGIN_BACKEND_API_ERROR_NONE;
if (plugin_container_handle_list != nullptr && buffer_size != 0) {
for (uint32_t i = 0; i < buffer_size; i++) {
auto handle = plugin_container_handle_list[i];
auto *container = (PluginContainer *) handle;
+ strncpy(plugin_information_list[i].id, container->metaInformation.getId().c_str(), 255);
strncpy(plugin_information_list[i].author, container->metaInformation.getAuthor().c_str(), 255);
strncpy(plugin_information_list[i].buildTimestamp, container->metaInformation.getBuildTimestamp().c_str(), 255);
strncpy(plugin_information_list[i].description, container->metaInformation.getDescription().c_str(), 255);
@@ -169,12 +171,12 @@ extern "C" int32_t WUPSGetMetaInformation(const plugin_container_handle *plugin_
plugin_information_list[i].size = container->metaInformation.getSize();
}
} else {
- res = ERROR_INVALID_ARG;
+ res = PLUGIN_BACKEND_API_ERROR_INVALID_ARG;
}
return res;
}
-extern "C" int32_t WUPSGetLoadedPlugins(plugin_container_handle *io_handles, uint32_t buffer_size, uint32_t *outSize) {
+extern "C" PluginBackendApiErrorType WUPSGetLoadedPlugins(plugin_container_handle *io_handles, uint32_t buffer_size, uint32_t *outSize) {
auto plugins = PluginContainerPersistence::loadPlugins(gPluginInformation);
uint32_t counter = 0;
for (auto &plugin: plugins) {
@@ -190,10 +192,9 @@ extern "C" int32_t WUPSGetLoadedPlugins(plugin_container_handle *io_handles, uin
if (outSize != nullptr) {
*outSize = counter;
}
- return 0;
+ return PLUGIN_BACKEND_API_ERROR_NONE;
}
-
WUMS_EXPORT_FUNCTION(WUPSLoadPluginAsDataByPath);
WUMS_EXPORT_FUNCTION(WUPSLoadPluginAsDataByBuffer);
WUMS_EXPORT_FUNCTION(WUPSLoadPluginAsData);
diff --git a/source/utils/exports.h b/source/utils/exports.h
index baf43ed..3c6c2db 100644
--- a/source/utils/exports.h
+++ b/source/utils/exports.h
@@ -1,61 +1,37 @@
#pragma once
#include
+#include
#ifdef __cplusplus
extern "C" {
#endif
-#define ERROR_NONE 0
-#define ERROR_INVALID_SIZE 0xFFFFFFFF
-#define ERROR_INVALID_ARG 0xFFFFFFFE
-#define ERROR_FAILED_ALLOC 0xFFFFFFFD
-#define ERROR_FILE_NOT_FOUND 0xFFFFFFFC
-
-typedef enum GetPluginInformationInputType {
- PLUGIN_INFORMATION_INPUT_TYPE_PATH = 0,
- PLUGIN_INFORMATION_INPUT_TYPE_BUFFER = 1,
-} GetPluginInformationInputType;
-
-typedef uint32_t plugin_container_handle;
-typedef uint32_t plugin_data_handle;
-
-/* plugin_information message */
-typedef struct __attribute__((__packed__)) plugin_information {
- char name[256];
- char author[256];
- char buildTimestamp[256];
- char description[256];
- char license[256];
- char version[256];
- size_t size;
-} plugin_information;
-
void fillPluginInformation(plugin_information *out, PluginMetaInformation *metaInformation);
-int32_t WUPSLoadAndLinkByDataHandle(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size);
+PluginBackendApiErrorType WUPSLoadAndLinkByDataHandle(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size);
-int32_t WUPSDeletePluginContainer(const plugin_container_handle *handle_list, uint32_t handle_list_size);
+PluginBackendApiErrorType WUPSDeletePluginContainer(const plugin_container_handle *handle_list, uint32_t handle_list_size);
-int32_t WUPSDeletePluginData(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size);
+PluginBackendApiErrorType WUPSDeletePluginData(const plugin_data_handle *plugin_data_handle_list, uint32_t plugin_data_handle_list_size);
-int32_t WUPSLoadPluginAsData(GetPluginInformationInputType inputType, const char *path, char *buffer, size_t size, plugin_data_handle *out);
+PluginBackendApiErrorType WUPSLoadPluginAsData(GetPluginInformationInputType inputType, const char *path, char *buffer, size_t size, plugin_data_handle *out);
-int32_t WUPSLoadPluginAsDataByPath(plugin_data_handle *output, const char *path);
+PluginBackendApiErrorType WUPSLoadPluginAsDataByPath(plugin_data_handle *output, const char *path);
-int32_t WUPSLoadPluginAsDataByBuffer(plugin_data_handle *output, char *buffer, size_t size);
+PluginBackendApiErrorType WUPSLoadPluginAsDataByBuffer(plugin_data_handle *output, char *buffer, size_t size);
-int32_t WUPSGetPluginMetaInformation(GetPluginInformationInputType inputType, const char *path, char *buffer, size_t size, plugin_information *output);
+PluginBackendApiErrorType WUPSGetPluginMetaInformation(GetPluginInformationInputType inputType, const char *path, char *buffer, size_t size, plugin_information *output);
-int32_t WUPSGetPluginMetaInformationByPath(plugin_information *output, const char *path);
+PluginBackendApiErrorType WUPSGetPluginMetaInformationByPath(plugin_information *output, const char *path);
-int32_t WUPSGetPluginMetaInformationByBuffer(plugin_information *output, char *buffer, size_t size);
+PluginBackendApiErrorType WUPSGetPluginMetaInformationByBuffer(plugin_information *output, char *buffer, size_t size);
-int32_t WUPSGetPluginDataForContainerHandles(const plugin_container_handle *plugin_container_handle_list, plugin_data_handle *plugin_data_list, uint32_t buffer_size);
+PluginBackendApiErrorType WUPSGetPluginDataForContainerHandles(const plugin_container_handle *plugin_container_handle_list, plugin_data_handle *plugin_data_list, uint32_t buffer_size);
-int32_t WUPSGetMetaInformation(const plugin_container_handle *plugin_container_handle_list, plugin_information *plugin_information_list, uint32_t buffer_size);
+PluginBackendApiErrorType WUPSGetMetaInformation(const plugin_container_handle *plugin_container_handle_list, plugin_information *plugin_information_list, uint32_t buffer_size);
-int32_t WUPSGetLoadedPlugins(plugin_container_handle *io_handles, uint32_t buffer_size, uint32_t *outSize);
+PluginBackendApiErrorType WUPSGetLoadedPlugins(plugin_container_handle *io_handles, uint32_t buffer_size, uint32_t *outSize);
#ifdef __cplusplus
}
diff --git a/source/utils/json.hpp b/source/utils/json.hpp
new file mode 100644
index 0000000..242f034
--- /dev/null
+++ b/source/utils/json.hpp
@@ -0,0 +1,25447 @@
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.9.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License .
+SPDX-License-Identifier: MIT
+Copyright (c) 2013-2019 Niels Lohmann .
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+
+#define NLOHMANN_JSON_VERSION_MAJOR 3
+#define NLOHMANN_JSON_VERSION_MINOR 9
+#define NLOHMANN_JSON_VERSION_PATCH 1
+
+#include // all_of, find, for_each
+#include // nullptr_t, ptrdiff_t, size_t
+#include // hash, less
+#include // initializer_list
+#include // istream, ostream
+#include // random_access_iterator_tag
+#include // unique_ptr
+#include // accumulate
+#include // string, stoi, to_string
+#include // declval, forward, move, pair, swap
+#include // vector
+
+// #include
+
+
+#include
+
+// #include
+
+
+#include // transform
+#include // array
+#include // forward_list
+#include // inserter, front_inserter, end
+#include