diff --git a/core/libs/prx/libSceSaveData.native/Export.cpp b/core/libs/prx/libSceSaveData.native/Export.cpp index 58e898833..719a79dbf 100644 --- a/core/libs/prx/libSceSaveData.native/Export.cpp +++ b/core/libs/prx/libSceSaveData.native/Export.cpp @@ -463,7 +463,6 @@ static int mount3(const SaveDataMount3* mount, SaveDataMountResult* mount_result 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; @@ -498,6 +497,7 @@ static int mount3(const SaveDataMount3* mount, SaveDataMountResult* mount_result if (slot == -1) { return SAVE_DATA_ERROR_MOUNT_FULL; } + std::memset(mount_result, 0, sizeof(*mount_result)); if (create || create2) { std::filesystem::create_directories(real_path); } diff --git a/core/libs/tests/GuestSaveDataMountStatus.cpp b/core/libs/tests/GuestSaveDataMountStatus.cpp index c51a1cb5c..9249ab383 100644 --- a/core/libs/tests/GuestSaveDataMountStatus.cpp +++ b/core/libs/tests/GuestSaveDataMountStatus.cpp @@ -18,6 +18,8 @@ namespace { constexpr std::uint32_t MountReadWrite = 2; constexpr std::uint32_t MountCreate = 4; constexpr std::uint32_t MountCreate2 = 32; +constexpr int SaveDataErrorBusy = -2137063421; +constexpr int SaveDataErrorNotFound = -2137063416; int failures = 0; @@ -41,6 +43,26 @@ void MountAndCheck(const char* name, std::uint32_t mode, std::uint32_t expectedS Check(sceSaveDataUmount2(0, &result.mount_point) == 0, what + ": unmounts"); } +void CheckFailedMountKeepsResult() { + SceSaveDataDirName held{}; + std::memcpy(held.data, "held", sizeof("held")); + SaveDataMount3 mount{}; + mount.dir_name = &held; + mount.mount_mode = MountReadWrite | MountCreate2; + SaveDataMountResult result{}; + Check(sceSaveDataMount3(&mount, &result) == 0, "first mount of a save"); + const SaveDataMountResult mounted = result; + Check(sceSaveDataMount3(&mount, &result) == SaveDataErrorBusy, "second mount of a mounted save is BUSY"); + Check(std::memcmp(&result, &mounted, sizeof(result)) == 0, "a BUSY mount leaves the result as it was"); + SceSaveDataDirName absent{}; + std::memcpy(absent.data, "absent", sizeof("absent")); + mount.dir_name = &absent; + mount.mount_mode = MountReadWrite; + Check(sceSaveDataMount3(&mount, &result) == SaveDataErrorNotFound, "open of a missing save is NOT_FOUND"); + Check(std::memcmp(&result, &mounted, sizeof(result)) == 0, "a NOT_FOUND mount leaves the result as it was"); + Check(sceSaveDataUmount2(0, &result.mount_point) == 0, "the kept mount point unmounts the save"); +} + } int main() { @@ -53,6 +75,7 @@ int main() { MountAndCheck("fresh", MountReadWrite | MountCreate2, 0u, "CREATE2 of an existing save"); MountAndCheck("made", MountReadWrite | MountCreate, 1u, "CREATE of a missing save"); MountAndCheck("made", MountReadWrite, 0u, "open of an existing save"); + CheckFailedMountKeepsResult(); std::filesystem::current_path(previous); std::error_code error; std::filesystem::remove_all(root, error); diff --git a/docs/dev/debt/libSceSaveData.native.md b/docs/dev/debt/libSceSaveData.native.md index dfec69c2a..6f990d175 100644 --- a/docs/dev/debt/libSceSaveData.native.md +++ b/docs/dev/debt/libSceSaveData.native.md @@ -12,4 +12,5 @@ ### Functional - [sceSaveDataDirNameSearch2](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) runs the same search as `sceSaveDataDirNameSearch` with the same condition and result structures, as [SharpEmu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/SaveData/SaveDataExports.cs) does, its only source; what distinguishes it on the console is unknown. +- [sceSaveDataMount3](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - a failed mount (BUSY, EXISTS, NOT_FOUND, MOUNT_FULL) leaves the result as it was, as shadPS4 does; not checked on hardware. Animal Well (PPSA22520) depends on it: it checks for its save with a mount it never unmounts, then saves with a second mount of the same directory, ignores its BUSY and keeps using the first mount point for `sceSaveDataPrepare`, its file and `sceSaveDataUmount2`