From 3e879fdfba860931c2b42b73097184e4d0bd364d Mon Sep 17 00:00:00 2001 From: gugueU Date: Thu, 8 Jun 2023 10:23:09 +0200 Subject: [PATCH] feat(frontend): add other version or same licence search --- RELEASE-NOTES.md | 12 +- .../frontend/es-app/src/games/FileData.cpp | 55 ++++++- projects/frontend/es-app/src/games/FileData.h | 17 +- .../frontend/es-app/src/games/FileSorts.cpp | 2 +- .../frontend/es-app/src/games/FolderData.cpp | 78 ++------- .../frontend/es-app/src/games/FolderData.h | 49 +----- .../es-app/src/games/MetadataDescriptor.cpp | 5 + .../es-app/src/games/MetadataDescriptor.h | 58 +++++-- .../es-app/src/games/MetadataStringHolder.h | 2 + .../frontend/es-app/src/guis/GuiSearch.cpp | 148 +++++++++++++----- projects/frontend/es-app/src/guis/GuiSearch.h | 8 +- .../frontend/es-app/src/guis/MenuMessages.h | 6 +- .../es-app/src/guis/SearchForceOptions.h | 24 +++ .../es-app/src/guis/menus/GuiCheckMenu.cpp | 19 ++- .../es-app/src/guis/menus/GuiCheckMenu.h | 6 + .../src/guis/menus/GuiMenuGamelistOptions.cpp | 52 ++++++ .../src/guis/menus/GuiMenuGamelistOptions.h | 4 + .../src/guis/menus/GuiMenuUserInterface.cpp | 40 ++++- .../src/guis/menus/GuiMenuUserInterface.h | 9 +- .../screenscraper/ScreenScraperApis.cpp | 15 +- .../screenscraper/ScreenScraperApis.h | 5 + .../ScreenScraperSingleEngine.cpp | 11 ++ .../es-app/src/systems/SystemData.cpp | 17 +- .../frontend/es-app/src/systems/SystemData.h | 8 - .../es-app/src/systems/SystemManager.cpp | 96 ++++++++++-- .../es-app/src/systems/SystemManager.h | 5 +- .../usernotifications/NotificationManager.cpp | 3 +- .../src/views/gamelist/BasicGameListView.cpp | 16 +- .../views/gamelist/ISimpleGameListView.cpp | 2 +- .../frontend/es-core/src/RecalboxConf.cpp | 25 +++ projects/frontend/es-core/src/RecalboxConf.h | 21 ++- .../src/components/GameClipContainer.cpp | 4 +- .../es-core/src/components/MenuComponent.cpp | 7 + .../es-core/src/components/MenuComponent.h | 1 + 34 files changed, 602 insertions(+), 228 deletions(-) create mode 100644 projects/frontend/es-app/src/guis/SearchForceOptions.h diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index b78f651924..3eea5eaf14 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -22,8 +22,18 @@ as release notes for end user on a Recalbox upgrade. - Add direct accesses to most usefull recalbox folder in samba (`\\RECALBOX\...`) - Add 240p test suite support for all platforms - Add libretro-mojozork as Z-Machine core -- Add libretro-supafaust core for Snes +- Add libretro-supafaust core for snes +<<<<<<< HEAD - Add support for break key for pad2keyboard +<<<<<<< HEAD +- Add new system Arduboy (libretro arduous) +- Add libretro-wasm4 for new system Wasm4 +- Add launching a game from savestate slot +======= +======= +- Add new searchs (other versions or with same licence) +>>>>>>> 60f1730d34... feat(frontend): add other version or same licence search +>>>>>>> 3c2b56d63f... feat(frontend): add other version or same licence search ### Bumps - Bump FBneo to aad581 diff --git a/projects/frontend/es-app/src/games/FileData.cpp b/projects/frontend/es-app/src/games/FileData.cpp index 188c92d515..281aaafe41 100644 --- a/projects/frontend/es-app/src/games/FileData.cpp +++ b/projects/frontend/es-app/src/games/FileData.cpp @@ -25,6 +25,37 @@ std::string FileData::DisplayName(const Path& romPath) const return adapter.DisplayName(); } +std::string FileData::DisplayableName() const +{ + RecalboxConf& recalboxConf = RecalboxConf::Instance(); + + std::string displayableName = Name(); + DisplayGameBy displayGameBy = recalboxConf.GetDisplayGameBy(); + + switch (displayGameBy) + { + case DisplayGameBy::Filename : displayableName = RomPath().Filename(); break; + case DisplayGameBy::Alias : + if (!Metadata().Alias().empty()) + displayableName = Metadata().Alias(); + break; + case DisplayGameBy::Name : break; + } + + std::string diskNumber = ExtractGameSupportNumber(RomPath().Filename()); + + // add region tag or disk number only if we are not in filename case + if (DisplayGameBy::Filename != displayGameBy) + { + if (!diskNumber.empty()) + displayableName.append(" (").append(diskNumber).append(")"); + + if (recalboxConf.GetDisplayGameRegions() && !Regions().empty()) + displayableName.append(" [").append(Regions()).append("]"); + } + return displayableName; +} + bool FileData::HasP2K() const { // Check game file @@ -73,7 +104,7 @@ FileData& FileData::CalculateHash() return *this; } -std::string FileData::Regions() +std::string FileData::Regions() const { std::string fileName = mMetadata.RomFileOnly().ToString(); Regions::RegionPack regions = Regions::ExtractRegionsFromNoIntroName(fileName); @@ -115,4 +146,26 @@ bool FileData::IsDisplayable() const return false; return true; +} + +std::string FileData::ExtractGameSupportNumber(const std::string& filename) +{ + for(int end = 0;;) + { + int begin = (int)filename.find('(', end); + if (begin == (int)std::string::npos) break; + end = (int)filename.find(')', begin); + if (end == (int)std::string::npos) break; + + // begin + 5 = Disk[space] + std::string tag = Strings::ToLowerASCII(filename.substr(begin +1, end - begin - 1)); + if (tag.empty()) + break; + + if (Strings::Contains(tag, "disk ") || Strings::Contains(tag, "disc ")) + return tag; + + } + return Strings::Empty; + } \ No newline at end of file diff --git a/projects/frontend/es-app/src/games/FileData.h b/projects/frontend/es-app/src/games/FileData.h index 4f93e9bc71..8d78b1227b 100644 --- a/projects/frontend/es-app/src/games/FileData.h +++ b/projects/frontend/es-app/src/games/FileData.h @@ -7,6 +7,7 @@ #include "MetadataDescriptor.h" #include "ItemType.h" #include "utils/cplusplus/Bitflags.h" +#include "utils/storage/Set.h" // Forward declarations class SystemData; @@ -19,6 +20,7 @@ class FileData public: typedef HashMap StringMap; typedef std::vector List; + typedef HashSet Set; typedef std::vector ConstList; typedef int (*Comparer)(const FileData& a, const FileData& b); @@ -36,6 +38,13 @@ class FileData All = 127, //!< Include all }; + enum class DisplayGameBy + { + Name = 0, + Filename = 1, + Alias = 2, + }; + //! Search attribute enumeration enum class SearchAttributes { @@ -160,7 +169,7 @@ class FileData * @brief Get region string * @return */ - std::string Regions(); + std::string Regions() const; /*! * @brief Check if file data can be displayable @@ -198,6 +207,12 @@ class FileData * @return True if rom path are equal, false otherwise */ bool AreRomEqual(const FileData& other) { return mMetadata.AreRomEqual(other.mMetadata); } + + std::string DisplayableName() const; + + static std::string ExtractGameSupportNumber(const std::string& filename); + + }; DEFINE_BITFLAG_ENUM(FileData::Filter, int) diff --git a/projects/frontend/es-app/src/games/FileSorts.cpp b/projects/frontend/es-app/src/games/FileSorts.cpp index 7444a7e46e..edbbea5a6d 100644 --- a/projects/frontend/es-app/src/games/FileSorts.cpp +++ b/projects/frontend/es-app/src/games/FileSorts.cpp @@ -69,7 +69,7 @@ ImplementSortMethod(compareSystemName) ImplementSortMethod(compareFileName) { CheckFoldersAndGames(file1, file2) - return unicodeCompareUppercase(file1.Name(), file2.Name()); + return unicodeCompareUppercase(file1.DisplayableName(), file2.DisplayableName()); } ImplementSortMethod(compareRating) diff --git a/projects/frontend/es-app/src/games/FolderData.cpp b/projects/frontend/es-app/src/games/FolderData.cpp index cd4fe3922b..d9e716701f 100644 --- a/projects/frontend/es-app/src/games/FolderData.cpp +++ b/projects/frontend/es-app/src/games/FolderData.cpp @@ -952,95 +952,51 @@ int FolderData::GetItemsTo(FileData::List& to, FileData::Filter includes, FileDa return getItems(to, includes, excludes, includefolders); } -void FolderData::LookupGamesFromPath(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const -{ - for(FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->LookupGamesFromPath(index, games); - else if (game->Metadata().IsMatchingFileIndex(index.Index)) - games.push_back(game); -} - -void FolderData::LookupGamesFromName(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const -{ - for(FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->LookupGamesFromName(index, games); - else if (game->Metadata().IsMatchingNameIndex(index.Index)) - games.push_back(game); -} - -void FolderData::LookupGamesFromDescription(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const -{ - for(FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->LookupGamesFromDescription(index, games); - else if (game->Metadata().IsMatchingDescriptionIndex(index.Index)) - games.push_back(game); -} - -void FolderData::LookupGamesFromDeveloper(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const -{ - for(FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->LookupGamesFromDeveloper(index, games); - else if (game->Metadata().IsMatchingDeveloperIndex(index.Index)) - games.push_back(game); -} - -void -FolderData::LookupGamesFromPublisher(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const +void FolderData::BuildFastSearchSeriesPath(FolderData::FastSearchItemSerie& into) const { - for(FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->LookupGamesFromPublisher(index, games); - else if (game->Metadata().IsMatchingPublisherIndex(index.Index)) - games.push_back(game); + for(const FileData* game : mChildren) + if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesPath(into); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().FileIndex()); } -void FolderData::LookupGamesFromAll(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const +void FolderData::BuildFastSearchSeriesName(FolderData::FastSearchItemSerie& into) const { - for(FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->LookupGamesFromAll(index, games); - else - switch((FastSearchContext)index.Context) - { - case FastSearchContext::Path: if (game->Metadata().IsMatchingFileIndex(index.Index)) games.push_back(game); break; - case FastSearchContext::Name: if (game->Metadata().IsMatchingNameIndex(index.Index)) games.push_back(game); break; - case FastSearchContext::Description: if (game->Metadata().IsMatchingDescriptionIndex(index.Index)) games.push_back(game); break; - case FastSearchContext::Developer: if (game->Metadata().IsMatchingDeveloperIndex(index.Index)) games.push_back(game); break; - case FastSearchContext::Publisher: if (game->Metadata().IsMatchingPublisherIndex(index.Index)) games.push_back(game); break; - case FastSearchContext::All: - default: break; - } + for(const FileData* game : mChildren) + if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesName(into); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().NameIndex()); } -void FolderData::BuildFastSearchSeriesPath(FolderData::FastSearchItemSerie& into) const +void FolderData::BuildFastSearchSeriesAlias(FolderData::FastSearchItemSerie& into) const { for(const FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesPath(into); - else into.Set(game, game->Metadata().FileIndex()); + if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesAlias(into); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().AliasIndex()); } -void FolderData::BuildFastSearchSeriesName(FolderData::FastSearchItemSerie& into) const +void FolderData::BuildFastSearchSeriesFamily(FolderData::FastSearchItemSerie& into) const { for(const FileData* game : mChildren) - if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesName(into); - else into.Set(game, game->Metadata().NameIndex()); + if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesFamily(into); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().FamiliesIndex()); } void FolderData::BuildFastSearchSeriesDescription(FolderData::FastSearchItemSerie& into) const { for(const FileData* game : mChildren) if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesDescription(into); - else into.Set(game, game->Metadata().DescriptionIndex()); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().DescriptionIndex()); } void FolderData::BuildFastSearchSeriesDeveloper(FolderData::FastSearchItemSerie& into) const { for(const FileData* game : mChildren) if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesDeveloper(into); - else into.Set(game, game->Metadata().DeveloperIndex()); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().DeveloperIndex()); } void FolderData::BuildFastSearchSeriesPublisher(FolderData::FastSearchItemSerie& into) const { for(const FileData* game : mChildren) if (game->IsFolder()) CastFolder(game)->BuildFastSearchSeriesPublisher(into); - else into.Set(game, game->Metadata().PublisherIndex()); + else if (game->IsDisplayable()) into.Set(game, game->Metadata().PublisherIndex()); } diff --git a/projects/frontend/es-app/src/games/FolderData.h b/projects/frontend/es-app/src/games/FolderData.h index 274723ea8e..bcfe72bf2e 100644 --- a/projects/frontend/es-app/src/games/FolderData.h +++ b/projects/frontend/es-app/src/games/FolderData.h @@ -143,6 +143,8 @@ class FolderData : public FileData { Path, Name, + Alias, + Family, Description, Developer, Publisher, @@ -231,7 +233,8 @@ class FolderData : public FileData FastSearchItem* Next(FastSearchItem* item) { - if (item->Next >= 0) return &mItems(item->Next); + if (item->Next >= 0) + return &mItems(item->Next); return nullptr; } @@ -535,51 +538,11 @@ class FolderData : public FileData * @return file data filtered state */ bool IsFiltered(FileData* fd, Filter includes, Filter excludes) const; - - /*! - * @brief Lookup games whose path index matches one of the given indexes - * @param index Index to seek into - * @param games Output list - */ - void LookupGamesFromPath(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; - - /*! - * @brief Lookup games whose name index matches one of the given indexes - * @param index Index to seek into - * @param games Output list - */ - void LookupGamesFromName(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; - - /*! - * @brief Lookup games whose description index matches one of the given indexes - * @param index Index to seek into - * @param games Output list - */ - void LookupGamesFromDescription(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; - - /*! - * @brief Lookup games whose developer index matches one of the given indexes - * @param index Index to seek into - * @param games Output list - */ - void LookupGamesFromDeveloper(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; - - /*! - * @brief Lookup games whose publisher index matches one of the given indexes - * @param index Index to seek into - * @param games Output list - */ - void LookupGamesFromPublisher(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; - - /*! - * @brief Lookup games whose any index matches one of the given indexes - * @param index Index to seek into - * @param games Output list - */ - void LookupGamesFromAll(const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; void BuildFastSearchSeriesPath(FastSearchItemSerie& into) const; void BuildFastSearchSeriesName(FastSearchItemSerie& into) const; + void BuildFastSearchSeriesAlias(FastSearchItemSerie& into) const; + void BuildFastSearchSeriesFamily(FastSearchItemSerie& into) const; void BuildFastSearchSeriesDescription(FastSearchItemSerie& into) const; void BuildFastSearchSeriesDeveloper(FastSearchItemSerie& into) const; void BuildFastSearchSeriesPublisher(FastSearchItemSerie& into) const; diff --git a/projects/frontend/es-app/src/games/MetadataDescriptor.cpp b/projects/frontend/es-app/src/games/MetadataDescriptor.cpp index 24defa1f61..e2957fb8f5 100644 --- a/projects/frontend/es-app/src/games/MetadataDescriptor.cpp +++ b/projects/frontend/es-app/src/games/MetadataDescriptor.cpp @@ -7,6 +7,7 @@ const std::string MetadataDescriptor::GameNodeIdentifier("game"); const std::string MetadataDescriptor::FolderNodeIdentifier("folder"); MetadataStringHolder MetadataDescriptor::sNameHolder(1 << 20, 128 << 10); +MetadataStringHolder MetadataDescriptor::sAliasHolder(1 << 20, 128 << 10); MetadataStringHolder MetadataDescriptor::sDescriptionHolder(1 << 20, 128 << 10); MetadataStringHolder MetadataDescriptor::sDeveloperHolder(64 << 10, 32 << 10); MetadataStringHolder MetadataDescriptor::sPublisherHolder(64 << 10, 32 << 10); @@ -35,6 +36,8 @@ const MetadataFieldDescriptor* MetadataDescriptor::GetMetadataFieldDescriptors(I { MetadataFieldDescriptor("path" , "" , _("Path") , _("enter game path") , MetadataFieldDescriptor::DataType::Path , MetadataFieldDescriptor::EditableType::None , &MetadataDescriptor::IsDefaultRom , &MetadataDescriptor::RomAsString , &MetadataDescriptor::SetRomPathAsString , false, true), MetadataFieldDescriptor("name" , "" , _("Name") , _("enter game name") , MetadataFieldDescriptor::DataType::String , MetadataFieldDescriptor::EditableType::Text , &MetadataDescriptor::IsDefaultName , &MetadataDescriptor::NameAsString , &MetadataDescriptor::SetName , false, true), + MetadataFieldDescriptor("alias" , "" , _("Alias") , _("enter game alias") , MetadataFieldDescriptor::DataType::String , MetadataFieldDescriptor::EditableType::Text , &MetadataDescriptor::IsDefaultAlias , &MetadataDescriptor::AliasAsString , &MetadataDescriptor::SetAlias , false, true), + MetadataFieldDescriptor("families" , "" , _("Families") , _("enter game families") , MetadataFieldDescriptor::DataType::String , MetadataFieldDescriptor::EditableType::Text , &MetadataDescriptor::IsDefaultFamilies , &MetadataDescriptor::FamiliesAsString , &MetadataDescriptor::SetFamilies , false, true), MetadataFieldDescriptor("rating" , "0.0" , _("Rating") , _("enter rating") , MetadataFieldDescriptor::DataType::Rating , MetadataFieldDescriptor::EditableType::Rating , &MetadataDescriptor::IsDefaultRating , &MetadataDescriptor::RatingAsString , &MetadataDescriptor::SetRatingAsString , false, true), MetadataFieldDescriptor("favorite" , "false", _("Favorite") , _("enter favorite") , MetadataFieldDescriptor::DataType::Bool , MetadataFieldDescriptor::EditableType::Switch , &MetadataDescriptor::IsDefaultFavorite , &MetadataDescriptor::FavoriteAsString , &MetadataDescriptor::SetFavoriteAsString , false, true), MetadataFieldDescriptor("hidden" , "false", _("Hidden") , _("set hidden") , MetadataFieldDescriptor::DataType::Bool , MetadataFieldDescriptor::EditableType::Switch , &MetadataDescriptor::IsDefaultHidden , &MetadataDescriptor::HiddenAsString , &MetadataDescriptor::SetHiddenAsString , false, true), @@ -371,6 +374,7 @@ void MetadataDescriptor::Merge(const MetadataDescriptor& sourceMetadata) void MetadataDescriptor::CleanupHolders() { LOG(LogDebug) << "[MetadataDescriptor] Name storage: " << sNameHolder.StorageSize() << " - object count: " << sNameHolder.ObjectCount() ; + LOG(LogDebug) << "[MetadataDescriptor] Name storage: " << sAliasHolder.StorageSize() << " - object count: " << sAliasHolder.ObjectCount() ; LOG(LogDebug) << "[MetadataDescriptor] Description storage: " << sDescriptionHolder.StorageSize() << " - object count: " << sDescriptionHolder.ObjectCount(); LOG(LogDebug) << "[MetadataDescriptor] Publisher storage: " << sPublisherHolder.StorageSize() << " - object count: " << sPublisherHolder.ObjectCount() ; LOG(LogDebug) << "[MetadataDescriptor] Developer storage: " << sDeveloperHolder.StorageSize() << " - object count: " << sDeveloperHolder.ObjectCount() ; @@ -383,6 +387,7 @@ void MetadataDescriptor::CleanupHolders() LOG(LogDebug) << "[MetadataDescriptor] Patch Path storage: " << sLastPatchPathHolder.StorageSize() << " - object count: " << sLastPatchPathHolder.ObjectCount() ; LOG(LogDebug) << "[MetadataDescriptor] Patcn File storage: " << sLastPatchFileHolder.StorageSize() << " - object count: " << sLastPatchFileHolder.ObjectCount() ; sNameHolder.Finalize(); + sAliasHolder.Finalize(); sDescriptionHolder.Finalize(); sPublisherHolder.Finalize(); sDeveloperHolder.Finalize(); diff --git a/projects/frontend/es-app/src/games/MetadataDescriptor.h b/projects/frontend/es-app/src/games/MetadataDescriptor.h index f6423648e7..49ca84fb58 100755 --- a/projects/frontend/es-app/src/games/MetadataDescriptor.h +++ b/projects/frontend/es-app/src/games/MetadataDescriptor.h @@ -4,10 +4,12 @@ #include #include #include +#include #include "ItemType.h" #include "games/classifications/Genres.h" #include "MetadataStringHolder.h" #include "hardware/RotationType.h" +#include "utils/String.h" //#define _METADATA_STATS_ @@ -30,6 +32,9 @@ class MetadataDescriptor //! Game name string holder static MetadataStringHolder sNameHolder; + + //! Game name string holder + static MetadataStringHolder sAliasHolder; //! Description string holder static MetadataStringHolder sDescriptionHolder; //! Developer string holder @@ -57,6 +62,8 @@ class MetadataDescriptor unsigned int mTimeStamp; //!< Scraping timestamp MetadataStringHolder::Index32 mRomFile; //!< Rom file MetadataStringHolder::Index32 mName; //!< Name as simple string + MetadataStringHolder::Index32 mAlias; //!< Alias as simple string + MetadataStringHolder::Index32 mFamilies; //!< Families lit as simple string MetadataStringHolder::Index32 mDescription; //!< Description, multiline text MetadataStringHolder::Index32 mImageFile; //!< Image file MetadataStringHolder::Index32 mThumbnailFile;//!< Thumbnail file @@ -177,6 +184,8 @@ class MetadataDescriptor : mTimeStamp(0) , mRomFile(0) , mName(0) + , mAlias(0) + , mFamilies(0) , mDescription(0) , mImageFile(0) , mThumbnailFile(0) @@ -258,6 +267,8 @@ class MetadataDescriptor mDirty(source.mDirty), mType(source.mType), mRotation(source.mRotation), + mAlias(source.mAlias), + mFamilies(source.mFamilies) { LivingClasses++; if (_Type == ItemType::Game) LivingGames++; @@ -284,6 +295,8 @@ class MetadataDescriptor mRomFile = source.mRomFile ; mRomPath = source.mRomPath ; mName = source.mName ; + mAlias = source.mAlias ; + mFamilies = source.mFamilies ; mDescription = source.mDescription ; mImagePath = source.mImagePath ; mImageFile = source.mImageFile ; @@ -338,6 +351,7 @@ class MetadataDescriptor mRomFile = source.mRomFile ; mRomPath = source.mRomPath ; mName = source.mName ; + mAlias = source.mAlias ; mDescription = source.mDescription ; mImagePath = source.mImagePath ; mImageFile = source.mImageFile ; @@ -368,7 +382,7 @@ class MetadataDescriptor mPreinstalled = source.mPreinstalled ; mNoGame = source.mNoGame ; mType = source.mType ; - mRotation = source.mRotation ; + mFamilies = source.mFamilies ; #ifdef _METADATA_STATS_ if (_Type == ItemType::Game) LivingGames++; @@ -417,6 +431,8 @@ class MetadataDescriptor [[nodiscard]] Path Rom() const { return sPathHolder.GetPath(mRomPath) / sFileHolder.GetString(mRomFile); } [[nodiscard]] Path RomFileOnly() const { return sFileHolder.GetPath(mRomFile); } [[nodiscard]] std::string Name() const { return sNameHolder.GetString(mName); } + [[nodiscard]] std::string Alias() const { return sAliasHolder.GetString(mAlias); } + [[nodiscard]] std::string Families() const { return sNameHolder.GetString(mFamilies); } [[nodiscard]] std::string Description() const { return sDescriptionHolder.GetString(mDescription); } [[nodiscard]] Path Image() const { return sPathHolder.GetPath(mImagePath) / sFileHolder.GetString(mImageFile); } [[nodiscard]] Path Thumbnail() const { return sPathHolder.GetPath(mThumbnailPath) / sFileHolder.GetString(mThumbnailFile); } @@ -462,6 +478,8 @@ class MetadataDescriptor [[nodiscard]] std::string RomAsString() const { return (sPathHolder.GetPath(mRomPath) / sFileHolder.GetString(mRomFile)).ToString(); } [[nodiscard]] std::string NameAsString() const { return sNameHolder.GetString(mName); } + [[nodiscard]] std::string AliasAsString() const { return sAliasHolder.GetString(mAlias); } + [[nodiscard]] std::string FamiliesAsString() const { return sNameHolder.GetString(mFamilies); } [[nodiscard]] std::string EmulatorAsString() const { return sEmulatorHolder.GetString(mEmulator); } [[nodiscard]] std::string CoreAsString() const { return sCoreHolder.GetString(mCore); } [[nodiscard]] std::string RatioAsString() const { return sRatioHolder.GetString(mRatio, "auto"); } @@ -487,6 +505,9 @@ class MetadataDescriptor [[nodiscard]] std::string LastPatchAsString() const { return (sPathHolder.GetPath(mLastPatchPath) / sFileHolder.GetString(mLastPatchFile)).ToString(); } [[nodiscard]] std::string RotationAsString() const { return RotationUtils::StringValue(mRotation); } + [[nodiscard]] Strings::Vector FamiliesAsList() const { + return Strings::Split(sNameHolder.GetString(mFamilies), ','); + } /* * Setters @@ -527,6 +548,8 @@ class MetadataDescriptor void SetRatio(const std::string& ratio) { mRatio = sRatioHolder.AddString8(ratio); mDirty = true; } void SetGenre(const std::string& genre) { mGenre = sGenreHolder.AddString32(genre); mDirty = true; } void SetName(const std::string& name) { mName = sNameHolder.AddString32(name); mDirty = true; } + void SetAlias(const std::string& alias) { mAlias = sAliasHolder.AddString32(alias); mDirty = true; } + void SetFamilies(const std::string& families) { mFamilies = sNameHolder.AddString32(families); mDirty = true; } void SetDescription(const std::string& description) { mDescription = sDescriptionHolder.AddString32(description); mDirty = true; } void SetReleaseDate(const DateTime& releasedate) { mReleaseDate = (int)releasedate.ToEpochTime(); mDirty = true; } void SetDeveloper(const std::string& developer) { mDeveloper = sDeveloperHolder.AddString32(developer); mDirty = true; } @@ -587,12 +610,17 @@ class MetadataDescriptor void SetGenreIdAsString(const std::string& genre) { int g = 0; if (StringToInt(genre, g)) { mGenreId = (GameGenres)g; mDirty = true; } } void SetRegionAsString(const std::string& region) { mRegion = Regions::Deserialize4Regions(region); mDirty = true; } void SetRotationAsString(const std::string& rotation) { mRotation = RotationUtils::FromString(rotation); mDirty = true;} + + void SetFamiliesFromList(const String::List& families) { SetFamilies(String::Join(families, ',')); } + /* * Defaults */ [[nodiscard]] bool IsDefaultRom() const { return Default().mRomFile == mRomFile && Default().mRomPath == mRomPath; } [[nodiscard]] bool IsDefaultName() const { return Default().mName == mName; } + [[nodiscard]] bool IsDefaultAlias() const { return Default().mAlias == mAlias; } + [[nodiscard]] bool IsDefaultFamilies() const { return Default().mFamilies == mFamilies; } [[nodiscard]] bool IsDefaultEmulator() const { return Default().mEmulator == mEmulator; } [[nodiscard]] bool IsDefaultCore() const { return Default().mCore == mCore; } [[nodiscard]] bool IsDefaultRatio() const { return Default().mRatio == mRatio; } @@ -649,26 +677,20 @@ class MetadataDescriptor [[nodiscard]] MetadataStringHolder::Index32 FileIndex() const { return mRomFile; } [[nodiscard]] MetadataStringHolder::Index32 NameIndex() const { return mName; } + [[nodiscard]] MetadataStringHolder::Index32 AliasIndex() const { return mAlias; } + [[nodiscard]] MetadataStringHolder::Index32 FamiliesIndex() const { return mFamilies; } [[nodiscard]] MetadataStringHolder::Index32 DescriptionIndex() const { return mDescription; } [[nodiscard]] MetadataStringHolder::Index32 DeveloperIndex() const { return mDeveloper; } [[nodiscard]] MetadataStringHolder::Index32 PublisherIndex() const { return mPublisher; } static int FileIndexCount() { return sFileHolder.ObjectCount(); } static int NameIndexCount() { return sNameHolder.ObjectCount(); } + static int AliasIndexCount() { return sAliasHolder.ObjectCount(); } + static int FamiliesIndexCount() { return sNameHolder.ObjectCount(); } static int DescriptionIndexCount() { return sDescriptionHolder.ObjectCount(); } static int DeveloperIndexCount() { return sDeveloperHolder.ObjectCount(); } static int PublisherIndexCount() { return sPublisherHolder.ObjectCount(); } - /* - * Search - */ - - [[nodiscard]] bool IsMatchingFileIndex(MetadataStringHolder::Index32 index) const { return mRomFile == index; } - [[nodiscard]] bool IsMatchingNameIndex(MetadataStringHolder::Index32 index) const { return mName == index; } - [[nodiscard]] bool IsMatchingDescriptionIndex(MetadataStringHolder::Index32 index) const { return mDescription == index; } - [[nodiscard]] bool IsMatchingDeveloperIndex(MetadataStringHolder::Index32 index) const { return mDeveloper == index; } - [[nodiscard]] bool IsMatchingPublisherIndex(MetadataStringHolder::Index32 index) const { return mPublisher == index; } - /*! * @brief Search text in game names * @param originaltext Text to search for @@ -676,6 +698,20 @@ class MetadataDescriptor */ static void SearchInNames(const std::string& originaltext, MetadataStringHolder::FoundTextList& output, int context) { return sNameHolder.FindText(originaltext, output, context); } + /*! + * @brief Search text in game aliases + * @param originaltext Text to search for + * @param output Result container + */ + static void SearchInAlias(const std::string& originaltext, MetadataStringHolder::FoundTextList& output, int context) { return sAliasHolder.FindText(originaltext, output, context); } + + /*! + * @brief Search text in game by familly + * @param originaltext Text to search for + * @param output Result container + */ + static void SearchInFamily(const std::string& originaltext, MetadataStringHolder::FoundTextList& output, int context) { return sNameHolder.FindText(originaltext, output, context); } + /*! * @brief Search text in descriptions * @param originaltext Text to search for diff --git a/projects/frontend/es-app/src/games/MetadataStringHolder.h b/projects/frontend/es-app/src/games/MetadataStringHolder.h index 7893ed3b13..a779f6587a 100644 --- a/projects/frontend/es-app/src/games/MetadataStringHolder.h +++ b/projects/frontend/es-app/src/games/MetadataStringHolder.h @@ -114,6 +114,8 @@ class MetadataStringHolder */ void FindText(const std::string& text, FoundTextList& output, int context); + void FindIndex(const std::string& index, FoundTextList& output, int context); + private: //! Synchronizer Mutex mSyncher; diff --git a/projects/frontend/es-app/src/guis/GuiSearch.cpp b/projects/frontend/es-app/src/guis/GuiSearch.cpp index c27f57a311..bacca41b84 100755 --- a/projects/frontend/es-app/src/guis/GuiSearch.cpp +++ b/projects/frontend/es-app/src/guis/GuiSearch.cpp @@ -7,10 +7,9 @@ #include #include #include -#include -#include "components/ScrollableContainer.h" #include "GuiSearch.h" #include "GuiNetPlayHostPasswords.h" +#include "SearchForceOptions.h" #include @@ -19,14 +18,26 @@ #define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + Renderer::Instance().DisplayHeightAsFloat()*0.0437f ) -GuiSearch::GuiSearch(WindowManager& window, SystemManager& systemManager) - : Gui(window), - mSystemManager(systemManager), - mBackground(window, Path(":/frame.png")), - mGrid(window, Vector2i(3, 3)), - mList(nullptr), - mJustOpen(true) +GuiSearch::GuiSearch(WindowManager& window, SystemManager& systemManager, SearchForcedOptions* forcedOptions) + : Gui(window) + , mSystemManager(systemManager) + , mBackground(window, Path(":/frame.png")) + , mGrid(window, Vector2i(3, 3)) + , mList(nullptr) + , mFullMatch(false) + , mForcedOptions(false) + , mJustOpen(true) { + + if (forcedOptions != nullptr) + { + mForcedOptions = true; + mForcedSearch = forcedOptions->mSearchText; + mFullMatch = forcedOptions->mFullMatch; + mForcedContext = forcedOptions->mContext; + mJustOpen = false; + } + addChild(&mBackground); addChild(&mGrid); @@ -42,36 +53,74 @@ GuiSearch::GuiSearch(WindowManager& window, SystemManager& systemManager) setPosition((Renderer::Instance().DisplayWidthAsFloat() - mSize.x()) / 2, (Renderer::Instance().DisplayHeightAsFloat() - mSize.y()) / 2); - PopulateGrid(""); + + PopulateGrid(mForcedOptions ? mForcedSearch : Strings::Empty); } void GuiSearch::initGridsNStuff() { - //init Title - mTitle = std::make_shared(mWindow, _("SEARCH") + " : ", mMenuTheme->menuText.font, - mMenuTheme->menuText.color, TextAlignment::Right); - mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1)); - - //init search textfield - mSearch = std::make_shared(mWindow); - mGrid.setEntry(mSearch, Vector2i(0, 1), false, false, Vector2i(3, 1)); - - //init search option selector - mSearchChoices = std::make_shared >(mWindow, _("SEARCH BY"), false); - FolderData::FastSearchContext currentSearch = FolderData::FastSearchContext::Name; - - mSearchChoices->add(_("Name"), FolderData::FastSearchContext::Name, currentSearch == FolderData::FastSearchContext::Name); - mSearchChoices->add(_("Description"), FolderData::FastSearchContext::Description, currentSearch == FolderData::FastSearchContext::Description); - mSearchChoices->add(_("DEVELOPER"), FolderData::FastSearchContext::Developer, currentSearch == FolderData::FastSearchContext::Developer); - mSearchChoices->add(_("PUBLISHER"), FolderData::FastSearchContext::Publisher, currentSearch == FolderData::FastSearchContext::Publisher); - mSearchChoices->add(_("FILENAME"), FolderData::FastSearchContext::Path, currentSearch == FolderData::FastSearchContext::Path); - mSearchChoices->add(_("ALL"), FolderData::FastSearchContext::All, currentSearch == FolderData::FastSearchContext::All); - - mSearchChoices->setChangedCallback([this]{ - mGrid.setColWidthPerc(1, mSearchChoices->getSize().x() / mSize.x()); - }); - mGrid.setEntry(mSearchChoices, Vector2i(1, 0), false, false, Vector2i(1, 1)); + //init Title + std::string title = _("SEARCH") + " : "; + + if (mForcedOptions) + { + switch(mForcedContext) + { + case FolderData::FastSearchContext::Alias: title = _("Games") + ": " + mForcedSearch; break; + case FolderData::FastSearchContext::Family: title = _("Games of licence") + ": " + mForcedSearch; break; + case FolderData::FastSearchContext::Path: + case FolderData::FastSearchContext::Name: + case FolderData::FastSearchContext::Description: + case FolderData::FastSearchContext::Developer: + case FolderData::FastSearchContext::Publisher: + case FolderData::FastSearchContext::All: + default: break; + } + } + + mTitle = std::make_shared(mWindow, title, mMenuTheme->menuText.font, + mMenuTheme->menuText.color, TextAlignment::Right); + + mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1)); + //init search textfield + mSearch = std::make_shared(mWindow); + if (!mForcedOptions) + { + mGrid.setEntry(mSearch, Vector2i(0, 1), false, false, Vector2i(3, 1)); + } + + //init search option selector + mSearchChoices = std::make_shared >(mWindow, _("SEARCH BY"), + false); + FolderData::FastSearchContext currentSearch = FolderData::FastSearchContext::Name; + + mSearchChoices->add(_("Name"), FolderData::FastSearchContext::Name, + currentSearch == FolderData::FastSearchContext::Name); + mSearchChoices->add(_("Description"), FolderData::FastSearchContext::Description, + currentSearch == FolderData::FastSearchContext::Description); + mSearchChoices->add(_("DEVELOPER"), FolderData::FastSearchContext::Developer, + currentSearch == FolderData::FastSearchContext::Developer); + mSearchChoices->add(_("PUBLISHER"), FolderData::FastSearchContext::Publisher, + currentSearch == FolderData::FastSearchContext::Publisher); + mSearchChoices->add(_("FILENAME"), FolderData::FastSearchContext::Path, + currentSearch == FolderData::FastSearchContext::Path); + mSearchChoices->add(_("ALIAS"), FolderData::FastSearchContext::Alias, + currentSearch == FolderData::FastSearchContext::Alias); + mSearchChoices->add(_("FAMILY"), FolderData::FastSearchContext::Family, + currentSearch == FolderData::FastSearchContext::Family); + mSearchChoices->add(_("ALL"), FolderData::FastSearchContext::All, + currentSearch == FolderData::FastSearchContext::All); + + mSearchChoices->setChangedCallback([this] + { + mGrid.setColWidthPerc(1, mSearchChoices->getSize().x() / mSize.x()); + }); + + if (!mForcedOptions) + { + mGrid.setEntry(mSearchChoices, Vector2i(1, 0), false, false, Vector2i(1, 1)); +} //init big center grid with List and Meta mGridMeta = std::make_shared(mWindow, Vector2i(4, 3)); mGridMeta->setEntry(std::make_shared(mWindow), Vector2i(1, 0), false, false, Vector2i(1, 3)); @@ -178,7 +227,7 @@ bool GuiSearch::ProcessInput(const class InputCompactEvent & event) Close(); return true; } - else if (event.R1Pressed()) + else if (event.R1Pressed() && !mForcedOptions) { mWindow.pushGui(new GuiArcadeVirtualKeyboard(mWindow, _("SEARCH"), mSearch->getValue(), this)); return true; @@ -234,7 +283,7 @@ bool GuiSearch::ProcessInput(const class InputCompactEvent & event) } } - if (event.AnyLeftPressed() || event.AnyRightPressed()) return mSearchChoices->ProcessInput(event); + if ((event.AnyLeftPressed() || event.AnyRightPressed()) && !mForcedOptions) return mSearchChoices->ProcessInput(event); return Component::ProcessInput(event); } @@ -266,10 +315,13 @@ bool GuiSearch::getHelpPrompts(Help& help) help.Clear(); if (AmIOnTopOfScreen()) { - help.Set(HelpType::LeftRight, _("SEARCH IN...")) - .Set(HelpType::UpDown, _("SELECT")) - .Set(Help::Cancel(), _("BACK")) - .Set(HelpType::R, _("KEYBOARD")); + help.Set(HelpType::UpDown, _("SELECT")) + .Set(Help::Cancel(), _("BACK")); + + if (!mForcedOptions) + help.Set(HelpType::LeftRight, _("SEARCH IN...")) + .Set(HelpType::R, _("KEYBOARD")); + if (!mList->isEmpty()) help.Set(Help::Valid(), _("LAUNCH")) .Set(HelpType::Select, _("GO TO GAME")) @@ -311,8 +363,18 @@ void GuiSearch::PopulateGrid(const std::string& search) { ViewController::State viewControllerState = ViewController::Instance().getState(); SystemData* systemData = viewControllerState.viewing == ViewController::ViewMode::GameList ? viewControllerState.getSystem() : nullptr; - mSearchResults = mSystemManager.SearchTextInGames(mSearchChoices->getSelected(), search, 100, systemData); - if (!mSearchResults.empty()) + + if (mForcedOptions) + { + if (mFullMatch) + mSearchResults = mSystemManager.SearchFullMatchInGames(mForcedContext, search, 100, nullptr); + else + mSearchResults = mSystemManager.SearchTextInGames(mForcedContext, search, 100, systemData); + } + else + mSearchResults = mSystemManager.SearchTextInGames(mSearchChoices->getSelected() , search, 100, systemData); + + if (!mSearchResults.empty()) { mText->setValue(""); ComponentListRow row; @@ -323,7 +385,7 @@ void GuiSearch::PopulateGrid(const std::string& search) std::string gameName; gameName.append(game->System().Descriptor().IconPrefix()); - gameName.append(game->Metadata().Name()); + gameName.append(game->DisplayableName()); ed = std::make_shared(mWindow, gameName, mMenuTheme->menuText.font, mMenuTheme->menuText.color, diff --git a/projects/frontend/es-app/src/guis/GuiSearch.h b/projects/frontend/es-app/src/guis/GuiSearch.h index 429b86ccb1..e8697b08f4 100755 --- a/projects/frontend/es-app/src/guis/GuiSearch.h +++ b/projects/frontend/es-app/src/guis/GuiSearch.h @@ -14,11 +14,12 @@ #include #include #include "systems/SystemManager.h" +#include "SearchForceOptions.h" class GuiSearch : public Gui, public IGuiArcadeVirtualKeyboardInterface { public: - GuiSearch(WindowManager& window, SystemManager& systemManager); + GuiSearch(WindowManager& window, SystemManager& systemManager, SearchForcedOptions* forcedOptions = nullptr); ~GuiSearch() override; @@ -80,8 +81,11 @@ class GuiSearch : public Gui, public IGuiArcadeVirtualKeyboardInterface std::shared_ptr mResultDesc; std::shared_ptr> mSearchChoices; FileData::List mSearchResults; - SystemData* mSystemData; + std::string mForcedSearch; + FolderData::FastSearchContext mForcedContext = FolderData::FastSearchContext::Name; + bool mFullMatch; + bool mForcedOptions; //! Just-open flag bool mJustOpen; diff --git a/projects/frontend/es-app/src/guis/MenuMessages.h b/projects/frontend/es-app/src/guis/MenuMessages.h index ae551c5370..efe5ce1519 100755 --- a/projects/frontend/es-app/src/guis/MenuMessages.h +++ b/projects/frontend/es-app/src/guis/MenuMessages.h @@ -92,7 +92,8 @@ class MenuMessages #define MENUMESSAGE_UI_SHOW_HIDDEN_MSG "Switch between seing or not the hidden games. To hide a game, edit its data and select 'Hide'." #define MENUMESSAGE_UI_HIDE_PREINSTALLED_MSG "Hide all pre-installed games." #define MENUMESSAGE_UI_HIDE_NO_GAMES_MSG "Hide no executable games. for example bios" - #define MENUMESSAGE_UI_FILE_NAME_MSG "Display game by file name instead of game name." + #define MENUMESSAGE_UI_SHOW_REGION_MSG "Display regions code after game name" + #define MENUMESSAGE_UI_DISPLAY_GAME_BY_MSG "Display game in list by file name, game name (scraped or real game name for arcade), or alias (international name)." #define MENUMESSAGE_TATE_HELP_MSG "Manage screen and game rotation options" #define MENUMESSAGE_TATE_SCREEN_ROTATION "Proceed to a complete screen rotation, for frontend and games." @@ -163,6 +164,9 @@ class MenuMessages #define MENUMESSAGE_GAMELISTOPTION_FILTER_REGION_MSG "Select a region to filter out games not matching the selected region." #define MENUMESSAGE_GAMELISTOPTION_SHOW_FOLDER_CONTENT_MSG "Switch between seeing the folders structure and seeing all games in a flatten top level." #define MENUMESSAGE_GAMELISTOPTION_EDIT_METADATA_MSG "This option display a menu which allows to change game data and many others options." + #define MENUMESSAGE_GAMELISTOPTION_DELETE_GAME_MSG "This option display a menu which allows to DELETE game data." + #define MENUMESSAGE_GAMELISTOPTION_SEARCH_SIBLINGS_MSG "This option search the others versions of a game." + #define MENUMESSAGE_GAMELISTOPTION_SEARCH_LICENCE_MSG "This option search the others games of the same licence." #define MENUMESSAGE_GAMELISTOPTION_DELETE_GAME_MSG "This option display a menu which allows to DELETE game data" #define MENUMESSAGE_GAMELISTOPTION_SAVE_STATES_MSG "This option display a menu which allows to manage savestates for a game." diff --git a/projects/frontend/es-app/src/guis/SearchForceOptions.h b/projects/frontend/es-app/src/guis/SearchForceOptions.h new file mode 100644 index 0000000000..129144adc6 --- /dev/null +++ b/projects/frontend/es-app/src/guis/SearchForceOptions.h @@ -0,0 +1,24 @@ +// +// Created by pre2010-02 on 10/03/23. +// + + +#pragma once + +#include +#include "games/FolderData.h" + +class SearchForcedOptions +{ + public: + explicit SearchForcedOptions(std::string& searchText, FolderData::FastSearchContext context, bool fullMatch) + : mSearchText(searchText), + mContext(context), + mFullMatch(fullMatch) + {} + + std::string mSearchText; + FolderData::FastSearchContext mContext; + bool mFullMatch; +}; + diff --git a/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.cpp b/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.cpp index f3dc13a76b..5effd0b0fd 100755 --- a/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.cpp +++ b/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.cpp @@ -6,6 +6,7 @@ #include #include "GuiCheckMenu.h" +#include "components/ButtonComponent.h" GuiCheckMenu::GuiCheckMenu(WindowManager& window, const std::string& title, @@ -77,6 +78,22 @@ GuiCheckMenu::GuiCheckMenu(WindowManager& window, lastChoice, footer); } +GuiCheckMenu::GuiCheckMenu(WindowManager& window, const std::string& title, const std::string& footer, int lastChoice, + const std::list& buttons) + : GuiMenuBase(window, title, nullptr) +{ + for (auto& button : buttons) + { + if (mMenu.getButtonsSize() == 4) + break; + mMenu.addButton(button); + } + + mMenu.setCursorToButtons(); + mMenu.SetDefaultButton(lastChoice); + GuiMenuBase::SetFooter(footer); +} + bool GuiCheckMenu::ProcessInput(const InputCompactEvent& event) { if (event.AnyDownPressed() || event.AnyUpPressed()) @@ -117,4 +134,4 @@ void GuiCheckMenu::build(const std::string &name1, const std::string &help1, con mMenu.setCursorToButtons(); mMenu.SetDefaultButton(lastChoice); GuiMenuBase::SetFooter(footer); -} +} \ No newline at end of file diff --git a/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.h b/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.h index 85936cf845..b0116aedd1 100644 --- a/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.h +++ b/projects/frontend/es-app/src/guis/menus/GuiCheckMenu.h @@ -55,6 +55,12 @@ class GuiCheckMenu : public GuiMenuBase const std::string& help4, const std::function& func4); + GuiCheckMenu(WindowManager& window, + const std::string& title, + const std::string& footer, + int lastChoice, + const std::list& buttons); + private: bool ProcessInput(const InputCompactEvent& event) override; diff --git a/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.cpp b/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.cpp index 9b824bcfa0..861649763a 100644 --- a/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.cpp +++ b/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.cpp @@ -1,5 +1,7 @@ #include "GuiMenuGamelistOptions.h" #include "guis/GuiSaveStates.h" +#include "guis/SearchForceOptions.h" +#include "GuiCheckMenu.h" #include "guis/GuiDownloader.h" #include #include @@ -30,6 +32,7 @@ GuiMenuGamelistOptions::GuiMenuGamelistOptions(WindowManager& window, SystemData if(!mGamelist.getCursor()->IsEmpty()) { + mAlias = mGamelist.getCursor()->Metadata().Alias(); if (!mSystem.IsVirtual() && mGamelist.getCursor()->IsGame() && !mGamelist.getCursor()->TopAncestor().ReadOnly() && !mSystem.IsScreenshots()) { @@ -45,7 +48,12 @@ GuiMenuGamelistOptions::GuiMenuGamelistOptions(WindowManager& window, SystemData if (!GameFilesUtils::GetGameSaveStateFiles(*mGamelist.getCursor()).empty()) AddSubMenu(_("SAVE STATES"), (int) Components::SaveStates, _(MENUMESSAGE_GAMELISTOPTION_SAVE_STATES_MSG)); + + if(!mGamelist.getCursor()->Metadata().Alias().empty()) + AddSubMenu(_("SEARCH OTHERS VERSIONS"), (int) Components::SearchSiblings, _(MENUMESSAGE_GAMELISTOPTION_SEARCH_SIBLINGS_MSG)); } + if(!mGamelist.getCursor()->Metadata().Families().empty()) + AddSubMenu(_("SEARCH GAMES OF SAME LICENCE"), (int) Components::SearchFamily, _(MENUMESSAGE_GAMELISTOPTION_SEARCH_LICENCE_MSG)); } RefreshGameMenuContext(); @@ -61,6 +69,11 @@ GuiMenuGamelistOptions::GuiMenuGamelistOptions(WindowManager& window, SystemData if (!system.IsFavorite()) AddSubMenu(_("SEARCH GAMES HERE"), (int)Components::Search, Strings::Empty); + RefreshGameMenuContext(); + + // Jump to letter + mJumpToLetterList = AddList(_("JUMP TO LETTER"), (int)Components::JumpToLetter, this, GetLetterEntries()); + // Sorting if (!system.IsSelfSorted()) mListSort = AddList(_("SORT GAMES BY"), (int)Components::Sorts, this, GetSortEntries(), _(MENUMESSAGE_GAMELISTOPTION_SORT_GAMES_MSG)); @@ -235,6 +248,7 @@ void GuiMenuGamelistOptions::OptionListComponentChanged(int id, int index, const void GuiMenuGamelistOptions::SubMenuSelected(int id) { + Strings::Vector families = mGamelist.getCursor()->Metadata().FamiliesAsList(); switch((Components)id) { case Components::Download: @@ -289,6 +303,42 @@ void GuiMenuGamelistOptions::SubMenuSelected(int id) mWindow.pushGui(new GuiSearch(mWindow, mSystemManager)); break; } + case Components::SearchSiblings: + { + std::string alias = mGamelist.getCursor()->Metadata().Alias(); + SearchForcedOptions forcedOptions = SearchForcedOptions(alias, FolderData::FastSearchContext::Alias, true); + mWindow.pushGui(new GuiSearch(mWindow, mSystemManager, &forcedOptions)); + break; + } + + case Components::SearchFamily: + if (families.size() == 1) + { + std::string family = mGamelist.getCursor()->Metadata().Families(); + SearchForcedOptions forcedOptions = SearchForcedOptions(family, FolderData::FastSearchContext::Family, true); + mWindow.pushGui(new GuiSearch(mWindow, mSystemManager, &forcedOptions)); + } else + { + std::list buttons; + for (auto& family : families) + { + buttons.push_back(ButtonComponent(mWindow, + family, + family, + [this, family] { + std::string familyLocal = family; + SearchForcedOptions forcedOptions = SearchForcedOptions(familyLocal, FolderData::FastSearchContext::Family, true); + mWindow.pushGui(new GuiSearch(mWindow, mSystemManager, &forcedOptions));})); + } + mWindow.pushGui(new GuiCheckMenu(mWindow, + _("licences"), + mGamelist.getCursor()->Name(), + 0, + buttons + )); + } + break; + case Components::JumpToLetter: case Components::Sorts: case Components::Regions: @@ -314,6 +364,8 @@ void GuiMenuGamelistOptions::SwitchComponentChanged(int id, bool status) case Components::Delete: case Components::DeleteScreeshot: case Components::SaveStates: + case Components::SearchSiblings: + case Components::SearchFamily: case Components::Quit: break; } diff --git a/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.h b/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.h index 3131081c58..e77f3f166b 100644 --- a/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.h +++ b/projects/frontend/es-app/src/guis/menus/GuiMenuGamelistOptions.h @@ -41,6 +41,8 @@ class GuiMenuGamelistOptions : public GuiMenuBase MainMenu, Quit, Search, + SearchSiblings, + SearchFamily, }; //! System reference @@ -50,6 +52,8 @@ class GuiMenuGamelistOptions : public GuiMenuBase //! Gamelist UI reference IGameListView& mGamelist; + std::string mAlias; + std::shared_ptr> mJumpToLetterList; std::shared_ptr> mListSort; std::shared_ptr> mListRegion; diff --git a/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.cpp b/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.cpp index 3d913723c8..a191df9dbb 100644 --- a/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.cpp +++ b/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.cpp @@ -53,7 +53,8 @@ GuiMenuUserInterface::GuiMenuUserInterface(WindowManager& window, SystemManager& AddSubMenu(_("GAME FILTERS"), (int)Components::Filters, _(MENUMESSAGE_UI_GAME_FILTERS_MSG)); // Display filename - AddSwitch(_("DISPLAY BY FILENAME"), RecalboxConf::Instance().GetDisplayByFileName(), (int)Components::DisplayByFileName, this, _(MENUMESSAGE_UI_FILE_NAME_MSG)); + AddSwitch(_("SHOW REGION"), RecalboxConf::Instance().GetDisplayGameRegions(), (int)Components::DisplayGameRegions, this, _(MENUMESSAGE_UI_SHOW_REGION_MSG)); + AddList(_("DISPLAY GAME BY"), (int)Components::GetDisplayGameBy, this, GetDisplayGameBy(), _(MENUMESSAGE_UI_DISPLAY_GAME_BY_MSG)); // Game List Update AddSubMenu(_("UPDATE GAMES LISTS"), (int)Components::UpdateGamelist, _(MENUMESSAGE_UI_UPDATE_GAMELIST_HELP_MSG)); @@ -96,7 +97,8 @@ void GuiMenuUserInterface::SubMenuSelected(int id) case Components::SwapValidateAndCancel: case Components::Help: case Components::SystemSort: - case Components::DisplayByFileName: + case Components::GetDisplayGameBy: + case Components::DisplayGameRegions: case Components::QuickSelect: break; } } @@ -114,6 +116,7 @@ void GuiMenuUserInterface::SliderMoved(int id, float value) void GuiMenuUserInterface::SwitchComponentChanged(int id, bool status) { SystemData* systemData = ViewController::Instance().getState().getSystem(); + FileData* game = ViewController::Instance().getGameListView(systemData)->getCursor(); switch((Components)id) { @@ -126,11 +129,13 @@ void GuiMenuUserInterface::SwitchComponentChanged(int id, bool status) updateHelpPrompts(); break; } - case Components::DisplayByFileName: - RecalboxConf::Instance().SetDisplayByFileName(status).Save(); - ViewController::Instance().getGameListView(systemData)->refreshList(); + case Components::DisplayGameRegions: + RecalboxConf::Instance().SetDisplayGameRegions(status).Save(); ViewController::Instance().setAllInvalidGamesList(nullptr); + ViewController::Instance().getGameListView(systemData)->refreshList(); + ViewController::Instance().getGameListView(systemData)->setCursor(game); break; + case Components::GetDisplayGameBy: case Components::Popups: case Components::Theme: case Components::ThemeConfig: @@ -153,6 +158,19 @@ void GuiMenuUserInterface::OptionListComponentChanged(int id, int index, const S ViewController::Instance().getSystemListView().Sort(); } +void GuiMenuUserInterface::OptionListComponentChanged(int id, int index, const FileData::DisplayGameBy & value) +{ + (void)index; + (void)id; + SystemData* systemData = ViewController::Instance().getState().getSystem(); + FileData* game = ViewController::Instance().getGameListView(systemData)->getCursor(); + RecalboxConf::Instance().SetDisplayGameBy(value).Save(); + ViewController::Instance().setAllInvalidGamesList(nullptr); + ViewController::Instance().getGameListView(systemData)->refreshList(); + ViewController::Instance().getGameListView(systemData)->setCursor(game); + +} + std::vector> GuiMenuUserInterface::GetSortingEntries() { mOriginalSort = RecalboxConf::Instance().GetSystemSorting(); @@ -169,3 +187,15 @@ std::vector> GuiMenuUserInterface::GetSort { _("TYPE, THEN MANUFACTURER, THEN RELEASE DATE") , SystemSorting::SystemTypeThenManufacturerThenReleasdeDate , mOriginalSort == SystemSorting::SystemTypeThenManufacturerThenReleasdeDate }, }); } + +std::vector> GuiMenuUserInterface::GetDisplayGameBy() +{ + FileData::DisplayGameBy confValue = RecalboxConf::Instance().GetDisplayGameBy(); + + return std::vector> + ({ + {"NAME", FileData::DisplayGameBy::Name, FileData::DisplayGameBy::Name == confValue }, + {"ALIAS", FileData::DisplayGameBy::Alias, FileData::DisplayGameBy::Alias == confValue }, + {"FILENAME", FileData::DisplayGameBy::Filename, FileData::DisplayGameBy::Filename == confValue } + }); +} diff --git a/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.h b/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.h index 9e0e78d2af..b75107b9fb 100644 --- a/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.h +++ b/projects/frontend/es-app/src/guis/menus/GuiMenuUserInterface.h @@ -14,6 +14,7 @@ class GuiMenuUserInterface : public GuiMenuBase , private ISliderComponent , private ISwitchComponent , private IOptionListComponent + , private IOptionListComponent { public: /*! @@ -44,7 +45,8 @@ class GuiMenuUserInterface : public GuiMenuBase SystemSort, UpdateGamelist, Filters, - DisplayByFileName + GetDisplayGameBy, + DisplayGameRegions }; //! System Manager @@ -66,6 +68,10 @@ class GuiMenuUserInterface : public GuiMenuBase std::shared_ptr> mSort; //! Get Sorting List std::vector> GetSortingEntries(); + /*! + * Get game name display type List + */ + std::vector> GetDisplayGameBy(); /*! * @brief Reload gamelists @@ -96,5 +102,6 @@ class GuiMenuUserInterface : public GuiMenuBase void OptionListComponentChanged(int id, int index, const SystemSorting& value) override; + void OptionListComponentChanged(int id, int index, const FileData::DisplayGameBy& value) override; }; diff --git a/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.cpp b/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.cpp index 2a2de09c26..d165445de4 100644 --- a/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.cpp +++ b/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.cpp @@ -5,8 +5,6 @@ #include #include #include "ScreenScraperApis.h" -#include -#include #include #include @@ -150,6 +148,19 @@ void ScreenScraperApis::DeserializeGameInformationInner(const rapidjson::Value& game.mName = ExtractRegionalizedText(json["noms"], requiredRegion); game.mScreenScraperName = CleanGameName(ExtractRegionalizedText(json["noms"], "ss")); } + if (json.HasMember("familles")) + { + String::List families; + for(const auto& famille : json["familles"].GetArray()) + { + if (famille.HasMember("noms")) + for (const auto& nom: famille["noms"].GetArray()) + if (nom.HasMember("text")) + families.push_back(nom["text"].GetString()); + + game.mFamilies = families; + } + } if (json.HasMember("synopsis")) { game.mSynopsis = ExtractLocalizedText(json["synopsis"], language); diff --git a/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.h b/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.h index 51e46217fc..a0cae5fb9e 100644 --- a/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.h +++ b/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperApis.h @@ -12,6 +12,7 @@ #include #include #include +#include class ScreenScraperApis { @@ -25,6 +26,10 @@ class ScreenScraperApis std::string mScreenScraperName; //! Game Name std::string mName; + + //! Game Families + String::List mFamilies; + //! Developer std::string mDeveloper; //! Publisher diff --git a/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperSingleEngine.cpp b/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperSingleEngine.cpp index 34a5ea500c..1442b900e2 100644 --- a/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperSingleEngine.cpp +++ b/projects/frontend/es-app/src/scraping/scrapers/screenscraper/ScreenScraperSingleEngine.cpp @@ -307,6 +307,17 @@ ScreenScraperSingleEngine::StoreTextData(ScrapingMethod method, const ScreenScra game.Metadata().SetName(sourceData.mName); mTextInfo++; } + if (!sourceData.mScreenScraperName.empty()) + { + game.Metadata().SetAlias(sourceData.mScreenScraperName); + mTextInfo++; + } + + if (!sourceData.mFamilies.empty()) + { + game.Metadata().SetFamiliesFromList(sourceData.mFamilies); + mTextInfo++; + } // Store data only if they are not empty and not scraped if method is IncompleteKeep if (!sourceData.mSynopsis.empty()) if (game.Metadata().Description().empty() || noKeep) diff --git a/projects/frontend/es-app/src/systems/SystemData.cpp b/projects/frontend/es-app/src/systems/SystemData.cpp index fbc5d5247c..06938168e3 100644 --- a/projects/frontend/es-app/src/systems/SystemData.cpp +++ b/projects/frontend/es-app/src/systems/SystemData.cpp @@ -608,21 +608,6 @@ FileData::Filter SystemData::Excludes() const return excludesFilter; } -void SystemData::LookupGames(FolderData::FastSearchContext context, const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const -{ - for(const RootFolderData* root : mRootOfRoot.SubRoots()) - switch(context) - { - case FolderData::FastSearchContext::Path: root->LookupGamesFromPath(index, games); break; - case FolderData::FastSearchContext::Name: root->LookupGamesFromName(index, games); break; - case FolderData::FastSearchContext::Description: root->LookupGamesFromDescription(index, games); break; - case FolderData::FastSearchContext::Developer: root->LookupGamesFromDeveloper(index, games); break; - case FolderData::FastSearchContext::Publisher: root->LookupGamesFromPublisher(index, games); break; - case FolderData::FastSearchContext::All: root->LookupGamesFromAll(index, games); break; - default: break; - } -} - void SystemData::BuildFastSearchSeries(FolderData::FastSearchItemSerie& into, FolderData::FastSearchContext context) const { for(const RootFolderData* root : mRootOfRoot.SubRoots()) @@ -630,6 +615,8 @@ void SystemData::BuildFastSearchSeries(FolderData::FastSearchItemSerie& into, Fo { case FolderData::FastSearchContext::Path: root->BuildFastSearchSeriesPath(into); break; case FolderData::FastSearchContext::Name: root->BuildFastSearchSeriesName(into); break; + case FolderData::FastSearchContext::Alias: root->BuildFastSearchSeriesAlias(into); break; + case FolderData::FastSearchContext::Family: root->BuildFastSearchSeriesFamily(into); break; case FolderData::FastSearchContext::Description: root->BuildFastSearchSeriesDescription(into); break; case FolderData::FastSearchContext::Developer: root->BuildFastSearchSeriesDeveloper(into); break; case FolderData::FastSearchContext::Publisher: root->BuildFastSearchSeriesPublisher(into); break; diff --git a/projects/frontend/es-app/src/systems/SystemData.h b/projects/frontend/es-app/src/systems/SystemData.h index 29b5d7055e..a7a3c13ea8 100644 --- a/projects/frontend/es-app/src/systems/SystemData.h +++ b/projects/frontend/es-app/src/systems/SystemData.h @@ -239,14 +239,6 @@ class SystemData : private INoCopy */ [[nodiscard]] SystemManager& Manager() const { return mSystemManager; } - /*! - * @brief Search for all games containing 'text' and add them to 'result' - * @param context Field in which to search text for - * @param indexes item indexes resulting from a fast search - * @param games Resulting game list - */ - void LookupGames(FolderData::FastSearchContext context, const MetadataStringHolder::IndexAndDistance& index, FileData::List& games) const; - /*! * @brief Fill in the given Item series with all filedata recursively * @param into Item series to fill in diff --git a/projects/frontend/es-app/src/systems/SystemManager.cpp b/projects/frontend/es-app/src/systems/SystemManager.cpp index 0f6e4d635a..6fb5371d40 100644 --- a/projects/frontend/es-app/src/systems/SystemManager.cpp +++ b/projects/frontend/es-app/src/systems/SystemManager.cpp @@ -202,7 +202,8 @@ void SystemManager::BuildDynamicMetadata(SystemData& system) Path romPath = game.RomPath(); std::string fileName = romPath.Filename(); Versions::GameVersions version = Versions::ExtractGameVersionNoIntro(fileName); - std::string gameNameWithRegion = Strings::RemoveParenthesis(fileName).append(Regions::Serialize4Regions(Regions::ExtractRegionsFromNoIntroName(fileName))); + std::string diskNumber = FileData::ExtractGameSupportNumber(fileName); + std::string gameNameWithRegion = Strings::RemoveParenthesis(fileName).append(Regions::Serialize4Regions(Regions::ExtractRegionsFromNoIntroName(fileName))).append(diskNumber); VersionedGame* previous = mHighestVersions.try_get(gameNameWithRegion); if (previous == nullptr) @@ -972,6 +973,62 @@ void SystemManager::UpdateLastPlayedSystem(FileData& game) system.UpdateLastPlayedGame(game); } +FileData::List SystemManager::SearchFullMatchInGames(FolderData::FastSearchContext context, const std::string& originaltext, int maxglobal, const SystemData* targetSystem) +{ + std::string lowercaseText = Strings::ToLowerUTF8(originaltext); + FileData::List resultsList; + + // Build searchable system list + std::list searchableSystems; + + if (targetSystem != nullptr && targetSystem->IsSearchable() && !targetSystem->IsFavorite()) + searchableSystems.push_back(targetSystem); + else + for (const SystemData* system : GetVisibleSystemList()) + { + if (system->IsSearchable() && system->HasVisibleGame()) + searchableSystems.push_back(system); + } + + for (const SystemData* system: searchableSystems) + { + for (auto& game: system->getGames()) { + if(game->IsGame()) + { + std::string found; + switch (context) + { + case FolderData::FastSearchContext::Family: + for (auto& familly : game->Metadata().FamiliesAsList()) + { + if (familly == originaltext) + resultsList.push_back(game); + } + break; + + case FolderData::FastSearchContext::Name: found = game->Metadata().Name(); + break; + case FolderData::FastSearchContext::Alias: found = game->Metadata().Alias(); + break; + case FolderData::FastSearchContext::Path: + case FolderData::FastSearchContext::Description: + case FolderData::FastSearchContext::Developer: + case FolderData::FastSearchContext::Publisher: + case FolderData::FastSearchContext::All: break; + } + + if (FolderData::FastSearchContext::Family != context && Strings::ToLowerUTF8(found) == lowercaseText) + resultsList.push_back(game); + } + if ((int)resultsList.size() >= maxglobal) + break; + } + } + + return resultsList; +} + + FileData::List SystemManager::SearchTextInGames(FolderData::FastSearchContext context, const std::string& originaltext, int maxglobal, const SystemData* targetSystem) { // Everything to lowercase cause search is not case sensitive @@ -982,7 +1039,13 @@ FileData::List SystemManager::SearchTextInGames(FolderData::FastSearchContext co MetadataStringHolder::FoundTextList resultIndexes(1024, 1024); switch(context) { - case FolderData::FastSearchContext::Name : MetadataDescriptor::SearchInNames(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Name); break; + case FolderData::FastSearchContext::Name : + MetadataDescriptor::SearchInNames(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Name); + MetadataDescriptor::SearchInAlias(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Alias); + break; + + case FolderData::FastSearchContext::Alias : MetadataDescriptor::SearchInAlias(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Alias); break; + case FolderData::FastSearchContext::Family : MetadataDescriptor::SearchInFamily(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Family); break; case FolderData::FastSearchContext::Path : MetadataDescriptor::SearchInPath(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Path); break; case FolderData::FastSearchContext::Description: MetadataDescriptor::SearchInDescription(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Description); break; case FolderData::FastSearchContext::Developer : MetadataDescriptor::SearchInDeveloper(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Developer); break; @@ -990,6 +1053,7 @@ FileData::List SystemManager::SearchTextInGames(FolderData::FastSearchContext co case FolderData::FastSearchContext::All : { MetadataDescriptor::SearchInNames(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Name); break; + MetadataDescriptor::SearchInAlias(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Alias); break; MetadataDescriptor::SearchInPath(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Path); break; MetadataDescriptor::SearchInDescription(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Description); break; MetadataDescriptor::SearchInDeveloper(lowercaseText, resultIndexes, (int)FolderData::FastSearchContext::Developer); break; @@ -1023,22 +1087,31 @@ FileData::List SystemManager::SearchTextInGames(FolderData::FastSearchContext co // Build Item series DateTime start; - CreateFastSearchCache(resultIndexes, searchableSystems); + CreateFastSearchCache(resultIndexes, searchableSystems, context); + if (FolderData::FastSearchContext::Name == context) + CreateFastSearchCache(resultIndexes, searchableSystems, FolderData::FastSearchContext::Alias); + { LOG(LogDebug) << "[Search] Fast lookup cache built in " << ((DateTime() - start).TotalMilliseconds()) << "ms"; } // Collect result - FileData::List results; + FileData::Set results; for(int i = -1; ++i < (int)resultIndexes.Count(); ) { const MetadataStringHolder::IndexAndDistance& resultIndex = resultIndexes(i); FolderData::FastSearchItemSerie& serie = mFastSearchSeries[resultIndex.Context]; for(FolderData::FastSearchItem* item = serie.Get(resultIndex.Index); item != nullptr; item = serie.Next(item)) - if (item->Game != nullptr) results.push_back((FileData*)item->Game); - if ((int)results.size() >= maxglobal) break; + if (item->Game != nullptr) + results.insert((FileData*) item->Game); + if ((int)results.size() >= maxglobal) + break; } { LOG(LogDebug) << "[Search] Final result: " << results.size() << " games"; } - return results; + FileData::List resultsList; + for(auto& game : results) + resultsList.push_back(game); + + return resultsList; } void SystemManager::SystemSorting() @@ -1063,14 +1136,15 @@ void SystemManager::SystemSorting(std::vector& systems, const std: } } -void SystemManager::CreateFastSearchCache(const MetadataStringHolder::FoundTextList& resultIndexes, const Array& searchableSystems) +void SystemManager::CreateFastSearchCache(const MetadataStringHolder::FoundTextList& resultIndexes, const Array& searchableSystems, FolderData::FastSearchContext context) { // Calculate searchable system hash unsigned int hash = 0; for(int i = searchableSystems.Count(); --i >= 0; ) { const SystemData& system = *searchableSystems[i]; - hash = crc32_16bytes(system.FullName().c_str(), system.FullName().size(), hash); + String key = String(system.FullName().c_str()).Append((int)context); + hash = crc32_16bytes(key.c_str(), key.size(), hash); } // Refresh hash? @@ -1089,6 +1163,8 @@ void SystemManager::CreateFastSearchCache(const MetadataStringHolder::FoundTextL { case FolderData::FastSearchContext::Path: count = MetadataDescriptor::FileIndexCount(); break; case FolderData::FastSearchContext::Name: count = MetadataDescriptor::NameIndexCount(); break; + case FolderData::FastSearchContext::Alias: count = MetadataDescriptor::AliasIndexCount(); break; + case FolderData::FastSearchContext::Family: count = MetadataDescriptor::FamiliesIndexCount(); break; case FolderData::FastSearchContext::Description: count = MetadataDescriptor::DescriptionIndexCount(); break; case FolderData::FastSearchContext::Developer: count = MetadataDescriptor::DeveloperIndexCount(); break; case FolderData::FastSearchContext::Publisher: count = MetadataDescriptor::PublisherIndexCount(); break; @@ -1096,7 +1172,7 @@ void SystemManager::CreateFastSearchCache(const MetadataStringHolder::FoundTextL } FolderData::FastSearchItemSerie serie(count); for(int s = searchableSystems.Count(); --s >= 0; ) - searchableSystems[s]->BuildFastSearchSeries(serie, (FolderData::FastSearchContext)resultIndexes[i].Context); + searchableSystems[s]->BuildFastSearchSeries(serie, (FolderData::FastSearchContext) resultIndexes[i].Context); mFastSearchSeries[resultIndexes[i].Context] = std::move(serie); } } diff --git a/projects/frontend/es-app/src/systems/SystemManager.h b/projects/frontend/es-app/src/systems/SystemManager.h index 62b6db8785..c65825be06 100644 --- a/projects/frontend/es-app/src/systems/SystemManager.h +++ b/projects/frontend/es-app/src/systems/SystemManager.h @@ -326,7 +326,7 @@ class SystemManager : * @param resultIndexes Index to get context from * @param searchableSystems Searchable systems */ - void CreateFastSearchCache(const MetadataStringHolder::FoundTextList& resultIndexes, const Array& searchableSystems); + void CreateFastSearchCache(const MetadataStringHolder::FoundTextList& resultIndexes, const Array& searchableSystems, FolderData::FastSearchContext context); //! Remove all cache void DeleteFastSearchCache(); @@ -480,6 +480,9 @@ class SystemManager : //! Get emulator manager [[nodiscard]] const EmulatorManager& Emulators() const { return mEmulatorManager; } + FileData::List SearchFullMatchInGames(FolderData::FastSearchContext context, const std::string& text, int maxglobal, const SystemData* targetSystem); + + /*! * @brief Search games from text * @param text Text to search for diff --git a/projects/frontend/es-app/src/usernotifications/NotificationManager.cpp b/projects/frontend/es-app/src/usernotifications/NotificationManager.cpp index 5c5b924186..d79fc5ce84 100644 --- a/projects/frontend/es-app/src/usernotifications/NotificationManager.cpp +++ b/projects/frontend/es-app/src/usernotifications/NotificationManager.cpp @@ -222,7 +222,8 @@ void NotificationManager::BuildStateGame(std::string& output, const FileData* ga .append("GenreId=").append(game->Metadata().GenreIdAsString()).append(eol) .append("Favorite=").append(game->Metadata().Favorite() ? "1" : "0").append(eol) .append("Hidden=").append(game->Metadata().Hidden() ? "1" : "0").append(eol) - .append("Adult=").append(game->Metadata().Adult() ? "1" : "0").append(eol); + .append("Adult=").append(game->Metadata().Adult() ? "1" : "0").append(eol) + .append("Alias=").append(game->Metadata().Alias()).append(eol); if (action != Notification::ScrapGame) { diff --git a/projects/frontend/es-app/src/views/gamelist/BasicGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/BasicGameListView.cpp index bbed706f70..0da245424f 100644 --- a/projects/frontend/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/BasicGameListView.cpp @@ -77,7 +77,9 @@ std::string BasicGameListView::GetDisplayName(FileData* game) // Select Icon std::string result = getItemIcon(game); // Get name - result.append(RecalboxConf::Instance().GetDisplayByFileName() ? game->RomPath().Filename() : game->Name()); + std::string name = game->DisplayableName(); + + result.append(name); return result; } @@ -122,25 +124,21 @@ void BasicGameListView::populateList(const FolderData& folder) // Add to list mHasGenre = false; //mList.reserve(items.size()); // TODO: Reserve memory once + for (FileData* fd : items) - { - // Select fron icon - std::string icon = getItemIcon(fd); - // Get name + { - std::string name = RecalboxConf::Instance().GetDisplayByFileName() ? fd->RomPath().Filename() : fd->Name(); - std::string line = !icon.empty() ? icon + name : name; // Region filtering? int colorIndexOffset = 0; if (activeRegionFiltering) if (!Regions::IsIn4Regions(fd->Metadata().Region().Pack, currentRegion)) colorIndexOffset = 2; - // Store + // Store mList.add(GetDisplayName(fd), fd, colorIndexOffset + (fd->IsFolder() ? 1 : 0), false); // Attribute analysis if (fd->IsGame() && fd->Metadata().GenreId() != GameGenres::None) mHasGenre = true; - } + } } FileData::List BasicGameListView::getFileDataList() diff --git a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp index 558d9ca71c..a2c31e6821 100755 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -427,7 +427,7 @@ void ISimpleGameListView::jumpToLetter(unsigned int unicode) FileData::List files = getFileDataList(); for(int c = (int)files.size(), i = 0; --c >= 0; ++i) if (files[i]->IsGame()) - if (Strings::UpperChar(files[i]->Name()) == unicode) + if (Strings::UpperChar(files[i]->DisplayableName()) == unicode) { setCursor(files[i]); break; diff --git a/projects/frontend/es-core/src/RecalboxConf.cpp b/projects/frontend/es-core/src/RecalboxConf.cpp index 9049f93558..4acbd2225b 100644 --- a/projects/frontend/es-core/src/RecalboxConf.cpp +++ b/projects/frontend/es-core/src/RecalboxConf.cpp @@ -124,6 +124,31 @@ const std::string& RecalboxConf::ScraperTypeFromEnum(ScraperType type) return defaultString; } +FileData::DisplayGameBy RecalboxConf::DisplayGameByFromString(const std::string& displayGameBy) +{ + if (displayGameBy == "name") + return FileData::DisplayGameBy::Name; + else if (displayGameBy == "filename") + return FileData::DisplayGameBy::Filename; + else if (displayGameBy == "alias") + return FileData::DisplayGameBy::Alias; + + return FileData::DisplayGameBy::Name; +} + +const std::string& RecalboxConf::DisplayGameByFromEnum(FileData::DisplayGameBy displayGameBy) +{ + static std::string defaultString("name"); + switch(displayGameBy) + { + case FileData::DisplayGameBy::Name : return defaultString; + case FileData::DisplayGameBy::Alias : { static std::string string("alias"); return string; } + case FileData::DisplayGameBy::Filename : { static std::string string("filename"); return string; } + default: break; + } + return defaultString; +} + DefineSystemGetterSetterImplementation(Emulator, std::string, String, sSystemEmulator, "") DefineSystemGetterSetterImplementation(Core, std::string, String, sSystemCore, "") DefineSystemGetterSetterImplementation(Ratio, std::string, String, sSystemRatio, GetGlobalRatio()) diff --git a/projects/frontend/es-core/src/RecalboxConf.h b/projects/frontend/es-core/src/RecalboxConf.h index 4a79b12c3b..9d37efd651 100644 --- a/projects/frontend/es-core/src/RecalboxConf.h +++ b/projects/frontend/es-core/src/RecalboxConf.h @@ -163,7 +163,7 @@ class RecalboxConf: public IniFile, public StaticLifeCycleControlerSystem().FullName()); onThemeChanged(mSystem->Theme()); - - mGameName.setValue(mGame->Metadata().Name()); + + mGameName.setValue(game->DisplayableName()); mRating.setValue(mGame->Metadata().RatingAsString()); mReleaseDate.setValue(mGame->Metadata().ReleaseDateAsString()); mDeveloper.setValue(mGame->Metadata().Developer()); diff --git a/projects/frontend/es-core/src/components/MenuComponent.cpp b/projects/frontend/es-core/src/components/MenuComponent.cpp index a191eaf185..e69c60a143 100755 --- a/projects/frontend/es-core/src/components/MenuComponent.cpp +++ b/projects/frontend/es-core/src/components/MenuComponent.cpp @@ -169,6 +169,13 @@ void MenuComponent::addButton(const std::string& name, const std::string& helpTe updateSize(); } +void MenuComponent::addButton(const ButtonComponent& button) +{ + mButtons.push_back(std::make_shared(mWindow, Strings::ToUpperUTF8(button.getText()), button.getValue(), button.getPressedFunc())); + updateGrid(); + updateSize(); +} + void MenuComponent::updateGrid() { Vector2i buttonGridCursor = Vector2i(0, 0); diff --git a/projects/frontend/es-core/src/components/MenuComponent.h b/projects/frontend/es-core/src/components/MenuComponent.h index 63e5215416..41374b7639 100644 --- a/projects/frontend/es-core/src/components/MenuComponent.h +++ b/projects/frontend/es-core/src/components/MenuComponent.h @@ -100,6 +100,7 @@ class MenuComponent : public Component } void addButton(const std::string& label, const std::string& helpText, const std::function& callback); + void addButton(const ButtonComponent& button); void setFooter(const std::string& label); -- GitLab