diff --git a/core/libs/prx/libSceSaveData.native/Export.cpp b/core/libs/prx/libSceSaveData.native/Export.cpp index 89fd8a121..38acdbe8c 100644 --- a/core/libs/prx/libSceSaveData.native/Export.cpp +++ b/core/libs/prx/libSceSaveData.native/Export.cpp @@ -647,22 +647,30 @@ static int setupSaveDataMemory2(const SaveDataMemorySetup2* setup_param, SaveDat if (!have) { existed = 0; } + const bool needParam = (setup_param->init_param != nullptr && (setup_param->option & 1u) != 0); + const std::string paramPath = mem_path(setup_param->user_id, setup_param->slot_id, "param"); + std::size_t paramSize = 0; + const bool haveParam = file_size_of(paramPath, ¶mSize); // 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) { + if (!have || existed < setup_param->memory_size || (needParam && !haveParam)) { std::error_code ec; std::filesystem::create_directories(std::filesystem::path(path).parent_path(), ec); - std::vector data; - if (have && !read_file_all(path, data)) { - return SAVE_DATA_ERROR_INTERNAL; - } - 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) { + if (needParam) { std::vector 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); + if (!write_file_replace(paramPath, pd)) { + return SAVE_DATA_ERROR_INTERNAL; + } + } + if (!have || existed < setup_param->memory_size) { + std::vector data; + if (have && !read_file_all(path, data)) { + return SAVE_DATA_ERROR_INTERNAL; + } + data.resize(setup_param->memory_size, 0); + if (!write_file_replace(path, data)) { + return SAVE_DATA_ERROR_INTERNAL; + } } } if (result != nullptr) { diff --git a/core/libs/tests/SaveDataMemoryGrowth.cpp b/core/libs/tests/SaveDataMemoryGrowth.cpp index 95d5225a3..87d06e2a3 100644 --- a/core/libs/tests/SaveDataMemoryGrowth.cpp +++ b/core/libs/tests/SaveDataMemoryGrowth.cpp @@ -82,10 +82,10 @@ int main(int argc, char**) { std::fprintf(stderr, "Read denial unavailable for this user\n"); exitCode = 77; } else { - Require(Read(path) == original); Require(status == static_cast(0x809F000Bu)); + Require(Read(path) == original); Require(result.existed_memory_size == 123); - Require(!std::filesystem::exists("_sd_mem/u7531/slot0.param")); + Require(std::filesystem::is_regular_file("_sd_mem/u7531/slot0.param")); Require(!std::filesystem::exists(path.string() + ".tmp")); } } else { diff --git a/core/libs/tests/SaveDataMemoryMetadata.cpp b/core/libs/tests/SaveDataMemoryMetadata.cpp index 70724bd18..8f40cccf5 100644 --- a/core/libs/tests/SaveDataMemoryMetadata.cpp +++ b/core/libs/tests/SaveDataMemoryMetadata.cpp @@ -19,6 +19,7 @@ extern "C" { int APS5_VABI sceSaveDataInitialize3(const void*); int APS5_VABI sceSaveDataTerminate(); int APS5_VABI sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2*); +int APS5_VABI sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2*, SaveDataMemorySetupResult*); } static void Check(bool value, int line) { @@ -115,6 +116,176 @@ int main() { Require(sceSaveDataSetSaveDataMemory2(&set) == 0); std::copy(first.begin(), first.end(), memoryExpected.begin() + 4); Require(Read(memoryPath) == memoryExpected); + const auto setupParamPath = std::filesystem::path("_sd_mem/u42/slot0.param"); + const auto setupBinPath = std::filesystem::path("_sd_mem/u42/slot0.bin"); + const auto setupParamTemp = setupParamPath.string() + ".tmp"; + Require(std::filesystem::create_directories(setupParamPath.parent_path())); + Require(std::filesystem::create_directory(setupParamTemp)); + SaveDataParam setupParam{}; + setupParam.user_param = 100; + SaveDataMemorySetup2 setup2{}; + setup2.user_id = 42; + setup2.slot_id = 0; + setup2.memory_size = 128; + setup2.option = 1; + setup2.init_param = &setupParam; + SaveDataMemorySetupResult setupResult{}; + setupResult.existed_memory_size = 555; + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == static_cast(0x809F000Bu)); + Require(setupResult.existed_memory_size == 555); + Require(!std::filesystem::exists(setupBinPath)); + Require(!std::filesystem::exists(setupParamPath)); + Require(!std::filesystem::exists(setupParamTemp)); + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == 0); + Require(setupResult.existed_memory_size == 0); + Require(Read(setupBinPath) == std::vector(128, 0)); + const auto* setupBytes = reinterpret_cast(&setupParam); + Require(Read(setupParamPath) == std::vector(setupBytes, setupBytes + sizeof(setupParam))); + std::vector seededBin(128); + for (std::size_t i = 0; i < seededBin.size(); ++i) { + seededBin[i] = static_cast(static_cast(i * 3 + 7)); + } + { + std::ofstream seededFile(setupBinPath, std::ios::binary); + seededFile.write(seededBin.data(), static_cast(seededBin.size())); + Require(static_cast(seededFile)); + } + const auto growthParamTemp = setupParamPath.string() + ".tmp"; + Require(std::filesystem::create_directory(growthParamTemp)); + SaveDataParam previousParam = setupParam; + setupParam.user_param = 200; + setup2.memory_size = 256; + setupResult.existed_memory_size = 999; + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == static_cast(0x809F000Bu)); + Require(setupResult.existed_memory_size == 999); + Require(Read(setupBinPath) == seededBin); + const auto* previousBytes = reinterpret_cast(&previousParam); + Require(Read(setupParamPath) == std::vector(previousBytes, previousBytes + sizeof(previousParam))); + Require(!std::filesystem::exists(growthParamTemp)); + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == 0); + Require(setupResult.existed_memory_size == 128); + std::vector expectedGrownBin = seededBin; + expectedGrownBin.resize(256, 0); + Require(Read(setupBinPath) == expectedGrownBin); + const auto* updatedBytes = reinterpret_cast(&setupParam); + Require(Read(setupParamPath) == std::vector(updatedBytes, updatedBytes + sizeof(setupParam))); + const auto growthBinTemp = setupBinPath.string() + ".tmp"; + Require(std::filesystem::create_directory(growthBinTemp)); + setupParam.user_param = 300; + setup2.memory_size = 512; + setupResult.existed_memory_size = 888; + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == static_cast(0x809F000Bu)); + Require(setupResult.existed_memory_size == 888); + Require(Read(setupBinPath) == expectedGrownBin); + const auto* param300Bytes = reinterpret_cast(&setupParam); + Require(Read(setupParamPath) == std::vector(param300Bytes, param300Bytes + sizeof(setupParam))); + Require(!std::filesystem::exists(growthBinTemp)); + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == 0); + Require(setupResult.existed_memory_size == 256); + std::vector expectedBlob512 = expectedGrownBin; + expectedBlob512.resize(512, 0); + Require(Read(setupBinPath) == expectedBlob512); + Require(Read(setupParamPath) == std::vector(param300Bytes, param300Bytes + sizeof(setupParam))); + const auto freshFailBinPath = std::filesystem::path("_sd_mem/u55/slot0.bin"); + const auto freshFailParamPath = std::filesystem::path("_sd_mem/u55/slot0.param"); + Require(std::filesystem::create_directories(freshFailBinPath.parent_path())); + const auto freshFailBinTemp = freshFailBinPath.string() + ".tmp"; + Require(std::filesystem::create_directory(freshFailBinTemp)); + SaveDataParam freshFailParam{}; + freshFailParam.user_param = 55; + SaveDataMemorySetup2 freshFailSetup{}; + freshFailSetup.user_id = 55; + freshFailSetup.slot_id = 0; + freshFailSetup.memory_size = 64; + freshFailSetup.option = 1; + freshFailSetup.init_param = &freshFailParam; + SaveDataMemorySetupResult freshFailResult{}; + freshFailResult.existed_memory_size = 777; + Require(sceSaveDataSetupSaveDataMemory2(&freshFailSetup, &freshFailResult) == static_cast(0x809F000Bu)); + Require(freshFailResult.existed_memory_size == 777); + Require(!std::filesystem::exists(freshFailBinPath)); + const auto* freshFailBytes = reinterpret_cast(&freshFailParam); + Require(Read(freshFailParamPath) == std::vector(freshFailBytes, freshFailBytes + sizeof(freshFailParam))); + Require(!std::filesystem::exists(freshFailBinTemp)); + Require(sceSaveDataSetupSaveDataMemory2(&freshFailSetup, &freshFailResult) == 0); + Require(freshFailResult.existed_memory_size == 0); + Require(Read(freshFailBinPath) == std::vector(64, 0)); + Require(Read(freshFailParamPath) == std::vector(freshFailBytes, freshFailBytes + sizeof(freshFailParam))); + SaveDataParam noParam{}; + noParam.user_param = 5; + SaveDataMemorySetup2 noParamSetup{}; + SaveDataMemorySetupResult noParamResult{}; + noParamSetup.user_id = 56; + noParamSetup.slot_id = 0; + noParamSetup.memory_size = 16; + noParamSetup.option = 1; + noParamSetup.init_param = nullptr; + Require(sceSaveDataSetupSaveDataMemory2(&noParamSetup, &noParamResult) == 0); + Require(Read("_sd_mem/u56/slot0.bin") == std::vector(16, 0)); + Require(!std::filesystem::exists("_sd_mem/u56/slot0.param")); + noParamSetup.user_id = 57; + noParamSetup.option = 0; + noParamSetup.init_param = &noParam; + Require(sceSaveDataSetupSaveDataMemory2(&noParamSetup, &noParamResult) == 0); + Require(Read("_sd_mem/u57/slot0.bin") == std::vector(16, 0)); + Require(!std::filesystem::exists("_sd_mem/u57/slot0.param")); + SaveDataParam userA{}; + userA.user_param = 1; + SaveDataParam userB{}; + userB.user_param = 2; + SaveDataMemorySetup2 isoSetup{}; + SaveDataMemorySetupResult isoResult{}; + isoSetup.user_id = 60; + isoSetup.slot_id = 0; + isoSetup.memory_size = 8; + isoSetup.option = 1; + isoSetup.init_param = &userA; + Require(sceSaveDataSetupSaveDataMemory2(&isoSetup, &isoResult) == 0); + isoSetup.slot_id = 1; + isoSetup.init_param = &userB; + Require(sceSaveDataSetupSaveDataMemory2(&isoSetup, &isoResult) == 0); + const auto* userABytes = reinterpret_cast(&userA); + const auto* userBBytes = reinterpret_cast(&userB); + Require(Read("_sd_mem/u60/slot0.param") == std::vector(userABytes, userABytes + sizeof(userA))); + Require(Read("_sd_mem/u60/slot1.param") == std::vector(userBBytes, userBBytes + sizeof(userB))); + isoSetup.user_id = 61; + isoSetup.slot_id = 0; + Require(sceSaveDataSetupSaveDataMemory2(&isoSetup, &isoResult) == 0); + Require(Read("_sd_mem/u60/slot0.param") == std::vector(userABytes, userABytes + sizeof(userA))); + Require(Read("_sd_mem/u61/slot0.param") == std::vector(userBBytes, userBBytes + sizeof(userB))); + const auto orphanParamPath = std::filesystem::path("_sd_mem/u99/slot0.param"); + const auto orphanBinPath = std::filesystem::path("_sd_mem/u99/slot0.bin"); + Require(std::filesystem::create_directories(orphanBinPath.parent_path())); + const std::vector orphanBin(64, 'k'); + { + std::ofstream orphanFile(orphanBinPath, std::ios::binary); + orphanFile.write(orphanBin.data(), static_cast(orphanBin.size())); + Require(static_cast(orphanFile)); + } + SaveDataParam orphanParam{}; + orphanParam.user_param = 42; + SaveDataMemorySetup2 orphanSetup{}; + orphanSetup.user_id = 99; + orphanSetup.slot_id = 0; + orphanSetup.memory_size = 64; + orphanSetup.option = 1; + orphanSetup.init_param = &orphanParam; + SaveDataMemorySetupResult orphanResult{}; + orphanResult.existed_memory_size = 333; + Require(sceSaveDataSetupSaveDataMemory2(&orphanSetup, &orphanResult) == 0); + Require(orphanResult.existed_memory_size == 64); + Require(Read(orphanBinPath) == orphanBin); + const auto* orphanBytes = reinterpret_cast(&orphanParam); + Require(Read(orphanParamPath) == std::vector(orphanBytes, orphanBytes + sizeof(orphanParam))); + Require(sceSaveDataTerminate() == 0); + Require(Read(setupBinPath) == expectedBlob512); + Require(Read(setupParamPath) == std::vector(param300Bytes, param300Bytes + sizeof(setupParam))); + Require(sceSaveDataInitialize3(nullptr) == 0); + setupResult.existed_memory_size = 111; + Require(sceSaveDataSetupSaveDataMemory2(&setup2, &setupResult) == 0); + Require(setupResult.existed_memory_size == 512); + Require(Read(setupBinPath) == expectedBlob512); + Require(Read(setupParamPath) == std::vector(param300Bytes, param300Bytes + sizeof(setupParam))); Require(sceSaveDataTerminate() == 0); std::filesystem::current_path(previous); std::filesystem::remove_all(root);