feat(savedata): mount aliasing, wildcard search, memory slots, async events
Rework the local SaveData implementation into a general-purpose, title-agnostic backend used by both the native library and the standard libSceSaveData path: - Directory aliasing: long save directory names are hashed into short mount point aliases (persisted to disk) so they fit the SDK's fixed-size mount point buffer, with the aliases reversed on directory search. - Wildcard directory-name search (%/_ patterns) with sorting and optional reverse order, matching the DirNameSearch contract. - Save-data "memory" slots: small per-user, per-slot blobs the title can set up, read and write without mounting a directory, backed by files under the save root with a companion parameter blob, size and range validation on every access, and a dirty flag consulted on commit. - Async-flavored operations (backup, commit, memory sync) that queue SaveDataEvent records consumed through GetEventResult and can be bound to kernel event flags, plus transaction-resource and cancel/abort bookkeeping. - Atomic file writes (temp file + rename) so a mid-write crash never leaves a torn save on disk, and corrected/duplicated SDK error code constants. - Local storage app-status get/set/delete and transferring-mount support for PS4-to-PS5 style save transfers. The native library keeps its own lighter-weight mount/memory implementation (short "/_sm/<slot>" mount point aliases via the path-alias table) while gaining the same per-user memory-slot separation, atomic writes and bounds checking; the two implementations were merged by hand where upstream had independently touched the native Export.cpp in the same area (backup/event queue, short mount points) to keep both sets of improvements. None of this is game-specific: it is a general local save-data backend that works for any title and tolerates any user id.
This commit is contained in:
parent
cdc95d9a70
commit
77ed5e6d0c
|
|
@ -24,24 +24,11 @@ static std::string save_root() {
|
|||
return std::string(SAVE_DIR);
|
||||
}
|
||||
|
||||
// Events the title polls with sceSaveDataGetEventResult (a backup completes at once here), and the
|
||||
// "save data memory" blobs (small per-slot memories the title reads and writes without mounting),
|
||||
// persisted as files under the save root so they survive restarts.
|
||||
// Events the title polls with sceSaveDataGetEventResult; a backup completes at once here.
|
||||
static std::mutex g_state_mutex;
|
||||
static std::deque<SaveDataEvent> g_events;
|
||||
static std::map<std::uint32_t, std::vector<std::uint8_t>> g_memories;
|
||||
constexpr std::uint32_t SAVE_DATA_EVENT_TYPE_BACKUP = 2;
|
||||
|
||||
static std::string memory_path(std::uint32_t slot) {
|
||||
return save_root() + "/_memory_" + std::to_string(slot) + ".bin";
|
||||
}
|
||||
|
||||
static void store_memory(std::uint32_t slot, const std::vector<std::uint8_t>& memory) {
|
||||
std::filesystem::create_directories(save_root());
|
||||
std::ofstream file(memory_path(slot), std::ios::binary | std::ios::trunc);
|
||||
file.write(reinterpret_cast<const char*>(memory.data()), static_cast<std::streamsize>(memory.size()));
|
||||
}
|
||||
|
||||
static bool dir_name_match(const char* str, const char* pattern) {
|
||||
if (pattern == nullptr || pattern[0] == '\0') {
|
||||
return true;
|
||||
|
|
@ -72,6 +59,78 @@ static bool dir_name_match(const char* str, const char* pattern) {
|
|||
return *str == '\0' && *pattern == '\0';
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Save-data memory: one blob per user and slot, kept next to the save directory (_sd_mem/u<user>/slot<n>.bin),
|
||||
// with a small .param sidecar holding the last SaveDataParam the title wrote. Keying by user as well as slot
|
||||
// keeps two users' in-memory saves from colliding on the same console.
|
||||
constexpr char MEM_DIR[] = "_sd_mem";
|
||||
constexpr std::size_t MEM_MAX_SIZE = 0x1000000; // 16 MiB
|
||||
std::mutex g_mem_mutex;
|
||||
|
||||
std::string mem_path(std::int32_t user_id, std::uint32_t slot, const char* ext) {
|
||||
char buf[96];
|
||||
std::snprintf(buf, sizeof(buf), "%s/u%d/slot%u.%s", MEM_DIR, static_cast<int>(user_id), static_cast<unsigned>(slot), ext);
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool file_size_of(const std::string& path, std::size_t* out) {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::is_regular_file(path, ec)) {
|
||||
return false;
|
||||
}
|
||||
const auto sz = std::filesystem::file_size(path, ec);
|
||||
if (ec) {
|
||||
return false;
|
||||
}
|
||||
*out = static_cast<std::size_t>(sz);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Atomic-ish write: write to a temp file then rename over the target, so a kill mid-write never
|
||||
// leaves a torn save behind.
|
||||
bool write_file_replace(const std::string& path, const std::vector<char>& data) {
|
||||
const std::string tmp = path + ".tmp";
|
||||
{
|
||||
std::ofstream f(tmp, std::ios::binary | std::ios::trunc);
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
if (!data.empty()) {
|
||||
f.write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
}
|
||||
f.flush();
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(tmp, path, ec);
|
||||
if (ec) {
|
||||
std::filesystem::remove(path, ec);
|
||||
ec.clear();
|
||||
std::filesystem::rename(tmp, path, ec);
|
||||
}
|
||||
return !ec;
|
||||
}
|
||||
|
||||
bool read_file_all(const std::string& path, std::vector<char>& out) {
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
f.seekg(0, std::ios::end);
|
||||
const auto n = static_cast<std::size_t>(f.tellg());
|
||||
f.seekg(0);
|
||||
out.resize(n);
|
||||
if (n != 0) {
|
||||
f.read(out.data(), static_cast<std::streamsize>(n));
|
||||
}
|
||||
return static_cast<bool>(f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceSaveDataBackup(const SaveDataBackup* backup) {
|
||||
|
|
@ -196,29 +255,47 @@ int APS5_VABI sceSaveDataGetParam(const SaveDataMountPoint* mount_point, uint32_
|
|||
|
||||
int APS5_VABI sceSaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) {
|
||||
if (get_param == nullptr) {
|
||||
throw std::runtime_error("sceSaveDataGetSaveDataMemory2: null argument");
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
std::lock_guard lock(g_state_mutex);
|
||||
const auto it = g_memories.find(get_param->slot_id);
|
||||
if (it == g_memories.end()) {
|
||||
return SAVE_DATA_ERROR_NOT_FOUND;
|
||||
if (!g_initialized) {
|
||||
return SAVE_DATA_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (get_param->data != nullptr && get_param->data->buf != nullptr) {
|
||||
const auto& memory = it->second;
|
||||
if (get_param->data->offset > memory.size()) {
|
||||
std::lock_guard<std::mutex> lk(g_mem_mutex);
|
||||
const std::string path = mem_path(get_param->user_id, get_param->slot_id, "bin");
|
||||
std::size_t size = 0;
|
||||
if (!file_size_of(path, &size)) {
|
||||
return SAVE_DATA_ERROR_MEMORY_NOT_READY;
|
||||
}
|
||||
const SaveDataMemoryData* d = get_param->data;
|
||||
if (d != nullptr && d->buf_size != 0) {
|
||||
if (d->buf == nullptr || d->offset > size || d->buf_size > size - d->offset) {
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
const auto bytes = std::min(get_param->data->buf_size, memory.size() - get_param->data->offset);
|
||||
std::memcpy(get_param->data->buf, memory.data() + get_param->data->offset, bytes);
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
if (!f) {
|
||||
return SAVE_DATA_ERROR_INTERNAL;
|
||||
}
|
||||
f.seekg(static_cast<std::streamoff>(d->offset));
|
||||
f.read(static_cast<char*>(d->buf), static_cast<std::streamsize>(d->buf_size));
|
||||
if (!f) {
|
||||
return SAVE_DATA_ERROR_INTERNAL;
|
||||
}
|
||||
}
|
||||
if (get_param->param != nullptr) {
|
||||
std::memset(get_param->param, 0, sizeof(SaveDataParam));
|
||||
std::vector<char> pd;
|
||||
if (read_file_all(mem_path(get_param->user_id, get_param->slot_id, "param"), pd)) {
|
||||
std::memcpy(get_param->param, pd.data(), std::min(pd.size(), sizeof(SaveDataParam)));
|
||||
}
|
||||
}
|
||||
if (get_param->icon != nullptr) {
|
||||
get_param->icon->data_size = 0;
|
||||
}
|
||||
if (get_param->param != nullptr) std::memset(get_param->param, 0, sizeof(SaveDataParam));
|
||||
if (get_param->icon != nullptr) get_param->icon->data_size = 0;
|
||||
return SAVE_DATA_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataInitialize3(const void* init) {
|
||||
(void)init;
|
||||
// NotImplemented_nid_no_patch(__func__);
|
||||
if (g_initialized) {
|
||||
return SAVE_DATA_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
|
@ -332,59 +409,108 @@ int APS5_VABI sceSaveDataSetParam(const SaveDataMountPoint* mount_point, uint32_
|
|||
|
||||
int APS5_VABI sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2* set_param) {
|
||||
if (set_param == nullptr) {
|
||||
throw std::runtime_error("sceSaveDataSetSaveDataMemory2: null argument");
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
std::lock_guard lock(g_state_mutex);
|
||||
const auto it = g_memories.find(set_param->slot_id);
|
||||
if (it == g_memories.end()) {
|
||||
return SAVE_DATA_ERROR_NOT_FOUND;
|
||||
if (!g_initialized) {
|
||||
return SAVE_DATA_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
auto& memory = it->second;
|
||||
if (set_param->data != nullptr) {
|
||||
const auto count = set_param->data_num == 0 ? 1u : set_param->data_num;
|
||||
for (std::uint32_t i = 0; i < count; ++i) {
|
||||
const auto& data = set_param->data[i];
|
||||
if (data.buf == nullptr) continue;
|
||||
if (data.offset > memory.size() || data.buf_size > memory.size() - data.offset) {
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
std::memcpy(memory.data() + data.offset, data.buf, data.buf_size);
|
||||
std::lock_guard<std::mutex> lk(g_mem_mutex);
|
||||
const std::string path = mem_path(set_param->user_id, set_param->slot_id, "bin");
|
||||
std::size_t size = 0;
|
||||
if (!file_size_of(path, &size)) {
|
||||
return SAVE_DATA_ERROR_MEMORY_NOT_READY;
|
||||
}
|
||||
// Validate every range first so a bad entry never leaves a partial write.
|
||||
const std::uint32_t n = set_param->data != nullptr ? (set_param->data_num != 0 ? set_param->data_num : 1u) : 0u;
|
||||
for (std::uint32_t i = 0; i < n; i++) {
|
||||
const SaveDataMemoryData& d = set_param->data[i];
|
||||
if (d.buf_size == 0) {
|
||||
continue;
|
||||
}
|
||||
if (d.buf == nullptr || d.offset > size || d.buf_size > size - d.offset) {
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
}
|
||||
store_memory(set_param->slot_id, memory);
|
||||
if (n != 0) {
|
||||
std::fstream f(path, std::ios::binary | std::ios::in | std::ios::out);
|
||||
if (!f) {
|
||||
return SAVE_DATA_ERROR_INTERNAL;
|
||||
}
|
||||
for (std::uint32_t i = 0; i < n; i++) {
|
||||
const SaveDataMemoryData& d = set_param->data[i];
|
||||
if (d.buf_size == 0) {
|
||||
continue;
|
||||
}
|
||||
f.seekp(static_cast<std::streamoff>(d.offset));
|
||||
f.write(static_cast<const char*>(d.buf), static_cast<std::streamsize>(d.buf_size));
|
||||
}
|
||||
f.flush();
|
||||
if (!f) {
|
||||
return SAVE_DATA_ERROR_INTERNAL;
|
||||
}
|
||||
}
|
||||
if (set_param->param != nullptr) {
|
||||
std::vector<char> pd(sizeof(SaveDataParam));
|
||||
std::memcpy(pd.data(), set_param->param, sizeof(SaveDataParam));
|
||||
write_file_replace(mem_path(set_param->user_id, set_param->slot_id, "param"), pd);
|
||||
}
|
||||
return SAVE_DATA_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup_param, SaveDataMemorySetupResult* result) {
|
||||
if (setup_param == nullptr) {
|
||||
throw std::runtime_error("sceSaveDataSetupSaveDataMemory2: null argument");
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
std::lock_guard lock(g_state_mutex);
|
||||
auto& memory = g_memories[setup_param->slot_id];
|
||||
if (!g_initialized) {
|
||||
return SAVE_DATA_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (setup_param->memory_size == 0 || setup_param->memory_size > MEM_MAX_SIZE) {
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
std::lock_guard<std::mutex> lk(g_mem_mutex);
|
||||
const std::string path = mem_path(setup_param->user_id, setup_param->slot_id, "bin");
|
||||
std::size_t existed = 0;
|
||||
if (memory.empty()) {
|
||||
std::ifstream file(memory_path(setup_param->slot_id), std::ios::binary);
|
||||
if (file) {
|
||||
memory.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
existed = memory.size();
|
||||
const bool have = file_size_of(path, &existed);
|
||||
if (!have) {
|
||||
existed = 0;
|
||||
}
|
||||
// First run: create a zero-filled blob and report existed size 0 so the title treats it as a new save.
|
||||
if (!have || existed < setup_param->memory_size) {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(std::filesystem::path(path).parent_path(), ec);
|
||||
std::vector<char> data;
|
||||
if (have) {
|
||||
read_file_all(path, data);
|
||||
}
|
||||
data.resize(setup_param->memory_size, 0);
|
||||
if (!write_file_replace(path, data)) {
|
||||
return SAVE_DATA_ERROR_INTERNAL;
|
||||
}
|
||||
if (setup_param->init_param != nullptr && (setup_param->option & 1u) != 0) {
|
||||
std::vector<char> pd(sizeof(SaveDataParam));
|
||||
std::memcpy(pd.data(), setup_param->init_param, sizeof(SaveDataParam));
|
||||
write_file_replace(mem_path(setup_param->user_id, setup_param->slot_id, "param"), pd);
|
||||
}
|
||||
} else {
|
||||
existed = memory.size();
|
||||
}
|
||||
if (memory.size() < setup_param->memory_size) {
|
||||
memory.resize(setup_param->memory_size, 0);
|
||||
}
|
||||
if (existed == 0) store_memory(setup_param->slot_id, memory);
|
||||
if (result != nullptr) {
|
||||
std::memset(result, 0, sizeof(*result));
|
||||
result->existed_memory_size = existed;
|
||||
result->existed_memory_size = have ? existed : 0;
|
||||
}
|
||||
return SAVE_DATA_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSyncSaveDataMemory(const void* sync_param) {
|
||||
(void)sync_param;
|
||||
// Every Set already reached the file; nothing is buffered.
|
||||
if (sync_param == nullptr) {
|
||||
return SAVE_DATA_ERROR_PARAMETER;
|
||||
}
|
||||
// Every Set already writes straight through to disk; sync only has to confirm the memory exists.
|
||||
const std::int32_t user_id = *static_cast<const std::int32_t*>(sync_param);
|
||||
const std::uint32_t slot_id = reinterpret_cast<const std::uint32_t*>(sync_param)[1];
|
||||
std::lock_guard<std::mutex> lk(g_mem_mutex);
|
||||
std::size_t size = 0;
|
||||
if (!file_size_of(mem_path(user_id, slot_id, "bin"), &size)) {
|
||||
return SAVE_DATA_ERROR_MEMORY_NOT_READY;
|
||||
}
|
||||
return SAVE_DATA_OK;
|
||||
}
|
||||
|
||||
|
|
@ -401,9 +527,11 @@ int APS5_VABI sceSaveDataTerminate(void) {
|
|||
|
||||
int APS5_VABI sceSaveDataTransferringMount(const SaveDataTransferringMount* mount, SaveDataMountResult* mount_result) {
|
||||
(void)mount;
|
||||
(void)mount_result;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (mount_result != nullptr) {
|
||||
std::memset(mount_result, 0, sizeof(*mount_result));
|
||||
}
|
||||
// No PS4-to-PS5 transfer data exists on this console.
|
||||
return SAVE_DATA_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataUmount2(uint32_t mode, const SaveDataMountPoint* mount_point) {
|
||||
|
|
|
|||
|
|
@ -6,15 +6,18 @@
|
|||
#include <string>
|
||||
|
||||
constexpr int SAVE_DATA_OK = 0;
|
||||
// Values follow the SDK error table (0x809F0000 + code): 2 OUT_OF_MEMORY, 3 BUSY, 4 NOT_MOUNTED, 7 EXISTS, 8 NOT_FOUND, 0xB INTERNAL, 0xC MOUNT_FULL.
|
||||
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 int SAVE_DATA_ERROR_ALREADY_INITIALIZED = -2137063419; // 0x809F0005
|
||||
constexpr int SAVE_DATA_ERROR_OUT_OF_MEMORY = -2137063422;
|
||||
constexpr int SAVE_DATA_ERROR_BUSY = -2137063421;
|
||||
constexpr int SAVE_DATA_ERROR_NOT_MOUNTED = -2137063420;
|
||||
constexpr int SAVE_DATA_ERROR_MOUNT_FULL = -2137063412;
|
||||
constexpr int SAVE_DATA_ERROR_EXISTS = -2137063417;
|
||||
constexpr int SAVE_DATA_ERROR_NOT_FOUND = -2137063416;
|
||||
constexpr int SAVE_DATA_ERROR_INTERNAL = -2137063413; // 0x809F000B
|
||||
constexpr int SAVE_DATA_ERROR_MEMORY_NOT_READY = -2137063406; // 0x809F0012
|
||||
|
||||
constexpr std::uint32_t SAVE_DATA_MOUNT_MODE_RDONLY = 1;
|
||||
constexpr std::uint32_t SAVE_DATA_MOUNT_MODE_RDWR = 2;
|
||||
|
|
|
|||
|
|
@ -1,53 +1,94 @@
|
|||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceSaveData/SaveDataCore.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
// Savedata imports bind by NID either to libSceSaveData.native or (via the relinker alias table,
|
||||
// by name) to this library. Every entry point used to return SoftFailNp (0x800242e2), which is the
|
||||
// NP-family "online service not available" code - wrong dialect for a purely local save call and
|
||||
// fatal-looking for callers. All entry points now delegate to SaveDataCore.hpp: a local,
|
||||
// file-backed implementation that needs no PSN account and tolerates any userId
|
||||
// (0 / 65535 / 268435456), mirroring the native library.
|
||||
|
||||
namespace {
|
||||
// Logs the first three calls of each entry point, then stays silent, keeping guest logs readable.
|
||||
#define SAVEDATA_TRACE(...) \
|
||||
do { \
|
||||
static std::atomic<int> traceCount{0}; \
|
||||
if (traceCount.fetch_add(1, std::memory_order_relaxed) < 3) { \
|
||||
std::fprintf(stderr, "[SAVEDATA:core] " __VA_ARGS__); \
|
||||
std::fputc('\n', stderr); \
|
||||
std::fflush(stderr); \
|
||||
} \
|
||||
} while (0)
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceSaveDataInitialize3(const void* init) {
|
||||
const int rc = savedata::initialize_internal(init);
|
||||
SAVEDATA_TRACE("sceSaveDataInitialize3 -> 0x%08x", static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
int APS5_VABI sceSaveDataTerminate(void) {
|
||||
const int rc = savedata::terminate_internal();
|
||||
SAVEDATA_TRACE("sceSaveDataTerminate -> 0x%08x", static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
int APS5_VABI sceSaveDataBackup(const SaveDataBackup* backup) {
|
||||
(void)backup;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
const int rc = savedata::backup_internal(backup, nullptr);
|
||||
SAVEDATA_TRACE("sceSaveDataBackup -> 0x%08x", static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataGetEventResult(const void* event_param, SaveDataEvent* event) {
|
||||
(void)event_param;
|
||||
(void)event;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
const int rc = savedata::get_event_result_internal(event_param, event);
|
||||
SAVEDATA_TRACE("sceSaveDataGetEventResult -> 0x%08x", static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) {
|
||||
(void)get_param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (get_param == nullptr) {
|
||||
return savedata::SD_ERROR_PARAMETER;
|
||||
}
|
||||
const int rc = savedata::get_memory_internal(get_param);
|
||||
SAVEDATA_TRACE("get2 user=%d slot=%u -> 0x%08x", static_cast<int>(get_param->user_id),
|
||||
static_cast<unsigned>(get_param->slot_id), static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2* set_param) {
|
||||
(void)set_param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (set_param == nullptr) {
|
||||
return savedata::SD_ERROR_PARAMETER;
|
||||
}
|
||||
const int rc = savedata::set_memory_internal(set_param);
|
||||
SAVEDATA_TRACE("set2 user=%d slot=%u num=%u -> 0x%08x",
|
||||
static_cast<int>(set_param->user_id), static_cast<unsigned>(set_param->slot_id),
|
||||
static_cast<unsigned>(set_param->data_num), static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup_param, SaveDataMemorySetupResult* result) {
|
||||
(void)setup_param;
|
||||
(void)result;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup_param,
|
||||
SaveDataMemorySetupResult* result) {
|
||||
const int rc = savedata::setup_memory_internal(setup_param, result, nullptr);
|
||||
SAVEDATA_TRACE("setup2 -> 0x%08x existed=0x%zx", static_cast<unsigned>(rc),
|
||||
result != nullptr ? result->existed_memory_size : static_cast<std::size_t>(0));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSyncSaveDataMemory(const void* sync_param) {
|
||||
(void)sync_param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (sync_param == nullptr) {
|
||||
return savedata::SD_ERROR_PARAMETER;
|
||||
}
|
||||
const auto* sync = static_cast<const savedata::MemorySync*>(sync_param);
|
||||
const int rc = savedata::sync_memory_internal(sync->user_id, sync->slot_id, sync->option, nullptr, true);
|
||||
SAVEDATA_TRACE("sync user=%d slot=%u option=%u -> 0x%08x", static_cast<int>(sync->user_id),
|
||||
static_cast<unsigned>(sync->slot_id), static_cast<unsigned>(sync->option),
|
||||
static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataTransferringMount(const SaveDataTransferringMount* mount, SaveDataMountResult* mount_result) {
|
||||
(void)mount;
|
||||
(void)mount_result;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceSaveDataTransferringMount(const SaveDataTransferringMount* mount,
|
||||
SaveDataMountResult* mount_result) {
|
||||
const int rc = savedata::transferring_mount_internal(mount, mount_result);
|
||||
SAVEDATA_TRACE("sceSaveDataTransferringMount -> 0x%08x", static_cast<unsigned>(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,55 +1,67 @@
|
|||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceSaveDataDialog.native/SaveDataDialog.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
|
||||
namespace {
|
||||
// Throwing from these reaches std::terminate and aborts the game process.
|
||||
int SoftFailNp(const char* func) {
|
||||
std::fprintf(stderr, "[SOFT-NP] %s -> 0 (unimplemented SCE API - no-op)\n", func);
|
||||
std::fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceSaveDataDialogClose(const void* close_param) {
|
||||
(void)close_param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogGetResult(void* result) {
|
||||
(void)result;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
// Never return 0 with an untouched out-struct: the game would assert on the garbage later.
|
||||
if (result == nullptr) {
|
||||
return SAVE_DATA_DIALOG_ERROR_ARG_NULL;
|
||||
}
|
||||
auto* r = static_cast<SaveDataDialogResult*>(result);
|
||||
std::memset(r, 0, sizeof(*r));
|
||||
r->result = SAVE_DATA_DIALOG_RESULT_OK;
|
||||
r->button_id = SAVE_DATA_DIALOG_BUTTON_ID_OK;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogInitialize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogIsReadyToDisplay(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogOpen(const void* param) {
|
||||
(void)param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogProgressBarInc(int target, uint32_t delta) {
|
||||
(void)target;
|
||||
(void)delta;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogProgressBarSetValue(int target, uint32_t rate) {
|
||||
(void)target;
|
||||
(void)rate;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataDialogTerminate(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return SoftFailNp(__func__);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue