Simplify the StorageAPI

This commit is contained in:
Maschell 2023-12-16 12:44:20 +01:00
parent 921b5ce157
commit acc372c836
12 changed files with 254 additions and 141 deletions

View File

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

View File

@ -89,21 +89,22 @@ void CallHook(const PluginContainer &plugin, wups_loader_hook_type_t hook_type)
} }
wups_loader_init_storage_args_t_ args{}; wups_loader_init_storage_args_t_ args{};
args.version = WUPS_STORAGE_CUR_API_VERSION; args.version = WUPS_STORAGE_CUR_API_VERSION;
args.open_storage_ptr = &StorageUtils::API::OpenStorage; args.root_item = plugin.getStorageRootItem();
args.close_storage_ptr = &StorageUtils::API::CloseStorage; args.save_function_ptr = &StorageUtils::API::SaveStorage;
args.force_reload_function_ptr = &StorageUtils::API::ForceReloadStorage;
args.wipe_storage_function_ptr = &StorageUtils::API::WipeStorage;
args.delete_item_function_ptr = &StorageUtils::API::DeleteItem; args.delete_item_function_ptr = &StorageUtils::API::DeleteItem;
args.create_sub_item_function_ptr = &StorageUtils::API::CreateSubItem; args.create_sub_item_function_ptr = &StorageUtils::API::CreateSubItem;
args.get_sub_item_function_ptr = &StorageUtils::API::GetSubItem; args.get_sub_item_function_ptr = &StorageUtils::API::GetSubItem;
args.store_item_function_ptr = &StorageUtils::API::StoreItem; args.store_item_function_ptr = &StorageUtils::API::StoreItem;
args.get_item_function_ptr = &StorageUtils::API::GetItem; args.get_item_function_ptr = &StorageUtils::API::GetItem;
args.get_item_size_function_ptr = &StorageUtils::API::GetItemSize; args.get_item_size_function_ptr = &StorageUtils::API::GetItemSize;
args.plugin_id = plugin.getMetaInformation().getStorageId().c_str();
// clang-format off // clang-format off
auto res = ((WUPSStorageError(*)(wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args); auto res = ((WUPSStorageError(*)(wups_loader_init_storage_args_t_))((uint32_t *) func_ptr))(args);
// clang-format on // clang-format on
if (res != WUPS_STORAGE_ERROR_SUCCESS) { if (res != WUPS_STORAGE_ERROR_SUCCESS) {
// TODO: More error handling? Notification? // TODO: More error handling? Notification?
DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_STORAGE failed for plugin %s: %s", plugin.getMetaInformation().getName().c_str(), WUPS_GetStorageStatusStr(res)); DEBUG_FUNCTION_LINE_ERR("WUPS_LOADER_HOOK_INIT_STORAGE failed for plugin %s: %s", plugin.getMetaInformation().getName().c_str(), WUPSStorageAPI_GetStatusStr(res));
} }
break; break;
} }

View File

@ -135,6 +135,14 @@ WUMS_APPLICATION_STARTS() {
DEBUG_FUNCTION_LINE("Restore function patches of currently loaded plugins."); DEBUG_FUNCTION_LINE("Restore function patches of currently loaded plugins.");
PluginManagement::RestoreFunctionPatches(gLoadedPlugins); PluginManagement::RestoreFunctionPatches(gLoadedPlugins);
for (auto &plugin : gLoadedPlugins) {
WUPSStorageError err = plugin->CloseStorage();
if (err != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close storage for plugin: %s", plugin->getMetaInformation().getName().c_str());
}
}
DEBUG_FUNCTION_LINE("Unload existing plugins."); DEBUG_FUNCTION_LINE("Unload existing plugins.");
gLoadedPlugins.clear(); gLoadedPlugins.clear();
for (auto &cur : gTrampData) { for (auto &cur : gTrampData) {
@ -170,6 +178,12 @@ WUMS_APPLICATION_STARTS() {
} }
if (initNeeded) { if (initNeeded) {
for (auto &plugin : gLoadedPlugins) {
WUPSStorageError err = plugin->OpenStorage();
if (err != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to open storage for plugin: %s. (%s)", plugin->getMetaInformation().getName().c_str(), WUPSStorageAPI_GetStatusStr(err));
}
}
PluginManagement::callInitHooks(gLoadedPlugins); PluginManagement::callInitHooks(gLoadedPlugins);
} }

View File

@ -21,6 +21,7 @@
#include "PluginData.h" #include "PluginData.h"
#include "PluginInformation.h" #include "PluginInformation.h"
#include "PluginMetaInformation.h" #include "PluginMetaInformation.h"
#include "utils/storage/StorageUtils.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <wups/config_api.h> #include <wups/config_api.h>
@ -45,7 +46,7 @@ public:
return mPluginData; return mPluginData;
} }
uint32_t getHandle() const { [[nodiscard]] uint32_t getHandle() const {
return (uint32_t) this; return (uint32_t) this;
} }
@ -57,9 +58,40 @@ public:
mPluginConfigData = pluginConfigData; mPluginConfigData = pluginConfigData;
} }
WUPSStorageError OpenStorage() {
if (getMetaInformation().getWUPSVersion() < WUPSVersion(0, 8, 0)) {
return WUPS_STORAGE_ERROR_SUCCESS;
}
auto &storageId = getMetaInformation().getStorageId();
if (storageId.empty()) {
return WUPS_STORAGE_ERROR_SUCCESS;
}
auto res = StorageUtils::API::Internal::OpenStorage(storageId, storageRootItem);
if (res != WUPS_STORAGE_ERROR_SUCCESS) {
storageRootItem = nullptr;
}
return res;
}
WUPSStorageError CloseStorage() {
if (getMetaInformation().getWUPSVersion() < WUPSVersion(0, 8, 0)) {
return WUPS_STORAGE_ERROR_SUCCESS;
}
if (storageRootItem == nullptr) {
return WUPS_STORAGE_ERROR_SUCCESS;
}
return StorageUtils::API::Internal::CloseStorage(storageRootItem);
}
[[nodiscard]] wups_storage_root_item getStorageRootItem() const {
return storageRootItem;
}
private: private:
const std::unique_ptr<PluginMetaInformation> mMetaInformation; const std::unique_ptr<PluginMetaInformation> mMetaInformation;
const std::unique_ptr<PluginInformation> mPluginInformation; const std::unique_ptr<PluginInformation> mPluginInformation;
const std::shared_ptr<PluginData> mPluginData; const std::shared_ptr<PluginData> mPluginData;
std::optional<PluginConfigData> mPluginConfigData; std::optional<PluginConfigData> mPluginConfigData;
wups_storage_root_item storageRootItem = nullptr;
}; };

View File

@ -83,7 +83,7 @@ bool StorageItem::getValue(int32_t &result) const {
return false; return false;
} }
bool StorageItem::getValue(std::vector<uint8_t> &result) { bool StorageItem::getValue(std::vector<uint8_t> &result) const {
if (mType == StorageItemType::Binary) { if (mType == StorageItemType::Binary) {
result = std::get<std::vector<uint8_t>>(mData); result = std::get<std::vector<uint8_t>>(mData);
return true; return true;
@ -174,7 +174,7 @@ bool StorageItem::attemptBinaryConversion() {
} }
free(dec); free(dec);
} else { } else {
DEBUG_FUNCTION_LINE_ERR("Malloc failed for string->binary parsing"); DEBUG_FUNCTION_LINE_WARN("Malloc failed for string->binary parsing");
return false; return false;
} }
} }

View File

@ -19,7 +19,7 @@ enum class StorageItemType { None,
class StorageItem { class StorageItem {
public: public:
explicit StorageItem(std::string key) : mData(std::monostate{}), mType(StorageItemType::None), mKey(std::move(key)) { explicit StorageItem(std::string_view key) : mData(std::monostate{}), mType(StorageItemType::None), mKey(key) {
} }
[[nodiscard]] uint32_t getHandle() const { [[nodiscard]] uint32_t getHandle() const {
@ -63,7 +63,7 @@ public:
bool getValue(std::string &result) const; bool getValue(std::string &result) const;
bool getValue(std::vector<uint8_t> &result); bool getValue(std::vector<uint8_t> &result) const;
[[nodiscard]] StorageItemType getType() const { [[nodiscard]] StorageItemType getType() const {
return mType; return mType;

View File

@ -12,13 +12,18 @@
class StorageItemRoot : public StorageSubItem { class StorageItemRoot : public StorageSubItem {
public: public:
explicit StorageItemRoot(const std::string &plugin_name) : StorageSubItem(plugin_name), mPluginName(plugin_name) { explicit StorageItemRoot(std::string_view plugin_name) : StorageSubItem(plugin_name), mPluginName(plugin_name) {
} }
[[nodiscard]] const std::string &getPluginId() const { [[nodiscard]] const std::string &getPluginId() const {
return mPluginName; return mPluginName;
} }
void wipe() {
mSubCategories.clear();
mItems.clear();
}
private: private:
std::string mPluginName; std::string mPluginName;
}; };

View File

@ -1,16 +1,16 @@
#include "StorageSubItem.h" #include "StorageSubItem.h"
StorageSubItem *StorageSubItem::getSubItem(wups_storage_item item) const { StorageSubItem *StorageSubItem::getSubItem(wups_storage_item item) {
// Try to find the sub-item based on item handle. // Try to find the sub-item based on item handle.
for (const auto &cur : mSubCategories) { for (auto &cur : mSubCategories) {
if (cur->getHandle() == (uint32_t) item) { if (cur.getHandle() == (uint32_t) item) {
return cur.get(); return &cur;
} }
} }
// If not found in current category, recursively search in sub-categories. // If not found in current category, recursively search in sub-categories.
for (const auto &cur : mSubCategories) { for (auto &cur : mSubCategories) {
auto res = cur->getSubItem(item); auto res = cur.getSubItem(item);
if (res) { if (res) {
return res; return res;
} }
@ -22,8 +22,8 @@ StorageSubItem *StorageSubItem::getSubItem(wups_storage_item item) const {
const StorageSubItem *StorageSubItem::getSubItem(const char *key) const { const StorageSubItem *StorageSubItem::getSubItem(const char *key) const {
// Try to find the sub-item based on key. // Try to find the sub-item based on key.
for (const auto &cur : mSubCategories) { for (const auto &cur : mSubCategories) {
if (cur->getKey() == key) { if (cur.getKey() == key) {
return cur.get(); return &cur;
} }
} }
@ -31,7 +31,7 @@ const StorageSubItem *StorageSubItem::getSubItem(const char *key) const {
} }
bool StorageSubItem::deleteItem(const char *key) { bool StorageSubItem::deleteItem(const char *key) {
if (remove_first_if(mSubCategories, [key](auto &cur) { return cur->getKey() == key; })) { if (remove_first_if(mSubCategories, [key](auto &cur) { return cur.getKey() == key; })) {
return true; return true;
} }
@ -45,20 +45,17 @@ bool StorageSubItem::deleteItem(const char *key) {
StorageItem *StorageSubItem::createItem(const char *key, StorageSubItem::StorageSubItemError &error) { StorageItem *StorageSubItem::createItem(const char *key, StorageSubItem::StorageSubItemError &error) {
for (const auto &cur : mSubCategories) { for (const auto &cur : mSubCategories) {
if (cur->getKey() == key) { if (cur.getKey() == key) {
error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE; error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE;
return nullptr; return nullptr;
} }
} }
auto res = make_unique_nothrow<StorageItem>(key); auto result = mItems.insert({key, StorageItem(key)});
if (!res) { if (result.second) {
error = STORAGE_SUB_ITEM_ERROR_MALLOC_FAILED; return &result.first->second;
return nullptr;
} }
auto *result = res.get(); return nullptr;
mItems[key] = std::move(res);
return result;
} }
StorageSubItem *StorageSubItem::createSubItem(const char *key, StorageSubItem::StorageSubItemError &error) { StorageSubItem *StorageSubItem::createSubItem(const char *key, StorageSubItem::StorageSubItemError &error) {
@ -68,26 +65,20 @@ StorageSubItem *StorageSubItem::createSubItem(const char *key, StorageSubItem::S
return nullptr; return nullptr;
} }
for (const auto &cur : mSubCategories) { for (const auto &cur : mSubCategories) {
if (cur->getKey() == key) { if (cur.getKey() == key) {
error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE; error = STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE;
return nullptr; return nullptr;
} }
} }
auto res = make_unique_nothrow<StorageSubItem>(key); mSubCategories.emplace_front(key);
if (!res) { return &mSubCategories.front();
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) { StorageItem *StorageSubItem::getItem(const char *name) {
auto resItr = mItems.find(name); auto resItr = mItems.find(name);
if (resItr != mItems.end()) { if (resItr != mItems.end()) {
return resItr->second.get(); return &resItr->second;
} }
return nullptr; return nullptr;
} }

View File

@ -17,10 +17,10 @@ public:
STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE = 2, STORAGE_SUB_ITEM_KEY_ALREADY_IN_USE = 2,
}; };
explicit StorageSubItem(const std::string &key) : StorageItem(key) { explicit StorageSubItem(std::string_view key) : StorageItem(key) {
} }
StorageSubItem *getSubItem(wups_storage_item item) const; StorageSubItem *getSubItem(wups_storage_item item);
const StorageSubItem *getSubItem(const char *key) const; const StorageSubItem *getSubItem(const char *key) const;
@ -32,15 +32,15 @@ public:
StorageItem *getItem(const char *name); StorageItem *getItem(const char *name);
[[nodiscard]] const std::forward_list<std::unique_ptr<StorageSubItem>> &getSubItems() const { [[nodiscard]] const std::forward_list<StorageSubItem> &getSubItems() const {
return mSubCategories; return mSubCategories;
} }
[[nodiscard]] const std::map<std::string, std::unique_ptr<StorageItem>> &getItems() const { [[nodiscard]] const std::map<std::string, StorageItem> &getItems() const {
return mItems; return mItems;
} }
private: protected:
std::forward_list<std::unique_ptr<StorageSubItem>> mSubCategories; std::forward_list<StorageSubItem> mSubCategories;
std::map<std::string, std::unique_ptr<StorageItem>> mItems; std::map<std::string, StorageItem> mItems;
}; };

View File

@ -43,7 +43,6 @@ namespace StorageUtils {
} }
} }
} else { } else {
auto res = item.createItem(it.key().c_str(), subItemError); auto res = item.createItem(it.key().c_str(), subItemError);
if (!res) { if (!res) {
@ -73,7 +72,7 @@ namespace StorageUtils {
return true; return true;
} }
static std::unique_ptr<StorageItemRoot> deserializeFromJson(const nlohmann::json &json, const std::string &key) { static std::unique_ptr<StorageItemRoot> deserializeFromJson(const nlohmann::json &json, std::string_view key) {
if (json.empty() || !json.is_object()) { if (json.empty() || !json.is_object()) {
return nullptr; return nullptr;
} }
@ -90,49 +89,49 @@ namespace StorageUtils {
nlohmann::json json = nlohmann::json::object(); nlohmann::json json = nlohmann::json::object();
for (const auto &curSubItem : baseItem.getSubItems()) { for (const auto &curSubItem : baseItem.getSubItems()) {
json[curSubItem->getKey()] = serializeToJson(*curSubItem); json[curSubItem.getKey()] = serializeToJson(curSubItem);
} }
for (const auto &[key, value] : baseItem.getItems()) { for (const auto &[key, value] : baseItem.getItems()) {
switch ((StorageItemType) value->getType()) { switch ((StorageItemType) value.getType()) {
case StorageItemType::String: { case StorageItemType::String: {
std::string res; std::string res;
if (value->getValue(res)) { if (value.getValue(res)) {
json[key] = res; json[key] = res;
} }
break; break;
} }
case StorageItemType::Boolean: { case StorageItemType::Boolean: {
bool res; bool res;
if (value->getValue(res)) { if (value.getValue(res)) {
json[key] = res; json[key] = res;
} }
break; break;
} }
case StorageItemType::S64: { case StorageItemType::S64: {
int64_t res; int64_t res;
if (value->getValue(res)) { if (value.getValue(res)) {
json[key] = res; json[key] = res;
} }
break; break;
} }
case StorageItemType::U64: { case StorageItemType::U64: {
uint64_t res; uint64_t res;
if (value->getValue(res)) { if (value.getValue(res)) {
json[key] = res; json[key] = res;
} }
break; break;
} }
case StorageItemType::Double: { case StorageItemType::Double: {
double res; double res;
if (value->getValue(res)) { if (value.getValue(res)) {
json[key] = res; json[key] = res;
} }
break; break;
} }
case StorageItemType::Binary: { case StorageItemType::Binary: {
std::vector<uint8_t> tmp; std::vector<uint8_t> tmp;
if (value->getValue(tmp)) { if (value.getValue(tmp)) {
auto *enc = b64_encode(tmp.data(), tmp.size()); auto *enc = b64_encode(tmp.data(), tmp.size());
if (enc) { if (enc) {
json[key] = enc; json[key] = enc;
@ -152,7 +151,7 @@ namespace StorageUtils {
return json; return json;
} }
static StorageSubItem *getRootItem(wups_storage_root_item root) { static StorageItemRoot *getRootItem(wups_storage_root_item root) {
for (const auto &cur : gStorage) { for (const auto &cur : gStorage) {
if (cur->getHandle() == (uint32_t) root) { if (cur->getHandle() == (uint32_t) root) {
return cur.get(); return cur.get();
@ -173,40 +172,100 @@ namespace StorageUtils {
return nullptr; return nullptr;
} }
WUPSStorageError LoadFromFile(std::string_view plugin_id, nlohmann::json &outJson) {
std::string filePath = getPluginPath() + "/config/" + plugin_id.data() + ".json";
CFile file(filePath, CFile::ReadOnly);
if (!file.isOpen() || file.size() == 0) {
return WUPS_STORAGE_ERROR_IO_ERROR;
}
auto *json_data = (uint8_t *) memalign(0x40, ROUNDUP(file.size() + 1, 0x40));
if (!json_data) {
return WUPS_STORAGE_ERROR_MALLOC_FAILED;
}
WUPSStorageError result = WUPS_STORAGE_ERROR_SUCCESS;
uint64_t readRes = file.read(json_data, file.size());
if (readRes == file.size()) {
json_data[file.size()] = '\0';
outJson = nlohmann::json::parse(json_data, nullptr, false);
} else {
result = WUPS_STORAGE_ERROR_IO_ERROR;
}
file.close();
free(json_data);
return result;
}
static WUPSStorageError WriteStorageToSD(const char *plugin_id) { WUPSStorageError LoadFromFile(std::string_view plugin_id, StorageItemRoot &rootItem) {
nlohmann::json j;
WUPSStorageError err;
if ((err = LoadFromFile(plugin_id, j)) != WUPS_STORAGE_ERROR_SUCCESS) {
return err;
}
std::unique_ptr<StorageItemRoot> storage;
if (j.empty() || !j.is_object() || !j.contains("storageitems") || j["storageitems"].empty() || !j["storageitems"].is_object()) {
storage = make_unique_nothrow<StorageItemRoot>(plugin_id);
} else {
storage = StorageUtils::Helper::deserializeFromJson(j["storageitems"], plugin_id);
if (!storage) {
storage = make_unique_nothrow<StorageItemRoot>(plugin_id);
}
}
rootItem = *storage;
return WUPS_STORAGE_ERROR_SUCCESS;
}
static WUPSStorageError WriteStorageToSD(wups_storage_root_item root, bool forceSave) {
std::shared_ptr<StorageItemRoot> rootItem; std::shared_ptr<StorageItemRoot> rootItem;
for (const auto &cur : gStorage) { for (const auto &cur : gStorage) {
if (cur->getPluginId() == plugin_id) { if (cur->getHandle() == (uint32_t) root) {
rootItem = cur; rootItem = cur;
break; break;
} }
} }
if (!rootItem) { if (!rootItem) {
return WUPS_STORAGE_ERROR_NOT_FOUND; return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
} }
std::string folderPath = getPluginPath() + "/config/"; std::string folderPath = getPluginPath() + "/config/";
std::string filePath = folderPath + rootItem->getPluginId() + ".json"; std::string filePath = folderPath + rootItem->getPluginId() + ".json";
FSUtils::CreateSubfolder(folderPath); nlohmann::json j;
j["storageitems"] = serializeToJson(*rootItem);
if (!forceSave) {
nlohmann::json jsonFromFile;
if (Helper::LoadFromFile(rootItem->getPluginId(), jsonFromFile) == WUPS_STORAGE_ERROR_SUCCESS) {
if (j == jsonFromFile) {
DEBUG_FUNCTION_LINE_VERBOSE("Storage has no changes, avoid saving \"%s.json\"", rootItem->getPluginId().c_str());
return WUPS_STORAGE_ERROR_SUCCESS;
}
} else {
DEBUG_FUNCTION_LINE_WARN("Failed to load \"%s.json\"", rootItem->getPluginId().c_str());
}
DEBUG_FUNCTION_LINE_VERBOSE("Saving \"%s.json\"...", rootItem->getPluginId().c_str());
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Force saving \"%s.json\"...", rootItem->getPluginId().c_str());
}
if (!FSUtils::CreateSubfolder(folderPath)) {
return WUPS_STORAGE_ERROR_IO_ERROR;
}
CFile file(filePath, CFile::WriteOnly); CFile file(filePath, CFile::WriteOnly);
if (!file.isOpen()) { if (!file.isOpen()) {
DEBUG_FUNCTION_LINE_ERR("Cannot create file %s", filePath.c_str()); DEBUG_FUNCTION_LINE_ERR("Cannot create file %s", filePath.c_str());
return WUPS_STORAGE_ERROR_SUCCESS; return WUPS_STORAGE_ERROR_IO_ERROR;
} }
nlohmann::json j;
j["storageitems"] = serializeToJson(*rootItem);
std::string jsonString = j.dump(4, ' ', false, nlohmann::json::error_handler_t::ignore); 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()); auto writeResult = file.write((const uint8_t *) jsonString.c_str(), jsonString.size());
file.close(); file.close();
if (writeResult != (int32_t) jsonString.size()) { if (writeResult != (int32_t) jsonString.size()) {
return WUPS_STORAGE_ERROR_SUCCESS; return WUPS_STORAGE_ERROR_IO_ERROR;
} }
return WUPS_STORAGE_ERROR_SUCCESS; return WUPS_STORAGE_ERROR_SUCCESS;
} }
@ -252,14 +311,13 @@ namespace StorageUtils {
if (item->getValue(result)) { if (item->getValue(result)) {
return WUPS_STORAGE_ERROR_SUCCESS; return WUPS_STORAGE_ERROR_SUCCESS;
} }
DEBUG_FUNCTION_LINE_ERR("GetValue failed? %s", key);
return WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE; return WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE;
} }
return WUPS_STORAGE_ERROR_NOT_FOUND; return WUPS_STORAGE_ERROR_NOT_FOUND;
} }
template<typename T> template<typename T>
WUPSStorageError GetItemGeneric(wups_storage_root_item root, wups_storage_item parent, const char *key, T *result) { WUPSStorageError GetItemGeneric(wups_storage_root_item root, wups_storage_item parent, const char *key, T *result, uint32_t *outSize) {
if (!result) { if (!result) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
@ -267,6 +325,9 @@ namespace StorageUtils {
auto res = GetItemEx<T>(root, parent, key, tmp); auto res = GetItemEx<T>(root, parent, key, tmp);
if (res == WUPS_STORAGE_ERROR_SUCCESS) { if (res == WUPS_STORAGE_ERROR_SUCCESS) {
*result = tmp; *result = tmp;
if (outSize) {
*outSize = sizeof(T);
}
} }
return res; return res;
} }
@ -330,78 +391,72 @@ namespace StorageUtils {
} // namespace Helper } // namespace Helper
namespace API { namespace API {
WUPSStorageError OpenStorage(const char *plugin_id, wups_storage_root_item *item) { namespace Internal {
WUPSStorageError OpenStorage(std::string_view plugin_id, wups_storage_root_item &outItem) {
std::unique_ptr<StorageItemRoot> root = make_unique_nothrow<StorageItemRoot>(plugin_id);
if (!root) {
return WUPS_STORAGE_ERROR_MALLOC_FAILED;
}
WUPSStorageError err;
if ((err = Helper::LoadFromFile(plugin_id, *root)) != WUPS_STORAGE_ERROR_SUCCESS) {
return err;
}
outItem = (wups_storage_root_item) root->getHandle();
{
std::lock_guard lock(gStorageMutex);
gStorage.push_front(std::move(root));
}
return WUPS_STORAGE_ERROR_SUCCESS;
}
WUPSStorageError CloseStorage(wups_storage_root_item root) {
std::lock_guard lock(gStorageMutex);
auto res = StorageUtils::Helper::WriteStorageToSD(root, false);
// TODO: handle write error?
if (!remove_locked_first_if(gStorageMutex, gStorage, [root](auto &cur) { return cur->getHandle() == (uint32_t) root; })) {
DEBUG_FUNCTION_LINE_WARN("Failed to close storage: Not opened (\"%08X\")", root);
return WUPS_STORAGE_ERROR_NOT_FOUND;
}
return res;
}
} // namespace Internal
WUPSStorageError SaveStorage(wups_storage_root_item root, bool force) {
std::lock_guard lock(gStorageMutex); std::lock_guard lock(gStorageMutex);
// Check if we already have a storage with the given plugin id return StorageUtils::Helper::WriteStorageToSD(root, force);
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<StorageItemRoot> storage;
if (j.empty() || !j.is_object() || !j.contains("storageitems") || j["storageitems"].empty() || !j["storageitems"].is_object()) {
storage = make_unique_nothrow<StorageItemRoot>(plugin_id);
} else {
storage = StorageUtils::Helper::deserializeFromJson(j["storageitems"], plugin_id);
if (!storage) {
storage = make_unique_nothrow<StorageItemRoot>(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) { WUPSStorageError ForceReloadStorage(wups_storage_root_item root) {
std::lock_guard lock(gStorageMutex); std::lock_guard lock(gStorageMutex);
auto res = StorageUtils::Helper::WriteStorageToSD(plugin_id); auto rootItem = Helper::getRootItem(root);
// TODO: handle write error? if (!rootItem) {
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
}
if (!remove_locked_first_if(gStorageMutex, gStorage, [plugin_id](auto &cur) { return cur->getPluginId() == plugin_id; })) { WUPSStorageError result;
DEBUG_FUNCTION_LINE_WARN("Failed to close storage: Not opened (\"%s\")", plugin_id); if ((result = Helper::LoadFromFile(rootItem->getPluginId(), *rootItem)) != WUPS_STORAGE_ERROR_SUCCESS) {
return result;
}
return result;
}
WUPSStorageError WipeStorage(wups_storage_root_item root) {
std::lock_guard lock(gStorageMutex);
auto rootItem = Helper::getRootItem(root);
if (!rootItem) {
return WUPS_STORAGE_ERROR_NOT_FOUND; return WUPS_STORAGE_ERROR_NOT_FOUND;
} }
return res;
rootItem->wipe();
return WUPS_STORAGE_ERROR_SUCCESS;
} }
WUPSStorageError CreateSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem) { WUPSStorageError CreateSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem) {
@ -554,6 +609,9 @@ namespace StorageUtils {
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 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); std::lock_guard lock(gStorageMutex);
if (outSize) {
*outSize = 0;
}
switch ((WUPSStorageItemTypes) itemType) { switch ((WUPSStorageItemTypes) itemType) {
case WUPS_STORAGE_ITEM_STRING: { case WUPS_STORAGE_ITEM_STRING: {
if (!data || maxSize == 0) { if (!data || maxSize == 0) {
@ -571,43 +629,43 @@ namespace StorageUtils {
if (!data || maxSize != sizeof(bool)) { if (!data || maxSize != sizeof(bool)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<bool>(root, parent, key, (bool *) data); return StorageUtils::Helper::GetItemGeneric<bool>(root, parent, key, (bool *) data, outSize);
} }
case WUPS_STORAGE_ITEM_S32: { case WUPS_STORAGE_ITEM_S32: {
if (!data || maxSize != sizeof(int32_t)) { if (!data || maxSize != sizeof(int32_t)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<int32_t>(root, parent, key, (int32_t *) data); return StorageUtils::Helper::GetItemGeneric<int32_t>(root, parent, key, (int32_t *) data, outSize);
} }
case WUPS_STORAGE_ITEM_S64: { case WUPS_STORAGE_ITEM_S64: {
if (!data || maxSize != sizeof(int64_t)) { if (!data || maxSize != sizeof(int64_t)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<int64_t>(root, parent, key, (int64_t *) data); return StorageUtils::Helper::GetItemGeneric<int64_t>(root, parent, key, (int64_t *) data, outSize);
} }
case WUPS_STORAGE_ITEM_U32: { case WUPS_STORAGE_ITEM_U32: {
if (!data || maxSize != sizeof(uint32_t)) { if (!data || maxSize != sizeof(uint32_t)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<uint32_t>(root, parent, key, (uint32_t *) data); return StorageUtils::Helper::GetItemGeneric<uint32_t>(root, parent, key, (uint32_t *) data, outSize);
} }
case WUPS_STORAGE_ITEM_U64: { case WUPS_STORAGE_ITEM_U64: {
if (!data || maxSize != sizeof(uint64_t)) { if (!data || maxSize != sizeof(uint64_t)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<uint64_t>(root, parent, key, (uint64_t *) data); return StorageUtils::Helper::GetItemGeneric<uint64_t>(root, parent, key, (uint64_t *) data, outSize);
} }
case WUPS_STORAGE_ITEM_FLOAT: { case WUPS_STORAGE_ITEM_FLOAT: {
if (!data || maxSize != sizeof(float)) { if (!data || maxSize != sizeof(float)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<float>(root, parent, key, (float *) data); return StorageUtils::Helper::GetItemGeneric<float>(root, parent, key, (float *) data, outSize);
} }
case WUPS_STORAGE_ITEM_DOUBLE: { case WUPS_STORAGE_ITEM_DOUBLE: {
if (!data || maxSize != sizeof(double)) { if (!data || maxSize != sizeof(double)) {
return WUPS_STORAGE_ERROR_INVALID_ARGS; return WUPS_STORAGE_ERROR_INVALID_ARGS;
} }
return StorageUtils::Helper::GetItemGeneric<double>(root, parent, key, (double *) data); return StorageUtils::Helper::GetItemGeneric<double>(root, parent, key, (double *) data, outSize);
} }
} }
return WUPS_STORAGE_ERROR_NOT_FOUND; return WUPS_STORAGE_ERROR_NOT_FOUND;

View File

@ -3,8 +3,15 @@
#include <wups/storage.h> #include <wups/storage.h>
namespace StorageUtils::API { namespace StorageUtils::API {
WUPSStorageError OpenStorage(const char *plugin_id, wups_storage_root_item *item); namespace Internal {
WUPSStorageError CloseStorage(const char *plugin_id); WUPSStorageError OpenStorage(std::string_view plugin_id, wups_storage_root_item &outItem);
WUPSStorageError CloseStorage(wups_storage_root_item item);
} // namespace Internal
WUPSStorageError SaveStorage(wups_storage_root_item root, bool force);
WUPSStorageError ForceReloadStorage(wups_storage_root_item root);
WUPSStorageError WipeStorage(wups_storage_root_item root);
WUPSStorageError DeleteItem(wups_storage_root_item root, wups_storage_item parent, const char *key); 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 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 GetSubItem(wups_storage_root_item root, wups_storage_item parent, const char *key, wups_storage_item *outItem);

View File

@ -5,7 +5,11 @@
#include <coreinit/ios.h> #include <coreinit/ios.h>
#include <string> #include <string>
static std::string sPluginPath;
std::string getPluginPath() { std::string getPluginPath() {
if (!sPluginPath.empty()) {
return sPluginPath;
}
char environmentPath[0x100]; char environmentPath[0x100];
memset(environmentPath, 0, sizeof(environmentPath)); memset(environmentPath, 0, sizeof(environmentPath));
@ -18,7 +22,8 @@ std::string getPluginPath() {
IOS_Close(handle); IOS_Close(handle);
} }
return std::string(environmentPath).append("/plugins"); sPluginPath = std::string(environmentPath).append("/plugins");
return sPluginPath;
} }
// https://gist.github.com/ccbrown/9722406 // https://gist.github.com/ccbrown/9722406