diff --git a/package/recalbox-romfs2/scripts/installroms.py b/package/recalbox-romfs2/scripts/installroms.py index 28893679d295437c5bf4ae9d18ce9cb69f9567e4..9cce8f71ffb43ad170041af083d6cb418de00665 100644 --- a/package/recalbox-romfs2/scripts/installroms.py +++ b/package/recalbox-romfs2/scripts/installroms.py @@ -159,7 +159,7 @@ class InstallRoms: replacement = chunks[1 + holder.Properties.KeyboardEnum] elif chunks[0] == "MOUSE" and len(chunks) == 5: replacement = chunks[1 + holder.Properties.MouseEnum] - elif chunks[0] == "TYPE" and len(chunks) == 8: + elif chunks[0] == "TYPE" and len(chunks) == 9: replacement = chunks[1 + holder.Properties.TypeEnum] line = line.replace(line[start: stop + 1], replacement) diff --git a/package/recalbox-romfs2/scripts/systemholder.py b/package/recalbox-romfs2/scripts/systemholder.py index bcda539a433719d9bc5cbcec2fe4280b5bd721ba..7cc81dd16154fae53e9741e3dfa3b478b66c0e0a 100644 --- a/package/recalbox-romfs2/scripts/systemholder.py +++ b/package/recalbox-romfs2/scripts/systemholder.py @@ -16,6 +16,7 @@ class SystemHolder: "virtual": 4, "engine": 5, "port": 6, + "fantasy": 7, } __deviceRequirement: Dict[str, int] = { diff --git a/package/recalbox-romfs2/systems/.templates/readme/_leggime.txt b/package/recalbox-romfs2/systems/.templates/readme/_leggime.txt index 08194b50f321e0401c6afaa3677dc0c57706bbd4..2825b75d12160704a83d1da91f0cf00e53b1fe4d 100644 --- a/package/recalbox-romfs2/systems/.templates/readme/_leggime.txt +++ b/package/recalbox-romfs2/systems/.templates/readme/_leggime.txt @@ -1,5 +1,5 @@ ======================================================================= -# RECALBOX: Sistema $(FULLNAME) ($(TYPE:Cabinato Arcade:Console da Tavolo:Console Portatile:Computer:Systema virtuale:Motore grafico:Port)) +# RECALBOX: Sistema $(FULLNAME) ($(TYPE:Cabinato Arcade:Console da Tavolo:Console Portatile:Computer:Systema virtuale:Motore grafico:Port:Console virtuale)) ======================================================================= Copia qui le rom per $(FULLNAME). diff --git a/package/recalbox-romfs2/systems/.templates/readme/_leiame.txt b/package/recalbox-romfs2/systems/.templates/readme/_leiame.txt index 171456f599f39567a661dcd91e95c8bc7a8f68a4..790b07802ce5ba952435ecc4d8859a184a772336 100644 --- a/package/recalbox-romfs2/systems/.templates/readme/_leiame.txt +++ b/package/recalbox-romfs2/systems/.templates/readme/_leiame.txt @@ -1,5 +1,5 @@ ======================================================================= -# RECALBOX: Sistema $(FULLNAME) ($(TYPE:Sistema Arcade:Console caseiro:Console portátil:Computador:Sistema Virtual:Game engine:Port)) +# RECALBOX: Sistema $(FULLNAME) ($(TYPE:Sistema Arcade:Console caseiro:Console portátil:Computador:Sistema Virtual:Game engine:Port:Console Virtual)) ======================================================================= Copie suas roms de $(FULLNAME) aqui. diff --git a/package/recalbox-romfs2/systems/.templates/readme/_liesmich.txt b/package/recalbox-romfs2/systems/.templates/readme/_liesmich.txt index cafb304080cd0f3ca727b742ae8b40cf0b6107e7..52fb97c35f7e5de201cd4b773495554e3e5296ca 100644 --- a/package/recalbox-romfs2/systems/.templates/readme/_liesmich.txt +++ b/package/recalbox-romfs2/systems/.templates/readme/_liesmich.txt @@ -1,5 +1,5 @@ ======================================================================= -# RECALBOX: System $(FULLNAME) ($(TYPE:Arcade:Heimkonsole:Handheld:Computer:Virtuelles System:Spiel-Engine:Port)) +# RECALBOX: System $(FULLNAME) ($(TYPE:Arcade:Heimkonsole:Handheld:Computer:Virtuelle System:Spiel-Engine:Port:Virtuelle Konsole)) ======================================================================= Kopiere deine $(FULLNAME) roms hier her. diff --git a/package/recalbox-romfs2/systems/.templates/readme/_lisezmoi.txt b/package/recalbox-romfs2/systems/.templates/readme/_lisezmoi.txt index 5e740564ef625395e3a84c10a6adccca83f881ae..503c181bbdf5dfeff788f4180b19ee9166970257 100644 --- a/package/recalbox-romfs2/systems/.templates/readme/_lisezmoi.txt +++ b/package/recalbox-romfs2/systems/.templates/readme/_lisezmoi.txt @@ -1,5 +1,5 @@ ======================================================================= -# RECALBOX: Système $(FULLNAME) ($(TYPE:Systeme Arcade:Console de Salon:Console portable:Ordinateur:System virtuel:Moteur de jeu:Portage)) +# RECALBOX: Système $(FULLNAME) ($(TYPE:Système Arcade:Console de Salon:Console portable:Ordinateur:Système virtuel:Moteur de jeu:Portage:Console virtuelle)) ======================================================================= Copiez vos roms $(FULLNAME) ici. diff --git a/package/recalbox-romfs2/systems/.templates/readme/_readme.txt b/package/recalbox-romfs2/systems/.templates/readme/_readme.txt index e2ae47b388d08f20ec68f61fb96cadb79b971030..e863dfaef3a85b029283ac5b9babcc1d9376a55f 100644 --- a/package/recalbox-romfs2/systems/.templates/readme/_readme.txt +++ b/package/recalbox-romfs2/systems/.templates/readme/_readme.txt @@ -1,5 +1,5 @@ ======================================================================= -# RECALBOX: System $(FULLNAME) ($(TYPE:Arcade system:Home console:Handheld console:Computer:Fantasy console:Game engine:Port)) +# RECALBOX: System $(FULLNAME) ($(TYPE:Arcade system:Home console:Handheld console:Computer:Virtual system:Game engine:Port:Virtual console)) ======================================================================= Copy your $(FULLNAME) ROMs here. diff --git a/package/recalbox-romfs2/systems/easyrpg/system.ini b/package/recalbox-romfs2/systems/easyrpg/system.ini index c910fce752b47afc04c60cf6874f3221a0937ca5..486a1f5b59b9fddead131091988a6d841583b204 100644 --- a/package/recalbox-romfs2/systems/easyrpg/system.ini +++ b/package/recalbox-romfs2/systems/easyrpg/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $F2a6 [properties] -type = virtual +type = engine device.pad = mandatory device.keyboard = no device.mouse = no diff --git a/package/recalbox-romfs2/systems/lowresnx/system.ini b/package/recalbox-romfs2/systems/lowresnx/system.ini index 9131b5b9efd491fda47a9a499332cb419ddc084d..921f53f68a40457f677080ad005f221f378af526 100644 --- a/package/recalbox-romfs2/systems/lowresnx/system.ini +++ b/package/recalbox-romfs2/systems/lowresnx/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $0 [properties] -type = virtual +type = fantasy device.pad = mandatory device.keyboard = no device.mouse = no diff --git a/package/recalbox-romfs2/systems/lutro/system.ini b/package/recalbox-romfs2/systems/lutro/system.ini index c6084b489c43f50d2002fa3fdb6e33da75ad2444..5065814568f48ce004277b87ac13add9f216744f 100644 --- a/package/recalbox-romfs2/systems/lutro/system.ini +++ b/package/recalbox-romfs2/systems/lutro/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $F27d [properties] -type = virtual +type = fantasy device.pad = mandatory device.keyboard = no device.mouse = no diff --git a/package/recalbox-romfs2/systems/pico8/system.ini b/package/recalbox-romfs2/systems/pico8/system.ini index 04709d86095bd2cb241f8471dfb0e33fd42a285a..e955de816e5ce1e89e73b8618d0c313d6aa3b9d7 100644 --- a/package/recalbox-romfs2/systems/pico8/system.ini +++ b/package/recalbox-romfs2/systems/pico8/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $F2c4 [properties] -type = virtual +type = fantasy device.pad = mandatory device.keyboard = no device.mouse = no diff --git a/package/recalbox-romfs2/systems/scummvm/system.ini b/package/recalbox-romfs2/systems/scummvm/system.ini index 0438abbc1ee23a0a86d41cc8fc701dfc8038efc6..3cebe96d298f31d0929a7e1ac93b5109c1de9008 100644 --- a/package/recalbox-romfs2/systems/scummvm/system.ini +++ b/package/recalbox-romfs2/systems/scummvm/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $F27a [properties] -type = computer +type = engine device.pad = mandatory device.keyboard = no device.mouse = mandatory diff --git a/package/recalbox-romfs2/systems/tic80/system.ini b/package/recalbox-romfs2/systems/tic80/system.ini index 55c2303a3e7bd7f5181a8680a487ce146e9e536f..df25018a8558233bba26f05c005d70affe8fb80e 100644 --- a/package/recalbox-romfs2/systems/tic80/system.ini +++ b/package/recalbox-romfs2/systems/tic80/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $F2e4 [properties] -type = virtual +type = fantasy device.pad = mandatory device.keyboard = no device.mouse = no diff --git a/package/recalbox-romfs2/systems/uzebox/system.ini b/package/recalbox-romfs2/systems/uzebox/system.ini index 2c3ab417aa76c6b43eee95dab140e8ea7eb44444..503fecedca2f97b7b37d038b98085274ea55a70a 100644 --- a/package/recalbox-romfs2/systems/uzebox/system.ini +++ b/package/recalbox-romfs2/systems/uzebox/system.ini @@ -27,7 +27,7 @@ readonly = 0 icon.unicode = $F2e6 [properties] -type = virtual +type = console device.pad = mandatory device.keyboard = no device.mouse = no diff --git a/projects/frontend/es-app/src/games/IFilter.h b/projects/frontend/es-app/src/games/IFilter.h index 102fb931c4dcafa73a60bb3c8b8815052b8600cb..c3fc944e7c9dc2f5186078dd029c9a74a489343e 100644 --- a/projects/frontend/es-app/src/games/IFilter.h +++ b/projects/frontend/es-app/src/games/IFilter.h @@ -16,5 +16,5 @@ class IFilter * @param file FileData to filter * @return Return true to validate this entry, false to ignore it */ - virtual bool ApplyFilter(const FileData& file) const = 0; + [[nodiscard]] virtual bool ApplyFilter(const FileData& file) const = 0; }; \ No newline at end of file diff --git a/projects/frontend/es-app/src/systems/SystemDescriptor.h b/projects/frontend/es-app/src/systems/SystemDescriptor.h index d8e019fe118f969aafd402d12fd70f59ba144841..4385f3b2b0feff88d7d193e1c3db87832e262f0e 100644 --- a/projects/frontend/es-app/src/systems/SystemDescriptor.h +++ b/projects/frontend/es-app/src/systems/SystemDescriptor.h @@ -10,17 +10,13 @@ class SystemDescriptor { public: - //! Maximum platform Id (usually only one is used) - static constexpr int sMaximumPlatformIds = 8; - //! Platform array - typedef PlatformIds::PlatformId PlateformIdentifiers[sMaximumPlatformIds]; - enum class SystemType { Unknown , //!< ?! Arcade , //!< Arcade system Console , //!< Home console Handheld, //!< Handheld console + Fantasy, //!< Fantasy console (no real hardware) Computer, //!< Computer Virtual , //!< Virtual system Engine , //!< Game Engine @@ -41,9 +37,7 @@ class SystemDescriptor * @brief Default constructor */ SystemDescriptor() - : mPlatformIds{} - , mPlateformCount(0) - , mIcon(0) + : mIcon(0) , mScreenScraperID(0) , mReleaseDate(0) , mManufacturer() @@ -117,6 +111,20 @@ class SystemDescriptor return *this; } + /*! + * @brief Set property information + * @param systemtype System type + * @param pad Pad requirement + * @param keyboard Keyboard requirement + * @param mouse Mouser requirement + * @param releasedate Release data + * @param manufacturer Manufacturer + * @param lightgun Has lightgun support? + * @param multiresolution Support CRT Multi resolution? + * @param multiregion Support multi region? + * @param ignoredfiles Ignored files + * @return This + */ SystemDescriptor& SetPropertiesInformation(const std::string& systemtype, const std::string& pad, const std::string& keyboard, @@ -142,8 +150,32 @@ class SystemDescriptor return *this; } - //! Clear all platform entries - SystemDescriptor& ClearPlatforms() { mPlateformCount = 0; return *this; } + /*! + * @brief Set property information for virtual systems + * @param systemtype System type (virtual most of the time, but arcade for all arcade) + * @param pad + * @param keyboard + * @param mouse + * @return + */ + SystemDescriptor& SetVirtualPropertiesInformation(SystemType systemtype, + DeviceRequirement keyboard, + DeviceRequirement mouse, + DeviceRequirement pad) + { + mType = systemtype; + mPad = pad; + mKeyboard = keyboard; + mMouse = mouse; + mReleaseDate = 0; + mManufacturer = "Virtual"; + mLightgun = false; + mCrtInterlaced = false; + mCrtMultiRegion = false; + mIgnoredFiles.clear(); + return *this; + } + //! Clear all emulator entries SystemDescriptor& ClearEmulators() { mEmulators.Clear(); return *this; } @@ -171,32 +203,32 @@ class SystemDescriptor * Accessors */ - const std::string& GUID() const { return mGUID; } - const std::string& Name() const { return mName; } - const std::string& FullName() const { return mFullName; } + [[nodiscard]] const std::string& GUID() const { return mGUID; } + [[nodiscard]] const std::string& Name() const { return mName; } + [[nodiscard]] const std::string& FullName() const { return mFullName; } - const Path& RomPath() const { return mPath; } - const std::string& Extension() const { return mExtensions; } - const std::string& ThemeFolder() const { return mThemeFolder; } - const std::string& Command() const { return mCommand.empty() ? mDefaultCommand : mCommand; } - unsigned int Icon() const { return (unsigned int)mIcon; } - std::string IconPrefix() const; + [[nodiscard]] const Path& RomPath() const { return mPath; } + [[nodiscard]] const std::string& Extension() const { return mExtensions; } + [[nodiscard]] const std::string& ThemeFolder() const { return mThemeFolder; } + [[nodiscard]] const std::string& Command() const { return mCommand.empty() ? mDefaultCommand : mCommand; } + [[nodiscard]] unsigned int Icon() const { return (unsigned int)mIcon; } + [[nodiscard]] std::string IconPrefix() const; - int ScreenScaperID() const { return mScreenScraperID; } + [[nodiscard]] int ScreenScaperID() const { return mScreenScraperID; } - int ReleaseDate() const { return mReleaseDate; } - const std::string& Manufacturer() const { return mManufacturer; } + [[nodiscard]] int ReleaseDate() const { return mReleaseDate; } + [[nodiscard]] const std::string& Manufacturer() const { return mManufacturer; } - SystemType Type() const { return mType; } - DeviceRequirement PadRequirement() const { return mPad; } - DeviceRequirement KeyboardRequirement() const { return mKeyboard; } - DeviceRequirement MouseRequirement() const { return mMouse; } - bool LightGun() const { return mLightgun; } - bool CrtHighResolution() const { return mCrtInterlaced; } - bool CrtMultiRegion() const { return mCrtMultiRegion; } - const std::string& IgnoredFiles() const { return mIgnoredFiles; } + [[nodiscard]] SystemType Type() const { return mType; } + [[nodiscard]] DeviceRequirement PadRequirement() const { return mPad; } + [[nodiscard]] DeviceRequirement KeyboardRequirement() const { return mKeyboard; } + [[nodiscard]] DeviceRequirement MouseRequirement() const { return mMouse; } + [[nodiscard]] bool LightGun() const { return mLightgun; } + [[nodiscard]] bool CrtHighResolution() const { return mCrtInterlaced; } + [[nodiscard]] bool CrtMultiRegion() const { return mCrtMultiRegion; } + [[nodiscard]] const std::string& IgnoredFiles() const { return mIgnoredFiles; } - bool HasNetPlayCores() const + [[nodiscard]] bool HasNetPlayCores() const { for(int i = mEmulators.Count(); --i >= 0; ) for(int j = mEmulators.EmulatorAt(i).CoreCount(); --j >= 0; ) @@ -205,7 +237,7 @@ class SystemDescriptor return false; } - bool IsSoftpatching(std::string emulatorName, std::string coreName) const + [[nodiscard]] bool IsSoftpatching(const std::string& emulatorName, const std::string& coreName) const { for(int i = mEmulators.Count(); --i >= 0; ) { @@ -217,10 +249,10 @@ class SystemDescriptor return false; } - bool IsPort() const { return mPort; } - bool IsReadOnly() const { return mReadOnly; } + [[nodiscard]] bool IsPort() const { return mPort; } + [[nodiscard]] bool IsReadOnly() const { return mReadOnly; } - const EmulatorList& EmulatorTree() const { return mEmulators; } + [[nodiscard]] const EmulatorList& EmulatorTree() const { return mEmulators; } private: static std::string mDefaultCommand; //!< Default command @@ -229,8 +261,6 @@ class SystemDescriptor std::string mGUID; //!< System GUID std::string mName; //!< Short name ("snes") std::string mFullName; //!< Full name ("Super Nintendo Entertainment System") - PlateformIdentifiers mPlatformIds; //!< Platform identifiers - int mPlateformCount; //!< Platform count // Descriptor Path mPath; //!< Rom path std::string mThemeFolder; //!< Theme sub-folder @@ -262,6 +292,7 @@ class SystemDescriptor if (systemtype == "arcade" ) result = SystemType::Arcade; else if (systemtype == "console" ) result = SystemType::Console; else if (systemtype == "handheld") result = SystemType::Handheld; + else if (systemtype == "fantasy" ) result = SystemType::Fantasy; else if (systemtype == "computer") result = SystemType::Computer; else if (systemtype == "virtual" ) result = SystemType::Virtual; else if (systemtype == "engine" ) result = SystemType::Engine; diff --git a/projects/frontend/es-app/src/systems/SystemDeserializer.cpp b/projects/frontend/es-app/src/systems/SystemDeserializer.cpp index 1688e7e11a3d8b2b3a72e20b1f40500592dad691..5dd5b73321161f8640c785dc002c5a82206a273c 100644 --- a/projects/frontend/es-app/src/systems/SystemDeserializer.cpp +++ b/projects/frontend/es-app/src/systems/SystemDeserializer.cpp @@ -28,7 +28,6 @@ void SystemDeserializer::DeserializeEmulatorTree(XmlNode emulators, EmulatorList bool SystemDeserializer::Deserialize(int index, SystemDescriptor& systemDescriptor) { systemDescriptor.ClearEmulators(); - systemDescriptor.ClearPlatforms(); XmlNode systemNode = mSystemList[index]; // System Information diff --git a/projects/frontend/es-app/src/systems/SystemManager.cpp b/projects/frontend/es-app/src/systems/SystemManager.cpp index ecc52292fbbd7c9b79e225ace40b853930b1fb7b..7d91c1c0dc7ea47b2252dd7e1a60f87b14b70295 100644 --- a/projects/frontend/es-app/src/systems/SystemManager.cpp +++ b/projects/frontend/es-app/src/systems/SystemManager.cpp @@ -14,14 +14,13 @@ #include #include #include -#include #include #include SystemManager::RomSources SystemManager::GetRomSource(const SystemDescriptor& systemDescriptor, PortTypes port) { RomSources roots; - if (Strings::Contains(systemDescriptor.RomPath().ToString(), sRootTag)) + //if (Strings::Contains(systemDescriptor.RomPath().ToString(), sRootTag)) { std::string rootTag(sRootTag); // Share_init roms @@ -46,7 +45,7 @@ SystemManager::RomSources SystemManager::GetRomSource(const SystemDescriptor& sy } } } - else + /*else { // For compatibility until we move romfs bool ok = false; @@ -67,7 +66,7 @@ SystemManager::RomSources SystemManager::GetRomSource(const SystemDescriptor& sy roots[systemDescriptor.RomPath().ToString()] = false; { LOG(LogError) << "[System] " << systemDescriptor.RomPath().ToString() << " is a standalone folder."; } } - } + }*/ return roots; } @@ -322,7 +321,7 @@ SystemData* SystemManager::CreateFavoriteSystem(const std::string& name, const s SystemDescriptor descriptor; descriptor.SetSystemInformation(fullName, name, fullName) - .SetPropertiesInformation("virtual", "mandatory", "mandatory", "mandatory", "2020-01-01", "None", false, false, false, "") + .SetVirtualPropertiesInformation(SystemDescriptor::SystemType::Virtual, SystemDescriptor::DeviceRequirement::Recommended, SystemDescriptor::DeviceRequirement::Recommended, SystemDescriptor::DeviceRequirement::Recommended) .SetDescriptorInformation("", "", themeFolder, "", "", false, false); SystemData* result = new SystemData(*this, descriptor, SystemData::Properties::Virtual | SystemData::Properties::AlwaysFlat | SystemData::Properties::Favorite); @@ -345,6 +344,10 @@ SystemData* SystemManager::CreateFavoriteSystem(const std::string& name, const s SystemData* SystemManager::CreateMetaSystem(const std::string& name, const std::string& fullName, const std::string& themeFolder, const std::vector& systems, + SystemDescriptor::SystemType systemType, + SystemDescriptor::DeviceRequirement keyboard, + SystemDescriptor::DeviceRequirement mouse, + SystemDescriptor::DeviceRequirement pad, SystemData::Properties properties, FileData::StringMap& doppelganger, FileSorts::Sorts fixedSort) { @@ -353,7 +356,7 @@ SystemData* SystemManager::CreateMetaSystem(const std::string& name, const std:: SystemDescriptor descriptor; descriptor.SetSystemInformation(fullName, name, fullName) - .SetPropertiesInformation("engine", "mandatory", "optional", "no", "2020-01-01", "None", false, false, false, "") + .SetVirtualPropertiesInformation(systemType, keyboard, mouse, pad) .SetDescriptorInformation("", "", themeFolder, "", "", false, false); SystemData* result = new SystemData(*this, descriptor, SystemData::Properties::Virtual | properties, fixedSort); @@ -376,6 +379,10 @@ SystemData* SystemManager::CreateMetaSystem(const std::string& name, const std:: SystemData* SystemManager::CreateMetaSystem(const std::string& name, const std::string& fullName, const std::string& themeFolder, const FileData::List& games, + SystemDescriptor::SystemType systemType, + SystemDescriptor::DeviceRequirement keyboard, + SystemDescriptor::DeviceRequirement mouse, + SystemDescriptor::DeviceRequirement pad, SystemData::Properties properties, FileData::StringMap& doppelganger, FileSorts::Sorts fixedSort) { @@ -384,7 +391,7 @@ SystemData* SystemManager::CreateMetaSystem(const std::string& name, const std:: SystemDescriptor descriptor; descriptor.SetSystemInformation(fullName, name, fullName) - .SetPropertiesInformation("virtual", "mandatory", "mandatory", "mandatory", "2020-01-01", "None", false, false, false, "") + .SetVirtualPropertiesInformation(systemType, keyboard, mouse, pad) .SetDescriptorInformation("", "", themeFolder, "", "", false, false); SystemData* result = new SystemData(*this, descriptor, SystemData::Properties::Virtual | properties, fixedSort); @@ -483,7 +490,12 @@ bool SystemManager::AddArcadeMetaSystem() // Create meta-system SystemData::Properties properties = SystemData::Properties::Virtual; if (hideOriginals) properties |= SystemData::Properties::Searchable; - SystemData* arcade = CreateMetaSystem("arcade", "Arcade", "arcade", arcades, properties, doppelganger); + SystemData* arcade = CreateMetaSystem("arcade", "Arcade", "arcade", arcades, + SystemDescriptor::SystemType::Arcade, + SystemDescriptor::DeviceRequirement::None, + SystemDescriptor::DeviceRequirement::None, + SystemDescriptor::DeviceRequirement::Required, + properties, doppelganger); { LOG(LogInfo) << "[System] Creating Arcade meta-system"; } int position = RecalboxConf::Instance().GetCollectionArcadePosition() % (int)mVisibleSystemVector.size(); auto it = position >= 0 ? mVisibleSystemVector.begin() + position : mVisibleSystemVector.end() + (position + 1); @@ -523,7 +535,12 @@ bool SystemManager::AddPorts() if ((!RecalboxConf::Instance().GetCollectionHide("ports")) || (mVisibleSystemVector.size() == 1)) { // Create meta-system - SystemData* portSystem = CreateMetaSystem("ports", "Ports", "ports", ports, SystemData::Properties::Virtual | SystemData::Properties::Searchable, doppelganger); + SystemData* portSystem = CreateMetaSystem("ports", "Ports", "ports", ports, + SystemDescriptor::SystemType::Port, + SystemDescriptor::DeviceRequirement::None, + SystemDescriptor::DeviceRequirement::None, + SystemDescriptor::DeviceRequirement::Required, + SystemData::Properties::Virtual | SystemData::Properties::Searchable, doppelganger); { LOG(LogInfo) << "[System] Creating Ports"; } // Seek default position int position = RecalboxConf::Instance().GetCollectionPosition("ports") % (int)mVisibleSystemVector.size(); @@ -537,7 +554,12 @@ bool SystemManager::AddPorts() return !ports.empty(); } -bool SystemManager::AddManuallyFilteredMetasystem(IFilter* filter, FileData::Comparer comparer, const std::string& identifier, const std::string& fullname, SystemData::Properties properties, FileSorts::Sorts fixedSort) +bool SystemManager::AddManuallyFilteredMetasystem(IFilter* filter, FileData::Comparer comparer, const std::string& identifier, + SystemDescriptor::SystemType systemType, + SystemDescriptor::DeviceRequirement keyboard, + SystemDescriptor::DeviceRequirement mouse, + SystemDescriptor::DeviceRequirement pad, + const std::string& fullname, SystemData::Properties properties, FileSorts::Sorts fixedSort) { // Collection activated? bool collection = RecalboxConf::Instance().GetCollection(identifier); @@ -579,7 +601,9 @@ bool SystemManager::AddManuallyFilteredMetasystem(IFilter* filter, FileData::Com // Create! { LOG(LogInfo) << "[System] Creating " << fullname << " meta-system"; } - SystemData* allsystem = CreateMetaSystem(identifier, _S(fullname), theme, allGames, properties, doppelganger, fixedSort); + SystemData* allsystem = CreateMetaSystem(identifier, _S(fullname), theme, allGames, + systemType, keyboard, mouse, pad, + properties, doppelganger, fixedSort); // And add the system int position = RecalboxConf::Instance().GetCollectionPosition(identifier) % (int) mVisibleSystemVector.size(); @@ -636,7 +660,12 @@ bool SystemManager::AddLightGunMetaSystem() { LOG(LogInfo) << "[System] Creating " << fullname << " meta-system"; } SystemData::Properties props = SystemData::Properties::Virtual | SystemData::Properties::AlwaysFlat; - SystemData* allsystem = CreateMetaSystem(identifier, _S(fullname), theme, allGames, props, doppelganger); + SystemData* allsystem = CreateMetaSystem(identifier, _S(fullname), theme, allGames, + SystemDescriptor::SystemType::Virtual, + SystemDescriptor::DeviceRequirement::None, + SystemDescriptor::DeviceRequirement::None, + SystemDescriptor::DeviceRequirement::Required, + props, doppelganger); // And add the system int position = RecalboxConf::Instance().GetCollectionPosition(identifier) % (int) mVisibleSystemVector.size(); @@ -659,8 +688,12 @@ bool SystemManager::AddAllGamesMetaSystem() return fileData.IsDisplayable(); } } filter; - return AddManuallyFilteredMetasystem(&filter, nullptr, sAllGamesSystemShortName, sAllGamesSystemFullName, - SystemData::Properties::None); + return AddManuallyFilteredMetasystem(&filter, nullptr, sAllGamesSystemShortName, + SystemDescriptor::SystemType::Virtual, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + sAllGamesSystemFullName, SystemData::Properties::None); } bool SystemManager::AddMultiplayerMetaSystems() @@ -673,8 +706,12 @@ bool SystemManager::AddMultiplayerMetaSystems() return file.IsDisplayable() && (file.Metadata().PlayerMin() > 1 || file.Metadata().PlayerMax() > 1); } } filter; - return AddManuallyFilteredMetasystem(&filter, nullptr, sMultiplayerSystemShortName, sMultiplayerSystemFullName, - SystemData::Properties::None); + return AddManuallyFilteredMetasystem(&filter, nullptr, sMultiplayerSystemShortName, + SystemDescriptor::SystemType::Virtual, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + sMultiplayerSystemFullName, SystemData::Properties::None); } bool SystemManager::AddLastPlayedMetaSystem() @@ -687,7 +724,12 @@ bool SystemManager::AddLastPlayedMetaSystem() return file.IsDisplayable() && file.Metadata().LastPlayedEpoc() != 0; } } filter; - return AddManuallyFilteredMetasystem(&filter, nullptr, sLastPlayedSystemShortName, sLastPlayedSystemFullName, + return AddManuallyFilteredMetasystem(&filter, nullptr, sLastPlayedSystemShortName, + SystemDescriptor::SystemType::Virtual, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + sLastPlayedSystemFullName, SystemData::Properties::FixedSort | SystemData::Properties::AlwaysFlat, FileSorts::Sorts::LastPlayedDescending); } @@ -718,7 +760,12 @@ bool SystemManager::AddGenresMetaSystem() for(const auto& genre : genres) { Filter filter(genre.first); - AddManuallyFilteredMetasystem(&filter, nullptr, genre.second, Genres::GetName(genre.first), + AddManuallyFilteredMetasystem(&filter, nullptr, genre.second, + SystemDescriptor::SystemType::Virtual, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + SystemDescriptor::DeviceRequirement::Unknown, + Genres::GetName(genre.first), SystemData::Properties::None); } return true; @@ -834,6 +881,10 @@ bool SystemManager::LoadSystemConfigurations(FileNotifier& gamelistWatcher, bool if (path.Exists()) gamelistWatcher.WatchFile(path); + ThemeData::SetThemeChanged(true); + for(SystemData* system : mAllSystemVector) + system->loadTheme(); + return true; } diff --git a/projects/frontend/es-app/src/systems/SystemManager.h b/projects/frontend/es-app/src/systems/SystemManager.h index 278d127adfa8f5a24b27c4c71e294774a8b5922f..4bfa4b6d082841898b6df76e496a24b73c318dd6 100644 --- a/projects/frontend/es-app/src/systems/SystemManager.h +++ b/projects/frontend/es-app/src/systems/SystemManager.h @@ -105,7 +105,6 @@ class SystemManager : HashSet& mWatcherIgnoredFiles; - private: //! The system manager is instructed to reload game list from disk, not only from gamelist.xml bool mForceReload; @@ -155,10 +154,20 @@ class SystemManager : * @param filter Filter to apply * @param comparer Comparer to sort items. If null, no sorting is applied * @param identifier System identifier (short name) + * @param systemType System type + * @param keyboard Keyboard requirement + * @param mouse Mouse requirement + * @param pad Pad requirement * @param fullname System full name + * @param properties System property flags + * @param fixedSort Fixed sort if required * @return True if the system has been added */ bool AddManuallyFilteredMetasystem(IFilter* filter, FileData::Comparer comparer, const std::string& identifier, + SystemDescriptor::SystemType systemType, + SystemDescriptor::DeviceRequirement keyboard, + SystemDescriptor::DeviceRequirement mouse, + SystemDescriptor::DeviceRequirement pad, const std::string& fullname, SystemData::Properties properties, FileSorts::Sorts fixedSort = FileSorts::Sorts::FileNameAscending); @@ -231,12 +240,20 @@ class SystemManager : * @param fullName Target system fullname * @param themeFolder Theme folder name * @param systems System to fetch games to aggregate into a single list + * @param systemType System type + * @param keyboard Keyboard requirement + * @param mouse Mouse requirement + * @param pad Pad requirement * @param properties System properties * @param doppelganger Map to FileData * @return New meta-system */ SystemData* CreateMetaSystem(const std::string& name, const std::string& fullName, const std::string& themeFolder, const std::vector& systems, + SystemDescriptor::SystemType systemType, + SystemDescriptor::DeviceRequirement keyboard, + SystemDescriptor::DeviceRequirement mouse, + SystemDescriptor::DeviceRequirement pad, SystemData::Properties properties, FileData::StringMap& doppelganger, FileSorts::Sorts fixedSort = FileSorts::Sorts::FileNameAscending); @@ -246,12 +263,20 @@ class SystemManager : * @param fullName Target system fullname * @param themeFolder Theme folder name * @param games Games to add + * @param systemType System type + * @param keyboard Keyboard requirement + * @param mouse Mouse requirement + * @param pad Pad requirement * @param properties System properties * @param doppelganger Map to FileData * @return New meta-system */ SystemData* CreateMetaSystem(const std::string& name, const std::string& fullName, const std::string& themeFolder, const FileData::List& games, + SystemDescriptor::SystemType systemType, + SystemDescriptor::DeviceRequirement keyboard, + SystemDescriptor::DeviceRequirement mouse, + SystemDescriptor::DeviceRequirement pad, SystemData::Properties properties, FileData::StringMap& doppelganger, FileSorts::Sorts fixedSort = FileSorts::Sorts::FileNameAscending); @@ -330,6 +355,7 @@ class SystemManager : , mWatcherIgnoredFiles(watcherIgnoredFiles) , mForceReload(false) { + ThemeData::SetSystemManager(this); } //! Destructor @@ -463,7 +489,7 @@ class SystemManager : } //! Get emulator manager - const EmulatorManager& Emulators() const { return mEmulatorManager; } + [[nodiscard]] const EmulatorManager& Emulators() const { return mEmulatorManager; } /*! * @brief Search games from text diff --git a/projects/frontend/es-app/src/views/SystemView.cpp b/projects/frontend/es-app/src/views/SystemView.cpp index 2fcf2d4790147376db0cfbddc63b3849161ae6f0..e9d62bc109d9867acab548ca5e4c3e43abf5c9e2 100755 --- a/projects/frontend/es-app/src/views/SystemView.cpp +++ b/projects/frontend/es-app/src/views/SystemView.cpp @@ -196,7 +196,7 @@ void SystemView::populate() { mEntries.clear(); - for (const auto it : mSystemManager.GetVisibleSystemList()) + for (auto *const it : mSystemManager.GetVisibleSystemList()) if (it->HasVisibleGame()) addSystem(it); } @@ -618,9 +618,9 @@ void SystemView::renderCarousel(const Transform4x4f& trans) { int index = i; while (index < 0) - index += mEntries.size(); + index += (int)mEntries.size(); while (index >= (int)mEntries.size()) - index -= mEntries.size(); + index -= (int)mEntries.size(); Transform4x4f logoTrans = carouselTrans; logoTrans.translate(Vector3f((float)i * logoSpacing[0] + xOff, (float)i * logoSpacing[1] + yOff, 0)); diff --git a/projects/frontend/es-core/src/themes/ThemeData.cpp b/projects/frontend/es-core/src/themes/ThemeData.cpp index d5593bc7b25a73349590af9cc9d8a0ca95980789..b279150a449f5b6c775a40ea934199c60c9606c1 100755 --- a/projects/frontend/es-core/src/themes/ThemeData.cpp +++ b/projects/frontend/es-core/src/themes/ThemeData.cpp @@ -12,11 +12,13 @@ #include "RootFolders.h" #include "ThemeException.h" #include "MenuThemeData.h" +#include "utils/String.h" ThemeData* ThemeData::sCurrent = nullptr; bool ThemeData::sThemeChanged = false; bool ThemeData::sThemeHasMenuView = true; bool ThemeData::sThemeHasHelpSystem = true; +SystemManager* ThemeData::sSystemManager = nullptr; std::vector& ThemeData::SupportedViews() { @@ -977,4 +979,93 @@ bool ThemeData::isFolderHandled() const { const auto* elem = getElement("detailed", "md_folder_name", "text"); return elem != nullptr && elem->HasProperty("pos"); -} \ No newline at end of file +} + +std::string ThemeData::resolveSystemVariable(const std::string& systemThemeFolder, const std::string& path, std::string& randomPath) +{ + std::string lccc = Strings::ToLowerASCII(RecalboxConf::Instance().GetSystemLanguage()); + std::string lc = "en"; + std::string cc = "us"; + if (lccc.size() >= 5) + { + size_t pos = lccc.find('_'); + if (pos >=2 && pos < lccc.size() - 1) + { + lc = lccc.substr(0, pos); + cc = lccc.substr(pos + 1); + } + } + + // Take path + std::string result = path; + + // Handle system specific variables + if (sSystemManager != nullptr && !systemThemeFolder.empty()) + { + const std::vector systems = sSystemManager->GetAllSystemList(); + int index = -1; + for (int i = (int)systems.size(); --i >= 0;) + if (systems[i]->ThemeFolder() == systemThemeFolder) { index = i; break; } + + if (index >= 0) + { + const SystemData& currentSystem = *systems[index]; + // System properties + Strings::ReplaceAllIn(result, "$manufacturer", currentSystem.Descriptor().Manufacturer()); + Strings::ReplaceAllIn(result, "$lightgun", Strings::ToString(currentSystem.Descriptor().LightGun())); + Strings::ReplaceAllIn(result, "$keyboard", ConvertDeviceRequirement(currentSystem.Descriptor().KeyboardRequirement())); + Strings::ReplaceAllIn(result, "$mouse", ConvertDeviceRequirement(currentSystem.Descriptor().MouseRequirement())); + Strings::ReplaceAllIn(result, "$pad", ConvertDeviceRequirement(currentSystem.Descriptor().PadRequirement())); + Strings::ReplaceAllIn(result, "$type", ConvertSystemType(currentSystem.Descriptor().Type())); + + // system+x and system-x variables (up to +10/-10) + for (int i = 0; i < 10; ++i) + { + int positiveOffset = (index + i) % (int) systems.size(); + Strings::ReplaceAllIn(result, String("$system+").Append(positiveOffset), systems[positiveOffset]->ThemeFolder()); + + int negativeOffset = ((index + (int) systems.size() * 10) - i) % (int) systems.size(); + Strings::ReplaceAllIn(result, String("$system-").Append(positiveOffset), systems[negativeOffset]->ThemeFolder()); + } + } + } + + // Replace static variables + Strings::ReplaceAllIn(result, "$system", systemThemeFolder); + Strings::ReplaceAllIn(result, "$language", lc); + Strings::ReplaceAllIn(result, "$country", cc); + + return PickRandomPath(result, randomPath);; +} + +std::string ThemeData::ConvertDeviceRequirement(SystemDescriptor::DeviceRequirement requirement) +{ + switch(requirement) + { + case SystemDescriptor::DeviceRequirement::Unknown: return "unknown"; + case SystemDescriptor::DeviceRequirement::Required: return "required"; + case SystemDescriptor::DeviceRequirement::Recommended: return "recommended"; + case SystemDescriptor::DeviceRequirement::Optional: return "pptional"; + case SystemDescriptor::DeviceRequirement::None: + default: break; + } + return "none"; +} + +std::string ThemeData::ConvertSystemType(SystemDescriptor::SystemType type) +{ + switch(type) + { + case SystemDescriptor::SystemType::Arcade: return "arcade"; + case SystemDescriptor::SystemType::Console: return "console"; + case SystemDescriptor::SystemType::Handheld: return "handheld"; + case SystemDescriptor::SystemType::Fantasy: return "fantasy"; + case SystemDescriptor::SystemType::Computer: return "computer"; + case SystemDescriptor::SystemType::Virtual: return "virtual"; + case SystemDescriptor::SystemType::Engine: return "engine"; + case SystemDescriptor::SystemType::Port: return "port"; + case SystemDescriptor::SystemType::Unknown: + default: break; + } + return "unknown"; +} diff --git a/projects/frontend/es-core/src/themes/ThemeData.h b/projects/frontend/es-core/src/themes/ThemeData.h index f289b114bd724acc4720d68fcd464486f5100b01..4b58494555f2d1d95242eb87e5ec9d7acbfd6254 100755 --- a/projects/frontend/es-core/src/themes/ThemeData.h +++ b/projects/frontend/es-core/src/themes/ThemeData.h @@ -8,6 +8,7 @@ #include #include "pugixml/pugixml.hpp" #include "ThemeElement.h" +#include template class TextListComponent; @@ -17,6 +18,7 @@ class ImageComponent; class NinePatchComponent; class TextComponent; class WindowManager; +class SystemManager; class ThemeSet { @@ -58,6 +60,11 @@ class ThemeData // throws ThemeException void loadFile(const std::string& systemThemeFolder, const Path& path); + /*! + * @brief Just another crappy thing in this awfully crappy class... + */ + static void SetSystemManager(SystemManager* sm) { sSystemManager = sm; } + enum class ElementProperty { NormalizedPair, @@ -109,9 +116,9 @@ class ThemeData std::string mGameClipView; std::string mSystemThemeFolder; std::string mRandomPath; + static SystemManager* sSystemManager; static constexpr const char* sRandomMethod = "$random("; - void parseFeatures(const pugi::xml_node& themeRoot); void parseIncludes(const pugi::xml_node& themeRoot); void parseViews(const pugi::xml_node& themeRoot); @@ -123,50 +130,30 @@ class ThemeData static void findRegion(const pugi::xml_document& doc, std::map& sets); static bool CheckThemeOption(std::string& selected, const std::map& subsets, const std::string& subset); - static std::string resolveSystemVariable(const std::string& systemThemeFolder, const std::string& path, std::string& randomPath) - { - std::string lccc = Strings::ToLowerASCII(RecalboxConf::Instance().GetSystemLanguage()); - std::string lc = "en"; - std::string cc = "us"; - if (lccc.size() >= 5) - { - size_t pos = lccc.find('_'); - if (pos >=2 && pos < lccc.size() - 1) - { - lc = lccc.substr(0, pos); - cc = lccc.substr(pos + 1); - } - } - - std::string result = path; - Strings::ReplaceAllIn(result, "$system", systemThemeFolder); - Strings::ReplaceAllIn(result, "$language", lc); - Strings::ReplaceAllIn(result, "$country", cc); - - - return PickRandomPath(result, randomPath);; - } + static std::string resolveSystemVariable(const std::string& systemThemeFolder, const std::string& path, std::string& randomPath); static std::string PickRandomPath(std::string value, std::string& randomPath) { - if(!Strings::Contains(value, sRandomMethod)) return value; std::string args = Strings::Extract(value, sRandomMethod, ")", 8, 1); - if(randomPath.empty()) { std::vector paths = Strings::Split(args, ','); std::random_device rd; std::default_random_engine engine(rd()); - const int max = paths.size(); + const int max = (int)paths.size(); std::uniform_int_distribution distrib{0, max-1}; - randomPath = paths[distrib(engine)]; + randomPath = std::string(paths[distrib(engine)]); } return Strings::Replace(value, sRandomMethod + args + ")", randomPath); } std::map mViews; + + static std::string ConvertDeviceRequirement(SystemDescriptor::DeviceRequirement requirement); + + static std::string ConvertSystemType(SystemDescriptor::SystemType type); }; diff --git a/projects/frontend/es-core/src/utils/String.cpp b/projects/frontend/es-core/src/utils/String.cpp index 61060a155cdafe0d3efde6f7b43f457552eeba23..0bbe4253c20e746044e7792a026b09e3373c2499 100644 --- a/projects/frontend/es-core/src/utils/String.cpp +++ b/projects/frontend/es-core/src/utils/String.cpp @@ -7,6 +7,7 @@ #include "String.h" #include "Unicode.h" +#include const String StringStatics::Empty; const char CR = '\r'; @@ -36,3 +37,13 @@ bool StringStatics::InitializeUTF8Tables() return true; } +String::String(const Path& source) + : std::string(source.ToString()) +{ +} + +String& String::operator =(const Path& source) +{ + *((std::string*)this) = source.ToString(); + return *this; +} \ No newline at end of file diff --git a/projects/frontend/es-core/src/utils/String.h b/projects/frontend/es-core/src/utils/String.h index 20edaaafdeaf290f2690fcf976958c1e68d9d8d8..854dfbc34779e6c1904230d55294c79bbb263595 100644 --- a/projects/frontend/es-core/src/utils/String.h +++ b/projects/frontend/es-core/src/utils/String.h @@ -20,6 +20,7 @@ #define LEGACY_STRING(x) x, (int)sizeof(x)-1 class String; +class Path; /*! * @brief Strings statics moved in this class @@ -431,6 +432,12 @@ class String : public std::string */ String(const std::string& source, int from, int length) : std::string(source, (size_t)from, (size_t)length) {} + /*! + * @brief Build a string from a path + * @param source Source path + */ + String(const Path& source); + /*! * @brief Build a string from a part of a source c-string * @param source Source string @@ -802,14 +809,14 @@ class String : public std::string * @param from Start position to get string from * @return new string */ - String SubString(int from) const { return String(*this, from); } + [[nodiscard]] String SubString(int from) const { return String(*this, from); } /*! * @brief Get the substring starting at position "from" for "length" characters * @param from Start position to get string from * @param length Amount of character to get * @return new string */ - String SubString(int from, int length) const { return String(*this, from, length); } + [[nodiscard]] String SubString(int from, int length) const { return String(*this, from, length); } // Replacers @@ -3281,6 +3288,13 @@ class String : public std::string */ String& operator =(String&& source) noexcept { if (&source != this) *((std::string*)this) = source; return *this; } + /*! + * @brief Copy assignment from Path + * @param source Source path + * @return This + */ + String& operator =(const Path& source); + #ifdef NO_STD_STRING_AUTOBOXING /*! * @brief Copy assignment operator