Files
AnyPS5/core/libs/tests/SaveDataWriteReplacement.cpp
T
Savvanis SpyrosandClaude Opus 5.5 4e8bd1609c fix(savedata): build libSceSaveData and libSceSaveDataDialog from the native sources
PS4 titles import save data from libSceSaveData and libSceSaveDataDialog,
and a converted title loads exactly the modules its DT_NEEDED names. Those
two libraries were separate, older implementations that export only 9 and 8
functions, while libSceSaveData.native and libSceSaveDataDialog.native carry
sceSaveDataDelete, sceSaveDataDirNameSearch, sceSaveDataSetParam,
sceSaveDataSaveIcon, sceSaveDataDialogUpdateStatus and the rest. A PS4 title
importing any of those failed at startup with unresolved imports.

Both plain libraries are now built from the native sources, the way
libSceNgs2 is built from libSceNgs2.native, and the old implementation and
its test variant are removed. The earlier implementation's save-data memory
and mount alias files are not read; that is recorded in the debt file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 10:10:50 +03:00

71 lines
2.5 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
extern "C" {
int APS5_VABI sceSaveDataInitialize3(const void*);
int APS5_VABI sceSaveDataTerminate(void);
int APS5_VABI sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2*, SaveDataMemorySetupResult*);
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Save-data replacement check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
int main() {
const auto originalDirectory = std::filesystem::current_path();
const auto root = std::filesystem::temp_directory_path() /
("anyps5-savedata-replacement-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directory(root));
std::filesystem::current_path(root);
constexpr std::int32_t userId = 7531;
const auto savePath = std::filesystem::path("_sd_mem") / ("u" + std::to_string(userId)) / "slot0.bin";
std::filesystem::create_directories(savePath.parent_path());
const std::vector<char> oldData{'o', 'l', 'd'};
{
std::ofstream file(savePath, std::ios::binary);
Require(file.is_open());
file.write(oldData.data(), static_cast<std::streamsize>(oldData.size()));
Require(static_cast<bool>(file));
}
Require(sceSaveDataInitialize3(nullptr) == 0);
SaveDataMemorySetup2 setup{};
setup.user_id = userId;
setup.memory_size = oldData.size() + 3;
SaveDataMemorySetupResult result{};
const int status = sceSaveDataSetupSaveDataMemory2(&setup, &result);
Require(result.existed_memory_size == oldData.size());
Require(status == 0);
std::vector<char> savedData;
{
std::ifstream savedFile(savePath, std::ios::binary);
savedData.assign(std::istreambuf_iterator<char>(savedFile), std::istreambuf_iterator<char>());
}
Require(savedData.size() == setup.memory_size);
Require(std::equal(oldData.begin(), oldData.end(), savedData.begin()));
Require(std::all_of(savedData.begin() + static_cast<std::ptrdiff_t>(oldData.size()),
savedData.end(), [](char byte) { return byte == 0; }));
Require(sceSaveDataTerminate() == 0);
std::filesystem::current_path(originalDirectory);
std::filesystem::remove_all(root);
}