Files
AnyPS5/docs/dev/debt/libSceSaveData.native.md
Adrià 2147af70b0 Merge pull request #13690 from xXPBSXx/docs/savedata-umount-unmounted
docs(libSceSaveData): name what the emulators do when a mount point is not mounted
2026-10-31 20:08:37 +01:00

23 KiB

libSceSaveData.native technical debt

Silent stubs

  • sceSaveDataMount3: host filesystem failures and a failed block-count sidecar replacement return the existing INTERNAL error (0x809F000B), following the host-I/O policy used by other save operations. The corresponding PS5 error mapping for each host failure is not confirmed; directory creation and sidecar replacement must finish before exposing a mount.

  • sceSaveDataAbort, sceSaveDataClearProgress (libSceSaveData.native) return SAVE_DATA_OK after initialization without cancelling operations or clearing any progress. The current host operations run synchronously and no backup progress is tracked; sceSaveDataClearProgress does not reset sceSaveDataGetConvertProgress. Their console signatures, errors and behavior are unverified.

  • sceSaveDataPrepare, sceSaveDataCommit (libSceSaveData.native) return success without looking at their arguments; what preparing and committing a mounted save do on a console is unknown. The same six titles call both; this counts tests of the result in the three instructions after each call. sceSaveDataPrepare has 11 call sites: PPSA01494, PPSA30839 and PPSA31162 test none of them there, PPSA03984 takes an error path when it is negative, PPSA34502 checks it at one of two sites and PPSA36591 at two of four. sceSaveDataCommit has 14: PPSA01494 and PPSA30839 test none, PPSA03984 and PPSA31162 check its sign, PPSA34502 checks it at all five sites and PPSA36591 at two of four. A failure code from either would change the save flow of the titles that check it, while the stubs keep all six on the success path.

Unknown function info

Functional

  • sceSaveDataDirNameSearch, sceSaveDataDirNameSearch2 (libSceSaveData.native) - only the running title's saves are kept, so a search with another title's title_id reports zero hits, as shadPS4 and KytyPS5 do when that title has no save directory; what a console returns for another title's saves is not verified. The running title's ID is the titleId in its param.json

  • sceSaveDataDirNameSearch2 (libSceSaveData.native) runs the same search as sceSaveDataDirNameSearch with the same condition and result structures, as SharpEmu does, its only source; what distinguishes it on the console is unknown. One title (PPSA35669, in a plugin) disassembled for this entry has one call site (0x2eda71); it clears 56 bytes for the result structure first (the size of the structure used here), puts a value returned by a call with the argument 0x8000 at offset 8 and 0x400 at offset 0x10, and afterwards branches on a negative result and reads the 32-bit field at offset 0, skipping its loop when it is 0. That agrees with the result structure of sceSaveDataDirNameSearch. The condition structure was not read.

  • sceSaveDataMount3 (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. In PPSA05347 the function that calls it at 0x235fbb5 zeroes its 0x40-byte result before the call and, for a negative result other than 0x809f0008 and 0x809f000f (BUSY included), logs the code and returns it without recording the mount; no abort follows.

  • sceSaveDataMount3 (libSceSaveData.native) - an unterminated or empty directory name, ., .., or a name containing /, \ or : returns SAVE_DATA_ERROR_PARAMETER, which the SDK 12.000 reference lists for an invalid parameter, and leaves the result as it was, as KytyPS5 does. The SDK lets an application use only letters, digits, -, . and @ in a name, and KytyPS5 also accepts _ (valid_path_component); which other names the console's mount rejects is not checked on hardware

  • sceSaveDataBackup (libSceSaveData.native) - an unterminated directory name returns SAVE_DATA_ERROR_PARAMETER to prevent unbounded copy; which other directory names the console's backup rejects is unverified on hardware.

  • sceSaveDataBackup, sceSaveDataConvert, sceSaveDataDelete, sceSaveDataDirNameSearch, sceSaveDataDirNameSearchPs4, sceSaveDataGetEventResult, sceSaveDataGetMountInfo, sceSaveDataGetParam, sceSaveDataLoadIcon, sceSaveDataMount, sceSaveDataMount3, sceSaveDataSaveIcon, sceSaveDataSetParam, sceSaveDataUmount2 (libSceSaveData.native) - null pointer arguments return SAVE_DATA_ERROR_PARAMETER instead of throwing C++ exceptions, an implementation assumption matching guest error expectations not checked on hardware; unmounted targets return SAVE_DATA_ERROR_NOT_MOUNTED, and parameter type or buffer size violations return SAVE_DATA_ERROR_PARAMETER after slot lookup

  • sceSaveDataUmount2 (libSceSaveData.native) - a mount point that is not mounted returns SAVE_DATA_ERROR_NOT_MOUNTED (0x809F0004), which no console run has checked. KytyPS5's sceSaveDataUmount2 (PS5, src/libs/libSaveData.cpp, commit 5e837a9beac82c031868eaad9348d6a48245fb99) returns SAVE_DATA_ERROR_NOT_FOUND (0x809F0008) there. For the PS4 sceSaveDataUmount, which none of the local titles import, shadPS4 (PS4, src/core/libraries/save_data/savedata.cpp, commit 0614ce324be092791b758d79f93d9f1ca5b10a81) returns SAVE_DATA_ERROR_NOT_FOUND and fpPS4 (PS4, sys/sys_path.pas, commit 04cefd43e6fddd1ab033e7980cd356d14c964905) SAVE_DATA_ERROR_NOT_MOUNTED. Forty of the 53 local titles import sceSaveDataUmount2.

  • sceSaveDataSyncSaveDataMemory (libSceSaveData.native) - validates library initialization and parameters; since in-memory saves flush synchronously on write, sync confirms existence and readiness of the memory blob

  • sceSaveDataGetMountedSaveDataCount (libSceSaveData.native) - signature and behavior are unverified; the function throws. SharpEmu and RPS5E write the count through a second argument and return a status; their console accuracy is unverified.

  • sceSaveDataMount3 (libSceSaveData.native) - the console's NO_SPACE_FS code, the rounding of required_blocks and whether it checks the volume when a save is created are not measured; with ANYPS5_FREE_SPACE=host a save larger than the room of its volume fails with 0x809F000A and sets required_blocks to the missing blocks rounded up to 64 KiB, as shadPS4 does at savedata.cpp L468-L481, which rounds down and checks the full size

  • sceSaveDataGetMountInfo, sceSaveDataDirNameSearch (libSceSaveData.native) - whether the console bounds a save's free blocks by the room of the disk is not measured; with ANYPS5_FREE_SPACE=host they are the smaller of the save's own free blocks and the 64 KiB blocks its volume still has, and a full volume reports none

  • sceSaveDataMount3, sceSaveDataGetMountInfo (libSceSaveData.native) - the 64 KiB block that the room and free-block figures use is not measured on a console; shadPS4 L50-L52 uses 32 KiB blocks and 96 to 32768 blocks, so the bytes that host mode requires for a save, and its free-block counts, would differ by a factor of two if the console uses 32 KiB