SaveDataMemoryData was declared as 24 bytes, so libSceSaveData.native read the second and later entries of a guest data array from the reserved bytes of the first one, saw a zero size and skipped them while returning OK. The entry is 64 bytes (buffer, size, offset, 40 reserved), as SaveDataCore.hpp already asserted for its own copy of the struct and shadPS4 declares.
With the shared type the right size, SaveDataCore.hpp drops its private MemoryData and the casts to it and uses SaveDataMemoryData. Its range check takes the unsigned offset; a negative guest offset is still rejected because it exceeds any memory size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Write initial SaveDataParam metadata before replacing the memory blob so metadata failure cannot leave a prematurely grown blob. Handle blob write failure with metadata rollback, and allow retries to recover slots with missing metadata. Expand tests with nonzero data, blob failure, and fresh readback.
Report SAVE_DATA_ERROR_INTERNAL when setupSaveDataMemory2 fails to persist initial SaveDataParam metadata, and restore previous state so retrying setup can initialize cleanly. Closes#1667.
A second sceSaveDataInitialize3 returned SAVE_DATA_ERROR_ALREADY_INITIALIZED.
PPSA12544's executable and its Unity SaveData plugin both initialize, and
the plugin raises Unity.SaveData.PS5.Core.SaveDataException on any error,
so the console must accept the second call. Initializations are now
counted; sceSaveDataTerminate ends the session at the last one and only
then checks for mounted slots.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>