Merge pull request #4234 from solbjorg1245/upstream/savedata-search-info

fix(savedata): fill the search infos of sceSaveDataDirNameSearch
This commit is contained in:
Dean 2026-10-13 01:21:48 +13:00 committed by GitHub
commit a88c4f272f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 22 additions and 2 deletions

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -328,7 +328,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