savedata: implement sceSaveDataConvert's synchronous checks

Co-authored-by: Mac <me@itsmemac.com>
Co-authored-by: Senaxx <michelbeld@gmail.com>
This commit is contained in:
nmzik
2026-10-25 23:31:47 +01:00
parent 95bbc9c65d
commit 22a9392d23
3 changed files with 106 additions and 4 deletions
+2
View File
@@ -658,6 +658,8 @@ constexpr int SAVE_DATA_ERROR_BUSY_FOR_SAVING = -2137063402
constexpr int SAVE_DATA_ERROR_LIMITATION_OVER = -2137063401; /* 0x809F0017 */
constexpr int SAVE_DATA_ERROR_EVENT_BUSY = -2137063400; /* 0x809F0018 */
constexpr int SAVE_DATA_ERROR_PARAMSFO_TRANSFER_TITLE_ID_NOT_FOUND = -2137063399; /* 0x809F0019 */
constexpr int SAVE_DATA_ERROR_NO_NEED_CONVERT = -2137063394; /* 0x809F001E */
constexpr int SAVE_DATA_ERROR_BLOCKS_FEWER_THAN_SRC = -2137063392; /* 0x809F0020 */
} // namespace SaveData
+71 -4
View File
@@ -231,6 +231,15 @@ struct SaveDataDelete {
int32_t pad2;
};
struct SaveDataConvert {
int32_t user_id;
int32_t pad;
const SceSaveDataDirName* src_dir_name;
const SceSaveDataDirName* dst_dir_name;
uint64_t dst_blocks;
uint8_t reserved[24];
};
struct SaveDataEvent {
uint32_t type;
int32_t error_code;
@@ -309,14 +318,20 @@ static std::filesystem::path save_user_directory(std::string_view title_id, int3
return directory / title_id;
}
static std::filesystem::path save_directory(std::string_view title_id, std::string_view dir_name,
int32_t user_id) {
static bool is_memory_dir_name(std::string_view dir_name) {
for (uint32_t slot = 0; slot < 4; slot++) {
if (dir_name == memory_dir_name(slot)) {
return std::filesystem::path(SAVE_DATA_DIR) / title_id / dir_name /
std::to_string(user_id);
return true;
}
}
return false;
}
static std::filesystem::path save_directory(std::string_view title_id, std::string_view dir_name,
int32_t user_id) {
if (is_memory_dir_name(dir_name)) {
return std::filesystem::path(SAVE_DATA_DIR) / title_id / dir_name / std::to_string(user_id);
}
return save_user_directory(title_id, user_id) / dir_name;
}
@@ -1180,6 +1195,56 @@ int KYTY_SYSV_ABI SaveDataGetEventResult(const void* event_param, SaveDataEvent*
return OK;
}
int KYTY_SYSV_ABI SaveDataConvert(const SaveDataConvert* convert) {
PRINT_NAME();
const auto valid_name = [](const SceSaveDataDirName* name) {
return valid_path_component(name->data) && !is_memory_dir_name(name->data);
};
if (convert == nullptr || convert->src_dir_name == nullptr ||
!valid_name(convert->src_dir_name) ||
(convert->dst_dir_name != nullptr && !valid_name(convert->dst_dir_name)) ||
convert->dst_blocks < SAVE_DATA_BLOCKS_MIN || convert->dst_blocks > SAVE_DATA_BLOCKS_MAX) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (convert->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %" PRId32 "\n"
"\t src_dir_name = %s\n"
"\t dst_dir_name = %s\n"
"\t dst_blocks = %" PRIu64 "\n",
convert->user_id, convert->src_dir_name->data,
convert->dst_dir_name != nullptr ? convert->dst_dir_name->data : "<none>",
convert->dst_blocks);
Common::LockGuard lock(g_mount_mutex);
const auto source =
save_directory(get_title_id(), convert->src_dir_name->data, convert->user_id);
if (!Common::File::IsDirectoryExisting(source)) {
return SAVE_DATA_ERROR_NOT_FOUND;
}
if (g_mount_slots.FindAvailable(source) == SaveDataMountSlots::BUSY) {
return SAVE_DATA_ERROR_BUSY;
}
if (convert->dst_dir_name != nullptr &&
Common::File::IsDirectoryExisting(
save_directory(get_title_id(), convert->dst_dir_name->data, convert->user_id))) {
return SAVE_DATA_ERROR_EXISTS;
}
uint64_t blocks = 0;
if (read_save_blocks(source, &blocks) == OK) {
if (convert->dst_blocks == blocks) {
return SAVE_DATA_ERROR_NO_NEED_CONVERT;
}
if (convert->dst_blocks < blocks) {
return SAVE_DATA_ERROR_BLOCKS_FEWER_THAN_SRC;
}
}
EXIT("save data conversion of %s is not implemented\n", convert->src_dir_name->data);
}
int KYTY_SYSV_ABI SaveDataBackup(const SaveDataBackup* backup) {
PRINT_NAME();
@@ -1313,6 +1378,7 @@ LIB_DEFINE(InitSaveDataNative_1) {
LIB_FUNC("cduy9v4YmT4", ::Libs::SaveData::SaveDataSetSaveDataMemory2);
LIB_FUNC("wiT9jeC7xPw", ::Libs::SaveData::SaveDataSyncSaveDataMemory);
LIB_FUNC("j8xKtiFj0SY", ::Libs::SaveData::SaveDataGetEventResult);
LIB_FUNC("2mfSRGdshtk", ::Libs::SaveData::SaveDataConvert);
LIB_FUNC("WAzWTZm1H+I", ::Libs::SaveData::SaveDataTransferringMount);
LIB_FUNC("RjMlsR8EXrw", ::Libs::SaveData::SaveDataTransferringMount);
LIB_FUNC("z1JA8-iJt3k", ::Libs::SaveData::SaveDataBackup);
@@ -1343,6 +1409,7 @@ LIB_DEFINE(InitSaveData_1) {
LIB_FUNC("cduy9v4YmT4", SaveData::SaveDataSetSaveDataMemory2);
LIB_FUNC("wiT9jeC7xPw", SaveData::SaveDataSyncSaveDataMemory);
LIB_FUNC("j8xKtiFj0SY", SaveData::SaveDataGetEventResult);
LIB_FUNC("2mfSRGdshtk", SaveData::SaveDataConvert);
LIB_FUNC("WAzWTZm1H+I", SaveData::SaveDataTransferringMount);
LIB_FUNC("RjMlsR8EXrw", SaveData::SaveDataTransferringMount);
LIB_FUNC("z1JA8-iJt3k", SaveData::SaveDataBackup);
+33
View File
@@ -396,6 +396,38 @@ void TestClassicSavePaths() {
CHECK(Search(1) == names);
}
void TestConvert() {
Reset("CONVERT");
const auto source = DirName("save");
const auto memory = DirName("sce_sdmemory");
struct SaveDataConvert convert {};
convert.user_id = 1;
convert.src_dir_name = &memory;
convert.dst_blocks = 96;
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_PARAMETER);
// A missing source fails at once.
convert.src_dir_name = &source;
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_NOT_FOUND);
struct SaveDataMount3 mount {};
mount.user_id = 1;
mount.dir_name = &source;
mount.mount_mode = 4;
mount.blocks = 96;
SaveDataMountResult mounted {};
CHECK(SaveDataMount3(&mount, &mounted) == OK);
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_BUSY);
CHECK(SaveDataUmount2(0, &mounted.mount_point) == OK);
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_NO_NEED_CONVERT);
convert.dst_blocks = 95;
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_BLOCKS_FEWER_THAN_SRC);
convert.dst_dir_name = &source;
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_EXISTS);
for (const uint64_t blocks: {47u, 16385u}) {
convert.dst_blocks = blocks;
CHECK(SaveDataConvert(&convert) == SAVE_DATA_ERROR_PARAMETER);
}
}
void TestGuestSaveIsolation() {
Reset("MULTIUSER");
const auto name = DirName("slot");
@@ -670,6 +702,7 @@ int main(int argc, char** argv) {
TestIsolationAndSync();
TestFailedWritePreservesSave();
TestClassicSavePaths();
TestConvert();
TestGuestSaveIsolation();
TestSaveAllocations();
TestClassicSaveParams();