Formatted the es-app source tree using clang-format.

This commit is contained in:
Leon Styhre 2021-07-07 20:03:42 +02:00
parent 745cf6ff92
commit af5e32e121
78 changed files with 4345 additions and 4308 deletions

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@ class Window;
struct SystemEnvironmentData; struct SystemEnvironmentData;
enum CollectionSystemType { enum CollectionSystemType {
AUTO_ALL_GAMES, AUTO_ALL_GAMES, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
AUTO_LAST_PLAYED, AUTO_LAST_PLAYED,
AUTO_FAVORITES, AUTO_FAVORITES,
CUSTOM_COLLECTION CUSTOM_COLLECTION
@ -115,13 +115,17 @@ public:
// Repopulate the collection, which is basically a forced update of its complete content. // Repopulate the collection, which is basically a forced update of its complete content.
void repopulateCollection(SystemData* sysData); void repopulateCollection(SystemData* sysData);
inline std::map<std::string, CollectionSystemData, stringComparator> std::map<std::string, CollectionSystemData, stringComparator> getAutoCollectionSystems()
getAutoCollectionSystems() { return mAutoCollectionSystemsData; }; {
inline std::map<std::string, CollectionSystemData, stringComparator> return mAutoCollectionSystemsData;
getCustomCollectionSystems() { return mCustomCollectionSystemsData; }; }
inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }; std::map<std::string, CollectionSystemData, stringComparator> getCustomCollectionSystems()
inline bool isEditing() { return mIsEditingCustom; }; {
inline std::string getEditingCollection() { return mEditingCollection; }; return mCustomCollectionSystemsData;
}
SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }
bool isEditing() { return mIsEditingCustom; }
std::string getEditingCollection() { return mEditingCollection; }
private: private:
static CollectionSystemsManager* sInstance; static CollectionSystemsManager* sInstance;
@ -143,7 +147,9 @@ private:
SystemData* getAllGamesCollection(); SystemData* getAllGamesCollection();
// Create a new empty collection system based on the name and declaration. // Create a new empty collection system based on the name and declaration.
SystemData* createNewCollectionEntry(std::string name, SystemData* createNewCollectionEntry(std::string name,
CollectionSystemDecl sysDecl, bool index = true, bool custom = false); CollectionSystemDecl sysDecl,
bool index = true,
bool custom = false);
// Populate an automatic collection system. // Populate an automatic collection system.
void populateAutoCollection(CollectionSystemData* sysData); void populateAutoCollection(CollectionSystemData* sysData);
// Populate a custom collection system. // Populate a custom collection system.
@ -151,8 +157,8 @@ private:
// Functions to handle System View removal and insertion of collections: // Functions to handle System View removal and insertion of collections:
void removeCollectionsFromDisplayedSystems(); void removeCollectionsFromDisplayedSystems();
void addEnabledCollectionsToDisplayedSystems(std::map<std::string, void addEnabledCollectionsToDisplayedSystems(
CollectionSystemData, stringComparator>* colSystemData); std::map<std::string, CollectionSystemData, stringComparator>* colSystemData);
// Auxiliary functions: // Auxiliary functions:
std::vector<std::string> getSystemsFromConfig(); std::vector<std::string> getSystemsFromConfig();

View file

@ -10,14 +10,16 @@
// These numbers and strings need to be manually updated for a new version. // These numbers and strings need to be manually updated for a new version.
// Do this version number update as the very last commit for the new release version. // Do this version number update as the very last commit for the new release version.
// clang-format off
#define PROGRAM_VERSION_MAJOR 1 #define PROGRAM_VERSION_MAJOR 1
#define PROGRAM_VERSION_MINOR 0 #define PROGRAM_VERSION_MINOR 1
#define PROGRAM_VERSION_MAINTENANCE 0 #define PROGRAM_VERSION_MAINTENANCE 0
// clang-format on
#define PROGRAM_VERSION_STRING "1.1.0-rc-dev" #define PROGRAM_VERSION_STRING "1.1.0-rc-dev"
#define PROGRAM_BUILT_STRING __DATE__ " - " __TIME__ #define PROGRAM_BUILT_STRING __DATE__ " - " __TIME__
#define RESOURCE_VERSION_STRING "1,1,0\0" #define RESOURCE_VERSION_STRING "1,1,0\0"
#define RESOURCE_VERSION PROGRAM_VERSION_MAJOR,PROGRAM_VERSION_MINOR,PROGRAM_VERSION_MAINTENANCE #define RESOURCE_VERSION PROGRAM_VERSION_MAJOR, PROGRAM_VERSION_MINOR, PROGRAM_VERSION_MAINTENANCE
#endif // ES_APP_EMULATION_STATION_H #endif // ES_APP_EMULATION_STATION_H

View file

@ -10,12 +10,6 @@
#include "FileData.h" #include "FileData.h"
#include "guis/GuiInfoPopup.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
@ -26,33 +20,37 @@
#include "Scripting.h" #include "Scripting.h"
#include "SystemData.h" #include "SystemData.h"
#include "Window.h" #include "Window.h"
#include "guis/GuiInfoPopup.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include <assert.h> #include <assert.h>
FileData::FileData( FileData::FileData(FileType type,
FileType type, const std::string& path,
const std::string& path, SystemEnvironmentData* envData,
SystemEnvironmentData* envData, SystemData* system)
SystemData* system) : mType(type)
: mType(type), , mPath(path)
mPath(path), , mSystem(system)
mSystem(system), , mEnvData(envData)
mEnvData(envData), , mSourceFileData(nullptr)
mSourceFileData(nullptr), , mParent(nullptr)
mParent(nullptr), , mOnlyFolders(false)
mOnlyFolders(false), , mDeletionFlag(false)
mDeletionFlag(false), // Metadata is set in the constructor.
// Metadata is set in the constructor. , metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
{ {
// Metadata needs at least a name field (since that's what getName() will return). // Metadata needs at least a name field (since that's what getName() will return).
if (metadata.get("name").empty()) { if (metadata.get("name").empty()) {
if ((system->hasPlatformId(PlatformIds::ARCADE) || if ((system->hasPlatformId(PlatformIds::ARCADE) ||
system->hasPlatformId(PlatformIds::SNK_NEO_GEO)) && system->hasPlatformId(PlatformIds::SNK_NEO_GEO)) &&
metadata.getType() != FOLDER_METADATA) { metadata.getType() != FOLDER_METADATA) {
// If it's a MAME or Neo Geo game, expand the game name accordingly. // If it's a MAME or Neo Geo game, expand the game name accordingly.
metadata.set("name", metadata.set("name", MameNames::getInstance()->getCleanName(getCleanName()));
MameNames::getInstance()->getCleanName(getCleanName()));
} }
else { else {
if (metadata.getType() == FOLDER_METADATA && Utils::FileSystem::isHidden(mPath)) { if (metadata.getType() == FOLDER_METADATA && Utils::FileSystem::isHidden(mPath)) {
@ -89,6 +87,7 @@ std::string FileData::getCleanName() const
const std::string& FileData::getName() const std::string& FileData::getName()
{ {
// Return metadata name.
return metadata.get("name"); return metadata.get("name");
} }
@ -144,14 +143,13 @@ const std::vector<FileData*> FileData::getChildrenRecursive() const
{ {
std::vector<FileData*> childrenRecursive; std::vector<FileData*> childrenRecursive;
for (auto it = mChildrenByFilename.cbegin(); for (auto it = mChildrenByFilename.cbegin(); it != mChildrenByFilename.cend(); it++) {
it != mChildrenByFilename.cend(); it++) {
childrenRecursive.push_back((*it).second); childrenRecursive.push_back((*it).second);
// Recurse through any subdirectories. // Recurse through any subdirectories.
if ((*it).second->getType() == FOLDER) { if ((*it).second->getType() == FOLDER) {
std::vector<FileData*> childrenSubdirectory = (*it).second->getChildrenRecursive(); std::vector<FileData*> childrenSubdirectory = (*it).second->getChildrenRecursive();
childrenRecursive.insert(childrenRecursive.end(), childrenRecursive.insert(childrenRecursive.end(), childrenSubdirectory.begin(),
childrenSubdirectory.begin(), childrenSubdirectory.end()); childrenSubdirectory.end());
} }
} }
@ -171,13 +169,13 @@ const std::string FileData::getROMDirectory()
// Expand home path if ~ is used. // Expand home path if ~ is used.
romDirPath = Utils::FileSystem::expandHomePath(romDirPath); romDirPath = Utils::FileSystem::expandHomePath(romDirPath);
#if defined(_WIN64) #if defined(_WIN64)
if (romDirPath.back() != '\\') if (romDirPath.back() != '\\')
romDirPath = romDirPath + "\\"; romDirPath = romDirPath + "\\";
#else #else
if (romDirPath.back() != '/') if (romDirPath.back() != '/')
romDirPath = romDirPath + "/"; romDirPath = romDirPath + "/";
#endif #endif
} }
// If %ESPATH% is used for the ROM path configuration, then expand it to the binary // If %ESPATH% is used for the ROM path configuration, then expand it to the binary
@ -202,10 +200,10 @@ const std::string FileData::getMediaDirectory()
// If %ESPATH% is used for the media directory configuration, then expand it to the // If %ESPATH% is used for the media directory configuration, then expand it to the
// binary directory of ES-DE. // binary directory of ES-DE.
mediaDirPath = Utils::String::replace( mediaDirPath =
mediaDirPath, "%ESPATH%", Utils::FileSystem::getExePath()); Utils::String::replace(mediaDirPath, "%ESPATH%", Utils::FileSystem::getExePath());
if (mediaDirPath.back() != '/') if (mediaDirPath.back() != '/')
mediaDirPath = mediaDirPath + "/"; mediaDirPath = mediaDirPath + "/";
} }
@ -219,11 +217,11 @@ const std::string FileData::getMediafilePath(std::string subdirectory, std::stri
// Extract possible subfolders from the path. // Extract possible subfolders from the path.
if (mEnvData->mStartPath != "") if (mEnvData->mStartPath != "")
subFolders = Utils::String::replace( subFolders =
Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, ""); Utils::String::replace(Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, "");
const std::string tempPath = getMediaDirectory() + mSystemName + "/" + subdirectory + const std::string tempPath = getMediaDirectory() + mSystemName + "/" + subdirectory +
subFolders + "/" + getDisplayName(); subFolders + "/" + getDisplayName();
// Look for an image file in the media directory. // Look for an image file in the media directory.
for (int i = 0; i < extList.size(); i++) { for (int i = 0; i < extList.size(); i++) {
@ -253,31 +251,37 @@ const std::string FileData::getImagePath() const
const std::string FileData::get3DBoxPath() const const std::string FileData::get3DBoxPath() const
{ {
// Return path to the 3D box image.
return getMediafilePath("3dboxes", "3dbox"); return getMediafilePath("3dboxes", "3dbox");
} }
const std::string FileData::getCoverPath() const const std::string FileData::getCoverPath() const
{ {
// Return path to the cover image.
return getMediafilePath("covers", "cover"); return getMediafilePath("covers", "cover");
} }
const std::string FileData::getMarqueePath() const const std::string FileData::getMarqueePath() const
{ {
// Return path to the marquee image.
return getMediafilePath("marquees", "marquee"); return getMediafilePath("marquees", "marquee");
} }
const std::string FileData::getMiximagePath() const const std::string FileData::getMiximagePath() const
{ {
// Return path to the miximage.
return getMediafilePath("miximages", "miximage"); return getMediafilePath("miximages", "miximage");
} }
const std::string FileData::getScreenshotPath() const const std::string FileData::getScreenshotPath() const
{ {
// Return path to the screenshot image.
return getMediafilePath("screenshots", "screenshot"); return getMediafilePath("screenshots", "screenshot");
} }
const std::string FileData::getThumbnailPath() const const std::string FileData::getThumbnailPath() const
{ {
// Return path to the thumbnail image.
return getMediafilePath("thumbnails", "thumbnail"); return getMediafilePath("thumbnails", "thumbnail");
} }
@ -288,11 +292,11 @@ const std::string FileData::getVideoPath() const
// Extract possible subfolders from the path. // Extract possible subfolders from the path.
if (mEnvData->mStartPath != "") if (mEnvData->mStartPath != "")
subFolders = Utils::String::replace( subFolders =
Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, ""); Utils::String::replace(Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, "");
const std::string tempPath = const std::string tempPath =
getMediaDirectory() + mSystemName + "/videos" + subFolders + "/" + getDisplayName(); getMediaDirectory() + mSystemName + "/videos" + subFolders + "/" + getDisplayName();
// Look for media in the media directory. // Look for media in the media directory.
for (int i = 0; i < extList.size(); i++) { for (int i = 0; i < extList.size(); i++) {
@ -322,7 +326,8 @@ const std::vector<FileData*>& FileData::getChildrenListToDisplay()
} }
std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask, std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask,
bool displayedOnly, bool countAllGames) const bool displayedOnly,
bool countAllGames) const
{ {
std::vector<FileData*> out; std::vector<FileData*> out;
FileFilterIndex* idx = mSystem->getIndex(); FileFilterIndex* idx = mSystem->getIndex();
@ -354,7 +359,8 @@ std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask,
} }
std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders, std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
bool excludeRecursively, bool respectExclusions) const bool excludeRecursively,
bool respectExclusions) const
{ {
std::vector<FileData*> out; std::vector<FileData*> out;
@ -375,7 +381,7 @@ std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
if ((*it)->getChildren().size() > 0) { if ((*it)->getChildren().size() > 0) {
std::vector<FileData*> subChildren = (*it)->getScrapeFilesRecursive( std::vector<FileData*> subChildren = (*it)->getScrapeFilesRecursive(
includeFolders, excludeRecursively, respectExclusions); includeFolders, excludeRecursively, respectExclusions);
out.insert(out.cend(), subChildren.cbegin(), subChildren.cend()); out.insert(out.cend(), subChildren.cbegin(), subChildren.cend());
} }
} }
@ -383,32 +389,25 @@ std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
return out; return out;
} }
std::string FileData::getKey() { std::string FileData::getKey() { return getFileName(); }
return getFileName();
}
const bool FileData::isArcadeAsset() const bool FileData::isArcadeAsset()
{ {
const std::string stem = Utils::FileSystem::getStem(mPath); const std::string stem = Utils::FileSystem::getStem(mPath);
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) && mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
(MameNames::getInstance()->isBios(stem) || (MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem)));
MameNames::getInstance()->isDevice(stem)));
} }
const bool FileData::isArcadeGame() const bool FileData::isArcadeGame()
{ {
const std::string stem = Utils::FileSystem::getStem(mPath); const std::string stem = Utils::FileSystem::getStem(mPath);
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) && mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
(!MameNames::getInstance()->isBios(stem) && (!MameNames::getInstance()->isBios(stem) && !MameNames::getInstance()->isDevice(stem)));
!MameNames::getInstance()->isDevice(stem)));
} }
FileData* FileData::getSourceFileData() FileData* FileData::getSourceFileData() { return this; }
{
return this;
}
void FileData::addChild(FileData* file) void FileData::addChild(FileData* file)
{ {
@ -441,7 +440,7 @@ void FileData::removeChild(FileData* file)
} }
void FileData::sort(ComparisonFunction& comparator, void FileData::sort(ComparisonFunction& comparator,
std::pair<unsigned int, unsigned int>& gameCount) std::pair<unsigned int, unsigned int>& gameCount)
{ {
mOnlyFolders = true; mOnlyFolders = true;
mHasFolders = false; mHasFolders = false;
@ -456,17 +455,17 @@ void FileData::sort(ComparisonFunction& comparator,
if (!showHiddenGames) { if (!showHiddenGames) {
for (auto it = mChildren.begin(); it != mChildren.end();) { for (auto it = mChildren.begin(); it != mChildren.end();) {
// If the option to hide hidden games has been set and the game is hidden, // If the option to hide hidden games has been set and the game is hidden,
// then skip it. Normally games are hidden during loading of the gamelists in // then skip it. Normally games are hidden during loading of the gamelists in
// Gamelist::parseGamelist() and this code should only run when a user has marked // Gamelist::parseGamelist() and this code should only run when a user has marked
// an entry manually as hidden. So upon the next application startup, this game // an entry manually as hidden. So upon the next application startup, this game
// should be filtered already at that earlier point. // should be filtered already at that earlier point.
if ((*it)->getHidden()) if ((*it)->getHidden())
it = mChildren.erase(it); it = mChildren.erase(it);
// Also hide folders where all its entries have been hidden, unless it's a // Also hide folders where all its entries have been hidden, unless it's a
// grouped custom collection. // grouped custom collection.
else if ((*it)->getType() == FOLDER && (*it)->getChildren().size() == 0 && else if ((*it)->getType() == FOLDER && (*it)->getChildren().size() == 0 &&
!(*it)->getSystem()->isGroupedCustomCollection()) !(*it)->getSystem()->isGroupedCustomCollection())
it = mChildren.erase(it); it = mChildren.erase(it);
else else
it++; it++;
@ -502,11 +501,11 @@ void FileData::sort(ComparisonFunction& comparator,
// If the requested sorting is not by filename, then sort in ascending filename order // If the requested sorting is not by filename, then sort in ascending filename order
// as a first step, in order to get a correct secondary sorting. // as a first step, in order to get a correct secondary sorting.
if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator && if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator &&
getSortTypeFromString("filename, descending").comparisonFunction != comparator) { getSortTypeFromString("filename, descending").comparisonFunction != comparator) {
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
} }
if (foldersOnTop && mOnlyFolders) if (foldersOnTop && mOnlyFolders)
@ -523,9 +522,9 @@ void FileData::sort(ComparisonFunction& comparator,
// If the requested sorting is not by filename, then sort in ascending filename order // If the requested sorting is not by filename, then sort in ascending filename order
// as a first step, in order to get a correct secondary sorting. // as a first step, in order to get a correct secondary sorting.
if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator && if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator &&
getSortTypeFromString("filename, descending").comparisonFunction != comparator) getSortTypeFromString("filename, descending").comparisonFunction != comparator)
std::stable_sort(mChildren.begin(), mChildren.end(), std::stable_sort(mChildren.begin(), mChildren.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildren.begin(), mChildren.end(), comparator); std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
} }
@ -555,7 +554,7 @@ void FileData::sort(ComparisonFunction& comparator,
} }
void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
std::pair<unsigned int, unsigned int>& gameCount) std::pair<unsigned int, unsigned int>& gameCount)
{ {
mOnlyFolders = true; mOnlyFolders = true;
mHasFolders = false; mHasFolders = false;
@ -634,39 +633,39 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
// some folders as favorites is probably a rare situation. // some folders as favorites is probably a rare situation.
if (!mOnlyFolders && mChildrenFavoritesFolders.size() > 0) { if (!mOnlyFolders && mChildrenFavoritesFolders.size() > 0) {
mChildrenFolders.insert(mChildrenFolders.end(), mChildrenFavoritesFolders.begin(), mChildrenFolders.insert(mChildrenFolders.end(), mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end()); mChildrenFavoritesFolders.end());
mChildrenFavoritesFolders.erase(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.erase(mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end()); mChildrenFavoritesFolders.end());
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
} }
// If the requested sorting is not by filename, then sort in ascending filename order // If the requested sorting is not by filename, then sort in ascending filename order
// as a first step, in order to get a correct secondary sorting. // as a first step, in order to get a correct secondary sorting.
if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator && if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator &&
getSortTypeFromString("filename, descending").comparisonFunction != comparator) { getSortTypeFromString("filename, descending").comparisonFunction != comparator) {
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(), std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(),
getSortTypeFromString("filename, ascending").comparisonFunction); getSortTypeFromString("filename, ascending").comparisonFunction);
} }
// Sort favorite games and the other games separately. // Sort favorite games and the other games separately.
if (foldersOnTop && mOnlyFolders) { if (foldersOnTop && mOnlyFolders) {
std::stable_sort(mChildrenFavoritesFolders.begin(), std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
mChildrenFavoritesFolders.end(), comparator); comparator);
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator); std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
} }
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator); std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator); std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
// Iterate through any child favorite folders. // Iterate through any child favorite folders.
for (auto it = mChildrenFavoritesFolders.cbegin(); it != for (auto it = mChildrenFavoritesFolders.cbegin(); // Line break.
mChildrenFavoritesFolders.cend(); it++) { it != mChildrenFavoritesFolders.cend(); it++) {
if ((*it)->getChildren().size() > 0) if ((*it)->getChildren().size() > 0)
(*it)->sortFavoritesOnTop(comparator, gameCount); (*it)->sortFavoritesOnTop(comparator, gameCount);
} }
@ -690,9 +689,9 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
// Combine the individually sorted favorite games and other games vectors. // Combine the individually sorted favorite games and other games vectors.
mChildren.erase(mChildren.begin(), mChildren.end()); mChildren.erase(mChildren.begin(), mChildren.end());
mChildren.reserve(mChildrenFavoritesFolders.size() + mChildrenFolders.size() + mChildren.reserve(mChildrenFavoritesFolders.size() + mChildrenFolders.size() +
mChildrenFavorites.size() + mChildrenOthers.size()); mChildrenFavorites.size() + mChildrenOthers.size());
mChildren.insert(mChildren.end(), mChildrenFavoritesFolders.begin(), mChildren.insert(mChildren.end(), mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end()); mChildrenFavoritesFolders.end());
mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end()); mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end());
mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end()); mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end());
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end()); mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());
@ -711,10 +710,10 @@ void FileData::sort(const SortType& type, bool mFavoritesOnTop)
void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount) void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
{ {
bool isKidMode = (Settings::getInstance()->getString("UIMode") == "kid" || bool isKidMode = (Settings::getInstance()->getString("UIMode") == "kid" ||
Settings::getInstance()->getBool("ForceKid")); Settings::getInstance()->getBool("ForceKid"));
(Settings::getInstance()->getString("UIMode") == "kid" || (Settings::getInstance()->getString("UIMode") == "kid" ||
Settings::getInstance()->getBool("ForceKid")); Settings::getInstance()->getBool("ForceKid"));
for (unsigned int i = 0; i < mChildren.size(); i++) { for (unsigned int i = 0; i < mChildren.size(); i++) {
if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) { if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) {
@ -731,7 +730,8 @@ void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
mGameCount = gameCount; mGameCount = gameCount;
} }
FileData::SortType FileData::getSortTypeFromString(std::string desc) { FileData::SortType FileData::getSortTypeFromString(std::string desc)
{
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes; std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
@ -752,10 +752,12 @@ void FileData::launchGame(Window* window)
// Check if there is a launch command override for the game // Check if there is a launch command override for the game
// and the corresponding option to use it has been set. // and the corresponding option to use it has been set.
if (Settings::getInstance()->getBool("LaunchCommandOverride") && if (Settings::getInstance()->getBool("LaunchCommandOverride") &&
!metadata.get("launchcommand").empty()) !metadata.get("launchcommand").empty()) {
command = metadata.get("launchcommand"); command = metadata.get("launchcommand");
else }
else {
command = mEnvData->mLaunchCommand; command = mEnvData->mLaunchCommand;
}
std::string commandRaw = command; std::string commandRaw = command;
@ -794,14 +796,14 @@ void FileData::launchGame(Window* window)
// Hack to show an error message if there was no emulator entry in es_find_rules.xml. // Hack to show an error message if there was no emulator entry in es_find_rules.xml.
if (binaryPath.substr(0, 18) == "NO EMULATOR RULE: ") { if (binaryPath.substr(0, 18) == "NO EMULATOR RULE: ") {
std::string emulatorEntry = binaryPath.substr(18, binaryPath.size() - 18); std::string emulatorEntry = binaryPath.substr(18, binaryPath.size() - 18);
LOG(LogError) << "Couldn't launch game, either there is no emulator entry for \"" << LOG(LogError)
emulatorEntry << "\" in es_find_rules.xml or there are no systempath or staticpath " << "Couldn't launch game, either there is no emulator entry for \"" << emulatorEntry
"rules defined"; << "\" in es_find_rules.xml or there are no systempath or staticpath rules defined";
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: MISSING EMULATOR CONFIGURATION FOR '" + GuiInfoPopup* s = new GuiInfoPopup(
emulatorEntry + "'", 6000); window, "ERROR: MISSING EMULATOR CONFIGURATION FOR '" + emulatorEntry + "'", 6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
@ -810,20 +812,23 @@ void FileData::launchGame(Window* window)
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR, HAS IT " \ GuiInfoPopup* s = new GuiInfoPopup(window,
"BEEN PROPERLY INSTALLED?", 6000); "ERROR: COULDN'T FIND EMULATOR, HAS IT "
"BEEN PROPERLY INSTALLED?",
6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
else { else {
#if defined(_WIN64) #if defined(_WIN64)
LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" << LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \""
Utils::String::replace(Utils::String::replace( << Utils::String::replace(
binaryPath, "%ESPATH%", esPath), "/", "\\") << "\""; Utils::String::replace(binaryPath, "%ESPATH%", esPath), "/", "\\")
#else << "\"";
LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" << #else
Utils::String::replace(binaryPath, "%ESPATH%", esPath) << "\""; LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \""
#endif << Utils::String::replace(binaryPath, "%ESPATH%", esPath) << "\"";
#endif
} }
// If %EMUPATH% is used in es_systems.xml for this system, then check that the core // If %EMUPATH% is used in es_systems.xml for this system, then check that the core
@ -834,8 +839,8 @@ void FileData::launchGame(Window* window)
unsigned int quotationMarkPos = 0; unsigned int quotationMarkPos = 0;
if (command.find("\"%EMUPATH%", emuPathPos - 1) != std::string::npos) { if (command.find("\"%EMUPATH%", emuPathPos - 1) != std::string::npos) {
hasQuotationMark = true; hasQuotationMark = true;
quotationMarkPos = static_cast<unsigned int>( quotationMarkPos =
command.find("\"", emuPathPos + 9) - emuPathPos); static_cast<unsigned int>(command.find("\"", emuPathPos + 9) - emuPathPos);
} }
size_t spacePos = command.find(" ", emuPathPos + quotationMarkPos); size_t spacePos = command.find(" ", emuPathPos + quotationMarkPos);
std::string coreRaw; std::string coreRaw;
@ -843,22 +848,23 @@ void FileData::launchGame(Window* window)
if (spacePos != std::string::npos) { if (spacePos != std::string::npos) {
coreRaw = command.substr(emuPathPos, spacePos - emuPathPos); coreRaw = command.substr(emuPathPos, spacePos - emuPathPos);
coreFile = Utils::FileSystem::getParent(binaryPath) + coreFile = Utils::FileSystem::getParent(binaryPath) +
command.substr(emuPathPos + 9, spacePos - emuPathPos - 9); command.substr(emuPathPos + 9, spacePos - emuPathPos - 9);
if (hasQuotationMark) { if (hasQuotationMark) {
coreRaw.pop_back(); coreRaw.pop_back();
coreFile.pop_back(); coreFile.pop_back();
} }
if (!Utils::FileSystem::isRegularFile(coreFile) && if (!Utils::FileSystem::isRegularFile(coreFile) &&
!Utils::FileSystem::isSymlink(coreFile)) { !Utils::FileSystem::isSymlink(coreFile)) {
LOG(LogError) << "Couldn't launch game, emulator core file \"" << LOG(LogError) << "Couldn't launch game, emulator core file \""
Utils::FileSystem::getFileName(coreFile) << "\" not found"; << Utils::FileSystem::getFileName(coreFile) << "\" not found";
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
GuiInfoPopup* s = new GuiInfoPopup(window, GuiInfoPopup* s = new GuiInfoPopup(
"ERROR: COULDN'T FIND EMULATOR CORE FILE '" + window,
Utils::String::toUpper(Utils::FileSystem::getFileName(coreFile)) + "ERROR: COULDN'T FIND EMULATOR CORE FILE '" +
"'", 6000); Utils::String::toUpper(Utils::FileSystem::getFileName(coreFile)) + "'",
6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
@ -877,8 +883,10 @@ void FileData::launchGame(Window* window)
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: INVALID ENTRY IN SYSTEMS " \ GuiInfoPopup* s = new GuiInfoPopup(window,
"CONFIGURATION FILE", 6000); "ERROR: INVALID ENTRY IN SYSTEMS "
"CONFIGURATION FILE",
6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
@ -886,13 +894,13 @@ void FileData::launchGame(Window* window)
// Error handling in case of no core find rule. // Error handling in case of no core find rule.
if (coreEntry != "" && emulatorCorePaths.empty()) { if (coreEntry != "" && emulatorCorePaths.empty()) {
LOG(LogError) << "Couldn't launch game, either there is no core entry for \"" << LOG(LogError) << "Couldn't launch game, either there is no core entry for \"" << coreEntry
coreEntry << "\" in es_find_rules.xml or there are no corepath rules defined"; << "\" in es_find_rules.xml or there are no corepath rules defined";
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: MISSING CORE CONFIGURATION FOR '" + GuiInfoPopup* s = new GuiInfoPopup(
coreEntry + "'", 6000); window, "ERROR: MISSING CORE CONFIGURATION FOR '" + coreEntry + "'", 6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
@ -913,46 +921,47 @@ void FileData::launchGame(Window* window)
separatorPos = quotePos; separatorPos = quotePos;
if (separatorPos != std::string::npos) { if (separatorPos != std::string::npos) {
coreName = command.substr(coreFilePos + 2, separatorPos - (coreFilePos + 2)); coreName = command.substr(coreFilePos + 2, separatorPos - (coreFilePos + 2));
#if defined(_WIN64) #if defined(_WIN64)
std::string coreFile = Utils::FileSystem::expandHomePath(path + "\\" + coreName); std::string coreFile = Utils::FileSystem::expandHomePath(path + "\\" + coreName);
#else #else
std::string coreFile = Utils::FileSystem::expandHomePath(path + "/" + coreName); std::string coreFile = Utils::FileSystem::expandHomePath(path + "/" + coreName);
#endif #endif
// Expand %EMUPATH% if it has been used in the %CORE_ variable. // Expand %EMUPATH% if it has been used in the %CORE_ variable.
size_t stringPos = coreFile.find("%EMUPATH%"); size_t stringPos = coreFile.find("%EMUPATH%");
if (stringPos != std::string::npos) { if (stringPos != std::string::npos) {
#if defined (_WIN64) #if defined(_WIN64)
coreFile = Utils::String::replace(coreFile.replace(stringPos, 9, coreFile = Utils::String::replace(
Utils::FileSystem::getParent(binaryPath)), "/", "\\"); coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath)), "/",
#else "\\");
#else
coreFile = coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath)); coreFile = coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath));
#endif #endif
} }
// Expand %ESPATH% if it has been used in the %CORE_ variable. // Expand %ESPATH% if it has been used in the %CORE_ variable.
stringPos = coreFile.find("%ESPATH%"); stringPos = coreFile.find("%ESPATH%");
if (stringPos != std::string::npos) { if (stringPos != std::string::npos) {
coreFile = coreFile.replace(stringPos, 8, esPath); coreFile = coreFile.replace(stringPos, 8, esPath);
#if defined(_WIN64) #if defined(_WIN64)
coreFile = Utils::String::replace(coreFile, "/", "\\"); coreFile = Utils::String::replace(coreFile, "/", "\\");
#endif #endif
} }
if (Utils::FileSystem::isRegularFile(coreFile) || if (Utils::FileSystem::isRegularFile(coreFile) ||
Utils::FileSystem::isSymlink(coreFile)) { Utils::FileSystem::isSymlink(coreFile)) {
foundCoreFile = true; foundCoreFile = true;
// Escape any blankspaces. // Escape any blankspaces.
if (coreFile.find(" ") != std::string::npos) if (coreFile.find(" ") != std::string::npos)
coreFile = Utils::FileSystem::getEscapedPath(coreFile); coreFile = Utils::FileSystem::getEscapedPath(coreFile);
command.replace(coreEntryPos, separatorPos - coreEntryPos, coreFile); command.replace(coreEntryPos, separatorPos - coreEntryPos, coreFile);
#if !defined(_WIN64) #if !defined(_WIN64)
// Remove any quotation marks as it would make the launch function fail. // Remove any quotation marks as it would make the launch function fail.
if (command.find("\"") != std::string::npos) if (command.find("\"") != std::string::npos)
command = Utils::String::replace(command, "\"", ""); command = Utils::String::replace(command, "\"", "");
#endif #endif
break; break;
} }
} }
@ -961,23 +970,28 @@ void FileData::launchGame(Window* window)
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: INVALID ENTRY IN SYSTEMS " \ GuiInfoPopup* s = new GuiInfoPopup(window,
"CONFIGURATION FILE", 6000); "ERROR: INVALID ENTRY IN SYSTEMS "
"CONFIGURATION FILE",
6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
} }
if (!foundCoreFile && coreName.size() > 0) { if (!foundCoreFile && coreName.size() > 0) {
LOG(LogError) << "Couldn't launch game, emulator core file \"" << LOG(LogError) << "Couldn't launch game, emulator core file \""
coreName.substr(0, coreName.size()) << "\" not found"; << coreName.substr(0, coreName.size()) << "\" not found";
LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << "Raw emulator launch command:";
LOG(LogError) << commandRaw; LOG(LogError) << commandRaw;
LOG(LogError) << LOG(LogError)
"Tried to find the core file using these paths as defined by es_find_rules.xml:"; << "Tried to find the core file using these paths as defined by es_find_rules.xml:";
LOG(LogError) << Utils::String::vectorToDelimitedString(emulatorCorePaths, ", "); LOG(LogError) << Utils::String::vectorToDelimitedString(emulatorCorePaths, ", ");
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR CORE FILE '" + GuiInfoPopup* s =
Utils::String::toUpper(coreName.substr(0, coreName.size()) + "'"), 6000); new GuiInfoPopup(window,
"ERROR: COULDN'T FIND EMULATOR CORE FILE '" +
Utils::String::toUpper(coreName.substr(0, coreName.size()) + "'"),
6000);
window->setInfoPopup(s); window->setInfoPopup(s);
return; return;
} }
@ -990,12 +1004,13 @@ void FileData::launchGame(Window* window)
// swapBuffers() is called here to turn the screen black to eliminate some potential // swapBuffers() is called here to turn the screen black to eliminate some potential
// flickering and to avoid showing the game launch message briefly when returning // flickering and to avoid showing the game launch message briefly when returning
// from the game. // from the game.
#if defined(_WIN64)
#if defined(_WIN64)
if (!(Settings::getInstance()->getBool("LaunchWorkaround") || if (!(Settings::getInstance()->getBool("LaunchWorkaround") ||
ViewController::get()->runInBackground(mSystem))) ViewController::get()->runInBackground(mSystem)))
#else #else
if (!ViewController::get()->runInBackground(mSystem)) if (!ViewController::get()->runInBackground(mSystem))
#endif #endif
Renderer::swapBuffers(); Renderer::swapBuffers();
Scripting::fireEvent("game-start", romPath, getSourceFileData()->metadata.get("name")); Scripting::fireEvent("game-start", romPath, getSourceFileData()->metadata.get("name"));
@ -1004,28 +1019,31 @@ void FileData::launchGame(Window* window)
LOG(LogDebug) << "Raw emulator launch command:"; LOG(LogDebug) << "Raw emulator launch command:";
LOG(LogDebug) << commandRaw; LOG(LogDebug) << commandRaw;
LOG(LogInfo) << "Expanded emulator launch command:"; LOG(LogInfo) << "Expanded emulator launch command:";
LOG(LogInfo) << command; LOG(LogInfo) << command;
// Possibly keep ES-DE running in the background while the game is launched. // Possibly keep ES-DE running in the background while the game is launched.
#if defined(_WIN64)
#if defined(_WIN64)
returnValue = launchGameWindows(Utils::String::stringToWideString(command), returnValue = launchGameWindows(Utils::String::stringToWideString(command),
ViewController::get()->runInBackground(mSystem)); ViewController::get()->runInBackground(mSystem));
#else #else
returnValue = launchGameUnix(command, ViewController::get()->runInBackground(mSystem)); returnValue = launchGameUnix(command, ViewController::get()->runInBackground(mSystem));
#endif #endif
// Notify the user in case of a failed game launch using a popup window. // Notify the user in case of a failed game launch using a popup window.
if (returnValue != 0) { if (returnValue != 0) {
LOG(LogWarning) << "...launch terminated with nonzero return value " << returnValue; LOG(LogWarning) << "...launch terminated with nonzero return value " << returnValue;
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR LAUNCHING GAME '" + GuiInfoPopup* s = new GuiInfoPopup(
Utils::String::toUpper(metadata.get("name")) + "' (ERROR CODE " + window,
Utils::String::toUpper(std::to_string(returnValue) + ")"), 6000); "ERROR LAUNCHING GAME '" + Utils::String::toUpper(metadata.get("name")) +
"' (ERROR CODE " + Utils::String::toUpper(std::to_string(returnValue) + ")"),
6000);
window->setInfoPopup(s); window->setInfoPopup(s);
} }
else { else {
// Stop showing the game launch notification. // Stop showing the game launch notification.
window->stopInfoPopup(); window->stopInfoPopup();
#if defined(_WIN64) #if defined(_WIN64)
// For some game systems or if the "RunInBackground" setting has been enabled, keep // For some game systems or if the "RunInBackground" setting has been enabled, keep
// ES-DE running while the game is launched. This pauses any video and keeps the // ES-DE running while the game is launched. This pauses any video and keeps the
// screensaver from getting activated. // screensaver from getting activated.
@ -1035,7 +1053,7 @@ void FileData::launchGame(Window* window)
// Normalize deltaTime so that the screensaver does not start immediately // Normalize deltaTime so that the screensaver does not start immediately
// when returning from the game. // when returning from the game.
window->normalizeNextUpdate(); window->normalizeNextUpdate();
#else #else
// For some game systems we need to keep ES-DE running while the game is launched. // For some game systems we need to keep ES-DE running while the game is launched.
// This pauses any video and keeps the screensaver from getting activated. // This pauses any video and keeps the screensaver from getting activated.
if (ViewController::get()->runInBackground(mSystem)) if (ViewController::get()->runInBackground(mSystem))
@ -1043,7 +1061,7 @@ void FileData::launchGame(Window* window)
// Normalize deltaTime so that the screensaver does not start immediately // Normalize deltaTime so that the screensaver does not start immediately
// when returning from the game. // when returning from the game.
window->normalizeNextUpdate(); window->normalizeNextUpdate();
#endif #endif
} }
Scripting::fireEvent("game-end", romPath, getSourceFileData()->metadata.get("name")); Scripting::fireEvent("game-end", romPath, getSourceFileData()->metadata.get("name"));
@ -1062,10 +1080,10 @@ void FileData::launchGame(Window* window)
// If the parent is a folder and it's not the root of the system, then update its lastplayed // If the parent is a folder and it's not the root of the system, then update its lastplayed
// timestamp to the same time as the game that was just launched. // timestamp to the same time as the game that was just launched.
if (gameToUpdate->getParent()->getType() == FOLDER && gameToUpdate->getParent()->getName() != if (gameToUpdate->getParent()->getType() == FOLDER &&
gameToUpdate->getSystem()->getFullName()) { gameToUpdate->getParent()->getName() != gameToUpdate->getSystem()->getFullName()) {
gameToUpdate->getParent()->metadata.set("lastplayed", gameToUpdate->getParent()->metadata.set("lastplayed",
gameToUpdate->metadata.get("lastplayed")); gameToUpdate->metadata.get("lastplayed"));
} }
CollectionSystemsManager::get()->refreshCollectionSystems(gameToUpdate); CollectionSystemsManager::get()->refreshCollectionSystems(gameToUpdate);
@ -1086,9 +1104,9 @@ std::string FileData::findEmulatorPath(std::string& command)
// Method 1, emulator binary is defined using find rules: // Method 1, emulator binary is defined using find rules:
#if defined(_WIN64) #if defined(_WIN64)
std::vector<std::string> emulatorWinRegistryPaths; std::vector<std::string> emulatorWinRegistryPaths;
#endif #endif
std::vector<std::string> emulatorSystemPaths; std::vector<std::string> emulatorSystemPaths;
std::vector<std::string> emulatorStaticPaths; std::vector<std::string> emulatorStaticPaths;
std::string emulatorEntry; std::string emulatorEntry;
@ -1101,10 +1119,10 @@ std::string FileData::findEmulatorPath(std::string& command)
} }
if (emulatorEntry != "") { if (emulatorEntry != "") {
#if defined(_WIN64) #if defined(_WIN64)
emulatorWinRegistryPaths = emulatorWinRegistryPaths =
SystemData::sFindRules.get()->mEmulators[emulatorEntry].winRegistryPaths; SystemData::sFindRules.get()->mEmulators[emulatorEntry].winRegistryPaths;
#endif #endif
emulatorSystemPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].systemPaths; emulatorSystemPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].systemPaths;
emulatorStaticPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].staticPaths; emulatorStaticPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].staticPaths;
} }
@ -1113,11 +1131,11 @@ std::string FileData::findEmulatorPath(std::string& command)
if (emulatorEntry != "" && emulatorSystemPaths.empty() && emulatorStaticPaths.empty()) if (emulatorEntry != "" && emulatorSystemPaths.empty() && emulatorStaticPaths.empty())
return "NO EMULATOR RULE: " + emulatorEntry; return "NO EMULATOR RULE: " + emulatorEntry;
#if defined(_WIN64) #if defined(_WIN64)
for (std::string path : emulatorWinRegistryPaths) { for (std::string path : emulatorWinRegistryPaths) {
// Search for the emulator using the App Paths keys in the Windows Registry. // Search for the emulator using the App Paths keys in the Windows Registry.
std::string registryKeyPath = std::string registryKeyPath =
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path; "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path;
HKEY registryKey; HKEY registryKey;
LSTATUS keyStatus = -1; LSTATUS keyStatus = -1;
@ -1126,33 +1144,19 @@ std::string FileData::findEmulatorPath(std::string& command)
DWORD pathSize = 1024; DWORD pathSize = 1024;
// First look in HKEY_CURRENT_USER. // First look in HKEY_CURRENT_USER.
keyStatus = RegOpenKeyEx( keyStatus = RegOpenKeyEx(HKEY_CURRENT_USER, registryKeyPath.c_str(), 0, KEY_QUERY_VALUE,
HKEY_CURRENT_USER, &registryKey);
registryKeyPath.c_str(),
0,
KEY_QUERY_VALUE,
&registryKey);
// If not found, then try in HKEY_LOCAL_MACHINE. // If not found, then try in HKEY_LOCAL_MACHINE.
if (keyStatus != ERROR_SUCCESS) { if (keyStatus != ERROR_SUCCESS) {
keyStatus = RegOpenKeyEx( keyStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryKeyPath.c_str(), 0,
HKEY_LOCAL_MACHINE, KEY_QUERY_VALUE, &registryKey);
registryKeyPath.c_str(),
0,
KEY_QUERY_VALUE,
&registryKey);
} }
// If the key exists, then try to retrieve the value. // If the key exists, then try to retrieve the value.
if (keyStatus == ERROR_SUCCESS) { if (keyStatus == ERROR_SUCCESS) {
pathStatus = RegGetValue( pathStatus = RegGetValue(registryKey, nullptr, nullptr, RRF_RT_REG_SZ, nullptr,
registryKey, &registryPath, &pathSize);
nullptr,
nullptr,
RRF_RT_REG_SZ,
nullptr,
&registryPath,
&pathSize);
} }
else { else {
RegCloseKey(registryKey); RegCloseKey(registryKey);
@ -1163,7 +1167,7 @@ std::string FileData::findEmulatorPath(std::string& command)
// so check for that as well. // so check for that as well.
if (pathStatus == ERROR_SUCCESS) { if (pathStatus == ERROR_SUCCESS) {
if (Utils::FileSystem::isRegularFile(registryPath) || if (Utils::FileSystem::isRegularFile(registryPath) ||
Utils::FileSystem::isSymlink(registryPath)) { Utils::FileSystem::isSymlink(registryPath)) {
command.replace(0, endPos + 1, registryPath); command.replace(0, endPos + 1, registryPath);
RegCloseKey(registryKey); RegCloseKey(registryKey);
return registryPath; return registryPath;
@ -1171,25 +1175,24 @@ std::string FileData::findEmulatorPath(std::string& command)
} }
RegCloseKey(registryKey); RegCloseKey(registryKey);
} }
#endif #endif
for (std::string path : emulatorSystemPaths) { for (std::string path : emulatorSystemPaths) {
#if defined(_WIN64) #if defined(_WIN64)
std::wstring pathWide = Utils::String::stringToWideString(path); std::wstring pathWide = Utils::String::stringToWideString(path);
// Search for the emulator using the PATH environmental variable. // Search for the emulator using the PATH environmental variable.
DWORD size = SearchPathW(nullptr, pathWide.c_str(), L".exe", 0, nullptr, nullptr); DWORD size = SearchPathW(nullptr, pathWide.c_str(), L".exe", 0, nullptr, nullptr);
if (size) { if (size) {
std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1 ); std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1);
wchar_t* fileName = nullptr; wchar_t* fileName = nullptr;
SearchPathW(nullptr, pathWide.c_str(), L".exe", size + 1 , SearchPathW(nullptr, pathWide.c_str(), L".exe", size + 1, pathBuffer.data(), &fileName);
pathBuffer.data(), &fileName);
std::wstring pathString = pathBuffer.data(); std::wstring pathString = pathBuffer.data();
if (pathString.length()) { if (pathString.length()) {
exePath = Utils::String::wideStringToString(pathString.substr(0, exePath = Utils::String::wideStringToString(
pathString.size() - std::wstring(fileName).size())); pathString.substr(0, pathString.size() - std::wstring(fileName).size()));
exePath.pop_back(); exePath.pop_back();
} }
} }
@ -1198,14 +1201,14 @@ std::string FileData::findEmulatorPath(std::string& command)
command.replace(0, endPos + 1, exePath); command.replace(0, endPos + 1, exePath);
return exePath; return exePath;
} }
#else #else
exePath = Utils::FileSystem::getPathToBinary(path); exePath = Utils::FileSystem::getPathToBinary(path);
if (exePath != "") { if (exePath != "") {
exePath += "/" + path; exePath += "/" + path;
command.replace(0, endPos + 1, exePath); command.replace(0, endPos + 1, exePath);
return exePath; return exePath;
} }
#endif #endif
} }
for (std::string path : emulatorStaticPaths) { for (std::string path : emulatorStaticPaths) {
@ -1214,11 +1217,10 @@ std::string FileData::findEmulatorPath(std::string& command)
path = Utils::String::replace(path, "%ESPATH%", Utils::FileSystem::getExePath()); path = Utils::String::replace(path, "%ESPATH%", Utils::FileSystem::getExePath());
// Likewise for the %ROMPATH% variable which expands to the configured ROM directory. // Likewise for the %ROMPATH% variable which expands to the configured ROM directory.
path = Utils::String::replace(path, "%ROMPATH%", getROMDirectory()); path = Utils::String::replace(path, "%ROMPATH%", getROMDirectory());
#if defined(_WIN64) #if defined(_WIN64)
path = Utils::String::replace(path, "/", "\\"); path = Utils::String::replace(path, "/", "\\");
#endif #endif
if (Utils::FileSystem::isRegularFile(path) || if (Utils::FileSystem::isRegularFile(path) || Utils::FileSystem::isSymlink(path)) {
Utils::FileSystem::isSymlink(path)) {
command.replace(0, endPos + 1, path); command.replace(0, endPos + 1, path);
return path; return path;
} }
@ -1228,9 +1230,9 @@ std::string FileData::findEmulatorPath(std::string& command)
// If %ESPATH% is used, then expand it to the binary directory of ES-DE. // If %ESPATH% is used, then expand it to the binary directory of ES-DE.
command = Utils::String::replace(command, "%ESPATH%", Utils::FileSystem::getExePath()); command = Utils::String::replace(command, "%ESPATH%", Utils::FileSystem::getExePath());
#if defined(_WIN64) #if defined(_WIN64)
command = Utils::String::replace(command, "/", "\\"); command = Utils::String::replace(command, "/", "\\");
#endif #endif
// If the first character is a quotation mark, then we need to extract up to the // If the first character is a quotation mark, then we need to extract up to the
// next quotation mark, otherwise we'll only extract up to the first space character. // next quotation mark, otherwise we'll only extract up to the first space character.
@ -1242,23 +1244,23 @@ std::string FileData::findEmulatorPath(std::string& command)
emuExecutable = command.substr(0, command.find(' ')); emuExecutable = command.substr(0, command.find(' '));
} }
#if defined(_WIN64) #if defined(_WIN64)
std::wstring emuExecutableWide = Utils::String::stringToWideString(emuExecutable); std::wstring emuExecutableWide = Utils::String::stringToWideString(emuExecutable);
// Search for the emulator using the PATH environmental variable. // Search for the emulator using the PATH environmental variable.
DWORD size = SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr); DWORD size = SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr);
if (size) { if (size) {
std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1 ); std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1);
wchar_t* fileName = nullptr; wchar_t* fileName = nullptr;
SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1 , SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1, pathBuffer.data(),
pathBuffer.data(), &fileName); &fileName);
exePath = Utils::String::wideStringToString(pathBuffer.data()); exePath = Utils::String::wideStringToString(pathBuffer.data());
} }
#else #else
if (Utils::FileSystem::isRegularFile(emuExecutable) || if (Utils::FileSystem::isRegularFile(emuExecutable) ||
Utils::FileSystem::isSymlink(emuExecutable)) { Utils::FileSystem::isSymlink(emuExecutable)) {
exePath = emuExecutable; exePath = emuExecutable;
} }
else { else {
@ -1266,14 +1268,16 @@ std::string FileData::findEmulatorPath(std::string& command)
if (exePath != "") if (exePath != "")
exePath += "/" + emuExecutable; exePath += "/" + emuExecutable;
} }
#endif #endif
return exePath; return exePath;
} }
CollectionFileData::CollectionFileData(FileData* file, SystemData* system) CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
: FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), : FileData(file->getSourceFileData()->getType(),
file->getSourceFileData()->getSystemEnvData(), system) file->getSourceFileData()->getPath(),
file->getSourceFileData()->getSystemEnvData(),
system)
{ {
// We use this constructor to create a clone of the filedata, and change its system. // We use this constructor to create a clone of the filedata, and change its system.
mSourceFileData = file->getSourceFileData(); mSourceFileData = file->getSourceFileData();
@ -1291,15 +1295,6 @@ CollectionFileData::~CollectionFileData()
mParent = nullptr; mParent = nullptr;
} }
std::string CollectionFileData::getKey() {
return getFullPath();
}
FileData* CollectionFileData::getSourceFileData()
{
return mSourceFileData;
}
void CollectionFileData::refreshMetadata() void CollectionFileData::refreshMetadata()
{ {
metadata = mSourceFileData->metadata; metadata = mSourceFileData->metadata;
@ -1310,9 +1305,9 @@ const std::string& CollectionFileData::getName()
{ {
if (mDirty) { if (mDirty) {
mCollectionFileName = mCollectionFileName =
Utils::String::removeParenthesis(mSourceFileData->metadata.get("name")); Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
mCollectionFileName += mCollectionFileName +=
" [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]"; " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
mDirty = false; mDirty = false;
} }

View file

@ -11,8 +11,8 @@
#ifndef ES_APP_FILE_DATA_H #ifndef ES_APP_FILE_DATA_H
#define ES_APP_FILE_DATA_H #define ES_APP_FILE_DATA_H
#include "utils/FileSystemUtil.h"
#include "MetaData.h" #include "MetaData.h"
#include "utils/FileSystemUtil.h"
#include <unordered_map> #include <unordered_map>
@ -30,8 +30,10 @@ enum FileType {
class FileData class FileData
{ {
public: public:
FileData(FileType type, const std::string& path, FileData(FileType type,
SystemEnvironmentData* envData, SystemData* system); const std::string& path,
SystemEnvironmentData* envData,
SystemData* system);
virtual ~FileData(); virtual ~FileData();
@ -41,17 +43,19 @@ public:
const bool getKidgame(); const bool getKidgame();
const bool getHidden(); const bool getHidden();
const bool getCountAsGame(); const bool getCountAsGame();
const std::pair<unsigned int, unsigned int> getGameCount() { return mGameCount; }; const std::pair<unsigned int, unsigned int> getGameCount() { return mGameCount; }
const bool getExcludeFromScraper(); const bool getExcludeFromScraper();
const std::vector<FileData*> getChildrenRecursive() const; const std::vector<FileData*> getChildrenRecursive() const;
inline FileType getType() const { return mType; } FileType getType() const { return mType; }
inline const std::string& getPath() const { return mPath; } const std::string& getPath() const { return mPath; }
inline FileData* getParent() const { return mParent; } FileData* getParent() const { return mParent; }
inline const std::unordered_map<std::string, FileData*>& getChildrenByFilename() const const std::unordered_map<std::string, FileData*>& getChildrenByFilename() const
{ return mChildrenByFilename; } {
inline const std::vector<FileData*>& getChildren() const { return mChildren; } return mChildrenByFilename;
inline SystemData* getSystem() const { return mSystem; } }
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } const std::vector<FileData*>& getChildren() const { return mChildren; }
SystemData* getSystem() const { return mSystem; }
SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
const bool getOnlyFoldersFlag() { return mOnlyFolders; } const bool getOnlyFoldersFlag() { return mOnlyFolders; }
const bool getHasFoldersFlag() { return mHasFolders; } const bool getHasFoldersFlag() { return mHasFolders; }
static const std::string getROMDirectory(); static const std::string getROMDirectory();
@ -66,29 +70,31 @@ public:
const std::string getThumbnailPath() const; const std::string getThumbnailPath() const;
const std::string getVideoPath() const; const std::string getVideoPath() const;
bool getDeletionFlag() { return mDeletionFlag; }; bool getDeletionFlag() { return mDeletionFlag; }
void setDeletionFlag(bool setting) { mDeletionFlag = setting; }; void setDeletionFlag(bool setting) { mDeletionFlag = setting; }
const std::vector<FileData*>& getChildrenListToDisplay(); const std::vector<FileData*>& getChildrenListToDisplay();
std::vector<FileData*> getFilesRecursive(unsigned int typeMask, std::vector<FileData*> getFilesRecursive(unsigned int typeMask,
bool displayedOnly = false, bool countAllGames = true) const; bool displayedOnly = false,
std::vector<FileData*> getScrapeFilesRecursive(bool includeFolders, bool excludeRecursively, bool countAllGames = true) const;
bool respectExclusions) const; std::vector<FileData*> getScrapeFilesRecursive(bool includeFolders,
bool excludeRecursively,
bool respectExclusions) const;
void addChild(FileData* file); // Error if mType != FOLDER void addChild(FileData* file); // Error if mType != FOLDER
void removeChild(FileData* file); //Error if mType != FOLDER void removeChild(FileData* file); // Error if mType != FOLDER
inline bool isPlaceHolder() { return mType == PLACEHOLDER; }; bool isPlaceHolder() { return mType == PLACEHOLDER; }
virtual inline void refreshMetadata() { return; }; virtual void refreshMetadata() { return; }
virtual std::string getKey(); virtual std::string getKey();
const bool isArcadeAsset(); const bool isArcadeAsset();
const bool isArcadeGame(); const bool isArcadeGame();
inline std::string getFullPath() { return getPath(); }; std::string getFullPath() { return getPath(); }
inline std::string getFileName() { return Utils::FileSystem::getFileName(getPath()); }; std::string getFileName() { return Utils::FileSystem::getFileName(getPath()); }
virtual FileData* getSourceFileData(); virtual FileData* getSourceFileData();
inline std::string getSystemName() const { return mSystemName; }; std::string getSystemName() const { return mSystemName; }
// Returns our best guess at the "real" name for this file. // Returns our best guess at the "real" name for this file.
std::string getDisplayName() const; std::string getDisplayName() const;
@ -104,19 +110,22 @@ public:
ComparisonFunction* comparisonFunction; ComparisonFunction* comparisonFunction;
std::string description; std::string description;
SortType(ComparisonFunction* sortFunction, const std::string& sortDescription) SortType(ComparisonFunction* sortFunction, const std::string& sortDescription)
: comparisonFunction(sortFunction), description(sortDescription) {} : comparisonFunction(sortFunction)
, description(sortDescription)
{
}
}; };
void sort(ComparisonFunction& comparator, std::pair<unsigned int, unsigned int>& gameCount); void sort(ComparisonFunction& comparator, std::pair<unsigned int, unsigned int>& gameCount);
void sortFavoritesOnTop(ComparisonFunction& comparator, void sortFavoritesOnTop(ComparisonFunction& comparator,
std::pair<unsigned int, unsigned int>& gameCount); std::pair<unsigned int, unsigned int>& gameCount);
void sort(const SortType& type, bool mFavoritesOnTop = false); void sort(const SortType& type, bool mFavoritesOnTop = false);
MetaDataList metadata; MetaDataList metadata;
// Only count the games, a cheaper alternative to a full sort when that is not required. // Only count the games, a cheaper alternative to a full sort when that is not required.
void countGames(std::pair<unsigned int, unsigned int>& gameCount); void countGames(std::pair<unsigned int, unsigned int>& gameCount);
inline void setSortTypeString(std::string typestring) { mSortTypeString = typestring; } void setSortTypeString(std::string typestring) { mSortTypeString = typestring; }
inline std::string getSortTypeString() { return mSortTypeString; } std::string getSortTypeString() { return mSortTypeString; }
FileData::SortType getSortTypeFromString(std::string desc); FileData::SortType getSortTypeFromString(std::string desc);
protected: protected:
@ -130,7 +139,7 @@ private:
std::string mPath; std::string mPath;
SystemEnvironmentData* mEnvData; SystemEnvironmentData* mEnvData;
SystemData* mSystem; SystemData* mSystem;
std::unordered_map<std::string,FileData*> mChildrenByFilename; std::unordered_map<std::string, FileData*> mChildrenByFilename;
std::vector<FileData*> mChildren; std::vector<FileData*> mChildren;
std::vector<FileData*> mFilteredChildren; std::vector<FileData*> mFilteredChildren;
// The pair includes all games, and favorite games. // The pair includes all games, and favorite games.
@ -148,8 +157,8 @@ public:
~CollectionFileData(); ~CollectionFileData();
const std::string& getName(); const std::string& getName();
void refreshMetadata(); void refreshMetadata();
FileData* getSourceFileData(); FileData* getSourceFileData() { return mSourceFileData; }
std::string getKey(); std::string getKey() { return getFullPath(); }
private: private:
// Needs to be updated when metadata changes. // Needs to be updated when metadata changes.

View file

@ -8,12 +8,12 @@
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "views/UIModeController.h"
#include "FileData.h" #include "FileData.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "views/UIModeController.h"
#include <cmath> #include <cmath>
@ -21,19 +21,20 @@
#define INCLUDE_UNKNOWN false; #define INCLUDE_UNKNOWN false;
FileFilterIndex::FileFilterIndex() FileFilterIndex::FileFilterIndex()
: mFilterByText(false), : mFilterByText(false)
mFilterByFavorites(false), , mFilterByFavorites(false)
mFilterByGenre(false), , mFilterByGenre(false)
mFilterByPlayers(false), , mFilterByPlayers(false)
mFilterByPubDev(false), , mFilterByPubDev(false)
mFilterByRatings(false), , mFilterByRatings(false)
mFilterByKidGame(false), , mFilterByKidGame(false)
mFilterByCompleted(false), , mFilterByCompleted(false)
mFilterByBroken(false), , mFilterByBroken(false)
mFilterByHidden(false) , mFilterByHidden(false)
{ {
clearAllFilters(); clearAllFilters();
// clang-format off
FilterDataDecl filterDecls[] = { FilterDataDecl filterDecls[] = {
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
{ FAVORITES_FILTER, &mFavoritesIndexAllKeys, &mFilterByFavorites, &mFavoritesIndexFilteredKeys, "favorite", false, "", "FAVORITES" }, { FAVORITES_FILTER, &mFavoritesIndexAllKeys, &mFilterByFavorites, &mFavoritesIndexFilteredKeys, "favorite", false, "", "FAVORITES" },
@ -46,21 +47,18 @@ FileFilterIndex::FileFilterIndex()
{ BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN" }, { BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN" },
{ HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" } { HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" }
}; };
// clang-format on
filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls + filterDataDecl = std::vector<FilterDataDecl>(
sizeof(filterDecls) / sizeof(filterDecls[0])); filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0]));
} }
FileFilterIndex::~FileFilterIndex() FileFilterIndex::~FileFilterIndex()
{ {
// Reset the index when destroyed.
resetIndex(); resetIndex();
} }
std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
{
return filterDataDecl;
}
void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
{ {
struct IndexImportStructure { struct IndexImportStructure {
@ -80,22 +78,23 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
{ &mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys) }, { &mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys) },
}; };
std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(
std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls + indexStructDecls,
sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0]));
for (std::vector<IndexImportStructure>::const_iterator indexesIt = for (std::vector<IndexImportStructure>::const_iterator indexesIt = indexImportDecl.cbegin();
indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); indexesIt++) indexesIt != indexImportDecl.cend(); indexesIt++) {
{
for (std::map<std::string, int>::const_iterator sourceIt = for (std::map<std::string, int>::const_iterator sourceIt =
(*indexesIt).sourceIndex->cbegin(); sourceIt != (*indexesIt).sourceIndex->cbegin();
(*indexesIt).sourceIndex->cend(); sourceIt++) { sourceIt != (*indexesIt).sourceIndex->cend(); sourceIt++) {
if ((*indexesIt).destinationIndex->find((*sourceIt).first) == if ((*indexesIt).destinationIndex->find((*sourceIt).first) ==
(*indexesIt).destinationIndex->cend()) (*indexesIt).destinationIndex->cend()) {
// Entry doesn't exist. // Entry doesn't exist.
(*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second; (*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second;
else }
else {
(*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second; (*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second;
}
} }
} }
} }
@ -115,7 +114,8 @@ void FileFilterIndex::resetIndex()
} }
std::string FileFilterIndex::getIndexableKey(FileData* game, std::string FileFilterIndex::getIndexableKey(FileData* game,
FilterIndexType type, bool getSecondary) FilterIndexType type,
bool getSecondary)
{ {
std::string key = ""; std::string key = "";
switch (type) { switch (type) {
@ -166,8 +166,8 @@ std::string FileFilterIndex::getIndexableKey(FileData* game,
// These values should only exist if a third party application has // These values should only exist if a third party application has
// been used for scraping the ratings, or if the gamelist.xml file // been used for scraping the ratings, or if the gamelist.xml file
// has been manually edited. // has been manually edited.
ratingNumber = static_cast<int>( ratingNumber =
(ceilf(stof(ratingString) / 0.1f) / 10) * 5); static_cast<int>((ceilf(stof(ratingString) / 0.1f) / 10.0f) * 5.0f);
if (ratingNumber < 0) if (ratingNumber < 0)
ratingNumber = 0; ratingNumber = 0;
@ -176,11 +176,11 @@ std::string FileFilterIndex::getIndexableKey(FileData* game,
key = "5 STARS"; key = "5 STARS";
else else
key = std::to_string(ratingNumber) + " - " + key = std::to_string(ratingNumber) + " - " +
std::to_string(ratingNumber) + ".5 STARS"; std::to_string(ratingNumber) + ".5 STARS";
} }
catch (int e) { catch (int e) {
LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " << LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): "
ratingString << ", " << e; << ratingString << ", " << e;
} }
} }
} }
@ -254,13 +254,13 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>*
} }
else { else {
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin(); for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
it != filterDataDecl.cend(); it++) { it != filterDataDecl.cend(); it++) {
if ((*it).type == type) { if ((*it).type == type) {
FilterDataDecl filterData = (*it); FilterDataDecl filterData = (*it);
*(filterData.filteredByRef) = values->size() > 0; *(filterData.filteredByRef) = values->size() > 0;
filterData.currentFilteredKeys->clear(); filterData.currentFilteredKeys->clear();
for (std::vector<std::string>::const_iterator vit = for (std::vector<std::string>::const_iterator vit = values->cbegin();
values->cbegin(); vit != values->cend(); vit++) { vit != values->cend(); vit++) {
// Check if it exists. // Check if it exists.
if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) { if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) {
filterData.currentFilteredKeys->push_back(std::string(*vit)); filterData.currentFilteredKeys->push_back(std::string(*vit));
@ -272,20 +272,20 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>*
return; return;
} }
void FileFilterIndex::setTextFilter(std::string textFilter) void FileFilterIndex::setTextFilter(std::string textFilter)
{ {
mTextFilter = textFilter; mTextFilter = textFilter;
if (textFilter == "") if (textFilter == "")
mFilterByText = false; mFilterByText = false;
else else
mFilterByText = true; mFilterByText = true;
}; };
void FileFilterIndex::clearAllFilters() void FileFilterIndex::clearAllFilters()
{ {
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin(); for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
it != filterDataDecl.cend(); it++) { it != filterDataDecl.cend(); it++) {
FilterDataDecl filterData = (*it); FilterDataDecl filterData = (*it);
*(filterData.filteredByRef) = false; *(filterData.filteredByRef) = false;
filterData.currentFilteredKeys->clear(); filterData.currentFilteredKeys->clear();
@ -312,19 +312,19 @@ void FileFilterIndex::setKidModeFilters()
void FileFilterIndex::debugPrintIndexes() void FileFilterIndex::debugPrintIndexes()
{ {
LOG(LogInfo) << "Printing Indexes..."; LOG(LogInfo) << "Printing Indexes...";
for (auto x: mFavoritesIndexAllKeys) { for (auto x : mFavoritesIndexAllKeys) {
LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second; LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second;
} }
for (auto x: mGenreIndexAllKeys) { for (auto x : mGenreIndexAllKeys) {
LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second; LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second;
} }
for (auto x: mPlayersIndexAllKeys) { for (auto x : mPlayersIndexAllKeys) {
LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second; LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second;
} }
for (auto x: mPubDevIndexAllKeys) { for (auto x : mPubDevIndexAllKeys) {
LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second; LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second;
} }
for (auto x: mRatingsIndexAllKeys) { for (auto x : mRatingsIndexAllKeys) {
LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second; LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second;
} }
for (auto x : mKidGameIndexAllKeys) { for (auto x : mKidGameIndexAllKeys) {
@ -348,8 +348,8 @@ bool FileFilterIndex::showFile(FileData* game)
if (game->getType() == FOLDER) { if (game->getType() == FOLDER) {
std::vector<FileData*> children = game->getChildren(); std::vector<FileData*> children = game->getChildren();
// Iterate through all of the children, until there's a match. // Iterate through all of the children, until there's a match.
for (std::vector<FileData*>::const_iterator it = children.cbegin(); for (std::vector<FileData*>::const_iterator it = children.cbegin(); it != children.cend();
it != children.cend(); it++) { it++) {
if (showFile(*it)) if (showFile(*it))
return true; return true;
} }
@ -361,8 +361,8 @@ bool FileFilterIndex::showFile(FileData* game)
// Name filters take precedence over all other filters, so if there is no match for // Name filters take precedence over all other filters, so if there is no match for
// the game name, then always return false. // the game name, then always return false.
if (mTextFilter != "" && !(Utils::String::toUpper(game-> if (mTextFilter != "" &&
getName()).find(mTextFilter) != std::string::npos)) { !(Utils::String::toUpper(game->getName()).find(mTextFilter) != std::string::npos)) {
return false; return false;
} }
else if (mTextFilter != "") { else if (mTextFilter != "") {
@ -370,7 +370,7 @@ bool FileFilterIndex::showFile(FileData* game)
} }
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin(); for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
it != filterDataDecl.cend(); it++) { it != filterDataDecl.cend(); it++) {
FilterDataDecl filterData = (*it); FilterDataDecl filterData = (*it);
if (filterData.primaryKey == "kidgame" && UIModeController::getInstance()->isUIModeKid()) { if (filterData.primaryKey == "kidgame" && UIModeController::getInstance()->isUIModeKid()) {
return (getIndexableKey(game, filterData.type, false) != "FALSE"); return (getIndexableKey(game, filterData.type, false) != "FALSE");
@ -419,18 +419,19 @@ bool FileFilterIndex::isFiltered()
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type)
{ {
const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER, const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER,
PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER, PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER,
COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER }; COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER };
std::vector<std::string> filterKeysList[9] = { mFavoritesIndexFilteredKeys, std::vector<std::string> filterKeysList[9] = {
mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys, mPubDevIndexFilteredKeys, mFavoritesIndexFilteredKeys, mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys,
mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys, mCompletedIndexFilteredKeys, mPubDevIndexFilteredKeys, mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys,
mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys }; mCompletedIndexFilteredKeys, mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys
};
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
if (filterTypes[i] == type) { if (filterTypes[i] == type) {
for (std::vector<std::string>::const_iterator it = filterKeysList[i].cbegin(); for (std::vector<std::string>::const_iterator it = filterKeysList[i].cbegin();
it != filterKeysList[i].cend(); it++) { it != filterKeysList[i].cend(); it++) {
if (key == (*it)) if (key == (*it))
return true; return true;
} }
@ -589,7 +590,8 @@ void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove)
} }
void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index, void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
std::string key, bool remove) std::string key,
bool remove)
{ {
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
if (!includeUnknown && key == UNKNOWN_LABEL) if (!includeUnknown && key == UNKNOWN_LABEL)
@ -600,7 +602,7 @@ void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
if (index->find(key) == index->cend()) { if (index->find(key) == index->cend()) {
// Disabled for now as this could happen because default values are assigned as // Disabled for now as this could happen because default values are assigned as
// filters, for example 'FALSE' for favorites and kidgames for non-game entries. // filters, for example 'FALSE' for favorites and kidgames for non-game entries.
// LOG(LogDebug) << "Couldn't find entry in index! " << key; // LOG(LogDebug) << "Couldn't find entry in index! " << key;
} }
else { else {
(index->at(key))--; (index->at(key))--;
@ -617,8 +619,3 @@ void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
(index->at(key))++; (index->at(key))++;
} }
} }
void FileFilterIndex::clearIndex(std::map<std::string, int>& indexMap)
{
indexMap.clear();
}

View file

@ -52,13 +52,13 @@ public:
void removeFromIndex(FileData* game); void removeFromIndex(FileData* game);
void setFilter(FilterIndexType type, std::vector<std::string>* values); void setFilter(FilterIndexType type, std::vector<std::string>* values);
void setTextFilter(std::string textFilter); void setTextFilter(std::string textFilter);
std::string getTextFilter() { return mTextFilter; }; std::string getTextFilter() { return mTextFilter; }
void clearAllFilters(); void clearAllFilters();
void debugPrintIndexes(); void debugPrintIndexes();
bool showFile(FileData* game); bool showFile(FileData* game);
bool isFiltered(); bool isFiltered();
bool isKeyBeingFilteredBy(std::string key, FilterIndexType type); bool isKeyBeingFilteredBy(std::string key, FilterIndexType type);
std::vector<FilterDataDecl>& getFilterDataDecls(); std::vector<FilterDataDecl>& getFilterDataDecls() { return filterDataDecl; }
void importIndex(FileFilterIndex* indexToImport); void importIndex(FileFilterIndex* indexToImport);
void resetIndex(); void resetIndex();
@ -81,7 +81,7 @@ private:
void manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove); void manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove);
void clearIndex(std::map<std::string, int>& indexMap); void clearIndex(std::map<std::string, int>& indexMap) { indexMap.clear(); }
std::string mTextFilter; std::string mTextFilter;
bool mFilterByText; bool mFilterByText;
@ -117,7 +117,6 @@ private:
std::vector<std::string> mHiddenIndexFilteredKeys; std::vector<std::string> mHiddenIndexFilteredKeys;
FileData* mRootFolder; FileData* mRootFolder;
}; };
#endif // ES_APP_FILE_FILTER_INDEX_H #endif // ES_APP_FILE_FILTER_INDEX_H

View file

@ -48,8 +48,9 @@ namespace FileSorts
FileData::SortType(&compareSystemDescending, "system, descending") FileData::SortType(&compareSystemDescending, "system, descending")
}; };
const std::vector<FileData::SortType> SortTypes(typesArr, typesArr + const std::vector<FileData::SortType> SortTypes(typesArr,
sizeof(typesArr)/sizeof(typesArr[0])); typesArr +
sizeof(typesArr) / sizeof(typesArr[0]));
bool compareName(const FileData* file1, const FileData* file2) bool compareName(const FileData* file1, const FileData* file2)
{ {
@ -155,11 +156,13 @@ namespace FileSorts
file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1); file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1);
// Any non-numeric value will end up as zero. // Any non-numeric value will end up as zero.
if (!file1Players.empty() && if (!file1Players.empty() &&
std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) {
file1Int = stoi(file1Players); file1Int = stoi(file1Players);
}
if (!file2Players.empty() && if (!file2Players.empty() &&
std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) {
file2Int = stoi(file2Players); file2Int = stoi(file2Players);
}
return file1Int < file2Int; return file1Int < file2Int;
} }
@ -177,11 +180,13 @@ namespace FileSorts
if (dashPos != std::string::npos) if (dashPos != std::string::npos)
file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1); file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1);
if (!file1Players.empty() && if (!file1Players.empty() &&
std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) {
file1Int = stoi(file1Players); file1Int = stoi(file1Players);
}
if (!file2Players.empty() && if (!file2Players.empty() &&
std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) {
file2Int = stoi(file2Players); file2Int = stoi(file2Players);
}
return file1Int > file2Int; return file1Int > file2Int;
} }
@ -201,16 +206,18 @@ namespace FileSorts
{ {
// Only games have playcount metadata. // Only games have playcount metadata.
if (file1->metadata.getType() == GAME_METADATA && if (file1->metadata.getType() == GAME_METADATA &&
file2->metadata.getType() == GAME_METADATA) file2->metadata.getType() == GAME_METADATA) {
return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount");
}
return false; return false;
} }
bool compareTimesPlayedDescending(const FileData* file1, const FileData* file2) bool compareTimesPlayedDescending(const FileData* file1, const FileData* file2)
{ {
if (file1->metadata.getType() == GAME_METADATA && if (file1->metadata.getType() == GAME_METADATA &&
file2->metadata.getType() == GAME_METADATA) file2->metadata.getType() == GAME_METADATA) {
return (file1)->metadata.getInt("playcount") > (file2)->metadata.getInt("playcount"); return (file1)->metadata.getInt("playcount") > (file2)->metadata.getInt("playcount");
}
return false; return false;
} }
@ -227,4 +234,5 @@ namespace FileSorts
std::string system2 = Utils::String::toUpper(file2->getSystemName()); std::string system2 = Utils::String::toUpper(file2->getSystemName());
return system1.compare(system2) > 0; return system1.compare(system2) > 0;
} }
};
}; // namespace FileSorts

View file

@ -38,6 +38,6 @@ namespace FileSorts
bool compareSystemDescending(const FileData* file1, const FileData* file2); bool compareSystemDescending(const FileData* file1, const FileData* file2);
extern const std::vector<FileData::SortType> SortTypes; extern const std::vector<FileData::SortType> SortTypes;
}; }; // namespace FileSorts
#endif // ES_APP_FILE_SORTS_H #endif // ES_APP_FILE_SORTS_H

View file

@ -8,12 +8,12 @@
#include "Gamelist.h" #include "Gamelist.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "FileData.h" #include "FileData.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include <pugixml.hpp> #include <pugixml.hpp>
@ -25,8 +25,8 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains); std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains);
if (!contains) { if (!contains) {
LOG(LogError) << "Path \"" << path << "\" is outside system path \"" << LOG(LogError) << "Path \"" << path << "\" is outside system path \""
system->getStartPath() << "\""; << system->getStartPath() << "\"";
return nullptr; return nullptr;
} }
@ -36,7 +36,7 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
bool found = false; bool found = false;
while (path_it != pathList.end()) { while (path_it != pathList.end()) {
const std::unordered_map<std::string, FileData*>& children = const std::unordered_map<std::string, FileData*>& children =
treeNode->getChildrenByFilename(); treeNode->getChildrenByFilename();
std::string key = *path_it; std::string key = *path_it;
found = children.find(key) != children.cend(); found = children.find(key) != children.cend();
@ -71,8 +71,9 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
} }
// Create missing folder. // Create missing folder.
FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) FileData* folder = new FileData(
+ "/" + *path_it, system->getSystemEnvData(), system); FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it,
system->getSystemEnvData(), system);
treeNode->addChild(folder); treeNode->addChild(folder);
treeNode = folder; treeNode = folder;
} }
@ -89,24 +90,24 @@ void parseGamelist(SystemData* system)
std::string xmlpath = system->getGamelistPath(false); std::string xmlpath = system->getGamelistPath(false);
if (!Utils::FileSystem::exists(xmlpath)) { if (!Utils::FileSystem::exists(xmlpath)) {
LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName() << LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName()
"\" does not have a gamelist.xml file"; << "\" does not have a gamelist.xml file";
return; return;
} }
LOG(LogInfo) << "Parsing gamelist file \"" << xmlpath << "\"..."; LOG(LogInfo) << "Parsing gamelist file \"" << xmlpath << "\"...";
pugi::xml_document doc; pugi::xml_document doc;
#if defined(_WIN64) #if defined(_WIN64)
pugi::xml_parse_result result = pugi::xml_parse_result result =
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
#else #else
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
#endif #endif
if (!result) { if (!result) {
LOG(LogError) << "Error parsing gamelist file \"" << xmlpath << LOG(LogError) << "Error parsing gamelist file \"" << xmlpath
"\": " << result.description(); << "\": " << result.description();
return; return;
} }
@ -124,24 +125,24 @@ void parseGamelist(SystemData* system)
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
std::string tag = tagList[i]; std::string tag = tagList[i];
FileType type = typeList[i]; FileType type = typeList[i];
for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode; fileNode = for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode;
fileNode.next_sibling(tag.c_str())) { fileNode = fileNode.next_sibling(tag.c_str())) {
const std::string path = const std::string path = Utils::FileSystem::resolveRelativePath(
Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(), fileNode.child("path").text().get(), relativeTo, false);
relativeTo, false);
if (!trustGamelist && !Utils::FileSystem::exists(path)) { if (!trustGamelist && !Utils::FileSystem::exists(path)) {
LOG(LogWarning) << (type == GAME ? "File \"" : "Folder \"") << path << LOG(LogWarning) << (type == GAME ? "File \"" : "Folder \"") << path
"\" does not exist, ignoring entry"; << "\" does not exist, ignoring entry";
continue; continue;
} }
// Skip hidden files, check both the file itself and the directory in which // Skip hidden files, check both the file itself and the directory in which
// it is located. // it is located.
if (!showHiddenFiles && (Utils::FileSystem::isHidden(path) || if (!showHiddenFiles &&
Utils::FileSystem::isHidden(Utils::FileSystem::getParent(path)))) { (Utils::FileSystem::isHidden(path) ||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden file \"" << Utils::FileSystem::isHidden(Utils::FileSystem::getParent(path)))) {
path << "\""; LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden file \"" << path
<< "\"";
continue; continue;
} }
@ -163,8 +164,8 @@ void parseGamelist(SystemData* system)
else { else {
// Skip arcade asset entries as these will not be used in any way inside // Skip arcade asset entries as these will not be used in any way inside
// the application. // the application.
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping arcade asset \"" << LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping arcade asset \""
file->getName() << "\""; << file->getName() << "\"";
delete file; delete file;
continue; continue;
} }
@ -174,9 +175,10 @@ void parseGamelist(SystemData* system)
// application restart. // application restart.
if (!Settings::getInstance()->getBool("ShowHiddenGames")) { if (!Settings::getInstance()->getBool("ShowHiddenGames")) {
if (file->getHidden()) { if (file->getHidden()) {
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden " << LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden "
(type == GAME ? "file" : "folder") << " entry \"" << << (type == GAME ? "file" : "folder") << " entry \""
file->getName() << "\"" << " (\"" << file->getPath() << "\")"; << file->getName() << "\""
<< " (\"" << file->getPath() << "\")";
delete file; delete file;
} }
// Also delete any folders which are empty, i.e. all their entries are hidden. // Also delete any folders which are empty, i.e. all their entries are hidden.
@ -188,8 +190,10 @@ void parseGamelist(SystemData* system)
} }
} }
void addFileDataNode(pugi::xml_node& parent, const FileData* file, void addFileDataNode(pugi::xml_node& parent,
const std::string& tag, SystemData* system) const FileData* file,
const std::string& tag,
SystemData* system)
{ {
// Create game and add to parent node. // Create game and add to parent node.
pugi::xml_node newNode = parent.append_child(tag.c_str()); pugi::xml_node newNode = parent.append_child(tag.c_str());
@ -199,8 +203,8 @@ void addFileDataNode(pugi::xml_node& parent, const FileData* file,
// First element is "name", there's only one element and the name is the default. // First element is "name", there's only one element and the name is the default.
if (newNode.children().begin() == newNode.child("name") && if (newNode.children().begin() == newNode.child("name") &&
++newNode.children().begin() == newNode.children().end() && ++newNode.children().begin() == newNode.children().end() &&
newNode.child("name").text().get() == file->getDisplayName()) { newNode.child("name").text().get() == file->getDisplayName()) {
// If the only info is the default name, don't bother // If the only info is the default name, don't bother
// with this node, delete it and ultimately do nothing. // with this node, delete it and ultimately do nothing.
@ -211,8 +215,9 @@ void addFileDataNode(pugi::xml_node& parent, const FileData* file,
// Try and make the path relative if we can so things still // Try and make the path relative if we can so things still
// work if we change the ROM folder location in the future. // work if we change the ROM folder location in the future.
newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file-> newNode.prepend_child("path").text().set(
getPath(), system->getStartPath(), false).c_str()); Utils::FileSystem::createRelativePath(file->getPath(), system->getStartPath(), false)
.c_str());
} }
} }
@ -232,23 +237,23 @@ void updateGamelist(SystemData* system)
if (Utils::FileSystem::exists(xmlReadPath)) { if (Utils::FileSystem::exists(xmlReadPath)) {
// Parse an existing file first. // Parse an existing file first.
#if defined(_WIN64)
#if defined(_WIN64)
pugi::xml_parse_result result = pugi::xml_parse_result result =
doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str()); doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str());
#else #else
pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str()); pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str());
#endif #endif
if (!result) { if (!result) {
LOG(LogError) << "Error parsing gamelist file \"" << xmlReadPath << "\": " << LOG(LogError) << "Error parsing gamelist file \"" << xmlReadPath
result.description(); << "\": " << result.description();
return; return;
} }
root = doc.child("gameList"); root = doc.child("gameList");
if (!root) { if (!root) {
LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" << LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" << xmlReadPath << "\"";
xmlReadPath << "\"";
return; return;
} }
} }
@ -266,8 +271,8 @@ void updateGamelist(SystemData* system)
// Get only files, no folders. // Get only files, no folders.
std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER); std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER);
// Iterate through all files, checking if they're already in the XML file. // Iterate through all files, checking if they're already in the XML file.
for (std::vector<FileData*>::const_iterator fit = files.cbegin(); for (std::vector<FileData*>::const_iterator fit = files.cbegin(); // Line break.
fit != files.cend(); fit++) { fit != files.cend(); fit++) {
const std::string tag = ((*fit)->getType() == GAME) ? "game" : "folder"; const std::string tag = ((*fit)->getType() == GAME) ? "game" : "folder";
// Do not touch if it wasn't changed and is not flagged for deletion. // Do not touch if it wasn't changed and is not flagged for deletion.
@ -277,16 +282,16 @@ void updateGamelist(SystemData* system)
// Check if the file already exists in the XML file. // Check if the file already exists in the XML file.
// If it does, remove the entry before adding it back. // If it does, remove the entry before adding it back.
for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode; for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode;
fileNode = fileNode.next_sibling(tag.c_str())) { fileNode = fileNode.next_sibling(tag.c_str())) {
pugi::xml_node pathNode = fileNode.child("path"); pugi::xml_node pathNode = fileNode.child("path");
if (!pathNode) { if (!pathNode) {
LOG(LogError) << "<" << tag << "> node contains no <path> child"; LOG(LogError) << "<" << tag << "> node contains no <path> child";
continue; continue;
} }
std::string nodePath = Utils::FileSystem::getCanonicalPath( std::string nodePath =
Utils::FileSystem::resolveRelativePath(pathNode.text().get(), Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath(
system->getStartPath(), true)); pathNode.text().get(), system->getStartPath(), true));
std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath()); std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath());
if (nodePath == gamePath) { if (nodePath == gamePath) {
@ -312,16 +317,17 @@ void updateGamelist(SystemData* system)
std::string xmlWritePath(system->getGamelistPath(true)); std::string xmlWritePath(system->getGamelistPath(true));
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath)); Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated << LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated
(numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath << "\""; << (numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath
<< "\"";
#if defined(_WIN64) #if defined(_WIN64)
if (!doc.save_file(Utils::String::stringToWideString(xmlWritePath).c_str())) { if (!doc.save_file(Utils::String::stringToWideString(xmlWritePath).c_str())) {
#else #else
if (!doc.save_file(xmlWritePath.c_str())) { if (!doc.save_file(xmlWritePath.c_str())) {
#endif #endif
LOG(LogError) << "Error saving gamelist.xml to \"" << LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath
xmlWritePath << "\" (for system " << system->getName() << ")"; << "\" (for system " << system->getName() << ")";
} }
} }
} }

View file

@ -12,11 +12,14 @@
#if defined(BUILD_VLC_PLAYER) #if defined(BUILD_VLC_PLAYER)
#include "components/VideoVlcComponent.h" #include "components/VideoVlcComponent.h"
#endif #endif
#include "views/ViewController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "Sound.h" #include "Sound.h"
#include "views/ViewController.h"
MediaViewer::MediaViewer(Window* window) : mWindow(window), mVideo(nullptr), mImage(nullptr) MediaViewer::MediaViewer(Window* window)
: mWindow(window)
, mVideo(nullptr)
, mImage(nullptr)
{ {
mWindow->setMediaViewer(this); mWindow->setMediaViewer(this);
} }
@ -85,12 +88,12 @@ void MediaViewer::render()
// Render a black background below the game media. // Render a black background below the game media.
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()), Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
if (mVideo && !mDisplayingImage) { if (mVideo && !mDisplayingImage) {
mVideo->render(transform); mVideo->render(transform);
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters videoParameters; Renderer::shaderParameters videoParameters;
unsigned int shaders = 0; unsigned int shaders = 0;
if (Settings::getInstance()->getBool("MediaViewerVideoScanlines")) if (Settings::getInstance()->getBool("MediaViewerVideoScanlines"))
@ -98,6 +101,7 @@ void MediaViewer::render()
if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) { if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
shaders |= Renderer::SHADER_BLUR_HORIZONTAL; shaders |= Renderer::SHADER_BLUR_HORIZONTAL;
float heightModifier = Renderer::getScreenHeightModifier(); float heightModifier = Renderer::getScreenHeightModifier();
// clang-format off
if (heightModifier < 1) if (heightModifier < 1)
videoParameters.blurPasses = 2; // Below 1080 videoParameters.blurPasses = 2; // Below 1080
else if (heightModifier >= 4) else if (heightModifier >= 4)
@ -112,18 +116,19 @@ void MediaViewer::render()
videoParameters.blurPasses = 3; // 1440 videoParameters.blurPasses = 3; // 1440
else if (heightModifier >= 1) else if (heightModifier >= 1)
videoParameters.blurPasses = 2; // 1080 videoParameters.blurPasses = 2; // 1080
// clang-format on
} }
Renderer::shaderPostprocessing(shaders, videoParameters); Renderer::shaderPostprocessing(shaders, videoParameters);
#endif #endif
} }
else if (mImage && mImage->hasImage() && mImage->getSize() != 0) { else if (mImage && mImage->hasImage() && mImage->getSize() != 0) {
mImage->render(transform); mImage->render(transform);
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
if (mCurrentImageIndex == mScreenShotIndex && if (mCurrentImageIndex == mScreenShotIndex &&
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"))
Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
#endif #endif
// This is necessary so that the video loops if viewing an image when // This is necessary so that the video loops if viewing an image when
// the video ends. // the video ends.
@ -244,14 +249,14 @@ void MediaViewer::playVideo()
mDisplayingImage = false; mDisplayingImage = false;
ViewController::get()->onStopVideo(); ViewController::get()->onStopVideo();
#if defined(BUILD_VLC_PLAYER) #if defined(BUILD_VLC_PLAYER)
if (Settings::getInstance()->getString("VideoPlayer") == "ffmpeg") if (Settings::getInstance()->getString("VideoPlayer") == "ffmpeg")
mVideo = new VideoFFmpegComponent(mWindow); mVideo = new VideoFFmpegComponent(mWindow);
else else
mVideo = new VideoVlcComponent(mWindow); mVideo = new VideoVlcComponent(mWindow);
#else #else
mVideo = new VideoFFmpegComponent(mWindow); mVideo = new VideoFFmpegComponent(mWindow);
#endif #endif
mVideo->topWindow(true); mVideo->topWindow(true);
mVideo->setOrigin(0.5f, 0.5f); mVideo->setOrigin(0.5f, 0.5f);
@ -259,10 +264,10 @@ void MediaViewer::playVideo()
if (Settings::getInstance()->getBool("MediaViewerStretchVideos")) if (Settings::getInstance()->getBool("MediaViewerStretchVideos"))
mVideo->setResize(static_cast<float>(Renderer::getScreenWidth()), mVideo->setResize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
else else
mVideo->setMaxSize(static_cast<float>(Renderer::getScreenWidth()), mVideo->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
mVideo->setVideo(mVideoFile); mVideo->setVideo(mVideoFile);
mVideo->setMediaViewerMode(true); mVideo->setMediaViewerMode(true);
@ -282,6 +287,6 @@ void MediaViewer::showImage(int index)
mImage->setOrigin(0.5f, 0.5f); mImage->setOrigin(0.5f, 0.5f);
mImage->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); mImage->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f);
mImage->setMaxSize(static_cast<float>(Renderer::getScreenWidth()), mImage->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
} }
} }

View file

@ -9,10 +9,10 @@
#ifndef ES_APP_MEDIA_VIEWER_H #ifndef ES_APP_MEDIA_VIEWER_H
#define ES_APP_MEDIA_VIEWER_H #define ES_APP_MEDIA_VIEWER_H
#include "components/ImageComponent.h"
#include "components/VideoComponent.h"
#include "FileData.h" #include "FileData.h"
#include "Window.h" #include "Window.h"
#include "components/ImageComponent.h"
#include "components/VideoComponent.h"
class MediaViewer : public Window::MediaViewer class MediaViewer : public Window::MediaViewer
{ {

View file

@ -9,11 +9,12 @@
#include "MetaData.h" #include "MetaData.h"
#include "utils/FileSystemUtil.h"
#include "Log.h" #include "Log.h"
#include "utils/FileSystemUtil.h"
#include <pugixml.hpp> #include <pugixml.hpp>
// clang-format off
MetaDataDecl gameDecls[] = { MetaDataDecl gameDecls[] = {
// key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd, shouldScrape // key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd, shouldScrape
{"name", MD_STRING, "", false, "name", "enter name", true}, {"name", MD_STRING, "", false, "name", "enter name", true},
@ -39,9 +40,6 @@ MetaDataDecl gameDecls[] = {
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false}
}; };
const std::vector<MetaDataDecl> gameMDD(gameDecls, gameDecls +
sizeof(gameDecls) / sizeof(gameDecls[0]));
MetaDataDecl folderDecls[] = { MetaDataDecl folderDecls[] = {
{"name", MD_STRING, "", false, "name", "enter name", true}, {"name", MD_STRING, "", false, "name", "enter name", true},
{"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true}, {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true},
@ -59,13 +57,18 @@ MetaDataDecl folderDecls[] = {
{"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false},
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false}
}; };
// clang-format on
const std::vector<MetaDataDecl> folderMDD(folderDecls, folderDecls + const std::vector<MetaDataDecl> gameMDD(gameDecls,
sizeof(folderDecls) / sizeof(folderDecls[0])); gameDecls + sizeof(gameDecls) / sizeof(gameDecls[0]));
const std::vector<MetaDataDecl> folderMDD(folderDecls,
folderDecls +
sizeof(folderDecls) / sizeof(folderDecls[0]));
const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type) const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
{ {
switch(type) { switch (type) {
case GAME_METADATA: case GAME_METADATA:
return gameMDD; return gameMDD;
case FOLDER_METADATA: case FOLDER_METADATA:
@ -77,7 +80,8 @@ const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
} }
MetaDataList::MetaDataList(MetaDataListType type) MetaDataList::MetaDataList(MetaDataListType type)
: mType(type), mWasChanged(false) : mType(type)
, mWasChanged(false)
{ {
const std::vector<MetaDataDecl>& mdd = getMDD(); const std::vector<MetaDataDecl>& mdd = getMDD();
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++)
@ -85,7 +89,8 @@ MetaDataList::MetaDataList(MetaDataListType type)
} }
MetaDataList MetaDataList::createFromXML(MetaDataListType type, MetaDataList MetaDataList::createFromXML(MetaDataListType type,
pugi::xml_node& node, const std::string& relativeTo) pugi::xml_node& node,
const std::string& relativeTo)
{ {
MetaDataList mdl(type); MetaDataList mdl(type);
@ -107,8 +112,9 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type,
return mdl; return mdl;
} }
void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults, void MetaDataList::appendToXML(pugi::xml_node& parent,
const std::string& relativeTo) const bool ignoreDefaults,
const std::string& relativeTo) const
{ {
const std::vector<MetaDataDecl>& mdd = getMDD(); const std::vector<MetaDataDecl>& mdd = getMDD();
@ -138,30 +144,33 @@ void MetaDataList::set(const std::string& key, const std::string& value)
const std::string& MetaDataList::get(const std::string& key) const const std::string& MetaDataList::get(const std::string& key) const
{ {
// Check that the key actually exists, otherwise return empty string. // Check that the key actually exists, otherwise return an empty string.
if (mMap.count(key) > 0) if (mMap.count(key) > 0)
return mMap.at(key); return mMap.at(key);
else else
return mNoResult;
return mNoResult;
} }
int MetaDataList::getInt(const std::string& key) const int MetaDataList::getInt(const std::string& key) const
{ {
// Return integer value.
return atoi(get(key).c_str()); return atoi(get(key).c_str());
} }
float MetaDataList::getFloat(const std::string& key) const float MetaDataList::getFloat(const std::string& key) const
{ {
// Return float value.
return static_cast<float>(atof(get(key).c_str())); return static_cast<float>(atof(get(key).c_str()));
} }
bool MetaDataList::wasChanged() const bool MetaDataList::wasChanged() const
{ {
// Return whether the metadata was changed.
return mWasChanged; return mWasChanged;
} }
void MetaDataList::resetChangedFlag() void MetaDataList::resetChangedFlag()
{ {
// Reset the change flag.
mWasChanged = false; mWasChanged = false;
} }

View file

@ -18,7 +18,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace pugi { class xml_node; } namespace pugi
{
class xml_node;
}
enum MetaDataType { enum MetaDataType {
// Generic types. // Generic types.
@ -40,7 +43,7 @@ struct MetaDataDecl {
std::string key; std::string key;
MetaDataType type; MetaDataType type;
std::string defaultValue; std::string defaultValue;
// If true, ignore values for this metadata. // If true, ignore values for this metadata.
bool isStatistic; bool isStatistic;
// Displayed as this in editors. // Displayed as this in editors.
std::string displayName; std::string displayName;
@ -51,7 +54,7 @@ struct MetaDataDecl {
}; };
enum MetaDataListType { enum MetaDataListType {
GAME_METADATA, GAME_METADATA, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
FOLDER_METADATA FOLDER_METADATA
}; };
@ -61,9 +64,11 @@ class MetaDataList
{ {
public: public:
static MetaDataList createFromXML(MetaDataListType type, static MetaDataList createFromXML(MetaDataListType type,
pugi::xml_node& node, const std::string& relativeTo); pugi::xml_node& node,
void appendToXML(pugi::xml_node& parent, bool ignoreDefaults, const std::string& relativeTo);
const std::string& relativeTo) const; void appendToXML(pugi::xml_node& parent,
bool ignoreDefaults,
const std::string& relativeTo) const;
MetaDataList(MetaDataListType type); MetaDataList(MetaDataListType type);
@ -76,10 +81,12 @@ public:
bool wasChanged() const; bool wasChanged() const;
void resetChangedFlag(); void resetChangedFlag();
inline MetaDataListType getType() const { return mType; } MetaDataListType getType() const { return mType; }
inline const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); } const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); }
inline const std::vector<MetaDataDecl>& getMDD(MetaDataListType type) const const std::vector<MetaDataDecl>& getMDD(MetaDataListType type) const
{ return getMDDByType(type); } {
return getMDDByType(type);
}
private: private:
MetaDataListType mType; MetaDataListType mType;

View file

@ -9,47 +9,45 @@
#include "MiximageGenerator.h" #include "MiximageGenerator.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include <chrono> #include <chrono>
MiximageGenerator::MiximageGenerator(FileData* game, std::string& resultMessage) MiximageGenerator::MiximageGenerator(FileData* game, std::string& resultMessage)
: mGame(game), : mGame(game)
mResultMessage(resultMessage), , mResultMessage(resultMessage)
mWidth(1280), , mWidth(1280)
mHeight(960), , mHeight(960)
mMarquee(false), , mMarquee(false)
mBox3D(false), , mBox3D(false)
mCover(false) , mCover(false)
{ {
} }
MiximageGenerator::~MiximageGenerator() MiximageGenerator::~MiximageGenerator() {}
{
}
void MiximageGenerator::startThread(std::promise<bool>* miximagePromise) void MiximageGenerator::startThread(std::promise<bool>* miximagePromise)
{ {
mMiximagePromise = miximagePromise; mMiximagePromise = miximagePromise;
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Creating miximage for \"" LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Creating miximage for \""
<< mGame->getFileName() << "\""; << mGame->getFileName() << "\"";
if (mGame->getMiximagePath() != "" && !Settings::getInstance()->getBool("MiximageOverwrite")) { if (mGame->getMiximagePath() != "" && !Settings::getInstance()->getBool("MiximageOverwrite")) {
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): File already exists and miximage " LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): File already exists and miximage "
"overwriting has not been enabled, aborting"; "overwriting has not been enabled, aborting";
mMiximagePromise->set_value(true); mMiximagePromise->set_value(true);
return; return;
} }
if ((mScreenshotPath = mGame->getScreenshotPath()) == "") { if ((mScreenshotPath = mGame->getScreenshotPath()) == "") {
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): " LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): "
"No screenshot image found, aborting"; "No screenshot image found, aborting";
mResultMessage = "No screenshot image found, couldn't generate miximage"; mResultMessage = "No screenshot image found, couldn't generate miximage";
mMiximagePromise->set_value(true); mMiximagePromise->set_value(true);
return; return;
} }
@ -68,14 +66,14 @@ void MiximageGenerator::startThread(std::promise<bool>* miximagePromise)
mBox3D = true; mBox3D = true;
} }
else if (Settings::getInstance()->getBool("MiximageCoverFallback") && else if (Settings::getInstance()->getBool("MiximageCoverFallback") &&
(mCoverPath= mGame->getCoverPath()) != "") { (mCoverPath = mGame->getCoverPath()) != "") {
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): " LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): "
"No 3D box image found, using cover image as fallback"; "No 3D box image found, using cover image as fallback";
mCover = true; mCover = true;
} }
else if (Settings::getInstance()->getBool("MiximageCoverFallback")) { else if (Settings::getInstance()->getBool("MiximageCoverFallback")) {
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): " LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): "
"No 3D box or cover images found"; "No 3D box or cover images found";
} }
else { else {
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): No 3D box image found"; LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): No 3D box image found";
@ -93,9 +91,10 @@ void MiximageGenerator::startThread(std::promise<bool>* miximagePromise)
else { else {
const auto endTime = std::chrono::system_clock::now(); const auto endTime = std::chrono::system_clock::now();
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Processing completed in: " << LOG(LogDebug)
std::chrono::duration_cast<std::chrono::milliseconds> << "MiximageGenerator::MiximageGenerator(): Processing completed in: "
(endTime - startTime).count() << " ms"; << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
<< " ms";
} }
mResultMessage = mMessage; mResultMessage = mMessage;
@ -113,19 +112,19 @@ bool MiximageGenerator::generateImage()
unsigned int fileHeight = 0; unsigned int fileHeight = 0;
unsigned int filePitch = 0; unsigned int filePitch = 0;
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mScreenshotPath).c_str()); fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mScreenshotPath).c_str());
#else #else
fileFormat = FreeImage_GetFileType(mScreenshotPath.c_str()); fileFormat = FreeImage_GetFileType(mScreenshotPath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) if (fileFormat == FIF_UNKNOWN)
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFIFFromFilenameU( fileFormat = FreeImage_GetFIFFromFilenameU(
Utils::String::stringToWideString(mScreenshotPath).c_str()); Utils::String::stringToWideString(mScreenshotPath).c_str());
#else #else
fileFormat = FreeImage_GetFIFFromFilename(mScreenshotPath.c_str()); fileFormat = FreeImage_GetFIFFromFilename(mScreenshotPath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) { if (fileFormat == FIF_UNKNOWN) {
LOG(LogError) << "Screenshot image in unknown image format, aborting"; LOG(LogError) << "Screenshot image in unknown image format, aborting";
@ -135,12 +134,12 @@ bool MiximageGenerator::generateImage()
// Make sure that we can actually read this format. // Make sure that we can actually read this format.
if (FreeImage_FIFSupportsReading(fileFormat)) { if (FreeImage_FIFSupportsReading(fileFormat)) {
#if defined(_WIN64) #if defined(_WIN64)
screenshotFile = FreeImage_LoadU(fileFormat, screenshotFile =
Utils::String::stringToWideString(mScreenshotPath).c_str()); FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mScreenshotPath).c_str());
#else #else
screenshotFile = FreeImage_Load(fileFormat, mScreenshotPath.c_str()); screenshotFile = FreeImage_Load(fileFormat, mScreenshotPath.c_str());
#endif #endif
} }
else { else {
LOG(LogError) << "Screenshot file format not supported"; LOG(LogError) << "Screenshot file format not supported";
@ -155,20 +154,20 @@ bool MiximageGenerator::generateImage()
} }
if (mMarquee) { if (mMarquee) {
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFileTypeU( fileFormat =
Utils::String::stringToWideString(mMarqueePath).c_str()); FreeImage_GetFileTypeU(Utils::String::stringToWideString(mMarqueePath).c_str());
#else #else
fileFormat = FreeImage_GetFileType(mMarqueePath.c_str()); fileFormat = FreeImage_GetFileType(mMarqueePath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) if (fileFormat == FIF_UNKNOWN)
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFIFFromFilenameU( fileFormat = FreeImage_GetFIFFromFilenameU(
Utils::String::stringToWideString(mMarqueePath).c_str()); Utils::String::stringToWideString(mMarqueePath).c_str());
#else #else
fileFormat = FreeImage_GetFIFFromFilename(mMarqueePath.c_str()); fileFormat = FreeImage_GetFIFFromFilename(mMarqueePath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) { if (fileFormat == FIF_UNKNOWN) {
LOG(LogDebug) << "Marquee in unknown format, skipping image"; LOG(LogDebug) << "Marquee in unknown format, skipping image";
@ -180,12 +179,12 @@ bool MiximageGenerator::generateImage()
mMarquee = false; mMarquee = false;
} }
else { else {
#if defined(_WIN64) #if defined(_WIN64)
marqueeFile = FreeImage_LoadU(fileFormat, marqueeFile = FreeImage_LoadU(fileFormat,
Utils::String::stringToWideString(mMarqueePath).c_str()); Utils::String::stringToWideString(mMarqueePath).c_str());
#else #else
marqueeFile = FreeImage_Load(fileFormat, mMarqueePath.c_str()); marqueeFile = FreeImage_Load(fileFormat, mMarqueePath.c_str());
#endif #endif
if (!marqueeFile) { if (!marqueeFile) {
LOG(LogError) << "Couldn't load marquee image, corrupt file?"; LOG(LogError) << "Couldn't load marquee image, corrupt file?";
mMessage = "Error loading marquee image, corrupt file?"; mMessage = "Error loading marquee image, corrupt file?";
@ -195,19 +194,19 @@ bool MiximageGenerator::generateImage()
} }
if (mBox3D) { if (mBox3D) {
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mBox3DPath).c_str()); fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mBox3DPath).c_str());
#else #else
fileFormat = FreeImage_GetFileType(mBox3DPath.c_str()); fileFormat = FreeImage_GetFileType(mBox3DPath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) if (fileFormat == FIF_UNKNOWN)
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFIFFromFilenameU( fileFormat = FreeImage_GetFIFFromFilenameU(
Utils::String::stringToWideString(mBox3DPath).c_str()); Utils::String::stringToWideString(mBox3DPath).c_str());
#else #else
fileFormat = FreeImage_GetFIFFromFilename(mBox3DPath.c_str()); fileFormat = FreeImage_GetFIFFromFilename(mBox3DPath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) { if (fileFormat == FIF_UNKNOWN) {
LOG(LogDebug) << "3D box in unknown format, skipping image"; LOG(LogDebug) << "3D box in unknown format, skipping image";
@ -219,12 +218,12 @@ bool MiximageGenerator::generateImage()
mBox3D = false; mBox3D = false;
} }
else { else {
#if defined(_WIN64) #if defined(_WIN64)
boxFile = FreeImage_LoadU(fileFormat, boxFile =
Utils::String::stringToWideString(mBox3DPath).c_str()); FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mBox3DPath).c_str());
#else #else
boxFile = FreeImage_Load(fileFormat, mBox3DPath.c_str()); boxFile = FreeImage_Load(fileFormat, mBox3DPath.c_str());
#endif #endif
if (!boxFile) { if (!boxFile) {
LOG(LogError) << "Couldn't load 3D box image, corrupt file?"; LOG(LogError) << "Couldn't load 3D box image, corrupt file?";
mMessage = "Error loading 3d box image, corrupt file?"; mMessage = "Error loading 3d box image, corrupt file?";
@ -233,20 +232,19 @@ bool MiximageGenerator::generateImage()
} }
} }
else if (mCover) { else if (mCover) {
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFileTypeU( fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mCoverPath).c_str());
Utils::String::stringToWideString(mCoverPath).c_str()); #else
#else
fileFormat = FreeImage_GetFileType(mCoverPath.c_str()); fileFormat = FreeImage_GetFileType(mCoverPath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) if (fileFormat == FIF_UNKNOWN)
#if defined(_WIN64) #if defined(_WIN64)
fileFormat = FreeImage_GetFIFFromFilenameU( fileFormat = FreeImage_GetFIFFromFilenameU(
Utils::String::stringToWideString(mCoverPath).c_str()); Utils::String::stringToWideString(mCoverPath).c_str());
#else #else
fileFormat = FreeImage_GetFIFFromFilename(mCoverPath.c_str()); fileFormat = FreeImage_GetFIFFromFilename(mCoverPath.c_str());
#endif #endif
if (fileFormat == FIF_UNKNOWN) { if (fileFormat == FIF_UNKNOWN) {
LOG(LogDebug) << "Box cover in unknown format, skipping image"; LOG(LogDebug) << "Box cover in unknown format, skipping image";
@ -258,12 +256,12 @@ bool MiximageGenerator::generateImage()
mCover = false; mCover = false;
} }
else { else {
#if defined(_WIN64) #if defined(_WIN64)
boxFile = FreeImage_LoadU(fileFormat, boxFile =
Utils::String::stringToWideString(mCoverPath).c_str()); FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mCoverPath).c_str());
#else #else
boxFile = FreeImage_Load(fileFormat, mCoverPath.c_str()); boxFile = FreeImage_Load(fileFormat, mCoverPath.c_str());
#endif #endif
if (!boxFile) { if (!boxFile) {
LOG(LogError) << "Couldn't load box cover image, corrupt file?"; LOG(LogError) << "Couldn't load box cover image, corrupt file?";
mMessage = "Error loading box cover image, corrupt file?"; mMessage = "Error loading box cover image, corrupt file?";
@ -318,7 +316,7 @@ bool MiximageGenerator::generateImage()
std::vector<unsigned char> screenshotVector(fileWidth * fileHeight * 4); std::vector<unsigned char> screenshotVector(fileWidth * fileHeight * 4);
FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&screenshotVector.at(0)), screenshotFile, FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&screenshotVector.at(0)), screenshotFile,
filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1);
CImg<unsigned char> screenshotImage(fileWidth, fileHeight, 1, 4, 0); CImg<unsigned char> screenshotImage(fileWidth, fileHeight, 1, 4, 0);
@ -386,10 +384,10 @@ bool MiximageGenerator::generateImage()
std::vector<unsigned char> marqueeVector(fileWidth * fileHeight * 4); std::vector<unsigned char> marqueeVector(fileWidth * fileHeight * 4);
FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&marqueeVector.at(0)), marqueeFile, FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&marqueeVector.at(0)), marqueeFile,
filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1);
marqueeImage = CImg<unsigned char>(FreeImage_GetWidth(marqueeFile), marqueeImage = CImg<unsigned char>(FreeImage_GetWidth(marqueeFile),
FreeImage_GetHeight(marqueeFile), 1, 4, 0); FreeImage_GetHeight(marqueeFile), 1, 4, 0);
Utils::CImg::convertRGBAToCImg(marqueeVector, marqueeImage); Utils::CImg::convertRGBAToCImg(marqueeVector, marqueeImage);
Utils::CImg::removeTransparentPadding(marqueeImage); Utils::CImg::removeTransparentPadding(marqueeImage);
@ -409,7 +407,7 @@ bool MiximageGenerator::generateImage()
yPosMarquee = 0; yPosMarquee = 0;
// Only RGB channels for the image. // Only RGB channels for the image.
marqueeImageRGB = CImg<unsigned char>(marqueeImage.get_shared_channels(0,2)); marqueeImageRGB = CImg<unsigned char>(marqueeImage.get_shared_channels(0, 2));
// Only alpha channel for the image. // Only alpha channel for the image.
marqueeImageAlpha = CImg<unsigned char>(marqueeImage.get_shared_channel(3)); marqueeImageAlpha = CImg<unsigned char>(marqueeImage.get_shared_channel(3));
} }
@ -427,17 +425,17 @@ bool MiximageGenerator::generateImage()
std::vector<unsigned char> boxVector(fileWidth * fileHeight * 4); std::vector<unsigned char> boxVector(fileWidth * fileHeight * 4);
FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&boxVector.at(0)), boxFile, FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&boxVector.at(0)), boxFile, filePitch,
filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1);
boxImage = CImg<unsigned char>(FreeImage_GetWidth(boxFile), boxImage =
FreeImage_GetHeight(boxFile), 1, 4); CImg<unsigned char>(FreeImage_GetWidth(boxFile), FreeImage_GetHeight(boxFile), 1, 4);
Utils::CImg::convertRGBAToCImg(boxVector, boxImage); Utils::CImg::convertRGBAToCImg(boxVector, boxImage);
Utils::CImg::removeTransparentPadding(boxImage); Utils::CImg::removeTransparentPadding(boxImage);
float scaleFactor = static_cast<float>(boxTargetHeight) / float scaleFactor =
static_cast<float>(boxImage.height()); static_cast<float>(boxTargetHeight) / static_cast<float>(boxImage.height());
unsigned int width = static_cast<int>(static_cast<float>(boxImage.width()) * scaleFactor); unsigned int width = static_cast<int>(static_cast<float>(boxImage.width()) * scaleFactor);
unsigned int targetWidth = 0; unsigned int targetWidth = 0;
@ -464,7 +462,7 @@ bool MiximageGenerator::generateImage()
yPosBox = canvasImage.height() - boxImage.height(); yPosBox = canvasImage.height() - boxImage.height();
// Only RGB channels for the image. // Only RGB channels for the image.
boxImageRGB = CImg<unsigned char>(boxImage.get_shared_channels(0,2)); boxImageRGB = CImg<unsigned char>(boxImage.get_shared_channels(0, 2));
// Only alpha channel for the image. // Only alpha channel for the image.
boxImageAlpha = CImg<unsigned char>(boxImage.get_shared_channel(3)); boxImageAlpha = CImg<unsigned char>(boxImage.get_shared_channel(3));
} }
@ -478,20 +476,15 @@ bool MiximageGenerator::generateImage()
sampleFrameColor(screenshotImage, frameColor); sampleFrameColor(screenshotImage, frameColor);
// Upper / lower frame. // Upper / lower frame.
frameImage.draw_rectangle( frameImage.draw_rectangle(xPosScreenshot + 2, yPosScreenshot - screenshotFrameWidth,
xPosScreenshot + 2, xPosScreenshot + screenshotWidth - 2,
yPosScreenshot - screenshotFrameWidth, yPosScreenshot + screenshotHeight + screenshotFrameWidth - 1,
xPosScreenshot + screenshotWidth - 2, frameColor);
yPosScreenshot + screenshotHeight + screenshotFrameWidth - 1,
frameColor);
// Left / right frame. // Left / right frame.
frameImage.draw_rectangle( frameImage.draw_rectangle(xPosScreenshot - screenshotFrameWidth, yPosScreenshot + 2,
xPosScreenshot - screenshotFrameWidth, xPosScreenshot + screenshotWidth + screenshotFrameWidth - 1,
yPosScreenshot + 2, yPosScreenshot + screenshotHeight - 2, frameColor);
xPosScreenshot + screenshotWidth + screenshotFrameWidth - 1,
yPosScreenshot + screenshotHeight - 2,
frameColor);
// We draw circles in order to get rounded corners for the frame. // We draw circles in order to get rounded corners for the frame.
const unsigned int circleRadius = 8 * resolutionMultiplier; const unsigned int circleRadius = 8 * resolutionMultiplier;
@ -499,16 +492,18 @@ bool MiximageGenerator::generateImage()
// Upper left corner. // Upper left corner.
frameImage.draw_circle(xPosScreenshot + circleOffset, yPosScreenshot + circleOffset, frameImage.draw_circle(xPosScreenshot + circleOffset, yPosScreenshot + circleOffset,
circleRadius, frameColor); circleRadius, frameColor);
// Upper right corner. // Upper right corner.
frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1, frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1,
yPosScreenshot + circleOffset, circleRadius, frameColor); yPosScreenshot + circleOffset, circleRadius, frameColor);
// Lower right corner. // Lower right corner.
frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1, frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1,
yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, frameColor); yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius,
frameColor);
// Lower left corner. // Lower left corner.
frameImage.draw_circle(xPosScreenshot + circleOffset, frameImage.draw_circle(xPosScreenshot + circleOffset,
yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, frameColor); yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius,
frameColor);
CImg<unsigned char> frameImageRGB(frameImage.get_shared_channels(0, 2)); CImg<unsigned char> frameImageRGB(frameImage.get_shared_channels(0, 2));
@ -516,8 +511,8 @@ bool MiximageGenerator::generateImage()
canvasImage.draw_image(xPosScreenshot, yPosScreenshot, screenshotImage); canvasImage.draw_image(xPosScreenshot, yPosScreenshot, screenshotImage);
if (mMarquee) if (mMarquee)
canvasImage.draw_image(xPosMarquee, yPosMarquee, marqueeImageRGB, canvasImage.draw_image(xPosMarquee, yPosMarquee, marqueeImageRGB, marqueeImageAlpha, 1,
marqueeImageAlpha, 1, 255); 255);
if (mBox3D || mCover) if (mBox3D || mCover)
canvasImage.draw_image(xPosBox, yPosBox, boxImageRGB, boxImageAlpha, 1, 255); canvasImage.draw_image(xPosBox, yPosBox, boxImageRGB, boxImageAlpha, 1, 255);
@ -528,15 +523,16 @@ bool MiximageGenerator::generateImage()
FIBITMAP* mixImage = nullptr; FIBITMAP* mixImage = nullptr;
mixImage = FreeImage_ConvertFromRawBits(&canvasVector.at(0), canvasImage.width(), mixImage = FreeImage_ConvertFromRawBits(&canvasVector.at(0), canvasImage.width(),
canvasImage.height(), canvasImage.width() * 4, 32, canvasImage.height(), canvasImage.width() * 4, 32,
FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE); FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE);
#if defined(_WIN64) #if defined(_WIN64)
bool savedImage = (FreeImage_SaveU(FIF_PNG, mixImage, bool savedImage =
Utils::String::stringToWideString(getSavePath()).c_str()) != 0); (FreeImage_SaveU(FIF_PNG, mixImage,
#else Utils::String::stringToWideString(getSavePath()).c_str()) != 0);
#else
bool savedImage = (FreeImage_Save(FIF_PNG, mixImage, getSavePath().c_str()) != 0); bool savedImage = (FreeImage_Save(FIF_PNG, mixImage, getSavePath().c_str()) != 0);
#endif #endif
if (!savedImage) { if (!savedImage) {
LOG(LogError) << "Couldn't save miximage, permission problems or disk full?"; LOG(LogError) << "Couldn't save miximage, permission problems or disk full?";
@ -555,7 +551,9 @@ bool MiximageGenerator::generateImage()
} }
void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth, void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth,
const unsigned int& targetHeight, unsigned int& width, unsigned int& height) const unsigned int& targetHeight,
unsigned int& width,
unsigned int& height)
{ {
unsigned int adjustedTargetWidth = 0; unsigned int adjustedTargetWidth = 0;
float widthModifier = 0.5f; float widthModifier = 0.5f;
@ -572,13 +570,13 @@ void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth,
if (widthRatio >= 4) if (widthRatio >= 4)
widthModifier += Math::clamp(widthRatio / 40.0f, 0.0f, 0.3f); widthModifier += Math::clamp(widthRatio / 40.0f, 0.0f, 0.3f);
adjustedTargetWidth = static_cast<unsigned int>( adjustedTargetWidth =
static_cast<float>(targetWidth) * widthModifier); static_cast<unsigned int>(static_cast<float>(targetWidth) * widthModifier);
scaleFactor = static_cast<float>(adjustedTargetWidth) / static_cast<float>(width); scaleFactor = static_cast<float>(adjustedTargetWidth) / static_cast<float>(width);
// For really tall and narrow images, we may have exceeded the target height. // For really tall and narrow images, we may have exceeded the target height.
if (static_cast<int>(scaleFactor * static_cast<float>(height)) > if (static_cast<int>(scaleFactor * static_cast<float>(height)) >
static_cast<float>(targetHeight)) static_cast<float>(targetHeight))
scaleFactor = static_cast<float>(targetHeight) / static_cast<float>(height); scaleFactor = static_cast<float>(targetHeight) / static_cast<float>(height);
width = static_cast<int>(static_cast<float>(width) * scaleFactor); width = static_cast<int>(static_cast<float>(width) * scaleFactor);
@ -586,7 +584,7 @@ void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth,
} }
void MiximageGenerator::sampleFrameColor(CImg<unsigned char>& screenshotImage, void MiximageGenerator::sampleFrameColor(CImg<unsigned char>& screenshotImage,
unsigned char (&frameColor)[4]) unsigned char (&frameColor)[4])
{ {
// Calculate the number of samples relative to the configured resolution so we get // Calculate the number of samples relative to the configured resolution so we get
// the same result regardless of miximage target size seting. // the same result regardless of miximage target size seting.
@ -627,7 +625,7 @@ void MiximageGenerator::sampleFrameColor(CImg<unsigned char>& screenshotImage,
unsigned char blueC = Math::clamp(static_cast<int>(blueLine / 255), 0, 255); unsigned char blueC = Math::clamp(static_cast<int>(blueLine / 255), 0, 255);
// Convert to the HSL color space to be able to modify saturation and lightness. // Convert to the HSL color space to be able to modify saturation and lightness.
CImg<float> colorHSL = CImg<>(1,1,1,3).fill(redC, greenC, blueC).RGBtoHSL(); CImg<float> colorHSL = CImg<>(1, 1, 1, 3).fill(redC, greenC, blueC).RGBtoHSL();
float hue = colorHSL(0, 0, 0, 0); float hue = colorHSL(0, 0, 0, 0);
float saturation = colorHSL(0, 0, 0, 1); float saturation = colorHSL(0, 0, 0, 1);
@ -656,7 +654,7 @@ std::string MiximageGenerator::getSavePath()
// Extract possible subfolders from the path. // Extract possible subfolders from the path.
if (mGame->getSystemEnvData()->mStartPath != "") if (mGame->getSystemEnvData()->mStartPath != "")
subFolders = Utils::String::replace(Utils::FileSystem::getParent(mGame->getPath()), subFolders = Utils::String::replace(Utils::FileSystem::getParent(mGame->getPath()),
mGame->getSystemEnvData()->mStartPath, ""); mGame->getSystemEnvData()->mStartPath, "");
std::string path = FileData::getMediaDirectory(); std::string path = FileData::getMediaDirectory();

View file

@ -10,9 +10,9 @@
#ifndef ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H #ifndef ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H
#define ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H #define ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H
#include "utils/CImgUtil.h"
#include "FileData.h" #include "FileData.h"
#include "GuiComponent.h" #include "GuiComponent.h"
#include "utils/CImgUtil.h"
#include <FreeImage.h> #include <FreeImage.h>
#include <future> #include <future>
@ -29,8 +29,10 @@ public:
private: private:
bool generateImage(); bool generateImage();
void calculateMarqueeSize(const unsigned int& targetWidth, const unsigned int& targetHeight, void calculateMarqueeSize(const unsigned int& targetWidth,
unsigned int& width, unsigned int& height); const unsigned int& targetHeight,
unsigned int& width,
unsigned int& height);
void sampleFrameColor(CImg<unsigned char>& screenshotImage, unsigned char (&frameColor)[4]); void sampleFrameColor(CImg<unsigned char>& screenshotImage, unsigned char (&frameColor)[4]);
std::string getSavePath(); std::string getSavePath();

View file

@ -13,6 +13,7 @@
namespace PlatformIds namespace PlatformIds
{ {
// clang-format off
std::vector<std::string> platformNames = { std::vector<std::string> platformNames = {
"unknown", // Nothing set. "unknown", // Nothing set.
@ -132,6 +133,7 @@ namespace PlatformIds
"ignore", // Do not allow scraping for this system. "ignore", // Do not allow scraping for this system.
"invalid" "invalid"
}; };
// clang-format on
PlatformId getPlatformId(const std::string& str) PlatformId getPlatformId(const std::string& str)
{ {
@ -148,6 +150,8 @@ namespace PlatformIds
const std::string getPlatformName(PlatformId id) const std::string getPlatformName(PlatformId id)
{ {
// Return the platform name.
return platformNames[id]; return platformNames[id];
} }
}
} // namespace PlatformIds

View file

@ -135,6 +135,7 @@ namespace PlatformIds
PlatformId getPlatformId(const std::string& str); PlatformId getPlatformId(const std::string& str);
const std::string getPlatformName(PlatformId id); const std::string getPlatformName(PlatformId id);
}
} // namespace PlatformIds
#endif // ES_APP_PLATFORM_ID_H #endif // ES_APP_PLATFORM_ID_H

View file

@ -11,12 +11,6 @@
#include "SystemData.h" #include "SystemData.h"
#include "resources/ResourceManager.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "views/gamelist/IGameListView.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "FileSorts.h" #include "FileSorts.h"
@ -25,6 +19,12 @@
#include "Platform.h" #include "Platform.h"
#include "Settings.h" #include "Settings.h"
#include "ThemeData.h" #include "ThemeData.h"
#include "resources/ResourceManager.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "views/gamelist/IGameListView.h"
#include <fstream> #include <fstream>
#include <pugixml.hpp> #include <pugixml.hpp>
@ -39,14 +39,10 @@ FindRules::FindRules()
loadFindRules(); loadFindRules();
} }
FindRules::~FindRules()
{
}
void FindRules::loadFindRules() void FindRules::loadFindRules()
{ {
std::string customSystemsDirectory = std::string customSystemsDirectory =
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems"; Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
std::string path = customSystemsDirectory + "/es_find_rules.xml"; std::string path = customSystemsDirectory + "/es_find_rules.xml";
@ -54,16 +50,16 @@ void FindRules::loadFindRules()
LOG(LogInfo) << "Found custom find rules configuration file"; LOG(LogInfo) << "Found custom find rules configuration file";
} }
else { else {
#if defined(_WIN64) #if defined(_WIN64)
path = ResourceManager::getInstance()-> path = ResourceManager::getInstance()->getResourcePath(
getResourcePath(":/systems/windows/es_find_rules.xml", false); ":/systems/windows/es_find_rules.xml", false);
#elif defined(__APPLE__) #elif defined(__APPLE__)
path = ResourceManager::getInstance()-> path = ResourceManager::getInstance()->getResourcePath(":/systems/macos/es_find_rules.xml",
getResourcePath(":/systems/macos/es_find_rules.xml", false); false);
#else #else
path = ResourceManager::getInstance()-> path = ResourceManager::getInstance()->getResourcePath(":/systems/unix/es_find_rules.xml",
getResourcePath(":/systems/unix/es_find_rules.xml", false); false);
#endif #endif
} }
if (path == "") { if (path == "") {
@ -74,11 +70,11 @@ void FindRules::loadFindRules()
LOG(LogInfo) << "Parsing find rules configuration file \"" << path << "\"..."; LOG(LogInfo) << "Parsing find rules configuration file \"" << path << "\"...";
pugi::xml_document doc; pugi::xml_document doc;
#if defined(_WIN64) #if defined(_WIN64)
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
#else #else
pugi::xml_parse_result res = doc.load_file(path.c_str()); pugi::xml_parse_result res = doc.load_file(path.c_str());
#endif #endif
if (!res) { if (!res) {
LOG(LogError) << "Couldn't parse es_find_rules.xml: " << res.description(); LOG(LogError) << "Couldn't parse es_find_rules.xml: " << res.description();
@ -97,81 +93,79 @@ void FindRules::loadFindRules()
CoreRules coreRules; CoreRules coreRules;
for (pugi::xml_node emulator = ruleList.child("emulator"); emulator; for (pugi::xml_node emulator = ruleList.child("emulator"); emulator;
emulator = emulator.next_sibling("emulator")) { emulator = emulator.next_sibling("emulator")) {
std::string emulatorName = emulator.attribute("name").as_string(); std::string emulatorName = emulator.attribute("name").as_string();
if (emulatorName.empty()) { if (emulatorName.empty()) {
LOG(LogWarning) << "Found emulator tag without name attribute, skipping entry"; LOG(LogWarning) << "Found emulator tag without name attribute, skipping entry";
continue; continue;
} }
if (mEmulators.find(emulatorName) != mEmulators.end()) { if (mEmulators.find(emulatorName) != mEmulators.end()) {
LOG(LogWarning) << "Found repeating emulator tag \"" << emulatorName << LOG(LogWarning) << "Found repeating emulator tag \"" << emulatorName
"\", skipping entry"; << "\", skipping entry";
continue; continue;
} }
for (pugi::xml_node rule = emulator.child("rule"); rule; rule = rule.next_sibling("rule")) { for (pugi::xml_node rule = emulator.child("rule"); rule; rule = rule.next_sibling("rule")) {
std::string ruleType = rule.attribute("type").as_string(); std::string ruleType = rule.attribute("type").as_string();
if (ruleType.empty()) { if (ruleType.empty()) {
LOG(LogWarning) << "Found rule tag without type attribute for emulator \"" << LOG(LogWarning) << "Found rule tag without type attribute for emulator \""
emulatorName << "\", skipping entry"; << emulatorName << "\", skipping entry";
continue; continue;
} }
#if defined(_WIN64) #if defined(_WIN64)
if (ruleType != "winregistrypath" && ruleType != "systempath" && if (ruleType != "winregistrypath" && ruleType != "systempath" &&
ruleType != "staticpath") { ruleType != "staticpath") {
#else #else
if (ruleType != "systempath" && ruleType != "staticpath") { if (ruleType != "systempath" && ruleType != "staticpath") {
#endif #endif
LOG(LogWarning) << "Found invalid rule type \"" << ruleType << LOG(LogWarning) << "Found invalid rule type \"" << ruleType << "\" for emulator \""
"\" for emulator \"" << emulatorName << "\", skipping entry"; << emulatorName << "\", skipping entry";
continue; continue;
} }
for (pugi::xml_node entry = rule.child("entry"); entry; for (pugi::xml_node entry = rule.child("entry"); entry;
entry = entry.next_sibling("entry")) { entry = entry.next_sibling("entry")) {
std::string entryValue = entry.text().get(); std::string entryValue = entry.text().get();
if (ruleType == "systempath") if (ruleType == "systempath")
emulatorRules.systemPaths.push_back(entryValue); emulatorRules.systemPaths.push_back(entryValue);
else if (ruleType == "staticpath") else if (ruleType == "staticpath")
emulatorRules.staticPaths.push_back(entryValue); emulatorRules.staticPaths.push_back(entryValue);
#if defined(_WIN64) #if defined(_WIN64)
else if (ruleType == "winregistrypath") else if (ruleType == "winregistrypath")
emulatorRules.winRegistryPaths.push_back(entryValue); emulatorRules.winRegistryPaths.push_back(entryValue);
#endif #endif
} }
} }
mEmulators[emulatorName] = emulatorRules; mEmulators[emulatorName] = emulatorRules;
emulatorRules.systemPaths.clear(); emulatorRules.systemPaths.clear();
emulatorRules.staticPaths.clear(); emulatorRules.staticPaths.clear();
#if defined(_WIN64) #if defined(_WIN64)
emulatorRules.winRegistryPaths.clear(); emulatorRules.winRegistryPaths.clear();
#endif #endif
} }
for (pugi::xml_node core = ruleList.child("core"); core; for (pugi::xml_node core = ruleList.child("core"); core; core = core.next_sibling("core")) {
core = core.next_sibling("core")) {
std::string coreName = core.attribute("name").as_string(); std::string coreName = core.attribute("name").as_string();
if (coreName.empty()) { if (coreName.empty()) {
LOG(LogWarning) << "Found core tag without name attribute, skipping entry"; LOG(LogWarning) << "Found core tag without name attribute, skipping entry";
continue; continue;
} }
if (mCores.find(coreName) != mCores.end()) { if (mCores.find(coreName) != mCores.end()) {
LOG(LogWarning) << "Found repeating core tag \"" << coreName << LOG(LogWarning) << "Found repeating core tag \"" << coreName << "\", skipping entry";
"\", skipping entry";
continue; continue;
} }
for (pugi::xml_node rule = core.child("rule"); rule; rule = rule.next_sibling("rule")) { for (pugi::xml_node rule = core.child("rule"); rule; rule = rule.next_sibling("rule")) {
std::string ruleType = rule.attribute("type").as_string(); std::string ruleType = rule.attribute("type").as_string();
if (ruleType.empty()) { if (ruleType.empty()) {
LOG(LogWarning) << "Found rule tag without type attribute for core \"" << LOG(LogWarning) << "Found rule tag without type attribute for core \"" << coreName
coreName << "\", skipping entry"; << "\", skipping entry";
continue; continue;
} }
if (ruleType != "corepath") { if (ruleType != "corepath") {
LOG(LogWarning) << "Found invalid rule type \"" << ruleType << LOG(LogWarning) << "Found invalid rule type \"" << ruleType << "\" for core \""
"\" for core \"" << coreName << "\", skipping entry"; << coreName << "\", skipping entry";
continue; continue;
} }
for (pugi::xml_node entry = rule.child("entry"); entry; for (pugi::xml_node entry = rule.child("entry"); entry;
entry = entry.next_sibling("entry")) { entry = entry.next_sibling("entry")) {
std::string entryValue = entry.text().get(); std::string entryValue = entry.text().get();
if (ruleType == "corepath") if (ruleType == "corepath")
coreRules.corePaths.push_back(entryValue); coreRules.corePaths.push_back(entryValue);
@ -182,23 +176,22 @@ void FindRules::loadFindRules()
} }
} }
SystemData::SystemData( SystemData::SystemData(const std::string& name,
const std::string& name, const std::string& fullName,
const std::string& fullName, SystemEnvironmentData* envData,
SystemEnvironmentData* envData, const std::string& themeFolder,
const std::string& themeFolder, bool CollectionSystem,
bool CollectionSystem, bool CustomCollectionSystem)
bool CustomCollectionSystem) : mName(name)
: mName(name), , mFullName(fullName)
mFullName(fullName), , mEnvData(envData)
mEnvData(envData), , mThemeFolder(themeFolder)
mThemeFolder(themeFolder), , mIsCollectionSystem(CollectionSystem)
mIsCollectionSystem(CollectionSystem), , mIsCustomCollectionSystem(CustomCollectionSystem)
mIsCustomCollectionSystem(CustomCollectionSystem), , mIsGroupedCustomCollectionSystem(false)
mIsGroupedCustomCollectionSystem(false), , mIsGameSystem(true)
mIsGameSystem(true), , mScrapeFlag(false)
mScrapeFlag(false), , mPlaceholder(nullptr)
mPlaceholder(nullptr)
{ {
mFilterIndex = new FileFilterIndex(); mFilterIndex = new FileFilterIndex();
@ -220,7 +213,7 @@ SystemData::SystemData(
setupSystemSortType(mRootFolder); setupSystemSortType(mRootFolder);
mRootFolder->sort(mRootFolder->getSortTypeFromString(mRootFolder->getSortTypeString()), mRootFolder->sort(mRootFolder->getSortTypeFromString(mRootFolder->getSortTypeString()),
Settings::getInstance()->getBool("FavoritesFirst")); Settings::getInstance()->getBool("FavoritesFirst"));
indexAllGameFilters(mRootFolder); indexAllGameFilters(mRootFolder);
} }
@ -275,13 +268,13 @@ bool SystemData::populateFolder(FileData* folder)
return false; return false;
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
it != dirContent.cend(); it++) { it != dirContent.cend(); it++) {
filePath = *it; filePath = *it;
// Skip any recursive symlinks as those would hang the application at various places. // Skip any recursive symlinks as those would hang the application at various places.
if (Utils::FileSystem::isSymlink(filePath)) { if (Utils::FileSystem::isSymlink(filePath)) {
if (Utils::FileSystem::resolveSymlink(filePath) == if (Utils::FileSystem::resolveSymlink(filePath) ==
Utils::FileSystem::getFileName(filePath)) { Utils::FileSystem::getFileName(filePath)) {
LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink"; LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink";
continue; continue;
} }
@ -289,9 +282,9 @@ bool SystemData::populateFolder(FileData* folder)
// Skip hidden files and folders. // Skip hidden files and folders.
if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) { if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) {
LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden " << LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden "
(Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"") << << (Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"")
filePath << "\""; << filePath << "\"";
continue; continue;
} }
@ -306,7 +299,7 @@ bool SystemData::populateFolder(FileData* folder)
isGame = false; isGame = false;
if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(), if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(),
extension) != mEnvData->mSearchExtensions.cend()) { extension) != mEnvData->mSearchExtensions.cend()) {
FileData* newGame = new FileData(GAME, filePath, mEnvData, this); FileData* newGame = new FileData(GAME, filePath, mEnvData, this);
// Prevent new arcade assets from being added. // Prevent new arcade assets from being added.
@ -326,15 +319,17 @@ bool SystemData::populateFolder(FileData* folder)
if (Utils::FileSystem::isSymlink(filePath)) { if (Utils::FileSystem::isSymlink(filePath)) {
const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(filePath); const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(filePath);
const std::string canonicalStartPath = const std::string canonicalStartPath =
Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath); Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath);
const std::string combinedPath = mEnvData->mStartPath + const std::string combinedPath =
canonicalPath.substr(canonicalStartPath.size(), mEnvData->mStartPath +
canonicalStartPath.size() - canonicalPath.size()); canonicalPath.substr(canonicalStartPath.size(),
canonicalStartPath.size() - canonicalPath.size());
if (filePath.find(combinedPath) == 0) { if (filePath.find(combinedPath) == 0) {
LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink"; LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink";
continue; continue;
} }
} }
FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this); FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this);
populateFolder(newFolder); populateFolder(newFolder);
@ -352,17 +347,15 @@ void SystemData::indexAllGameFilters(const FileData* folder)
{ {
const std::vector<FileData*>& children = folder->getChildren(); const std::vector<FileData*>& children = folder->getChildren();
for (std::vector<FileData*>::const_iterator it = children.cbegin(); for (std::vector<FileData*>::const_iterator it = children.cbegin(); // Line break.
it != children.cend(); it++) { it != children.cend(); it++) {
switch ((*it)->getType()) { switch ((*it)->getType()) {
case GAME: { case GAME:
mFilterIndex->addToIndex(*it); mFilterIndex->addToIndex(*it);
} break;
break; case FOLDER:
case FOLDER: {
indexAllGameFilters(*it); indexAllGameFilters(*it);
} break;
break;
default: default:
break; break;
} }
@ -400,11 +393,11 @@ bool SystemData::loadConfig()
LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"..."; LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"...";
pugi::xml_document doc; pugi::xml_document doc;
#if defined(_WIN64) #if defined(_WIN64)
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
#else #else
pugi::xml_parse_result res = doc.load_file(path.c_str()); pugi::xml_parse_result res = doc.load_file(path.c_str());
#endif #endif
if (!res) { if (!res) {
LOG(LogError) << "Couldn't parse es_systems.xml: " << res.description(); LOG(LogError) << "Couldn't parse es_systems.xml: " << res.description();
@ -420,7 +413,7 @@ bool SystemData::loadConfig()
} }
for (pugi::xml_node system = systemList.child("system"); system; for (pugi::xml_node system = systemList.child("system"); system;
system = system.next_sibling("system")) { system = system.next_sibling("system")) {
std::string name; std::string name;
std::string fullname; std::string fullname;
std::string path; std::string path;
@ -436,21 +429,21 @@ bool SystemData::loadConfig()
// the ROM path configured as ROMDirectory in es_settings.xml. If it's set to "" // the ROM path configured as ROMDirectory in es_settings.xml. If it's set to ""
// in this configuration file, the default hardcoded path $HOME/ROMs/ will be used. // in this configuration file, the default hardcoded path $HOME/ROMs/ will be used.
path = Utils::String::replace(path, "%ROMPATH%", rompath); path = Utils::String::replace(path, "%ROMPATH%", rompath);
#if defined(_WIN64) #if defined(_WIN64)
path = Utils::String::replace(path, "\\", "/"); path = Utils::String::replace(path, "\\", "/");
#endif #endif
path = Utils::String::replace(path, "//", "/"); path = Utils::String::replace(path, "//", "/");
// Check that the ROM directory for the system is valid or otherwise abort the processing. // Check that the ROM directory for the system is valid or otherwise abort the processing.
if (!Utils::FileSystem::exists(path)) { if (!Utils::FileSystem::exists(path)) {
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name
name << "\" as the defined ROM directory \"" << path << "\" does not exist"; << "\" as the defined ROM directory \"" << path << "\" does not exist";
continue; continue;
} }
if (!Utils::FileSystem::isDirectory(path)) { if (!Utils::FileSystem::isDirectory(path)) {
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name
name << "\" as the defined ROM directory \"" << path << << "\" as the defined ROM directory \"" << path
"\" is not actually a directory"; << "\" is not actually a directory";
continue; continue;
} }
if (Utils::FileSystem::isSymlink(path)) { if (Utils::FileSystem::isSymlink(path)) {
@ -458,9 +451,9 @@ bool SystemData::loadConfig()
// as that would lead to an infite loop, meaning the application would never start. // as that would lead to an infite loop, meaning the application would never start.
std::string resolvedRompath = Utils::FileSystem::getCanonicalPath(rompath); std::string resolvedRompath = Utils::FileSystem::getCanonicalPath(rompath);
if (resolvedRompath.find(Utils::FileSystem::getCanonicalPath(path)) == 0) { if (resolvedRompath.find(Utils::FileSystem::getCanonicalPath(path)) == 0) {
LOG(LogWarning) << "Skipping system \"" << name << LOG(LogWarning) << "Skipping system \"" << name
"\" as the defined ROM directory \"" << path << << "\" as the defined ROM directory \"" << path
"\" is an infinitely recursive symlink"; << "\" is an infinitely recursive symlink";
continue; continue;
} }
} }
@ -472,7 +465,7 @@ bool SystemData::loadConfig()
// Platform ID list // Platform ID list
const std::string platformList = const std::string platformList =
Utils::String::toLower(system.child("platform").text().get()); Utils::String::toLower(system.child("platform").text().get());
std::vector<std::string> platformStrs = readList(platformList); std::vector<std::string> platformStrs = readList(platformList);
std::vector<PlatformIds::PlatformId> platformIds; std::vector<PlatformIds::PlatformId> platformIds;
for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) { for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) {
@ -489,8 +482,8 @@ bool SystemData::loadConfig()
// If there's a platform entry defined but it does not match the list of supported // If there's a platform entry defined but it does not match the list of supported
// platforms, then generate a warning. // platforms, then generate a warning.
if (str != "" && platformId == PlatformIds::PLATFORM_UNKNOWN) if (str != "" && platformId == PlatformIds::PLATFORM_UNKNOWN)
LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \"" << LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \""
name << "\""; << name << "\"";
else if (platformId != PlatformIds::PLATFORM_UNKNOWN) else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
platformIds.push_back(platformId); platformIds.push_back(platformId);
} }
@ -501,26 +494,27 @@ bool SystemData::loadConfig()
// Validate. // Validate.
if (name.empty()) { if (name.empty()) {
LOG(LogError) << LOG(LogError)
"A system in the es_systems.xml file has no name defined, skipping entry"; << "A system in the es_systems.xml file has no name defined, skipping entry";
continue; continue;
} }
else if (fullname.empty() || path.empty() || extensions.empty() || cmd.empty()) { else if (fullname.empty() || path.empty() || extensions.empty() || cmd.empty()) {
LOG(LogError) << "System \"" << name << "\" is missing the fullname, path, " LOG(LogError) << "System \"" << name
"extension, or command tag, skipping entry"; << "\" is missing the fullname, path, "
"extension, or command tag, skipping entry";
continue; continue;
} }
// Convert path to generic directory seperators. // Convert path to generic directory seperators.
path = Utils::FileSystem::getGenericPath(path); path = Utils::FileSystem::getGenericPath(path);
#if defined(_WIN64) #if defined(_WIN64)
if (!Settings::getInstance()->getBool("ShowHiddenFiles") && if (!Settings::getInstance()->getBool("ShowHiddenFiles") &&
Utils::FileSystem::isHidden(path)) { Utils::FileSystem::isHidden(path)) {
LOG(LogWarning) << "Skipping hidden ROM folder \"" << path << "\""; LOG(LogWarning) << "Skipping hidden ROM folder \"" << path << "\"";
continue; continue;
} }
#endif #endif
// Create the system runtime environment data. // Create the system runtime environment data.
SystemEnvironmentData* envData = new SystemEnvironmentData; SystemEnvironmentData* envData = new SystemEnvironmentData;
@ -535,8 +529,7 @@ bool SystemData::loadConfig()
// If the option to show hidden games has been disabled, then check whether all // If the option to show hidden games has been disabled, then check whether all
// games for the system are hidden. That will flag the system as empty. // games for the system are hidden. That will flag the system as empty.
if (!Settings::getInstance()->getBool("ShowHiddenGames")) { if (!Settings::getInstance()->getBool("ShowHiddenGames")) {
std::vector<FileData*> recursiveGames = std::vector<FileData*> recursiveGames = newSys->getRootFolder()->getChildrenRecursive();
newSys->getRootFolder()->getChildrenRecursive();
onlyHidden = true; onlyHidden = true;
for (auto it = recursiveGames.cbegin(); it != recursiveGames.cend(); it++) { for (auto it = recursiveGames.cbegin(); it != recursiveGames.cend(); it++) {
if ((*it)->getType() != FOLDER) { if ((*it)->getType() != FOLDER) {
@ -548,8 +541,8 @@ bool SystemData::loadConfig()
} }
if (newSys->getRootFolder()->getChildrenByFilename().size() == 0 || onlyHidden) { if (newSys->getRootFolder()->getChildrenByFilename().size() == 0 || onlyHidden) {
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name
name << "\" as no files matched any of the defined file extensions"; << "\" as no files matched any of the defined file extensions";
delete newSys; delete newSys;
} }
else { else {
@ -559,8 +552,7 @@ bool SystemData::loadConfig()
// Sort systems by their full names. // Sort systems by their full names.
std::sort(std::begin(sSystemVector), std::end(sSystemVector), std::sort(std::begin(sSystemVector), std::end(sSystemVector),
[](SystemData* a, SystemData* b) { [](SystemData* a, SystemData* b) { return a->getFullName() < b->getFullName(); });
return a->getFullName() < b->getFullName(); });
// Don't load any collections if there are no systems available. // Don't load any collections if there are no systems available.
if (sSystemVector.size() > 0) if (sSystemVector.size() > 0)
@ -580,18 +572,18 @@ void SystemData::deleteSystems()
std::string SystemData::getConfigPath(bool legacyWarning) std::string SystemData::getConfigPath(bool legacyWarning)
{ {
if (legacyWarning) { if (legacyWarning) {
std::string legacyConfigFile = Utils::FileSystem::getHomePath() + std::string legacyConfigFile =
"/.emulationstation/es_systems.cfg"; Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg";
if (Utils::FileSystem::exists(legacyConfigFile)) { if (Utils::FileSystem::exists(legacyConfigFile)) {
LOG(LogInfo) << "Found legacy systems configuration file \"" << legacyConfigFile << LOG(LogInfo) << "Found legacy systems configuration file \"" << legacyConfigFile
"\", to retain your customizations move it to " << "\", to retain your customizations move it to "
"\"custom_systems/es_systems.xml\" or otherwise delete the file"; "\"custom_systems/es_systems.xml\" or otherwise delete the file";
} }
} }
std::string customSystemsDirectory = std::string customSystemsDirectory =
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems"; Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
if (!Utils::FileSystem::exists(customSystemsDirectory)) { if (!Utils::FileSystem::exists(customSystemsDirectory)) {
LOG(LogInfo) << "Creating custom systems directory \"" << customSystemsDirectory << "\"..."; LOG(LogInfo) << "Creating custom systems directory \"" << customSystemsDirectory << "\"...";
@ -608,16 +600,14 @@ std::string SystemData::getConfigPath(bool legacyWarning)
return path; return path;
} }
#if defined(_WIN64) #if defined(_WIN64)
path = ResourceManager::getInstance()-> path =
getResourcePath(":/systems/windows/es_systems.xml", true); ResourceManager::getInstance()->getResourcePath(":/systems/windows/es_systems.xml", true);
#elif defined(__APPLE__) #elif defined(__APPLE__)
path = ResourceManager::getInstance()-> path = ResourceManager::getInstance()->getResourcePath(":/systems/macos/es_systems.xml", true);
getResourcePath(":/systems/macos/es_systems.xml", true); #else
#else path = ResourceManager::getInstance()->getResourcePath(":/systems/unix/es_systems.xml", true);
path = ResourceManager::getInstance()-> #endif
getResourcePath(":/systems/unix/es_systems.xml", true);
#endif
return path; return path;
} }
@ -627,16 +617,16 @@ bool SystemData::createSystemDirectories()
std::string path = getConfigPath(false); std::string path = getConfigPath(false);
const std::string rompath = FileData::getROMDirectory(); const std::string rompath = FileData::getROMDirectory();
if (!Utils::FileSystem::exists(path)) { if (!Utils::FileSystem::exists(path)) {
LOG(LogInfo) << "Systems configuration file does not exist, aborting"; LOG(LogInfo) << "Systems configuration file does not exist, aborting";
return true; return true;
} }
LOG(LogInfo) << "Generating ROM directory structure..."; LOG(LogInfo) << "Generating ROM directory structure...";
if (Utils::FileSystem::exists(rompath) && Utils::FileSystem::isRegularFile(rompath)) { if (Utils::FileSystem::exists(rompath) && Utils::FileSystem::isRegularFile(rompath)) {
LOG(LogError) << LOG(LogError) << "Requested ROM directory \"" << rompath
"Requested ROM directory \"" << rompath << "\" is actually a file, aborting"; << "\" is actually a file, aborting";
return true; return true;
} }
@ -654,11 +644,11 @@ bool SystemData::createSystemDirectories()
LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"..."; LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"...";
pugi::xml_document doc; pugi::xml_document doc;
#if defined(_WIN64) #if defined(_WIN64)
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
#else #else
pugi::xml_parse_result res = doc.load_file(path.c_str()); pugi::xml_parse_result res = doc.load_file(path.c_str());
#endif #endif
if (!res) { if (!res) {
LOG(LogError) << "Couldn't parse es_systems.xml"; LOG(LogError) << "Couldn't parse es_systems.xml";
@ -677,7 +667,7 @@ bool SystemData::createSystemDirectories()
std::vector<std::string> systemsVector; std::vector<std::string> systemsVector;
for (pugi::xml_node system = systemList.child("system"); system; for (pugi::xml_node system = systemList.child("system"); system;
system = system.next_sibling("system")) { system = system.next_sibling("system")) {
std::string systemDir; std::string systemDir;
std::string name; std::string name;
std::string fullname; std::string fullname;
@ -701,8 +691,9 @@ bool SystemData::createSystemDirectories()
// Check that the %ROMPATH% variable is actually used for the path element. // Check that the %ROMPATH% variable is actually used for the path element.
// If not, skip the system. // If not, skip the system.
if (path.find("%ROMPATH%") != 0) { if (path.find("%ROMPATH%") != 0) {
LOG(LogWarning) << "The path element for system \"" << name << "\" does not " LOG(LogWarning) << "The path element for system \"" << name
"utilize the %ROMPATH% variable, skipping entry"; << "\" does not "
"utilize the %ROMPATH% variable, skipping entry";
continue; continue;
} }
else { else {
@ -711,14 +702,13 @@ bool SystemData::createSystemDirectories()
// Trim any leading directory separator characters. // Trim any leading directory separator characters.
systemDir.erase(systemDir.begin(), systemDir.erase(systemDir.begin(),
std::find_if(systemDir.begin(), systemDir.end(), [](char c) { std::find_if(systemDir.begin(), systemDir.end(),
return c != '/' && c != '\\'; [](char c) { return c != '/' && c != '\\'; }));
}));
if (!Utils::FileSystem::exists(rompath + systemDir)) { if (!Utils::FileSystem::exists(rompath + systemDir)) {
if (!Utils::FileSystem::createDirectory(rompath + systemDir)) { if (!Utils::FileSystem::createDirectory(rompath + systemDir)) {
LOG(LogError) << "Couldn't create system directory \"" << systemDir << LOG(LogError) << "Couldn't create system directory \"" << systemDir
"\", permission problems or disk full?"; << "\", permission problems or disk full?";
return true; return true;
} }
else { else {
@ -739,16 +729,17 @@ bool SystemData::createSystemDirectories()
return true; return true;
} }
#if defined(_WIN64) #if defined(_WIN64)
systemInfoFile.open(Utils::String::stringToWideString(rompath + systemInfoFile.open(
systemDir + systemInfoFileName).c_str()); Utils::String::stringToWideString(rompath + systemDir + systemInfoFileName).c_str());
#else #else
systemInfoFile.open(rompath + systemDir + systemInfoFileName); systemInfoFile.open(rompath + systemDir + systemInfoFileName);
#endif #endif
if (systemInfoFile.fail()) { if (systemInfoFile.fail()) {
LOG(LogError) << "Couldn't create system information file \"" << rompath + LOG(LogError) << "Couldn't create system information file \""
systemDir + systemInfoFileName << "\", permission problems or disk full?"; << rompath + systemDir + systemInfoFileName
<< "\", permission problems or disk full?";
systemInfoFile.close(); systemInfoFile.close();
return true; return true;
} }
@ -770,12 +761,12 @@ bool SystemData::createSystemDirectories()
systemsVector.push_back(systemDir + ": " + fullname); systemsVector.push_back(systemDir + ": " + fullname);
if (replaceInfoFile) { if (replaceInfoFile) {
LOG(LogInfo) << "Replaced existing system information file \"" << LOG(LogInfo) << "Replaced existing system information file \""
rompath + systemDir + systemInfoFileName << "\""; << rompath + systemDir + systemInfoFileName << "\"";
} }
else { else {
LOG(LogInfo) << "Created system information file \"" << LOG(LogInfo) << "Created system information file \""
rompath + systemDir + systemInfoFileName << "\""; << rompath + systemDir + systemInfoFileName << "\"";
} }
} }
@ -793,11 +784,11 @@ bool SystemData::createSystemDirectories()
if (systemsFileSuccess) { if (systemsFileSuccess) {
std::ofstream systemsFile; std::ofstream systemsFile;
#if defined(_WIN64) #if defined(_WIN64)
systemsFile.open(Utils::String::stringToWideString(rompath + systemsFileName).c_str()); systemsFile.open(Utils::String::stringToWideString(rompath + systemsFileName).c_str());
#else #else
systemsFile.open(rompath + systemsFileName); systemsFile.open(rompath + systemsFileName);
#endif #endif
if (systemsFile.fail()) { if (systemsFile.fail()) {
systemsFileSuccess = false; systemsFileSuccess = false;
} }
@ -811,7 +802,7 @@ bool SystemData::createSystemDirectories()
if (!systemsFileSuccess) { if (!systemsFileSuccess) {
LOG(LogWarning) << "System directories successfully created but couldn't create " LOG(LogWarning) << "System directories successfully created but couldn't create "
"the systems.txt file in the ROM directory root"; "the systems.txt file in the ROM directory root";
return false; return false;
} }
} }
@ -867,8 +858,8 @@ std::string SystemData::getGamelistPath(bool forWrite) const
if (Utils::FileSystem::exists(filePath)) if (Utils::FileSystem::exists(filePath))
return filePath; return filePath;
filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName +
mName + "/gamelist.xml"; "/gamelist.xml";
// Make sure the directory exists if we're going to write to it, // Make sure the directory exists if we're going to write to it,
// or crashes will happen. // or crashes will happen.
@ -899,17 +890,12 @@ std::string SystemData::getThemePath() const
return localThemePath; return localThemePath;
// Not system theme, try default system theme in theme set. // Not system theme, try default system theme in theme set.
localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + localThemePath =
"/theme.xml"; Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml";
return localThemePath; return localThemePath;
} }
bool SystemData::hasGamelist() const
{
return (Utils::FileSystem::exists(getGamelistPath(false)));
}
SystemData* SystemData::getRandomSystem(const SystemData* currentSystem) SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
{ {
unsigned int total = 0; unsigned int total = 0;
@ -927,7 +913,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
// Get a random number in range. // Get a random number in range.
std::random_device randDev; std::random_device randDev;
// Mersenne Twister pseudorandom number generator. // Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()}; std::mt19937 engine { randDev() };
std::uniform_int_distribution<int> uniform_dist(0, total - 1); std::uniform_int_distribution<int> uniform_dist(0, total - 1);
int target = uniform_dist(engine); int target = uniform_dist(engine);
@ -942,8 +928,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
} }
} }
} }
} } while (randomSystem == currentSystem);
while (randomSystem == currentSystem);
return randomSystem; return randomSystem;
} }
@ -956,13 +941,17 @@ FileData* SystemData::getRandomGame(const FileData* currentGame)
// If we're in the custom collection group list, then get the list of collections, // If we're in the custom collection group list, then get the list of collections,
// otherwise get a list of all the folder and file entries in the view. // otherwise get a list of all the folder and file entries in the view.
if (currentGame && currentGame->getType() == FOLDER && currentGame-> if (currentGame && currentGame->getType() == FOLDER &&
getSystem()->isGroupedCustomCollection()) { currentGame->getSystem()->isGroupedCustomCollection()) {
gameList = mRootFolder->getParent()->getChildrenListToDisplay(); gameList = mRootFolder->getParent()->getChildrenListToDisplay();
} }
else { else {
gameList = ViewController::get()->getGameListView(mRootFolder-> gameList = ViewController::get()
getSystem()).get()->getCursor()->getParent()->getChildrenListToDisplay(); ->getGameListView(mRootFolder->getSystem())
.get()
->getCursor()
->getParent()
->getChildrenListToDisplay();
} }
if (gameList.size() > 0 && gameList.front()->getParent()->getOnlyFoldersFlag()) if (gameList.size() > 0 && gameList.front()->getParent()->getOnlyFoldersFlag())
@ -1003,11 +992,10 @@ FileData* SystemData::getRandomGame(const FileData* currentGame)
// Get a random number in range. // Get a random number in range.
std::random_device randDev; std::random_device randDev;
// Mersenne Twister pseudorandom number generator. // Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()}; std::mt19937 engine { randDev() };
std::uniform_int_distribution<int> uniform_dist(0, total - 1); std::uniform_int_distribution<int> uniform_dist(0, total - 1);
target = uniform_dist(engine); target = uniform_dist(engine);
} } while (currentGame && gameList.at(target) == currentGame);
while (currentGame && gameList.at(target) == currentGame);
return gameList.at(target); return gameList.at(target);
} }
@ -1020,23 +1008,25 @@ void SystemData::sortSystem(bool reloadGamelist, bool jumpToFirstRow)
bool favoritesSorting; bool favoritesSorting;
if (this->isCustomCollection() || if (this->isCustomCollection() ||
(this->isCollection() && this->getFullName() == "collections")) (this->isCollection() && this->getFullName() == "collections")) {
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
else }
else {
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
}
FileData* rootFolder = getRootFolder(); FileData* rootFolder = getRootFolder();
// Assign the sort type to all grouped custom collections. // Assign the sort type to all grouped custom collections.
if (mIsCollectionSystem && mFullName == "collections") { if (mIsCollectionSystem && mFullName == "collections") {
for (auto it = rootFolder->getChildren().begin(); for (auto it = rootFolder->getChildren().begin(); // Line break.
it != rootFolder->getChildren().end(); it++) { it != rootFolder->getChildren().end(); it++) {
setupSystemSortType((*it)->getSystem()->getRootFolder()); setupSystemSortType((*it)->getSystem()->getRootFolder());
} }
} }
setupSystemSortType(rootFolder); setupSystemSortType(rootFolder);
rootFolder->sort(rootFolder->getSortTypeFromString( rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()),
rootFolder->getSortTypeString()), favoritesSorting); favoritesSorting);
if (reloadGamelist) if (reloadGamelist)
ViewController::get()->reloadGameListView(this, false); ViewController::get()->reloadGameListView(this, false);
@ -1079,7 +1069,8 @@ void SystemData::loadTheme()
} }
} }
void SystemData::writeMetaData() { void SystemData::writeMetaData()
{
if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem) if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem)
return; return;
@ -1087,7 +1078,8 @@ void SystemData::writeMetaData() {
updateGamelist(this); updateGamelist(this);
} }
void SystemData::onMetaDataSavePoint() { void SystemData::onMetaDataSavePoint()
{
if (Settings::getInstance()->getString("SaveGamelistsMode") != "always") if (Settings::getInstance()->getString("SaveGamelistsMode") != "always")
return; return;
@ -1100,15 +1092,15 @@ void SystemData::setupSystemSortType(FileData* mRootFolder)
if (Settings::getInstance()->getString("DefaultSortOrder") != "") { if (Settings::getInstance()->getString("DefaultSortOrder") != "") {
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
if (FileSorts::SortTypes.at(i).description == if (FileSorts::SortTypes.at(i).description ==
Settings::getInstance()->getString("DefaultSortOrder")) { Settings::getInstance()->getString("DefaultSortOrder")) {
mRootFolder->setSortTypeString(Settings::getInstance()-> mRootFolder->setSortTypeString(
getString("DefaultSortOrder")); Settings::getInstance()->getString("DefaultSortOrder"));
break; break;
} }
} }
} }
// If no valid sort type was defined in the configuration file, set to default sorting. // If no valid sort type was defined in the configuration file, set to default sorting.
if (mRootFolder->getSortTypeString() == "") if (mRootFolder->getSortTypeString() == "")
mRootFolder->setSortTypeString(Settings::getInstance()-> mRootFolder->setSortTypeString(
getDefaultString("DefaultSortOrder")); Settings::getInstance()->getDefaultString("DefaultSortOrder"));
} }

View file

@ -35,15 +35,14 @@ class FindRules
{ {
public: public:
FindRules(); FindRules();
~FindRules();
void loadFindRules(); void loadFindRules();
private: private:
struct EmulatorRules { struct EmulatorRules {
#if defined(_WIN64) #if defined(_WIN64)
std::vector<std::string> winRegistryPaths; std::vector<std::string> winRegistryPaths;
#endif #endif
std::vector<std::string> systemPaths; std::vector<std::string> systemPaths;
std::vector<std::string> staticPaths; std::vector<std::string> staticPaths;
}; };
@ -61,38 +60,41 @@ private:
class SystemData class SystemData
{ {
public: public:
SystemData( SystemData(const std::string& name,
const std::string& name, const std::string& fullName,
const std::string& fullName, SystemEnvironmentData* envData,
SystemEnvironmentData* envData, const std::string& themeFolder,
const std::string& themeFolder, bool CollectionSystem = false,
bool CollectionSystem = false, bool CustomCollectionSystem = false);
bool CustomCollectionSystem = false);
~SystemData(); ~SystemData();
inline FileData* getRootFolder() const { return mRootFolder; }; FileData* getRootFolder() const { return mRootFolder; }
inline const std::string& getName() const { return mName; } const std::string& getName() const { return mName; }
inline const std::string& getFullName() const { return mFullName; } const std::string& getFullName() const { return mFullName; }
inline const std::string& getStartPath() const { return mEnvData->mStartPath; } const std::string& getStartPath() const { return mEnvData->mStartPath; }
inline const std::vector<std::string>& getExtensions() const const std::vector<std::string>& getExtensions() const { return mEnvData->mSearchExtensions; }
{ return mEnvData->mSearchExtensions; } const std::string& getThemeFolder() const { return mThemeFolder; }
inline const std::string& getThemeFolder() const { return mThemeFolder; } SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } const std::vector<PlatformIds::PlatformId>& getPlatformIds() const
inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const {
{ return mEnvData->mPlatformIds; } return mEnvData->mPlatformIds;
inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; }
return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) bool hasPlatformId(PlatformIds::PlatformId id)
!= mEnvData->mPlatformIds.cend(); } {
if (!mEnvData)
return false;
return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) !=
mEnvData->mPlatformIds.cend();
}
inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; } const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
std::string getGamelistPath(bool forWrite) const; std::string getGamelistPath(bool forWrite) const;
bool hasGamelist() const;
std::string getThemePath() const; std::string getThemePath() const;
std::pair<unsigned int, unsigned int> getDisplayedGameCount() const; std::pair<unsigned int, unsigned int> getDisplayedGameCount() const;
bool getScrapeFlag() { return mScrapeFlag; }; bool getScrapeFlag() { return mScrapeFlag; }
void setScrapeFlag(bool scrapeflag) { mScrapeFlag = scrapeflag; } void setScrapeFlag(bool scrapeflag) { mScrapeFlag = scrapeflag; }
static void deleteSystems(); static void deleteSystems();
@ -106,16 +108,22 @@ public:
static std::vector<SystemData*> sSystemVector; static std::vector<SystemData*> sSystemVector;
static std::unique_ptr<FindRules> sFindRules; static std::unique_ptr<FindRules> sFindRules;
inline std::vector<SystemData*>::const_iterator getIterator() const std::vector<SystemData*>::const_iterator getIterator() const
{ return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); }; {
inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this);
{ return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); }; }
inline bool isCollection() { return mIsCollectionSystem; }; std::vector<SystemData*>::const_reverse_iterator getRevIterator() const
inline bool isCustomCollection() { return mIsCustomCollectionSystem; }; {
inline bool isGroupedCustomCollection() { return mIsGroupedCustomCollectionSystem; }; return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this);
}
bool isCollection() { return mIsCollectionSystem; }
bool isCustomCollection() { return mIsCustomCollectionSystem; }
bool isGroupedCustomCollection() { return mIsGroupedCustomCollectionSystem; }
void setIsGroupedCustomCollection(bool isGroupedCustom) void setIsGroupedCustomCollection(bool isGroupedCustom)
{ mIsGroupedCustomCollectionSystem = isGroupedCustom; }; {
inline bool isGameSystem() { return mIsGameSystem; }; mIsGroupedCustomCollectionSystem = isGroupedCustom;
};
bool isGameSystem() { return mIsGameSystem; }
bool isVisible(); bool isVisible();
@ -123,14 +131,14 @@ public:
SystemData* getPrev() const; SystemData* getPrev() const;
static SystemData* getRandomSystem(const SystemData* currentSystem); static SystemData* getRandomSystem(const SystemData* currentSystem);
FileData* getRandomGame(const FileData* currentGame = nullptr); FileData* getRandomGame(const FileData* currentGame = nullptr);
FileData* getPlaceholder() { return mPlaceholder; }; FileData* getPlaceholder() { return mPlaceholder; }
void sortSystem(bool reloadGamelist = true, bool jumpToFirstRow = false); void sortSystem(bool reloadGamelist = true, bool jumpToFirstRow = false);
// Load or re-load theme. // Load or re-load theme.
void loadTheme(); void loadTheme();
FileFilterIndex* getIndex() { return mFilterIndex; }; FileFilterIndex* getIndex() { return mFilterIndex; }
void onMetaDataSavePoint(); void onMetaDataSavePoint();
void writeMetaData(); void writeMetaData();
@ -141,7 +149,7 @@ private:
bool mIsCustomCollectionSystem; bool mIsCustomCollectionSystem;
bool mIsGroupedCustomCollectionSystem; bool mIsGroupedCustomCollectionSystem;
bool mIsGameSystem; bool mIsGameSystem;
bool mScrapeFlag; // Only used by scraper GUI to remember which systems to scrape. bool mScrapeFlag; // Only used by scraper GUI to remember which systems to scrape.
std::string mName; std::string mName;
std::string mFullName; std::string mFullName;
SystemEnvironmentData* mEnvData; SystemEnvironmentData* mEnvData;

View file

@ -16,15 +16,15 @@
#if defined(BUILD_VLC_PLAYER) #if defined(BUILD_VLC_PLAYER)
#include "components/VideoVlcComponent.h" #include "components/VideoVlcComponent.h"
#endif #endif
#include "resources/Font.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "views/gamelist/IGameListView.h"
#include "views/ViewController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "FileData.h" #include "FileData.h"
#include "Log.h" #include "Log.h"
#include "SystemData.h" #include "SystemData.h"
#include "resources/Font.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "views/ViewController.h"
#include "views/gamelist/IGameListView.h"
#include <random> #include <random>
#include <time.h> #include <time.h>
@ -36,24 +36,23 @@
#define FADE_TIME 300 #define FADE_TIME 300
SystemScreensaver::SystemScreensaver( SystemScreensaver::SystemScreensaver(Window* window)
Window* window) : mWindow(window)
: mWindow(window), , mState(STATE_INACTIVE)
mState(STATE_INACTIVE), , mImageScreensaver(nullptr)
mImageScreensaver(nullptr), , mVideoScreensaver(nullptr)
mVideoScreensaver(nullptr), , mCurrentGame(nullptr)
mCurrentGame(nullptr), , mPreviousGame(nullptr)
mPreviousGame(nullptr), , mTimer(0)
mTimer(0), , mMediaSwapTime(0)
mMediaSwapTime(0), , mTriggerNextGame(false)
mTriggerNextGame(false), , mHasMediaFiles(false)
mHasMediaFiles(false), , mFallbackScreensaver(false)
mFallbackScreensaver(false), , mOpacity(0.0f)
mOpacity(0.0f), , mDimValue(1.0)
mDimValue(1.0), , mRectangleFadeIn(50)
mRectangleFadeIn(50), , mTextFadeIn(0)
mTextFadeIn(0), , mSaturationAmount(1.0)
mSaturationAmount(1.0)
{ {
mWindow->setScreensaver(this); mWindow->setScreensaver(this);
} }
@ -65,21 +64,6 @@ SystemScreensaver::~SystemScreensaver()
delete mImageScreensaver; delete mImageScreensaver;
} }
bool SystemScreensaver::allowSleep()
{
return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr));
}
bool SystemScreensaver::isScreensaverActive()
{
return (mState != STATE_INACTIVE);
}
bool SystemScreensaver::isFallbackScreensaver()
{
return mFallbackScreensaver;
}
void SystemScreensaver::startScreensaver(bool generateMediaList) void SystemScreensaver::startScreensaver(bool generateMediaList)
{ {
std::string path = ""; std::string path = "";
@ -146,14 +130,14 @@ void SystemScreensaver::startScreensaver(bool generateMediaList)
mImageScreensaver->setImage(path); mImageScreensaver->setImage(path);
mImageScreensaver->setOrigin(0.5f, 0.5f); mImageScreensaver->setOrigin(0.5f, 0.5f);
mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
Renderer::getScreenHeight() / 2.0f); Renderer::getScreenHeight() / 2.0f);
if (Settings::getInstance()->getBool("ScreensaverStretchImages")) if (Settings::getInstance()->getBool("ScreensaverStretchImages"))
mImageScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()), mImageScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
else else
mImageScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()), mImageScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
} }
mTimer = 0; mTimer = 0;
return; return;
@ -179,7 +163,7 @@ void SystemScreensaver::startScreensaver(bool generateMediaList)
if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo"))
generateOverlayInfo(); generateOverlayInfo();
#if defined(_RPI_) #if defined(_RPI_)
// Create the correct type of video component. // Create the correct type of video component.
if (Settings::getInstance()->getBool("ScreensaverOmxPlayer")) if (Settings::getInstance()->getBool("ScreensaverOmxPlayer"))
mVideoScreensaver = new VideoOmxComponent(mWindow); mVideoScreensaver = new VideoOmxComponent(mWindow);
@ -187,26 +171,26 @@ void SystemScreensaver::startScreensaver(bool generateMediaList)
mVideoScreensaver = new VideoVlcComponent(mWindow); mVideoScreensaver = new VideoVlcComponent(mWindow);
else else
mVideoScreensaver = new VideoFFmpegComponent(mWindow); mVideoScreensaver = new VideoFFmpegComponent(mWindow);
#elif defined(BUILD_VLC_PLAYER) #elif defined(BUILD_VLC_PLAYER)
if (Settings::getInstance()->getString("VideoPlayer") == "vlc") if (Settings::getInstance()->getString("VideoPlayer") == "vlc")
mVideoScreensaver = new VideoVlcComponent(mWindow); mVideoScreensaver = new VideoVlcComponent(mWindow);
else else
mVideoScreensaver = new VideoFFmpegComponent(mWindow); mVideoScreensaver = new VideoFFmpegComponent(mWindow);
#else #else
mVideoScreensaver = new VideoFFmpegComponent(mWindow); mVideoScreensaver = new VideoFFmpegComponent(mWindow);
#endif #endif
mVideoScreensaver->topWindow(true); mVideoScreensaver->topWindow(true);
mVideoScreensaver->setOrigin(0.5f, 0.5f); mVideoScreensaver->setOrigin(0.5f, 0.5f);
mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
Renderer::getScreenHeight() / 2.0f); Renderer::getScreenHeight() / 2.0f);
if (Settings::getInstance()->getBool("ScreensaverStretchVideos")) if (Settings::getInstance()->getBool("ScreensaverStretchVideos"))
mVideoScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()), mVideoScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
else else
mVideoScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()), mVideoScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
mVideoScreensaver->setVideo(path); mVideoScreensaver->setVideo(path);
mVideoScreensaver->setScreensaverMode(true); mVideoScreensaver->setScreensaverMode(true);
@ -229,16 +213,17 @@ void SystemScreensaver::stopScreensaver()
mState = STATE_INACTIVE; mState = STATE_INACTIVE;
mDimValue = 1.0; mDimValue = 1.0f;
mRectangleFadeIn = 50; mRectangleFadeIn = 50;
mTextFadeIn = 0; mTextFadeIn = 0;
mSaturationAmount = 1.0; mSaturationAmount = 1.0f;
if (mGameOverlay) if (mGameOverlay)
mGameOverlay.reset(); mGameOverlay.reset();
} }
void SystemScreensaver::nextGame() { void SystemScreensaver::nextGame()
{
stopScreensaver(); stopScreensaver();
startScreensaver(false); startScreensaver(false);
} }
@ -249,8 +234,8 @@ void SystemScreensaver::launchGame()
// Launching game // Launching game
ViewController::get()->triggerGameLaunch(mCurrentGame); ViewController::get()->triggerGameLaunch(mCurrentGame);
ViewController::get()->goToGameList(mCurrentGame->getSystem()); ViewController::get()->goToGameList(mCurrentGame->getSystem());
IGameListView* view = ViewController::get()-> IGameListView* view =
getGameListView(mCurrentGame->getSystem()).get(); ViewController::get()->getGameListView(mCurrentGame->getSystem()).get();
view->setCursor(mCurrentGame); view->setCursor(mCurrentGame);
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
} }
@ -261,8 +246,8 @@ void SystemScreensaver::goToGame()
if (mCurrentGame != nullptr) { if (mCurrentGame != nullptr) {
// Go to the game in the gamelist view, but don't launch it. // Go to the game in the gamelist view, but don't launch it.
ViewController::get()->goToGameList(mCurrentGame->getSystem()); ViewController::get()->goToGameList(mCurrentGame->getSystem());
IGameListView* view = ViewController::get()-> IGameListView* view =
getGameListView(mCurrentGame->getSystem()).get(); ViewController::get()->getGameListView(mCurrentGame->getSystem()).get();
view->setCursor(mCurrentGame); view->setCursor(mCurrentGame);
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
} }
@ -276,7 +261,7 @@ void SystemScreensaver::renderScreensaver()
// Render a black background below the video. // Render a black background below the video.
Renderer::setMatrix(Transform4x4f::Identity()); Renderer::setMatrix(Transform4x4f::Identity());
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()), Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
// Only render the video if the state requires it. // Only render the video if the state requires it.
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) { if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
@ -288,7 +273,7 @@ void SystemScreensaver::renderScreensaver()
// Render a black background below the image. // Render a black background below the image.
Renderer::setMatrix(Transform4x4f::Identity()); Renderer::setMatrix(Transform4x4f::Identity());
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()), Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
// Only render the image if the state requires it. // Only render the image if the state requires it.
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) { if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
@ -305,20 +290,20 @@ void SystemScreensaver::renderScreensaver()
Renderer::setMatrix(Transform4x4f::Identity()); Renderer::setMatrix(Transform4x4f::Identity());
if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") { if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") {
if (mHasMediaFiles) { if (mHasMediaFiles) {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
if (Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) if (Settings::getInstance()->getBool("ScreensaverSlideshowScanlines"))
Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
#endif #endif
if (Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo") && if (Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo") &&
mGameOverlay) { mGameOverlay) {
if (mGameOverlayRectangleCoords.size() == 4) { if (mGameOverlayRectangleCoords.size() == 4) {
Renderer::drawRect(mGameOverlayRectangleCoords[0], Renderer::drawRect(
mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1],
mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn, mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3],
0x00000000 | mRectangleFadeIn ); 0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn);
} }
mRectangleFadeIn = Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn =
mRectangleFadeIn / 20, 0, 170); Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn); mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn);
if (mTextFadeIn > 50) if (mTextFadeIn > 50)
@ -333,7 +318,7 @@ void SystemScreensaver::renderScreensaver()
} }
else if (Settings::getInstance()->getString("ScreensaverType") == "video") { else if (Settings::getInstance()->getString("ScreensaverType") == "video") {
if (mHasMediaFiles) { if (mHasMediaFiles) {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters videoParameters; Renderer::shaderParameters videoParameters;
unsigned int shaders = 0; unsigned int shaders = 0;
if (Settings::getInstance()->getBool("ScreensaverVideoScanlines")) if (Settings::getInstance()->getBool("ScreensaverVideoScanlines"))
@ -341,6 +326,7 @@ void SystemScreensaver::renderScreensaver()
if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) { if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
shaders |= Renderer::SHADER_BLUR_HORIZONTAL; shaders |= Renderer::SHADER_BLUR_HORIZONTAL;
float heightModifier = Renderer::getScreenHeightModifier(); float heightModifier = Renderer::getScreenHeightModifier();
// clang-format off
if (heightModifier < 1) if (heightModifier < 1)
videoParameters.blurPasses = 2; // Below 1080 videoParameters.blurPasses = 2; // Below 1080
else if (heightModifier >= 4) else if (heightModifier >= 4)
@ -355,21 +341,22 @@ void SystemScreensaver::renderScreensaver()
videoParameters.blurPasses = 3; // 1440 videoParameters.blurPasses = 3; // 1440
else if (heightModifier >= 1) else if (heightModifier >= 1)
videoParameters.blurPasses = 2; // 1080 videoParameters.blurPasses = 2; // 1080
// clang-format on
} }
Renderer::shaderPostprocessing(shaders, videoParameters); Renderer::shaderPostprocessing(shaders, videoParameters);
#endif #endif
if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) { if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) {
if (mGameOverlayRectangleCoords.size() == 4) { if (mGameOverlayRectangleCoords.size() == 4) {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderPostprocessing(Renderer::SHADER_OPACITY); Renderer::shaderPostprocessing(Renderer::SHADER_OPACITY);
#endif #endif
Renderer::drawRect(mGameOverlayRectangleCoords[0], Renderer::drawRect(
mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1],
mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn, mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3],
0x00000000 | mRectangleFadeIn ); 0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn);
} }
mRectangleFadeIn = Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn =
mRectangleFadeIn / 20, 0, 170); Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn); mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn);
if (mTextFadeIn > 50) if (mTextFadeIn > 50)
@ -383,8 +370,8 @@ void SystemScreensaver::renderScreensaver()
} }
} }
if (mFallbackScreensaver || if (mFallbackScreensaver ||
Settings::getInstance()->getString("ScreensaverType") == "dim") { Settings::getInstance()->getString("ScreensaverType") == "dim") {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters dimParameters; Renderer::shaderParameters dimParameters;
dimParameters.fragmentDimValue = mDimValue; dimParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters); Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters);
@ -394,22 +381,22 @@ void SystemScreensaver::renderScreensaver()
Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters); Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters);
if (mSaturationAmount > 0.0) if (mSaturationAmount > 0.0)
mSaturationAmount = Math::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f); mSaturationAmount = Math::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f);
#else #else
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
Renderer::getScreenHeight(), 0x000000A0, 0x000000A0); 0x000000A0, 0x000000A0);
#endif #endif
} }
else if (Settings::getInstance()->getString("ScreensaverType") == "black") { else if (Settings::getInstance()->getString("ScreensaverType") == "black") {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters blackParameters; Renderer::shaderParameters blackParameters;
blackParameters.fragmentDimValue = mDimValue; blackParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters); Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters);
if (mDimValue > 0.0) if (mDimValue > 0.0)
mDimValue = Math::clamp(mDimValue - 0.045f, 0.0f, 1.0f); mDimValue = Math::clamp(mDimValue - 0.045f, 0.0f, 1.0f);
#else #else
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); 0x000000FF, 0x000000FF);
#endif #endif
} }
} }
} }
@ -458,8 +445,8 @@ void SystemScreensaver::update(int deltaTime)
void SystemScreensaver::generateImageList() void SystemScreensaver::generateImageList()
{ {
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
// We only want nodes from game systems that are not collections. // We only want nodes from game systems that are not collections.
if (!(*it)->isGameSystem() || (*it)->isCollection()) if (!(*it)->isGameSystem() || (*it)->isCollection())
continue; continue;
@ -475,8 +462,8 @@ void SystemScreensaver::generateImageList()
void SystemScreensaver::generateVideoList() void SystemScreensaver::generateVideoList()
{ {
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
// We only want nodes from game systems that are not collections. // We only want nodes from game systems that are not collections.
if (!(*it)->isGameSystem() || (*it)->isCollection()) if (!(*it)->isGameSystem() || (*it)->isCollection())
continue; continue;
@ -493,7 +480,7 @@ void SystemScreensaver::generateVideoList()
void SystemScreensaver::generateCustomImageList() void SystemScreensaver::generateCustomImageList()
{ {
std::string imageDir = Utils::FileSystem::expandHomePath( std::string imageDir = Utils::FileSystem::expandHomePath(
Settings::getInstance()->getString("ScreensaverSlideshowImageDir")); Settings::getInstance()->getString("ScreensaverSlideshowImageDir"));
// This makes it possible to set the custom image directory relative to the ES-DE binary // This makes it possible to set the custom image directory relative to the ES-DE binary
// directory or the ROM directory. // directory or the ROM directory.
@ -503,7 +490,7 @@ void SystemScreensaver::generateCustomImageList()
if (imageDir != "" && Utils::FileSystem::isDirectory(imageDir)) { if (imageDir != "" && Utils::FileSystem::isDirectory(imageDir)) {
std::string imageFilter = ".jpg, .JPG, .png, .PNG"; std::string imageFilter = ".jpg, .JPG, .png, .PNG";
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent( Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(
imageDir, Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")); imageDir, Settings::getInstance()->getBool("ScreensaverSlideshowRecurse"));
for (auto it = dirContent.begin(); it != dirContent.end(); it++) { for (auto it = dirContent.begin(); it != dirContent.end(); it++) {
if (Utils::FileSystem::isRegularFile(*it)) { if (Utils::FileSystem::isRegularFile(*it)) {
@ -513,8 +500,7 @@ void SystemScreensaver::generateCustomImageList()
} }
} }
else { else {
LOG(LogWarning) << "Custom screensaver image directory '" << LOG(LogWarning) << "Custom screensaver image directory '" << imageDir << "' does not exist";
imageDir << "' does not exist.";
} }
} }
@ -540,12 +526,11 @@ void SystemScreensaver::pickRandomImage(std::string& path)
// Get a random number in range. // Get a random number in range.
std::random_device randDev; std::random_device randDev;
// Mersenne Twister pseudorandom number generator. // Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()}; std::mt19937 engine { randDev() };
std::uniform_int_distribution<int> std::uniform_int_distribution<int> uniform_dist(0,
uniform_dist(0, static_cast<int>(mImageFiles.size()) - 1); static_cast<int>(mImageFiles.size()) - 1);
index = uniform_dist(engine); index = uniform_dist(engine);
} } while (mPreviousGame && mImageFiles.at(index) == mPreviousGame);
while (mPreviousGame && mImageFiles.at(index) == mPreviousGame);
path = mImageFiles.at(index)->getImagePath(); path = mImageFiles.at(index)->getImagePath();
mGameName = mImageFiles.at(index)->getName(); mGameName = mImageFiles.at(index)->getName();
@ -575,12 +560,11 @@ void SystemScreensaver::pickRandomVideo(std::string& path)
// Get a random number in range. // Get a random number in range.
std::random_device randDev; std::random_device randDev;
// Mersenne Twister pseudorandom number generator. // Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()}; std::mt19937 engine { randDev() };
std::uniform_int_distribution<int> std::uniform_int_distribution<int> uniform_dist(0,
uniform_dist(0, static_cast<int>(mVideoFiles.size()) - 1); static_cast<int>(mVideoFiles.size()) - 1);
index = uniform_dist(engine); index = uniform_dist(engine);
} } while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame);
while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame);
path = mVideoFiles.at(index)->getVideoPath(); path = mVideoFiles.at(index)->getVideoPath();
mGameName = mVideoFiles.at(index)->getName(); mGameName = mVideoFiles.at(index)->getName();
@ -604,12 +588,11 @@ void SystemScreensaver::pickRandomCustomImage(std::string& path)
// Get a random number in range. // Get a random number in range.
std::random_device randDev; std::random_device randDev;
// Mersenne Twister pseudorandom number generator. // Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()}; std::mt19937 engine { randDev() };
std::uniform_int_distribution<int> std::uniform_int_distribution<int> uniform_dist(
uniform_dist(0, static_cast<int>(mImageCustomFiles.size()) - 1); 0, static_cast<int>(mImageCustomFiles.size()) - 1);
index = uniform_dist(engine); index = uniform_dist(engine);
} } while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage);
while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage);
path = mImageCustomFiles.at(index); path = mImageCustomFiles.at(index);
mPreviousCustomImage = path; mPreviousCustomImage = path;
@ -633,8 +616,8 @@ void SystemScreensaver::generateOverlayInfo()
const std::string systemName = Utils::String::toUpper(mSystemName); const std::string systemName = Utils::String::toUpper(mSystemName);
const std::string overlayText = gameName + "\n" + systemName; const std::string overlayText = gameName + "\n" + systemName;
mGameOverlay = std::unique_ptr<TextCache>(mGameOverlayFont.at(0)-> mGameOverlay = std::unique_ptr<TextCache>(
buildTextCache(overlayText, posX, posY, 0xFFFFFFFF)); mGameOverlayFont.at(0)->buildTextCache(overlayText, posX, posY, 0xFFFFFFFF));
float textSizeX; float textSizeX;
float textSizeY = mGameOverlayFont[0].get()->sizeText(overlayText).y(); float textSizeY = mGameOverlayFont[0].get()->sizeText(overlayText).y();
@ -645,7 +628,7 @@ void SystemScreensaver::generateOverlayInfo()
// injected in the size calculation. Regardless, this workaround is working // injected in the size calculation. Regardless, this workaround is working
// fine for the time being. // fine for the time being.
if (mGameOverlayFont[0].get()->sizeText(gameName).x() > if (mGameOverlayFont[0].get()->sizeText(gameName).x() >
mGameOverlayFont[0].get()->sizeText(systemName).x()) mGameOverlayFont[0].get()->sizeText(systemName).x())
textSizeX = mGameOverlayFont[0].get()->sizeText(gameName).x(); textSizeX = mGameOverlayFont[0].get()->sizeText(gameName).x();
else else
textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x(); textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x();

View file

@ -22,9 +22,12 @@ public:
SystemScreensaver(Window* window); SystemScreensaver(Window* window);
virtual ~SystemScreensaver(); virtual ~SystemScreensaver();
virtual bool allowSleep(); virtual bool allowSleep()
virtual bool isScreensaverActive(); {
virtual bool isFallbackScreensaver(); return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr));
}
virtual bool isScreensaverActive() { return (mState != STATE_INACTIVE); }
virtual bool isFallbackScreensaver() { return mFallbackScreensaver; }
virtual void startScreensaver(bool generateMediaList); virtual void startScreensaver(bool generateMediaList);
virtual void stopScreensaver(); virtual void stopScreensaver();
@ -35,8 +38,8 @@ public:
virtual void renderScreensaver(); virtual void renderScreensaver();
virtual void update(int deltaTime); virtual void update(int deltaTime);
virtual FileData* getCurrentGame() { return mCurrentGame; }; virtual FileData* getCurrentGame() { return mCurrentGame; }
virtual void triggerNextGame() { mTriggerNextGame = true; }; virtual void triggerNextGame() { mTriggerNextGame = true; }
private: private:
void generateImageList(); void generateImageList();

View file

@ -8,8 +8,8 @@
#include "VolumeControl.h" #include "VolumeControl.h"
#include "math/Misc.h"
#include "Log.h" #include "Log.h"
#include "math/Misc.h"
#if defined(_RPI_) #if defined(_RPI_)
#include "Settings.h" #include "Settings.h"
@ -38,15 +38,15 @@ std::string VolumeControl::mixerCard = "default";
VolumeControl* VolumeControl::sInstance = nullptr; VolumeControl* VolumeControl::sInstance = nullptr;
VolumeControl::VolumeControl() VolumeControl::VolumeControl()
#if defined(__linux__) #if defined(__linux__)
: mixerIndex(0), : mixerIndex(0)
mixerHandle(nullptr), , mixerHandle(nullptr)
mixerElem(nullptr), , mixerElem(nullptr)
mixerSelemId(nullptr) , mixerSelemId(nullptr)
#elif defined(_WIN64) #elif defined(_WIN64)
: mixerHandle(nullptr), : mixerHandle(nullptr)
endpointVolume(nullptr) , endpointVolume(nullptr)
#endif #endif
{ {
init(); init();
} }
@ -54,9 +54,9 @@ VolumeControl::VolumeControl()
VolumeControl::~VolumeControl() VolumeControl::~VolumeControl()
{ {
deinit(); deinit();
#if defined(__linux__) #if defined(__linux__)
snd_config_update_free_global(); snd_config_update_free_global();
#endif #endif
} }
VolumeControl* VolumeControl::getInstance() VolumeControl* VolumeControl::getInstance()
@ -79,14 +79,15 @@ void VolumeControl::deleteInstance()
void VolumeControl::init() void VolumeControl::init()
{ {
// Initialize audio mixer interface. // Initialize audio mixer interface.
#if defined(__linux__)
#if defined(__linux__)
// Try to open mixer device. // Try to open mixer device.
if (mixerHandle == nullptr) { if (mixerHandle == nullptr) {
#if defined(_RPI_)
// Allow user to override the AudioCard and AudioDevice in es_settings.xml. // Allow user to override the AudioCard and AudioDevice in es_settings.xml.
#if defined(_RPI_)
mixerCard = Settings::getInstance()->getString("AudioCard"); mixerCard = Settings::getInstance()->getString("AudioCard");
mixerName = Settings::getInstance()->getString("AudioDevice"); mixerName = Settings::getInstance()->getString("AudioDevice");
#endif #endif
snd_mixer_selem_id_alloca(&mixerSelemId); snd_mixer_selem_id_alloca(&mixerSelemId);
// Sets simple-mixer index and name. // Sets simple-mixer index and name.
@ -106,8 +107,8 @@ void VolumeControl::init()
LOG(LogDebug) << "VolumeControl::init(): Mixer initialized"; LOG(LogDebug) << "VolumeControl::init(): Mixer initialized";
} }
else { else {
LOG(LogError) << LOG(LogError)
"VolumeControl::init(): Failed to find mixer elements!"; << "VolumeControl::init(): Failed to find mixer elements!";
snd_mixer_close(mixerHandle); snd_mixer_close(mixerHandle);
mixerHandle = nullptr; mixerHandle = nullptr;
} }
@ -119,8 +120,8 @@ void VolumeControl::init()
} }
} }
else { else {
LOG(LogError) << LOG(LogError)
"VolumeControl::init(): Failed to register simple element class!"; << "VolumeControl::init(): Failed to register simple element class!";
snd_mixer_close(mixerHandle); snd_mixer_close(mixerHandle);
mixerHandle = nullptr; mixerHandle = nullptr;
} }
@ -135,31 +136,30 @@ void VolumeControl::init()
LOG(LogError) << "VolumeControl::init(): Failed to open ALSA mixer!"; LOG(LogError) << "VolumeControl::init(): Failed to open ALSA mixer!";
} }
} }
#elif defined(_WIN64) #elif defined(_WIN64)
// Windows Vista or above. // Windows Vista or above.
if (endpointVolume == nullptr) { if (endpointVolume == nullptr) {
CoInitialize(nullptr); CoInitialize(nullptr);
IMMDeviceEnumerator* deviceEnumerator = nullptr; IMMDeviceEnumerator* deviceEnumerator = nullptr;
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator), reinterpret_cast<LPVOID *>(&deviceEnumerator)); __uuidof(IMMDeviceEnumerator),
reinterpret_cast<LPVOID*>(&deviceEnumerator));
if (deviceEnumerator != nullptr) { if (deviceEnumerator != nullptr) {
// Get default endpoint. // Get default endpoint.
IMMDevice * defaultDevice = nullptr; IMMDevice* defaultDevice = nullptr;
deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
if (defaultDevice != nullptr) { if (defaultDevice != nullptr) {
// Retrieve endpoint volume. // Retrieve endpoint volume.
defaultDevice->Activate(__uuidof(IAudioEndpointVolume), defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER,
CLSCTX_INPROC_SERVER, nullptr, nullptr, reinterpret_cast<LPVOID*>(&endpointVolume));
reinterpret_cast<LPVOID *>(&endpointVolume));
if (endpointVolume == nullptr) if (endpointVolume == nullptr)
LOG(LogError) << "VolumeControl::init(): " LOG(LogError) << "VolumeControl::init(): "
"Failed to get default audio endpoint volume!"; "Failed to get default audio endpoint volume!";
// Release default device. we don't need it anymore. // Release default device. we don't need it anymore.
defaultDevice->Release(); defaultDevice->Release();
} }
else { else {
LOG(LogError) << LOG(LogError) << "VolumeControl::init(): Failed to get default audio endpoint!";
"VolumeControl::init(): Failed to get default audio endpoint!";
} }
// Release device enumerator. we don't need it anymore. // Release device enumerator. we don't need it anymore.
deviceEnumerator->Release(); deviceEnumerator->Release();
@ -169,13 +169,14 @@ void VolumeControl::init()
CoUninitialize(); CoUninitialize();
} }
} }
#endif #endif
} }
void VolumeControl::deinit() void VolumeControl::deinit()
{ {
// Deinitialize audio mixer interface. // Deinitialize audio mixer interface.
#if defined(__linux__)
#if defined(__linux__)
if (mixerHandle != nullptr) { if (mixerHandle != nullptr) {
snd_mixer_detach(mixerHandle, mixerCard.c_str()); snd_mixer_detach(mixerHandle, mixerCard.c_str());
snd_mixer_free(mixerHandle); snd_mixer_free(mixerHandle);
@ -183,28 +184,28 @@ void VolumeControl::deinit()
mixerHandle = nullptr; mixerHandle = nullptr;
mixerElem = nullptr; mixerElem = nullptr;
} }
#elif defined(_WIN64) #elif defined(_WIN64)
if (endpointVolume != nullptr) { if (endpointVolume != nullptr) {
endpointVolume->Release(); endpointVolume->Release();
endpointVolume = nullptr; endpointVolume = nullptr;
CoUninitialize(); CoUninitialize();
} }
#endif #endif
} }
int VolumeControl::getVolume() const int VolumeControl::getVolume() const
{ {
int volume = 0; int volume = 0;
#if defined(__linux__) #if defined(__linux__)
if (mixerElem != nullptr) { if (mixerElem != nullptr) {
// Get volume range. // Get volume range.
long minVolume; long minVolume;
long maxVolume; long maxVolume;
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) { if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
long rawVolume; long rawVolume;
if (snd_mixer_selem_get_playback_volume(mixerElem, if (snd_mixer_selem_get_playback_volume(mixerElem, SND_MIXER_SCHN_MONO, &rawVolume) ==
SND_MIXER_SCHN_MONO, &rawVolume) == 0) { 0) {
// Bring into range 0-100. // Bring into range 0-100.
rawVolume -= minVolume; rawVolume -= minVolume;
if (rawVolume > 0) if (rawVolume > 0)
@ -218,7 +219,7 @@ int VolumeControl::getVolume() const
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range"; LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range";
} }
} }
#elif defined(_WIN64) #elif defined(_WIN64)
if (endpointVolume != nullptr) { if (endpointVolume != nullptr) {
// Windows Vista or above, uses EndpointVolume API. // Windows Vista or above, uses EndpointVolume API.
float floatVolume = 0.0f; // 0-1 float floatVolume = 0.0f; // 0-1
@ -230,7 +231,7 @@ int VolumeControl::getVolume() const
LOG(LogError) << "VolumeControl::getVolume(): Failed to get master volume!"; LOG(LogError) << "VolumeControl::getVolume(): Failed to get master volume!";
} }
} }
#endif #endif
volume = Math::clamp(volume, 0, 100); volume = Math::clamp(volume, 0, 100);
return volume; return volume;
@ -240,7 +241,7 @@ void VolumeControl::setVolume(int volume)
{ {
volume = Math::clamp(volume, 0, 100); volume = Math::clamp(volume, 0, 100);
#if defined(__linux__) #if defined(__linux__)
if (mixerElem != nullptr) { if (mixerElem != nullptr) {
// Get volume range. // Get volume range.
long minVolume; long minVolume;
@ -248,10 +249,10 @@ void VolumeControl::setVolume(int volume)
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) { if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
// Bring into minVolume-maxVolume range and set. // Bring into minVolume-maxVolume range and set.
long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume; long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume;
if (snd_mixer_selem_set_playback_volume(mixerElem, if (snd_mixer_selem_set_playback_volume(mixerElem, SND_MIXER_SCHN_FRONT_LEFT,
SND_MIXER_SCHN_FRONT_LEFT, rawVolume) < 0 || rawVolume) < 0 ||
snd_mixer_selem_set_playback_volume(mixerElem, snd_mixer_selem_set_playback_volume(mixerElem, SND_MIXER_SCHN_FRONT_RIGHT,
SND_MIXER_SCHN_FRONT_RIGHT, rawVolume) < 0) { rawVolume) < 0) {
LOG(LogError) << "VolumeControl::getVolume(): Failed to set mixer volume"; LOG(LogError) << "VolumeControl::getVolume(): Failed to set mixer volume";
} }
} }
@ -259,7 +260,7 @@ void VolumeControl::setVolume(int volume)
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range"; LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range";
} }
} }
#elif defined(_WIN64) #elif defined(_WIN64)
if (endpointVolume != nullptr) { if (endpointVolume != nullptr) {
// Windows Vista or above, uses EndpointVolume API. // Windows Vista or above, uses EndpointVolume API.
float floatVolume = 0.0f; // 0-1 float floatVolume = 0.0f; // 0-1
@ -268,5 +269,5 @@ void VolumeControl::setVolume(int volume)
if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK) if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK)
LOG(LogError) << "VolumeControl::setVolume(): Failed to set master volume"; LOG(LogError) << "VolumeControl::setVolume(): Failed to set master volume";
} }
#endif #endif
} }

View file

@ -38,18 +38,18 @@ public:
static VolumeControl* sInstance; static VolumeControl* sInstance;
#if defined(__linux__) #if defined(__linux__)
static std::string mixerName; static std::string mixerName;
static std::string mixerCard; static std::string mixerCard;
int mixerIndex; int mixerIndex;
snd_mixer_t* mixerHandle; snd_mixer_t* mixerHandle;
snd_mixer_elem_t* mixerElem; snd_mixer_elem_t* mixerElem;
snd_mixer_selem_id_t* mixerSelemId; snd_mixer_selem_id_t* mixerSelemId;
#elif defined(_WIN64) #elif defined(_WIN64)
HMIXER mixerHandle; HMIXER mixerHandle;
MIXERCONTROL mixerControl; MIXERCONTROL mixerControl;
IAudioEndpointVolume * endpointVolume; IAudioEndpointVolume* endpointVolume;
#endif #endif
}; };
#endif // ES_APP_VOLUME_CONTROL_H #endif // ES_APP_VOLUME_CONTROL_H

View file

@ -14,12 +14,12 @@
class MoveCameraAnimation : public Animation class MoveCameraAnimation : public Animation
{ {
public: public:
MoveCameraAnimation( MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target)
Transform4x4f& camera, : mCameraStart(camera)
const Vector3f& target) , mTarget(target)
: mCameraStart(camera), , cameraOut(camera)
mTarget(target), {
cameraOut(camera) {} }
int getDuration() const override { return 400; } int getDuration() const override { return 400; }
@ -28,7 +28,7 @@ public:
// Cubic ease out. // Cubic ease out.
t -= 1; t -= 1;
cameraOut.translation() = cameraOut.translation() =
-Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1); -Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1);
} }
private: private:

View file

@ -9,6 +9,7 @@
#include "guis/GuiCollectionSystemsOptions.h" #include "guis/GuiCollectionSystemsOptions.h"
#include "CollectionSystemsManager.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
@ -16,21 +17,23 @@
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "CollectionSystemsManager.h"
GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::string title)
Window* window, : GuiSettings(window, title)
std::string title) , mAddedCustomCollection(false)
: GuiSettings(window, title), , mDeletedCustomCollection(false)
mAddedCustomCollection(false),
mDeletedCustomCollection(false)
{ {
// Finish editing custom collection. // Finish editing custom collection.
if (CollectionSystemsManager::get()->isEditing()) { if (CollectionSystemsManager::get()->isEditing()) {
ComponentListRow row; ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" + row.addElement(std::make_shared<TextComponent>(
Utils::String::toUpper(CollectionSystemsManager::get()->getEditingCollection()) + mWindow,
"' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); "FINISH EDITING '" +
Utils::String::toUpper(
CollectionSystemsManager::get()->getEditingCollection()) +
"' COLLECTION",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.makeAcceptInputHandler([this] { row.makeAcceptInputHandler([this] {
CollectionSystemsManager::get()->exitEditMode(); CollectionSystemsManager::get()->exitEditMode();
mWindow->invalidateCachedBackground(); mWindow->invalidateCachedBackground();
@ -40,19 +43,20 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
} }
// Automatic collections. // Automatic collections.
collection_systems_auto = std::make_shared<OptionListComponent<std::string>> collection_systems_auto = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
std::map<std::string, CollectionSystemData, stringComparator> autoSystems = std::map<std::string, CollectionSystemData, stringComparator> autoSystems =
CollectionSystemsManager::get()->getAutoCollectionSystems(); CollectionSystemsManager::get()->getAutoCollectionSystems();
// Add automatic systems. // Add automatic systems.
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator it =
it = autoSystems.cbegin(); it != autoSystems.cend() ; it++) autoSystems.cbegin();
it != autoSystems.cend(); it++)
collection_systems_auto->add(it->second.decl.longName, it->second.decl.name, collection_systems_auto->add(it->second.decl.longName, it->second.decl.name,
it->second.isEnabled); it->second.isEnabled);
addWithLabel("AUTOMATIC GAME COLLECTIONS", collection_systems_auto); addWithLabel("AUTOMATIC GAME COLLECTIONS", collection_systems_auto);
addSaveFunc([this, autoSystems] { addSaveFunc([this, autoSystems] {
std::string autoSystemsSelected = Utils::String::vectorToDelimitedString( std::string autoSystemsSelected = Utils::String::vectorToDelimitedString(
collection_systems_auto->getSelectedObjects(), ",", true); collection_systems_auto->getSelectedObjects(), ",", true);
std::string autoSystemsConfig = Settings::getInstance()->getString("CollectionSystemsAuto"); std::string autoSystemsConfig = Settings::getInstance()->getString("CollectionSystemsAuto");
if (autoSystemsSelected != autoSystemsConfig) { if (autoSystemsSelected != autoSystemsConfig) {
if (CollectionSystemsManager::get()->isEditing()) if (CollectionSystemsManager::get()->isEditing())
@ -67,19 +71,19 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
} }
else if (autoSystemsSelected != "") { else if (autoSystemsSelected != "") {
std::vector<std::string> selectedVector = std::vector<std::string> selectedVector =
Utils::String::delimitedStringToVector(autoSystemsSelected, ","); Utils::String::delimitedStringToVector(autoSystemsSelected, ",");
std::vector<std::string> configuredVector = std::vector<std::string> configuredVector =
Utils::String::delimitedStringToVector(autoSystemsConfig, ","); Utils::String::delimitedStringToVector(autoSystemsConfig, ",");
for (std::string system : selectedVector) { for (std::string system : selectedVector) {
if (std::find(configuredVector.begin(), configuredVector.end(), system) == if (std::find(configuredVector.begin(), configuredVector.end(), system) ==
configuredVector.end()) configuredVector.end())
addedAutoSystems.push_back(system); addedAutoSystems.push_back(system);
} }
} }
if (!addedAutoSystems.empty()) { if (!addedAutoSystems.empty()) {
for (std::string system : addedAutoSystems) for (std::string system : addedAutoSystems)
CollectionSystemsManager::get()-> CollectionSystemsManager::get()->repopulateCollection(
repopulateCollection(autoSystems.find(system)->second.system); autoSystems.find(system)->second.system);
} }
setNeedsSaving(); setNeedsSaving();
setNeedsReloading(); setNeedsReloading();
@ -88,50 +92,52 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
}); });
// Custom collections. // Custom collections.
collection_systems_custom = std::make_shared<OptionListComponent<std::string>> collection_systems_custom = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
std::map<std::string, CollectionSystemData, stringComparator> customSystems = std::map<std::string, CollectionSystemData, stringComparator> customSystems =
CollectionSystemsManager::get()->getCustomCollectionSystems(); CollectionSystemsManager::get()->getCustomCollectionSystems();
// Add custom systems. // Add custom systems.
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator it =
it = customSystems.cbegin(); it != customSystems.cend() ; it++) customSystems.cbegin();
it != customSystems.cend(); it++)
collection_systems_custom->add(it->second.decl.longName, it->second.decl.name, collection_systems_custom->add(it->second.decl.longName, it->second.decl.name,
it->second.isEnabled); it->second.isEnabled);
addWithLabel("CUSTOM GAME COLLECTIONS", collection_systems_custom); addWithLabel("CUSTOM GAME COLLECTIONS", collection_systems_custom);
addSaveFunc([this, customSystems] { addSaveFunc([this, customSystems] {
if (!mDeletedCustomCollection) { if (!mDeletedCustomCollection) {
std::string customSystemsSelected = Utils::String::vectorToDelimitedString( std::string customSystemsSelected = Utils::String::vectorToDelimitedString(
collection_systems_custom->getSelectedObjects(), ",", true); collection_systems_custom->getSelectedObjects(), ",", true);
std::string customSystemsConfig = Settings::getInstance()-> std::string customSystemsConfig =
getString("CollectionSystemsCustom"); Settings::getInstance()->getString("CollectionSystemsCustom");
if (customSystemsSelected != customSystemsConfig) { if (customSystemsSelected != customSystemsConfig) {
if (CollectionSystemsManager::get()->isEditing()) if (CollectionSystemsManager::get()->isEditing())
CollectionSystemsManager::get()->exitEditMode(); CollectionSystemsManager::get()->exitEditMode();
Settings::getInstance()->setString("CollectionSystemsCustom", Settings::getInstance()->setString("CollectionSystemsCustom",
customSystemsSelected); customSystemsSelected);
// Check if any systems have been enabled, and if so repopulate them, which // Check if any systems have been enabled, and if so repopulate them, which
// results in a complete initialization of their content. This is necessary as // results in a complete initialization of their content. This is necessary as
// collections aren't updated while they are disabled. // collections aren't updated while they are disabled.
std::vector<std::string> addedCustomSystems; std::vector<std::string> addedCustomSystems;
if (customSystemsConfig == "") { if (customSystemsConfig == "") {
addedCustomSystems = addedCustomSystems =
Utils::String::delimitedStringToVector(customSystemsSelected, ","); Utils::String::delimitedStringToVector(customSystemsSelected, ",");
} }
else if (customSystemsSelected != "") { else if (customSystemsSelected != "") {
std::vector<std::string> selectedVector = std::vector<std::string> selectedVector =
Utils::String::delimitedStringToVector(customSystemsSelected, ","); Utils::String::delimitedStringToVector(customSystemsSelected, ",");
std::vector<std::string> configuredVector = std::vector<std::string> configuredVector =
Utils::String::delimitedStringToVector(customSystemsConfig, ","); Utils::String::delimitedStringToVector(customSystemsConfig, ",");
for (std::string system : selectedVector) { for (std::string system : selectedVector) {
if (std::find(configuredVector.begin(), configuredVector.end(), system) == if (std::find(configuredVector.begin(), configuredVector.end(), system) ==
configuredVector.end()) configuredVector.end())
addedCustomSystems.push_back(system); addedCustomSystems.push_back(system);
} }
} }
if (!mAddedCustomCollection && !addedCustomSystems.empty()) { if (!mAddedCustomCollection && !addedCustomSystems.empty()) {
for (std::string system : addedCustomSystems) for (std::string system : addedCustomSystems)
CollectionSystemsManager::get()-> CollectionSystemsManager::get()->repopulateCollection(
repopulateCollection(customSystems.find(system)->second.system); customSystems.find(system)->second.system);
} }
setNeedsSaving(); setNeedsSaving();
setNeedsReloading(); setNeedsReloading();
@ -143,32 +149,33 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
// Create custom collection from theme. // Create custom collection from theme.
std::vector<std::string> unusedFolders = std::vector<std::string> unusedFolders =
CollectionSystemsManager::get()->getUnusedSystemsFromTheme(); CollectionSystemsManager::get()->getUnusedSystemsFromTheme();
if (unusedFolders.size() > 0) { if (unusedFolders.size() > 0) {
ComponentListRow row; ComponentListRow row;
auto themeCollection = std::make_shared<TextComponent>(mWindow, auto themeCollection =
"CREATE NEW CUSTOM COLLECTION FROM THEME", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION FROM THEME",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketThemeCollection = std::make_shared<ImageComponent>(mWindow); auto bracketThemeCollection = std::make_shared<ImageComponent>(mWindow);
bracketThemeCollection->setImage(":/graphics/arrow.svg"); bracketThemeCollection->setImage(":/graphics/arrow.svg");
bracketThemeCollection->setResize(Vector2f(0, bracketThemeCollection->setResize(
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
row.addElement(themeCollection, true); row.addElement(themeCollection, true);
row.addElement(bracketThemeCollection, false); row.addElement(bracketThemeCollection, false);
row.makeAcceptInputHandler([this, unusedFolders] { row.makeAcceptInputHandler([this, unusedFolders] {
auto ss = new GuiSettings(mWindow, "SELECT THEME FOLDER"); auto ss = new GuiSettings(mWindow, "SELECT THEME FOLDER");
std::shared_ptr<OptionListComponent<std::string>> folderThemes = std::shared_ptr<OptionListComponent<std::string>> folderThemes =
std::make_shared<OptionListComponent<std::string>>(mWindow, std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(),
getHelpStyle(), "SELECT THEME FOLDER", true); "SELECT THEME FOLDER", true);
// Add custom systems. // Add custom systems.
for (auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) { for (auto it = unusedFolders.cbegin(); it != unusedFolders.cend(); it++) {
ComponentListRow row; ComponentListRow row;
std::string name = *it; std::string name = *it;
std::function<void()> createCollectionCall = [this, name] { std::function<void()> createCollectionCall = [this, name] {
createCustomCollection(name); createCustomCollection(name);
}; };
row.makeAcceptInputHandler(createCollectionCall); row.makeAcceptInputHandler(createCollectionCall);
auto themeFolder = std::make_shared<TextComponent>(mWindow, auto themeFolder = std::make_shared<TextComponent>(
Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
row.addElement(themeFolder, true); row.addElement(themeFolder, true);
// This transparent bracket is only added to generate the correct help prompts. // This transparent bracket is only added to generate the correct help prompts.
auto bracket = std::make_shared<ImageComponent>(mWindow); auto bracket = std::make_shared<ImageComponent>(mWindow);
@ -184,12 +191,11 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
// Create new custom collection. // Create new custom collection.
ComponentListRow row; ComponentListRow row;
auto newCollection = std::make_shared<TextComponent>(mWindow, auto newCollection = std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION",
"CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketNewCollection = std::make_shared<ImageComponent>(mWindow); auto bracketNewCollection = std::make_shared<ImageComponent>(mWindow);
bracketNewCollection->setImage(":/graphics/arrow.svg"); bracketNewCollection->setImage(":/graphics/arrow.svg");
bracketNewCollection->setResize(Vector2f(0, bracketNewCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
row.addElement(newCollection, true); row.addElement(newCollection, true);
row.addElement(bracketNewCollection, false); row.addElement(bracketNewCollection, false);
auto createCollectionCall = [this](const std::string& newVal) { auto createCollectionCall = [this](const std::string& newVal) {
@ -202,72 +208,72 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
createCustomCollection(name); createCustomCollection(name);
}; };
row.makeAcceptInputHandler([this, createCollectionCall] { row.makeAcceptInputHandler([this, createCollectionCall] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name", "",
"New Collection Name", "", createCollectionCall, false, "SAVE")); createCollectionCall, false, "SAVE"));
}); });
addRow(row); addRow(row);
// Delete custom collection. // Delete custom collection.
row.elements.clear(); row.elements.clear();
auto deleteCollection = std::make_shared<TextComponent>(mWindow, auto deleteCollection = std::make_shared<TextComponent>(
"DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); mWindow, "DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketDeleteCollection = std::make_shared<ImageComponent>(mWindow); auto bracketDeleteCollection = std::make_shared<ImageComponent>(mWindow);
bracketDeleteCollection->setImage(":/graphics/arrow.svg"); bracketDeleteCollection->setImage(":/graphics/arrow.svg");
bracketDeleteCollection->setResize(Vector2f(0, bracketDeleteCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
row.addElement(deleteCollection, true); row.addElement(deleteCollection, true);
row.addElement(bracketDeleteCollection, false); row.addElement(bracketDeleteCollection, false);
row.makeAcceptInputHandler([this, customSystems] { row.makeAcceptInputHandler([this, customSystems] {
auto ss = new GuiSettings(mWindow, "SELECT COLLECTION TO DELETE"); auto ss = new GuiSettings(mWindow, "SELECT COLLECTION TO DELETE");
std::shared_ptr<OptionListComponent<std::string>> customCollections = std::shared_ptr<OptionListComponent<std::string>> customCollections =
std::make_shared<OptionListComponent<std::string>>(mWindow, std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(), "", true);
getHelpStyle(), "", true); for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator it =
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator customSystems.cbegin();
it = customSystems.cbegin(); it != customSystems.cend() ; it++) { it != customSystems.cend(); it++) {
ComponentListRow row; ComponentListRow row;
std::string name = (*it).first; std::string name = (*it).first;
std::function<void()> deleteCollectionCall = [this, name] { std::function<void()> deleteCollectionCall = [this, name] {
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(
"THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" + mWindow, getHelpStyle(),
Utils::String::toUpper(name) + "'\n" "THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" +
Utils::String::toUpper(name) +
"'\n"
"ARE YOU SURE?", "ARE YOU SURE?",
"YES", [this, name] { "YES",
if (CollectionSystemsManager::get()->isEditing()) [this, name] {
CollectionSystemsManager::get()->exitEditMode(); if (CollectionSystemsManager::get()->isEditing())
mDeletedCustomCollection = true; CollectionSystemsManager::get()->exitEditMode();
std::vector<std::string> selectedCustomCollections = mDeletedCustomCollection = true;
collection_systems_custom->getSelectedObjects(); std::vector<std::string> selectedCustomCollections =
std::string collectionsConfigEntry; collection_systems_custom->getSelectedObjects();
// Create the configuration file entry. If the collection to be std::string collectionsConfigEntry;
// deleted was activated, then exclude it. // Create the configuration file entry. If the collection to be
for (auto it = selectedCustomCollections.begin(); // deleted was activated, then exclude it.
it != selectedCustomCollections.end(); it++) { for (auto it = selectedCustomCollections.begin();
if ((*it) != name) { it != selectedCustomCollections.end(); it++) {
if ((*it) != selectedCustomCollections.front() && if ((*it) != name) {
collectionsConfigEntry != "") if ((*it) != selectedCustomCollections.front() &&
collectionsConfigEntry += ","; collectionsConfigEntry != "")
collectionsConfigEntry += (*it); collectionsConfigEntry += ",";
} collectionsConfigEntry += (*it);
} }
// If the system to be deleted was present in es_settings.xml, we }
// need to re-write it. // If the system to be deleted was present in es_settings.xml, we
if (collectionsConfigEntry != // need to re-write it.
Settings::getInstance()->getString("CollectionSystemsCustom")) { if (collectionsConfigEntry !=
Settings::getInstance()->setString("CollectionSystemsCustom", Settings::getInstance()->getString("CollectionSystemsCustom")) {
collectionsConfigEntry); Settings::getInstance()->setString("CollectionSystemsCustom",
setNeedsSaving(); collectionsConfigEntry);
setNeedsGoToStart(); setNeedsSaving();
} setNeedsGoToStart();
CollectionSystemsManager::get()->deleteCustomCollection(name); }
return true; CollectionSystemsManager::get()->deleteCustomCollection(name);
}, return true;
"NO", [this] { },
return false; "NO", [this] { return false; }));
}));
}; };
row.makeAcceptInputHandler(deleteCollectionCall); row.makeAcceptInputHandler(deleteCollectionCall);
auto customCollection = std::make_shared<TextComponent>(mWindow, auto customCollection = std::make_shared<TextComponent>(
Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
row.addElement(customCollection, true); row.addElement(customCollection, true);
// This transparent bracket is only added generate the correct help prompts. // This transparent bracket is only added generate the correct help prompts.
auto bracket = std::make_shared<ImageComponent>(mWindow); auto bracket = std::make_shared<ImageComponent>(mWindow);
@ -310,14 +316,14 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
// Group unthemed custom collections. // Group unthemed custom collections.
auto use_custom_collections_system = std::make_shared<SwitchComponent>(mWindow); auto use_custom_collections_system = std::make_shared<SwitchComponent>(mWindow);
use_custom_collections_system->setState(Settings::getInstance()-> use_custom_collections_system->setState(
getBool("UseCustomCollectionsSystem")); Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", use_custom_collections_system); addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", use_custom_collections_system);
addSaveFunc([this, use_custom_collections_system] { addSaveFunc([this, use_custom_collections_system] {
if (use_custom_collections_system->getState() != if (use_custom_collections_system->getState() !=
Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { Settings::getInstance()->getBool("UseCustomCollectionsSystem")) {
Settings::getInstance()->setBool("UseCustomCollectionsSystem", Settings::getInstance()->setBool("UseCustomCollectionsSystem",
use_custom_collections_system->getState()); use_custom_collections_system->getState());
if (CollectionSystemsManager::get()->isEditing()) if (CollectionSystemsManager::get()->isEditing())
CollectionSystemsManager::get()->exitEditMode(); CollectionSystemsManager::get()->exitEditMode();
setNeedsSaving(); setNeedsSaving();
@ -330,14 +336,14 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
// Show system names in collections. // Show system names in collections.
auto collection_show_system_info = std::make_shared<SwitchComponent>(mWindow); auto collection_show_system_info = std::make_shared<SwitchComponent>(mWindow);
collection_show_system_info->setState(Settings::getInstance()-> collection_show_system_info->setState(
getBool("CollectionShowSystemInfo")); Settings::getInstance()->getBool("CollectionShowSystemInfo"));
addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", collection_show_system_info); addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", collection_show_system_info);
addSaveFunc([this, collection_show_system_info] { addSaveFunc([this, collection_show_system_info] {
if (collection_show_system_info->getState() != if (collection_show_system_info->getState() !=
Settings::getInstance()->getBool("CollectionShowSystemInfo")) { Settings::getInstance()->getBool("CollectionShowSystemInfo")) {
Settings::getInstance()->setBool("CollectionShowSystemInfo", Settings::getInstance()->setBool("CollectionShowSystemInfo",
collection_show_system_info->getState()); collection_show_system_info->getState());
setNeedsSaving(); setNeedsSaving();
setNeedsReloading(); setNeedsReloading();
setInvalidateCachedBackground(); setInvalidateCachedBackground();
@ -350,10 +356,9 @@ void GuiCollectionSystemsOptions::createCustomCollection(std::string inName)
if (CollectionSystemsManager::get()->isEditing()) if (CollectionSystemsManager::get()->isEditing())
CollectionSystemsManager::get()->exitEditMode(); CollectionSystemsManager::get()->exitEditMode();
std::string collectionName = CollectionSystemsManager::get()-> std::string collectionName = CollectionSystemsManager::get()->getValidNewCollectionName(inName);
getValidNewCollectionName(inName); SystemData* newCollection =
SystemData* newCollection = CollectionSystemsManager::get()-> CollectionSystemsManager::get()->addNewCustomCollection(collectionName);
addNewCustomCollection(collectionName);
CollectionSystemsManager::get()->saveCustomCollection(newCollection); CollectionSystemsManager::get()->saveCustomCollection(newCollection);
collection_systems_custom->add(collectionName, collectionName, true); collection_systems_custom->add(collectionName, collectionName, true);

View file

@ -12,8 +12,7 @@
#include "GuiSettings.h" #include "GuiSettings.h"
template<typename T> template <typename T> class OptionListComponent;
class OptionListComponent;
class GuiCollectionSystemsOptions : public GuiSettings class GuiCollectionSystemsOptions : public GuiSettings
{ {

View file

@ -10,23 +10,22 @@
#include "guis/GuiGameScraper.h" #include "guis/GuiGameScraper.h"
#include "FileData.h"
#include "MameNames.h"
#include "SystemData.h"
#include "components/ButtonComponent.h" #include "components/ButtonComponent.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "FileData.h"
#include "MameNames.h"
#include "SystemData.h"
GuiGameScraper::GuiGameScraper( GuiGameScraper::GuiGameScraper(Window* window,
Window* window, ScraperSearchParams params,
ScraperSearchParams params, std::function<void(const ScraperSearchResult&)> doneFunc)
std::function<void(const ScraperSearchResult&)> doneFunc) : GuiComponent(window)
: GuiComponent(window), , mGrid(window, Vector2i(1, 7))
mGrid(window, Vector2i(1, 7)), , mBox(window, ":/graphics/frame.svg")
mBox(window, ":/graphics/frame.svg"), , mSearchParams(params)
mSearchParams(params), , mClose(false)
mClose(false)
{ {
addChild(&mBox); addChild(&mBox);
addChild(&mGrid); addChild(&mGrid);
@ -40,82 +39,62 @@ GuiGameScraper::GuiGameScraper(
} }
else { else {
if (params.game->isArcadeGame() && if (params.game->isArcadeGame() &&
Settings::getInstance()->getString("Scraper") == "thegamesdb") Settings::getInstance()->getString("Scraper") == "thegamesdb")
scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" + scrapeName =
MameNames::getInstance()->getCleanName(mSearchParams.game->getCleanName()) + Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" +
")"; MameNames::getInstance()->getCleanName(mSearchParams.game->getCleanName()) + ")";
else else
scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()); scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath());
} }
mGameName = std::make_shared<TextComponent>(mWindow, scrapeName + mGameName = std::make_shared<TextComponent>(
mWindow,
scrapeName +
((mSearchParams.game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : ""), ((mSearchParams.game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : ""),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
mGrid.setEntry(mGameName, Vector2i(0, 1), false, true); mGrid.setEntry(mGameName, Vector2i(0, 1), false, true);
// Row 2 is a spacer. // Row 2 is a spacer.
mSystemName = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper( mSystemName = std::make_shared<TextComponent>(
mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL), mWindow, Utils::String::toUpper(mSearchParams.system->getFullName()),
0x888888FF, ALIGN_CENTER); Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
mGrid.setEntry(mSystemName, Vector2i(0, 3), false, true); mGrid.setEntry(mSystemName, Vector2i(0, 3), false, true);
// Row 4 is a spacer. // Row 4 is a spacer.
// GuiScraperSearch. // GuiScraperSearch.
mSearch = std::make_shared<GuiScraperSearch>(window, mSearch = std::make_shared<GuiScraperSearch>(window, GuiScraperSearch::NEVER_AUTO_ACCEPT, 1);
GuiScraperSearch::NEVER_AUTO_ACCEPT, 1);
mGrid.setEntry(mSearch, Vector2i(0, 5), true); mGrid.setEntry(mSearch, Vector2i(0, 5), true);
// Buttons // Buttons
std::vector<std::shared_ptr<ButtonComponent>> buttons; std::vector<std::shared_ptr<ButtonComponent>> buttons;
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH", buttons.push_back(
"refine search", [&] { std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH", "refine search", [&] {
mSearch->openInputScreen(mSearchParams); mSearch->openInputScreen(mSearchParams);
mGrid.resetCursor(); mGrid.resetCursor();
}));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "cancel", [&] {
if (mSearch->getSavedNewMedia()) {
// If the user aborted the scraping but there was still some media downloaded,
// then force an unload of the textures for the game image and marquee, and make
// an update of the game entry. Otherwise the images would not get updated until
// the user scrolls up and down the gamelist.
TextureResource::manualUnload(mSearchParams.game->getImagePath(), false);
TextureResource::manualUnload(mSearchParams.game->getMarqueePath(), false);
ViewController::get()->onFileChanged(mSearchParams.game, true);
}
delete this;
})); }));
buttons.push_back(std::make_shared<ButtonComponent>(
mWindow, "CANCEL", "cancel", [&] {
if (mSearch->getSavedNewMedia()) {
// If the user aborted the scraping but there was still some media downloaded,
// then force an unload of the textures for the game image and marquee, and make
// an update of the game entry. Otherwise the images would not get updated until
// the user scrolls up and down the gamelist.
TextureResource::manualUnload(mSearchParams.game->getImagePath(), false);
TextureResource::manualUnload(mSearchParams.game->getMarqueePath(), false);
ViewController::get()->onFileChanged(mSearchParams.game, true);
}
delete this; }));
mButtonGrid = makeButtonGrid(mWindow, buttons); mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false);
// We call this->close() instead of just 'delete this' in the accept callback.
// This is because of how GuiComponent::update works. If it was just 'delete this',
// the following would happen when the metadata resolver is done:
// GuiGameScraper::update()
// GuiComponent::update()
// it = mChildren.cbegin();
// mBox::update()
// it++;
// mSearchComponent::update()
// acceptCallback -> delete this
// it++; // Error, mChildren has been deleted because it was part of 'this'.
// So instead we do this:
// GuiGameScraper::update()
// GuiComponent::update()
// it = mChildren.cbegin();
// mBox::update()
// it++;
// mSearchComponent::update()
// acceptCallback -> close() -> mClose = true
// it++; // OK.
// if (mClose)
// delete this;
mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) {
doneFunc(result); close(); }); doneFunc(result);
close();
});
mSearch->setCancelCallback([&] { delete this; }); mSearch->setCancelCallback([&] { delete this; });
// Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is
@ -124,8 +103,8 @@ GuiGameScraper::GuiGameScraper(
float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth();
setSize(width, Renderer::getScreenHeight() * 0.747f); setSize(width, Renderer::getScreenHeight() * 0.747f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
mSize.y()) / 2); (Renderer::getScreenHeight() - mSize.y()) / 2.0f);
mGrid.resetCursor(); mGrid.resetCursor();
mSearch->search(params); // Start the search. mSearch->search(params); // Start the search.
@ -133,14 +112,14 @@ GuiGameScraper::GuiGameScraper(
void GuiGameScraper::onSizeChanged() void GuiGameScraper::onSizeChanged()
{ {
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mGrid.setRowHeightPerc(0, 0.04f, false); mGrid.setRowHeightPerc(0, 0.04f, false);
mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y(),
mSize.y(), false); // Game name. false); // Game name.
mGrid.setRowHeightPerc(2, 0.04f, false); mGrid.setRowHeightPerc(2, 0.04f, false);
mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y(),
mSize.y(), false); // System name. false); // System name.
mGrid.setRowHeightPerc(4, 0.04f, false); mGrid.setRowHeightPerc(4, 0.04f, false);
mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // Buttons. mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // Buttons.
mGrid.setSize(mSize); mGrid.setSize(mSize);
@ -190,5 +169,6 @@ HelpStyle GuiGameScraper::getHelpStyle()
void GuiGameScraper::close() void GuiGameScraper::close()
{ {
// This will cause update() to close the GUI.
mClose = true; mClose = true;
} }

View file

@ -11,15 +11,16 @@
#ifndef ES_APP_GUIS_GUI_GAME_SCRAPER_H #ifndef ES_APP_GUIS_GUI_GAME_SCRAPER_H
#define ES_APP_GUIS_GUI_GAME_SCRAPER_H #define ES_APP_GUIS_GUI_GAME_SCRAPER_H
#include "GuiComponent.h"
#include "components/NinePatchComponent.h" #include "components/NinePatchComponent.h"
#include "guis/GuiScraperSearch.h" #include "guis/GuiScraperSearch.h"
#include "GuiComponent.h"
class GuiGameScraper : public GuiComponent class GuiGameScraper : public GuiComponent
{ {
public: public:
GuiGameScraper(Window* window, ScraperSearchParams params, GuiGameScraper(Window* window,
std::function<void(const ScraperSearchResult&)> doneFunc); ScraperSearchParams params,
std::function<void(const ScraperSearchResult&)> doneFunc);
void onSizeChanged() override; void onSizeChanged() override;

View file

@ -10,21 +10,20 @@
#include "guis/GuiGamelistFilter.h" #include "guis/GuiGamelistFilter.h"
#include "SystemData.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "views/UIModeController.h" #include "views/UIModeController.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "SystemData.h"
GuiGamelistFilter::GuiGamelistFilter( GuiGamelistFilter::GuiGamelistFilter(Window* window,
Window* window, SystemData* system,
SystemData* system, std::function<void(bool)> filterChangedCallback)
std::function<void(bool)> filterChangedCallback) : GuiComponent(window)
: GuiComponent(window), , mMenu(window, "FILTER GAMELIST BY")
mMenu(window, "FILTER GAMELIST BY"), , mSystem(system)
mSystem(system), , mFiltersChangedCallback(filterChangedCallback)
mFiltersChangedCallback(filterChangedCallback), , mFiltersChanged(false)
mFiltersChanged(false)
{ {
initializeMenu(); initializeMenu();
} }
@ -41,7 +40,8 @@ void GuiGamelistFilter::initializeMenu()
// Show filtered menu. // Show filtered menu.
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, "RESET ALL FILTERS", row.addElement(std::make_shared<TextComponent>(mWindow, "RESET ALL FILTERS",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.makeAcceptInputHandler(std::bind(&GuiGamelistFilter::resetAllFilters, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistFilter::resetAllFilters, this));
mMenu.addRow(row); mMenu.addRow(row);
row.elements.clear(); row.elements.clear();
@ -51,13 +51,15 @@ void GuiGamelistFilter::initializeMenu()
mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this)); mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this));
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f, mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f,
Renderer::getScreenHeight() * 0.13f); Renderer::getScreenHeight() * 0.13f);
// Save the initial filter values to be able to check later if any changes were made. // Save the initial filter values to be able to check later if any changes were made.
mInitialTextFilter = mTextFilterField->getValue(); mInitialTextFilter = mTextFilterField->getValue();
for (std::map<FilterIndexType, std::shared_ptr<OptionListComponent<std::string>>>:: for (std::map<FilterIndexType,
const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) { std::shared_ptr<OptionListComponent<std::string>>>::const_iterator it =
mFilterOptions.cbegin();
it != mFilterOptions.cend(); it++) {
std::shared_ptr<OptionListComponent<std::string>> optionList = it->second; std::shared_ptr<OptionListComponent<std::string>> optionList = it->second;
std::vector<std::string> filters = optionList->getSelectedObjects(); std::vector<std::string> filters = optionList->getSelectedObjects();
mInitialFilters.push_back(filters); mInitialFilters.push_back(filters);
@ -67,8 +69,10 @@ void GuiGamelistFilter::initializeMenu()
void GuiGamelistFilter::resetAllFilters() void GuiGamelistFilter::resetAllFilters()
{ {
mFilterIndex->resetFilters(); mFilterIndex->resetFilters();
for (std::map<FilterIndexType, std::shared_ptr< OptionListComponent<std::string>>>:: for (std::map<FilterIndexType,
const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) { std::shared_ptr<OptionListComponent<std::string>>>::const_iterator it =
mFilterOptions.cbegin();
it != mFilterOptions.cend(); it++) {
std::shared_ptr<OptionListComponent<std::string>> optionList = it->second; std::shared_ptr<OptionListComponent<std::string>> optionList = it->second;
optionList->selectNone(); optionList->selectNone();
} }
@ -78,21 +82,18 @@ void GuiGamelistFilter::resetAllFilters()
mFiltersChanged = true; mFiltersChanged = true;
} }
GuiGamelistFilter::~GuiGamelistFilter() GuiGamelistFilter::~GuiGamelistFilter() { mFilterOptions.clear(); }
{
mFilterOptions.clear();
}
void GuiGamelistFilter::addFiltersToMenu() void GuiGamelistFilter::addFiltersToMenu()
{ {
ComponentListRow row; ComponentListRow row;
auto lbl = std::make_shared<TextComponent>(mWindow, auto lbl =
Utils::String::toUpper("TEXT FILTER (GAME NAME)"), std::make_shared<TextComponent>(mWindow, Utils::String::toUpper("TEXT FILTER (GAME NAME)"),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF); Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
mTextFilterField = std::make_shared<TextComponent>(mWindow, "", mTextFilterField = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_MEDIUM),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); 0x777777FF, ALIGN_RIGHT);
// Don't show the free text filter entry unless there are any games in the system. // Don't show the free text filter entry unless there are any games in the system.
if (mSystem->getRootFolder()->getChildren().size() > 0) { if (mSystem->getRootFolder()->getChildren().size() > 0) {
@ -113,25 +114,25 @@ void GuiGamelistFilter::addFiltersToMenu()
// Callback function. // Callback function.
auto updateVal = [this](const std::string& newVal) { auto updateVal = [this](const std::string& newVal) {
mTextFilterField->setValue(Utils::String::toUpper(newVal)); mTextFilterField->setValue(Utils::String::toUpper(newVal));
mFilterIndex->setTextFilter(Utils::String::toUpper(newVal)); mFilterIndex->setTextFilter(Utils::String::toUpper(newVal));
}; };
row.makeAcceptInputHandler([this, updateVal] { row.makeAcceptInputHandler([this, updateVal] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "TEXT FILTER (GAME NAME)",
"TEXT FILTER (GAME NAME)", mTextFilterField->getValue(), mTextFilterField->getValue(), updateVal, false, "OK",
updateVal, false, "OK", "APPLY CHANGES?")); "APPLY CHANGES?"));
}); });
mMenu.addRow(row); mMenu.addRow(row);
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls(); std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
for (std::vector<FilterDataDecl>::const_iterator it = for (std::vector<FilterDataDecl>::const_iterator it = decls.cbegin(); // Line break.
decls.cbegin(); it != decls.cend(); it++) { it != decls.cend(); it++) {
FilterIndexType type = (*it).type; // Type of filter. FilterIndexType type = (*it).type; // Type of filter.
// All possible filters for this type.
// All possible filters for this type.
std::map<std::string, int>* allKeys = (*it).allIndexKeys; std::map<std::string, int>* allKeys = (*it).allIndexKeys;
std::string menuLabel = (*it).menuLabel; // Text to show in menu. std::string menuLabel = (*it).menuLabel; // Text to show in menu.
std::shared_ptr<OptionListComponent<std::string>> optionList; std::shared_ptr<OptionListComponent<std::string>> optionList;
@ -140,9 +141,9 @@ void GuiGamelistFilter::addFiltersToMenu()
ComponentListRow row; ComponentListRow row;
// Add genres. // Add genres.
optionList = std::make_shared<OptionListComponent<std::string>> optionList = std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(),
(mWindow, getHelpStyle(), menuLabel, true); menuLabel, true);
for (auto it: *allKeys) for (auto it : *allKeys)
optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type)); optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type));
if (allKeys->size() > 0) if (allKeys->size() > 0)
mMenu.addWithLabel(menuLabel, optionList); mMenu.addWithLabel(menuLabel, optionList);
@ -157,8 +158,10 @@ void GuiGamelistFilter::applyFilters()
mFiltersChanged = true; mFiltersChanged = true;
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls(); std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
for (std::map<FilterIndexType, std::shared_ptr<OptionListComponent<std::string>>>:: for (std::map<FilterIndexType,
const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) { std::shared_ptr<OptionListComponent<std::string>>>::const_iterator it =
mFilterOptions.cbegin();
it != mFilterOptions.cend(); it++) {
std::shared_ptr<OptionListComponent<std::string>> optionList = it->second; std::shared_ptr<OptionListComponent<std::string>> optionList = it->second;
std::vector<std::string> filters = optionList->getSelectedObjects(); std::vector<std::string> filters = optionList->getSelectedObjects();
auto iteratorDistance = std::distance(mFilterOptions.cbegin(), it); auto iteratorDistance = std::distance(mFilterOptions.cbegin(), it);

View file

@ -11,19 +11,19 @@
#ifndef ES_APP_GUIS_GUI_GAME_LIST_FILTER_H #ifndef ES_APP_GUIS_GUI_GAME_LIST_FILTER_H
#define ES_APP_GUIS_GUI_GAME_LIST_FILTER_H #define ES_APP_GUIS_GUI_GAME_LIST_FILTER_H
#include "components/MenuComponent.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "GuiComponent.h" #include "GuiComponent.h"
#include "components/MenuComponent.h"
template<typename T> template <typename T> class OptionListComponent;
class OptionListComponent;
class SystemData; class SystemData;
class GuiGamelistFilter : public GuiComponent class GuiGamelistFilter : public GuiComponent
{ {
public: public:
GuiGamelistFilter(Window* window, GuiGamelistFilter(Window* window,
SystemData* system, std::function<void(bool)> filtersChangedCallback); SystemData* system,
std::function<void(bool)> filtersChangedCallback);
~GuiGamelistFilter(); ~GuiGamelistFilter();
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;

View file

@ -12,11 +12,6 @@
#include "GuiGamelistOptions.h" #include "GuiGamelistOptions.h"
#include "guis/GuiGamelistFilter.h"
#include "scrapers/Scraper.h"
#include "views/gamelist/IGameListView.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "FileSorts.h" #include "FileSorts.h"
@ -24,18 +19,21 @@
#include "MameNames.h" #include "MameNames.h"
#include "Sound.h" #include "Sound.h"
#include "SystemData.h" #include "SystemData.h"
#include "guis/GuiGamelistFilter.h"
#include "scrapers/Scraper.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "views/gamelist/IGameListView.h"
GuiGamelistOptions::GuiGamelistOptions( GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system)
Window* window, : GuiComponent(window)
SystemData* system) , mSystem(system)
: GuiComponent(window), , mMenu(window, "OPTIONS")
mSystem(system), , mFiltersChanged(false)
mMenu(window, "OPTIONS"), , mCancelled(false)
mFiltersChanged(false), , mIsCustomCollection(false)
mCancelled(false), , mIsCustomCollectionGroup(false)
mIsCustomCollection(false), , mCustomCollectionSystem(nullptr)
mIsCustomCollectionGroup(false),
mCustomCollectionSystem(nullptr)
{ {
addChild(&mMenu); addChild(&mMenu);
@ -46,11 +44,10 @@ GuiGamelistOptions::GuiGamelistOptions(
ComponentListRow row; ComponentListRow row;
// There is some special logic required for custom collections. // There is some special logic required for custom collections.
if (file->getSystem()->isCustomCollection() && if (file->getSystem()->isCustomCollection() && file->getPath() != file->getSystem()->getName())
file->getPath() != file->getSystem()->getName())
mIsCustomCollection = true; mIsCustomCollection = true;
else if (file->getSystem()->isCustomCollection() && else if (file->getSystem()->isCustomCollection() &&
file->getPath() == file->getSystem()->getName()) file->getPath() == file->getSystem()->getName())
mIsCustomCollectionGroup = true; mIsCustomCollectionGroup = true;
if (mFromPlaceholder && file->getSystem()->isGroupedCustomCollection()) if (mFromPlaceholder && file->getSystem()->isGroupedCustomCollection())
@ -84,12 +81,12 @@ GuiGamelistOptions::GuiGamelistOptions(
else { else {
// Check if the currently selected game is a favorite. // Check if the currently selected game is a favorite.
bool isFavorite = false; bool isFavorite = false;
if (mFirstLetterIndex.size() == 1 && mFirstLetterIndex.front() == if (mFirstLetterIndex.size() == 1 &&
ViewController::FAVORITE_CHAR) mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR)
isFavorite = true; isFavorite = true;
else if (mFirstLetterIndex.size() > 1 && (mFirstLetterIndex.front() == else if (mFirstLetterIndex.size() > 1 &&
ViewController::FAVORITE_CHAR || mFirstLetterIndex[1] == (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR ||
ViewController::FAVORITE_CHAR)) mFirstLetterIndex[1] == ViewController::FAVORITE_CHAR))
isFavorite = true; isFavorite = true;
// Get the first character of the game name (which could be a Unicode character). // Get the first character of the game name (which could be a Unicode character).
@ -99,8 +96,8 @@ GuiGamelistOptions::GuiGamelistOptions(
mCurrentFirstCharacter = Utils::String::getFirstCharacter(file->getSortName()); mCurrentFirstCharacter = Utils::String::getFirstCharacter(file->getSortName());
} }
mJumpToLetterList = std::make_shared<LetterList>(mWindow, getHelpStyle(), mJumpToLetterList =
"JUMP TO...", false); std::make_shared<LetterList>(mWindow, getHelpStyle(), "JUMP TO...", false);
// Populate the quick selector. // Populate the quick selector.
for (unsigned int i = 0; i < mFirstLetterIndex.size(); i++) { for (unsigned int i = 0; i < mFirstLetterIndex.size(); i++) {
@ -146,8 +143,9 @@ GuiGamelistOptions::GuiGamelistOptions(
if (!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() > 0) { if (!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() > 0) {
if (system->getName() != "recent" && Settings::getInstance()->getBool("GamelistFilters")) { if (system->getName() != "recent" && Settings::getInstance()->getBool("GamelistFilters")) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent> row.addElement(std::make_shared<TextComponent>(mWindow, "FILTER GAMELIST",
(mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
mMenu.addRow(row); mMenu.addRow(row);
@ -155,12 +153,12 @@ GuiGamelistOptions::GuiGamelistOptions(
} }
// Add a dummy entry when applicable as the menu looks quite ugly if it's just blank. // Add a dummy entry when applicable as the menu looks quite ugly if it's just blank.
else if (!CollectionSystemsManager::get()->isEditing() && else if (!CollectionSystemsManager::get()->isEditing() &&
mSystem->getRootFolder()->getChildren().size() == 0 && mSystem->getRootFolder()->getChildren().size() == 0 && !mIsCustomCollectionGroup &&
!mIsCustomCollectionGroup && !mIsCustomCollection) { !mIsCustomCollection) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent> row.addElement(std::make_shared<TextComponent>(mWindow, "THIS SYSTEM HAS NO GAMES",
(mWindow, "THIS SYSTEM HAS NO GAMES", Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); true);
mMenu.addRow(row); mMenu.addRow(row);
} }
@ -171,34 +169,40 @@ GuiGamelistOptions::GuiGamelistOptions(
customSystem = system->getName(); customSystem = system->getName();
if (UIModeController::getInstance()->isUIModeFull() && if (UIModeController::getInstance()->isUIModeFull() &&
(mIsCustomCollection || mIsCustomCollectionGroup)) { (mIsCustomCollection || mIsCustomCollectionGroup)) {
if (CollectionSystemsManager::get()->getEditingCollection() != customSystem) { if (CollectionSystemsManager::get()->getEditingCollection() != customSystem) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>( row.addElement(std::make_shared<TextComponent>(mWindow,
mWindow, "ADD/REMOVE GAMES TO THIS COLLECTION", "ADD/REMOVE GAMES TO THIS COLLECTION",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
mMenu.addRow(row); mMenu.addRow(row);
} }
} }
if (UIModeController::getInstance()->isUIModeFull() && if (UIModeController::getInstance()->isUIModeFull() &&
CollectionSystemsManager::get()->isEditing()) { CollectionSystemsManager::get()->isEditing()) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>( row.addElement(std::make_shared<TextComponent>(
mWindow, "FINISH EDITING '" + Utils::String::toUpper( mWindow,
CollectionSystemsManager::get()->getEditingCollection()) + "FINISH EDITING '" +
"' COLLECTION",Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Utils::String::toUpper(
CollectionSystemsManager::get()->getEditingCollection()) +
"' COLLECTION",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
mMenu.addRow(row); mMenu.addRow(row);
} }
if (file->getType() == FOLDER) { if (file->getType() == FOLDER) {
if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder && if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder &&
!(mSystem->isCollection() && file->getType() == FOLDER)) { !(mSystem->isCollection() && file->getType() == FOLDER)) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS FOLDER'S METADATA",
"EDIT THIS FOLDER'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
mMenu.addRow(row); mMenu.addRow(row);
@ -206,10 +210,11 @@ GuiGamelistOptions::GuiGamelistOptions(
} }
else { else {
if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder && if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder &&
!(mSystem->isCollection() && file->getType() == FOLDER)) { !(mSystem->isCollection() && file->getType() == FOLDER)) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS GAME'S METADATA",
"EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
mMenu.addRow(row); mMenu.addRow(row);
@ -218,19 +223,25 @@ GuiGamelistOptions::GuiGamelistOptions(
// Buttons. The logic to apply or cancel settings are handled by the destructor. // Buttons. The logic to apply or cancel settings are handled by the destructor.
if ((!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() == 0) || if ((!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() == 0) ||
system->getName() == "recent") { system->getName() == "recent") {
mMenu.addButton("CLOSE", "close", [&] { mCancelled = true; delete this; }); mMenu.addButton("CLOSE", "close", [&] {
mCancelled = true;
delete this;
});
} }
else { else {
mMenu.addButton("APPLY", "apply", [&] { delete this; }); mMenu.addButton("APPLY", "apply", [&] { delete this; });
mMenu.addButton("CANCEL", "cancel", [&] { mCancelled = true; delete this; }); mMenu.addButton("CANCEL", "cancel", [&] {
mCancelled = true;
delete this;
});
} }
// Center the menu. // Center the menu.
setSize(static_cast<float>(Renderer::getScreenWidth()), setSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f, (mSize.y() - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f,
mMenu.getSize().y()) / 2.0f); (mSize.y() - mMenu.getSize().y()) / 2.0f);
} }
GuiGamelistOptions::~GuiGamelistOptions() GuiGamelistOptions::~GuiGamelistOptions()
@ -248,11 +259,12 @@ GuiGamelistOptions::~GuiGamelistOptions()
if (!mFromPlaceholder) { if (!mFromPlaceholder) {
ViewController::get()->reloadGameListView(mSystem); ViewController::get()->reloadGameListView(mSystem);
} }
else if (!mCustomCollectionSystem->getRootFolder()-> else if (!mCustomCollectionSystem->getRootFolder()
getChildrenListToDisplay().empty()) { ->getChildrenListToDisplay()
ViewController::get()->reloadGameListView(mSystem); .empty()) {
getGamelist()->setCursor(mCustomCollectionSystem-> ViewController::get()->reloadGameListView(mSystem);
getRootFolder()->getChildrenListToDisplay().front()); getGamelist()->setCursor(
mCustomCollectionSystem->getRootFolder()->getChildrenListToDisplay().front());
} }
} }
} }
@ -262,6 +274,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
if (!mFromPlaceholder) { if (!mFromPlaceholder) {
FileData* root; FileData* root;
if (mIsCustomCollection) if (mIsCustomCollection)
root = getGamelist()->getCursor()->getSystem()->getRootFolder(); root = getGamelist()->getCursor()->getSystem()->getRootFolder();
else else
@ -269,7 +282,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
// If a new sorting type was selected, then sort and update mSortTypeString for the system. // If a new sorting type was selected, then sort and update mSortTypeString for the system.
if (!mIsCustomCollectionGroup && if (!mIsCustomCollectionGroup &&
(*mListSort->getSelected()).description != root->getSortTypeString()) { (*mListSort->getSelected()).description != root->getSortTypeString()) {
// This will also recursively sort children. // This will also recursively sort children.
root->sort(*mListSort->getSelected(), mFavoritesSorting); root->sort(*mListSort->getSelected(), mFavoritesSorting);
root->setSortTypeString((*mListSort->getSelected()).description); root->setSortTypeString((*mListSort->getSelected()).description);
@ -281,7 +294,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
// Has the user changed the letter using the quick selector? // Has the user changed the letter using the quick selector?
if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) { if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) {
if (mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR || if (mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR ||
mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR) mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR)
jumpToFirstRow(); jumpToFirstRow();
else else
jumpToLetter(); jumpToLetter();
@ -303,7 +316,7 @@ void GuiGamelistOptions::openGamelistFilter()
if (mIsCustomCollection) if (mIsCustomCollection)
ggf = new GuiGamelistFilter(mWindow, getGamelist()->getCursor()->getSystem(), ggf = new GuiGamelistFilter(mWindow, getGamelist()->getCursor()->getSystem(),
filtersChangedFunc); filtersChangedFunc);
else else
ggf = new GuiGamelistFilter(mWindow, mSystem, filtersChangedFunc); ggf = new GuiGamelistFilter(mWindow, mSystem, filtersChangedFunc);
@ -329,10 +342,9 @@ void GuiGamelistOptions::startEditMode()
// Display the indication icons which show what games are part of the custom collection // Display the indication icons which show what games are part of the custom collection
// currently being edited. This is done cheaply using onFileChanged() which will trigger // currently being edited. This is done cheaply using onFileChanged() which will trigger
// populateList(). // populateList().
for (auto it = SystemData::sSystemVector.begin(); for (auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) {
it != SystemData::sSystemVector.end(); it++) {
ViewController::get()->getGameListView((*it))->onFileChanged( ViewController::get()->getGameListView((*it))->onFileChanged(
ViewController::get()->getGameListView((*it))->getCursor(), false); ViewController::get()->getGameListView((*it))->getCursor(), false);
} }
if (mSystem->getRootFolder()->getChildren().size() == 0) if (mSystem->getRootFolder()->getChildren().size() == 0)
@ -362,12 +374,12 @@ void GuiGamelistOptions::openMetaDataEd()
clearGameBtnFunc = [this, file] { clearGameBtnFunc = [this, file] {
if (file->getType() == FOLDER) { if (file->getType() == FOLDER) {
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \"" << LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \""
file->getFullPath() << "\""; << file->getFullPath() << "\"";
} }
else { else {
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \"" << LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \""
file->getFullPath() << "\""; << file->getFullPath() << "\"";
} }
ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file); ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file);
@ -375,10 +387,10 @@ void GuiGamelistOptions::openMetaDataEd()
const std::vector<MetaDataDecl>& mdd = file->metadata.getMDD(); const std::vector<MetaDataDecl>& mdd = file->metadata.getMDD();
for (auto it = mdd.cbegin(); it != mdd.cend(); it++) { for (auto it = mdd.cbegin(); it != mdd.cend(); it++) {
if (it->key == "name") { if (it->key == "name") {
if (file->isArcadeGame()) { if (file->isArcadeGame()) {
// If it's a MAME or Neo Geo game, expand the game name accordingly. // If it's a MAME or Neo Geo game, expand the game name accordingly.
file->metadata.set(it->key, MameNames::getInstance()-> file->metadata.set(
getCleanName(file->getCleanName())); it->key, MameNames::getInstance()->getCleanName(file->getCleanName()));
} }
else { else {
file->metadata.set(it->key, file->getDisplayName()); file->metadata.set(it->key, file->getDisplayName());
@ -408,8 +420,8 @@ void GuiGamelistOptions::openMetaDataEd()
}; };
deleteGameBtnFunc = [this, file] { deleteGameBtnFunc = [this, file] {
LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath() << LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath()
"\", all its media files and its gamelist.xml entry."; << "\", all its media files and its gamelist.xml entry.";
CollectionSystemsManager::get()->deleteCollectionFiles(file); CollectionSystemsManager::get()->deleteCollectionFiles(file);
ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file); ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file);
ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true); ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true);
@ -420,20 +432,20 @@ void GuiGamelistOptions::openMetaDataEd()
}; };
if (file->getType() == FOLDER) { if (file->getType() == FOLDER) {
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, mWindow->pushGui(new GuiMetaDataEd(
file->metadata.getMDD(FOLDER_METADATA), p, mWindow, &file->metadata, file->metadata.getMDD(FOLDER_METADATA), p,
Utils::FileSystem::getFileName(file->getPath()), std::bind( Utils::FileSystem::getFileName(file->getPath()),
&IGameListView::onFileChanged, ViewController::get()->getGameListView( std::bind(&IGameListView::onFileChanged,
file->getSystem()).get(), file, true), ViewController::get()->getGameListView(file->getSystem()).get(), file, true),
clearGameBtnFunc, deleteGameBtnFunc)); clearGameBtnFunc, deleteGameBtnFunc));
} }
else { else {
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, mWindow->pushGui(new GuiMetaDataEd(
file->metadata.getMDD(GAME_METADATA), p, mWindow, &file->metadata, file->metadata.getMDD(GAME_METADATA), p,
Utils::FileSystem::getFileName(file->getPath()), std::bind( Utils::FileSystem::getFileName(file->getPath()),
&IGameListView::onFileChanged, ViewController::get()->getGameListView( std::bind(&IGameListView::onFileChanged,
file->getSystem()).get(), file, true), ViewController::get()->getGameListView(file->getSystem()).get(), file, true),
clearGameBtnFunc, deleteGameBtnFunc)); clearGameBtnFunc, deleteGameBtnFunc));
} }
} }
@ -442,14 +454,14 @@ void GuiGamelistOptions::jumpToLetter()
char letter = mJumpToLetterList->getSelected().front(); char letter = mJumpToLetterList->getSelected().front();
// Get the gamelist. // Get the gamelist.
const std::vector<FileData*>& files = getGamelist()->getCursor()-> const std::vector<FileData*>& files =
getParent()->getChildrenListToDisplay(); getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
for (unsigned int i = 0; i < files.size(); i++) { for (unsigned int i = 0; i < files.size(); i++) {
if (mFavoritesSorting && (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR || if (mFavoritesSorting && (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR ||
mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) { mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) {
if (static_cast<char>(toupper(files.at(i)->getSortName().front())) == if (static_cast<char>(toupper(files.at(i)->getSortName().front())) == letter &&
letter && !files.at(i)->getFavorite()) { !files.at(i)->getFavorite()) {
if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) { if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) {
continue; continue;
} }
@ -477,8 +489,8 @@ void GuiGamelistOptions::jumpToFirstRow()
{ {
if (mFoldersOnTop && mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR) { if (mFoldersOnTop && mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR) {
// Get the gamelist. // Get the gamelist.
const std::vector<FileData*>& files = getGamelist()->getCursor()-> const std::vector<FileData*>& files =
getParent()->getChildrenListToDisplay(); getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
// Select the first game that is not a folder, unless it's a folder-only list in // Select the first game that is not a folder, unless it's a folder-only list in
// which case the first line overall is selected. // which case the first line overall is selected.
for (auto it = files.cbegin(); it != files.cend(); it++) { for (auto it = files.cbegin(); it != files.cend(); it++) {
@ -502,8 +514,7 @@ bool GuiGamelistOptions::input(InputConfig* config, Input input)
if (input.value != 0 && config->isMappedTo("back", input)) if (input.value != 0 && config->isMappedTo("back", input))
mCancelled = true; mCancelled = true;
if (input.value != 0 && (config->isMappedTo("b", input) || if (input.value != 0 && (config->isMappedTo("b", input) || config->isMappedTo("back", input))) {
config->isMappedTo("back", input))) {
delete this; delete this;
return true; return true;
} }
@ -521,9 +532,8 @@ HelpStyle GuiGamelistOptions::getHelpStyle()
std::vector<HelpPrompt> GuiGamelistOptions::getHelpPrompts() std::vector<HelpPrompt> GuiGamelistOptions::getHelpPrompts()
{ {
auto prompts = mMenu.getHelpPrompts(); auto prompts = mMenu.getHelpPrompts();
if (mSystem->getRootFolder()->getChildren().size() > 0 || if (mSystem->getRootFolder()->getChildren().size() > 0 || mIsCustomCollectionGroup ||
mIsCustomCollectionGroup || mIsCustomCollection || mIsCustomCollection || CollectionSystemsManager::get()->isEditing())
CollectionSystemsManager::get()->isEditing())
prompts.push_back(HelpPrompt("a", "select")); prompts.push_back(HelpPrompt("a", "select"));
if (mSystem->getRootFolder()->getChildren().size() > 0 && mSystem->getName() != "recent") { if (mSystem->getRootFolder()->getChildren().size() > 0 && mSystem->getName() != "recent") {
prompts.push_back(HelpPrompt("b", "close (apply)")); prompts.push_back(HelpPrompt("b", "close (apply)"));

View file

@ -13,11 +13,11 @@
#ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H #ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H
#define ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H #define ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H
#include "FileData.h"
#include "GuiComponent.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "FileData.h"
#include "GuiComponent.h"
class IGameListView; class IGameListView;
class SystemData; class SystemData;

View file

@ -14,21 +14,18 @@
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
GuiInfoPopup::GuiInfoPopup( GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration)
Window* window, : GuiComponent(window)
std::string message, , mMessage(message)
int duration) , mDuration(duration)
: GuiComponent(window), , mRunning(true)
mMessage(message),
mDuration(duration),
mRunning(true)
{ {
mFrame = new NinePatchComponent(window); mFrame = new NinePatchComponent(window);
float maxWidth = Renderer::getScreenWidth() * 0.9f; float maxWidth = Renderer::getScreenWidth() * 0.9f;
float maxHeight = Renderer::getScreenHeight() * 0.2f; float maxHeight = Renderer::getScreenHeight() * 0.2f;
std::shared_ptr<TextComponent> s = std::make_shared<TextComponent>(mWindow, "", std::shared_ptr<TextComponent> s = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER); mWindow, "", Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER);
// We do this to force the text container to resize and return the actual expected popup size. // We do this to force the text container to resize and return the actual expected popup size.
s->setSize(0.0f, 0.0f); s->setSize(0.0f, 0.0f);
@ -51,13 +48,13 @@ GuiInfoPopup::GuiInfoPopup(
mSize[0] = mSize.x() + paddingX; mSize[0] = mSize.x() + paddingX;
mSize[1] = mSize.y() + paddingY; mSize[1] = mSize.y() + paddingY;
float posX = Renderer::getScreenWidth()* 0.5f - mSize.x() * 0.5f; float posX = Renderer::getScreenWidth() * 0.5f - mSize.x() * 0.5f;
float posY = Renderer::getScreenHeight() * 0.02f; float posY = Renderer::getScreenHeight() * 0.02f;
setPosition(posX, posY, 0); setPosition(posX, posY, 0);
mFrame->setImagePath(":/graphics/frame.svg"); mFrame->setImagePath(":/graphics/frame.svg");
mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
addChild(mFrame); addChild(mFrame);
// We only initialize the actual time when we first start to render. // We only initialize the actual time when we first start to render.

View file

@ -22,7 +22,7 @@ public:
~GuiInfoPopup(); ~GuiInfoPopup();
void render(const Transform4x4f& parentTrans) override; void render(const Transform4x4f& parentTrans) override;
inline void stop() override { mRunning = false; } void stop() override { mRunning = false; }
private: private:
bool updateState(); bool updateState();

View file

@ -8,20 +8,19 @@
#include "guis/GuiLaunchScreen.h" #include "guis/GuiLaunchScreen.h"
#include "FileData.h"
#include "SystemData.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "math/Misc.h" #include "math/Misc.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "FileData.h"
#include "SystemData.h"
GuiLaunchScreen::GuiLaunchScreen( GuiLaunchScreen::GuiLaunchScreen(Window* window)
Window* window) : GuiComponent(window)
: GuiComponent(window), , mBackground(window, ":/graphics/frame.svg")
mBackground(window, ":/graphics/frame.svg"), , mGrid(nullptr)
mGrid(nullptr), , mMarquee(nullptr)
mMarquee(nullptr), , mWindow(window)
mWindow(window)
{ {
addChild(&mBackground); addChild(&mBackground);
mWindow->setLaunchScreen(this); mWindow->setLaunchScreen(this);
@ -29,6 +28,7 @@ GuiLaunchScreen::GuiLaunchScreen(
GuiLaunchScreen::~GuiLaunchScreen() GuiLaunchScreen::~GuiLaunchScreen()
{ {
// This only executes when exiting the application.
closeLaunchScreen(); closeLaunchScreen();
} }
@ -51,49 +51,53 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
const float gameNameFontSize = 0.073f; const float gameNameFontSize = 0.073f;
// Spacer row. // Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), false, false,
false, false, Vector2i(1, 1)); Vector2i(1, 1));
// Title. // Title.
mTitle = std::make_shared<TextComponent>(mWindow, "LAUNCHING GAME", mTitle = std::make_shared<TextComponent>(
Font::get(static_cast<int>(titleFontSize * std::min(Renderer::getScreenHeight(), mWindow, "LAUNCHING GAME",
Renderer::getScreenWidth()))), 0x666666FF, ALIGN_CENTER); Font::get(static_cast<int>(
titleFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))),
0x666666FF, ALIGN_CENTER);
mGrid->setEntry(mTitle, Vector2i(1, 1), false, true, Vector2i(1, 1)); mGrid->setEntry(mTitle, Vector2i(1, 1), false, true, Vector2i(1, 1));
// Spacer row. // Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 2), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 2), false, false,
false, false, Vector2i(1, 1)); Vector2i(1, 1));
// Row for the marquee. // Row for the marquee.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 3), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 3), false, false,
false, false, Vector2i(1, 1)); Vector2i(1, 1));
// Spacer row. // Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 4), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 4), false, false,
false, false, Vector2i(1, 1)); Vector2i(1, 1));
// Game name. // Game name.
mGameName = std::make_shared<TextComponent>(mWindow, "GAME NAME", mGameName = std::make_shared<TextComponent>(
Font::get(static_cast<int>(gameNameFontSize * std::min(Renderer::getScreenHeight(), mWindow, "GAME NAME",
Renderer::getScreenWidth()))), 0x444444FF, ALIGN_CENTER); Font::get(static_cast<int>(
gameNameFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))),
0x444444FF, ALIGN_CENTER);
mGrid->setEntry(mGameName, Vector2i(1, 5), false, true, Vector2i(1, 1)); mGrid->setEntry(mGameName, Vector2i(1, 5), false, true, Vector2i(1, 1));
// System name. // System name.
mSystemName = std::make_shared<TextComponent>(mWindow, "SYSTEM NAME", mSystemName = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER); mWindow, "SYSTEM NAME", Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER);
mGrid->setEntry(mSystemName, Vector2i(1, 6), false, true, Vector2i(1, 1)); mGrid->setEntry(mSystemName, Vector2i(1, 6), false, true, Vector2i(1, 1));
// Spacer row. // Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 7), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 7), false, false,
false, false, Vector2i(1, 1)); Vector2i(1, 1));
// Left spacer. // Left spacer.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false, false,
false, false, Vector2i(1, 8)); Vector2i(1, 8));
// Right spacer. // Right spacer.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(2, 0), mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(2, 0), false, false,
false, false, Vector2i(1, 8)); Vector2i(1, 8));
// Adjust the width depending on the aspect ratio of the screen, to make the screen look // Adjust the width depending on the aspect ratio of the screen, to make the screen look
// somewhat coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 // somewhat coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9
@ -106,9 +110,11 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
float maxWidth = static_cast<float>(Renderer::getScreenWidth()) * maxWidthModifier; float maxWidth = static_cast<float>(Renderer::getScreenWidth()) * maxWidthModifier;
float minWidth = static_cast<float>(Renderer::getScreenWidth()) * minWidthModifier; float minWidth = static_cast<float>(Renderer::getScreenWidth()) * minWidthModifier;
float fontWidth = Font::get(static_cast<int>(gameNameFontSize * float fontWidth =
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))-> Font::get(static_cast<int>(gameNameFontSize * std::min(Renderer::getScreenHeight(),
sizeText(Utils::String::toUpper(game->getName())).x(); Renderer::getScreenWidth())))
->sizeText(Utils::String::toUpper(game->getName()))
.x();
// Add a bit of width to compensate for the left and right spacers. // Add a bit of width to compensate for the left and right spacers.
fontWidth += static_cast<float>(Renderer::getScreenWidth()) * 0.05f; fontWidth += static_cast<float>(Renderer::getScreenWidth()) * 0.05f;
@ -159,7 +165,8 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
if (mImagePath != "") { if (mImagePath != "") {
mMarquee->setImage(game->getMarqueePath(), false); mMarquee->setImage(game->getMarqueePath(), false);
mMarquee->cropTransparentPadding(static_cast<float>(Renderer::getScreenWidth()) * mMarquee->cropTransparentPadding(static_cast<float>(Renderer::getScreenWidth()) *
(0.25f * (1.778f / Renderer::getScreenAspectRatio())), mGrid->getRowHeight(3)); (0.25f * (1.778f / Renderer::getScreenAspectRatio())),
mGrid->getRowHeight(3));
mMarquee->setOrigin(0.5f, 0.5f); mMarquee->setOrigin(0.5f, 0.5f);
Vector3f currentPos = mMarquee->getPosition(); Vector3f currentPos = mMarquee->getPosition();
@ -167,18 +174,18 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
// Position the image in the middle of row four. // Position the image in the middle of row four.
currentPos.x() = mSize.x() / 2.0f; currentPos.x() = mSize.x() / 2.0f;
currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + mGrid->getRowHeight(2) +
mGrid->getRowHeight(2) + mGrid->getRowHeight(3) / 2.0f; mGrid->getRowHeight(3) / 2.0f;
mMarquee->setPosition(currentPos); mMarquee->setPosition(currentPos);
} }
setOrigin({0.5f, 0.5f}); setOrigin({ 0.5f, 0.5f });
// Center on the X axis and keep slightly off-center on the Y axis. // Center on the X axis and keep slightly off-center on the Y axis.
setPosition(static_cast<float>(Renderer::getScreenWidth()) / 2.0f, setPosition(static_cast<float>(Renderer::getScreenWidth()) / 2.0f,
static_cast<float>(Renderer::getScreenHeight()) / 2.25f); static_cast<float>(Renderer::getScreenHeight()) / 2.25f);
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBackground.setEdgeColor(0xEEEEEEFF); mBackground.setEdgeColor(0xEEEEEEFF);
} }
@ -203,6 +210,7 @@ void GuiLaunchScreen::closeLaunchScreen()
void GuiLaunchScreen::onSizeChanged() void GuiLaunchScreen::onSizeChanged()
{ {
// Update mGrid size.
mGrid->setSize(mSize); mGrid->setSize(mSize);
} }

View file

@ -9,12 +9,12 @@
#ifndef ES_APP_GUIS_GUI_LAUNCH_SCREEN_H #ifndef ES_APP_GUIS_GUI_LAUNCH_SCREEN_H
#define ES_APP_GUIS_GUI_LAUNCH_SCREEN_H #define ES_APP_GUIS_GUI_LAUNCH_SCREEN_H
#include "GuiComponent.h"
#include "Window.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
#include "components/ImageComponent.h" #include "components/ImageComponent.h"
#include "components/NinePatchComponent.h" #include "components/NinePatchComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "GuiComponent.h"
#include "Window.h"
class FileData; class FileData;
@ -35,7 +35,6 @@ public:
private: private:
Window* mWindow; Window* mWindow;
ComponentGrid* mGrid; ComponentGrid* mGrid;
NinePatchComponent mBackground; NinePatchComponent mBackground;
std::shared_ptr<TextComponent> mTitle; std::shared_ptr<TextComponent> mTitle;

View file

@ -9,11 +9,11 @@
#include "guis/GuiMediaViewerOptions.h" #include "guis/GuiMediaViewerOptions.h"
#include "components/SwitchComponent.h"
#include "Settings.h" #include "Settings.h"
#include "components/SwitchComponent.h"
GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& title) GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& title)
: GuiSettings(window, title) : GuiSettings(window, title)
{ {
// Keep videos running when viewing images. // Keep videos running when viewing images.
auto keep_video_running = std::make_shared<SwitchComponent>(mWindow); auto keep_video_running = std::make_shared<SwitchComponent>(mWindow);
@ -21,9 +21,9 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string&
addWithLabel("KEEP VIDEOS RUNNING WHEN VIEWING IMAGES", keep_video_running); addWithLabel("KEEP VIDEOS RUNNING WHEN VIEWING IMAGES", keep_video_running);
addSaveFunc([keep_video_running, this] { addSaveFunc([keep_video_running, this] {
if (keep_video_running->getState() != if (keep_video_running->getState() !=
Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) { Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) {
Settings::getInstance()->setBool("MediaViewerKeepVideoRunning", Settings::getInstance()->setBool("MediaViewerKeepVideoRunning",
keep_video_running->getState()); keep_video_running->getState());
setNeedsSaving(); setNeedsSaving();
} }
}); });
@ -34,23 +34,23 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string&
addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", stretch_videos); addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", stretch_videos);
addSaveFunc([stretch_videos, this] { addSaveFunc([stretch_videos, this] {
if (stretch_videos->getState() != if (stretch_videos->getState() !=
Settings::getInstance()->getBool("MediaViewerStretchVideos")) { Settings::getInstance()->getBool("MediaViewerStretchVideos")) {
Settings::getInstance()->setBool("MediaViewerStretchVideos", Settings::getInstance()->setBool("MediaViewerStretchVideos",
stretch_videos->getState()); stretch_videos->getState());
setNeedsSaving(); setNeedsSaving();
} }
}); });
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
// Render scanlines for videos using a shader. // Render scanlines for videos using a shader.
auto video_scanlines = std::make_shared<SwitchComponent>(mWindow); auto video_scanlines = std::make_shared<SwitchComponent>(mWindow);
video_scanlines->setState(Settings::getInstance()->getBool("MediaViewerVideoScanlines")); video_scanlines->setState(Settings::getInstance()->getBool("MediaViewerVideoScanlines"));
addWithLabel("RENDER SCANLINES FOR VIDEOS", video_scanlines); addWithLabel("RENDER SCANLINES FOR VIDEOS", video_scanlines);
addSaveFunc([video_scanlines, this] { addSaveFunc([video_scanlines, this] {
if (video_scanlines->getState() != if (video_scanlines->getState() !=
Settings::getInstance()->getBool("MediaViewerVideoScanlines")) { Settings::getInstance()->getBool("MediaViewerVideoScanlines")) {
Settings::getInstance()->setBool("MediaViewerVideoScanlines", Settings::getInstance()->setBool("MediaViewerVideoScanlines",
video_scanlines->getState()); video_scanlines->getState());
setNeedsSaving(); setNeedsSaving();
} }
}); });
@ -60,26 +60,24 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string&
video_blur->setState(Settings::getInstance()->getBool("MediaViewerVideoBlur")); video_blur->setState(Settings::getInstance()->getBool("MediaViewerVideoBlur"));
addWithLabel("RENDER BLUR FOR VIDEOS", video_blur); addWithLabel("RENDER BLUR FOR VIDEOS", video_blur);
addSaveFunc([video_blur, this] { addSaveFunc([video_blur, this] {
if (video_blur->getState() != if (video_blur->getState() != Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
Settings::getInstance()->getBool("MediaViewerVideoBlur")) { Settings::getInstance()->setBool("MediaViewerVideoBlur", video_blur->getState());
Settings::getInstance()->setBool("MediaViewerVideoBlur",
video_blur->getState());
setNeedsSaving(); setNeedsSaving();
} }
}); });
// Render scanlines for screenshots using a shader. // Render scanlines for screenshots using a shader.
auto screenshot_scanlines = std::make_shared<SwitchComponent>(mWindow); auto screenshot_scanlines = std::make_shared<SwitchComponent>(mWindow);
screenshot_scanlines->setState(Settings::getInstance()-> screenshot_scanlines->setState(
getBool("MediaViewerScreenshotScanlines")); Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"));
addWithLabel("RENDER SCANLINES FOR SCREENSHOTS", screenshot_scanlines); addWithLabel("RENDER SCANLINES FOR SCREENSHOTS", screenshot_scanlines);
addSaveFunc([screenshot_scanlines, this] { addSaveFunc([screenshot_scanlines, this] {
if (screenshot_scanlines->getState() != if (screenshot_scanlines->getState() !=
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) { Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) {
Settings::getInstance()->setBool("MediaViewerScreenshotScanlines", Settings::getInstance()->setBool("MediaViewerScreenshotScanlines",
screenshot_scanlines->getState()); screenshot_scanlines->getState());
setNeedsSaving(); setNeedsSaving();
} }
}); });
#endif #endif
} }

File diff suppressed because it is too large Load diff

View file

@ -10,9 +10,9 @@
#ifndef ES_APP_GUIS_GUI_MENU_H #ifndef ES_APP_GUIS_GUI_MENU_H
#define ES_APP_GUIS_GUI_MENU_H #define ES_APP_GUIS_GUI_MENU_H
#include "GuiComponent.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "guis/GuiSettings.h" #include "guis/GuiSettings.h"
#include "GuiComponent.h"
class GuiMenu : public GuiComponent class GuiMenu : public GuiComponent
{ {
@ -27,8 +27,10 @@ public:
private: private:
void close(bool closeAllWindows); void close(bool closeAllWindows);
void addEntry(const std::string& name, unsigned int color, void addEntry(const std::string& name,
bool add_arrow, const std::function<void()>& func); unsigned int color,
bool add_arrow,
const std::function<void()>& func);
void addVersionInfo(); void addVersionInfo();
void openScraperOptions(); void openScraperOptions();

View file

@ -11,6 +11,12 @@
#include "guis/GuiMetaDataEd.h" #include "guis/GuiMetaDataEd.h"
#include "CollectionSystemsManager.h"
#include "FileData.h"
#include "FileFilterIndex.h"
#include "Gamelist.h"
#include "SystemData.h"
#include "Window.h"
#include "components/ButtonComponent.h" #include "components/ButtonComponent.h"
#include "components/ComponentList.h" #include "components/ComponentList.h"
#include "components/DateTimeEditComponent.h" #include "components/DateTimeEditComponent.h"
@ -18,69 +24,63 @@
#include "components/RatingComponent.h" #include "components/RatingComponent.h"
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiGameScraper.h" #include "guis/GuiGameScraper.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "resources/Font.h" #include "resources/Font.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "CollectionSystemsManager.h"
#include "FileData.h"
#include "FileFilterIndex.h"
#include "Gamelist.h"
#include "SystemData.h"
#include "Window.h"
GuiMetaDataEd::GuiMetaDataEd( GuiMetaDataEd::GuiMetaDataEd(Window* window,
Window* window, MetaDataList* md,
MetaDataList* md, const std::vector<MetaDataDecl>& mdd,
const std::vector<MetaDataDecl>& mdd, ScraperSearchParams scraperParams,
ScraperSearchParams scraperParams, const std::string& /*header*/,
const std::string& /*header*/, std::function<void()> saveCallback,
std::function<void()> saveCallback, std::function<void()> clearGameFunc,
std::function<void()> clearGameFunc, std::function<void()> deleteGameFunc)
std::function<void()> deleteGameFunc) : GuiComponent(window)
: GuiComponent(window), , mScraperParams(scraperParams)
mScraperParams(scraperParams), , mBackground(window, ":/graphics/frame.svg")
mBackground(window, ":/graphics/frame.svg"), , mGrid(window, Vector2i(1, 3))
mGrid(window, Vector2i(1, 3)), , mMetaDataDecl(mdd)
mMetaDataDecl(mdd), , mMetaData(md)
mMetaData(md), , mSavedCallback(saveCallback)
mSavedCallback(saveCallback), , mClearGameFunc(clearGameFunc)
mClearGameFunc(clearGameFunc), , mDeleteGameFunc(deleteGameFunc)
mDeleteGameFunc(deleteGameFunc), , mMediaFilesUpdated(false)
mMediaFilesUpdated(false)
{ {
addChild(&mBackground); addChild(&mBackground);
addChild(&mGrid); addChild(&mGrid);
mHeaderGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i(1, 5)); mHeaderGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i(1, 5));
mTitle = std::make_shared<TextComponent>(mWindow, "EDIT METADATA", mTitle = std::make_shared<TextComponent>(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE),
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); 0x555555FF, ALIGN_CENTER);
// Extract possible subfolders from the path. // Extract possible subfolders from the path.
std::string folderPath = Utils::String::replace( std::string folderPath =
Utils::FileSystem::getParent(scraperParams.game->getPath()), Utils::String::replace(Utils::FileSystem::getParent(scraperParams.game->getPath()),
scraperParams.system->getSystemEnvData()->mStartPath, ""); scraperParams.system->getSystemEnvData()->mStartPath, "");
if (folderPath.size() >= 2) { if (folderPath.size() >= 2) {
folderPath.erase(0, 1); folderPath.erase(0, 1);
#if defined(_WIN64) #if defined(_WIN64)
folderPath.push_back('\\'); folderPath.push_back('\\');
folderPath = Utils::String::replace(folderPath, "/", "\\"); folderPath = Utils::String::replace(folderPath, "/", "\\");
#else #else
folderPath.push_back('/'); folderPath.push_back('/');
#endif #endif
} }
mSubtitle = std::make_shared<TextComponent>(mWindow, folderPath + mSubtitle = std::make_shared<TextComponent>(
Utils::FileSystem::getFileName(scraperParams.game->getPath()) + mWindow,
" [" + Utils::String::toUpper(scraperParams.system->getName()) + "]" + folderPath + Utils::FileSystem::getFileName(scraperParams.game->getPath()) + " [" +
Utils::String::toUpper(scraperParams.system->getName()) + "]" +
(scraperParams.game->getType() == FOLDER ? " " + ViewController::FOLDER_CHAR : ""), (scraperParams.game->getType() == FOLDER ? " " + ViewController::FOLDER_CHAR : ""),
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f), Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f),
Vector2f(0.0f, 0.0f), 0x00000000, 0.05f); Vector2f(0.0f, 0.0f), 0x00000000, 0.05f);
mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), false, true); mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), false, true);
mHeaderGrid->setEntry(mSubtitle, Vector2i(0, 3), false, true); mHeaderGrid->setEntry(mSubtitle, Vector2i(0, 3), false, true);
@ -102,27 +102,26 @@ GuiMetaDataEd::GuiMetaDataEd(
// Don't show the launch command override entry if this option has been disabled. // Don't show the launch command override entry if this option has been disabled.
if (!Settings::getInstance()->getBool("LaunchCommandOverride") && if (!Settings::getInstance()->getBool("LaunchCommandOverride") &&
iter->type == MD_LAUNCHCOMMAND) { iter->type == MD_LAUNCHCOMMAND) {
ed = std::make_shared<TextComponent>(window, "", Font::get(FONT_SIZE_SMALL, ed = std::make_shared<TextComponent>(
FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); window, "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT);
assert(ed); assert(ed);
ed->setValue(mMetaData->get(iter->key)); ed->setValue(mMetaData->get(iter->key));
mEditors.push_back(ed); mEditors.push_back(ed);
continue; continue;
} }
// Create ed and add it (and any related components) to mMenu.
// ed's value will be set below.
// It's very important to put the element with the help prompt as the last row // It's very important to put the element with the help prompt as the last row
// entry instead of for instance the spacer. That is so because ComponentList // entry instead of for instance the spacer. That is so because ComponentList
// always looks for the help prompt at the back of the element stack. // always looks for the help prompt at the back of the element stack.
ComponentListRow row; ComponentListRow row;
auto lbl = std::make_shared<TextComponent>(mWindow, auto lbl =
Utils::String::toUpper(iter->displayName), Font::get(FONT_SIZE_SMALL), 0x777777FF); std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(iter->displayName),
Font::get(FONT_SIZE_SMALL), 0x777777FF);
row.addElement(lbl, true); // Label. row.addElement(lbl, true); // Label.
switch (iter->type) { switch (iter->type) {
case MD_BOOL: { case MD_BOOL: {
ed = std::make_shared<SwitchComponent>(window); ed = std::make_shared<SwitchComponent>(window);
// Make the switches slightly smaller. // Make the switches slightly smaller.
auto switchSize = ed->getSize() * 0.9f; auto switchSize = ed->getSize() * 0.9f;
@ -133,9 +132,9 @@ GuiMetaDataEd::GuiMetaDataEd(
row.addElement(ed, false, true); row.addElement(ed, false, true);
break; break;
} }
case MD_RATING: { case MD_RATING: {
auto spacer = std::make_shared<GuiComponent>(mWindow); auto spacer = std::make_shared<GuiComponent>(mWindow);
spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0); spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0.0f);
row.addElement(spacer, false); row.addElement(spacer, false);
ed = std::make_shared<RatingComponent>(window, true); ed = std::make_shared<RatingComponent>(window, true);
@ -145,17 +144,17 @@ GuiMetaDataEd::GuiMetaDataEd(
row.addElement(ed, false, true); row.addElement(ed, false, true);
auto ratingSpacer = std::make_shared<GuiComponent>(mWindow); auto ratingSpacer = std::make_shared<GuiComponent>(mWindow);
ratingSpacer->setSize(Renderer::getScreenWidth() * 0.001f, 0); ratingSpacer->setSize(Renderer::getScreenWidth() * 0.001f, 0.0f);
row.addElement(ratingSpacer, false); row.addElement(ratingSpacer, false);
// Pass input to the actual RatingComponent instead of the spacer. // Pass input to the actual RatingComponent instead of the spacer.
row.input_handler = std::bind(&GuiComponent::input, row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1,
ed.get(), std::placeholders::_1, std::placeholders::_2); std::placeholders::_2);
break; break;
} }
case MD_DATE: { case MD_DATE: {
auto spacer = std::make_shared<GuiComponent>(mWindow); auto spacer = std::make_shared<GuiComponent>(mWindow);
spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0); spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0.0f);
row.addElement(spacer, false); row.addElement(spacer, false);
ed = std::make_shared<DateTimeEditComponent>(window, true); ed = std::make_shared<DateTimeEditComponent>(window, true);
@ -164,29 +163,22 @@ GuiMetaDataEd::GuiMetaDataEd(
row.addElement(ed, false); row.addElement(ed, false);
auto dateSpacer = std::make_shared<GuiComponent>(mWindow); auto dateSpacer = std::make_shared<GuiComponent>(mWindow);
dateSpacer->setSize(Renderer::getScreenWidth() * 0.0035f, 0); dateSpacer->setSize(Renderer::getScreenWidth() * 0.0035f, 0.0f);
row.addElement(dateSpacer, false); row.addElement(dateSpacer, false);
// Pass input to the actual DateTimeEditComponent instead of the spacer. // Pass input to the actual DateTimeEditComponent instead of the spacer.
row.input_handler = std::bind(&GuiComponent::input, ed.get(), row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1,
std::placeholders::_1, std::placeholders::_2); std::placeholders::_2);
break; break;
} }
// Not in use as 'lastplayed' is flagged as statistics and these are skipped. case MD_LAUNCHCOMMAND: {
// Let's still keep the code because it may be needed in the future.
// case MD_TIME: {
// ed = std::make_shared<DateTimeEditComponent>(window,
// DateTimeEditComponent::DISP_RELATIVE_TO_NOW);
// row.addElement(ed, false);
// break;
// }
case MD_LAUNCHCOMMAND: {
ed = std::make_shared<TextComponent>(window, "", ed = std::make_shared<TextComponent>(window, "",
Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT),
0x777777FF, ALIGN_RIGHT);
row.addElement(ed, true); row.addElement(ed, true);
auto spacer = std::make_shared<GuiComponent>(mWindow); auto spacer = std::make_shared<GuiComponent>(mWindow);
spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0); spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f);
row.addElement(spacer, false); row.addElement(spacer, false);
auto bracket = std::make_shared<ImageComponent>(mWindow); auto bracket = std::make_shared<ImageComponent>(mWindow);
@ -207,26 +199,27 @@ GuiMetaDataEd::GuiMetaDataEd(
}; };
std::string staticTextString = "Default value from es_systems.xml:"; std::string staticTextString = "Default value from es_systems.xml:";
std::string defaultLaunchCommand = scraperParams.system-> std::string defaultLaunchCommand =
getSystemEnvData()->mLaunchCommand; scraperParams.system->getSystemEnvData()->mLaunchCommand;
row.makeAcceptInputHandler([this, title, staticTextString, row.makeAcceptInputHandler([this, title, staticTextString, defaultLaunchCommand, ed,
defaultLaunchCommand, ed, updateVal, multiLine] { updateVal, multiLine] {
mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, getHelpStyle(), mWindow->pushGui(new GuiComplexTextEditPopup(
title, staticTextString, defaultLaunchCommand, ed->getValue(), mWindow, getHelpStyle(), title, staticTextString, defaultLaunchCommand,
updateVal, multiLine, "APPLY", "APPLY CHANGES?")); ed->getValue(), updateVal, multiLine, "APPLY", "APPLY CHANGES?"));
}); });
break; break;
} }
case MD_MULTILINE_STRING: case MD_MULTILINE_STRING:
default: { default: {
// MD_STRING. // MD_STRING.
ed = std::make_shared<TextComponent>(window, "", Font::get(FONT_SIZE_SMALL, ed = std::make_shared<TextComponent>(window, "",
FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT),
0x777777FF, ALIGN_RIGHT);
row.addElement(ed, true); row.addElement(ed, true);
auto spacer = std::make_shared<GuiComponent>(mWindow); auto spacer = std::make_shared<GuiComponent>(mWindow);
spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0); spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f);
row.addElement(spacer, false); row.addElement(spacer, false);
auto bracket = std::make_shared<ImageComponent>(mWindow); auto bracket = std::make_shared<ImageComponent>(mWindow);
@ -239,9 +232,9 @@ GuiMetaDataEd::GuiMetaDataEd(
gamePath = Utils::FileSystem::getStem(mScraperParams.game->getPath()); gamePath = Utils::FileSystem::getStem(mScraperParams.game->getPath());
// OK callback (apply new value to ed). // OK callback (apply new value to ed).
auto updateVal = [ed, currentKey, originalValue, gamePath] auto updateVal = [ed, currentKey, originalValue,
(const std::string& newVal) { gamePath](const std::string& newVal) {
// If the user has entered a blank game name, then set the name to the ROM // If the user has entered a blank game name, then set the name to the ROM
// filename (minus the extension). // filename (minus the extension).
if (currentKey == "name" && newVal == "") { if (currentKey == "name" && newVal == "") {
@ -251,27 +244,28 @@ GuiMetaDataEd::GuiMetaDataEd(
else else
ed->setColor(TEXTCOLOR_USERMARKED); ed->setColor(TEXTCOLOR_USERMARKED);
} }
else if (newVal == "" && (currentKey == "developer" || else if (newVal == "" &&
currentKey == "publisher" || currentKey == "genre" || (currentKey == "developer" || currentKey == "publisher" ||
currentKey == "players")) { currentKey == "genre" || currentKey == "players")) {
ed->setValue("unknown"); ed->setValue("unknown");
if (originalValue == "unknown") if (originalValue == "unknown")
ed->setColor(DEFAULT_TEXTCOLOR); ed->setColor(DEFAULT_TEXTCOLOR);
else else
ed->setColor(TEXTCOLOR_USERMARKED); ed->setColor(TEXTCOLOR_USERMARKED);
} }
else { else {
ed->setValue(newVal); ed->setValue(newVal);
if (newVal == originalValue) if (newVal == originalValue)
ed->setColor(DEFAULT_TEXTCOLOR); ed->setColor(DEFAULT_TEXTCOLOR);
else else
ed->setColor(TEXTCOLOR_USERMARKED); ed->setColor(TEXTCOLOR_USERMARKED);
} }
}; };
row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] { row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title, mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title,
ed->getValue(), updateVal, multiLine, "APPLY", "APPLY CHANGES?")); ed->getValue(), updateVal, multiLine,
"APPLY", "APPLY CHANGES?"));
}); });
break; break;
} }
@ -286,8 +280,8 @@ GuiMetaDataEd::GuiMetaDataEd(
std::vector<std::shared_ptr<ButtonComponent>> buttons; std::vector<std::shared_ptr<ButtonComponent>> buttons;
if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SCRAPE", "scrape", buttons.push_back(std::make_shared<ButtonComponent>(
std::bind(&GuiMetaDataEd::fetch, this))); mWindow, "SCRAPE", "scrape", std::bind(&GuiMetaDataEd::fetch, this)));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SAVE", "save metadata", [&] { buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SAVE", "save metadata", [&] {
save(); save();
@ -295,49 +289,60 @@ GuiMetaDataEd::GuiMetaDataEd(
delete this; delete this;
})); }));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "cancel changes", buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "cancel changes",
[&] { delete this; })); [&] { delete this; }));
if (scraperParams.game->getType() == FOLDER) { if (scraperParams.game->getType() == FOLDER) {
if (mClearGameFunc) { if (mClearGameFunc) {
auto clearSelf = [&] { mClearGameFunc(); delete this; }; auto clearSelf = [&] {
mClearGameFunc();
delete this;
};
auto clearSelfBtnFunc = [this, clearSelf] { auto clearSelfBtnFunc = [this, clearSelf] {
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
"THIS WILL DELETE ANY MEDIA FILES AND\n" "THIS WILL DELETE ANY MEDIA FILES AND\n"
"THE GAMELIST.XML ENTRY FOR THIS FOLDER,\n" "THE GAMELIST.XML ENTRY FOR THIS FOLDER,\n"
"BUT NEITHER THE FOLDER ITSELF OR ANY\n" "BUT NEITHER THE FOLDER ITSELF OR ANY\n"
"CONTENT INSIDE IT WILL BE REMOVED\n" "CONTENT INSIDE IT WILL BE REMOVED\n"
"ARE YOU SURE?", "ARE YOU SURE?",
"YES", clearSelf, "NO", nullptr)); }; "YES", clearSelf, "NO", nullptr));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", };
"clear folder", clearSelfBtnFunc)); buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear folder",
clearSelfBtnFunc));
} }
} }
else { else {
if (mClearGameFunc) { if (mClearGameFunc) {
auto clearSelf = [&] { mClearGameFunc(); delete this; }; auto clearSelf = [&] {
mClearGameFunc();
delete this;
};
auto clearSelfBtnFunc = [this, clearSelf] { auto clearSelfBtnFunc = [this, clearSelf] {
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
"THIS WILL DELETE ANY MEDIA FILES\n" "THIS WILL DELETE ANY MEDIA FILES\n"
"AND THE GAMELIST.XML ENTRY FOR\n" "AND THE GAMELIST.XML ENTRY FOR\n"
"THIS GAME, BUT THE GAME FILE\n" "THIS GAME, BUT THE GAME FILE\n"
"ITSELF WILL NOT BE REMOVED\n" "ITSELF WILL NOT BE REMOVED\n"
"ARE YOU SURE?", "ARE YOU SURE?",
"YES", clearSelf, "NO", nullptr)); }; "YES", clearSelf, "NO", nullptr));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", };
"clear file", clearSelfBtnFunc)); buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear file",
clearSelfBtnFunc));
} }
if (mDeleteGameFunc) { if (mDeleteGameFunc) {
auto deleteFilesAndSelf = [&] { mDeleteGameFunc(); delete this; }; auto deleteFilesAndSelf = [&] {
mDeleteGameFunc();
delete this;
};
auto deleteGameBtnFunc = [this, deleteFilesAndSelf] { auto deleteGameBtnFunc = [this, deleteFilesAndSelf] {
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
"THIS WILL DELETE THE GAME\n" "THIS WILL DELETE THE GAME\n"
"FILE, ANY MEDIA FILES AND\n" "FILE, ANY MEDIA FILES AND\n"
"THE GAMELIST.XML ENTRY\n" "THE GAMELIST.XML ENTRY\n"
"ARE YOU SURE?", "ARE YOU SURE?",
"YES", deleteFilesAndSelf, "NO", nullptr)); }; "YES", deleteFilesAndSelf, "NO", nullptr));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "DELETE", };
"delete game", deleteGameBtnFunc)); buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "DELETE", "delete game",
deleteGameBtnFunc));
} }
} }
@ -345,11 +350,12 @@ GuiMetaDataEd::GuiMetaDataEd(
mGrid.setEntry(mButtons, Vector2i(0, 2), true, false); mGrid.setEntry(mButtons, Vector2i(0, 2), true, false);
// Resize + center. // Resize + center.
float width = static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() * float width =
1.05f), static_cast<int>(Renderer::getScreenWidth() * 0.90f))); static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() * 1.05f),
static_cast<int>(Renderer::getScreenWidth() * 0.90f)));
setSize(width, Renderer::getScreenHeight() * 0.83f); setSize(width, Renderer::getScreenHeight() * 0.83f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
(Renderer::getScreenHeight() - mSize.y()) / 2); (Renderer::getScreenHeight() - mSize.y()) / 2.0f);
} }
void GuiMetaDataEd::onSizeChanged() void GuiMetaDataEd::onSizeChanged()
@ -360,8 +366,8 @@ void GuiMetaDataEd::onSizeChanged()
const float subtitleHeight = mSubtitle->getFont()->getLetterHeight(); const float subtitleHeight = mSubtitle->getFont()->getLetterHeight();
const float titleSubtitleSpacing = mSize.y() * 0.03f; const float titleSubtitleSpacing = mSize.y() * 0.03f;
mGrid.setRowHeightPerc(0, (titleHeight + titleSubtitleSpacing + subtitleHeight + mGrid.setRowHeightPerc(
TITLE_VERT_PADDING) / mSize.y()); 0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / mSize.y());
mGrid.setRowHeightPerc(2, mButtons->getSize().y() / mSize.y()); mGrid.setRowHeightPerc(2, mButtons->getSize().y() / mSize.y());
// Snap list size to the row height to prevent a fraction of a row from being displayed. // Snap list size to the row height to prevent a fraction of a row from being displayed.
@ -383,7 +389,7 @@ void GuiMetaDataEd::onSizeChanged()
mList->setSize(mList->getSize().x(), listHeight); mList->setSize(mList->getSize().x(), listHeight);
Vector2f newWindowSize = mSize; Vector2f newWindowSize = mSize;
newWindowSize.y() -= heightAdjustment; newWindowSize.y() -= heightAdjustment;
mBackground.fitTo(newWindowSize, Vector3f::Zero(), Vector2f(-32, -32)); mBackground.fitTo(newWindowSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
// Move the buttons up as well to make the layout align correctly after the resize. // Move the buttons up as well to make the layout align correctly after the resize.
Vector3f newButtonPos = mButtons->getPosition(); Vector3f newButtonPos = mButtons->getPosition();
@ -411,13 +417,13 @@ void GuiMetaDataEd::save()
continue; continue;
if (!showHiddenGames && mMetaDataDecl.at(i).key == "hidden" && if (!showHiddenGames && mMetaDataDecl.at(i).key == "hidden" &&
mEditors.at(i)->getValue() != mMetaData->get("hidden")) mEditors.at(i)->getValue() != mMetaData->get("hidden"))
hideGameWhileHidden = true; hideGameWhileHidden = true;
// Check whether the flag to count the entry as a game was set to enabled. // Check whether the flag to count the entry as a game was set to enabled.
if (mMetaDataDecl.at(i).key == "nogamecount" && if (mMetaDataDecl.at(i).key == "nogamecount" &&
mEditors.at(i)->getValue() != mMetaData->get("nogamecount") && mEditors.at(i)->getValue() != mMetaData->get("nogamecount") &&
mMetaData->get("nogamecount") == "true") { mMetaData->get("nogamecount") == "true") {
setGameAsCounted = true; setGameAsCounted = true;
} }
@ -450,7 +456,7 @@ void GuiMetaDataEd::save()
for (FileData* child : mScraperParams.game->getChildrenRecursive()) { for (FileData* child : mScraperParams.game->getChildrenRecursive()) {
if (!child->getHidden()) if (!child->getHidden())
child->metadata.set("hidden", "true"); child->metadata.set("hidden", "true");
hideGames.push_back(child); hideGames.push_back(child);
} }
} }
else { else {
@ -501,8 +507,8 @@ void GuiMetaDataEd::save()
void GuiMetaDataEd::fetch() void GuiMetaDataEd::fetch()
{ {
GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, GuiGameScraper* scr = new GuiGameScraper(
std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1));
mWindow->pushGui(scr); mWindow->pushGui(scr);
} }
@ -514,10 +520,6 @@ void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result)
mMediaFilesUpdated = result.savedNewMedia; mMediaFilesUpdated = result.savedNewMedia;
// Curently disabled as I'm not sure if this is more annoying than helpful.
// // Select the Save button.
// mButtons->moveCursor(Vector2i(1, 0));
// Check if any values were manually changed before starting the scraping. // Check if any values were manually changed before starting the scraping.
// If so, it's these values we should compare against when scraping, not // If so, it's these values we should compare against when scraping, not
// the values previously saved for the game. // the values previously saved for the game.
@ -561,42 +563,31 @@ void GuiMetaDataEd::close()
} }
} }
// Keep code for potential future use.
// std::function<void()> closeFunc;
// if (!closeAllWindows) {
// closeFunc = [this] { delete this; };
// }
// else {
// Window* window = mWindow;
// closeFunc = [window, this] {
// while (window->peekGui() != ViewController::get())
// delete window->peekGui();
// };
// }
std::function<void()> closeFunc; std::function<void()> closeFunc;
closeFunc = [this] { closeFunc = [this] {
if (mMediaFilesUpdated) { if (mMediaFilesUpdated) {
// Always reload the gamelist if media files were updated, even if the user // Always reload the gamelist if media files were updated, even if the user
// chose to not save any metadata changes. Also manually unload the game image // chose to not save any metadata changes. Also manually unload the game image
// and marquee, as otherwise they would not get updated until the user scrolls up // and marquee, as otherwise they would not get updated until the user scrolls up
// and down the gamelist. // and down the gamelist.
TextureResource::manualUnload(mScraperParams.game->getImagePath(), false); TextureResource::manualUnload(mScraperParams.game->getImagePath(), false);
TextureResource::manualUnload(mScraperParams.game->getMarqueePath(), false); TextureResource::manualUnload(mScraperParams.game->getMarqueePath(), false);
ViewController::get()->reloadGameListView(mScraperParams.system); ViewController::get()->reloadGameListView(mScraperParams.system);
mWindow->invalidateCachedBackground(); mWindow->invalidateCachedBackground();
} }
ViewController::get()->onPauseVideo(); ViewController::get()->onPauseVideo();
delete this; delete this;
}; };
if (metadataUpdated) { if (metadataUpdated) {
// Changes were made, ask if the user wants to save them. // Changes were made, ask if the user wants to save them.
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(
"SAVE CHANGES?", mWindow, getHelpStyle(), "SAVE CHANGES?", "YES",
"YES", [this, closeFunc] { save(); closeFunc(); }, [this, closeFunc] {
"NO", closeFunc save();
)); closeFunc();
},
"NO", closeFunc));
} }
else { else {
// Always save if the media files have been changed (i.e. newly scraped images). // Always save if the media files have been changed (i.e. newly scraped images).

View file

@ -12,11 +12,11 @@
#ifndef ES_APP_GUIS_GUI_META_DATA_ED_H #ifndef ES_APP_GUIS_GUI_META_DATA_ED_H
#define ES_APP_GUIS_GUI_META_DATA_ED_H #define ES_APP_GUIS_GUI_META_DATA_ED_H
#include "GuiComponent.h"
#include "MetaData.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
#include "components/NinePatchComponent.h" #include "components/NinePatchComponent.h"
#include "scrapers/Scraper.h" #include "scrapers/Scraper.h"
#include "GuiComponent.h"
#include "MetaData.h"
class ComponentList; class ComponentList;
class TextComponent; class TextComponent;
@ -24,14 +24,14 @@ class TextComponent;
class GuiMetaDataEd : public GuiComponent class GuiMetaDataEd : public GuiComponent
{ {
public: public:
GuiMetaDataEd( GuiMetaDataEd(Window* window,
Window* window, MetaDataList* md,
MetaDataList* md, const std::vector<MetaDataDecl>&mdd, const std::vector<MetaDataDecl>& mdd,
ScraperSearchParams params, ScraperSearchParams params,
const std::string& header, const std::string& header,
std::function<void()> savedCallback, std::function<void()> savedCallback,
std::function<void()> clearGameFunc, std::function<void()> clearGameFunc,
std::function<void()> deleteGameFunc); std::function<void()> deleteGameFunc);
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void onSizeChanged() override; void onSizeChanged() override;

View file

@ -9,17 +9,15 @@
#include "guis/GuiOfflineGenerator.h" #include "guis/GuiOfflineGenerator.h"
#include "SystemData.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "SystemData.h"
GuiOfflineGenerator::GuiOfflineGenerator( GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue<FileData*>& gameQueue)
Window* window, : GuiComponent(window)
const std::queue<FileData*>& gameQueue) , mBackground(window, ":/graphics/frame.svg")
: GuiComponent(window), , mGrid(window, Vector2i(6, 13))
mBackground(window, ":/graphics/frame.svg"), , mGameQueue(gameQueue)
mGrid(window, Vector2i(6, 13)),
mGameQueue(gameQueue)
{ {
addChild(&mBackground); addChild(&mBackground);
addChild(&mGrid); addChild(&mGrid);
@ -39,140 +37,139 @@ GuiOfflineGenerator::GuiOfflineGenerator(
// Header. // Header.
mTitle = std::make_shared<TextComponent>(mWindow, "MIXIMAGE OFFLINE GENERATOR", mTitle = std::make_shared<TextComponent>(mWindow, "MIXIMAGE OFFLINE GENERATOR",
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(6, 1)); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(6, 1));
mStatus = std::make_shared<TextComponent>(mWindow, "NOT STARTED", mStatus = std::make_shared<TextComponent>(mWindow, "NOT STARTED", Font::get(FONT_SIZE_MEDIUM),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); 0x777777FF, ALIGN_CENTER);
mGrid.setEntry(mStatus, Vector2i(0, 1), false, true, Vector2i(6, 1)); mGrid.setEntry(mStatus, Vector2i(0, 1), false, true, Vector2i(6, 1));
mGameCounter = std::make_shared<TextComponent>(mWindow, mGameCounter = std::make_shared<TextComponent>(
std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + mWindow,
std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) +
(mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED", (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED",
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
mGrid.setEntry(mGameCounter, Vector2i(0, 2), false, true, Vector2i(6, 1)); mGrid.setEntry(mGameCounter, Vector2i(0, 2), false, true, Vector2i(6, 1));
// Spacer row with top border. // Spacer row with top border.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 3), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 3), false, false,
false, false, Vector2i(6, 1), GridFlags::BORDER_TOP); Vector2i(6, 1), GridFlags::BORDER_TOP);
// Left spacer. // Left spacer.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 4), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 4), false, false,
false, false, Vector2i(1, 7)); Vector2i(1, 7));
// Generated label. // Generated label.
mGeneratedLbl = std::make_shared<TextComponent>(mWindow, "Generated:", mGeneratedLbl = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mWindow, "Generated:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mGeneratedLbl, Vector2i(1, 4), false, true, Vector2i(1, 1)); mGrid.setEntry(mGeneratedLbl, Vector2i(1, 4), false, true, Vector2i(1, 1));
// Generated value/counter. // Generated value/counter.
mGeneratedVal = std::make_shared<TextComponent>(mWindow, mGeneratedVal =
std::to_string(mGamesProcessed), std::make_shared<TextComponent>(mWindow, std::to_string(mGamesProcessed),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mGeneratedVal, Vector2i(2, 4), false, true, Vector2i(1, 1)); mGrid.setEntry(mGeneratedVal, Vector2i(2, 4), false, true, Vector2i(1, 1));
// Overwritten label. // Overwritten label.
mOverwrittenLbl = std::make_shared<TextComponent>(mWindow, "Overwritten:", mOverwrittenLbl = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mWindow, "Overwritten:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mOverwrittenLbl, Vector2i(1, 5), false, true, Vector2i(1, 1)); mGrid.setEntry(mOverwrittenLbl, Vector2i(1, 5), false, true, Vector2i(1, 1));
// Overwritten value/counter. // Overwritten value/counter.
mOverwrittenVal = std::make_shared<TextComponent>(mWindow, mOverwrittenVal =
std::to_string(mImagesOverwritten), std::make_shared<TextComponent>(mWindow, std::to_string(mImagesOverwritten),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mOverwrittenVal, Vector2i(2, 5), false, true, Vector2i(1, 1)); mGrid.setEntry(mOverwrittenVal, Vector2i(2, 5), false, true, Vector2i(1, 1));
// Skipping label. // Skipping label.
mSkippedLbl = std::make_shared<TextComponent>(mWindow, "Skipped (existing):", mSkippedLbl = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mWindow, "Skipped (existing):", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mSkippedLbl, Vector2i(1, 6), false, true, Vector2i(1, 1)); mGrid.setEntry(mSkippedLbl, Vector2i(1, 6), false, true, Vector2i(1, 1));
// Skipping value/counter. // Skipping value/counter.
mSkippedVal= std::make_shared<TextComponent>(mWindow, mSkippedVal = std::make_shared<TextComponent>(
std::to_string(mGamesSkipped), mWindow, std::to_string(mGamesSkipped), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mSkippedVal, Vector2i(2, 6), false, true, Vector2i(1, 1)); mGrid.setEntry(mSkippedVal, Vector2i(2, 6), false, true, Vector2i(1, 1));
// Failed label. // Failed label.
mFailedLbl = std::make_shared<TextComponent>(mWindow, "Failed:", mFailedLbl = std::make_shared<TextComponent>(mWindow, "Failed:", Font::get(FONT_SIZE_SMALL),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mFailedLbl, Vector2i(1, 7), false, true, Vector2i(1, 1)); mGrid.setEntry(mFailedLbl, Vector2i(1, 7), false, true, Vector2i(1, 1));
// Failed value/counter. // Failed value/counter.
mFailedVal = std::make_shared<TextComponent>(mWindow, mFailedVal = std::make_shared<TextComponent>(
std::to_string(mGamesFailed), mWindow, std::to_string(mGamesFailed), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mFailedVal, Vector2i(2, 7), false, true, Vector2i(1, 1)); mGrid.setEntry(mFailedVal, Vector2i(2, 7), false, true, Vector2i(1, 1));
// Processing label. // Processing label.
mProcessingLbl = std::make_shared<TextComponent>(mWindow, "Processing: ", mProcessingLbl = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mWindow, "Processing: ", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mProcessingLbl, Vector2i(3, 4), false, true, Vector2i(1, 1)); mGrid.setEntry(mProcessingLbl, Vector2i(3, 4), false, true, Vector2i(1, 1));
// Processing value. // Processing value.
mProcessingVal = std::make_shared<TextComponent>(mWindow, "", mProcessingVal = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mProcessingVal, Vector2i(4, 4), false, true, Vector2i(1, 1)); mGrid.setEntry(mProcessingVal, Vector2i(4, 4), false, true, Vector2i(1, 1));
// Spacer row. // Spacer row.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 8), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 8), false, false,
false, false, Vector2i(4, 1)); Vector2i(4, 1));
// Last error message label. // Last error message label.
mLastErrorLbl = std::make_shared<TextComponent>(mWindow, "Last error message:", mLastErrorLbl = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mWindow, "Last error message:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mLastErrorLbl, Vector2i(1, 9), false, true, Vector2i(4, 1)); mGrid.setEntry(mLastErrorLbl, Vector2i(1, 9), false, true, Vector2i(4, 1));
// Last error message value. // Last error message value.
mLastErrorVal = std::make_shared<TextComponent>(mWindow, "", mLastErrorVal = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mLastErrorVal, Vector2i(1, 10), false, true, Vector2i(4, 1)); mGrid.setEntry(mLastErrorVal, Vector2i(1, 10), false, true, Vector2i(4, 1));
// Right spacer. // Right spacer.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(5, 4), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(5, 4), false, false,
false, false, Vector2i(1, 7)); Vector2i(1, 7));
// Spacer row with bottom border. // Spacer row with bottom border.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 11), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 11), false, false,
false, false, Vector2i(6, 1), GridFlags::BORDER_BOTTOM); Vector2i(6, 1), GridFlags::BORDER_BOTTOM);
// Buttons. // Buttons.
std::vector<std::shared_ptr<ButtonComponent>> buttons; std::vector<std::shared_ptr<ButtonComponent>> buttons;
mStartPauseButton = std::make_shared<ButtonComponent>(mWindow, "START", mStartPauseButton =
"start processing", [this](){ std::make_shared<ButtonComponent>(mWindow, "START", "start processing", [this]() {
if (!mProcessing) { if (!mProcessing) {
mProcessing = true; mProcessing = true;
mPaused = false; mPaused = false;
mStartPauseButton->setText("PAUSE", "pause processing"); mStartPauseButton->setText("PAUSE", "pause processing");
mCloseButton->setText("CLOSE", "close (abort processing)"); mCloseButton->setText("CLOSE", "close (abort processing)");
mStatus->setText("RUNNING..."); mStatus->setText("RUNNING...");
if (mGamesProcessed == 0) { if (mGamesProcessed == 0) {
LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games"; LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games";
}
} }
} else {
else { if (mMiximageGeneratorThread.joinable())
if (mMiximageGeneratorThread.joinable()) mMiximageGeneratorThread.join();
mMiximageGeneratorThread.join(); mPaused = true;
mPaused = true; update(1);
update(1); mProcessing = false;
mProcessing = false; this->mStartPauseButton->setText("START", "start processing");
this->mStartPauseButton->setText("START", "start processing"); this->mCloseButton->setText("CLOSE", "close (abort processing)");
this->mCloseButton->setText("CLOSE", "close (abort processing)"); mStatus->setText("PAUSED");
mStatus->setText("PAUSED"); }
} });
});
buttons.push_back(mStartPauseButton); buttons.push_back(mStartPauseButton);
mCloseButton = std::make_shared<ButtonComponent>(mWindow, "CLOSE", "close", [this](){ mCloseButton = std::make_shared<ButtonComponent>(mWindow, "CLOSE", "close", [this]() {
if (mGamesProcessed != 0 && mGamesProcessed != mTotalGames) { if (mGamesProcessed != 0 && mGamesProcessed != mTotalGames) {
LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " << LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " << mGamesProcessed
mGamesProcessed << (mGamesProcessed == 1 ? " game (" : " games (") << << (mGamesProcessed == 1 ? " game (" : " games (") << mImagesGenerated
mImagesGenerated << (mImagesGenerated == 1 ? " image " : " images ") << << (mImagesGenerated == 1 ? " image " : " images ") << "generated, "
"generated, " << mGamesSkipped << << mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ")
(mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << mGamesFailed << << "skipped, " << mGamesFailed
(mGamesFailed == 1 ? " game " : " games ") << "failed)"; << (mGamesFailed == 1 ? " game " : " games ") << "failed)";
} }
delete this; delete this;
}); });
@ -184,12 +181,12 @@ GuiOfflineGenerator::GuiOfflineGenerator(
// For narrower displays (e.g. in 4:3 ratio), allow the window to fill 95% of the screen // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 95% of the screen
// width rather than the 85% allowed for wider displays. // width rather than the 85% allowed for wider displays.
float width = Renderer::getScreenWidth() * float width =
((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f); Renderer::getScreenWidth() * ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f);
setSize(width, Renderer::getScreenHeight() * 0.75f); setSize(width, Renderer::getScreenHeight() * 0.75f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
(Renderer::getScreenHeight() - mSize.y()) / 2.0f); (Renderer::getScreenHeight() - mSize.y()) / 2.0f);
} }
GuiOfflineGenerator::~GuiOfflineGenerator() GuiOfflineGenerator::~GuiOfflineGenerator()
@ -281,12 +278,12 @@ void GuiOfflineGenerator::update(int deltaTime)
mGame = mGameQueue.front(); mGame = mGameQueue.front();
mGameQueue.pop(); mGameQueue.pop();
mGameName = mGame->getName() + " [" + mGameName =
Utils::String::toUpper(mGame->getSystem()->getName()) + "]"; mGame->getName() + " [" + Utils::String::toUpper(mGame->getSystem()->getName()) + "]";
mProcessingVal->setText(mGameName); mProcessingVal->setText(mGameName);
if (!Settings::getInstance()->getBool("MiximageOverwrite") && if (!Settings::getInstance()->getBool("MiximageOverwrite") &&
mGame->getMiximagePath() != "") { mGame->getMiximagePath() != "") {
mGamesProcessed++; mGamesProcessed++;
mGamesSkipped++; mGamesSkipped++;
mSkippedVal->setText(std::to_string(mGamesSkipped)); mSkippedVal->setText(std::to_string(mGamesSkipped));
@ -303,14 +300,14 @@ void GuiOfflineGenerator::update(int deltaTime)
mGeneratorFuture = mGeneratorPromise.get_future(); mGeneratorFuture = mGeneratorPromise.get_future();
mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread, mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread,
mMiximageGenerator.get(), &mGeneratorPromise); mMiximageGenerator.get(), &mGeneratorPromise);
} }
} }
// Update the statistics. // Update the statistics.
mStatus->setText("RUNNING..."); mStatus->setText("RUNNING...");
mGameCounter->setText(std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + mGameCounter->setText(std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) +
(mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED"); (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED");
mGeneratedVal->setText(std::to_string(mImagesGenerated)); mGeneratedVal->setText(std::to_string(mImagesGenerated));
mFailedVal->setText(std::to_string(mGamesFailed)); mFailedVal->setText(std::to_string(mGamesFailed));
@ -319,13 +316,13 @@ void GuiOfflineGenerator::update(int deltaTime)
if (mGamesProcessed == mTotalGames) { if (mGamesProcessed == mTotalGames) {
mStatus->setText("COMPLETED"); mStatus->setText("COMPLETED");
mStartPauseButton->setText("DONE", "done (close)"); mStartPauseButton->setText("DONE", "done (close)");
mStartPauseButton->setPressedFunc([this](){ delete this; }); mStartPauseButton->setPressedFunc([this]() { delete this; });
mCloseButton->setText("CLOSE", "close"); mCloseButton->setText("CLOSE", "close");
mProcessingVal->setText(""); mProcessingVal->setText("");
LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated << LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated
(mImagesGenerated == 1 ? " image " : " images ") << "generated, " << << (mImagesGenerated == 1 ? " image " : " images ") << "generated, "
mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << << mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, "
mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)"; << mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)";
mProcessing = false; mProcessing = false;
} }
} }

View file

@ -10,10 +10,10 @@
#ifndef ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H #ifndef ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H
#define ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H #define ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H
#include "components/ButtonComponent.h"
#include "components/ComponentGrid.h"
#include "GuiComponent.h" #include "GuiComponent.h"
#include "MiximageGenerator.h" #include "MiximageGenerator.h"
#include "components/ButtonComponent.h"
#include "components/ComponentGrid.h"
#include <queue> #include <queue>

View file

@ -10,23 +10,23 @@
#include "guis/GuiScraperMenu.h" #include "guis/GuiScraperMenu.h"
#include "FileData.h"
#include "FileSorts.h"
#include "SystemData.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiOfflineGenerator.h" #include "guis/GuiOfflineGenerator.h"
#include "guis/GuiScraperMulti.h" #include "guis/GuiScraperMulti.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "FileData.h"
#include "FileSorts.h"
#include "SystemData.h"
GuiScraperMenu::GuiScraperMenu(Window* window, std::string title) GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
: GuiComponent(window), mMenu(window, title) : GuiComponent(window)
, mMenu(window, title)
{ {
// Scraper service. // Scraper service.
mScraper = std::make_shared<OptionListComponent<std::string>> mScraper = std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(),
(mWindow, getHelpStyle(), "SCRAPE FROM", false); "SCRAPE FROM", false);
std::vector<std::string> scrapers = getScraperList(); std::vector<std::string> scrapers = getScraperList();
// Select either the first entry or the one read from the settings, // Select either the first entry or the one read from the settings,
// just in case the scraper from settings has vanished. // just in case the scraper from settings has vanished.
@ -36,22 +36,50 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
// Search filters, getSearches() will generate a queue of games to scrape // Search filters, getSearches() will generate a queue of games to scrape
// based on the outcome of the checks below. // based on the outcome of the checks below.
mFilters = std::make_shared< OptionListComponent<GameFilterFunc>> mFilters = std::make_shared<OptionListComponent<GameFilterFunc>>(mWindow, getHelpStyle(),
(mWindow, getHelpStyle(), "SCRAPE THESE GAMES", false); "SCRAPE THESE GAMES", false);
mFilters->add("ALL GAMES", [](SystemData*, FileData*) -> bool { return true; }, false); mFilters->add(
mFilters->add("FAVORITE GAMES", [](SystemData*, FileData* g) -> bool { "ALL GAMES",
return g->getFavorite(); }, false); [](SystemData*, FileData*) -> bool {
mFilters->add("NO METADATA", [](SystemData*, FileData* g) -> bool { // All games.
return g->metadata.get("desc").empty(); }, false); return true;
mFilters->add("NO GAME IMAGE", },
[](SystemData*, FileData* g) -> bool { false);
return g->getImagePath().empty(); }, false); mFilters->add(
mFilters->add("NO GAME VIDEO", "FAVORITE GAMES",
[](SystemData*, FileData* g) -> bool { [](SystemData*, FileData* g) -> bool {
return g->getVideoPath().empty(); }, false); // Favorite games.
mFilters->add("FOLDERS ONLY", return g->getFavorite();
[](SystemData*, FileData* g) -> bool { },
return g->getType() == FOLDER; }, false); false);
mFilters->add(
"NO METADATA",
[](SystemData*, FileData* g) -> bool {
// No metadata.
return g->metadata.get("desc").empty();
},
false);
mFilters->add(
"NO GAME IMAGE",
[](SystemData*, FileData* g) -> bool {
// No game image.
return g->getImagePath().empty();
},
false);
mFilters->add(
"NO GAME VIDEO",
[](SystemData*, FileData* g) -> bool {
// No game video.
return g->getVideoPath().empty();
},
false);
mFilters->add(
"FOLDERS ONLY",
[](SystemData*, FileData* g) -> bool {
// Folders only.
return g->getType() == FOLDER;
},
false);
mFilters->selectEntry(Settings::getInstance()->getInt("ScraperFilter")); mFilters->selectEntry(Settings::getInstance()->getInt("ScraperFilter"));
mMenu.addWithLabel("SCRAPE THESE GAMES", mFilters); mMenu.addWithLabel("SCRAPE THESE GAMES", mFilters);
@ -68,20 +96,20 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
}); });
// Add systems (all systems with an existing platform ID are listed). // Add systems (all systems with an existing platform ID are listed).
mSystems = std::make_shared< OptionListComponent<SystemData*>> mSystems = std::make_shared<OptionListComponent<SystemData*>>(mWindow, getHelpStyle(),
(mWindow, getHelpStyle(), "SCRAPE THESE SYSTEMS", true); "SCRAPE THESE SYSTEMS", true);
for (unsigned int i = 0; i < SystemData::sSystemVector.size(); i++) { for (unsigned int i = 0; i < SystemData::sSystemVector.size(); i++) {
if (!SystemData::sSystemVector[i]->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) { if (!SystemData::sSystemVector[i]->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) {
mSystems->add(SystemData::sSystemVector[i]->getFullName(), mSystems->add(SystemData::sSystemVector[i]->getFullName(), SystemData::sSystemVector[i],
SystemData::sSystemVector[i], !SystemData::sSystemVector[i]->getPlatformIds().empty());
!SystemData::sSystemVector[i]->getPlatformIds().empty()); SystemData::sSystemVector[i]->getScrapeFlag() ? mSystems->selectEntry(i) :
SystemData::sSystemVector[i]->getScrapeFlag() ? mSystems->unselectEntry(i);
mSystems->selectEntry(i) : mSystems->unselectEntry(i);
} }
} }
mMenu.addWithLabel("SCRAPE THESE SYSTEMS", mSystems); mMenu.addWithLabel("SCRAPE THESE SYSTEMS", mSystems);
addEntry("ACCOUNT SETTINGS", 0x777777FF, true, [this] { addEntry("ACCOUNT SETTINGS", 0x777777FF, true, [this] {
// Open the account options menu.
openAccountOptions(); openAccountOptions();
}); });
addEntry("CONTENT SETTINGS", 0x777777FF, true, [this] { addEntry("CONTENT SETTINGS", 0x777777FF, true, [this] {
@ -93,6 +121,7 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
openContentOptions(); openContentOptions();
}); });
addEntry("MIXIMAGE SETTINGS", 0x777777FF, true, [this] { addEntry("MIXIMAGE SETTINGS", 0x777777FF, true, [this] {
// Open the miximage options menu.
openMiximageOptions(); openMiximageOptions();
}); });
addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { addEntry("OTHER SETTINGS", 0x777777FF, true, [this] {
@ -111,8 +140,8 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
setSize(mMenu.getSize()); setSize(mMenu.getSize());
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
Renderer::getScreenHeight() * 0.13f); Renderer::getScreenHeight() * 0.13f);
} }
GuiScraperMenu::~GuiScraperMenu() GuiScraperMenu::~GuiScraperMenu()
@ -120,9 +149,9 @@ GuiScraperMenu::~GuiScraperMenu()
// Save the scrape flags to the system settings so that they are // Save the scrape flags to the system settings so that they are
// remembered throughout the program session. // remembered throughout the program session.
std::vector<SystemData*> sys = mSystems->getSelectedObjects(); std::vector<SystemData*> sys = mSystems->getSelectedObjects();
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
(*it)->setScrapeFlag(false); (*it)->setScrapeFlag(false);
for (auto it_sys = sys.cbegin(); it_sys != sys.cend(); it_sys++) { for (auto it_sys = sys.cbegin(); it_sys != sys.cend(); it_sys++) {
if ((*it)->getFullName() == (*it_sys)->getFullName()) if ((*it)->getFullName() == (*it_sys)->getFullName())
(*it)->setScrapeFlag(true); (*it)->setScrapeFlag(true);
@ -136,48 +165,48 @@ void GuiScraperMenu::openAccountOptions()
// Whether to use the ScreenScraper account when scraping. // Whether to use the ScreenScraper account when scraping.
auto scraper_use_account_screenscraper = std::make_shared<SwitchComponent>(mWindow); auto scraper_use_account_screenscraper = std::make_shared<SwitchComponent>(mWindow);
scraper_use_account_screenscraper->setState(Settings::getInstance()-> scraper_use_account_screenscraper->setState(
getBool("ScraperUseAccountScreenScraper")); Settings::getInstance()->getBool("ScraperUseAccountScreenScraper"));
s->addWithLabel("USE THIS ACCOUNT FOR SCREENSCRAPER", scraper_use_account_screenscraper); s->addWithLabel("USE THIS ACCOUNT FOR SCREENSCRAPER", scraper_use_account_screenscraper);
s->addSaveFunc([scraper_use_account_screenscraper, s] { s->addSaveFunc([scraper_use_account_screenscraper, s] {
if (scraper_use_account_screenscraper->getState() != if (scraper_use_account_screenscraper->getState() !=
Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) { Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) {
Settings::getInstance()->setBool("ScraperUseAccountScreenScraper", Settings::getInstance()->setBool("ScraperUseAccountScreenScraper",
scraper_use_account_screenscraper->getState()); scraper_use_account_screenscraper->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// ScreenScraper username. // ScreenScraper username.
auto scraper_username_screenscraper = std::make_shared<TextComponent>(mWindow, "", auto scraper_username_screenscraper = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
s->addEditableTextComponent("SCREENSCRAPER USERNAME", scraper_username_screenscraper, s->addEditableTextComponent("SCREENSCRAPER USERNAME", scraper_username_screenscraper,
Settings::getInstance()->getString("ScraperUsernameScreenScraper")); Settings::getInstance()->getString("ScraperUsernameScreenScraper"));
s->addSaveFunc([scraper_username_screenscraper, s] { s->addSaveFunc([scraper_username_screenscraper, s] {
if (scraper_username_screenscraper->getValue() != if (scraper_username_screenscraper->getValue() !=
Settings::getInstance()->getString("ScraperUsernameScreenScraper")) { Settings::getInstance()->getString("ScraperUsernameScreenScraper")) {
Settings::getInstance()->setString("ScraperUsernameScreenScraper", Settings::getInstance()->setString("ScraperUsernameScreenScraper",
scraper_username_screenscraper->getValue()); scraper_username_screenscraper->getValue());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// ScreenScraper password. // ScreenScraper password.
auto scraper_password_screenscraper = std::make_shared<TextComponent>(mWindow, "", auto scraper_password_screenscraper = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
std::string passwordMasked; std::string passwordMasked;
if (Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") { if (Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") {
passwordMasked = "********"; passwordMasked = "********";
scraper_password_screenscraper->setHiddenValue( scraper_password_screenscraper->setHiddenValue(
Settings::getInstance()->getString("ScraperPasswordScreenScraper")); Settings::getInstance()->getString("ScraperPasswordScreenScraper"));
} }
s->addEditableTextComponent("SCREENSCRAPER PASSWORD", s->addEditableTextComponent("SCREENSCRAPER PASSWORD", scraper_password_screenscraper,
scraper_password_screenscraper, passwordMasked, "", true); passwordMasked, "", true);
s->addSaveFunc([scraper_password_screenscraper, s] { s->addSaveFunc([scraper_password_screenscraper, s] {
if (scraper_password_screenscraper->getHiddenValue() != if (scraper_password_screenscraper->getHiddenValue() !=
Settings::getInstance()->getString("ScraperPasswordScreenScraper")) { Settings::getInstance()->getString("ScraperPasswordScreenScraper")) {
Settings::getInstance()->setString("ScraperPasswordScreenScraper", Settings::getInstance()->setString("ScraperPasswordScreenScraper",
scraper_password_screenscraper->getHiddenValue()); scraper_password_screenscraper->getHiddenValue());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -215,8 +244,9 @@ void GuiScraperMenu::openContentOptions()
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrape_ratings->setEnabled(false); scrape_ratings->setEnabled(false);
scrape_ratings->setOpacity(DISABLED_OPACITY); scrape_ratings->setOpacity(DISABLED_OPACITY);
scrape_ratings->getParent()->getChild(scrape_ratings-> scrape_ratings->getParent()
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); ->getChild(scrape_ratings->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
} }
// Scrape other metadata. // Scrape other metadata.
@ -245,8 +275,9 @@ void GuiScraperMenu::openContentOptions()
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrape_videos->setEnabled(false); scrape_videos->setEnabled(false);
scrape_videos->setOpacity(DISABLED_OPACITY); scrape_videos->setOpacity(DISABLED_OPACITY);
scrape_videos->getParent()->getChild(scrape_videos-> scrape_videos->getParent()
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); ->getChild(scrape_videos->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
} }
// Scrape screenshots images. // Scrape screenshots images.
@ -255,7 +286,7 @@ void GuiScraperMenu::openContentOptions()
s->addWithLabel("SCRAPE SCREENSHOT IMAGES", scrape_screenshots); s->addWithLabel("SCRAPE SCREENSHOT IMAGES", scrape_screenshots);
s->addSaveFunc([scrape_screenshots, s] { s->addSaveFunc([scrape_screenshots, s] {
if (scrape_screenshots->getState() != if (scrape_screenshots->getState() !=
Settings::getInstance()->getBool("ScrapeScreenshots")) { Settings::getInstance()->getBool("ScrapeScreenshots")) {
Settings::getInstance()->setBool("ScrapeScreenshots", scrape_screenshots->getState()); Settings::getInstance()->setBool("ScrapeScreenshots", scrape_screenshots->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
@ -299,8 +330,9 @@ void GuiScraperMenu::openContentOptions()
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrape_3dboxes->setEnabled(false); scrape_3dboxes->setEnabled(false);
scrape_3dboxes->setOpacity(DISABLED_OPACITY); scrape_3dboxes->setOpacity(DISABLED_OPACITY);
scrape_3dboxes->getParent()->getChild(scrape_3dboxes-> scrape_3dboxes->getParent()
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); ->getChild(scrape_3dboxes->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
} }
mWindow->pushGui(s); mWindow->pushGui(s);
@ -311,12 +343,12 @@ void GuiScraperMenu::openMiximageOptions()
auto s = new GuiSettings(mWindow, "MIXIMAGE SETTINGS"); auto s = new GuiSettings(mWindow, "MIXIMAGE SETTINGS");
// Miximage resolution. // Miximage resolution.
auto miximage_resolution = std::make_shared<OptionListComponent<std::string>> auto miximage_resolution = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "MIXIMAGE RESOLUTION", false); mWindow, getHelpStyle(), "MIXIMAGE RESOLUTION", false);
std::string selectedResolution = Settings::getInstance()->getString("MiximageResolution"); std::string selectedResolution = Settings::getInstance()->getString("MiximageResolution");
miximage_resolution->add("1280x960", "1280x960", selectedResolution == "1280x960"); miximage_resolution->add("1280x960", "1280x960", selectedResolution == "1280x960");
miximage_resolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440"); miximage_resolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440");
miximage_resolution->add("640x480", "640x480", selectedResolution == "640x480"); miximage_resolution->add("640x480", "640x480", selectedResolution == "640x480");
// If there are no objects returned, then there must be a manually modified entry in the // If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the resolution to "1280x960" in this case. // configuration file. Simply set the resolution to "1280x960" in this case.
if (miximage_resolution->getSelectedObjects().size() == 0) if (miximage_resolution->getSelectedObjects().size() == 0)
@ -324,19 +356,19 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("MIXIMAGE RESOLUTION", miximage_resolution); s->addWithLabel("MIXIMAGE RESOLUTION", miximage_resolution);
s->addSaveFunc([miximage_resolution, s] { s->addSaveFunc([miximage_resolution, s] {
if (miximage_resolution->getSelected() != if (miximage_resolution->getSelected() !=
Settings::getInstance()->getString("MiximageResolution")) { Settings::getInstance()->getString("MiximageResolution")) {
Settings::getInstance()-> Settings::getInstance()->setString("MiximageResolution",
setString("MiximageResolution", miximage_resolution->getSelected()); miximage_resolution->getSelected());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Screenshot scaling method. // Screenshot scaling method.
auto miximage_scaling = std::make_shared<OptionListComponent<std::string>> auto miximage_scaling = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "SCREENSHOT SCALING", false); mWindow, getHelpStyle(), "SCREENSHOT SCALING", false);
std::string selectedScaling = Settings::getInstance()->getString("MiximageScreenshotScaling"); std::string selectedScaling = Settings::getInstance()->getString("MiximageScreenshotScaling");
miximage_scaling->add("sharp", "sharp", selectedScaling == "sharp"); miximage_scaling->add("sharp", "sharp", selectedScaling == "sharp");
miximage_scaling->add("smooth", "smooth", selectedScaling == "smooth"); miximage_scaling->add("smooth", "smooth", selectedScaling == "smooth");
// If there are no objects returned, then there must be a manually modified entry in the // If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the scaling method to "sharp" in this case. // configuration file. Simply set the scaling method to "sharp" in this case.
if (miximage_scaling->getSelectedObjects().size() == 0) if (miximage_scaling->getSelectedObjects().size() == 0)
@ -344,9 +376,9 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("SCREENSHOT SCALING METHOD", miximage_scaling); s->addWithLabel("SCREENSHOT SCALING METHOD", miximage_scaling);
s->addSaveFunc([miximage_scaling, s] { s->addSaveFunc([miximage_scaling, s] {
if (miximage_scaling->getSelected() != if (miximage_scaling->getSelected() !=
Settings::getInstance()->getString("MiximageScreenshotScaling")) { Settings::getInstance()->getString("MiximageScreenshotScaling")) {
Settings::getInstance()-> Settings::getInstance()->setString("MiximageScreenshotScaling",
setString("MiximageScreenshotScaling", miximage_scaling->getSelected()); miximage_scaling->getSelected());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -356,8 +388,7 @@ void GuiScraperMenu::openMiximageOptions()
miximage_generate->setState(Settings::getInstance()->getBool("MiximageGenerate")); miximage_generate->setState(Settings::getInstance()->getBool("MiximageGenerate"));
s->addWithLabel("GENERATE MIXIMAGES WHEN SCRAPING", miximage_generate); s->addWithLabel("GENERATE MIXIMAGES WHEN SCRAPING", miximage_generate);
s->addSaveFunc([miximage_generate, s] { s->addSaveFunc([miximage_generate, s] {
if (miximage_generate->getState() != if (miximage_generate->getState() != Settings::getInstance()->getBool("MiximageGenerate")) {
Settings::getInstance()->getBool("MiximageGenerate")) {
Settings::getInstance()->setBool("MiximageGenerate", miximage_generate->getState()); Settings::getInstance()->setBool("MiximageGenerate", miximage_generate->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
@ -369,7 +400,7 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("OVERWRITE MIXIMAGES (SCRAPER/OFFLINE GENERATOR)", miximage_overwrite); s->addWithLabel("OVERWRITE MIXIMAGES (SCRAPER/OFFLINE GENERATOR)", miximage_overwrite);
s->addSaveFunc([miximage_overwrite, s] { s->addSaveFunc([miximage_overwrite, s] {
if (miximage_overwrite->getState() != if (miximage_overwrite->getState() !=
Settings::getInstance()->getBool("MiximageOverwrite")) { Settings::getInstance()->getBool("MiximageOverwrite")) {
Settings::getInstance()->setBool("MiximageOverwrite", miximage_overwrite->getState()); Settings::getInstance()->setBool("MiximageOverwrite", miximage_overwrite->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
@ -381,9 +412,9 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("REMOVE LETTERBOXES FROM SCREENSHOTS", remove_letterboxes); s->addWithLabel("REMOVE LETTERBOXES FROM SCREENSHOTS", remove_letterboxes);
s->addSaveFunc([remove_letterboxes, s] { s->addSaveFunc([remove_letterboxes, s] {
if (remove_letterboxes->getState() != if (remove_letterboxes->getState() !=
Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) { Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) {
Settings::getInstance()->setBool("MiximageRemoveLetterboxes", Settings::getInstance()->setBool("MiximageRemoveLetterboxes",
remove_letterboxes->getState()); remove_letterboxes->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -394,9 +425,9 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("REMOVE PILLARBOXES FROM SCREENSHOTS", remove_pillarboxes); s->addWithLabel("REMOVE PILLARBOXES FROM SCREENSHOTS", remove_pillarboxes);
s->addSaveFunc([remove_pillarboxes, s] { s->addSaveFunc([remove_pillarboxes, s] {
if (remove_pillarboxes->getState() != if (remove_pillarboxes->getState() !=
Settings::getInstance()->getBool("MiximageRemovePillarboxes")) { Settings::getInstance()->getBool("MiximageRemovePillarboxes")) {
Settings::getInstance()->setBool("MiximageRemovePillarboxes", Settings::getInstance()->setBool("MiximageRemovePillarboxes",
remove_pillarboxes->getState()); remove_pillarboxes->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -407,9 +438,9 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("INCLUDE MARQUEE IMAGE", miximage_marquee); s->addWithLabel("INCLUDE MARQUEE IMAGE", miximage_marquee);
s->addSaveFunc([miximage_marquee, s] { s->addSaveFunc([miximage_marquee, s] {
if (miximage_marquee->getState() != if (miximage_marquee->getState() !=
Settings::getInstance()->getBool("MiximageIncludeMarquee")) { Settings::getInstance()->getBool("MiximageIncludeMarquee")) {
Settings::getInstance()-> Settings::getInstance()->setBool("MiximageIncludeMarquee",
setBool("MiximageIncludeMarquee", miximage_marquee->getState()); miximage_marquee->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -419,10 +450,8 @@ void GuiScraperMenu::openMiximageOptions()
miximage_box->setState(Settings::getInstance()->getBool("MiximageIncludeBox")); miximage_box->setState(Settings::getInstance()->getBool("MiximageIncludeBox"));
s->addWithLabel("INCLUDE BOX IMAGE", miximage_box); s->addWithLabel("INCLUDE BOX IMAGE", miximage_box);
s->addSaveFunc([miximage_box, s] { s->addSaveFunc([miximage_box, s] {
if (miximage_box->getState() != if (miximage_box->getState() != Settings::getInstance()->getBool("MiximageIncludeBox")) {
Settings::getInstance()->getBool("MiximageIncludeBox")) { Settings::getInstance()->setBool("MiximageIncludeBox", miximage_box->getState());
Settings::getInstance()->
setBool("MiximageIncludeBox", miximage_box->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -433,9 +462,9 @@ void GuiScraperMenu::openMiximageOptions()
s->addWithLabel("USE COVER IMAGE IF 3D BOX IS MISSING", miximage_cover_fallback); s->addWithLabel("USE COVER IMAGE IF 3D BOX IS MISSING", miximage_cover_fallback);
s->addSaveFunc([miximage_cover_fallback, s] { s->addSaveFunc([miximage_cover_fallback, s] {
if (miximage_cover_fallback->getState() != if (miximage_cover_fallback->getState() !=
Settings::getInstance()->getBool("MiximageCoverFallback")) { Settings::getInstance()->getBool("MiximageCoverFallback")) {
Settings::getInstance()-> Settings::getInstance()->setBool("MiximageCoverFallback",
setBool("MiximageCoverFallback", miximage_cover_fallback->getState()); miximage_cover_fallback->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -443,11 +472,13 @@ void GuiScraperMenu::openMiximageOptions()
// Miximage offline generator. // Miximage offline generator.
ComponentListRow offline_generator_row; ComponentListRow offline_generator_row;
offline_generator_row.elements.clear(); offline_generator_row.elements.clear();
offline_generator_row.addElement(std::make_shared<TextComponent> offline_generator_row.addElement(std::make_shared<TextComponent>(mWindow, "OFFLINE GENERATOR",
(mWindow, "OFFLINE GENERATOR", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM),
0x777777FF),
true);
offline_generator_row.addElement(makeArrow(mWindow), false); offline_generator_row.addElement(makeArrow(mWindow), false);
offline_generator_row.makeAcceptInputHandler( offline_generator_row.makeAcceptInputHandler(
std::bind(&GuiScraperMenu::openOfflineGenerator, this, s)); std::bind(&GuiScraperMenu::openOfflineGenerator, this, s));
s->addRow(offline_generator_row); s->addRow(offline_generator_row);
mWindow->pushGui(s); mWindow->pushGui(s);
@ -457,9 +488,9 @@ void GuiScraperMenu::openOfflineGenerator(GuiSettings* settings)
{ {
if (mSystems->getSelectedObjects().empty()) { if (mSystems->getSelectedObjects().empty()) {
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
"THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n" "THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n"
"SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n" "SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n"
"AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR")); "AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR"));
return; return;
} }
@ -492,13 +523,15 @@ void GuiScraperMenu::openOtherOptions()
auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); auto s = new GuiSettings(mWindow, "OTHER SETTINGS");
// Scraper region. // Scraper region.
auto scraper_region = std::make_shared<OptionListComponent<std::string>> auto scraper_region = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "REGION", false); mWindow, getHelpStyle(), "REGION", false);
std::string selectedScraperRegion = Settings::getInstance()->getString("ScraperRegion"); std::string selectedScraperRegion = Settings::getInstance()->getString("ScraperRegion");
// clang-format off
scraper_region->add("Europe", "eu", selectedScraperRegion == "eu"); scraper_region->add("Europe", "eu", selectedScraperRegion == "eu");
scraper_region->add("Japan", "jp", selectedScraperRegion == "jp"); scraper_region->add("Japan", "jp", selectedScraperRegion == "jp");
scraper_region->add("USA", "us", selectedScraperRegion == "us"); scraper_region->add("USA", "us", selectedScraperRegion == "us");
scraper_region->add("World", "wor", selectedScraperRegion == "wor"); scraper_region->add("World", "wor", selectedScraperRegion == "wor");
// clang-format on
// If there are no objects returned, then there must be a manually modified entry in the // If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the region to "Europe" in this case. // configuration file. Simply set the region to "Europe" in this case.
if (scraper_region->getSelectedObjects().size() == 0) if (scraper_region->getSelectedObjects().size() == 0)
@ -515,14 +548,16 @@ void GuiScraperMenu::openOtherOptions()
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scraper_region->setEnabled(false); scraper_region->setEnabled(false);
scraper_region->setOpacity(DISABLED_OPACITY); scraper_region->setOpacity(DISABLED_OPACITY);
scraper_region->getParent()->getChild(scraper_region-> scraper_region->getParent()
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); ->getChild(scraper_region->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
} }
// Scraper language. // Scraper language.
auto scraper_language = std::make_shared<OptionListComponent<std::string>> auto scraper_language = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "PREFERRED LANGUAGE", false); mWindow, getHelpStyle(), "PREFERRED LANGUAGE", false);
std::string selectedScraperLanguage = Settings::getInstance()->getString("ScraperLanguage"); std::string selectedScraperLanguage = Settings::getInstance()->getString("ScraperLanguage");
// clang-format off
scraper_language->add("English", "en", selectedScraperLanguage == "en"); scraper_language->add("English", "en", selectedScraperLanguage == "en");
scraper_language->add("Español", "es", selectedScraperLanguage == "es"); scraper_language->add("Español", "es", selectedScraperLanguage == "es");
scraper_language->add("Português", "pt", selectedScraperLanguage == "pt"); scraper_language->add("Português", "pt", selectedScraperLanguage == "pt");
@ -543,6 +578,7 @@ void GuiScraperMenu::openOtherOptions()
scraper_language->add("Čeština", "cz", selectedScraperLanguage == "cz"); scraper_language->add("Čeština", "cz", selectedScraperLanguage == "cz");
scraper_language->add("Slovenčina", "sk", selectedScraperLanguage == "sk"); scraper_language->add("Slovenčina", "sk", selectedScraperLanguage == "sk");
scraper_language->add("Türkçe", "tr", selectedScraperLanguage == "tr"); scraper_language->add("Türkçe", "tr", selectedScraperLanguage == "tr");
//clang-format on
// If there are no objects returned, then there must be a manually modified entry in the // If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the language to "English" in this case. // configuration file. Simply set the language to "English" in this case.
if (scraper_language->getSelectedObjects().size() == 0) if (scraper_language->getSelectedObjects().size() == 0)

View file

@ -16,11 +16,10 @@
#include "scrapers/Scraper.h" #include "scrapers/Scraper.h"
class FileData; class FileData;
template<typename T>
class OptionListComponent;
class SwitchComponent; class SwitchComponent;
class SystemData; class SystemData;
template <typename T> class OptionListComponent;
typedef std::function<bool(SystemData*, FileData*)> GameFilterFunc; typedef std::function<bool(SystemData*, FileData*)> GameFilterFunc;
class GuiScraperMenu : public GuiComponent class GuiScraperMenu : public GuiComponent
@ -38,16 +37,18 @@ private:
void pressedStart(); void pressedStart();
void start(); void start();
void addEntry(const std::string&, unsigned int color, void addEntry(const std::string&,
bool add_arrow, const std::function<void()>& func); unsigned int color,
bool add_arrow,
const std::function<void()>& func);
void openAccountOptions(); void openAccountOptions();
void openContentOptions(); void openContentOptions();
void openMiximageOptions(); void openMiximageOptions();
void openOfflineGenerator(GuiSettings* settings); void openOfflineGenerator(GuiSettings* settings);
void openOtherOptions(); void openOtherOptions();
std::queue<ScraperSearchParams> getSearches( std::queue<ScraperSearchParams> getSearches(std::vector<SystemData*> systems,
std::vector<SystemData*> systems, GameFilterFunc selector); GameFilterFunc selector);
std::shared_ptr<OptionListComponent<std::string>> mScraper; std::shared_ptr<OptionListComponent<std::string>> mScraper;
std::shared_ptr<OptionListComponent<GameFilterFunc>> mFilters; std::shared_ptr<OptionListComponent<GameFilterFunc>> mFilters;

View file

@ -11,27 +11,26 @@
#include "guis/GuiScraperMulti.h" #include "guis/GuiScraperMulti.h"
#include "CollectionSystemsManager.h"
#include "Gamelist.h"
#include "MameNames.h"
#include "SystemData.h"
#include "Window.h"
#include "components/ButtonComponent.h" #include "components/ButtonComponent.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiScraperSearch.h" #include "guis/GuiScraperSearch.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "CollectionSystemsManager.h"
#include "Gamelist.h"
#include "MameNames.h"
#include "SystemData.h"
#include "Window.h"
GuiScraperMulti::GuiScraperMulti( GuiScraperMulti::GuiScraperMulti(Window* window,
Window* window, const std::queue<ScraperSearchParams>& searches,
const std::queue<ScraperSearchParams>& searches, bool approveResults)
bool approveResults) : GuiComponent(window)
: GuiComponent(window), , mBackground(window, ":/graphics/frame.svg")
mBackground(window, ":/graphics/frame.svg"), , mGrid(window, Vector2i(1, 5))
mGrid(window, Vector2i(1, 5)), , mSearchQueue(searches)
mSearchQueue(searches), , mApproveResults(approveResults)
mApproveResults(approveResults)
{ {
assert(mSearchQueue.size()); assert(mSearchQueue.size());
@ -47,41 +46,42 @@ GuiScraperMulti::GuiScraperMulti(
// Set up grid. // Set up grid.
mTitle = std::make_shared<TextComponent>(mWindow, "SCRAPING IN PROGRESS", mTitle = std::make_shared<TextComponent>(mWindow, "SCRAPING IN PROGRESS",
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true);
mSystem = std::make_shared<TextComponent>(mWindow, "SYSTEM", mSystem = std::make_shared<TextComponent>(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); 0x777777FF, ALIGN_CENTER);
mGrid.setEntry(mSystem, Vector2i(0, 1), false, true); mGrid.setEntry(mSystem, Vector2i(0, 1), false, true);
mSubtitle = std::make_shared<TextComponent>(mWindow, "subtitle text", mSubtitle = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true); mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true);
if (mApproveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic")) if (mApproveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic"))
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow, mSearchComp = std::make_shared<GuiScraperSearch>(
GuiScraperSearch::NEVER_AUTO_ACCEPT, mTotalGames); mWindow, GuiScraperSearch::NEVER_AUTO_ACCEPT, mTotalGames);
else if (mApproveResults && Settings::getInstance()->getBool("ScraperSemiautomatic")) else if (mApproveResults && Settings::getInstance()->getBool("ScraperSemiautomatic"))
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow, mSearchComp = std::make_shared<GuiScraperSearch>(
GuiScraperSearch::ACCEPT_SINGLE_MATCHES, mTotalGames); mWindow, GuiScraperSearch::ACCEPT_SINGLE_MATCHES, mTotalGames);
else if (!mApproveResults) else if (!mApproveResults)
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow, mSearchComp = std::make_shared<GuiScraperSearch>(
GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, mTotalGames); mWindow, GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, mTotalGames);
mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult, mSearchComp->setAcceptCallback(
this, std::placeholders::_1)); std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1));
mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this)); mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this));
mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this)); mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this));
mGrid.setEntry(mSearchComp, Vector2i(0, 3), mSearchComp->getSearchType() != mGrid.setEntry(mSearchComp, Vector2i(0, 3),
GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, true); mSearchComp->getSearchType() != GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT,
true);
std::vector<std::shared_ptr<ButtonComponent>> buttons; std::vector<std::shared_ptr<ButtonComponent>> buttons;
if (mApproveResults) { if (mApproveResults) {
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH", buttons.push_back(
"refine search", [&] { std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH", "refine search", [&] {
mSearchComp->openInputScreen(mSearchQueue.front()); mSearchComp->openInputScreen(mSearchQueue.front());
mGrid.resetCursor(); mGrid.resetCursor();
})); }));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SKIP", "skip game", [&] { buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SKIP", "skip game", [&] {
skip(); skip();
@ -89,8 +89,8 @@ GuiScraperMulti::GuiScraperMulti(
})); }));
} }
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "STOP", buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "STOP", "stop",
"stop", std::bind(&GuiScraperMulti::finish, this))); std::bind(&GuiScraperMulti::finish, this)));
mButtonGrid = makeButtonGrid(mWindow, buttons); mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false);
@ -101,8 +101,8 @@ GuiScraperMulti::GuiScraperMulti(
float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth();
setSize(width, Renderer::getScreenHeight() * 0.849f); setSize(width, Renderer::getScreenHeight() * 0.849f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
mSize.y()) / 2); (Renderer::getScreenHeight() - mSize.y()) / 2.0f);
doNextSearch(); doNextSearch();
} }
@ -111,8 +111,8 @@ GuiScraperMulti::~GuiScraperMulti()
{ {
if (mTotalSuccessful > 0) { if (mTotalSuccessful > 0) {
// Sort all systems to possibly update their view style from Basic to Detailed or Video. // Sort all systems to possibly update their view style from Basic to Detailed or Video.
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it !=SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
(*it)->sortSystem(); (*it)->sortSystem();
} }
} }
@ -121,10 +121,10 @@ GuiScraperMulti::~GuiScraperMulti()
void GuiScraperMulti::onSizeChanged() void GuiScraperMulti::onSizeChanged()
{ {
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y(), false); mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y(), false);
mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2) / mSize.y(), false); mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2.0f) / mSize.y(), false);
mGrid.setRowHeightPerc(2, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y(), false); mGrid.setRowHeightPerc(2, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y(), false);
mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y() / mSize.y(), false); mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y() / mSize.y(), false);
mGrid.setSize(mSize); mGrid.setSize(mSize);
@ -148,34 +148,36 @@ void GuiScraperMulti::doNextSearch()
} }
else { else {
if (mSearchQueue.front().game->isArcadeGame() && if (mSearchQueue.front().game->isArcadeGame() &&
Settings::getInstance()->getString("Scraper") == "thegamesdb") Settings::getInstance()->getString("Scraper") == "thegamesdb")
scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()) + scrapeName =
" (" + MameNames::getInstance()->getCleanName(mSearchQueue.front().game-> Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()) + " (" +
getCleanName()) + ")"; MameNames::getInstance()->getCleanName(mSearchQueue.front().game->getCleanName()) +
")";
else else
scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()); scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath());
} }
// Extract possible subfolders from the path. // Extract possible subfolders from the path.
std::string folderPath = Utils::String::replace( std::string folderPath =
Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()), Utils::String::replace(Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()),
mSearchQueue.front().system->getSystemEnvData()->mStartPath, ""); mSearchQueue.front().system->getSystemEnvData()->mStartPath, "");
if (folderPath.size() >= 2) { if (folderPath.size() >= 2) {
folderPath.erase(0, 1); folderPath.erase(0, 1);
#if defined(_WIN64) #if defined(_WIN64)
folderPath.push_back('\\'); folderPath.push_back('\\');
folderPath = Utils::String::replace(folderPath, "/", "\\"); folderPath = Utils::String::replace(folderPath, "/", "\\");
#else #else
folderPath.push_back('/'); folderPath.push_back('/');
#endif #endif
} }
// Update subtitle. // Update subtitle.
ss.str(""); ss.str("");
ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << folderPath << ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << folderPath
scrapeName << ((mSearchQueue.front().game->getType() == FOLDER) ? " " + << scrapeName
ViewController::FOLDER_CHAR : ""); << ((mSearchQueue.front().game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR :
"");
mSubtitle->setText(ss.str()); mSubtitle->setText(ss.str());
mSearchComp->search(mSearchQueue.front()); mSearchComp->search(mSearchQueue.front());
@ -212,12 +214,12 @@ void GuiScraperMulti::finish()
ss << "NO GAMES WERE SCRAPED"; ss << "NO GAMES WERE SCRAPED";
} }
else { else {
ss << mTotalSuccessful << " GAME" << ss << mTotalSuccessful << " GAME" << ((mTotalSuccessful > 1) ? "S" : "")
((mTotalSuccessful > 1) ? "S" : "") << " SUCCESSFULLY SCRAPED"; << " SUCCESSFULLY SCRAPED";
if (mTotalSkipped > 0) if (mTotalSkipped > 0)
ss << "\n" << mTotalSkipped << " GAME" ss << "\n"
<< ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED"; << mTotalSkipped << " GAME" << ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED";
} }
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), ss.str(), "OK", [&] { mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), ss.str(), "OK", [&] {

View file

@ -12,11 +12,11 @@
#ifndef ES_APP_GUIS_GUI_SCRAPER_MULTI_H #ifndef ES_APP_GUIS_GUI_SCRAPER_MULTI_H
#define ES_APP_GUIS_GUI_SCRAPER_MULTI_H #define ES_APP_GUIS_GUI_SCRAPER_MULTI_H
#include "GuiComponent.h"
#include "MetaData.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
#include "components/NinePatchComponent.h" #include "components/NinePatchComponent.h"
#include "scrapers/Scraper.h" #include "scrapers/Scraper.h"
#include "GuiComponent.h"
#include "MetaData.h"
class GuiScraperSearch; class GuiScraperSearch;
class TextComponent; class TextComponent;
@ -24,10 +24,9 @@ class TextComponent;
class GuiScraperMulti : public GuiComponent class GuiScraperMulti : public GuiComponent
{ {
public: public:
GuiScraperMulti( GuiScraperMulti(Window* window,
Window* window, const std::queue<ScraperSearchParams>& searches,
const std::queue<ScraperSearchParams>& searches, bool approveResults);
bool approveResults);
virtual ~GuiScraperMulti(); virtual ~GuiScraperMulti();
@ -40,15 +39,7 @@ private:
void acceptResult(const ScraperSearchResult& result); void acceptResult(const ScraperSearchResult& result);
void skip(); void skip();
void doNextSearch(); void doNextSearch();
void finish(); void finish();
unsigned int mTotalGames;
unsigned int mCurrentGame;
unsigned int mTotalSuccessful;
unsigned int mTotalSkipped;
std::queue<ScraperSearchParams> mSearchQueue;
std::vector<MetaDataDecl> mMetaDataDecl;
bool mApproveResults;
NinePatchComponent mBackground; NinePatchComponent mBackground;
ComponentGrid mGrid; ComponentGrid mGrid;
@ -58,6 +49,14 @@ private:
std::shared_ptr<TextComponent> mSubtitle; std::shared_ptr<TextComponent> mSubtitle;
std::shared_ptr<GuiScraperSearch> mSearchComp; std::shared_ptr<GuiScraperSearch> mSearchComp;
std::shared_ptr<ComponentGrid> mButtonGrid; std::shared_ptr<ComponentGrid> mButtonGrid;
std::queue<ScraperSearchParams> mSearchQueue;
std::vector<MetaDataDecl> mMetaDataDecl;
unsigned int mTotalGames;
unsigned int mCurrentGame;
unsigned int mTotalSuccessful;
unsigned int mTotalSkipped;
bool mApproveResults;
}; };
#endif // ES_APP_GUIS_GUI_SCRAPER_MULTI_H #endif // ES_APP_GUIS_GUI_SCRAPER_MULTI_H

View file

@ -15,6 +15,13 @@
#include "guis/GuiScraperSearch.h" #include "guis/GuiScraperSearch.h"
#include "CollectionSystemsManager.h"
#include "FileData.h"
#include "Log.h"
#include "MameNames.h"
#include "PlatformId.h"
#include "SystemData.h"
#include "Window.h"
#include "components/ComponentList.h" #include "components/ComponentList.h"
#include "components/DateTimeEditComponent.h" #include "components/DateTimeEditComponent.h"
#include "components/ImageComponent.h" #include "components/ImageComponent.h"
@ -26,28 +33,18 @@
#include "resources/Font.h" #include "resources/Font.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "CollectionSystemsManager.h"
#include "FileData.h"
#include "Log.h"
#include "MameNames.h"
#include "PlatformId.h"
#include "SystemData.h"
#include "Window.h"
#define FAILED_VERIFICATION_RETRIES 8 #define FAILED_VERIFICATION_RETRIES 8
GuiScraperSearch::GuiScraperSearch( GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int scrapeCount)
Window* window, : GuiComponent(window)
SearchType type, , mGrid(window, Vector2i(4, 3))
unsigned int scrapeCount) , mBusyAnim(window)
: GuiComponent(window), , mSearchType(type)
mGrid(window, Vector2i(4, 3)), , mScrapeCount(scrapeCount)
mBusyAnim(window), , mScrapeRatings(false)
mSearchType(type), , mRefinedSearch(false)
mScrapeCount(scrapeCount), , mFoundGame(false)
mScrapeRatings(false),
mRefinedSearch(false),
mFoundGame(false)
{ {
addChild(&mGrid); addChild(&mGrid);
@ -56,12 +53,12 @@ GuiScraperSearch::GuiScraperSearch(
mRetryCount = 0; mRetryCount = 0;
// Left spacer (empty component, needed for borders). // Left spacer (empty component, needed for borders).
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false, false,
false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// Selected result name. // Selected result name.
mResultName = std::make_shared<TextComponent>(mWindow, "Result name", mResultName = std::make_shared<TextComponent>(mWindow, "Result name",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF); Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
// Selected result thumbnail. // Selected result thumbnail.
mResultThumbnail = std::make_shared<ImageComponent>(mWindow); mResultThumbnail = std::make_shared<ImageComponent>(mWindow);
@ -77,7 +74,7 @@ GuiScraperSearch::GuiScraperSearch(
mDescContainer->setScrollParameters(6000, 3000, 85); mDescContainer->setScrollParameters(6000, 3000, 85);
mResultDesc = std::make_shared<TextComponent>(mWindow, "Result desc", mResultDesc = std::make_shared<TextComponent>(mWindow, "Result desc",
Font::get(FONT_SIZE_SMALL), 0x777777FF); Font::get(FONT_SIZE_SMALL), 0x777777FF);
mDescContainer->addChild(mResultDesc.get()); mDescContainer->addChild(mResultDesc.get());
mDescContainer->setAutoScroll(true); mDescContainer->setAutoScroll(true);
@ -89,40 +86,47 @@ GuiScraperSearch::GuiScraperSearch(
mMD_ReleaseDate = std::make_shared<DateTimeEditComponent>(mWindow); mMD_ReleaseDate = std::make_shared<DateTimeEditComponent>(mWindow);
mMD_ReleaseDate->setColor(mdColor); mMD_ReleaseDate->setColor(mdColor);
mMD_ReleaseDate->setUppercase(true); mMD_ReleaseDate->setUppercase(true);
mMD_Developer = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, mMD_Developer =
Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
mMD_Publisher = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector2f::Zero(), 0x00000000, 0.02f);
Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); mMD_Publisher =
mMD_Genre = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Players = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, mMD_Genre =
Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Players =
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Filler = std::make_shared<TextComponent>(mWindow, "", font, mdColor); mMD_Filler = std::make_shared<TextComponent>(mWindow, "", font, mdColor);
if (Settings::getInstance()->getString("Scraper") != "thegamesdb") if (Settings::getInstance()->getString("Scraper") != "thegamesdb")
mScrapeRatings = true; mScrapeRatings = true;
if (mScrapeRatings) if (mScrapeRatings)
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Pairs.push_back(
(mWindow, "RATING:", font, mdLblColor), mMD_Rating, false)); MetaDataPair(std::make_shared<TextComponent>(mWindow, "RATING:", font, mdLblColor),
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Rating, false));
(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate));
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Pairs.push_back(MetaDataPair(
(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); std::make_shared<TextComponent>(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate));
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Pairs.push_back(MetaDataPair(
(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); std::make_shared<TextComponent>(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer));
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Pairs.push_back(MetaDataPair(
(mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); std::make_shared<TextComponent>(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher));
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Pairs.push_back(MetaDataPair(
(mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); std::make_shared<TextComponent>(mWindow, "GENRE:", font, mdLblColor), mMD_Genre));
mMD_Pairs.push_back(MetaDataPair(
std::make_shared<TextComponent>(mWindow, "PLAYERS:", font, mdLblColor), mMD_Players));
// If no rating is being scraped, add a filler to make sure that the fonts keep the same // If no rating is being scraped, add a filler to make sure that the fonts keep the same
// size so the GUI looks consistent. // size so the GUI looks consistent.
if (!mScrapeRatings) if (!mScrapeRatings)
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent> mMD_Pairs.push_back(MetaDataPair(
(mWindow, "", font, mdLblColor), mMD_Filler)); std::make_shared<TextComponent>(mWindow, "", font, mdLblColor), mMD_Filler));
mMD_Grid = std::make_shared<ComponentGrid>(mWindow, mMD_Grid = std::make_shared<ComponentGrid>(
Vector2i(2, static_cast<int>(mMD_Pairs.size()*2 - 1))); mWindow, Vector2i(2, static_cast<int>(mMD_Pairs.size() * 2 - 1)));
unsigned int i = 0; unsigned int i = 0;
for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) { for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) {
mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true); mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true);
@ -135,7 +139,9 @@ GuiScraperSearch::GuiScraperSearch(
// Result list. // Result list.
mResultList = std::make_shared<ComponentList>(mWindow); mResultList = std::make_shared<ComponentList>(mWindow);
mResultList->setCursorChangedCallback([this](CursorState state) { mResultList->setCursorChangedCallback([this](CursorState state) {
if (state == CURSOR_STOPPED) updateInfoPane(); }); if (state == CURSOR_STOPPED)
updateInfoPane();
});
updateViewStyle(); updateViewStyle();
} }
@ -163,7 +169,7 @@ GuiScraperSearch::~GuiScraperSearch()
// This is required to properly refresh the gamelist view if the user aborted the // This is required to properly refresh the gamelist view if the user aborted the
// scraping when the miximage was getting generated. // scraping when the miximage was getting generated.
if (Settings::getInstance()->getBool("MiximageGenerate") && if (Settings::getInstance()->getBool("MiximageGenerate") &&
mMiximageGeneratorThread.joinable()) { mMiximageGeneratorThread.joinable()) {
mScrapeResult.savedNewMedia = true; mScrapeResult.savedNewMedia = true;
// We always let the miximage generator thread complete. // We always let the miximage generator thread complete.
mMiximageGeneratorThread.join(); mMiximageGeneratorThread.join();
@ -196,7 +202,7 @@ void GuiScraperSearch::onSizeChanged()
// Row heights. // Row heights.
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name. if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name.
mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) / mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) /
mGrid.getSize().y()); // Result name. mGrid.getSize().y()); // Result name.
else else
mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding. mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding.
@ -216,11 +222,11 @@ void GuiScraperSearch::onSizeChanged()
resizeMetadata(); resizeMetadata();
if (mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) if (mSearchType != ALWAYS_ACCEPT_FIRST_RESULT)
mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale + mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale + mGrid.getColWidth(2),
mGrid.getColWidth(2), mResultDesc->getFont()->getHeight() * 3); mResultDesc->getFont()->getHeight() * 3.0f);
else else
mDescContainer->setSize(mGrid.getColWidth(3) * boxartCellScale, mDescContainer->setSize(mGrid.getColWidth(3) * boxartCellScale,
mResultDesc->getFont()->getHeight() * 6); mResultDesc->getFont()->getHeight() * 6.0f);
// Make description text wrap at edge of container. // Make description text wrap at edge of container.
mResultDesc->setSize(mDescContainer->getSize().x(), 0); mResultDesc->setSize(mDescContainer->getSize().x(), 0);
@ -247,13 +253,14 @@ void GuiScraperSearch::resizeMetadata()
it->first->setFont(fontLbl); it->first->setFont(fontLbl);
it->first->setSize(0, 0); it->first->setSize(0, 0);
if (it->first->getSize().x() > maxLblWidth) if (it->first->getSize().x() > maxLblWidth)
maxLblWidth = it->first->getSize().x() + maxLblWidth =
(16.0f * Renderer::getScreenWidthModifier()); it->first->getSize().x() + (16.0f * Renderer::getScreenWidthModifier());
} }
for (unsigned int i = 0; i < mMD_Pairs.size(); i++) for (unsigned int i = 0; i < mMD_Pairs.size(); i++)
mMD_Grid->setRowHeightPerc(i * 2, (fontLbl->getLetterHeight() + mMD_Grid->setRowHeightPerc(
(2.0f * Renderer::getScreenHeightModifier())) / mMD_Grid->getSize().y()); i * 2, (fontLbl->getLetterHeight() + (2.0f * Renderer::getScreenHeightModifier())) /
mMD_Grid->getSize().y());
// Update component fonts. // Update component fonts.
mMD_ReleaseDate->setFont(fontComp); mMD_ReleaseDate->setFont(fontComp);
@ -286,30 +293,30 @@ void GuiScraperSearch::updateViewStyle()
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) {
// Show name. // Show name.
mGrid.setEntry(mResultName, Vector2i(1, 0), false, false, Vector2i(2, 1), mGrid.setEntry(mResultName, Vector2i(1, 0), false, false, Vector2i(2, 1),
GridFlags::BORDER_TOP); GridFlags::BORDER_TOP);
// Need a border on the bottom left. // Need a border on the bottom left.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 2), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 2), false, false,
false, false, Vector2i(3, 1), GridFlags::BORDER_BOTTOM); Vector2i(3, 1), GridFlags::BORDER_BOTTOM);
// Show description on the right. // Show description on the right.
mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3), mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3),
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// Make description text wrap at edge of container. // Make description text wrap at edge of container.
mResultDesc->setSize(mDescContainer->getSize().x(), 0.0f); mResultDesc->setSize(mDescContainer->getSize().x(), 0.0f);
} }
else { else {
// Fake row where name would be. // Fake row where name would be.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), false, true,
false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); Vector2i(2, 1), GridFlags::BORDER_TOP);
// Show result list on the right. // Show result list on the right.
mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3), mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3),
GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// Show description under image/info. // Show description under image/info.
mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1), mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1),
GridFlags::BORDER_BOTTOM); GridFlags::BORDER_BOTTOM);
// Make description text wrap at edge of container. // Make description text wrap at edge of container.
mResultDesc->setSize(mDescContainer->getSize().x(), 0); mResultDesc->setSize(mDescContainer->getSize().x(), 0);
} }
@ -355,16 +362,17 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
if (results.empty()) { if (results.empty()) {
// Check if the scraper used is still valid. // Check if the scraper used is still valid.
if (!isValidConfiguredScraper()) { if (!isValidConfiguredScraper()) {
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(
Utils::String::toUpper("Configured scraper is no longer available.\n" mWindow, getHelpStyle(),
"Please change the scraping source in the settings."), Utils::String::toUpper("Configured scraper is no longer available.\n"
"Please change the scraping source in the settings."),
"FINISH", mSkipCallback)); "FINISH", mSkipCallback));
} }
else { else {
mFoundGame = false; mFoundGame = false;
ComponentListRow row; ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, "NO GAMES FOUND", row.addElement(std::make_shared<TextComponent>(mWindow, "NO GAMES FOUND", font, color),
font, color), true); true);
if (mSkipCallback) if (mSkipCallback)
row.makeAcceptInputHandler(mSkipCallback); row.makeAcceptInputHandler(mSkipCallback);
@ -379,8 +387,10 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
for (size_t i = 0; i < results.size(); i++) { for (size_t i = 0; i < results.size(); i++) {
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, row.addElement(
Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true); std::make_shared<TextComponent>(
mWindow, Utils::String::toUpper(results.at(i).mdl.get("name")), font, color),
true);
row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); }); row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); });
mResultList->addRow(row); mResultList->addRow(row);
} }
@ -393,14 +403,14 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
// If there is no thumbnail to download and we're in semi-automatic mode, proceed to return // If there is no thumbnail to download and we're in semi-automatic mode, proceed to return
// the results or we'll get stuck forever waiting for a thumbnail to be downloaded. // the results or we'll get stuck forever waiting for a thumbnail to be downloaded.
if (mSearchType == ACCEPT_SINGLE_MATCHES && results.size() == 1 && if (mSearchType == ACCEPT_SINGLE_MATCHES && results.size() == 1 &&
mScraperResults.front().thumbnailImageUrl == "") mScraperResults.front().thumbnailImageUrl == "")
returnResult(mScraperResults.front()); returnResult(mScraperResults.front());
// For automatic mode, if there's no thumbnail to download or no matching games found, // For automatic mode, if there's no thumbnail to download or no matching games found,
// proceed directly or we'll get stuck forever. // proceed directly or we'll get stuck forever.
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) {
if (mScraperResults.size() == 0 || (mScraperResults.size() > 0 && if (mScraperResults.size() == 0 ||
mScraperResults.front().thumbnailImageUrl == "")) { (mScraperResults.size() > 0 && mScraperResults.front().thumbnailImageUrl == "")) {
if (mScraperResults.size() == 0) if (mScraperResults.size() == 0)
mSkipCallback(); mSkipCallback();
else else
@ -420,12 +430,12 @@ void GuiScraperSearch::onSearchError(const std::string& error, HttpReq::Status s
// the error dialog will be presented to the user, and if the "Retry" button is pressed, // the error dialog will be presented to the user, and if the "Retry" button is pressed,
// a new round of retries will take place. // a new round of retries will take place.
if (status == HttpReq::REQ_FAILED_VERIFICATION && mRetryCount < FAILED_VERIFICATION_RETRIES && if (status == HttpReq::REQ_FAILED_VERIFICATION && mRetryCount < FAILED_VERIFICATION_RETRIES &&
Settings::getInstance()->getBool("ScraperRetryPeerVerification")) { Settings::getInstance()->getBool("ScraperRetryPeerVerification")) {
LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", ""); LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", "");
mRetrySearch = true; mRetrySearch = true;
mRetryCount++; mRetryCount++;
LOG(LogError) << "GuiScraperSearch: Attempting automatic retry " << mRetryCount << LOG(LogError) << "GuiScraperSearch: Attempting automatic retry " << mRetryCount << " of "
" of " << FAILED_VERIFICATION_RETRIES; << FAILED_VERIFICATION_RETRIES;
return; return;
} }
else { else {
@ -435,15 +445,16 @@ void GuiScraperSearch::onSearchError(const std::string& error, HttpReq::Status s
if (mScrapeCount > 1) { if (mScrapeCount > 1) {
LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", ""); LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", "");
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error), mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error),
"RETRY", std::bind(&GuiScraperSearch::search, this, mLastSearch), "RETRY",
"SKIP", mSkipCallback, std::bind(&GuiScraperSearch::search, this, mLastSearch),
"CANCEL", mCancelCallback, true)); "SKIP", mSkipCallback, "CANCEL", mCancelCallback, true));
} }
else { else {
LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", ""); LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", "");
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error), mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error),
"RETRY", std::bind(&GuiScraperSearch::search, this, mLastSearch), "RETRY",
"CANCEL", mCancelCallback, "", nullptr, true)); std::bind(&GuiScraperSearch::search, this, mLastSearch),
"CANCEL", mCancelCallback, "", nullptr, true));
} }
} }
@ -489,9 +500,9 @@ void GuiScraperSearch::updateInfoPane()
// Add an entry into the thumbnail map, this way we can track and download // Add an entry into the thumbnail map, this way we can track and download
// each thumbnail separately even as they're downloading while scrolling // each thumbnail separately even as they're downloading while scrolling
// through the result list. // through the result list.
mThumbnailReqMap.insert(std::pair<std::string, mThumbnailReqMap.insert(std::pair<std::string, std::unique_ptr<HttpReq>>(
std::unique_ptr<HttpReq>>(mScraperResults[i].thumbnailImageUrl, mScraperResults[i].thumbnailImageUrl,
std::unique_ptr<HttpReq>(new HttpReq(thumb)))); std::unique_ptr<HttpReq>(new HttpReq(thumb))));
} }
} }
} }
@ -593,8 +604,8 @@ void GuiScraperSearch::update(int deltaTime)
// Check if the thumbnail for the currently selected game has finished downloading. // Check if the thumbnail for the currently selected game has finished downloading.
if (mScraperResults.size() > 0) { if (mScraperResults.size() > 0) {
auto it = mThumbnailReqMap.find(mScraperResults[mResultList-> auto it =
getCursorId()].thumbnailImageUrl); mThumbnailReqMap.find(mScraperResults[mResultList->getCursorId()].thumbnailImageUrl);
if (it != mThumbnailReqMap.end() && it->second->status() != HttpReq::REQ_IN_PROGRESS) if (it != mThumbnailReqMap.end() && it->second->status() != HttpReq::REQ_IN_PROGRESS)
updateThumbnail(); updateThumbnail();
} }
@ -681,13 +692,14 @@ void GuiScraperSearch::update(int deltaTime)
mMDResolveHandle.reset(); mMDResolveHandle.reset();
if (mScrapeResult.mediaFilesDownloadStatus == COMPLETED && if (mScrapeResult.mediaFilesDownloadStatus == COMPLETED &&
Settings::getInstance()->getBool("MiximageGenerate")) { Settings::getInstance()->getBool("MiximageGenerate")) {
std::string currentMiximage = mLastSearch.game->getMiximagePath(); std::string currentMiximage = mLastSearch.game->getMiximagePath();
if (currentMiximage == "" || (currentMiximage != "" && if (currentMiximage == "" ||
Settings::getInstance()->getBool("MiximageOverwrite"))) { (currentMiximage != "" &&
Settings::getInstance()->getBool("MiximageOverwrite"))) {
mMiximageGenerator = std::make_unique<MiximageGenerator>(mLastSearch.game, mMiximageGenerator =
mResultMessage); std::make_unique<MiximageGenerator>(mLastSearch.game, mResultMessage);
// The promise/future mechanism is used as signaling for the thread to // The promise/future mechanism is used as signaling for the thread to
// indicate that processing has been completed. The reason to run a separate // indicate that processing has been completed. The reason to run a separate
@ -697,8 +709,9 @@ void GuiScraperSearch::update(int deltaTime)
std::promise<bool>().swap(mGeneratorPromise); std::promise<bool>().swap(mGeneratorPromise);
mGeneratorFuture = mGeneratorPromise.get_future(); mGeneratorFuture = mGeneratorPromise.get_future();
mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread, mMiximageGeneratorThread =
mMiximageGenerator.get(), &mGeneratorPromise); std::thread(&MiximageGenerator::startThread, mMiximageGenerator.get(),
&mGeneratorPromise);
} }
else { else {
returnResult(mScrapeResult); returnResult(mScrapeResult);
@ -724,7 +737,7 @@ void GuiScraperSearch::updateThumbnail()
// thumbnail download has been completed for this game. // thumbnail download has been completed for this game.
if (mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus == IN_PROGRESS) { if (mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus == IN_PROGRESS) {
mScraperResults[mResultList->getCursorId()].thumbnailImageData = mScraperResults[mResultList->getCursorId()].thumbnailImageData =
it->second->getContent(); it->second->getContent();
mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus = COMPLETED; mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus = COMPLETED;
} }
// Activate the thumbnail in the GUI. // Activate the thumbnail in the GUI.
@ -737,7 +750,7 @@ void GuiScraperSearch::updateThumbnail()
else { else {
mResultThumbnail->setImage(""); mResultThumbnail->setImage("");
onSearchError("Error downloading thumbnail:\n " + it->second->getErrorMsg(), onSearchError("Error downloading thumbnail:\n " + it->second->getErrorMsg(),
it->second->status()); it->second->status());
} }
mThumbnailReqMap.erase(it); mThumbnailReqMap.erase(it);
@ -746,9 +759,9 @@ void GuiScraperSearch::updateThumbnail()
// we are in semi-automatic mode with a single matching game result, we proceed // we are in semi-automatic mode with a single matching game result, we proceed
// to immediately download the rest of the media files. // to immediately download the rest of the media files.
if ((mSearchType == ALWAYS_ACCEPT_FIRST_RESULT || if ((mSearchType == ALWAYS_ACCEPT_FIRST_RESULT ||
(mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1 && (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1 &&
mRefinedSearch == false)) && mRefinedSearch == false)) &&
mScraperResults.front().thumbnailDownloadStatus == COMPLETED) { mScraperResults.front().thumbnailDownloadStatus == COMPLETED) {
mRefinedSearch = false; mRefinedSearch = false;
if (mScraperResults.size() == 0) if (mScraperResults.size() == 0)
mSkipCallback(); mSkipCallback();
@ -781,22 +794,23 @@ void GuiScraperSearch::openInputScreen(ScraperSearchParams& params)
// in case the scraper is set to TheGamesDB and it's an arcade game. This is required // in case the scraper is set to TheGamesDB and it's an arcade game. This is required
// as TheGamesDB has issues with searches using the short MAME names. // as TheGamesDB has issues with searches using the short MAME names.
if (params.game->isArcadeGame() && if (params.game->isArcadeGame() &&
Settings::getInstance()->getString("Scraper") == "thegamesdb") Settings::getInstance()->getString("Scraper") == "thegamesdb")
searchString = MameNames::getInstance()->getCleanName(params.game->getCleanName()); searchString = MameNames::getInstance()->getCleanName(params.game->getCleanName());
else else
searchString = params.game->getCleanName(); searchString = params.game->getCleanName();
} }
} }
else { else {
searchString = params.nameOverride; searchString = params.nameOverride;
} }
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH", mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH", searchString,
searchString, searchForFunc, false, "SEARCH", "APPLY CHANGES?")); searchForFunc, false, "SEARCH", "APPLY CHANGES?"));
} }
bool GuiScraperSearch::saveMetadata( bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result,
const ScraperSearchResult& result, MetaDataList& metadata, FileData* scrapedGame) MetaDataList& metadata,
FileData* scrapedGame)
{ {
bool metadataUpdated = false; bool metadataUpdated = false;
bool hasDefaultName = false; bool hasDefaultName = false;
@ -825,7 +839,7 @@ bool GuiScraperSearch::saveMetadata(
// Skip element if the setting to not scrape metadata has been set, // Skip element if the setting to not scrape metadata has been set,
// unless its type is rating or name. // unless its type is rating or name.
if (!Settings::getInstance()->getBool("ScrapeMetadata") && if (!Settings::getInstance()->getBool("ScrapeMetadata") &&
(key != "rating" && key != "name")) (key != "rating" && key != "name"))
continue; continue;
// Skip saving of rating if the corresponding option has been set to false. // Skip saving of rating if the corresponding option has been set to false.
@ -885,7 +899,7 @@ std::vector<HelpPrompt> GuiScraperSearch::getHelpPrompts()
if (mScrapeCount > 1) if (mScrapeCount > 1)
prompts.push_back(HelpPrompt("x", "skip")); prompts.push_back(HelpPrompt("x", "skip"));
if (mFoundGame && (mRefinedSearch || mSearchType != ACCEPT_SINGLE_MATCHES || if (mFoundGame && (mRefinedSearch || mSearchType != ACCEPT_SINGLE_MATCHES ||
(mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1))) (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1)))
prompts.push_back(HelpPrompt("a", "accept result")); prompts.push_back(HelpPrompt("a", "accept result"));
return prompts; return prompts;
@ -897,13 +911,3 @@ HelpStyle GuiScraperSearch::getHelpStyle()
style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system");
return style; return style;
} }
void GuiScraperSearch::onFocusGained()
{
mGrid.onFocusGained();
}
void GuiScraperSearch::onFocusLost()
{
mGrid.onFocusLost();
}

View file

@ -16,11 +16,11 @@
#ifndef ES_APP_GUIS_GUI_SCRAPER_SEARCH_H #ifndef ES_APP_GUIS_GUI_SCRAPER_SEARCH_H
#define ES_APP_GUIS_GUI_SCRAPER_SEARCH_H #define ES_APP_GUIS_GUI_SCRAPER_SEARCH_H
#include "GuiComponent.h"
#include "MiximageGenerator.h"
#include "components/BusyComponent.h" #include "components/BusyComponent.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
#include "scrapers/Scraper.h" #include "scrapers/Scraper.h"
#include "GuiComponent.h"
#include "MiximageGenerator.h"
#include <future> #include <future>
#include <thread> #include <thread>
@ -36,9 +36,9 @@ class GuiScraperSearch : public GuiComponent
{ {
public: public:
enum SearchType { enum SearchType {
ALWAYS_ACCEPT_FIRST_RESULT, ALWAYS_ACCEPT_FIRST_RESULT, // Automatic mode.
ACCEPT_SINGLE_MATCHES, ACCEPT_SINGLE_MATCHES, // Semi-automatic mode.
NEVER_AUTO_ACCEPT NEVER_AUTO_ACCEPT // Manual mode.
}; };
GuiScraperSearch(Window* window, SearchType searchType, unsigned int scrapeCount = 1); GuiScraperSearch(Window* window, SearchType searchType, unsigned int scrapeCount = 1);
@ -47,20 +47,29 @@ public:
void search(const ScraperSearchParams& params); void search(const ScraperSearchParams& params);
void openInputScreen(ScraperSearchParams& from); void openInputScreen(ScraperSearchParams& from);
void stop(); void stop();
inline SearchType getSearchType() const { return mSearchType; } SearchType getSearchType() const { return mSearchType; }
bool getSavedNewMedia() bool getSavedNewMedia()
{ return (mMDResolveHandle ? mMDResolveHandle->getSavedNewMedia() : false); }; {
return (mMDResolveHandle ? mMDResolveHandle->getSavedNewMedia() : false);
}
static bool saveMetadata(const ScraperSearchResult& result, static bool saveMetadata(const ScraperSearchResult& result,
MetaDataList& metadata, FileData* scrapedGame); MetaDataList& metadata,
FileData* scrapedGame);
// Metadata assets will be resolved before calling the accept callback // Metadata assets will be resolved before calling the accept callback.
// (e.g. result.mdl's "image" is automatically downloaded and properly set). void setAcceptCallback(const std::function<void(const ScraperSearchResult&)>& acceptCallback)
inline void setAcceptCallback(const std::function<void(const ScraperSearchResult&)>& {
acceptCallback) { mAcceptCallback = acceptCallback; } mAcceptCallback = acceptCallback;
inline void setSkipCallback(const std::function<void()>& }
skipCallback) { mSkipCallback = skipCallback; }; void setSkipCallback(const std::function<void()>& skipCallback)
inline void setCancelCallback(const std::function<void()>& {
cancelCallback) { mScrapeCount -= 1; mCancelCallback = cancelCallback; } mSkipCallback = skipCallback;
}
void setCancelCallback(const std::function<void()>& cancelCallback)
{
mScrapeCount -= 1;
mCancelCallback = cancelCallback;
}
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override; void update(int deltaTime) override;
@ -68,20 +77,19 @@ public:
std::vector<HelpPrompt> getHelpPrompts() override; std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override; HelpStyle getHelpStyle() override;
void onSizeChanged() override; void onSizeChanged() override;
void onFocusGained() override;
void onFocusLost() override;
void unsetRefinedSearch() { mRefinedSearch = false; } void unsetRefinedSearch() { mRefinedSearch = false; }
void onFocusGained() override { mGrid.onFocusGained(); }
void onFocusLost() override { mGrid.onFocusLost(); }
private: private:
void updateViewStyle(); void updateViewStyle();
void updateThumbnail(); void updateThumbnail();
void updateInfoPane(); void updateInfoPane();
void resizeMetadata(); void resizeMetadata();
void onSearchError(const std::string& error, HttpReq::Status status = void onSearchError(const std::string& error,
HttpReq::REQ_UNDEFINED_ERROR); HttpReq::Status status = HttpReq::REQ_UNDEFINED_ERROR);
void onSearchDone(const std::vector<ScraperSearchResult>& results); void onSearchDone(const std::vector<ScraperSearchResult>& results);
int getSelectedIndex(); int getSelectedIndex();
@ -117,8 +125,13 @@ private:
bool resize; bool resize;
MetaDataPair(const std::shared_ptr<TextComponent>& f, MetaDataPair(const std::shared_ptr<TextComponent>& f,
const std::shared_ptr<GuiComponent>& s, bool r = true) const std::shared_ptr<GuiComponent>& s,
: first(f), second(s), resize(r) {}; bool r = true)
: first(f)
, second(s)
, resize(r)
{
}
}; };
std::vector<MetaDataPair> mMD_Pairs; std::vector<MetaDataPair> mMD_Pairs;

View file

@ -9,54 +9,55 @@
#include "guis/GuiScreensaverOptions.h" #include "guis/GuiScreensaverOptions.h"
#include "Settings.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "components/SliderComponent.h" #include "components/SliderComponent.h"
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "Settings.h"
GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& title) GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& title)
: GuiSettings(window, title) : GuiSettings(window, title)
{ {
// Screensaver timer. // Screensaver timer.
auto screensaver_timer = std::make_shared<SliderComponent>(mWindow, 0.f, 30.f, 1.f, "m"); auto screensaver_timer = std::make_shared<SliderComponent>(mWindow, 0.0f, 30.0f, 1.0f, "m");
screensaver_timer->setValue(static_cast<float>(Settings::getInstance()-> screensaver_timer->setValue(
getInt("ScreensaverTimer") / (1000 * 60))); static_cast<float>(Settings::getInstance()->getInt("ScreensaverTimer") / (1000 * 60)));
addWithLabel("START SCREENSAVER AFTER (MINUTES)", screensaver_timer); addWithLabel("START SCREENSAVER AFTER (MINUTES)", screensaver_timer);
addSaveFunc([screensaver_timer, this] { addSaveFunc([screensaver_timer, this] {
if (static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60)) != if (static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60)) !=
Settings::getInstance()->getInt("ScreensaverTimer")) { Settings::getInstance()->getInt("ScreensaverTimer")) {
Settings::getInstance()->setInt("ScreensaverTimer", Settings::getInstance()->setInt(
static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60))); "ScreensaverTimer",
static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60)));
setNeedsSaving(); setNeedsSaving();
} }
}); });
// Screensaver type. // Screensaver type.
auto screensaver_type = std::make_shared<OptionListComponent<std::string>> auto screensaver_type = std::make_shared<OptionListComponent<std::string>>(
(mWindow, getHelpStyle(), "SCREENSAVER TYPE", false); mWindow, getHelpStyle(), "SCREENSAVER TYPE", false);
std::vector<std::string> screensavers; std::vector<std::string> screensavers;
screensavers.push_back("dim"); screensavers.push_back("dim");
screensavers.push_back("black"); screensavers.push_back("black");
screensavers.push_back("slideshow"); screensavers.push_back("slideshow");
screensavers.push_back("video"); screensavers.push_back("video");
for (auto it = screensavers.cbegin(); it != screensavers.cend(); it++) for (auto it = screensavers.cbegin(); it != screensavers.cend(); it++)
screensaver_type->add(*it, *it, Settings::getInstance()-> screensaver_type->add(*it, *it,
getString("ScreensaverType") == *it); Settings::getInstance()->getString("ScreensaverType") == *it);
addWithLabel("SCREENSAVER TYPE", screensaver_type); addWithLabel("SCREENSAVER TYPE", screensaver_type);
addSaveFunc([screensaver_type, this] { addSaveFunc([screensaver_type, this] {
if (screensaver_type->getSelected() != if (screensaver_type->getSelected() !=
Settings::getInstance()->getString("ScreensaverType")) { Settings::getInstance()->getString("ScreensaverType")) {
if (screensaver_type->getSelected() == "video") { if (screensaver_type->getSelected() == "video") {
// If before it wasn't risky but now there's a risk of problems, show warning. // If before it wasn't risky but now there's a risk of problems, show warning.
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(
"THE 'VIDEO' SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS\n\n" mWindow, getHelpStyle(),
"IF YOU DO NOT HAVE ANY VIDEOS, THE\n" "THE 'VIDEO' SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS\n\n"
"SCREENSAVER WILL DEFAULT TO 'DIM'", "IF YOU DO NOT HAVE ANY VIDEOS, THE\n"
"OK", [] { return; }, "", nullptr, "", nullptr)); "SCREENSAVER WILL DEFAULT TO 'DIM'",
} "OK", [] { return; }, "", nullptr, "", nullptr));
Settings::getInstance()->setString("ScreensaverType", }
screensaver_type->getSelected()); Settings::getInstance()->setString("ScreensaverType", screensaver_type->getSelected());
setNeedsSaving(); setNeedsSaving();
} }
}); });
@ -67,9 +68,9 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string&
addWithLabel("ENABLE SCREENSAVER CONTROLS", screensaver_controls); addWithLabel("ENABLE SCREENSAVER CONTROLS", screensaver_controls);
addSaveFunc([screensaver_controls, this] { addSaveFunc([screensaver_controls, this] {
if (screensaver_controls->getState() != if (screensaver_controls->getState() !=
Settings::getInstance()->getBool("ScreensaverControls")) { Settings::getInstance()->getBool("ScreensaverControls")) {
Settings::getInstance()->setBool("ScreensaverControls", Settings::getInstance()->setBool("ScreensaverControls",
screensaver_controls->getState()); screensaver_controls->getState());
setNeedsSaving(); setNeedsSaving();
} }
}); });
@ -77,19 +78,21 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string&
// Show filtered menu. // Show filtered menu.
ComponentListRow row; ComponentListRow row;
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, row.addElement(std::make_shared<TextComponent>(mWindow, "SLIDESHOW SCREENSAVER SETTINGS",
"SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind( row.makeAcceptInputHandler(
&GuiScreensaverOptions::openSlideshowScreensaverOptions, this)); std::bind(&GuiScreensaverOptions::openSlideshowScreensaverOptions, this));
addRow(row); addRow(row);
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, row.addElement(std::make_shared<TextComponent>(mWindow, "VIDEO SCREENSAVER SETTINGS",
"VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind( row.makeAcceptInputHandler(
&GuiScreensaverOptions::openVideoScreensaverOptions, this)); std::bind(&GuiScreensaverOptions::openVideoScreensaverOptions, this));
addRow(row); addRow(row);
} }
@ -99,104 +102,105 @@ void GuiScreensaverOptions::openSlideshowScreensaverOptions()
// Timer for swapping images (in seconds). // Timer for swapping images (in seconds).
auto screensaver_swap_image_timeout = auto screensaver_swap_image_timeout =
std::make_shared<SliderComponent>(mWindow, 2.f, 120.f, 2.f, "s"); std::make_shared<SliderComponent>(mWindow, 2.0f, 120.0f, 2.0f, "s");
screensaver_swap_image_timeout->setValue(static_cast<float>(Settings::getInstance()-> screensaver_swap_image_timeout->setValue(static_cast<float>(
getInt("ScreensaverSwapImageTimeout") / (1000))); Settings::getInstance()->getInt("ScreensaverSwapImageTimeout") / (1000)));
s->addWithLabel("SWAP IMAGES AFTER (SECONDS)", screensaver_swap_image_timeout); s->addWithLabel("SWAP IMAGES AFTER (SECONDS)", screensaver_swap_image_timeout);
s->addSaveFunc([screensaver_swap_image_timeout, s] { s->addSaveFunc([screensaver_swap_image_timeout, s] {
if (screensaver_swap_image_timeout->getValue() != if (screensaver_swap_image_timeout->getValue() !=
static_cast<float>(Settings::getInstance()-> static_cast<float>(Settings::getInstance()->getInt("ScreensaverSwapImageTimeout") /
getInt("ScreensaverSwapImageTimeout") / (1000))) { (1000))) {
Settings::getInstance()->setInt("ScreensaverSwapImageTimeout", Settings::getInstance()->setInt(
static_cast<int>(std::round(screensaver_swap_image_timeout->getValue()) * "ScreensaverSwapImageTimeout",
(1000))); static_cast<int>(std::round(screensaver_swap_image_timeout->getValue()) * (1000)));
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Stretch images to screen resolution. // Stretch images to screen resolution.
auto screensaver_stretch_images = std::make_shared<SwitchComponent>(mWindow); auto screensaver_stretch_images = std::make_shared<SwitchComponent>(mWindow);
screensaver_stretch_images-> screensaver_stretch_images->setState(
setState(Settings::getInstance()->getBool("ScreensaverStretchImages")); Settings::getInstance()->getBool("ScreensaverStretchImages"));
s->addWithLabel("STRETCH IMAGES TO SCREEN RESOLUTION", screensaver_stretch_images); s->addWithLabel("STRETCH IMAGES TO SCREEN RESOLUTION", screensaver_stretch_images);
s->addSaveFunc([screensaver_stretch_images, s] { s->addSaveFunc([screensaver_stretch_images, s] {
if (screensaver_stretch_images->getState() != if (screensaver_stretch_images->getState() !=
Settings::getInstance()->getBool("ScreensaverStretchImages")) { Settings::getInstance()->getBool("ScreensaverStretchImages")) {
Settings::getInstance()->setBool("ScreensaverStretchImages", Settings::getInstance()->setBool("ScreensaverStretchImages",
screensaver_stretch_images->getState()); screensaver_stretch_images->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Show game info overlay for slideshow screensaver. // Show game info overlay for slideshow screensaver.
auto screensaver_slideshow_game_info = std::make_shared<SwitchComponent>(mWindow); auto screensaver_slideshow_game_info = std::make_shared<SwitchComponent>(mWindow);
screensaver_slideshow_game_info-> screensaver_slideshow_game_info->setState(
setState(Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")); Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo"));
s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_slideshow_game_info); s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_slideshow_game_info);
s->addSaveFunc([screensaver_slideshow_game_info, s] { s->addSaveFunc([screensaver_slideshow_game_info, s] {
if (screensaver_slideshow_game_info->getState() != if (screensaver_slideshow_game_info->getState() !=
Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")) { Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")) {
Settings::getInstance()->setBool("ScreensaverSlideshowGameInfo", Settings::getInstance()->setBool("ScreensaverSlideshowGameInfo",
screensaver_slideshow_game_info->getState()); screensaver_slideshow_game_info->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
// Render scanlines using a shader. // Render scanlines using a shader.
auto screensaver_slideshow_scanlines = std::make_shared<SwitchComponent>(mWindow); auto screensaver_slideshow_scanlines = std::make_shared<SwitchComponent>(mWindow);
screensaver_slideshow_scanlines-> screensaver_slideshow_scanlines->setState(
setState(Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")); Settings::getInstance()->getBool("ScreensaverSlideshowScanlines"));
s->addWithLabel("RENDER SCANLINES", screensaver_slideshow_scanlines); s->addWithLabel("RENDER SCANLINES", screensaver_slideshow_scanlines);
s->addSaveFunc([screensaver_slideshow_scanlines, s] { s->addSaveFunc([screensaver_slideshow_scanlines, s] {
if (screensaver_slideshow_scanlines->getState() != if (screensaver_slideshow_scanlines->getState() !=
Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) { Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) {
Settings::getInstance()->setBool("ScreensaverSlideshowScanlines", Settings::getInstance()->setBool("ScreensaverSlideshowScanlines",
screensaver_slideshow_scanlines->getState()); screensaver_slideshow_scanlines->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
#endif #endif
// Whether to use custom images. // Whether to use custom images.
auto screensaver_slideshow_custom_images = std::make_shared<SwitchComponent>(mWindow); auto screensaver_slideshow_custom_images = std::make_shared<SwitchComponent>(mWindow);
screensaver_slideshow_custom_images->setState(Settings::getInstance()-> screensaver_slideshow_custom_images->setState(
getBool("ScreensaverSlideshowCustomImages")); Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages"));
s->addWithLabel("USE CUSTOM IMAGES", screensaver_slideshow_custom_images); s->addWithLabel("USE CUSTOM IMAGES", screensaver_slideshow_custom_images);
s->addSaveFunc([screensaver_slideshow_custom_images, s] { s->addSaveFunc([screensaver_slideshow_custom_images, s] {
if (screensaver_slideshow_custom_images->getState() != if (screensaver_slideshow_custom_images->getState() !=
Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) { Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) {
Settings::getInstance()->setBool("ScreensaverSlideshowCustomImages", Settings::getInstance()->setBool("ScreensaverSlideshowCustomImages",
screensaver_slideshow_custom_images->getState()); screensaver_slideshow_custom_images->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Whether to recurse the custom image directory. // Whether to recurse the custom image directory.
auto screensaver_slideshow_recurse = std::make_shared<SwitchComponent>(mWindow); auto screensaver_slideshow_recurse = std::make_shared<SwitchComponent>(mWindow);
screensaver_slideshow_recurse->setState(Settings::getInstance()-> screensaver_slideshow_recurse->setState(
getBool("ScreensaverSlideshowRecurse")); Settings::getInstance()->getBool("ScreensaverSlideshowRecurse"));
s->addWithLabel("CUSTOM IMAGE DIRECTORY RECURSIVE SEARCH", screensaver_slideshow_recurse); s->addWithLabel("CUSTOM IMAGE DIRECTORY RECURSIVE SEARCH", screensaver_slideshow_recurse);
s->addSaveFunc([screensaver_slideshow_recurse, s] { s->addSaveFunc([screensaver_slideshow_recurse, s] {
if (screensaver_slideshow_recurse->getState() != if (screensaver_slideshow_recurse->getState() !=
Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")) { Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")) {
Settings::getInstance()->setBool("ScreensaverSlideshowRecurse", Settings::getInstance()->setBool("ScreensaverSlideshowRecurse",
screensaver_slideshow_recurse->getState()); screensaver_slideshow_recurse->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Custom image directory. // Custom image directory.
auto screensaver_slideshow_image_dir = std::make_shared<TextComponent>(mWindow, "", auto screensaver_slideshow_image_dir = std::make_shared<TextComponent>(
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_RIGHT); mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_RIGHT);
s->addEditableTextComponent("CUSTOM IMAGE DIRECTORY", screensaver_slideshow_image_dir, s->addEditableTextComponent(
Settings::getInstance()->getString("ScreensaverSlideshowImageDir"), "CUSTOM IMAGE DIRECTORY", screensaver_slideshow_image_dir,
Settings::getInstance()->getDefaultString("ScreensaverSlideshowImageDir")); Settings::getInstance()->getString("ScreensaverSlideshowImageDir"),
Settings::getInstance()->getDefaultString("ScreensaverSlideshowImageDir"));
s->addSaveFunc([screensaver_slideshow_image_dir, s] { s->addSaveFunc([screensaver_slideshow_image_dir, s] {
if (screensaver_slideshow_image_dir->getValue() != if (screensaver_slideshow_image_dir->getValue() !=
Settings::getInstance()->getString("ScreensaverSlideshowImageDir")) { Settings::getInstance()->getString("ScreensaverSlideshowImageDir")) {
Settings::getInstance()->setString("ScreensaverSlideshowImageDir", Settings::getInstance()->setString("ScreensaverSlideshowImageDir",
screensaver_slideshow_image_dir->getValue()); screensaver_slideshow_image_dir->getValue());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -210,75 +214,75 @@ void GuiScreensaverOptions::openVideoScreensaverOptions()
// Timer for swapping videos (in seconds). // Timer for swapping videos (in seconds).
auto screensaver_swap_video_timeout = auto screensaver_swap_video_timeout =
std::make_shared<SliderComponent>(mWindow, 0.f, 120.f, 2.f, "s"); std::make_shared<SliderComponent>(mWindow, 0.0f, 120.0f, 2.0f, "s");
screensaver_swap_video_timeout->setValue(static_cast<float>(Settings::getInstance()-> screensaver_swap_video_timeout->setValue(static_cast<float>(
getInt("ScreensaverSwapVideoTimeout") / (1000))); Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") / (1000)));
s->addWithLabel("SWAP VIDEOS AFTER (SECONDS)", screensaver_swap_video_timeout); s->addWithLabel("SWAP VIDEOS AFTER (SECONDS)", screensaver_swap_video_timeout);
s->addSaveFunc([screensaver_swap_video_timeout, s] { s->addSaveFunc([screensaver_swap_video_timeout, s] {
if (screensaver_swap_video_timeout->getValue() != if (screensaver_swap_video_timeout->getValue() !=
static_cast<float>(Settings::getInstance()-> static_cast<float>(Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") /
getInt("ScreensaverSwapVideoTimeout") / (1000))) { (1000))) {
Settings::getInstance()->setInt("ScreensaverSwapVideoTimeout", Settings::getInstance()->setInt(
static_cast<int>(std::round(screensaver_swap_video_timeout->getValue()) * "ScreensaverSwapVideoTimeout",
(1000))); static_cast<int>(std::round(screensaver_swap_video_timeout->getValue()) * (1000)));
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Stretch videos to screen resolution. // Stretch videos to screen resolution.
auto screensaver_stretch_videos = std::make_shared<SwitchComponent>(mWindow); auto screensaver_stretch_videos = std::make_shared<SwitchComponent>(mWindow);
screensaver_stretch_videos-> screensaver_stretch_videos->setState(
setState(Settings::getInstance()->getBool("ScreensaverStretchVideos")); Settings::getInstance()->getBool("ScreensaverStretchVideos"));
s->addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", screensaver_stretch_videos); s->addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", screensaver_stretch_videos);
s->addSaveFunc([screensaver_stretch_videos, s] { s->addSaveFunc([screensaver_stretch_videos, s] {
if (screensaver_stretch_videos->getState() != if (screensaver_stretch_videos->getState() !=
Settings::getInstance()->getBool("ScreensaverStretchVideos")) { Settings::getInstance()->getBool("ScreensaverStretchVideos")) {
Settings::getInstance()->setBool("ScreensaverStretchVideos", Settings::getInstance()->setBool("ScreensaverStretchVideos",
screensaver_stretch_videos->getState()); screensaver_stretch_videos->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
// Show game info overlay for video screensaver. // Show game info overlay for video screensaver.
auto screensaver_video_game_info = std::make_shared<SwitchComponent>(mWindow); auto screensaver_video_game_info = std::make_shared<SwitchComponent>(mWindow);
screensaver_video_game_info-> screensaver_video_game_info->setState(
setState(Settings::getInstance()->getBool("ScreensaverVideoGameInfo")); Settings::getInstance()->getBool("ScreensaverVideoGameInfo"));
s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_video_game_info); s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_video_game_info);
s->addSaveFunc([screensaver_video_game_info, s] { s->addSaveFunc([screensaver_video_game_info, s] {
if (screensaver_video_game_info->getState() != if (screensaver_video_game_info->getState() !=
Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) { Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) {
Settings::getInstance()->setBool("ScreensaverVideoGameInfo", Settings::getInstance()->setBool("ScreensaverVideoGameInfo",
screensaver_video_game_info->getState()); screensaver_video_game_info->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
#if defined(_RPI_) #if defined(_RPI_)
// Use OMX player for screensaver. // Use OMX player for screensaver.
auto screensaver_omx_player = std::make_shared<SwitchComponent>(mWindow); auto screensaver_omx_player = std::make_shared<SwitchComponent>(mWindow);
screensaver_omx_player->setState(Settings::getInstance()->getBool("ScreensaverOmxPlayer")); screensaver_omx_player->setState(Settings::getInstance()->getBool("ScreensaverOmxPlayer"));
s->addWithLabel("USE OMX PLAYER FOR SCREENSAVER", screensaver_omx_player); s->addWithLabel("USE OMX PLAYER FOR SCREENSAVER", screensaver_omx_player);
s->addSaveFunc([screensaver_omx_player, s] { s->addSaveFunc([screensaver_omx_player, s] {
if (screensaver_omx_player->getState() != if (screensaver_omx_player->getState() !=
Settings::getInstance()->getBool("ScreensaverOmxPlayer")) { Settings::getInstance()->getBool("ScreensaverOmxPlayer")) {
Settings::getInstance()-> Settings::getInstance()->setBool("ScreensaverOmxPlayer",
setBool("ScreensaverOmxPlayer", screensaver_omx_player->getState()); screensaver_omx_player->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
#endif #endif
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
// Render scanlines using a shader. // Render scanlines using a shader.
auto screensaver_video_scanlines = std::make_shared<SwitchComponent>(mWindow); auto screensaver_video_scanlines = std::make_shared<SwitchComponent>(mWindow);
screensaver_video_scanlines-> screensaver_video_scanlines->setState(
setState(Settings::getInstance()->getBool("ScreensaverVideoScanlines")); Settings::getInstance()->getBool("ScreensaverVideoScanlines"));
s->addWithLabel("RENDER SCANLINES", screensaver_video_scanlines); s->addWithLabel("RENDER SCANLINES", screensaver_video_scanlines);
s->addSaveFunc([screensaver_video_scanlines, s] { s->addSaveFunc([screensaver_video_scanlines, s] {
if (screensaver_video_scanlines->getState() != if (screensaver_video_scanlines->getState() !=
Settings::getInstance()->getBool("ScreensaverVideoScanlines")) { Settings::getInstance()->getBool("ScreensaverVideoScanlines")) {
Settings::getInstance()->setBool("ScreensaverVideoScanlines", Settings::getInstance()->setBool("ScreensaverVideoScanlines",
screensaver_video_scanlines->getState()); screensaver_video_scanlines->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
@ -289,13 +293,13 @@ void GuiScreensaverOptions::openVideoScreensaverOptions()
s->addWithLabel("RENDER BLUR", screensaver_video_blur); s->addWithLabel("RENDER BLUR", screensaver_video_blur);
s->addSaveFunc([screensaver_video_blur, s] { s->addSaveFunc([screensaver_video_blur, s] {
if (screensaver_video_blur->getState() != if (screensaver_video_blur->getState() !=
Settings::getInstance()->getBool("ScreensaverVideoBlur")) { Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
Settings::getInstance()->setBool("ScreensaverVideoBlur", Settings::getInstance()->setBool("ScreensaverVideoBlur",
screensaver_video_blur->getState()); screensaver_video_blur->getState());
s->setNeedsSaving(); s->setNeedsSaving();
} }
}); });
#endif #endif
mWindow->pushGui(s); mWindow->pushGui(s);
} }

View file

@ -10,33 +10,31 @@
#include "guis/GuiSettings.h" #include "guis/GuiSettings.h"
#include "components/HelpComponent.h"
#include "guis/GuiTextEditPopup.h"
#include "views/gamelist/IGameListView.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "Window.h" #include "Window.h"
#include "components/HelpComponent.h"
#include "guis/GuiTextEditPopup.h"
#include "views/ViewController.h"
#include "views/gamelist/IGameListView.h"
GuiSettings::GuiSettings( GuiSettings::GuiSettings(Window* window, std::string title)
Window* window, : GuiComponent(window)
std::string title) , mMenu(window, title)
: GuiComponent(window), , mNeedsSaving(false)
mMenu(window, title), , mNeedsReloadHelpPrompts(false)
mNeedsSaving(false), , mNeedsCollectionsUpdate(false)
mNeedsReloadHelpPrompts(false), , mNeedsSorting(false)
mNeedsCollectionsUpdate(false), , mNeedsSortingCollections(false)
mNeedsSorting(false), , mNeedsResetFilters(false)
mNeedsSortingCollections(false), , mNeedsReloading(false)
mNeedsResetFilters(false), , mNeedsGoToStart(false)
mNeedsReloading(false), , mNeedsGoToSystem(false)
mNeedsGoToStart(false), , mNeedsGoToGroupedCollections(false)
mNeedsGoToSystem(false), , mInvalidateCachedBackground(false)
mNeedsGoToGroupedCollections(false), , mGoToSystem(nullptr)
mInvalidateCachedBackground(false),
mGoToSystem(nullptr)
{ {
addChild(&mMenu); addChild(&mMenu);
mMenu.addButton("BACK", "back", [this] { delete this; }); mMenu.addButton("BACK", "back", [this] { delete this; });
@ -44,11 +42,12 @@ GuiSettings::GuiSettings(
setSize(static_cast<float>(Renderer::getScreenWidth()), setSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f, mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f,
Renderer::getScreenHeight() * 0.13f); Renderer::getScreenHeight() * 0.13f);
} }
GuiSettings::~GuiSettings() GuiSettings::~GuiSettings()
{ {
// Save on exit.
save(); save();
} }
@ -72,11 +71,11 @@ void GuiSettings::save()
} }
if (mNeedsSorting) { if (mNeedsSorting) {
for (auto it = SystemData::sSystemVector.cbegin(); it != for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend();
SystemData::sSystemVector.cend(); it++) { it++) {
if (!(!mNeedsSortingCollections && (*it)->isCollection())) { if (!(!mNeedsSortingCollections && (*it)->isCollection()))
(*it)->sortSystem(true); (*it)->sortSystem(true);
}
// Jump to the first row of the gamelist. // Jump to the first row of the gamelist.
IGameListView* gameList = ViewController::get()->getGameListView((*it)).get(); IGameListView* gameList = ViewController::get()->getGameListView((*it)).get();
gameList->setCursor(gameList->getFirstEntry()); gameList->setCursor(gameList->getFirstEntry());
@ -84,11 +83,10 @@ void GuiSettings::save()
} }
if (mNeedsResetFilters) { if (mNeedsResetFilters) {
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
if ((*it)->getThemeFolder() == "custom-collections") { if ((*it)->getThemeFolder() == "custom-collections") {
for (FileData* customSystem : for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay())
(*it)->getRootFolder()->getChildrenListToDisplay())
customSystem->getSystem()->getIndex()->resetFilters(); customSystem->getSystem()->getIndex()->resetFilters();
} }
(*it)->getIndex()->resetFilters(); (*it)->getIndex()->resetFilters();
@ -125,7 +123,7 @@ void GuiSettings::save()
// these views can behave a bit strange during collection changes so it's better to be on // these views can behave a bit strange during collection changes so it's better to be on
// the safe side. // the safe side.
if (state.getSystem()->isCollection() && if (state.getSystem()->isCollection() &&
state.getSystem()->getThemeFolder() != "custom-collections") { state.getSystem()->getThemeFolder() != "custom-collections") {
ViewController::get()->goToStart(); ViewController::get()->goToStart();
ViewController::get()->goToSystem(SystemData::sSystemVector.front(), false); ViewController::get()->goToSystem(SystemData::sSystemVector.front(), false);
// We don't want to invalidate the cached background when there has been a collection // We don't want to invalidate the cached background when there has been a collection
@ -135,7 +133,7 @@ void GuiSettings::save()
// If the last displayed custom collection was just disabled, then go to start (to the // If the last displayed custom collection was just disabled, then go to start (to the
// system view). // system view).
if (std::find(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(), if (std::find(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(),
state.getSystem()) == SystemData::sSystemVector.end()) { state.getSystem()) == SystemData::sSystemVector.end()) {
ViewController::get()->goToStart(); ViewController::get()->goToStart();
return; return;
} }
@ -152,18 +150,17 @@ void GuiSettings::save()
} }
} }
void GuiSettings::addEditableTextComponent( void GuiSettings::addEditableTextComponent(const std::string label,
const std::string label, std::shared_ptr<GuiComponent> ed,
std::shared_ptr<GuiComponent> ed, std::string value,
std::string value, std::string defaultValue,
std::string defaultValue, bool isPassword)
bool isPassword)
{ {
ComponentListRow row; ComponentListRow row;
row.elements.clear(); row.elements.clear();
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label), auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF); Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
row.addElement(lbl, true); row.addElement(lbl, true);
row.addElement(ed, true); row.addElement(ed, true);
@ -200,11 +197,11 @@ void GuiSettings::addEditableTextComponent(
row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] { row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] {
// Never display the value if it's a password, instead set it to blank. // Never display the value if it's a password, instead set it to blank.
if (isPassword) if (isPassword)
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, mWindow->pushGui(
"", updateVal, false)); new GuiTextEditPopup(mWindow, getHelpStyle(), label, "", updateVal, false));
else else
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, ed->getValue(),
ed->getValue(), updateVal, false)); updateVal, false));
}); });
assert(ed); assert(ed);
addRow(row); addRow(row);
@ -219,15 +216,6 @@ bool GuiSettings::input(InputConfig* config, Input input)
return true; return true;
} }
// Keep code for potential future use.
// if (config->isMappedTo("start", input) && input.value != 0) {
// // Close everything.
// Window* window = mWindow;
// while (window->peekGui() && window->peekGui() != ViewController::get())
// delete window->peekGui();
// return true;
// }
return GuiComponent::input(config, input); return GuiComponent::input(config, input);
} }

View file

@ -11,8 +11,8 @@
#ifndef ES_APP_GUIS_GUI_SETTINGS_H #ifndef ES_APP_GUIS_GUI_SETTINGS_H
#define ES_APP_GUIS_GUI_SETTINGS_H #define ES_APP_GUIS_GUI_SETTINGS_H
#include "components/MenuComponent.h"
#include "SystemData.h" #include "SystemData.h"
#include "components/MenuComponent.h"
// This is just a really simple template for a GUI that calls some save functions when closed. // This is just a really simple template for a GUI that calls some save functions when closed.
class GuiSettings : public GuiComponent class GuiSettings : public GuiComponent
@ -22,29 +22,33 @@ public:
virtual ~GuiSettings(); virtual ~GuiSettings();
void save(); void save();
inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); }; void addRow(const ComponentListRow& row) { mMenu.addRow(row); }
inline void addWithLabel(const std::string& label, void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp)
const std::shared_ptr<GuiComponent>& comp) { mMenu.addWithLabel(label, comp); }; {
void addEditableTextComponent( mMenu.addWithLabel(label, comp);
const std::string label, }
std::shared_ptr<GuiComponent> ed, void addEditableTextComponent(const std::string label,
std::string value, std::shared_ptr<GuiComponent> ed,
std::string defaultValue = "", std::string value,
bool isPassword = false); std::string defaultValue = "",
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }; bool isPassword = false);
void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }
void setNeedsSaving(bool state = true) { mNeedsSaving = state; }; void setNeedsSaving(bool state = true) { mNeedsSaving = state; }
void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; }; void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; }
void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; }; void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; }
void setNeedsSorting() { mNeedsSorting = true; }; void setNeedsSorting() { mNeedsSorting = true; }
void setNeedsSortingCollections() { mNeedsSortingCollections = true; }; void setNeedsSortingCollections() { mNeedsSortingCollections = true; }
void setNeedsResetFilters() { mNeedsResetFilters = true; } void setNeedsResetFilters() { mNeedsResetFilters = true; }
void setNeedsReloading() { mNeedsReloading = true; }; void setNeedsReloading() { mNeedsReloading = true; }
void setNeedsGoToStart() { mNeedsGoToStart = true; }; void setNeedsGoToStart() { mNeedsGoToStart = true; }
void setNeedsGoToSystem(SystemData* goToSystem) void setNeedsGoToSystem(SystemData* goToSystem)
{ mNeedsGoToSystem = true; mGoToSystem = goToSystem; }; {
void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; }; mNeedsGoToSystem = true;
void setInvalidateCachedBackground() { mInvalidateCachedBackground = true; }; mGoToSystem = goToSystem;
};
void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; }
void setInvalidateCachedBackground() { mInvalidateCachedBackground = true; }
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
std::vector<HelpPrompt> getHelpPrompts() override; std::vector<HelpPrompt> getHelpPrompts() override;
@ -53,6 +57,8 @@ public:
private: private:
MenuComponent mMenu; MenuComponent mMenu;
std::vector<std::function<void()>> mSaveFuncs; std::vector<std::function<void()>> mSaveFuncs;
SystemData* mGoToSystem;
bool mNeedsSaving; bool mNeedsSaving;
bool mNeedsReloadHelpPrompts; bool mNeedsReloadHelpPrompts;
bool mNeedsCollectionsUpdate; bool mNeedsCollectionsUpdate;
@ -64,8 +70,6 @@ private:
bool mNeedsGoToSystem; bool mNeedsGoToSystem;
bool mNeedsGoToGroupedCollections; bool mNeedsGoToGroupedCollections;
bool mInvalidateCachedBackground; bool mInvalidateCachedBackground;
SystemData* mGoToSystem;
}; };
#endif // ES_APP_GUIS_GUI_SETTINGS_H #endif // ES_APP_GUIS_GUI_SETTINGS_H

View file

@ -18,13 +18,6 @@
// environment and starts listening to SDL events. // environment and starts listening to SDL events.
// //
#include "guis/GuiDetectDevice.h"
#include "guis/GuiMsgBox.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiLaunchScreen.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "views/ViewController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "EmulationStation.h" #include "EmulationStation.h"
@ -37,6 +30,13 @@
#include "Sound.h" #include "Sound.h"
#include "SystemData.h" #include "SystemData.h"
#include "SystemScreensaver.h" #include "SystemScreensaver.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiDetectDevice.h"
#include "guis/GuiLaunchScreen.h"
#include "guis/GuiMsgBox.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "views/ViewController.h"
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include <SDL2/SDL_main.h> #include <SDL2/SDL_main.h>
@ -56,14 +56,14 @@ bool forceInputConfig = false;
bool settingsNeedSaving = false; bool settingsNeedSaving = false;
enum loadSystemsReturnCode { enum loadSystemsReturnCode {
LOADING_OK, LOADING_OK, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
INVALID_FILE, INVALID_FILE,
NO_ROMS NO_ROMS
}; };
#if defined(_WIN64) #if defined(_WIN64)
enum win64ConsoleType { enum win64ConsoleType {
NO_CONSOLE, NO_CONSOLE, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
PARENT_CONSOLE, PARENT_CONSOLE,
ALLOCATED_CONSOLE ALLOCATED_CONSOLE
}; };
@ -85,7 +85,7 @@ win64ConsoleType outputToConsole(bool allocConsole)
// Try to attach to a parent console process. // Try to attach to a parent console process.
if (AttachConsole(ATTACH_PARENT_PROCESS)) if (AttachConsole(ATTACH_PARENT_PROCESS))
outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
// If there is a parent console process, then attempt to retrieve its handle. // If there is a parent console process, then attempt to retrieve its handle.
if (outputHandle != INVALID_HANDLE_VALUE && outputHandle != nullptr) { if (outputHandle != INVALID_HANDLE_VALUE && outputHandle != nullptr) {
@ -131,11 +131,11 @@ bool parseArgs(int argc, char* argv[])
{ {
Utils::FileSystem::setExePath(argv[0]); Utils::FileSystem::setExePath(argv[0]);
#if defined(_WIN64) #if defined(_WIN64)
// Print any command line output to the console. // Print any command line output to the console.
if (argc > 1) if (argc > 1)
win64ConsoleType consoleType = outputToConsole(false); win64ConsoleType consoleType = outputToConsole(false);
#endif #endif
std::string portableFilePath = Utils::FileSystem::getExePath() + "/portable.txt"; std::string portableFilePath = Utils::FileSystem::getExePath() + "/portable.txt";
@ -145,11 +145,11 @@ bool parseArgs(int argc, char* argv[])
std::cout << "Found portable.txt in the ES-DE executable directory\n"; std::cout << "Found portable.txt in the ES-DE executable directory\n";
std::ifstream portableFile; std::ifstream portableFile;
std::string homePath; std::string homePath;
#if defined(_WIN64) #if defined(_WIN64)
portableFile.open(Utils::String::stringToWideString(portableFilePath).c_str()); portableFile.open(Utils::String::stringToWideString(portableFilePath).c_str());
#else #else
portableFile.open(portableFilePath.c_str()); portableFile.open(portableFilePath.c_str());
#endif #endif
if (!portableFile.fail()) { if (!portableFile.fail()) {
std::string relativePath; std::string relativePath;
getline(portableFile, relativePath); getline(portableFile, relativePath);
@ -159,9 +159,9 @@ bool parseArgs(int argc, char* argv[])
else else
homePath = Utils::FileSystem::getExePath() + "/" + relativePath; homePath = Utils::FileSystem::getExePath() + "/" + relativePath;
#if defined(_WIN64) #if defined(_WIN64)
homePath = Utils::String::replace(homePath, "/", "\\"); homePath = Utils::String::replace(homePath, "/", "\\");
#endif #endif
if (!Utils::FileSystem::exists(homePath)) { if (!Utils::FileSystem::exists(homePath)) {
std::cerr << "Error: Defined home path \"" << homePath << "\" does not exist\n"; std::cerr << "Error: Defined home path \"" << homePath << "\" does not exist\n";
@ -185,18 +185,18 @@ bool parseArgs(int argc, char* argv[])
std::cerr << "Error: No home path supplied with \'--home'\n"; std::cerr << "Error: No home path supplied with \'--home'\n";
return false; return false;
} }
#if defined(_WIN64) #if defined(_WIN64)
if (!Utils::FileSystem::exists(argv[i + 1]) && if (!Utils::FileSystem::exists(argv[i + 1]) &&
(!Utils::FileSystem::driveExists(argv[i + 1]))) { (!Utils::FileSystem::driveExists(argv[i + 1]))) {
#else #else
if (!Utils::FileSystem::exists(argv[i + 1])) { if (!Utils::FileSystem::exists(argv[i + 1])) {
#endif #endif
std::cerr << "Error: Home path \'" << argv[i + 1] << "\' does not exist\n"; std::cerr << "Error: Home path \'" << argv[i + 1] << "\' does not exist\n";
return false; return false;
} }
if (Utils::FileSystem::isRegularFile(argv[i + 1])) { if (Utils::FileSystem::isRegularFile(argv[i + 1])) {
std::cerr << "Error: Home path \'" << argv[i + 1] << std::cerr << "Error: Home path \'" << argv[i + 1]
"\' is a file and not a directory\n"; << "\' is a file and not a directory\n";
return false; return false;
} }
Utils::FileSystem::setHomePath(argv[i + 1]); Utils::FileSystem::setHomePath(argv[i + 1]);
@ -228,16 +228,16 @@ bool parseArgs(int argc, char* argv[])
std::string widthArg = argv[i + 1]; std::string widthArg = argv[i + 1];
std::string heightArg = argv[i + 2]; std::string heightArg = argv[i + 2];
if (widthArg.find_first_not_of("0123456789") != std::string::npos || if (widthArg.find_first_not_of("0123456789") != std::string::npos ||
heightArg.find_first_not_of("0123456789") != std::string::npos) { heightArg.find_first_not_of("0123456789") != std::string::npos) {
std::cerr << "Error: Invalid resolution values supplied.\n"; std::cerr << "Error: Invalid resolution values supplied.\n";
return false; return false;
} }
int width = atoi(argv[i + 1]); int width = atoi(argv[i + 1]);
int height = atoi(argv[i + 2]); int height = atoi(argv[i + 2]);
if (width < 640 || height < 480 || width > 7680 || height > 4320 || if (width < 640 || height < 480 || width > 7680 || height > 4320 ||
height < width / 4 || width < height / 2) { height < width / 4 || width < height / 2) {
std::cerr << "Error: Unsupported resolution " std::cerr << "Error: Unsupported resolution " << width << "x" << height
<< width << "x" << height << " supplied.\n"; << " supplied.\n";
return false; return false;
} }
Settings::getInstance()->setInt("WindowWidth", width); Settings::getInstance()->setInt("WindowWidth", width);
@ -277,7 +277,8 @@ bool parseArgs(int argc, char* argv[])
} }
// On Unix, enable settings for the fullscreen mode. // On Unix, enable settings for the fullscreen mode.
// On macOS and Windows only windowed mode is supported. // On macOS and Windows only windowed mode is supported.
#if defined(__unix__)
#if defined(__unix__)
else if (strcmp(argv[i], "--windowed") == 0) { else if (strcmp(argv[i], "--windowed") == 0) {
Settings::getInstance()->setBool("Windowed", true); Settings::getInstance()->setBool("Windowed", true);
} }
@ -289,7 +290,7 @@ bool parseArgs(int argc, char* argv[])
Settings::getInstance()->setString("FullscreenMode", "borderless"); Settings::getInstance()->setString("FullscreenMode", "borderless");
settingsNeedSaving = true; settingsNeedSaving = true;
} }
#endif #endif
else if (strcmp(argv[i], "--vsync") == 0) { else if (strcmp(argv[i], "--vsync") == 0) {
if (i >= argc - 1) { if (i >= argc - 1) {
std::cerr << "Error: No VSync value supplied.\n"; std::cerr << "Error: No VSync value supplied.\n";
@ -297,7 +298,7 @@ bool parseArgs(int argc, char* argv[])
} }
std::string vSyncValue = argv[i + 1]; std::string vSyncValue = argv[i + 1];
if (vSyncValue != "on" && vSyncValue != "off" && vSyncValue != "1" && if (vSyncValue != "on" && vSyncValue != "off" && vSyncValue != "1" &&
vSyncValue != "0") { vSyncValue != "0") {
std::cerr << "Error: Invalid VSync value supplied.\n"; std::cerr << "Error: Invalid VSync value supplied.\n";
return false; return false;
} }
@ -350,12 +351,12 @@ bool parseArgs(int argc, char* argv[])
Log::setReportingLevel(LogDebug); Log::setReportingLevel(LogDebug);
} }
else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) { else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
std::cout << std::cout << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << "\n";
"EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << "\n";
return false; return false;
} }
else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
std::cout << std::cout <<
// clang-format off
"Usage: emulationstation [options]\n" "Usage: emulationstation [options]\n"
"EmulationStation Desktop Edition, Emulator Front-end\n\n" "EmulationStation Desktop Edition, Emulator Front-end\n\n"
"Options:\n" "Options:\n"
@ -381,6 +382,7 @@ bool parseArgs(int argc, char* argv[])
" --debug Print debug information\n" " --debug Print debug information\n"
" --version, -v Display version information\n" " --version, -v Display version information\n"
" --help, -h Summon a sentient, angry tuba\n"; " --help, -h Summon a sentient, angry tuba\n";
// clang-format on
return false; // Exit after printing help. return false; // Exit after printing help.
} }
else { else {
@ -400,13 +402,13 @@ bool checkApplicationHomeDirectory()
std::string home = Utils::FileSystem::getHomePath(); std::string home = Utils::FileSystem::getHomePath();
std::string applicationHome = home + "/.emulationstation"; std::string applicationHome = home + "/.emulationstation";
if (!Utils::FileSystem::exists(applicationHome)) { if (!Utils::FileSystem::exists(applicationHome)) {
#if defined(_WIN64) #if defined(_WIN64)
std::cout << "First startup, creating application home directory \"" << std::cout << "First startup, creating application home directory \""
Utils::String::replace(applicationHome, "/", "\\") << "\"\n"; << Utils::String::replace(applicationHome, "/", "\\") << "\"\n";
#else #else
std::cout << "First startup, creating application home directory \"" << std::cout << "First startup, creating application home directory \"" << applicationHome
applicationHome << "\"\n"; << "\"\n";
#endif #endif
Utils::FileSystem::createDirectory(applicationHome); Utils::FileSystem::createDirectory(applicationHome);
if (!Utils::FileSystem::exists(applicationHome)) { if (!Utils::FileSystem::exists(applicationHome)) {
std::cerr << "Fatal error: Couldn't create directory, permission problems?\n"; std::cerr << "Fatal error: Couldn't create directory, permission problems?\n";
@ -424,16 +426,16 @@ loadSystemsReturnCode loadSystemConfigFile()
if (SystemData::sSystemVector.size() == 0) { if (SystemData::sSystemVector.size() == 0) {
LOG(LogError) << "No game files were found, make sure that the system directories are " LOG(LogError) << "No game files were found, make sure that the system directories are "
"setup correctly and that the file extensions are supported"; "setup correctly and that the file extensions are supported";
return NO_ROMS; return NO_ROMS;
} }
return LOADING_OK; return LOADING_OK;
} }
// Called on exit, assuming we get far enough to have the log initialized.
void onExit() void onExit()
{ {
// Called on exit, assuming we get far enough to have the log initialized.
Log::close(); Log::close();
} }
@ -443,12 +445,12 @@ int main(int argc, char* argv[])
std::locale::global(std::locale("C")); std::locale::global(std::locale("C"));
#if defined(__APPLE__) #if defined(__APPLE__)
// This is a workaround to disable the incredibly annoying save state functionality in // This is a workaround to disable the incredibly annoying save state functionality in
// macOS which forces a restore of the previous window state. The problem is that this // macOS which forces a restore of the previous window state. The problem is that this
// removes the splash screen on startup and it may have other adverse effects as well. // removes the splash screen on startup and it may have other adverse effects as well.
std::string saveStateDir = Utils::FileSystem::expandHomePath( std::string saveStateDir = Utils::FileSystem::expandHomePath(
"~/Library/Saved Application State/org.es-de.EmulationStation.savedState"); "~/Library/Saved Application State/org.es-de.EmulationStation.savedState");
// Deletion of the state files should normally not be required as there shouldn't be any // Deletion of the state files should normally not be required as there shouldn't be any
// files to begin with. But maybe the files can still be created for unknown reasons // files to begin with. But maybe the files can still be created for unknown reasons
// as macOS really really loves to restore windows. Let's therefore include this deletion // as macOS really really loves to restore windows. Let's therefore include this deletion
@ -465,22 +467,22 @@ int main(int argc, char* argv[])
// the functionality. // the functionality.
std::string chmodCommand = "chmod 500 \"" + saveStateDir + "\""; std::string chmodCommand = "chmod 500 \"" + saveStateDir + "\"";
system(chmodCommand.c_str()); system(chmodCommand.c_str());
#endif #endif
if (!parseArgs(argc, argv)) { if (!parseArgs(argc, argv)) {
#if defined(_WIN64) #if defined(_WIN64)
FreeConsole(); FreeConsole();
#endif #endif
return 0; return 0;
} }
#if defined(_WIN64) #if defined(_WIN64)
// Send debug output to the console.. // Send debug output to the console..
if (Settings::getInstance()->getBool("Debug")) if (Settings::getInstance()->getBool("Debug"))
outputToConsole(true); outputToConsole(true);
#endif #endif
#if defined(_WIN64) #if defined(_WIN64)
// Hide taskbar if the setting for this is enabled. // Hide taskbar if the setting for this is enabled.
bool taskbarStateChanged = false; bool taskbarStateChanged = false;
unsigned int taskbarState; unsigned int taskbarState;
@ -490,12 +492,12 @@ int main(int argc, char* argv[])
taskbarState = getTaskbarState(); taskbarState = getTaskbarState();
hideTaskbar(); hideTaskbar();
} }
#endif #endif
#if defined(FREEIMAGE_LIB)
// Call this ONLY when linking with FreeImage as a static library. // Call this ONLY when linking with FreeImage as a static library.
#if defined(FREEIMAGE_LIB)
FreeImage_Initialise(); FreeImage_Initialise();
#endif #endif
// If ~/.emulationstation doesn't exist and cannot be created, bail. // If ~/.emulationstation doesn't exist and cannot be created, bail.
if (!checkApplicationHomeDirectory()) if (!checkApplicationHomeDirectory())
@ -504,8 +506,8 @@ int main(int argc, char* argv[])
// Start the logger. // Start the logger.
Log::init(); Log::init();
Log::open(); Log::open();
LOG(LogInfo) << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << LOG(LogInfo) << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << ", built "
", built " << PROGRAM_BUILT_STRING; << PROGRAM_BUILT_STRING;
// Always close the log on exit. // Always close the log on exit.
atexit(&onExit); atexit(&onExit);
@ -513,7 +515,7 @@ int main(int argc, char* argv[])
// Check if the configuration file exists, and if not, create it. // Check if the configuration file exists, and if not, create it.
// This should only happen on first application startup. // This should only happen on first application startup.
if (!Utils::FileSystem::exists(Utils::FileSystem::getHomePath() + if (!Utils::FileSystem::exists(Utils::FileSystem::getHomePath() +
"/.emulationstation/es_settings.xml")) { "/.emulationstation/es_settings.xml")) {
LOG(LogInfo) << "Settings file es_settings.xml does not exist, creating it..."; LOG(LogInfo) << "Settings file es_settings.xml does not exist, creating it...";
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();
} }
@ -525,15 +527,15 @@ int main(int argc, char* argv[])
// Check if the application version has changed, which would normally mean that the // Check if the application version has changed, which would normally mean that the
// user has upgraded to a newer release. // user has upgraded to a newer release.
std::string applicationVersion; std::string applicationVersion;
if ((applicationVersion = Settings::getInstance()-> if ((applicationVersion = Settings::getInstance()->getString("ApplicationVersion")) !=
getString("ApplicationVersion")) != PROGRAM_VERSION_STRING) { PROGRAM_VERSION_STRING) {
if (applicationVersion != "") { if (applicationVersion != "") {
LOG(LogInfo) << "Application version changed from previous startup, from \"" << LOG(LogInfo) << "Application version changed from previous startup, from \""
applicationVersion << "\" to \"" << PROGRAM_VERSION_STRING << "\""; << applicationVersion << "\" to \"" << PROGRAM_VERSION_STRING << "\"";
} }
else { else {
LOG(LogInfo) << "Application version setting is blank, changing it to \"" << LOG(LogInfo) << "Application version setting is blank, changing it to \""
PROGRAM_VERSION_STRING << "\""; << PROGRAM_VERSION_STRING << "\"";
} }
Settings::getInstance()->setString("ApplicationVersion", PROGRAM_VERSION_STRING); Settings::getInstance()->setString("ApplicationVersion", PROGRAM_VERSION_STRING);
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();
@ -582,11 +584,11 @@ int main(int argc, char* argv[])
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
return 1; return 1;
#if !defined(__APPLE__)
// This hides the mouse cursor during startup, i.e. before we have begun to capture SDL events. // This hides the mouse cursor during startup, i.e. before we have begun to capture SDL events.
// On macOS this causes the mouse cursor to jump back to the Dock so don't do it on this OS. // On macOS this causes the mouse cursor to jump back to the Dock so don't do it on this OS.
#if !defined(__APPLE__)
SDL_SetRelativeMouseMode(SDL_TRUE); SDL_SetRelativeMouseMode(SDL_TRUE);
#endif #endif
if (splashScreen) { if (splashScreen) {
std::string progressText = "Loading..."; std::string progressText = "Loading...";
@ -625,8 +627,8 @@ int main(int argc, char* argv[])
// Open the input configuration GUI if the flag to force this was passed from the command line. // Open the input configuration GUI if the flag to force this was passed from the command line.
if (!loadSystemsStatus) { if (!loadSystemsStatus) {
if (forceInputConfig) { if (forceInputConfig) {
window.pushGui(new GuiDetectDevice(&window, false, true, [] { window.pushGui(new GuiDetectDevice(&window, false, true,
ViewController::get()->goToStart(); })); [] { ViewController::get()->goToStart(); }));
} }
else { else {
ViewController::get()->goToStart(); ViewController::get()->goToStart();
@ -639,16 +641,19 @@ int main(int argc, char* argv[])
int lastTime = SDL_GetTicks(); int lastTime = SDL_GetTicks();
const auto applicationEndTime = std::chrono::system_clock::now(); const auto applicationEndTime = std::chrono::system_clock::now();
LOG(LogInfo) << "Application startup time: " << LOG(LogInfo) << "Application startup time: "
std::chrono::duration_cast<std::chrono::milliseconds> << std::chrono::duration_cast<std::chrono::milliseconds>(applicationEndTime -
(applicationEndTime - applicationStartTime).count() << " ms"; applicationStartTime)
.count()
<< " ms";
bool running = true; bool running = true;
#if !defined(__APPLE__)
// Now that we've finished loading, disable the relative mouse mode or otherwise mouse // Now that we've finished loading, disable the relative mouse mode or otherwise mouse
// input wouldn't work in any games that are launched. // input wouldn't work in any games that are launched.
#if !defined(__APPLE__)
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
#endif #endif
while (running) { while (running) {
if (SDL_PollEvent(&event)) { if (SDL_PollEvent(&event)) {
@ -657,8 +662,8 @@ int main(int argc, char* argv[])
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
running = false; running = false;
}
while (SDL_PollEvent(&event)); } while (SDL_PollEvent(&event));
} }
if (window.isSleeping()) { if (window.isSleeping()) {
@ -694,24 +699,24 @@ int main(int argc, char* argv[])
NavigationSounds::getInstance()->deinit(); NavigationSounds::getInstance()->deinit();
Settings::deinit(); Settings::deinit();
#if defined(FREEIMAGE_LIB)
// Call this ONLY when linking with FreeImage as a static library. // Call this ONLY when linking with FreeImage as a static library.
#if defined(FREEIMAGE_LIB)
FreeImage_DeInitialise(); FreeImage_DeInitialise();
#endif #endif
#if defined(_WIN64) #if defined(_WIN64)
// If the taskbar state was changed (taskbar was hidden), then revert it. // If the taskbar state was changed (taskbar was hidden), then revert it.
if (taskbarStateChanged) if (taskbarStateChanged)
revertTaskbarState(taskbarState); revertTaskbarState(taskbarState);
#endif #endif
processQuitMode(); processQuitMode();
LOG(LogInfo) << "EmulationStation cleanly shutting down"; LOG(LogInfo) << "EmulationStation cleanly shutting down";
#if defined(_WIN64) #if defined(_WIN64)
FreeConsole(); FreeConsole();
#endif #endif
return 0; return 0;
} }

View file

@ -10,20 +10,20 @@
#include "scrapers/GamesDBJSONScraper.h" #include "scrapers/GamesDBJSONScraper.h"
#include "scrapers/GamesDBJSONScraperResources.h" #include "scrapers/GamesDBJSONScraperResources.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
#include "FileData.h" #include "FileData.h"
#include "Log.h" #include "Log.h"
#include "MameNames.h" #include "MameNames.h"
#include "PlatformId.h" #include "PlatformId.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <exception> #include <exception>
#include <map> #include <map>
#include <pugixml.hpp> #include <pugixml.hpp>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
using namespace PlatformIds; using namespace PlatformIds;
using namespace rapidjson; using namespace rapidjson;
@ -103,10 +103,10 @@ const std::map<PlatformId, std::string> gamesdb_new_platformid_map {
{ SONY_PLAYSTATION_PORTABLE, "13" }, { SONY_PLAYSTATION_PORTABLE, "13" },
{ SUPER_NINTENDO, "6" }, { SUPER_NINTENDO, "6" },
{ SHARP_X1, "4977" }, { SHARP_X1, "4977" },
{ SHARP_X68000, "4931"}, { SHARP_X68000, "4931" },
{ NEC_SUPERGRAFX, "34" }, { NEC_SUPERGRAFX, "34" },
{ NEC_PC_8800, "4933"}, { NEC_PC_8800, "4933" },
{ NEC_PC_9800, "4934"}, { NEC_PC_9800, "4934" },
{ NEC_PC_ENGINE, "34" }, { NEC_PC_ENGINE, "34" },
{ NEC_PC_ENGINE_CD, "4955" }, { NEC_PC_ENGINE_CD, "4955" },
{ BANDAI_WONDERSWAN, "4925" }, { BANDAI_WONDERSWAN, "4925" },
@ -118,9 +118,10 @@ const std::map<PlatformId, std::string> gamesdb_new_platformid_map {
{ TANDY_TRS80, "4941" }, { TANDY_TRS80, "4941" },
}; };
void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, void thegamesdb_generate_json_scraper_requests(
std::queue<std::unique_ptr<ScraperRequest>>& requests, const ScraperSearchParams& params,
std::vector<ScraperSearchResult>& results) std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::vector<ScraperSearchResult>& results)
{ {
resources.prepare(); resources.prepare();
std::string path = "https://api.thegamesdb.net/v1"; std::string path = "https://api.thegamesdb.net/v1";
@ -168,8 +169,8 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params
if (!platforms.empty()) { if (!platforms.empty()) {
bool first = true; bool first = true;
platformQueryParam += "&filter%5Bplatform%5D="; platformQueryParam += "&filter%5Bplatform%5D=";
for (auto platformIt = platforms.cbegin(); for (auto platformIt = platforms.cbegin(); // Line break.
platformIt != platforms.cend(); platformIt++) { platformIt != platforms.cend(); platformIt++) {
auto mapIt = gamesdb_new_platformid_map.find(*platformIt); auto mapIt = gamesdb_new_platformid_map.find(*platformIt);
if (mapIt != gamesdb_new_platformid_map.cend()) { if (mapIt != gamesdb_new_platformid_map.cend()) {
if (!first) if (!first)
@ -178,8 +179,9 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params
first = false; first = false;
} }
else { else {
LOG(LogWarning) << "TheGamesDB scraper: No support for platform \"" << LOG(LogWarning)
getPlatformName(*platformIt) << "\", search will be inaccurate"; << "TheGamesDB scraper: No support for platform \""
<< getPlatformName(*platformIt) << "\", search will be inaccurate";
} }
} }
path += platformQueryParam; path += platformQueryParam;
@ -188,15 +190,15 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params
LOG(LogWarning) << "TheGamesDB scraper: No platform defined, search will be inaccurate"; LOG(LogWarning) << "TheGamesDB scraper: No platform defined, search will be inaccurate";
} }
requests.push(std::unique_ptr<ScraperRequest> requests.push(
(new TheGamesDBJSONRequest(requests, results, path))); std::unique_ptr<ScraperRequest>(new TheGamesDBJSONRequest(requests, results, path)));
} }
} }
void thegamesdb_generate_json_scraper_requests( void thegamesdb_generate_json_scraper_requests(
const std::string& gameIDs, const std::string& gameIDs,
std::queue<std::unique_ptr<ScraperRequest>>& requests, std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::vector<ScraperSearchResult>& results) std::vector<ScraperSearchResult>& results)
{ {
resources.prepare(); resources.prepare();
std::string path = "https://api.thegamesdb.net/v1"; std::string path = "https://api.thegamesdb.net/v1";
@ -204,158 +206,158 @@ void thegamesdb_generate_json_scraper_requests(
path += "/Games/Images/GamesImages?" + apiKey + "&games_id=" + gameIDs; path += "/Games/Images/GamesImages?" + apiKey + "&games_id=" + gameIDs;
requests.push(std::unique_ptr<ScraperRequest> requests.push(
(new TheGamesDBJSONRequest(requests, results, path))); std::unique_ptr<ScraperRequest>(new TheGamesDBJSONRequest(requests, results, path)));
} }
namespace namespace
{ {
std::string getStringOrThrow(const Value& v, const std::string& key)
std::string getStringOrThrow(const Value& v, const std::string& key) {
{ if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) {
if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) { throw std::runtime_error(
throw std::runtime_error(
"rapidjson internal assertion failure: missing or non string key:" + key); "rapidjson internal assertion failure: missing or non string key:" + key);
}
return v[key.c_str()].GetString();
} }
return v[key.c_str()].GetString();
}
int getIntOrThrow(const Value& v, const std::string& key) int getIntOrThrow(const Value& v, const std::string& key)
{ {
if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) { if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) {
throw std::runtime_error( throw std::runtime_error(
"rapidjson internal assertion failure: missing or non int key:" + key); "rapidjson internal assertion failure: missing or non int key:" + key);
} }
return v[key.c_str()].GetInt(); return v[key.c_str()].GetInt();
}
int getIntOrThrow(const Value& v)
{
if (!v.IsInt()) {
throw std::runtime_error("rapidjson internal assertion failure: not an int");
}
return v.GetInt();
}
std::string getDeveloperString(const Value& v)
{
if (!v.IsArray())
return "";
std::string out = "";
bool first = true;
for (int i = 0; i < static_cast<int>(v.Size()); i++) {
auto mapIt = resources.gamesdb_new_developers_map.find(getIntOrThrow(v[i]));
if (mapIt == resources.gamesdb_new_developers_map.cend())
continue;
if (!first)
out += ", ";
out += mapIt->second;
first = false;
}
return out;
}
std::string getPublisherString(const Value& v)
{
if (!v.IsArray())
return "";
std::string out = "";
bool first = true;
for (int i = 0; i < static_cast<int>(v.Size()); i++) {
auto mapIt = resources.gamesdb_new_publishers_map.find(getIntOrThrow(v[i]));
if (mapIt == resources.gamesdb_new_publishers_map.cend())
continue;
if (!first)
out += ", ";
out += mapIt->second;
first = false;
}
return out;
}
std::string getGenreString(const Value& v)
{
if (!v.IsArray())
return "";
std::string out = "";
bool first = true;
for (int i = 0; i < static_cast<int>(v.Size()); i++) {
auto mapIt = resources.gamesdb_new_genres_map.find(getIntOrThrow(v[i]));
if (mapIt == resources.gamesdb_new_genres_map.cend())
continue;
if (!first)
out += ", ";
out += mapIt->second;
first = false;
}
return out;
}
void processGame(const Value& game, std::vector<ScraperSearchResult>& results)
{
ScraperSearchResult result;
if (game.HasMember("id") && game["id"].IsInt())
result.gameID = std::to_string(getIntOrThrow(game, "id"));
result.mdl.set("name", getStringOrThrow(game, "game_title"));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Name: " << result.mdl.get("name");
if (game.HasMember("overview") && game["overview"].IsString())
result.mdl.set("desc", Utils::String::replace(game["overview"].GetString(), "\r", ""));
if (game.HasMember("release_date") && game["release_date"].IsString()) {
result.mdl.set("releasedate", Utils::Time::DateTime(Utils::Time::stringToTime(
game["release_date"].GetString(), "%Y-%m-%d")));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (unparsed): " <<
game["release_date"].GetString();
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (parsed): " <<
result.mdl.get("releasedate");
} }
if (game.HasMember("developers") && game["developers"].IsArray()) { int getIntOrThrow(const Value& v)
result.mdl.set("developer", getDeveloperString(game["developers"])); {
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Developer: " << if (!v.IsInt()) {
result.mdl.get("developer"); throw std::runtime_error("rapidjson internal assertion failure: not an int");
}
return v.GetInt();
} }
if (game.HasMember("publishers") && game["publishers"].IsArray()) { std::string getDeveloperString(const Value& v)
result.mdl.set("publisher", getPublisherString(game["publishers"])); {
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Publisher: " << if (!v.IsArray())
result.mdl.get("publisher"); return "";
std::string out = "";
bool first = true;
for (int i = 0; i < static_cast<int>(v.Size()); i++) {
auto mapIt = resources.gamesdb_new_developers_map.find(getIntOrThrow(v[i]));
if (mapIt == resources.gamesdb_new_developers_map.cend())
continue;
if (!first)
out += ", ";
out += mapIt->second;
first = false;
}
return out;
} }
if (game.HasMember("genres") && game["genres"].IsArray()) { std::string getPublisherString(const Value& v)
result.mdl.set("genre", getGenreString(game["genres"])); {
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Genre: " << if (!v.IsArray())
result.mdl.get("genre"); return "";
std::string out = "";
bool first = true;
for (int i = 0; i < static_cast<int>(v.Size()); i++) {
auto mapIt = resources.gamesdb_new_publishers_map.find(getIntOrThrow(v[i]));
if (mapIt == resources.gamesdb_new_publishers_map.cend())
continue;
if (!first)
out += ", ";
out += mapIt->second;
first = false;
}
return out;
} }
if (game.HasMember("players") && game["players"].IsInt()) { std::string getGenreString(const Value& v)
result.mdl.set("players", std::to_string(game["players"].GetInt())); {
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Players: " << if (!v.IsArray())
result.mdl.get("players"); return "";
std::string out = "";
bool first = true;
for (int i = 0; i < static_cast<int>(v.Size()); i++) {
auto mapIt = resources.gamesdb_new_genres_map.find(getIntOrThrow(v[i]));
if (mapIt == resources.gamesdb_new_genres_map.cend())
continue;
if (!first)
out += ", ";
out += mapIt->second;
first = false;
}
return out;
} }
result.mediaURLFetch = NOT_STARTED; void processGame(const Value& game, std::vector<ScraperSearchResult>& results)
results.push_back(result); {
} ScraperSearchResult result;
if (game.HasMember("id") && game["id"].IsInt())
result.gameID = std::to_string(getIntOrThrow(game, "id"));
result.mdl.set("name", getStringOrThrow(game, "game_title"));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Name: " << result.mdl.get("name");
if (game.HasMember("overview") && game["overview"].IsString())
result.mdl.set("desc", Utils::String::replace(game["overview"].GetString(), "\r", ""));
if (game.HasMember("release_date") && game["release_date"].IsString()) {
result.mdl.set("releasedate", Utils::Time::DateTime(Utils::Time::stringToTime(
game["release_date"].GetString(), "%Y-%m-%d")));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (unparsed): "
<< game["release_date"].GetString();
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (parsed): "
<< result.mdl.get("releasedate");
}
if (game.HasMember("developers") && game["developers"].IsArray()) {
result.mdl.set("developer", getDeveloperString(game["developers"]));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Developer: "
<< result.mdl.get("developer");
}
if (game.HasMember("publishers") && game["publishers"].IsArray()) {
result.mdl.set("publisher", getPublisherString(game["publishers"]));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Publisher: "
<< result.mdl.get("publisher");
}
if (game.HasMember("genres") && game["genres"].IsArray()) {
result.mdl.set("genre", getGenreString(game["genres"]));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Genre: "
<< result.mdl.get("genre");
}
if (game.HasMember("players") && game["players"].IsInt()) {
result.mdl.set("players", std::to_string(game["players"].GetInt()));
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Players: "
<< result.mdl.get("players");
}
result.mediaURLFetch = NOT_STARTED;
results.push_back(result);
}
} // namespace } // namespace
void processMediaURLs(const Value& images, const std::string& base_url, void processMediaURLs(const Value& images,
std::vector<ScraperSearchResult>& results) const std::string& base_url,
std::vector<ScraperSearchResult>& results)
{ {
ScraperSearchResult result; ScraperSearchResult result;
@ -367,9 +369,8 @@ void processMediaURLs(const Value& images, const std::string& base_url,
result.marqueeUrl = ""; result.marqueeUrl = "";
result.screenshotUrl = ""; result.screenshotUrl = "";
// Quite excessive testing for valid values, but you never know // Quite excessive testing for valid values, but you never know what the server has
// what the server has returned and we don't want to crash the // returned and we don't want to crash the program due to malformed data.
// program due to malformed data.
if (gameMedia.IsArray()) { if (gameMedia.IsArray()) {
for (SizeType i = 0; i < gameMedia.Size(); i++) { for (SizeType i = 0; i < gameMedia.Size(); i++) {
std::string mediatype; std::string mediatype;
@ -390,13 +391,13 @@ void processMediaURLs(const Value& images, const std::string& base_url,
result.screenshotUrl = base_url + gameMedia[i]["filename"].GetString(); result.screenshotUrl = base_url + gameMedia[i]["filename"].GetString();
} }
} }
result.mediaURLFetch = COMPLETED; result.mediaURLFetch = COMPLETED;
results.push_back(result); results.push_back(result);
} }
} }
void TheGamesDBJSONRequest::process(const std::unique_ptr<HttpReq>& req, void TheGamesDBJSONRequest::process(const std::unique_ptr<HttpReq>& req,
std::vector<ScraperSearchResult>& results) std::vector<ScraperSearchResult>& results)
{ {
assert(req->status() == HttpReq::REQ_SUCCESS); assert(req->status() == HttpReq::REQ_SUCCESS);
@ -404,9 +405,8 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr<HttpReq>& req,
doc.Parse(req->getContent().c_str()); doc.Parse(req->getContent().c_str());
if (doc.HasParseError()) { if (doc.HasParseError()) {
std::string err = std::string err = std::string("TheGamesDBJSONRequest - Error parsing JSON \n\t") +
std::string("TheGamesDBJSONRequest - Error parsing JSON \n\t") + GetParseError_En(doc.GetParseError());
GetParseError_En(doc.GetParseError());
setError(err); setError(err);
LOG(LogError) << err; LOG(LogError) << err;
return; return;
@ -414,7 +414,7 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr<HttpReq>& req,
// If the response contains the 'images' object, then it's a game media URL request. // If the response contains the 'images' object, then it's a game media URL request.
if (doc.HasMember("data") && doc["data"].HasMember("images") && if (doc.HasMember("data") && doc["data"].HasMember("images") &&
doc["data"]["images"].IsObject()) { doc["data"]["images"].IsObject()) {
const Value& images = doc["data"]["images"]; const Value& images = doc["data"]["images"];
const Value& base_url = doc["data"]["base_url"]; const Value& base_url = doc["data"]["base_url"];
@ -440,19 +440,18 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr<HttpReq>& req,
if (doc.HasMember("remaining_monthly_allowance") && doc.HasMember("extra_allowance")) { if (doc.HasMember("remaining_monthly_allowance") && doc.HasMember("extra_allowance")) {
for (auto i = 0; i < results.size(); i++) { for (auto i = 0; i < results.size(); i++) {
results[i].scraperRequestAllowance = results[i].scraperRequestAllowance =
doc["remaining_monthly_allowance"].GetInt() + doc["remaining_monthly_allowance"].GetInt() + doc["extra_allowance"].GetInt();
doc["extra_allowance"].GetInt();
} }
LOG(LogDebug) << "TheGamesDBJSONRequest::process(): " LOG(LogDebug) << "TheGamesDBJSONRequest::process(): "
"Remaining monthly scraping allowance: " << "Remaining monthly scraping allowance: "
results.back().scraperRequestAllowance; << results.back().scraperRequestAllowance;
} }
return; return;
} }
// These process steps are for the initial scraping response. // These process steps are for the initial scraping response.
if (!doc.HasMember("data") || !doc["data"].HasMember("games") || if (!doc.HasMember("data") || !doc["data"].HasMember("games") ||
!doc["data"]["games"].IsArray()) { !doc["data"]["games"].IsArray()) {
LOG(LogWarning) << "TheGamesDBJSONRequest - Response had no game data\n"; LOG(LogWarning) << "TheGamesDBJSONRequest - Response had no game data\n";
return; return;
} }

View file

@ -12,39 +12,42 @@
#include "scrapers/Scraper.h" #include "scrapers/Scraper.h"
namespace pugi { namespace pugi
{
class xml_document; class xml_document;
} }
void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, void thegamesdb_generate_json_scraper_requests(
std::queue<std::unique_ptr<ScraperRequest>>& requests, const ScraperSearchParams& params,
std::vector<ScraperSearchResult>& results); std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::vector<ScraperSearchResult>& results);
void thegamesdb_generate_json_scraper_requests(const std::string& gameIDs, void thegamesdb_generate_json_scraper_requests(
std::queue<std::unique_ptr<ScraperRequest>>& requests, const std::string& gameIDs,
std::vector<ScraperSearchResult>& results); std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::vector<ScraperSearchResult>& results);
class TheGamesDBJSONRequest : public ScraperHttpRequest class TheGamesDBJSONRequest : public ScraperHttpRequest
{ {
public: public:
// Constructor for a GetGameList request. // Constructor for a GetGameList request.
TheGamesDBJSONRequest( TheGamesDBJSONRequest(std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite,
std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite, std::vector<ScraperSearchResult>& resultsWrite,
std::vector<ScraperSearchResult>& resultsWrite, const std::string& url)
const std::string& url) : ScraperHttpRequest(resultsWrite, url)
: ScraperHttpRequest(resultsWrite, url), , mRequestQueue(&requestsWrite)
mRequestQueue(&requestsWrite)
{ {
} }
// Constructior for a GetGame request // Constructior for a GetGame request
TheGamesDBJSONRequest(std::vector<ScraperSearchResult>& resultsWrite, const std::string& url) TheGamesDBJSONRequest(std::vector<ScraperSearchResult>& resultsWrite, const std::string& url)
: ScraperHttpRequest(resultsWrite, url), mRequestQueue(nullptr) : ScraperHttpRequest(resultsWrite, url)
, mRequestQueue(nullptr)
{ {
} }
protected: protected:
void process(const std::unique_ptr<HttpReq>& req, void process(const std::unique_ptr<HttpReq>& req,
std::vector<ScraperSearchResult>& results) override; std::vector<ScraperSearchResult>& results) override;
bool isGameRequest() { return !mRequestQueue; } bool isGameRequest() { return !mRequestQueue; }
std::queue<std::unique_ptr<ScraperRequest>>* mRequestQueue; std::queue<std::unique_ptr<ScraperRequest>>* mRequestQueue;

View file

@ -14,52 +14,53 @@
#include "scrapers/GamesDBJSONScraperResources.h" #include "scrapers/GamesDBJSONScraperResources.h"
#include "utils/FileSystemUtil.h"
#include "Log.h" #include "Log.h"
#include "utils/FileSystemUtil.h"
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <chrono> #include <chrono>
#include <fstream> #include <fstream>
#include <memory> #include <memory>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <thread> #include <thread>
using namespace rapidjson; using namespace rapidjson;
namespace { namespace
constexpr char GamesDBAPIKey[] = {
constexpr char GamesDBAPIKey[] =
"445fcbc3f32bb2474bc27016b99eb963d318ee3a608212c543b9a79de1041600"; "445fcbc3f32bb2474bc27016b99eb963d318ee3a608212c543b9a79de1041600";
constexpr int MAX_WAIT_MS = 90000; constexpr int MAX_WAIT_MS = 90000;
constexpr int POLL_TIME_MS = 500; constexpr int POLL_TIME_MS = 500;
constexpr int MAX_WAIT_ITER = MAX_WAIT_MS / POLL_TIME_MS; constexpr int MAX_WAIT_ITER = MAX_WAIT_MS / POLL_TIME_MS;
constexpr char SCRAPER_RESOURCES_DIR[] = "scrapers"; constexpr char SCRAPER_RESOURCES_DIR[] = "scrapers";
constexpr char DEVELOPERS_JSON_FILE[] = "gamesdb_developers.json"; constexpr char DEVELOPERS_JSON_FILE[] = "gamesdb_developers.json";
constexpr char PUBLISHERS_JSON_FILE[] = "gamesdb_publishers.json"; constexpr char PUBLISHERS_JSON_FILE[] = "gamesdb_publishers.json";
constexpr char GENRES_JSON_FILE[] = "gamesdb_genres.json"; constexpr char GENRES_JSON_FILE[] = "gamesdb_genres.json";
constexpr char DEVELOPERS_ENDPOINT[] = "/Developers"; constexpr char DEVELOPERS_ENDPOINT[] = "/Developers";
constexpr char PUBLISHERS_ENDPOINT[] = "/Publishers"; constexpr char PUBLISHERS_ENDPOINT[] = "/Publishers";
constexpr char GENRES_ENDPOINT[] = "/Genres"; constexpr char GENRES_ENDPOINT[] = "/Genres";
std::string genFilePath(const std::string& file_name) std::string genFilePath(const std::string& file_name)
{ {
return Utils::FileSystem::getGenericPath(getScrapersResouceDir() + "/" + file_name); return Utils::FileSystem::getGenericPath(getScrapersResouceDir() + "/" + file_name);
} }
void ensureScrapersResourcesDir() void ensureScrapersResourcesDir()
{ {
std::string path = getScrapersResouceDir(); std::string path = getScrapersResouceDir();
if (!Utils::FileSystem::exists(path)) if (!Utils::FileSystem::exists(path))
Utils::FileSystem::createDirectory(path); Utils::FileSystem::createDirectory(path);
} }
} // namespace } // namespace
std::string getScrapersResouceDir() std::string getScrapersResouceDir()
{ {
return Utils::FileSystem::getGenericPath( return Utils::FileSystem::getGenericPath(Utils::FileSystem::getHomePath() +
Utils::FileSystem::getHomePath() + "/.emulationstation/" + SCRAPER_RESOURCES_DIR); "/.emulationstation/" + SCRAPER_RESOURCES_DIR);
} }
std::string TheGamesDBJSONRequestResources::getApiKey() const { return GamesDBAPIKey; } std::string TheGamesDBJSONRequestResources::getApiKey() const { return GamesDBAPIKey; }
@ -69,16 +70,16 @@ void TheGamesDBJSONRequestResources::prepare()
if (checkLoaded()) if (checkLoaded())
return; return;
if (loadResource(gamesdb_new_developers_map, "developers", if (loadResource(gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE)) &&
genFilePath(DEVELOPERS_JSON_FILE)) && !gamesdb_developers_resource_request) !gamesdb_developers_resource_request)
gamesdb_developers_resource_request = fetchResource(DEVELOPERS_ENDPOINT); gamesdb_developers_resource_request = fetchResource(DEVELOPERS_ENDPOINT);
if (loadResource(gamesdb_new_publishers_map, "publishers", if (loadResource(gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE)) &&
genFilePath(PUBLISHERS_JSON_FILE)) && !gamesdb_publishers_resource_request) !gamesdb_publishers_resource_request)
gamesdb_publishers_resource_request = fetchResource(PUBLISHERS_ENDPOINT); gamesdb_publishers_resource_request = fetchResource(PUBLISHERS_ENDPOINT);
if (loadResource(gamesdb_new_genres_map, "genres", if (loadResource(gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE)) &&
genFilePath(GENRES_JSON_FILE)) && !gamesdb_genres_resource_request) !gamesdb_genres_resource_request)
gamesdb_genres_resource_request = fetchResource(GENRES_ENDPOINT); gamesdb_genres_resource_request = fetchResource(GENRES_ENDPOINT);
} }
@ -90,21 +91,22 @@ void TheGamesDBJSONRequestResources::ensureResources()
for (int i = 0; i < MAX_WAIT_ITER; i++) { for (int i = 0; i < MAX_WAIT_ITER; i++) {
if (gamesdb_developers_resource_request && if (gamesdb_developers_resource_request &&
saveResource(gamesdb_developers_resource_request.get(), saveResource(gamesdb_developers_resource_request.get(), gamesdb_new_developers_map,
gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE))) "developers", genFilePath(DEVELOPERS_JSON_FILE)))
gamesdb_developers_resource_request.reset(nullptr); gamesdb_developers_resource_request.reset(nullptr);
if (gamesdb_publishers_resource_request && if (gamesdb_publishers_resource_request &&
saveResource(gamesdb_publishers_resource_request.get(), saveResource(gamesdb_publishers_resource_request.get(), gamesdb_new_publishers_map,
gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE))) "publishers", genFilePath(PUBLISHERS_JSON_FILE)))
gamesdb_publishers_resource_request.reset(nullptr); gamesdb_publishers_resource_request.reset(nullptr);
if (gamesdb_genres_resource_request && saveResource(gamesdb_genres_resource_request.get(), if (gamesdb_genres_resource_request &&
gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE))) saveResource(gamesdb_genres_resource_request.get(), gamesdb_new_genres_map, "genres",
genFilePath(GENRES_JSON_FILE)))
gamesdb_genres_resource_request.reset(nullptr); gamesdb_genres_resource_request.reset(nullptr);
if (!gamesdb_developers_resource_request && !gamesdb_publishers_resource_request && if (!gamesdb_developers_resource_request && !gamesdb_publishers_resource_request &&
!gamesdb_genres_resource_request) !gamesdb_genres_resource_request)
return; return;
std::this_thread::sleep_for(std::chrono::milliseconds(POLL_TIME_MS)); std::this_thread::sleep_for(std::chrono::milliseconds(POLL_TIME_MS));
@ -115,14 +117,13 @@ void TheGamesDBJSONRequestResources::ensureResources()
bool TheGamesDBJSONRequestResources::checkLoaded() bool TheGamesDBJSONRequestResources::checkLoaded()
{ {
return !gamesdb_new_genres_map.empty() && !gamesdb_new_developers_map.empty() && return !gamesdb_new_genres_map.empty() && !gamesdb_new_developers_map.empty() &&
!gamesdb_new_publishers_map.empty(); !gamesdb_new_publishers_map.empty();
} }
bool TheGamesDBJSONRequestResources::saveResource( bool TheGamesDBJSONRequestResources::saveResource(HttpReq* req,
HttpReq* req, std::unordered_map<int, std::string>& resource,
std::unordered_map<int, std::string>& resource, const std::string& resource_name,
const std::string& resource_name, const std::string& file_name)
const std::string& file_name)
{ {
if (req == nullptr) { if (req == nullptr) {
@ -133,8 +134,8 @@ bool TheGamesDBJSONRequestResources::saveResource(
return false; // Not ready: wait some more. return false; // Not ready: wait some more.
} }
if (req->status() != HttpReq::REQ_SUCCESS) { if (req->status() != HttpReq::REQ_SUCCESS) {
LOG(LogError) << "Resource request for " << file_name << LOG(LogError) << "Resource request for " << file_name << " failed:\n\t"
" failed:\n\t" << req->getErrorMsg(); << req->getErrorMsg();
return true; // Request failed, resetting request.. return true; // Request failed, resetting request..
} }
@ -156,10 +157,9 @@ std::unique_ptr<HttpReq> TheGamesDBJSONRequestResources::fetchResource(const std
return std::unique_ptr<HttpReq>(new HttpReq(path)); return std::unique_ptr<HttpReq>(new HttpReq(path));
} }
int TheGamesDBJSONRequestResources::loadResource( int TheGamesDBJSONRequestResources::loadResource(std::unordered_map<int, std::string>& resource,
std::unordered_map<int, std::string>& resource, const std::string& resource_name,
const std::string& resource_name, const std::string& file_name)
const std::string& file_name)
{ {
std::ifstream fin(file_name); std::ifstream fin(file_name);
if (!fin.good()) if (!fin.good())
@ -172,8 +172,8 @@ int TheGamesDBJSONRequestResources::loadResource(
if (doc.HasParseError()) { if (doc.HasParseError()) {
std::string err = std::string("TheGamesDBJSONRequest - " std::string err = std::string("TheGamesDBJSONRequest - "
"Error parsing JSON for resource file ") + file_name + "Error parsing JSON for resource file ") +
":\n\t" + GetParseError_En(doc.GetParseError()); file_name + ":\n\t" + GetParseError_En(doc.GetParseError());
LOG(LogError) << err; LOG(LogError) << err;
return 1; return 1;
} }
@ -189,7 +189,7 @@ int TheGamesDBJSONRequestResources::loadResource(
for (Value::ConstMemberIterator itr = data.MemberBegin(); itr != data.MemberEnd(); itr++) { for (Value::ConstMemberIterator itr = data.MemberBegin(); itr != data.MemberEnd(); itr++) {
auto& entry = itr->value; auto& entry = itr->value;
if (!entry.IsObject() || !entry.HasMember("id") || !entry["id"].IsInt() || if (!entry.IsObject() || !entry.HasMember("id") || !entry["id"].IsInt() ||
!entry.HasMember("name") || !entry["name"].IsString()) !entry.HasMember("name") || !entry["name"].IsString())
continue; continue;
resource[entry["id"].GetInt()] = entry["name"].GetString(); resource[entry["id"].GetInt()] = entry["name"].GetString();

View file

@ -33,20 +33,18 @@ struct TheGamesDBJSONRequestResources {
std::unordered_map<int, std::string> gamesdb_new_publishers_map; std::unordered_map<int, std::string> gamesdb_new_publishers_map;
std::unordered_map<int, std::string> gamesdb_new_genres_map; std::unordered_map<int, std::string> gamesdb_new_genres_map;
private: private:
bool checkLoaded(); bool checkLoaded();
bool saveResource( bool saveResource(HttpReq* req,
HttpReq* req, std::unordered_map<int, std::string>& resource,
std::unordered_map<int, std::string>& resource, const std::string& resource_name,
const std::string& resource_name, const std::string& file_name);
const std::string& file_name); std::unique_ptr<HttpReq> fetchResource(const std::string& endpoint);
std::unique_ptr<HttpReq> fetchResource(const std::string& endpoint);
int loadResource( int loadResource(std::unordered_map<int, std::string>& resource,
std::unordered_map<int, std::string>& resource, const std::string& resource_name,
const std::string& resource_name, const std::string& file_name);
const std::string& file_name);
std::unique_ptr<HttpReq> gamesdb_developers_resource_request; std::unique_ptr<HttpReq> gamesdb_developers_resource_request;
std::unique_ptr<HttpReq> gamesdb_publishers_resource_request; std::unique_ptr<HttpReq> gamesdb_publishers_resource_request;

View file

@ -10,20 +10,20 @@
#include "scrapers/Scraper.h" #include "scrapers/Scraper.h"
#include "utils/StringUtil.h"
#include "FileData.h" #include "FileData.h"
#include "GamesDBJSONScraper.h" #include "GamesDBJSONScraper.h"
#include "Log.h" #include "Log.h"
#include "ScreenScraper.h" #include "ScreenScraper.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "utils/StringUtil.h"
#if defined(_WIN64) #if defined(_WIN64)
#include "views/ViewController.h" #include "views/ViewController.h"
#endif #endif
#include <cmath>
#include <FreeImage.h> #include <FreeImage.h>
#include <cmath>
#include <fstream> #include <fstream>
const std::map<std::string, generate_scraper_requests_func> scraper_request_funcs { const std::map<std::string, generate_scraper_requests_func> scraper_request_funcs {
@ -41,9 +41,9 @@ std::unique_ptr<ScraperSearchHandle> startScraperSearch(const ScraperSearchParam
LOG(LogError) << "Configured scraper (" << name << ") unavailable, scraping aborted"; LOG(LogError) << "Configured scraper (" << name << ") unavailable, scraping aborted";
} }
else { else {
LOG(LogDebug) << "Scraper::startScraperSearch(): Scraping system \"" << LOG(LogDebug) << "Scraper::startScraperSearch(): Scraping system \""
params.system->getName() << "\", game file \"" << << params.system->getName() << "\", game file \""
params.game->getFileName() << "\""; << params.game->getFileName() << "\"";
scraper_request_funcs.at(name)(params, handle->mRequestQueue, handle->mResults); scraper_request_funcs.at(name)(params, handle->mRequestQueue, handle->mResults);
} }
@ -84,12 +84,6 @@ bool isValidConfiguredScraper()
return scraper_request_funcs.find(name) != scraper_request_funcs.end(); return scraper_request_funcs.find(name) != scraper_request_funcs.end();
} }
// ScraperSearchHandle.
ScraperSearchHandle::ScraperSearchHandle()
{
setStatus(ASYNC_IN_PROGRESS);
}
void ScraperSearchHandle::update() void ScraperSearchHandle::update()
{ {
if (mStatus == ASYNC_DONE) if (mStatus == ASYNC_DONE)
@ -128,13 +122,14 @@ void ScraperSearchHandle::update()
// ScraperRequest. // ScraperRequest.
ScraperRequest::ScraperRequest(std::vector<ScraperSearchResult>& resultsWrite) ScraperRequest::ScraperRequest(std::vector<ScraperSearchResult>& resultsWrite)
: mResults(resultsWrite) : mResults(resultsWrite)
{ {
} }
// ScraperHttpRequest. // ScraperHttpRequest.
ScraperHttpRequest::ScraperHttpRequest(std::vector<ScraperSearchResult>& resultsWrite, ScraperHttpRequest::ScraperHttpRequest(std::vector<ScraperSearchResult>& resultsWrite,
const std::string& url) : ScraperRequest(resultsWrite) const std::string& url)
: ScraperRequest(resultsWrite)
{ {
setStatus(ASYNC_IN_PROGRESS); setStatus(ASYNC_IN_PROGRESS);
mReq = std::unique_ptr<HttpReq>(new HttpReq(url)); mReq = std::unique_ptr<HttpReq>(new HttpReq(url));
@ -155,20 +150,21 @@ void ScraperHttpRequest::update()
return; return;
// Everything else is some sort of error. // Everything else is some sort of error.
LOG(LogError) << "ScraperHttpRequest network error (status: " << status<< ") - " LOG(LogError) << "ScraperHttpRequest network error (status: " << status << ") - "
<< mReq->getErrorMsg(); << mReq->getErrorMsg();
setError("Network error: " + mReq->getErrorMsg()); setError("Network error: " + mReq->getErrorMsg());
} }
// Download and write the media files to disk. // Download and write the media files to disk.
std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result, std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result,
const ScraperSearchParams& search) const ScraperSearchParams& search)
{ {
return std::unique_ptr<MDResolveHandle>(new MDResolveHandle(result, search)); return std::unique_ptr<MDResolveHandle>(new MDResolveHandle(result, search));
} }
MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
const ScraperSearchParams& search) : mResult(result) const ScraperSearchParams& search)
: mResult(result)
{ {
struct mediaFileInfoStruct { struct mediaFileInfoStruct {
std::string fileURL; std::string fileURL;
@ -221,10 +217,10 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
mediaFileInfo.existingMediaFile = search.game->getVideoPath(); mediaFileInfo.existingMediaFile = search.game->getVideoPath();
mediaFileInfo.resizeFile = false; mediaFileInfo.resizeFile = false;
scrapeFiles.push_back(mediaFileInfo); scrapeFiles.push_back(mediaFileInfo);
#if defined(_WIN64) #if defined(_WIN64)
// Required due to the idiotic file locking that exists on this operating system. // Required due to the idiotic file locking that exists on this operating system.
ViewController::get()->onStopVideo(); ViewController::get()->onStopVideo();
#endif #endif
} }
for (auto it = scrapeFiles.cbegin(); it != scrapeFiles.cend(); it++) { for (auto it = scrapeFiles.cbegin(); it != scrapeFiles.cend(); it++) {
@ -248,13 +244,12 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
// If there is an existing media file on disk and the setting to overwrite data // If there is an existing media file on disk and the setting to overwrite data
// has been set to no, then don't proceed with downloading or saving a new file. // has been set to no, then don't proceed with downloading or saving a new file.
if (it->existingMediaFile != "" && if (it->existingMediaFile != "" &&
!Settings::getInstance()->getBool("ScraperOverwriteData")) !Settings::getInstance()->getBool("ScraperOverwriteData"))
continue; continue;
// If the image is cached already as the thumbnail, then we don't need // If the image is cached already as the thumbnail, then we don't need
// to download it again, in this case just save it to disk and resize it. // to download it again, in this case just save it to disk and resize it.
if (mResult.thumbnailImageUrl == it->fileURL && if (mResult.thumbnailImageUrl == it->fileURL && mResult.thumbnailImageData.size() > 0) {
mResult.thumbnailImageData.size() > 0) {
// This is just a temporary workaround to avoid saving media files to disk that // This is just a temporary workaround to avoid saving media files to disk that
// are actually just containing error messages from the scraper service. The // are actually just containing error messages from the scraper service. The
@ -265,19 +260,19 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
// are sometimes returned from the scraper service and these can actually be // are sometimes returned from the scraper service and these can actually be
// less than 350 bytes in size. // less than 350 bytes in size.
if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") && if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") &&
mResult.thumbnailImageData.size() < 350) { mResult.thumbnailImageData.size() < 350) {
FIMEMORY* memoryStream = FreeImage_OpenMemory( FIMEMORY* memoryStream =
reinterpret_cast<BYTE*>(&mResult.thumbnailImageData.at(0)), FreeImage_OpenMemory(reinterpret_cast<BYTE*>(&mResult.thumbnailImageData.at(0)),
static_cast<DWORD>(mResult.thumbnailImageData.size())); static_cast<DWORD>(mResult.thumbnailImageData.size()));
FREE_IMAGE_FORMAT imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0); FREE_IMAGE_FORMAT imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0);
FreeImage_CloseMemory(memoryStream); FreeImage_CloseMemory(memoryStream);
if (imageFormat == FIF_UNKNOWN) { if (imageFormat == FIF_UNKNOWN) {
setError("The file \"" + Utils::FileSystem::getFileName(filePath) + setError("The file \"" + Utils::FileSystem::getFileName(filePath) +
"\" returned by the scraper seems to be invalid as it's less than " + "\" returned by the scraper seems to be invalid as it's less than " +
"350 bytes in size"); "350 bytes in size");
return; return;
} }
} }
@ -293,18 +288,18 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
// problems or the MediaDirectory setting points to a file instead of a directory. // problems or the MediaDirectory setting points to a file instead of a directory.
if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(filePath))) { if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(filePath))) {
setError("Media directory does not exist and can't be created. " setError("Media directory does not exist and can't be created. "
"Permission problems?"); "Permission problems?");
LOG(LogError) << "Couldn't create media directory: \"" << LOG(LogError) << "Couldn't create media directory: \""
Utils::FileSystem::getParent(filePath) << "\""; << Utils::FileSystem::getParent(filePath) << "\"";
return; return;
} }
#if defined(_WIN64) #if defined(_WIN64)
std::ofstream stream(Utils::String::stringToWideString(filePath).c_str(), std::ofstream stream(Utils::String::stringToWideString(filePath).c_str(),
std::ios_base::out | std::ios_base::binary); std::ios_base::out | std::ios_base::binary);
#else #else
std::ofstream stream(filePath, std::ios_base::out | std::ios_base::binary); std::ofstream stream(filePath, std::ios_base::out | std::ios_base::binary);
#endif #endif
if (!stream || stream.bad()) { if (!stream || stream.bad()) {
setError("Failed to open path for writing media file.\nPermission error?"); setError("Failed to open path for writing media file.\nPermission error?");
return; return;
@ -331,8 +326,9 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
// If it's not cached, then initiate the download. // If it's not cached, then initiate the download.
else { else {
mFuncs.push_back(ResolvePair(downloadMediaAsync(it->fileURL, filePath, mFuncs.push_back(ResolvePair(downloadMediaAsync(it->fileURL, filePath,
it->existingMediaFile, it->subDirectory, it->resizeFile, mResult.savedNewMedia), it->existingMediaFile, it->subDirectory,
[this, filePath] {})); it->resizeFile, mResult.savedNewMedia),
[this, filePath] {}));
} }
} }
} }
@ -361,37 +357,30 @@ void MDResolveHandle::update()
setStatus(ASYNC_DONE); setStatus(ASYNC_DONE);
} }
std::unique_ptr<MediaDownloadHandle> downloadMediaAsync( std::unique_ptr<MediaDownloadHandle> downloadMediaAsync(const std::string& url,
const std::string& url, const std::string& saveAs,
const std::string& saveAs, const std::string& existingMediaPath,
const std::string& existingMediaPath, const std::string& mediaType,
const std::string& mediaType, const bool resizeFile,
const bool resizeFile, bool& savedNewMedia)
bool& savedNewMedia)
{ {
return std::unique_ptr<MediaDownloadHandle>(new MediaDownloadHandle( return std::unique_ptr<MediaDownloadHandle>(new MediaDownloadHandle(
url, url, saveAs, existingMediaPath, mediaType, resizeFile, savedNewMedia));
saveAs,
existingMediaPath,
mediaType,
resizeFile,
savedNewMedia));
} }
MediaDownloadHandle::MediaDownloadHandle( MediaDownloadHandle::MediaDownloadHandle(const std::string& url,
const std::string& url, const std::string& path,
const std::string& path, const std::string& existingMediaPath,
const std::string& existingMediaPath, const std::string& mediaType,
const std::string& mediaType, const bool resizeFile,
const bool resizeFile, bool& savedNewMedia)
bool& savedNewMedia) : mSavePath(path)
: mSavePath(path), , mExistingMediaFile(existingMediaPath)
mExistingMediaFile(existingMediaPath), , mMediaType(mediaType)
mMediaType(mediaType), , mResizeFile(resizeFile)
mResizeFile(resizeFile), , mReq(new HttpReq(url))
mReq(new HttpReq(url))
{ {
mSavedNewMediaPtr = &savedNewMedia; mSavedNewMediaPtr = &savedNewMedia;
} }
void MediaDownloadHandle::update() void MediaDownloadHandle::update()
@ -421,25 +410,22 @@ void MediaDownloadHandle::update()
// Black/empty images are sometimes returned from the scraper service and these can actually // Black/empty images are sometimes returned from the scraper service and these can actually
// be less than 350 bytes in size. // be less than 350 bytes in size.
if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") && if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") &&
mReq->getContent().size() < 350) { mReq->getContent().size() < 350) {
FREE_IMAGE_FORMAT imageFormat = FIF_UNKNOWN; FREE_IMAGE_FORMAT imageFormat = FIF_UNKNOWN;
if (mMediaType != "videos") { if (mMediaType != "videos") {
std::string imageData = mReq->getContent(); std::string imageData = mReq->getContent();
FIMEMORY* memoryStream = FreeImage_OpenMemory(reinterpret_cast<BYTE*>(&imageData.at(0)),
FIMEMORY* memoryStream = FreeImage_OpenMemory( static_cast<DWORD>(imageData.size()));
reinterpret_cast<BYTE*>(&imageData.at(0)),
static_cast<DWORD>(imageData.size()));
imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0); imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0);
FreeImage_CloseMemory(memoryStream); FreeImage_CloseMemory(memoryStream);
} }
if (imageFormat == FIF_UNKNOWN) { if (imageFormat == FIF_UNKNOWN) {
setError("The file \"" + Utils::FileSystem::getFileName(mSavePath) + setError("The file \"" + Utils::FileSystem::getFileName(mSavePath) +
"\" returned by the scraper seems to be invalid as it's less than " + "\" returned by the scraper seems to be invalid as it's less than " +
"350 bytes in size"); "350 bytes in size");
return; return;
} }
} }
@ -455,17 +441,17 @@ void MediaDownloadHandle::update()
// problems or the MediaDirectory setting points to a file instead of a directory. // problems or the MediaDirectory setting points to a file instead of a directory.
if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(mSavePath))) { if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(mSavePath))) {
setError("Media directory does not exist and can't be created. Permission problems?"); setError("Media directory does not exist and can't be created. Permission problems?");
LOG(LogError) << "Couldn't create media directory: \"" << LOG(LogError) << "Couldn't create media directory: \""
Utils::FileSystem::getParent(mSavePath) << "\""; << Utils::FileSystem::getParent(mSavePath) << "\"";
return; return;
} }
#if defined(_WIN64) #if defined(_WIN64)
std::ofstream stream(Utils::String::stringToWideString(mSavePath).c_str(), std::ofstream stream(Utils::String::stringToWideString(mSavePath).c_str(),
std::ios_base::out | std::ios_base::binary); std::ios_base::out | std::ios_base::binary);
#else #else
std::ofstream stream(mSavePath, std::ios_base::out | std::ios_base::binary); std::ofstream stream(mSavePath, std::ios_base::out | std::ios_base::binary);
#endif #endif
if (!stream || stream.bad()) { if (!stream || stream.bad()) {
setError("Failed to open path for writing media file.\nPermission error?"); setError("Failed to open path for writing media file.\nPermission error?");
return; return;
@ -512,15 +498,16 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
FIBITMAP* image = nullptr; FIBITMAP* image = nullptr;
// Detect the file format. // Detect the file format.
#if defined(_WIN64)
#if defined(_WIN64)
format = FreeImage_GetFileTypeU(Utils::String::stringToWideString(path).c_str(), 0); format = FreeImage_GetFileTypeU(Utils::String::stringToWideString(path).c_str(), 0);
if (format == FIF_UNKNOWN) if (format == FIF_UNKNOWN)
format = FreeImage_GetFIFFromFilenameU(Utils::String::stringToWideString(path).c_str()); format = FreeImage_GetFIFFromFilenameU(Utils::String::stringToWideString(path).c_str());
#else #else
format = FreeImage_GetFileType(path.c_str(), 0); format = FreeImage_GetFileType(path.c_str(), 0);
if (format == FIF_UNKNOWN) if (format == FIF_UNKNOWN)
format = FreeImage_GetFIFFromFilename(path.c_str()); format = FreeImage_GetFIFFromFilename(path.c_str());
#endif #endif
if (format == FIF_UNKNOWN) { if (format == FIF_UNKNOWN) {
LOG(LogError) << "Could not detect filetype for image \"" << path << "\"!"; LOG(LogError) << "Could not detect filetype for image \"" << path << "\"!";
return false; return false;
@ -528,11 +515,11 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
// Make sure we can read this format, and if so, then load it. // Make sure we can read this format, and if so, then load it.
if (FreeImage_FIFSupportsReading(format)) { if (FreeImage_FIFSupportsReading(format)) {
#if defined(_WIN64) #if defined(_WIN64)
image = FreeImage_LoadU(format, Utils::String::stringToWideString(path).c_str()); image = FreeImage_LoadU(format, Utils::String::stringToWideString(path).c_str());
#else #else
image = FreeImage_Load(format, path.c_str()); image = FreeImage_Load(format, path.c_str());
#endif #endif
} }
else { else {
LOG(LogError) << "File format not supported for image \"" << path << "\""; LOG(LogError) << "File format not supported for image \"" << path << "\"";
@ -545,8 +532,8 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
// If the image is smaller than (or the same size as) maxWidth and maxHeight, then don't // If the image is smaller than (or the same size as) maxWidth and maxHeight, then don't
// do any scaling. It doesn't make sense to upscale the image and waste disk space. // do any scaling. It doesn't make sense to upscale the image and waste disk space.
if (maxWidth >= width && maxHeight >= height) { if (maxWidth >= width && maxHeight >= height) {
LOG(LogDebug) << "Scraper::resizeImage(): Saving image \"" << path << LOG(LogDebug) << "Scraper::resizeImage(): Saving image \"" << path
"\" at its original resolution " << width << "x" << height; << "\" at its original resolution " << width << "x" << height;
FreeImage_Unload(image); FreeImage_Unload(image);
return true; return true;
} }
@ -568,7 +555,7 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
// We use Lanczos3 which is the highest quality resampling method available in FreeImage. // We use Lanczos3 which is the highest quality resampling method available in FreeImage.
FIBITMAP* imageRescaled = FreeImage_Rescale(image, static_cast<int>(maxWidth), FIBITMAP* imageRescaled = FreeImage_Rescale(image, static_cast<int>(maxWidth),
static_cast<int>(maxHeight), FILTER_LANCZOS3); static_cast<int>(maxHeight), FILTER_LANCZOS3);
FreeImage_Unload(image); FreeImage_Unload(image);
if (imageRescaled == nullptr) { if (imageRescaled == nullptr) {
@ -576,12 +563,12 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
return false; return false;
} }
#if defined(_WIN64) #if defined(_WIN64)
bool saved = (FreeImage_SaveU(format, imageRescaled, bool saved = (FreeImage_SaveU(format, imageRescaled,
Utils::String::stringToWideString(path).c_str()) != 0); Utils::String::stringToWideString(path).c_str()) != 0);
#else #else
bool saved = (FreeImage_Save(format, imageRescaled, path.c_str()) != 0); bool saved = (FreeImage_Save(format, imageRescaled, path.c_str()) != 0);
#endif #endif
FreeImage_Unload(imageRescaled); FreeImage_Unload(imageRescaled);
if (!saved) { if (!saved) {
@ -589,14 +576,15 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
} }
else { else {
LOG(LogDebug) << "Scraper::resizeImage(): Downscaled image \"" << path << "\" from " LOG(LogDebug) << "Scraper::resizeImage(): Downscaled image \"" << path << "\" from "
<< width << "x" << height << " to " << maxWidth << "x" << maxHeight; << width << "x" << height << " to " << maxWidth << "x" << maxHeight;
} }
return saved; return saved;
} }
std::string getSaveAsPath(const ScraperSearchParams& params, std::string getSaveAsPath(const ScraperSearchParams& params,
const std::string& filetypeSubdirectory, const std::string& extension) const std::string& filetypeSubdirectory,
const std::string& extension)
{ {
const std::string systemsubdirectory = params.system->getName(); const std::string systemsubdirectory = params.system->getName();
const std::string name = Utils::FileSystem::getStem(params.game->getPath()); const std::string name = Utils::FileSystem::getStem(params.game->getPath());
@ -605,7 +593,7 @@ std::string getSaveAsPath(const ScraperSearchParams& params,
// Extract possible subfolders from the path. // Extract possible subfolders from the path.
if (params.system->getSystemEnvData()->mStartPath != "") if (params.system->getSystemEnvData()->mStartPath != "")
subFolders = Utils::String::replace(Utils::FileSystem::getParent(params.game->getPath()), subFolders = Utils::String::replace(Utils::FileSystem::getParent(params.game->getPath()),
params.system->getSystemEnvData()->mStartPath, ""); params.system->getSystemEnvData()->mStartPath, "");
std::string path = FileData::getMediaDirectory(); std::string path = FileData::getMediaDirectory();

View file

@ -27,7 +27,7 @@ class FileData;
class SystemData; class SystemData;
enum downloadStatus { enum downloadStatus {
NOT_STARTED, NOT_STARTED, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
IN_PROGRESS, IN_PROGRESS,
COMPLETED COMPLETED
}; };
@ -40,7 +40,10 @@ struct ScraperSearchParams {
}; };
struct ScraperSearchResult { struct ScraperSearchResult {
ScraperSearchResult() : mdl(GAME_METADATA) {}; ScraperSearchResult()
: mdl(GAME_METADATA)
{
}
MetaDataList mdl; MetaDataList mdl;
std::string gameID; std::string gameID;
@ -73,34 +76,6 @@ struct ScraperSearchResult {
bool savedNewMedia; bool savedNewMedia;
}; };
// So let me explain why I've abstracted this so heavily.
// There are two ways I can think of that you'd want to write a scraper.
// 1. Do some HTTP request(s) -> process it -> return the results.
// 2. Do some local filesystem queries (an offline scraper) -> return the results.
// The first way needs to be asynchronous while it's waiting for the HTTP request to return.
// The second doesn't.
// It would be nice if we could write it like this:
// search = generate_http_request(searchparams);
// wait_until_done(search);
// ... process search ...
// return results;
// We could do this if we used threads. Right now ES doesn't because I'm pretty sure I'll
// fuck it up, and I'm not sure of the performance of threads on the Pi (single-core ARM).
// We could also do this if we used coroutines.
// I can't find a really good cross-platform coroutine library (x86/64/ARM Linux + Windows),
// and I don't want to spend more time chasing libraries than just writing it the long way once.
// So, I did it the "long" way.
// ScraperSearchHandle - one logical search, e.g. "search for mario".
// ScraperRequest - encapsulates some sort of asynchronous request that will ultimately
// return some results.
// ScraperHttpRequest - implementation of ScraperRequest that waits on an HttpReq, then
// processes it with some processing function.
// A scraper search gathers results from (potentially multiple) ScraperRequests. // A scraper search gathers results from (potentially multiple) ScraperRequests.
class ScraperRequest : public AsyncHandle class ScraperRequest : public AsyncHandle
{ {
@ -123,7 +98,7 @@ public:
protected: protected:
virtual void process(const std::unique_ptr<HttpReq>& req, virtual void process(const std::unique_ptr<HttpReq>& req,
std::vector<ScraperSearchResult>& results) = 0; std::vector<ScraperSearchResult>& results) = 0;
private: private:
std::unique_ptr<HttpReq> mReq; std::unique_ptr<HttpReq> mReq;
@ -133,21 +108,20 @@ private:
class ScraperSearchHandle : public AsyncHandle class ScraperSearchHandle : public AsyncHandle
{ {
public: public:
ScraperSearchHandle(); ScraperSearchHandle() { setStatus(ASYNC_IN_PROGRESS); }
void update(); void update();
inline const std::vector<ScraperSearchResult>& getResults() const const std::vector<ScraperSearchResult>& getResults() const
{ {
assert(mStatus != ASYNC_IN_PROGRESS); assert(mStatus != ASYNC_IN_PROGRESS);
return mResults; return mResults;
} }
protected: protected:
friend std::unique_ptr<ScraperSearchHandle> friend std::unique_ptr<ScraperSearchHandle> startScraperSearch(
startScraperSearch(const ScraperSearchParams& params); const ScraperSearchParams& params);
friend std::unique_ptr<ScraperSearchHandle> friend std::unique_ptr<ScraperSearchHandle> startMediaURLsFetch(const std::string& gameIDs);
startMediaURLsFetch(const std::string& gameIDs);
std::queue<std::unique_ptr<ScraperRequest>> mRequestQueue; std::queue<std::unique_ptr<ScraperRequest>> mRequestQueue;
std::vector<ScraperSearchResult> mResults; std::vector<ScraperSearchResult> mResults;
@ -164,9 +138,10 @@ std::vector<std::string> getScraperList();
// Returns true if the scraper configured in the settings is still valid. // Returns true if the scraper configured in the settings is still valid.
bool isValidConfiguredScraper(); bool isValidConfiguredScraper();
typedef void (*generate_scraper_requests_func)(const ScraperSearchParams& params, typedef void (*generate_scraper_requests_func)(
std::queue<std::unique_ptr<ScraperRequest>>& requests, const ScraperSearchParams& params,
std::vector<ScraperSearchResult>& results); std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::vector<ScraperSearchResult>& results);
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -177,8 +152,11 @@ public:
MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search); MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search);
void update() override; void update() override;
inline const ScraperSearchResult& getResult() const const ScraperSearchResult& getResult() const
{ assert(mStatus == ASYNC_DONE); return mResult; } {
assert(mStatus == ASYNC_DONE);
return mResult;
}
bool getSavedNewMedia() { return mResult.savedNewMedia; } bool getSavedNewMedia() { return mResult.savedNewMedia; }
private: private:
@ -191,13 +169,12 @@ private:
class MediaDownloadHandle : public AsyncHandle class MediaDownloadHandle : public AsyncHandle
{ {
public: public:
MediaDownloadHandle( MediaDownloadHandle(const std::string& url,
const std::string& url, const std::string& path,
const std::string& path, const std::string& existingMediaPath,
const std::string& existingMediaPath, const std::string& mediaType,
const std::string& mediaType, const bool resizeFile,
const bool resizeFile, bool& savedNewMedia);
bool& savedNewMedia);
void update() override; void update() override;
@ -207,26 +184,26 @@ private:
std::string mExistingMediaFile; std::string mExistingMediaFile;
std::string mMediaType; std::string mMediaType;
bool mResizeFile; bool mResizeFile;
bool *mSavedNewMediaPtr; bool* mSavedNewMediaPtr;
}; };
// Downloads to the home directory, using this subdirectory structure: // Downloads to the home directory, using this subdirectory structure:
// ".emulationstation/downloaded_media/[system_name]/[media_type]/[game_name].[file_extension]". // ".emulationstation/downloaded_media/[system_name]/[media_type]/[game_name].[file_extension]".
// The subdirectories are automatically created if they do not exist. // The subdirectories are automatically created if they do not exist.
std::string getSaveAsPath(const ScraperSearchParams& params, std::string getSaveAsPath(const ScraperSearchParams& params,
const std::string& filetypeSubdirectory, const std::string& url); const std::string& filetypeSubdirectory,
const std::string& url);
std::unique_ptr<MediaDownloadHandle> downloadMediaAsync( std::unique_ptr<MediaDownloadHandle> downloadMediaAsync(const std::string& url,
const std::string& url, const std::string& saveAs,
const std::string& saveAs, const std::string& existingMediaPath,
const std::string& existingMediaPath, const std::string& mediaType,
const std::string& mediaType, const bool resizeFile,
const bool resizeFile, bool& savedNewMedia);
bool& savedNewMedia);
// Resolves all metadata assets that need to be downloaded. // Resolves all metadata assets that need to be downloaded.
std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result, std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result,
const ScraperSearchParams& search); const ScraperSearchParams& search);
bool resizeImage(const std::string& path, const std::string& mediaType); bool resizeImage(const std::string& path, const std::string& mediaType);

View file

@ -9,14 +9,14 @@
#include "scrapers/ScreenScraper.h" #include "scrapers/ScreenScraper.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
#include "FileData.h" #include "FileData.h"
#include "Log.h" #include "Log.h"
#include "PlatformId.h" #include "PlatformId.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
@ -42,7 +42,7 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
{ ATARI_JAGUAR, 27 }, { ATARI_JAGUAR, 27 },
{ ATARI_JAGUAR_CD, 171 }, { ATARI_JAGUAR_CD, 171 },
{ ATARI_LYNX, 28 }, { ATARI_LYNX, 28 },
{ ATARI_ST, 42}, { ATARI_ST, 42 },
{ ATARI_XE, 43 }, { ATARI_XE, 43 },
{ ATOMISWAVE, 53 }, { ATOMISWAVE, 53 },
{ BBC_MICRO, 37 }, { BBC_MICRO, 37 },
@ -62,7 +62,7 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
{ MSX_TURBO_R, 118 }, { MSX_TURBO_R, 118 },
{ SNK_NEO_GEO, 142 }, { SNK_NEO_GEO, 142 },
{ SNK_NEO_GEO_CD, 142 }, { SNK_NEO_GEO_CD, 142 },
{ SNK_NEO_GEO_POCKET, 25}, { SNK_NEO_GEO_POCKET, 25 },
{ SNK_NEO_GEO_POCKET_COLOR, 82 }, { SNK_NEO_GEO_POCKET_COLOR, 82 },
{ NINTENDO_3DS, 17 }, { NINTENDO_3DS, 17 },
{ NINTENDO_64, 14 }, { NINTENDO_64, 14 },
@ -88,7 +88,7 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
{ NEC_PCFX, 72 }, { NEC_PCFX, 72 },
{ GAMEENGINE_OPENBOR, 214 }, { GAMEENGINE_OPENBOR, 214 },
{ TANGERINE_ORIC, 131 }, { TANGERINE_ORIC, 131 },
{ GAMEENGINE_SCUMMVM, 123}, { GAMEENGINE_SCUMMVM, 123 },
{ SEGA_32X, 19 }, { SEGA_32X, 19 },
{ SEGA_CD, 20 }, { SEGA_CD, 20 },
{ SEGA_DREAMCAST, 23 }, { SEGA_DREAMCAST, 23 },
@ -98,8 +98,8 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
{ SEGA_MEGA_DRIVE, 1 }, { SEGA_MEGA_DRIVE, 1 },
{ SEGA_SATURN, 22 }, { SEGA_SATURN, 22 },
{ SEGA_SG1000, 109 }, { SEGA_SG1000, 109 },
{ SHARP_X1, 220}, { SHARP_X1, 220 },
{ SHARP_X68000, 79}, { SHARP_X68000, 79 },
{ GAMEENGINE_SOLARUS, 223 }, { GAMEENGINE_SOLARUS, 223 },
{ SONY_PLAYSTATION, 57 }, { SONY_PLAYSTATION, 57 },
{ SONY_PLAYSTATION_2, 58 }, { SONY_PLAYSTATION_2, 58 },
@ -110,8 +110,8 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
{ SUPER_NINTENDO, 4 }, { SUPER_NINTENDO, 4 },
{ NEC_SUPERGRAFX, 105 }, { NEC_SUPERGRAFX, 105 },
{ GAMEENGINE_TIC80, 222 }, { GAMEENGINE_TIC80, 222 },
{ NEC_PC_8800, 221}, { NEC_PC_8800, 221 },
{ NEC_PC_9800, 208}, { NEC_PC_9800, 208 },
{ NEC_PC_ENGINE, 31 }, { NEC_PC_ENGINE, 31 },
{ NEC_PC_ENGINE_CD, 114 }, { NEC_PC_ENGINE_CD, 114 },
{ BANDAI_WONDERSWAN, 45 }, { BANDAI_WONDERSWAN, 45 },
@ -131,7 +131,8 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
// Helper XML parsing method, finding a node-by-name recursively. // Helper XML parsing method, finding a node-by-name recursively.
pugi::xml_node find_node_by_name_re(const pugi::xml_node& node, pugi::xml_node find_node_by_name_re(const pugi::xml_node& node,
const std::vector<std::string> node_names) { const std::vector<std::string> node_names)
{
for (const std::string& _val : node_names) { for (const std::string& _val : node_names) {
pugi::xpath_query query_node_name((static_cast<std::string>("//") + _val).c_str()); pugi::xpath_query query_node_name((static_cast<std::string>("//") + _val).c_str());
@ -147,8 +148,9 @@ pugi::xml_node find_node_by_name_re(const pugi::xml_node& node,
// Help XML parsing method, finding an direct child XML node starting from the parent and // Help XML parsing method, finding an direct child XML node starting from the parent and
// filtering by an attribute value list. // filtering by an attribute value list.
pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent, pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent,
const std::string& node_name, const std::string& attribute_name, const std::string& node_name,
const std::vector<std::string> attribute_values) const std::string& attribute_name,
const std::vector<std::string> attribute_values)
{ {
for (auto _val : attribute_values) { for (auto _val : attribute_values) {
for (pugi::xml_node node : node_parent.children(node_name.c_str())) { for (pugi::xml_node node : node_parent.children(node_name.c_str())) {
@ -161,22 +163,22 @@ pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent,
} }
void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
std::queue<std::unique_ptr<ScraperRequest>>& requests, std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::vector<ScraperSearchResult>& results) std::vector<ScraperSearchResult>& results)
{ {
std::string path; std::string path;
ScreenScraperRequest::ScreenScraperConfig ssConfig; ScreenScraperRequest::ScreenScraperConfig ssConfig;
if (params.game->isArcadeGame()) if (params.game->isArcadeGame())
ssConfig.isArcadeSystem = true; ssConfig.isArcadeSystem = true;
else else
ssConfig.isArcadeSystem = false; ssConfig.isArcadeSystem = false;
if (params.nameOverride == "") { if (params.nameOverride == "") {
if (Settings::getInstance()->getBool("ScraperSearchMetadataName")) if (Settings::getInstance()->getBool("ScraperSearchMetadataName"))
path = ssConfig.getGameSearchUrl( path = ssConfig.getGameSearchUrl(
Utils::String::removeParenthesis(params.game->metadata.get("name"))); Utils::String::removeParenthesis(params.game->metadata.get("name")));
else else
path = ssConfig.getGameSearchUrl(params.game->getCleanName()); path = ssConfig.getGameSearchUrl(params.game->getCleanName());
} }
@ -195,19 +197,19 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
p_ids.push_back(mapIt->second); p_ids.push_back(mapIt->second);
} }
else { else {
LOG(LogWarning) << "ScreenScraper: No support for platform \"" << LOG(LogWarning) << "ScreenScraper: No support for platform \""
getPlatformName(*platformIt) << "\", search will be inaccurate"; << getPlatformName(*platformIt) << "\", search will be inaccurate";
// Add the scrape request without a platform/system ID. // Add the scrape request without a platform/system ID.
requests.push(std::unique_ptr<ScraperRequest> requests.push(
(new ScreenScraperRequest(requests, results, path))); std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(requests, results, path)));
} }
} }
if (p_ids.size() == 0) { if (p_ids.size() == 0) {
LOG(LogWarning) << "ScreenScraper: No platform defined, search will be inaccurate"; LOG(LogWarning) << "ScreenScraper: No platform defined, search will be inaccurate";
// Add the scrape request without a platform/system ID. // Add the scrape request without a platform/system ID.
requests.push(std::unique_ptr<ScraperRequest> requests.push(
(new ScreenScraperRequest(requests, results, path))); std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(requests, results, path)));
} }
// Sort the platform IDs and remove duplicates. // Sort the platform IDs and remove duplicates.
@ -218,13 +220,13 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
for (auto platform = p_ids.cbegin(); platform != p_ids.cend(); platform++) { for (auto platform = p_ids.cbegin(); platform != p_ids.cend(); platform++) {
path += "&systemeid="; path += "&systemeid=";
path += HttpReq::urlEncode(std::to_string(*platform)); path += HttpReq::urlEncode(std::to_string(*platform));
requests.push(std::unique_ptr<ScraperRequest> requests.push(
(new ScreenScraperRequest(requests, results, path))); std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(requests, results, path)));
} }
} }
void ScreenScraperRequest::process(const std::unique_ptr<HttpReq>& req, void ScreenScraperRequest::process(const std::unique_ptr<HttpReq>& req,
std::vector<ScraperSearchResult>& results) std::vector<ScraperSearchResult>& results)
{ {
assert(req->status() == HttpReq::REQ_SUCCESS); assert(req->status() == HttpReq::REQ_SUCCESS);
@ -263,7 +265,7 @@ void ScreenScraperRequest::process(const std::unique_ptr<HttpReq>& req,
std::string gameName = Utils::String::toUpper((*it).mdl.get("name")); std::string gameName = Utils::String::toUpper((*it).mdl.get("name"));
if (gameName.substr(0, 12) == "ZZZ(NOTGAME)") { if (gameName.substr(0, 12) == "ZZZ(NOTGAME)") {
LOG(LogWarning) << "ScreenScraperRequest - Received \"ZZZ(notgame)\" as game name, " LOG(LogWarning) << "ScreenScraperRequest - Received \"ZZZ(notgame)\" as game name, "
"ignoring response"; "ignoring response";
results.pop_back(); results.pop_back();
return; return;
} }
@ -271,7 +273,7 @@ void ScreenScraperRequest::process(const std::unique_ptr<HttpReq>& req,
} }
void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
std::vector<ScraperSearchResult>& out_results) std::vector<ScraperSearchResult>& out_results)
{ {
pugi::xml_node data = xmldoc.child("Data"); pugi::xml_node data = xmldoc.child("Data");
@ -280,17 +282,18 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
// also seems to correlate with missing scraper allowance data. This is however a scraper // also seems to correlate with missing scraper allowance data. This is however a scraper
// service issue so we're not attempting to compensate for it here. // service issue so we're not attempting to compensate for it here.
if (Settings::getInstance()->getBool("ScraperUseAccountScreenScraper") && if (Settings::getInstance()->getBool("ScraperUseAccountScreenScraper") &&
Settings::getInstance()->getString("ScraperUsernameScreenScraper") != "" && Settings::getInstance()->getString("ScraperUsernameScreenScraper") != "" &&
Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") { Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") {
std::string userID = data.child("ssuser").child("id").text().get(); std::string userID = data.child("ssuser").child("id").text().get();
if (userID != "") { if (userID != "") {
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Scraping using account \"" << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Scraping using account \""
userID << "\""; << userID << "\"";
} }
else { else {
LOG(LogDebug) << "ScreenScraperRequest::processGame(): The configured account '" << LOG(LogDebug)
Settings::getInstance()->getString("ScraperUsernameScreenScraper") << << "ScreenScraperRequest::processGame(): The configured account '"
"' was not included in the scraper response, wrong username or password?"; << Settings::getInstance()->getString("ScraperUsernameScreenScraper")
<< "' was not included in the scraper response, wrong username or password?";
} }
} }
@ -304,17 +307,17 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
// Scraping allowance. // Scraping allowance.
if (maxRequestsPerDay > 0) { if (maxRequestsPerDay > 0) {
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: "
requestsToday << "/" << maxRequestsPerDay << " (" << << requestsToday << "/" << maxRequestsPerDay << " ("
scraperRequestAllowance << " remaining)"; << scraperRequestAllowance << " remaining)";
} }
else { else {
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: " LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: "
"No statistics were provided with the response"; "No statistics were provided with the response";
} }
if (data.child("jeux")) if (data.child("jeux"))
data = data.child("jeux"); data = data.child("jeux");
for (pugi::xml_node game = data.child("jeu"); game; game = game.next_sibling("jeu")) { for (pugi::xml_node game = data.child("jeu"); game; game = game.next_sibling("jeu")) {
ScraperSearchResult result; ScraperSearchResult result;
@ -324,13 +327,16 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
result.gameID = game.attribute("id").as_string(); result.gameID = game.attribute("id").as_string();
std::string region = std::string region =
Utils::String::toLower(Settings::getInstance()->getString("ScraperRegion")); Utils::String::toLower(Settings::getInstance()->getString("ScraperRegion"));
std::string language = std::string language =
Utils::String::toLower(Settings::getInstance()->getString("ScraperLanguage")); Utils::String::toLower(Settings::getInstance()->getString("ScraperLanguage"));
// Name fallback: US, WOR(LD). (Xpath: Data/jeu[0]/noms/nom[*]). // Name fallback: US, WOR(LD). (Xpath: Data/jeu[0]/noms/nom[*]).
result.mdl.set("name", find_child_by_attribute_list(game.child("noms"), result.mdl.set("name",
"nom", "region", { region, "wor", "us" , "ss", "eu", "jp" }).text().get()); find_child_by_attribute_list(game.child("noms"), "nom", "region",
{ region, "wor", "us", "ss", "eu", "jp" })
.text()
.get());
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Name: " << result.mdl.get("name"); LOG(LogDebug) << "ScreenScraperRequest::processGame(): Name: " << result.mdl.get("name");
// Validate rating. // Validate rating.
@ -346,14 +352,16 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
ss << ratingVal; ss << ratingVal;
if (ratingVal > 0) { if (ratingVal > 0) {
result.mdl.set("rating", ss.str()); result.mdl.set("rating", ss.str());
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Rating: " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Rating: "
result.mdl.get("rating"); << result.mdl.get("rating");
} }
} }
// Description fallback language: EN, WOR(LD). // Description fallback language: EN, WOR(LD).
std::string description = find_child_by_attribute_list(game.child("synopsis"), std::string description = find_child_by_attribute_list(game.child("synopsis"), "synopsis",
"synopsis", "langue", { language, "en", "wor" }).text().get(); "langue", { language, "en", "wor" })
.text()
.get();
// Translate some HTML character codes to UTF-8 characters. // Translate some HTML character codes to UTF-8 characters.
if (!description.empty()) { if (!description.empty()) {
@ -362,80 +370,83 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
} }
// Get the date proper. The API returns multiple 'date' children nodes to the 'dates' // Get the date proper. The API returns multiple 'date' children nodes to the 'dates'
// main child of 'jeu'. // main child of 'jeu'. Date fallback: WOR(LD), US, SS, JP, EU.
// Date fallback: WOR(LD), US, SS, JP, EU.
std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region", std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region",
{ region, "wor", "us", "ss", "jp", "eu" }).text().get(); { region, "wor", "us", "ss", "jp", "eu" })
.text()
.get();
// Date can be YYYY-MM-DD or just YYYY. // Date can be YYYY-MM-DD or just YYYY.
if (_date.length() > 4) { if (_date.length() > 4) {
result.mdl.set("releasedate", Utils::Time::DateTime( result.mdl.set("releasedate",
Utils::Time::stringToTime(_date, "%Y-%m-%d"))); Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y-%m-%d")));
} }
else if (_date.length() > 0) { else if (_date.length() > 0) {
result.mdl.set("releasedate", Utils::Time::DateTime( result.mdl.set("releasedate",
Utils::Time::stringToTime(_date, "%Y"))); Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y")));
} }
if (_date.length() > 0) { if (_date.length() > 0) {
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (unparsed): " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (unparsed): "
_date; << _date;
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (parsed): " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (parsed): "
result.mdl.get("releasedate"); << result.mdl.get("releasedate");
} }
// Developer for the game (Xpath: Data/jeu[0]/developpeur). // Developer for the game (Xpath: Data/jeu[0]/developpeur).
std::string developer = game.child("developpeur").text().get(); std::string developer = game.child("developpeur").text().get();
if (!developer.empty()) { if (!developer.empty()) {
result.mdl.set("developer", Utils::String::replace(developer, "&nbsp;", " ")); result.mdl.set("developer", Utils::String::replace(developer, "&nbsp;", " "));
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Developer: " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Developer: "
result.mdl.get("developer"); << result.mdl.get("developer");
} }
// Publisher for the game (Xpath: Data/jeu[0]/editeur). // Publisher for the game (Xpath: Data/jeu[0]/editeur).
std::string publisher = game.child("editeur").text().get(); std::string publisher = game.child("editeur").text().get();
if (!publisher.empty()) { if (!publisher.empty()) {
result.mdl.set("publisher", Utils::String::replace(publisher, "&nbsp;", " ")); result.mdl.set("publisher", Utils::String::replace(publisher, "&nbsp;", " "));
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Publisher: " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Publisher: "
result.mdl.get("publisher"); << result.mdl.get("publisher");
} }
// Genre fallback language: EN. (Xpath: Data/jeu[0]/genres/genre[*]). // Genre fallback language: EN. (Xpath: Data/jeu[0]/genres/genre[*]).
std::string genre = find_child_by_attribute_list(game.child("genres"), std::string genre = find_child_by_attribute_list(game.child("genres"), "genre", "langue",
"genre", "langue", { language, "en" }).text().get(); { language, "en" })
.text()
.get();
if (!genre.empty()) { if (!genre.empty()) {
result.mdl.set("genre", genre); result.mdl.set("genre", genre);
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: "
result.mdl.get("genre"); << result.mdl.get("genre");
} }
// Players. // Players.
std::string players = game.child("joueurs").text().get(); std::string players = game.child("joueurs").text().get();
if (!players.empty()) { if (!players.empty()) {
result.mdl.set("players", players); result.mdl.set("players", players);
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Players: " << LOG(LogDebug) << "ScreenScraperRequest::processGame(): Players: "
result.mdl.get("players"); << result.mdl.get("players");
} }
// Media super-node. // Media super-node.
pugi::xml_node media_list = game.child("medias"); pugi::xml_node media_list = game.child("medias");
if (media_list) { if (media_list) {
// 3D box // 3D box.
processMedia(result, media_list, ssConfig.media_3dbox, processMedia(result, media_list, ssConfig.media_3dbox, result.box3DUrl,
result.box3DUrl, result.box3DFormat, region); result.box3DFormat, region);
// Cover // Cover.
processMedia(result, media_list, ssConfig.media_cover, processMedia(result, media_list, ssConfig.media_cover, result.coverUrl,
result.coverUrl, result.coverFormat, region); result.coverFormat, region);
// Marquee (wheel) // Marquee (wheel).
processMedia(result, media_list, ssConfig.media_marquee, processMedia(result, media_list, ssConfig.media_marquee, result.marqueeUrl,
result.marqueeUrl, result.marqueeFormat, region); result.marqueeFormat, region);
// Screenshot // Screenshot.
processMedia(result, media_list, ssConfig.media_screenshot, processMedia(result, media_list, ssConfig.media_screenshot, result.screenshotUrl,
result.screenshotUrl, result.screenshotFormat, region); result.screenshotFormat, region);
// Video // Video.
processMedia(result, media_list, ssConfig.media_video, processMedia(result, media_list, ssConfig.media_video, result.videoUrl,
result.videoUrl, result.videoFormat, region); result.videoFormat, region);
} }
result.mediaURLFetch = COMPLETED; result.mediaURLFetch = COMPLETED;
out_results.push_back(result); out_results.push_back(result);
@ -446,13 +457,12 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
} }
} }
void ScreenScraperRequest::processMedia( void ScreenScraperRequest::processMedia(ScraperSearchResult& result,
ScraperSearchResult& result, const pugi::xml_node& media_list,
const pugi::xml_node& media_list, std::string mediaType,
std::string mediaType, std::string& fileURL,
std::string& fileURL, std::string& fileFormat,
std::string& fileFormat, std::string region)
std::string region)
{ {
pugi::xml_node art = pugi::xml_node(nullptr); pugi::xml_node art = pugi::xml_node(nullptr);
@ -460,51 +470,52 @@ void ScreenScraperRequest::processMedia(
// We need to do this because any child of 'medias' has the form // We need to do this because any child of 'medias' has the form
// <media type="..." region="..." format="..."> // <media type="..." region="..." format="...">
// and we need to find the right media for the region. // and we need to find the right media for the region.
pugi::xpath_node_set results = media_list.select_nodes((static_cast<std::string> pugi::xpath_node_set results = media_list.select_nodes(
("media[@type='") + mediaType + "']").c_str()); (static_cast<std::string>("media[@type='") + mediaType + "']").c_str());
if (results.size()) { if (results.size()) {
// Videos don't have any region attributes, so just take the first entry // Videos don't have any region attributes, so just take the first entry
// (which should be the only entry as well). // (which should be the only entry as well).
if (mediaType == "video" || mediaType == "video-normalized") { if (mediaType == "video" || mediaType == "video-normalized") {
art = results.first().node(); art = results.first().node();
} }
else { else {
// Region fallback: WOR(LD), US, CUS(TOM?), JP, EU. // Region fallback: WOR(LD), US, CUS(TOM?), JP, EU.
for (auto _region : std::vector<std::string>{ for (auto _region :
region, "wor", "us", "cus", "jp", "eu" }) { std::vector<std::string> { region, "wor", "us", "cus", "jp", "eu" }) {
if (art) if (art)
break;
for (auto node : results) {
if (node.node().attribute("region").value() == _region) {
art = node.node();
break; break;
for (auto node : results) {
if (node.node().attribute("region").value() == _region) {
art = node.node();
break;
}
} }
} }
} }
} }
}
if (art) { if (art) {
// Sending a 'softname' containing space will make the media URLs returned // Sending a 'softname' containing space will make the media URLs returned
// by the API also contain the space. Escape any spaces in the URL here. // by the API also contain the space. Escape any spaces in the URL here.
fileURL = Utils::String::replace(art.text().get(), " ", "%20"); fileURL = Utils::String::replace(art.text().get(), " ", "%20");
// Get the media type returned by ScreenScraper. // Get the media type returned by ScreenScraper.
std::string media_type = art.attribute("format").value(); std::string media_type = art.attribute("format").value();
if (!media_type.empty()) if (!media_type.empty())
fileFormat = "." + media_type; fileFormat = "." + media_type;
} }
else { else {
LOG(LogDebug) << "ScreenScraperRequest::processMedia(): " LOG(LogDebug) << "ScreenScraperRequest::processMedia(): "
"Failed to find media XML node with name '" << mediaType << "'"; "Failed to find media XML node with name '"
} << mediaType << "'";
}
} }
// Currently not used in this module. // Currently not used in this module.
void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc,
std::vector<ScraperSearchResult>& results) std::vector<ScraperSearchResult>& results)
{ {
assert(mRequestQueue != nullptr); assert(mRequestQueue != nullptr);
@ -527,18 +538,18 @@ void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc,
std::string id = game.child("id").text().get(); std::string id = game.child("id").text().get();
std::string name = game.child("nom").text().get(); std::string name = game.child("nom").text().get();
std::string platformId = game.child("systemeid").text().get(); std::string platformId = game.child("systemeid").text().get();
std::string path = ssConfig.getGameSearchUrl(name) + "&systemeid=" + std::string path =
platformId + "&gameid=" + id; ssConfig.getGameSearchUrl(name) + "&systemeid=" + platformId + "&gameid=" + id;
mRequestQueue->push(std::unique_ptr<ScraperRequest> mRequestQueue->push(
(new ScreenScraperRequest(results, path))); std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(results, path)));
game = game.next_sibling("jeu"); game = game.next_sibling("jeu");
} }
} }
std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
const std::string gameName) const const std::string gameName) const
{ {
std::string screenScraperURL; std::string screenScraperURL;
std::string searchName = gameName; std::string searchName = gameName;
@ -546,12 +557,14 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
// Trim leading and trailing whitespaces. // Trim leading and trailing whitespaces.
searchName.erase(searchName.begin(), searchName.erase(searchName.begin(),
std::find_if(searchName.begin(), searchName.end(), [](char c) { std::find_if(searchName.begin(), searchName.end(), [](char c) {
return !std::isspace(static_cast<unsigned char>(c)); return !std::isspace(static_cast<unsigned char>(c));
})); }));
searchName.erase(std::find_if(searchName.rbegin(), searchName.rend(), [](char c) { searchName.erase(
return !std::isspace(static_cast<unsigned char>(c)); std::find_if(searchName.rbegin(), searchName.rend(),
}).base(), searchName.end()); [](char c) { return !std::isspace(static_cast<unsigned char>(c)); })
.base(),
searchName.end());
// If only whitespaces were entered as the search string, then search using a random string // If only whitespaces were entered as the search string, then search using a random string
// that will not return any results. This is a quick and dirty way to avoid french error // that will not return any results. This is a quick and dirty way to avoid french error
@ -578,9 +591,10 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
// than four characters which would break the wide search. // than four characters which would break the wide search.
std::string trimTrailingPluses = searchName; std::string trimTrailingPluses = searchName;
trimTrailingPluses.erase(std::find_if(trimTrailingPluses.rbegin(), trimTrailingPluses.erase(std::find_if(trimTrailingPluses.rbegin(),
trimTrailingPluses.rend(), [](char c) { trimTrailingPluses.rend(),
return c != '+'; [](char c) { return c != '+'; })
}).base(), trimTrailingPluses.end()); .base(),
trimTrailingPluses.end());
if (trimTrailingPluses.size() < 4) if (trimTrailingPluses.size() < 4)
singleSearch = true; singleSearch = true;
@ -589,12 +603,12 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
// could also lead to an error for short game names. // could also lead to an error for short game names.
if (!singleSearch) { if (!singleSearch) {
std::string removeThe = std::string removeThe =
Utils::String::replace(Utils::String::toUpper(searchName), "THE ", ""); Utils::String::replace(Utils::String::toUpper(searchName), "THE ", "");
// Any additional spaces must also be removed. // Any additional spaces must also be removed.
removeThe.erase(removeThe.begin(), removeThe.erase(removeThe.begin(),
std::find_if(removeThe.begin(), removeThe.end(), [](char c) { std::find_if(removeThe.begin(), removeThe.end(), [](char c) {
return !std::isspace(static_cast<unsigned char>(c)); return !std::isspace(static_cast<unsigned char>(c));
})); }));
// If "the" is placed at the end of the search string, ScreenScraper also removes it. // If "the" is placed at the end of the search string, ScreenScraper also removes it.
if (removeThe.size() > 4) { if (removeThe.size() > 4) {
if (removeThe.substr(removeThe.size() - 4, 4) == " THE") if (removeThe.substr(removeThe.size() - 4, 4) == " THE")
@ -605,20 +619,18 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
} }
if (singleSearch) { if (singleSearch) {
screenScraperURL = API_URL_BASE screenScraperURL = API_URL_BASE + "/jeuInfos.php?devid=" +
+ "/jeuInfos.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY) Utils::String::scramble(API_DEV_U, API_DEV_KEY) +
+ "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) +
+ "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" +
+ "&output=xml" "&romnom=" + HttpReq::urlEncode(searchName);
+ "&romnom=" + HttpReq::urlEncode(searchName);
} }
else { else {
screenScraperURL = API_URL_BASE screenScraperURL = API_URL_BASE + "/jeuRecherche.php?devid=" +
+ "/jeuRecherche.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY) Utils::String::scramble(API_DEV_U, API_DEV_KEY) +
+ "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) +
+ "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" +
+ "&output=xml" "&recherche=" + HttpReq::urlEncode(searchName);
+ "&recherche=" + HttpReq::urlEncode(searchName);
} }
// Username / password, if this has been setup and activated. // Username / password, if this has been setup and activated.
@ -626,8 +638,8 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
std::string username = Settings::getInstance()->getString("ScraperUsernameScreenScraper"); std::string username = Settings::getInstance()->getString("ScraperUsernameScreenScraper");
std::string password = Settings::getInstance()->getString("ScraperPasswordScreenScraper"); std::string password = Settings::getInstance()->getString("ScraperPasswordScreenScraper");
if (!username.empty() && !password.empty()) if (!username.empty() && !password.empty())
screenScraperURL += "&ssid=" + HttpReq::urlEncode(username) + "&sspassword=" + screenScraperURL += "&ssid=" + HttpReq::urlEncode(username) +
HttpReq::urlEncode(password); "&sspassword=" + HttpReq::urlEncode(password);
} }
return screenScraperURL; return screenScraperURL;

View file

@ -10,49 +10,54 @@
#ifndef ES_APP_SCRAPERS_SCREEN_SCRAPER_H #ifndef ES_APP_SCRAPERS_SCREEN_SCRAPER_H
#define ES_APP_SCRAPERS_SCREEN_SCRAPER_H #define ES_APP_SCRAPERS_SCREEN_SCRAPER_H
#include "scrapers/Scraper.h"
#include "EmulationStation.h" #include "EmulationStation.h"
#include "scrapers/Scraper.h"
namespace pugi { class xml_document; } namespace pugi
{
class xml_document;
}
void screenscraper_generate_scraper_requests( void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
const ScraperSearchParams& params, std::queue<std::unique_ptr<ScraperRequest>>& requests,
std::queue<std::unique_ptr<ScraperRequest>>& requests, std::vector<ScraperSearchResult>& results);
std::vector<ScraperSearchResult>& results);
class ScreenScraperRequest : public ScraperHttpRequest class ScreenScraperRequest : public ScraperHttpRequest
{ {
public: public:
// ctor for a GetGameList request. // ctor for a GetGameList request.
ScreenScraperRequest(std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite, ScreenScraperRequest(std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite,
std::vector<ScraperSearchResult>& resultsWrite, std::vector<ScraperSearchResult>& resultsWrite,
const std::string& url) : ScraperHttpRequest(resultsWrite, url), const std::string& url)
mRequestQueue(&requestsWrite) {} : ScraperHttpRequest(resultsWrite, url)
, mRequestQueue(&requestsWrite)
{
}
// ctor for a GetGame request. // ctor for a GetGame request.
ScreenScraperRequest(std::vector<ScraperSearchResult>& resultsWrite, ScreenScraperRequest(std::vector<ScraperSearchResult>& resultsWrite, const std::string& url)
const std::string& url) : ScraperHttpRequest(resultsWrite, url), : ScraperHttpRequest(resultsWrite, url)
mRequestQueue(nullptr) {} , mRequestQueue(nullptr)
{
}
// Settings for the scraper. // Settings for the scraper.
static const struct ScreenScraperConfig { static const struct ScreenScraperConfig {
std::string getGameSearchUrl(const std::string gameName) const; std::string getGameSearchUrl(const std::string gameName) const;
// Access to the API. // Access to the API.
const std::string API_DEV_U = const std::string API_DEV_U = { 15, 21, 39, 22, 42, 40 };
{ 15, 21, 39, 22, 42, 40 }; const std::string API_DEV_P = { 32, 70, 46, 54, 12, 5, 13, 120, 50, 66, 25 };
const std::string API_DEV_P = const std::string API_DEV_KEY = { 67, 112, 72, 120, 121, 77, 119, 74, 84, 56,
{ 32, 70, 46, 54, 12, 5, 13, 120, 50, 66, 25 }; 75, 122, 78, 98, 69, 86, 56, 120, 120, 49 };
const std::string API_DEV_KEY =
{ 67, 112, 72, 120, 121, 77, 119, 74, 84, 56, 75, 122, 78, 98, 69, 86, 56, 120, 120, 49 };
const std::string API_URL_BASE = "https://www.screenscraper.fr/api2"; const std::string API_URL_BASE = "https://www.screenscraper.fr/api2";
const std::string API_SOFT_NAME = "EmulationStation-DE " + const std::string API_SOFT_NAME =
static_cast<std::string>(PROGRAM_VERSION_STRING); "EmulationStation-DE " + static_cast<std::string>(PROGRAM_VERSION_STRING);
// Which type of image artwork we need. Possible values (not a comprehensive list): // Which type of image artwork we need. Possible values (not a comprehensive list):
// - ss: in-game screenshot // - ss: in-game screenshot
// - box-3D: 3D boxart // - box-3D: 3D boxart
// - box-2D: 2D boxart (default) // - box-2D: 2D boxart
// - screenmarque : marquee // - screenmarque : marquee
// - sstitle: in-game start screenshot // - sstitle: in-game start screenshot
// - steamgrid: Steam artwork // - steamgrid: Steam artwork
@ -75,27 +80,27 @@ public:
// Which Region to use when selecting the artwork. // Which Region to use when selecting the artwork.
// Applies to: artwork, name of the game, date of release. // Applies to: artwork, name of the game, date of release.
// This is read from es_settings.xml, setting 'ScraperRegion'. // This is read from es_settings.xml, setting "ScraperRegion".
// Which Language to use when selecting the textual information. // Which Language to use when selecting the textual information.
// Applies to: description, genre. // Applies to: description, genre.
// This is read from es_settings.xml, setting 'ScraperLanguage'. // This is read from es_settings.xml, setting "ScraperLanguage".
ScreenScraperConfig() {}; ScreenScraperConfig() {}
} configuration; } configuration;
protected: protected:
void process(const std::unique_ptr<HttpReq>& req, void process(const std::unique_ptr<HttpReq>& req,
std::vector<ScraperSearchResult>& results) override; std::vector<ScraperSearchResult>& results) override;
void processList(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results); void processList(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results);
void processGame(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results); void processGame(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results);
void processMedia(ScraperSearchResult& result, void processMedia(ScraperSearchResult& result,
const pugi::xml_node& media_list, const pugi::xml_node& media_list,
std::string mediaType, std::string mediaType,
std::string& fileURL, std::string& fileURL,
std::string& fileFormat, std::string& fileFormat,
std::string region); std::string region);
bool isGameRequest() { return !mRequestQueue; } bool isGameRequest() { return !mRequestQueue; }
std::queue<std::unique_ptr<ScraperRequest>>* mRequestQueue; std::queue<std::unique_ptr<ScraperRequest>>* mRequestQueue;

View file

@ -8,15 +8,15 @@
#include "views/SystemView.h" #include "views/SystemView.h"
#include "animations/LambdaAnimation.h"
#include "guis/GuiMsgBox.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include "Sound.h" #include "Sound.h"
#include "SystemData.h" #include "SystemData.h"
#include "Window.h" #include "Window.h"
#include "animations/LambdaAnimation.h"
#include "guis/GuiMsgBox.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#if defined(_WIN64) #if defined(_WIN64)
#include <cmath> #include <cmath>
@ -26,14 +26,12 @@
const int logoBuffersLeft[] = { -5, -2, -1 }; const int logoBuffersLeft[] = { -5, -2, -1 };
const int logoBuffersRight[] = { 1, 2, 5 }; const int logoBuffersRight[] = { 1, 2, 5 };
SystemView::SystemView( SystemView::SystemView(Window* window)
Window* window) : IList<SystemViewData, SystemData*>(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP)
: IList<SystemViewData, SystemData*> , mPreviousScrollVelocity(0)
(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), , mUpdatedGameCount(false)
mPreviousScrollVelocity(0), , mViewNeedsReload(true)
mUpdatedGameCount(false), , mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER)
mViewNeedsReload(true),
mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER)
{ {
mCamOffset = 0; mCamOffset = 0;
mExtrasCamOffset = 0; mExtrasCamOffset = 0;
@ -58,8 +56,8 @@ void SystemView::populate()
{ {
mEntries.clear(); mEntries.clear();
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
const std::shared_ptr<ThemeData>& theme = (*it)->getTheme(); const std::shared_ptr<ThemeData>& theme = (*it)->getTheme();
if (mViewNeedsReload) if (mViewNeedsReload)
@ -74,11 +72,11 @@ void SystemView::populate()
const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image");
if (logoElem) { if (logoElem) {
std::string path = logoElem->get<std::string>("path"); std::string path = logoElem->get<std::string>("path");
std::string defaultPath = logoElem->has("default") ? std::string defaultPath =
logoElem->get<std::string>("default") : ""; logoElem->has("default") ? logoElem->get<std::string>("default") : "";
if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) || if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) ||
(!defaultPath.empty() && (!defaultPath.empty() &&
ResourceManager::getInstance()->fileExists(defaultPath))) { ResourceManager::getInstance()->fileExists(defaultPath))) {
ImageComponent* logo = new ImageComponent(mWindow, false, false); ImageComponent* logo = new ImageComponent(mWindow, false, false);
logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale);
logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR); logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR);
@ -88,16 +86,14 @@ void SystemView::populate()
} }
if (!e.data.logo) { if (!e.data.logo) {
// No logo in theme; use text. // No logo in theme; use text.
TextComponent* text = new TextComponent( TextComponent* text =
mWindow, new TextComponent(mWindow, (*it)->getName(), Font::get(FONT_SIZE_LARGE),
(*it)->getName(), 0x000000FF, ALIGN_CENTER);
Font::get(FONT_SIZE_LARGE),
0x000000FF,
ALIGN_CENTER);
text->setSize(mCarousel.logoSize * mCarousel.logoScale); text->setSize(mCarousel.logoSize * mCarousel.logoScale);
text->applyTheme((*it)->getTheme(), "system", "logoText", text->applyTheme((*it)->getTheme(), "system", "logoText",
ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR |
ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT); ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING |
ThemeFlags::TEXT);
e.data.logo = std::shared_ptr<GuiComponent>(text); e.data.logo = std::shared_ptr<GuiComponent>(text);
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) {
@ -134,10 +130,9 @@ void SystemView::populate()
e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow); e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow);
// Sort the extras by z-index. // Sort the extras by z-index.
std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), std::stable_sort(
[](GuiComponent* a, GuiComponent* b) { e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(),
return b->getZIndex() > a->getZIndex(); [](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); });
});
this->add(e); this->add(e);
} }
@ -146,9 +141,10 @@ void SystemView::populate()
// Something is wrong, there is not a single system to show, check if UI mode is not full. // Something is wrong, there is not a single system to show, check if UI mode is not full.
if (!UIModeController::getInstance()->isUIModeFull()) { if (!UIModeController::getInstance()->isUIModeFull()) {
Settings::getInstance()->setString("UIMode", "full"); Settings::getInstance()->setString("UIMode", "full");
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), mWindow->pushGui(new GuiMsgBox(
"The selected UI mode has nothing to show,\n returning to UI mode \"Full\"", mWindow, getHelpStyle(),
"OK", nullptr)); "The selected UI mode has nothing to show,\n returning to UI mode \"Full\"", "OK",
nullptr));
} }
} }
} }
@ -162,14 +158,14 @@ void SystemView::updateGameCount()
ss << "CONFIGURATION"; ss << "CONFIGURATION";
else if (getSelected()->isCollection() && (getSelected()->getName() == "favorites")) else if (getSelected()->isCollection() && (getSelected()->getName() == "favorites"))
ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S"); ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S");
// The 'recent' gamelist has probably been trimmed after sorting, so we'll cap it at // The "recent" gamelist has probably been trimmed after sorting, so we'll cap it at
// its maximum limit of 50 games. // its maximum limit of 50 games.
else if (getSelected()->isCollection() && (getSelected()->getName() == "recent")) else if (getSelected()->isCollection() && (getSelected()->getName() == "recent"))
ss << (gameCount.first > 50 ? 50 : gameCount.first) << " GAME" << ss << (gameCount.first > 50 ? 50 : gameCount.first) << " GAME"
(gameCount.first == 1 ? " " : "S"); << (gameCount.first == 1 ? " " : "S");
else else
ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S ") << "(" << ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S ") << "("
gameCount.second << " FAVORITE" << (gameCount.second == 1 ? ")" : "S)"); << gameCount.second << " FAVORITE" << (gameCount.second == 1 ? ")" : "S)");
mSystemInfo.setText(ss.str()); mSystemInfo.setText(ss.str());
} }
@ -190,40 +186,40 @@ bool SystemView::input(InputConfig* config, Input input)
if (input.value != 0) { if (input.value != 0) {
if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r &&
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
LOG(LogDebug) << "SystemView::input(): Reloading all"; LOG(LogDebug) << "SystemView::input(): Reloading all";
ViewController::get()->reloadAll(); ViewController::get()->reloadAll();
return true; return true;
} }
switch (mCarousel.type) { switch (mCarousel.type) {
case VERTICAL: case VERTICAL:
case VERTICAL_WHEEL: case VERTICAL_WHEEL:
if (config->isMappedLike("up", input)) { if (config->isMappedLike("up", input)) {
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
listInput(-1); listInput(-1);
return true; return true;
} }
if (config->isMappedLike("down", input)) { if (config->isMappedLike("down", input)) {
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
listInput(1); listInput(1);
return true; return true;
} }
break; break;
case HORIZONTAL: case HORIZONTAL:
case HORIZONTAL_WHEEL: case HORIZONTAL_WHEEL:
default: default:
if (config->isMappedLike("left", input)) { if (config->isMappedLike("left", input)) {
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
listInput(-1); listInput(-1);
return true; return true;
} }
if (config->isMappedLike("right", input)) { if (config->isMappedLike("right", input)) {
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
listInput(1); listInput(1);
return true; return true;
} }
break; break;
} }
if (config->isMappedTo("a", input)) { if (config->isMappedTo("a", input)) {
@ -233,17 +229,16 @@ bool SystemView::input(InputConfig* config, Input input)
return true; return true;
} }
if (Settings::getInstance()->getBool("RandomAddButton") && if (Settings::getInstance()->getBool("RandomAddButton") &&
(config->isMappedTo("leftthumbstickclick", input) || (config->isMappedTo("leftthumbstickclick", input) ||
config->isMappedTo("rightthumbstickclick", input))) { config->isMappedTo("rightthumbstickclick", input))) {
// Get a random system and jump to it. // Get a random system and jump to it.
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND); NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
setCursor(SystemData::getRandomSystem(getSelected())); setCursor(SystemData::getRandomSystem(getSelected()));
return true; return true;
} }
if (!UIModeController::getInstance()->isUIModeKid() && if (!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("back", input) &&
config->isMappedTo("back", input) && Settings::getInstance()->getBool("ScreensaverControls")) {
Settings::getInstance()->getBool("ScreensaverControls")) {
if (!mWindow->isScreensaverActive()) { if (!mWindow->isScreensaverActive()) {
ViewController::get()->stopScrolling(); ViewController::get()->stopScrolling();
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
@ -254,10 +249,8 @@ bool SystemView::input(InputConfig* config, Input input)
} }
} }
else { else {
if (config->isMappedLike("left", input) || if (config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
config->isMappedLike("right", input) || config->isMappedLike("up", input) || config->isMappedLike("down", input))
config->isMappedLike("up", input) ||
config->isMappedLike("down", input))
listInput(0); listInput(0);
} }
@ -302,8 +295,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
// To prevent ugly jumps with two systems when quickly repeating the same direction. // To prevent ugly jumps with two systems when quickly repeating the same direction.
if (mPreviousScrollVelocity != 0 && posMax == 2 && if (mPreviousScrollVelocity != 0 && posMax == 2 && mScrollVelocity == mPreviousScrollVelocity) {
mScrollVelocity == mPreviousScrollVelocity ) {
if (fabs(endPos - startPos) < 0.5 || fabs(endPos - startPos) > 1.5) { if (fabs(endPos - startPos) < 0.5 || fabs(endPos - startPos) > 1.5) {
(mScrollVelocity < 0) ? endPos -= 1 : endPos += 1; (mScrollVelocity < 0) ? endPos -= 1 : endPos += 1;
(mCursor == 0) ? mCursor = 1 : mCursor = 0; (mCursor == 0) ? mCursor = 1 : mCursor = 0;
@ -321,77 +313,84 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
if (transition_style == "fade") { if (transition_style == "fade") {
float startExtrasFade = mExtrasFadeOpacity; float startExtrasFade = mExtrasFadeOpacity;
anim = new LambdaAnimation( anim = new LambdaAnimation(
[this, startExtrasFade, startPos, endPos, posMax](float t) { [this, startExtrasFade, startPos, endPos, posMax](float t) {
t -= 1; t -= 1;
float f = Math::lerp(startPos, endPos, t*t*t + 1); float f = Math::lerp(startPos, endPos, t * t * t + 1);
if (f < 0) if (f < 0)
f += posMax; f += posMax;
if (f >= posMax) if (f >= posMax)
f -= posMax; f -= posMax;
this->mCamOffset = f; this->mCamOffset = f;
t += 1; t += 1;
if (t < 0.3f) if (t < 0.3f)
this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade); this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade);
else if (t < 0.7f) else if (t < 0.7f)
this->mExtrasFadeOpacity = 1.0f; this->mExtrasFadeOpacity = 1.0f;
else else
this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f); this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f);
if (t > 0.5f) if (t > 0.5f)
this->mExtrasCamOffset = endPos; this->mExtrasCamOffset = endPos;
// Update the game count when the entire animation has been completed. // Update the game count when the entire animation has been completed.
if (mExtrasFadeOpacity == 1.0) if (mExtrasFadeOpacity == 1.0f)
updateGameCount(); updateGameCount();
}, 500); },
500);
} }
else if (transition_style == "slide") { else if (transition_style == "slide") {
mUpdatedGameCount = false; mUpdatedGameCount = false;
anim = new LambdaAnimation( anim = new LambdaAnimation(
[this, startPos, endPos, posMax](float t) { [this, startPos, endPos, posMax](float t) {
t -= 1; t -= 1;
float f = Math::lerp(startPos, endPos, t*t*t + 1); float f = Math::lerp(startPos, endPos, t * t * t + 1);
if (f < 0) if (f < 0)
f += posMax; f += posMax;
if (f >= posMax) if (f >= posMax)
f -= posMax; f -= posMax;
this->mCamOffset = f; this->mCamOffset = f;
this->mExtrasCamOffset = f; this->mExtrasCamOffset = f;
// Hack to make the game count being updated in the middle of the animation. // Hack to make the game count being updated in the middle of the animation.
bool update = false; bool update = false;
if (endPos == -1 && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5 && !mUpdatedGameCount) if (endPos == -1.0f && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5f &&
update = true; !mUpdatedGameCount) {
else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) < update = true;
0.5 && !mUpdatedGameCount) }
update = true; else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) < 0.5f &&
else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5 && !mUpdatedGameCount) !mUpdatedGameCount) {
update = true; update = true;
}
else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5f && !mUpdatedGameCount) {
update = true;
}
if (update) { if (update) {
mUpdatedGameCount = true; mUpdatedGameCount = true;
updateGameCount(); updateGameCount();
} }
}, 500); },
500);
} }
else { else {
// Instant. // Instant.
updateGameCount(); updateGameCount();
anim = new LambdaAnimation( anim = new LambdaAnimation(
[this, startPos, endPos, posMax](float t) { [this, startPos, endPos, posMax](float t) {
t -= 1; t -= 1;
float f = Math::lerp(startPos, endPos, t*t*t + 1); float f = Math::lerp(startPos, endPos, t * t * t + 1);
if (f < 0) if (f < 0)
f += posMax; f += posMax;
if (f >= posMax) if (f >= posMax)
f -= posMax; f -= posMax;
this->mCamOffset = f; this->mCamOffset = f;
this->mExtrasCamOffset = endPos; this->mExtrasCamOffset = endPos;
}, 500); },
500);
} }
setAnimation(anim, 0, nullptr, false, 0); setAnimation(anim, 0, nullptr, false, 0);
@ -400,7 +399,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
void SystemView::render(const Transform4x4f& parentTrans) void SystemView::render(const Transform4x4f& parentTrans)
{ {
if (size() == 0) if (size() == 0)
return; // Nothing to render. return; // Nothing to render.
Transform4x4f trans = getTransform() * parentTrans; Transform4x4f trans = getTransform() * parentTrans;
@ -430,7 +429,7 @@ std::vector<HelpPrompt> SystemView::getHelpPrompts()
prompts.push_back(HelpPrompt("thumbstickclick", "random")); prompts.push_back(HelpPrompt("thumbstickclick", "random"));
if (!UIModeController::getInstance()->isUIModeKid() && if (!UIModeController::getInstance()->isUIModeKid() &&
Settings::getInstance()->getBool("ScreensaverControls")) Settings::getInstance()->getBool("ScreensaverControls"))
prompts.push_back(HelpPrompt("back", "toggle screensaver")); prompts.push_back(HelpPrompt("back", "toggle screensaver"));
return prompts; return prompts;
@ -443,7 +442,7 @@ HelpStyle SystemView::getHelpStyle()
return style; return style;
} }
void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/) void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
{ {
LOG(LogDebug) << "SystemView::onThemeChanged()"; LOG(LogDebug) << "SystemView::onThemeChanged()";
mViewNeedsReload = true; mViewNeedsReload = true;
@ -451,7 +450,7 @@ void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
} }
// Get the ThemeElements that make up the SystemView. // Get the ThemeElements that make up the SystemView.
void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme) void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
{ {
LOG(LogDebug) << "SystemView::getViewElements()"; LOG(LogDebug) << "SystemView::getViewElements()";
@ -460,13 +459,14 @@ void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
if (!theme->hasView("system")) if (!theme->hasView("system"))
return; return;
const ThemeData::ThemeElement* carouselElem = theme-> const ThemeData::ThemeElement* carouselElem =
getElement("system", "systemcarousel", "carousel"); theme->getElement("system", "systemcarousel", "carousel");
if (carouselElem) if (carouselElem)
getCarouselFromTheme(carouselElem); getCarouselFromTheme(carouselElem);
const ThemeData::ThemeElement* sysInfoElem = theme-> const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text");
getElement("system", "systemInfo", "text");
if (sysInfoElem) if (sysInfoElem)
mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL); mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL);
@ -479,71 +479,79 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
// Background box behind logos. // Background box behind logos.
Transform4x4f carouselTrans = trans; Transform4x4f carouselTrans = trans;
carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0)); carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0));
carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1, carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1.0f,
mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f)); mCarousel.origin.y() * mCarousel.size.y() * -1.0f, 0.0f));
Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y()); Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y());
Renderer::pushClipRect(Vector2i(static_cast<int>(clipPos.x()), static_cast<int>(clipPos.y())), Renderer::pushClipRect(
Vector2i(static_cast<int>(mCarousel.size.x()), static_cast<int>(mCarousel.size.y()))); Vector2i(static_cast<int>(clipPos.x()), static_cast<int>(clipPos.y())),
Vector2i(static_cast<int>(mCarousel.size.x()), static_cast<int>(mCarousel.size.y())));
Renderer::setMatrix(carouselTrans); Renderer::setMatrix(carouselTrans);
Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), mCarousel.color,
mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal); mCarousel.colorEnd, mCarousel.colorGradientHorizontal);
// Draw logos. // Draw logos.
// Note: logoSpacing will also include the size of the logo itself. // Note: logoSpacing will also include the size of the logo itself.
Vector2f logoSpacing(0.0, 0.0); Vector2f logoSpacing(0.0f, 0.0f);
float xOff = 0.0; float xOff = 0.0f;
float yOff = 0.0; float yOff = 0.0f;
switch (mCarousel.type) { switch (mCarousel.type) {
case VERTICAL_WHEEL: case VERTICAL_WHEEL: {
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f -
(mCamOffset * logoSpacing[1]); (mCamOffset * logoSpacing[1]);
if (mCarousel.logoAlignment == ALIGN_LEFT) if (mCarousel.logoAlignment == ALIGN_LEFT)
xOff = mCarousel.logoSize.x() / 10.f; xOff = mCarousel.logoSize.x() / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_RIGHT) else if (mCarousel.logoAlignment == ALIGN_RIGHT)
xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f);
else else
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f;
break; break;
case VERTICAL: }
logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * case VERTICAL: {
mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); logoSpacing[1] =
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) /
(mCamOffset * logoSpacing[1]); (mCarousel.maxLogoCount)) +
mCarousel.logoSize.y();
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f -
(mCamOffset * logoSpacing[1]);
if (mCarousel.logoAlignment == ALIGN_LEFT) if (mCarousel.logoAlignment == ALIGN_LEFT)
xOff = mCarousel.logoSize.x() / 10.f; xOff = mCarousel.logoSize.x() / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_RIGHT) else if (mCarousel.logoAlignment == ALIGN_RIGHT)
xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f);
else else
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f;
break; break;
case HORIZONTAL_WHEEL: }
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - case HORIZONTAL_WHEEL: {
(mCamOffset * logoSpacing[1]); xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f -
(mCamOffset * logoSpacing[1]);
if (mCarousel.logoAlignment == ALIGN_TOP) if (mCarousel.logoAlignment == ALIGN_TOP)
yOff = mCarousel.logoSize.y() / 10; yOff = mCarousel.logoSize.y() / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_BOTTOM) else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f);
else else
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f;
break; break;
case HORIZONTAL: }
default: case HORIZONTAL: {
logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * }
mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); default: {
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - logoSpacing[0] =
(mCamOffset * logoSpacing[0]); ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) /
(mCarousel.maxLogoCount)) +
mCarousel.logoSize.x();
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f -
(mCamOffset * logoSpacing[0]);
if (mCarousel.logoAlignment == ALIGN_TOP) if (mCarousel.logoAlignment == ALIGN_TOP)
yOff = mCarousel.logoSize.y() / 10.f; yOff = mCarousel.logoSize.y() / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_BOTTOM) else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f);
else else
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f;
break; break;
}
} }
int center = static_cast<int>(mCamOffset); int center = static_cast<int>(mCamOffset);
@ -558,9 +566,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
bufferRight = 0; bufferRight = 0;
} }
for (int i = center - logoCount / 2 + bufferLeft; for (int i = center - logoCount / 2 + bufferLeft; // Line break.
i <= center + logoCount / 2 + bufferRight; i++) { i <= center + logoCount / 2 + bufferRight; i++) {
int index = i; int index = i;
while (index < 0) while (index < 0)
index += static_cast<int>(mEntries.size()); index += static_cast<int>(mEntries.size());
while (index >= static_cast<int>(mEntries.size())) while (index >= static_cast<int>(mEntries.size()))
@ -575,11 +584,11 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
scale = std::min(mCarousel.logoScale, std::max(1.0f, scale)); scale = std::min(mCarousel.logoScale, std::max(1.0f, scale));
scale /= mCarousel.logoScale; scale /= mCarousel.logoScale;
int opacity = static_cast<int>(std::round(0x80 + ((0xFF - 0x80) * int opacity =
(1.0f - fabs(distance))))); static_cast<int>(std::round(0x80 + ((0xFF - 0x80) * (1.0f - fabs(distance)))));
opacity = std::max(static_cast<int>(0x80), opacity); opacity = std::max(static_cast<int>(0x80), opacity);
const std::shared_ptr<GuiComponent> &comp = mEntries.at(index).data.logo; const std::shared_ptr<GuiComponent>& comp = mEntries.at(index).data.logo;
if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) { if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) {
comp->setRotationDegrees(mCarousel.logoRotation * distance); comp->setRotationDegrees(mCarousel.logoRotation * distance);
comp->setRotationOrigin(mCarousel.logoRotationOrigin); comp->setRotationOrigin(mCarousel.logoRotationOrigin);
@ -599,11 +608,11 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
// Adding texture loading buffers depending on scrolling speed and status. // Adding texture loading buffers depending on scrolling speed and status.
int bufferIndex = getScrollingVelocity() + 1; int bufferIndex = getScrollingVelocity() + 1;
Renderer::pushClipRect(Vector2i::Zero(), Vector2i(static_cast<int>(mSize.x()), Renderer::pushClipRect(Vector2i::Zero(),
static_cast<int>(mSize.y()))); Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter + for (int i = extrasCenter + logoBuffersLeft[bufferIndex];
logoBuffersRight[bufferIndex]; i++) { i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) {
int index = i; int index = i;
while (index < 0) while (index < 0)
index += static_cast<int>(mEntries.size()); index += static_cast<int>(mEntries.size());
@ -611,20 +620,20 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
index -= static_cast<int>(mEntries.size()); index -= static_cast<int>(mEntries.size());
// Only render selected system when not showing. // Only render selected system when not showing.
if (mShowing || index == mCursor) if (mShowing || index == mCursor) {
{
Transform4x4f extrasTrans = trans; Transform4x4f extrasTrans = trans;
if (mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL) if (mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL)
extrasTrans.translate(Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0)); extrasTrans.translate(Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0));
else else
extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0)); extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0));
Renderer::pushClipRect(Vector2i(static_cast<int>(extrasTrans.translation()[0]), Renderer::pushClipRect(
static_cast<int>(extrasTrans.translation()[1])), Vector2i(static_cast<int>(extrasTrans.translation()[0]),
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y()))); static_cast<int>(extrasTrans.translation()[1])),
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
SystemViewData data = mEntries.at(index).data; SystemViewData data = mEntries.at(index).data;
for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) { for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) {
GuiComponent *extra = data.backgroundExtras[j]; GuiComponent* extra = data.backgroundExtras[j];
if (extra->getZIndex() >= lower && extra->getZIndex() < upper) if (extra->getZIndex() >= lower && extra->getZIndex() < upper)
extra->render(extrasTrans); extra->render(extrasTrans);
} }
@ -637,13 +646,13 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
void SystemView::renderFade(const Transform4x4f& trans) void SystemView::renderFade(const Transform4x4f& trans)
{ {
unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mExtrasFadeOpacity * 255); unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mExtrasFadeOpacity * 255.0f);
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), fadeColor, fadeColor); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), fadeColor, fadeColor);
} }
// Populate the system carousel with the legacy values. // Populate the system carousel with the legacy values.
void SystemView::getDefaultElements(void) void SystemView::getDefaultElements(void)
{ {
// Carousel. // Carousel.
mCarousel.type = HORIZONTAL; mCarousel.type = HORIZONTAL;
@ -658,13 +667,13 @@ void SystemView::getDefaultElements(void)
mCarousel.colorEnd = 0xFFFFFFD8; mCarousel.colorEnd = 0xFFFFFFD8;
mCarousel.colorGradientHorizontal = true; mCarousel.colorGradientHorizontal = true;
mCarousel.logoScale = 1.2f; mCarousel.logoScale = 1.2f;
mCarousel.logoRotation = 7.5; mCarousel.logoRotation = 7.5f;
mCarousel.logoRotationOrigin.x() = -5; mCarousel.logoRotationOrigin.x() = -5.0f;
mCarousel.logoRotationOrigin.y() = 0.5; mCarousel.logoRotationOrigin.y() = 0.5f;
mCarousel.logoSize.x() = 0.25f * mSize.x(); mCarousel.logoSize.x() = 0.25f * mSize.x();
mCarousel.logoSize.y() = 0.155f * mSize.y(); mCarousel.logoSize.y() = 0.155f * mSize.y();
mCarousel.maxLogoCount = 3; mCarousel.maxLogoCount = 3;
mCarousel.zIndex = 40; mCarousel.zIndex = 40.0f;
// System info bar. // System info bar.
mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f); mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f);
@ -702,7 +711,8 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
if (elem->has("colorEnd")) if (elem->has("colorEnd"))
mCarousel.colorEnd = elem->get<unsigned int>("colorEnd"); mCarousel.colorEnd = elem->get<unsigned int>("colorEnd");
if (elem->has("gradientType")) if (elem->has("gradientType"))
mCarousel.colorGradientHorizontal = !(elem->get<std::string>("gradientType").compare("horizontal")); mCarousel.colorGradientHorizontal =
!(elem->get<std::string>("gradientType").compare("horizontal"));
if (elem->has("logoScale")) if (elem->has("logoScale"))
mCarousel.logoScale = elem->get<float>("logoScale"); mCarousel.logoScale = elem->get<float>("logoScale");
if (elem->has("logoSize")) if (elem->has("logoSize"))
@ -727,14 +737,4 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
else else
mCarousel.logoAlignment = ALIGN_CENTER; mCarousel.logoAlignment = ALIGN_CENTER;
} }
} }
void SystemView::onShow()
{
mShowing = true;
}
void SystemView::onHide()
{
mShowing = false;
}

View file

@ -9,11 +9,11 @@
#ifndef ES_APP_VIEWS_SYSTEM_VIEW_H #ifndef ES_APP_VIEWS_SYSTEM_VIEW_H
#define ES_APP_VIEWS_SYSTEM_VIEW_H #define ES_APP_VIEWS_SYSTEM_VIEW_H
#include "GuiComponent.h"
#include "Sound.h"
#include "components/IList.h" #include "components/IList.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "resources/Font.h" #include "resources/Font.h"
#include "GuiComponent.h"
#include "Sound.h"
#include <memory> #include <memory>
@ -55,8 +55,8 @@ public:
SystemView(Window* window); SystemView(Window* window);
~SystemView(); ~SystemView();
virtual void onShow() override; virtual void onShow() override { mShowing = true; }
virtual void onHide() override; virtual void onHide() override { mShowing = false; }
void goToSystem(SystemData* system, bool animate); void goToSystem(SystemData* system, bool animate);
@ -69,12 +69,14 @@ public:
std::vector<HelpPrompt> getHelpPrompts() override; std::vector<HelpPrompt> getHelpPrompts() override;
virtual HelpStyle getHelpStyle() override; virtual HelpStyle getHelpStyle() override;
CarouselType getCarouselType() { return mCarousel.type; }; CarouselType getCarouselType() { return mCarousel.type; }
protected: protected:
void onCursorChanged(const CursorState& state) override; void onCursorChanged(const CursorState& state) override;
virtual void onScroll() override { virtual void onScroll() override
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND); } {
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
}
private: private:
void populate(); void populate();

View file

@ -9,12 +9,12 @@
#include "UIModeController.h" #include "UIModeController.h"
#include "utils/StringUtil.h"
#include "views/ViewController.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "Log.h" #include "Log.h"
#include "SystemData.h" #include "SystemData.h"
#include "Window.h" #include "Window.h"
#include "utils/StringUtil.h"
#include "views/ViewController.h"
UIModeController* UIModeController::sInstance = nullptr; UIModeController* UIModeController::sInstance = nullptr;
@ -34,7 +34,8 @@ void UIModeController::deinit()
} }
} }
UIModeController::UIModeController() : mPassKeyCounter(0) UIModeController::UIModeController()
: mPassKeyCounter(0)
{ {
mPassKeySequence = Settings::getInstance()->getString("UIMode_passkey"); mPassKeySequence = Settings::getInstance()->getString("UIMode_passkey");
mCurrentUIMode = Settings::getInstance()->getString("UIMode"); mCurrentUIMode = Settings::getInstance()->getString("UIMode");
@ -47,13 +48,12 @@ void UIModeController::monitorUIMode()
if (uimode != mCurrentUIMode && !ViewController::get()->isCameraMoving()) { if (uimode != mCurrentUIMode && !ViewController::get()->isCameraMoving()) {
mCurrentUIMode = uimode; mCurrentUIMode = uimode;
// Reset filters and sort gamelists (which will update the game counter). // Reset filters and sort gamelists (which will update the game counter).
for (auto it = SystemData::sSystemVector.cbegin(); it != for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
(*it)->sortSystem(true); (*it)->sortSystem(true);
(*it)->getIndex()->resetFilters(); (*it)->getIndex()->resetFilters();
if ((*it)->getThemeFolder() == "custom-collections") { if ((*it)->getThemeFolder() == "custom-collections") {
for (FileData* customSystem : for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay())
(*it)->getRootFolder()->getChildrenListToDisplay())
customSystem->getSystem()->getIndex()->resetFilters(); customSystem->getSystem()->getIndex()->resetFilters();
} }
} }
@ -75,6 +75,7 @@ bool UIModeController::listen(InputConfig* config, Input input)
unlockUIMode(); unlockUIMode();
return true; return true;
} }
return false; return false;
} }
@ -101,21 +102,21 @@ void UIModeController::unlockUIMode()
bool UIModeController::isUIModeFull() bool UIModeController::isUIModeFull()
{ {
return ((mCurrentUIMode == "full" || (isUIModeKid() && return ((mCurrentUIMode == "full" ||
Settings::getInstance()->getBool("EnableMenuKidMode"))) (isUIModeKid() && Settings::getInstance()->getBool("EnableMenuKidMode"))) &&
&& !Settings::getInstance()->getBool("ForceKiosk")); !Settings::getInstance()->getBool("ForceKiosk"));
} }
bool UIModeController::isUIModeKid() bool UIModeController::isUIModeKid()
{ {
return (Settings::getInstance()->getBool("ForceKid") || return (Settings::getInstance()->getBool("ForceKid") ||
((mCurrentUIMode == "kid") && !Settings::getInstance()->getBool("ForceKiosk"))); ((mCurrentUIMode == "kid") && !Settings::getInstance()->getBool("ForceKiosk")));
} }
bool UIModeController::isUIModeKiosk() bool UIModeController::isUIModeKiosk()
{ {
return (Settings::getInstance()->getBool("ForceKiosk") || return (Settings::getInstance()->getBool("ForceKiosk") ||
((mCurrentUIMode == "kiosk") && !Settings::getInstance()->getBool("ForceKid"))); ((mCurrentUIMode == "kiosk") && !Settings::getInstance()->getBool("ForceKid")));
} }
std::string UIModeController::getFormattedPassKeyStr() std::string UIModeController::getFormattedPassKeyStr()
@ -124,7 +125,7 @@ std::string UIModeController::getFormattedPassKeyStr()
std::string out = ""; std::string out = "";
for (auto c : mPassKeySequence) { for (auto c : mPassKeySequence) {
out += (out == "") ? "" : " , "; // Add commas between the entries. out += (out == "") ? "" : " , "; // Add commas between the entries.
std::string controllerType = Settings::getInstance()->getString("InputControllerType"); std::string controllerType = Settings::getInstance()->getString("InputControllerType");
std::string symbolA; std::string symbolA;
@ -139,19 +140,19 @@ std::string UIModeController::getFormattedPassKeyStr()
symbolY = "X"; symbolY = "X";
} }
else if (controllerType == "ps4" || controllerType == "ps5") { else if (controllerType == "ps4" || controllerType == "ps5") {
#if defined(_MSC_VER) // MSVC compiler.
// These symbols are far from perfect but you can at least understand what // These symbols are far from perfect but you can at least understand what
// they are supposed to depict. // they are supposed to depict.
#if defined(_MSC_VER) // MSVC compiler.
symbolA = Utils::String::wideStringToString(L"\uF00D"); // Cross. symbolA = Utils::String::wideStringToString(L"\uF00D"); // Cross.
symbolB = Utils::String::wideStringToString(L"\uF111"); // Circle symbolB = Utils::String::wideStringToString(L"\uF111"); // Circle
symbolX = Utils::String::wideStringToString(L"\uF04D"); // Square. symbolX = Utils::String::wideStringToString(L"\uF04D"); // Square.
symbolY = Utils::String::wideStringToString(L"\uF0D8"); // Triangle. symbolY = Utils::String::wideStringToString(L"\uF0D8"); // Triangle.
#else #else
symbolA = "\uF00D"; // Cross. symbolA = "\uF00D"; // Cross.
symbolB = "\uF111"; // Circle symbolB = "\uF111"; // Circle
symbolX = "\uF04D"; // Square. symbolX = "\uF04D"; // Square.
symbolY = "\uF0D8"; // Triangle. symbolY = "\uF0D8"; // Triangle.
#endif #endif
} }
else { else {
// Xbox controller. // Xbox controller.
@ -193,8 +194,8 @@ std::string UIModeController::getFormattedPassKeyStr()
bool UIModeController::isValidInput(InputConfig* config, Input input) bool UIModeController::isValidInput(InputConfig* config, Input input)
{ {
if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore it. if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore it.
(!input.value)) // Not a key-down event. (!input.value)) // Not a key-down event.
return false; return false;
else else
return true; return true;

View file

@ -39,7 +39,7 @@ public:
bool isUIModeKid(); bool isUIModeKid();
bool isUIModeKiosk(); bool isUIModeKiosk();
void setCurrentUIMode(const std::string& mode) { mCurrentUIMode = mode; }; void setCurrentUIMode(const std::string& mode) { mCurrentUIMode = mode; }
private: private:
UIModeController(); UIModeController();
@ -58,8 +58,9 @@ private:
int mPassKeyCounter; int mPassKeyCounter;
// These are Xbox button names, so they may be different in pracise on non-Xbox controllers. // These are Xbox button names, so they may be different in pracise on non-Xbox controllers.
const std::vector<std::string> mInputVals = const std::vector<std::string> mInputVals = {
{ "up", "down", "left", "right", "a", "b", "x", "y" }; "up", "down", "left", "right", "a", "b", "x", "y"
};
}; };
#endif // ES_APP_VIEWS_UI_MODE_CONTROLLER_H #endif // ES_APP_VIEWS_UI_MODE_CONTROLLER_H

View file

@ -12,17 +12,6 @@
#include "views/ViewController.h" #include "views/ViewController.h"
#include "animations/Animation.h"
#include "animations/LambdaAnimation.h"
#include "animations/MoveCameraAnimation.h"
#include "guis/GuiInfoPopup.h"
#include "guis/GuiMenu.h"
#include "views/gamelist/DetailedGameListView.h"
#include "views/gamelist/GridGameListView.h"
#include "views/gamelist/IGameListView.h"
#include "views/gamelist/VideoGameListView.h"
#include "views/SystemView.h"
#include "views/UIModeController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "InputManager.h" #include "InputManager.h"
@ -32,8 +21,20 @@
#include "SystemData.h" #include "SystemData.h"
#include "SystemView.h" #include "SystemView.h"
#include "Window.h" #include "Window.h"
#include "animations/Animation.h"
#include "animations/LambdaAnimation.h"
#include "animations/MoveCameraAnimation.h"
#include "guis/GuiInfoPopup.h"
#include "guis/GuiMenu.h"
#include "views/SystemView.h"
#include "views/UIModeController.h"
#include "views/gamelist/DetailedGameListView.h"
#include "views/gamelist/GridGameListView.h"
#include "views/gamelist/IGameListView.h"
#include "views/gamelist/VideoGameListView.h"
ViewController* ViewController::sInstance = nullptr; ViewController* ViewController::sInstance = nullptr;
#if defined(_MSC_VER) // MSVC compiler. #if defined(_MSC_VER) // MSVC compiler.
const std::string ViewController::FAVORITE_CHAR = Utils::String::wideStringToString(L"\uF005"); const std::string ViewController::FAVORITE_CHAR = Utils::String::wideStringToString(L"\uF005");
const std::string ViewController::FOLDER_CHAR = Utils::String::wideStringToString(L"\uF07C"); const std::string ViewController::FOLDER_CHAR = Utils::String::wideStringToString(L"\uF07C");
@ -60,21 +61,20 @@ void ViewController::init(Window* window)
sInstance = new ViewController(window); sInstance = new ViewController(window);
} }
ViewController::ViewController( ViewController::ViewController(Window* window)
Window* window) : GuiComponent(window)
: GuiComponent(window), , mCurrentView(nullptr)
mCurrentView(nullptr), , mPreviousView(nullptr)
mPreviousView(nullptr), , mSkipView(nullptr)
mSkipView(nullptr), , mCamera(Transform4x4f::Identity())
mCamera(Transform4x4f::Identity()), , mSystemViewTransition(false)
mSystemViewTransition(false), , mWrappedViews(false)
mWrappedViews(false), , mFadeOpacity(0)
mFadeOpacity(0), , mCancelledTransition(false)
mCancelledTransition(false), , mLockInput(false)
mLockInput(false), , mNextSystem(false)
mNextSystem(false), , mGameToLaunch(nullptr)
mGameToLaunch(nullptr), , mNoGamesMessageBox(nullptr)
mNoGamesMessageBox(nullptr)
{ {
mState.viewing = NOTHING; mState.viewing = NOTHING;
mState.viewstyle = AUTOMATIC; mState.viewstyle = AUTOMATIC;
@ -89,108 +89,106 @@ ViewController::~ViewController()
void ViewController::invalidSystemsFileDialog() void ViewController::invalidSystemsFileDialog()
{ {
std::string errorMessage = std::string errorMessage = "COULDN'T PARSE THE SYSTEMS CONFIGURATION FILE.\n"
"COULDN'T PARSE THE SYSTEMS CONFIGURATION FILE.\n" "IF YOU HAVE A CUSTOMIZED es_systems.xml FILE, THEN\n"
"IF YOU HAVE A CUSTOMIZED es_systems.xml FILE, THEN\n" "SOMETHING IS LIKELY WRONG WITH YOUR XML SYNTAX.\n"
"SOMETHING IS LIKELY WRONG WITH YOUR XML SYNTAX.\n" "IF YOU DON'T HAVE A CUSTOM SYSTEMS FILE, THEN THE\n"
"IF YOU DON'T HAVE A CUSTOM SYSTEMS FILE, THEN THE\n" "EMULATIONSTATION INSTALLATION IS BROKEN. SEE THE\n"
"EMULATIONSTATION INSTALLATION IS BROKEN. SEE THE\n" "APPLICATION LOG FILE es_log.txt FOR ADDITIONAL INFO.";
"APPLICATION LOG FILE es_log.txt FOR ADDITIONAL INFO.";
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), mWindow->pushGui(new GuiMsgBox(
errorMessage.c_str(), mWindow, HelpStyle(), errorMessage.c_str(), "QUIT",
"QUIT", [] { [] {
SDL_Event quit; SDL_Event quit;
quit.type = SDL_QUIT; quit.type = SDL_QUIT;
SDL_PushEvent(&quit); SDL_PushEvent(&quit);
}, "", nullptr, "", nullptr, true)); },
"", nullptr, "", nullptr, true));
} }
void ViewController::noGamesDialog() void ViewController::noGamesDialog()
{ {
mNoGamesErrorMessage = mNoGamesErrorMessage = "NO GAME FILES WERE FOUND. EITHER PLACE YOUR GAMES IN\n"
"NO GAME FILES WERE FOUND. EITHER PLACE YOUR GAMES IN\n" "THE CURRENTLY CONFIGURED ROM DIRECTORY OR CHANGE\n"
"THE CURRENTLY CONFIGURED ROM DIRECTORY OR CHANGE\n" "ITS PATH USING THE BUTTON BELOW. OPTIONALLY THE ROM\n"
"ITS PATH USING THE BUTTON BELOW. OPTIONALLY THE ROM\n" "DIRECTORY STRUCTURE CAN BE GENERATED WHICH WILL\n"
"DIRECTORY STRUCTURE CAN BE GENERATED WHICH WILL\n" "CREATE A TEXT FILE FOR EACH SYSTEM PROVIDING SOME\n"
"CREATE A TEXT FILE FOR EACH SYSTEM PROVIDING SOME\n" "INFORMATION SUCH AS THE SUPPORTED FILE EXTENSIONS.\n"
"INFORMATION SUCH AS THE SUPPORTED FILE EXTENSIONS.\n" "THIS IS THE CURRENTLY CONFIGURED ROM DIRECTORY:\n";
"THIS IS THE CURRENTLY CONFIGURED ROM DIRECTORY:\n";
#if defined(_WIN64) #if defined(_WIN64)
mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
#else #else
mRomDirectory = FileData::getROMDirectory(); mRomDirectory = FileData::getROMDirectory();
#endif #endif
mNoGamesMessageBox = new GuiMsgBox(mWindow, HelpStyle(), mNoGamesErrorMessage + mRomDirectory, mNoGamesMessageBox = new GuiMsgBox(
"CHANGE ROM DIRECTORY", [this] { mWindow, HelpStyle(), mNoGamesErrorMessage + mRomDirectory, "CHANGE ROM DIRECTORY",
std::string currentROMDirectory; [this] {
#if defined(_WIN64) std::string currentROMDirectory;
currentROMDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); #if defined(_WIN64)
#else currentROMDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
currentROMDirectory = FileData::getROMDirectory(); #else
#endif currentROMDirectory = FileData::getROMDirectory();
#endif
mWindow->pushGui(new GuiComplexTextEditPopup( mWindow->pushGui(new GuiComplexTextEditPopup(
mWindow, mWindow, HelpStyle(), "ENTER ROM DIRECTORY PATH",
HelpStyle(), "Currently configured path:", currentROMDirectory, currentROMDirectory,
"ENTER ROM DIRECTORY PATH",
"Currently configured path:",
currentROMDirectory,
currentROMDirectory,
[this](const std::string& newROMDirectory) { [this](const std::string& newROMDirectory) {
Settings::getInstance()->setString("ROMDirectory", newROMDirectory); Settings::getInstance()->setString("ROMDirectory", newROMDirectory);
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();
#if defined(_WIN64) #if defined(_WIN64)
mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
#else #else
mRomDirectory = FileData::getROMDirectory(); mRomDirectory = FileData::getROMDirectory();
#endif #endif
mNoGamesMessageBox->changeText(mNoGamesErrorMessage + mRomDirectory); mNoGamesMessageBox->changeText(mNoGamesErrorMessage + mRomDirectory);
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
"ROM DIRECTORY SETTING SAVED, RESTART\n" "ROM DIRECTORY SETTING SAVED, RESTART\n"
"THE APPLICATION TO RESCAN THE SYSTEMS", "THE APPLICATION TO RESCAN THE SYSTEMS",
"OK", nullptr, "", nullptr, "", nullptr, true)); "OK", nullptr, "", nullptr, "", nullptr, true));
}, },
false, false, "SAVE", "SAVE CHANGES?", "LOAD CURRENT", "LOAD CURRENTLY CONFIGURED VALUE",
"SAVE", "CLEAR", "CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)", false));
"SAVE CHANGES?", },
"LOAD CURRENT", "CREATE DIRECTORIES",
"LOAD CURRENTLY CONFIGURED VALUE", [this] {
"CLEAR", mWindow->pushGui(new GuiMsgBox(
"CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)", mWindow, HelpStyle(),
false));
},
"CREATE DIRECTORIES", [this] {
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
"THIS WILL CREATE DIRECTORIES FOR ALL THE\n" "THIS WILL CREATE DIRECTORIES FOR ALL THE\n"
"GAME SYSTEMS DEFINED IN es_systems.xml\n\n" "GAME SYSTEMS DEFINED IN es_systems.xml\n\n"
"THIS MAY CREATE A LOT OF FOLDERS SO IT'S\n" "THIS MAY CREATE A LOT OF FOLDERS SO IT'S\n"
"ADVICED TO REMOVE THE ONES YOU DON'T NEED\n\n" "ADVICED TO REMOVE THE ONES YOU DON'T NEED\n\n"
"PROCEED?", "PROCEED?",
"YES", [this] { "YES",
if (!SystemData::createSystemDirectories()) { [this] {
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), if (!SystemData::createSystemDirectories()) {
"THE SYSTEM DIRECTORIES WERE SUCCESSFULLY\n" mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
"GENERATED, EXIT THE APPLICATION AND PLACE\n" "THE SYSTEM DIRECTORIES WERE SUCCESSFULLY\n"
"YOUR GAMES IN THE NEWLY CREATED FOLDERS", "OK", nullptr, "GENERATED, EXIT THE APPLICATION AND PLACE\n"
"", nullptr, "", nullptr, true)); "YOUR GAMES IN THE NEWLY CREATED FOLDERS",
} "OK", nullptr, "", nullptr, "", nullptr,
else { true));
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), }
"ERROR CREATING THE SYSTEM DIRECTORIES,\n" else {
"PERMISSION PROBLEMS OR DISK FULL?\n\n" mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
"SEE THE LOG FILE FOR MORE DETAILS", "OK", nullptr, "ERROR CREATING THE SYSTEM DIRECTORIES,\n"
"", nullptr, "", nullptr, true)); "PERMISSION PROBLEMS OR DISK FULL?\n\n"
} "SEE THE LOG FILE FOR MORE DETAILS",
}, "NO", nullptr, "", nullptr, true)); "OK", nullptr, "", nullptr, "", nullptr,
}, true));
"QUIT", [] { }
SDL_Event quit; },
quit.type = SDL_QUIT; "NO", nullptr, "", nullptr, true));
SDL_PushEvent(&quit); },
}, true, false); "QUIT",
[] {
SDL_Event quit;
quit.type = SDL_QUIT;
SDL_PushEvent(&quit);
},
true, false);
mWindow->pushGui(mNoGamesMessageBox); mWindow->pushGui(mNoGamesMessageBox);
} }
@ -205,8 +203,8 @@ void ViewController::goToStart()
// If a specific system is requested, go directly to its game list. // If a specific system is requested, go directly to its game list.
auto requestedSystem = Settings::getInstance()->getString("StartupSystem"); auto requestedSystem = Settings::getInstance()->getString("StartupSystem");
if ("" != requestedSystem && "retropie" != requestedSystem) { if ("" != requestedSystem && "retropie" != requestedSystem) {
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) { it != SystemData::sSystemVector.cend(); it++) {
if ((*it)->getName() == requestedSystem) { if ((*it)->getName() == requestedSystem) {
goToGameList(*it); goToGameList(*it);
return; return;
@ -237,7 +235,7 @@ bool ViewController::isCameraMoving()
{ {
if (mCurrentView) { if (mCurrentView) {
if (mCamera.r3().x() - -mCurrentView->getPosition().x() != 0 || if (mCamera.r3().x() - -mCurrentView->getPosition().x() != 0 ||
mCamera.r3().y() - -mCurrentView->getPosition().y() != 0) mCamera.r3().y() - -mCurrentView->getPosition().y() != 0)
return true; return true;
} }
return false; return false;
@ -322,7 +320,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
auto systemList = getSystemListView(); auto systemList = getSystemListView();
systemList->setPosition(getSystemId(system) * static_cast<float>(Renderer::getScreenWidth()), systemList->setPosition(getSystemId(system) * static_cast<float>(Renderer::getScreenWidth()),
systemList->getPosition().y()); systemList->getPosition().y());
systemList->goToSystem(system, false); systemList->goToSystem(system, false);
mCurrentView = systemList; mCurrentView = systemList;
@ -333,7 +331,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
mCamera.translation() = -mCurrentView->getPosition(); mCamera.translation() = -mCurrentView->getPosition();
if (Settings::getInstance()->getString("TransitionStyle") == "slide") { if (Settings::getInstance()->getString("TransitionStyle") == "slide") {
if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL || if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL ||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL) getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
mCamera.translation().y() += Renderer::getScreenHeight(); mCamera.translation().y() += Renderer::getScreenHeight();
else else
mCamera.translation().x() -= Renderer::getScreenWidth(); mCamera.translation().x() -= Renderer::getScreenWidth();
@ -341,7 +339,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
} }
else if (Settings::getInstance()->getString("TransitionStyle") == "fade") { else if (Settings::getInstance()->getString("TransitionStyle") == "fade") {
if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL || if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL ||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL) getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
mCamera.translation().y() += Renderer::getScreenHeight(); mCamera.translation().y() += Renderer::getScreenHeight();
else else
mCamera.translation().x() += Renderer::getScreenWidth(); mCamera.translation().x() += Renderer::getScreenWidth();
@ -396,8 +394,9 @@ void ViewController::goToGameList(SystemData* system)
restoreViewPosition(); restoreViewPosition();
if (mPreviousView && Settings::getInstance()->getString("TransitionStyle") == "fade" && if (mPreviousView && Settings::getInstance()->getString("TransitionStyle") == "fade" &&
isAnimationPlaying(0)) isAnimationPlaying(0)) {
mPreviousView->onHide(); mPreviousView->onHide();
}
if (mPreviousView) { if (mPreviousView) {
mSkipView = mPreviousView; mSkipView = mPreviousView;
@ -449,7 +448,7 @@ void ViewController::goToGameList(SystemData* system)
int sysId = getSystemId(system); int sysId = getSystemId(system);
sysList->setPosition(sysId * static_cast<float>(Renderer::getScreenWidth()), sysList->setPosition(sysId * static_cast<float>(Renderer::getScreenWidth()),
sysList->getPosition().y()); sysList->getPosition().y());
offsetX = sysList->getPosition().x() - offsetX; offsetX = sysList->getPosition().x() - offsetX;
mCamera.translation().x() -= offsetX; mCamera.translation().x() -= offsetX;
} }
@ -464,7 +463,7 @@ void ViewController::goToGameList(SystemData* system)
float offsetX = getGameListView(system)->getPosition().x(); float offsetX = getGameListView(system)->getPosition().x();
// This is needed to move the camera in the correct direction if there are only two systems. // This is needed to move the camera in the correct direction if there are only two systems.
if (SystemData::sSystemVector.size() == 2 && mNextSystem) if (SystemData::sSystemVector.size() == 2 && mNextSystem)
offsetX -= Renderer::getScreenWidth(); offsetX -= Renderer::getScreenWidth();
else else
offsetX += Renderer::getScreenWidth(); offsetX += Renderer::getScreenWidth();
currentPosition.x() = offsetX; currentPosition.x() = offsetX;
@ -541,11 +540,13 @@ void ViewController::playViewTransition(bool instant)
std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
if (instant || transition_style == "instant") { if (instant || transition_style == "instant") {
setAnimation(new LambdaAnimation([this, target](float /*t*/) { setAnimation(new LambdaAnimation(
this->mCamera.translation() = -target; [this, target](float /*t*/) {
if (mPreviousView) this->mCamera.translation() = -target;
mPreviousView->onHide(); if (mPreviousView)
}, 1)); mPreviousView->onHide();
},
1));
updateHelpPrompts(); updateHelpPrompts();
} }
else if (transition_style == "fade") { else if (transition_style == "fade") {
@ -558,7 +559,7 @@ void ViewController::playViewTransition(bool instant)
// Without this, a (much shorter) fade transition would still play as // Without this, a (much shorter) fade transition would still play as
// finishedCallback is calling this function. // finishedCallback is calling this function.
if (!mCancelledTransition) if (!mCancelledTransition)
mFadeOpacity = Math::lerp(0, 1, t); mFadeOpacity = Math::lerp(0.0f, 1.0f, t);
}; };
auto fadeCallback = [this]() { auto fadeCallback = [this]() {
@ -569,12 +570,12 @@ void ViewController::playViewTransition(bool instant)
const static int FADE_DURATION = 120; // Fade in/out time. const static int FADE_DURATION = 120; // Fade in/out time.
const static int FADE_WAIT = 200; // Time to wait between in/out. const static int FADE_WAIT = 200; // Time to wait between in/out.
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0, setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0,
[this, fadeFunc, fadeCallback, target] { [this, fadeFunc, fadeCallback, target] {
this->mCamera.translation() = -target; this->mCamera.translation() = -target;
updateHelpPrompts(); updateHelpPrompts();
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT,
FADE_WAIT, fadeCallback, true); fadeCallback, true);
}); });
// Fast-forward animation if we're partway faded. // Fast-forward animation if we're partway faded.
if (target == -mCamera.translation()) { if (target == -mCamera.translation()) {
@ -616,7 +617,7 @@ bool ViewController::runInBackground(SystemData* system)
// with the game. In that situation ES-DE would wait until the whole Steam application was // with the game. In that situation ES-DE would wait until the whole Steam application was
// shut down before it would resume. I.e. it would not be enough to just stop the game. // shut down before it would resume. I.e. it would not be enough to just stop the game.
if (system->hasPlatformId(PlatformIds::VALVE_STEAM) || if (system->hasPlatformId(PlatformIds::VALVE_STEAM) ||
Settings::getInstance()->getBool("RunInBackground")) Settings::getInstance()->getBool("RunInBackground"))
return true; return true;
else else
return false; return false;
@ -646,8 +647,9 @@ void ViewController::launch(FileData* game)
if (durationString == "disabled") { if (durationString == "disabled") {
// If the game launch screen has been set as disabled, show a simple info popup // If the game launch screen has been set as disabled, show a simple info popup
// notification instead. // notification instead.
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "LAUNCHING GAME '" + GuiInfoPopup* s = new GuiInfoPopup(
Utils::String::toUpper(game->metadata.get("name") + "'"), 10000); mWindow, "LAUNCHING GAME '" + Utils::String::toUpper(game->metadata.get("name") + "'"),
10000);
mWindow->setInfoPopup(s); mWindow->setInfoPopup(s);
duration = 1700; duration = 1700;
} }
@ -670,16 +672,14 @@ void ViewController::launch(FileData* game)
// This is just a dummy animation in order for the launch screen or notification popup // This is just a dummy animation in order for the launch screen or notification popup
// to be displayed briefly, and for the navigation sound playing to be able to complete. // to be displayed briefly, and for the navigation sound playing to be able to complete.
// During this time period, all user input is blocked. // During this time period, all user input is blocked.
setAnimation(new LambdaAnimation([](float t){}, duration), 0, [this, game] { setAnimation(new LambdaAnimation([](float t) {}, duration), 0, [this, game] {
game->launchGame(mWindow); game->launchGame(mWindow);
// If the launch screen is disabled then this will do nothing. // If the launch screen is disabled then this will do nothing.
mWindow->closeLaunchScreen(); mWindow->closeLaunchScreen();
onFileChanged(game, true); onFileChanged(game, true);
// This is a workaround so that any keys or button presses used for exiting the emulator // This is a workaround so that any keys or button presses used for exiting the emulator
// are not captured upon returning. // are not captured upon returning.
setAnimation(new LambdaAnimation([](float t){}, 1), 0, [this] { setAnimation(new LambdaAnimation([](float t) {}, 1), 0, [this] { mLockInput = false; });
mLockInput = false;
});
}); });
} }
@ -733,38 +733,41 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
} }
// Create the view. // Create the view.
switch (selectedViewStyle) switch (selectedViewStyle) {
{ case VIDEO: {
case VIDEO:
view = std::shared_ptr<IGameListView>( view = std::shared_ptr<IGameListView>(
new VideoGameListView(mWindow, system->getRootFolder())); new VideoGameListView(mWindow, system->getRootFolder()));
mState.viewstyle = VIDEO; mState.viewstyle = VIDEO;
break; break;
case DETAILED: }
case DETAILED: {
view = std::shared_ptr<IGameListView>( view = std::shared_ptr<IGameListView>(
new DetailedGameListView(mWindow, system->getRootFolder())); new DetailedGameListView(mWindow, system->getRootFolder()));
mState.viewstyle = DETAILED; mState.viewstyle = DETAILED;
break; break;
case GRID: }
case GRID: {
view = std::shared_ptr<IGameListView>( view = std::shared_ptr<IGameListView>(
new GridGameListView(mWindow, system->getRootFolder())); new GridGameListView(mWindow, system->getRootFolder()));
mState.viewstyle = GRID; mState.viewstyle = GRID;
break; break;
case BASIC: }
default: case BASIC: {
}
default: {
view = std::shared_ptr<IGameListView>( view = std::shared_ptr<IGameListView>(
new BasicGameListView(mWindow, system->getRootFolder())); new BasicGameListView(mWindow, system->getRootFolder()));
mState.viewstyle = BASIC; mState.viewstyle = BASIC;
break; break;
}
} }
view->setTheme(system->getTheme()); view->setTheme(system->getTheme());
std::vector<SystemData*>& sysVec = SystemData::sSystemVector; std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
int id = static_cast<int>( int id = static_cast<int>(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
view->setPosition(id * static_cast<float>(Renderer::getScreenWidth()), view->setPosition(id * static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight() * 2)); static_cast<float>(Renderer::getScreenHeight() * 2));
addChild(view.get()); addChild(view.get());
@ -799,8 +802,8 @@ bool ViewController::input(InputConfig* config, Input input)
// Open the main menu. // Open the main menu.
if (!(UIModeController::getInstance()->isUIModeKid() && if (!(UIModeController::getInstance()->isUIModeKid() &&
!Settings::getInstance()->getBool("EnableMenuKidMode")) && !Settings::getInstance()->getBool("EnableMenuKidMode")) &&
config->isMappedTo("start", input) && input.value != 0) { config->isMappedTo("start", input) && input.value != 0) {
// If we don't stop the scrolling here, it will continue to // If we don't stop the scrolling here, it will continue to
// run after closing the menu. // run after closing the menu.
if (mSystemListView->isScrolling()) if (mSystemListView->isScrolling())
@ -854,7 +857,7 @@ void ViewController::render(const Transform4x4f& parentTrans)
// Camera position, position + size. // Camera position, position + size.
Vector3f viewStart = transInverse.translation(); Vector3f viewStart = transInverse.translation();
Vector3f viewEnd = transInverse * Vector3f(static_cast<float>(Renderer::getScreenWidth()), Vector3f viewEnd = transInverse * Vector3f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight(), 0)); static_cast<float>(Renderer::getScreenHeight(), 0));
// Keep track of UI mode changes. // Keep track of UI mode changes.
UIModeController::getInstance()->monitorUIMode(); UIModeController::getInstance()->monitorUIMode();
@ -870,11 +873,11 @@ void ViewController::render(const Transform4x4f& parentTrans)
if (it->second == mCurrentView || (it->second == mPreviousView && isCameraMoving())) { if (it->second == mCurrentView || (it->second == mPreviousView && isCameraMoving())) {
// Clipping. // Clipping.
Vector3f guiStart = it->second->getPosition(); Vector3f guiStart = it->second->getPosition();
Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(), Vector3f guiEnd = it->second->getPosition() +
it->second->getSize().y(), 0); Vector3f(it->second->getSize().x(), it->second->getSize().y(), 0);
if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() && if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() &&
guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y())
it->second->render(trans); it->second->render(trans);
} }
} }
@ -887,7 +890,7 @@ void ViewController::render(const Transform4x4f& parentTrans)
unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mFadeOpacity * 255); unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mFadeOpacity * 255);
Renderer::setMatrix(parentTrans); Renderer::setMatrix(parentTrans);
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()), Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), fadeColor, fadeColor); static_cast<float>(Renderer::getScreenHeight()), fadeColor, fadeColor);
} }
} }
@ -895,13 +898,14 @@ void ViewController::preload()
{ {
unsigned int systemCount = static_cast<int>(SystemData::sSystemVector.size()); unsigned int systemCount = static_cast<int>(SystemData::sSystemVector.size());
for (auto it = SystemData::sSystemVector.cbegin(); for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend();
it != SystemData::sSystemVector.cend(); it ++) { it++) {
if (Settings::getInstance()->getBool("SplashScreen") && if (Settings::getInstance()->getBool("SplashScreen") &&
Settings::getInstance()->getBool("SplashScreenProgress")) { Settings::getInstance()->getBool("SplashScreenProgress")) {
mWindow->renderLoadingScreen("Loading '" + (*it)->getFullName() + "' (" + mWindow->renderLoadingScreen(
std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it)+1) + "Loading '" + (*it)->getFullName() + "' (" +
"/" + std::to_string(systemCount) + ")"); std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it) + 1) + "/" +
std::to_string(systemCount) + ")");
} }
(*it)->getIndex()->resetFilters(); (*it)->getIndex()->resetFilters();
getGameListView(*it); getGameListView(*it);
@ -1028,7 +1032,7 @@ std::vector<HelpPrompt> ViewController::getHelpPrompts()
prompts = mCurrentView->getHelpPrompts(); prompts = mCurrentView->getHelpPrompts();
if (!(UIModeController::getInstance()->isUIModeKid() && if (!(UIModeController::getInstance()->isUIModeKid() &&
!Settings::getInstance()->getBool("EnableMenuKidMode"))) !Settings::getInstance()->getBool("EnableMenuKidMode")))
prompts.push_back(HelpPrompt("start", "menu")); prompts.push_back(HelpPrompt("start", "menu"));
return prompts; return prompts;
} }

View file

@ -13,11 +13,11 @@
#ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H #ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H
#define ES_APP_VIEWS_VIEW_CONTROLLER_H #define ES_APP_VIEWS_VIEW_CONTROLLER_H
#include "FileData.h"
#include "GuiComponent.h"
#include "guis/GuiComplexTextEditPopup.h" #include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "renderers/Renderer.h" #include "renderers/Renderer.h"
#include "FileData.h"
#include "GuiComponent.h"
#include <vector> #include <vector>
@ -46,8 +46,10 @@ public:
// If a basic view detected a metadata change, it can request to recreate // If a basic view detected a metadata change, it can request to recreate
// the current gamelist view (as it may change to be detailed). // the current gamelist view (as it may change to be detailed).
void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false); void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false);
inline void reloadGameListView(SystemData* system, bool reloadTheme = false) void reloadGameListView(SystemData* system, bool reloadTheme = false)
{ reloadGameListView(getGameListView(system).get(), reloadTheme); } {
reloadGameListView(getGameListView(system).get(), reloadTheme);
}
// Reload everything with a theme. // Reload everything with a theme.
// Used when the "ThemeSet" setting changes. // Used when the "ThemeSet" setting changes.
void reloadAll(); void reloadAll();
@ -67,22 +69,26 @@ public:
void stopScrolling(); void stopScrolling();
void onFileChanged(FileData* file, bool reloadGameList); void onFileChanged(FileData* file, bool reloadGameList);
void triggerGameLaunch(FileData* game) { mGameToLaunch = game; mLockInput = true; }; void triggerGameLaunch(FileData* game)
bool getGameLaunchTriggered() { return (mGameToLaunch != nullptr); }; {
mGameToLaunch = game;
mLockInput = true;
};
bool getGameLaunchTriggered() { return (mGameToLaunch != nullptr); }
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override; void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override; void render(const Transform4x4f& parentTrans) override;
enum ViewMode { enum ViewMode {
NOTHING, NOTHING, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
START_SCREEN, START_SCREEN,
SYSTEM_SELECT, SYSTEM_SELECT,
GAME_LIST GAME_LIST
}; };
enum GameListViewStyle { enum GameListViewStyle {
AUTOMATIC, AUTOMATIC, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
BASIC, BASIC,
DETAILED, DETAILED,
GRID, GRID,
@ -93,18 +99,18 @@ public:
ViewMode viewing; ViewMode viewing;
GameListViewStyle viewstyle; GameListViewStyle viewstyle;
inline SystemData* getSystem() const SystemData* getSystem() const
{ {
assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT);
return system; return system;
} }
private: private:
friend ViewController; friend ViewController;
SystemData* system; SystemData* system;
}; };
inline const State& getState() const { return mState; } const State& getState() const { return mState; }
virtual std::vector<HelpPrompt> getHelpPrompts() override; virtual std::vector<HelpPrompt> getHelpPrompts() override;
virtual HelpStyle getHelpStyle() override; virtual HelpStyle getHelpStyle() override;
@ -143,6 +149,9 @@ private:
std::map<SystemData*, std::shared_ptr<IGameListView>> mGameListViews; std::map<SystemData*, std::shared_ptr<IGameListView>> mGameListViews;
std::shared_ptr<SystemView> mSystemListView; std::shared_ptr<SystemView> mSystemListView;
FileData* mGameToLaunch;
State mState;
Transform4x4f mCamera; Transform4x4f mCamera;
bool mSystemViewTransition; bool mSystemViewTransition;
bool mWrappedViews; bool mWrappedViews;
@ -151,9 +160,6 @@ private:
bool mCancelledTransition; // Needed only for the Fade transition style. bool mCancelledTransition; // Needed only for the Fade transition style.
bool mLockInput; bool mLockInput;
bool mNextSystem; bool mNextSystem;
FileData* mGameToLaunch;
State mState;
}; };
#endif // ES_APP_VIEWS_VIEW_CONTROLLER_H #endif // ES_APP_VIEWS_VIEW_CONTROLLER_H

View file

@ -8,15 +8,16 @@
#include "views/gamelist/BasicGameListView.h" #include "views/gamelist/BasicGameListView.h"
#include "utils/FileSystemUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
#include "utils/FileSystemUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
BasicGameListView::BasicGameListView(Window* window, FileData* root) BasicGameListView::BasicGameListView(Window* window, FileData* root)
: ISimpleGameListView(window, root), mList(window) : ISimpleGameListView(window, root)
, mList(window)
{ {
mList.setSize(mSize.x(), mSize.y() * 0.8f); mList.setSize(mSize.x(), mSize.y() * 0.8f);
mList.setPosition(0, mSize.y() * 0.2f); mList.setPosition(0, mSize.y() * 0.2f);
@ -89,16 +90,16 @@ void BasicGameListView::populateList(const std::vector<FileData*>& files, FileDa
} }
if ((*it)->getFavorite() && favoriteStar && if ((*it)->getFavorite() && favoriteStar &&
mRoot->getSystem()->getName() != "favorites") { mRoot->getSystem()->getName() != "favorites") {
if (Settings::getInstance()->getBool("SpecialCharsASCII")) if (Settings::getInstance()->getBool("SpecialCharsASCII"))
mList.add(inCollectionPrefix + "* " + mList.add(inCollectionPrefix + "* " + (*it)->getName(), *it,
(*it)->getName(), *it, ((*it)->getType() == FOLDER)); ((*it)->getType() == FOLDER));
else else
mList.add(inCollectionPrefix + ViewController::FAVORITE_CHAR + " " + mList.add(inCollectionPrefix + ViewController::FAVORITE_CHAR + " " +
(*it)->getName(), *it, ((*it)->getType() == FOLDER)); (*it)->getName(),
*it, ((*it)->getType() == FOLDER));
} }
else if ((*it)->getType() == FOLDER && else if ((*it)->getType() == FOLDER && mRoot->getSystem()->getName() != "collections") {
mRoot->getSystem()->getName() != "collections") {
if (Settings::getInstance()->getBool("SpecialCharsASCII")) if (Settings::getInstance()->getBool("SpecialCharsASCII"))
mList.add("# " + (*it)->getName(), *it, true); mList.add("# " + (*it)->getName(), *it, true);
else else
@ -117,11 +118,6 @@ void BasicGameListView::populateList(const std::vector<FileData*>& files, FileDa
generateFirstLetterIndex(files); generateFirstLetterIndex(files);
} }
FileData* BasicGameListView::getCursor()
{
return mList.getSelected();
}
void BasicGameListView::setCursor(FileData* cursor) void BasicGameListView::setCursor(FileData* cursor)
{ {
if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) { if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) {
@ -133,6 +129,7 @@ void BasicGameListView::setCursor(FileData* cursor)
if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) { if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) {
std::stack<FileData*> tmp; std::stack<FileData*> tmp;
FileData* ptr = cursor->getParent(); FileData* ptr = cursor->getParent();
while (ptr && ptr != mRoot) { while (ptr && ptr != mRoot) {
tmp.push(ptr); tmp.push(ptr);
ptr = ptr->getParent(); ptr = ptr->getParent();
@ -148,31 +145,6 @@ void BasicGameListView::setCursor(FileData* cursor)
} }
} }
FileData* BasicGameListView::getNextEntry()
{
return mList.getNext();
}
FileData* BasicGameListView::getPreviousEntry()
{
return mList.getPrevious();
}
FileData* BasicGameListView::getFirstEntry()
{
return mList.getFirst();
}
FileData* BasicGameListView::getLastEntry()
{
return mList.getLast();
}
FileData* BasicGameListView::getFirstGameEntry()
{
return mFirstGameEntry;
}
void BasicGameListView::addPlaceholder(FileData* firstEntry) void BasicGameListView::addPlaceholder(FileData* firstEntry)
{ {
// Empty list, add a placeholder. // Empty list, add a placeholder.
@ -186,18 +158,9 @@ void BasicGameListView::addPlaceholder(FileData* firstEntry)
mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER)); mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER));
} }
std::string BasicGameListView::getQuickSystemSelectRightButton()
{
return "right";
}
std::string BasicGameListView::getQuickSystemSelectLeftButton()
{
return "left";
}
void BasicGameListView::launch(FileData* game) void BasicGameListView::launch(FileData* game)
{ {
// This triggers ViewController to launch the game.
ViewController::get()->triggerGameLaunch(game); ViewController::get()->triggerGameLaunch(game);
} }
@ -235,7 +198,7 @@ void BasicGameListView::remove(FileData* game, bool deleteFile)
if (deleteFile) { if (deleteFile) {
parent->sort(parent->getSortTypeFromString(parent->getSortTypeString()), parent->sort(parent->getSortTypeFromString(parent->getSortTypeString()),
Settings::getInstance()->getBool("FavoritesFirst")); Settings::getInstance()->getBool("FavoritesFirst"));
onFileChanged(parent, false); onFileChanged(parent, false);
} }
} }
@ -251,8 +214,8 @@ void BasicGameListView::removeMedia(FileData* game)
// If there are no media files left in the directory after the deletion, then remove // If there are no media files left in the directory after the deletion, then remove
// the directory too. Remove any empty parent directories as well. // the directory too. Remove any empty parent directories as well.
auto removeEmptyDirFunc = [] auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType,
(std::string systemMediaDir, std::string mediaType, std::string path) { std::string path) {
std::string parentPath = Utils::FileSystem::getParent(path); std::string parentPath = Utils::FileSystem::getParent(path);
while (parentPath != systemMediaDir + "/" + mediaType) { while (parentPath != systemMediaDir + "/" + mediaType) {
if (Utils::FileSystem::getDirContent(parentPath).size() == 0) { if (Utils::FileSystem::getDirContent(parentPath).size() == 0) {
@ -321,13 +284,13 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
std::vector<HelpPrompt> prompts; std::vector<HelpPrompt> prompts;
if (Settings::getInstance()->getBool("QuickSystemSelect") && if (Settings::getInstance()->getBool("QuickSystemSelect") &&
SystemData::sSystemVector.size() > 1) SystemData::sSystemVector.size() > 1)
prompts.push_back(HelpPrompt("left/right", "system")); prompts.push_back(HelpPrompt("left/right", "system"));
prompts.push_back(HelpPrompt("up/down", "choose")); prompts.push_back(HelpPrompt("up/down", "choose"));
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() &&
ViewController::get()->getState().viewing == ViewController::GAME_LIST) ViewController::get()->getState().viewing == ViewController::GAME_LIST)
prompts.push_back(HelpPrompt("a", "enter")); prompts.push_back(HelpPrompt("a", "enter"));
else else
prompts.push_back(HelpPrompt("a", "launch")); prompts.push_back(HelpPrompt("a", "launch"));
@ -341,24 +304,24 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
prompts.push_back(HelpPrompt("thumbstickclick", "random")); prompts.push_back(HelpPrompt("thumbstickclick", "random"));
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && if (mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
!CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() && !CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() &&
ViewController::get()->getState().viewing == ViewController::GAME_LIST && ViewController::get()->getState().viewing == ViewController::GAME_LIST &&
ViewController::get()->getState().viewstyle != ViewController::BASIC) { ViewController::get()->getState().viewstyle != ViewController::BASIC) {
prompts.push_back(HelpPrompt("y", "jump to game")); prompts.push_back(HelpPrompt("y", "jump to game"));
} }
else if (mRoot->getSystem()->isGameSystem() && else if (mRoot->getSystem()->isGameSystem() &&
(mRoot->getSystem()->getThemeFolder() != "custom-collections" || (mRoot->getSystem()->getThemeFolder() != "custom-collections" ||
!mCursorStack.empty()) && !mCursorStack.empty()) &&
!UIModeController::getInstance()->isUIModeKid() && !UIModeController::getInstance()->isUIModeKid() &&
!UIModeController::getInstance()->isUIModeKiosk() && !UIModeController::getInstance()->isUIModeKiosk() &&
(Settings::getInstance()->getBool("FavoritesAddButton") || (Settings::getInstance()->getBool("FavoritesAddButton") ||
CollectionSystemsManager::get()->isEditing())) { CollectionSystemsManager::get()->isEditing())) {
std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));
} }
else if (mRoot->getSystem()->isGameSystem() && else if (mRoot->getSystem()->isGameSystem() &&
mRoot->getSystem()->getThemeFolder() == "custom-collections" && mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
CollectionSystemsManager::get()->isEditing()) { CollectionSystemsManager::get()->isEditing()) {
std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));
} }

View file

@ -21,30 +21,33 @@ public:
virtual void onFileChanged(FileData* file, bool reloadGameList) override; virtual void onFileChanged(FileData* file, bool reloadGameList) override;
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override; virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
virtual FileData* getCursor() override;
virtual void setCursor(FileData* cursor) override; virtual void setCursor(FileData* cursor) override;
virtual FileData* getNextEntry() override;
virtual FileData* getPreviousEntry() override; virtual FileData* getCursor() override { return mList.getSelected(); }
virtual FileData* getFirstEntry() override; virtual FileData* getNextEntry() override { return mList.getNext(); }
virtual FileData* getLastEntry() override; virtual FileData* getPreviousEntry() override { return mList.getPrevious(); }
virtual FileData* getFirstGameEntry() override; virtual FileData* getFirstEntry() override { return mList.getFirst(); }
virtual FileData* getLastEntry() override { return mList.getLast(); }
virtual FileData* getFirstGameEntry() override { return mFirstGameEntry; }
virtual std::string getName() const override { return "basic"; } virtual std::string getName() const override { return "basic"; }
virtual std::vector<HelpPrompt> getHelpPrompts() override; virtual std::vector<HelpPrompt> getHelpPrompts() override;
virtual void launch(FileData* game) override;
virtual bool isListScrolling() override { return mList.isScrolling(); }; virtual bool isListScrolling() override { return mList.isScrolling(); }
virtual void stopListScrolling() override { mList.stopScrolling(); }; virtual void stopListScrolling() override { mList.stopScrolling(); }
virtual const std::vector<std::string>& getFirstLetterIndex() override virtual const std::vector<std::string>& getFirstLetterIndex() override
{ return mFirstLetterIndex; }; {
return mFirstLetterIndex;
}
virtual void addPlaceholder(FileData* firstEntry = nullptr) override; virtual void addPlaceholder(FileData* firstEntry = nullptr) override;
virtual void launch(FileData* game) override;
protected: protected:
virtual std::string getQuickSystemSelectRightButton() override; virtual std::string getQuickSystemSelectRightButton() override { return "right"; }
virtual std::string getQuickSystemSelectLeftButton() override; virtual std::string getQuickSystemSelectLeftButton() override { return "left"; }
virtual void populateList(const std::vector<FileData*>& files, FileData* firstEntry) override; virtual void populateList(const std::vector<FileData*>& files, FileData* firstEntry) override;
virtual void remove(FileData* game, bool deleteFile) override; virtual void remove(FileData* game, bool deleteFile) override;
virtual void removeMedia(FileData* game) override; virtual void removeMedia(FileData* game) override;

View file

@ -8,45 +8,40 @@
#include "views/gamelist/DetailedGameListView.h" #include "views/gamelist/DetailedGameListView.h"
#include "animations/LambdaAnimation.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "SystemData.h" #include "SystemData.h"
#include "animations/LambdaAnimation.h"
#include "views/ViewController.h"
#define FADE_IN_START_OPACITY 0.5f #define FADE_IN_START_OPACITY 0.5f
#define FADE_IN_TIME 650 #define FADE_IN_TIME 650
DetailedGameListView::DetailedGameListView( DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
Window* window, : BasicGameListView(window, root)
FileData* root) , mDescContainer(window)
: BasicGameListView(window, root), , mDescription(window)
mDescContainer(window), , mGamelistInfo(window)
mDescription(window), , mThumbnail(window)
mGamelistInfo(window), , mMarquee(window)
, mImage(window)
mThumbnail(window), , mLblRating(window)
mMarquee(window), , mLblReleaseDate(window)
mImage(window), , mLblDeveloper(window)
, mLblPublisher(window)
mLblRating(window), , mLblGenre(window)
mLblReleaseDate(window), , mLblPlayers(window)
mLblDeveloper(window), , mLblLastPlayed(window)
mLblPublisher(window), , mLblPlayCount(window)
mLblGenre(window), , mRating(window)
mLblPlayers(window), , mReleaseDate(window)
mLblLastPlayed(window), , mDeveloper(window)
mLblPlayCount(window), , mPublisher(window)
, mGenre(window)
mRating(window), , mPlayers(window)
mReleaseDate(window), , mLastPlayed(window)
mDeveloper(window), , mPlayCount(window)
mPublisher(window), , mName(window)
mGenre(window), , mLastUpdated(nullptr)
mPlayers(window),
mLastPlayed(window),
mPlayCount(window),
mName(window),
mLastUpdated(nullptr)
{ {
const float padding = 0.01f; const float padding = 0.01f;
@ -114,8 +109,8 @@ DetailedGameListView::DetailedGameListView(
addChild(&mName); addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
mDescContainer.getPosition().y()); mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setAutoScroll(true); mDescContainer.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40); mDescContainer.setDefaultZIndex(40);
addChild(&mDescContainer); addChild(&mDescContainer);
@ -140,20 +135,20 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
using namespace ThemeFlags; using namespace ThemeFlags;
mThumbnail.applyTheme(theme, getName(), "md_thumbnail", mThumbnail.applyTheme(theme, getName(), "md_thumbnail",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mMarquee.applyTheme(theme, getName(), "md_marquee", mMarquee.applyTheme(theme, getName(), "md_marquee",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mImage.applyTheme(theme, getName(), "md_image", mImage.applyTheme(theme, getName(), "md_image",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mName.applyTheme(theme, getName(), "md_name", ALL); mName.applyTheme(theme, getName(), "md_name", ALL);
initMDLabels(); initMDLabels();
std::vector<TextComponent*> labels = getMDLabels(); std::vector<TextComponent*> labels = getMDLabels();
assert(labels.size() == 8); assert(labels.size() == 8);
std::vector<std::string> lblElements = { std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", "md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" "md_lbl_genre", "md_lbl_players",
}; "md_lbl_lastplayed", "md_lbl_playcount" };
for (unsigned int i = 0; i < labels.size(); i++) for (unsigned int i = 0; i < labels.size(); i++)
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
@ -161,19 +156,19 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
initMDValues(); initMDValues();
std::vector<GuiComponent*> values = getMDValues(); std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 8); assert(values.size() == 8);
std::vector<std::string> valElements = { std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
"md_rating", "md_releasedate", "md_developer", "md_publisher", "md_publisher", "md_genre", "md_players",
"md_genre", "md_players", "md_lastplayed", "md_playcount" "md_lastplayed", "md_playcount" };
};
for (unsigned int i = 0; i < values.size(); i++) for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
mDescContainer.applyTheme(theme, getName(), "md_description", mDescContainer.applyTheme(theme, getName(), "md_description",
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
mDescription.setSize(mDescContainer.getSize().x(), 0); mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.applyTheme(theme, getName(), "md_description", mDescription.applyTheme(
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); theme, getName(), "md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
// If there is no position defined in the theme for gamelistInfo, then hide it. // If there is no position defined in the theme for gamelistInfo, then hide it.
@ -205,7 +200,7 @@ void DetailedGameListView::initMDLabels()
} }
else { else {
// Work from the last component. // Work from the last component.
GuiComponent* lc = components[i-1]; GuiComponent* lc = components[i - 1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
} }
@ -232,13 +227,13 @@ void DetailedGameListView::initMDValues()
float bottom = 0.0f; float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2; const float colSize = (mSize.x() * 0.48f) / 2.0f;
for (unsigned int i = 0; i < labels.size(); i++) { for (unsigned int i = 0; i < labels.size(); i++) {
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f;
values[i]->setPosition(labels[i]->getPosition() + values[i]->setPosition(labels[i]->getPosition() +
Vector3f(labels[i]->getSize().x(), heightDiff, 0)); Vector3f(labels[i]->getSize().x(), heightDiff, 0));
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40); values[i]->setDefaultZIndex(40.0f);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
@ -247,8 +242,8 @@ void DetailedGameListView::initMDValues()
} }
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.setSize(mDescContainer.getSize().x(),
mDescContainer.getPosition().y()); mSize.y() - mDescContainer.getPosition().y());
} }
void DetailedGameListView::updateInfoPanel() void DetailedGameListView::updateInfoPanel()
@ -267,7 +262,7 @@ void DetailedGameListView::updateInfoPanel()
if (file) { if (file) {
// Always hide the metadata fields if browsing grouped custom collections. // Always hide the metadata fields if browsing grouped custom collections.
if (file->getSystem()->isCustomCollection() && if (file->getSystem()->isCustomCollection() &&
file->getPath() == file->getSystem()->getName()) file->getPath() == file->getSystem()->getName())
hideMetaDataFields = true; hideMetaDataFields = true;
else else
hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); hideMetaDataFields = (file->metadata.get("hidemetadata") == "true");
@ -283,9 +278,9 @@ void DetailedGameListView::updateInfoPanel()
// or if we're in the grouped custom collection view. // or if we're in the grouped custom collection view.
if (mList.isScrolling()) if (mList.isScrolling())
if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") ||
(mLastUpdated->getSystem()->isCustomCollection() && (mLastUpdated->getSystem()->isCustomCollection() &&
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
hideMetaDataFields = true; hideMetaDataFields = true;
if (hideMetaDataFields) { if (hideMetaDataFields) {
mLblRating.setVisible(false); mLblRating.setVisible(false);
@ -333,9 +328,9 @@ void DetailedGameListView::updateInfoPanel()
// which will generate a description of three random games and return a pointer to // which will generate a description of three random games and return a pointer to
// the first of these so that we can display its game media. // the first of these so that we can display its game media.
if (file->getSystem()->isCustomCollection() && if (file->getSystem()->isCustomCollection() &&
file->getPath() == file->getSystem()->getName()) { file->getPath() == file->getSystem()->getName()) {
mRandomGame = CollectionSystemsManager::get()-> mRandomGame =
updateCollectionFolderMetadata(file->getSystem()); CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem());
if (mRandomGame) { if (mRandomGame) {
mThumbnail.setImage(mRandomGame->getThumbnailPath()); mThumbnail.setImage(mRandomGame->getThumbnailPath());
mMarquee.setImage(mRandomGame->getMarqueePath()); mMarquee.setImage(mRandomGame->getMarqueePath());
@ -366,21 +361,21 @@ void DetailedGameListView::updateInfoPanel()
if (mIsFiltered) { if (mIsFiltered) {
if (mFilteredGameCountAll == mFilteredGameCount) if (mFilteredGameCountAll == mFilteredGameCount)
gamelistInfoString += ViewController::FILTER_CHAR + " " + gamelistInfoString += ViewController::FILTER_CHAR + " " +
std::to_string(mFilteredGameCount) + " / " + std::to_string(mFilteredGameCount) + " / " +
std::to_string(mGameCount); std::to_string(mGameCount);
else else
gamelistInfoString += ViewController::FILTER_CHAR + " " + gamelistInfoString += ViewController::FILTER_CHAR + " " +
std::to_string(mFilteredGameCount) + " + " + std::to_string(mFilteredGameCount) + " + " +
std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " + std::to_string(mFilteredGameCountAll - mFilteredGameCount) +
std::to_string(mGameCount); " / " + std::to_string(mGameCount);
} }
else { else {
gamelistInfoString += ViewController::CONTROLLER_CHAR + " " + gamelistInfoString +=
std::to_string(mGameCount); ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
if (!(file->getSystem()->isCollection() && if (!(file->getSystem()->isCollection() &&
file->getSystem()->getFullName() == "favorites")) file->getSystem()->getFullName() == "favorites"))
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
+ std::to_string(mFavoritesGameCount); std::to_string(mFavoritesGameCount);
} }
if (mIsFolder && infoAlign != ALIGN_RIGHT) if (mIsFolder && infoAlign != ALIGN_RIGHT)
@ -390,9 +385,9 @@ void DetailedGameListView::updateInfoPanel()
// Fade in the game image. // Fade in the game image.
auto func = [this](float t) { auto func = [this](float t) {
mImage.setOpacity(static_cast<unsigned char>(Math::lerp( mImage.setOpacity(static_cast<unsigned char>(
static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255)); Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
}; };
mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
mDescription.setText(file->metadata.get("desc")); mDescription.setText(file->metadata.get("desc"));

View file

@ -8,48 +8,42 @@
#include "views/gamelist/GridGameListView.h" #include "views/gamelist/GridGameListView.h"
#include "animations/LambdaAnimation.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "Settings.h" #include "Settings.h"
#include "Sound.h" #include "Sound.h"
#include "SystemData.h" #include "SystemData.h"
#include "animations/LambdaAnimation.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#define FADE_IN_START_OPACITY 0.5f #define FADE_IN_START_OPACITY 0.5f
#define FADE_IN_TIME 650 #define FADE_IN_TIME 650
GridGameListView::GridGameListView( GridGameListView::GridGameListView(Window* window, FileData* root)
Window* window, : ISimpleGameListView(window, root)
FileData* root) , mGrid(window)
: ISimpleGameListView(window, root), , mMarquee(window)
, mImage(window)
mGrid(window), , mDescContainer(window)
mMarquee(window), , mDescription(window)
mImage(window), , mGamelistInfo(window)
, mLblRating(window)
mDescContainer(window), , mLblReleaseDate(window)
mDescription(window), , mLblDeveloper(window)
mGamelistInfo(window), , mLblPublisher(window)
, mLblGenre(window)
mLblRating(window), , mLblPlayers(window)
mLblReleaseDate(window), , mLblLastPlayed(window)
mLblDeveloper(window), , mLblPlayCount(window)
mLblPublisher(window), , mRating(window)
mLblGenre(window), , mReleaseDate(window)
mLblPlayers(window), , mDeveloper(window)
mLblLastPlayed(window), , mPublisher(window)
mLblPlayCount(window), , mGenre(window)
, mPlayers(window)
mRating(window), , mLastPlayed(window)
mReleaseDate(window), , mPlayCount(window)
mDeveloper(window), , mName(window)
mPublisher(window),
mGenre(window),
mPlayers(window),
mLastPlayed(window),
mPlayCount(window),
mName(window)
{ {
const float padding = 0.01f; const float padding = 0.01f;
@ -95,8 +89,8 @@ GridGameListView::GridGameListView(
addChild(&mName); addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
mDescContainer.getPosition().y()); mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setAutoScroll(true); mDescContainer.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40); mDescContainer.setDefaultZIndex(40);
addChild(&mDescContainer); addChild(&mDescContainer);
@ -107,7 +101,7 @@ GridGameListView::GridGameListView(
mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setOrigin(0.5f, 0.5f);
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.18f); mMarquee.setMaxSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35); mMarquee.setDefaultZIndex(35);
mMarquee.setVisible(false); mMarquee.setVisible(false);
addChild(&mMarquee); addChild(&mMarquee);
@ -130,10 +124,6 @@ GridGameListView::GridGameListView(
updateInfoPanel(); updateInfoPanel();
} }
GridGameListView::~GridGameListView()
{
}
void GridGameListView::onFileChanged(FileData* file, bool reloadGameList) void GridGameListView::onFileChanged(FileData* file, bool reloadGameList)
{ {
if (reloadGameList) { if (reloadGameList) {
@ -145,11 +135,6 @@ void GridGameListView::onFileChanged(FileData* file, bool reloadGameList)
ISimpleGameListView::onFileChanged(file, reloadGameList); ISimpleGameListView::onFileChanged(file, reloadGameList);
} }
FileData* GridGameListView::getCursor()
{
return mGrid.getSelected();
}
void GridGameListView::setCursor(FileData* cursor) void GridGameListView::setCursor(FileData* cursor)
{ {
if (!mGrid.setCursor(cursor) && (!cursor->isPlaceHolder())) { if (!mGrid.setCursor(cursor) && (!cursor->isPlaceHolder())) {
@ -176,47 +161,11 @@ void GridGameListView::setCursor(FileData* cursor)
} }
} }
FileData* GridGameListView::getNextEntry()
{
return mGrid.getNext();;
}
FileData* GridGameListView::getPreviousEntry()
{
return mGrid.getPrevious();
}
FileData* GridGameListView::getFirstEntry()
{
return mGrid.getFirst();;
}
FileData* GridGameListView::getLastEntry()
{
return mGrid.getLast();
}
FileData* GridGameListView::getFirstGameEntry()
{
return firstGameEntry;
}
std::string GridGameListView::getQuickSystemSelectRightButton()
{
return "rightshoulder";
}
std::string GridGameListView::getQuickSystemSelectLeftButton()
{
return "leftshoulder";
}
bool GridGameListView::input(InputConfig* config, Input input) bool GridGameListView::input(InputConfig* config, Input input)
{ {
if (input.value == 0 && (config->isMappedLike("left", input) || if (input.value == 0 &&
config->isMappedLike("right", input) || (config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
(config->isMappedLike("up", input)) || (config->isMappedLike("up", input)) || (config->isMappedLike("down", input))))
(config->isMappedLike("down", input)) ))
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
if (input.value != 0 && config->isMappedLike("righttrigger", input)) { if (input.value != 0 && config->isMappedLike("righttrigger", input)) {
@ -288,17 +237,17 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
mGrid.applyTheme(theme, getName(), "gamegrid", ALL); mGrid.applyTheme(theme, getName(), "gamegrid", ALL);
mName.applyTheme(theme, getName(), "md_name", ALL); mName.applyTheme(theme, getName(), "md_name", ALL);
mMarquee.applyTheme(theme, getName(), "md_marquee", mMarquee.applyTheme(theme, getName(), "md_marquee",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mImage.applyTheme(theme, getName(), "md_image", mImage.applyTheme(theme, getName(), "md_image",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
initMDLabels(); initMDLabels();
std::vector<TextComponent*> labels = getMDLabels(); std::vector<TextComponent*> labels = getMDLabels();
assert(labels.size() == 8); assert(labels.size() == 8);
std::vector<std::string> lblElements = { std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", "md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" "md_lbl_genre", "md_lbl_players",
}; "md_lbl_lastplayed", "md_lbl_playcount" };
for (unsigned int i = 0; i < labels.size(); i++) for (unsigned int i = 0; i < labels.size(); i++)
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
@ -306,19 +255,19 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDValues(); initMDValues();
std::vector<GuiComponent*> values = getMDValues(); std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 8); assert(values.size() == 8);
std::vector<std::string> valElements = { std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
"md_rating", "md_releasedate", "md_developer", "md_publisher", "md_publisher", "md_genre", "md_players",
"md_genre", "md_players", "md_lastplayed", "md_playcount" "md_lastplayed", "md_playcount" };
};
for (unsigned int i = 0; i < values.size(); i++) for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
mDescContainer.applyTheme(theme, getName(), "md_description", mDescContainer.applyTheme(theme, getName(), "md_description",
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
mDescription.setSize(mDescContainer.getSize().x(), 0); mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.applyTheme(theme, getName(), "md_description", mDescription.applyTheme(
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); theme, getName(), "md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
// Repopulate list in case a new theme is displaying a different image. // Repopulate list in case a new theme is displaying a different image.
// Preserve selection. // Preserve selection.
@ -336,6 +285,12 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
sortChildren(); sortChildren();
} }
void GridGameListView::onShow()
{
GuiComponent::onShow();
updateInfoPanel();
}
void GridGameListView::initMDLabels() void GridGameListView::initMDLabels()
{ {
std::vector<TextComponent*> components = getMDLabels(); std::vector<TextComponent*> components = getMDLabels();
@ -356,7 +311,7 @@ void GridGameListView::initMDLabels()
} }
else { else {
// Work from the last component. // Work from the last component.
GuiComponent* lc = components[i-1]; GuiComponent* lc = components[i - 1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
} }
@ -383,11 +338,11 @@ void GridGameListView::initMDValues()
float bottom = 0.0f; float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2; const float colSize = (mSize.x() * 0.48f) / 2.0f;
for (unsigned int i = 0; i < labels.size(); i++) { for (unsigned int i = 0; i < labels.size(); i++) {
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f;
values[i]->setPosition(labels[i]->getPosition() + values[i]->setPosition(labels[i]->getPosition() +
Vector3f(labels[i]->getSize().x(), heightDiff, 0)); Vector3f(labels[i]->getSize().x(), heightDiff, 0));
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40); values[i]->setDefaultZIndex(40);
@ -397,8 +352,8 @@ void GridGameListView::initMDValues()
} }
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.setSize(mDescContainer.getSize().x(),
mDescContainer.getPosition().y()); mSize.y() - mDescContainer.getPosition().y());
} }
void GridGameListView::updateInfoPanel() void GridGameListView::updateInfoPanel()
@ -465,21 +420,22 @@ void GridGameListView::updateInfoPanel()
if (mIsFiltered) { if (mIsFiltered) {
if (mFilteredGameCountAll == mFilteredGameCount) if (mFilteredGameCountAll == mFilteredGameCount)
gamelistInfoString += ViewController::FILTER_CHAR + " " gamelistInfoString += ViewController::FILTER_CHAR + " " +
+ std::to_string(mFilteredGameCount) + " / " + std::to_string(mGameCount); std::to_string(mFilteredGameCount) + " / " +
std::to_string(mGameCount);
else else
gamelistInfoString += ViewController::FILTER_CHAR + " " + gamelistInfoString += ViewController::FILTER_CHAR + " " +
std::to_string(mFilteredGameCount) + " + " + std::to_string(mFilteredGameCount) + " + " +
std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " + std::to_string(mFilteredGameCountAll - mFilteredGameCount) +
std::to_string(mGameCount); " / " + std::to_string(mGameCount);
} }
else { else {
gamelistInfoString += ViewController::CONTROLLER_CHAR + " " + gamelistInfoString +=
std::to_string(mGameCount); ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
if (!(file->getSystem()->isCollection() && if (!(file->getSystem()->isCollection() &&
file->getSystem()->getFullName() == "favorites")) file->getSystem()->getFullName() == "favorites"))
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
std::to_string(mFavoritesGameCount); std::to_string(mFavoritesGameCount);
} }
if (mIsFolder && infoAlign != ALIGN_RIGHT) if (mIsFolder && infoAlign != ALIGN_RIGHT)
@ -489,9 +445,9 @@ void GridGameListView::updateInfoPanel()
// Fade in the game image. // Fade in the game image.
auto func = [this](float t) { auto func = [this](float t) {
mImage.setOpacity(static_cast<unsigned char>(Math::lerp( mImage.setOpacity(static_cast<unsigned char>(
static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255)); Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
}; };
mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
mDescription.setText(file->metadata.get("desc")); mDescription.setText(file->metadata.get("desc"));
@ -535,10 +491,10 @@ void GridGameListView::updateInfoPanel()
// An animation is playing, then animate if reverse != fadingOut. // An animation is playing, then animate if reverse != fadingOut.
// An animation is not playing, then animate if opacity != our target opacity. // An animation is not playing, then animate if opacity != our target opacity.
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
auto func = [comp](float t) { auto func = [comp](float t) {
// TEMPORARY - This does not seem to work, needs to be reviewed later. // TEMPORARY - This does not seem to work, needs to be reviewed later.
// comp->setOpacity(static_cast<unsigned char>(Math::lerp(0.0f, 1.0f, t) * 255)); // comp->setOpacity(static_cast<unsigned char>(Math::lerp(0.0f, 1.0f, t) * 255));
}; };
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
} }
@ -560,6 +516,7 @@ void GridGameListView::addPlaceholder(FileData* firstEntry)
void GridGameListView::launch(FileData* game) void GridGameListView::launch(FileData* game)
{ {
// This triggers ViewController to launch the game.
ViewController::get()->triggerGameLaunch(game); ViewController::get()->triggerGameLaunch(game);
} }
@ -605,8 +562,8 @@ void GridGameListView::removeMedia(FileData* game)
// If there are no media files left in the directory after the deletion, then remove // If there are no media files left in the directory after the deletion, then remove
// the directory too. Remove any empty parent directories as well. // the directory too. Remove any empty parent directories as well.
auto removeEmptyDirFunc = [] auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType,
(std::string systemMediaDir, std::string mediaType, std::string path) { std::string path) {
std::string parentPath = Utils::FileSystem::getParent(path); std::string parentPath = Utils::FileSystem::getParent(path);
while (parentPath != systemMediaDir + "/" + mediaType) { while (parentPath != systemMediaDir + "/" + mediaType) {
if (Utils::FileSystem::getDirContent(parentPath).size() == 0) { if (Utils::FileSystem::getDirContent(parentPath).size() == 0) {
@ -707,7 +664,7 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
prompts.push_back(HelpPrompt("up/down/left/right", "choose")); prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() &&
ViewController::get()->getState().viewing == ViewController::GAME_LIST) ViewController::get()->getState().viewing == ViewController::GAME_LIST)
prompts.push_back(HelpPrompt("a", "enter")); prompts.push_back(HelpPrompt("a", "enter"));
else else
prompts.push_back(HelpPrompt("a", "launch")); prompts.push_back(HelpPrompt("a", "launch"));
@ -715,32 +672,30 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
prompts.push_back(HelpPrompt("b", "back")); prompts.push_back(HelpPrompt("b", "back"));
if (mRoot->getSystem()->isGameSystem() && if (mRoot->getSystem()->isGameSystem() &&
mRoot->getSystem()->getThemeFolder() != "custom-collections") mRoot->getSystem()->getThemeFolder() != "custom-collections")
prompts.push_back(HelpPrompt("x", "view media")); prompts.push_back(HelpPrompt("x", "view media"));
if (mRoot->getSystem()->isGameSystem() && !mCursorStack.empty() && if (mRoot->getSystem()->isGameSystem() && !mCursorStack.empty() &&
mRoot->getSystem()->getThemeFolder() == "custom-collections") mRoot->getSystem()->getThemeFolder() == "custom-collections")
prompts.push_back(HelpPrompt("x", "view media")); prompts.push_back(HelpPrompt("x", "view media"));
if (!UIModeController::getInstance()->isUIModeKid()) if (!UIModeController::getInstance()->isUIModeKid())
prompts.push_back(HelpPrompt("back", "options")); prompts.push_back(HelpPrompt("back", "options"));
if (mRoot->getSystem()->isGameSystem() && if (mRoot->getSystem()->isGameSystem() && Settings::getInstance()->getBool("RandomAddButton"))
Settings::getInstance()->getBool("RandomAddButton"))
prompts.push_back(HelpPrompt("thumbstickclick", "random")); prompts.push_back(HelpPrompt("thumbstickclick", "random"));
if (mRoot->getSystem()->isGameSystem() && if (mRoot->getSystem()->isGameSystem() &&
(mRoot->getSystem()->getThemeFolder() != "custom-collections" || (mRoot->getSystem()->getThemeFolder() != "custom-collections" || !mCursorStack.empty()) &&
!mCursorStack.empty()) && !UIModeController::getInstance()->isUIModeKid() &&
!UIModeController::getInstance()->isUIModeKid() && !UIModeController::getInstance()->isUIModeKiosk() &&
!UIModeController::getInstance()->isUIModeKiosk() && (Settings::getInstance()->getBool("FavoritesAddButton") ||
(Settings::getInstance()->getBool("FavoritesAddButton") || CollectionSystemsManager::get()->isEditing())) {
CollectionSystemsManager::get()->isEditing())) {
std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));
} }
else if (mRoot->getSystem()->isGameSystem() && else if (mRoot->getSystem()->isGameSystem() &&
mRoot->getSystem()->getThemeFolder() == "custom-collections" && mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
CollectionSystemsManager::get()->isEditing()) { CollectionSystemsManager::get()->isEditing()) {
std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));
} }
@ -749,11 +704,6 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
void GridGameListView::update(int deltaTime) void GridGameListView::update(int deltaTime)
{ {
// Update.
ISimpleGameListView::update(deltaTime); ISimpleGameListView::update(deltaTime);
} }
void GridGameListView::onShow()
{
GuiComponent::onShow();
updateInfoPanel();
}

View file

@ -20,48 +20,50 @@ class GridGameListView : public ISimpleGameListView
{ {
public: public:
GridGameListView(Window* window, FileData* root); GridGameListView(Window* window, FileData* root);
virtual ~GridGameListView(); virtual ~GridGameListView() {}
// Called when a FileData* is added, has its metadata changed, or is removed. // Called when a FileData* is added, has its metadata changed, or is removed.
virtual void onFileChanged(FileData* file, bool reloadGameList) override; virtual void onFileChanged(FileData* file, bool reloadGameList) override;
virtual void onShow() override; virtual void onShow() override;
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override; virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
virtual FileData* getCursor() override;
virtual void setCursor(FileData* cursor) override; virtual void setCursor(FileData* cursor) override;
virtual FileData* getNextEntry() override;
virtual FileData* getPreviousEntry() override;
virtual FileData* getFirstEntry() override;
virtual FileData* getLastEntry() override;
virtual FileData* getFirstGameEntry() override;
virtual bool input(InputConfig* config, Input input) override; virtual FileData* getCursor() override { return mGrid.getSelected(); }
virtual FileData* getNextEntry() override { return mGrid.getNext(); }
virtual FileData* getPreviousEntry() override { return mGrid.getPrevious(); }
virtual FileData* getFirstEntry() override { return mGrid.getFirst(); }
virtual FileData* getLastEntry() override { return mGrid.getLast(); }
virtual FileData* getFirstGameEntry() override { return firstGameEntry; }
virtual std::string getName() const override { return "grid"; } virtual std::string getName() const override { return "grid"; }
virtual bool input(InputConfig* config, Input input) override;
virtual std::vector<HelpPrompt> getHelpPrompts() override; virtual std::vector<HelpPrompt> getHelpPrompts() override;
virtual void launch(FileData* game) override; virtual void launch(FileData* game) override;
virtual bool isListScrolling() override { return mGrid.isScrolling(); }; virtual bool isListScrolling() override { return mGrid.isScrolling(); }
virtual void stopListScrolling() override virtual void stopListScrolling() override
{ {
mGrid.stopAllAnimations(); mGrid.stopAllAnimations();
mGrid.stopScrolling(); mGrid.stopScrolling();
}; }
virtual const std::vector<std::string>& getFirstLetterIndex() override virtual const std::vector<std::string>& getFirstLetterIndex() override
{ return mFirstLetterIndex; }; {
return mFirstLetterIndex;
}
virtual void addPlaceholder(FileData* firstEntry = nullptr) override; virtual void addPlaceholder(FileData* firstEntry = nullptr) override;
protected: protected:
virtual void update(int deltaTime) override; virtual std::string getQuickSystemSelectRightButton() override { return "rightshoulder"; }
virtual std::string getQuickSystemSelectRightButton() override; virtual std::string getQuickSystemSelectLeftButton() override { return "leftshoulder"; }
virtual std::string getQuickSystemSelectLeftButton() override;
virtual void populateList(const std::vector<FileData*>& files, FileData* firstEntry) override; virtual void populateList(const std::vector<FileData*>& files, FileData* firstEntry) override;
virtual void remove(FileData* game, bool deleteFile) override; virtual void remove(FileData* game, bool deleteFile) override;
virtual void removeMedia(FileData* game) override; virtual void removeMedia(FileData* game) override;
virtual void update(int deltaTime) override;
ImageGridComponent<FileData*> mGrid; ImageGridComponent<FileData*> mGrid;
// Points to the first game in the list, i.e. the first entry which is of the type 'GAME'. // Points to the first game in the list, i.e. the first entry which is of the type 'GAME'.

View file

@ -8,18 +8,32 @@
#include "views/gamelist/IGameListView.h" #include "views/gamelist/IGameListView.h"
#include "guis/GuiGamelistOptions.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "Sound.h" #include "Sound.h"
#include "Window.h" #include "Window.h"
#include "guis/GuiGamelistOptions.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
IGameListView::IGameListView(Window* window, FileData* root)
: GuiComponent(window)
, mRoot(root)
{
setSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
}
void IGameListView::setTheme(const std::shared_ptr<ThemeData>& theme)
{
mTheme = theme;
onThemeChanged(theme);
}
bool IGameListView::input(InputConfig* config, Input input) bool IGameListView::input(InputConfig* config, Input input)
{ {
// Select button opens GuiGamelistOptions. // Select button opens GuiGamelistOptions.
if (!UIModeController::getInstance()->isUIModeKid() && if (!UIModeController::getInstance()->isUIModeKid() && // Line break.
config->isMappedTo("back", input) && input.value) { config->isMappedTo("back", input) && input.value) {
ViewController::get()->cancelViewTransitions(); ViewController::get()->cancelViewTransitions();
stopListScrolling(); stopListScrolling();
mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem())); mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem()));
@ -28,9 +42,9 @@ bool IGameListView::input(InputConfig* config, Input input)
// Ctrl-R reloads the view when debugging. // Ctrl-R reloads the view when debugging.
else if (Settings::getInstance()->getBool("Debug") && else if (Settings::getInstance()->getBool("Debug") &&
config->getDeviceId() == DEVICE_KEYBOARD && config->getDeviceId() == DEVICE_KEYBOARD &&
(SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && input.id == SDLK_r &&
input.id == SDLK_r && input.value != 0) { input.value != 0) {
LOG(LogDebug) << "IGameListView::input(): Reloading view"; LOG(LogDebug) << "IGameListView::input(): Reloading view";
ViewController::get()->reloadGameListView(this, true); ViewController::get()->reloadGameListView(this, true);
return true; return true;
@ -39,12 +53,6 @@ bool IGameListView::input(InputConfig* config, Input input)
return GuiComponent::input(config, input); return GuiComponent::input(config, input);
} }
void IGameListView::setTheme(const std::shared_ptr<ThemeData>& theme)
{
mTheme = theme;
onThemeChanged(theme);
}
HelpStyle IGameListView::getHelpStyle() HelpStyle IGameListView::getHelpStyle()
{ {
HelpStyle style; HelpStyle style;
@ -60,9 +68,9 @@ void IGameListView::render(const Transform4x4f& parentTrans)
float scaleY = trans.r1().y(); float scaleY = trans.r1().y();
Vector2i pos(static_cast<int>(std::round(trans.translation()[0])), Vector2i pos(static_cast<int>(std::round(trans.translation()[0])),
static_cast<int>(std::round(trans.translation()[1]))); static_cast<int>(std::round(trans.translation()[1])));
Vector2i size(static_cast<int>(std::round(mSize.x() * scaleX)), Vector2i size(static_cast<int>(std::round(mSize.x() * scaleX)),
static_cast<int>(std::round(mSize.y() * scaleY))); static_cast<int>(std::round(mSize.y() * scaleY)));
Renderer::pushClipRect(pos, size); Renderer::pushClipRect(pos, size);
renderChildren(trans); renderChildren(trans);

View file

@ -9,9 +9,9 @@
#ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H
#define ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H
#include "renderers/Renderer.h"
#include "FileData.h" #include "FileData.h"
#include "GuiComponent.h" #include "GuiComponent.h"
#include "renderers/Renderer.h"
class ThemeData; class ThemeData;
class Window; class Window;
@ -20,12 +20,7 @@ class Window;
class IGameListView : public GuiComponent class IGameListView : public GuiComponent
{ {
public: public:
IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root) IGameListView(Window* window, FileData* root);
{
setSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
}
virtual ~IGameListView() {} virtual ~IGameListView() {}
// Called when a FileData* is added, has its metadata changed, or is removed. // Called when a FileData* is added, has its metadata changed, or is removed.
@ -35,7 +30,7 @@ public:
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) = 0; virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) = 0;
void setTheme(const std::shared_ptr<ThemeData>& theme); void setTheme(const std::shared_ptr<ThemeData>& theme);
inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; } const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
virtual FileData* getCursor() = 0; virtual FileData* getCursor() = 0;
virtual void setCursor(FileData*) = 0; virtual void setCursor(FileData*) = 0;

View file

@ -8,26 +8,24 @@
#include "views/gamelist/ISimpleGameListView.h" #include "views/gamelist/ISimpleGameListView.h"
#include "guis/GuiInfoPopup.h"
#include "utils/StringUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "Settings.h" #include "Settings.h"
#include "Sound.h" #include "Sound.h"
#include "SystemData.h" #include "SystemData.h"
#include "guis/GuiInfoPopup.h"
#include "utils/StringUtil.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "Log.h" #include "Log.h"
ISimpleGameListView::ISimpleGameListView( ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root)
Window* window, : IGameListView(window, root)
FileData* root) , mHeaderText(window)
: IGameListView(window, root), , mHeaderImage(window)
mHeaderText(window), , mBackground(window)
mHeaderImage(window), , mRandomGame(nullptr)
mBackground(window),
mRandomGame(nullptr)
{ {
mHeaderText.setText("Logo Text"); mHeaderText.setText("Logo Text");
mHeaderText.setSize(mSize.x(), 0); mHeaderText.setSize(mSize.x(), 0);
@ -124,10 +122,10 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
std::vector<FileData*> listEntries = cursor->getChildrenListToDisplay(); std::vector<FileData*> listEntries = cursor->getChildrenListToDisplay();
// Check if there is an entry in the cursor stack history matching any entry // Check if there is an entry in the cursor stack history matching any entry
// in the currect folder. If so, select that entry. // in the currect folder. If so, select that entry.
for (auto it = mCursorStackHistory.begin(); for (auto it = mCursorStackHistory.begin(); // Line break.
it != mCursorStackHistory.end(); it++) { it != mCursorStackHistory.end(); it++) {
if (std::find(listEntries.begin(), listEntries.end(), *it) != if (std::find(listEntries.begin(), listEntries.end(), *it) !=
listEntries.end()) { listEntries.end()) {
newCursor = *it; newCursor = *it;
mCursorStackHistory.erase(it); mCursorStackHistory.erase(it);
break; break;
@ -155,7 +153,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
mCursorStackHistory.push_back(getCursor()); mCursorStackHistory.push_back(getCursor());
NavigationSounds::getInstance()->playThemeNavigationSound(BACKSOUND); NavigationSounds::getInstance()->playThemeNavigationSound(BACKSOUND);
populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay(), populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay(),
mCursorStack.top()->getParent()); mCursorStack.top()->getParent());
setCursor(mCursorStack.top()); setCursor(mCursorStack.top());
if (mCursorStack.size() > 0) if (mCursorStack.size() > 0)
mCursorStack.pop(); mCursorStack.pop();
@ -169,9 +167,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
stopListScrolling(); stopListScrolling();
SystemData* systemToView = getCursor()->getSystem(); SystemData* systemToView = getCursor()->getSystem();
if (systemToView->isCustomCollection() && if (systemToView->isCustomCollection() &&
systemToView->getRootFolder()->getParent()) systemToView->getRootFolder()->getParent())
ViewController::get()->goToSystemView( ViewController::get()->goToSystemView(
systemToView->getRootFolder()->getParent()->getSystem(), true); systemToView->getRootFolder()->getParent()->getSystem(), true);
else else
ViewController::get()->goToSystemView(systemToView, true); ViewController::get()->goToSystemView(systemToView, true);
} }
@ -184,9 +182,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
return true; return true;
} }
else if (config->isMappedTo("x", input) && else if (config->isMappedTo("x", input) &&
mRoot->getSystem()->getThemeFolder() == "custom-collections" && mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
mCursorStack.empty() && ViewController::get()->getState().viewing == mCursorStack.empty() &&
ViewController::GAME_LIST) { ViewController::get()->getState().viewing == ViewController::GAME_LIST) {
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
// Jump to the randomly selected game. // Jump to the randomly selected game.
if (mRandomGame) { if (mRandomGame) {
@ -206,7 +204,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) { else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) {
if (Settings::getInstance()->getBool("QuickSystemSelect") && if (Settings::getInstance()->getBool("QuickSystemSelect") &&
SystemData::sSystemVector.size() > 1) { SystemData::sSystemVector.size() > 1) {
onPauseVideo(); onPauseVideo();
onFocusLost(); onFocusLost();
stopListScrolling(); stopListScrolling();
@ -216,7 +214,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) { else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) {
if (Settings::getInstance()->getBool("QuickSystemSelect") && if (Settings::getInstance()->getBool("QuickSystemSelect") &&
SystemData::sSystemVector.size() > 1) { SystemData::sSystemVector.size() > 1) {
onPauseVideo(); onPauseVideo();
onFocusLost(); onFocusLost();
stopListScrolling(); stopListScrolling();
@ -225,8 +223,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
} }
else if (Settings::getInstance()->getBool("RandomAddButton") && else if (Settings::getInstance()->getBool("RandomAddButton") &&
(config->isMappedTo("leftthumbstickclick", input) || (config->isMappedTo("leftthumbstickclick", input) ||
config->isMappedTo("rightthumbstickclick", input))) { config->isMappedTo("rightthumbstickclick", input))) {
if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER) { if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER) {
stopListScrolling(); stopListScrolling();
// Jump to a random game. // Jump to a random game.
@ -238,21 +236,19 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
} }
else if (config->isMappedTo("y", input) && else if (config->isMappedTo("y", input) &&
mRoot->getSystem()->getThemeFolder() == "custom-collections" && mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
!CollectionSystemsManager::get()->isEditing() && !CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() &&
mCursorStack.empty() && ViewController::get()->getState().viewing == ViewController::get()->getState().viewing == ViewController::GAME_LIST) {
ViewController::GAME_LIST) {
// Jump to the randomly selected game. // Jump to the randomly selected game.
if (mRandomGame) { if (mRandomGame) {
NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND); NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND);
// If there is already an mCursorStackHistory entry for the collection, then // If there is already an mCursorStackHistory entry for the collection, then
// remove it so we don't get multiple entries. // remove it so we don't get multiple entries.
std::vector<FileData*> listEntries = std::vector<FileData*> listEntries =
mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay(); mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay();
for (auto it = mCursorStackHistory.begin(); for (auto it = mCursorStackHistory.begin(); it != mCursorStackHistory.end(); it++) {
it != mCursorStackHistory.end(); it++) {
if (std::find(listEntries.begin(), listEntries.end(), *it) != if (std::find(listEntries.begin(), listEntries.end(), *it) !=
listEntries.end()) { listEntries.end()) {
mCursorStackHistory.erase(it); mCursorStackHistory.erase(it);
break; break;
} }
@ -265,34 +261,33 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
} }
else if (config->isMappedTo("y", input) && else if (config->isMappedTo("y", input) &&
!Settings::getInstance()->getBool("FavoritesAddButton") && !Settings::getInstance()->getBool("FavoritesAddButton") &&
!CollectionSystemsManager::get()->isEditing()) { !CollectionSystemsManager::get()->isEditing()) {
return true; return true;
} }
else if (config->isMappedTo("y", input) && else if (config->isMappedTo("y", input) &&
!UIModeController::getInstance()->isUIModeKid() && !UIModeController::getInstance()->isUIModeKid() &&
!UIModeController::getInstance()->isUIModeKiosk()) { !UIModeController::getInstance()->isUIModeKiosk()) {
// Notify the user if attempting to add a custom collection to a custom collection. // Notify the user if attempting to add a custom collection to a custom collection.
if (CollectionSystemsManager::get()->isEditing() && if (CollectionSystemsManager::get()->isEditing() &&
mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
getCursor()->getParent()->getPath() == "collections") { getCursor()->getParent()->getPath() == "collections") {
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
GuiInfoPopup* s; GuiInfoPopup* s;
s = new GuiInfoPopup(mWindow, s = new GuiInfoPopup(mWindow, "CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS",
"CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS", 4000); 4000);
mWindow->setInfoPopup(s); mWindow->setInfoPopup(s);
} }
// Notify the user if attempting to add a placeholder to a custom collection. // Notify the user if attempting to add a placeholder to a custom collection.
if (CollectionSystemsManager::get()->isEditing() && if (CollectionSystemsManager::get()->isEditing() &&
mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) { mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) {
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
GuiInfoPopup* s; GuiInfoPopup* s;
s = new GuiInfoPopup(mWindow, s = new GuiInfoPopup(mWindow, "CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000);
"CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000);
mWindow->setInfoPopup(s); mWindow->setInfoPopup(s);
} }
else if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && else if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
getCursor()->getParent()->getPath() != "collections") { getCursor()->getParent()->getPath() != "collections") {
if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER) if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER)
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
// When marking or unmarking a game as favorite, don't jump to the new position // When marking or unmarking a game as favorite, don't jump to the new position
@ -312,13 +307,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
foldersOnTop = !getCursor()->getParent()->getOnlyFoldersFlag(); foldersOnTop = !getCursor()->getParent()->getOnlyFoldersFlag();
if (mRoot->getSystem()->isCustomCollection() || if (mRoot->getSystem()->isCustomCollection() ||
mRoot->getSystem()->getThemeFolder() == "custom-collections") mRoot->getSystem()->getThemeFolder() == "custom-collections")
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
else else
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
if (favoritesSorting && static_cast<std::string>( if (favoritesSorting &&
mRoot->getSystem()->getName()) != "recent" && !isEditing) { static_cast<std::string>(mRoot->getSystem()->getName()) != "recent" &&
!isEditing) {
FileData* entryToSelect; FileData* entryToSelect;
// Add favorite flag. // Add favorite flag.
if (!getCursor()->getFavorite()) { if (!getCursor()->getFavorite()) {
@ -331,7 +327,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
entryToSelect = getNextEntry(); entryToSelect = getNextEntry();
} }
else if (getCursor() == getLastEntry() && else if (getCursor() == getLastEntry() &&
getPreviousEntry()->getFavorite()) { getPreviousEntry()->getFavorite()) {
entryToSelect = getLastEntry(); entryToSelect = getLastEntry();
selectLastEntry = true; selectLastEntry = true;
} }
@ -342,14 +338,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
// If we mark the second entry as favorite and the first entry is not a // If we mark the second entry as favorite and the first entry is not a
// favorite, then select this entry if they are of the same type. // favorite, then select this entry if they are of the same type.
else if (getPreviousEntry() == getFirstEntry() && else if (getPreviousEntry() == getFirstEntry() &&
getCursor()->getType() == getPreviousEntry()->getType()) { getCursor()->getType() == getPreviousEntry()->getType()) {
entryToSelect = getPreviousEntry(); entryToSelect = getPreviousEntry();
} }
// For all other scenarios try to select the next entry, and if it doesn't // For all other scenarios try to select the next entry, and if it doesn't
// exist, select the previous entry. // exist, select the previous entry.
else { else {
entryToSelect = getCursor() != getNextEntry() ? entryToSelect =
getNextEntry() : getPreviousEntry(); getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry();
} }
} }
// Remove favorite flag. // Remove favorite flag.
@ -365,9 +361,10 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
// If we are on the favorite marking boundary, select the previous entry, // If we are on the favorite marking boundary, select the previous entry,
// unless folders are sorted on top and the previous entry is a folder. // unless folders are sorted on top and the previous entry is a folder.
else if (foldersOnTop && else if (foldersOnTop &&
getCursor()->getFavorite() != getNextEntry()->getFavorite()) { getCursor()->getFavorite() != getNextEntry()->getFavorite()) {
entryToSelect = getPreviousEntry()->getType() == FOLDER ? entryToSelect = getPreviousEntry()->getType() == FOLDER ?
getCursor() : getPreviousEntry(); getCursor() :
getPreviousEntry();
} }
// If we are on the favorite marking boundary, select the previous entry. // If we are on the favorite marking boundary, select the previous entry.
else if (getCursor()->getFavorite() != getNextEntry()->getFavorite()) { else if (getCursor()->getFavorite() != getNextEntry()->getFavorite()) {
@ -376,17 +373,17 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
// For all other scenarios try to select the next entry, and if it doesn't // For all other scenarios try to select the next entry, and if it doesn't
// exist, select the previous entry. // exist, select the previous entry.
else { else {
entryToSelect = getCursor() != getNextEntry() ? entryToSelect =
getNextEntry() : getPreviousEntry(); getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry();
} }
// If we removed the last favorite marking, set the flag to jump to the // If we removed the last favorite marking, set the flag to jump to the
// first list entry after the sorting has been performed. // first list entry after the sorting has been performed.
if (foldersOnTop && getCursor() == getFirstGameEntry() && if (foldersOnTop && getCursor() == getFirstGameEntry() &&
!getNextEntry()->getFavorite()) !getNextEntry()->getFavorite())
removedLastFavorite = true; removedLastFavorite = true;
else if (getCursor() == getFirstEntry() && !getNextEntry()->getFavorite()) else if (getCursor() == getFirstEntry() && !getNextEntry()->getFavorite())
removedLastFavorite = true; removedLastFavorite = true;
} }
setCursor(entryToSelect); setCursor(entryToSelect);
@ -399,22 +396,30 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
if (entryToUpdate->getType() == FOLDER) { if (entryToUpdate->getType() == FOLDER) {
GuiInfoPopup* s; GuiInfoPopup* s;
if (isEditing) { if (isEditing) {
s = new GuiInfoPopup(mWindow, s = new GuiInfoPopup(mWindow, "CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS",
"CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS", 4000); 4000);
} }
else { else {
MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata; MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata;
if (md->get("favorite") == "false") { if (md->get("favorite") == "false") {
md->set("favorite", "true"); md->set("favorite", "true");
s = new GuiInfoPopup(mWindow, "MARKED FOLDER '" + s = new GuiInfoPopup(
mWindow,
"MARKED FOLDER '" +
Utils::String::toUpper(Utils::String::removeParenthesis( Utils::String::toUpper(Utils::String::removeParenthesis(
entryToUpdate->getName())) + "' AS FAVORITE", 4000); entryToUpdate->getName())) +
"' AS FAVORITE",
4000);
} }
else { else {
md->set("favorite", "false"); md->set("favorite", "false");
s = new GuiInfoPopup(mWindow, "REMOVED FAVORITE MARKING FOR FOLDER '" + s = new GuiInfoPopup(
mWindow,
"REMOVED FAVORITE MARKING FOR FOLDER '" +
Utils::String::toUpper(Utils::String::removeParenthesis( Utils::String::toUpper(Utils::String::removeParenthesis(
entryToUpdate->getName())) + "'", 4000); entryToUpdate->getName())) +
"'",
4000);
} }
} }
@ -422,8 +427,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint(); entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint();
getCursor()->getParent()->sort( getCursor()->getParent()->sort(
mRoot->getSortTypeFromString(mRoot->getSortTypeString()), mRoot->getSortTypeFromString(mRoot->getSortTypeString()),
Settings::getInstance()->getBool("FavoritesFirst")); Settings::getInstance()->getBool("FavoritesFirst"));
ViewController::get()->onFileChanged(getCursor(), false); ViewController::get()->onFileChanged(getCursor(), false);
@ -431,16 +436,19 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
// was unmarked. We couldn't do this earlier as we didn't have the list // was unmarked. We couldn't do this earlier as we didn't have the list
// sorted yet. // sorted yet.
if (removedLastFavorite) { if (removedLastFavorite) {
ViewController::get()->getGameListView(entryToUpdate-> ViewController::get()
getSystem())->setCursor(ViewController::get()-> ->getGameListView(entryToUpdate->getSystem())
getGameListView(entryToUpdate->getSystem())->getFirstEntry()); ->setCursor(ViewController::get()
->getGameListView(entryToUpdate->getSystem())
->getFirstEntry());
} }
return true; return true;
} }
else if (isEditing && entryToUpdate->metadata.get("nogamecount") == "true") { else if (isEditing && entryToUpdate->metadata.get("nogamecount") == "true") {
GuiInfoPopup* s = new GuiInfoPopup(mWindow, GuiInfoPopup* s = new GuiInfoPopup(mWindow,
"CAN'T ADD ENTRIES THAT ARE NOT COUNTED " "CAN'T ADD ENTRIES THAT ARE NOT COUNTED "
"AS GAMES TO CUSTOM COLLECTIONS", 4000); "AS GAMES TO CUSTOM COLLECTIONS",
4000);
mWindow->setInfoPopup(s); mWindow->setInfoPopup(s);
} }
else if (CollectionSystemsManager::get()->toggleGameInCollection(entryToUpdate)) { else if (CollectionSystemsManager::get()->toggleGameInCollection(entryToUpdate)) {
@ -450,13 +458,15 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
IGameListView* view = ViewController::get()->getGameListView(system).get(); IGameListView* view = ViewController::get()->getGameListView(system).get();
// Jump to the first entry in the gamelist if the last favorite was unmarked. // Jump to the first entry in the gamelist if the last favorite was unmarked.
if (foldersOnTop && removedLastFavorite && if (foldersOnTop && removedLastFavorite &&
!entryToUpdate->getSystem()->isCustomCollection()) { !entryToUpdate->getSystem()->isCustomCollection()) {
ViewController::get()->getGameListView(entryToUpdate->getSystem())-> ViewController::get()
setCursor(ViewController::get()->getGameListView(entryToUpdate-> ->getGameListView(entryToUpdate->getSystem())
getSystem())->getFirstGameEntry()); ->setCursor(ViewController::get()
->getGameListView(entryToUpdate->getSystem())
->getFirstGameEntry());
} }
else if (removedLastFavorite && else if (removedLastFavorite &&
!entryToUpdate->getSystem()->isCustomCollection()) { !entryToUpdate->getSystem()->isCustomCollection()) {
view->setCursor(view->getFirstEntry()); view->setCursor(view->getFirstEntry());
} }
else if (selectLastEntry) { else if (selectLastEntry) {
@ -467,10 +477,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
// onFileChanged() which will trigger populateList(). // onFileChanged() which will trigger populateList().
if (isEditing) { if (isEditing) {
for (auto it = SystemData::sSystemVector.begin(); for (auto it = SystemData::sSystemVector.begin();
it != SystemData::sSystemVector.end(); it++) { it != SystemData::sSystemVector.end(); it++) {
ViewController::get()->getGameListView((*it))->onFileChanged( ViewController::get()->getGameListView((*it))->onFileChanged(
ViewController::get()->getGameListView((*it))-> ViewController::get()->getGameListView((*it))->getCursor(), false);
getCursor(), false);
} }
} }
return true; return true;
@ -511,13 +520,13 @@ void ISimpleGameListView::generateGamelistInfo(FileData* cursor, FileData* first
if (idx->isFiltered()) { if (idx->isFiltered()) {
mIsFiltered = true; mIsFiltered = true;
mFilteredGameCount = static_cast<unsigned int>(rootFolder-> mFilteredGameCount =
getFilesRecursive(GAME, true, false).size()); static_cast<unsigned int>(rootFolder->getFilesRecursive(GAME, true, false).size());
// Also count the games that are set to not be counted as games, as the filter may // Also count the games that are set to not be counted as games, as the filter may
// apply to such entries as well and this will be indicated with a separate '+ XX' // apply to such entries as well and this will be indicated with a separate '+ XX'
// in the GamelistInfo field. // in the GamelistInfo field.
mFilteredGameCountAll = static_cast<unsigned int>(rootFolder-> mFilteredGameCountAll =
getFilesRecursive(GAME, true, true).size()); static_cast<unsigned int>(rootFolder->getFilesRecursive(GAME, true, true).size());
} }
if (firstEntry->getParent() && firstEntry->getParent()->getType() == FOLDER) if (firstEntry->getParent() && firstEntry->getParent()->getType() == FOLDER)
@ -541,7 +550,7 @@ void ISimpleGameListView::generateFirstLetterIndex(const std::vector<FileData*>&
else else
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop"); bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
// Find out if there are only favorites and/or only folders in the list. // Find out if there are only favorites and/or only folders in the list.
for (auto it = files.begin(); it != files.end(); it++) { for (auto it = files.begin(); it != files.end(); it++) {
@ -553,16 +562,20 @@ void ISimpleGameListView::generateFirstLetterIndex(const std::vector<FileData*>&
// Build the index. // Build the index.
for (auto it = files.begin(); it != files.end(); it++) { for (auto it = files.begin(); it != files.end(); it++) {
if ((*it)->getType() == FOLDER && (*it)->getFavorite() && if ((*it)->getType() == FOLDER && (*it)->getFavorite() && favoritesSorting &&
favoritesSorting && !onlyFavorites) !onlyFavorites) {
hasFavorites = true; hasFavorites = true;
else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders) }
else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders) {
hasFolders = true; hasFolders = true;
else if ((*it)->getType() == GAME && (*it)->getFavorite() && }
favoritesSorting && !onlyFavorites) else if ((*it)->getType() == GAME && (*it)->getFavorite() && favoritesSorting &&
!onlyFavorites) {
hasFavorites = true; hasFavorites = true;
else }
else {
mFirstLetterIndex.push_back(Utils::String::getFirstCharacter((*it)->getSortName())); mFirstLetterIndex.push_back(Utils::String::getFirstCharacter((*it)->getSortName()));
}
} }
// Sort and make each entry unique. // Sort and make each entry unique.

View file

@ -9,9 +9,9 @@
#ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H
#define ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H
#include "views/gamelist/IGameListView.h"
#include "components/ImageComponent.h" #include "components/ImageComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "views/gamelist/IGameListView.h"
#include <stack> #include <stack>
@ -39,9 +39,13 @@ public:
// These functions are used to retain the folder cursor history, for instance // These functions are used to retain the folder cursor history, for instance
// during a view reload. The calling function stores the history temporarily. // during a view reload. The calling function stores the history temporarily.
void copyCursorHistory(std::vector<FileData*>& cursorHistory) override void copyCursorHistory(std::vector<FileData*>& cursorHistory) override
{ cursorHistory = mCursorStackHistory; }; {
cursorHistory = mCursorStackHistory;
};
void populateCursorHistory(std::vector<FileData*>& cursorHistory) override void populateCursorHistory(std::vector<FileData*>& cursorHistory) override
{ mCursorStackHistory = cursorHistory; }; {
mCursorStackHistory = cursorHistory;
};
protected: protected:
virtual std::string getQuickSystemSelectRightButton() = 0; virtual std::string getQuickSystemSelectRightButton() = 0;

View file

@ -16,67 +16,62 @@
#if defined(BUILD_VLC_PLAYER) #if defined(BUILD_VLC_PLAYER)
#include "components/VideoVlcComponent.h" #include "components/VideoVlcComponent.h"
#endif #endif
#include "utils/FileSystemUtil.h"
#include "views/ViewController.h"
#include "AudioManager.h" #include "AudioManager.h"
#include "CollectionSystemsManager.h" #include "CollectionSystemsManager.h"
#include "SystemData.h" #include "SystemData.h"
#include "utils/FileSystemUtil.h"
#include "views/ViewController.h"
#define FADE_IN_START_OPACITY 0.5f #define FADE_IN_START_OPACITY 0.5f
#define FADE_IN_TIME 650 #define FADE_IN_TIME 650
VideoGameListView::VideoGameListView( VideoGameListView::VideoGameListView(Window* window, FileData* root)
Window* window, : BasicGameListView(window, root)
FileData* root) , mDescContainer(window)
: BasicGameListView(window, root), , mDescription(window)
mDescContainer(window), , mGamelistInfo(window)
mDescription(window), , mThumbnail(window)
mGamelistInfo(window), , mMarquee(window)
, mImage(window)
mThumbnail(window), , mVideo(nullptr)
mMarquee(window), , mVideoPlaying(false)
mImage(window), , mLblRating(window)
mVideo(nullptr), , mLblReleaseDate(window)
mVideoPlaying(false), , mLblDeveloper(window)
, mLblPublisher(window)
mLblRating(window), , mLblGenre(window)
mLblReleaseDate(window), , mLblPlayers(window)
mLblDeveloper(window), , mLblLastPlayed(window)
mLblPublisher(window), , mLblPlayCount(window)
mLblGenre(window), , mRating(window)
mLblPlayers(window), , mReleaseDate(window)
mLblLastPlayed(window), , mDeveloper(window)
mLblPlayCount(window), , mPublisher(window)
, mGenre(window)
mRating(window), , mPlayers(window)
mReleaseDate(window), , mLastPlayed(window)
mDeveloper(window), , mPlayCount(window)
mPublisher(window), , mName(window)
mGenre(window), , mLastUpdated(nullptr)
mPlayers(window),
mLastPlayed(window),
mPlayCount(window),
mName(window),
mLastUpdated(nullptr)
{ {
const float padding = 0.01f; const float padding = 0.01f;
// Create the correct type of video window. // Create the correct type of video window.
#if defined(_RPI_) #if defined(_RPI_)
if (Settings::getInstance()->getBool("VideoOmxPlayer")) if (Settings::getInstance()->getBool("VideoOmxPlayer"))
mVideo = new VideoOmxComponent(window); mVideo = new VideoOmxComponent(window);
else if (Settings::getInstance()->getString("VideoPlayer") == "vlc") else if (Settings::getInstance()->getString("VideoPlayer") == "vlc")
mVideo = new VideoVlcComponent(window); mVideo = new VideoVlcComponent(window);
else else
mVideo = new VideoFFmpegComponent(window); mVideo = new VideoFFmpegComponent(window);
#elif defined(BUILD_VLC_PLAYER) #elif defined(BUILD_VLC_PLAYER)
if (Settings::getInstance()->getString("VideoPlayer") == "vlc") if (Settings::getInstance()->getString("VideoPlayer") == "vlc")
mVideo = new VideoVlcComponent(window); mVideo = new VideoVlcComponent(window);
else else
mVideo = new VideoFFmpegComponent(window); mVideo = new VideoFFmpegComponent(window);
#else #else
mVideo = new VideoFFmpegComponent(window); mVideo = new VideoFFmpegComponent(window);
#endif #endif
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y()); mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y());
@ -87,21 +82,21 @@ VideoGameListView::VideoGameListView(
mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setOrigin(0.5f, 0.5f);
mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setPosition(2.0f, 2.0f);
mThumbnail.setVisible(false); mThumbnail.setVisible(false);
mThumbnail.setMaxSize(mSize.x() * (0.25f - 2 * padding), mSize.y() * 0.10f); mThumbnail.setMaxSize(mSize.x() * (0.25f - 2.0f * padding), mSize.y() * 0.10f);
mThumbnail.setDefaultZIndex(35); mThumbnail.setDefaultZIndex(35);
addChild(&mThumbnail); addChild(&mThumbnail);
// Marquee. // Marquee.
mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setOrigin(0.5f, 0.5f);
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.18f); mMarquee.setMaxSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35); mMarquee.setDefaultZIndex(35);
addChild(&mMarquee); addChild(&mMarquee);
// Video. // Video.
mVideo->setOrigin(0.5f, 0.5f); mVideo->setOrigin(0.5f, 0.5f);
mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f); mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f);
mVideo->setSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.4f); mVideo->setSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.4f);
mVideo->setDefaultZIndex(30); mVideo->setDefaultZIndex(30);
addChild(mVideo); addChild(mVideo);
@ -140,8 +135,8 @@ VideoGameListView::VideoGameListView(
addChild(&mName); addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
mDescContainer.getPosition().y()); mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setAutoScroll(true); mDescContainer.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40); mDescContainer.setDefaultZIndex(40);
addChild(&mDescContainer); addChild(&mDescContainer);
@ -160,10 +155,7 @@ VideoGameListView::VideoGameListView(
initMDValues(); initMDValues();
} }
VideoGameListView::~VideoGameListView() VideoGameListView::~VideoGameListView() { delete mVideo; }
{
delete mVideo;
}
void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme) void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
{ {
@ -171,22 +163,23 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
using namespace ThemeFlags; using namespace ThemeFlags;
mThumbnail.applyTheme(theme, getName(), "md_thumbnail", mThumbnail.applyTheme(theme, getName(), "md_thumbnail",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mMarquee.applyTheme(theme, getName(), "md_marquee", mMarquee.applyTheme(theme, getName(), "md_marquee",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mImage.applyTheme(theme, getName(), "md_image", mImage.applyTheme(theme, getName(), "md_image",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mVideo->applyTheme(theme, getName(), "md_video", mVideo->applyTheme(theme, getName(), "md_video",
POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION |
VISIBLE);
mName.applyTheme(theme, getName(), "md_name", ALL); mName.applyTheme(theme, getName(), "md_name", ALL);
initMDLabels(); initMDLabels();
std::vector<TextComponent*> labels = getMDLabels(); std::vector<TextComponent*> labels = getMDLabels();
assert(labels.size() == 8); assert(labels.size() == 8);
std::vector<std::string> lblElements = { std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", "md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" "md_lbl_genre", "md_lbl_players",
}; "md_lbl_lastplayed", "md_lbl_playcount" };
for (unsigned int i = 0; i < labels.size(); i++) for (unsigned int i = 0; i < labels.size(); i++)
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
@ -194,19 +187,19 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDValues(); initMDValues();
std::vector<GuiComponent*> values = getMDValues(); std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 8); assert(values.size() == 8);
std::vector<std::string> valElements = { std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
"md_rating", "md_releasedate", "md_developer", "md_publisher", "md_publisher", "md_genre", "md_players",
"md_genre", "md_players", "md_lastplayed", "md_playcount" "md_lastplayed", "md_playcount" };
};
for (unsigned int i = 0; i < values.size(); i++) for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
mDescContainer.applyTheme(theme, getName(), "md_description", mDescContainer.applyTheme(theme, getName(), "md_description",
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
mDescription.setSize(mDescContainer.getSize().x(), 0); mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.applyTheme(theme, getName(), "md_description", mDescription.applyTheme(
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); theme, getName(), "md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
// If there is no position defined in the theme for gamelistInfo, then hide it. // If there is no position defined in the theme for gamelistInfo, then hide it.
@ -238,7 +231,7 @@ void VideoGameListView::initMDLabels()
} }
else { else {
// Work from the last component. // Work from the last component.
GuiComponent* lc = components[i-1]; GuiComponent* lc = components[i - 1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
} }
@ -265,11 +258,11 @@ void VideoGameListView::initMDValues()
float bottom = 0.0f; float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2; const float colSize = (mSize.x() * 0.48f) / 2.0f;
for (unsigned int i = 0; i < labels.size(); i++) { for (unsigned int i = 0; i < labels.size(); i++) {
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f;
values[i]->setPosition(labels[i]->getPosition() + values[i]->setPosition(labels[i]->getPosition() +
Vector3f(labels[i]->getSize().x(),heightDiff, 0)); Vector3f(labels[i]->getSize().x(), heightDiff, 0));
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40); values[i]->setDefaultZIndex(40);
@ -280,8 +273,8 @@ void VideoGameListView::initMDValues()
} }
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.setSize(mDescContainer.getSize().x(),
mDescContainer.getPosition().y()); mSize.y() - mDescContainer.getPosition().y());
} }
void VideoGameListView::updateInfoPanel() void VideoGameListView::updateInfoPanel()
@ -300,7 +293,7 @@ void VideoGameListView::updateInfoPanel()
if (file) { if (file) {
// Always hide the metadata fields if browsing grouped custom collections. // Always hide the metadata fields if browsing grouped custom collections.
if (file->getSystem()->isCustomCollection() && if (file->getSystem()->isCustomCollection() &&
file->getPath() == file->getSystem()->getName()) file->getPath() == file->getSystem()->getName())
hideMetaDataFields = true; hideMetaDataFields = true;
else else
hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); hideMetaDataFields = (file->metadata.get("hidemetadata") == "true");
@ -316,9 +309,9 @@ void VideoGameListView::updateInfoPanel()
// or if we're in the grouped custom collection view. // or if we're in the grouped custom collection view.
if (mList.isScrolling()) if (mList.isScrolling())
if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") ||
(mLastUpdated->getSystem()->isCustomCollection() && (mLastUpdated->getSystem()->isCustomCollection() &&
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
hideMetaDataFields = true; hideMetaDataFields = true;
if (hideMetaDataFields) { if (hideMetaDataFields) {
mLblRating.setVisible(false); mLblRating.setVisible(false);
@ -367,9 +360,9 @@ void VideoGameListView::updateInfoPanel()
// which will generate a description of three random games and return a pointer to // which will generate a description of three random games and return a pointer to
// the first of these so that we can display its game media. // the first of these so that we can display its game media.
if (file->getSystem()->isCustomCollection() && if (file->getSystem()->isCustomCollection() &&
file->getPath() == file->getSystem()->getName()) { file->getPath() == file->getSystem()->getName()) {
mRandomGame = CollectionSystemsManager::get()-> mRandomGame =
updateCollectionFolderMetadata(file->getSystem()); CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem());
if (mRandomGame) { if (mRandomGame) {
mThumbnail.setImage(mRandomGame->getThumbnailPath()); mThumbnail.setImage(mRandomGame->getThumbnailPath());
mMarquee.setImage(mRandomGame->getMarqueePath()); mMarquee.setImage(mRandomGame->getMarqueePath());
@ -398,7 +391,6 @@ void VideoGameListView::updateInfoPanel()
mVideo->setImage(file->getImagePath()); mVideo->setImage(file->getImagePath());
mVideo->onHide(); mVideo->onHide();
if (!mVideo->setVideo(file->getVideoPath())) if (!mVideo->setVideo(file->getVideoPath()))
mVideo->setDefaultVideo(); mVideo->setDefaultVideo();
} }
@ -418,21 +410,21 @@ void VideoGameListView::updateInfoPanel()
if (mIsFiltered) { if (mIsFiltered) {
if (mFilteredGameCountAll == mFilteredGameCount) if (mFilteredGameCountAll == mFilteredGameCount)
gamelistInfoString += ViewController::FILTER_CHAR + " " + gamelistInfoString += ViewController::FILTER_CHAR + " " +
std::to_string(mFilteredGameCount) + " / " + std::to_string(mFilteredGameCount) + " / " +
std::to_string(mGameCount); std::to_string(mGameCount);
else else
gamelistInfoString += ViewController::FILTER_CHAR + " " + gamelistInfoString += ViewController::FILTER_CHAR + " " +
std::to_string(mFilteredGameCount) + " + " + std::to_string(mFilteredGameCount) + " + " +
std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " + std::to_string(mFilteredGameCountAll - mFilteredGameCount) +
std::to_string(mGameCount); " / " + std::to_string(mGameCount);
} }
else { else {
gamelistInfoString += ViewController::CONTROLLER_CHAR + " " + gamelistInfoString +=
std::to_string(mGameCount); ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
if (!(file->getSystem()->isCollection() && if (!(file->getSystem()->isCollection() &&
file->getSystem()->getFullName() == "favorites")) file->getSystem()->getFullName() == "favorites"))
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
+ std::to_string(mFavoritesGameCount); std::to_string(mFavoritesGameCount);
} }
if (mIsFolder && infoAlign != ALIGN_RIGHT) if (mIsFolder && infoAlign != ALIGN_RIGHT)
@ -442,9 +434,9 @@ void VideoGameListView::updateInfoPanel()
// Fade in the game image. // Fade in the game image.
auto func = [this](float t) { auto func = [this](float t) {
mVideo->setOpacity(static_cast<unsigned char>(Math::lerp( mVideo->setOpacity(static_cast<unsigned char>(
static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255)); Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
}; };
mVideo->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); mVideo->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
mDescription.setText(file->metadata.get("desc")); mDescription.setText(file->metadata.get("desc"));
@ -498,10 +490,7 @@ void VideoGameListView::updateInfoPanel()
} }
} }
void VideoGameListView::launch(FileData* game) void VideoGameListView::launch(FileData* game) { ViewController::get()->triggerGameLaunch(game); }
{
ViewController::get()->triggerGameLaunch(game);
}
std::vector<TextComponent*> VideoGameListView::getMDLabels() std::vector<TextComponent*> VideoGameListView::getMDLabels()
{ {