diff --git a/src/libs/libSaveData.cpp b/src/libs/libSaveData.cpp index f26d78ea8..6f3e038a5 100644 --- a/src/libs/libSaveData.cpp +++ b/src/libs/libSaveData.cpp @@ -615,7 +615,12 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond, Common::LockGuard lock(g_mount_mutex); 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); - std::vector dir_list; + struct Found { + uint64_t key = 0; + std::string name; + auto operator<=>(const Found&) const = default; + }; + std::vector dir_list; if (Common::File::IsDirectoryExisting(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' || dir_name_match(Common::ToLower(entry.name).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) { - return std::strcmp(a.c_str(), b.c_str()) < 0; - }); - - if (cond->order == SaveDataSortOrder::Descent) { - std::vector reversed; - for (size_t i = dir_list.size(); i > 0; i--) { - reversed.push_back(dir_list[i - 1]); + // A save whose metadata cannot be read sorts as 0. + for (auto& found: dir_list) { + const auto directory = root / found.name; + switch (cond->key) { + case SaveDataSortKey::UserParam: { + SaveDataParam param {}; + read_save_param(save_param_path(directory), ¶m); + 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(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 = @@ -651,9 +668,9 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond, for (size_t i = 0; i < max_count; i++) { 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) { - 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) { return status; } @@ -661,7 +678,7 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond, if (result->infos != nullptr) { auto& info = result->infos[i]; 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) { return status; } diff --git a/tests/SaveDataMemoryTests.cpp b/tests/SaveDataMemoryTests.cpp index a39dae7fd..637a59f4d 100644 --- a/tests/SaveDataMemoryTests.cpp +++ b/tests/SaveDataMemoryTests.cpp @@ -660,6 +660,47 @@ void TestClassicSaveParams() { CHECK(params[0].mtime == nested_mtime); } +void TestSearchSortKeys() { + Reset("SORTED"); + const std::array names {DirName("alpha"), DirName("bravo"), DirName("charlie")}; + constexpr std::array blocks {96, 48, 48}; + constexpr std::array user_params {2, 3, 1}; + constexpr std::array 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 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) { #ifdef _WIN32 CHECK(_spawnl(_P_WAIT, executable.string().c_str(), executable.string().c_str(), mode, @@ -706,6 +747,7 @@ int main(int argc, char** argv) { TestGuestSaveIsolation(); TestSaveAllocations(); TestClassicSaveParams(); + TestSearchSortKeys(); CHECK(SaveDataTerminate() == OK); fs::current_path(previous); fs::remove_all(temp);