diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 4269cee09..29dddbc5a 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -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 { diff --git a/core/libs/prx/libSceSaveData.native/Export.cpp b/core/libs/prx/libSceSaveData.native/Export.cpp index 89fd8a121..a2c9cc131 100644 --- a/core/libs/prx/libSceSaveData.native/Export.cpp +++ b/core/libs/prx/libSceSaveData.native/Export.cpp @@ -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(matches.size()); result->set_num = set; diff --git a/core/libs/tests/GuestSaveDataSearch.cpp b/core/libs/tests/GuestSaveDataSearch.cpp index 0be7b2d33..601783fe8 100644 --- a/core/libs/tests/GuestSaveDataSearch.cpp +++ b/core/libs/tests/GuestSaveDataSearch.cpp @@ -92,11 +92,23 @@ static void SearchAndMount() { SaveDataDirNameSearchResult result{}; result.dir_names = reinterpret_cast(buffer.data()); result.dir_names_num = 2; + alignas(SaveDataSearchInfo) std::array infos; + infos.fill(0x21); + result.infos = reinterpret_cast(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 found{}; for (int i = 0; i < 2; ++i) { const char* record = buffer.data() + i * 32; diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 0fa78fec4..4df622c51 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -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