From bae2442d0a6f358a99d10a90661d8825edccf6da Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Mon, 19 May 2025 00:01:18 +0200 Subject: [PATCH 1/3] GameList: Allow sorting by more columns namely: Play Time, Last Start, region, TitleID --- src/gui/components/wxGameList.cpp | 74 ++++++++++++++++++++++++++----- src/gui/components/wxGameList.h | 6 +-- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index e418ca0afd..98da34f85c 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -434,25 +434,74 @@ static inline int order_to_int(const std::weak_ordering &wo) return 0; } -int wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData) +std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData) { const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1); const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2); - const auto& name1 = GetNameByTitleId(titleId1); - const auto& name2 = GetNameByTitleId(titleId2); - if(sortData->dir > 0) - return order_to_int(std::tie(isFavoriteB, name1) <=> std::tie(isFavoriteA, name2)); - else - return order_to_int(std::tie(isFavoriteB, name2) <=> std::tie(isFavoriteA, name1)); + auto compareFn = [&, isFavoriteA, isFavoriteB](T a, T b, bool prioritizeFave = false) + { + if(!prioritizeFave) + { + if (sortData->dir > 0) + return a <=> b; + else + return b <=> a; + } + else + { + if (sortData->dir > 0) + return std::tie(isFavoriteB, a) <=> std::tie(isFavoriteA, b); + else + return std::tie(isFavoriteB, b) <=> std::tie(isFavoriteA, a); + } + }; + + auto titlePlayDateSortString = [](uint64_t id) + { + iosu::pdm::GameListStat playTimeStat; + if (!iosu::pdm::GetStatForGamelist(id, playTimeStat)) + return std::string{"00000'00'00"}; + auto& lastPlay = playTimeStat.last_played; + return fmt::format("{:0>5}'{:0>2}'{:0>2}", lastPlay.year, lastPlay.month, lastPlay.day); + }; + + auto titlePlayMinutes = [](uint64_t id) + { + iosu::pdm::GameListStat playTimeStat; + if (!iosu::pdm::GetStatForGamelist(id, playTimeStat)) + return 0u; + return playTimeStat.numMinutesPlayed; + }; + + auto titleRegion = [](uint64_t id) + { + return CafeTitleList::GetGameInfo(id).GetRegion(); + }; + + switch(sortData->column) + { + default: + case ColumnName: + return compareFn(GetNameByTitleId(titleId1), GetNameByTitleId(titleId2), true); + case ColumnGameStarted: + return compareFn(titlePlayDateSortString(titleId1), titlePlayDateSortString(titleId2)); + case ColumnGameTime: + return compareFn(titlePlayMinutes(titleId1), titlePlayMinutes(titleId2)); + case ColumnRegion: + return compareFn(titleRegion(titleId1), titleRegion(titleId2)); + case ColumnTitleID: + return compareFn(titleId1, titleId2); + } + // unreachable + cemu_assert_debug(false); + return std::weak_ordering::less; } int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) { const auto sort_data = (SortData*)sortData; - const int dir = sort_data->dir; - - return sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data); + return order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data)); } void wxGameList::SortEntries(int column) @@ -479,8 +528,9 @@ void wxGameList::SortEntries(int column) case ColumnGameTime: case ColumnGameStarted: case ColumnRegion: + case ColumnTitleID: { - SortData data{ this, column, s_direction }; + SortData data{ this, ItemColumns{column}, s_direction }; SortItems(SortFunction, (wxIntPtr)&data); break; } @@ -1004,7 +1054,7 @@ void wxGameList::OnClose(wxCloseEvent& event) int wxGameList::FindInsertPosition(TitleId titleId) { - SortData data{ this, s_last_column, s_direction }; + SortData data{ this, ItemColumns{s_last_column}, s_direction }; const auto itemCount = GetItemCount(); if (itemCount == 0) return 0; diff --git a/src/gui/components/wxGameList.h b/src/gui/components/wxGameList.h index b285d259f2..74cbc87df9 100644 --- a/src/gui/components/wxGameList.h +++ b/src/gui/components/wxGameList.h @@ -70,7 +70,7 @@ class wxGameList : public wxListCtrl inline static const wxColour kSecondColor{ 0xFDF9F2 }; void UpdateItemColors(sint32 startIndex = 0); - enum ItemColumns + enum ItemColumns : int { ColumnHiddenName = 0, ColumnIcon, @@ -91,12 +91,12 @@ class wxGameList : public wxListCtrl struct SortData { wxGameList* thisptr; - int column; + ItemColumns column; int dir; }; int FindInsertPosition(TitleId titleId); - int SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData); + std::weak_ordering SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData); static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData); wxTimer* m_tooltip_timer; From 15f8f87f5253074e0b5bc9784904c6ccc09520ce Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Sun, 1 Jun 2025 17:05:01 +0200 Subject: [PATCH 2/3] Cleanup, don't use string comparisons for dates. remove verbose lambda sorting function. --- src/gui/components/wxGameList.cpp | 87 +++++++++++++++++++------------ 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 98da34f85c..ee1f7b1231 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -434,36 +434,53 @@ static inline int order_to_int(const std::weak_ordering &wo) return 0; } -std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData) +static bool operator<(const iosu::pdm::GameListStat& a, const iosu::pdm::GameListStat& b) { - const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1); - const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2); + const auto& lastA = a.last_played; + const auto& lastB = b.last_played; - auto compareFn = [&, isFavoriteA, isFavoriteB](T a, T b, bool prioritizeFave = false) - { - if(!prioritizeFave) - { - if (sortData->dir > 0) - return a <=> b; - else - return b <=> a; - } - else - { - if (sortData->dir > 0) - return std::tie(isFavoriteB, a) <=> std::tie(isFavoriteA, b); - else - return std::tie(isFavoriteB, b) <=> std::tie(isFavoriteA, a); - } - }; + if(lastA.year < lastB.year) + return true; + if(lastA.year > lastB.year) + return false; + + // same year + if(lastA.month < lastB.month) + return true; + if(lastA.month > lastB.month) + return false; + + // same year and month + return lastA.day < lastB.day; +} - auto titlePlayDateSortString = [](uint64_t id) +static bool operator==(const iosu::pdm::GameListStat& a, const iosu::pdm::GameListStat& b) +{ + const auto& lastA = a.last_played; + const auto& lastB = b.last_played; + + return lastA.year == lastB.year && + lastA.month == lastB.month && + lastA.day == lastB.day; +} + +static std::weak_ordering operator<=>(const iosu::pdm::GameListStat& a, const iosu::pdm::GameListStat& b) +{ + if (a == b) + return std::weak_ordering::equivalent; + if (a < b) + return std::weak_ordering::less; + else + return std::weak_ordering::greater; +} + +std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData) +{ + auto titleLastPlayed = [](uint64_t id) { - iosu::pdm::GameListStat playTimeStat; - if (!iosu::pdm::GetStatForGamelist(id, playTimeStat)) - return std::string{"00000'00'00"}; - auto& lastPlay = playTimeStat.last_played; - return fmt::format("{:0>5}'{:0>2}'{:0>2}", lastPlay.year, lastPlay.month, lastPlay.day); + iosu::pdm::GameListStat playTimeStat{}; + iosu::pdm::GetStatForGamelist(id, playTimeStat); + return playTimeStat; }; auto titlePlayMinutes = [](uint64_t id) @@ -483,15 +500,21 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, { default: case ColumnName: - return compareFn(GetNameByTitleId(titleId1), GetNameByTitleId(titleId2), true); + { + const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1); + const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2); + const auto nameA = GetNameByTitleId(titleId1); + const auto nameB = GetNameByTitleId(titleId2); + return std::tie(isFavoriteB, nameA) <=> std::tie(isFavoriteA, nameB); + } case ColumnGameStarted: - return compareFn(titlePlayDateSortString(titleId1), titlePlayDateSortString(titleId2)); + return titleLastPlayed(titleId1) <=> titleLastPlayed(titleId2); case ColumnGameTime: - return compareFn(titlePlayMinutes(titleId1), titlePlayMinutes(titleId2)); + return titlePlayMinutes(titleId1) <=> titlePlayMinutes(titleId2); case ColumnRegion: - return compareFn(titleRegion(titleId1), titleRegion(titleId2)); + return titleRegion(titleId1) <=> titleRegion(titleId2); case ColumnTitleID: - return compareFn(titleId1, titleId2); + return titleId1 <=> titleId2; } // unreachable cemu_assert_debug(false); @@ -501,7 +524,7 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) { const auto sort_data = (SortData*)sortData; - return order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data)); + return sort_data->dir * order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data)); } void wxGameList::SortEntries(int column) From b605b359f4f29ad665eecde0f925482ce3f4c9b3 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:29:31 +0200 Subject: [PATCH 3/3] move date comparison code to date class declaration --- src/Cafe/IOSU/PDM/iosu_pdm.cpp | 29 +++++++++++++++++++++ src/Cafe/IOSU/PDM/iosu_pdm.h | 6 ++++- src/gui/components/wxGameList.cpp | 42 +------------------------------ 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.cpp b/src/Cafe/IOSU/PDM/iosu_pdm.cpp index d94b1dbfc3..b9dda445f8 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.cpp +++ b/src/Cafe/IOSU/PDM/iosu_pdm.cpp @@ -464,5 +464,34 @@ namespace iosu return static_cast(&sIOSUModuleNNPDM); } + + bool GameListStat::LastPlayDate::operator<(const LastPlayDate& b) const + { + const auto& a = *this; + + if(a.year < b.year) + return true; + if(a.year > b.year) + return false; + + // same year + if(a.month < b.month) + return true; + if(a.month > b.month) + return false; + + // same year and month + return a.day < b.day; + } + + bool GameListStat::LastPlayDate::operator==(const LastPlayDate& b) const + { + const auto& a = *this; + return a.year == b.year && + a.month == b.month && + a.day == b.day; + } + std::weak_ordering GameListStat::LastPlayDate::operator<=>(const LastPlayDate& b) const = default; + }; }; diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.h b/src/Cafe/IOSU/PDM/iosu_pdm.h index 0dd8a39d2d..63f99a4abd 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.h +++ b/src/Cafe/IOSU/PDM/iosu_pdm.h @@ -21,11 +21,15 @@ namespace iosu /* Helper for UI game list */ struct GameListStat { - struct + struct LastPlayDate { uint32 year; // if 0 -> never played uint32 month; uint32 day; + + bool operator<(const LastPlayDate& b) const; + bool operator==(const LastPlayDate& b) const; + std::weak_ordering operator<=>(const LastPlayDate& b) const; }last_played; uint32 numMinutesPlayed; }; diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index ee1f7b1231..1d5e5a4605 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -434,46 +434,6 @@ static inline int order_to_int(const std::weak_ordering &wo) return 0; } -static bool operator<(const iosu::pdm::GameListStat& a, const iosu::pdm::GameListStat& b) -{ - const auto& lastA = a.last_played; - const auto& lastB = b.last_played; - - if(lastA.year < lastB.year) - return true; - if(lastA.year > lastB.year) - return false; - - // same year - if(lastA.month < lastB.month) - return true; - if(lastA.month > lastB.month) - return false; - - // same year and month - return lastA.day < lastB.day; -} - -static bool operator==(const iosu::pdm::GameListStat& a, const iosu::pdm::GameListStat& b) -{ - const auto& lastA = a.last_played; - const auto& lastB = b.last_played; - - return lastA.year == lastB.year && - lastA.month == lastB.month && - lastA.day == lastB.day; -} - -static std::weak_ordering operator<=>(const iosu::pdm::GameListStat& a, const iosu::pdm::GameListStat& b) -{ - if (a == b) - return std::weak_ordering::equivalent; - if (a < b) - return std::weak_ordering::less; - else - return std::weak_ordering::greater; -} - std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData) { auto titleLastPlayed = [](uint64_t id) @@ -508,7 +468,7 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, return std::tie(isFavoriteB, nameA) <=> std::tie(isFavoriteA, nameB); } case ColumnGameStarted: - return titleLastPlayed(titleId1) <=> titleLastPlayed(titleId2); + return titleLastPlayed(titleId1).last_played <=> titleLastPlayed(titleId2).last_played; case ColumnGameTime: return titlePlayMinutes(titleId1) <=> titlePlayMinutes(titleId2); case ColumnRegion: