savedata: sort directory searches by the requested key

This commit is contained in:
nmzik
2026-10-27 18:08:34 +01:00
parent ffc81377b2
commit 4b0282e343
2 changed files with 73 additions and 14 deletions
+31 -14
View File
@@ -615,7 +615,12 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
Common::LockGuard lock(g_mount_mutex); Common::LockGuard lock(g_mount_mutex);
const std::string title_id = cond->title_id != nullptr ? cond->title_id->data : get_title_id(); const std::string title_id = cond->title_id != nullptr ? cond->title_id->data : get_title_id();
const auto root = save_user_directory(title_id, cond->user_id); const auto root = save_user_directory(title_id, cond->user_id);
std::vector<std::string> dir_list; struct Found {
uint64_t key = 0;
std::string name;
auto operator<=>(const Found&) const = default;
};
std::vector<Found> dir_list;
if (Common::File::IsDirectoryExisting(root)) { if (Common::File::IsDirectoryExisting(root)) {
for (const auto& entry: Common::File::GetDirEntries(root)) { for (const auto& entry: Common::File::GetDirEntries(root)) {
@@ -625,22 +630,34 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
if (cond->dir_name == nullptr || cond->dir_name->data[0] == '\0' || if (cond->dir_name == nullptr || cond->dir_name->data[0] == '\0' ||
dir_name_match(Common::ToLower(entry.name).c_str(), dir_name_match(Common::ToLower(entry.name).c_str(),
Common::ToLower(std::string(cond->dir_name->data)).c_str())) { Common::ToLower(std::string(cond->dir_name->data)).c_str())) {
dir_list.push_back(entry.name); dir_list.push_back({.name = entry.name});
} }
} }
} }
} }
std::sort(dir_list.begin(), dir_list.end(), [](const std::string& a, const std::string& b) { // A save whose metadata cannot be read sorts as 0.
return std::strcmp(a.c_str(), b.c_str()) < 0; for (auto& found: dir_list) {
}); const auto directory = root / found.name;
switch (cond->key) {
if (cond->order == SaveDataSortOrder::Descent) { case SaveDataSortKey::UserParam: {
std::vector<std::string> reversed; SaveDataParam param {};
for (size_t i = dir_list.size(); i > 0; i--) { read_save_param(save_param_path(directory), &param);
reversed.push_back(dir_list[i - 1]); found.key = param.user_param;
break;
}
case SaveDataSortKey::Blocks:
case SaveDataSortKey::FreeBlocks: read_save_blocks(directory, &found.key); break;
case SaveDataSortKey::Mtime:
found.key = static_cast<uint64_t>(newest_save_time(directory));
break;
default: break;
} }
dir_list = reversed; }
// Directory names break ties; descending order reverses both.
std::sort(dir_list.begin(), dir_list.end());
if (cond->order == SaveDataSortOrder::Descent) {
std::reverse(dir_list.begin(), dir_list.end());
} }
auto max_count = auto max_count =
@@ -651,9 +668,9 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
for (size_t i = 0; i < max_count; i++) { for (size_t i = 0; i < max_count; i++) {
std::snprintf(result->dir_names[i].data, sizeof(result->dir_names[i].data), "%s", std::snprintf(result->dir_names[i].data, sizeof(result->dir_names[i].data), "%s",
dir_list[i].c_str()); dir_list[i].name.c_str());
if (result->params != nullptr) { if (result->params != nullptr) {
const int status = load_save_param(root / dir_list[i], &result->params[i]); const int status = load_save_param(root / dir_list[i].name, &result->params[i]);
if (status != OK) { if (status != OK) {
return status; return status;
} }
@@ -661,7 +678,7 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
if (result->infos != nullptr) { if (result->infos != nullptr) {
auto& info = result->infos[i]; auto& info = result->infos[i];
info = {}; info = {};
const int status = read_save_blocks(root / dir_list[i], &info.blocks); const int status = read_save_blocks(root / dir_list[i].name, &info.blocks);
if (status != OK) { if (status != OK) {
return status; return status;
} }
+42
View File
@@ -660,6 +660,47 @@ void TestClassicSaveParams() {
CHECK(params[0].mtime == nested_mtime); CHECK(params[0].mtime == nested_mtime);
} }
void TestSearchSortKeys() {
Reset("SORTED");
const std::array names {DirName("alpha"), DirName("bravo"), DirName("charlie")};
constexpr std::array<uint64_t, 3> blocks {96, 48, 48};
constexpr std::array<uint32_t, 3> user_params {2, 3, 1};
constexpr std::array<int, 3> hours_ahead {1, 3, 2};
for (size_t i = 0; i < names.size(); i++) {
struct SaveDataMount3 mount {};
mount.user_id = 1;
mount.dir_name = &names[i];
mount.mount_mode = 4;
mount.blocks = blocks[i];
SaveDataMountResult result {};
CHECK(SaveDataMount3(&mount, &result) == OK);
CHECK(SaveDataSetParam(&result.mount_point, 4, &user_params[i], sizeof(user_params[i])) ==
OK);
CHECK(SaveDataUmount2(0, &result.mount_point) == OK);
fs::last_write_time(fs::path("_SaveData/SORTED") / names[i].data / "sce_sys/param.bin",
fs::file_time_type::clock::now() + std::chrono::hours(hours_ahead[i]));
}
const auto search = [](SaveDataSortKey key, SaveDataSortOrder order) {
std::array<SceSaveDataDirName, 3> found {};
SaveDataDirNameSearchCond cond {};
cond.user_id = 1;
cond.key = key;
cond.order = order;
SaveDataDirNameSearchResult result {};
result.dir_names = found.data();
result.dir_names_num = found.size();
CHECK(SaveDataDirNameSearch(&cond, &result) == OK && result.set_num == 3);
return std::string(found[0].data) + " " + found[1].data + " " + found[2].data;
};
CHECK(search(SaveDataSortKey::DirName, SaveDataSortOrder::Descent) == "charlie bravo alpha");
CHECK(search(SaveDataSortKey::UserParam, SaveDataSortOrder::Ascent) == "charlie alpha bravo");
CHECK(search(SaveDataSortKey::Blocks, SaveDataSortOrder::Ascent) == "bravo charlie alpha");
CHECK(search(SaveDataSortKey::FreeBlocks, SaveDataSortOrder::Descent) == "alpha charlie bravo");
CHECK(search(SaveDataSortKey::Mtime, SaveDataSortOrder::Descent) == "bravo charlie alpha");
CHECK(fs::remove("_SaveData/SORTED/alpha/sce_sys/blocks.bin"));
CHECK(search(SaveDataSortKey::Blocks, SaveDataSortOrder::Ascent) == "alpha bravo charlie");
}
void RunChild(const fs::path& executable, const char* mode) { void RunChild(const fs::path& executable, const char* mode) {
#ifdef _WIN32 #ifdef _WIN32
CHECK(_spawnl(_P_WAIT, executable.string().c_str(), executable.string().c_str(), mode, CHECK(_spawnl(_P_WAIT, executable.string().c_str(), executable.string().c_str(), mode,
@@ -706,6 +747,7 @@ int main(int argc, char** argv) {
TestGuestSaveIsolation(); TestGuestSaveIsolation();
TestSaveAllocations(); TestSaveAllocations();
TestClassicSaveParams(); TestClassicSaveParams();
TestSearchSortKeys();
CHECK(SaveDataTerminate() == OK); CHECK(SaveDataTerminate() == OK);
fs::current_path(previous); fs::current_path(previous);
fs::remove_all(temp); fs::remove_all(temp);