From 5f7ef909f88094e11dee888b5aab0dc3934cf945 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Mon, 7 Sep 2026 13:42:25 +0300 Subject: [PATCH] feat(libs): libcSceSaveData impl (wip) --- .../libs/prx/libSceSaveData.native/Export.cpp | 325 +++++++++++++----- .../prx/libSceSaveData.native/SaveData.hpp | 63 ++++ core/libs/prx/libSceSaveData/Export.cpp | 64 ---- 3 files changed, 301 insertions(+), 151 deletions(-) create mode 100644 core/libs/prx/libSceSaveData.native/SaveData.hpp diff --git a/core/libs/prx/libSceSaveData.native/Export.cpp b/core/libs/prx/libSceSaveData.native/Export.cpp index 266a1c88b..030d739ac 100644 --- a/core/libs/prx/libSceSaveData.native/Export.cpp +++ b/core/libs/prx/libSceSaveData.native/Export.cpp @@ -1,163 +1,314 @@ -#include +#include #include +#include +#include +#include +#include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "SaveData.hpp" + +static constexpr char SAVE_DIR[] = "_SaveData"; + +static std::atomic g_transaction_counter{1}; +static bool g_initialized = false; + +static std::string save_root() { + return std::string(SAVE_DIR); +} + +static std::string slot_mount_point(int slot) { + return std::string("/savedata") + std::to_string(slot); +} + +static bool dir_name_match(const char* str, const char* pattern) { + if (pattern == nullptr || pattern[0] == '\0') { + return true; + } + while (*str != '\0' && *pattern != '\0') { + if (*pattern == '%') { + for (const char* s = str;; s++) { + if (dir_name_match(s, pattern + 1)) { + return true; + } + if (*s == '\0') { + break; + } + } + return false; + } + if (*pattern == '_') { + str++; + pattern++; + continue; + } + if (*pattern != *str) { + return false; + } + str++; + pattern++; + } + return *str == '\0' && *pattern == '\0'; +} extern "C" { int sceSaveDataBackup(const SaveDataBackup* backup) { - (void)backup; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)backup; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataCommit(const SaveDataCommitParam* param) { - (void)param; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)param; + return SAVE_DATA_OK; } int sceSaveDataCreateTransactionResource(uint32_t size) { - (void)size; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)size; + return g_transaction_counter.fetch_add(1); } int sceSaveDataDelete(const SaveDataDelete* del) { - (void)del; - NotImplemented_nid_no_patch(__func__); - return 0; + if (del == nullptr || del->dir_name == nullptr) { + throw std::runtime_error("sceSaveDataDelete: null argument"); + } + const std::string path = save_root() + "/" + std::string(del->dir_name->data); + if (std::filesystem::is_directory(path)) { + std::filesystem::remove_all(path); + } + return SAVE_DATA_OK; } int sceSaveDataDeleteTransactionResource(int32_t resource) { - (void)resource; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)resource; + return SAVE_DATA_OK; } int sceSaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond, SaveDataDirNameSearchResult* result) { - (void)cond; - (void)result; - NotImplemented_nid_no_patch(__func__); - return 0; + if (cond == nullptr || result == nullptr) { + throw std::runtime_error("sceSaveDataDirNameSearch: null argument"); + } + result->hit_num = 0; + result->set_num = 0; + const char* pattern = (cond->dir_name != nullptr) ? cond->dir_name->data : nullptr; + const std::string root = save_root(); + if (!std::filesystem::is_directory(root)) { + return SAVE_DATA_OK; + } + std::uint32_t hit = 0; + std::uint32_t set = 0; + for (const auto& entry : std::filesystem::directory_iterator(root)) { + if (!entry.is_directory()) { + continue; + } + const std::string name = entry.path().filename().string(); + if (!dir_name_match(name.c_str(), pattern)) { + continue; + } + hit++; + if (result->dir_names != nullptr && set < result->dir_names_num) { + std::snprintf(result->dir_names[set].data, sizeof(result->dir_names[set].data), "%s", name.c_str()); + if (result->params != nullptr) { + std::memset(&result->params[set], 0, sizeof(SaveDataParam)); + } + set++; + } + } + result->hit_num = hit; + result->set_num = set; + return SAVE_DATA_OK; } int sceSaveDataGetEventResult(const void* event_param, SaveDataEvent* event) { - (void)event_param; - (void)event; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)event_param; + (void)event; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataGetMountInfo(const SaveDataMountPoint* mount_point, SaveDataMountInfo* info) { - (void)mount_point; - (void)info; - NotImplemented_nid_no_patch(__func__); - return 0; + if (mount_point == nullptr || info == nullptr) { + throw std::runtime_error("sceSaveDataGetMountInfo: null argument"); + } + if (find_slot_by_mount_point(mount_point->data) == -1) { + return SAVE_DATA_ERROR_NOT_MOUNTED; + } + std::memset(info, 0, sizeof(*info)); + info->blocks = SAVE_DATA_BLOCKS_MAX; + info->free_blocks = SAVE_DATA_BLOCKS_MAX; + return SAVE_DATA_OK; } int sceSaveDataGetParam(const SaveDataMountPoint* mount_point, uint32_t param_type, void* param_buf, size_t param_buf_size, size_t* got_size) { - (void)mount_point; - (void)param_type; - (void)param_buf; - (void)param_buf_size; - (void)got_size; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)param_type; + if (mount_point == nullptr || param_buf == nullptr) { + throw std::runtime_error("sceSaveDataGetParam: null argument"); + } + if (find_slot_by_mount_point(mount_point->data) == -1) { + return SAVE_DATA_ERROR_NOT_MOUNTED; + } + std::memset(param_buf, 0, param_buf_size); + if (got_size != nullptr) { + *got_size = param_buf_size; + } + return SAVE_DATA_OK; } int sceSaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) { - (void)get_param; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)get_param; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataInitialize3(const void* init) { - (void)init; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)init; + NotImplemented_nid_no_patch(__func__); + if (g_initialized) { + return SAVE_DATA_ERROR_ALREADY_INITIALIZED; + } + g_initialized = true; + return SAVE_DATA_OK; } int sceSaveDataLoadIcon(const SaveDataMountPoint* mount_point, SaveDataIcon* icon) { - (void)mount_point; - (void)icon; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)icon; + if (mount_point == nullptr) { + throw std::runtime_error("sceSaveDataLoadIcon: null mount_point"); + } + if (find_slot_by_mount_point(mount_point->data) == -1) { + return SAVE_DATA_ERROR_NOT_MOUNTED; + } + if (icon != nullptr) { + icon->data_size = 0; + } + return SAVE_DATA_OK; } int sceSaveDataMount3(const SaveDataMount3* mount, SaveDataMountResult* mount_result) { - (void)mount; - (void)mount_result; - NotImplemented_nid_no_patch(__func__); - return 0; + if (mount == nullptr || mount_result == nullptr || mount->dir_name == nullptr) { + throw std::runtime_error("sceSaveDataMount3: null argument"); + } + std::memset(mount_result, 0, sizeof(*mount_result)); + const bool create = (mount->mount_mode & SAVE_DATA_MOUNT_MODE_CREATE) != 0; + const bool create2 = (mount->mount_mode & SAVE_DATA_MOUNT_MODE_CREATE2) != 0; + const bool rdonly = (mount->mount_mode & SAVE_DATA_MOUNT_MODE_RDONLY) != 0; + const bool rdwr = (mount->mount_mode & SAVE_DATA_MOUNT_MODE_RDWR) != 0; + const bool open = !create && !create2 && (rdonly || rdwr); + if (!create && !create2 && !open) { + throw std::runtime_error("sceSaveDataMount3: unknown mount_mode"); + } + const std::string dir_name = mount->dir_name->data; + const std::string real_path = save_root() + "/" + dir_name; + const bool exists = std::filesystem::is_directory(real_path); + if (create && exists) { + return SAVE_DATA_ERROR_EXISTS; + } + if (open && !exists) { + return SAVE_DATA_ERROR_NOT_FOUND; + } + int slot = find_free_slot(); + if (slot == -1) { + return SAVE_DATA_ERROR_MOUNT_FULL; + } + if (create || create2) { + std::filesystem::create_directories(real_path); + } + const std::string mp = slot_mount_point(slot); + g_slots[slot].used = true; + g_slots[slot].mount_point = mp; + g_slots[slot].real_path = real_path; + std::snprintf(mount_result->mount_point.data, sizeof(mount_result->mount_point.data), "%s", mp.c_str()); + mount_result->required_blocks = 0; + mount_result->mount_status = (create || create2) ? 1u : 0u; + return SAVE_DATA_OK; } int sceSaveDataPrepare(const SaveDataMountPoint* mount_point, const SaveDataPrepareParam* param) { - (void)mount_point; - (void)param; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)mount_point; + (void)param; + return SAVE_DATA_OK; } int sceSaveDataSaveIcon(const SaveDataMountPoint* mount_point, const SaveDataIcon* icon) { - (void)mount_point; - (void)icon; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)icon; + if (mount_point == nullptr) { + throw std::runtime_error("sceSaveDataSaveIcon: null mount_point"); + } + if (find_slot_by_mount_point(mount_point->data) == -1) { + return SAVE_DATA_ERROR_NOT_MOUNTED; + } + return SAVE_DATA_OK; } int sceSaveDataSaveIconByPath(const SaveDataMountPoint* mount_point, const char* path) { - (void)mount_point; - (void)path; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)mount_point; + (void)path; + return SAVE_DATA_OK; } int sceSaveDataSetParam(const SaveDataMountPoint* mount_point, uint32_t param_type, const void* param_buf, size_t param_buf_size) { - (void)mount_point; - (void)param_type; - (void)param_buf; - (void)param_buf_size; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)param_type; + (void)param_buf; + (void)param_buf_size; + if (mount_point == nullptr) { + throw std::runtime_error("sceSaveDataSetParam: null mount_point"); + } + if (find_slot_by_mount_point(mount_point->data) == -1) { + return SAVE_DATA_ERROR_NOT_MOUNTED; + } + return SAVE_DATA_OK; } int sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2* set_param) { - (void)set_param; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)set_param; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup_param, SaveDataMemorySetupResult* result) { - (void)setup_param; - (void)result; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)setup_param; + (void)result; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataSyncSaveDataMemory(const void* sync_param) { - (void)sync_param; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)sync_param; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataTerminate(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (!g_initialized) { + return SAVE_DATA_ERROR_NOT_INITIALIZED; + } + if (any_slot_used()) { + return SAVE_DATA_ERROR_BUSY; + } + g_initialized = false; + return SAVE_DATA_OK; } int sceSaveDataTransferringMount(const SaveDataTransferringMount* mount, SaveDataMountResult* mount_result) { - (void)mount; - (void)mount_result; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)mount; + (void)mount_result; + NotImplemented_nid_no_patch(__func__); + return 0; } int sceSaveDataUmount2(uint32_t mode, const SaveDataMountPoint* mount_point) { - (void)mode; - (void)mount_point; - NotImplemented_nid_no_patch(__func__); - return 0; + (void)mode; + if (mount_point == nullptr) { + throw std::runtime_error("sceSaveDataUmount2: null mount_point"); + } + int slot = find_slot_by_mount_point(mount_point->data); + if (slot == -1) { + return SAVE_DATA_ERROR_NOT_MOUNTED; + } + g_slots[slot] = MountSlot{}; + return SAVE_DATA_OK; } } diff --git a/core/libs/prx/libSceSaveData.native/SaveData.hpp b/core/libs/prx/libSceSaveData.native/SaveData.hpp new file mode 100644 index 000000000..dd69aca6c --- /dev/null +++ b/core/libs/prx/libSceSaveData.native/SaveData.hpp @@ -0,0 +1,63 @@ +#ifndef CORE_LIBS_PRX_LIBSCESAVEDATANATIVE_SAVEDATA_HPP +#define CORE_LIBS_PRX_LIBSCESAVEDATANATIVE_SAVEDATA_HPP + +#include +#include +#include + +constexpr int SAVE_DATA_OK = 0; +constexpr int SAVE_DATA_ERROR_PARAMETER = -2137063424; +constexpr int SAVE_DATA_ERROR_NOT_INITIALIZED = -2137063423; +constexpr int SAVE_DATA_ERROR_ALREADY_INITIALIZED = -2137063422; +constexpr int SAVE_DATA_ERROR_OUT_OF_MEMORY = -2137063421; +constexpr int SAVE_DATA_ERROR_BUSY = -2137063420; +constexpr int SAVE_DATA_ERROR_NOT_MOUNTED = -2137063419; +constexpr int SAVE_DATA_ERROR_MOUNT_FULL = -2137063418; +constexpr int SAVE_DATA_ERROR_EXISTS = -2137063414; +constexpr int SAVE_DATA_ERROR_NOT_FOUND = -2137063413; + +constexpr std::uint32_t SAVE_DATA_MOUNT_MODE_RDONLY = 1; +constexpr std::uint32_t SAVE_DATA_MOUNT_MODE_RDWR = 2; +constexpr std::uint32_t SAVE_DATA_MOUNT_MODE_CREATE = 4; +constexpr std::uint32_t SAVE_DATA_MOUNT_MODE_CREATE2 = 32; + +constexpr std::uint64_t SAVE_DATA_BLOCKS_MAX = 32768; + +constexpr std::size_t SAVE_DATA_MOUNT_SLOTS = 16; + +struct MountSlot { + bool used = false; + std::string mount_point; + std::string real_path; +}; + +inline std::array g_slots; + +inline int find_slot_by_mount_point(const char* mount_point) { + for (int i = 0; i < static_cast(SAVE_DATA_MOUNT_SLOTS); i++) { + if (g_slots[i].used && g_slots[i].mount_point == mount_point) { + return i; + } + } + return -1; +} + +inline int find_free_slot() { + for (int i = 0; i < static_cast(SAVE_DATA_MOUNT_SLOTS); i++) { + if (!g_slots[i].used) { + return i; + } + } + return -1; +} + +inline bool any_slot_used() { + for (const auto& s : g_slots) { + if (s.used) { + return true; + } + } + return false; +} + +#endif diff --git a/core/libs/prx/libSceSaveData/Export.cpp b/core/libs/prx/libSceSaveData/Export.cpp index 4cd757dd4..208e6ed5d 100644 --- a/core/libs/prx/libSceSaveData/Export.cpp +++ b/core/libs/prx/libSceSaveData/Export.cpp @@ -11,19 +11,6 @@ int sceSaveDataBackup(const SaveDataBackup* backup) { return 0; } -int sceSaveDataDelete(const SaveDataDelete* del) { - (void)del; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int sceSaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond, SaveDataDirNameSearchResult* result) { - (void)cond; - (void)result; - NotImplemented_nid_no_patch(__func__); - return 0; -} - int sceSaveDataGetEventResult(const void* event_param, SaveDataEvent* event) { (void)event_param; (void)event; @@ -31,58 +18,12 @@ int sceSaveDataGetEventResult(const void* event_param, SaveDataEvent* event) { return 0; } -int sceSaveDataGetMountInfo(const SaveDataMountPoint* mount_point, SaveDataMountInfo* info) { - (void)mount_point; - (void)info; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int sceSaveDataGetParam(const SaveDataMountPoint* mount_point, uint32_t param_type, void* param_buf, size_t param_buf_size, size_t* got_size) { - (void)mount_point; - (void)param_type; - (void)param_buf; - (void)param_buf_size; - (void)got_size; - NotImplemented_nid_no_patch(__func__); - return 0; -} - int sceSaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) { (void)get_param; NotImplemented_nid_no_patch(__func__); return 0; } -int sceSaveDataInitialize3(const void* init) { - (void)init; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int sceSaveDataLoadIcon(const SaveDataMountPoint* mount_point, SaveDataIcon* icon) { - (void)mount_point; - (void)icon; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int sceSaveDataSaveIcon(const SaveDataMountPoint* mount_point, const SaveDataIcon* icon) { - (void)mount_point; - (void)icon; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int sceSaveDataSetParam(const SaveDataMountPoint* mount_point, uint32_t param_type, const void* param_buf, size_t param_buf_size) { - (void)mount_point; - (void)param_type; - (void)param_buf; - (void)param_buf_size; - NotImplemented_nid_no_patch(__func__); - return 0; -} - int sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2* set_param) { (void)set_param; NotImplemented_nid_no_patch(__func__); @@ -102,11 +43,6 @@ int sceSaveDataSyncSaveDataMemory(const void* sync_param) { return 0; } -int sceSaveDataTerminate(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int sceSaveDataTransferringMount(const SaveDataTransferringMount* mount, SaveDataMountResult* mount_result) { (void)mount; (void)mount_result;