fix(savedata): fill the search infos of sceSaveDataDirNameSearch
SaveDataDirNameSearchResult.infos points at an array the title reads one entry per returned directory name, but the search never wrote it, and SaveDataSearchInfo was declared as 128 opaque bytes. A title that asks for the infos read whatever its buffer held. SaveDataSearchInfo is now the 48-byte record shadPS4 declares as OrbisSaveDataSearchInfo (blocks, free blocks, 32 reserved bytes), and the search fills one per returned name. Every native save reports SAVE_DATA_BLOCKS_MAX blocks, as sceSaveDataGetMountInfo and the block sort keys already assume, so both counts are SAVE_DATA_BLOCKS_MAX and the reserved bytes are zero. guest_savedata_search_tests checks the two records, their zero reserved bytes and that the search writes nothing past them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFVE5WbaUJ2vyj8NAW44CS
This commit is contained in:
parent
7a84b22267
commit
4b125c0ca5
|
|
@ -1448,7 +1448,12 @@ struct SaveDataMountInfo {
|
|||
|
||||
struct SceSaveDataTitleId { char data[10]; char pad[2]; };
|
||||
struct SceSaveDataDirName { char data[32]; };
|
||||
struct SaveDataSearchInfo { std::uint8_t opaque[128]; };
|
||||
struct SaveDataSearchInfo {
|
||||
std::uint64_t blocks;
|
||||
std::uint64_t free_blocks;
|
||||
std::uint8_t reserved[32];
|
||||
};
|
||||
static_assert(sizeof(SaveDataSearchInfo) == 48);
|
||||
struct SaveDataMemoryData { void* buf; std::size_t buf_size; std::size_t offset; };
|
||||
|
||||
struct SaveDataMount3 {
|
||||
|
|
|
|||
|
|
@ -315,6 +315,9 @@ static int dirNameSearch(const SaveDataDirNameSearchCond* cond, SaveDataDirNameS
|
|||
if (result->params != nullptr) {
|
||||
result->params[i] = sortByParam ? matches[i].param : load_param(matches[i].path);
|
||||
}
|
||||
if (result->infos != nullptr) {
|
||||
result->infos[i] = SaveDataSearchInfo{SAVE_DATA_BLOCKS_MAX, SAVE_DATA_BLOCKS_MAX, {}};
|
||||
}
|
||||
}
|
||||
result->hit_num = static_cast<std::uint32_t>(matches.size());
|
||||
result->set_num = set;
|
||||
|
|
|
|||
|
|
@ -92,11 +92,23 @@ static void SearchAndMount() {
|
|||
SaveDataDirNameSearchResult result{};
|
||||
result.dir_names = reinterpret_cast<SceSaveDataDirName*>(buffer.data());
|
||||
result.dir_names_num = 2;
|
||||
alignas(SaveDataSearchInfo) std::array<unsigned char, 112> infos;
|
||||
infos.fill(0x21);
|
||||
result.infos = reinterpret_cast<SaveDataSearchInfo*>(infos.data());
|
||||
Require(sceSaveDataDirNameSearch(&cond, &result) == 0, "search failed");
|
||||
Require(result.hit_num == 2 && result.set_num == 2, "search must return both saves");
|
||||
for (std::size_t i = 64; i < buffer.size(); ++i) {
|
||||
Require(buffer[i] == '!', "directory search overran two 32-byte guest records");
|
||||
}
|
||||
for (std::size_t i = 96; i < infos.size(); ++i) {
|
||||
Require(infos[i] == 0x21, "directory search overran two 48-byte search infos");
|
||||
}
|
||||
for (std::size_t i = 0; i < 2; ++i) {
|
||||
SaveDataSearchInfo info{};
|
||||
std::memcpy(&info, infos.data() + i * 48, sizeof(info));
|
||||
Require(info.blocks == 32768 && info.free_blocks == 32768, "search info must report the save's blocks and free blocks");
|
||||
for (const auto byte : info.reserved) Require(byte == 0, "search info reserved bytes must be zero");
|
||||
}
|
||||
std::array<bool, 2> found{};
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const char* record = buffer.data() + i * 32;
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceNgs2VoiceQueryInfo](../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - only info id 0x4001 is known: PPSA22520 passes an 8-byte buffer and branches on the first word being 1 or 2, so it is assumed to be the voice channel count (0 before the voice is set up); the second word is zeroed and other ids throw
|
||||
- [sceNgs2VoiceGetPortInfo](../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - layout from shadPS4; the delay and destination input are zero because ports have neither yet
|
||||
- [sceNgs2ParseWaveformFile](../../core/libs/prx/libSceNgs2.native/src/Atrac9.cpp) (libSceNgs2.native) - the signature `(path, offset, info)` is taken from the call site of PPSA21402, which sets only three argument registers; the file is read from `offset` to its end, the data and block offsets are taken to be relative to the start of the file, not of `offset`, and an unreadable file throws. The audio unit fields of a 16-bit PCM waveform are assumed to be one sample frame per unit and per frame; `INVALID_WAVEFORM_DATA` for a `data` chunk that runs past the buffer or ends inside a frame is assumed, and looped PCM and other PCM formats throw
|
||||
- [sceSaveDataDirNameSearch](../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - the sort keys (0 directory name, 1 user parameter, 2 blocks, 3 modification time, 5 free blocks) and orders (0 ascending, 1 descending) follow shadPS4, not a hardware run; saves with equal keys keep directory-name order before a descending reverse. Every native save reports `SAVE_DATA_BLOCKS_MAX` blocks, as `sceSaveDataGetMountInfo` does, so the two block keys keep directory-name order. Keys other than 0, 1, 2, 3 and 5 (including 4) and orders above 1 return `SCE_SAVE_DATA_ERROR_PARAMETER`; shadPS4 and KytyPS5 only reject keys above 5, so key 4 is rejected here because no source names it
|
||||
- [sceSaveDataDirNameSearch](../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - the sort keys (0 directory name, 1 user parameter, 2 blocks, 3 modification time, 5 free blocks) and orders (0 ascending, 1 descending) follow shadPS4, not a hardware run; saves with equal keys keep directory-name order before a descending reverse. Every native save reports `SAVE_DATA_BLOCKS_MAX` blocks, as `sceSaveDataGetMountInfo` does, so the two block keys keep directory-name order. The search infos (blocks, free blocks and 32 reserved bytes per returned name, following shadPS4's `OrbisSaveDataSearchInfo`) report `SAVE_DATA_BLOCKS_MAX` for both. Keys other than 0, 1, 2, 3 and 5 (including 4) and orders above 1 return `SCE_SAVE_DATA_ERROR_PARAMETER`; shadPS4 and KytyPS5 only reject keys above 5, so key 4 is rejected here because no source names it
|
||||
- [sceSaveDataInitialize3](../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - a repeated initialize succeeds: PPSA12544's executable and its Unity SaveData plugin both initialize, and the plugin fails on any error. Initializations are counted and `sceSaveDataTerminate` ends the session at the last one; how the console pairs them is unknown
|
||||
- [sceTextToSpeech2GetSystemStatus](../../core/libs/prx/libSceTextToSpeech2/Export.cpp) (libSceTextToSpeech2) - unknown signature; returns `EOPNOTSUPP` like `sceTextToSpeech2Initialize`
|
||||
- [sceCoredumpAttachUserMemoryFile](../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - unknown signature
|
||||
|
|
|
|||
Loading…
Reference in New Issue