// SPDX-License-Identifier: MIT // // ES-DE Frontend // FileSorts.cpp // // Gamelist sorting functions. // Actual sorting takes place in FileData. // #include "FileSorts.h" #include "SystemData.h" #include "utils/LocalizationUtil.h" #include "utils/StringUtil.h" #include #include namespace FileSorts { const FileData::SortType typesArr[] { FileData::SortType(&compareName, "name, ascending"), FileData::SortType(&compareNameDescending, "name, descending"), FileData::SortType(&compareRating, "rating, ascending"), FileData::SortType(&compareRatingDescending, "rating, descending"), FileData::SortType(&compareReleaseDate, "release date, ascending"), FileData::SortType(&compareReleaseDateDescending, "release date, descending"), FileData::SortType(&compareDeveloper, "developer, ascending"), FileData::SortType(&compareDeveloperDescending, "developer, descending"), FileData::SortType(&comparePublisher, "publisher, ascending"), FileData::SortType(&comparePublisherDescending, "publisher, descending"), FileData::SortType(&compareGenre, "genre, ascending"), FileData::SortType(&compareGenreDescending, "genre, descending"), FileData::SortType(&compareNumPlayers, "players, ascending"), FileData::SortType(&compareNumPlayersDescending, "players, descending"), FileData::SortType(&compareLastPlayed, "last played, ascending"), FileData::SortType(&compareLastPlayedDescending, "last played, descending"), FileData::SortType(&compareTimesPlayed, "times played, ascending"), FileData::SortType(&compareTimesPlayedDescending, "times played, descending"), FileData::SortType(&compareSystem, "system, ascending"), FileData::SortType(&compareSystemDescending, "system, descending")}; const std::vector SortTypes {typesArr, typesArr + sizeof(typesArr) / sizeof(typesArr[0])}; bool compareName(const FileData* file1, const FileData* file2) { // We compare the actual metadata name, as collection files have the system // appended which messes up the order. std::string name1; std::string name2; if (file1->getSystem()->isCustomCollection()) { if (Utils::String::toUpper(file1->metadata.get("collectionsortname")) != "") name1 = Utils::String::toUpper(file1->metadata.get("collectionsortname")); else if (Utils::String::toUpper(file1->metadata.get("sortname")) != "") name1 = Utils::String::toUpper(file1->metadata.get("sortname")); else name1 = Utils::String::toUpper(file1->metadata.get("name")); if (Utils::String::toUpper(file2->metadata.get("collectionsortname")) != "") name2 = Utils::String::toUpper(file2->metadata.get("collectionsortname")); else if (Utils::String::toUpper(file2->metadata.get("sortname")) != "") name2 = Utils::String::toUpper(file2->metadata.get("sortname")); else name2 = Utils::String::toUpper(file2->metadata.get("name")); return name1.compare(name2) < 0; } name1 = Utils::String::toUpper(file1->metadata.get("sortname")); name2 = Utils::String::toUpper(file2->metadata.get("sortname")); if (name1.empty()) name1 = Utils::String::toUpper(file1->metadata.get("name")); if (name2.empty()) name2 = Utils::String::toUpper(file2->metadata.get("name")); return name1.compare(name2) < 0; } bool compareNameDescending(const FileData* file1, const FileData* file2) { std::string name1; std::string name2; if (file1->getSystem()->isCustomCollection()) { if (Utils::String::toUpper(file1->metadata.get("collectionsortname")) != "") name1 = Utils::String::toUpper(file1->metadata.get("collectionsortname")); else if (Utils::String::toUpper(file1->metadata.get("sortname")) != "") name1 = Utils::String::toUpper(file1->metadata.get("sortname")); else name1 = Utils::String::toUpper(file1->metadata.get("name")); if (Utils::String::toUpper(file2->metadata.get("collectionsortname")) != "") name2 = Utils::String::toUpper(file2->metadata.get("collectionsortname")); else if (Utils::String::toUpper(file2->metadata.get("sortname")) != "") name2 = Utils::String::toUpper(file2->metadata.get("sortname")); else name2 = Utils::String::toUpper(file2->metadata.get("name")); return name1.compare(name2) > 0; } name1 = Utils::String::toUpper(file1->metadata.get("sortname")); name2 = Utils::String::toUpper(file2->metadata.get("sortname")); if (name1.empty()) name1 = Utils::String::toUpper(file1->metadata.get("name")); if (name2.empty()) name2 = Utils::String::toUpper(file2->metadata.get("name")); return name1.compare(name2) > 0; } bool compareRating(const FileData* file1, const FileData* file2) { return file1->metadata.getFloat("rating") < file2->metadata.getFloat("rating"); } bool compareRatingDescending(const FileData* file1, const FileData* file2) { return file1->metadata.getFloat("rating") > file2->metadata.getFloat("rating"); } bool compareReleaseDate(const FileData* file1, const FileData* file2) { // Since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string // which is a lot faster than the time casts and the time comparisons. return (file1)->metadata.get("releasedate") < (file2)->metadata.get("releasedate"); } bool compareReleaseDateDescending(const FileData* file1, const FileData* file2) { return (file1)->metadata.get("releasedate") > (file2)->metadata.get("releasedate"); } bool compareDeveloper(const FileData* file1, const FileData* file2) { std::string developer1 = Utils::String::toUpper(file1->metadata.get("developer")); std::string developer2 = Utils::String::toUpper(file2->metadata.get("developer")); return developer1.compare(developer2) < 0; } bool compareDeveloperDescending(const FileData* file1, const FileData* file2) { std::string developer1 = Utils::String::toUpper(file1->metadata.get("developer")); std::string developer2 = Utils::String::toUpper(file2->metadata.get("developer")); return developer1.compare(developer2) > 0; } bool comparePublisher(const FileData* file1, const FileData* file2) { std::string publisher1 = Utils::String::toUpper(file1->metadata.get("publisher")); std::string publisher2 = Utils::String::toUpper(file2->metadata.get("publisher")); return publisher1.compare(publisher2) < 0; } bool comparePublisherDescending(const FileData* file1, const FileData* file2) { std::string publisher1 = Utils::String::toUpper(file1->metadata.get("publisher")); std::string publisher2 = Utils::String::toUpper(file2->metadata.get("publisher")); return publisher1.compare(publisher2) > 0; } bool compareGenre(const FileData* file1, const FileData* file2) { std::string genre1 = Utils::String::toUpper(file1->metadata.get("genre")); std::string genre2 = Utils::String::toUpper(file2->metadata.get("genre")); return genre1.compare(genre2) < 0; } bool compareGenreDescending(const FileData* file1, const FileData* file2) { std::string genre1 = Utils::String::toUpper(file1->metadata.get("genre")); std::string genre2 = Utils::String::toUpper(file2->metadata.get("genre")); return genre1.compare(genre2) > 0; } bool compareNumPlayers(const FileData* file1, const FileData* file2) { std::string file1Players = (file1)->metadata.get("players"); std::string file2Players = (file2)->metadata.get("players"); unsigned int file1Int = 0; unsigned int file2Int = 0; size_t dashPos; // If there is a range of players such as '1-4' then capture the number after the dash. dashPos = file1Players.find("-"); if (dashPos != std::string::npos) file1Players = file1Players.substr(dashPos + 1, file1Players.size() - dashPos - 1); dashPos = file2Players.find("-"); if (dashPos != std::string::npos) file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1); // Any non-numeric value will end up as zero. if (!file1Players.empty() && std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) { file1Int = stoi(file1Players); } if (!file2Players.empty() && std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) { file2Int = stoi(file2Players); } return file1Int < file2Int; } bool compareNumPlayersDescending(const FileData* file1, const FileData* file2) { std::string file1Players = (file1)->metadata.get("players"); std::string file2Players = (file2)->metadata.get("players"); unsigned int file1Int = 0; unsigned int file2Int = 0; size_t dashPos; dashPos = file1Players.find("-"); if (dashPos != std::string::npos) file1Players = file1Players.substr(dashPos + 1, file1Players.size() - dashPos - 1); dashPos = file2Players.find("-"); if (dashPos != std::string::npos) file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1); if (!file1Players.empty() && std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) { file1Int = stoi(file1Players); } if (!file2Players.empty() && std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) { file2Int = stoi(file2Players); } return file1Int > file2Int; } bool compareLastPlayed(const FileData* file1, const FileData* file2) { // Since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string // which is a lot faster than the time casts and the time comparisons. return (file1)->metadata.get("lastplayed") > (file2)->metadata.get("lastplayed"); } bool compareLastPlayedDescending(const FileData* file1, const FileData* file2) { return (file1)->metadata.get("lastplayed") < (file2)->metadata.get("lastplayed"); } bool compareTimesPlayed(const FileData* file1, const FileData* file2) { // Only games have playcount metadata. if (file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) { return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); } return false; } bool compareTimesPlayedDescending(const FileData* file1, const FileData* file2) { if (file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) { return (file1)->metadata.getInt("playcount") > (file2)->metadata.getInt("playcount"); } return false; } bool compareSystem(const FileData* file1, const FileData* file2) { std::string system1 = Utils::String::toUpper(file1->getSystemName()); std::string system2 = Utils::String::toUpper(file2->getSystemName()); return system1.compare(system2) < 0; } bool compareSystemDescending(const FileData* file1, const FileData* file2) { std::string system1 = Utils::String::toUpper(file1->getSystemName()); std::string system2 = Utils::String::toUpper(file2->getSystemName()); return system1.compare(system2) > 0; } #if defined(GETTEXT_DUMMY_ENTRIES) void gettextMessageCatalogEntries() { _("name, ascending"); _("name, descending"); _("rating, ascending"); _("rating, descending"); _("release date, ascending"); _("release date, descending"); _("developer, ascending"); _("developer, descending"); _("publisher, ascending"); _("publisher, descending"); _("genre, ascending"); _("genre, descending"); _("players, ascending"); _("players, descending"); _("last played, ascending"); _("last played, descending"); _("times played, ascending"); _("times played, descending"); _("system, ascending"); _("system, descending"); } #endif } // namespace FileSorts