Merge pull request #4504 from yunusshaikh7/fix/savedata-dir-name-trailing-wildcard

fix(savedata): let a trailing % in a directory name pattern match nothing
This commit is contained in:
Dean 2026-10-13 07:29:49 +13:00 committed by GitHub
commit 4756c051ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 1 deletions

View File

@ -62,6 +62,9 @@ static bool dir_name_match(const char* str, const char* pattern) {
str++;
pattern++;
}
while (*pattern == '%') {
pattern++;
}
return *str == '\0' && *pattern == '\0';
}

View File

@ -81,6 +81,31 @@ static void SortByKey() {
Require(sceSaveDataDirNameSearch(&cond, &result) == SaveDataErrorParameter, "an unknown sort order must be rejected");
}
static std::vector<std::string> SearchPattern(const char* text) {
SceSaveDataDirName pattern{};
std::snprintf(pattern.data, sizeof(pattern.data), "%s", text);
SaveDataDirNameSearchCond cond{};
cond.user_id = 1;
cond.dir_name = &pattern;
std::array<SceSaveDataDirName, 4> names{};
SaveDataDirNameSearchResult result{};
result.dir_names = names.data();
result.dir_names_num = static_cast<std::uint32_t>(names.size());
Require(sceSaveDataDirNameSearch(&cond, &result) == 0, "pattern search failed");
std::vector<std::string> found;
for (std::uint32_t i = 0; i < result.set_num; ++i) found.emplace_back(names[i].data);
return found;
}
static void TrailingWildcards() {
for (const char* name : std::array{"TRAIL", "TRAIL1"}) std::filesystem::create_directories(std::filesystem::path("_sd") / name);
using Names = std::vector<std::string>;
Require(SearchPattern("TRAIL%") == Names{"TRAIL", "TRAIL1"}, "a trailing % must also match no characters");
Require(SearchPattern("TRAIL%%") == Names{"TRAIL", "TRAIL1"}, "trailing %% must also match no characters");
Require(SearchPattern("TRAI_%") == Names{"TRAIL", "TRAIL1"}, "_ then % must match one character and then none");
Require(SearchPattern("TRAIL_%") == Names{"TRAIL1"}, "_ must still match exactly one character");
}
static void SearchAndMount() {
const std::array names{std::string(31, 'A'), std::string(31, 'B')};
for (const auto& name : names) std::filesystem::create_directories(std::filesystem::path("_sd") / name);
@ -139,6 +164,7 @@ int main() {
try {
SearchAndMount();
SortByKey();
TrailingWildcards();
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
status = 1;

View File

@ -330,7 +330,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. 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
- [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. In the directory name pattern `%` matches any run of characters, none included, and `_` exactly one, as the comments of shadPS4's matcher describe; also not confirmed on hardware
- [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