From ed13894dc32743a64a53581724bdd24e3d8f4d37 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sat, 7 Oct 2023 22:38:27 +0200 Subject: [PATCH] Implementation of new StorageAPI --- Makefile | 3 +- source/PluginManagement.cpp | 1 + source/hooks.cpp | 37 +- .../plugin/PluginMetaInformationFactory.cpp | 2 + source/utils/base64.cpp | 127 ++++ source/utils/base64.h | 20 + source/utils/logger.h | 2 + source/utils/storage/StorageItem.cpp | 184 ++++++ source/utils/storage/StorageItem.h | 86 +++ source/utils/storage/StorageItemRoot.h | 24 + source/utils/storage/StorageSubItem.cpp | 93 +++ source/utils/storage/StorageSubItem.h | 46 ++ source/utils/storage/StorageUtils.cpp | 616 ++++++++++++++++++ source/utils/storage/StorageUtils.h | 14 + source/utils/utils.h | 34 +- 15 files changed, 1274 insertions(+), 15 deletions(-) create mode 100644 source/utils/base64.cpp create mode 100644 source/utils/base64.h create mode 100644 source/utils/storage/StorageItem.cpp create mode 100644 source/utils/storage/StorageItem.h create mode 100644 source/utils/storage/StorageItemRoot.h create mode 100644 source/utils/storage/StorageSubItem.cpp create mode 100644 source/utils/storage/StorageSubItem.h create mode 100644 source/utils/storage/StorageUtils.cpp create mode 100644 source/utils/storage/StorageUtils.h diff --git a/Makefile b/Makefile index cbace7c..8e83609 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,8 @@ SOURCES := source \ source/elfio \ source/patcher \ source/plugin \ - source/utils + source/utils \ + source/utils/storage DATA := data INCLUDES := source diff --git a/source/PluginManagement.cpp b/source/PluginManagement.cpp index 402957d..3fa1ad9 100644 --- a/source/PluginManagement.cpp +++ b/source/PluginManagement.cpp @@ -181,6 +181,7 @@ bool PluginManagement::DoFunctionPatches(const std::vector> &plugins) { + CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED); CallHook(plugins, WUPS_LOADER_HOOK_INIT_STORAGE); CallHook(plugins, WUPS_LOADER_HOOK_INIT_PLUGIN); DEBUG_FUNCTION_LINE_VERBOSE("Done calling init hooks"); diff --git a/source/hooks.cpp b/source/hooks.cpp index f4d18a2..82f52ec 100644 --- a/source/hooks.cpp +++ b/source/hooks.cpp @@ -2,6 +2,8 @@ #include "plugin/PluginContainer.h" #include "utils/StorageUtilsDeprecated.h" #include "utils/logger.h" +#include "utils/storage/StorageUtils.h" +#include static const char **hook_names = (const char *[]){ "WUPS_LOADER_HOOK_INIT_WUT_MALLOC", @@ -21,7 +23,7 @@ static const char **hook_names = (const char *[]){ "WUPS_LOADER_HOOK_GET_CONFIG", "WUPS_LOADER_HOOK_CONFIG_CLOSED", - "WUPS_LOADER_HOOK_INIT_STORAGE", + "WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED", "WUPS_LOADER_HOOK_INIT_PLUGIN", "WUPS_LOADER_HOOK_DEINIT_PLUGIN", @@ -29,7 +31,8 @@ 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_APPLICATION_ENDS", + "WUPS_LOADER_HOOK_INIT_STORAGE"}; void CallHook(const std::vector> &plugins, wups_loader_hook_type_t hook_type) { DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hook_names[hook_type], hook_type); @@ -70,20 +73,38 @@ void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type) ((void(*)())((uint32_t *) func_ptr))(); // clang-format on break; - case WUPS_LOADER_HOOK_INIT_STORAGE: { + case WUPS_LOADER_HOOK_INIT_STORAGE: + case WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED: { if (plugin.getMetaInformation().getWUPSVersion() < WUPSVersion(0, 7, 2)) { - WUPSStorageDeprecated::wups_loader_init_storage_args_t args; + WUPSStorageDeprecated::wups_loader_init_storage_args_t_ args{}; args.open_storage_ptr = &WUPSStorageDeprecated::StorageUtils::OpenStorage; args.close_storage_ptr = &WUPSStorageDeprecated::StorageUtils::CloseStorage; args.plugin_id = plugin.getMetaInformation().getStorageId().c_str(); // clang-format off - ((void(*)(WUPSStorageDeprecated::wups_loader_init_storage_args_t))((uint32_t *) func_ptr))(args); + + ((void(*)(WUPSStorageDeprecated::wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args); // clang-format on break; - } else { - DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_STORAGE hook for WUPSVersion 0.7.2 or higher not implemented"); - break; } + wups_loader_init_storage_args_t_ args{}; + args.version = WUPS_STORAGE_CUR_API_VERSION; + args.open_storage_ptr = &StorageUtils::API::OpenStorage; + args.close_storage_ptr = &StorageUtils::API::CloseStorage; + args.delete_item_function_ptr = &StorageUtils::API::DeleteItem; + args.create_sub_item_function_ptr = &StorageUtils::API::CreateSubItem; + args.get_sub_item_function_ptr = &StorageUtils::API::GetSubItem; + args.store_item_function_ptr = &StorageUtils::API::StoreItem; + args.get_item_function_ptr = &StorageUtils::API::GetItem; + args.get_item_size_function_ptr = &StorageUtils::API::GetItemSize; + args.plugin_id = plugin.getMetaInformation().getStorageId().c_str(); + // clang-format off + auto res = ((WUPSStorageError(*)(wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args); + // clang-format on + if (res == WUPS_STORAGE_ERROR_INVALID_VERSION) { + // TODO: More error handling? Notification? + DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_STORAGE failed for plugin %s: WUPS_STORAGE_ERROR_INVALID_VERSION", plugin.getMetaInformation().getName().c_str()); + } + break; } default: { DEBUG_FUNCTION_LINE_ERR("######################################"); diff --git a/source/plugin/PluginMetaInformationFactory.cpp b/source/plugin/PluginMetaInformationFactory.cpp index 628e405..52c0ca9 100644 --- a/source/plugin/PluginMetaInformationFactory.cpp +++ b/source/plugin/PluginMetaInformationFactory.cpp @@ -105,6 +105,8 @@ std::unique_ptr PluginMetaInformationFactory::loadPlugin( } else if (key == "wups") { if (value == "0.7.1") { pluginInfo->setWUPSVersion(0, 7, 1); + } else if (value == "0.7.2") { + pluginInfo->setWUPSVersion(0, 7, 2); } else { error = PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION; DEBUG_FUNCTION_LINE_ERR("Warning: Ignoring plugin - Unsupported WUPS version: %s.", value.c_str()); diff --git a/source/utils/base64.cpp b/source/utils/base64.cpp new file mode 100644 index 0000000..c0d7871 --- /dev/null +++ b/source/utils/base64.cpp @@ -0,0 +1,127 @@ +#include "base64.h" + +#include + +static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +size_t b64_encoded_size(size_t inlen) { + size_t ret; + + ret = inlen; + if (inlen % 3 != 0) + ret += 3 - (inlen % 3); + ret /= 3; + ret *= 4; + + return ret; +} + +char *b64_encode(const uint8_t *in, size_t len) { + char *out; + size_t elen; + size_t i; + size_t j; + size_t v; + + if (in == NULL || len == 0) + return NULL; + + elen = b64_encoded_size(len); + out = (char *) malloc(elen + 1); + out[elen] = '\0'; + + for (i = 0, j = 0; i < len; i += 3, j += 4) { + v = in[i]; + v = i + 1 < len ? v << 8 | in[i + 1] : v << 8; + v = i + 2 < len ? v << 8 | in[i + 2] : v << 8; + + out[j] = b64chars[(v >> 18) & 0x3F]; + out[j + 1] = b64chars[(v >> 12) & 0x3F]; + if (i + 1 < len) { + out[j + 2] = b64chars[(v >> 6) & 0x3F]; + } else { + out[j + 2] = '='; + } + if (i + 2 < len) { + out[j + 3] = b64chars[v & 0x3F]; + } else { + out[j + 3] = '='; + } + } + + return out; +} + +size_t b64_decoded_size(const char *in) { + size_t len; + size_t ret; + size_t i; + + if (in == NULL) + return 0; + + len = strlen(in); + ret = len / 4 * 3; + + for (i = len; i-- > 0;) { + if (in[i] == '=') { + ret--; + } else { + break; + } + } + + return ret; +} + +static const int b64invs[] = { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + +static int b64_isvalidchar(char c) { + if (c >= '0' && c <= '9') + return 1; + if (c >= 'A' && c <= 'Z') + return 1; + if (c >= 'a' && c <= 'z') + return 1; + if (c == '+' || c == '/' || c == '=') + return 1; + return 0; +} + +int b64_decode(const char *in, uint8_t *out, size_t outlen) { + size_t len; + size_t i; + size_t j; + int v; + + if (in == NULL || out == NULL) + return 0; + + len = strlen(in); + if (outlen < b64_decoded_size(in) || len % 4 != 0) + return 0; + + for (i = 0; i < len; i++) { + if (!b64_isvalidchar(in[i])) { + return 0; + } + } + + for (i = 0, j = 0; i < len; i += 4, j += 3) { + v = b64invs[in[i] - 43]; + v = (v << 6) | b64invs[in[i + 1] - 43]; + v = in[i + 2] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 2] - 43]; + v = in[i + 3] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 3] - 43]; + + out[j] = (v >> 16) & 0xFF; + if (in[i + 2] != '=') + out[j + 1] = (v >> 8) & 0xFF; + if (in[i + 3] != '=') + out[j + 2] = v & 0xFF; + } + + return 1; +} diff --git a/source/utils/base64.h b/source/utils/base64.h new file mode 100644 index 0000000..afedc45 --- /dev/null +++ b/source/utils/base64.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +// based on https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/ + +#ifdef __cplusplus +extern "C" { +#endif + +size_t b64_encoded_size(size_t inlen); +char *b64_encode(const uint8_t *in, size_t len); + +size_t b64_decoded_size(const char *in); +int b64_decode(const char *in, uint8_t *out, size_t outlen); + +#ifdef __cplusplus +} +#endif diff --git a/source/utils/logger.h b/source/utils/logger.h index aaa3ef6..1b9f89b 100644 --- a/source/utils/logger.h +++ b/source/utils/logger.h @@ -39,6 +39,7 @@ extern "C" { #define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS); @@ -54,6 +55,7 @@ extern "C" { #define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) while (0) #define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS); diff --git a/source/utils/storage/StorageItem.cpp b/source/utils/storage/StorageItem.cpp new file mode 100644 index 0000000..4ba2856 --- /dev/null +++ b/source/utils/storage/StorageItem.cpp @@ -0,0 +1,184 @@ +#include "StorageItem.h" + +void StorageItem::setValue(const std::string &value) { + mData = value; + mType = StorageItemType::String; + mBinaryConversionDone = false; +} + +void StorageItem::setValue(bool value) { + mData = value; + mType = StorageItemType::Boolean; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(int32_t value) { + mData = (int64_t) value; + mType = StorageItemType::S64; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(int64_t value) { + mData = value; + mType = StorageItemType::S64; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(uint64_t value) { + mData = value; + mType = StorageItemType::U64; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(uint32_t value) { + mData = (uint64_t) value; + mType = StorageItemType::U64; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(float value) { + mData = (double) value; + mType = StorageItemType::Double; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(double value) { + mData = value; + mType = StorageItemType::Double; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(const std::vector &data) { + mData = data; + mType = StorageItemType::Binary; + mBinaryConversionDone = true; +} + +void StorageItem::setValue(const uint8_t *data, size_t size) { + setValue(std::vector(data, data + size)); +} + +bool StorageItem::getValue(bool &result) const { + if (mType == StorageItemType::Boolean) { + result = std::get(mData); + return true; + } else if (mType == StorageItemType::S64) { + result = !!(std::get(mData)); + return true; + } else if (mType == StorageItemType::U64) { + result = !!(std::get(mData)); + return true; + } + return false; +} + +bool StorageItem::getValue(int32_t &result) const { + if (mType == StorageItemType::S64) { + result = (int32_t) std::get(mData); + return true; + } else if (mType == StorageItemType::U64) { + result = (int32_t) std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(std::vector &result) { + if (mType == StorageItemType::Binary) { + result = std::get>(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(std::string &result) const { + if (mType == StorageItemType::String) { + result = std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(double &result) const { + if (mType == StorageItemType::Double) { + result = std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(float &result) const { + if (mType == StorageItemType::Double) { + result = (float) std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(uint64_t &result) const { + if (mType == StorageItemType::U64) { + result = std::get(mData); + return true; + } else if (mType == StorageItemType::S64) { + result = (uint64_t) std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(uint32_t &result) const { + if (mType == StorageItemType::U64) { + result = (uint32_t) std::get(mData); + return true; + } else if (mType == StorageItemType::S64) { + result = (uint32_t) std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getValue(int64_t &result) const { + if (mType == StorageItemType::S64) { + result = std::get(mData); + return true; + } else if (mType == StorageItemType::U64) { + result = (int64_t) std::get(mData); + return true; + } + return false; +} + +bool StorageItem::getItemSize(uint32_t &outSize) const { + if (mType == StorageItemType::String) { + outSize = (std::get(mData).length() + 1); + return true; + } else if (mType == StorageItemType::Binary) { + outSize = std::get>(mData).size(); + return true; + } + return false; +} + +bool StorageItem::attemptBinaryConversion() { + if (mBinaryConversionDone) { + return true; + } + if (mType == StorageItemType::String) { + auto &tmp = std::get(mData); + auto dec_size = b64_decoded_size(tmp.c_str()); + if (dec_size > 0) { + auto *dec = (uint8_t *) malloc(dec_size); + if (dec) { + if (b64_decode(tmp.c_str(), dec, dec_size)) { + setValue(dec, dec_size); + } + free(dec); + } else { + DEBUG_FUNCTION_LINE_ERR("Malloc failed for string->binary parsing"); + return false; + } + } + } + mBinaryConversionDone = true; + return true; +} diff --git a/source/utils/storage/StorageItem.h b/source/utils/storage/StorageItem.h new file mode 100644 index 0000000..ea4f8d3 --- /dev/null +++ b/source/utils/storage/StorageItem.h @@ -0,0 +1,86 @@ +#pragma once + +#include "utils/base64.h" +#include "utils/logger.h" +#include +#include +#include +#include +#include +#include + +enum class StorageItemType { None, + Boolean, + String, + Binary, + S64, + U64, + Double }; + +class StorageItem { +public: + explicit StorageItem(std::string key) : mData(std::monostate{}), mType(StorageItemType::None), mKey(std::move(key)) { + } + + [[nodiscard]] uint32_t getHandle() const { + return (uint32_t) this; + } + + // Setters for different types + void setValue(bool value); + + void setValue(const std::string &value); + + void setValue(int32_t value); + + void setValue(int64_t value); + + void setValue(uint64_t value); + + void setValue(uint32_t value); + + void setValue(float value); + + void setValue(double value); + + void setValue(const std::vector &data); + + void setValue(const uint8_t *data, size_t size); + + bool getValue(bool &result) const; + + bool getValue(int32_t &result) const; + + bool getValue(int64_t &result) const; + + bool getValue(uint32_t &result) const; + + bool getValue(uint64_t &result) const; + + bool getValue(float &result) const; + + bool getValue(double &result) const; + + bool getValue(std::string &result) const; + + bool getValue(std::vector &result); + + [[nodiscard]] StorageItemType getType() const { + return mType; + } + + [[nodiscard]] const std::string &getKey() const { + return mKey; + } + + bool getItemSize(uint32_t &outSize) const; + + bool attemptBinaryConversion(); + +private: + std::variant> mData = std::monostate{}; + StorageItemType mType = StorageItemType::None; + std::string mKey = {}; + + bool mBinaryConversionDone = true; +}; diff --git a/source/utils/storage/StorageItemRoot.h b/source/utils/storage/StorageItemRoot.h new file mode 100644 index 0000000..78f74c7 --- /dev/null +++ b/source/utils/storage/StorageItemRoot.h @@ -0,0 +1,24 @@ +#pragma once + +#include "StorageItem.h" +#include "StorageSubItem.h" +#include "utils/logger.h" +#include +#include +#include +#include +#include +#include + +class StorageItemRoot : public StorageSubItem { +public: + explicit StorageItemRoot(const std::string &plugin_name) : StorageSubItem(plugin_name), mPluginName(plugin_name) { + } + + [[nodiscard]] const std::string &getPluginId() const { + return mPluginName; + } + +private: + std::string mPluginName; +}; diff --git a/source/utils/storage/StorageSubItem.cpp b/source/utils/storage/StorageSubItem.cpp new file mode 100644 index 0000000..f86b03d --- /dev/null +++ b/source/utils/storage/StorageSubItem.cpp @@ -0,0 +1,93 @@ +#include "StorageSubItem.h" + +StorageSubItem *StorageSubItem::getSubItem(wups_storage_item item) const { + // Try to find the sub-item based on item handle. + for (const auto &cur : mSubCategories) { + if (cur->getHandle() == (uint32_t) item) { + return cur.get(); + } + } + + // If not found in current category, recursively search in sub-categories. + for (const auto &cur : mSubCategories) { + auto res = cur->getSubItem(item); + if (res) { + return res; + } + } + + return nullptr; +} + +const StorageSubItem *StorageSubItem::getSubItem(const char *key) const { + // Try to find the sub-item based on key. + for (const auto &cur : mSubCategories) { + if (cur->getKey() == key) { + return cur.get(); + } + } + + return nullptr; +} + +bool StorageSubItem::deleteItem(const char *key) { + if (remove_first_if(mSubCategories, [key](auto &cur) { return cur->getKey() == key; })) { + return true; + } + + auto itemItr = mItems.find(key); + if (itemItr != mItems.end()) { + mItems.erase(itemItr); + return true; // Item found and deleted. + } + return false; +} + +StorageItem *StorageSubItem::createItem(const char *key, StorageSubItem::StorageSubItemError &error) { + for (const auto &cur : mSubCategories) { + if (cur->getKey() == key) { + error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE; + return nullptr; + } + } + + auto res = make_unique_nothrow(key); + if (!res) { + error = STORAGE_SUB_ITEM_ERROR_MALLOC_FAILED; + return nullptr; + } + auto *result = res.get(); + mItems[key] = std::move(res); + return result; +} + +StorageSubItem *StorageSubItem::createSubItem(const char *key, StorageSubItem::StorageSubItemError &error) { + auto resItr = mItems.find(key); + if (resItr != mItems.end()) { + error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE; + return nullptr; + } + for (const auto &cur : mSubCategories) { + if (cur->getKey() == key) { + error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE; + return nullptr; + } + } + + auto res = make_unique_nothrow(key); + if (!res) { + error = STORAGE_SUB_ITEM_ERROR_MALLOC_FAILED; + return nullptr; + } + auto *result = res.get(); + mSubCategories.push_front(std::move(res)); + return result; +} + +StorageItem *StorageSubItem::getItem(const char *name) { + auto resItr = mItems.find(name); + if (resItr != mItems.end()) { + return resItr->second.get(); + } + return nullptr; +} diff --git a/source/utils/storage/StorageSubItem.h b/source/utils/storage/StorageSubItem.h new file mode 100644 index 0000000..8feca09 --- /dev/null +++ b/source/utils/storage/StorageSubItem.h @@ -0,0 +1,46 @@ +#pragma once + +#include "StorageItem.h" +#include "utils/utils.h" +#include +#include +#include +#include +#include +#include + +class StorageSubItem : public StorageItem { +public: + enum StorageSubItemError { + STORAGE_SUB_ITEM_ERROR_NONE = 0, + STORAGE_SUB_ITEM_ERROR_MALLOC_FAILED = 1, + STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE = 2, + }; + + explicit StorageSubItem(const std::string &key) : StorageItem(key) { + } + + StorageSubItem *getSubItem(wups_storage_item item) const; + + const StorageSubItem *getSubItem(const char *key) const; + + bool deleteItem(const char *key); + + StorageItem *createItem(const char *key, StorageSubItem::StorageSubItemError &error); + + StorageSubItem *createSubItem(const char *key, StorageSubItem::StorageSubItemError &error); + + StorageItem *getItem(const char *name); + + [[nodiscard]] const std::forward_list> &getSubItems() const { + return mSubCategories; + } + + [[nodiscard]] const std::map> &getItems() const { + return mItems; + } + +private: + std::forward_list> mSubCategories; + std::map> mItems; +}; diff --git a/source/utils/storage/StorageUtils.cpp b/source/utils/storage/StorageUtils.cpp new file mode 100644 index 0000000..54f89d6 --- /dev/null +++ b/source/utils/storage/StorageUtils.cpp @@ -0,0 +1,616 @@ +#include "StorageUtils.h" +#include "NotificationsUtils.h" +#include "StorageItemRoot.h" +#include "fs/CFile.hpp" +#include "fs/FSUtils.h" +#include "utils/StringTools.h" +#include "utils/base64.h" +#include "utils/json.hpp" +#include "utils/logger.h" +#include "utils/utils.h" +#include +#include +namespace StorageUtils { + std::forward_list> gStorage; + std::mutex gStorageMutex; + + namespace Helper { + static WUPSStorageError ConvertToWUPSError(const StorageSubItem::StorageSubItemError &error) { + switch (error) { + case StorageSubItem::STORAGE_SUB_ITEM_ERROR_NONE: + return WUPS_STORAGE_ERROR_SUCCESS; + case StorageSubItem::STORAGE_SUB_ITEM_ERROR_MALLOC_FAILED: + return WUPS_STORAGE_ERROR_MALLOC_FAILED; + case StorageSubItem::STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE: + return WUPS_STORAGE_ERROR_ALREADY_EXISTS; + } + return WUPS_STORAGE_ERROR_UNKNOWN_ERROR; + } + + static bool deserializeFromJson(const nlohmann::json &json, StorageSubItem &item) { + for (auto it = json.begin(); it != json.end(); ++it) { + StorageSubItem::StorageSubItemError subItemError = StorageSubItem::STORAGE_SUB_ITEM_ERROR_NONE; + if (it.value().is_object()) { + auto res = item.createSubItem(it.key().c_str(), subItemError); + if (!res) { + DEBUG_FUNCTION_LINE_WARN("Failed to create sub item: Error %d", subItemError); + return false; + } + if (!it.value().empty()) { + if (!deserializeFromJson(it.value(), *res)) { + DEBUG_FUNCTION_LINE_WARN("Deserialization of sub item failed."); + return false; + } + } + } else { + + auto res = item.createItem(it.key().c_str(), subItemError); + + if (!res) { + DEBUG_FUNCTION_LINE_WARN("Failed to create Item for key %s. Error %d", it.key().c_str(), subItemError); + return false; + } + if (it.value().is_string()) { + auto val = it.value().get(); + res->setValue(val); + } else if (it.value().is_boolean()) { + auto val = it.value().get(); + res->setValue(val); + } else if (it.value().is_number_unsigned()) { + auto val = it.value().get(); + res->setValue(val); + } else if (it.value().is_number_integer()) { + auto val = it.value().get(); + res->setValue(val); + } else if (it.value().is_number_float()) { + auto val = it.value().get(); + res->setValue(val); + } else { + DEBUG_FUNCTION_LINE_ERR("Unknown type %s for value %s", it.value().type_name(), it.key().c_str()); + } + } + } + return true; + } + + static std::unique_ptr deserializeFromJson(const nlohmann::json &json, const std::string &key) { + if (json.empty() || !json.is_object()) { + return nullptr; + } + auto root = make_unique_nothrow(key); + if (root) { + if (deserializeFromJson(json, *root)) { + return root; + } + } + return nullptr; + } + + static nlohmann::json serializeToJson(const StorageSubItem &baseItem) { + nlohmann::json json = nlohmann::json::object(); + + for (const auto &curSubItem : baseItem.getSubItems()) { + json[curSubItem->getKey()] = serializeToJson(*curSubItem); + } + + for (const auto &[key, value] : baseItem.getItems()) { + switch ((StorageItemType) value->getType()) { + case StorageItemType::String: { + std::string res; + if (value->getValue(res)) { + json[key] = res; + } + break; + } + case StorageItemType::Boolean: { + bool res; + if (value->getValue(res)) { + json[key] = res; + } + break; + } + case StorageItemType::S64: { + int64_t res; + if (value->getValue(res)) { + json[key] = res; + } + break; + } + case StorageItemType::U64: { + uint64_t res; + if (value->getValue(res)) { + json[key] = res; + } + break; + } + case StorageItemType::Double: { + double res; + if (value->getValue(res)) { + json[key] = res; + } + break; + } + case StorageItemType::Binary: { + std::vector tmp; + if (value->getValue(tmp)) { + auto *enc = b64_encode(tmp.data(), tmp.size()); + if (enc) { + json[key] = enc; + free(enc); + } else { + DEBUG_FUNCTION_LINE_WARN("Failed to store binary item: Malloc failed"); + } + } + break; + } + case StorageItemType::None: + DEBUG_FUNCTION_LINE_WARN("Skip: StorageItemType::None"); + break; + } + } + + return json; + } + + static StorageSubItem *getRootItem(wups_storage_root_item root) { + for (const auto &cur : gStorage) { + if (cur->getHandle() == (uint32_t) root) { + return cur.get(); + } + } + + return nullptr; + } + + static StorageSubItem *getSubItem(wups_storage_root_item root, wups_storage_item parent) { + auto rootItem = getRootItem(root); + if (rootItem) { + if (parent == nullptr) { + return rootItem; + } + return rootItem->getSubItem(parent); + } + return nullptr; + } + + + static WUPSStorageError WriteStorageToSD(const char *plugin_id) { + std::shared_ptr rootItem; + for (const auto &cur : gStorage) { + if (cur->getPluginId() == plugin_id) { + rootItem = cur; + break; + } + } + if (!rootItem) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + std::string folderPath = getPluginPath() + "/config/"; + std::string filePath = folderPath + rootItem->getPluginId() + ".json"; + + FSUtils::CreateSubfolder(folderPath); + + CFile file(filePath, CFile::WriteOnly); + if (!file.isOpen()) { + DEBUG_FUNCTION_LINE_ERR("Cannot create file %s", filePath.c_str()); + return WUPS_STORAGE_ERROR_SUCCESS; + } + + nlohmann::json j; + j["storageitems"] = serializeToJson(*rootItem); + + std::string jsonString = j.dump(4, ' ', false, nlohmann::json::error_handler_t::ignore); + auto writeResult = file.write((const uint8_t *) jsonString.c_str(), jsonString.size()); + + file.close(); + + if (writeResult != (int32_t) jsonString.size()) { + return WUPS_STORAGE_ERROR_SUCCESS; + } + return WUPS_STORAGE_ERROR_SUCCESS; + } + + static StorageItem *createOrGetItem(wups_storage_root_item root, wups_storage_item parent, const char *key, WUPSStorageError &error) { + const auto subItem = getSubItem(root, parent); + if (!subItem) { + error = WUPS_STORAGE_ERROR_NOT_FOUND; + return {}; + } + auto res = subItem->getItem(key); + StorageSubItem::StorageSubItemError subItemError = StorageSubItem::STORAGE_SUB_ITEM_ERROR_NONE; + if (!res) { + if (!(res = subItem->createItem(key, subItemError))) { + error = StorageUtils::Helper::ConvertToWUPSError(subItemError); + } + } + if (res) { + error = WUPS_STORAGE_ERROR_SUCCESS; + } + return res; + } + + template + WUPSStorageError StoreItemGeneric(wups_storage_root_item root, wups_storage_item parent, const char *key, T value) { + WUPSStorageError err; + auto item = createOrGetItem(root, parent, key, err); + if (item && err == WUPS_STORAGE_ERROR_SUCCESS) { + item->setValue(value); + return WUPS_STORAGE_ERROR_SUCCESS; + } + return err; + } + + template + WUPSStorageError GetItemEx(wups_storage_root_item root, wups_storage_item parent, const char *key, T &result) { + auto subItem = getSubItem(root, parent); + if (!subItem) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + auto item = subItem->getItem(key); + if (item) { + if (item->getValue(result)) { + return WUPS_STORAGE_ERROR_SUCCESS; + } + DEBUG_FUNCTION_LINE_ERR("GetValue failed? %s", key); + return WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE; + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + template + WUPSStorageError GetItemGeneric(wups_storage_root_item root, wups_storage_item parent, const char *key, T *result) { + if (!result) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + T tmp; + auto res = GetItemEx(root, parent, key, tmp); + if (res == WUPS_STORAGE_ERROR_SUCCESS) { + *result = tmp; + } + return res; + } + + + /** + * Binary items are serialized as base64 encoded string. The first time they are read they'll get converted into binary data. + */ + WUPSStorageError GetAndFixBinaryItem(wups_storage_root_item root, wups_storage_item parent, const char *key, std::vector &result) { + auto subItem = getSubItem(root, parent); + if (!subItem) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + WUPSStorageError err = WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE; + auto item = subItem->getItem(key); + if (item) { + // Trigger potential string->binary conversion + if (!item->attemptBinaryConversion()) { + return WUPS_STORAGE_ERROR_MALLOC_FAILED; + } + if (item->getValue(result)) { + return WUPS_STORAGE_ERROR_SUCCESS; + } + return err; + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + static WUPSStorageError GetStringItem(wups_storage_root_item root, wups_storage_item parent, const char *key, void *data, uint32_t maxSize, uint32_t *outSize) { + std::string tmp; + auto res = GetItemEx(root, parent, key, tmp); + if (res == WUPS_STORAGE_ERROR_SUCCESS) { + if (maxSize <= tmp.size()) { // maxSize needs to be bigger because of the null-terminator + return WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL; + } + strncpy((char *) data, tmp.c_str(), tmp.size()); + ((char *) data)[maxSize - 1] = '\0'; + if (outSize) { + *outSize = strlen((char *) data) + 1; + } + return WUPS_STORAGE_ERROR_SUCCESS; + } + return res; + } + + static WUPSStorageError GetBinaryItem(wups_storage_root_item root, wups_storage_item parent, const char *key, const void *data, uint32_t maxSize, uint32_t *outSize) { + std::vector tmp; + auto res = GetAndFixBinaryItem(root, parent, key, tmp); + if (res == WUPS_STORAGE_ERROR_SUCCESS) { + if (maxSize < tmp.size()) { + return WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL; + } + memcpy((uint8_t *) data, tmp.data(), tmp.size()); + if (outSize) { + *outSize = tmp.size(); + } + return WUPS_STORAGE_ERROR_SUCCESS; + } + return res; + } + } // namespace Helper + + namespace API { + WUPSStorageError OpenStorage(const char *plugin_id, wups_storage_root_item *item) { + std::lock_guard lock(gStorageMutex); + // Check if we already have a storage with the given plugin id + for (const auto &cur : gStorage) { + if (cur->getPluginId() == plugin_id) { + *item = (wups_storage_root_item) cur->getHandle(); + return WUPS_STORAGE_ERROR_ALREADY_OPENED; + } + } + + + std::string filePath = getPluginPath() + "/config/" + plugin_id + ".json"; + nlohmann::json j; + { + CFile file(filePath, CFile::ReadOnly); + if (file.isOpen() && file.size() > 0) { + auto *json_data = (uint8_t *) memalign(0x40, ROUNDUP(file.size() + 1, 0x40)); + if (!json_data) { + DEBUG_FUNCTION_LINE_WARN("Failed to create StorageItem: Malloc failed"); + return WUPS_STORAGE_ERROR_MALLOC_FAILED; + } + + file.read(json_data, file.size()); + + json_data[file.size()] = '\0'; + + file.close(); + j = nlohmann::json::parse(json_data, nullptr, false); + free(json_data); + + if (j == nlohmann::detail::value_t::discarded || j.empty() || !j.is_object()) { + std::string errorMessage = string_format("Corrupted plugin storage detected: \"%s\". You have to reconfigure the plugin.", plugin_id); + DEBUG_FUNCTION_LINE_ERR("%s", errorMessage.c_str()); + remove(filePath.c_str()); + + DisplayErrorNotificationMessage(errorMessage, 10.0f); + } + } + } + + + std::unique_ptr storage; + if (j.empty() || !j.is_object() || !j.contains("storageitems") || j["storageitems"].empty() || !j["storageitems"].is_object()) { + storage = make_unique_nothrow(plugin_id); + } else { + storage = StorageUtils::Helper::deserializeFromJson(j["storageitems"], plugin_id); + if (!storage) { + storage = make_unique_nothrow(plugin_id); + } + } + + if (!storage) { + return WUPS_STORAGE_ERROR_MALLOC_FAILED; + } + + *item = (wups_storage_root_item) storage->getHandle(); + gStorage.push_front(std::move(storage)); + + return WUPS_STORAGE_ERROR_SUCCESS; + } + + WUPSStorageError CloseStorage(const char *plugin_id) { + std::lock_guard lock(gStorageMutex); + + auto res = StorageUtils::Helper::WriteStorageToSD(plugin_id); + // TODO: handle write error? + + if (!remove_locked_first_if(gStorageMutex, gStorage, [plugin_id](auto &cur) { return cur->getPluginId() == plugin_id; })) { + DEBUG_FUNCTION_LINE_WARN("Failed to close storage: Not opened (\"%s\")", plugin_id); + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + return res; + } + + WUPSStorageError CreateSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem) { + if (!outItem) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + std::lock_guard lock(gStorageMutex); + auto subItem = StorageUtils::Helper::getSubItem(root, parent); + if (subItem) { + StorageSubItem::StorageSubItemError error = StorageSubItem::STORAGE_SUB_ITEM_ERROR_NONE; + auto res = subItem->createSubItem(key, error); + if (!res) { + return StorageUtils::Helper::ConvertToWUPSError(error); + } + *outItem = (wups_storage_item) res->getHandle(); + return WUPS_STORAGE_ERROR_SUCCESS; + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + WUPSStorageError GetSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem) { + if (!outItem) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + std::lock_guard lock(gStorageMutex); + auto subItem = StorageUtils::Helper::getSubItem(root, parent); + if (subItem) { + auto res = subItem->getSubItem(key); + if (!res) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + *outItem = (wups_storage_item) res->getHandle(); + return WUPS_STORAGE_ERROR_SUCCESS; + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + WUPSStorageError DeleteItem(wups_storage_root_item root, wups_storage_item parent, const char *key) { + std::lock_guard lock(gStorageMutex); + auto subItem = StorageUtils::Helper::getSubItem(root, parent); + if (subItem) { + auto res = subItem->deleteItem(key); + if (!res) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + return WUPS_STORAGE_ERROR_SUCCESS; + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + WUPSStorageError GetItemSize(wups_storage_root_item root, wups_storage_item parent, const char *key, uint32_t *outSize) { + if (!outSize) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + std::lock_guard lock(gStorageMutex); + auto subItem = StorageUtils::Helper::getSubItem(root, parent); + if (!subItem) { + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + auto item = subItem->getItem(key); + if (item) { + // Trigger potential string -> binary conversion. + if (!item->attemptBinaryConversion()) { + return WUPS_STORAGE_ERROR_MALLOC_FAILED; + } + uint32_t tmp = 0; + if (item->getItemSize(tmp)) { + *outSize = tmp; + return WUPS_STORAGE_ERROR_SUCCESS; + } + return WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE; + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + + WUPSStorageError StoreItem(wups_storage_root_item root, wups_storage_item parent, const char *key, WUPSStorageItemType itemType, void *data, uint32_t length) { + std::lock_guard lock(gStorageMutex); + switch ((WUPSStorageItemTypes) itemType) { + case WUPS_STORAGE_ITEM_S32: { + if (data == nullptr || length != sizeof(int32_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as S32: %d", key, *(int32_t *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(int32_t *) data); + } + case WUPS_STORAGE_ITEM_S64: { + if (data == nullptr || length != sizeof(int64_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as S64: %lld", key, *(int64_t *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(int64_t *) data); + } + case WUPS_STORAGE_ITEM_U32: { + if (data == nullptr || length != sizeof(uint32_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as u32: %u", key, *(uint32_t *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(uint32_t *) data); + } + case WUPS_STORAGE_ITEM_U64: { + if (data == nullptr || length != sizeof(uint64_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as u64: %llu", key, *(uint64_t *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(uint64_t *) data); + } + case WUPS_STORAGE_ITEM_STRING: { + if (data == nullptr || length == 0) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + std::string tmp((const char *) data, length); + + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as string: %s", key, tmp.c_str()); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, tmp); + } + case WUPS_STORAGE_ITEM_BINARY: { + if (data == nullptr || length == 0) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + std::vector tmp((uint8_t *) data, ((uint8_t *) data) + length); + + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as binary: size %d", key, tmp.size()); + return StorageUtils::Helper::StoreItemGeneric>(root, parent, key, tmp); + } + case WUPS_STORAGE_ITEM_BOOL: { + if (data == nullptr || length != sizeof(bool)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as bool: %d", key, *(bool *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(bool *) data); + } + case WUPS_STORAGE_ITEM_FLOAT: { + if (data == nullptr || length != sizeof(float)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as float: %f", key, *(float *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(float *) data); + } + case WUPS_STORAGE_ITEM_DOUBLE: { + if (data == nullptr || length != sizeof(double)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Store %s as double: %f", key, *(double *) data); + return StorageUtils::Helper::StoreItemGeneric(root, parent, key, *(double *) data); + } + } + DEBUG_FUNCTION_LINE_ERR("Store failed!"); + return WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE; + } + + WUPSStorageError GetItem(wups_storage_root_item root, wups_storage_item parent, const char *key, WUPSStorageItemType itemType, void *data, uint32_t maxSize, uint32_t *outSize) { + std::lock_guard lock(gStorageMutex); + switch ((WUPSStorageItemTypes) itemType) { + case WUPS_STORAGE_ITEM_STRING: { + if (!data || maxSize == 0) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetStringItem(root, parent, key, data, maxSize, outSize); + } + case WUPS_STORAGE_ITEM_BINARY: { + if (!data || maxSize == 0) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetBinaryItem(root, parent, key, data, maxSize, outSize); + } + case WUPS_STORAGE_ITEM_BOOL: { + if (!data || maxSize != sizeof(bool)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (bool *) data); + } + case WUPS_STORAGE_ITEM_S32: { + if (!data || maxSize != sizeof(int32_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (int32_t *) data); + } + case WUPS_STORAGE_ITEM_S64: { + if (!data || maxSize != sizeof(int64_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (int64_t *) data); + } + case WUPS_STORAGE_ITEM_U32: { + if (!data || maxSize != sizeof(uint32_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (uint32_t *) data); + } + case WUPS_STORAGE_ITEM_U64: { + if (!data || maxSize != sizeof(uint64_t)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (uint64_t *) data); + } + case WUPS_STORAGE_ITEM_FLOAT: { + if (!data || maxSize != sizeof(float)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (float *) data); + } + case WUPS_STORAGE_ITEM_DOUBLE: { + if (!data || maxSize != sizeof(double)) { + return WUPS_STORAGE_ERROR_INVALID_ARGS; + } + return StorageUtils::Helper::GetItemGeneric(root, parent, key, (double *) data); + } + } + return WUPS_STORAGE_ERROR_NOT_FOUND; + } + } // namespace API +} // namespace StorageUtils \ No newline at end of file diff --git a/source/utils/storage/StorageUtils.h b/source/utils/storage/StorageUtils.h new file mode 100644 index 0000000..3ef5071 --- /dev/null +++ b/source/utils/storage/StorageUtils.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace StorageUtils::API { + WUPSStorageError OpenStorage(const char *plugin_id, wups_storage_root_item *item); + WUPSStorageError CloseStorage(const char *plugin_id); + WUPSStorageError DeleteItem(wups_storage_root_item root, wups_storage_item parent, const char *key); + WUPSStorageError CreateSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem); + WUPSStorageError GetSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem); + WUPSStorageError StoreItem(wups_storage_root_item root, wups_storage_item parent, const char *key, WUPSStorageItemType itemType, void *data, uint32_t length); + WUPSStorageError GetItem(wups_storage_root_item root, wups_storage_item parent, const char *key, WUPSStorageItemType itemType, void *data, uint32_t maxSize, uint32_t *outSize); + WUPSStorageError GetItemSize(wups_storage_root_item root, wups_storage_item parent, const char *key, uint32_t *outSize); +} // namespace StorageUtils::API diff --git a/source/utils/utils.h b/source/utils/utils.h index 50a4f51..eaf0b5c 100644 --- a/source/utils/utils.h +++ b/source/utils/utils.h @@ -63,18 +63,40 @@ std::shared_ptr make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std:: } template -bool remove_locked_first_if(std::mutex &mutex, Container &container, Predicate pred) { - std::lock_guard lock(mutex); +typename std::enable_if>::value, bool>::type +remove_first_if(Container &container, Predicate pred) { + auto it = container.before_begin(); - auto it = std::find_if(container.begin(), container.end(), pred); - if (it != container.end()) { - container.erase(it); - return true; + for (auto prev = it, current = ++it; current != container.end(); ++prev, ++current) { + if (pred(*current)) { + container.erase_after(prev); + return true; + } } return false; } +template +typename std::enable_if>::value, bool>::type +remove_first_if(Container &container, Predicate pred) { + auto it = container.begin(); + while (it != container.end()) { + if (pred(*it)) { + container.erase(it); + return true; + } + ++it; + } + return false; +} + +template +bool remove_locked_first_if(std::mutex &mutex, Container &container, Predicate pred) { + std::lock_guard lock(mutex); + return remove_first_if(container, pred); +} + std::string getPluginPath(); OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr);