# Conflicts:
# docs/dev/debt/libSceSaveData.native.md
The pending declarations line keeps main's sceSaveDataGetProgress split and drops the three names this branch implements (sceSaveDataCheckBackupData, sceSaveDataRestoreBackupData, sceSaveDataUmountWithBackup).
JJenkx's #13324 gives isMountedDirectory an std::error_code parameter.
The merge passes one in checkBackupData and restoreBackupData and
returns SAVE_DATA_ERROR_INTERNAL on a host error, as main's callers do.
GuestSaveDataMountStatus.cpp: CheckBackups writes param.json where
ResolvePath_nid_no_patch puts /app0/sce_sys/param.json, not under the
working directory. #13324's CheckHostMountFailures now resolves the
first guest path from its mount-host-failures subdirectory, so /app0
maps there, and checkBackupData didn't find the title's param.json.
sceSaveDataUmount2 returns NOT_MOUNTED for a mount point that is not mounted.
No console run has checked it, and KytyPS5's sceSaveDataUmount2, shadPS4's PS4
sceSaveDataUmount and fpPS4's PS4 sceSaveDataUmount do not agree on the code, so
the debt file now says so.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
sceSaveDataInitialize3 started the callback delivery thread for every
title that initializes save data, though nearly none register a
callback, and could throw there if scePthreadCreate failed. The thread
now starts at the first successful sceSaveDataRegisterEventCallback of
an initialization, under the calling, g_slots_mutex and callbacks.mutex
locks that registration already takes, so a title that never registers
gets no thread and sceSaveDataInitialize3 is again only the count.
A replacing registration, one from inside a callback and one after an
unregister reuse the running thread; a refused one starts nothing.
The last sceSaveDataTerminate still takes the worker out under
g_slots_mutex and joins it outside the lock, and only when one was
started, so a session that registers between that terminate's detach
and its join starts its own thread.
The tests run two sessions with no registration, and on Linux count the
process's threads to check that Initialize3 and a refused registration
start none, that the first registration starts one and later ones reuse
it, and that the final terminate joins it.
sceSaveDataRegisterEventCallback and sceSaveDataUnregisterEventCallback
were parameterless declarations that threw. They now follow shadPS4
#5226: registering before initialization is NOT_INITIALIZED, a null
callback is PARAMETER, a second registration replaces the callback and
its userdata, and unregistering needs no initialization.
Every backup completion that queues an event for
sceSaveDataGetEventResult, type 2 from sceSaveDataBackup and type 1 from
sceSaveDataUmountWithBackup, is queued for polling first and then handed
to the callback registered at that moment. A library thread delivers it:
sceSaveDataInitialize3 starts it with scePthreadCreate, as
libSceUsbStorage starts its monitor, so the callback runs on a guest
thread, and the last sceSaveDataTerminate joins it. Unregistering waits
for a running callback and drops completions not yet delivered, and a
terminate from inside the callback returns BUSY instead of joining its
own thread, as shadPS4 does.
guest_savedata_event_callback covers the errors, both event types, the
delivering thread, unregistration, replacement, a terminate from inside
the callback and an idle terminate followed by a restart.
ntamas94 ran sceSaveDataCheckBackupData, sceSaveDataRestoreBackupData
and sceSaveDataUmountWithBackup on a PS4 Pro with firmware 10.71's
system libSceSaveData.sprx. The console agrees with this library on
every case it covers. Two of its rows go in the debt file: another
title's titleId returns NOT_FOUND, and sceSaveDataDelete drops the
save's backup as this library does, which the entry called unknown.
The run also shows the check returning PARAMETER for a param buffer
filled with 0xAA, and 0 for a zeroed or null one. Which bytes the
console checks was not measured, so the check is not changed; a new
entry states the measured cases and that the byte set is unknown.
Three entries in the libSceSaveData.native debt file: a failed restore
leaves the live save as it was, and where a crash during a restore or a
backup leaves the save and its backup, since neither is recovered at the
next start; and that on Windows directory entries cannot be flushed, so
crash safety there rests on NTFS journaling and is not shown.