mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 15:45:38 +00:00
Formatted the es-app source tree using clang-format.
This commit is contained in:
parent
745cf6ff92
commit
af5e32e121
File diff suppressed because it is too large
Load diff
|
@ -34,7 +34,7 @@ class Window;
|
|||
struct SystemEnvironmentData;
|
||||
|
||||
enum CollectionSystemType {
|
||||
AUTO_ALL_GAMES,
|
||||
AUTO_ALL_GAMES, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
AUTO_LAST_PLAYED,
|
||||
AUTO_FAVORITES,
|
||||
CUSTOM_COLLECTION
|
||||
|
@ -115,13 +115,17 @@ public:
|
|||
// Repopulate the collection, which is basically a forced update of its complete content.
|
||||
void repopulateCollection(SystemData* sysData);
|
||||
|
||||
inline std::map<std::string, CollectionSystemData, stringComparator>
|
||||
getAutoCollectionSystems() { return mAutoCollectionSystemsData; };
|
||||
inline std::map<std::string, CollectionSystemData, stringComparator>
|
||||
getCustomCollectionSystems() { return mCustomCollectionSystemsData; };
|
||||
inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; };
|
||||
inline bool isEditing() { return mIsEditingCustom; };
|
||||
inline std::string getEditingCollection() { return mEditingCollection; };
|
||||
std::map<std::string, CollectionSystemData, stringComparator> getAutoCollectionSystems()
|
||||
{
|
||||
return mAutoCollectionSystemsData;
|
||||
}
|
||||
std::map<std::string, CollectionSystemData, stringComparator> getCustomCollectionSystems()
|
||||
{
|
||||
return mCustomCollectionSystemsData;
|
||||
}
|
||||
SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }
|
||||
bool isEditing() { return mIsEditingCustom; }
|
||||
std::string getEditingCollection() { return mEditingCollection; }
|
||||
|
||||
private:
|
||||
static CollectionSystemsManager* sInstance;
|
||||
|
@ -143,7 +147,9 @@ private:
|
|||
SystemData* getAllGamesCollection();
|
||||
// Create a new empty collection system based on the name and declaration.
|
||||
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.
|
||||
void populateAutoCollection(CollectionSystemData* sysData);
|
||||
// Populate a custom collection system.
|
||||
|
@ -151,8 +157,8 @@ private:
|
|||
|
||||
// Functions to handle System View removal and insertion of collections:
|
||||
void removeCollectionsFromDisplayedSystems();
|
||||
void addEnabledCollectionsToDisplayedSystems(std::map<std::string,
|
||||
CollectionSystemData, stringComparator>* colSystemData);
|
||||
void addEnabledCollectionsToDisplayedSystems(
|
||||
std::map<std::string, CollectionSystemData, stringComparator>* colSystemData);
|
||||
|
||||
// Auxiliary functions:
|
||||
std::vector<std::string> getSystemsFromConfig();
|
||||
|
|
|
@ -10,14 +10,16 @@
|
|||
|
||||
// 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.
|
||||
// clang-format off
|
||||
#define PROGRAM_VERSION_MAJOR 1
|
||||
#define PROGRAM_VERSION_MINOR 0
|
||||
#define PROGRAM_VERSION_MINOR 1
|
||||
#define PROGRAM_VERSION_MAINTENANCE 0
|
||||
// clang-format on
|
||||
#define PROGRAM_VERSION_STRING "1.1.0-rc-dev"
|
||||
|
||||
#define PROGRAM_BUILT_STRING __DATE__ " - " __TIME__
|
||||
|
||||
#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
|
||||
|
|
|
@ -10,12 +10,6 @@
|
|||
|
||||
#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 "CollectionSystemsManager.h"
|
||||
#include "FileFilterIndex.h"
|
||||
|
@ -26,33 +20,37 @@
|
|||
#include "Scripting.h"
|
||||
#include "SystemData.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>
|
||||
|
||||
FileData::FileData(
|
||||
FileType type,
|
||||
const std::string& path,
|
||||
SystemEnvironmentData* envData,
|
||||
SystemData* system)
|
||||
: mType(type),
|
||||
mPath(path),
|
||||
mSystem(system),
|
||||
mEnvData(envData),
|
||||
mSourceFileData(nullptr),
|
||||
mParent(nullptr),
|
||||
mOnlyFolders(false),
|
||||
mDeletionFlag(false),
|
||||
// Metadata is set in the constructor.
|
||||
metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
|
||||
FileData::FileData(FileType type,
|
||||
const std::string& path,
|
||||
SystemEnvironmentData* envData,
|
||||
SystemData* system)
|
||||
: mType(type)
|
||||
, mPath(path)
|
||||
, mSystem(system)
|
||||
, mEnvData(envData)
|
||||
, mSourceFileData(nullptr)
|
||||
, mParent(nullptr)
|
||||
, mOnlyFolders(false)
|
||||
, mDeletionFlag(false)
|
||||
// Metadata is set in the constructor.
|
||||
, metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
|
||||
{
|
||||
// Metadata needs at least a name field (since that's what getName() will return).
|
||||
if (metadata.get("name").empty()) {
|
||||
if ((system->hasPlatformId(PlatformIds::ARCADE) ||
|
||||
system->hasPlatformId(PlatformIds::SNK_NEO_GEO)) &&
|
||||
metadata.getType() != FOLDER_METADATA) {
|
||||
system->hasPlatformId(PlatformIds::SNK_NEO_GEO)) &&
|
||||
metadata.getType() != FOLDER_METADATA) {
|
||||
// If it's a MAME or Neo Geo game, expand the game name accordingly.
|
||||
metadata.set("name",
|
||||
MameNames::getInstance()->getCleanName(getCleanName()));
|
||||
metadata.set("name", MameNames::getInstance()->getCleanName(getCleanName()));
|
||||
}
|
||||
else {
|
||||
if (metadata.getType() == FOLDER_METADATA && Utils::FileSystem::isHidden(mPath)) {
|
||||
|
@ -89,6 +87,7 @@ std::string FileData::getCleanName() const
|
|||
|
||||
const std::string& FileData::getName()
|
||||
{
|
||||
// Return metadata name.
|
||||
return metadata.get("name");
|
||||
}
|
||||
|
||||
|
@ -144,14 +143,13 @@ const std::vector<FileData*> FileData::getChildrenRecursive() const
|
|||
{
|
||||
std::vector<FileData*> childrenRecursive;
|
||||
|
||||
for (auto it = mChildrenByFilename.cbegin();
|
||||
it != mChildrenByFilename.cend(); it++) {
|
||||
for (auto it = mChildrenByFilename.cbegin(); it != mChildrenByFilename.cend(); it++) {
|
||||
childrenRecursive.push_back((*it).second);
|
||||
// Recurse through any subdirectories.
|
||||
if ((*it).second->getType() == FOLDER) {
|
||||
std::vector<FileData*> childrenSubdirectory = (*it).second->getChildrenRecursive();
|
||||
childrenRecursive.insert(childrenRecursive.end(),
|
||||
childrenSubdirectory.begin(), childrenSubdirectory.end());
|
||||
childrenRecursive.insert(childrenRecursive.end(), childrenSubdirectory.begin(),
|
||||
childrenSubdirectory.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,13 +169,13 @@ const std::string FileData::getROMDirectory()
|
|||
// Expand home path if ~ is used.
|
||||
romDirPath = Utils::FileSystem::expandHomePath(romDirPath);
|
||||
|
||||
#if defined(_WIN64)
|
||||
if (romDirPath.back() != '\\')
|
||||
#if defined(_WIN64)
|
||||
if (romDirPath.back() != '\\')
|
||||
romDirPath = romDirPath + "\\";
|
||||
#else
|
||||
if (romDirPath.back() != '/')
|
||||
#else
|
||||
if (romDirPath.back() != '/')
|
||||
romDirPath = romDirPath + "/";
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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
|
||||
// binary directory of ES-DE.
|
||||
mediaDirPath = Utils::String::replace(
|
||||
mediaDirPath, "%ESPATH%", Utils::FileSystem::getExePath());
|
||||
mediaDirPath =
|
||||
Utils::String::replace(mediaDirPath, "%ESPATH%", Utils::FileSystem::getExePath());
|
||||
|
||||
if (mediaDirPath.back() != '/')
|
||||
if (mediaDirPath.back() != '/')
|
||||
mediaDirPath = mediaDirPath + "/";
|
||||
}
|
||||
|
||||
|
@ -219,11 +217,11 @@ const std::string FileData::getMediafilePath(std::string subdirectory, std::stri
|
|||
|
||||
// Extract possible subfolders from the path.
|
||||
if (mEnvData->mStartPath != "")
|
||||
subFolders = Utils::String::replace(
|
||||
Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, "");
|
||||
subFolders =
|
||||
Utils::String::replace(Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, "");
|
||||
|
||||
const std::string tempPath = getMediaDirectory() + mSystemName + "/" + subdirectory +
|
||||
subFolders + "/" + getDisplayName();
|
||||
subFolders + "/" + getDisplayName();
|
||||
|
||||
// Look for an image file in the media directory.
|
||||
for (int i = 0; i < extList.size(); i++) {
|
||||
|
@ -253,31 +251,37 @@ const std::string FileData::getImagePath() const
|
|||
|
||||
const std::string FileData::get3DBoxPath() const
|
||||
{
|
||||
// Return path to the 3D box image.
|
||||
return getMediafilePath("3dboxes", "3dbox");
|
||||
}
|
||||
|
||||
const std::string FileData::getCoverPath() const
|
||||
{
|
||||
// Return path to the cover image.
|
||||
return getMediafilePath("covers", "cover");
|
||||
}
|
||||
|
||||
const std::string FileData::getMarqueePath() const
|
||||
{
|
||||
// Return path to the marquee image.
|
||||
return getMediafilePath("marquees", "marquee");
|
||||
}
|
||||
|
||||
const std::string FileData::getMiximagePath() const
|
||||
{
|
||||
// Return path to the miximage.
|
||||
return getMediafilePath("miximages", "miximage");
|
||||
}
|
||||
|
||||
const std::string FileData::getScreenshotPath() const
|
||||
{
|
||||
// Return path to the screenshot image.
|
||||
return getMediafilePath("screenshots", "screenshot");
|
||||
}
|
||||
|
||||
const std::string FileData::getThumbnailPath() const
|
||||
{
|
||||
// Return path to the thumbnail image.
|
||||
return getMediafilePath("thumbnails", "thumbnail");
|
||||
}
|
||||
|
||||
|
@ -288,11 +292,11 @@ const std::string FileData::getVideoPath() const
|
|||
|
||||
// Extract possible subfolders from the path.
|
||||
if (mEnvData->mStartPath != "")
|
||||
subFolders = Utils::String::replace(
|
||||
Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, "");
|
||||
subFolders =
|
||||
Utils::String::replace(Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, "");
|
||||
|
||||
const std::string tempPath =
|
||||
getMediaDirectory() + mSystemName + "/videos" + subFolders + "/" + getDisplayName();
|
||||
getMediaDirectory() + mSystemName + "/videos" + subFolders + "/" + getDisplayName();
|
||||
|
||||
// Look for media in the media directory.
|
||||
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,
|
||||
bool displayedOnly, bool countAllGames) const
|
||||
bool displayedOnly,
|
||||
bool countAllGames) const
|
||||
{
|
||||
std::vector<FileData*> out;
|
||||
FileFilterIndex* idx = mSystem->getIndex();
|
||||
|
@ -354,7 +359,8 @@ std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask,
|
|||
}
|
||||
|
||||
std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
|
||||
bool excludeRecursively, bool respectExclusions) const
|
||||
bool excludeRecursively,
|
||||
bool respectExclusions) const
|
||||
{
|
||||
std::vector<FileData*> out;
|
||||
|
||||
|
@ -375,7 +381,7 @@ std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
|
|||
|
||||
if ((*it)->getChildren().size() > 0) {
|
||||
std::vector<FileData*> subChildren = (*it)->getScrapeFilesRecursive(
|
||||
includeFolders, excludeRecursively, respectExclusions);
|
||||
includeFolders, excludeRecursively, respectExclusions);
|
||||
out.insert(out.cend(), subChildren.cbegin(), subChildren.cend());
|
||||
}
|
||||
}
|
||||
|
@ -383,32 +389,25 @@ std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
|
|||
return out;
|
||||
}
|
||||
|
||||
std::string FileData::getKey() {
|
||||
return getFileName();
|
||||
}
|
||||
std::string FileData::getKey() { return getFileName(); }
|
||||
|
||||
const bool FileData::isArcadeAsset()
|
||||
{
|
||||
const std::string stem = Utils::FileSystem::getStem(mPath);
|
||||
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
||||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
||||
(MameNames::getInstance()->isBios(stem) ||
|
||||
MameNames::getInstance()->isDevice(stem)));
|
||||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
||||
(MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem)));
|
||||
}
|
||||
|
||||
const bool FileData::isArcadeGame()
|
||||
{
|
||||
const std::string stem = Utils::FileSystem::getStem(mPath);
|
||||
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
||||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
||||
(!MameNames::getInstance()->isBios(stem) &&
|
||||
!MameNames::getInstance()->isDevice(stem)));
|
||||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
||||
(!MameNames::getInstance()->isBios(stem) && !MameNames::getInstance()->isDevice(stem)));
|
||||
}
|
||||
|
||||
FileData* FileData::getSourceFileData()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
FileData* FileData::getSourceFileData() { return this; }
|
||||
|
||||
void FileData::addChild(FileData* file)
|
||||
{
|
||||
|
@ -441,7 +440,7 @@ void FileData::removeChild(FileData* file)
|
|||
}
|
||||
|
||||
void FileData::sort(ComparisonFunction& comparator,
|
||||
std::pair<unsigned int, unsigned int>& gameCount)
|
||||
std::pair<unsigned int, unsigned int>& gameCount)
|
||||
{
|
||||
mOnlyFolders = true;
|
||||
mHasFolders = false;
|
||||
|
@ -456,17 +455,17 @@ void FileData::sort(ComparisonFunction& comparator,
|
|||
|
||||
if (!showHiddenGames) {
|
||||
for (auto it = mChildren.begin(); it != mChildren.end();) {
|
||||
// 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
|
||||
// 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
|
||||
// should be filtered already at that earlier point.
|
||||
// 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
|
||||
// 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
|
||||
// should be filtered already at that earlier point.
|
||||
if ((*it)->getHidden())
|
||||
it = mChildren.erase(it);
|
||||
// Also hide folders where all its entries have been hidden, unless it's a
|
||||
// grouped custom collection.
|
||||
else if ((*it)->getType() == FOLDER && (*it)->getChildren().size() == 0 &&
|
||||
!(*it)->getSystem()->isGroupedCustomCollection())
|
||||
!(*it)->getSystem()->isGroupedCustomCollection())
|
||||
it = mChildren.erase(it);
|
||||
else
|
||||
it++;
|
||||
|
@ -502,11 +501,11 @@ void FileData::sort(ComparisonFunction& comparator,
|
|||
// 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.
|
||||
if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator &&
|
||||
getSortTypeFromString("filename, descending").comparisonFunction != comparator) {
|
||||
getSortTypeFromString("filename, descending").comparisonFunction != comparator) {
|
||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
}
|
||||
|
||||
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
|
||||
// as a first step, in order to get a correct secondary sorting.
|
||||
if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator &&
|
||||
getSortTypeFromString("filename, descending").comparisonFunction != comparator)
|
||||
getSortTypeFromString("filename, descending").comparisonFunction != comparator)
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
|
||||
}
|
||||
|
@ -555,7 +554,7 @@ void FileData::sort(ComparisonFunction& comparator,
|
|||
}
|
||||
|
||||
void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
|
||||
std::pair<unsigned int, unsigned int>& gameCount)
|
||||
std::pair<unsigned int, unsigned int>& gameCount)
|
||||
{
|
||||
mOnlyFolders = true;
|
||||
mHasFolders = false;
|
||||
|
@ -634,39 +633,39 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
|
|||
// some folders as favorites is probably a rare situation.
|
||||
if (!mOnlyFolders && mChildrenFavoritesFolders.size() > 0) {
|
||||
mChildrenFolders.insert(mChildrenFolders.end(), mChildrenFavoritesFolders.begin(),
|
||||
mChildrenFavoritesFolders.end());
|
||||
mChildrenFavoritesFolders.end());
|
||||
mChildrenFavoritesFolders.erase(mChildrenFavoritesFolders.begin(),
|
||||
mChildrenFavoritesFolders.end());
|
||||
mChildrenFavoritesFolders.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
|
||||
// as a first step, in order to get a correct secondary sorting.
|
||||
if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator &&
|
||||
getSortTypeFromString("filename, descending").comparisonFunction != comparator) {
|
||||
getSortTypeFromString("filename, descending").comparisonFunction != comparator) {
|
||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(),
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||
}
|
||||
|
||||
// Sort favorite games and the other games separately.
|
||||
if (foldersOnTop && mOnlyFolders) {
|
||||
std::stable_sort(mChildrenFavoritesFolders.begin(),
|
||||
mChildrenFavoritesFolders.end(), comparator);
|
||||
std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
|
||||
comparator);
|
||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
||||
}
|
||||
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
||||
|
||||
// Iterate through any child favorite folders.
|
||||
for (auto it = mChildrenFavoritesFolders.cbegin(); it !=
|
||||
mChildrenFavoritesFolders.cend(); it++) {
|
||||
for (auto it = mChildrenFavoritesFolders.cbegin(); // Line break.
|
||||
it != mChildrenFavoritesFolders.cend(); it++) {
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sortFavoritesOnTop(comparator, gameCount);
|
||||
}
|
||||
|
@ -690,9 +689,9 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
|
|||
// Combine the individually sorted favorite games and other games vectors.
|
||||
mChildren.erase(mChildren.begin(), mChildren.end());
|
||||
mChildren.reserve(mChildrenFavoritesFolders.size() + mChildrenFolders.size() +
|
||||
mChildrenFavorites.size() + mChildrenOthers.size());
|
||||
mChildrenFavorites.size() + mChildrenOthers.size());
|
||||
mChildren.insert(mChildren.end(), mChildrenFavoritesFolders.begin(),
|
||||
mChildrenFavoritesFolders.end());
|
||||
mChildrenFavoritesFolders.end());
|
||||
mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end());
|
||||
mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.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)
|
||||
{
|
||||
bool isKidMode = (Settings::getInstance()->getString("UIMode") == "kid" ||
|
||||
Settings::getInstance()->getBool("ForceKid"));
|
||||
Settings::getInstance()->getBool("ForceKid"));
|
||||
|
||||
(Settings::getInstance()->getString("UIMode") == "kid" ||
|
||||
Settings::getInstance()->getBool("ForceKid"));
|
||||
Settings::getInstance()->getBool("ForceKid"));
|
||||
|
||||
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||
if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) {
|
||||
|
@ -731,7 +730,8 @@ void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
|
|||
mGameCount = gameCount;
|
||||
}
|
||||
|
||||
FileData::SortType FileData::getSortTypeFromString(std::string desc) {
|
||||
FileData::SortType FileData::getSortTypeFromString(std::string desc)
|
||||
{
|
||||
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
|
||||
|
||||
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
|
||||
// and the corresponding option to use it has been set.
|
||||
if (Settings::getInstance()->getBool("LaunchCommandOverride") &&
|
||||
!metadata.get("launchcommand").empty())
|
||||
!metadata.get("launchcommand").empty()) {
|
||||
command = metadata.get("launchcommand");
|
||||
else
|
||||
}
|
||||
else {
|
||||
command = mEnvData->mLaunchCommand;
|
||||
}
|
||||
|
||||
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.
|
||||
if (binaryPath.substr(0, 18) == "NO EMULATOR RULE: ") {
|
||||
std::string emulatorEntry = binaryPath.substr(18, binaryPath.size() - 18);
|
||||
LOG(LogError) << "Couldn't launch game, either there is no emulator entry for \"" <<
|
||||
emulatorEntry << "\" in es_find_rules.xml or there are no systempath or staticpath "
|
||||
"rules defined";
|
||||
LOG(LogError)
|
||||
<< "Couldn't launch game, either there is no emulator entry for \"" << emulatorEntry
|
||||
<< "\" in es_find_rules.xml or there are no systempath or staticpath rules defined";
|
||||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: MISSING EMULATOR CONFIGURATION FOR '" +
|
||||
emulatorEntry + "'", 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(
|
||||
window, "ERROR: MISSING EMULATOR CONFIGURATION FOR '" + emulatorEntry + "'", 6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
|
@ -810,20 +812,23 @@ void FileData::launchGame(Window* window)
|
|||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR, HAS IT " \
|
||||
"BEEN PROPERLY INSTALLED?", 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window,
|
||||
"ERROR: COULDN'T FIND EMULATOR, HAS IT "
|
||||
"BEEN PROPERLY INSTALLED?",
|
||||
6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
#if defined(_WIN64)
|
||||
LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" <<
|
||||
Utils::String::replace(Utils::String::replace(
|
||||
binaryPath, "%ESPATH%", esPath), "/", "\\") << "\"";
|
||||
#else
|
||||
LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" <<
|
||||
Utils::String::replace(binaryPath, "%ESPATH%", esPath) << "\"";
|
||||
#endif
|
||||
#if defined(_WIN64)
|
||||
LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \""
|
||||
<< Utils::String::replace(
|
||||
Utils::String::replace(binaryPath, "%ESPATH%", esPath), "/", "\\")
|
||||
<< "\"";
|
||||
#else
|
||||
LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \""
|
||||
<< Utils::String::replace(binaryPath, "%ESPATH%", esPath) << "\"";
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (command.find("\"%EMUPATH%", emuPathPos - 1) != std::string::npos) {
|
||||
hasQuotationMark = true;
|
||||
quotationMarkPos = static_cast<unsigned int>(
|
||||
command.find("\"", emuPathPos + 9) - emuPathPos);
|
||||
quotationMarkPos =
|
||||
static_cast<unsigned int>(command.find("\"", emuPathPos + 9) - emuPathPos);
|
||||
}
|
||||
size_t spacePos = command.find(" ", emuPathPos + quotationMarkPos);
|
||||
std::string coreRaw;
|
||||
|
@ -843,22 +848,23 @@ void FileData::launchGame(Window* window)
|
|||
if (spacePos != std::string::npos) {
|
||||
coreRaw = command.substr(emuPathPos, spacePos - emuPathPos);
|
||||
coreFile = Utils::FileSystem::getParent(binaryPath) +
|
||||
command.substr(emuPathPos + 9, spacePos - emuPathPos - 9);
|
||||
command.substr(emuPathPos + 9, spacePos - emuPathPos - 9);
|
||||
if (hasQuotationMark) {
|
||||
coreRaw.pop_back();
|
||||
coreFile.pop_back();
|
||||
}
|
||||
if (!Utils::FileSystem::isRegularFile(coreFile) &&
|
||||
!Utils::FileSystem::isSymlink(coreFile)) {
|
||||
LOG(LogError) << "Couldn't launch game, emulator core file \"" <<
|
||||
Utils::FileSystem::getFileName(coreFile) << "\" not found";
|
||||
!Utils::FileSystem::isSymlink(coreFile)) {
|
||||
LOG(LogError) << "Couldn't launch game, emulator core file \""
|
||||
<< Utils::FileSystem::getFileName(coreFile) << "\" not found";
|
||||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window,
|
||||
"ERROR: COULDN'T FIND EMULATOR CORE FILE '" +
|
||||
Utils::String::toUpper(Utils::FileSystem::getFileName(coreFile)) +
|
||||
"'", 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(
|
||||
window,
|
||||
"ERROR: COULDN'T FIND EMULATOR CORE FILE '" +
|
||||
Utils::String::toUpper(Utils::FileSystem::getFileName(coreFile)) + "'",
|
||||
6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
|
@ -877,8 +883,10 @@ void FileData::launchGame(Window* window)
|
|||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: INVALID ENTRY IN SYSTEMS " \
|
||||
"CONFIGURATION FILE", 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window,
|
||||
"ERROR: INVALID ENTRY IN SYSTEMS "
|
||||
"CONFIGURATION FILE",
|
||||
6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
|
@ -886,13 +894,13 @@ void FileData::launchGame(Window* window)
|
|||
|
||||
// Error handling in case of no core find rule.
|
||||
if (coreEntry != "" && emulatorCorePaths.empty()) {
|
||||
LOG(LogError) << "Couldn't launch game, either there is no core entry for \"" <<
|
||||
coreEntry << "\" in es_find_rules.xml or there are no corepath rules defined";
|
||||
LOG(LogError) << "Couldn't launch game, either there is no core entry for \"" << coreEntry
|
||||
<< "\" in es_find_rules.xml or there are no corepath rules defined";
|
||||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: MISSING CORE CONFIGURATION FOR '" +
|
||||
coreEntry + "'", 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(
|
||||
window, "ERROR: MISSING CORE CONFIGURATION FOR '" + coreEntry + "'", 6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
|
@ -913,46 +921,47 @@ void FileData::launchGame(Window* window)
|
|||
separatorPos = quotePos;
|
||||
|
||||
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);
|
||||
#else
|
||||
#else
|
||||
std::string coreFile = Utils::FileSystem::expandHomePath(path + "/" + coreName);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Expand %EMUPATH% if it has been used in the %CORE_ variable.
|
||||
size_t stringPos = coreFile.find("%EMUPATH%");
|
||||
if (stringPos != std::string::npos) {
|
||||
#if defined (_WIN64)
|
||||
coreFile = Utils::String::replace(coreFile.replace(stringPos, 9,
|
||||
Utils::FileSystem::getParent(binaryPath)), "/", "\\");
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
coreFile = Utils::String::replace(
|
||||
coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath)), "/",
|
||||
"\\");
|
||||
#else
|
||||
coreFile = coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Expand %ESPATH% if it has been used in the %CORE_ variable.
|
||||
stringPos = coreFile.find("%ESPATH%");
|
||||
if (stringPos != std::string::npos) {
|
||||
coreFile = coreFile.replace(stringPos, 8, esPath);
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
coreFile = Utils::String::replace(coreFile, "/", "\\");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
if (Utils::FileSystem::isRegularFile(coreFile) ||
|
||||
Utils::FileSystem::isSymlink(coreFile)) {
|
||||
Utils::FileSystem::isSymlink(coreFile)) {
|
||||
foundCoreFile = true;
|
||||
// Escape any blankspaces.
|
||||
if (coreFile.find(" ") != std::string::npos)
|
||||
coreFile = Utils::FileSystem::getEscapedPath(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.
|
||||
if (command.find("\"") != std::string::npos)
|
||||
command = Utils::String::replace(command, "\"", "");
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -961,23 +970,28 @@ void FileData::launchGame(Window* window)
|
|||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: INVALID ENTRY IN SYSTEMS " \
|
||||
"CONFIGURATION FILE", 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window,
|
||||
"ERROR: INVALID ENTRY IN SYSTEMS "
|
||||
"CONFIGURATION FILE",
|
||||
6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!foundCoreFile && coreName.size() > 0) {
|
||||
LOG(LogError) << "Couldn't launch game, emulator core file \"" <<
|
||||
coreName.substr(0, coreName.size()) << "\" not found";
|
||||
LOG(LogError) << "Couldn't launch game, emulator core file \""
|
||||
<< coreName.substr(0, coreName.size()) << "\" not found";
|
||||
LOG(LogError) << "Raw emulator launch command:";
|
||||
LOG(LogError) << commandRaw;
|
||||
LOG(LogError) <<
|
||||
"Tried to find the core file using these paths as defined by es_find_rules.xml:";
|
||||
LOG(LogError)
|
||||
<< "Tried to find the core file using these paths as defined by es_find_rules.xml:";
|
||||
LOG(LogError) << Utils::String::vectorToDelimitedString(emulatorCorePaths, ", ");
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR CORE FILE '" +
|
||||
Utils::String::toUpper(coreName.substr(0, coreName.size()) + "'"), 6000);
|
||||
GuiInfoPopup* s =
|
||||
new GuiInfoPopup(window,
|
||||
"ERROR: COULDN'T FIND EMULATOR CORE FILE '" +
|
||||
Utils::String::toUpper(coreName.substr(0, coreName.size()) + "'"),
|
||||
6000);
|
||||
window->setInfoPopup(s);
|
||||
return;
|
||||
}
|
||||
|
@ -990,12 +1004,13 @@ void FileData::launchGame(Window* window)
|
|||
// 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
|
||||
// from the game.
|
||||
#if defined(_WIN64)
|
||||
|
||||
#if defined(_WIN64)
|
||||
if (!(Settings::getInstance()->getBool("LaunchWorkaround") ||
|
||||
ViewController::get()->runInBackground(mSystem)))
|
||||
#else
|
||||
ViewController::get()->runInBackground(mSystem)))
|
||||
#else
|
||||
if (!ViewController::get()->runInBackground(mSystem))
|
||||
#endif
|
||||
#endif
|
||||
Renderer::swapBuffers();
|
||||
|
||||
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) << commandRaw;
|
||||
LOG(LogInfo) << "Expanded emulator launch command:";
|
||||
|
||||
LOG(LogInfo) << command;
|
||||
|
||||
// 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),
|
||||
ViewController::get()->runInBackground(mSystem));
|
||||
#else
|
||||
ViewController::get()->runInBackground(mSystem));
|
||||
#else
|
||||
returnValue = launchGameUnix(command, ViewController::get()->runInBackground(mSystem));
|
||||
#endif
|
||||
#endif
|
||||
// Notify the user in case of a failed game launch using a popup window.
|
||||
if (returnValue != 0) {
|
||||
LOG(LogWarning) << "...launch terminated with nonzero return value " << returnValue;
|
||||
|
||||
GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR LAUNCHING GAME '" +
|
||||
Utils::String::toUpper(metadata.get("name")) + "' (ERROR CODE " +
|
||||
Utils::String::toUpper(std::to_string(returnValue) + ")"), 6000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(
|
||||
window,
|
||||
"ERROR LAUNCHING GAME '" + Utils::String::toUpper(metadata.get("name")) +
|
||||
"' (ERROR CODE " + Utils::String::toUpper(std::to_string(returnValue) + ")"),
|
||||
6000);
|
||||
window->setInfoPopup(s);
|
||||
}
|
||||
else {
|
||||
// Stop showing the game launch notification.
|
||||
window->stopInfoPopup();
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
// 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
|
||||
// screensaver from getting activated.
|
||||
|
@ -1035,7 +1053,7 @@ void FileData::launchGame(Window* window)
|
|||
// Normalize deltaTime so that the screensaver does not start immediately
|
||||
// when returning from the game.
|
||||
window->normalizeNextUpdate();
|
||||
#else
|
||||
#else
|
||||
// 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.
|
||||
if (ViewController::get()->runInBackground(mSystem))
|
||||
|
@ -1043,7 +1061,7 @@ void FileData::launchGame(Window* window)
|
|||
// Normalize deltaTime so that the screensaver does not start immediately
|
||||
// when returning from the game.
|
||||
window->normalizeNextUpdate();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
// timestamp to the same time as the game that was just launched.
|
||||
if (gameToUpdate->getParent()->getType() == FOLDER && gameToUpdate->getParent()->getName() !=
|
||||
gameToUpdate->getSystem()->getFullName()) {
|
||||
if (gameToUpdate->getParent()->getType() == FOLDER &&
|
||||
gameToUpdate->getParent()->getName() != gameToUpdate->getSystem()->getFullName()) {
|
||||
gameToUpdate->getParent()->metadata.set("lastplayed",
|
||||
gameToUpdate->metadata.get("lastplayed"));
|
||||
gameToUpdate->metadata.get("lastplayed"));
|
||||
}
|
||||
|
||||
CollectionSystemsManager::get()->refreshCollectionSystems(gameToUpdate);
|
||||
|
@ -1086,9 +1104,9 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
|
||||
// Method 1, emulator binary is defined using find rules:
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
std::vector<std::string> emulatorWinRegistryPaths;
|
||||
#endif
|
||||
#endif
|
||||
std::vector<std::string> emulatorSystemPaths;
|
||||
std::vector<std::string> emulatorStaticPaths;
|
||||
std::string emulatorEntry;
|
||||
|
@ -1101,10 +1119,10 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
}
|
||||
|
||||
if (emulatorEntry != "") {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
emulatorWinRegistryPaths =
|
||||
SystemData::sFindRules.get()->mEmulators[emulatorEntry].winRegistryPaths;
|
||||
#endif
|
||||
SystemData::sFindRules.get()->mEmulators[emulatorEntry].winRegistryPaths;
|
||||
#endif
|
||||
emulatorSystemPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].systemPaths;
|
||||
emulatorStaticPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].staticPaths;
|
||||
}
|
||||
|
@ -1113,11 +1131,11 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
if (emulatorEntry != "" && emulatorSystemPaths.empty() && emulatorStaticPaths.empty())
|
||||
return "NO EMULATOR RULE: " + emulatorEntry;
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
for (std::string path : emulatorWinRegistryPaths) {
|
||||
// Search for the emulator using the App Paths keys in the Windows Registry.
|
||||
std::string registryKeyPath =
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path;
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path;
|
||||
|
||||
HKEY registryKey;
|
||||
LSTATUS keyStatus = -1;
|
||||
|
@ -1126,33 +1144,19 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
DWORD pathSize = 1024;
|
||||
|
||||
// First look in HKEY_CURRENT_USER.
|
||||
keyStatus = RegOpenKeyEx(
|
||||
HKEY_CURRENT_USER,
|
||||
registryKeyPath.c_str(),
|
||||
0,
|
||||
KEY_QUERY_VALUE,
|
||||
®istryKey);
|
||||
keyStatus = RegOpenKeyEx(HKEY_CURRENT_USER, registryKeyPath.c_str(), 0, KEY_QUERY_VALUE,
|
||||
®istryKey);
|
||||
|
||||
// If not found, then try in HKEY_LOCAL_MACHINE.
|
||||
if (keyStatus != ERROR_SUCCESS) {
|
||||
keyStatus = RegOpenKeyEx(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
registryKeyPath.c_str(),
|
||||
0,
|
||||
KEY_QUERY_VALUE,
|
||||
®istryKey);
|
||||
keyStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryKeyPath.c_str(), 0,
|
||||
KEY_QUERY_VALUE, ®istryKey);
|
||||
}
|
||||
|
||||
// If the key exists, then try to retrieve the value.
|
||||
if (keyStatus == ERROR_SUCCESS) {
|
||||
pathStatus = RegGetValue(
|
||||
registryKey,
|
||||
nullptr,
|
||||
nullptr,
|
||||
RRF_RT_REG_SZ,
|
||||
nullptr,
|
||||
®istryPath,
|
||||
&pathSize);
|
||||
pathStatus = RegGetValue(registryKey, nullptr, nullptr, RRF_RT_REG_SZ, nullptr,
|
||||
®istryPath, &pathSize);
|
||||
}
|
||||
else {
|
||||
RegCloseKey(registryKey);
|
||||
|
@ -1163,7 +1167,7 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
// so check for that as well.
|
||||
if (pathStatus == ERROR_SUCCESS) {
|
||||
if (Utils::FileSystem::isRegularFile(registryPath) ||
|
||||
Utils::FileSystem::isSymlink(registryPath)) {
|
||||
Utils::FileSystem::isSymlink(registryPath)) {
|
||||
command.replace(0, endPos + 1, registryPath);
|
||||
RegCloseKey(registryKey);
|
||||
return registryPath;
|
||||
|
@ -1171,25 +1175,24 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
}
|
||||
RegCloseKey(registryKey);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (std::string path : emulatorSystemPaths) {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
std::wstring pathWide = Utils::String::stringToWideString(path);
|
||||
// Search for the emulator using the PATH environmental variable.
|
||||
DWORD size = SearchPathW(nullptr, pathWide.c_str(), L".exe", 0, nullptr, nullptr);
|
||||
|
||||
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;
|
||||
|
||||
SearchPathW(nullptr, pathWide.c_str(), L".exe", size + 1 ,
|
||||
pathBuffer.data(), &fileName);
|
||||
SearchPathW(nullptr, pathWide.c_str(), L".exe", size + 1, pathBuffer.data(), &fileName);
|
||||
std::wstring pathString = pathBuffer.data();
|
||||
|
||||
if (pathString.length()) {
|
||||
exePath = Utils::String::wideStringToString(pathString.substr(0,
|
||||
pathString.size() - std::wstring(fileName).size()));
|
||||
exePath = Utils::String::wideStringToString(
|
||||
pathString.substr(0, pathString.size() - std::wstring(fileName).size()));
|
||||
exePath.pop_back();
|
||||
}
|
||||
}
|
||||
|
@ -1198,14 +1201,14 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
command.replace(0, endPos + 1, exePath);
|
||||
return exePath;
|
||||
}
|
||||
#else
|
||||
#else
|
||||
exePath = Utils::FileSystem::getPathToBinary(path);
|
||||
if (exePath != "") {
|
||||
exePath += "/" + path;
|
||||
command.replace(0, endPos + 1, exePath);
|
||||
return exePath;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
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());
|
||||
// Likewise for the %ROMPATH% variable which expands to the configured ROM directory.
|
||||
path = Utils::String::replace(path, "%ROMPATH%", getROMDirectory());
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
path = Utils::String::replace(path, "/", "\\");
|
||||
#endif
|
||||
if (Utils::FileSystem::isRegularFile(path) ||
|
||||
Utils::FileSystem::isSymlink(path)) {
|
||||
#endif
|
||||
if (Utils::FileSystem::isRegularFile(path) || Utils::FileSystem::isSymlink(path)) {
|
||||
command.replace(0, endPos + 1, 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.
|
||||
command = Utils::String::replace(command, "%ESPATH%", Utils::FileSystem::getExePath());
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
command = Utils::String::replace(command, "/", "\\");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 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.
|
||||
|
@ -1242,23 +1244,23 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
emuExecutable = command.substr(0, command.find(' '));
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
std::wstring emuExecutableWide = Utils::String::stringToWideString(emuExecutable);
|
||||
// Search for the emulator using the PATH environmental variable.
|
||||
DWORD size = SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr);
|
||||
|
||||
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;
|
||||
|
||||
SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1 ,
|
||||
pathBuffer.data(), &fileName);
|
||||
SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1, pathBuffer.data(),
|
||||
&fileName);
|
||||
|
||||
exePath = Utils::String::wideStringToString(pathBuffer.data());
|
||||
}
|
||||
#else
|
||||
#else
|
||||
if (Utils::FileSystem::isRegularFile(emuExecutable) ||
|
||||
Utils::FileSystem::isSymlink(emuExecutable)) {
|
||||
Utils::FileSystem::isSymlink(emuExecutable)) {
|
||||
exePath = emuExecutable;
|
||||
}
|
||||
else {
|
||||
|
@ -1266,14 +1268,16 @@ std::string FileData::findEmulatorPath(std::string& command)
|
|||
if (exePath != "")
|
||||
exePath += "/" + emuExecutable;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return exePath;
|
||||
}
|
||||
|
||||
CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
|
||||
: FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(),
|
||||
file->getSourceFileData()->getSystemEnvData(), system)
|
||||
: FileData(file->getSourceFileData()->getType(),
|
||||
file->getSourceFileData()->getPath(),
|
||||
file->getSourceFileData()->getSystemEnvData(),
|
||||
system)
|
||||
{
|
||||
// We use this constructor to create a clone of the filedata, and change its system.
|
||||
mSourceFileData = file->getSourceFileData();
|
||||
|
@ -1291,15 +1295,6 @@ CollectionFileData::~CollectionFileData()
|
|||
mParent = nullptr;
|
||||
}
|
||||
|
||||
std::string CollectionFileData::getKey() {
|
||||
return getFullPath();
|
||||
}
|
||||
|
||||
FileData* CollectionFileData::getSourceFileData()
|
||||
{
|
||||
return mSourceFileData;
|
||||
}
|
||||
|
||||
void CollectionFileData::refreshMetadata()
|
||||
{
|
||||
metadata = mSourceFileData->metadata;
|
||||
|
@ -1310,9 +1305,9 @@ const std::string& CollectionFileData::getName()
|
|||
{
|
||||
if (mDirty) {
|
||||
mCollectionFileName =
|
||||
Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
|
||||
Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
|
||||
mCollectionFileName +=
|
||||
" [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
|
||||
" [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
#ifndef ES_APP_FILE_DATA_H
|
||||
#define ES_APP_FILE_DATA_H
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "MetaData.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -30,8 +30,10 @@ enum FileType {
|
|||
class FileData
|
||||
{
|
||||
public:
|
||||
FileData(FileType type, const std::string& path,
|
||||
SystemEnvironmentData* envData, SystemData* system);
|
||||
FileData(FileType type,
|
||||
const std::string& path,
|
||||
SystemEnvironmentData* envData,
|
||||
SystemData* system);
|
||||
|
||||
virtual ~FileData();
|
||||
|
||||
|
@ -41,17 +43,19 @@ public:
|
|||
const bool getKidgame();
|
||||
const bool getHidden();
|
||||
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 std::vector<FileData*> getChildrenRecursive() const;
|
||||
inline FileType getType() const { return mType; }
|
||||
inline const std::string& getPath() const { return mPath; }
|
||||
inline FileData* getParent() const { return mParent; }
|
||||
inline const std::unordered_map<std::string, FileData*>& getChildrenByFilename() const
|
||||
{ return mChildrenByFilename; }
|
||||
inline const std::vector<FileData*>& getChildren() const { return mChildren; }
|
||||
inline SystemData* getSystem() const { return mSystem; }
|
||||
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
FileType getType() const { return mType; }
|
||||
const std::string& getPath() const { return mPath; }
|
||||
FileData* getParent() const { return mParent; }
|
||||
const std::unordered_map<std::string, FileData*>& getChildrenByFilename() const
|
||||
{
|
||||
return mChildrenByFilename;
|
||||
}
|
||||
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 getHasFoldersFlag() { return mHasFolders; }
|
||||
static const std::string getROMDirectory();
|
||||
|
@ -66,29 +70,31 @@ public:
|
|||
const std::string getThumbnailPath() const;
|
||||
const std::string getVideoPath() const;
|
||||
|
||||
bool getDeletionFlag() { return mDeletionFlag; };
|
||||
void setDeletionFlag(bool setting) { mDeletionFlag = setting; };
|
||||
bool getDeletionFlag() { return mDeletionFlag; }
|
||||
void setDeletionFlag(bool setting) { mDeletionFlag = setting; }
|
||||
|
||||
const std::vector<FileData*>& getChildrenListToDisplay();
|
||||
std::vector<FileData*> getFilesRecursive(unsigned int typeMask,
|
||||
bool displayedOnly = false, bool countAllGames = true) const;
|
||||
std::vector<FileData*> getScrapeFilesRecursive(bool includeFolders, bool excludeRecursively,
|
||||
bool respectExclusions) const;
|
||||
bool displayedOnly = false,
|
||||
bool countAllGames = true) const;
|
||||
std::vector<FileData*> getScrapeFilesRecursive(bool includeFolders,
|
||||
bool excludeRecursively,
|
||||
bool respectExclusions) const;
|
||||
|
||||
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();
|
||||
const bool isArcadeAsset();
|
||||
const bool isArcadeGame();
|
||||
inline std::string getFullPath() { return getPath(); };
|
||||
inline std::string getFileName() { return Utils::FileSystem::getFileName(getPath()); };
|
||||
std::string getFullPath() { return getPath(); }
|
||||
std::string getFileName() { return Utils::FileSystem::getFileName(getPath()); }
|
||||
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.
|
||||
std::string getDisplayName() const;
|
||||
|
@ -104,19 +110,22 @@ public:
|
|||
ComparisonFunction* comparisonFunction;
|
||||
std::string description;
|
||||
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 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);
|
||||
MetaDataList metadata;
|
||||
// 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);
|
||||
|
||||
inline void setSortTypeString(std::string typestring) { mSortTypeString = typestring; }
|
||||
inline std::string getSortTypeString() { return mSortTypeString; }
|
||||
void setSortTypeString(std::string typestring) { mSortTypeString = typestring; }
|
||||
std::string getSortTypeString() { return mSortTypeString; }
|
||||
FileData::SortType getSortTypeFromString(std::string desc);
|
||||
|
||||
protected:
|
||||
|
@ -130,7 +139,7 @@ private:
|
|||
std::string mPath;
|
||||
SystemEnvironmentData* mEnvData;
|
||||
SystemData* mSystem;
|
||||
std::unordered_map<std::string,FileData*> mChildrenByFilename;
|
||||
std::unordered_map<std::string, FileData*> mChildrenByFilename;
|
||||
std::vector<FileData*> mChildren;
|
||||
std::vector<FileData*> mFilteredChildren;
|
||||
// The pair includes all games, and favorite games.
|
||||
|
@ -148,8 +157,8 @@ public:
|
|||
~CollectionFileData();
|
||||
const std::string& getName();
|
||||
void refreshMetadata();
|
||||
FileData* getSourceFileData();
|
||||
std::string getKey();
|
||||
FileData* getSourceFileData() { return mSourceFileData; }
|
||||
std::string getKey() { return getFullPath(); }
|
||||
|
||||
private:
|
||||
// Needs to be updated when metadata changes.
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
#include "FileFilterIndex.h"
|
||||
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "FileData.h"
|
||||
#include "Log.h"
|
||||
#include "Settings.h"
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "views/UIModeController.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
@ -21,19 +21,20 @@
|
|||
#define INCLUDE_UNKNOWN false;
|
||||
|
||||
FileFilterIndex::FileFilterIndex()
|
||||
: mFilterByText(false),
|
||||
mFilterByFavorites(false),
|
||||
mFilterByGenre(false),
|
||||
mFilterByPlayers(false),
|
||||
mFilterByPubDev(false),
|
||||
mFilterByRatings(false),
|
||||
mFilterByKidGame(false),
|
||||
mFilterByCompleted(false),
|
||||
mFilterByBroken(false),
|
||||
mFilterByHidden(false)
|
||||
: mFilterByText(false)
|
||||
, mFilterByFavorites(false)
|
||||
, mFilterByGenre(false)
|
||||
, mFilterByPlayers(false)
|
||||
, mFilterByPubDev(false)
|
||||
, mFilterByRatings(false)
|
||||
, mFilterByKidGame(false)
|
||||
, mFilterByCompleted(false)
|
||||
, mFilterByBroken(false)
|
||||
, mFilterByHidden(false)
|
||||
{
|
||||
clearAllFilters();
|
||||
|
||||
// clang-format off
|
||||
FilterDataDecl filterDecls[] = {
|
||||
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
|
||||
{ FAVORITES_FILTER, &mFavoritesIndexAllKeys, &mFilterByFavorites, &mFavoritesIndexFilteredKeys, "favorite", false, "", "FAVORITES" },
|
||||
|
@ -46,21 +47,18 @@ FileFilterIndex::FileFilterIndex()
|
|||
{ BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN" },
|
||||
{ HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" }
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls +
|
||||
sizeof(filterDecls) / sizeof(filterDecls[0]));
|
||||
filterDataDecl = std::vector<FilterDataDecl>(
|
||||
filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0]));
|
||||
}
|
||||
|
||||
FileFilterIndex::~FileFilterIndex()
|
||||
{
|
||||
// Reset the index when destroyed.
|
||||
resetIndex();
|
||||
}
|
||||
|
||||
std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
|
||||
{
|
||||
return filterDataDecl;
|
||||
}
|
||||
|
||||
void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
|
||||
{
|
||||
struct IndexImportStructure {
|
||||
|
@ -80,22 +78,23 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
|
|||
{ &mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys) },
|
||||
};
|
||||
|
||||
std::vector<IndexImportStructure> indexImportDecl =
|
||||
std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls +
|
||||
sizeof(indexStructDecls) / sizeof(indexStructDecls[0]));
|
||||
std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(
|
||||
indexStructDecls,
|
||||
indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0]));
|
||||
|
||||
for (std::vector<IndexImportStructure>::const_iterator indexesIt =
|
||||
indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); indexesIt++)
|
||||
{
|
||||
for (std::vector<IndexImportStructure>::const_iterator indexesIt = indexImportDecl.cbegin();
|
||||
indexesIt != indexImportDecl.cend(); indexesIt++) {
|
||||
for (std::map<std::string, int>::const_iterator sourceIt =
|
||||
(*indexesIt).sourceIndex->cbegin(); sourceIt !=
|
||||
(*indexesIt).sourceIndex->cend(); sourceIt++) {
|
||||
(*indexesIt).sourceIndex->cbegin();
|
||||
sourceIt != (*indexesIt).sourceIndex->cend(); sourceIt++) {
|
||||
if ((*indexesIt).destinationIndex->find((*sourceIt).first) ==
|
||||
(*indexesIt).destinationIndex->cend())
|
||||
(*indexesIt).destinationIndex->cend()) {
|
||||
// Entry doesn't exist.
|
||||
(*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second;
|
||||
else
|
||||
}
|
||||
else {
|
||||
(*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +114,8 @@ void FileFilterIndex::resetIndex()
|
|||
}
|
||||
|
||||
std::string FileFilterIndex::getIndexableKey(FileData* game,
|
||||
FilterIndexType type, bool getSecondary)
|
||||
FilterIndexType type,
|
||||
bool getSecondary)
|
||||
{
|
||||
std::string key = "";
|
||||
switch (type) {
|
||||
|
@ -166,8 +166,8 @@ std::string FileFilterIndex::getIndexableKey(FileData* game,
|
|||
// These values should only exist if a third party application has
|
||||
// been used for scraping the ratings, or if the gamelist.xml file
|
||||
// has been manually edited.
|
||||
ratingNumber = static_cast<int>(
|
||||
(ceilf(stof(ratingString) / 0.1f) / 10) * 5);
|
||||
ratingNumber =
|
||||
static_cast<int>((ceilf(stof(ratingString) / 0.1f) / 10.0f) * 5.0f);
|
||||
|
||||
if (ratingNumber < 0)
|
||||
ratingNumber = 0;
|
||||
|
@ -176,11 +176,11 @@ std::string FileFilterIndex::getIndexableKey(FileData* game,
|
|||
key = "5 STARS";
|
||||
else
|
||||
key = std::to_string(ratingNumber) + " - " +
|
||||
std::to_string(ratingNumber) + ".5 STARS";
|
||||
std::to_string(ratingNumber) + ".5 STARS";
|
||||
}
|
||||
catch (int e) {
|
||||
LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " <<
|
||||
ratingString << ", " << e;
|
||||
LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): "
|
||||
<< ratingString << ", " << e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,13 +254,13 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>*
|
|||
}
|
||||
else {
|
||||
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
|
||||
it != filterDataDecl.cend(); it++) {
|
||||
it != filterDataDecl.cend(); it++) {
|
||||
if ((*it).type == type) {
|
||||
FilterDataDecl filterData = (*it);
|
||||
*(filterData.filteredByRef) = values->size() > 0;
|
||||
filterData.currentFilteredKeys->clear();
|
||||
for (std::vector<std::string>::const_iterator vit =
|
||||
values->cbegin(); vit != values->cend(); vit++) {
|
||||
for (std::vector<std::string>::const_iterator vit = values->cbegin();
|
||||
vit != values->cend(); vit++) {
|
||||
// Check if it exists.
|
||||
if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) {
|
||||
filterData.currentFilteredKeys->push_back(std::string(*vit));
|
||||
|
@ -272,20 +272,20 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>*
|
|||
return;
|
||||
}
|
||||
|
||||
void FileFilterIndex::setTextFilter(std::string textFilter)
|
||||
{
|
||||
void FileFilterIndex::setTextFilter(std::string textFilter)
|
||||
{
|
||||
mTextFilter = textFilter;
|
||||
|
||||
if (textFilter == "")
|
||||
mFilterByText = false;
|
||||
else
|
||||
mFilterByText = true;
|
||||
};
|
||||
};
|
||||
|
||||
void FileFilterIndex::clearAllFilters()
|
||||
{
|
||||
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
|
||||
it != filterDataDecl.cend(); it++) {
|
||||
it != filterDataDecl.cend(); it++) {
|
||||
FilterDataDecl filterData = (*it);
|
||||
*(filterData.filteredByRef) = false;
|
||||
filterData.currentFilteredKeys->clear();
|
||||
|
@ -312,19 +312,19 @@ void FileFilterIndex::setKidModeFilters()
|
|||
void FileFilterIndex::debugPrintIndexes()
|
||||
{
|
||||
LOG(LogInfo) << "Printing Indexes...";
|
||||
for (auto x: mFavoritesIndexAllKeys) {
|
||||
for (auto x : mFavoritesIndexAllKeys) {
|
||||
LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: mGenreIndexAllKeys) {
|
||||
for (auto x : mGenreIndexAllKeys) {
|
||||
LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: mPlayersIndexAllKeys) {
|
||||
for (auto x : mPlayersIndexAllKeys) {
|
||||
LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: mPubDevIndexAllKeys) {
|
||||
for (auto x : mPubDevIndexAllKeys) {
|
||||
LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: mRatingsIndexAllKeys) {
|
||||
for (auto x : mRatingsIndexAllKeys) {
|
||||
LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x : mKidGameIndexAllKeys) {
|
||||
|
@ -348,8 +348,8 @@ bool FileFilterIndex::showFile(FileData* game)
|
|||
if (game->getType() == FOLDER) {
|
||||
std::vector<FileData*> children = game->getChildren();
|
||||
// Iterate through all of the children, until there's a match.
|
||||
for (std::vector<FileData*>::const_iterator it = children.cbegin();
|
||||
it != children.cend(); it++) {
|
||||
for (std::vector<FileData*>::const_iterator it = children.cbegin(); it != children.cend();
|
||||
it++) {
|
||||
if (showFile(*it))
|
||||
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
|
||||
// the game name, then always return false.
|
||||
if (mTextFilter != "" && !(Utils::String::toUpper(game->
|
||||
getName()).find(mTextFilter) != std::string::npos)) {
|
||||
if (mTextFilter != "" &&
|
||||
!(Utils::String::toUpper(game->getName()).find(mTextFilter) != std::string::npos)) {
|
||||
return false;
|
||||
}
|
||||
else if (mTextFilter != "") {
|
||||
|
@ -370,7 +370,7 @@ bool FileFilterIndex::showFile(FileData* game)
|
|||
}
|
||||
|
||||
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
|
||||
it != filterDataDecl.cend(); it++) {
|
||||
it != filterDataDecl.cend(); it++) {
|
||||
FilterDataDecl filterData = (*it);
|
||||
if (filterData.primaryKey == "kidgame" && UIModeController::getInstance()->isUIModeKid()) {
|
||||
return (getIndexableKey(game, filterData.type, false) != "FALSE");
|
||||
|
@ -419,18 +419,19 @@ bool FileFilterIndex::isFiltered()
|
|||
|
||||
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type)
|
||||
{
|
||||
const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER,
|
||||
PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER,
|
||||
COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER };
|
||||
std::vector<std::string> filterKeysList[9] = { mFavoritesIndexFilteredKeys,
|
||||
mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys, mPubDevIndexFilteredKeys,
|
||||
mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys, mCompletedIndexFilteredKeys,
|
||||
mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys };
|
||||
const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER,
|
||||
PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER,
|
||||
COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER };
|
||||
std::vector<std::string> filterKeysList[9] = {
|
||||
mFavoritesIndexFilteredKeys, mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys,
|
||||
mPubDevIndexFilteredKeys, mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys,
|
||||
mCompletedIndexFilteredKeys, mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys
|
||||
};
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
if (filterTypes[i] == type) {
|
||||
for (std::vector<std::string>::const_iterator it = filterKeysList[i].cbegin();
|
||||
it != filterKeysList[i].cend(); it++) {
|
||||
it != filterKeysList[i].cend(); it++) {
|
||||
if (key == (*it))
|
||||
return true;
|
||||
}
|
||||
|
@ -589,7 +590,8 @@ void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove)
|
|||
}
|
||||
|
||||
void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
|
||||
std::string key, bool remove)
|
||||
std::string key,
|
||||
bool remove)
|
||||
{
|
||||
bool includeUnknown = INCLUDE_UNKNOWN;
|
||||
if (!includeUnknown && key == UNKNOWN_LABEL)
|
||||
|
@ -600,7 +602,7 @@ void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
|
|||
if (index->find(key) == index->cend()) {
|
||||
// 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.
|
||||
// LOG(LogDebug) << "Couldn't find entry in index! " << key;
|
||||
// LOG(LogDebug) << "Couldn't find entry in index! " << key;
|
||||
}
|
||||
else {
|
||||
(index->at(key))--;
|
||||
|
@ -617,8 +619,3 @@ void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
|
|||
(index->at(key))++;
|
||||
}
|
||||
}
|
||||
|
||||
void FileFilterIndex::clearIndex(std::map<std::string, int>& indexMap)
|
||||
{
|
||||
indexMap.clear();
|
||||
}
|
||||
|
|
|
@ -52,13 +52,13 @@ public:
|
|||
void removeFromIndex(FileData* game);
|
||||
void setFilter(FilterIndexType type, std::vector<std::string>* values);
|
||||
void setTextFilter(std::string textFilter);
|
||||
std::string getTextFilter() { return mTextFilter; };
|
||||
std::string getTextFilter() { return mTextFilter; }
|
||||
void clearAllFilters();
|
||||
void debugPrintIndexes();
|
||||
bool showFile(FileData* game);
|
||||
bool isFiltered();
|
||||
bool isKeyBeingFilteredBy(std::string key, FilterIndexType type);
|
||||
std::vector<FilterDataDecl>& getFilterDataDecls();
|
||||
std::vector<FilterDataDecl>& getFilterDataDecls() { return filterDataDecl; }
|
||||
|
||||
void importIndex(FileFilterIndex* indexToImport);
|
||||
void resetIndex();
|
||||
|
@ -81,7 +81,7 @@ private:
|
|||
|
||||
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;
|
||||
bool mFilterByText;
|
||||
|
@ -117,7 +117,6 @@ private:
|
|||
std::vector<std::string> mHiddenIndexFilteredKeys;
|
||||
|
||||
FileData* mRootFolder;
|
||||
|
||||
};
|
||||
|
||||
#endif // ES_APP_FILE_FILTER_INDEX_H
|
||||
|
|
|
@ -48,8 +48,9 @@ namespace FileSorts
|
|||
FileData::SortType(&compareSystemDescending, "system, descending")
|
||||
};
|
||||
|
||||
const std::vector<FileData::SortType> SortTypes(typesArr, typesArr +
|
||||
sizeof(typesArr)/sizeof(typesArr[0]));
|
||||
const std::vector<FileData::SortType> SortTypes(typesArr,
|
||||
typesArr +
|
||||
sizeof(typesArr) / sizeof(typesArr[0]));
|
||||
|
||||
bool compareName(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
|
@ -155,11 +156,13 @@ namespace FileSorts
|
|||
file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1);
|
||||
// Any non-numeric value will end up as zero.
|
||||
if (!file1Players.empty() &&
|
||||
std::all_of(file1Players.begin(), file1Players.end(), ::isdigit))
|
||||
std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) {
|
||||
file1Int = stoi(file1Players);
|
||||
}
|
||||
if (!file2Players.empty() &&
|
||||
std::all_of(file2Players.begin(), file2Players.end(), ::isdigit))
|
||||
std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) {
|
||||
file2Int = stoi(file2Players);
|
||||
}
|
||||
return file1Int < file2Int;
|
||||
}
|
||||
|
||||
|
@ -177,11 +180,13 @@ namespace FileSorts
|
|||
if (dashPos != std::string::npos)
|
||||
file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1);
|
||||
if (!file1Players.empty() &&
|
||||
std::all_of(file1Players.begin(), file1Players.end(), ::isdigit))
|
||||
std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) {
|
||||
file1Int = stoi(file1Players);
|
||||
}
|
||||
if (!file2Players.empty() &&
|
||||
std::all_of(file2Players.begin(), file2Players.end(), ::isdigit))
|
||||
std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) {
|
||||
file2Int = stoi(file2Players);
|
||||
}
|
||||
return file1Int > file2Int;
|
||||
}
|
||||
|
||||
|
@ -201,16 +206,18 @@ namespace FileSorts
|
|||
{
|
||||
// Only games have playcount 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 false;
|
||||
}
|
||||
|
||||
bool compareTimesPlayedDescending(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
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 false;
|
||||
}
|
||||
|
||||
|
@ -227,4 +234,5 @@ namespace FileSorts
|
|||
std::string system2 = Utils::String::toUpper(file2->getSystemName());
|
||||
return system1.compare(system2) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace FileSorts
|
||||
|
|
|
@ -38,6 +38,6 @@ namespace FileSorts
|
|||
bool compareSystemDescending(const FileData* file1, const FileData* file2);
|
||||
|
||||
extern const std::vector<FileData::SortType> SortTypes;
|
||||
};
|
||||
}; // namespace FileSorts
|
||||
|
||||
#endif // ES_APP_FILE_SORTS_H
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
#include "Gamelist.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "Log.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "utils/StringUtil.h"
|
||||
|
||||
#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);
|
||||
|
||||
if (!contains) {
|
||||
LOG(LogError) << "Path \"" << path << "\" is outside system path \"" <<
|
||||
system->getStartPath() << "\"";
|
||||
LOG(LogError) << "Path \"" << path << "\" is outside system path \""
|
||||
<< system->getStartPath() << "\"";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
|
|||
bool found = false;
|
||||
while (path_it != pathList.end()) {
|
||||
const std::unordered_map<std::string, FileData*>& children =
|
||||
treeNode->getChildrenByFilename();
|
||||
treeNode->getChildrenByFilename();
|
||||
|
||||
std::string key = *path_it;
|
||||
found = children.find(key) != children.cend();
|
||||
|
@ -71,8 +71,9 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
|
|||
}
|
||||
|
||||
// Create missing folder.
|
||||
FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath())
|
||||
+ "/" + *path_it, system->getSystemEnvData(), system);
|
||||
FileData* folder = new FileData(
|
||||
FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it,
|
||||
system->getSystemEnvData(), system);
|
||||
treeNode->addChild(folder);
|
||||
treeNode = folder;
|
||||
}
|
||||
|
@ -89,24 +90,24 @@ void parseGamelist(SystemData* system)
|
|||
std::string xmlpath = system->getGamelistPath(false);
|
||||
|
||||
if (!Utils::FileSystem::exists(xmlpath)) {
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName() <<
|
||||
"\" does not have a gamelist.xml file";
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName()
|
||||
<< "\" does not have a gamelist.xml file";
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LogInfo) << "Parsing gamelist file \"" << xmlpath << "\"...";
|
||||
|
||||
pugi::xml_document doc;
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
pugi::xml_parse_result result =
|
||||
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
||||
#else
|
||||
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
||||
#else
|
||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!result) {
|
||||
LOG(LogError) << "Error parsing gamelist file \"" << xmlpath <<
|
||||
"\": " << result.description();
|
||||
LOG(LogError) << "Error parsing gamelist file \"" << xmlpath
|
||||
<< "\": " << result.description();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -124,24 +125,24 @@ void parseGamelist(SystemData* system)
|
|||
for (int i = 0; i < 2; i++) {
|
||||
std::string tag = tagList[i];
|
||||
FileType type = typeList[i];
|
||||
for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode; fileNode =
|
||||
fileNode.next_sibling(tag.c_str())) {
|
||||
const std::string path =
|
||||
Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(),
|
||||
relativeTo, false);
|
||||
for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode;
|
||||
fileNode = fileNode.next_sibling(tag.c_str())) {
|
||||
const std::string path = Utils::FileSystem::resolveRelativePath(
|
||||
fileNode.child("path").text().get(), relativeTo, false);
|
||||
|
||||
if (!trustGamelist && !Utils::FileSystem::exists(path)) {
|
||||
LOG(LogWarning) << (type == GAME ? "File \"" : "Folder \"") << path <<
|
||||
"\" does not exist, ignoring entry";
|
||||
LOG(LogWarning) << (type == GAME ? "File \"" : "Folder \"") << path
|
||||
<< "\" does not exist, ignoring entry";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip hidden files, check both the file itself and the directory in which
|
||||
// it is located.
|
||||
if (!showHiddenFiles && (Utils::FileSystem::isHidden(path) ||
|
||||
Utils::FileSystem::isHidden(Utils::FileSystem::getParent(path)))) {
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden file \"" <<
|
||||
path << "\"";
|
||||
if (!showHiddenFiles &&
|
||||
(Utils::FileSystem::isHidden(path) ||
|
||||
Utils::FileSystem::isHidden(Utils::FileSystem::getParent(path)))) {
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden file \"" << path
|
||||
<< "\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -163,8 +164,8 @@ void parseGamelist(SystemData* system)
|
|||
else {
|
||||
// Skip arcade asset entries as these will not be used in any way inside
|
||||
// the application.
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping arcade asset \"" <<
|
||||
file->getName() << "\"";
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping arcade asset \""
|
||||
<< file->getName() << "\"";
|
||||
delete file;
|
||||
continue;
|
||||
}
|
||||
|
@ -174,9 +175,10 @@ void parseGamelist(SystemData* system)
|
|||
// application restart.
|
||||
if (!Settings::getInstance()->getBool("ShowHiddenGames")) {
|
||||
if (file->getHidden()) {
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden " <<
|
||||
(type == GAME ? "file" : "folder") << " entry \"" <<
|
||||
file->getName() << "\"" << " (\"" << file->getPath() << "\")";
|
||||
LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden "
|
||||
<< (type == GAME ? "file" : "folder") << " entry \""
|
||||
<< file->getName() << "\""
|
||||
<< " (\"" << file->getPath() << "\")";
|
||||
delete file;
|
||||
}
|
||||
// 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,
|
||||
const std::string& tag, SystemData* system)
|
||||
void addFileDataNode(pugi::xml_node& parent,
|
||||
const FileData* file,
|
||||
const std::string& tag,
|
||||
SystemData* system)
|
||||
{
|
||||
// Create game and add to parent node.
|
||||
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.
|
||||
if (newNode.children().begin() == newNode.child("name") &&
|
||||
++newNode.children().begin() == newNode.children().end() &&
|
||||
newNode.child("name").text().get() == file->getDisplayName()) {
|
||||
++newNode.children().begin() == newNode.children().end() &&
|
||||
newNode.child("name").text().get() == file->getDisplayName()) {
|
||||
|
||||
// If the only info is the default name, don't bother
|
||||
// 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
|
||||
// work if we change the ROM folder location in the future.
|
||||
newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file->
|
||||
getPath(), system->getStartPath(), false).c_str());
|
||||
newNode.prepend_child("path").text().set(
|
||||
Utils::FileSystem::createRelativePath(file->getPath(), system->getStartPath(), false)
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,23 +237,23 @@ void updateGamelist(SystemData* system)
|
|||
|
||||
if (Utils::FileSystem::exists(xmlReadPath)) {
|
||||
// Parse an existing file first.
|
||||
#if defined(_WIN64)
|
||||
|
||||
#if defined(_WIN64)
|
||||
pugi::xml_parse_result result =
|
||||
doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str());
|
||||
#else
|
||||
doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str());
|
||||
#else
|
||||
pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!result) {
|
||||
LOG(LogError) << "Error parsing gamelist file \"" << xmlReadPath << "\": " <<
|
||||
result.description();
|
||||
LOG(LogError) << "Error parsing gamelist file \"" << xmlReadPath
|
||||
<< "\": " << result.description();
|
||||
return;
|
||||
}
|
||||
|
||||
root = doc.child("gameList");
|
||||
if (!root) {
|
||||
LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" <<
|
||||
xmlReadPath << "\"";
|
||||
LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" << xmlReadPath << "\"";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -266,8 +271,8 @@ void updateGamelist(SystemData* system)
|
|||
// Get only files, no folders.
|
||||
std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER);
|
||||
// Iterate through all files, checking if they're already in the XML file.
|
||||
for (std::vector<FileData*>::const_iterator fit = files.cbegin();
|
||||
fit != files.cend(); fit++) {
|
||||
for (std::vector<FileData*>::const_iterator fit = files.cbegin(); // Line break.
|
||||
fit != files.cend(); fit++) {
|
||||
const std::string tag = ((*fit)->getType() == GAME) ? "game" : "folder";
|
||||
|
||||
// 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.
|
||||
// If it does, remove the entry before adding it back.
|
||||
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");
|
||||
if (!pathNode) {
|
||||
LOG(LogError) << "<" << tag << "> node contains no <path> child";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string nodePath = Utils::FileSystem::getCanonicalPath(
|
||||
Utils::FileSystem::resolveRelativePath(pathNode.text().get(),
|
||||
system->getStartPath(), true));
|
||||
std::string nodePath =
|
||||
Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath(
|
||||
pathNode.text().get(), system->getStartPath(), true));
|
||||
std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath());
|
||||
|
||||
if (nodePath == gamePath) {
|
||||
|
@ -312,16 +317,17 @@ void updateGamelist(SystemData* system)
|
|||
std::string xmlWritePath(system->getGamelistPath(true));
|
||||
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
|
||||
|
||||
LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated <<
|
||||
(numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath << "\"";
|
||||
LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated
|
||||
<< (numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath
|
||||
<< "\"";
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
if (!doc.save_file(Utils::String::stringToWideString(xmlWritePath).c_str())) {
|
||||
#else
|
||||
#else
|
||||
if (!doc.save_file(xmlWritePath.c_str())) {
|
||||
#endif
|
||||
LOG(LogError) << "Error saving gamelist.xml to \"" <<
|
||||
xmlWritePath << "\" (for system " << system->getName() << ")";
|
||||
#endif
|
||||
LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath
|
||||
<< "\" (for system " << system->getName() << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
#if defined(BUILD_VLC_PLAYER)
|
||||
#include "components/VideoVlcComponent.h"
|
||||
#endif
|
||||
#include "views/ViewController.h"
|
||||
#include "AudioManager.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);
|
||||
}
|
||||
|
@ -85,12 +88,12 @@ void MediaViewer::render()
|
|||
|
||||
// Render a black background below the game media.
|
||||
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) {
|
||||
mVideo->render(transform);
|
||||
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
Renderer::shaderParameters videoParameters;
|
||||
unsigned int shaders = 0;
|
||||
if (Settings::getInstance()->getBool("MediaViewerVideoScanlines"))
|
||||
|
@ -98,6 +101,7 @@ void MediaViewer::render()
|
|||
if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
|
||||
shaders |= Renderer::SHADER_BLUR_HORIZONTAL;
|
||||
float heightModifier = Renderer::getScreenHeightModifier();
|
||||
// clang-format off
|
||||
if (heightModifier < 1)
|
||||
videoParameters.blurPasses = 2; // Below 1080
|
||||
else if (heightModifier >= 4)
|
||||
|
@ -112,18 +116,19 @@ void MediaViewer::render()
|
|||
videoParameters.blurPasses = 3; // 1440
|
||||
else if (heightModifier >= 1)
|
||||
videoParameters.blurPasses = 2; // 1080
|
||||
// clang-format on
|
||||
}
|
||||
Renderer::shaderPostprocessing(shaders, videoParameters);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else if (mImage && mImage->hasImage() && mImage->getSize() != 0) {
|
||||
mImage->render(transform);
|
||||
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
if (mCurrentImageIndex == mScreenShotIndex &&
|
||||
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"))
|
||||
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"))
|
||||
Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// This is necessary so that the video loops if viewing an image when
|
||||
// the video ends.
|
||||
|
@ -244,14 +249,14 @@ void MediaViewer::playVideo()
|
|||
mDisplayingImage = false;
|
||||
ViewController::get()->onStopVideo();
|
||||
|
||||
#if defined(BUILD_VLC_PLAYER)
|
||||
#if defined(BUILD_VLC_PLAYER)
|
||||
if (Settings::getInstance()->getString("VideoPlayer") == "ffmpeg")
|
||||
mVideo = new VideoFFmpegComponent(mWindow);
|
||||
else
|
||||
mVideo = new VideoVlcComponent(mWindow);
|
||||
#else
|
||||
#else
|
||||
mVideo = new VideoFFmpegComponent(mWindow);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mVideo->topWindow(true);
|
||||
mVideo->setOrigin(0.5f, 0.5f);
|
||||
|
@ -259,10 +264,10 @@ void MediaViewer::playVideo()
|
|||
|
||||
if (Settings::getInstance()->getBool("MediaViewerStretchVideos"))
|
||||
mVideo->setResize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
else
|
||||
mVideo->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
|
||||
mVideo->setVideo(mVideoFile);
|
||||
mVideo->setMediaViewerMode(true);
|
||||
|
@ -282,6 +287,6 @@ void MediaViewer::showImage(int index)
|
|||
mImage->setOrigin(0.5f, 0.5f);
|
||||
mImage->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f);
|
||||
mImage->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
#ifndef ES_APP_MEDIA_VIEWER_H
|
||||
#define ES_APP_MEDIA_VIEWER_H
|
||||
|
||||
#include "components/ImageComponent.h"
|
||||
#include "components/VideoComponent.h"
|
||||
#include "FileData.h"
|
||||
#include "Window.h"
|
||||
#include "components/ImageComponent.h"
|
||||
#include "components/VideoComponent.h"
|
||||
|
||||
class MediaViewer : public Window::MediaViewer
|
||||
{
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
|
||||
#include "MetaData.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "Log.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
// clang-format off
|
||||
MetaDataDecl gameDecls[] = {
|
||||
// key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd, shouldScrape
|
||||
{"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}
|
||||
};
|
||||
|
||||
const std::vector<MetaDataDecl> gameMDD(gameDecls, gameDecls +
|
||||
sizeof(gameDecls) / sizeof(gameDecls[0]));
|
||||
|
||||
MetaDataDecl folderDecls[] = {
|
||||
{"name", MD_STRING, "", false, "name", "enter name", 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},
|
||||
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
const std::vector<MetaDataDecl> folderMDD(folderDecls, folderDecls +
|
||||
sizeof(folderDecls) / sizeof(folderDecls[0]));
|
||||
const std::vector<MetaDataDecl> gameMDD(gameDecls,
|
||||
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)
|
||||
{
|
||||
switch(type) {
|
||||
switch (type) {
|
||||
case GAME_METADATA:
|
||||
return gameMDD;
|
||||
case FOLDER_METADATA:
|
||||
|
@ -77,7 +80,8 @@ const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
|
|||
}
|
||||
|
||||
MetaDataList::MetaDataList(MetaDataListType type)
|
||||
: mType(type), mWasChanged(false)
|
||||
: mType(type)
|
||||
, mWasChanged(false)
|
||||
{
|
||||
const std::vector<MetaDataDecl>& mdd = getMDD();
|
||||
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++)
|
||||
|
@ -85,7 +89,8 @@ MetaDataList::MetaDataList(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);
|
||||
|
||||
|
@ -107,8 +112,9 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type,
|
|||
return mdl;
|
||||
}
|
||||
|
||||
void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults,
|
||||
const std::string& relativeTo) const
|
||||
void MetaDataList::appendToXML(pugi::xml_node& parent,
|
||||
bool ignoreDefaults,
|
||||
const std::string& relativeTo) const
|
||||
{
|
||||
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
|
||||
{
|
||||
// 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)
|
||||
return mMap.at(key);
|
||||
else
|
||||
|
||||
return mNoResult;
|
||||
return mNoResult;
|
||||
}
|
||||
|
||||
int MetaDataList::getInt(const std::string& key) const
|
||||
{
|
||||
// Return integer value.
|
||||
return atoi(get(key).c_str());
|
||||
}
|
||||
|
||||
float MetaDataList::getFloat(const std::string& key) const
|
||||
{
|
||||
// Return float value.
|
||||
return static_cast<float>(atof(get(key).c_str()));
|
||||
}
|
||||
|
||||
bool MetaDataList::wasChanged() const
|
||||
{
|
||||
// Return whether the metadata was changed.
|
||||
return mWasChanged;
|
||||
}
|
||||
|
||||
void MetaDataList::resetChangedFlag()
|
||||
{
|
||||
// Reset the change flag.
|
||||
mWasChanged = false;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace pugi { class xml_node; }
|
||||
namespace pugi
|
||||
{
|
||||
class xml_node;
|
||||
}
|
||||
|
||||
enum MetaDataType {
|
||||
// Generic types.
|
||||
|
@ -40,7 +43,7 @@ struct MetaDataDecl {
|
|||
std::string key;
|
||||
MetaDataType type;
|
||||
std::string defaultValue;
|
||||
// If true, ignore values for this metadata.
|
||||
// If true, ignore values for this metadata.
|
||||
bool isStatistic;
|
||||
// Displayed as this in editors.
|
||||
std::string displayName;
|
||||
|
@ -51,7 +54,7 @@ struct MetaDataDecl {
|
|||
};
|
||||
|
||||
enum MetaDataListType {
|
||||
GAME_METADATA,
|
||||
GAME_METADATA, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
FOLDER_METADATA
|
||||
};
|
||||
|
||||
|
@ -61,9 +64,11 @@ class MetaDataList
|
|||
{
|
||||
public:
|
||||
static MetaDataList createFromXML(MetaDataListType type,
|
||||
pugi::xml_node& node, const std::string& relativeTo);
|
||||
void appendToXML(pugi::xml_node& parent, bool ignoreDefaults,
|
||||
const std::string& relativeTo) const;
|
||||
pugi::xml_node& node,
|
||||
const std::string& relativeTo);
|
||||
void appendToXML(pugi::xml_node& parent,
|
||||
bool ignoreDefaults,
|
||||
const std::string& relativeTo) const;
|
||||
|
||||
MetaDataList(MetaDataListType type);
|
||||
|
||||
|
@ -76,10 +81,12 @@ public:
|
|||
bool wasChanged() const;
|
||||
void resetChangedFlag();
|
||||
|
||||
inline MetaDataListType getType() const { return mType; }
|
||||
inline const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); }
|
||||
inline const std::vector<MetaDataDecl>& getMDD(MetaDataListType type) const
|
||||
{ return getMDDByType(type); }
|
||||
MetaDataListType getType() const { return mType; }
|
||||
const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); }
|
||||
const std::vector<MetaDataDecl>& getMDD(MetaDataListType type) const
|
||||
{
|
||||
return getMDDByType(type);
|
||||
}
|
||||
|
||||
private:
|
||||
MetaDataListType mType;
|
||||
|
|
|
@ -9,47 +9,45 @@
|
|||
|
||||
#include "MiximageGenerator.h"
|
||||
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "Log.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
MiximageGenerator::MiximageGenerator(FileData* game, std::string& resultMessage)
|
||||
: mGame(game),
|
||||
mResultMessage(resultMessage),
|
||||
mWidth(1280),
|
||||
mHeight(960),
|
||||
mMarquee(false),
|
||||
mBox3D(false),
|
||||
mCover(false)
|
||||
: mGame(game)
|
||||
, mResultMessage(resultMessage)
|
||||
, mWidth(1280)
|
||||
, mHeight(960)
|
||||
, mMarquee(false)
|
||||
, mBox3D(false)
|
||||
, mCover(false)
|
||||
{
|
||||
}
|
||||
|
||||
MiximageGenerator::~MiximageGenerator()
|
||||
{
|
||||
}
|
||||
MiximageGenerator::~MiximageGenerator() {}
|
||||
|
||||
void MiximageGenerator::startThread(std::promise<bool>* miximagePromise)
|
||||
{
|
||||
mMiximagePromise = miximagePromise;
|
||||
|
||||
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Creating miximage for \""
|
||||
<< mGame->getFileName() << "\"";
|
||||
<< mGame->getFileName() << "\"";
|
||||
|
||||
if (mGame->getMiximagePath() != "" && !Settings::getInstance()->getBool("MiximageOverwrite")) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mScreenshotPath = mGame->getScreenshotPath()) == "") {
|
||||
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): "
|
||||
"No screenshot image found, aborting";
|
||||
mResultMessage = "No screenshot image found, couldn't generate miximage";
|
||||
"No screenshot image found, aborting";
|
||||
mResultMessage = "No screenshot image found, couldn't generate miximage";
|
||||
mMiximagePromise->set_value(true);
|
||||
return;
|
||||
}
|
||||
|
@ -68,14 +66,14 @@ void MiximageGenerator::startThread(std::promise<bool>* miximagePromise)
|
|||
mBox3D = true;
|
||||
}
|
||||
else if (Settings::getInstance()->getBool("MiximageCoverFallback") &&
|
||||
(mCoverPath= mGame->getCoverPath()) != "") {
|
||||
(mCoverPath = mGame->getCoverPath()) != "") {
|
||||
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;
|
||||
}
|
||||
else if (Settings::getInstance()->getBool("MiximageCoverFallback")) {
|
||||
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): "
|
||||
"No 3D box or cover images found";
|
||||
"No 3D box or cover images found";
|
||||
}
|
||||
else {
|
||||
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): No 3D box image found";
|
||||
|
@ -93,9 +91,10 @@ void MiximageGenerator::startThread(std::promise<bool>* miximagePromise)
|
|||
else {
|
||||
const auto endTime = std::chrono::system_clock::now();
|
||||
|
||||
LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Processing completed in: " <<
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(endTime - startTime).count() << " ms";
|
||||
LOG(LogDebug)
|
||||
<< "MiximageGenerator::MiximageGenerator(): Processing completed in: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
|
||||
<< " ms";
|
||||
}
|
||||
|
||||
mResultMessage = mMessage;
|
||||
|
@ -113,19 +112,19 @@ bool MiximageGenerator::generateImage()
|
|||
unsigned int fileHeight = 0;
|
||||
unsigned int filePitch = 0;
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mScreenshotPath).c_str());
|
||||
#else
|
||||
#else
|
||||
fileFormat = FreeImage_GetFileType(mScreenshotPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN)
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFIFFromFilenameU(
|
||||
Utils::String::stringToWideString(mScreenshotPath).c_str());
|
||||
#else
|
||||
Utils::String::stringToWideString(mScreenshotPath).c_str());
|
||||
#else
|
||||
fileFormat = FreeImage_GetFIFFromFilename(mScreenshotPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN) {
|
||||
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.
|
||||
if (FreeImage_FIFSupportsReading(fileFormat)) {
|
||||
#if defined(_WIN64)
|
||||
screenshotFile = FreeImage_LoadU(fileFormat,
|
||||
Utils::String::stringToWideString(mScreenshotPath).c_str());
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
screenshotFile =
|
||||
FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mScreenshotPath).c_str());
|
||||
#else
|
||||
screenshotFile = FreeImage_Load(fileFormat, mScreenshotPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "Screenshot file format not supported";
|
||||
|
@ -155,20 +154,20 @@ bool MiximageGenerator::generateImage()
|
|||
}
|
||||
|
||||
if (mMarquee) {
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFileTypeU(
|
||||
Utils::String::stringToWideString(mMarqueePath).c_str());
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
fileFormat =
|
||||
FreeImage_GetFileTypeU(Utils::String::stringToWideString(mMarqueePath).c_str());
|
||||
#else
|
||||
fileFormat = FreeImage_GetFileType(mMarqueePath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN)
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFIFFromFilenameU(
|
||||
Utils::String::stringToWideString(mMarqueePath).c_str());
|
||||
#else
|
||||
Utils::String::stringToWideString(mMarqueePath).c_str());
|
||||
#else
|
||||
fileFormat = FreeImage_GetFIFFromFilename(mMarqueePath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN) {
|
||||
LOG(LogDebug) << "Marquee in unknown format, skipping image";
|
||||
|
@ -180,12 +179,12 @@ bool MiximageGenerator::generateImage()
|
|||
mMarquee = false;
|
||||
}
|
||||
else {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
marqueeFile = FreeImage_LoadU(fileFormat,
|
||||
Utils::String::stringToWideString(mMarqueePath).c_str());
|
||||
#else
|
||||
Utils::String::stringToWideString(mMarqueePath).c_str());
|
||||
#else
|
||||
marqueeFile = FreeImage_Load(fileFormat, mMarqueePath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
if (!marqueeFile) {
|
||||
LOG(LogError) << "Couldn't load marquee image, corrupt file?";
|
||||
mMessage = "Error loading marquee image, corrupt file?";
|
||||
|
@ -195,19 +194,19 @@ bool MiximageGenerator::generateImage()
|
|||
}
|
||||
|
||||
if (mBox3D) {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mBox3DPath).c_str());
|
||||
#else
|
||||
#else
|
||||
fileFormat = FreeImage_GetFileType(mBox3DPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN)
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFIFFromFilenameU(
|
||||
Utils::String::stringToWideString(mBox3DPath).c_str());
|
||||
#else
|
||||
Utils::String::stringToWideString(mBox3DPath).c_str());
|
||||
#else
|
||||
fileFormat = FreeImage_GetFIFFromFilename(mBox3DPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN) {
|
||||
LOG(LogDebug) << "3D box in unknown format, skipping image";
|
||||
|
@ -219,12 +218,12 @@ bool MiximageGenerator::generateImage()
|
|||
mBox3D = false;
|
||||
}
|
||||
else {
|
||||
#if defined(_WIN64)
|
||||
boxFile = FreeImage_LoadU(fileFormat,
|
||||
Utils::String::stringToWideString(mBox3DPath).c_str());
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
boxFile =
|
||||
FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mBox3DPath).c_str());
|
||||
#else
|
||||
boxFile = FreeImage_Load(fileFormat, mBox3DPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
if (!boxFile) {
|
||||
LOG(LogError) << "Couldn't load 3D box image, corrupt file?";
|
||||
mMessage = "Error loading 3d box image, corrupt file?";
|
||||
|
@ -233,20 +232,19 @@ bool MiximageGenerator::generateImage()
|
|||
}
|
||||
}
|
||||
else if (mCover) {
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFileTypeU(
|
||||
Utils::String::stringToWideString(mCoverPath).c_str());
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mCoverPath).c_str());
|
||||
#else
|
||||
fileFormat = FreeImage_GetFileType(mCoverPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN)
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
fileFormat = FreeImage_GetFIFFromFilenameU(
|
||||
Utils::String::stringToWideString(mCoverPath).c_str());
|
||||
#else
|
||||
Utils::String::stringToWideString(mCoverPath).c_str());
|
||||
#else
|
||||
fileFormat = FreeImage_GetFIFFromFilename(mCoverPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fileFormat == FIF_UNKNOWN) {
|
||||
LOG(LogDebug) << "Box cover in unknown format, skipping image";
|
||||
|
@ -258,12 +256,12 @@ bool MiximageGenerator::generateImage()
|
|||
mCover = false;
|
||||
}
|
||||
else {
|
||||
#if defined(_WIN64)
|
||||
boxFile = FreeImage_LoadU(fileFormat,
|
||||
Utils::String::stringToWideString(mCoverPath).c_str());
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
boxFile =
|
||||
FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mCoverPath).c_str());
|
||||
#else
|
||||
boxFile = FreeImage_Load(fileFormat, mCoverPath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
if (!boxFile) {
|
||||
LOG(LogError) << "Couldn't load 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);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -386,10 +384,10 @@ bool MiximageGenerator::generateImage()
|
|||
std::vector<unsigned char> marqueeVector(fileWidth * fileHeight * 4);
|
||||
|
||||
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),
|
||||
FreeImage_GetHeight(marqueeFile), 1, 4, 0);
|
||||
FreeImage_GetHeight(marqueeFile), 1, 4, 0);
|
||||
|
||||
Utils::CImg::convertRGBAToCImg(marqueeVector, marqueeImage);
|
||||
Utils::CImg::removeTransparentPadding(marqueeImage);
|
||||
|
@ -409,7 +407,7 @@ bool MiximageGenerator::generateImage()
|
|||
yPosMarquee = 0;
|
||||
|
||||
// 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.
|
||||
marqueeImageAlpha = CImg<unsigned char>(marqueeImage.get_shared_channel(3));
|
||||
}
|
||||
|
@ -427,17 +425,17 @@ bool MiximageGenerator::generateImage()
|
|||
|
||||
std::vector<unsigned char> boxVector(fileWidth * fileHeight * 4);
|
||||
|
||||
FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&boxVector.at(0)), boxFile,
|
||||
filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1);
|
||||
FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&boxVector.at(0)), boxFile, filePitch,
|
||||
32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1);
|
||||
|
||||
boxImage = CImg<unsigned char>(FreeImage_GetWidth(boxFile),
|
||||
FreeImage_GetHeight(boxFile), 1, 4);
|
||||
boxImage =
|
||||
CImg<unsigned char>(FreeImage_GetWidth(boxFile), FreeImage_GetHeight(boxFile), 1, 4);
|
||||
|
||||
Utils::CImg::convertRGBAToCImg(boxVector, boxImage);
|
||||
Utils::CImg::removeTransparentPadding(boxImage);
|
||||
|
||||
float scaleFactor = static_cast<float>(boxTargetHeight) /
|
||||
static_cast<float>(boxImage.height());
|
||||
float scaleFactor =
|
||||
static_cast<float>(boxTargetHeight) / static_cast<float>(boxImage.height());
|
||||
unsigned int width = static_cast<int>(static_cast<float>(boxImage.width()) * scaleFactor);
|
||||
unsigned int targetWidth = 0;
|
||||
|
||||
|
@ -464,7 +462,7 @@ bool MiximageGenerator::generateImage()
|
|||
yPosBox = canvasImage.height() - boxImage.height();
|
||||
|
||||
// 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.
|
||||
boxImageAlpha = CImg<unsigned char>(boxImage.get_shared_channel(3));
|
||||
}
|
||||
|
@ -478,20 +476,15 @@ bool MiximageGenerator::generateImage()
|
|||
sampleFrameColor(screenshotImage, frameColor);
|
||||
|
||||
// Upper / lower frame.
|
||||
frameImage.draw_rectangle(
|
||||
xPosScreenshot + 2,
|
||||
yPosScreenshot - screenshotFrameWidth,
|
||||
xPosScreenshot + screenshotWidth - 2,
|
||||
yPosScreenshot + screenshotHeight + screenshotFrameWidth - 1,
|
||||
frameColor);
|
||||
frameImage.draw_rectangle(xPosScreenshot + 2, yPosScreenshot - screenshotFrameWidth,
|
||||
xPosScreenshot + screenshotWidth - 2,
|
||||
yPosScreenshot + screenshotHeight + screenshotFrameWidth - 1,
|
||||
frameColor);
|
||||
|
||||
// Left / right frame.
|
||||
frameImage.draw_rectangle(
|
||||
xPosScreenshot - screenshotFrameWidth,
|
||||
yPosScreenshot + 2,
|
||||
xPosScreenshot + screenshotWidth + screenshotFrameWidth - 1,
|
||||
yPosScreenshot + screenshotHeight - 2,
|
||||
frameColor);
|
||||
frameImage.draw_rectangle(xPosScreenshot - screenshotFrameWidth, yPosScreenshot + 2,
|
||||
xPosScreenshot + screenshotWidth + screenshotFrameWidth - 1,
|
||||
yPosScreenshot + screenshotHeight - 2, frameColor);
|
||||
|
||||
// We draw circles in order to get rounded corners for the frame.
|
||||
const unsigned int circleRadius = 8 * resolutionMultiplier;
|
||||
|
@ -499,16 +492,18 @@ bool MiximageGenerator::generateImage()
|
|||
|
||||
// Upper left corner.
|
||||
frameImage.draw_circle(xPosScreenshot + circleOffset, yPosScreenshot + circleOffset,
|
||||
circleRadius, frameColor);
|
||||
circleRadius, frameColor);
|
||||
// Upper right corner.
|
||||
frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1,
|
||||
yPosScreenshot + circleOffset, circleRadius, frameColor);
|
||||
yPosScreenshot + circleOffset, circleRadius, frameColor);
|
||||
// Lower right corner.
|
||||
frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1,
|
||||
yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, frameColor);
|
||||
yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius,
|
||||
frameColor);
|
||||
// Lower left corner.
|
||||
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));
|
||||
|
||||
|
@ -516,8 +511,8 @@ bool MiximageGenerator::generateImage()
|
|||
canvasImage.draw_image(xPosScreenshot, yPosScreenshot, screenshotImage);
|
||||
|
||||
if (mMarquee)
|
||||
canvasImage.draw_image(xPosMarquee, yPosMarquee, marqueeImageRGB,
|
||||
marqueeImageAlpha, 1, 255);
|
||||
canvasImage.draw_image(xPosMarquee, yPosMarquee, marqueeImageRGB, marqueeImageAlpha, 1,
|
||||
255);
|
||||
if (mBox3D || mCover)
|
||||
canvasImage.draw_image(xPosBox, yPosBox, boxImageRGB, boxImageAlpha, 1, 255);
|
||||
|
||||
|
@ -528,15 +523,16 @@ bool MiximageGenerator::generateImage()
|
|||
|
||||
FIBITMAP* mixImage = nullptr;
|
||||
mixImage = FreeImage_ConvertFromRawBits(&canvasVector.at(0), canvasImage.width(),
|
||||
canvasImage.height(), canvasImage.width() * 4, 32,
|
||||
FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE);
|
||||
canvasImage.height(), canvasImage.width() * 4, 32,
|
||||
FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE);
|
||||
|
||||
#if defined(_WIN64)
|
||||
bool savedImage = (FreeImage_SaveU(FIF_PNG, mixImage,
|
||||
Utils::String::stringToWideString(getSavePath()).c_str()) != 0);
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
bool savedImage =
|
||||
(FreeImage_SaveU(FIF_PNG, mixImage,
|
||||
Utils::String::stringToWideString(getSavePath()).c_str()) != 0);
|
||||
#else
|
||||
bool savedImage = (FreeImage_Save(FIF_PNG, mixImage, getSavePath().c_str()) != 0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!savedImage) {
|
||||
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,
|
||||
const unsigned int& targetHeight, unsigned int& width, unsigned int& height)
|
||||
const unsigned int& targetHeight,
|
||||
unsigned int& width,
|
||||
unsigned int& height)
|
||||
{
|
||||
unsigned int adjustedTargetWidth = 0;
|
||||
float widthModifier = 0.5f;
|
||||
|
@ -572,13 +570,13 @@ void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth,
|
|||
if (widthRatio >= 4)
|
||||
widthModifier += Math::clamp(widthRatio / 40.0f, 0.0f, 0.3f);
|
||||
|
||||
adjustedTargetWidth = static_cast<unsigned int>(
|
||||
static_cast<float>(targetWidth) * widthModifier);
|
||||
adjustedTargetWidth =
|
||||
static_cast<unsigned int>(static_cast<float>(targetWidth) * widthModifier);
|
||||
scaleFactor = static_cast<float>(adjustedTargetWidth) / static_cast<float>(width);
|
||||
|
||||
// For really tall and narrow images, we may have exceeded the target 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);
|
||||
|
||||
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,
|
||||
unsigned char (&frameColor)[4])
|
||||
unsigned char (&frameColor)[4])
|
||||
{
|
||||
// Calculate the number of samples relative to the configured resolution so we get
|
||||
// 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);
|
||||
|
||||
// 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 saturation = colorHSL(0, 0, 0, 1);
|
||||
|
@ -656,7 +654,7 @@ std::string MiximageGenerator::getSavePath()
|
|||
// Extract possible subfolders from the path.
|
||||
if (mGame->getSystemEnvData()->mStartPath != "")
|
||||
subFolders = Utils::String::replace(Utils::FileSystem::getParent(mGame->getPath()),
|
||||
mGame->getSystemEnvData()->mStartPath, "");
|
||||
mGame->getSystemEnvData()->mStartPath, "");
|
||||
|
||||
std::string path = FileData::getMediaDirectory();
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#ifndef ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H
|
||||
#define ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H
|
||||
|
||||
#include "utils/CImgUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "utils/CImgUtil.h"
|
||||
|
||||
#include <FreeImage.h>
|
||||
#include <future>
|
||||
|
@ -29,8 +29,10 @@ public:
|
|||
|
||||
private:
|
||||
bool generateImage();
|
||||
void calculateMarqueeSize(const unsigned int& targetWidth, const unsigned int& targetHeight,
|
||||
unsigned int& width, unsigned int& height);
|
||||
void calculateMarqueeSize(const unsigned int& targetWidth,
|
||||
const unsigned int& targetHeight,
|
||||
unsigned int& width,
|
||||
unsigned int& height);
|
||||
void sampleFrameColor(CImg<unsigned char>& screenshotImage, unsigned char (&frameColor)[4]);
|
||||
|
||||
std::string getSavePath();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
namespace PlatformIds
|
||||
{
|
||||
// clang-format off
|
||||
std::vector<std::string> platformNames = {
|
||||
"unknown", // Nothing set.
|
||||
|
||||
|
@ -132,6 +133,7 @@ namespace PlatformIds
|
|||
"ignore", // Do not allow scraping for this system.
|
||||
"invalid"
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
PlatformId getPlatformId(const std::string& str)
|
||||
{
|
||||
|
@ -148,6 +150,8 @@ namespace PlatformIds
|
|||
|
||||
const std::string getPlatformName(PlatformId id)
|
||||
{
|
||||
// Return the platform name.
|
||||
return platformNames[id];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PlatformIds
|
||||
|
|
|
@ -135,6 +135,7 @@ namespace PlatformIds
|
|||
|
||||
PlatformId getPlatformId(const std::string& str);
|
||||
const std::string getPlatformName(PlatformId id);
|
||||
}
|
||||
|
||||
} // namespace PlatformIds
|
||||
|
||||
#endif // ES_APP_PLATFORM_ID_H
|
||||
|
|
|
@ -11,12 +11,6 @@
|
|||
|
||||
#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 "FileFilterIndex.h"
|
||||
#include "FileSorts.h"
|
||||
|
@ -25,6 +19,12 @@
|
|||
#include "Platform.h"
|
||||
#include "Settings.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 <pugixml.hpp>
|
||||
|
@ -39,14 +39,10 @@ FindRules::FindRules()
|
|||
loadFindRules();
|
||||
}
|
||||
|
||||
FindRules::~FindRules()
|
||||
{
|
||||
}
|
||||
|
||||
void FindRules::loadFindRules()
|
||||
{
|
||||
std::string customSystemsDirectory =
|
||||
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
|
||||
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
|
||||
|
||||
std::string path = customSystemsDirectory + "/es_find_rules.xml";
|
||||
|
||||
|
@ -54,16 +50,16 @@ void FindRules::loadFindRules()
|
|||
LOG(LogInfo) << "Found custom find rules configuration file";
|
||||
}
|
||||
else {
|
||||
#if defined(_WIN64)
|
||||
path = ResourceManager::getInstance()->
|
||||
getResourcePath(":/systems/windows/es_find_rules.xml", false);
|
||||
#elif defined(__APPLE__)
|
||||
path = ResourceManager::getInstance()->
|
||||
getResourcePath(":/systems/macos/es_find_rules.xml", false);
|
||||
#else
|
||||
path = ResourceManager::getInstance()->
|
||||
getResourcePath(":/systems/unix/es_find_rules.xml", false);
|
||||
#endif
|
||||
#if defined(_WIN64)
|
||||
path = ResourceManager::getInstance()->getResourcePath(
|
||||
":/systems/windows/es_find_rules.xml", false);
|
||||
#elif defined(__APPLE__)
|
||||
path = ResourceManager::getInstance()->getResourcePath(":/systems/macos/es_find_rules.xml",
|
||||
false);
|
||||
#else
|
||||
path = ResourceManager::getInstance()->getResourcePath(":/systems/unix/es_find_rules.xml",
|
||||
false);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (path == "") {
|
||||
|
@ -74,11 +70,11 @@ void FindRules::loadFindRules()
|
|||
LOG(LogInfo) << "Parsing find rules configuration file \"" << path << "\"...";
|
||||
|
||||
pugi::xml_document doc;
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
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());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!res) {
|
||||
LOG(LogError) << "Couldn't parse es_find_rules.xml: " << res.description();
|
||||
|
@ -97,81 +93,79 @@ void FindRules::loadFindRules()
|
|||
CoreRules coreRules;
|
||||
|
||||
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();
|
||||
if (emulatorName.empty()) {
|
||||
LOG(LogWarning) << "Found emulator tag without name attribute, skipping entry";
|
||||
continue;
|
||||
}
|
||||
if (mEmulators.find(emulatorName) != mEmulators.end()) {
|
||||
LOG(LogWarning) << "Found repeating emulator tag \"" << emulatorName <<
|
||||
"\", skipping entry";
|
||||
LOG(LogWarning) << "Found repeating emulator tag \"" << emulatorName
|
||||
<< "\", skipping entry";
|
||||
continue;
|
||||
}
|
||||
for (pugi::xml_node rule = emulator.child("rule"); rule; rule = rule.next_sibling("rule")) {
|
||||
std::string ruleType = rule.attribute("type").as_string();
|
||||
if (ruleType.empty()) {
|
||||
LOG(LogWarning) << "Found rule tag without type attribute for emulator \"" <<
|
||||
emulatorName << "\", skipping entry";
|
||||
LOG(LogWarning) << "Found rule tag without type attribute for emulator \""
|
||||
<< emulatorName << "\", skipping entry";
|
||||
continue;
|
||||
}
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
if (ruleType != "winregistrypath" && ruleType != "systempath" &&
|
||||
ruleType != "staticpath") {
|
||||
#else
|
||||
ruleType != "staticpath") {
|
||||
#else
|
||||
if (ruleType != "systempath" && ruleType != "staticpath") {
|
||||
#endif
|
||||
LOG(LogWarning) << "Found invalid rule type \"" << ruleType <<
|
||||
"\" for emulator \"" << emulatorName << "\", skipping entry";
|
||||
#endif
|
||||
LOG(LogWarning) << "Found invalid rule type \"" << ruleType << "\" for emulator \""
|
||||
<< emulatorName << "\", skipping entry";
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
if (ruleType == "systempath")
|
||||
emulatorRules.systemPaths.push_back(entryValue);
|
||||
else if (ruleType == "staticpath")
|
||||
emulatorRules.staticPaths.push_back(entryValue);
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
else if (ruleType == "winregistrypath")
|
||||
emulatorRules.winRegistryPaths.push_back(entryValue);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
mEmulators[emulatorName] = emulatorRules;
|
||||
emulatorRules.systemPaths.clear();
|
||||
emulatorRules.staticPaths.clear();
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
emulatorRules.winRegistryPaths.clear();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
for (pugi::xml_node core = ruleList.child("core"); core;
|
||||
core = core.next_sibling("core")) {
|
||||
for (pugi::xml_node core = ruleList.child("core"); core; core = core.next_sibling("core")) {
|
||||
std::string coreName = core.attribute("name").as_string();
|
||||
if (coreName.empty()) {
|
||||
LOG(LogWarning) << "Found core tag without name attribute, skipping entry";
|
||||
continue;
|
||||
}
|
||||
if (mCores.find(coreName) != mCores.end()) {
|
||||
LOG(LogWarning) << "Found repeating core tag \"" << coreName <<
|
||||
"\", skipping entry";
|
||||
LOG(LogWarning) << "Found repeating core tag \"" << coreName << "\", skipping entry";
|
||||
continue;
|
||||
}
|
||||
for (pugi::xml_node rule = core.child("rule"); rule; rule = rule.next_sibling("rule")) {
|
||||
std::string ruleType = rule.attribute("type").as_string();
|
||||
if (ruleType.empty()) {
|
||||
LOG(LogWarning) << "Found rule tag without type attribute for core \"" <<
|
||||
coreName << "\", skipping entry";
|
||||
LOG(LogWarning) << "Found rule tag without type attribute for core \"" << coreName
|
||||
<< "\", skipping entry";
|
||||
continue;
|
||||
}
|
||||
if (ruleType != "corepath") {
|
||||
LOG(LogWarning) << "Found invalid rule type \"" << ruleType <<
|
||||
"\" for core \"" << coreName << "\", skipping entry";
|
||||
LOG(LogWarning) << "Found invalid rule type \"" << ruleType << "\" for core \""
|
||||
<< coreName << "\", skipping entry";
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
if (ruleType == "corepath")
|
||||
coreRules.corePaths.push_back(entryValue);
|
||||
|
@ -182,23 +176,22 @@ void FindRules::loadFindRules()
|
|||
}
|
||||
}
|
||||
|
||||
SystemData::SystemData(
|
||||
const std::string& name,
|
||||
const std::string& fullName,
|
||||
SystemEnvironmentData* envData,
|
||||
const std::string& themeFolder,
|
||||
bool CollectionSystem,
|
||||
bool CustomCollectionSystem)
|
||||
: mName(name),
|
||||
mFullName(fullName),
|
||||
mEnvData(envData),
|
||||
mThemeFolder(themeFolder),
|
||||
mIsCollectionSystem(CollectionSystem),
|
||||
mIsCustomCollectionSystem(CustomCollectionSystem),
|
||||
mIsGroupedCustomCollectionSystem(false),
|
||||
mIsGameSystem(true),
|
||||
mScrapeFlag(false),
|
||||
mPlaceholder(nullptr)
|
||||
SystemData::SystemData(const std::string& name,
|
||||
const std::string& fullName,
|
||||
SystemEnvironmentData* envData,
|
||||
const std::string& themeFolder,
|
||||
bool CollectionSystem,
|
||||
bool CustomCollectionSystem)
|
||||
: mName(name)
|
||||
, mFullName(fullName)
|
||||
, mEnvData(envData)
|
||||
, mThemeFolder(themeFolder)
|
||||
, mIsCollectionSystem(CollectionSystem)
|
||||
, mIsCustomCollectionSystem(CustomCollectionSystem)
|
||||
, mIsGroupedCustomCollectionSystem(false)
|
||||
, mIsGameSystem(true)
|
||||
, mScrapeFlag(false)
|
||||
, mPlaceholder(nullptr)
|
||||
{
|
||||
mFilterIndex = new FileFilterIndex();
|
||||
|
||||
|
@ -220,7 +213,7 @@ SystemData::SystemData(
|
|||
setupSystemSortType(mRootFolder);
|
||||
|
||||
mRootFolder->sort(mRootFolder->getSortTypeFromString(mRootFolder->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
|
||||
indexAllGameFilters(mRootFolder);
|
||||
}
|
||||
|
@ -275,13 +268,13 @@ bool SystemData::populateFolder(FileData* folder)
|
|||
return false;
|
||||
|
||||
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
|
||||
it != dirContent.cend(); it++) {
|
||||
it != dirContent.cend(); it++) {
|
||||
filePath = *it;
|
||||
|
||||
// Skip any recursive symlinks as those would hang the application at various places.
|
||||
if (Utils::FileSystem::isSymlink(filePath)) {
|
||||
if (Utils::FileSystem::resolveSymlink(filePath) ==
|
||||
Utils::FileSystem::getFileName(filePath)) {
|
||||
Utils::FileSystem::getFileName(filePath)) {
|
||||
LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink";
|
||||
continue;
|
||||
}
|
||||
|
@ -289,9 +282,9 @@ bool SystemData::populateFolder(FileData* folder)
|
|||
|
||||
// Skip hidden files and folders.
|
||||
if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) {
|
||||
LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden " <<
|
||||
(Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"") <<
|
||||
filePath << "\"";
|
||||
LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden "
|
||||
<< (Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"")
|
||||
<< filePath << "\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -306,7 +299,7 @@ bool SystemData::populateFolder(FileData* folder)
|
|||
|
||||
isGame = false;
|
||||
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);
|
||||
|
||||
// Prevent new arcade assets from being added.
|
||||
|
@ -326,15 +319,17 @@ bool SystemData::populateFolder(FileData* folder)
|
|||
if (Utils::FileSystem::isSymlink(filePath)) {
|
||||
const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(filePath);
|
||||
const std::string canonicalStartPath =
|
||||
Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath);
|
||||
const std::string combinedPath = mEnvData->mStartPath +
|
||||
canonicalPath.substr(canonicalStartPath.size(),
|
||||
canonicalStartPath.size() - canonicalPath.size());
|
||||
Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath);
|
||||
const std::string combinedPath =
|
||||
mEnvData->mStartPath +
|
||||
canonicalPath.substr(canonicalStartPath.size(),
|
||||
canonicalStartPath.size() - canonicalPath.size());
|
||||
if (filePath.find(combinedPath) == 0) {
|
||||
LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this);
|
||||
populateFolder(newFolder);
|
||||
|
||||
|
@ -352,17 +347,15 @@ void SystemData::indexAllGameFilters(const FileData* folder)
|
|||
{
|
||||
const std::vector<FileData*>& children = folder->getChildren();
|
||||
|
||||
for (std::vector<FileData*>::const_iterator it = children.cbegin();
|
||||
it != children.cend(); it++) {
|
||||
for (std::vector<FileData*>::const_iterator it = children.cbegin(); // Line break.
|
||||
it != children.cend(); it++) {
|
||||
switch ((*it)->getType()) {
|
||||
case GAME: {
|
||||
case GAME:
|
||||
mFilterIndex->addToIndex(*it);
|
||||
}
|
||||
break;
|
||||
case FOLDER: {
|
||||
break;
|
||||
case FOLDER:
|
||||
indexAllGameFilters(*it);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -400,11 +393,11 @@ bool SystemData::loadConfig()
|
|||
LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"...";
|
||||
|
||||
pugi::xml_document doc;
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
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());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!res) {
|
||||
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;
|
||||
system = system.next_sibling("system")) {
|
||||
system = system.next_sibling("system")) {
|
||||
std::string name;
|
||||
std::string fullname;
|
||||
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 ""
|
||||
// in this configuration file, the default hardcoded path $HOME/ROMs/ will be used.
|
||||
path = Utils::String::replace(path, "%ROMPATH%", rompath);
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
path = Utils::String::replace(path, "\\", "/");
|
||||
#endif
|
||||
#endif
|
||||
path = Utils::String::replace(path, "//", "/");
|
||||
|
||||
// Check that the ROM directory for the system is valid or otherwise abort the processing.
|
||||
if (!Utils::FileSystem::exists(path)) {
|
||||
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" <<
|
||||
name << "\" as the defined ROM directory \"" << path << "\" does not exist";
|
||||
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name
|
||||
<< "\" as the defined ROM directory \"" << path << "\" does not exist";
|
||||
continue;
|
||||
}
|
||||
if (!Utils::FileSystem::isDirectory(path)) {
|
||||
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" <<
|
||||
name << "\" as the defined ROM directory \"" << path <<
|
||||
"\" is not actually a directory";
|
||||
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name
|
||||
<< "\" as the defined ROM directory \"" << path
|
||||
<< "\" is not actually a directory";
|
||||
continue;
|
||||
}
|
||||
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.
|
||||
std::string resolvedRompath = Utils::FileSystem::getCanonicalPath(rompath);
|
||||
if (resolvedRompath.find(Utils::FileSystem::getCanonicalPath(path)) == 0) {
|
||||
LOG(LogWarning) << "Skipping system \"" << name <<
|
||||
"\" as the defined ROM directory \"" << path <<
|
||||
"\" is an infinitely recursive symlink";
|
||||
LOG(LogWarning) << "Skipping system \"" << name
|
||||
<< "\" as the defined ROM directory \"" << path
|
||||
<< "\" is an infinitely recursive symlink";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -472,7 +465,7 @@ bool SystemData::loadConfig()
|
|||
|
||||
// Platform ID list
|
||||
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<PlatformIds::PlatformId> platformIds;
|
||||
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
|
||||
// platforms, then generate a warning.
|
||||
if (str != "" && platformId == PlatformIds::PLATFORM_UNKNOWN)
|
||||
LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \"" <<
|
||||
name << "\"";
|
||||
LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \""
|
||||
<< name << "\"";
|
||||
else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
|
||||
platformIds.push_back(platformId);
|
||||
}
|
||||
|
@ -501,26 +494,27 @@ bool SystemData::loadConfig()
|
|||
// Validate.
|
||||
|
||||
if (name.empty()) {
|
||||
LOG(LogError) <<
|
||||
"A system in the es_systems.xml file has no name defined, skipping entry";
|
||||
LOG(LogError)
|
||||
<< "A system in the es_systems.xml file has no name defined, skipping entry";
|
||||
continue;
|
||||
}
|
||||
else if (fullname.empty() || path.empty() || extensions.empty() || cmd.empty()) {
|
||||
LOG(LogError) << "System \"" << name << "\" is missing the fullname, path, "
|
||||
"extension, or command tag, skipping entry";
|
||||
LOG(LogError) << "System \"" << name
|
||||
<< "\" is missing the fullname, path, "
|
||||
"extension, or command tag, skipping entry";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert path to generic directory seperators.
|
||||
path = Utils::FileSystem::getGenericPath(path);
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
if (!Settings::getInstance()->getBool("ShowHiddenFiles") &&
|
||||
Utils::FileSystem::isHidden(path)) {
|
||||
Utils::FileSystem::isHidden(path)) {
|
||||
LOG(LogWarning) << "Skipping hidden ROM folder \"" << path << "\"";
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Create the system runtime environment data.
|
||||
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
|
||||
// games for the system are hidden. That will flag the system as empty.
|
||||
if (!Settings::getInstance()->getBool("ShowHiddenGames")) {
|
||||
std::vector<FileData*> recursiveGames =
|
||||
newSys->getRootFolder()->getChildrenRecursive();
|
||||
std::vector<FileData*> recursiveGames = newSys->getRootFolder()->getChildrenRecursive();
|
||||
onlyHidden = true;
|
||||
for (auto it = recursiveGames.cbegin(); it != recursiveGames.cend(); it++) {
|
||||
if ((*it)->getType() != FOLDER) {
|
||||
|
@ -548,8 +541,8 @@ bool SystemData::loadConfig()
|
|||
}
|
||||
|
||||
if (newSys->getRootFolder()->getChildrenByFilename().size() == 0 || onlyHidden) {
|
||||
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" <<
|
||||
name << "\" as no files matched any of the defined file extensions";
|
||||
LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name
|
||||
<< "\" as no files matched any of the defined file extensions";
|
||||
delete newSys;
|
||||
}
|
||||
else {
|
||||
|
@ -559,8 +552,7 @@ bool SystemData::loadConfig()
|
|||
|
||||
// Sort systems by their full names.
|
||||
std::sort(std::begin(sSystemVector), std::end(sSystemVector),
|
||||
[](SystemData* a, SystemData* b) {
|
||||
return a->getFullName() < b->getFullName(); });
|
||||
[](SystemData* a, SystemData* b) { return a->getFullName() < b->getFullName(); });
|
||||
|
||||
// Don't load any collections if there are no systems available.
|
||||
if (sSystemVector.size() > 0)
|
||||
|
@ -580,18 +572,18 @@ void SystemData::deleteSystems()
|
|||
std::string SystemData::getConfigPath(bool legacyWarning)
|
||||
{
|
||||
if (legacyWarning) {
|
||||
std::string legacyConfigFile = Utils::FileSystem::getHomePath() +
|
||||
"/.emulationstation/es_systems.cfg";
|
||||
std::string legacyConfigFile =
|
||||
Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg";
|
||||
|
||||
if (Utils::FileSystem::exists(legacyConfigFile)) {
|
||||
LOG(LogInfo) << "Found legacy systems configuration file \"" << legacyConfigFile <<
|
||||
"\", to retain your customizations move it to "
|
||||
"\"custom_systems/es_systems.xml\" or otherwise delete the file";
|
||||
LOG(LogInfo) << "Found legacy systems configuration file \"" << legacyConfigFile
|
||||
<< "\", to retain your customizations move it to "
|
||||
"\"custom_systems/es_systems.xml\" or otherwise delete the file";
|
||||
}
|
||||
}
|
||||
|
||||
std::string customSystemsDirectory =
|
||||
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
|
||||
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
|
||||
|
||||
if (!Utils::FileSystem::exists(customSystemsDirectory)) {
|
||||
LOG(LogInfo) << "Creating custom systems directory \"" << customSystemsDirectory << "\"...";
|
||||
|
@ -608,16 +600,14 @@ std::string SystemData::getConfigPath(bool legacyWarning)
|
|||
return path;
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
path = ResourceManager::getInstance()->
|
||||
getResourcePath(":/systems/windows/es_systems.xml", true);
|
||||
#elif defined(__APPLE__)
|
||||
path = ResourceManager::getInstance()->
|
||||
getResourcePath(":/systems/macos/es_systems.xml", true);
|
||||
#else
|
||||
path = ResourceManager::getInstance()->
|
||||
getResourcePath(":/systems/unix/es_systems.xml", true);
|
||||
#endif
|
||||
#if defined(_WIN64)
|
||||
path =
|
||||
ResourceManager::getInstance()->getResourcePath(":/systems/windows/es_systems.xml", true);
|
||||
#elif defined(__APPLE__)
|
||||
path = ResourceManager::getInstance()->getResourcePath(":/systems/macos/es_systems.xml", true);
|
||||
#else
|
||||
path = ResourceManager::getInstance()->getResourcePath(":/systems/unix/es_systems.xml", true);
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
@ -627,16 +617,16 @@ bool SystemData::createSystemDirectories()
|
|||
std::string path = getConfigPath(false);
|
||||
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";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(LogInfo) << "Generating ROM directory structure...";
|
||||
|
||||
if (Utils::FileSystem::exists(rompath) && Utils::FileSystem::isRegularFile(rompath)) {
|
||||
LOG(LogError) <<
|
||||
"Requested ROM directory \"" << rompath << "\" is actually a file, aborting";
|
||||
LOG(LogError) << "Requested ROM directory \"" << rompath
|
||||
<< "\" is actually a file, aborting";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -654,11 +644,11 @@ bool SystemData::createSystemDirectories()
|
|||
LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"...";
|
||||
|
||||
pugi::xml_document doc;
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
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());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!res) {
|
||||
LOG(LogError) << "Couldn't parse es_systems.xml";
|
||||
|
@ -677,7 +667,7 @@ bool SystemData::createSystemDirectories()
|
|||
std::vector<std::string> systemsVector;
|
||||
|
||||
for (pugi::xml_node system = systemList.child("system"); system;
|
||||
system = system.next_sibling("system")) {
|
||||
system = system.next_sibling("system")) {
|
||||
std::string systemDir;
|
||||
std::string name;
|
||||
std::string fullname;
|
||||
|
@ -701,8 +691,9 @@ bool SystemData::createSystemDirectories()
|
|||
// Check that the %ROMPATH% variable is actually used for the path element.
|
||||
// If not, skip the system.
|
||||
if (path.find("%ROMPATH%") != 0) {
|
||||
LOG(LogWarning) << "The path element for system \"" << name << "\" does not "
|
||||
"utilize the %ROMPATH% variable, skipping entry";
|
||||
LOG(LogWarning) << "The path element for system \"" << name
|
||||
<< "\" does not "
|
||||
"utilize the %ROMPATH% variable, skipping entry";
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
|
@ -711,14 +702,13 @@ bool SystemData::createSystemDirectories()
|
|||
|
||||
// Trim any leading directory separator characters.
|
||||
systemDir.erase(systemDir.begin(),
|
||||
std::find_if(systemDir.begin(), systemDir.end(), [](char c) {
|
||||
return c != '/' && c != '\\';
|
||||
}));
|
||||
std::find_if(systemDir.begin(), systemDir.end(),
|
||||
[](char c) { return c != '/' && c != '\\'; }));
|
||||
|
||||
if (!Utils::FileSystem::exists(rompath + systemDir)) {
|
||||
if (!Utils::FileSystem::createDirectory(rompath + systemDir)) {
|
||||
LOG(LogError) << "Couldn't create system directory \"" << systemDir <<
|
||||
"\", permission problems or disk full?";
|
||||
LOG(LogError) << "Couldn't create system directory \"" << systemDir
|
||||
<< "\", permission problems or disk full?";
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -739,16 +729,17 @@ bool SystemData::createSystemDirectories()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
systemInfoFile.open(Utils::String::stringToWideString(rompath +
|
||||
systemDir + systemInfoFileName).c_str());
|
||||
#else
|
||||
#if defined(_WIN64)
|
||||
systemInfoFile.open(
|
||||
Utils::String::stringToWideString(rompath + systemDir + systemInfoFileName).c_str());
|
||||
#else
|
||||
systemInfoFile.open(rompath + systemDir + systemInfoFileName);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (systemInfoFile.fail()) {
|
||||
LOG(LogError) << "Couldn't create system information file \"" << rompath +
|
||||
systemDir + systemInfoFileName << "\", permission problems or disk full?";
|
||||
LOG(LogError) << "Couldn't create system information file \""
|
||||
<< rompath + systemDir + systemInfoFileName
|
||||
<< "\", permission problems or disk full?";
|
||||
systemInfoFile.close();
|
||||
return true;
|
||||
}
|
||||
|
@ -770,12 +761,12 @@ bool SystemData::createSystemDirectories()
|
|||
systemsVector.push_back(systemDir + ": " + fullname);
|
||||
|
||||
if (replaceInfoFile) {
|
||||
LOG(LogInfo) << "Replaced existing system information file \"" <<
|
||||
rompath + systemDir + systemInfoFileName << "\"";
|
||||
LOG(LogInfo) << "Replaced existing system information file \""
|
||||
<< rompath + systemDir + systemInfoFileName << "\"";
|
||||
}
|
||||
else {
|
||||
LOG(LogInfo) << "Created system information file \"" <<
|
||||
rompath + systemDir + systemInfoFileName << "\"";
|
||||
LOG(LogInfo) << "Created system information file \""
|
||||
<< rompath + systemDir + systemInfoFileName << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,11 +784,11 @@ bool SystemData::createSystemDirectories()
|
|||
|
||||
if (systemsFileSuccess) {
|
||||
std::ofstream systemsFile;
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
systemsFile.open(Utils::String::stringToWideString(rompath + systemsFileName).c_str());
|
||||
#else
|
||||
#else
|
||||
systemsFile.open(rompath + systemsFileName);
|
||||
#endif
|
||||
#endif
|
||||
if (systemsFile.fail()) {
|
||||
systemsFileSuccess = false;
|
||||
}
|
||||
|
@ -811,7 +802,7 @@ bool SystemData::createSystemDirectories()
|
|||
|
||||
if (!systemsFileSuccess) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -867,8 +858,8 @@ std::string SystemData::getGamelistPath(bool forWrite) const
|
|||
if (Utils::FileSystem::exists(filePath))
|
||||
return filePath;
|
||||
|
||||
filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" +
|
||||
mName + "/gamelist.xml";
|
||||
filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName +
|
||||
"/gamelist.xml";
|
||||
|
||||
// Make sure the directory exists if we're going to write to it,
|
||||
// or crashes will happen.
|
||||
|
@ -899,17 +890,12 @@ std::string SystemData::getThemePath() const
|
|||
return localThemePath;
|
||||
|
||||
// Not system theme, try default system theme in theme set.
|
||||
localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) +
|
||||
"/theme.xml";
|
||||
localThemePath =
|
||||
Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml";
|
||||
|
||||
return localThemePath;
|
||||
}
|
||||
|
||||
bool SystemData::hasGamelist() const
|
||||
{
|
||||
return (Utils::FileSystem::exists(getGamelistPath(false)));
|
||||
}
|
||||
|
||||
SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
||||
{
|
||||
unsigned int total = 0;
|
||||
|
@ -927,7 +913,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
|||
// Get a random number in range.
|
||||
std::random_device randDev;
|
||||
// Mersenne Twister pseudorandom number generator.
|
||||
std::mt19937 engine{randDev()};
|
||||
std::mt19937 engine { randDev() };
|
||||
std::uniform_int_distribution<int> uniform_dist(0, total - 1);
|
||||
int target = uniform_dist(engine);
|
||||
|
||||
|
@ -942,8 +928,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (randomSystem == currentSystem);
|
||||
} while (randomSystem == currentSystem);
|
||||
|
||||
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,
|
||||
// otherwise get a list of all the folder and file entries in the view.
|
||||
if (currentGame && currentGame->getType() == FOLDER && currentGame->
|
||||
getSystem()->isGroupedCustomCollection()) {
|
||||
if (currentGame && currentGame->getType() == FOLDER &&
|
||||
currentGame->getSystem()->isGroupedCustomCollection()) {
|
||||
gameList = mRootFolder->getParent()->getChildrenListToDisplay();
|
||||
}
|
||||
else {
|
||||
gameList = ViewController::get()->getGameListView(mRootFolder->
|
||||
getSystem()).get()->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
gameList = ViewController::get()
|
||||
->getGameListView(mRootFolder->getSystem())
|
||||
.get()
|
||||
->getCursor()
|
||||
->getParent()
|
||||
->getChildrenListToDisplay();
|
||||
}
|
||||
|
||||
if (gameList.size() > 0 && gameList.front()->getParent()->getOnlyFoldersFlag())
|
||||
|
@ -1003,11 +992,10 @@ FileData* SystemData::getRandomGame(const FileData* currentGame)
|
|||
// Get a random number in range.
|
||||
std::random_device randDev;
|
||||
// Mersenne Twister pseudorandom number generator.
|
||||
std::mt19937 engine{randDev()};
|
||||
std::mt19937 engine { randDev() };
|
||||
std::uniform_int_distribution<int> uniform_dist(0, total - 1);
|
||||
target = uniform_dist(engine);
|
||||
}
|
||||
while (currentGame && gameList.at(target) == currentGame);
|
||||
} while (currentGame && gameList.at(target) == currentGame);
|
||||
|
||||
return gameList.at(target);
|
||||
}
|
||||
|
@ -1020,23 +1008,25 @@ void SystemData::sortSystem(bool reloadGamelist, bool jumpToFirstRow)
|
|||
bool favoritesSorting;
|
||||
|
||||
if (this->isCustomCollection() ||
|
||||
(this->isCollection() && this->getFullName() == "collections"))
|
||||
(this->isCollection() && this->getFullName() == "collections")) {
|
||||
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
||||
else
|
||||
}
|
||||
else {
|
||||
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
||||
}
|
||||
|
||||
FileData* rootFolder = getRootFolder();
|
||||
// Assign the sort type to all grouped custom collections.
|
||||
if (mIsCollectionSystem && mFullName == "collections") {
|
||||
for (auto it = rootFolder->getChildren().begin();
|
||||
it != rootFolder->getChildren().end(); it++) {
|
||||
for (auto it = rootFolder->getChildren().begin(); // Line break.
|
||||
it != rootFolder->getChildren().end(); it++) {
|
||||
setupSystemSortType((*it)->getSystem()->getRootFolder());
|
||||
}
|
||||
}
|
||||
setupSystemSortType(rootFolder);
|
||||
|
||||
rootFolder->sort(rootFolder->getSortTypeFromString(
|
||||
rootFolder->getSortTypeString()), favoritesSorting);
|
||||
rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()),
|
||||
favoritesSorting);
|
||||
|
||||
if (reloadGamelist)
|
||||
ViewController::get()->reloadGameListView(this, false);
|
||||
|
@ -1079,7 +1069,8 @@ void SystemData::loadTheme()
|
|||
}
|
||||
}
|
||||
|
||||
void SystemData::writeMetaData() {
|
||||
void SystemData::writeMetaData()
|
||||
{
|
||||
if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem)
|
||||
return;
|
||||
|
||||
|
@ -1087,7 +1078,8 @@ void SystemData::writeMetaData() {
|
|||
updateGamelist(this);
|
||||
}
|
||||
|
||||
void SystemData::onMetaDataSavePoint() {
|
||||
void SystemData::onMetaDataSavePoint()
|
||||
{
|
||||
if (Settings::getInstance()->getString("SaveGamelistsMode") != "always")
|
||||
return;
|
||||
|
||||
|
@ -1100,15 +1092,15 @@ void SystemData::setupSystemSortType(FileData* mRootFolder)
|
|||
if (Settings::getInstance()->getString("DefaultSortOrder") != "") {
|
||||
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
|
||||
if (FileSorts::SortTypes.at(i).description ==
|
||||
Settings::getInstance()->getString("DefaultSortOrder")) {
|
||||
mRootFolder->setSortTypeString(Settings::getInstance()->
|
||||
getString("DefaultSortOrder"));
|
||||
Settings::getInstance()->getString("DefaultSortOrder")) {
|
||||
mRootFolder->setSortTypeString(
|
||||
Settings::getInstance()->getString("DefaultSortOrder"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no valid sort type was defined in the configuration file, set to default sorting.
|
||||
if (mRootFolder->getSortTypeString() == "")
|
||||
mRootFolder->setSortTypeString(Settings::getInstance()->
|
||||
getDefaultString("DefaultSortOrder"));
|
||||
mRootFolder->setSortTypeString(
|
||||
Settings::getInstance()->getDefaultString("DefaultSortOrder"));
|
||||
}
|
||||
|
|
|
@ -35,15 +35,14 @@ class FindRules
|
|||
{
|
||||
public:
|
||||
FindRules();
|
||||
~FindRules();
|
||||
|
||||
void loadFindRules();
|
||||
|
||||
private:
|
||||
struct EmulatorRules {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
std::vector<std::string> winRegistryPaths;
|
||||
#endif
|
||||
#endif
|
||||
std::vector<std::string> systemPaths;
|
||||
std::vector<std::string> staticPaths;
|
||||
};
|
||||
|
@ -61,38 +60,41 @@ private:
|
|||
class SystemData
|
||||
{
|
||||
public:
|
||||
SystemData(
|
||||
const std::string& name,
|
||||
const std::string& fullName,
|
||||
SystemEnvironmentData* envData,
|
||||
const std::string& themeFolder,
|
||||
bool CollectionSystem = false,
|
||||
bool CustomCollectionSystem = false);
|
||||
SystemData(const std::string& name,
|
||||
const std::string& fullName,
|
||||
SystemEnvironmentData* envData,
|
||||
const std::string& themeFolder,
|
||||
bool CollectionSystem = false,
|
||||
bool CustomCollectionSystem = false);
|
||||
|
||||
~SystemData();
|
||||
|
||||
inline FileData* getRootFolder() const { return mRootFolder; };
|
||||
inline const std::string& getName() const { return mName; }
|
||||
inline const std::string& getFullName() const { return mFullName; }
|
||||
inline const std::string& getStartPath() const { return mEnvData->mStartPath; }
|
||||
inline const std::vector<std::string>& getExtensions() const
|
||||
{ return mEnvData->mSearchExtensions; }
|
||||
inline const std::string& getThemeFolder() const { return mThemeFolder; }
|
||||
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const
|
||||
{ return mEnvData->mPlatformIds; }
|
||||
inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false;
|
||||
return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id)
|
||||
!= mEnvData->mPlatformIds.cend(); }
|
||||
FileData* getRootFolder() const { return mRootFolder; }
|
||||
const std::string& getName() const { return mName; }
|
||||
const std::string& getFullName() const { return mFullName; }
|
||||
const std::string& getStartPath() const { return mEnvData->mStartPath; }
|
||||
const std::vector<std::string>& getExtensions() const { return mEnvData->mSearchExtensions; }
|
||||
const std::string& getThemeFolder() const { return mThemeFolder; }
|
||||
SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
const std::vector<PlatformIds::PlatformId>& getPlatformIds() const
|
||||
{
|
||||
return mEnvData->mPlatformIds;
|
||||
}
|
||||
bool hasPlatformId(PlatformIds::PlatformId id)
|
||||
{
|
||||
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;
|
||||
bool hasGamelist() const;
|
||||
std::string getThemePath() const;
|
||||
|
||||
std::pair<unsigned int, unsigned int> getDisplayedGameCount() const;
|
||||
bool getScrapeFlag() { return mScrapeFlag; };
|
||||
bool getScrapeFlag() { return mScrapeFlag; }
|
||||
void setScrapeFlag(bool scrapeflag) { mScrapeFlag = scrapeflag; }
|
||||
|
||||
static void deleteSystems();
|
||||
|
@ -106,16 +108,22 @@ public:
|
|||
static std::vector<SystemData*> sSystemVector;
|
||||
static std::unique_ptr<FindRules> sFindRules;
|
||||
|
||||
inline 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.crbegin(), sSystemVector.crend(), this); };
|
||||
inline bool isCollection() { return mIsCollectionSystem; };
|
||||
inline bool isCustomCollection() { return mIsCustomCollectionSystem; };
|
||||
inline bool isGroupedCustomCollection() { return mIsGroupedCustomCollectionSystem; };
|
||||
std::vector<SystemData*>::const_iterator getIterator() const
|
||||
{
|
||||
return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this);
|
||||
}
|
||||
std::vector<SystemData*>::const_reverse_iterator getRevIterator() const
|
||||
{
|
||||
return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this);
|
||||
}
|
||||
bool isCollection() { return mIsCollectionSystem; }
|
||||
bool isCustomCollection() { return mIsCustomCollectionSystem; }
|
||||
bool isGroupedCustomCollection() { return mIsGroupedCustomCollectionSystem; }
|
||||
void setIsGroupedCustomCollection(bool isGroupedCustom)
|
||||
{ mIsGroupedCustomCollectionSystem = isGroupedCustom; };
|
||||
inline bool isGameSystem() { return mIsGameSystem; };
|
||||
{
|
||||
mIsGroupedCustomCollectionSystem = isGroupedCustom;
|
||||
};
|
||||
bool isGameSystem() { return mIsGameSystem; }
|
||||
|
||||
bool isVisible();
|
||||
|
||||
|
@ -123,14 +131,14 @@ public:
|
|||
SystemData* getPrev() const;
|
||||
static SystemData* getRandomSystem(const SystemData* currentSystem);
|
||||
FileData* getRandomGame(const FileData* currentGame = nullptr);
|
||||
FileData* getPlaceholder() { return mPlaceholder; };
|
||||
FileData* getPlaceholder() { return mPlaceholder; }
|
||||
|
||||
void sortSystem(bool reloadGamelist = true, bool jumpToFirstRow = false);
|
||||
|
||||
// Load or re-load theme.
|
||||
void loadTheme();
|
||||
|
||||
FileFilterIndex* getIndex() { return mFilterIndex; };
|
||||
FileFilterIndex* getIndex() { return mFilterIndex; }
|
||||
void onMetaDataSavePoint();
|
||||
void writeMetaData();
|
||||
|
||||
|
@ -141,7 +149,7 @@ private:
|
|||
bool mIsCustomCollectionSystem;
|
||||
bool mIsGroupedCustomCollectionSystem;
|
||||
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 mFullName;
|
||||
SystemEnvironmentData* mEnvData;
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
#if defined(BUILD_VLC_PLAYER)
|
||||
#include "components/VideoVlcComponent.h"
|
||||
#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 "FileData.h"
|
||||
#include "Log.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 <time.h>
|
||||
|
@ -36,24 +36,23 @@
|
|||
|
||||
#define FADE_TIME 300
|
||||
|
||||
SystemScreensaver::SystemScreensaver(
|
||||
Window* window)
|
||||
: mWindow(window),
|
||||
mState(STATE_INACTIVE),
|
||||
mImageScreensaver(nullptr),
|
||||
mVideoScreensaver(nullptr),
|
||||
mCurrentGame(nullptr),
|
||||
mPreviousGame(nullptr),
|
||||
mTimer(0),
|
||||
mMediaSwapTime(0),
|
||||
mTriggerNextGame(false),
|
||||
mHasMediaFiles(false),
|
||||
mFallbackScreensaver(false),
|
||||
mOpacity(0.0f),
|
||||
mDimValue(1.0),
|
||||
mRectangleFadeIn(50),
|
||||
mTextFadeIn(0),
|
||||
mSaturationAmount(1.0)
|
||||
SystemScreensaver::SystemScreensaver(Window* window)
|
||||
: mWindow(window)
|
||||
, mState(STATE_INACTIVE)
|
||||
, mImageScreensaver(nullptr)
|
||||
, mVideoScreensaver(nullptr)
|
||||
, mCurrentGame(nullptr)
|
||||
, mPreviousGame(nullptr)
|
||||
, mTimer(0)
|
||||
, mMediaSwapTime(0)
|
||||
, mTriggerNextGame(false)
|
||||
, mHasMediaFiles(false)
|
||||
, mFallbackScreensaver(false)
|
||||
, mOpacity(0.0f)
|
||||
, mDimValue(1.0)
|
||||
, mRectangleFadeIn(50)
|
||||
, mTextFadeIn(0)
|
||||
, mSaturationAmount(1.0)
|
||||
{
|
||||
mWindow->setScreensaver(this);
|
||||
}
|
||||
|
@ -65,21 +64,6 @@ SystemScreensaver::~SystemScreensaver()
|
|||
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)
|
||||
{
|
||||
std::string path = "";
|
||||
|
@ -146,14 +130,14 @@ void SystemScreensaver::startScreensaver(bool generateMediaList)
|
|||
mImageScreensaver->setImage(path);
|
||||
mImageScreensaver->setOrigin(0.5f, 0.5f);
|
||||
mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
|
||||
Renderer::getScreenHeight() / 2.0f);
|
||||
Renderer::getScreenHeight() / 2.0f);
|
||||
|
||||
if (Settings::getInstance()->getBool("ScreensaverStretchImages"))
|
||||
mImageScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
else
|
||||
mImageScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
}
|
||||
mTimer = 0;
|
||||
return;
|
||||
|
@ -179,7 +163,7 @@ void SystemScreensaver::startScreensaver(bool generateMediaList)
|
|||
if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo"))
|
||||
generateOverlayInfo();
|
||||
|
||||
#if defined(_RPI_)
|
||||
#if defined(_RPI_)
|
||||
// Create the correct type of video component.
|
||||
if (Settings::getInstance()->getBool("ScreensaverOmxPlayer"))
|
||||
mVideoScreensaver = new VideoOmxComponent(mWindow);
|
||||
|
@ -187,26 +171,26 @@ void SystemScreensaver::startScreensaver(bool generateMediaList)
|
|||
mVideoScreensaver = new VideoVlcComponent(mWindow);
|
||||
else
|
||||
mVideoScreensaver = new VideoFFmpegComponent(mWindow);
|
||||
#elif defined(BUILD_VLC_PLAYER)
|
||||
#elif defined(BUILD_VLC_PLAYER)
|
||||
if (Settings::getInstance()->getString("VideoPlayer") == "vlc")
|
||||
mVideoScreensaver = new VideoVlcComponent(mWindow);
|
||||
else
|
||||
mVideoScreensaver = new VideoFFmpegComponent(mWindow);
|
||||
#else
|
||||
#else
|
||||
mVideoScreensaver = new VideoFFmpegComponent(mWindow);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mVideoScreensaver->topWindow(true);
|
||||
mVideoScreensaver->setOrigin(0.5f, 0.5f);
|
||||
mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
|
||||
Renderer::getScreenHeight() / 2.0f);
|
||||
Renderer::getScreenHeight() / 2.0f);
|
||||
|
||||
if (Settings::getInstance()->getBool("ScreensaverStretchVideos"))
|
||||
mVideoScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
else
|
||||
mVideoScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
|
||||
mVideoScreensaver->setVideo(path);
|
||||
mVideoScreensaver->setScreensaverMode(true);
|
||||
|
@ -229,16 +213,17 @@ void SystemScreensaver::stopScreensaver()
|
|||
|
||||
mState = STATE_INACTIVE;
|
||||
|
||||
mDimValue = 1.0;
|
||||
mDimValue = 1.0f;
|
||||
mRectangleFadeIn = 50;
|
||||
mTextFadeIn = 0;
|
||||
mSaturationAmount = 1.0;
|
||||
mSaturationAmount = 1.0f;
|
||||
|
||||
if (mGameOverlay)
|
||||
mGameOverlay.reset();
|
||||
}
|
||||
|
||||
void SystemScreensaver::nextGame() {
|
||||
void SystemScreensaver::nextGame()
|
||||
{
|
||||
stopScreensaver();
|
||||
startScreensaver(false);
|
||||
}
|
||||
|
@ -249,8 +234,8 @@ void SystemScreensaver::launchGame()
|
|||
// Launching game
|
||||
ViewController::get()->triggerGameLaunch(mCurrentGame);
|
||||
ViewController::get()->goToGameList(mCurrentGame->getSystem());
|
||||
IGameListView* view = ViewController::get()->
|
||||
getGameListView(mCurrentGame->getSystem()).get();
|
||||
IGameListView* view =
|
||||
ViewController::get()->getGameListView(mCurrentGame->getSystem()).get();
|
||||
view->setCursor(mCurrentGame);
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
}
|
||||
|
@ -261,8 +246,8 @@ void SystemScreensaver::goToGame()
|
|||
if (mCurrentGame != nullptr) {
|
||||
// Go to the game in the gamelist view, but don't launch it.
|
||||
ViewController::get()->goToGameList(mCurrentGame->getSystem());
|
||||
IGameListView* view = ViewController::get()->
|
||||
getGameListView(mCurrentGame->getSystem()).get();
|
||||
IGameListView* view =
|
||||
ViewController::get()->getGameListView(mCurrentGame->getSystem()).get();
|
||||
view->setCursor(mCurrentGame);
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
}
|
||||
|
@ -276,7 +261,7 @@ void SystemScreensaver::renderScreensaver()
|
|||
// Render a black background below the video.
|
||||
Renderer::setMatrix(Transform4x4f::Identity());
|
||||
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.
|
||||
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
|
||||
|
@ -288,7 +273,7 @@ void SystemScreensaver::renderScreensaver()
|
|||
// Render a black background below the image.
|
||||
Renderer::setMatrix(Transform4x4f::Identity());
|
||||
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.
|
||||
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
|
||||
|
@ -305,20 +290,20 @@ void SystemScreensaver::renderScreensaver()
|
|||
Renderer::setMatrix(Transform4x4f::Identity());
|
||||
if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") {
|
||||
if (mHasMediaFiles) {
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
if (Settings::getInstance()->getBool("ScreensaverSlideshowScanlines"))
|
||||
Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
|
||||
#endif
|
||||
#endif
|
||||
if (Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo") &&
|
||||
mGameOverlay) {
|
||||
mGameOverlay) {
|
||||
if (mGameOverlayRectangleCoords.size() == 4) {
|
||||
Renderer::drawRect(mGameOverlayRectangleCoords[0],
|
||||
mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2],
|
||||
mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn,
|
||||
0x00000000 | mRectangleFadeIn );
|
||||
Renderer::drawRect(
|
||||
mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1],
|
||||
mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3],
|
||||
0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn);
|
||||
}
|
||||
mRectangleFadeIn = Math::clamp(mRectangleFadeIn + 6 +
|
||||
mRectangleFadeIn / 20, 0, 170);
|
||||
mRectangleFadeIn =
|
||||
Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
|
||||
|
||||
mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn);
|
||||
if (mTextFadeIn > 50)
|
||||
|
@ -333,7 +318,7 @@ void SystemScreensaver::renderScreensaver()
|
|||
}
|
||||
else if (Settings::getInstance()->getString("ScreensaverType") == "video") {
|
||||
if (mHasMediaFiles) {
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
Renderer::shaderParameters videoParameters;
|
||||
unsigned int shaders = 0;
|
||||
if (Settings::getInstance()->getBool("ScreensaverVideoScanlines"))
|
||||
|
@ -341,6 +326,7 @@ void SystemScreensaver::renderScreensaver()
|
|||
if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
|
||||
shaders |= Renderer::SHADER_BLUR_HORIZONTAL;
|
||||
float heightModifier = Renderer::getScreenHeightModifier();
|
||||
// clang-format off
|
||||
if (heightModifier < 1)
|
||||
videoParameters.blurPasses = 2; // Below 1080
|
||||
else if (heightModifier >= 4)
|
||||
|
@ -355,21 +341,22 @@ void SystemScreensaver::renderScreensaver()
|
|||
videoParameters.blurPasses = 3; // 1440
|
||||
else if (heightModifier >= 1)
|
||||
videoParameters.blurPasses = 2; // 1080
|
||||
// clang-format on
|
||||
}
|
||||
Renderer::shaderPostprocessing(shaders, videoParameters);
|
||||
#endif
|
||||
#endif
|
||||
if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) {
|
||||
if (mGameOverlayRectangleCoords.size() == 4) {
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
Renderer::shaderPostprocessing(Renderer::SHADER_OPACITY);
|
||||
#endif
|
||||
Renderer::drawRect(mGameOverlayRectangleCoords[0],
|
||||
mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2],
|
||||
mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn,
|
||||
0x00000000 | mRectangleFadeIn );
|
||||
#endif
|
||||
Renderer::drawRect(
|
||||
mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1],
|
||||
mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3],
|
||||
0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn);
|
||||
}
|
||||
mRectangleFadeIn = Math::clamp(mRectangleFadeIn + 6 +
|
||||
mRectangleFadeIn / 20, 0, 170);
|
||||
mRectangleFadeIn =
|
||||
Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
|
||||
|
||||
mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn);
|
||||
if (mTextFadeIn > 50)
|
||||
|
@ -383,8 +370,8 @@ void SystemScreensaver::renderScreensaver()
|
|||
}
|
||||
}
|
||||
if (mFallbackScreensaver ||
|
||||
Settings::getInstance()->getString("ScreensaverType") == "dim") {
|
||||
#if defined(USE_OPENGL_21)
|
||||
Settings::getInstance()->getString("ScreensaverType") == "dim") {
|
||||
#if defined(USE_OPENGL_21)
|
||||
Renderer::shaderParameters dimParameters;
|
||||
dimParameters.fragmentDimValue = mDimValue;
|
||||
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters);
|
||||
|
@ -394,22 +381,22 @@ void SystemScreensaver::renderScreensaver()
|
|||
Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters);
|
||||
if (mSaturationAmount > 0.0)
|
||||
mSaturationAmount = Math::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f);
|
||||
#else
|
||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
||||
Renderer::getScreenHeight(), 0x000000A0, 0x000000A0);
|
||||
#endif
|
||||
#else
|
||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
|
||||
0x000000A0, 0x000000A0);
|
||||
#endif
|
||||
}
|
||||
else if (Settings::getInstance()->getString("ScreensaverType") == "black") {
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
Renderer::shaderParameters blackParameters;
|
||||
blackParameters.fragmentDimValue = mDimValue;
|
||||
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters);
|
||||
if (mDimValue > 0.0)
|
||||
mDimValue = Math::clamp(mDimValue - 0.045f, 0.0f, 1.0f);
|
||||
#else
|
||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
||||
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
|
||||
#endif
|
||||
#else
|
||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
|
||||
0x000000FF, 0x000000FF);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -458,8 +445,8 @@ void SystemScreensaver::update(int deltaTime)
|
|||
|
||||
void SystemScreensaver::generateImageList()
|
||||
{
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
// We only want nodes from game systems that are not collections.
|
||||
if (!(*it)->isGameSystem() || (*it)->isCollection())
|
||||
continue;
|
||||
|
@ -475,8 +462,8 @@ void SystemScreensaver::generateImageList()
|
|||
|
||||
void SystemScreensaver::generateVideoList()
|
||||
{
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
// We only want nodes from game systems that are not collections.
|
||||
if (!(*it)->isGameSystem() || (*it)->isCollection())
|
||||
continue;
|
||||
|
@ -493,7 +480,7 @@ void SystemScreensaver::generateVideoList()
|
|||
void SystemScreensaver::generateCustomImageList()
|
||||
{
|
||||
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
|
||||
// directory or the ROM directory.
|
||||
|
@ -503,7 +490,7 @@ void SystemScreensaver::generateCustomImageList()
|
|||
if (imageDir != "" && Utils::FileSystem::isDirectory(imageDir)) {
|
||||
std::string imageFilter = ".jpg, .JPG, .png, .PNG";
|
||||
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++) {
|
||||
if (Utils::FileSystem::isRegularFile(*it)) {
|
||||
|
@ -513,8 +500,7 @@ void SystemScreensaver::generateCustomImageList()
|
|||
}
|
||||
}
|
||||
else {
|
||||
LOG(LogWarning) << "Custom screensaver image directory '" <<
|
||||
imageDir << "' does not exist.";
|
||||
LOG(LogWarning) << "Custom screensaver image directory '" << imageDir << "' does not exist";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,12 +526,11 @@ void SystemScreensaver::pickRandomImage(std::string& path)
|
|||
// Get a random number in range.
|
||||
std::random_device randDev;
|
||||
// Mersenne Twister pseudorandom number generator.
|
||||
std::mt19937 engine{randDev()};
|
||||
std::uniform_int_distribution<int>
|
||||
uniform_dist(0, static_cast<int>(mImageFiles.size()) - 1);
|
||||
std::mt19937 engine { randDev() };
|
||||
std::uniform_int_distribution<int> uniform_dist(0,
|
||||
static_cast<int>(mImageFiles.size()) - 1);
|
||||
index = uniform_dist(engine);
|
||||
}
|
||||
while (mPreviousGame && mImageFiles.at(index) == mPreviousGame);
|
||||
} while (mPreviousGame && mImageFiles.at(index) == mPreviousGame);
|
||||
|
||||
path = mImageFiles.at(index)->getImagePath();
|
||||
mGameName = mImageFiles.at(index)->getName();
|
||||
|
@ -575,12 +560,11 @@ void SystemScreensaver::pickRandomVideo(std::string& path)
|
|||
// Get a random number in range.
|
||||
std::random_device randDev;
|
||||
// Mersenne Twister pseudorandom number generator.
|
||||
std::mt19937 engine{randDev()};
|
||||
std::uniform_int_distribution<int>
|
||||
uniform_dist(0, static_cast<int>(mVideoFiles.size()) - 1);
|
||||
std::mt19937 engine { randDev() };
|
||||
std::uniform_int_distribution<int> uniform_dist(0,
|
||||
static_cast<int>(mVideoFiles.size()) - 1);
|
||||
index = uniform_dist(engine);
|
||||
}
|
||||
while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame);
|
||||
} while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame);
|
||||
|
||||
path = mVideoFiles.at(index)->getVideoPath();
|
||||
mGameName = mVideoFiles.at(index)->getName();
|
||||
|
@ -604,12 +588,11 @@ void SystemScreensaver::pickRandomCustomImage(std::string& path)
|
|||
// Get a random number in range.
|
||||
std::random_device randDev;
|
||||
// Mersenne Twister pseudorandom number generator.
|
||||
std::mt19937 engine{randDev()};
|
||||
std::uniform_int_distribution<int>
|
||||
uniform_dist(0, static_cast<int>(mImageCustomFiles.size()) - 1);
|
||||
std::mt19937 engine { randDev() };
|
||||
std::uniform_int_distribution<int> uniform_dist(
|
||||
0, static_cast<int>(mImageCustomFiles.size()) - 1);
|
||||
index = uniform_dist(engine);
|
||||
}
|
||||
while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage);
|
||||
} while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage);
|
||||
|
||||
path = mImageCustomFiles.at(index);
|
||||
mPreviousCustomImage = path;
|
||||
|
@ -633,8 +616,8 @@ void SystemScreensaver::generateOverlayInfo()
|
|||
const std::string systemName = Utils::String::toUpper(mSystemName);
|
||||
const std::string overlayText = gameName + "\n" + systemName;
|
||||
|
||||
mGameOverlay = std::unique_ptr<TextCache>(mGameOverlayFont.at(0)->
|
||||
buildTextCache(overlayText, posX, posY, 0xFFFFFFFF));
|
||||
mGameOverlay = std::unique_ptr<TextCache>(
|
||||
mGameOverlayFont.at(0)->buildTextCache(overlayText, posX, posY, 0xFFFFFFFF));
|
||||
|
||||
float textSizeX;
|
||||
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
|
||||
// fine for the time being.
|
||||
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();
|
||||
else
|
||||
textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x();
|
||||
|
|
|
@ -22,9 +22,12 @@ public:
|
|||
SystemScreensaver(Window* window);
|
||||
virtual ~SystemScreensaver();
|
||||
|
||||
virtual bool allowSleep();
|
||||
virtual bool isScreensaverActive();
|
||||
virtual bool isFallbackScreensaver();
|
||||
virtual bool allowSleep()
|
||||
{
|
||||
return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr));
|
||||
}
|
||||
virtual bool isScreensaverActive() { return (mState != STATE_INACTIVE); }
|
||||
virtual bool isFallbackScreensaver() { return mFallbackScreensaver; }
|
||||
|
||||
virtual void startScreensaver(bool generateMediaList);
|
||||
virtual void stopScreensaver();
|
||||
|
@ -35,8 +38,8 @@ public:
|
|||
virtual void renderScreensaver();
|
||||
virtual void update(int deltaTime);
|
||||
|
||||
virtual FileData* getCurrentGame() { return mCurrentGame; };
|
||||
virtual void triggerNextGame() { mTriggerNextGame = true; };
|
||||
virtual FileData* getCurrentGame() { return mCurrentGame; }
|
||||
virtual void triggerNextGame() { mTriggerNextGame = true; }
|
||||
|
||||
private:
|
||||
void generateImageList();
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include "VolumeControl.h"
|
||||
|
||||
#include "math/Misc.h"
|
||||
#include "Log.h"
|
||||
#include "math/Misc.h"
|
||||
|
||||
#if defined(_RPI_)
|
||||
#include "Settings.h"
|
||||
|
@ -38,15 +38,15 @@ std::string VolumeControl::mixerCard = "default";
|
|||
VolumeControl* VolumeControl::sInstance = nullptr;
|
||||
|
||||
VolumeControl::VolumeControl()
|
||||
#if defined(__linux__)
|
||||
: mixerIndex(0),
|
||||
mixerHandle(nullptr),
|
||||
mixerElem(nullptr),
|
||||
mixerSelemId(nullptr)
|
||||
#elif defined(_WIN64)
|
||||
: mixerHandle(nullptr),
|
||||
endpointVolume(nullptr)
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
: mixerIndex(0)
|
||||
, mixerHandle(nullptr)
|
||||
, mixerElem(nullptr)
|
||||
, mixerSelemId(nullptr)
|
||||
#elif defined(_WIN64)
|
||||
: mixerHandle(nullptr)
|
||||
, endpointVolume(nullptr)
|
||||
#endif
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
@ -54,9 +54,9 @@ VolumeControl::VolumeControl()
|
|||
VolumeControl::~VolumeControl()
|
||||
{
|
||||
deinit();
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
snd_config_update_free_global();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
VolumeControl* VolumeControl::getInstance()
|
||||
|
@ -79,14 +79,15 @@ void VolumeControl::deleteInstance()
|
|||
void VolumeControl::init()
|
||||
{
|
||||
// Initialize audio mixer interface.
|
||||
#if defined(__linux__)
|
||||
|
||||
#if defined(__linux__)
|
||||
// Try to open mixer device.
|
||||
if (mixerHandle == nullptr) {
|
||||
#if defined(_RPI_)
|
||||
// Allow user to override the AudioCard and AudioDevice in es_settings.xml.
|
||||
#if defined(_RPI_)
|
||||
mixerCard = Settings::getInstance()->getString("AudioCard");
|
||||
mixerName = Settings::getInstance()->getString("AudioDevice");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
snd_mixer_selem_id_alloca(&mixerSelemId);
|
||||
// Sets simple-mixer index and name.
|
||||
|
@ -106,8 +107,8 @@ void VolumeControl::init()
|
|||
LOG(LogDebug) << "VolumeControl::init(): Mixer initialized";
|
||||
}
|
||||
else {
|
||||
LOG(LogError) <<
|
||||
"VolumeControl::init(): Failed to find mixer elements!";
|
||||
LOG(LogError)
|
||||
<< "VolumeControl::init(): Failed to find mixer elements!";
|
||||
snd_mixer_close(mixerHandle);
|
||||
mixerHandle = nullptr;
|
||||
}
|
||||
|
@ -119,8 +120,8 @@ void VolumeControl::init()
|
|||
}
|
||||
}
|
||||
else {
|
||||
LOG(LogError) <<
|
||||
"VolumeControl::init(): Failed to register simple element class!";
|
||||
LOG(LogError)
|
||||
<< "VolumeControl::init(): Failed to register simple element class!";
|
||||
snd_mixer_close(mixerHandle);
|
||||
mixerHandle = nullptr;
|
||||
}
|
||||
|
@ -135,31 +136,30 @@ void VolumeControl::init()
|
|||
LOG(LogError) << "VolumeControl::init(): Failed to open ALSA mixer!";
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN64)
|
||||
#elif defined(_WIN64)
|
||||
// Windows Vista or above.
|
||||
if (endpointVolume == nullptr) {
|
||||
CoInitialize(nullptr);
|
||||
IMMDeviceEnumerator* deviceEnumerator = nullptr;
|
||||
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IMMDeviceEnumerator), reinterpret_cast<LPVOID *>(&deviceEnumerator));
|
||||
__uuidof(IMMDeviceEnumerator),
|
||||
reinterpret_cast<LPVOID*>(&deviceEnumerator));
|
||||
if (deviceEnumerator != nullptr) {
|
||||
// Get default endpoint.
|
||||
IMMDevice * defaultDevice = nullptr;
|
||||
IMMDevice* defaultDevice = nullptr;
|
||||
deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
|
||||
if (defaultDevice != nullptr) {
|
||||
// Retrieve endpoint volume.
|
||||
defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
|
||||
CLSCTX_INPROC_SERVER, nullptr,
|
||||
reinterpret_cast<LPVOID *>(&endpointVolume));
|
||||
defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER,
|
||||
nullptr, reinterpret_cast<LPVOID*>(&endpointVolume));
|
||||
if (endpointVolume == nullptr)
|
||||
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.
|
||||
defaultDevice->Release();
|
||||
}
|
||||
else {
|
||||
LOG(LogError) <<
|
||||
"VolumeControl::init(): Failed to get default audio endpoint!";
|
||||
LOG(LogError) << "VolumeControl::init(): Failed to get default audio endpoint!";
|
||||
}
|
||||
// Release device enumerator. we don't need it anymore.
|
||||
deviceEnumerator->Release();
|
||||
|
@ -169,13 +169,14 @@ void VolumeControl::init()
|
|||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void VolumeControl::deinit()
|
||||
{
|
||||
// Deinitialize audio mixer interface.
|
||||
#if defined(__linux__)
|
||||
|
||||
#if defined(__linux__)
|
||||
if (mixerHandle != nullptr) {
|
||||
snd_mixer_detach(mixerHandle, mixerCard.c_str());
|
||||
snd_mixer_free(mixerHandle);
|
||||
|
@ -183,28 +184,28 @@ void VolumeControl::deinit()
|
|||
mixerHandle = nullptr;
|
||||
mixerElem = nullptr;
|
||||
}
|
||||
#elif defined(_WIN64)
|
||||
#elif defined(_WIN64)
|
||||
if (endpointVolume != nullptr) {
|
||||
endpointVolume->Release();
|
||||
endpointVolume = nullptr;
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int VolumeControl::getVolume() const
|
||||
{
|
||||
int volume = 0;
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (mixerElem != nullptr) {
|
||||
// Get volume range.
|
||||
long minVolume;
|
||||
long maxVolume;
|
||||
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
|
||||
long rawVolume;
|
||||
if (snd_mixer_selem_get_playback_volume(mixerElem,
|
||||
SND_MIXER_SCHN_MONO, &rawVolume) == 0) {
|
||||
if (snd_mixer_selem_get_playback_volume(mixerElem, SND_MIXER_SCHN_MONO, &rawVolume) ==
|
||||
0) {
|
||||
// Bring into range 0-100.
|
||||
rawVolume -= minVolume;
|
||||
if (rawVolume > 0)
|
||||
|
@ -218,7 +219,7 @@ int VolumeControl::getVolume() const
|
|||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range";
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN64)
|
||||
#elif defined(_WIN64)
|
||||
if (endpointVolume != nullptr) {
|
||||
// Windows Vista or above, uses EndpointVolume API.
|
||||
float floatVolume = 0.0f; // 0-1
|
||||
|
@ -230,7 +231,7 @@ int VolumeControl::getVolume() const
|
|||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get master volume!";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
volume = Math::clamp(volume, 0, 100);
|
||||
return volume;
|
||||
|
@ -240,7 +241,7 @@ void VolumeControl::setVolume(int volume)
|
|||
{
|
||||
volume = Math::clamp(volume, 0, 100);
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (mixerElem != nullptr) {
|
||||
// Get volume range.
|
||||
long minVolume;
|
||||
|
@ -248,10 +249,10 @@ void VolumeControl::setVolume(int volume)
|
|||
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
|
||||
// Bring into minVolume-maxVolume range and set.
|
||||
long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume;
|
||||
if (snd_mixer_selem_set_playback_volume(mixerElem,
|
||||
SND_MIXER_SCHN_FRONT_LEFT, rawVolume) < 0 ||
|
||||
snd_mixer_selem_set_playback_volume(mixerElem,
|
||||
SND_MIXER_SCHN_FRONT_RIGHT, rawVolume) < 0) {
|
||||
if (snd_mixer_selem_set_playback_volume(mixerElem, SND_MIXER_SCHN_FRONT_LEFT,
|
||||
rawVolume) < 0 ||
|
||||
snd_mixer_selem_set_playback_volume(mixerElem, SND_MIXER_SCHN_FRONT_RIGHT,
|
||||
rawVolume) < 0) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN64)
|
||||
#elif defined(_WIN64)
|
||||
if (endpointVolume != nullptr) {
|
||||
// Windows Vista or above, uses EndpointVolume API.
|
||||
float floatVolume = 0.0f; // 0-1
|
||||
|
@ -268,5 +269,5 @@ void VolumeControl::setVolume(int volume)
|
|||
if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK)
|
||||
LOG(LogError) << "VolumeControl::setVolume(): Failed to set master volume";
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -38,18 +38,18 @@ public:
|
|||
|
||||
static VolumeControl* sInstance;
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
static std::string mixerName;
|
||||
static std::string mixerCard;
|
||||
int mixerIndex;
|
||||
snd_mixer_t* mixerHandle;
|
||||
snd_mixer_elem_t* mixerElem;
|
||||
snd_mixer_selem_id_t* mixerSelemId;
|
||||
#elif defined(_WIN64)
|
||||
#elif defined(_WIN64)
|
||||
HMIXER mixerHandle;
|
||||
MIXERCONTROL mixerControl;
|
||||
IAudioEndpointVolume * endpointVolume;
|
||||
#endif
|
||||
IAudioEndpointVolume* endpointVolume;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // ES_APP_VOLUME_CONTROL_H
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
class MoveCameraAnimation : public Animation
|
||||
{
|
||||
public:
|
||||
MoveCameraAnimation(
|
||||
Transform4x4f& camera,
|
||||
const Vector3f& target)
|
||||
: mCameraStart(camera),
|
||||
mTarget(target),
|
||||
cameraOut(camera) {}
|
||||
MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target)
|
||||
: mCameraStart(camera)
|
||||
, mTarget(target)
|
||||
, cameraOut(camera)
|
||||
{
|
||||
}
|
||||
|
||||
int getDuration() const override { return 400; }
|
||||
|
||||
|
@ -28,7 +28,7 @@ public:
|
|||
// Cubic ease out.
|
||||
t -= 1;
|
||||
cameraOut.translation() =
|
||||
-Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1);
|
||||
-Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "guis/GuiCollectionSystemsOptions.h"
|
||||
|
||||
#include "CollectionSystemsManager.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
|
@ -16,21 +17,23 @@
|
|||
#include "guis/GuiTextEditPopup.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "CollectionSystemsManager.h"
|
||||
|
||||
GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
||||
Window* window,
|
||||
std::string title)
|
||||
: GuiSettings(window, title),
|
||||
mAddedCustomCollection(false),
|
||||
mDeletedCustomCollection(false)
|
||||
GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::string title)
|
||||
: GuiSettings(window, title)
|
||||
, mAddedCustomCollection(false)
|
||||
, mDeletedCustomCollection(false)
|
||||
{
|
||||
// Finish editing custom collection.
|
||||
if (CollectionSystemsManager::get()->isEditing()) {
|
||||
ComponentListRow row;
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" +
|
||||
Utils::String::toUpper(CollectionSystemsManager::get()->getEditingCollection()) +
|
||||
"' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow,
|
||||
"FINISH EDITING '" +
|
||||
Utils::String::toUpper(
|
||||
CollectionSystemsManager::get()->getEditingCollection()) +
|
||||
"' COLLECTION",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.makeAcceptInputHandler([this] {
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
mWindow->invalidateCachedBackground();
|
||||
|
@ -40,19 +43,20 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
}
|
||||
|
||||
// Automatic collections.
|
||||
collection_systems_auto = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
|
||||
collection_systems_auto = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
|
||||
std::map<std::string, CollectionSystemData, stringComparator> autoSystems =
|
||||
CollectionSystemsManager::get()->getAutoCollectionSystems();
|
||||
CollectionSystemsManager::get()->getAutoCollectionSystems();
|
||||
// Add automatic systems.
|
||||
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator
|
||||
it = autoSystems.cbegin(); it != autoSystems.cend() ; it++)
|
||||
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator it =
|
||||
autoSystems.cbegin();
|
||||
it != autoSystems.cend(); it++)
|
||||
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);
|
||||
addSaveFunc([this, autoSystems] {
|
||||
std::string autoSystemsSelected = Utils::String::vectorToDelimitedString(
|
||||
collection_systems_auto->getSelectedObjects(), ",", true);
|
||||
collection_systems_auto->getSelectedObjects(), ",", true);
|
||||
std::string autoSystemsConfig = Settings::getInstance()->getString("CollectionSystemsAuto");
|
||||
if (autoSystemsSelected != autoSystemsConfig) {
|
||||
if (CollectionSystemsManager::get()->isEditing())
|
||||
|
@ -67,19 +71,19 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
}
|
||||
else if (autoSystemsSelected != "") {
|
||||
std::vector<std::string> selectedVector =
|
||||
Utils::String::delimitedStringToVector(autoSystemsSelected, ",");
|
||||
Utils::String::delimitedStringToVector(autoSystemsSelected, ",");
|
||||
std::vector<std::string> configuredVector =
|
||||
Utils::String::delimitedStringToVector(autoSystemsConfig, ",");
|
||||
Utils::String::delimitedStringToVector(autoSystemsConfig, ",");
|
||||
for (std::string system : selectedVector) {
|
||||
if (std::find(configuredVector.begin(), configuredVector.end(), system) ==
|
||||
configuredVector.end())
|
||||
configuredVector.end())
|
||||
addedAutoSystems.push_back(system);
|
||||
}
|
||||
}
|
||||
if (!addedAutoSystems.empty()) {
|
||||
for (std::string system : addedAutoSystems)
|
||||
CollectionSystemsManager::get()->
|
||||
repopulateCollection(autoSystems.find(system)->second.system);
|
||||
CollectionSystemsManager::get()->repopulateCollection(
|
||||
autoSystems.find(system)->second.system);
|
||||
}
|
||||
setNeedsSaving();
|
||||
setNeedsReloading();
|
||||
|
@ -88,50 +92,52 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
});
|
||||
|
||||
// Custom collections.
|
||||
collection_systems_custom = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
|
||||
collection_systems_custom = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
|
||||
std::map<std::string, CollectionSystemData, stringComparator> customSystems =
|
||||
CollectionSystemsManager::get()->getCustomCollectionSystems();
|
||||
CollectionSystemsManager::get()->getCustomCollectionSystems();
|
||||
// Add custom systems.
|
||||
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator
|
||||
it = customSystems.cbegin(); it != customSystems.cend() ; it++)
|
||||
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator it =
|
||||
customSystems.cbegin();
|
||||
it != customSystems.cend(); it++)
|
||||
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);
|
||||
addSaveFunc([this, customSystems] {
|
||||
if (!mDeletedCustomCollection) {
|
||||
std::string customSystemsSelected = Utils::String::vectorToDelimitedString(
|
||||
collection_systems_custom->getSelectedObjects(), ",", true);
|
||||
std::string customSystemsConfig = Settings::getInstance()->
|
||||
getString("CollectionSystemsCustom");
|
||||
collection_systems_custom->getSelectedObjects(), ",", true);
|
||||
std::string customSystemsConfig =
|
||||
Settings::getInstance()->getString("CollectionSystemsCustom");
|
||||
if (customSystemsSelected != customSystemsConfig) {
|
||||
if (CollectionSystemsManager::get()->isEditing())
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
Settings::getInstance()->setString("CollectionSystemsCustom",
|
||||
customSystemsSelected);
|
||||
customSystemsSelected);
|
||||
// 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
|
||||
// collections aren't updated while they are disabled.
|
||||
std::vector<std::string> addedCustomSystems;
|
||||
if (customSystemsConfig == "") {
|
||||
addedCustomSystems =
|
||||
Utils::String::delimitedStringToVector(customSystemsSelected, ",");
|
||||
Utils::String::delimitedStringToVector(customSystemsSelected, ",");
|
||||
}
|
||||
else if (customSystemsSelected != "") {
|
||||
std::vector<std::string> selectedVector =
|
||||
Utils::String::delimitedStringToVector(customSystemsSelected, ",");
|
||||
Utils::String::delimitedStringToVector(customSystemsSelected, ",");
|
||||
std::vector<std::string> configuredVector =
|
||||
Utils::String::delimitedStringToVector(customSystemsConfig, ",");
|
||||
Utils::String::delimitedStringToVector(customSystemsConfig, ",");
|
||||
for (std::string system : selectedVector) {
|
||||
if (std::find(configuredVector.begin(), configuredVector.end(), system) ==
|
||||
configuredVector.end())
|
||||
configuredVector.end())
|
||||
addedCustomSystems.push_back(system);
|
||||
}
|
||||
}
|
||||
if (!mAddedCustomCollection && !addedCustomSystems.empty()) {
|
||||
for (std::string system : addedCustomSystems)
|
||||
CollectionSystemsManager::get()->
|
||||
repopulateCollection(customSystems.find(system)->second.system);
|
||||
CollectionSystemsManager::get()->repopulateCollection(
|
||||
customSystems.find(system)->second.system);
|
||||
}
|
||||
setNeedsSaving();
|
||||
setNeedsReloading();
|
||||
|
@ -143,32 +149,33 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
|
||||
// Create custom collection from theme.
|
||||
std::vector<std::string> unusedFolders =
|
||||
CollectionSystemsManager::get()->getUnusedSystemsFromTheme();
|
||||
CollectionSystemsManager::get()->getUnusedSystemsFromTheme();
|
||||
if (unusedFolders.size() > 0) {
|
||||
ComponentListRow row;
|
||||
auto themeCollection = std::make_shared<TextComponent>(mWindow,
|
||||
"CREATE NEW CUSTOM COLLECTION FROM THEME", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto themeCollection =
|
||||
std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION FROM THEME",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto bracketThemeCollection = std::make_shared<ImageComponent>(mWindow);
|
||||
bracketThemeCollection->setImage(":/graphics/arrow.svg");
|
||||
bracketThemeCollection->setResize(Vector2f(0,
|
||||
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
|
||||
bracketThemeCollection->setResize(
|
||||
Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
|
||||
row.addElement(themeCollection, true);
|
||||
row.addElement(bracketThemeCollection, false);
|
||||
row.makeAcceptInputHandler([this, unusedFolders] {
|
||||
auto ss = new GuiSettings(mWindow, "SELECT THEME FOLDER");
|
||||
std::shared_ptr<OptionListComponent<std::string>> folderThemes =
|
||||
std::make_shared<OptionListComponent<std::string>>(mWindow,
|
||||
getHelpStyle(), "SELECT THEME FOLDER", true);
|
||||
std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(),
|
||||
"SELECT THEME FOLDER", true);
|
||||
// Add custom systems.
|
||||
for (auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) {
|
||||
for (auto it = unusedFolders.cbegin(); it != unusedFolders.cend(); it++) {
|
||||
ComponentListRow row;
|
||||
std::string name = *it;
|
||||
std::function<void()> createCollectionCall = [this, name] {
|
||||
createCustomCollection(name);
|
||||
};
|
||||
row.makeAcceptInputHandler(createCollectionCall);
|
||||
auto themeFolder = std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
auto themeFolder = std::make_shared<TextComponent>(
|
||||
mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
row.addElement(themeFolder, true);
|
||||
// This transparent bracket is only added to generate the correct help prompts.
|
||||
auto bracket = std::make_shared<ImageComponent>(mWindow);
|
||||
|
@ -184,12 +191,11 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
|
||||
// Create new custom collection.
|
||||
ComponentListRow row;
|
||||
auto newCollection = std::make_shared<TextComponent>(mWindow,
|
||||
"CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto newCollection = std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto bracketNewCollection = std::make_shared<ImageComponent>(mWindow);
|
||||
bracketNewCollection->setImage(":/graphics/arrow.svg");
|
||||
bracketNewCollection->setResize(Vector2f(0,
|
||||
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
|
||||
bracketNewCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
|
||||
row.addElement(newCollection, true);
|
||||
row.addElement(bracketNewCollection, false);
|
||||
auto createCollectionCall = [this](const std::string& newVal) {
|
||||
|
@ -202,72 +208,72 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
createCustomCollection(name);
|
||||
};
|
||||
row.makeAcceptInputHandler([this, createCollectionCall] {
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(),
|
||||
"New Collection Name", "", createCollectionCall, false, "SAVE"));
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name", "",
|
||||
createCollectionCall, false, "SAVE"));
|
||||
});
|
||||
addRow(row);
|
||||
|
||||
// Delete custom collection.
|
||||
row.elements.clear();
|
||||
auto deleteCollection = std::make_shared<TextComponent>(mWindow,
|
||||
"DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto deleteCollection = std::make_shared<TextComponent>(
|
||||
mWindow, "DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto bracketDeleteCollection = std::make_shared<ImageComponent>(mWindow);
|
||||
bracketDeleteCollection->setImage(":/graphics/arrow.svg");
|
||||
bracketDeleteCollection->setResize(Vector2f(0,
|
||||
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
|
||||
bracketDeleteCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
|
||||
row.addElement(deleteCollection, true);
|
||||
row.addElement(bracketDeleteCollection, false);
|
||||
row.makeAcceptInputHandler([this, customSystems] {
|
||||
auto ss = new GuiSettings(mWindow, "SELECT COLLECTION TO DELETE");
|
||||
std::shared_ptr<OptionListComponent<std::string>> customCollections =
|
||||
std::make_shared<OptionListComponent<std::string>>(mWindow,
|
||||
getHelpStyle(), "", true);
|
||||
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator
|
||||
it = customSystems.cbegin(); it != customSystems.cend() ; it++) {
|
||||
std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(), "", true);
|
||||
for (std::map<std::string, CollectionSystemData, stringComparator>::const_iterator it =
|
||||
customSystems.cbegin();
|
||||
it != customSystems.cend(); it++) {
|
||||
ComponentListRow row;
|
||||
std::string name = (*it).first;
|
||||
std::function<void()> deleteCollectionCall = [this, name] {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" +
|
||||
Utils::String::toUpper(name) + "'\n"
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, getHelpStyle(),
|
||||
"THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" +
|
||||
Utils::String::toUpper(name) +
|
||||
"'\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", [this, name] {
|
||||
if (CollectionSystemsManager::get()->isEditing())
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
mDeletedCustomCollection = true;
|
||||
std::vector<std::string> selectedCustomCollections =
|
||||
collection_systems_custom->getSelectedObjects();
|
||||
std::string collectionsConfigEntry;
|
||||
// Create the configuration file entry. If the collection to be
|
||||
// deleted was activated, then exclude it.
|
||||
for (auto it = selectedCustomCollections.begin();
|
||||
it != selectedCustomCollections.end(); it++) {
|
||||
if ((*it) != name) {
|
||||
if ((*it) != selectedCustomCollections.front() &&
|
||||
collectionsConfigEntry != "")
|
||||
collectionsConfigEntry += ",";
|
||||
collectionsConfigEntry += (*it);
|
||||
}
|
||||
"YES",
|
||||
[this, name] {
|
||||
if (CollectionSystemsManager::get()->isEditing())
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
mDeletedCustomCollection = true;
|
||||
std::vector<std::string> selectedCustomCollections =
|
||||
collection_systems_custom->getSelectedObjects();
|
||||
std::string collectionsConfigEntry;
|
||||
// Create the configuration file entry. If the collection to be
|
||||
// deleted was activated, then exclude it.
|
||||
for (auto it = selectedCustomCollections.begin();
|
||||
it != selectedCustomCollections.end(); it++) {
|
||||
if ((*it) != name) {
|
||||
if ((*it) != selectedCustomCollections.front() &&
|
||||
collectionsConfigEntry != "")
|
||||
collectionsConfigEntry += ",";
|
||||
collectionsConfigEntry += (*it);
|
||||
}
|
||||
// If the system to be deleted was present in es_settings.xml, we
|
||||
// need to re-write it.
|
||||
if (collectionsConfigEntry !=
|
||||
Settings::getInstance()->getString("CollectionSystemsCustom")) {
|
||||
Settings::getInstance()->setString("CollectionSystemsCustom",
|
||||
collectionsConfigEntry);
|
||||
setNeedsSaving();
|
||||
setNeedsGoToStart();
|
||||
}
|
||||
CollectionSystemsManager::get()->deleteCustomCollection(name);
|
||||
return true;
|
||||
},
|
||||
"NO", [this] {
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
// If the system to be deleted was present in es_settings.xml, we
|
||||
// need to re-write it.
|
||||
if (collectionsConfigEntry !=
|
||||
Settings::getInstance()->getString("CollectionSystemsCustom")) {
|
||||
Settings::getInstance()->setString("CollectionSystemsCustom",
|
||||
collectionsConfigEntry);
|
||||
setNeedsSaving();
|
||||
setNeedsGoToStart();
|
||||
}
|
||||
CollectionSystemsManager::get()->deleteCustomCollection(name);
|
||||
return true;
|
||||
},
|
||||
"NO", [this] { return false; }));
|
||||
};
|
||||
row.makeAcceptInputHandler(deleteCollectionCall);
|
||||
auto customCollection = std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
auto customCollection = std::make_shared<TextComponent>(
|
||||
mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
row.addElement(customCollection, true);
|
||||
// This transparent bracket is only added generate the correct help prompts.
|
||||
auto bracket = std::make_shared<ImageComponent>(mWindow);
|
||||
|
@ -310,14 +316,14 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
|
||||
// Group unthemed custom collections.
|
||||
auto use_custom_collections_system = std::make_shared<SwitchComponent>(mWindow);
|
||||
use_custom_collections_system->setState(Settings::getInstance()->
|
||||
getBool("UseCustomCollectionsSystem"));
|
||||
use_custom_collections_system->setState(
|
||||
Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
|
||||
addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", use_custom_collections_system);
|
||||
addSaveFunc([this, use_custom_collections_system] {
|
||||
if (use_custom_collections_system->getState() !=
|
||||
Settings::getInstance()->getBool("UseCustomCollectionsSystem")) {
|
||||
Settings::getInstance()->getBool("UseCustomCollectionsSystem")) {
|
||||
Settings::getInstance()->setBool("UseCustomCollectionsSystem",
|
||||
use_custom_collections_system->getState());
|
||||
use_custom_collections_system->getState());
|
||||
if (CollectionSystemsManager::get()->isEditing())
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
setNeedsSaving();
|
||||
|
@ -330,14 +336,14 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(
|
|||
|
||||
// Show system names in collections.
|
||||
auto collection_show_system_info = std::make_shared<SwitchComponent>(mWindow);
|
||||
collection_show_system_info->setState(Settings::getInstance()->
|
||||
getBool("CollectionShowSystemInfo"));
|
||||
collection_show_system_info->setState(
|
||||
Settings::getInstance()->getBool("CollectionShowSystemInfo"));
|
||||
addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", collection_show_system_info);
|
||||
addSaveFunc([this, collection_show_system_info] {
|
||||
if (collection_show_system_info->getState() !=
|
||||
Settings::getInstance()->getBool("CollectionShowSystemInfo")) {
|
||||
Settings::getInstance()->getBool("CollectionShowSystemInfo")) {
|
||||
Settings::getInstance()->setBool("CollectionShowSystemInfo",
|
||||
collection_show_system_info->getState());
|
||||
collection_show_system_info->getState());
|
||||
setNeedsSaving();
|
||||
setNeedsReloading();
|
||||
setInvalidateCachedBackground();
|
||||
|
@ -350,10 +356,9 @@ void GuiCollectionSystemsOptions::createCustomCollection(std::string inName)
|
|||
if (CollectionSystemsManager::get()->isEditing())
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
|
||||
std::string collectionName = CollectionSystemsManager::get()->
|
||||
getValidNewCollectionName(inName);
|
||||
SystemData* newCollection = CollectionSystemsManager::get()->
|
||||
addNewCustomCollection(collectionName);
|
||||
std::string collectionName = CollectionSystemsManager::get()->getValidNewCollectionName(inName);
|
||||
SystemData* newCollection =
|
||||
CollectionSystemsManager::get()->addNewCustomCollection(collectionName);
|
||||
|
||||
CollectionSystemsManager::get()->saveCustomCollection(newCollection);
|
||||
collection_systems_custom->add(collectionName, collectionName, true);
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
|
||||
#include "GuiSettings.h"
|
||||
|
||||
template<typename T>
|
||||
class OptionListComponent;
|
||||
template <typename T> class OptionListComponent;
|
||||
|
||||
class GuiCollectionSystemsOptions : public GuiSettings
|
||||
{
|
||||
|
|
|
@ -10,23 +10,22 @@
|
|||
|
||||
#include "guis/GuiGameScraper.h"
|
||||
|
||||
#include "FileData.h"
|
||||
#include "MameNames.h"
|
||||
#include "SystemData.h"
|
||||
#include "components/ButtonComponent.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "FileData.h"
|
||||
#include "MameNames.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
GuiGameScraper::GuiGameScraper(
|
||||
Window* window,
|
||||
ScraperSearchParams params,
|
||||
std::function<void(const ScraperSearchResult&)> doneFunc)
|
||||
: GuiComponent(window),
|
||||
mGrid(window, Vector2i(1, 7)),
|
||||
mBox(window, ":/graphics/frame.svg"),
|
||||
mSearchParams(params),
|
||||
mClose(false)
|
||||
GuiGameScraper::GuiGameScraper(Window* window,
|
||||
ScraperSearchParams params,
|
||||
std::function<void(const ScraperSearchResult&)> doneFunc)
|
||||
: GuiComponent(window)
|
||||
, mGrid(window, Vector2i(1, 7))
|
||||
, mBox(window, ":/graphics/frame.svg")
|
||||
, mSearchParams(params)
|
||||
, mClose(false)
|
||||
{
|
||||
addChild(&mBox);
|
||||
addChild(&mGrid);
|
||||
|
@ -40,82 +39,62 @@ GuiGameScraper::GuiGameScraper(
|
|||
}
|
||||
else {
|
||||
if (params.game->isArcadeGame() &&
|
||||
Settings::getInstance()->getString("Scraper") == "thegamesdb")
|
||||
scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" +
|
||||
MameNames::getInstance()->getCleanName(mSearchParams.game->getCleanName()) +
|
||||
")";
|
||||
Settings::getInstance()->getString("Scraper") == "thegamesdb")
|
||||
scrapeName =
|
||||
Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" +
|
||||
MameNames::getInstance()->getCleanName(mSearchParams.game->getCleanName()) + ")";
|
||||
else
|
||||
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 : ""),
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
|
||||
mGrid.setEntry(mGameName, Vector2i(0, 1), false, true);
|
||||
|
||||
// Row 2 is a spacer.
|
||||
|
||||
mSystemName = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(
|
||||
mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL),
|
||||
0x888888FF, ALIGN_CENTER);
|
||||
mSystemName = std::make_shared<TextComponent>(
|
||||
mWindow, Utils::String::toUpper(mSearchParams.system->getFullName()),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
|
||||
mGrid.setEntry(mSystemName, Vector2i(0, 3), false, true);
|
||||
|
||||
// Row 4 is a spacer.
|
||||
|
||||
// GuiScraperSearch.
|
||||
mSearch = std::make_shared<GuiScraperSearch>(window,
|
||||
GuiScraperSearch::NEVER_AUTO_ACCEPT, 1);
|
||||
mSearch = std::make_shared<GuiScraperSearch>(window, GuiScraperSearch::NEVER_AUTO_ACCEPT, 1);
|
||||
mGrid.setEntry(mSearch, Vector2i(0, 5), true);
|
||||
|
||||
// Buttons
|
||||
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
||||
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH",
|
||||
"refine search", [&] {
|
||||
mSearch->openInputScreen(mSearchParams);
|
||||
mGrid.resetCursor();
|
||||
buttons.push_back(
|
||||
std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH", "refine search", [&] {
|
||||
mSearch->openInputScreen(mSearchParams);
|
||||
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);
|
||||
|
||||
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) {
|
||||
doneFunc(result); close(); });
|
||||
doneFunc(result);
|
||||
close();
|
||||
});
|
||||
mSearch->setCancelCallback([&] { delete this; });
|
||||
|
||||
// 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();
|
||||
|
||||
setSize(width, Renderer::getScreenHeight() * 0.747f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() -
|
||||
mSize.y()) / 2);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||
|
||||
mGrid.resetCursor();
|
||||
mSearch->search(params); // Start the search.
|
||||
|
@ -133,14 +112,14 @@ GuiGameScraper::GuiGameScraper(
|
|||
|
||||
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(1, mGameName->getFont()->getLetterHeight() /
|
||||
mSize.y(), false); // Game name.
|
||||
mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y(),
|
||||
false); // Game name.
|
||||
mGrid.setRowHeightPerc(2, 0.04f, false);
|
||||
mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() /
|
||||
mSize.y(), false); // System name.
|
||||
mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y(),
|
||||
false); // System name.
|
||||
mGrid.setRowHeightPerc(4, 0.04f, false);
|
||||
mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // Buttons.
|
||||
mGrid.setSize(mSize);
|
||||
|
@ -190,5 +169,6 @@ HelpStyle GuiGameScraper::getHelpStyle()
|
|||
|
||||
void GuiGameScraper::close()
|
||||
{
|
||||
// This will cause update() to close the GUI.
|
||||
mClose = true;
|
||||
}
|
||||
|
|
|
@ -11,15 +11,16 @@
|
|||
#ifndef ES_APP_GUIS_GUI_GAME_SCRAPER_H
|
||||
#define ES_APP_GUIS_GUI_GAME_SCRAPER_H
|
||||
|
||||
#include "GuiComponent.h"
|
||||
#include "components/NinePatchComponent.h"
|
||||
#include "guis/GuiScraperSearch.h"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
class GuiGameScraper : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiGameScraper(Window* window, ScraperSearchParams params,
|
||||
std::function<void(const ScraperSearchResult&)> doneFunc);
|
||||
GuiGameScraper(Window* window,
|
||||
ScraperSearchParams params,
|
||||
std::function<void(const ScraperSearchResult&)> doneFunc);
|
||||
|
||||
void onSizeChanged() override;
|
||||
|
||||
|
|
|
@ -10,21 +10,20 @@
|
|||
|
||||
#include "guis/GuiGamelistFilter.h"
|
||||
|
||||
#include "SystemData.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
#include "guis/GuiTextEditPopup.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
GuiGamelistFilter::GuiGamelistFilter(
|
||||
Window* window,
|
||||
SystemData* system,
|
||||
std::function<void(bool)> filterChangedCallback)
|
||||
: GuiComponent(window),
|
||||
mMenu(window, "FILTER GAMELIST BY"),
|
||||
mSystem(system),
|
||||
mFiltersChangedCallback(filterChangedCallback),
|
||||
mFiltersChanged(false)
|
||||
GuiGamelistFilter::GuiGamelistFilter(Window* window,
|
||||
SystemData* system,
|
||||
std::function<void(bool)> filterChangedCallback)
|
||||
: GuiComponent(window)
|
||||
, mMenu(window, "FILTER GAMELIST BY")
|
||||
, mSystem(system)
|
||||
, mFiltersChangedCallback(filterChangedCallback)
|
||||
, mFiltersChanged(false)
|
||||
{
|
||||
initializeMenu();
|
||||
}
|
||||
|
@ -41,7 +40,8 @@ void GuiGamelistFilter::initializeMenu()
|
|||
// Show filtered menu.
|
||||
row.elements.clear();
|
||||
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));
|
||||
mMenu.addRow(row);
|
||||
row.elements.clear();
|
||||
|
@ -51,13 +51,15 @@ void GuiGamelistFilter::initializeMenu()
|
|||
mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this));
|
||||
|
||||
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.
|
||||
mInitialTextFilter = mTextFilterField->getValue();
|
||||
|
||||
for (std::map<FilterIndexType, std::shared_ptr<OptionListComponent<std::string>>>::
|
||||
const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) {
|
||||
for (std::map<FilterIndexType,
|
||||
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::vector<std::string> filters = optionList->getSelectedObjects();
|
||||
mInitialFilters.push_back(filters);
|
||||
|
@ -67,8 +69,10 @@ void GuiGamelistFilter::initializeMenu()
|
|||
void GuiGamelistFilter::resetAllFilters()
|
||||
{
|
||||
mFilterIndex->resetFilters();
|
||||
for (std::map<FilterIndexType, std::shared_ptr< OptionListComponent<std::string>>>::
|
||||
const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) {
|
||||
for (std::map<FilterIndexType,
|
||||
std::shared_ptr<OptionListComponent<std::string>>>::const_iterator it =
|
||||
mFilterOptions.cbegin();
|
||||
it != mFilterOptions.cend(); it++) {
|
||||
std::shared_ptr<OptionListComponent<std::string>> optionList = it->second;
|
||||
optionList->selectNone();
|
||||
}
|
||||
|
@ -78,21 +82,18 @@ void GuiGamelistFilter::resetAllFilters()
|
|||
mFiltersChanged = true;
|
||||
}
|
||||
|
||||
GuiGamelistFilter::~GuiGamelistFilter()
|
||||
{
|
||||
mFilterOptions.clear();
|
||||
}
|
||||
GuiGamelistFilter::~GuiGamelistFilter() { mFilterOptions.clear(); }
|
||||
|
||||
void GuiGamelistFilter::addFiltersToMenu()
|
||||
{
|
||||
ComponentListRow row;
|
||||
|
||||
auto lbl = std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper("TEXT FILTER (GAME NAME)"),
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto lbl =
|
||||
std::make_shared<TextComponent>(mWindow, Utils::String::toUpper("TEXT FILTER (GAME NAME)"),
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
|
||||
mTextFilterField = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
|
||||
mTextFilterField = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_MEDIUM),
|
||||
0x777777FF, ALIGN_RIGHT);
|
||||
|
||||
// Don't show the free text filter entry unless there are any games in the system.
|
||||
if (mSystem->getRootFolder()->getChildren().size() > 0) {
|
||||
|
@ -113,25 +114,25 @@ void GuiGamelistFilter::addFiltersToMenu()
|
|||
|
||||
// Callback function.
|
||||
auto updateVal = [this](const std::string& newVal) {
|
||||
mTextFilterField->setValue(Utils::String::toUpper(newVal));
|
||||
mFilterIndex->setTextFilter(Utils::String::toUpper(newVal));
|
||||
mTextFilterField->setValue(Utils::String::toUpper(newVal));
|
||||
mFilterIndex->setTextFilter(Utils::String::toUpper(newVal));
|
||||
};
|
||||
|
||||
row.makeAcceptInputHandler([this, updateVal] {
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(),
|
||||
"TEXT FILTER (GAME NAME)", mTextFilterField->getValue(),
|
||||
updateVal, false, "OK", "APPLY CHANGES?"));
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "TEXT FILTER (GAME NAME)",
|
||||
mTextFilterField->getValue(), updateVal, false, "OK",
|
||||
"APPLY CHANGES?"));
|
||||
});
|
||||
|
||||
mMenu.addRow(row);
|
||||
|
||||
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
|
||||
|
||||
for (std::vector<FilterDataDecl>::const_iterator it =
|
||||
decls.cbegin(); it != decls.cend(); it++) {
|
||||
|
||||
for (std::vector<FilterDataDecl>::const_iterator it = decls.cbegin(); // Line break.
|
||||
it != decls.cend(); it++) {
|
||||
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::string menuLabel = (*it).menuLabel; // Text to show in menu.
|
||||
std::shared_ptr<OptionListComponent<std::string>> optionList;
|
||||
|
@ -140,9 +141,9 @@ void GuiGamelistFilter::addFiltersToMenu()
|
|||
ComponentListRow row;
|
||||
|
||||
// Add genres.
|
||||
optionList = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), menuLabel, true);
|
||||
for (auto it: *allKeys)
|
||||
optionList = std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(),
|
||||
menuLabel, true);
|
||||
for (auto it : *allKeys)
|
||||
optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type));
|
||||
if (allKeys->size() > 0)
|
||||
mMenu.addWithLabel(menuLabel, optionList);
|
||||
|
@ -157,8 +158,10 @@ void GuiGamelistFilter::applyFilters()
|
|||
mFiltersChanged = true;
|
||||
|
||||
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
|
||||
for (std::map<FilterIndexType, std::shared_ptr<OptionListComponent<std::string>>>::
|
||||
const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) {
|
||||
for (std::map<FilterIndexType,
|
||||
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::vector<std::string> filters = optionList->getSelectedObjects();
|
||||
auto iteratorDistance = std::distance(mFilterOptions.cbegin(), it);
|
||||
|
|
|
@ -11,19 +11,19 @@
|
|||
#ifndef 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 "GuiComponent.h"
|
||||
#include "components/MenuComponent.h"
|
||||
|
||||
template<typename T>
|
||||
class OptionListComponent;
|
||||
template <typename T> class OptionListComponent;
|
||||
class SystemData;
|
||||
|
||||
class GuiGamelistFilter : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiGamelistFilter(Window* window,
|
||||
SystemData* system, std::function<void(bool)> filtersChangedCallback);
|
||||
SystemData* system,
|
||||
std::function<void(bool)> filtersChangedCallback);
|
||||
|
||||
~GuiGamelistFilter();
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
|
|
|
@ -12,11 +12,6 @@
|
|||
|
||||
#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 "FileFilterIndex.h"
|
||||
#include "FileSorts.h"
|
||||
|
@ -24,18 +19,21 @@
|
|||
#include "MameNames.h"
|
||||
#include "Sound.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(
|
||||
Window* window,
|
||||
SystemData* system)
|
||||
: GuiComponent(window),
|
||||
mSystem(system),
|
||||
mMenu(window, "OPTIONS"),
|
||||
mFiltersChanged(false),
|
||||
mCancelled(false),
|
||||
mIsCustomCollection(false),
|
||||
mIsCustomCollectionGroup(false),
|
||||
mCustomCollectionSystem(nullptr)
|
||||
GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system)
|
||||
: GuiComponent(window)
|
||||
, mSystem(system)
|
||||
, mMenu(window, "OPTIONS")
|
||||
, mFiltersChanged(false)
|
||||
, mCancelled(false)
|
||||
, mIsCustomCollection(false)
|
||||
, mIsCustomCollectionGroup(false)
|
||||
, mCustomCollectionSystem(nullptr)
|
||||
{
|
||||
addChild(&mMenu);
|
||||
|
||||
|
@ -46,11 +44,10 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
ComponentListRow row;
|
||||
|
||||
// There is some special logic required for custom collections.
|
||||
if (file->getSystem()->isCustomCollection() &&
|
||||
file->getPath() != file->getSystem()->getName())
|
||||
if (file->getSystem()->isCustomCollection() && file->getPath() != file->getSystem()->getName())
|
||||
mIsCustomCollection = true;
|
||||
else if (file->getSystem()->isCustomCollection() &&
|
||||
file->getPath() == file->getSystem()->getName())
|
||||
file->getPath() == file->getSystem()->getName())
|
||||
mIsCustomCollectionGroup = true;
|
||||
|
||||
if (mFromPlaceholder && file->getSystem()->isGroupedCustomCollection())
|
||||
|
@ -84,12 +81,12 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
else {
|
||||
// Check if the currently selected game is a favorite.
|
||||
bool isFavorite = false;
|
||||
if (mFirstLetterIndex.size() == 1 && mFirstLetterIndex.front() ==
|
||||
ViewController::FAVORITE_CHAR)
|
||||
if (mFirstLetterIndex.size() == 1 &&
|
||||
mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR)
|
||||
isFavorite = true;
|
||||
else if (mFirstLetterIndex.size() > 1 && (mFirstLetterIndex.front() ==
|
||||
ViewController::FAVORITE_CHAR || mFirstLetterIndex[1] ==
|
||||
ViewController::FAVORITE_CHAR))
|
||||
else if (mFirstLetterIndex.size() > 1 &&
|
||||
(mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR ||
|
||||
mFirstLetterIndex[1] == ViewController::FAVORITE_CHAR))
|
||||
isFavorite = true;
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
mJumpToLetterList = std::make_shared<LetterList>(mWindow, getHelpStyle(),
|
||||
"JUMP TO...", false);
|
||||
mJumpToLetterList =
|
||||
std::make_shared<LetterList>(mWindow, getHelpStyle(), "JUMP TO...", false);
|
||||
|
||||
// Populate the quick selector.
|
||||
for (unsigned int i = 0; i < mFirstLetterIndex.size(); i++) {
|
||||
|
@ -146,8 +143,9 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
if (!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() > 0) {
|
||||
if (system->getName() != "recent" && Settings::getInstance()->getBool("GamelistFilters")) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>
|
||||
(mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FILTER GAMELIST",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
|
||||
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.
|
||||
else if (!CollectionSystemsManager::get()->isEditing() &&
|
||||
mSystem->getRootFolder()->getChildren().size() == 0 &&
|
||||
!mIsCustomCollectionGroup && !mIsCustomCollection) {
|
||||
mSystem->getRootFolder()->getChildren().size() == 0 && !mIsCustomCollectionGroup &&
|
||||
!mIsCustomCollection) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>
|
||||
(mWindow, "THIS SYSTEM HAS NO GAMES",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "THIS SYSTEM HAS NO GAMES",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
|
@ -171,34 +169,40 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
customSystem = system->getName();
|
||||
|
||||
if (UIModeController::getInstance()->isUIModeFull() &&
|
||||
(mIsCustomCollection || mIsCustomCollectionGroup)) {
|
||||
(mIsCustomCollection || mIsCustomCollectionGroup)) {
|
||||
if (CollectionSystemsManager::get()->getEditingCollection() != customSystem) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "ADD/REMOVE GAMES TO THIS COLLECTION",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"ADD/REMOVE GAMES TO THIS COLLECTION",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
if (UIModeController::getInstance()->isUIModeFull() &&
|
||||
CollectionSystemsManager::get()->isEditing()) {
|
||||
CollectionSystemsManager::get()->isEditing()) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "FINISH EDITING '" + Utils::String::toUpper(
|
||||
CollectionSystemsManager::get()->getEditingCollection()) +
|
||||
"' COLLECTION",Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
mWindow,
|
||||
"FINISH EDITING '" +
|
||||
Utils::String::toUpper(
|
||||
CollectionSystemsManager::get()->getEditingCollection()) +
|
||||
"' COLLECTION",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
if (file->getType() == FOLDER) {
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder &&
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"EDIT THIS FOLDER'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS FOLDER'S METADATA",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
|
@ -206,10 +210,11 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
}
|
||||
else {
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder &&
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS GAME'S METADATA",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
|
@ -218,19 +223,25 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
|
||||
// Buttons. The logic to apply or cancel settings are handled by the destructor.
|
||||
if ((!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() == 0) ||
|
||||
system->getName() == "recent") {
|
||||
mMenu.addButton("CLOSE", "close", [&] { mCancelled = true; delete this; });
|
||||
system->getName() == "recent") {
|
||||
mMenu.addButton("CLOSE", "close", [&] {
|
||||
mCancelled = true;
|
||||
delete this;
|
||||
});
|
||||
}
|
||||
else {
|
||||
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.
|
||||
setSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f, (mSize.y() -
|
||||
mMenu.getSize().y()) / 2.0f);
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f,
|
||||
(mSize.y() - mMenu.getSize().y()) / 2.0f);
|
||||
}
|
||||
|
||||
GuiGamelistOptions::~GuiGamelistOptions()
|
||||
|
@ -248,11 +259,12 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
if (!mFromPlaceholder) {
|
||||
ViewController::get()->reloadGameListView(mSystem);
|
||||
}
|
||||
else if (!mCustomCollectionSystem->getRootFolder()->
|
||||
getChildrenListToDisplay().empty()) {
|
||||
ViewController::get()->reloadGameListView(mSystem);
|
||||
getGamelist()->setCursor(mCustomCollectionSystem->
|
||||
getRootFolder()->getChildrenListToDisplay().front());
|
||||
else if (!mCustomCollectionSystem->getRootFolder()
|
||||
->getChildrenListToDisplay()
|
||||
.empty()) {
|
||||
ViewController::get()->reloadGameListView(mSystem);
|
||||
getGamelist()->setCursor(
|
||||
mCustomCollectionSystem->getRootFolder()->getChildrenListToDisplay().front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,6 +274,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
|
||||
if (!mFromPlaceholder) {
|
||||
FileData* root;
|
||||
|
||||
if (mIsCustomCollection)
|
||||
root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
||||
else
|
||||
|
@ -269,7 +282,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
|
||||
// If a new sorting type was selected, then sort and update mSortTypeString for the system.
|
||||
if (!mIsCustomCollectionGroup &&
|
||||
(*mListSort->getSelected()).description != root->getSortTypeString()) {
|
||||
(*mListSort->getSelected()).description != root->getSortTypeString()) {
|
||||
// This will also recursively sort children.
|
||||
root->sort(*mListSort->getSelected(), mFavoritesSorting);
|
||||
root->setSortTypeString((*mListSort->getSelected()).description);
|
||||
|
@ -281,7 +294,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
// Has the user changed the letter using the quick selector?
|
||||
if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) {
|
||||
if (mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR ||
|
||||
mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR)
|
||||
mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR)
|
||||
jumpToFirstRow();
|
||||
else
|
||||
jumpToLetter();
|
||||
|
@ -303,7 +316,7 @@ void GuiGamelistOptions::openGamelistFilter()
|
|||
|
||||
if (mIsCustomCollection)
|
||||
ggf = new GuiGamelistFilter(mWindow, getGamelist()->getCursor()->getSystem(),
|
||||
filtersChangedFunc);
|
||||
filtersChangedFunc);
|
||||
else
|
||||
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
|
||||
// currently being edited. This is done cheaply using onFileChanged() which will trigger
|
||||
// populateList().
|
||||
for (auto it = SystemData::sSystemVector.begin();
|
||||
it != SystemData::sSystemVector.end(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) {
|
||||
ViewController::get()->getGameListView((*it))->onFileChanged(
|
||||
ViewController::get()->getGameListView((*it))->getCursor(), false);
|
||||
ViewController::get()->getGameListView((*it))->getCursor(), false);
|
||||
}
|
||||
|
||||
if (mSystem->getRootFolder()->getChildren().size() == 0)
|
||||
|
@ -362,12 +374,12 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
|
||||
clearGameBtnFunc = [this, file] {
|
||||
if (file->getType() == FOLDER) {
|
||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \"" <<
|
||||
file->getFullPath() << "\"";
|
||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \""
|
||||
<< file->getFullPath() << "\"";
|
||||
}
|
||||
else {
|
||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \"" <<
|
||||
file->getFullPath() << "\"";
|
||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \""
|
||||
<< file->getFullPath() << "\"";
|
||||
}
|
||||
ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file);
|
||||
|
||||
|
@ -375,10 +387,10 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
const std::vector<MetaDataDecl>& mdd = file->metadata.getMDD();
|
||||
for (auto it = mdd.cbegin(); it != mdd.cend(); it++) {
|
||||
if (it->key == "name") {
|
||||
if (file->isArcadeGame()) {
|
||||
if (file->isArcadeGame()) {
|
||||
// If it's a MAME or Neo Geo game, expand the game name accordingly.
|
||||
file->metadata.set(it->key, MameNames::getInstance()->
|
||||
getCleanName(file->getCleanName()));
|
||||
file->metadata.set(
|
||||
it->key, MameNames::getInstance()->getCleanName(file->getCleanName()));
|
||||
}
|
||||
else {
|
||||
file->metadata.set(it->key, file->getDisplayName());
|
||||
|
@ -408,8 +420,8 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
};
|
||||
|
||||
deleteGameBtnFunc = [this, file] {
|
||||
LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath() <<
|
||||
"\", all its media files and its gamelist.xml entry.";
|
||||
LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath()
|
||||
<< "\", all its media files and its gamelist.xml entry.";
|
||||
CollectionSystemsManager::get()->deleteCollectionFiles(file);
|
||||
ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file);
|
||||
ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true);
|
||||
|
@ -420,20 +432,20 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
};
|
||||
|
||||
if (file->getType() == FOLDER) {
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata,
|
||||
file->metadata.getMDD(FOLDER_METADATA), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
||||
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
||||
file->getSystem()).get(), file, true),
|
||||
clearGameBtnFunc, deleteGameBtnFunc));
|
||||
mWindow->pushGui(new GuiMetaDataEd(
|
||||
mWindow, &file->metadata, file->metadata.getMDD(FOLDER_METADATA), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()),
|
||||
std::bind(&IGameListView::onFileChanged,
|
||||
ViewController::get()->getGameListView(file->getSystem()).get(), file, true),
|
||||
clearGameBtnFunc, deleteGameBtnFunc));
|
||||
}
|
||||
else {
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata,
|
||||
file->metadata.getMDD(GAME_METADATA), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
||||
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
||||
file->getSystem()).get(), file, true),
|
||||
clearGameBtnFunc, deleteGameBtnFunc));
|
||||
mWindow->pushGui(new GuiMetaDataEd(
|
||||
mWindow, &file->metadata, file->metadata.getMDD(GAME_METADATA), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()),
|
||||
std::bind(&IGameListView::onFileChanged,
|
||||
ViewController::get()->getGameListView(file->getSystem()).get(), file, true),
|
||||
clearGameBtnFunc, deleteGameBtnFunc));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,14 +454,14 @@ void GuiGamelistOptions::jumpToLetter()
|
|||
char letter = mJumpToLetterList->getSelected().front();
|
||||
|
||||
// Get the gamelist.
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
const std::vector<FileData*>& files =
|
||||
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
|
||||
for (unsigned int i = 0; i < files.size(); i++) {
|
||||
if (mFavoritesSorting && (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR ||
|
||||
mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) {
|
||||
if (static_cast<char>(toupper(files.at(i)->getSortName().front())) ==
|
||||
letter && !files.at(i)->getFavorite()) {
|
||||
mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) {
|
||||
if (static_cast<char>(toupper(files.at(i)->getSortName().front())) == letter &&
|
||||
!files.at(i)->getFavorite()) {
|
||||
if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) {
|
||||
continue;
|
||||
}
|
||||
|
@ -477,8 +489,8 @@ void GuiGamelistOptions::jumpToFirstRow()
|
|||
{
|
||||
if (mFoldersOnTop && mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR) {
|
||||
// Get the gamelist.
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
const std::vector<FileData*>& files =
|
||||
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
// 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.
|
||||
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))
|
||||
mCancelled = true;
|
||||
|
||||
if (input.value != 0 && (config->isMappedTo("b", input) ||
|
||||
config->isMappedTo("back", input))) {
|
||||
if (input.value != 0 && (config->isMappedTo("b", input) || config->isMappedTo("back", input))) {
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
|
@ -521,9 +532,8 @@ HelpStyle GuiGamelistOptions::getHelpStyle()
|
|||
std::vector<HelpPrompt> GuiGamelistOptions::getHelpPrompts()
|
||||
{
|
||||
auto prompts = mMenu.getHelpPrompts();
|
||||
if (mSystem->getRootFolder()->getChildren().size() > 0 ||
|
||||
mIsCustomCollectionGroup || mIsCustomCollection ||
|
||||
CollectionSystemsManager::get()->isEditing())
|
||||
if (mSystem->getRootFolder()->getChildren().size() > 0 || mIsCustomCollectionGroup ||
|
||||
mIsCustomCollection || CollectionSystemsManager::get()->isEditing())
|
||||
prompts.push_back(HelpPrompt("a", "select"));
|
||||
if (mSystem->getRootFolder()->getChildren().size() > 0 && mSystem->getName() != "recent") {
|
||||
prompts.push_back(HelpPrompt("b", "close (apply)"));
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
#ifndef 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/OptionListComponent.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
class IGameListView;
|
||||
class SystemData;
|
||||
|
|
|
@ -14,21 +14,18 @@
|
|||
|
||||
#include <SDL2/SDL_timer.h>
|
||||
|
||||
GuiInfoPopup::GuiInfoPopup(
|
||||
Window* window,
|
||||
std::string message,
|
||||
int duration)
|
||||
: GuiComponent(window),
|
||||
mMessage(message),
|
||||
mDuration(duration),
|
||||
mRunning(true)
|
||||
GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration)
|
||||
: GuiComponent(window)
|
||||
, mMessage(message)
|
||||
, mDuration(duration)
|
||||
, mRunning(true)
|
||||
{
|
||||
mFrame = new NinePatchComponent(window);
|
||||
float maxWidth = Renderer::getScreenWidth() * 0.9f;
|
||||
float maxHeight = Renderer::getScreenHeight() * 0.2f;
|
||||
|
||||
std::shared_ptr<TextComponent> s = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER);
|
||||
std::shared_ptr<TextComponent> s = std::make_shared<TextComponent>(
|
||||
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.
|
||||
s->setSize(0.0f, 0.0f);
|
||||
|
@ -51,13 +48,13 @@ GuiInfoPopup::GuiInfoPopup(
|
|||
mSize[0] = mSize.x() + paddingX;
|
||||
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;
|
||||
|
||||
setPosition(posX, posY, 0);
|
||||
|
||||
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);
|
||||
|
||||
// We only initialize the actual time when we first start to render.
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
~GuiInfoPopup();
|
||||
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
inline void stop() override { mRunning = false; }
|
||||
void stop() override { mRunning = false; }
|
||||
|
||||
private:
|
||||
bool updateState();
|
||||
|
|
|
@ -8,20 +8,19 @@
|
|||
|
||||
#include "guis/GuiLaunchScreen.h"
|
||||
|
||||
#include "FileData.h"
|
||||
#include "SystemData.h"
|
||||
#include "components/ComponentGrid.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
GuiLaunchScreen::GuiLaunchScreen(
|
||||
Window* window)
|
||||
: GuiComponent(window),
|
||||
mBackground(window, ":/graphics/frame.svg"),
|
||||
mGrid(nullptr),
|
||||
mMarquee(nullptr),
|
||||
mWindow(window)
|
||||
GuiLaunchScreen::GuiLaunchScreen(Window* window)
|
||||
: GuiComponent(window)
|
||||
, mBackground(window, ":/graphics/frame.svg")
|
||||
, mGrid(nullptr)
|
||||
, mMarquee(nullptr)
|
||||
, mWindow(window)
|
||||
{
|
||||
addChild(&mBackground);
|
||||
mWindow->setLaunchScreen(this);
|
||||
|
@ -29,6 +28,7 @@ GuiLaunchScreen::GuiLaunchScreen(
|
|||
|
||||
GuiLaunchScreen::~GuiLaunchScreen()
|
||||
{
|
||||
// This only executes when exiting the application.
|
||||
closeLaunchScreen();
|
||||
}
|
||||
|
||||
|
@ -51,49 +51,53 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
|
|||
const float gameNameFontSize = 0.073f;
|
||||
|
||||
// Spacer row.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0),
|
||||
false, false, Vector2i(1, 1));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), false, false,
|
||||
Vector2i(1, 1));
|
||||
|
||||
// Title.
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, "LAUNCHING GAME",
|
||||
Font::get(static_cast<int>(titleFontSize * std::min(Renderer::getScreenHeight(),
|
||||
Renderer::getScreenWidth()))), 0x666666FF, ALIGN_CENTER);
|
||||
mTitle = std::make_shared<TextComponent>(
|
||||
mWindow, "LAUNCHING GAME",
|
||||
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));
|
||||
|
||||
// Spacer row.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 2),
|
||||
false, false, Vector2i(1, 1));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 2), false, false,
|
||||
Vector2i(1, 1));
|
||||
|
||||
// Row for the marquee.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 3),
|
||||
false, false, Vector2i(1, 1));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 3), false, false,
|
||||
Vector2i(1, 1));
|
||||
|
||||
// Spacer row.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 4),
|
||||
false, false, Vector2i(1, 1));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 4), false, false,
|
||||
Vector2i(1, 1));
|
||||
|
||||
// Game name.
|
||||
mGameName = std::make_shared<TextComponent>(mWindow, "GAME NAME",
|
||||
Font::get(static_cast<int>(gameNameFontSize * std::min(Renderer::getScreenHeight(),
|
||||
Renderer::getScreenWidth()))), 0x444444FF, ALIGN_CENTER);
|
||||
mGameName = std::make_shared<TextComponent>(
|
||||
mWindow, "GAME NAME",
|
||||
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));
|
||||
|
||||
// System name.
|
||||
mSystemName = std::make_shared<TextComponent>(mWindow, "SYSTEM NAME",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER);
|
||||
mSystemName = std::make_shared<TextComponent>(
|
||||
mWindow, "SYSTEM NAME", Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER);
|
||||
mGrid->setEntry(mSystemName, Vector2i(1, 6), false, true, Vector2i(1, 1));
|
||||
|
||||
// Spacer row.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 7),
|
||||
false, false, Vector2i(1, 1));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 7), false, false,
|
||||
Vector2i(1, 1));
|
||||
|
||||
// Left spacer.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0),
|
||||
false, false, Vector2i(1, 8));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false, false,
|
||||
Vector2i(1, 8));
|
||||
|
||||
// Right spacer.
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(2, 0),
|
||||
false, false, Vector2i(1, 8));
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(2, 0), false, false,
|
||||
Vector2i(1, 8));
|
||||
|
||||
// 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
|
||||
|
@ -106,9 +110,11 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
|
|||
float maxWidth = static_cast<float>(Renderer::getScreenWidth()) * maxWidthModifier;
|
||||
float minWidth = static_cast<float>(Renderer::getScreenWidth()) * minWidthModifier;
|
||||
|
||||
float fontWidth = Font::get(static_cast<int>(gameNameFontSize *
|
||||
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))->
|
||||
sizeText(Utils::String::toUpper(game->getName())).x();
|
||||
float fontWidth =
|
||||
Font::get(static_cast<int>(gameNameFontSize * std::min(Renderer::getScreenHeight(),
|
||||
Renderer::getScreenWidth())))
|
||||
->sizeText(Utils::String::toUpper(game->getName()))
|
||||
.x();
|
||||
|
||||
// Add a bit of width to compensate for the left and right spacers.
|
||||
fontWidth += static_cast<float>(Renderer::getScreenWidth()) * 0.05f;
|
||||
|
@ -159,7 +165,8 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
|
|||
if (mImagePath != "") {
|
||||
mMarquee->setImage(game->getMarqueePath(), false);
|
||||
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);
|
||||
Vector3f currentPos = mMarquee->getPosition();
|
||||
|
@ -167,18 +174,18 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
|
|||
|
||||
// Position the image in the middle of row four.
|
||||
currentPos.x() = mSize.x() / 2.0f;
|
||||
currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) +
|
||||
mGrid->getRowHeight(2) + mGrid->getRowHeight(3) / 2.0f;
|
||||
currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + mGrid->getRowHeight(2) +
|
||||
mGrid->getRowHeight(3) / 2.0f;
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -203,6 +210,7 @@ void GuiLaunchScreen::closeLaunchScreen()
|
|||
|
||||
void GuiLaunchScreen::onSizeChanged()
|
||||
{
|
||||
// Update mGrid size.
|
||||
mGrid->setSize(mSize);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
#ifndef 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/ImageComponent.h"
|
||||
#include "components/NinePatchComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "Window.h"
|
||||
|
||||
class FileData;
|
||||
|
||||
|
@ -35,7 +35,6 @@ public:
|
|||
private:
|
||||
Window* mWindow;
|
||||
ComponentGrid* mGrid;
|
||||
|
||||
NinePatchComponent mBackground;
|
||||
|
||||
std::shared_ptr<TextComponent> mTitle;
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
|
||||
#include "guis/GuiMediaViewerOptions.h"
|
||||
|
||||
#include "components/SwitchComponent.h"
|
||||
#include "Settings.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
|
||||
GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& title)
|
||||
: GuiSettings(window, title)
|
||||
: GuiSettings(window, title)
|
||||
{
|
||||
// Keep videos running when viewing images.
|
||||
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);
|
||||
addSaveFunc([keep_video_running, this] {
|
||||
if (keep_video_running->getState() !=
|
||||
Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) {
|
||||
Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) {
|
||||
Settings::getInstance()->setBool("MediaViewerKeepVideoRunning",
|
||||
keep_video_running->getState());
|
||||
keep_video_running->getState());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -34,23 +34,23 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string&
|
|||
addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", stretch_videos);
|
||||
addSaveFunc([stretch_videos, this] {
|
||||
if (stretch_videos->getState() !=
|
||||
Settings::getInstance()->getBool("MediaViewerStretchVideos")) {
|
||||
Settings::getInstance()->getBool("MediaViewerStretchVideos")) {
|
||||
Settings::getInstance()->setBool("MediaViewerStretchVideos",
|
||||
stretch_videos->getState());
|
||||
stretch_videos->getState());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
// Render scanlines for videos using a shader.
|
||||
auto video_scanlines = std::make_shared<SwitchComponent>(mWindow);
|
||||
video_scanlines->setState(Settings::getInstance()->getBool("MediaViewerVideoScanlines"));
|
||||
addWithLabel("RENDER SCANLINES FOR VIDEOS", video_scanlines);
|
||||
addSaveFunc([video_scanlines, this] {
|
||||
if (video_scanlines->getState() !=
|
||||
Settings::getInstance()->getBool("MediaViewerVideoScanlines")) {
|
||||
Settings::getInstance()->getBool("MediaViewerVideoScanlines")) {
|
||||
Settings::getInstance()->setBool("MediaViewerVideoScanlines",
|
||||
video_scanlines->getState());
|
||||
video_scanlines->getState());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -60,26 +60,24 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string&
|
|||
video_blur->setState(Settings::getInstance()->getBool("MediaViewerVideoBlur"));
|
||||
addWithLabel("RENDER BLUR FOR VIDEOS", video_blur);
|
||||
addSaveFunc([video_blur, this] {
|
||||
if (video_blur->getState() !=
|
||||
Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
|
||||
Settings::getInstance()->setBool("MediaViewerVideoBlur",
|
||||
video_blur->getState());
|
||||
if (video_blur->getState() != Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
|
||||
Settings::getInstance()->setBool("MediaViewerVideoBlur", video_blur->getState());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Render scanlines for screenshots using a shader.
|
||||
auto screenshot_scanlines = std::make_shared<SwitchComponent>(mWindow);
|
||||
screenshot_scanlines->setState(Settings::getInstance()->
|
||||
getBool("MediaViewerScreenshotScanlines"));
|
||||
screenshot_scanlines->setState(
|
||||
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"));
|
||||
addWithLabel("RENDER SCANLINES FOR SCREENSHOTS", screenshot_scanlines);
|
||||
addSaveFunc([screenshot_scanlines, this] {
|
||||
if (screenshot_scanlines->getState() !=
|
||||
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) {
|
||||
Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) {
|
||||
Settings::getInstance()->setBool("MediaViewerScreenshotScanlines",
|
||||
screenshot_scanlines->getState());
|
||||
screenshot_scanlines->getState());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,9 +10,9 @@
|
|||
#ifndef ES_APP_GUIS_GUI_MENU_H
|
||||
#define ES_APP_GUIS_GUI_MENU_H
|
||||
|
||||
#include "GuiComponent.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "guis/GuiSettings.h"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
class GuiMenu : public GuiComponent
|
||||
{
|
||||
|
@ -27,8 +27,10 @@ public:
|
|||
|
||||
private:
|
||||
void close(bool closeAllWindows);
|
||||
void addEntry(const std::string& name, unsigned int color,
|
||||
bool add_arrow, const std::function<void()>& func);
|
||||
void addEntry(const std::string& name,
|
||||
unsigned int color,
|
||||
bool add_arrow,
|
||||
const std::function<void()>& func);
|
||||
void addVersionInfo();
|
||||
|
||||
void openScraperOptions();
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
|
||||
#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/ComponentList.h"
|
||||
#include "components/DateTimeEditComponent.h"
|
||||
|
@ -18,69 +24,63 @@
|
|||
#include "components/RatingComponent.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "guis/GuiComplexTextEditPopup.h"
|
||||
#include "guis/GuiGameScraper.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "guis/GuiTextEditPopup.h"
|
||||
#include "guis/GuiComplexTextEditPopup.h"
|
||||
#include "resources/Font.h"
|
||||
#include "utils/StringUtil.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(
|
||||
Window* window,
|
||||
MetaDataList* md,
|
||||
const std::vector<MetaDataDecl>& mdd,
|
||||
ScraperSearchParams scraperParams,
|
||||
const std::string& /*header*/,
|
||||
std::function<void()> saveCallback,
|
||||
std::function<void()> clearGameFunc,
|
||||
std::function<void()> deleteGameFunc)
|
||||
: GuiComponent(window),
|
||||
mScraperParams(scraperParams),
|
||||
mBackground(window, ":/graphics/frame.svg"),
|
||||
mGrid(window, Vector2i(1, 3)),
|
||||
mMetaDataDecl(mdd),
|
||||
mMetaData(md),
|
||||
mSavedCallback(saveCallback),
|
||||
mClearGameFunc(clearGameFunc),
|
||||
mDeleteGameFunc(deleteGameFunc),
|
||||
mMediaFilesUpdated(false)
|
||||
GuiMetaDataEd::GuiMetaDataEd(Window* window,
|
||||
MetaDataList* md,
|
||||
const std::vector<MetaDataDecl>& mdd,
|
||||
ScraperSearchParams scraperParams,
|
||||
const std::string& /*header*/,
|
||||
std::function<void()> saveCallback,
|
||||
std::function<void()> clearGameFunc,
|
||||
std::function<void()> deleteGameFunc)
|
||||
: GuiComponent(window)
|
||||
, mScraperParams(scraperParams)
|
||||
, mBackground(window, ":/graphics/frame.svg")
|
||||
, mGrid(window, Vector2i(1, 3))
|
||||
, mMetaDataDecl(mdd)
|
||||
, mMetaData(md)
|
||||
, mSavedCallback(saveCallback)
|
||||
, mClearGameFunc(clearGameFunc)
|
||||
, mDeleteGameFunc(deleteGameFunc)
|
||||
, mMediaFilesUpdated(false)
|
||||
{
|
||||
addChild(&mBackground);
|
||||
addChild(&mGrid);
|
||||
|
||||
mHeaderGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i(1, 5));
|
||||
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, "EDIT METADATA",
|
||||
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE),
|
||||
0x555555FF, ALIGN_CENTER);
|
||||
|
||||
// Extract possible subfolders from the path.
|
||||
std::string folderPath = Utils::String::replace(
|
||||
Utils::FileSystem::getParent(scraperParams.game->getPath()),
|
||||
scraperParams.system->getSystemEnvData()->mStartPath, "");
|
||||
std::string folderPath =
|
||||
Utils::String::replace(Utils::FileSystem::getParent(scraperParams.game->getPath()),
|
||||
scraperParams.system->getSystemEnvData()->mStartPath, "");
|
||||
|
||||
if (folderPath.size() >= 2) {
|
||||
folderPath.erase(0, 1);
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
folderPath.push_back('\\');
|
||||
folderPath = Utils::String::replace(folderPath, "/", "\\");
|
||||
#else
|
||||
#else
|
||||
folderPath.push_back('/');
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
mSubtitle = std::make_shared<TextComponent>(mWindow, folderPath +
|
||||
Utils::FileSystem::getFileName(scraperParams.game->getPath()) +
|
||||
" [" + Utils::String::toUpper(scraperParams.system->getName()) + "]" +
|
||||
mSubtitle = std::make_shared<TextComponent>(
|
||||
mWindow,
|
||||
folderPath + Utils::FileSystem::getFileName(scraperParams.game->getPath()) + " [" +
|
||||
Utils::String::toUpper(scraperParams.system->getName()) + "]" +
|
||||
(scraperParams.game->getType() == FOLDER ? " " + ViewController::FOLDER_CHAR : ""),
|
||||
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f),
|
||||
Vector2f(0.0f, 0.0f), 0x00000000, 0.05f);
|
||||
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f),
|
||||
Vector2f(0.0f, 0.0f), 0x00000000, 0.05f);
|
||||
mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), 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.
|
||||
if (!Settings::getInstance()->getBool("LaunchCommandOverride") &&
|
||||
iter->type == MD_LAUNCHCOMMAND) {
|
||||
ed = std::make_shared<TextComponent>(window, "", Font::get(FONT_SIZE_SMALL,
|
||||
FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT);
|
||||
iter->type == MD_LAUNCHCOMMAND) {
|
||||
ed = std::make_shared<TextComponent>(
|
||||
window, "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT);
|
||||
assert(ed);
|
||||
ed->setValue(mMetaData->get(iter->key));
|
||||
mEditors.push_back(ed);
|
||||
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
|
||||
// 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.
|
||||
ComponentListRow row;
|
||||
auto lbl = std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper(iter->displayName), Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
auto lbl =
|
||||
std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(iter->displayName),
|
||||
Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
row.addElement(lbl, true); // Label.
|
||||
|
||||
switch (iter->type) {
|
||||
case MD_BOOL: {
|
||||
case MD_BOOL: {
|
||||
ed = std::make_shared<SwitchComponent>(window);
|
||||
// Make the switches slightly smaller.
|
||||
auto switchSize = ed->getSize() * 0.9f;
|
||||
|
@ -133,9 +132,9 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
row.addElement(ed, false, true);
|
||||
break;
|
||||
}
|
||||
case MD_RATING: {
|
||||
case MD_RATING: {
|
||||
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);
|
||||
|
||||
ed = std::make_shared<RatingComponent>(window, true);
|
||||
|
@ -145,17 +144,17 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
row.addElement(ed, false, true);
|
||||
|
||||
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);
|
||||
|
||||
// Pass input to the actual RatingComponent instead of the spacer.
|
||||
row.input_handler = std::bind(&GuiComponent::input,
|
||||
ed.get(), std::placeholders::_1, std::placeholders::_2);
|
||||
row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
break;
|
||||
}
|
||||
case MD_DATE: {
|
||||
case MD_DATE: {
|
||||
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);
|
||||
|
||||
ed = std::make_shared<DateTimeEditComponent>(window, true);
|
||||
|
@ -164,29 +163,22 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
row.addElement(ed, false);
|
||||
|
||||
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);
|
||||
|
||||
// Pass input to the actual DateTimeEditComponent instead of the spacer.
|
||||
row.input_handler = std::bind(&GuiComponent::input, ed.get(),
|
||||
std::placeholders::_1, std::placeholders::_2);
|
||||
row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
break;
|
||||
}
|
||||
// Not in use as 'lastplayed' is flagged as statistics and these are skipped.
|
||||
// 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: {
|
||||
case MD_LAUNCHCOMMAND: {
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
auto bracket = std::make_shared<ImageComponent>(mWindow);
|
||||
|
@ -207,26 +199,27 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
};
|
||||
|
||||
std::string staticTextString = "Default value from es_systems.xml:";
|
||||
std::string defaultLaunchCommand = scraperParams.system->
|
||||
getSystemEnvData()->mLaunchCommand;
|
||||
std::string defaultLaunchCommand =
|
||||
scraperParams.system->getSystemEnvData()->mLaunchCommand;
|
||||
|
||||
row.makeAcceptInputHandler([this, title, staticTextString,
|
||||
defaultLaunchCommand, ed, updateVal, multiLine] {
|
||||
mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, getHelpStyle(),
|
||||
title, staticTextString, defaultLaunchCommand, ed->getValue(),
|
||||
updateVal, multiLine, "APPLY", "APPLY CHANGES?"));
|
||||
row.makeAcceptInputHandler([this, title, staticTextString, defaultLaunchCommand, ed,
|
||||
updateVal, multiLine] {
|
||||
mWindow->pushGui(new GuiComplexTextEditPopup(
|
||||
mWindow, getHelpStyle(), title, staticTextString, defaultLaunchCommand,
|
||||
ed->getValue(), updateVal, multiLine, "APPLY", "APPLY CHANGES?"));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case MD_MULTILINE_STRING:
|
||||
default: {
|
||||
case MD_MULTILINE_STRING:
|
||||
default: {
|
||||
// MD_STRING.
|
||||
ed = std::make_shared<TextComponent>(window, "", Font::get(FONT_SIZE_SMALL,
|
||||
FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT);
|
||||
ed = std::make_shared<TextComponent>(window, "",
|
||||
Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT),
|
||||
0x777777FF, ALIGN_RIGHT);
|
||||
row.addElement(ed, true);
|
||||
|
||||
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);
|
||||
|
||||
auto bracket = std::make_shared<ImageComponent>(mWindow);
|
||||
|
@ -239,9 +232,9 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
|
||||
gamePath = Utils::FileSystem::getStem(mScraperParams.game->getPath());
|
||||
|
||||
// OK callback (apply new value to ed).
|
||||
auto updateVal = [ed, currentKey, originalValue, gamePath]
|
||||
(const std::string& newVal) {
|
||||
// OK callback (apply new value to ed).
|
||||
auto updateVal = [ed, currentKey, originalValue,
|
||||
gamePath](const std::string& newVal) {
|
||||
// If the user has entered a blank game name, then set the name to the ROM
|
||||
// filename (minus the extension).
|
||||
if (currentKey == "name" && newVal == "") {
|
||||
|
@ -251,27 +244,28 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
else
|
||||
ed->setColor(TEXTCOLOR_USERMARKED);
|
||||
}
|
||||
else if (newVal == "" && (currentKey == "developer" ||
|
||||
currentKey == "publisher" || currentKey == "genre" ||
|
||||
currentKey == "players")) {
|
||||
else if (newVal == "" &&
|
||||
(currentKey == "developer" || currentKey == "publisher" ||
|
||||
currentKey == "genre" || currentKey == "players")) {
|
||||
ed->setValue("unknown");
|
||||
if (originalValue == "unknown")
|
||||
ed->setColor(DEFAULT_TEXTCOLOR);
|
||||
else
|
||||
ed->setColor(TEXTCOLOR_USERMARKED);
|
||||
ed->setColor(TEXTCOLOR_USERMARKED);
|
||||
}
|
||||
else {
|
||||
ed->setValue(newVal);
|
||||
if (newVal == originalValue)
|
||||
ed->setColor(DEFAULT_TEXTCOLOR);
|
||||
else
|
||||
ed->setColor(TEXTCOLOR_USERMARKED);
|
||||
ed->setColor(TEXTCOLOR_USERMARKED);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] {
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title,
|
||||
ed->getValue(), updateVal, multiLine, "APPLY", "APPLY CHANGES?"));
|
||||
ed->getValue(), updateVal, multiLine,
|
||||
"APPLY", "APPLY CHANGES?"));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -286,8 +280,8 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
||||
|
||||
if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SCRAPE", "scrape",
|
||||
std::bind(&GuiMetaDataEd::fetch, this)));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(
|
||||
mWindow, "SCRAPE", "scrape", std::bind(&GuiMetaDataEd::fetch, this)));
|
||||
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SAVE", "save metadata", [&] {
|
||||
save();
|
||||
|
@ -295,49 +289,60 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
delete this;
|
||||
}));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "cancel changes",
|
||||
[&] { delete this; }));
|
||||
|
||||
[&] { delete this; }));
|
||||
if (scraperParams.game->getType() == FOLDER) {
|
||||
if (mClearGameFunc) {
|
||||
auto clearSelf = [&] { mClearGameFunc(); delete this; };
|
||||
auto clearSelf = [&] {
|
||||
mClearGameFunc();
|
||||
delete this;
|
||||
};
|
||||
auto clearSelfBtnFunc = [this, clearSelf] {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"THIS WILL DELETE ANY MEDIA FILES AND\n"
|
||||
"THE GAMELIST.XML ENTRY FOR THIS FOLDER,\n"
|
||||
"BUT NEITHER THE FOLDER ITSELF OR ANY\n"
|
||||
"CONTENT INSIDE IT WILL BE REMOVED\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", clearSelf, "NO", nullptr)); };
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR",
|
||||
"clear folder", clearSelfBtnFunc));
|
||||
"THIS WILL DELETE ANY MEDIA FILES AND\n"
|
||||
"THE GAMELIST.XML ENTRY FOR THIS FOLDER,\n"
|
||||
"BUT NEITHER THE FOLDER ITSELF OR ANY\n"
|
||||
"CONTENT INSIDE IT WILL BE REMOVED\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", clearSelf, "NO", nullptr));
|
||||
};
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear folder",
|
||||
clearSelfBtnFunc));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mClearGameFunc) {
|
||||
auto clearSelf = [&] { mClearGameFunc(); delete this; };
|
||||
auto clearSelf = [&] {
|
||||
mClearGameFunc();
|
||||
delete this;
|
||||
};
|
||||
auto clearSelfBtnFunc = [this, clearSelf] {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"THIS WILL DELETE ANY MEDIA FILES\n"
|
||||
"AND THE GAMELIST.XML ENTRY FOR\n"
|
||||
"THIS GAME, BUT THE GAME FILE\n"
|
||||
"ITSELF WILL NOT BE REMOVED\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", clearSelf, "NO", nullptr)); };
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR",
|
||||
"clear file", clearSelfBtnFunc));
|
||||
"THIS WILL DELETE ANY MEDIA FILES\n"
|
||||
"AND THE GAMELIST.XML ENTRY FOR\n"
|
||||
"THIS GAME, BUT THE GAME FILE\n"
|
||||
"ITSELF WILL NOT BE REMOVED\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", clearSelf, "NO", nullptr));
|
||||
};
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear file",
|
||||
clearSelfBtnFunc));
|
||||
}
|
||||
|
||||
if (mDeleteGameFunc) {
|
||||
auto deleteFilesAndSelf = [&] { mDeleteGameFunc(); delete this; };
|
||||
auto deleteFilesAndSelf = [&] {
|
||||
mDeleteGameFunc();
|
||||
delete this;
|
||||
};
|
||||
auto deleteGameBtnFunc = [this, deleteFilesAndSelf] {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"THIS WILL DELETE THE GAME\n"
|
||||
"FILE, ANY MEDIA FILES AND\n"
|
||||
"THE GAMELIST.XML ENTRY\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", deleteFilesAndSelf, "NO", nullptr)); };
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "DELETE",
|
||||
"delete game", deleteGameBtnFunc));
|
||||
"THIS WILL DELETE THE GAME\n"
|
||||
"FILE, ANY MEDIA FILES AND\n"
|
||||
"THE GAMELIST.XML ENTRY\n"
|
||||
"ARE YOU SURE?",
|
||||
"YES", deleteFilesAndSelf, "NO", nullptr));
|
||||
};
|
||||
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);
|
||||
|
||||
// Resize + center.
|
||||
float width = static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() *
|
||||
1.05f), static_cast<int>(Renderer::getScreenWidth() * 0.90f)));
|
||||
float width =
|
||||
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);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||
}
|
||||
|
||||
void GuiMetaDataEd::onSizeChanged()
|
||||
|
@ -360,8 +366,8 @@ void GuiMetaDataEd::onSizeChanged()
|
|||
const float subtitleHeight = mSubtitle->getFont()->getLetterHeight();
|
||||
const float titleSubtitleSpacing = mSize.y() * 0.03f;
|
||||
|
||||
mGrid.setRowHeightPerc(0, (titleHeight + titleSubtitleSpacing + subtitleHeight +
|
||||
TITLE_VERT_PADDING) / mSize.y());
|
||||
mGrid.setRowHeightPerc(
|
||||
0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / 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.
|
||||
|
@ -383,7 +389,7 @@ void GuiMetaDataEd::onSizeChanged()
|
|||
mList->setSize(mList->getSize().x(), listHeight);
|
||||
Vector2f newWindowSize = mSize;
|
||||
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.
|
||||
Vector3f newButtonPos = mButtons->getPosition();
|
||||
|
@ -411,13 +417,13 @@ void GuiMetaDataEd::save()
|
|||
continue;
|
||||
|
||||
if (!showHiddenGames && mMetaDataDecl.at(i).key == "hidden" &&
|
||||
mEditors.at(i)->getValue() != mMetaData->get("hidden"))
|
||||
mEditors.at(i)->getValue() != mMetaData->get("hidden"))
|
||||
hideGameWhileHidden = true;
|
||||
|
||||
// Check whether the flag to count the entry as a game was set to enabled.
|
||||
if (mMetaDataDecl.at(i).key == "nogamecount" &&
|
||||
mEditors.at(i)->getValue() != mMetaData->get("nogamecount") &&
|
||||
mMetaData->get("nogamecount") == "true") {
|
||||
mEditors.at(i)->getValue() != mMetaData->get("nogamecount") &&
|
||||
mMetaData->get("nogamecount") == "true") {
|
||||
setGameAsCounted = true;
|
||||
}
|
||||
|
||||
|
@ -450,7 +456,7 @@ void GuiMetaDataEd::save()
|
|||
for (FileData* child : mScraperParams.game->getChildrenRecursive()) {
|
||||
if (!child->getHidden())
|
||||
child->metadata.set("hidden", "true");
|
||||
hideGames.push_back(child);
|
||||
hideGames.push_back(child);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -501,8 +507,8 @@ void GuiMetaDataEd::save()
|
|||
|
||||
void GuiMetaDataEd::fetch()
|
||||
{
|
||||
GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams,
|
||||
std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1));
|
||||
GuiGameScraper* scr = new GuiGameScraper(
|
||||
mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1));
|
||||
mWindow->pushGui(scr);
|
||||
}
|
||||
|
||||
|
@ -514,10 +520,6 @@ void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result)
|
|||
|
||||
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.
|
||||
// If so, it's these values we should compare against when scraping, not
|
||||
// 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;
|
||||
closeFunc = [this] {
|
||||
if (mMediaFilesUpdated) {
|
||||
// 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
|
||||
// and marquee, as otherwise they would not get updated until the user scrolls up
|
||||
// and down the gamelist.
|
||||
TextureResource::manualUnload(mScraperParams.game->getImagePath(), false);
|
||||
TextureResource::manualUnload(mScraperParams.game->getMarqueePath(), false);
|
||||
ViewController::get()->reloadGameListView(mScraperParams.system);
|
||||
mWindow->invalidateCachedBackground();
|
||||
}
|
||||
ViewController::get()->onPauseVideo();
|
||||
delete this;
|
||||
};
|
||||
closeFunc = [this] {
|
||||
if (mMediaFilesUpdated) {
|
||||
// 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
|
||||
// and marquee, as otherwise they would not get updated until the user scrolls up
|
||||
// and down the gamelist.
|
||||
TextureResource::manualUnload(mScraperParams.game->getImagePath(), false);
|
||||
TextureResource::manualUnload(mScraperParams.game->getMarqueePath(), false);
|
||||
ViewController::get()->reloadGameListView(mScraperParams.system);
|
||||
mWindow->invalidateCachedBackground();
|
||||
}
|
||||
ViewController::get()->onPauseVideo();
|
||||
delete this;
|
||||
};
|
||||
|
||||
if (metadataUpdated) {
|
||||
// Changes were made, ask if the user wants to save them.
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"SAVE CHANGES?",
|
||||
"YES", [this, closeFunc] { save(); closeFunc(); },
|
||||
"NO", closeFunc
|
||||
));
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, getHelpStyle(), "SAVE CHANGES?", "YES",
|
||||
[this, closeFunc] {
|
||||
save();
|
||||
closeFunc();
|
||||
},
|
||||
"NO", closeFunc));
|
||||
}
|
||||
else {
|
||||
// Always save if the media files have been changed (i.e. newly scraped images).
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
#ifndef 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/NinePatchComponent.h"
|
||||
#include "scrapers/Scraper.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "MetaData.h"
|
||||
|
||||
class ComponentList;
|
||||
class TextComponent;
|
||||
|
@ -24,14 +24,14 @@ class TextComponent;
|
|||
class GuiMetaDataEd : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiMetaDataEd(
|
||||
Window* window,
|
||||
MetaDataList* md, const std::vector<MetaDataDecl>&mdd,
|
||||
ScraperSearchParams params,
|
||||
const std::string& header,
|
||||
std::function<void()> savedCallback,
|
||||
std::function<void()> clearGameFunc,
|
||||
std::function<void()> deleteGameFunc);
|
||||
GuiMetaDataEd(Window* window,
|
||||
MetaDataList* md,
|
||||
const std::vector<MetaDataDecl>& mdd,
|
||||
ScraperSearchParams params,
|
||||
const std::string& header,
|
||||
std::function<void()> savedCallback,
|
||||
std::function<void()> clearGameFunc,
|
||||
std::function<void()> deleteGameFunc);
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void onSizeChanged() override;
|
||||
|
|
|
@ -9,17 +9,15 @@
|
|||
|
||||
#include "guis/GuiOfflineGenerator.h"
|
||||
|
||||
#include "SystemData.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
GuiOfflineGenerator::GuiOfflineGenerator(
|
||||
Window* window,
|
||||
const std::queue<FileData*>& gameQueue)
|
||||
: GuiComponent(window),
|
||||
mBackground(window, ":/graphics/frame.svg"),
|
||||
mGrid(window, Vector2i(6, 13)),
|
||||
mGameQueue(gameQueue)
|
||||
GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue<FileData*>& gameQueue)
|
||||
: GuiComponent(window)
|
||||
, mBackground(window, ":/graphics/frame.svg")
|
||||
, mGrid(window, Vector2i(6, 13))
|
||||
, mGameQueue(gameQueue)
|
||||
{
|
||||
addChild(&mBackground);
|
||||
addChild(&mGrid);
|
||||
|
@ -39,140 +37,139 @@ GuiOfflineGenerator::GuiOfflineGenerator(
|
|||
|
||||
// Header.
|
||||
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));
|
||||
|
||||
mStatus = std::make_shared<TextComponent>(mWindow, "NOT STARTED",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
|
||||
mStatus = std::make_shared<TextComponent>(mWindow, "NOT STARTED", Font::get(FONT_SIZE_MEDIUM),
|
||||
0x777777FF, ALIGN_CENTER);
|
||||
mGrid.setEntry(mStatus, Vector2i(0, 1), false, true, Vector2i(6, 1));
|
||||
|
||||
mGameCounter = std::make_shared<TextComponent>(mWindow,
|
||||
std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) +
|
||||
mGameCounter = std::make_shared<TextComponent>(
|
||||
mWindow,
|
||||
std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) +
|
||||
(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));
|
||||
|
||||
// Spacer row with top border.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 3),
|
||||
false, false, Vector2i(6, 1), GridFlags::BORDER_TOP);
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 3), false, false,
|
||||
Vector2i(6, 1), GridFlags::BORDER_TOP);
|
||||
|
||||
// Left spacer.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 4),
|
||||
false, false, Vector2i(1, 7));
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 4), false, false,
|
||||
Vector2i(1, 7));
|
||||
|
||||
// Generated label.
|
||||
mGeneratedLbl = std::make_shared<TextComponent>(mWindow, "Generated:",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGeneratedLbl = std::make_shared<TextComponent>(
|
||||
mWindow, "Generated:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mGeneratedLbl, Vector2i(1, 4), false, true, Vector2i(1, 1));
|
||||
|
||||
// Generated value/counter.
|
||||
mGeneratedVal = std::make_shared<TextComponent>(mWindow,
|
||||
std::to_string(mGamesProcessed),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGeneratedVal =
|
||||
std::make_shared<TextComponent>(mWindow, std::to_string(mGamesProcessed),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mGeneratedVal, Vector2i(2, 4), false, true, Vector2i(1, 1));
|
||||
|
||||
// Overwritten label.
|
||||
mOverwrittenLbl = std::make_shared<TextComponent>(mWindow, "Overwritten:",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mOverwrittenLbl = std::make_shared<TextComponent>(
|
||||
mWindow, "Overwritten:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mOverwrittenLbl, Vector2i(1, 5), false, true, Vector2i(1, 1));
|
||||
|
||||
// Overwritten value/counter.
|
||||
mOverwrittenVal = std::make_shared<TextComponent>(mWindow,
|
||||
std::to_string(mImagesOverwritten),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mOverwrittenVal =
|
||||
std::make_shared<TextComponent>(mWindow, std::to_string(mImagesOverwritten),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mOverwrittenVal, Vector2i(2, 5), false, true, Vector2i(1, 1));
|
||||
|
||||
// Skipping label.
|
||||
mSkippedLbl = std::make_shared<TextComponent>(mWindow, "Skipped (existing):",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mSkippedLbl = std::make_shared<TextComponent>(
|
||||
mWindow, "Skipped (existing):", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mSkippedLbl, Vector2i(1, 6), false, true, Vector2i(1, 1));
|
||||
|
||||
// Skipping value/counter.
|
||||
mSkippedVal= std::make_shared<TextComponent>(mWindow,
|
||||
std::to_string(mGamesSkipped),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mSkippedVal = std::make_shared<TextComponent>(
|
||||
mWindow, std::to_string(mGamesSkipped), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mSkippedVal, Vector2i(2, 6), false, true, Vector2i(1, 1));
|
||||
|
||||
// Failed label.
|
||||
mFailedLbl = std::make_shared<TextComponent>(mWindow, "Failed:",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mFailedLbl = std::make_shared<TextComponent>(mWindow, "Failed:", Font::get(FONT_SIZE_SMALL),
|
||||
0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mFailedLbl, Vector2i(1, 7), false, true, Vector2i(1, 1));
|
||||
|
||||
// Failed value/counter.
|
||||
mFailedVal = std::make_shared<TextComponent>(mWindow,
|
||||
std::to_string(mGamesFailed),
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mFailedVal = std::make_shared<TextComponent>(
|
||||
mWindow, std::to_string(mGamesFailed), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mFailedVal, Vector2i(2, 7), false, true, Vector2i(1, 1));
|
||||
|
||||
// Processing label.
|
||||
mProcessingLbl = std::make_shared<TextComponent>(mWindow, "Processing: ",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mProcessingLbl = std::make_shared<TextComponent>(
|
||||
mWindow, "Processing: ", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mProcessingLbl, Vector2i(3, 4), false, true, Vector2i(1, 1));
|
||||
|
||||
// Processing value.
|
||||
mProcessingVal = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mProcessingVal = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL),
|
||||
0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mProcessingVal, Vector2i(4, 4), false, true, Vector2i(1, 1));
|
||||
|
||||
// Spacer row.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 8),
|
||||
false, false, Vector2i(4, 1));
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 8), false, false,
|
||||
Vector2i(4, 1));
|
||||
|
||||
// Last error message label.
|
||||
mLastErrorLbl = std::make_shared<TextComponent>(mWindow, "Last error message:",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mLastErrorLbl = std::make_shared<TextComponent>(
|
||||
mWindow, "Last error message:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mLastErrorLbl, Vector2i(1, 9), false, true, Vector2i(4, 1));
|
||||
|
||||
// Last error message value.
|
||||
mLastErrorVal = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
|
||||
mLastErrorVal = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL),
|
||||
0x888888FF, ALIGN_LEFT);
|
||||
mGrid.setEntry(mLastErrorVal, Vector2i(1, 10), false, true, Vector2i(4, 1));
|
||||
|
||||
// Right spacer.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(5, 4),
|
||||
false, false, Vector2i(1, 7));
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(5, 4), false, false,
|
||||
Vector2i(1, 7));
|
||||
|
||||
// Spacer row with bottom border.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 11),
|
||||
false, false, Vector2i(6, 1), GridFlags::BORDER_BOTTOM);
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 11), false, false,
|
||||
Vector2i(6, 1), GridFlags::BORDER_BOTTOM);
|
||||
|
||||
// Buttons.
|
||||
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
||||
|
||||
mStartPauseButton = std::make_shared<ButtonComponent>(mWindow, "START",
|
||||
"start processing", [this](){
|
||||
if (!mProcessing) {
|
||||
mProcessing = true;
|
||||
mPaused = false;
|
||||
mStartPauseButton->setText("PAUSE", "pause processing");
|
||||
mCloseButton->setText("CLOSE", "close (abort processing)");
|
||||
mStatus->setText("RUNNING...");
|
||||
if (mGamesProcessed == 0) {
|
||||
LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games";
|
||||
mStartPauseButton =
|
||||
std::make_shared<ButtonComponent>(mWindow, "START", "start processing", [this]() {
|
||||
if (!mProcessing) {
|
||||
mProcessing = true;
|
||||
mPaused = false;
|
||||
mStartPauseButton->setText("PAUSE", "pause processing");
|
||||
mCloseButton->setText("CLOSE", "close (abort processing)");
|
||||
mStatus->setText("RUNNING...");
|
||||
if (mGamesProcessed == 0) {
|
||||
LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mMiximageGeneratorThread.joinable())
|
||||
mMiximageGeneratorThread.join();
|
||||
mPaused = true;
|
||||
update(1);
|
||||
mProcessing = false;
|
||||
this->mStartPauseButton->setText("START", "start processing");
|
||||
this->mCloseButton->setText("CLOSE", "close (abort processing)");
|
||||
mStatus->setText("PAUSED");
|
||||
}
|
||||
});
|
||||
else {
|
||||
if (mMiximageGeneratorThread.joinable())
|
||||
mMiximageGeneratorThread.join();
|
||||
mPaused = true;
|
||||
update(1);
|
||||
mProcessing = false;
|
||||
this->mStartPauseButton->setText("START", "start processing");
|
||||
this->mCloseButton->setText("CLOSE", "close (abort processing)");
|
||||
mStatus->setText("PAUSED");
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " <<
|
||||
mGamesProcessed << (mGamesProcessed == 1 ? " game (" : " games (") <<
|
||||
mImagesGenerated << (mImagesGenerated == 1 ? " image " : " images ") <<
|
||||
"generated, " << mGamesSkipped <<
|
||||
(mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << mGamesFailed <<
|
||||
(mGamesFailed == 1 ? " game " : " games ") << "failed)";
|
||||
LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " << mGamesProcessed
|
||||
<< (mGamesProcessed == 1 ? " game (" : " games (") << mImagesGenerated
|
||||
<< (mImagesGenerated == 1 ? " image " : " images ") << "generated, "
|
||||
<< mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ")
|
||||
<< "skipped, " << mGamesFailed
|
||||
<< (mGamesFailed == 1 ? " game " : " games ") << "failed)";
|
||||
}
|
||||
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
|
||||
// width rather than the 85% allowed for wider displays.
|
||||
float width = Renderer::getScreenWidth() *
|
||||
((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f);
|
||||
float width =
|
||||
Renderer::getScreenWidth() * ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f);
|
||||
|
||||
setSize(width, Renderer::getScreenHeight() * 0.75f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||
}
|
||||
|
||||
GuiOfflineGenerator::~GuiOfflineGenerator()
|
||||
|
@ -281,12 +278,12 @@ void GuiOfflineGenerator::update(int deltaTime)
|
|||
mGame = mGameQueue.front();
|
||||
mGameQueue.pop();
|
||||
|
||||
mGameName = mGame->getName() + " [" +
|
||||
Utils::String::toUpper(mGame->getSystem()->getName()) + "]";
|
||||
mGameName =
|
||||
mGame->getName() + " [" + Utils::String::toUpper(mGame->getSystem()->getName()) + "]";
|
||||
mProcessingVal->setText(mGameName);
|
||||
|
||||
if (!Settings::getInstance()->getBool("MiximageOverwrite") &&
|
||||
mGame->getMiximagePath() != "") {
|
||||
mGame->getMiximagePath() != "") {
|
||||
mGamesProcessed++;
|
||||
mGamesSkipped++;
|
||||
mSkippedVal->setText(std::to_string(mGamesSkipped));
|
||||
|
@ -303,14 +300,14 @@ void GuiOfflineGenerator::update(int deltaTime)
|
|||
mGeneratorFuture = mGeneratorPromise.get_future();
|
||||
|
||||
mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread,
|
||||
mMiximageGenerator.get(), &mGeneratorPromise);
|
||||
mMiximageGenerator.get(), &mGeneratorPromise);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the statistics.
|
||||
mStatus->setText("RUNNING...");
|
||||
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));
|
||||
mFailedVal->setText(std::to_string(mGamesFailed));
|
||||
|
@ -319,13 +316,13 @@ void GuiOfflineGenerator::update(int deltaTime)
|
|||
if (mGamesProcessed == mTotalGames) {
|
||||
mStatus->setText("COMPLETED");
|
||||
mStartPauseButton->setText("DONE", "done (close)");
|
||||
mStartPauseButton->setPressedFunc([this](){ delete this; });
|
||||
mStartPauseButton->setPressedFunc([this]() { delete this; });
|
||||
mCloseButton->setText("CLOSE", "close");
|
||||
mProcessingVal->setText("");
|
||||
LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated <<
|
||||
(mImagesGenerated == 1 ? " image " : " images ") << "generated, " <<
|
||||
mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " <<
|
||||
mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)";
|
||||
LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated
|
||||
<< (mImagesGenerated == 1 ? " image " : " images ") << "generated, "
|
||||
<< mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, "
|
||||
<< mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)";
|
||||
mProcessing = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
#ifndef 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 "MiximageGenerator.h"
|
||||
#include "components/ButtonComponent.h"
|
||||
#include "components/ComponentGrid.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
|
|
|
@ -10,23 +10,23 @@
|
|||
|
||||
#include "guis/GuiScraperMenu.h"
|
||||
|
||||
#include "FileData.h"
|
||||
#include "FileSorts.h"
|
||||
#include "SystemData.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "guis/GuiOfflineGenerator.h"
|
||||
#include "guis/GuiScraperMulti.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "FileData.h"
|
||||
#include "FileSorts.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
|
||||
GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
|
||||
: GuiComponent(window), mMenu(window, title)
|
||||
: GuiComponent(window)
|
||||
, mMenu(window, title)
|
||||
{
|
||||
// Scraper service.
|
||||
mScraper = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "SCRAPE FROM", false);
|
||||
mScraper = std::make_shared<OptionListComponent<std::string>>(mWindow, getHelpStyle(),
|
||||
"SCRAPE FROM", false);
|
||||
std::vector<std::string> scrapers = getScraperList();
|
||||
// Select either the first entry or the one read from the settings,
|
||||
// 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
|
||||
// based on the outcome of the checks below.
|
||||
mFilters = std::make_shared< OptionListComponent<GameFilterFunc>>
|
||||
(mWindow, getHelpStyle(), "SCRAPE THESE GAMES", false);
|
||||
mFilters->add("ALL GAMES", [](SystemData*, FileData*) -> bool { return true; }, false);
|
||||
mFilters->add("FAVORITE GAMES", [](SystemData*, FileData* g) -> bool {
|
||||
return g->getFavorite(); }, false);
|
||||
mFilters->add("NO METADATA", [](SystemData*, FileData* g) -> bool {
|
||||
return g->metadata.get("desc").empty(); }, false);
|
||||
mFilters->add("NO GAME IMAGE",
|
||||
[](SystemData*, FileData* g) -> bool {
|
||||
return g->getImagePath().empty(); }, false);
|
||||
mFilters->add("NO GAME VIDEO",
|
||||
[](SystemData*, FileData* g) -> bool {
|
||||
return g->getVideoPath().empty(); }, false);
|
||||
mFilters->add("FOLDERS ONLY",
|
||||
[](SystemData*, FileData* g) -> bool {
|
||||
return g->getType() == FOLDER; }, false);
|
||||
mFilters = std::make_shared<OptionListComponent<GameFilterFunc>>(mWindow, getHelpStyle(),
|
||||
"SCRAPE THESE GAMES", false);
|
||||
mFilters->add(
|
||||
"ALL GAMES",
|
||||
[](SystemData*, FileData*) -> bool {
|
||||
// All games.
|
||||
return true;
|
||||
},
|
||||
false);
|
||||
mFilters->add(
|
||||
"FAVORITE GAMES",
|
||||
[](SystemData*, FileData* g) -> bool {
|
||||
// Favorite games.
|
||||
return g->getFavorite();
|
||||
},
|
||||
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"));
|
||||
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).
|
||||
mSystems = std::make_shared< OptionListComponent<SystemData*>>
|
||||
(mWindow, getHelpStyle(), "SCRAPE THESE SYSTEMS", true);
|
||||
mSystems = std::make_shared<OptionListComponent<SystemData*>>(mWindow, getHelpStyle(),
|
||||
"SCRAPE THESE SYSTEMS", true);
|
||||
for (unsigned int i = 0; i < SystemData::sSystemVector.size(); i++) {
|
||||
if (!SystemData::sSystemVector[i]->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) {
|
||||
mSystems->add(SystemData::sSystemVector[i]->getFullName(),
|
||||
SystemData::sSystemVector[i],
|
||||
!SystemData::sSystemVector[i]->getPlatformIds().empty());
|
||||
SystemData::sSystemVector[i]->getScrapeFlag() ?
|
||||
mSystems->selectEntry(i) : mSystems->unselectEntry(i);
|
||||
mSystems->add(SystemData::sSystemVector[i]->getFullName(), SystemData::sSystemVector[i],
|
||||
!SystemData::sSystemVector[i]->getPlatformIds().empty());
|
||||
SystemData::sSystemVector[i]->getScrapeFlag() ? mSystems->selectEntry(i) :
|
||||
mSystems->unselectEntry(i);
|
||||
}
|
||||
}
|
||||
mMenu.addWithLabel("SCRAPE THESE SYSTEMS", mSystems);
|
||||
|
||||
addEntry("ACCOUNT SETTINGS", 0x777777FF, true, [this] {
|
||||
// Open the account options menu.
|
||||
openAccountOptions();
|
||||
});
|
||||
addEntry("CONTENT SETTINGS", 0x777777FF, true, [this] {
|
||||
|
@ -93,6 +121,7 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
|
|||
openContentOptions();
|
||||
});
|
||||
addEntry("MIXIMAGE SETTINGS", 0x777777FF, true, [this] {
|
||||
// Open the miximage options menu.
|
||||
openMiximageOptions();
|
||||
});
|
||||
addEntry("OTHER SETTINGS", 0x777777FF, true, [this] {
|
||||
|
@ -111,8 +140,8 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
|
|||
|
||||
setSize(mMenu.getSize());
|
||||
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
||||
Renderer::getScreenHeight() * 0.13f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||
Renderer::getScreenHeight() * 0.13f);
|
||||
}
|
||||
|
||||
GuiScraperMenu::~GuiScraperMenu()
|
||||
|
@ -120,9 +149,9 @@ GuiScraperMenu::~GuiScraperMenu()
|
|||
// Save the scrape flags to the system settings so that they are
|
||||
// remembered throughout the program session.
|
||||
std::vector<SystemData*> sys = mSystems->getSelectedObjects();
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
(*it)->setScrapeFlag(false);
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
(*it)->setScrapeFlag(false);
|
||||
for (auto it_sys = sys.cbegin(); it_sys != sys.cend(); it_sys++) {
|
||||
if ((*it)->getFullName() == (*it_sys)->getFullName())
|
||||
(*it)->setScrapeFlag(true);
|
||||
|
@ -136,48 +165,48 @@ void GuiScraperMenu::openAccountOptions()
|
|||
|
||||
// Whether to use the ScreenScraper account when scraping.
|
||||
auto scraper_use_account_screenscraper = std::make_shared<SwitchComponent>(mWindow);
|
||||
scraper_use_account_screenscraper->setState(Settings::getInstance()->
|
||||
getBool("ScraperUseAccountScreenScraper"));
|
||||
scraper_use_account_screenscraper->setState(
|
||||
Settings::getInstance()->getBool("ScraperUseAccountScreenScraper"));
|
||||
s->addWithLabel("USE THIS ACCOUNT FOR SCREENSCRAPER", scraper_use_account_screenscraper);
|
||||
s->addSaveFunc([scraper_use_account_screenscraper, s] {
|
||||
if (scraper_use_account_screenscraper->getState() !=
|
||||
Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) {
|
||||
Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) {
|
||||
Settings::getInstance()->setBool("ScraperUseAccountScreenScraper",
|
||||
scraper_use_account_screenscraper->getState());
|
||||
scraper_use_account_screenscraper->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// ScreenScraper username.
|
||||
auto scraper_username_screenscraper = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
|
||||
auto scraper_username_screenscraper = std::make_shared<TextComponent>(
|
||||
mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
|
||||
s->addEditableTextComponent("SCREENSCRAPER USERNAME", scraper_username_screenscraper,
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper"));
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper"));
|
||||
s->addSaveFunc([scraper_username_screenscraper, s] {
|
||||
if (scraper_username_screenscraper->getValue() !=
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper")) {
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper")) {
|
||||
Settings::getInstance()->setString("ScraperUsernameScreenScraper",
|
||||
scraper_username_screenscraper->getValue());
|
||||
scraper_username_screenscraper->getValue());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// ScreenScraper password.
|
||||
auto scraper_password_screenscraper = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
|
||||
auto scraper_password_screenscraper = std::make_shared<TextComponent>(
|
||||
mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT);
|
||||
std::string passwordMasked;
|
||||
if (Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") {
|
||||
passwordMasked = "********";
|
||||
scraper_password_screenscraper->setHiddenValue(
|
||||
Settings::getInstance()->getString("ScraperPasswordScreenScraper"));
|
||||
Settings::getInstance()->getString("ScraperPasswordScreenScraper"));
|
||||
}
|
||||
s->addEditableTextComponent("SCREENSCRAPER PASSWORD",
|
||||
scraper_password_screenscraper, passwordMasked, "", true);
|
||||
s->addEditableTextComponent("SCREENSCRAPER PASSWORD", scraper_password_screenscraper,
|
||||
passwordMasked, "", true);
|
||||
s->addSaveFunc([scraper_password_screenscraper, s] {
|
||||
if (scraper_password_screenscraper->getHiddenValue() !=
|
||||
Settings::getInstance()->getString("ScraperPasswordScreenScraper")) {
|
||||
Settings::getInstance()->getString("ScraperPasswordScreenScraper")) {
|
||||
Settings::getInstance()->setString("ScraperPasswordScreenScraper",
|
||||
scraper_password_screenscraper->getHiddenValue());
|
||||
scraper_password_screenscraper->getHiddenValue());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -215,8 +244,9 @@ void GuiScraperMenu::openContentOptions()
|
|||
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
|
||||
scrape_ratings->setEnabled(false);
|
||||
scrape_ratings->setOpacity(DISABLED_OPACITY);
|
||||
scrape_ratings->getParent()->getChild(scrape_ratings->
|
||||
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY);
|
||||
scrape_ratings->getParent()
|
||||
->getChild(scrape_ratings->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
|
||||
// Scrape other metadata.
|
||||
|
@ -245,8 +275,9 @@ void GuiScraperMenu::openContentOptions()
|
|||
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
|
||||
scrape_videos->setEnabled(false);
|
||||
scrape_videos->setOpacity(DISABLED_OPACITY);
|
||||
scrape_videos->getParent()->getChild(scrape_videos->
|
||||
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY);
|
||||
scrape_videos->getParent()
|
||||
->getChild(scrape_videos->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
|
||||
// Scrape screenshots images.
|
||||
|
@ -255,7 +286,7 @@ void GuiScraperMenu::openContentOptions()
|
|||
s->addWithLabel("SCRAPE SCREENSHOT IMAGES", scrape_screenshots);
|
||||
s->addSaveFunc([scrape_screenshots, s] {
|
||||
if (scrape_screenshots->getState() !=
|
||||
Settings::getInstance()->getBool("ScrapeScreenshots")) {
|
||||
Settings::getInstance()->getBool("ScrapeScreenshots")) {
|
||||
Settings::getInstance()->setBool("ScrapeScreenshots", scrape_screenshots->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
|
@ -299,8 +330,9 @@ void GuiScraperMenu::openContentOptions()
|
|||
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
|
||||
scrape_3dboxes->setEnabled(false);
|
||||
scrape_3dboxes->setOpacity(DISABLED_OPACITY);
|
||||
scrape_3dboxes->getParent()->getChild(scrape_3dboxes->
|
||||
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY);
|
||||
scrape_3dboxes->getParent()
|
||||
->getChild(scrape_3dboxes->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
|
||||
mWindow->pushGui(s);
|
||||
|
@ -311,12 +343,12 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
auto s = new GuiSettings(mWindow, "MIXIMAGE SETTINGS");
|
||||
|
||||
// Miximage resolution.
|
||||
auto miximage_resolution = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "MIXIMAGE RESOLUTION", false);
|
||||
auto miximage_resolution = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "MIXIMAGE RESOLUTION", false);
|
||||
std::string selectedResolution = Settings::getInstance()->getString("MiximageResolution");
|
||||
miximage_resolution->add("1280x960", "1280x960", selectedResolution == "1280x960");
|
||||
miximage_resolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440");
|
||||
miximage_resolution->add("640x480", "640x480", selectedResolution == "640x480");
|
||||
miximage_resolution->add("1280x960", "1280x960", selectedResolution == "1280x960");
|
||||
miximage_resolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440");
|
||||
miximage_resolution->add("640x480", "640x480", selectedResolution == "640x480");
|
||||
// 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.
|
||||
if (miximage_resolution->getSelectedObjects().size() == 0)
|
||||
|
@ -324,19 +356,19 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("MIXIMAGE RESOLUTION", miximage_resolution);
|
||||
s->addSaveFunc([miximage_resolution, s] {
|
||||
if (miximage_resolution->getSelected() !=
|
||||
Settings::getInstance()->getString("MiximageResolution")) {
|
||||
Settings::getInstance()->
|
||||
setString("MiximageResolution", miximage_resolution->getSelected());
|
||||
Settings::getInstance()->getString("MiximageResolution")) {
|
||||
Settings::getInstance()->setString("MiximageResolution",
|
||||
miximage_resolution->getSelected());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Screenshot scaling method.
|
||||
auto miximage_scaling = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "SCREENSHOT SCALING", false);
|
||||
auto miximage_scaling = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "SCREENSHOT SCALING", false);
|
||||
std::string selectedScaling = Settings::getInstance()->getString("MiximageScreenshotScaling");
|
||||
miximage_scaling->add("sharp", "sharp", selectedScaling == "sharp");
|
||||
miximage_scaling->add("smooth", "smooth", selectedScaling == "smooth");
|
||||
miximage_scaling->add("sharp", "sharp", selectedScaling == "sharp");
|
||||
miximage_scaling->add("smooth", "smooth", selectedScaling == "smooth");
|
||||
// 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.
|
||||
if (miximage_scaling->getSelectedObjects().size() == 0)
|
||||
|
@ -344,9 +376,9 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("SCREENSHOT SCALING METHOD", miximage_scaling);
|
||||
s->addSaveFunc([miximage_scaling, s] {
|
||||
if (miximage_scaling->getSelected() !=
|
||||
Settings::getInstance()->getString("MiximageScreenshotScaling")) {
|
||||
Settings::getInstance()->
|
||||
setString("MiximageScreenshotScaling", miximage_scaling->getSelected());
|
||||
Settings::getInstance()->getString("MiximageScreenshotScaling")) {
|
||||
Settings::getInstance()->setString("MiximageScreenshotScaling",
|
||||
miximage_scaling->getSelected());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -356,8 +388,7 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
miximage_generate->setState(Settings::getInstance()->getBool("MiximageGenerate"));
|
||||
s->addWithLabel("GENERATE MIXIMAGES WHEN SCRAPING", miximage_generate);
|
||||
s->addSaveFunc([miximage_generate, s] {
|
||||
if (miximage_generate->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageGenerate")) {
|
||||
if (miximage_generate->getState() != Settings::getInstance()->getBool("MiximageGenerate")) {
|
||||
Settings::getInstance()->setBool("MiximageGenerate", miximage_generate->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
|
@ -369,7 +400,7 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("OVERWRITE MIXIMAGES (SCRAPER/OFFLINE GENERATOR)", miximage_overwrite);
|
||||
s->addSaveFunc([miximage_overwrite, s] {
|
||||
if (miximage_overwrite->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageOverwrite")) {
|
||||
Settings::getInstance()->getBool("MiximageOverwrite")) {
|
||||
Settings::getInstance()->setBool("MiximageOverwrite", miximage_overwrite->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
|
@ -381,9 +412,9 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("REMOVE LETTERBOXES FROM SCREENSHOTS", remove_letterboxes);
|
||||
s->addSaveFunc([remove_letterboxes, s] {
|
||||
if (remove_letterboxes->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) {
|
||||
Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) {
|
||||
Settings::getInstance()->setBool("MiximageRemoveLetterboxes",
|
||||
remove_letterboxes->getState());
|
||||
remove_letterboxes->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -394,9 +425,9 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("REMOVE PILLARBOXES FROM SCREENSHOTS", remove_pillarboxes);
|
||||
s->addSaveFunc([remove_pillarboxes, s] {
|
||||
if (remove_pillarboxes->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageRemovePillarboxes")) {
|
||||
Settings::getInstance()->getBool("MiximageRemovePillarboxes")) {
|
||||
Settings::getInstance()->setBool("MiximageRemovePillarboxes",
|
||||
remove_pillarboxes->getState());
|
||||
remove_pillarboxes->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -407,9 +438,9 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("INCLUDE MARQUEE IMAGE", miximage_marquee);
|
||||
s->addSaveFunc([miximage_marquee, s] {
|
||||
if (miximage_marquee->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageIncludeMarquee")) {
|
||||
Settings::getInstance()->
|
||||
setBool("MiximageIncludeMarquee", miximage_marquee->getState());
|
||||
Settings::getInstance()->getBool("MiximageIncludeMarquee")) {
|
||||
Settings::getInstance()->setBool("MiximageIncludeMarquee",
|
||||
miximage_marquee->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -419,10 +450,8 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
miximage_box->setState(Settings::getInstance()->getBool("MiximageIncludeBox"));
|
||||
s->addWithLabel("INCLUDE BOX IMAGE", miximage_box);
|
||||
s->addSaveFunc([miximage_box, s] {
|
||||
if (miximage_box->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageIncludeBox")) {
|
||||
Settings::getInstance()->
|
||||
setBool("MiximageIncludeBox", miximage_box->getState());
|
||||
if (miximage_box->getState() != Settings::getInstance()->getBool("MiximageIncludeBox")) {
|
||||
Settings::getInstance()->setBool("MiximageIncludeBox", miximage_box->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -433,9 +462,9 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
s->addWithLabel("USE COVER IMAGE IF 3D BOX IS MISSING", miximage_cover_fallback);
|
||||
s->addSaveFunc([miximage_cover_fallback, s] {
|
||||
if (miximage_cover_fallback->getState() !=
|
||||
Settings::getInstance()->getBool("MiximageCoverFallback")) {
|
||||
Settings::getInstance()->
|
||||
setBool("MiximageCoverFallback", miximage_cover_fallback->getState());
|
||||
Settings::getInstance()->getBool("MiximageCoverFallback")) {
|
||||
Settings::getInstance()->setBool("MiximageCoverFallback",
|
||||
miximage_cover_fallback->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -443,11 +472,13 @@ void GuiScraperMenu::openMiximageOptions()
|
|||
// Miximage offline generator.
|
||||
ComponentListRow offline_generator_row;
|
||||
offline_generator_row.elements.clear();
|
||||
offline_generator_row.addElement(std::make_shared<TextComponent>
|
||||
(mWindow, "OFFLINE GENERATOR", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
offline_generator_row.addElement(std::make_shared<TextComponent>(mWindow, "OFFLINE GENERATOR",
|
||||
Font::get(FONT_SIZE_MEDIUM),
|
||||
0x777777FF),
|
||||
true);
|
||||
offline_generator_row.addElement(makeArrow(mWindow), false);
|
||||
offline_generator_row.makeAcceptInputHandler(
|
||||
std::bind(&GuiScraperMenu::openOfflineGenerator, this, s));
|
||||
std::bind(&GuiScraperMenu::openOfflineGenerator, this, s));
|
||||
s->addRow(offline_generator_row);
|
||||
|
||||
mWindow->pushGui(s);
|
||||
|
@ -457,9 +488,9 @@ void GuiScraperMenu::openOfflineGenerator(GuiSettings* settings)
|
|||
{
|
||||
if (mSystems->getSelectedObjects().empty()) {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n"
|
||||
"SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n"
|
||||
"AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR"));
|
||||
"THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n"
|
||||
"SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n"
|
||||
"AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -492,13 +523,15 @@ void GuiScraperMenu::openOtherOptions()
|
|||
auto s = new GuiSettings(mWindow, "OTHER SETTINGS");
|
||||
|
||||
// Scraper region.
|
||||
auto scraper_region = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "REGION", false);
|
||||
auto scraper_region = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "REGION", false);
|
||||
std::string selectedScraperRegion = Settings::getInstance()->getString("ScraperRegion");
|
||||
// clang-format off
|
||||
scraper_region->add("Europe", "eu", selectedScraperRegion == "eu");
|
||||
scraper_region->add("Japan", "jp", selectedScraperRegion == "jp");
|
||||
scraper_region->add("USA", "us", selectedScraperRegion == "us");
|
||||
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
|
||||
// configuration file. Simply set the region to "Europe" in this case.
|
||||
if (scraper_region->getSelectedObjects().size() == 0)
|
||||
|
@ -515,14 +548,16 @@ void GuiScraperMenu::openOtherOptions()
|
|||
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
|
||||
scraper_region->setEnabled(false);
|
||||
scraper_region->setOpacity(DISABLED_OPACITY);
|
||||
scraper_region->getParent()->getChild(scraper_region->
|
||||
getChildIndex() - 1)->setOpacity(DISABLED_OPACITY);
|
||||
scraper_region->getParent()
|
||||
->getChild(scraper_region->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
|
||||
// Scraper language.
|
||||
auto scraper_language = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "PREFERRED LANGUAGE", false);
|
||||
auto scraper_language = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "PREFERRED LANGUAGE", false);
|
||||
std::string selectedScraperLanguage = Settings::getInstance()->getString("ScraperLanguage");
|
||||
// clang-format off
|
||||
scraper_language->add("English", "en", selectedScraperLanguage == "en");
|
||||
scraper_language->add("Español", "es", selectedScraperLanguage == "es");
|
||||
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("Slovenčina", "sk", selectedScraperLanguage == "sk");
|
||||
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
|
||||
// configuration file. Simply set the language to "English" in this case.
|
||||
if (scraper_language->getSelectedObjects().size() == 0)
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
#include "scrapers/Scraper.h"
|
||||
|
||||
class FileData;
|
||||
template<typename T>
|
||||
class OptionListComponent;
|
||||
class SwitchComponent;
|
||||
class SystemData;
|
||||
|
||||
template <typename T> class OptionListComponent;
|
||||
typedef std::function<bool(SystemData*, FileData*)> GameFilterFunc;
|
||||
|
||||
class GuiScraperMenu : public GuiComponent
|
||||
|
@ -38,16 +37,18 @@ private:
|
|||
void pressedStart();
|
||||
void start();
|
||||
|
||||
void addEntry(const std::string&, unsigned int color,
|
||||
bool add_arrow, const std::function<void()>& func);
|
||||
void addEntry(const std::string&,
|
||||
unsigned int color,
|
||||
bool add_arrow,
|
||||
const std::function<void()>& func);
|
||||
void openAccountOptions();
|
||||
void openContentOptions();
|
||||
void openMiximageOptions();
|
||||
void openOfflineGenerator(GuiSettings* settings);
|
||||
void openOtherOptions();
|
||||
|
||||
std::queue<ScraperSearchParams> getSearches(
|
||||
std::vector<SystemData*> systems, GameFilterFunc selector);
|
||||
std::queue<ScraperSearchParams> getSearches(std::vector<SystemData*> systems,
|
||||
GameFilterFunc selector);
|
||||
|
||||
std::shared_ptr<OptionListComponent<std::string>> mScraper;
|
||||
std::shared_ptr<OptionListComponent<GameFilterFunc>> mFilters;
|
||||
|
|
|
@ -11,27 +11,26 @@
|
|||
|
||||
#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/MenuComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "guis/GuiScraperSearch.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "CollectionSystemsManager.h"
|
||||
#include "Gamelist.h"
|
||||
#include "MameNames.h"
|
||||
#include "SystemData.h"
|
||||
#include "Window.h"
|
||||
|
||||
GuiScraperMulti::GuiScraperMulti(
|
||||
Window* window,
|
||||
const std::queue<ScraperSearchParams>& searches,
|
||||
bool approveResults)
|
||||
: GuiComponent(window),
|
||||
mBackground(window, ":/graphics/frame.svg"),
|
||||
mGrid(window, Vector2i(1, 5)),
|
||||
mSearchQueue(searches),
|
||||
mApproveResults(approveResults)
|
||||
GuiScraperMulti::GuiScraperMulti(Window* window,
|
||||
const std::queue<ScraperSearchParams>& searches,
|
||||
bool approveResults)
|
||||
: GuiComponent(window)
|
||||
, mBackground(window, ":/graphics/frame.svg")
|
||||
, mGrid(window, Vector2i(1, 5))
|
||||
, mSearchQueue(searches)
|
||||
, mApproveResults(approveResults)
|
||||
{
|
||||
assert(mSearchQueue.size());
|
||||
|
||||
|
@ -47,41 +46,42 @@ GuiScraperMulti::GuiScraperMulti(
|
|||
|
||||
// Set up grid.
|
||||
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);
|
||||
|
||||
mSystem = std::make_shared<TextComponent>(mWindow, "SYSTEM",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
|
||||
mSystem = std::make_shared<TextComponent>(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM),
|
||||
0x777777FF, ALIGN_CENTER);
|
||||
mGrid.setEntry(mSystem, Vector2i(0, 1), false, true);
|
||||
|
||||
mSubtitle = std::make_shared<TextComponent>(mWindow, "subtitle text",
|
||||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
|
||||
mSubtitle = std::make_shared<TextComponent>(
|
||||
mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
|
||||
mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true);
|
||||
|
||||
if (mApproveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic"))
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
GuiScraperSearch::NEVER_AUTO_ACCEPT, mTotalGames);
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(
|
||||
mWindow, GuiScraperSearch::NEVER_AUTO_ACCEPT, mTotalGames);
|
||||
else if (mApproveResults && Settings::getInstance()->getBool("ScraperSemiautomatic"))
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
GuiScraperSearch::ACCEPT_SINGLE_MATCHES, mTotalGames);
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(
|
||||
mWindow, GuiScraperSearch::ACCEPT_SINGLE_MATCHES, mTotalGames);
|
||||
else if (!mApproveResults)
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, mTotalGames);
|
||||
mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult,
|
||||
this, std::placeholders::_1));
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(
|
||||
mWindow, GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, mTotalGames);
|
||||
mSearchComp->setAcceptCallback(
|
||||
std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1));
|
||||
mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this));
|
||||
mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this));
|
||||
mGrid.setEntry(mSearchComp, Vector2i(0, 3), mSearchComp->getSearchType() !=
|
||||
GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, true);
|
||||
mGrid.setEntry(mSearchComp, Vector2i(0, 3),
|
||||
mSearchComp->getSearchType() != GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT,
|
||||
true);
|
||||
|
||||
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
||||
|
||||
if (mApproveResults) {
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH",
|
||||
"refine search", [&] {
|
||||
mSearchComp->openInputScreen(mSearchQueue.front());
|
||||
mGrid.resetCursor();
|
||||
}));
|
||||
buttons.push_back(
|
||||
std::make_shared<ButtonComponent>(mWindow, "REFINE SEARCH", "refine search", [&] {
|
||||
mSearchComp->openInputScreen(mSearchQueue.front());
|
||||
mGrid.resetCursor();
|
||||
}));
|
||||
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SKIP", "skip game", [&] {
|
||||
skip();
|
||||
|
@ -89,8 +89,8 @@ GuiScraperMulti::GuiScraperMulti(
|
|||
}));
|
||||
}
|
||||
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "STOP",
|
||||
"stop", std::bind(&GuiScraperMulti::finish, this)));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "STOP", "stop",
|
||||
std::bind(&GuiScraperMulti::finish, this)));
|
||||
|
||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||
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();
|
||||
|
||||
setSize(width, Renderer::getScreenHeight() * 0.849f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() -
|
||||
mSize.y()) / 2);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||
|
||||
doNextSearch();
|
||||
}
|
||||
|
@ -111,8 +111,8 @@ GuiScraperMulti::~GuiScraperMulti()
|
|||
{
|
||||
if (mTotalSuccessful > 0) {
|
||||
// Sort all systems to possibly update their view style from Basic to Detailed or Video.
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it !=SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
(*it)->sortSystem();
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +121,10 @@ GuiScraperMulti::~GuiScraperMulti()
|
|||
|
||||
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(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(4, mButtonGrid->getSize().y() / mSize.y(), false);
|
||||
mGrid.setSize(mSize);
|
||||
|
@ -148,34 +148,36 @@ void GuiScraperMulti::doNextSearch()
|
|||
}
|
||||
else {
|
||||
if (mSearchQueue.front().game->isArcadeGame() &&
|
||||
Settings::getInstance()->getString("Scraper") == "thegamesdb")
|
||||
scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()) +
|
||||
" (" + MameNames::getInstance()->getCleanName(mSearchQueue.front().game->
|
||||
getCleanName()) + ")";
|
||||
Settings::getInstance()->getString("Scraper") == "thegamesdb")
|
||||
scrapeName =
|
||||
Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()) + " (" +
|
||||
MameNames::getInstance()->getCleanName(mSearchQueue.front().game->getCleanName()) +
|
||||
")";
|
||||
else
|
||||
scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath());
|
||||
}
|
||||
|
||||
// Extract possible subfolders from the path.
|
||||
std::string folderPath = Utils::String::replace(
|
||||
Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()),
|
||||
mSearchQueue.front().system->getSystemEnvData()->mStartPath, "");
|
||||
std::string folderPath =
|
||||
Utils::String::replace(Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()),
|
||||
mSearchQueue.front().system->getSystemEnvData()->mStartPath, "");
|
||||
|
||||
if (folderPath.size() >= 2) {
|
||||
folderPath.erase(0, 1);
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
folderPath.push_back('\\');
|
||||
folderPath = Utils::String::replace(folderPath, "/", "\\");
|
||||
#else
|
||||
#else
|
||||
folderPath.push_back('/');
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update subtitle.
|
||||
ss.str("");
|
||||
ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << folderPath <<
|
||||
scrapeName << ((mSearchQueue.front().game->getType() == FOLDER) ? " " +
|
||||
ViewController::FOLDER_CHAR : "");
|
||||
ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << folderPath
|
||||
<< scrapeName
|
||||
<< ((mSearchQueue.front().game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR :
|
||||
"");
|
||||
mSubtitle->setText(ss.str());
|
||||
|
||||
mSearchComp->search(mSearchQueue.front());
|
||||
|
@ -212,12 +214,12 @@ void GuiScraperMulti::finish()
|
|||
ss << "NO GAMES WERE SCRAPED";
|
||||
}
|
||||
else {
|
||||
ss << mTotalSuccessful << " GAME" <<
|
||||
((mTotalSuccessful > 1) ? "S" : "") << " SUCCESSFULLY SCRAPED";
|
||||
ss << mTotalSuccessful << " GAME" << ((mTotalSuccessful > 1) ? "S" : "")
|
||||
<< " SUCCESSFULLY SCRAPED";
|
||||
|
||||
if (mTotalSkipped > 0)
|
||||
ss << "\n" << mTotalSkipped << " GAME"
|
||||
<< ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED";
|
||||
ss << "\n"
|
||||
<< mTotalSkipped << " GAME" << ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED";
|
||||
}
|
||||
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), ss.str(), "OK", [&] {
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
#ifndef 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/NinePatchComponent.h"
|
||||
#include "scrapers/Scraper.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "MetaData.h"
|
||||
|
||||
class GuiScraperSearch;
|
||||
class TextComponent;
|
||||
|
@ -24,10 +24,9 @@ class TextComponent;
|
|||
class GuiScraperMulti : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiScraperMulti(
|
||||
Window* window,
|
||||
const std::queue<ScraperSearchParams>& searches,
|
||||
bool approveResults);
|
||||
GuiScraperMulti(Window* window,
|
||||
const std::queue<ScraperSearchParams>& searches,
|
||||
bool approveResults);
|
||||
|
||||
virtual ~GuiScraperMulti();
|
||||
|
||||
|
@ -40,15 +39,7 @@ private:
|
|||
void acceptResult(const ScraperSearchResult& result);
|
||||
void skip();
|
||||
void doNextSearch();
|
||||
|
||||
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;
|
||||
ComponentGrid mGrid;
|
||||
|
@ -58,6 +49,14 @@ private:
|
|||
std::shared_ptr<TextComponent> mSubtitle;
|
||||
std::shared_ptr<GuiScraperSearch> mSearchComp;
|
||||
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
|
||||
|
|
|
@ -15,6 +15,13 @@
|
|||
|
||||
#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/DateTimeEditComponent.h"
|
||||
#include "components/ImageComponent.h"
|
||||
|
@ -26,28 +33,18 @@
|
|||
#include "resources/Font.h"
|
||||
#include "utils/StringUtil.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
|
||||
|
||||
GuiScraperSearch::GuiScraperSearch(
|
||||
Window* window,
|
||||
SearchType type,
|
||||
unsigned int scrapeCount)
|
||||
: GuiComponent(window),
|
||||
mGrid(window, Vector2i(4, 3)),
|
||||
mBusyAnim(window),
|
||||
mSearchType(type),
|
||||
mScrapeCount(scrapeCount),
|
||||
mScrapeRatings(false),
|
||||
mRefinedSearch(false),
|
||||
mFoundGame(false)
|
||||
GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int scrapeCount)
|
||||
: GuiComponent(window)
|
||||
, mGrid(window, Vector2i(4, 3))
|
||||
, mBusyAnim(window)
|
||||
, mSearchType(type)
|
||||
, mScrapeCount(scrapeCount)
|
||||
, mScrapeRatings(false)
|
||||
, mRefinedSearch(false)
|
||||
, mFoundGame(false)
|
||||
{
|
||||
addChild(&mGrid);
|
||||
|
||||
|
@ -56,12 +53,12 @@ GuiScraperSearch::GuiScraperSearch(
|
|||
mRetryCount = 0;
|
||||
|
||||
// Left spacer (empty component, needed for borders).
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0),
|
||||
false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false, false,
|
||||
Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
||||
|
||||
// Selected 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.
|
||||
mResultThumbnail = std::make_shared<ImageComponent>(mWindow);
|
||||
|
@ -77,7 +74,7 @@ GuiScraperSearch::GuiScraperSearch(
|
|||
mDescContainer->setScrollParameters(6000, 3000, 85);
|
||||
|
||||
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->setAutoScroll(true);
|
||||
|
||||
|
@ -89,40 +86,47 @@ GuiScraperSearch::GuiScraperSearch(
|
|||
mMD_ReleaseDate = std::make_shared<DateTimeEditComponent>(mWindow);
|
||||
mMD_ReleaseDate->setColor(mdColor);
|
||||
mMD_ReleaseDate->setUppercase(true);
|
||||
mMD_Developer = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT,
|
||||
Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f);
|
||||
mMD_Publisher = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT,
|
||||
Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f);
|
||||
mMD_Genre = 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_Developer =
|
||||
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
|
||||
Vector2f::Zero(), 0x00000000, 0.02f);
|
||||
mMD_Publisher =
|
||||
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
|
||||
Vector2f::Zero(), 0x00000000, 0.02f);
|
||||
mMD_Genre =
|
||||
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);
|
||||
|
||||
if (Settings::getInstance()->getString("Scraper") != "thegamesdb")
|
||||
mScrapeRatings = true;
|
||||
|
||||
if (mScrapeRatings)
|
||||
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent>
|
||||
(mWindow, "RATING:", font, mdLblColor), mMD_Rating, false));
|
||||
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent>
|
||||
(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate));
|
||||
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent>
|
||||
(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer));
|
||||
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent>
|
||||
(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher));
|
||||
mMD_Pairs.push_back(MetaDataPair(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));
|
||||
mMD_Pairs.push_back(
|
||||
MetaDataPair(std::make_shared<TextComponent>(mWindow, "RATING:", font, mdLblColor),
|
||||
mMD_Rating, false));
|
||||
|
||||
mMD_Pairs.push_back(MetaDataPair(
|
||||
std::make_shared<TextComponent>(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate));
|
||||
mMD_Pairs.push_back(MetaDataPair(
|
||||
std::make_shared<TextComponent>(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer));
|
||||
mMD_Pairs.push_back(MetaDataPair(
|
||||
std::make_shared<TextComponent>(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher));
|
||||
mMD_Pairs.push_back(MetaDataPair(
|
||||
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
|
||||
// size so the GUI looks consistent.
|
||||
if (!mScrapeRatings)
|
||||
mMD_Pairs.push_back(MetaDataPair(std::make_shared<TextComponent>
|
||||
(mWindow, "", font, mdLblColor), mMD_Filler));
|
||||
mMD_Pairs.push_back(MetaDataPair(
|
||||
std::make_shared<TextComponent>(mWindow, "", font, mdLblColor), mMD_Filler));
|
||||
|
||||
mMD_Grid = std::make_shared<ComponentGrid>(mWindow,
|
||||
Vector2i(2, static_cast<int>(mMD_Pairs.size()*2 - 1)));
|
||||
mMD_Grid = std::make_shared<ComponentGrid>(
|
||||
mWindow, Vector2i(2, static_cast<int>(mMD_Pairs.size() * 2 - 1)));
|
||||
unsigned int i = 0;
|
||||
for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) {
|
||||
mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true);
|
||||
|
@ -135,7 +139,9 @@ GuiScraperSearch::GuiScraperSearch(
|
|||
// Result list.
|
||||
mResultList = std::make_shared<ComponentList>(mWindow);
|
||||
mResultList->setCursorChangedCallback([this](CursorState state) {
|
||||
if (state == CURSOR_STOPPED) updateInfoPane(); });
|
||||
if (state == CURSOR_STOPPED)
|
||||
updateInfoPane();
|
||||
});
|
||||
|
||||
updateViewStyle();
|
||||
}
|
||||
|
@ -163,7 +169,7 @@ GuiScraperSearch::~GuiScraperSearch()
|
|||
// This is required to properly refresh the gamelist view if the user aborted the
|
||||
// scraping when the miximage was getting generated.
|
||||
if (Settings::getInstance()->getBool("MiximageGenerate") &&
|
||||
mMiximageGeneratorThread.joinable()) {
|
||||
mMiximageGeneratorThread.joinable()) {
|
||||
mScrapeResult.savedNewMedia = true;
|
||||
// We always let the miximage generator thread complete.
|
||||
mMiximageGeneratorThread.join();
|
||||
|
@ -196,7 +202,7 @@ void GuiScraperSearch::onSizeChanged()
|
|||
// Row heights.
|
||||
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name.
|
||||
mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) /
|
||||
mGrid.getSize().y()); // Result name.
|
||||
mGrid.getSize().y()); // Result name.
|
||||
else
|
||||
mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding.
|
||||
|
||||
|
@ -216,11 +222,11 @@ void GuiScraperSearch::onSizeChanged()
|
|||
resizeMetadata();
|
||||
|
||||
if (mSearchType != ALWAYS_ACCEPT_FIRST_RESULT)
|
||||
mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale +
|
||||
mGrid.getColWidth(2), mResultDesc->getFont()->getHeight() * 3);
|
||||
mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale + mGrid.getColWidth(2),
|
||||
mResultDesc->getFont()->getHeight() * 3.0f);
|
||||
else
|
||||
mDescContainer->setSize(mGrid.getColWidth(3) * boxartCellScale,
|
||||
mResultDesc->getFont()->getHeight() * 6);
|
||||
mResultDesc->getFont()->getHeight() * 6.0f);
|
||||
|
||||
// Make description text wrap at edge of container.
|
||||
mResultDesc->setSize(mDescContainer->getSize().x(), 0);
|
||||
|
@ -247,13 +253,14 @@ void GuiScraperSearch::resizeMetadata()
|
|||
it->first->setFont(fontLbl);
|
||||
it->first->setSize(0, 0);
|
||||
if (it->first->getSize().x() > maxLblWidth)
|
||||
maxLblWidth = it->first->getSize().x() +
|
||||
(16.0f * Renderer::getScreenWidthModifier());
|
||||
maxLblWidth =
|
||||
it->first->getSize().x() + (16.0f * Renderer::getScreenWidthModifier());
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < mMD_Pairs.size(); i++)
|
||||
mMD_Grid->setRowHeightPerc(i * 2, (fontLbl->getLetterHeight() +
|
||||
(2.0f * Renderer::getScreenHeightModifier())) / mMD_Grid->getSize().y());
|
||||
mMD_Grid->setRowHeightPerc(
|
||||
i * 2, (fontLbl->getLetterHeight() + (2.0f * Renderer::getScreenHeightModifier())) /
|
||||
mMD_Grid->getSize().y());
|
||||
|
||||
// Update component fonts.
|
||||
mMD_ReleaseDate->setFont(fontComp);
|
||||
|
@ -286,30 +293,30 @@ void GuiScraperSearch::updateViewStyle()
|
|||
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) {
|
||||
// Show name.
|
||||
mGrid.setEntry(mResultName, Vector2i(1, 0), false, false, Vector2i(2, 1),
|
||||
GridFlags::BORDER_TOP);
|
||||
GridFlags::BORDER_TOP);
|
||||
|
||||
// Need a border on the bottom left.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 2),
|
||||
false, false, Vector2i(3, 1), GridFlags::BORDER_BOTTOM);
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 2), false, false,
|
||||
Vector2i(3, 1), GridFlags::BORDER_BOTTOM);
|
||||
|
||||
// Show description on the right.
|
||||
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.
|
||||
mResultDesc->setSize(mDescContainer->getSize().x(), 0.0f);
|
||||
}
|
||||
else {
|
||||
// Fake row where name would be.
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0),
|
||||
false, true, Vector2i(2, 1), GridFlags::BORDER_TOP);
|
||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), false, true,
|
||||
Vector2i(2, 1), GridFlags::BORDER_TOP);
|
||||
|
||||
// Show result list on the right.
|
||||
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.
|
||||
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.
|
||||
mResultDesc->setSize(mDescContainer->getSize().x(), 0);
|
||||
}
|
||||
|
@ -355,16 +362,17 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
|
|||
if (results.empty()) {
|
||||
// Check if the scraper used is still valid.
|
||||
if (!isValidConfiguredScraper()) {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
Utils::String::toUpper("Configured scraper is no longer available.\n"
|
||||
"Please change the scraping source in the settings."),
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, getHelpStyle(),
|
||||
Utils::String::toUpper("Configured scraper is no longer available.\n"
|
||||
"Please change the scraping source in the settings."),
|
||||
"FINISH", mSkipCallback));
|
||||
}
|
||||
else {
|
||||
mFoundGame = false;
|
||||
ComponentListRow row;
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "NO GAMES FOUND",
|
||||
font, color), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "NO GAMES FOUND", font, color),
|
||||
true);
|
||||
|
||||
if (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++) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true);
|
||||
row.addElement(
|
||||
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)); });
|
||||
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
|
||||
// the results or we'll get stuck forever waiting for a thumbnail to be downloaded.
|
||||
if (mSearchType == ACCEPT_SINGLE_MATCHES && results.size() == 1 &&
|
||||
mScraperResults.front().thumbnailImageUrl == "")
|
||||
mScraperResults.front().thumbnailImageUrl == "")
|
||||
returnResult(mScraperResults.front());
|
||||
|
||||
// For automatic mode, if there's no thumbnail to download or no matching games found,
|
||||
// proceed directly or we'll get stuck forever.
|
||||
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) {
|
||||
if (mScraperResults.size() == 0 || (mScraperResults.size() > 0 &&
|
||||
mScraperResults.front().thumbnailImageUrl == "")) {
|
||||
if (mScraperResults.size() == 0 ||
|
||||
(mScraperResults.size() > 0 && mScraperResults.front().thumbnailImageUrl == "")) {
|
||||
if (mScraperResults.size() == 0)
|
||||
mSkipCallback();
|
||||
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,
|
||||
// a new round of retries will take place.
|
||||
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", "");
|
||||
mRetrySearch = true;
|
||||
mRetryCount++;
|
||||
LOG(LogError) << "GuiScraperSearch: Attempting automatic retry " << mRetryCount <<
|
||||
" of " << FAILED_VERIFICATION_RETRIES;
|
||||
LOG(LogError) << "GuiScraperSearch: Attempting automatic retry " << mRetryCount << " of "
|
||||
<< FAILED_VERIFICATION_RETRIES;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
|
@ -435,15 +445,16 @@ void GuiScraperSearch::onSearchError(const std::string& error, HttpReq::Status s
|
|||
if (mScrapeCount > 1) {
|
||||
LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", "");
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error),
|
||||
"RETRY", std::bind(&GuiScraperSearch::search, this, mLastSearch),
|
||||
"SKIP", mSkipCallback,
|
||||
"CANCEL", mCancelCallback, true));
|
||||
"RETRY",
|
||||
std::bind(&GuiScraperSearch::search, this, mLastSearch),
|
||||
"SKIP", mSkipCallback, "CANCEL", mCancelCallback, true));
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", "");
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error),
|
||||
"RETRY", std::bind(&GuiScraperSearch::search, this, mLastSearch),
|
||||
"CANCEL", mCancelCallback, "", nullptr, true));
|
||||
"RETRY",
|
||||
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
|
||||
// each thumbnail separately even as they're downloading while scrolling
|
||||
// through the result list.
|
||||
mThumbnailReqMap.insert(std::pair<std::string,
|
||||
std::unique_ptr<HttpReq>>(mScraperResults[i].thumbnailImageUrl,
|
||||
std::unique_ptr<HttpReq>(new HttpReq(thumb))));
|
||||
mThumbnailReqMap.insert(std::pair<std::string, std::unique_ptr<HttpReq>>(
|
||||
mScraperResults[i].thumbnailImageUrl,
|
||||
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.
|
||||
if (mScraperResults.size() > 0) {
|
||||
auto it = mThumbnailReqMap.find(mScraperResults[mResultList->
|
||||
getCursorId()].thumbnailImageUrl);
|
||||
auto it =
|
||||
mThumbnailReqMap.find(mScraperResults[mResultList->getCursorId()].thumbnailImageUrl);
|
||||
if (it != mThumbnailReqMap.end() && it->second->status() != HttpReq::REQ_IN_PROGRESS)
|
||||
updateThumbnail();
|
||||
}
|
||||
|
@ -681,13 +692,14 @@ void GuiScraperSearch::update(int deltaTime)
|
|||
mMDResolveHandle.reset();
|
||||
|
||||
if (mScrapeResult.mediaFilesDownloadStatus == COMPLETED &&
|
||||
Settings::getInstance()->getBool("MiximageGenerate")) {
|
||||
Settings::getInstance()->getBool("MiximageGenerate")) {
|
||||
std::string currentMiximage = mLastSearch.game->getMiximagePath();
|
||||
if (currentMiximage == "" || (currentMiximage != "" &&
|
||||
Settings::getInstance()->getBool("MiximageOverwrite"))) {
|
||||
if (currentMiximage == "" ||
|
||||
(currentMiximage != "" &&
|
||||
Settings::getInstance()->getBool("MiximageOverwrite"))) {
|
||||
|
||||
mMiximageGenerator = std::make_unique<MiximageGenerator>(mLastSearch.game,
|
||||
mResultMessage);
|
||||
mMiximageGenerator =
|
||||
std::make_unique<MiximageGenerator>(mLastSearch.game, mResultMessage);
|
||||
|
||||
// The promise/future mechanism is used as signaling for the thread to
|
||||
// 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);
|
||||
mGeneratorFuture = mGeneratorPromise.get_future();
|
||||
|
||||
mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread,
|
||||
mMiximageGenerator.get(), &mGeneratorPromise);
|
||||
mMiximageGeneratorThread =
|
||||
std::thread(&MiximageGenerator::startThread, mMiximageGenerator.get(),
|
||||
&mGeneratorPromise);
|
||||
}
|
||||
else {
|
||||
returnResult(mScrapeResult);
|
||||
|
@ -724,7 +737,7 @@ void GuiScraperSearch::updateThumbnail()
|
|||
// thumbnail download has been completed for this game.
|
||||
if (mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus == IN_PROGRESS) {
|
||||
mScraperResults[mResultList->getCursorId()].thumbnailImageData =
|
||||
it->second->getContent();
|
||||
it->second->getContent();
|
||||
mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus = COMPLETED;
|
||||
}
|
||||
// Activate the thumbnail in the GUI.
|
||||
|
@ -737,7 +750,7 @@ void GuiScraperSearch::updateThumbnail()
|
|||
else {
|
||||
mResultThumbnail->setImage("");
|
||||
onSearchError("Error downloading thumbnail:\n " + it->second->getErrorMsg(),
|
||||
it->second->status());
|
||||
it->second->status());
|
||||
}
|
||||
|
||||
mThumbnailReqMap.erase(it);
|
||||
|
@ -746,9 +759,9 @@ void GuiScraperSearch::updateThumbnail()
|
|||
// we are in semi-automatic mode with a single matching game result, we proceed
|
||||
// to immediately download the rest of the media files.
|
||||
if ((mSearchType == ALWAYS_ACCEPT_FIRST_RESULT ||
|
||||
(mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1 &&
|
||||
mRefinedSearch == false)) &&
|
||||
mScraperResults.front().thumbnailDownloadStatus == COMPLETED) {
|
||||
(mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1 &&
|
||||
mRefinedSearch == false)) &&
|
||||
mScraperResults.front().thumbnailDownloadStatus == COMPLETED) {
|
||||
mRefinedSearch = false;
|
||||
if (mScraperResults.size() == 0)
|
||||
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
|
||||
// as TheGamesDB has issues with searches using the short MAME names.
|
||||
if (params.game->isArcadeGame() &&
|
||||
Settings::getInstance()->getString("Scraper") == "thegamesdb")
|
||||
Settings::getInstance()->getString("Scraper") == "thegamesdb")
|
||||
searchString = MameNames::getInstance()->getCleanName(params.game->getCleanName());
|
||||
else
|
||||
searchString = params.game->getCleanName();
|
||||
}
|
||||
}
|
||||
else {
|
||||
else {
|
||||
searchString = params.nameOverride;
|
||||
}
|
||||
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH",
|
||||
searchString, searchForFunc, false, "SEARCH", "APPLY CHANGES?"));
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH", searchString,
|
||||
searchForFunc, false, "SEARCH", "APPLY CHANGES?"));
|
||||
}
|
||||
|
||||
bool GuiScraperSearch::saveMetadata(
|
||||
const ScraperSearchResult& result, MetaDataList& metadata, FileData* scrapedGame)
|
||||
bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result,
|
||||
MetaDataList& metadata,
|
||||
FileData* scrapedGame)
|
||||
{
|
||||
bool metadataUpdated = false;
|
||||
bool hasDefaultName = false;
|
||||
|
@ -825,7 +839,7 @@ bool GuiScraperSearch::saveMetadata(
|
|||
// Skip element if the setting to not scrape metadata has been set,
|
||||
// unless its type is rating or name.
|
||||
if (!Settings::getInstance()->getBool("ScrapeMetadata") &&
|
||||
(key != "rating" && key != "name"))
|
||||
(key != "rating" && key != "name"))
|
||||
continue;
|
||||
|
||||
// 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)
|
||||
prompts.push_back(HelpPrompt("x", "skip"));
|
||||
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"));
|
||||
|
||||
return prompts;
|
||||
|
@ -897,13 +911,3 @@ HelpStyle GuiScraperSearch::getHelpStyle()
|
|||
style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system");
|
||||
return style;
|
||||
}
|
||||
|
||||
void GuiScraperSearch::onFocusGained()
|
||||
{
|
||||
mGrid.onFocusGained();
|
||||
}
|
||||
|
||||
void GuiScraperSearch::onFocusLost()
|
||||
{
|
||||
mGrid.onFocusLost();
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
#ifndef 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/ComponentGrid.h"
|
||||
#include "scrapers/Scraper.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "MiximageGenerator.h"
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
@ -36,9 +36,9 @@ class GuiScraperSearch : public GuiComponent
|
|||
{
|
||||
public:
|
||||
enum SearchType {
|
||||
ALWAYS_ACCEPT_FIRST_RESULT,
|
||||
ACCEPT_SINGLE_MATCHES,
|
||||
NEVER_AUTO_ACCEPT
|
||||
ALWAYS_ACCEPT_FIRST_RESULT, // Automatic mode.
|
||||
ACCEPT_SINGLE_MATCHES, // Semi-automatic mode.
|
||||
NEVER_AUTO_ACCEPT // Manual mode.
|
||||
};
|
||||
|
||||
GuiScraperSearch(Window* window, SearchType searchType, unsigned int scrapeCount = 1);
|
||||
|
@ -47,20 +47,29 @@ public:
|
|||
void search(const ScraperSearchParams& params);
|
||||
void openInputScreen(ScraperSearchParams& from);
|
||||
void stop();
|
||||
inline SearchType getSearchType() const { return mSearchType; }
|
||||
SearchType getSearchType() const { return mSearchType; }
|
||||
bool getSavedNewMedia()
|
||||
{ return (mMDResolveHandle ? mMDResolveHandle->getSavedNewMedia() : false); };
|
||||
{
|
||||
return (mMDResolveHandle ? mMDResolveHandle->getSavedNewMedia() : false);
|
||||
}
|
||||
static bool saveMetadata(const ScraperSearchResult& result,
|
||||
MetaDataList& metadata, FileData* scrapedGame);
|
||||
MetaDataList& metadata,
|
||||
FileData* scrapedGame);
|
||||
|
||||
// Metadata assets will be resolved before calling the accept callback
|
||||
// (e.g. result.mdl's "image" is automatically downloaded and properly set).
|
||||
inline void setAcceptCallback(const std::function<void(const ScraperSearchResult&)>&
|
||||
acceptCallback) { mAcceptCallback = acceptCallback; }
|
||||
inline void setSkipCallback(const std::function<void()>&
|
||||
skipCallback) { mSkipCallback = skipCallback; };
|
||||
inline void setCancelCallback(const std::function<void()>&
|
||||
cancelCallback) { mScrapeCount -= 1; mCancelCallback = cancelCallback; }
|
||||
// Metadata assets will be resolved before calling the accept callback.
|
||||
void setAcceptCallback(const std::function<void(const ScraperSearchResult&)>& acceptCallback)
|
||||
{
|
||||
mAcceptCallback = acceptCallback;
|
||||
}
|
||||
void setSkipCallback(const std::function<void()>& skipCallback)
|
||||
{
|
||||
mSkipCallback = skipCallback;
|
||||
}
|
||||
void setCancelCallback(const std::function<void()>& cancelCallback)
|
||||
{
|
||||
mScrapeCount -= 1;
|
||||
mCancelCallback = cancelCallback;
|
||||
}
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
|
@ -68,20 +77,19 @@ public:
|
|||
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
HelpStyle getHelpStyle() override;
|
||||
void onSizeChanged() override;
|
||||
void onFocusGained() override;
|
||||
void onFocusLost() override;
|
||||
|
||||
void unsetRefinedSearch() { mRefinedSearch = false; }
|
||||
void onFocusGained() override { mGrid.onFocusGained(); }
|
||||
void onFocusLost() override { mGrid.onFocusLost(); }
|
||||
|
||||
private:
|
||||
void updateViewStyle();
|
||||
void updateThumbnail();
|
||||
void updateInfoPane();
|
||||
|
||||
void resizeMetadata();
|
||||
|
||||
void onSearchError(const std::string& error, HttpReq::Status status =
|
||||
HttpReq::REQ_UNDEFINED_ERROR);
|
||||
void onSearchError(const std::string& error,
|
||||
HttpReq::Status status = HttpReq::REQ_UNDEFINED_ERROR);
|
||||
void onSearchDone(const std::vector<ScraperSearchResult>& results);
|
||||
|
||||
int getSelectedIndex();
|
||||
|
@ -117,8 +125,13 @@ private:
|
|||
bool resize;
|
||||
|
||||
MetaDataPair(const std::shared_ptr<TextComponent>& f,
|
||||
const std::shared_ptr<GuiComponent>& s, bool r = true)
|
||||
: first(f), second(s), resize(r) {};
|
||||
const std::shared_ptr<GuiComponent>& s,
|
||||
bool r = true)
|
||||
: first(f)
|
||||
, second(s)
|
||||
, resize(r)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MetaDataPair> mMD_Pairs;
|
||||
|
|
|
@ -9,54 +9,55 @@
|
|||
|
||||
#include "guis/GuiScreensaverOptions.h"
|
||||
|
||||
#include "Settings.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
#include "components/SliderComponent.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "Settings.h"
|
||||
|
||||
GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& title)
|
||||
: GuiSettings(window, title)
|
||||
: GuiSettings(window, title)
|
||||
{
|
||||
// Screensaver timer.
|
||||
auto screensaver_timer = std::make_shared<SliderComponent>(mWindow, 0.f, 30.f, 1.f, "m");
|
||||
screensaver_timer->setValue(static_cast<float>(Settings::getInstance()->
|
||||
getInt("ScreensaverTimer") / (1000 * 60)));
|
||||
auto screensaver_timer = std::make_shared<SliderComponent>(mWindow, 0.0f, 30.0f, 1.0f, "m");
|
||||
screensaver_timer->setValue(
|
||||
static_cast<float>(Settings::getInstance()->getInt("ScreensaverTimer") / (1000 * 60)));
|
||||
addWithLabel("START SCREENSAVER AFTER (MINUTES)", screensaver_timer);
|
||||
addSaveFunc([screensaver_timer, this] {
|
||||
if (static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60)) !=
|
||||
Settings::getInstance()->getInt("ScreensaverTimer")) {
|
||||
Settings::getInstance()->setInt("ScreensaverTimer",
|
||||
static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60)));
|
||||
Settings::getInstance()->getInt("ScreensaverTimer")) {
|
||||
Settings::getInstance()->setInt(
|
||||
"ScreensaverTimer",
|
||||
static_cast<int>(std::round(screensaver_timer->getValue()) * (1000 * 60)));
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Screensaver type.
|
||||
auto screensaver_type = std::make_shared<OptionListComponent<std::string>>
|
||||
(mWindow, getHelpStyle(), "SCREENSAVER TYPE", false);
|
||||
auto screensaver_type = std::make_shared<OptionListComponent<std::string>>(
|
||||
mWindow, getHelpStyle(), "SCREENSAVER TYPE", false);
|
||||
std::vector<std::string> screensavers;
|
||||
screensavers.push_back("dim");
|
||||
screensavers.push_back("black");
|
||||
screensavers.push_back("slideshow");
|
||||
screensavers.push_back("video");
|
||||
for (auto it = screensavers.cbegin(); it != screensavers.cend(); it++)
|
||||
screensaver_type->add(*it, *it, Settings::getInstance()->
|
||||
getString("ScreensaverType") == *it);
|
||||
screensaver_type->add(*it, *it,
|
||||
Settings::getInstance()->getString("ScreensaverType") == *it);
|
||||
addWithLabel("SCREENSAVER TYPE", screensaver_type);
|
||||
addSaveFunc([screensaver_type, this] {
|
||||
if (screensaver_type->getSelected() !=
|
||||
Settings::getInstance()->getString("ScreensaverType")) {
|
||||
Settings::getInstance()->getString("ScreensaverType")) {
|
||||
if (screensaver_type->getSelected() == "video") {
|
||||
// If before it wasn't risky but now there's a risk of problems, show warning.
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"THE 'VIDEO' SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS\n\n"
|
||||
"IF YOU DO NOT HAVE ANY VIDEOS, THE\n"
|
||||
"SCREENSAVER WILL DEFAULT TO 'DIM'",
|
||||
"OK", [] { return; }, "", nullptr, "", nullptr));
|
||||
}
|
||||
Settings::getInstance()->setString("ScreensaverType",
|
||||
screensaver_type->getSelected());
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, getHelpStyle(),
|
||||
"THE 'VIDEO' SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS\n\n"
|
||||
"IF YOU DO NOT HAVE ANY VIDEOS, THE\n"
|
||||
"SCREENSAVER WILL DEFAULT TO 'DIM'",
|
||||
"OK", [] { return; }, "", nullptr, "", nullptr));
|
||||
}
|
||||
Settings::getInstance()->setString("ScreensaverType", screensaver_type->getSelected());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -67,9 +68,9 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string&
|
|||
addWithLabel("ENABLE SCREENSAVER CONTROLS", screensaver_controls);
|
||||
addSaveFunc([screensaver_controls, this] {
|
||||
if (screensaver_controls->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverControls")) {
|
||||
Settings::getInstance()->getBool("ScreensaverControls")) {
|
||||
Settings::getInstance()->setBool("ScreensaverControls",
|
||||
screensaver_controls->getState());
|
||||
screensaver_controls->getState());
|
||||
setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -77,19 +78,21 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string&
|
|||
// Show filtered menu.
|
||||
ComponentListRow row;
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "SLIDESHOW SCREENSAVER SETTINGS",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(
|
||||
&GuiScreensaverOptions::openSlideshowScreensaverOptions, this));
|
||||
row.makeAcceptInputHandler(
|
||||
std::bind(&GuiScreensaverOptions::openSlideshowScreensaverOptions, this));
|
||||
addRow(row);
|
||||
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "VIDEO SCREENSAVER SETTINGS",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||
true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(
|
||||
&GuiScreensaverOptions::openVideoScreensaverOptions, this));
|
||||
row.makeAcceptInputHandler(
|
||||
std::bind(&GuiScreensaverOptions::openVideoScreensaverOptions, this));
|
||||
addRow(row);
|
||||
}
|
||||
|
||||
|
@ -99,104 +102,105 @@ void GuiScreensaverOptions::openSlideshowScreensaverOptions()
|
|||
|
||||
// Timer for swapping images (in seconds).
|
||||
auto screensaver_swap_image_timeout =
|
||||
std::make_shared<SliderComponent>(mWindow, 2.f, 120.f, 2.f, "s");
|
||||
screensaver_swap_image_timeout->setValue(static_cast<float>(Settings::getInstance()->
|
||||
getInt("ScreensaverSwapImageTimeout") / (1000)));
|
||||
std::make_shared<SliderComponent>(mWindow, 2.0f, 120.0f, 2.0f, "s");
|
||||
screensaver_swap_image_timeout->setValue(static_cast<float>(
|
||||
Settings::getInstance()->getInt("ScreensaverSwapImageTimeout") / (1000)));
|
||||
s->addWithLabel("SWAP IMAGES AFTER (SECONDS)", screensaver_swap_image_timeout);
|
||||
s->addSaveFunc([screensaver_swap_image_timeout, s] {
|
||||
if (screensaver_swap_image_timeout->getValue() !=
|
||||
static_cast<float>(Settings::getInstance()->
|
||||
getInt("ScreensaverSwapImageTimeout") / (1000))) {
|
||||
Settings::getInstance()->setInt("ScreensaverSwapImageTimeout",
|
||||
static_cast<int>(std::round(screensaver_swap_image_timeout->getValue()) *
|
||||
(1000)));
|
||||
static_cast<float>(Settings::getInstance()->getInt("ScreensaverSwapImageTimeout") /
|
||||
(1000))) {
|
||||
Settings::getInstance()->setInt(
|
||||
"ScreensaverSwapImageTimeout",
|
||||
static_cast<int>(std::round(screensaver_swap_image_timeout->getValue()) * (1000)));
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Stretch images to screen resolution.
|
||||
auto screensaver_stretch_images = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_stretch_images->
|
||||
setState(Settings::getInstance()->getBool("ScreensaverStretchImages"));
|
||||
screensaver_stretch_images->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverStretchImages"));
|
||||
s->addWithLabel("STRETCH IMAGES TO SCREEN RESOLUTION", screensaver_stretch_images);
|
||||
s->addSaveFunc([screensaver_stretch_images, s] {
|
||||
if (screensaver_stretch_images->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverStretchImages")) {
|
||||
Settings::getInstance()->getBool("ScreensaverStretchImages")) {
|
||||
Settings::getInstance()->setBool("ScreensaverStretchImages",
|
||||
screensaver_stretch_images->getState());
|
||||
screensaver_stretch_images->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Show game info overlay for slideshow screensaver.
|
||||
auto screensaver_slideshow_game_info = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_slideshow_game_info->
|
||||
setState(Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo"));
|
||||
screensaver_slideshow_game_info->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo"));
|
||||
s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_slideshow_game_info);
|
||||
s->addSaveFunc([screensaver_slideshow_game_info, s] {
|
||||
if (screensaver_slideshow_game_info->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")) {
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")) {
|
||||
Settings::getInstance()->setBool("ScreensaverSlideshowGameInfo",
|
||||
screensaver_slideshow_game_info->getState());
|
||||
screensaver_slideshow_game_info->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
// Render scanlines using a shader.
|
||||
auto screensaver_slideshow_scanlines = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_slideshow_scanlines->
|
||||
setState(Settings::getInstance()->getBool("ScreensaverSlideshowScanlines"));
|
||||
screensaver_slideshow_scanlines->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowScanlines"));
|
||||
s->addWithLabel("RENDER SCANLINES", screensaver_slideshow_scanlines);
|
||||
s->addSaveFunc([screensaver_slideshow_scanlines, s] {
|
||||
if (screensaver_slideshow_scanlines->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) {
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) {
|
||||
Settings::getInstance()->setBool("ScreensaverSlideshowScanlines",
|
||||
screensaver_slideshow_scanlines->getState());
|
||||
screensaver_slideshow_scanlines->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Whether to use custom images.
|
||||
auto screensaver_slideshow_custom_images = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_slideshow_custom_images->setState(Settings::getInstance()->
|
||||
getBool("ScreensaverSlideshowCustomImages"));
|
||||
screensaver_slideshow_custom_images->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages"));
|
||||
s->addWithLabel("USE CUSTOM IMAGES", screensaver_slideshow_custom_images);
|
||||
s->addSaveFunc([screensaver_slideshow_custom_images, s] {
|
||||
if (screensaver_slideshow_custom_images->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) {
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) {
|
||||
Settings::getInstance()->setBool("ScreensaverSlideshowCustomImages",
|
||||
screensaver_slideshow_custom_images->getState());
|
||||
screensaver_slideshow_custom_images->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Whether to recurse the custom image directory.
|
||||
auto screensaver_slideshow_recurse = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_slideshow_recurse->setState(Settings::getInstance()->
|
||||
getBool("ScreensaverSlideshowRecurse"));
|
||||
screensaver_slideshow_recurse->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowRecurse"));
|
||||
s->addWithLabel("CUSTOM IMAGE DIRECTORY RECURSIVE SEARCH", screensaver_slideshow_recurse);
|
||||
s->addSaveFunc([screensaver_slideshow_recurse, s] {
|
||||
if (screensaver_slideshow_recurse->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")) {
|
||||
Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")) {
|
||||
Settings::getInstance()->setBool("ScreensaverSlideshowRecurse",
|
||||
screensaver_slideshow_recurse->getState());
|
||||
screensaver_slideshow_recurse->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Custom image directory.
|
||||
auto screensaver_slideshow_image_dir = std::make_shared<TextComponent>(mWindow, "",
|
||||
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_RIGHT);
|
||||
s->addEditableTextComponent("CUSTOM IMAGE DIRECTORY", screensaver_slideshow_image_dir,
|
||||
Settings::getInstance()->getString("ScreensaverSlideshowImageDir"),
|
||||
Settings::getInstance()->getDefaultString("ScreensaverSlideshowImageDir"));
|
||||
auto screensaver_slideshow_image_dir = std::make_shared<TextComponent>(
|
||||
mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_RIGHT);
|
||||
s->addEditableTextComponent(
|
||||
"CUSTOM IMAGE DIRECTORY", screensaver_slideshow_image_dir,
|
||||
Settings::getInstance()->getString("ScreensaverSlideshowImageDir"),
|
||||
Settings::getInstance()->getDefaultString("ScreensaverSlideshowImageDir"));
|
||||
s->addSaveFunc([screensaver_slideshow_image_dir, s] {
|
||||
if (screensaver_slideshow_image_dir->getValue() !=
|
||||
Settings::getInstance()->getString("ScreensaverSlideshowImageDir")) {
|
||||
Settings::getInstance()->getString("ScreensaverSlideshowImageDir")) {
|
||||
Settings::getInstance()->setString("ScreensaverSlideshowImageDir",
|
||||
screensaver_slideshow_image_dir->getValue());
|
||||
screensaver_slideshow_image_dir->getValue());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -210,75 +214,75 @@ void GuiScreensaverOptions::openVideoScreensaverOptions()
|
|||
|
||||
// Timer for swapping videos (in seconds).
|
||||
auto screensaver_swap_video_timeout =
|
||||
std::make_shared<SliderComponent>(mWindow, 0.f, 120.f, 2.f, "s");
|
||||
screensaver_swap_video_timeout->setValue(static_cast<float>(Settings::getInstance()->
|
||||
getInt("ScreensaverSwapVideoTimeout") / (1000)));
|
||||
std::make_shared<SliderComponent>(mWindow, 0.0f, 120.0f, 2.0f, "s");
|
||||
screensaver_swap_video_timeout->setValue(static_cast<float>(
|
||||
Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") / (1000)));
|
||||
s->addWithLabel("SWAP VIDEOS AFTER (SECONDS)", screensaver_swap_video_timeout);
|
||||
s->addSaveFunc([screensaver_swap_video_timeout, s] {
|
||||
if (screensaver_swap_video_timeout->getValue() !=
|
||||
static_cast<float>(Settings::getInstance()->
|
||||
getInt("ScreensaverSwapVideoTimeout") / (1000))) {
|
||||
Settings::getInstance()->setInt("ScreensaverSwapVideoTimeout",
|
||||
static_cast<int>(std::round(screensaver_swap_video_timeout->getValue()) *
|
||||
(1000)));
|
||||
static_cast<float>(Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") /
|
||||
(1000))) {
|
||||
Settings::getInstance()->setInt(
|
||||
"ScreensaverSwapVideoTimeout",
|
||||
static_cast<int>(std::round(screensaver_swap_video_timeout->getValue()) * (1000)));
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Stretch videos to screen resolution.
|
||||
auto screensaver_stretch_videos = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_stretch_videos->
|
||||
setState(Settings::getInstance()->getBool("ScreensaverStretchVideos"));
|
||||
screensaver_stretch_videos->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverStretchVideos"));
|
||||
s->addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", screensaver_stretch_videos);
|
||||
s->addSaveFunc([screensaver_stretch_videos, s] {
|
||||
if (screensaver_stretch_videos->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverStretchVideos")) {
|
||||
Settings::getInstance()->getBool("ScreensaverStretchVideos")) {
|
||||
Settings::getInstance()->setBool("ScreensaverStretchVideos",
|
||||
screensaver_stretch_videos->getState());
|
||||
screensaver_stretch_videos->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
// Show game info overlay for video screensaver.
|
||||
auto screensaver_video_game_info = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_video_game_info->
|
||||
setState(Settings::getInstance()->getBool("ScreensaverVideoGameInfo"));
|
||||
screensaver_video_game_info->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverVideoGameInfo"));
|
||||
s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_video_game_info);
|
||||
s->addSaveFunc([screensaver_video_game_info, s] {
|
||||
if (screensaver_video_game_info->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) {
|
||||
Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) {
|
||||
Settings::getInstance()->setBool("ScreensaverVideoGameInfo",
|
||||
screensaver_video_game_info->getState());
|
||||
screensaver_video_game_info->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
||||
#if defined(_RPI_)
|
||||
#if defined(_RPI_)
|
||||
// Use OMX player for screensaver.
|
||||
auto screensaver_omx_player = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_omx_player->setState(Settings::getInstance()->getBool("ScreensaverOmxPlayer"));
|
||||
s->addWithLabel("USE OMX PLAYER FOR SCREENSAVER", screensaver_omx_player);
|
||||
s->addSaveFunc([screensaver_omx_player, s] {
|
||||
if (screensaver_omx_player->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverOmxPlayer")) {
|
||||
Settings::getInstance()->
|
||||
setBool("ScreensaverOmxPlayer", screensaver_omx_player->getState());
|
||||
Settings::getInstance()->getBool("ScreensaverOmxPlayer")) {
|
||||
Settings::getInstance()->setBool("ScreensaverOmxPlayer",
|
||||
screensaver_omx_player->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_OPENGL_21)
|
||||
#if defined(USE_OPENGL_21)
|
||||
// Render scanlines using a shader.
|
||||
auto screensaver_video_scanlines = std::make_shared<SwitchComponent>(mWindow);
|
||||
screensaver_video_scanlines->
|
||||
setState(Settings::getInstance()->getBool("ScreensaverVideoScanlines"));
|
||||
screensaver_video_scanlines->setState(
|
||||
Settings::getInstance()->getBool("ScreensaverVideoScanlines"));
|
||||
s->addWithLabel("RENDER SCANLINES", screensaver_video_scanlines);
|
||||
s->addSaveFunc([screensaver_video_scanlines, s] {
|
||||
if (screensaver_video_scanlines->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverVideoScanlines")) {
|
||||
Settings::getInstance()->getBool("ScreensaverVideoScanlines")) {
|
||||
Settings::getInstance()->setBool("ScreensaverVideoScanlines",
|
||||
screensaver_video_scanlines->getState());
|
||||
screensaver_video_scanlines->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
|
@ -289,13 +293,13 @@ void GuiScreensaverOptions::openVideoScreensaverOptions()
|
|||
s->addWithLabel("RENDER BLUR", screensaver_video_blur);
|
||||
s->addSaveFunc([screensaver_video_blur, s] {
|
||||
if (screensaver_video_blur->getState() !=
|
||||
Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
|
||||
Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
|
||||
Settings::getInstance()->setBool("ScreensaverVideoBlur",
|
||||
screensaver_video_blur->getState());
|
||||
screensaver_video_blur->getState());
|
||||
s->setNeedsSaving();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mWindow->pushGui(s);
|
||||
}
|
||||
|
|
|
@ -10,33 +10,31 @@
|
|||
|
||||
#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 "FileFilterIndex.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "Window.h"
|
||||
#include "components/HelpComponent.h"
|
||||
#include "guis/GuiTextEditPopup.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "views/gamelist/IGameListView.h"
|
||||
|
||||
GuiSettings::GuiSettings(
|
||||
Window* window,
|
||||
std::string title)
|
||||
: GuiComponent(window),
|
||||
mMenu(window, title),
|
||||
mNeedsSaving(false),
|
||||
mNeedsReloadHelpPrompts(false),
|
||||
mNeedsCollectionsUpdate(false),
|
||||
mNeedsSorting(false),
|
||||
mNeedsSortingCollections(false),
|
||||
mNeedsResetFilters(false),
|
||||
mNeedsReloading(false),
|
||||
mNeedsGoToStart(false),
|
||||
mNeedsGoToSystem(false),
|
||||
mNeedsGoToGroupedCollections(false),
|
||||
mInvalidateCachedBackground(false),
|
||||
mGoToSystem(nullptr)
|
||||
GuiSettings::GuiSettings(Window* window, std::string title)
|
||||
: GuiComponent(window)
|
||||
, mMenu(window, title)
|
||||
, mNeedsSaving(false)
|
||||
, mNeedsReloadHelpPrompts(false)
|
||||
, mNeedsCollectionsUpdate(false)
|
||||
, mNeedsSorting(false)
|
||||
, mNeedsSortingCollections(false)
|
||||
, mNeedsResetFilters(false)
|
||||
, mNeedsReloading(false)
|
||||
, mNeedsGoToStart(false)
|
||||
, mNeedsGoToSystem(false)
|
||||
, mNeedsGoToGroupedCollections(false)
|
||||
, mInvalidateCachedBackground(false)
|
||||
, mGoToSystem(nullptr)
|
||||
{
|
||||
addChild(&mMenu);
|
||||
mMenu.addButton("BACK", "back", [this] { delete this; });
|
||||
|
@ -44,11 +42,12 @@ GuiSettings::GuiSettings(
|
|||
setSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f,
|
||||
Renderer::getScreenHeight() * 0.13f);
|
||||
Renderer::getScreenHeight() * 0.13f);
|
||||
}
|
||||
|
||||
GuiSettings::~GuiSettings()
|
||||
{
|
||||
// Save on exit.
|
||||
save();
|
||||
}
|
||||
|
||||
|
@ -72,11 +71,11 @@ void GuiSettings::save()
|
|||
}
|
||||
|
||||
if (mNeedsSorting) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it !=
|
||||
SystemData::sSystemVector.cend(); it++) {
|
||||
if (!(!mNeedsSortingCollections && (*it)->isCollection())) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend();
|
||||
it++) {
|
||||
if (!(!mNeedsSortingCollections && (*it)->isCollection()))
|
||||
(*it)->sortSystem(true);
|
||||
}
|
||||
|
||||
// Jump to the first row of the gamelist.
|
||||
IGameListView* gameList = ViewController::get()->getGameListView((*it)).get();
|
||||
gameList->setCursor(gameList->getFirstEntry());
|
||||
|
@ -84,11 +83,10 @@ void GuiSettings::save()
|
|||
}
|
||||
|
||||
if (mNeedsResetFilters) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
if ((*it)->getThemeFolder() == "custom-collections") {
|
||||
for (FileData* customSystem :
|
||||
(*it)->getRootFolder()->getChildrenListToDisplay())
|
||||
for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay())
|
||||
customSystem->getSystem()->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
|
||||
// the safe side.
|
||||
if (state.getSystem()->isCollection() &&
|
||||
state.getSystem()->getThemeFolder() != "custom-collections") {
|
||||
state.getSystem()->getThemeFolder() != "custom-collections") {
|
||||
ViewController::get()->goToStart();
|
||||
ViewController::get()->goToSystem(SystemData::sSystemVector.front(), false);
|
||||
// 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
|
||||
// system view).
|
||||
if (std::find(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(),
|
||||
state.getSystem()) == SystemData::sSystemVector.end()) {
|
||||
state.getSystem()) == SystemData::sSystemVector.end()) {
|
||||
ViewController::get()->goToStart();
|
||||
return;
|
||||
}
|
||||
|
@ -152,18 +150,17 @@ void GuiSettings::save()
|
|||
}
|
||||
}
|
||||
|
||||
void GuiSettings::addEditableTextComponent(
|
||||
const std::string label,
|
||||
std::shared_ptr<GuiComponent> ed,
|
||||
std::string value,
|
||||
std::string defaultValue,
|
||||
bool isPassword)
|
||||
void GuiSettings::addEditableTextComponent(const std::string label,
|
||||
std::shared_ptr<GuiComponent> ed,
|
||||
std::string value,
|
||||
std::string defaultValue,
|
||||
bool isPassword)
|
||||
{
|
||||
ComponentListRow row;
|
||||
row.elements.clear();
|
||||
|
||||
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(ed, true);
|
||||
|
@ -200,11 +197,11 @@ void GuiSettings::addEditableTextComponent(
|
|||
row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] {
|
||||
// Never display the value if it's a password, instead set it to blank.
|
||||
if (isPassword)
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label,
|
||||
"", updateVal, false));
|
||||
mWindow->pushGui(
|
||||
new GuiTextEditPopup(mWindow, getHelpStyle(), label, "", updateVal, false));
|
||||
else
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label,
|
||||
ed->getValue(), updateVal, false));
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, ed->getValue(),
|
||||
updateVal, false));
|
||||
});
|
||||
assert(ed);
|
||||
addRow(row);
|
||||
|
@ -219,15 +216,6 @@ bool GuiSettings::input(InputConfig* config, Input input)
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
#ifndef ES_APP_GUIS_GUI_SETTINGS_H
|
||||
#define ES_APP_GUIS_GUI_SETTINGS_H
|
||||
|
||||
#include "components/MenuComponent.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.
|
||||
class GuiSettings : public GuiComponent
|
||||
|
@ -22,29 +22,33 @@ public:
|
|||
virtual ~GuiSettings();
|
||||
|
||||
void save();
|
||||
inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); };
|
||||
inline void addWithLabel(const std::string& label,
|
||||
const std::shared_ptr<GuiComponent>& comp) { mMenu.addWithLabel(label, comp); };
|
||||
void addEditableTextComponent(
|
||||
const std::string label,
|
||||
std::shared_ptr<GuiComponent> ed,
|
||||
std::string value,
|
||||
std::string defaultValue = "",
|
||||
bool isPassword = false);
|
||||
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); };
|
||||
void addRow(const ComponentListRow& row) { mMenu.addRow(row); }
|
||||
void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp)
|
||||
{
|
||||
mMenu.addWithLabel(label, comp);
|
||||
}
|
||||
void addEditableTextComponent(const std::string label,
|
||||
std::shared_ptr<GuiComponent> ed,
|
||||
std::string value,
|
||||
std::string defaultValue = "",
|
||||
bool isPassword = false);
|
||||
void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }
|
||||
|
||||
void setNeedsSaving(bool state = true) { mNeedsSaving = state; };
|
||||
void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; };
|
||||
void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; };
|
||||
void setNeedsSorting() { mNeedsSorting = true; };
|
||||
void setNeedsSortingCollections() { mNeedsSortingCollections = true; };
|
||||
void setNeedsSaving(bool state = true) { mNeedsSaving = state; }
|
||||
void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; }
|
||||
void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; }
|
||||
void setNeedsSorting() { mNeedsSorting = true; }
|
||||
void setNeedsSortingCollections() { mNeedsSortingCollections = true; }
|
||||
void setNeedsResetFilters() { mNeedsResetFilters = true; }
|
||||
void setNeedsReloading() { mNeedsReloading = true; };
|
||||
void setNeedsGoToStart() { mNeedsGoToStart = true; };
|
||||
void setNeedsReloading() { mNeedsReloading = true; }
|
||||
void setNeedsGoToStart() { mNeedsGoToStart = true; }
|
||||
void setNeedsGoToSystem(SystemData* goToSystem)
|
||||
{ mNeedsGoToSystem = true; mGoToSystem = goToSystem; };
|
||||
void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; };
|
||||
void setInvalidateCachedBackground() { mInvalidateCachedBackground = true; };
|
||||
{
|
||||
mNeedsGoToSystem = true;
|
||||
mGoToSystem = goToSystem;
|
||||
};
|
||||
void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; }
|
||||
void setInvalidateCachedBackground() { mInvalidateCachedBackground = true; }
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
|
@ -53,6 +57,8 @@ public:
|
|||
private:
|
||||
MenuComponent mMenu;
|
||||
std::vector<std::function<void()>> mSaveFuncs;
|
||||
SystemData* mGoToSystem;
|
||||
|
||||
bool mNeedsSaving;
|
||||
bool mNeedsReloadHelpPrompts;
|
||||
bool mNeedsCollectionsUpdate;
|
||||
|
@ -64,8 +70,6 @@ private:
|
|||
bool mNeedsGoToSystem;
|
||||
bool mNeedsGoToGroupedCollections;
|
||||
bool mInvalidateCachedBackground;
|
||||
|
||||
SystemData* mGoToSystem;
|
||||
};
|
||||
|
||||
#endif // ES_APP_GUIS_GUI_SETTINGS_H
|
||||
|
|
|
@ -18,13 +18,6 @@
|
|||
// 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 "CollectionSystemsManager.h"
|
||||
#include "EmulationStation.h"
|
||||
|
@ -37,6 +30,13 @@
|
|||
#include "Sound.h"
|
||||
#include "SystemData.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_main.h>
|
||||
|
@ -56,14 +56,14 @@ bool forceInputConfig = false;
|
|||
bool settingsNeedSaving = false;
|
||||
|
||||
enum loadSystemsReturnCode {
|
||||
LOADING_OK,
|
||||
LOADING_OK, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
INVALID_FILE,
|
||||
NO_ROMS
|
||||
};
|
||||
|
||||
#if defined(_WIN64)
|
||||
enum win64ConsoleType {
|
||||
NO_CONSOLE,
|
||||
NO_CONSOLE, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
PARENT_CONSOLE,
|
||||
ALLOCATED_CONSOLE
|
||||
};
|
||||
|
@ -85,7 +85,7 @@ win64ConsoleType outputToConsole(bool allocConsole)
|
|||
|
||||
// Try to attach to a parent console 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 (outputHandle != INVALID_HANDLE_VALUE && outputHandle != nullptr) {
|
||||
|
@ -131,11 +131,11 @@ bool parseArgs(int argc, char* argv[])
|
|||
{
|
||||
Utils::FileSystem::setExePath(argv[0]);
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
// Print any command line output to the console.
|
||||
if (argc > 1)
|
||||
win64ConsoleType consoleType = outputToConsole(false);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
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::ifstream portableFile;
|
||||
std::string homePath;
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
portableFile.open(Utils::String::stringToWideString(portableFilePath).c_str());
|
||||
#else
|
||||
#else
|
||||
portableFile.open(portableFilePath.c_str());
|
||||
#endif
|
||||
#endif
|
||||
if (!portableFile.fail()) {
|
||||
std::string relativePath;
|
||||
getline(portableFile, relativePath);
|
||||
|
@ -159,9 +159,9 @@ bool parseArgs(int argc, char* argv[])
|
|||
else
|
||||
homePath = Utils::FileSystem::getExePath() + "/" + relativePath;
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
homePath = Utils::String::replace(homePath, "/", "\\");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!Utils::FileSystem::exists(homePath)) {
|
||||
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";
|
||||
return false;
|
||||
}
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
if (!Utils::FileSystem::exists(argv[i + 1]) &&
|
||||
(!Utils::FileSystem::driveExists(argv[i + 1]))) {
|
||||
#else
|
||||
(!Utils::FileSystem::driveExists(argv[i + 1]))) {
|
||||
#else
|
||||
if (!Utils::FileSystem::exists(argv[i + 1])) {
|
||||
#endif
|
||||
#endif
|
||||
std::cerr << "Error: Home path \'" << argv[i + 1] << "\' does not exist\n";
|
||||
return false;
|
||||
}
|
||||
if (Utils::FileSystem::isRegularFile(argv[i + 1])) {
|
||||
std::cerr << "Error: Home path \'" << argv[i + 1] <<
|
||||
"\' is a file and not a directory\n";
|
||||
std::cerr << "Error: Home path \'" << argv[i + 1]
|
||||
<< "\' is a file and not a directory\n";
|
||||
return false;
|
||||
}
|
||||
Utils::FileSystem::setHomePath(argv[i + 1]);
|
||||
|
@ -228,16 +228,16 @@ bool parseArgs(int argc, char* argv[])
|
|||
std::string widthArg = argv[i + 1];
|
||||
std::string heightArg = argv[i + 2];
|
||||
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";
|
||||
return false;
|
||||
}
|
||||
int width = atoi(argv[i + 1]);
|
||||
int height = atoi(argv[i + 2]);
|
||||
if (width < 640 || height < 480 || width > 7680 || height > 4320 ||
|
||||
height < width / 4 || width < height / 2) {
|
||||
std::cerr << "Error: Unsupported resolution "
|
||||
<< width << "x" << height << " supplied.\n";
|
||||
height < width / 4 || width < height / 2) {
|
||||
std::cerr << "Error: Unsupported resolution " << width << "x" << height
|
||||
<< " supplied.\n";
|
||||
return false;
|
||||
}
|
||||
Settings::getInstance()->setInt("WindowWidth", width);
|
||||
|
@ -277,7 +277,8 @@ bool parseArgs(int argc, char* argv[])
|
|||
}
|
||||
// On Unix, enable settings for the fullscreen mode.
|
||||
// On macOS and Windows only windowed mode is supported.
|
||||
#if defined(__unix__)
|
||||
|
||||
#if defined(__unix__)
|
||||
else if (strcmp(argv[i], "--windowed") == 0) {
|
||||
Settings::getInstance()->setBool("Windowed", true);
|
||||
}
|
||||
|
@ -289,7 +290,7 @@ bool parseArgs(int argc, char* argv[])
|
|||
Settings::getInstance()->setString("FullscreenMode", "borderless");
|
||||
settingsNeedSaving = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
else if (strcmp(argv[i], "--vsync") == 0) {
|
||||
if (i >= argc - 1) {
|
||||
std::cerr << "Error: No VSync value supplied.\n";
|
||||
|
@ -297,7 +298,7 @@ bool parseArgs(int argc, char* argv[])
|
|||
}
|
||||
std::string vSyncValue = argv[i + 1];
|
||||
if (vSyncValue != "on" && vSyncValue != "off" && vSyncValue != "1" &&
|
||||
vSyncValue != "0") {
|
||||
vSyncValue != "0") {
|
||||
std::cerr << "Error: Invalid VSync value supplied.\n";
|
||||
return false;
|
||||
}
|
||||
|
@ -350,12 +351,12 @@ bool parseArgs(int argc, char* argv[])
|
|||
Log::setReportingLevel(LogDebug);
|
||||
}
|
||||
else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
|
||||
std::cout <<
|
||||
"EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << "\n";
|
||||
std::cout << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << "\n";
|
||||
return false;
|
||||
}
|
||||
else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
||||
std::cout <<
|
||||
// clang-format off
|
||||
"Usage: emulationstation [options]\n"
|
||||
"EmulationStation Desktop Edition, Emulator Front-end\n\n"
|
||||
"Options:\n"
|
||||
|
@ -381,6 +382,7 @@ bool parseArgs(int argc, char* argv[])
|
|||
" --debug Print debug information\n"
|
||||
" --version, -v Display version information\n"
|
||||
" --help, -h Summon a sentient, angry tuba\n";
|
||||
// clang-format on
|
||||
return false; // Exit after printing help.
|
||||
}
|
||||
else {
|
||||
|
@ -400,13 +402,13 @@ bool checkApplicationHomeDirectory()
|
|||
std::string home = Utils::FileSystem::getHomePath();
|
||||
std::string applicationHome = home + "/.emulationstation";
|
||||
if (!Utils::FileSystem::exists(applicationHome)) {
|
||||
#if defined(_WIN64)
|
||||
std::cout << "First startup, creating application home directory \"" <<
|
||||
Utils::String::replace(applicationHome, "/", "\\") << "\"\n";
|
||||
#else
|
||||
std::cout << "First startup, creating application home directory \"" <<
|
||||
applicationHome << "\"\n";
|
||||
#endif
|
||||
#if defined(_WIN64)
|
||||
std::cout << "First startup, creating application home directory \""
|
||||
<< Utils::String::replace(applicationHome, "/", "\\") << "\"\n";
|
||||
#else
|
||||
std::cout << "First startup, creating application home directory \"" << applicationHome
|
||||
<< "\"\n";
|
||||
#endif
|
||||
Utils::FileSystem::createDirectory(applicationHome);
|
||||
if (!Utils::FileSystem::exists(applicationHome)) {
|
||||
std::cerr << "Fatal error: Couldn't create directory, permission problems?\n";
|
||||
|
@ -424,16 +426,16 @@ loadSystemsReturnCode loadSystemConfigFile()
|
|||
|
||||
if (SystemData::sSystemVector.size() == 0) {
|
||||
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 LOADING_OK;
|
||||
}
|
||||
|
||||
// Called on exit, assuming we get far enough to have the log initialized.
|
||||
void onExit()
|
||||
{
|
||||
// Called on exit, assuming we get far enough to have the log initialized.
|
||||
Log::close();
|
||||
}
|
||||
|
||||
|
@ -443,12 +445,12 @@ int main(int argc, char* argv[])
|
|||
|
||||
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
|
||||
// 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.
|
||||
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
|
||||
// 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
|
||||
|
@ -465,22 +467,22 @@ int main(int argc, char* argv[])
|
|||
// the functionality.
|
||||
std::string chmodCommand = "chmod 500 \"" + saveStateDir + "\"";
|
||||
system(chmodCommand.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!parseArgs(argc, argv)) {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
FreeConsole();
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
// Send debug output to the console..
|
||||
if (Settings::getInstance()->getBool("Debug"))
|
||||
outputToConsole(true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
// Hide taskbar if the setting for this is enabled.
|
||||
bool taskbarStateChanged = false;
|
||||
unsigned int taskbarState;
|
||||
|
@ -490,12 +492,12 @@ int main(int argc, char* argv[])
|
|||
taskbarState = getTaskbarState();
|
||||
hideTaskbar();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(FREEIMAGE_LIB)
|
||||
// Call this ONLY when linking with FreeImage as a static library.
|
||||
#if defined(FREEIMAGE_LIB)
|
||||
FreeImage_Initialise();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If ~/.emulationstation doesn't exist and cannot be created, bail.
|
||||
if (!checkApplicationHomeDirectory())
|
||||
|
@ -504,8 +506,8 @@ int main(int argc, char* argv[])
|
|||
// Start the logger.
|
||||
Log::init();
|
||||
Log::open();
|
||||
LOG(LogInfo) << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING <<
|
||||
", built " << PROGRAM_BUILT_STRING;
|
||||
LOG(LogInfo) << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << ", built "
|
||||
<< PROGRAM_BUILT_STRING;
|
||||
|
||||
// Always close the log on exit.
|
||||
atexit(&onExit);
|
||||
|
@ -513,7 +515,7 @@ int main(int argc, char* argv[])
|
|||
// Check if the configuration file exists, and if not, create it.
|
||||
// This should only happen on first application startup.
|
||||
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...";
|
||||
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
|
||||
// user has upgraded to a newer release.
|
||||
std::string applicationVersion;
|
||||
if ((applicationVersion = Settings::getInstance()->
|
||||
getString("ApplicationVersion")) != PROGRAM_VERSION_STRING) {
|
||||
if ((applicationVersion = Settings::getInstance()->getString("ApplicationVersion")) !=
|
||||
PROGRAM_VERSION_STRING) {
|
||||
if (applicationVersion != "") {
|
||||
LOG(LogInfo) << "Application version changed from previous startup, from \"" <<
|
||||
applicationVersion << "\" to \"" << PROGRAM_VERSION_STRING << "\"";
|
||||
LOG(LogInfo) << "Application version changed from previous startup, from \""
|
||||
<< applicationVersion << "\" to \"" << PROGRAM_VERSION_STRING << "\"";
|
||||
}
|
||||
else {
|
||||
LOG(LogInfo) << "Application version setting is blank, changing it to \"" <<
|
||||
PROGRAM_VERSION_STRING << "\"";
|
||||
LOG(LogInfo) << "Application version setting is blank, changing it to \""
|
||||
<< PROGRAM_VERSION_STRING << "\"";
|
||||
}
|
||||
Settings::getInstance()->setString("ApplicationVersion", PROGRAM_VERSION_STRING);
|
||||
Settings::getInstance()->saveFile();
|
||||
|
@ -582,11 +584,11 @@ int main(int argc, char* argv[])
|
|||
if (event.type == SDL_QUIT)
|
||||
return 1;
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
// 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.
|
||||
#if !defined(__APPLE__)
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (splashScreen) {
|
||||
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.
|
||||
if (!loadSystemsStatus) {
|
||||
if (forceInputConfig) {
|
||||
window.pushGui(new GuiDetectDevice(&window, false, true, [] {
|
||||
ViewController::get()->goToStart(); }));
|
||||
window.pushGui(new GuiDetectDevice(&window, false, true,
|
||||
[] { ViewController::get()->goToStart(); }));
|
||||
}
|
||||
else {
|
||||
ViewController::get()->goToStart();
|
||||
|
@ -639,16 +641,19 @@ int main(int argc, char* argv[])
|
|||
int lastTime = SDL_GetTicks();
|
||||
const auto applicationEndTime = std::chrono::system_clock::now();
|
||||
|
||||
LOG(LogInfo) << "Application startup time: " <<
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(applicationEndTime - applicationStartTime).count() << " ms";
|
||||
LOG(LogInfo) << "Application startup time: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(applicationEndTime -
|
||||
applicationStartTime)
|
||||
.count()
|
||||
<< " ms";
|
||||
|
||||
bool running = true;
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
// Now that we've finished loading, disable the relative mouse mode or otherwise mouse
|
||||
// input wouldn't work in any games that are launched.
|
||||
#if !defined(__APPLE__)
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
while (running) {
|
||||
if (SDL_PollEvent(&event)) {
|
||||
|
@ -657,8 +662,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
if (event.type == SDL_QUIT)
|
||||
running = false;
|
||||
}
|
||||
while (SDL_PollEvent(&event));
|
||||
|
||||
} while (SDL_PollEvent(&event));
|
||||
}
|
||||
|
||||
if (window.isSleeping()) {
|
||||
|
@ -694,24 +699,24 @@ int main(int argc, char* argv[])
|
|||
NavigationSounds::getInstance()->deinit();
|
||||
Settings::deinit();
|
||||
|
||||
#if defined(FREEIMAGE_LIB)
|
||||
// Call this ONLY when linking with FreeImage as a static library.
|
||||
#if defined(FREEIMAGE_LIB)
|
||||
FreeImage_DeInitialise();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
// If the taskbar state was changed (taskbar was hidden), then revert it.
|
||||
if (taskbarStateChanged)
|
||||
revertTaskbarState(taskbarState);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
processQuitMode();
|
||||
|
||||
LOG(LogInfo) << "EmulationStation cleanly shutting down";
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
FreeConsole();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,20 +10,20 @@
|
|||
#include "scrapers/GamesDBJSONScraper.h"
|
||||
#include "scrapers/GamesDBJSONScraperResources.h"
|
||||
|
||||
#include "utils/StringUtil.h"
|
||||
#include "utils/TimeUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "Log.h"
|
||||
#include "MameNames.h"
|
||||
#include "PlatformId.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "utils/TimeUtil.h"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/error/en.h>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <pugixml.hpp>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/error/en.h>
|
||||
|
||||
using namespace PlatformIds;
|
||||
using namespace rapidjson;
|
||||
|
@ -103,10 +103,10 @@ const std::map<PlatformId, std::string> gamesdb_new_platformid_map {
|
|||
{ SONY_PLAYSTATION_PORTABLE, "13" },
|
||||
{ SUPER_NINTENDO, "6" },
|
||||
{ SHARP_X1, "4977" },
|
||||
{ SHARP_X68000, "4931"},
|
||||
{ SHARP_X68000, "4931" },
|
||||
{ NEC_SUPERGRAFX, "34" },
|
||||
{ NEC_PC_8800, "4933"},
|
||||
{ NEC_PC_9800, "4934"},
|
||||
{ NEC_PC_8800, "4933" },
|
||||
{ NEC_PC_9800, "4934" },
|
||||
{ NEC_PC_ENGINE, "34" },
|
||||
{ NEC_PC_ENGINE_CD, "4955" },
|
||||
{ BANDAI_WONDERSWAN, "4925" },
|
||||
|
@ -118,9 +118,10 @@ const std::map<PlatformId, std::string> gamesdb_new_platformid_map {
|
|||
{ TANDY_TRS80, "4941" },
|
||||
};
|
||||
|
||||
void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
void thegamesdb_generate_json_scraper_requests(
|
||||
const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
resources.prepare();
|
||||
std::string path = "https://api.thegamesdb.net/v1";
|
||||
|
@ -168,8 +169,8 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params
|
|||
if (!platforms.empty()) {
|
||||
bool first = true;
|
||||
platformQueryParam += "&filter%5Bplatform%5D=";
|
||||
for (auto platformIt = platforms.cbegin();
|
||||
platformIt != platforms.cend(); platformIt++) {
|
||||
for (auto platformIt = platforms.cbegin(); // Line break.
|
||||
platformIt != platforms.cend(); platformIt++) {
|
||||
auto mapIt = gamesdb_new_platformid_map.find(*platformIt);
|
||||
if (mapIt != gamesdb_new_platformid_map.cend()) {
|
||||
if (!first)
|
||||
|
@ -178,8 +179,9 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params
|
|||
first = false;
|
||||
}
|
||||
else {
|
||||
LOG(LogWarning) << "TheGamesDB scraper: No support for platform \"" <<
|
||||
getPlatformName(*platformIt) << "\", search will be inaccurate";
|
||||
LOG(LogWarning)
|
||||
<< "TheGamesDB scraper: No support for platform \""
|
||||
<< getPlatformName(*platformIt) << "\", search will be inaccurate";
|
||||
}
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
requests.push(std::unique_ptr<ScraperRequest>
|
||||
(new TheGamesDBJSONRequest(requests, results, path)));
|
||||
requests.push(
|
||||
std::unique_ptr<ScraperRequest>(new TheGamesDBJSONRequest(requests, results, path)));
|
||||
}
|
||||
}
|
||||
|
||||
void thegamesdb_generate_json_scraper_requests(
|
||||
const std::string& gameIDs,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
const std::string& gameIDs,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
resources.prepare();
|
||||
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;
|
||||
|
||||
requests.push(std::unique_ptr<ScraperRequest>
|
||||
(new TheGamesDBJSONRequest(requests, results, path)));
|
||||
requests.push(
|
||||
std::unique_ptr<ScraperRequest>(new TheGamesDBJSONRequest(requests, results, path)));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string getStringOrThrow(const Value& v, const std::string& key)
|
||||
{
|
||||
if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) {
|
||||
throw std::runtime_error(
|
||||
std::string getStringOrThrow(const Value& v, const std::string& key)
|
||||
{
|
||||
if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) {
|
||||
throw std::runtime_error(
|
||||
"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)
|
||||
{
|
||||
if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) {
|
||||
throw std::runtime_error(
|
||||
int getIntOrThrow(const Value& v, const std::string& key)
|
||||
{
|
||||
if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) {
|
||||
throw std::runtime_error(
|
||||
"rapidjson internal assertion failure: missing or non int key:" + key);
|
||||
}
|
||||
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");
|
||||
}
|
||||
return v[key.c_str()].GetInt();
|
||||
}
|
||||
|
||||
if (game.HasMember("developers") && game["developers"].IsArray()) {
|
||||
result.mdl.set("developer", getDeveloperString(game["developers"]));
|
||||
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Developer: " <<
|
||||
result.mdl.get("developer");
|
||||
int getIntOrThrow(const Value& v)
|
||||
{
|
||||
if (!v.IsInt()) {
|
||||
throw std::runtime_error("rapidjson internal assertion failure: not an int");
|
||||
}
|
||||
return v.GetInt();
|
||||
}
|
||||
|
||||
if (game.HasMember("publishers") && game["publishers"].IsArray()) {
|
||||
result.mdl.set("publisher", getPublisherString(game["publishers"]));
|
||||
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Publisher: " <<
|
||||
result.mdl.get("publisher");
|
||||
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;
|
||||
}
|
||||
|
||||
if (game.HasMember("genres") && game["genres"].IsArray()) {
|
||||
result.mdl.set("genre", getGenreString(game["genres"]));
|
||||
LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Genre: " <<
|
||||
result.mdl.get("genre");
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
result.mediaURLFetch = NOT_STARTED;
|
||||
results.push_back(result);
|
||||
}
|
||||
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()) {
|
||||
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
|
||||
|
||||
void processMediaURLs(const Value& images, const std::string& base_url,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
void processMediaURLs(const Value& images,
|
||||
const std::string& base_url,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
ScraperSearchResult result;
|
||||
|
||||
|
@ -367,9 +369,8 @@ void processMediaURLs(const Value& images, const std::string& base_url,
|
|||
result.marqueeUrl = "";
|
||||
result.screenshotUrl = "";
|
||||
|
||||
// Quite excessive testing for valid values, but you never know
|
||||
// what the server has returned and we don't want to crash the
|
||||
// program due to malformed data.
|
||||
// Quite excessive testing for valid values, but you never know what the server has
|
||||
// returned and we don't want to crash the program due to malformed data.
|
||||
if (gameMedia.IsArray()) {
|
||||
for (SizeType i = 0; i < gameMedia.Size(); i++) {
|
||||
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.mediaURLFetch = COMPLETED;
|
||||
results.push_back(result);
|
||||
result.mediaURLFetch = COMPLETED;
|
||||
results.push_back(result);
|
||||
}
|
||||
}
|
||||
|
||||
void TheGamesDBJSONRequest::process(const std::unique_ptr<HttpReq>& req,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
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());
|
||||
|
||||
if (doc.HasParseError()) {
|
||||
std::string err =
|
||||
std::string("TheGamesDBJSONRequest - Error parsing JSON \n\t") +
|
||||
GetParseError_En(doc.GetParseError());
|
||||
std::string err = std::string("TheGamesDBJSONRequest - Error parsing JSON \n\t") +
|
||||
GetParseError_En(doc.GetParseError());
|
||||
setError(err);
|
||||
LOG(LogError) << err;
|
||||
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 (doc.HasMember("data") && doc["data"].HasMember("images") &&
|
||||
doc["data"]["images"].IsObject()) {
|
||||
doc["data"]["images"].IsObject()) {
|
||||
|
||||
const Value& images = doc["data"]["images"];
|
||||
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")) {
|
||||
for (auto i = 0; i < results.size(); i++) {
|
||||
results[i].scraperRequestAllowance =
|
||||
doc["remaining_monthly_allowance"].GetInt() +
|
||||
doc["extra_allowance"].GetInt();
|
||||
doc["remaining_monthly_allowance"].GetInt() + doc["extra_allowance"].GetInt();
|
||||
}
|
||||
LOG(LogDebug) << "TheGamesDBJSONRequest::process(): "
|
||||
"Remaining monthly scraping allowance: " <<
|
||||
results.back().scraperRequestAllowance;
|
||||
"Remaining monthly scraping allowance: "
|
||||
<< results.back().scraperRequestAllowance;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// These process steps are for the initial scraping response.
|
||||
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";
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -12,39 +12,42 @@
|
|||
|
||||
#include "scrapers/Scraper.h"
|
||||
|
||||
namespace pugi {
|
||||
namespace pugi
|
||||
{
|
||||
class xml_document;
|
||||
}
|
||||
|
||||
void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
void thegamesdb_generate_json_scraper_requests(
|
||||
const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
|
||||
void thegamesdb_generate_json_scraper_requests(const std::string& gameIDs,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
void thegamesdb_generate_json_scraper_requests(
|
||||
const std::string& gameIDs,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
|
||||
class TheGamesDBJSONRequest : public ScraperHttpRequest
|
||||
{
|
||||
public:
|
||||
public:
|
||||
// Constructor for a GetGameList request.
|
||||
TheGamesDBJSONRequest(
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite,
|
||||
std::vector<ScraperSearchResult>& resultsWrite,
|
||||
const std::string& url)
|
||||
: ScraperHttpRequest(resultsWrite, url),
|
||||
mRequestQueue(&requestsWrite)
|
||||
TheGamesDBJSONRequest(std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite,
|
||||
std::vector<ScraperSearchResult>& resultsWrite,
|
||||
const std::string& url)
|
||||
: ScraperHttpRequest(resultsWrite, url)
|
||||
, mRequestQueue(&requestsWrite)
|
||||
{
|
||||
}
|
||||
// Constructior for a GetGame request
|
||||
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,
|
||||
std::vector<ScraperSearchResult>& results) override;
|
||||
std::vector<ScraperSearchResult>& results) override;
|
||||
bool isGameRequest() { return !mRequestQueue; }
|
||||
|
||||
std::queue<std::unique_ptr<ScraperRequest>>* mRequestQueue;
|
||||
|
|
|
@ -14,52 +14,53 @@
|
|||
|
||||
#include "scrapers/GamesDBJSONScraperResources.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "Log.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/error/en.h>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/error/en.h>
|
||||
#include <thread>
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace {
|
||||
constexpr char GamesDBAPIKey[] =
|
||||
namespace
|
||||
{
|
||||
constexpr char GamesDBAPIKey[] =
|
||||
"445fcbc3f32bb2474bc27016b99eb963d318ee3a608212c543b9a79de1041600";
|
||||
|
||||
constexpr int MAX_WAIT_MS = 90000;
|
||||
constexpr int POLL_TIME_MS = 500;
|
||||
constexpr int MAX_WAIT_ITER = MAX_WAIT_MS / POLL_TIME_MS;
|
||||
constexpr int MAX_WAIT_MS = 90000;
|
||||
constexpr int POLL_TIME_MS = 500;
|
||||
constexpr int MAX_WAIT_ITER = MAX_WAIT_MS / POLL_TIME_MS;
|
||||
|
||||
constexpr char SCRAPER_RESOURCES_DIR[] = "scrapers";
|
||||
constexpr char DEVELOPERS_JSON_FILE[] = "gamesdb_developers.json";
|
||||
constexpr char PUBLISHERS_JSON_FILE[] = "gamesdb_publishers.json";
|
||||
constexpr char GENRES_JSON_FILE[] = "gamesdb_genres.json";
|
||||
constexpr char DEVELOPERS_ENDPOINT[] = "/Developers";
|
||||
constexpr char PUBLISHERS_ENDPOINT[] = "/Publishers";
|
||||
constexpr char GENRES_ENDPOINT[] = "/Genres";
|
||||
constexpr char SCRAPER_RESOURCES_DIR[] = "scrapers";
|
||||
constexpr char DEVELOPERS_JSON_FILE[] = "gamesdb_developers.json";
|
||||
constexpr char PUBLISHERS_JSON_FILE[] = "gamesdb_publishers.json";
|
||||
constexpr char GENRES_JSON_FILE[] = "gamesdb_genres.json";
|
||||
constexpr char DEVELOPERS_ENDPOINT[] = "/Developers";
|
||||
constexpr char PUBLISHERS_ENDPOINT[] = "/Publishers";
|
||||
constexpr char GENRES_ENDPOINT[] = "/Genres";
|
||||
|
||||
std::string genFilePath(const std::string& file_name)
|
||||
{
|
||||
return Utils::FileSystem::getGenericPath(getScrapersResouceDir() + "/" + file_name);
|
||||
}
|
||||
std::string genFilePath(const std::string& file_name)
|
||||
{
|
||||
return Utils::FileSystem::getGenericPath(getScrapersResouceDir() + "/" + file_name);
|
||||
}
|
||||
|
||||
void ensureScrapersResourcesDir()
|
||||
{
|
||||
std::string path = getScrapersResouceDir();
|
||||
if (!Utils::FileSystem::exists(path))
|
||||
Utils::FileSystem::createDirectory(path);
|
||||
}
|
||||
void ensureScrapersResourcesDir()
|
||||
{
|
||||
std::string path = getScrapersResouceDir();
|
||||
if (!Utils::FileSystem::exists(path))
|
||||
Utils::FileSystem::createDirectory(path);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string getScrapersResouceDir()
|
||||
{
|
||||
return Utils::FileSystem::getGenericPath(
|
||||
Utils::FileSystem::getHomePath() + "/.emulationstation/" + SCRAPER_RESOURCES_DIR);
|
||||
return Utils::FileSystem::getGenericPath(Utils::FileSystem::getHomePath() +
|
||||
"/.emulationstation/" + SCRAPER_RESOURCES_DIR);
|
||||
}
|
||||
|
||||
std::string TheGamesDBJSONRequestResources::getApiKey() const { return GamesDBAPIKey; }
|
||||
|
@ -69,16 +70,16 @@ void TheGamesDBJSONRequestResources::prepare()
|
|||
if (checkLoaded())
|
||||
return;
|
||||
|
||||
if (loadResource(gamesdb_new_developers_map, "developers",
|
||||
genFilePath(DEVELOPERS_JSON_FILE)) && !gamesdb_developers_resource_request)
|
||||
if (loadResource(gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE)) &&
|
||||
!gamesdb_developers_resource_request)
|
||||
gamesdb_developers_resource_request = fetchResource(DEVELOPERS_ENDPOINT);
|
||||
|
||||
if (loadResource(gamesdb_new_publishers_map, "publishers",
|
||||
genFilePath(PUBLISHERS_JSON_FILE)) && !gamesdb_publishers_resource_request)
|
||||
if (loadResource(gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE)) &&
|
||||
!gamesdb_publishers_resource_request)
|
||||
gamesdb_publishers_resource_request = fetchResource(PUBLISHERS_ENDPOINT);
|
||||
|
||||
if (loadResource(gamesdb_new_genres_map, "genres",
|
||||
genFilePath(GENRES_JSON_FILE)) && !gamesdb_genres_resource_request)
|
||||
if (loadResource(gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE)) &&
|
||||
!gamesdb_genres_resource_request)
|
||||
gamesdb_genres_resource_request = fetchResource(GENRES_ENDPOINT);
|
||||
}
|
||||
|
||||
|
@ -90,21 +91,22 @@ void TheGamesDBJSONRequestResources::ensureResources()
|
|||
for (int i = 0; i < MAX_WAIT_ITER; i++) {
|
||||
|
||||
if (gamesdb_developers_resource_request &&
|
||||
saveResource(gamesdb_developers_resource_request.get(),
|
||||
gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE)))
|
||||
saveResource(gamesdb_developers_resource_request.get(), gamesdb_new_developers_map,
|
||||
"developers", genFilePath(DEVELOPERS_JSON_FILE)))
|
||||
gamesdb_developers_resource_request.reset(nullptr);
|
||||
|
||||
if (gamesdb_publishers_resource_request &&
|
||||
saveResource(gamesdb_publishers_resource_request.get(),
|
||||
gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE)))
|
||||
saveResource(gamesdb_publishers_resource_request.get(), gamesdb_new_publishers_map,
|
||||
"publishers", genFilePath(PUBLISHERS_JSON_FILE)))
|
||||
gamesdb_publishers_resource_request.reset(nullptr);
|
||||
|
||||
if (gamesdb_genres_resource_request && saveResource(gamesdb_genres_resource_request.get(),
|
||||
gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE)))
|
||||
if (gamesdb_genres_resource_request &&
|
||||
saveResource(gamesdb_genres_resource_request.get(), gamesdb_new_genres_map, "genres",
|
||||
genFilePath(GENRES_JSON_FILE)))
|
||||
gamesdb_genres_resource_request.reset(nullptr);
|
||||
|
||||
if (!gamesdb_developers_resource_request && !gamesdb_publishers_resource_request &&
|
||||
!gamesdb_genres_resource_request)
|
||||
!gamesdb_genres_resource_request)
|
||||
return;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(POLL_TIME_MS));
|
||||
|
@ -115,14 +117,13 @@ void TheGamesDBJSONRequestResources::ensureResources()
|
|||
bool TheGamesDBJSONRequestResources::checkLoaded()
|
||||
{
|
||||
return !gamesdb_new_genres_map.empty() && !gamesdb_new_developers_map.empty() &&
|
||||
!gamesdb_new_publishers_map.empty();
|
||||
!gamesdb_new_publishers_map.empty();
|
||||
}
|
||||
|
||||
bool TheGamesDBJSONRequestResources::saveResource(
|
||||
HttpReq* req,
|
||||
std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name)
|
||||
bool TheGamesDBJSONRequestResources::saveResource(HttpReq* req,
|
||||
std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name)
|
||||
{
|
||||
|
||||
if (req == nullptr) {
|
||||
|
@ -133,8 +134,8 @@ bool TheGamesDBJSONRequestResources::saveResource(
|
|||
return false; // Not ready: wait some more.
|
||||
}
|
||||
if (req->status() != HttpReq::REQ_SUCCESS) {
|
||||
LOG(LogError) << "Resource request for " << file_name <<
|
||||
" failed:\n\t" << req->getErrorMsg();
|
||||
LOG(LogError) << "Resource request for " << file_name << " failed:\n\t"
|
||||
<< req->getErrorMsg();
|
||||
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));
|
||||
}
|
||||
|
||||
int TheGamesDBJSONRequestResources::loadResource(
|
||||
std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name)
|
||||
int TheGamesDBJSONRequestResources::loadResource(std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name)
|
||||
{
|
||||
std::ifstream fin(file_name);
|
||||
if (!fin.good())
|
||||
|
@ -172,8 +172,8 @@ int TheGamesDBJSONRequestResources::loadResource(
|
|||
|
||||
if (doc.HasParseError()) {
|
||||
std::string err = std::string("TheGamesDBJSONRequest - "
|
||||
"Error parsing JSON for resource file ") + file_name +
|
||||
":\n\t" + GetParseError_En(doc.GetParseError());
|
||||
"Error parsing JSON for resource file ") +
|
||||
file_name + ":\n\t" + GetParseError_En(doc.GetParseError());
|
||||
LOG(LogError) << err;
|
||||
return 1;
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ int TheGamesDBJSONRequestResources::loadResource(
|
|||
for (Value::ConstMemberIterator itr = data.MemberBegin(); itr != data.MemberEnd(); itr++) {
|
||||
auto& entry = itr->value;
|
||||
if (!entry.IsObject() || !entry.HasMember("id") || !entry["id"].IsInt() ||
|
||||
!entry.HasMember("name") || !entry["name"].IsString())
|
||||
!entry.HasMember("name") || !entry["name"].IsString())
|
||||
continue;
|
||||
|
||||
resource[entry["id"].GetInt()] = entry["name"].GetString();
|
||||
|
|
|
@ -33,20 +33,18 @@ struct TheGamesDBJSONRequestResources {
|
|||
std::unordered_map<int, std::string> gamesdb_new_publishers_map;
|
||||
std::unordered_map<int, std::string> gamesdb_new_genres_map;
|
||||
|
||||
private:
|
||||
private:
|
||||
bool checkLoaded();
|
||||
|
||||
bool saveResource(
|
||||
HttpReq* req,
|
||||
std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name);
|
||||
std::unique_ptr<HttpReq> fetchResource(const std::string& endpoint);
|
||||
bool saveResource(HttpReq* req,
|
||||
std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name);
|
||||
std::unique_ptr<HttpReq> fetchResource(const std::string& endpoint);
|
||||
|
||||
int loadResource(
|
||||
std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name);
|
||||
int loadResource(std::unordered_map<int, std::string>& resource,
|
||||
const std::string& resource_name,
|
||||
const std::string& file_name);
|
||||
|
||||
std::unique_ptr<HttpReq> gamesdb_developers_resource_request;
|
||||
std::unique_ptr<HttpReq> gamesdb_publishers_resource_request;
|
||||
|
|
|
@ -10,20 +10,20 @@
|
|||
|
||||
#include "scrapers/Scraper.h"
|
||||
|
||||
#include "utils/StringUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "GamesDBJSONScraper.h"
|
||||
#include "Log.h"
|
||||
#include "ScreenScraper.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "utils/StringUtil.h"
|
||||
|
||||
#if defined(_WIN64)
|
||||
#include "views/ViewController.h"
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <FreeImage.h>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
|
||||
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";
|
||||
}
|
||||
else {
|
||||
LOG(LogDebug) << "Scraper::startScraperSearch(): Scraping system \"" <<
|
||||
params.system->getName() << "\", game file \"" <<
|
||||
params.game->getFileName() << "\"";
|
||||
LOG(LogDebug) << "Scraper::startScraperSearch(): Scraping system \""
|
||||
<< params.system->getName() << "\", game file \""
|
||||
<< params.game->getFileName() << "\"";
|
||||
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();
|
||||
}
|
||||
|
||||
// ScraperSearchHandle.
|
||||
ScraperSearchHandle::ScraperSearchHandle()
|
||||
{
|
||||
setStatus(ASYNC_IN_PROGRESS);
|
||||
}
|
||||
|
||||
void ScraperSearchHandle::update()
|
||||
{
|
||||
if (mStatus == ASYNC_DONE)
|
||||
|
@ -128,13 +122,14 @@ void ScraperSearchHandle::update()
|
|||
|
||||
// ScraperRequest.
|
||||
ScraperRequest::ScraperRequest(std::vector<ScraperSearchResult>& resultsWrite)
|
||||
: mResults(resultsWrite)
|
||||
: mResults(resultsWrite)
|
||||
{
|
||||
}
|
||||
|
||||
// ScraperHttpRequest.
|
||||
ScraperHttpRequest::ScraperHttpRequest(std::vector<ScraperSearchResult>& resultsWrite,
|
||||
const std::string& url) : ScraperRequest(resultsWrite)
|
||||
const std::string& url)
|
||||
: ScraperRequest(resultsWrite)
|
||||
{
|
||||
setStatus(ASYNC_IN_PROGRESS);
|
||||
mReq = std::unique_ptr<HttpReq>(new HttpReq(url));
|
||||
|
@ -155,20 +150,21 @@ void ScraperHttpRequest::update()
|
|||
return;
|
||||
|
||||
// Everything else is some sort of error.
|
||||
LOG(LogError) << "ScraperHttpRequest network error (status: " << status<< ") - "
|
||||
<< mReq->getErrorMsg();
|
||||
LOG(LogError) << "ScraperHttpRequest network error (status: " << status << ") - "
|
||||
<< mReq->getErrorMsg();
|
||||
setError("Network error: " + mReq->getErrorMsg());
|
||||
}
|
||||
|
||||
// Download and write the media files to disk.
|
||||
std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result,
|
||||
const ScraperSearchParams& search)
|
||||
const ScraperSearchParams& search)
|
||||
{
|
||||
return std::unique_ptr<MDResolveHandle>(new MDResolveHandle(result, search));
|
||||
}
|
||||
|
||||
MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
|
||||
const ScraperSearchParams& search) : mResult(result)
|
||||
const ScraperSearchParams& search)
|
||||
: mResult(result)
|
||||
{
|
||||
struct mediaFileInfoStruct {
|
||||
std::string fileURL;
|
||||
|
@ -221,10 +217,10 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
|
|||
mediaFileInfo.existingMediaFile = search.game->getVideoPath();
|
||||
mediaFileInfo.resizeFile = false;
|
||||
scrapeFiles.push_back(mediaFileInfo);
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
// Required due to the idiotic file locking that exists on this operating system.
|
||||
ViewController::get()->onStopVideo();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
// has been set to no, then don't proceed with downloading or saving a new file.
|
||||
if (it->existingMediaFile != "" &&
|
||||
!Settings::getInstance()->getBool("ScraperOverwriteData"))
|
||||
!Settings::getInstance()->getBool("ScraperOverwriteData"))
|
||||
continue;
|
||||
|
||||
// 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.
|
||||
if (mResult.thumbnailImageUrl == it->fileURL &&
|
||||
mResult.thumbnailImageData.size() > 0) {
|
||||
if (mResult.thumbnailImageUrl == it->fileURL && mResult.thumbnailImageData.size() > 0) {
|
||||
|
||||
// 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
|
||||
|
@ -265,19 +260,19 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
|
|||
// are sometimes returned from the scraper service and these can actually be
|
||||
// less than 350 bytes in size.
|
||||
if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") &&
|
||||
mResult.thumbnailImageData.size() < 350) {
|
||||
mResult.thumbnailImageData.size() < 350) {
|
||||
|
||||
FIMEMORY* memoryStream = FreeImage_OpenMemory(
|
||||
reinterpret_cast<BYTE*>(&mResult.thumbnailImageData.at(0)),
|
||||
static_cast<DWORD>(mResult.thumbnailImageData.size()));
|
||||
FIMEMORY* memoryStream =
|
||||
FreeImage_OpenMemory(reinterpret_cast<BYTE*>(&mResult.thumbnailImageData.at(0)),
|
||||
static_cast<DWORD>(mResult.thumbnailImageData.size()));
|
||||
|
||||
FREE_IMAGE_FORMAT imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0);
|
||||
FreeImage_CloseMemory(memoryStream);
|
||||
|
||||
if (imageFormat == FIF_UNKNOWN) {
|
||||
setError("The file \"" + Utils::FileSystem::getFileName(filePath) +
|
||||
"\" returned by the scraper seems to be invalid as it's less than " +
|
||||
"350 bytes in size");
|
||||
"\" returned by the scraper seems to be invalid as it's less than " +
|
||||
"350 bytes in size");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -293,18 +288,18 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
|
|||
// problems or the MediaDirectory setting points to a file instead of a directory.
|
||||
if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(filePath))) {
|
||||
setError("Media directory does not exist and can't be created. "
|
||||
"Permission problems?");
|
||||
LOG(LogError) << "Couldn't create media directory: \"" <<
|
||||
Utils::FileSystem::getParent(filePath) << "\"";
|
||||
return;
|
||||
"Permission problems?");
|
||||
LOG(LogError) << "Couldn't create media directory: \""
|
||||
<< Utils::FileSystem::getParent(filePath) << "\"";
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
std::ofstream stream(Utils::String::stringToWideString(filePath).c_str(),
|
||||
std::ios_base::out | std::ios_base::binary);
|
||||
#else
|
||||
std::ios_base::out | std::ios_base::binary);
|
||||
#else
|
||||
std::ofstream stream(filePath, std::ios_base::out | std::ios_base::binary);
|
||||
#endif
|
||||
#endif
|
||||
if (!stream || stream.bad()) {
|
||||
setError("Failed to open path for writing media file.\nPermission error?");
|
||||
return;
|
||||
|
@ -331,8 +326,9 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result,
|
|||
// If it's not cached, then initiate the download.
|
||||
else {
|
||||
mFuncs.push_back(ResolvePair(downloadMediaAsync(it->fileURL, filePath,
|
||||
it->existingMediaFile, it->subDirectory, it->resizeFile, mResult.savedNewMedia),
|
||||
[this, filePath] {}));
|
||||
it->existingMediaFile, it->subDirectory,
|
||||
it->resizeFile, mResult.savedNewMedia),
|
||||
[this, filePath] {}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -361,37 +357,30 @@ void MDResolveHandle::update()
|
|||
setStatus(ASYNC_DONE);
|
||||
}
|
||||
|
||||
std::unique_ptr<MediaDownloadHandle> downloadMediaAsync(
|
||||
const std::string& url,
|
||||
const std::string& saveAs,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia)
|
||||
std::unique_ptr<MediaDownloadHandle> downloadMediaAsync(const std::string& url,
|
||||
const std::string& saveAs,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia)
|
||||
{
|
||||
return std::unique_ptr<MediaDownloadHandle>(new MediaDownloadHandle(
|
||||
url,
|
||||
saveAs,
|
||||
existingMediaPath,
|
||||
mediaType,
|
||||
resizeFile,
|
||||
savedNewMedia));
|
||||
url, saveAs, existingMediaPath, mediaType, resizeFile, savedNewMedia));
|
||||
}
|
||||
|
||||
MediaDownloadHandle::MediaDownloadHandle(
|
||||
const std::string& url,
|
||||
const std::string& path,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia)
|
||||
: mSavePath(path),
|
||||
mExistingMediaFile(existingMediaPath),
|
||||
mMediaType(mediaType),
|
||||
mResizeFile(resizeFile),
|
||||
mReq(new HttpReq(url))
|
||||
MediaDownloadHandle::MediaDownloadHandle(const std::string& url,
|
||||
const std::string& path,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia)
|
||||
: mSavePath(path)
|
||||
, mExistingMediaFile(existingMediaPath)
|
||||
, mMediaType(mediaType)
|
||||
, mResizeFile(resizeFile)
|
||||
, mReq(new HttpReq(url))
|
||||
{
|
||||
mSavedNewMediaPtr = &savedNewMedia;
|
||||
mSavedNewMediaPtr = &savedNewMedia;
|
||||
}
|
||||
|
||||
void MediaDownloadHandle::update()
|
||||
|
@ -421,25 +410,22 @@ void MediaDownloadHandle::update()
|
|||
// Black/empty images are sometimes returned from the scraper service and these can actually
|
||||
// be less than 350 bytes in size.
|
||||
if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") &&
|
||||
mReq->getContent().size() < 350) {
|
||||
mReq->getContent().size() < 350) {
|
||||
|
||||
FREE_IMAGE_FORMAT imageFormat = FIF_UNKNOWN;
|
||||
|
||||
if (mMediaType != "videos") {
|
||||
std::string imageData = mReq->getContent();
|
||||
|
||||
FIMEMORY* memoryStream = FreeImage_OpenMemory(
|
||||
reinterpret_cast<BYTE*>(&imageData.at(0)),
|
||||
static_cast<DWORD>(imageData.size()));
|
||||
|
||||
FIMEMORY* memoryStream = FreeImage_OpenMemory(reinterpret_cast<BYTE*>(&imageData.at(0)),
|
||||
static_cast<DWORD>(imageData.size()));
|
||||
imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0);
|
||||
FreeImage_CloseMemory(memoryStream);
|
||||
}
|
||||
|
||||
if (imageFormat == FIF_UNKNOWN) {
|
||||
setError("The file \"" + Utils::FileSystem::getFileName(mSavePath) +
|
||||
"\" returned by the scraper seems to be invalid as it's less than " +
|
||||
"350 bytes in size");
|
||||
"\" returned by the scraper seems to be invalid as it's less than " +
|
||||
"350 bytes in size");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -455,17 +441,17 @@ void MediaDownloadHandle::update()
|
|||
// problems or the MediaDirectory setting points to a file instead of a directory.
|
||||
if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(mSavePath))) {
|
||||
setError("Media directory does not exist and can't be created. Permission problems?");
|
||||
LOG(LogError) << "Couldn't create media directory: \"" <<
|
||||
Utils::FileSystem::getParent(mSavePath) << "\"";
|
||||
LOG(LogError) << "Couldn't create media directory: \""
|
||||
<< Utils::FileSystem::getParent(mSavePath) << "\"";
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
std::ofstream stream(Utils::String::stringToWideString(mSavePath).c_str(),
|
||||
std::ios_base::out | std::ios_base::binary);
|
||||
#else
|
||||
std::ios_base::out | std::ios_base::binary);
|
||||
#else
|
||||
std::ofstream stream(mSavePath, std::ios_base::out | std::ios_base::binary);
|
||||
#endif
|
||||
#endif
|
||||
if (!stream || stream.bad()) {
|
||||
setError("Failed to open path for writing media file.\nPermission error?");
|
||||
return;
|
||||
|
@ -512,15 +498,16 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
|
|||
FIBITMAP* image = nullptr;
|
||||
|
||||
// Detect the file format.
|
||||
#if defined(_WIN64)
|
||||
|
||||
#if defined(_WIN64)
|
||||
format = FreeImage_GetFileTypeU(Utils::String::stringToWideString(path).c_str(), 0);
|
||||
if (format == FIF_UNKNOWN)
|
||||
format = FreeImage_GetFIFFromFilenameU(Utils::String::stringToWideString(path).c_str());
|
||||
#else
|
||||
#else
|
||||
format = FreeImage_GetFileType(path.c_str(), 0);
|
||||
if (format == FIF_UNKNOWN)
|
||||
format = FreeImage_GetFIFFromFilename(path.c_str());
|
||||
#endif
|
||||
#endif
|
||||
if (format == FIF_UNKNOWN) {
|
||||
LOG(LogError) << "Could not detect filetype for image \"" << path << "\"!";
|
||||
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.
|
||||
if (FreeImage_FIFSupportsReading(format)) {
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
image = FreeImage_LoadU(format, Utils::String::stringToWideString(path).c_str());
|
||||
#else
|
||||
#else
|
||||
image = FreeImage_Load(format, path.c_str());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
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
|
||||
// do any scaling. It doesn't make sense to upscale the image and waste disk space.
|
||||
if (maxWidth >= width && maxHeight >= height) {
|
||||
LOG(LogDebug) << "Scraper::resizeImage(): Saving image \"" << path <<
|
||||
"\" at its original resolution " << width << "x" << height;
|
||||
LOG(LogDebug) << "Scraper::resizeImage(): Saving image \"" << path
|
||||
<< "\" at its original resolution " << width << "x" << height;
|
||||
FreeImage_Unload(image);
|
||||
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.
|
||||
FIBITMAP* imageRescaled = FreeImage_Rescale(image, static_cast<int>(maxWidth),
|
||||
static_cast<int>(maxHeight), FILTER_LANCZOS3);
|
||||
static_cast<int>(maxHeight), FILTER_LANCZOS3);
|
||||
FreeImage_Unload(image);
|
||||
|
||||
if (imageRescaled == nullptr) {
|
||||
|
@ -576,12 +563,12 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
|
|||
return false;
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
bool saved = (FreeImage_SaveU(format, imageRescaled,
|
||||
Utils::String::stringToWideString(path).c_str()) != 0);
|
||||
#else
|
||||
Utils::String::stringToWideString(path).c_str()) != 0);
|
||||
#else
|
||||
bool saved = (FreeImage_Save(format, imageRescaled, path.c_str()) != 0);
|
||||
#endif
|
||||
#endif
|
||||
FreeImage_Unload(imageRescaled);
|
||||
|
||||
if (!saved) {
|
||||
|
@ -589,14 +576,15 @@ bool resizeImage(const std::string& path, const std::string& mediaType)
|
|||
}
|
||||
else {
|
||||
LOG(LogDebug) << "Scraper::resizeImage(): Downscaled image \"" << path << "\" from "
|
||||
<< width << "x" << height << " to " << maxWidth << "x" << maxHeight;
|
||||
<< width << "x" << height << " to " << maxWidth << "x" << maxHeight;
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
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 name = Utils::FileSystem::getStem(params.game->getPath());
|
||||
|
@ -605,7 +593,7 @@ std::string getSaveAsPath(const ScraperSearchParams& params,
|
|||
// Extract possible subfolders from the path.
|
||||
if (params.system->getSystemEnvData()->mStartPath != "")
|
||||
subFolders = Utils::String::replace(Utils::FileSystem::getParent(params.game->getPath()),
|
||||
params.system->getSystemEnvData()->mStartPath, "");
|
||||
params.system->getSystemEnvData()->mStartPath, "");
|
||||
|
||||
std::string path = FileData::getMediaDirectory();
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class FileData;
|
|||
class SystemData;
|
||||
|
||||
enum downloadStatus {
|
||||
NOT_STARTED,
|
||||
NOT_STARTED, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
IN_PROGRESS,
|
||||
COMPLETED
|
||||
};
|
||||
|
@ -40,7 +40,10 @@ struct ScraperSearchParams {
|
|||
};
|
||||
|
||||
struct ScraperSearchResult {
|
||||
ScraperSearchResult() : mdl(GAME_METADATA) {};
|
||||
ScraperSearchResult()
|
||||
: mdl(GAME_METADATA)
|
||||
{
|
||||
}
|
||||
|
||||
MetaDataList mdl;
|
||||
std::string gameID;
|
||||
|
@ -73,34 +76,6 @@ struct ScraperSearchResult {
|
|||
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.
|
||||
class ScraperRequest : public AsyncHandle
|
||||
{
|
||||
|
@ -123,7 +98,7 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void process(const std::unique_ptr<HttpReq>& req,
|
||||
std::vector<ScraperSearchResult>& results) = 0;
|
||||
std::vector<ScraperSearchResult>& results) = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<HttpReq> mReq;
|
||||
|
@ -133,21 +108,20 @@ private:
|
|||
class ScraperSearchHandle : public AsyncHandle
|
||||
{
|
||||
public:
|
||||
ScraperSearchHandle();
|
||||
ScraperSearchHandle() { setStatus(ASYNC_IN_PROGRESS); }
|
||||
|
||||
void update();
|
||||
inline const std::vector<ScraperSearchResult>& getResults() const
|
||||
const std::vector<ScraperSearchResult>& getResults() const
|
||||
{
|
||||
assert(mStatus != ASYNC_IN_PROGRESS);
|
||||
return mResults;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend std::unique_ptr<ScraperSearchHandle>
|
||||
startScraperSearch(const ScraperSearchParams& params);
|
||||
friend std::unique_ptr<ScraperSearchHandle> startScraperSearch(
|
||||
const ScraperSearchParams& params);
|
||||
|
||||
friend std::unique_ptr<ScraperSearchHandle>
|
||||
startMediaURLsFetch(const std::string& gameIDs);
|
||||
friend std::unique_ptr<ScraperSearchHandle> startMediaURLsFetch(const std::string& gameIDs);
|
||||
|
||||
std::queue<std::unique_ptr<ScraperRequest>> mRequestQueue;
|
||||
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.
|
||||
bool isValidConfiguredScraper();
|
||||
|
||||
typedef void (*generate_scraper_requests_func)(const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
typedef void (*generate_scraper_requests_func)(
|
||||
const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
@ -177,8 +152,11 @@ public:
|
|||
MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search);
|
||||
|
||||
void update() override;
|
||||
inline const ScraperSearchResult& getResult() const
|
||||
{ assert(mStatus == ASYNC_DONE); return mResult; }
|
||||
const ScraperSearchResult& getResult() const
|
||||
{
|
||||
assert(mStatus == ASYNC_DONE);
|
||||
return mResult;
|
||||
}
|
||||
bool getSavedNewMedia() { return mResult.savedNewMedia; }
|
||||
|
||||
private:
|
||||
|
@ -191,13 +169,12 @@ private:
|
|||
class MediaDownloadHandle : public AsyncHandle
|
||||
{
|
||||
public:
|
||||
MediaDownloadHandle(
|
||||
const std::string& url,
|
||||
const std::string& path,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia);
|
||||
MediaDownloadHandle(const std::string& url,
|
||||
const std::string& path,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia);
|
||||
|
||||
void update() override;
|
||||
|
||||
|
@ -207,26 +184,26 @@ private:
|
|||
std::string mExistingMediaFile;
|
||||
std::string mMediaType;
|
||||
bool mResizeFile;
|
||||
bool *mSavedNewMediaPtr;
|
||||
bool* mSavedNewMediaPtr;
|
||||
};
|
||||
|
||||
// Downloads to the home directory, using this subdirectory structure:
|
||||
// ".emulationstation/downloaded_media/[system_name]/[media_type]/[game_name].[file_extension]".
|
||||
// The subdirectories are automatically created if they do not exist.
|
||||
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(
|
||||
const std::string& url,
|
||||
const std::string& saveAs,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia);
|
||||
std::unique_ptr<MediaDownloadHandle> downloadMediaAsync(const std::string& url,
|
||||
const std::string& saveAs,
|
||||
const std::string& existingMediaPath,
|
||||
const std::string& mediaType,
|
||||
const bool resizeFile,
|
||||
bool& savedNewMedia);
|
||||
|
||||
// Resolves all metadata assets that need to be downloaded.
|
||||
std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result,
|
||||
const ScraperSearchParams& search);
|
||||
const ScraperSearchParams& search);
|
||||
|
||||
bool resizeImage(const std::string& path, const std::string& mediaType);
|
||||
|
||||
|
|
|
@ -9,14 +9,14 @@
|
|||
|
||||
#include "scrapers/ScreenScraper.h"
|
||||
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "utils/TimeUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "Log.h"
|
||||
#include "PlatformId.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "math/Misc.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "utils/TimeUtil.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
@ -42,7 +42,7 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
|
|||
{ ATARI_JAGUAR, 27 },
|
||||
{ ATARI_JAGUAR_CD, 171 },
|
||||
{ ATARI_LYNX, 28 },
|
||||
{ ATARI_ST, 42},
|
||||
{ ATARI_ST, 42 },
|
||||
{ ATARI_XE, 43 },
|
||||
{ ATOMISWAVE, 53 },
|
||||
{ BBC_MICRO, 37 },
|
||||
|
@ -62,7 +62,7 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
|
|||
{ MSX_TURBO_R, 118 },
|
||||
{ SNK_NEO_GEO, 142 },
|
||||
{ SNK_NEO_GEO_CD, 142 },
|
||||
{ SNK_NEO_GEO_POCKET, 25},
|
||||
{ SNK_NEO_GEO_POCKET, 25 },
|
||||
{ SNK_NEO_GEO_POCKET_COLOR, 82 },
|
||||
{ NINTENDO_3DS, 17 },
|
||||
{ NINTENDO_64, 14 },
|
||||
|
@ -88,7 +88,7 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
|
|||
{ NEC_PCFX, 72 },
|
||||
{ GAMEENGINE_OPENBOR, 214 },
|
||||
{ TANGERINE_ORIC, 131 },
|
||||
{ GAMEENGINE_SCUMMVM, 123},
|
||||
{ GAMEENGINE_SCUMMVM, 123 },
|
||||
{ SEGA_32X, 19 },
|
||||
{ SEGA_CD, 20 },
|
||||
{ SEGA_DREAMCAST, 23 },
|
||||
|
@ -98,8 +98,8 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
|
|||
{ SEGA_MEGA_DRIVE, 1 },
|
||||
{ SEGA_SATURN, 22 },
|
||||
{ SEGA_SG1000, 109 },
|
||||
{ SHARP_X1, 220},
|
||||
{ SHARP_X68000, 79},
|
||||
{ SHARP_X1, 220 },
|
||||
{ SHARP_X68000, 79 },
|
||||
{ GAMEENGINE_SOLARUS, 223 },
|
||||
{ SONY_PLAYSTATION, 57 },
|
||||
{ SONY_PLAYSTATION_2, 58 },
|
||||
|
@ -110,8 +110,8 @@ const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
|
|||
{ SUPER_NINTENDO, 4 },
|
||||
{ NEC_SUPERGRAFX, 105 },
|
||||
{ GAMEENGINE_TIC80, 222 },
|
||||
{ NEC_PC_8800, 221},
|
||||
{ NEC_PC_9800, 208},
|
||||
{ NEC_PC_8800, 221 },
|
||||
{ NEC_PC_9800, 208 },
|
||||
{ NEC_PC_ENGINE, 31 },
|
||||
{ NEC_PC_ENGINE_CD, 114 },
|
||||
{ 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.
|
||||
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) {
|
||||
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
|
||||
// filtering by an attribute value list.
|
||||
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::vector<std::string> attribute_values)
|
||||
const std::string& node_name,
|
||||
const std::string& attribute_name,
|
||||
const std::vector<std::string> attribute_values)
|
||||
{
|
||||
for (auto _val : attribute_values) {
|
||||
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,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
std::string path;
|
||||
|
||||
ScreenScraperRequest::ScreenScraperConfig ssConfig;
|
||||
|
||||
if (params.game->isArcadeGame())
|
||||
ssConfig.isArcadeSystem = true;
|
||||
ssConfig.isArcadeSystem = true;
|
||||
else
|
||||
ssConfig.isArcadeSystem = false;
|
||||
|
||||
if (params.nameOverride == "") {
|
||||
if (Settings::getInstance()->getBool("ScraperSearchMetadataName"))
|
||||
path = ssConfig.getGameSearchUrl(
|
||||
Utils::String::removeParenthesis(params.game->metadata.get("name")));
|
||||
Utils::String::removeParenthesis(params.game->metadata.get("name")));
|
||||
else
|
||||
path = ssConfig.getGameSearchUrl(params.game->getCleanName());
|
||||
}
|
||||
|
@ -195,19 +197,19 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
|
|||
p_ids.push_back(mapIt->second);
|
||||
}
|
||||
else {
|
||||
LOG(LogWarning) << "ScreenScraper: No support for platform \"" <<
|
||||
getPlatformName(*platformIt) << "\", search will be inaccurate";
|
||||
LOG(LogWarning) << "ScreenScraper: No support for platform \""
|
||||
<< getPlatformName(*platformIt) << "\", search will be inaccurate";
|
||||
// Add the scrape request without a platform/system ID.
|
||||
requests.push(std::unique_ptr<ScraperRequest>
|
||||
(new ScreenScraperRequest(requests, results, path)));
|
||||
requests.push(
|
||||
std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(requests, results, path)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p_ids.size() == 0) {
|
||||
LOG(LogWarning) << "ScreenScraper: No platform defined, search will be inaccurate";
|
||||
// Add the scrape request without a platform/system ID.
|
||||
requests.push(std::unique_ptr<ScraperRequest>
|
||||
(new ScreenScraperRequest(requests, results, path)));
|
||||
requests.push(
|
||||
std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(requests, results, path)));
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
path += "&systemeid=";
|
||||
path += HttpReq::urlEncode(std::to_string(*platform));
|
||||
requests.push(std::unique_ptr<ScraperRequest>
|
||||
(new ScreenScraperRequest(requests, results, path)));
|
||||
requests.push(
|
||||
std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(requests, results, path)));
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenScraperRequest::process(const std::unique_ptr<HttpReq>& req,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
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"));
|
||||
if (gameName.substr(0, 12) == "ZZZ(NOTGAME)") {
|
||||
LOG(LogWarning) << "ScreenScraperRequest - Received \"ZZZ(notgame)\" as game name, "
|
||||
"ignoring response";
|
||||
"ignoring response";
|
||||
results.pop_back();
|
||||
return;
|
||||
}
|
||||
|
@ -271,7 +273,7 @@ void ScreenScraperRequest::process(const std::unique_ptr<HttpReq>& req,
|
|||
}
|
||||
|
||||
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");
|
||||
|
||||
|
@ -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
|
||||
// service issue so we're not attempting to compensate for it here.
|
||||
if (Settings::getInstance()->getBool("ScraperUseAccountScreenScraper") &&
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper") != "" &&
|
||||
Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") {
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper") != "" &&
|
||||
Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") {
|
||||
std::string userID = data.child("ssuser").child("id").text().get();
|
||||
if (userID != "") {
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Scraping using account \"" <<
|
||||
userID << "\"";
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Scraping using account \""
|
||||
<< userID << "\"";
|
||||
}
|
||||
else {
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): The configured account '" <<
|
||||
Settings::getInstance()->getString("ScraperUsernameScreenScraper") <<
|
||||
"' was not included in the scraper response, wrong username or password?";
|
||||
LOG(LogDebug)
|
||||
<< "ScreenScraperRequest::processGame(): The configured account '"
|
||||
<< 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.
|
||||
if (maxRequestsPerDay > 0) {
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: " <<
|
||||
requestsToday << "/" << maxRequestsPerDay << " (" <<
|
||||
scraperRequestAllowance << " remaining)";
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: "
|
||||
<< requestsToday << "/" << maxRequestsPerDay << " ("
|
||||
<< scraperRequestAllowance << " remaining)";
|
||||
}
|
||||
else {
|
||||
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"))
|
||||
data = data.child("jeux");
|
||||
data = data.child("jeux");
|
||||
|
||||
for (pugi::xml_node game = data.child("jeu"); game; game = game.next_sibling("jeu")) {
|
||||
ScraperSearchResult result;
|
||||
|
@ -324,13 +327,16 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
|
|||
result.gameID = game.attribute("id").as_string();
|
||||
|
||||
std::string region =
|
||||
Utils::String::toLower(Settings::getInstance()->getString("ScraperRegion"));
|
||||
Utils::String::toLower(Settings::getInstance()->getString("ScraperRegion"));
|
||||
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[*]).
|
||||
result.mdl.set("name", find_child_by_attribute_list(game.child("noms"),
|
||||
"nom", "region", { region, "wor", "us" , "ss", "eu", "jp" }).text().get());
|
||||
result.mdl.set("name",
|
||||
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");
|
||||
|
||||
// Validate rating.
|
||||
|
@ -346,14 +352,16 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
|
|||
ss << ratingVal;
|
||||
if (ratingVal > 0) {
|
||||
result.mdl.set("rating", ss.str());
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Rating: " <<
|
||||
result.mdl.get("rating");
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Rating: "
|
||||
<< result.mdl.get("rating");
|
||||
}
|
||||
}
|
||||
|
||||
// Description fallback language: EN, WOR(LD).
|
||||
std::string description = find_child_by_attribute_list(game.child("synopsis"),
|
||||
"synopsis", "langue", { language, "en", "wor" }).text().get();
|
||||
std::string description = find_child_by_attribute_list(game.child("synopsis"), "synopsis",
|
||||
"langue", { language, "en", "wor" })
|
||||
.text()
|
||||
.get();
|
||||
|
||||
// Translate some HTML character codes to UTF-8 characters.
|
||||
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'
|
||||
// main child of 'jeu'.
|
||||
// Date fallback: WOR(LD), US, SS, JP, EU.
|
||||
// main child of 'jeu'. Date fallback: WOR(LD), US, SS, JP, EU.
|
||||
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.
|
||||
if (_date.length() > 4) {
|
||||
result.mdl.set("releasedate", Utils::Time::DateTime(
|
||||
Utils::Time::stringToTime(_date, "%Y-%m-%d")));
|
||||
result.mdl.set("releasedate",
|
||||
Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y-%m-%d")));
|
||||
}
|
||||
else if (_date.length() > 0) {
|
||||
result.mdl.set("releasedate", Utils::Time::DateTime(
|
||||
Utils::Time::stringToTime(_date, "%Y")));
|
||||
result.mdl.set("releasedate",
|
||||
Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y")));
|
||||
}
|
||||
|
||||
if (_date.length() > 0) {
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (unparsed): " <<
|
||||
_date;
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (parsed): " <<
|
||||
result.mdl.get("releasedate");
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (unparsed): "
|
||||
<< _date;
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (parsed): "
|
||||
<< result.mdl.get("releasedate");
|
||||
}
|
||||
|
||||
// Developer for the game (Xpath: Data/jeu[0]/developpeur).
|
||||
std::string developer = game.child("developpeur").text().get();
|
||||
if (!developer.empty()) {
|
||||
result.mdl.set("developer", Utils::String::replace(developer, " ", " "));
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Developer: " <<
|
||||
result.mdl.get("developer");
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Developer: "
|
||||
<< result.mdl.get("developer");
|
||||
}
|
||||
|
||||
// Publisher for the game (Xpath: Data/jeu[0]/editeur).
|
||||
std::string publisher = game.child("editeur").text().get();
|
||||
if (!publisher.empty()) {
|
||||
result.mdl.set("publisher", Utils::String::replace(publisher, " ", " "));
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Publisher: " <<
|
||||
result.mdl.get("publisher");
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Publisher: "
|
||||
<< result.mdl.get("publisher");
|
||||
}
|
||||
|
||||
// Genre fallback language: EN. (Xpath: Data/jeu[0]/genres/genre[*]).
|
||||
std::string genre = find_child_by_attribute_list(game.child("genres"),
|
||||
"genre", "langue", { language, "en" }).text().get();
|
||||
std::string genre = find_child_by_attribute_list(game.child("genres"), "genre", "langue",
|
||||
{ language, "en" })
|
||||
.text()
|
||||
.get();
|
||||
if (!genre.empty()) {
|
||||
result.mdl.set("genre", genre);
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: " <<
|
||||
result.mdl.get("genre");
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: "
|
||||
<< result.mdl.get("genre");
|
||||
}
|
||||
|
||||
// Players.
|
||||
std::string players = game.child("joueurs").text().get();
|
||||
if (!players.empty()) {
|
||||
result.mdl.set("players", players);
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Players: " <<
|
||||
result.mdl.get("players");
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Players: "
|
||||
<< result.mdl.get("players");
|
||||
}
|
||||
|
||||
// Media super-node.
|
||||
pugi::xml_node media_list = game.child("medias");
|
||||
|
||||
if (media_list) {
|
||||
// 3D box
|
||||
processMedia(result, media_list, ssConfig.media_3dbox,
|
||||
result.box3DUrl, result.box3DFormat, region);
|
||||
// Cover
|
||||
processMedia(result, media_list, ssConfig.media_cover,
|
||||
result.coverUrl, result.coverFormat, region);
|
||||
// Marquee (wheel)
|
||||
processMedia(result, media_list, ssConfig.media_marquee,
|
||||
result.marqueeUrl, result.marqueeFormat, region);
|
||||
// Screenshot
|
||||
processMedia(result, media_list, ssConfig.media_screenshot,
|
||||
result.screenshotUrl, result.screenshotFormat, region);
|
||||
// Video
|
||||
processMedia(result, media_list, ssConfig.media_video,
|
||||
result.videoUrl, result.videoFormat, region);
|
||||
// 3D box.
|
||||
processMedia(result, media_list, ssConfig.media_3dbox, result.box3DUrl,
|
||||
result.box3DFormat, region);
|
||||
// Cover.
|
||||
processMedia(result, media_list, ssConfig.media_cover, result.coverUrl,
|
||||
result.coverFormat, region);
|
||||
// Marquee (wheel).
|
||||
processMedia(result, media_list, ssConfig.media_marquee, result.marqueeUrl,
|
||||
result.marqueeFormat, region);
|
||||
// Screenshot.
|
||||
processMedia(result, media_list, ssConfig.media_screenshot, result.screenshotUrl,
|
||||
result.screenshotFormat, region);
|
||||
// Video.
|
||||
processMedia(result, media_list, ssConfig.media_video, result.videoUrl,
|
||||
result.videoFormat, region);
|
||||
}
|
||||
result.mediaURLFetch = COMPLETED;
|
||||
out_results.push_back(result);
|
||||
|
@ -446,13 +457,12 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
|
|||
}
|
||||
}
|
||||
|
||||
void ScreenScraperRequest::processMedia(
|
||||
ScraperSearchResult& result,
|
||||
const pugi::xml_node& media_list,
|
||||
std::string mediaType,
|
||||
std::string& fileURL,
|
||||
std::string& fileFormat,
|
||||
std::string region)
|
||||
void ScreenScraperRequest::processMedia(ScraperSearchResult& result,
|
||||
const pugi::xml_node& media_list,
|
||||
std::string mediaType,
|
||||
std::string& fileURL,
|
||||
std::string& fileFormat,
|
||||
std::string region)
|
||||
{
|
||||
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
|
||||
// <media type="..." region="..." format="...">
|
||||
// and we need to find the right media for the region.
|
||||
pugi::xpath_node_set results = media_list.select_nodes((static_cast<std::string>
|
||||
("media[@type='") + mediaType + "']").c_str());
|
||||
pugi::xpath_node_set results = media_list.select_nodes(
|
||||
(static_cast<std::string>("media[@type='") + mediaType + "']").c_str());
|
||||
|
||||
if (results.size()) {
|
||||
// Videos don't have any region attributes, so just take the first entry
|
||||
// (which should be the only entry as well).
|
||||
if (mediaType == "video" || mediaType == "video-normalized") {
|
||||
art = results.first().node();
|
||||
}
|
||||
else {
|
||||
// Region fallback: WOR(LD), US, CUS(TOM?), JP, EU.
|
||||
for (auto _region : std::vector<std::string>{
|
||||
region, "wor", "us", "cus", "jp", "eu" }) {
|
||||
if (art)
|
||||
if (results.size()) {
|
||||
// Videos don't have any region attributes, so just take the first entry
|
||||
// (which should be the only entry as well).
|
||||
if (mediaType == "video" || mediaType == "video-normalized") {
|
||||
art = results.first().node();
|
||||
}
|
||||
else {
|
||||
// Region fallback: WOR(LD), US, CUS(TOM?), JP, EU.
|
||||
for (auto _region :
|
||||
std::vector<std::string> { region, "wor", "us", "cus", "jp", "eu" }) {
|
||||
if (art)
|
||||
break;
|
||||
|
||||
for (auto node : results) {
|
||||
if (node.node().attribute("region").value() == _region) {
|
||||
art = node.node();
|
||||
break;
|
||||
|
||||
for (auto node : results) {
|
||||
if (node.node().attribute("region").value() == _region) {
|
||||
art = node.node();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (art) {
|
||||
// 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.
|
||||
fileURL = Utils::String::replace(art.text().get(), " ", "%20");
|
||||
if (art) {
|
||||
// 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.
|
||||
fileURL = Utils::String::replace(art.text().get(), " ", "%20");
|
||||
|
||||
// Get the media type returned by ScreenScraper.
|
||||
std::string media_type = art.attribute("format").value();
|
||||
if (!media_type.empty())
|
||||
fileFormat = "." + media_type;
|
||||
}
|
||||
else {
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processMedia(): "
|
||||
"Failed to find media XML node with name '" << mediaType << "'";
|
||||
}
|
||||
// Get the media type returned by ScreenScraper.
|
||||
std::string media_type = art.attribute("format").value();
|
||||
if (!media_type.empty())
|
||||
fileFormat = "." + media_type;
|
||||
}
|
||||
else {
|
||||
LOG(LogDebug) << "ScreenScraperRequest::processMedia(): "
|
||||
"Failed to find media XML node with name '"
|
||||
<< mediaType << "'";
|
||||
}
|
||||
}
|
||||
|
||||
// Currently not used in this module.
|
||||
void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc,
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
std::vector<ScraperSearchResult>& results)
|
||||
{
|
||||
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 name = game.child("nom").text().get();
|
||||
std::string platformId = game.child("systemeid").text().get();
|
||||
std::string path = ssConfig.getGameSearchUrl(name) + "&systemeid=" +
|
||||
platformId + "&gameid=" + id;
|
||||
std::string path =
|
||||
ssConfig.getGameSearchUrl(name) + "&systemeid=" + platformId + "&gameid=" + id;
|
||||
|
||||
mRequestQueue->push(std::unique_ptr<ScraperRequest>
|
||||
(new ScreenScraperRequest(results, path)));
|
||||
mRequestQueue->push(
|
||||
std::unique_ptr<ScraperRequest>(new ScreenScraperRequest(results, path)));
|
||||
|
||||
game = game.next_sibling("jeu");
|
||||
}
|
||||
}
|
||||
|
||||
std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
|
||||
const std::string gameName) const
|
||||
const std::string gameName) const
|
||||
{
|
||||
std::string screenScraperURL;
|
||||
std::string searchName = gameName;
|
||||
|
@ -546,12 +557,14 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
|
|||
|
||||
// Trim leading and trailing whitespaces.
|
||||
searchName.erase(searchName.begin(),
|
||||
std::find_if(searchName.begin(), searchName.end(), [](char c) {
|
||||
return !std::isspace(static_cast<unsigned char>(c));
|
||||
}));
|
||||
searchName.erase(std::find_if(searchName.rbegin(), searchName.rend(), [](char c) {
|
||||
return !std::isspace(static_cast<unsigned char>(c));
|
||||
}).base(), searchName.end());
|
||||
std::find_if(searchName.begin(), searchName.end(), [](char c) {
|
||||
return !std::isspace(static_cast<unsigned char>(c));
|
||||
}));
|
||||
searchName.erase(
|
||||
std::find_if(searchName.rbegin(), searchName.rend(),
|
||||
[](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
|
||||
// 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.
|
||||
std::string trimTrailingPluses = searchName;
|
||||
trimTrailingPluses.erase(std::find_if(trimTrailingPluses.rbegin(),
|
||||
trimTrailingPluses.rend(), [](char c) {
|
||||
return c != '+';
|
||||
}).base(), trimTrailingPluses.end());
|
||||
trimTrailingPluses.rend(),
|
||||
[](char c) { return c != '+'; })
|
||||
.base(),
|
||||
trimTrailingPluses.end());
|
||||
|
||||
if (trimTrailingPluses.size() < 4)
|
||||
singleSearch = true;
|
||||
|
@ -589,12 +603,12 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
|
|||
// could also lead to an error for short game names.
|
||||
if (!singleSearch) {
|
||||
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.
|
||||
removeThe.erase(removeThe.begin(),
|
||||
std::find_if(removeThe.begin(), removeThe.end(), [](char c) {
|
||||
return !std::isspace(static_cast<unsigned char>(c));
|
||||
}));
|
||||
std::find_if(removeThe.begin(), removeThe.end(), [](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 (removeThe.size() > 4) {
|
||||
if (removeThe.substr(removeThe.size() - 4, 4) == " THE")
|
||||
|
@ -605,20 +619,18 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(
|
|||
}
|
||||
|
||||
if (singleSearch) {
|
||||
screenScraperURL = API_URL_BASE
|
||||
+ "/jeuInfos.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY)
|
||||
+ "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY)
|
||||
+ "&softname=" + HttpReq::urlEncode(API_SOFT_NAME)
|
||||
+ "&output=xml"
|
||||
+ "&romnom=" + HttpReq::urlEncode(searchName);
|
||||
screenScraperURL = API_URL_BASE + "/jeuInfos.php?devid=" +
|
||||
Utils::String::scramble(API_DEV_U, API_DEV_KEY) +
|
||||
"&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) +
|
||||
"&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" +
|
||||
"&romnom=" + HttpReq::urlEncode(searchName);
|
||||
}
|
||||
else {
|
||||
screenScraperURL = API_URL_BASE
|
||||
+ "/jeuRecherche.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY)
|
||||
+ "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY)
|
||||
+ "&softname=" + HttpReq::urlEncode(API_SOFT_NAME)
|
||||
+ "&output=xml"
|
||||
+ "&recherche=" + HttpReq::urlEncode(searchName);
|
||||
screenScraperURL = API_URL_BASE + "/jeuRecherche.php?devid=" +
|
||||
Utils::String::scramble(API_DEV_U, API_DEV_KEY) +
|
||||
"&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) +
|
||||
"&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" +
|
||||
"&recherche=" + HttpReq::urlEncode(searchName);
|
||||
}
|
||||
|
||||
// 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 password = Settings::getInstance()->getString("ScraperPasswordScreenScraper");
|
||||
if (!username.empty() && !password.empty())
|
||||
screenScraperURL += "&ssid=" + HttpReq::urlEncode(username) + "&sspassword=" +
|
||||
HttpReq::urlEncode(password);
|
||||
screenScraperURL += "&ssid=" + HttpReq::urlEncode(username) +
|
||||
"&sspassword=" + HttpReq::urlEncode(password);
|
||||
}
|
||||
|
||||
return screenScraperURL;
|
||||
|
|
|
@ -10,49 +10,54 @@
|
|||
#ifndef ES_APP_SCRAPERS_SCREEN_SCRAPER_H
|
||||
#define ES_APP_SCRAPERS_SCREEN_SCRAPER_H
|
||||
|
||||
#include "scrapers/Scraper.h"
|
||||
#include "EmulationStation.h"
|
||||
#include "scrapers/Scraper.h"
|
||||
|
||||
namespace pugi { class xml_document; }
|
||||
namespace pugi
|
||||
{
|
||||
class xml_document;
|
||||
}
|
||||
|
||||
void screenscraper_generate_scraper_requests(
|
||||
const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
|
||||
std::queue<std::unique_ptr<ScraperRequest>>& requests,
|
||||
std::vector<ScraperSearchResult>& results);
|
||||
|
||||
class ScreenScraperRequest : public ScraperHttpRequest
|
||||
{
|
||||
public:
|
||||
// ctor for a GetGameList request.
|
||||
ScreenScraperRequest(std::queue<std::unique_ptr<ScraperRequest>>& requestsWrite,
|
||||
std::vector<ScraperSearchResult>& resultsWrite,
|
||||
const std::string& url) : ScraperHttpRequest(resultsWrite, url),
|
||||
mRequestQueue(&requestsWrite) {}
|
||||
std::vector<ScraperSearchResult>& resultsWrite,
|
||||
const std::string& url)
|
||||
: ScraperHttpRequest(resultsWrite, url)
|
||||
, mRequestQueue(&requestsWrite)
|
||||
{
|
||||
}
|
||||
|
||||
// ctor for a GetGame request.
|
||||
ScreenScraperRequest(std::vector<ScraperSearchResult>& resultsWrite,
|
||||
const std::string& url) : ScraperHttpRequest(resultsWrite, url),
|
||||
mRequestQueue(nullptr) {}
|
||||
ScreenScraperRequest(std::vector<ScraperSearchResult>& resultsWrite, const std::string& url)
|
||||
: ScraperHttpRequest(resultsWrite, url)
|
||||
, mRequestQueue(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// Settings for the scraper.
|
||||
static const struct ScreenScraperConfig {
|
||||
std::string getGameSearchUrl(const std::string gameName) const;
|
||||
|
||||
// Access to the API.
|
||||
const std::string API_DEV_U =
|
||||
{ 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_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_DEV_U = { 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_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_SOFT_NAME = "EmulationStation-DE " +
|
||||
static_cast<std::string>(PROGRAM_VERSION_STRING);
|
||||
const std::string API_SOFT_NAME =
|
||||
"EmulationStation-DE " + static_cast<std::string>(PROGRAM_VERSION_STRING);
|
||||
|
||||
// Which type of image artwork we need. Possible values (not a comprehensive list):
|
||||
// - ss: in-game screenshot
|
||||
// - box-3D: 3D boxart
|
||||
// - box-2D: 2D boxart (default)
|
||||
// - box-2D: 2D boxart
|
||||
// - screenmarque : marquee
|
||||
// - sstitle: in-game start screenshot
|
||||
// - steamgrid: Steam artwork
|
||||
|
@ -75,27 +80,27 @@ public:
|
|||
|
||||
// Which Region to use when selecting the artwork.
|
||||
// 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.
|
||||
// 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;
|
||||
|
||||
protected:
|
||||
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 processGame(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results);
|
||||
void processMedia(ScraperSearchResult& result,
|
||||
const pugi::xml_node& media_list,
|
||||
std::string mediaType,
|
||||
std::string& fileURL,
|
||||
std::string& fileFormat,
|
||||
std::string region);
|
||||
const pugi::xml_node& media_list,
|
||||
std::string mediaType,
|
||||
std::string& fileURL,
|
||||
std::string& fileFormat,
|
||||
std::string region);
|
||||
bool isGameRequest() { return !mRequestQueue; }
|
||||
|
||||
std::queue<std::unique_ptr<ScraperRequest>>* mRequestQueue;
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
#include "views/SystemView.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "Log.h"
|
||||
#include "Settings.h"
|
||||
#include "Sound.h"
|
||||
#include "SystemData.h"
|
||||
#include "Window.h"
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#if defined(_WIN64)
|
||||
#include <cmath>
|
||||
|
@ -26,14 +26,12 @@
|
|||
const int logoBuffersLeft[] = { -5, -2, -1 };
|
||||
const int logoBuffersRight[] = { 1, 2, 5 };
|
||||
|
||||
SystemView::SystemView(
|
||||
Window* window)
|
||||
: IList<SystemViewData, SystemData*>
|
||||
(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP),
|
||||
mPreviousScrollVelocity(0),
|
||||
mUpdatedGameCount(false),
|
||||
mViewNeedsReload(true),
|
||||
mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER)
|
||||
SystemView::SystemView(Window* window)
|
||||
: IList<SystemViewData, SystemData*>(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP)
|
||||
, mPreviousScrollVelocity(0)
|
||||
, mUpdatedGameCount(false)
|
||||
, mViewNeedsReload(true)
|
||||
, mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER)
|
||||
{
|
||||
mCamOffset = 0;
|
||||
mExtrasCamOffset = 0;
|
||||
|
@ -58,8 +56,8 @@ void SystemView::populate()
|
|||
{
|
||||
mEntries.clear();
|
||||
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
const std::shared_ptr<ThemeData>& theme = (*it)->getTheme();
|
||||
|
||||
if (mViewNeedsReload)
|
||||
|
@ -74,11 +72,11 @@ void SystemView::populate()
|
|||
const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image");
|
||||
if (logoElem) {
|
||||
std::string path = logoElem->get<std::string>("path");
|
||||
std::string defaultPath = logoElem->has("default") ?
|
||||
logoElem->get<std::string>("default") : "";
|
||||
std::string defaultPath =
|
||||
logoElem->has("default") ? logoElem->get<std::string>("default") : "";
|
||||
if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) ||
|
||||
(!defaultPath.empty() &&
|
||||
ResourceManager::getInstance()->fileExists(defaultPath))) {
|
||||
(!defaultPath.empty() &&
|
||||
ResourceManager::getInstance()->fileExists(defaultPath))) {
|
||||
ImageComponent* logo = new ImageComponent(mWindow, false, false);
|
||||
logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale);
|
||||
logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR);
|
||||
|
@ -88,16 +86,14 @@ void SystemView::populate()
|
|||
}
|
||||
if (!e.data.logo) {
|
||||
// No logo in theme; use text.
|
||||
TextComponent* text = new TextComponent(
|
||||
mWindow,
|
||||
(*it)->getName(),
|
||||
Font::get(FONT_SIZE_LARGE),
|
||||
0x000000FF,
|
||||
ALIGN_CENTER);
|
||||
TextComponent* text =
|
||||
new TextComponent(mWindow, (*it)->getName(), Font::get(FONT_SIZE_LARGE),
|
||||
0x000000FF, ALIGN_CENTER);
|
||||
text->setSize(mCarousel.logoSize * mCarousel.logoScale);
|
||||
text->applyTheme((*it)->getTheme(), "system", "logoText",
|
||||
ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR |
|
||||
ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT);
|
||||
ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR |
|
||||
ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING |
|
||||
ThemeFlags::TEXT);
|
||||
e.data.logo = std::shared_ptr<GuiComponent>(text);
|
||||
|
||||
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) {
|
||||
|
@ -134,10 +130,9 @@ void SystemView::populate()
|
|||
e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow);
|
||||
|
||||
// Sort the extras by z-index.
|
||||
std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(),
|
||||
[](GuiComponent* a, GuiComponent* b) {
|
||||
return b->getZIndex() > a->getZIndex();
|
||||
});
|
||||
std::stable_sort(
|
||||
e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(),
|
||||
[](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); });
|
||||
|
||||
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.
|
||||
if (!UIModeController::getInstance()->isUIModeFull()) {
|
||||
Settings::getInstance()->setString("UIMode", "full");
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||
"The selected UI mode has nothing to show,\n returning to UI mode \"Full\"",
|
||||
"OK", nullptr));
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, getHelpStyle(),
|
||||
"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";
|
||||
else if (getSelected()->isCollection() && (getSelected()->getName() == "favorites"))
|
||||
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.
|
||||
else if (getSelected()->isCollection() && (getSelected()->getName() == "recent"))
|
||||
ss << (gameCount.first > 50 ? 50 : gameCount.first) << " GAME" <<
|
||||
(gameCount.first == 1 ? " " : "S");
|
||||
ss << (gameCount.first > 50 ? 50 : gameCount.first) << " GAME"
|
||||
<< (gameCount.first == 1 ? " " : "S");
|
||||
else
|
||||
ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S ") << "(" <<
|
||||
gameCount.second << " FAVORITE" << (gameCount.second == 1 ? ")" : "S)");
|
||||
ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S ") << "("
|
||||
<< gameCount.second << " FAVORITE" << (gameCount.second == 1 ? ")" : "S)");
|
||||
|
||||
mSystemInfo.setText(ss.str());
|
||||
}
|
||||
|
@ -190,40 +186,40 @@ bool SystemView::input(InputConfig* config, Input input)
|
|||
|
||||
if (input.value != 0) {
|
||||
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";
|
||||
ViewController::get()->reloadAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (mCarousel.type) {
|
||||
case VERTICAL:
|
||||
case VERTICAL_WHEEL:
|
||||
if (config->isMappedLike("up", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(-1);
|
||||
return true;
|
||||
}
|
||||
if (config->isMappedLike("down", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case HORIZONTAL:
|
||||
case HORIZONTAL_WHEEL:
|
||||
default:
|
||||
if (config->isMappedLike("left", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(-1);
|
||||
return true;
|
||||
}
|
||||
if (config->isMappedLike("right", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VERTICAL:
|
||||
case VERTICAL_WHEEL:
|
||||
if (config->isMappedLike("up", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(-1);
|
||||
return true;
|
||||
}
|
||||
if (config->isMappedLike("down", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case HORIZONTAL:
|
||||
case HORIZONTAL_WHEEL:
|
||||
default:
|
||||
if (config->isMappedLike("left", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(-1);
|
||||
return true;
|
||||
}
|
||||
if (config->isMappedLike("right", input)) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
listInput(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (config->isMappedTo("a", input)) {
|
||||
|
@ -233,17 +229,16 @@ bool SystemView::input(InputConfig* config, Input input)
|
|||
return true;
|
||||
}
|
||||
if (Settings::getInstance()->getBool("RandomAddButton") &&
|
||||
(config->isMappedTo("leftthumbstickclick", input) ||
|
||||
config->isMappedTo("rightthumbstickclick", input))) {
|
||||
(config->isMappedTo("leftthumbstickclick", input) ||
|
||||
config->isMappedTo("rightthumbstickclick", input))) {
|
||||
// Get a random system and jump to it.
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
|
||||
setCursor(SystemData::getRandomSystem(getSelected()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!UIModeController::getInstance()->isUIModeKid() &&
|
||||
config->isMappedTo("back", input) &&
|
||||
Settings::getInstance()->getBool("ScreensaverControls")) {
|
||||
if (!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("back", input) &&
|
||||
Settings::getInstance()->getBool("ScreensaverControls")) {
|
||||
if (!mWindow->isScreensaverActive()) {
|
||||
ViewController::get()->stopScrolling();
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
|
@ -254,10 +249,8 @@ bool SystemView::input(InputConfig* config, Input input)
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (config->isMappedLike("left", input) ||
|
||||
config->isMappedLike("right", input) ||
|
||||
config->isMappedLike("up", input) ||
|
||||
config->isMappedLike("down", input))
|
||||
if (config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
|
||||
config->isMappedLike("up", input) || config->isMappedLike("down", input))
|
||||
listInput(0);
|
||||
}
|
||||
|
||||
|
@ -302,8 +295,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
|
|||
std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
|
||||
|
||||
// To prevent ugly jumps with two systems when quickly repeating the same direction.
|
||||
if (mPreviousScrollVelocity != 0 && posMax == 2 &&
|
||||
mScrollVelocity == mPreviousScrollVelocity ) {
|
||||
if (mPreviousScrollVelocity != 0 && posMax == 2 && mScrollVelocity == mPreviousScrollVelocity) {
|
||||
if (fabs(endPos - startPos) < 0.5 || fabs(endPos - startPos) > 1.5) {
|
||||
(mScrollVelocity < 0) ? endPos -= 1 : endPos += 1;
|
||||
(mCursor == 0) ? mCursor = 1 : mCursor = 0;
|
||||
|
@ -321,77 +313,84 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
|
|||
if (transition_style == "fade") {
|
||||
float startExtrasFade = mExtrasFadeOpacity;
|
||||
anim = new LambdaAnimation(
|
||||
[this, startExtrasFade, startPos, endPos, posMax](float t) {
|
||||
t -= 1;
|
||||
float f = Math::lerp(startPos, endPos, t*t*t + 1);
|
||||
if (f < 0)
|
||||
f += posMax;
|
||||
if (f >= posMax)
|
||||
f -= posMax;
|
||||
[this, startExtrasFade, startPos, endPos, posMax](float t) {
|
||||
t -= 1;
|
||||
float f = Math::lerp(startPos, endPos, t * t * t + 1);
|
||||
if (f < 0)
|
||||
f += posMax;
|
||||
if (f >= posMax)
|
||||
f -= posMax;
|
||||
|
||||
this->mCamOffset = f;
|
||||
this->mCamOffset = f;
|
||||
|
||||
t += 1;
|
||||
if (t < 0.3f)
|
||||
this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade);
|
||||
else if (t < 0.7f)
|
||||
this->mExtrasFadeOpacity = 1.0f;
|
||||
else
|
||||
this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f);
|
||||
t += 1;
|
||||
if (t < 0.3f)
|
||||
this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade);
|
||||
else if (t < 0.7f)
|
||||
this->mExtrasFadeOpacity = 1.0f;
|
||||
else
|
||||
this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f);
|
||||
|
||||
if (t > 0.5f)
|
||||
this->mExtrasCamOffset = endPos;
|
||||
if (t > 0.5f)
|
||||
this->mExtrasCamOffset = endPos;
|
||||
|
||||
// Update the game count when the entire animation has been completed.
|
||||
if (mExtrasFadeOpacity == 1.0)
|
||||
updateGameCount();
|
||||
}, 500);
|
||||
// Update the game count when the entire animation has been completed.
|
||||
if (mExtrasFadeOpacity == 1.0f)
|
||||
updateGameCount();
|
||||
},
|
||||
500);
|
||||
}
|
||||
else if (transition_style == "slide") {
|
||||
mUpdatedGameCount = false;
|
||||
anim = new LambdaAnimation(
|
||||
[this, startPos, endPos, posMax](float t) {
|
||||
t -= 1;
|
||||
float f = Math::lerp(startPos, endPos, t*t*t + 1);
|
||||
if (f < 0)
|
||||
f += posMax;
|
||||
if (f >= posMax)
|
||||
f -= posMax;
|
||||
[this, startPos, endPos, posMax](float t) {
|
||||
t -= 1;
|
||||
float f = Math::lerp(startPos, endPos, t * t * t + 1);
|
||||
if (f < 0)
|
||||
f += posMax;
|
||||
if (f >= posMax)
|
||||
f -= posMax;
|
||||
|
||||
this->mCamOffset = f;
|
||||
this->mExtrasCamOffset = f;
|
||||
this->mCamOffset = f;
|
||||
this->mExtrasCamOffset = f;
|
||||
|
||||
// Hack to make the game count being updated in the middle of the animation.
|
||||
bool update = false;
|
||||
if (endPos == -1 && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5 && !mUpdatedGameCount)
|
||||
update = true;
|
||||
else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) <
|
||||
0.5 && !mUpdatedGameCount)
|
||||
update = true;
|
||||
else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5 && !mUpdatedGameCount)
|
||||
update = true;
|
||||
// Hack to make the game count being updated in the middle of the animation.
|
||||
bool update = false;
|
||||
if (endPos == -1.0f && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5f &&
|
||||
!mUpdatedGameCount) {
|
||||
update = true;
|
||||
}
|
||||
else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) < 0.5f &&
|
||||
!mUpdatedGameCount) {
|
||||
update = true;
|
||||
}
|
||||
else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5f && !mUpdatedGameCount) {
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
mUpdatedGameCount = true;
|
||||
updateGameCount();
|
||||
}
|
||||
}, 500);
|
||||
if (update) {
|
||||
mUpdatedGameCount = true;
|
||||
updateGameCount();
|
||||
}
|
||||
},
|
||||
500);
|
||||
}
|
||||
else {
|
||||
// Instant.
|
||||
updateGameCount();
|
||||
anim = new LambdaAnimation(
|
||||
[this, startPos, endPos, posMax](float t) {
|
||||
t -= 1;
|
||||
float f = Math::lerp(startPos, endPos, t*t*t + 1);
|
||||
if (f < 0)
|
||||
f += posMax;
|
||||
if (f >= posMax)
|
||||
f -= posMax;
|
||||
[this, startPos, endPos, posMax](float t) {
|
||||
t -= 1;
|
||||
float f = Math::lerp(startPos, endPos, t * t * t + 1);
|
||||
if (f < 0)
|
||||
f += posMax;
|
||||
if (f >= posMax)
|
||||
f -= posMax;
|
||||
|
||||
this->mCamOffset = f;
|
||||
this->mExtrasCamOffset = endPos;
|
||||
}, 500);
|
||||
this->mCamOffset = f;
|
||||
this->mExtrasCamOffset = endPos;
|
||||
},
|
||||
500);
|
||||
}
|
||||
|
||||
setAnimation(anim, 0, nullptr, false, 0);
|
||||
|
@ -400,7 +399,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
|
|||
void SystemView::render(const Transform4x4f& parentTrans)
|
||||
{
|
||||
if (size() == 0)
|
||||
return; // Nothing to render.
|
||||
return; // Nothing to render.
|
||||
|
||||
Transform4x4f trans = getTransform() * parentTrans;
|
||||
|
||||
|
@ -430,7 +429,7 @@ std::vector<HelpPrompt> SystemView::getHelpPrompts()
|
|||
prompts.push_back(HelpPrompt("thumbstickclick", "random"));
|
||||
|
||||
if (!UIModeController::getInstance()->isUIModeKid() &&
|
||||
Settings::getInstance()->getBool("ScreensaverControls"))
|
||||
Settings::getInstance()->getBool("ScreensaverControls"))
|
||||
prompts.push_back(HelpPrompt("back", "toggle screensaver"));
|
||||
|
||||
return prompts;
|
||||
|
@ -443,7 +442,7 @@ HelpStyle SystemView::getHelpStyle()
|
|||
return style;
|
||||
}
|
||||
|
||||
void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
|
||||
void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
|
||||
{
|
||||
LOG(LogDebug) << "SystemView::onThemeChanged()";
|
||||
mViewNeedsReload = true;
|
||||
|
@ -451,7 +450,7 @@ void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
|
|||
}
|
||||
|
||||
// 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()";
|
||||
|
||||
|
@ -460,13 +459,14 @@ void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
|
|||
if (!theme->hasView("system"))
|
||||
return;
|
||||
|
||||
const ThemeData::ThemeElement* carouselElem = theme->
|
||||
getElement("system", "systemcarousel", "carousel");
|
||||
const ThemeData::ThemeElement* carouselElem =
|
||||
theme->getElement("system", "systemcarousel", "carousel");
|
||||
|
||||
if (carouselElem)
|
||||
getCarouselFromTheme(carouselElem);
|
||||
|
||||
const ThemeData::ThemeElement* sysInfoElem = theme->
|
||||
getElement("system", "systemInfo", "text");
|
||||
const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text");
|
||||
|
||||
if (sysInfoElem)
|
||||
mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL);
|
||||
|
||||
|
@ -479,71 +479,79 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
|
|||
// Background box behind logos.
|
||||
Transform4x4f carouselTrans = trans;
|
||||
carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0));
|
||||
carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1,
|
||||
mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f));
|
||||
carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1.0f,
|
||||
mCarousel.origin.y() * mCarousel.size.y() * -1.0f, 0.0f));
|
||||
|
||||
Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y());
|
||||
Renderer::pushClipRect(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::pushClipRect(
|
||||
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::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(),
|
||||
mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal);
|
||||
Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), mCarousel.color,
|
||||
mCarousel.colorEnd, mCarousel.colorGradientHorizontal);
|
||||
|
||||
// Draw logos.
|
||||
// Note: logoSpacing will also include the size of the logo itself.
|
||||
Vector2f logoSpacing(0.0, 0.0);
|
||||
float xOff = 0.0;
|
||||
float yOff = 0.0;
|
||||
Vector2f logoSpacing(0.0f, 0.0f);
|
||||
float xOff = 0.0f;
|
||||
float yOff = 0.0f;
|
||||
|
||||
switch (mCarousel.type) {
|
||||
case VERTICAL_WHEEL:
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f -
|
||||
(mCamOffset * logoSpacing[1]);
|
||||
case VERTICAL_WHEEL: {
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f -
|
||||
(mCamOffset * logoSpacing[1]);
|
||||
if (mCarousel.logoAlignment == ALIGN_LEFT)
|
||||
xOff = mCarousel.logoSize.x() / 10.f;
|
||||
xOff = mCarousel.logoSize.x() / 10.0f;
|
||||
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
|
||||
xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f);
|
||||
else
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f;
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f;
|
||||
break;
|
||||
case VERTICAL:
|
||||
logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() *
|
||||
mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y();
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f -
|
||||
(mCamOffset * logoSpacing[1]);
|
||||
|
||||
}
|
||||
case VERTICAL: {
|
||||
logoSpacing[1] =
|
||||
((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) /
|
||||
(mCarousel.maxLogoCount)) +
|
||||
mCarousel.logoSize.y();
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f -
|
||||
(mCamOffset * logoSpacing[1]);
|
||||
if (mCarousel.logoAlignment == ALIGN_LEFT)
|
||||
xOff = mCarousel.logoSize.x() / 10.f;
|
||||
xOff = mCarousel.logoSize.x() / 10.0f;
|
||||
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
|
||||
xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f);
|
||||
else
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2;
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f;
|
||||
break;
|
||||
case HORIZONTAL_WHEEL:
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 -
|
||||
(mCamOffset * logoSpacing[1]);
|
||||
}
|
||||
case HORIZONTAL_WHEEL: {
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f -
|
||||
(mCamOffset * logoSpacing[1]);
|
||||
if (mCarousel.logoAlignment == ALIGN_TOP)
|
||||
yOff = mCarousel.logoSize.y() / 10;
|
||||
yOff = mCarousel.logoSize.y() / 10.0f;
|
||||
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
|
||||
yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f);
|
||||
else
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2;
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f;
|
||||
break;
|
||||
case HORIZONTAL:
|
||||
default:
|
||||
logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() *
|
||||
mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x();
|
||||
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f -
|
||||
(mCamOffset * logoSpacing[0]);
|
||||
|
||||
}
|
||||
case HORIZONTAL: {
|
||||
}
|
||||
default: {
|
||||
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)
|
||||
yOff = mCarousel.logoSize.y() / 10.f;
|
||||
yOff = mCarousel.logoSize.y() / 10.0f;
|
||||
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
|
||||
yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f);
|
||||
else
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f;
|
||||
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int center = static_cast<int>(mCamOffset);
|
||||
|
@ -558,9 +566,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
|
|||
bufferRight = 0;
|
||||
}
|
||||
|
||||
for (int i = center - logoCount / 2 + bufferLeft;
|
||||
i <= center + logoCount / 2 + bufferRight; i++) {
|
||||
for (int i = center - logoCount / 2 + bufferLeft; // Line break.
|
||||
i <= center + logoCount / 2 + bufferRight; i++) {
|
||||
int index = i;
|
||||
|
||||
while (index < 0)
|
||||
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 /= mCarousel.logoScale;
|
||||
|
||||
int opacity = static_cast<int>(std::round(0x80 + ((0xFF - 0x80) *
|
||||
(1.0f - fabs(distance)))));
|
||||
int opacity =
|
||||
static_cast<int>(std::round(0x80 + ((0xFF - 0x80) * (1.0f - fabs(distance)))));
|
||||
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) {
|
||||
comp->setRotationDegrees(mCarousel.logoRotation * distance);
|
||||
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.
|
||||
int bufferIndex = getScrollingVelocity() + 1;
|
||||
|
||||
Renderer::pushClipRect(Vector2i::Zero(), Vector2i(static_cast<int>(mSize.x()),
|
||||
static_cast<int>(mSize.y())));
|
||||
Renderer::pushClipRect(Vector2i::Zero(),
|
||||
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
|
||||
|
||||
for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter +
|
||||
logoBuffersRight[bufferIndex]; i++) {
|
||||
for (int i = extrasCenter + logoBuffersLeft[bufferIndex];
|
||||
i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) {
|
||||
int index = i;
|
||||
while (index < 0)
|
||||
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());
|
||||
|
||||
// Only render selected system when not showing.
|
||||
if (mShowing || index == mCursor)
|
||||
{
|
||||
if (mShowing || index == mCursor) {
|
||||
Transform4x4f extrasTrans = trans;
|
||||
if (mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL)
|
||||
extrasTrans.translate(Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0));
|
||||
else
|
||||
extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0));
|
||||
|
||||
Renderer::pushClipRect(Vector2i(static_cast<int>(extrasTrans.translation()[0]),
|
||||
static_cast<int>(extrasTrans.translation()[1])),
|
||||
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
|
||||
Renderer::pushClipRect(
|
||||
Vector2i(static_cast<int>(extrasTrans.translation()[0]),
|
||||
static_cast<int>(extrasTrans.translation()[1])),
|
||||
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
|
||||
SystemViewData data = mEntries.at(index).data;
|
||||
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)
|
||||
extra->render(extrasTrans);
|
||||
}
|
||||
|
@ -637,13 +646,13 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
|
|||
|
||||
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::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), fadeColor, fadeColor);
|
||||
}
|
||||
|
||||
// Populate the system carousel with the legacy values.
|
||||
void SystemView::getDefaultElements(void)
|
||||
void SystemView::getDefaultElements(void)
|
||||
{
|
||||
// Carousel.
|
||||
mCarousel.type = HORIZONTAL;
|
||||
|
@ -658,13 +667,13 @@ void SystemView::getDefaultElements(void)
|
|||
mCarousel.colorEnd = 0xFFFFFFD8;
|
||||
mCarousel.colorGradientHorizontal = true;
|
||||
mCarousel.logoScale = 1.2f;
|
||||
mCarousel.logoRotation = 7.5;
|
||||
mCarousel.logoRotationOrigin.x() = -5;
|
||||
mCarousel.logoRotationOrigin.y() = 0.5;
|
||||
mCarousel.logoRotation = 7.5f;
|
||||
mCarousel.logoRotationOrigin.x() = -5.0f;
|
||||
mCarousel.logoRotationOrigin.y() = 0.5f;
|
||||
mCarousel.logoSize.x() = 0.25f * mSize.x();
|
||||
mCarousel.logoSize.y() = 0.155f * mSize.y();
|
||||
mCarousel.maxLogoCount = 3;
|
||||
mCarousel.zIndex = 40;
|
||||
mCarousel.zIndex = 40.0f;
|
||||
|
||||
// System info bar.
|
||||
mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f);
|
||||
|
@ -702,7 +711,8 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
|
|||
if (elem->has("colorEnd"))
|
||||
mCarousel.colorEnd = elem->get<unsigned int>("colorEnd");
|
||||
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"))
|
||||
mCarousel.logoScale = elem->get<float>("logoScale");
|
||||
if (elem->has("logoSize"))
|
||||
|
@ -727,14 +737,4 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
|
|||
else
|
||||
mCarousel.logoAlignment = ALIGN_CENTER;
|
||||
}
|
||||
}
|
||||
|
||||
void SystemView::onShow()
|
||||
{
|
||||
mShowing = true;
|
||||
}
|
||||
|
||||
void SystemView::onHide()
|
||||
{
|
||||
mShowing = false;
|
||||
}
|
||||
}
|
|
@ -9,11 +9,11 @@
|
|||
#ifndef 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/TextComponent.h"
|
||||
#include "resources/Font.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "Sound.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -55,8 +55,8 @@ public:
|
|||
SystemView(Window* window);
|
||||
~SystemView();
|
||||
|
||||
virtual void onShow() override;
|
||||
virtual void onHide() override;
|
||||
virtual void onShow() override { mShowing = true; }
|
||||
virtual void onHide() override { mShowing = false; }
|
||||
|
||||
void goToSystem(SystemData* system, bool animate);
|
||||
|
||||
|
@ -69,12 +69,14 @@ public:
|
|||
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
virtual HelpStyle getHelpStyle() override;
|
||||
|
||||
CarouselType getCarouselType() { return mCarousel.type; };
|
||||
CarouselType getCarouselType() { return mCarousel.type; }
|
||||
|
||||
protected:
|
||||
void onCursorChanged(const CursorState& state) override;
|
||||
virtual void onScroll() override {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND); }
|
||||
virtual void onScroll() override
|
||||
{
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
|
||||
}
|
||||
|
||||
private:
|
||||
void populate();
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
|
||||
#include "UIModeController.h"
|
||||
|
||||
#include "utils/StringUtil.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "FileFilterIndex.h"
|
||||
#include "Log.h"
|
||||
#include "SystemData.h"
|
||||
#include "Window.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
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");
|
||||
mCurrentUIMode = Settings::getInstance()->getString("UIMode");
|
||||
|
@ -47,13 +48,12 @@ void UIModeController::monitorUIMode()
|
|||
if (uimode != mCurrentUIMode && !ViewController::get()->isCameraMoving()) {
|
||||
mCurrentUIMode = uimode;
|
||||
// Reset filters and sort gamelists (which will update the game counter).
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it !=
|
||||
SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
(*it)->sortSystem(true);
|
||||
(*it)->getIndex()->resetFilters();
|
||||
if ((*it)->getThemeFolder() == "custom-collections") {
|
||||
for (FileData* customSystem :
|
||||
(*it)->getRootFolder()->getChildrenListToDisplay())
|
||||
for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay())
|
||||
customSystem->getSystem()->getIndex()->resetFilters();
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ bool UIModeController::listen(InputConfig* config, Input input)
|
|||
unlockUIMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -101,21 +102,21 @@ void UIModeController::unlockUIMode()
|
|||
|
||||
bool UIModeController::isUIModeFull()
|
||||
{
|
||||
return ((mCurrentUIMode == "full" || (isUIModeKid() &&
|
||||
Settings::getInstance()->getBool("EnableMenuKidMode")))
|
||||
&& !Settings::getInstance()->getBool("ForceKiosk"));
|
||||
return ((mCurrentUIMode == "full" ||
|
||||
(isUIModeKid() && Settings::getInstance()->getBool("EnableMenuKidMode"))) &&
|
||||
!Settings::getInstance()->getBool("ForceKiosk"));
|
||||
}
|
||||
|
||||
bool UIModeController::isUIModeKid()
|
||||
{
|
||||
return (Settings::getInstance()->getBool("ForceKid") ||
|
||||
((mCurrentUIMode == "kid") && !Settings::getInstance()->getBool("ForceKiosk")));
|
||||
((mCurrentUIMode == "kid") && !Settings::getInstance()->getBool("ForceKiosk")));
|
||||
}
|
||||
|
||||
bool UIModeController::isUIModeKiosk()
|
||||
{
|
||||
return (Settings::getInstance()->getBool("ForceKiosk") ||
|
||||
((mCurrentUIMode == "kiosk") && !Settings::getInstance()->getBool("ForceKid")));
|
||||
((mCurrentUIMode == "kiosk") && !Settings::getInstance()->getBool("ForceKid")));
|
||||
}
|
||||
|
||||
std::string UIModeController::getFormattedPassKeyStr()
|
||||
|
@ -124,7 +125,7 @@ std::string UIModeController::getFormattedPassKeyStr()
|
|||
|
||||
std::string out = "";
|
||||
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 symbolA;
|
||||
|
@ -139,19 +140,19 @@ std::string UIModeController::getFormattedPassKeyStr()
|
|||
symbolY = "X";
|
||||
}
|
||||
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
|
||||
// they are supposed to depict.
|
||||
#if defined(_MSC_VER) // MSVC compiler.
|
||||
symbolA = Utils::String::wideStringToString(L"\uF00D"); // Cross.
|
||||
symbolB = Utils::String::wideStringToString(L"\uF111"); // Circle
|
||||
symbolX = Utils::String::wideStringToString(L"\uF04D"); // Square.
|
||||
symbolY = Utils::String::wideStringToString(L"\uF0D8"); // Triangle.
|
||||
#else
|
||||
#else
|
||||
symbolA = "\uF00D"; // Cross.
|
||||
symbolB = "\uF111"; // Circle
|
||||
symbolX = "\uF04D"; // Square.
|
||||
symbolY = "\uF0D8"; // Triangle.
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// Xbox controller.
|
||||
|
@ -193,8 +194,8 @@ std::string UIModeController::getFormattedPassKeyStr()
|
|||
|
||||
bool UIModeController::isValidInput(InputConfig* config, Input input)
|
||||
{
|
||||
if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore it.
|
||||
(!input.value)) // Not a key-down event.
|
||||
if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore it.
|
||||
(!input.value)) // Not a key-down event.
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
bool isUIModeKid();
|
||||
bool isUIModeKiosk();
|
||||
|
||||
void setCurrentUIMode(const std::string& mode) { mCurrentUIMode = mode; };
|
||||
void setCurrentUIMode(const std::string& mode) { mCurrentUIMode = mode; }
|
||||
|
||||
private:
|
||||
UIModeController();
|
||||
|
@ -58,8 +58,9 @@ private:
|
|||
int mPassKeyCounter;
|
||||
|
||||
// These are Xbox button names, so they may be different in pracise on non-Xbox controllers.
|
||||
const std::vector<std::string> mInputVals =
|
||||
{ "up", "down", "left", "right", "a", "b", "x", "y" };
|
||||
const std::vector<std::string> mInputVals = {
|
||||
"up", "down", "left", "right", "a", "b", "x", "y"
|
||||
};
|
||||
};
|
||||
|
||||
#endif // ES_APP_VIEWS_UI_MODE_CONTROLLER_H
|
||||
|
|
|
@ -12,17 +12,6 @@
|
|||
|
||||
#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 "FileFilterIndex.h"
|
||||
#include "InputManager.h"
|
||||
|
@ -32,8 +21,20 @@
|
|||
#include "SystemData.h"
|
||||
#include "SystemView.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;
|
||||
|
||||
#if defined(_MSC_VER) // MSVC compiler.
|
||||
const std::string ViewController::FAVORITE_CHAR = Utils::String::wideStringToString(L"\uF005");
|
||||
const std::string ViewController::FOLDER_CHAR = Utils::String::wideStringToString(L"\uF07C");
|
||||
|
@ -60,21 +61,20 @@ void ViewController::init(Window* window)
|
|||
sInstance = new ViewController(window);
|
||||
}
|
||||
|
||||
ViewController::ViewController(
|
||||
Window* window)
|
||||
: GuiComponent(window),
|
||||
mCurrentView(nullptr),
|
||||
mPreviousView(nullptr),
|
||||
mSkipView(nullptr),
|
||||
mCamera(Transform4x4f::Identity()),
|
||||
mSystemViewTransition(false),
|
||||
mWrappedViews(false),
|
||||
mFadeOpacity(0),
|
||||
mCancelledTransition(false),
|
||||
mLockInput(false),
|
||||
mNextSystem(false),
|
||||
mGameToLaunch(nullptr),
|
||||
mNoGamesMessageBox(nullptr)
|
||||
ViewController::ViewController(Window* window)
|
||||
: GuiComponent(window)
|
||||
, mCurrentView(nullptr)
|
||||
, mPreviousView(nullptr)
|
||||
, mSkipView(nullptr)
|
||||
, mCamera(Transform4x4f::Identity())
|
||||
, mSystemViewTransition(false)
|
||||
, mWrappedViews(false)
|
||||
, mFadeOpacity(0)
|
||||
, mCancelledTransition(false)
|
||||
, mLockInput(false)
|
||||
, mNextSystem(false)
|
||||
, mGameToLaunch(nullptr)
|
||||
, mNoGamesMessageBox(nullptr)
|
||||
{
|
||||
mState.viewing = NOTHING;
|
||||
mState.viewstyle = AUTOMATIC;
|
||||
|
@ -89,108 +89,106 @@ ViewController::~ViewController()
|
|||
|
||||
void ViewController::invalidSystemsFileDialog()
|
||||
{
|
||||
std::string errorMessage =
|
||||
"COULDN'T PARSE THE SYSTEMS CONFIGURATION FILE.\n"
|
||||
"IF YOU HAVE A CUSTOMIZED es_systems.xml FILE, THEN\n"
|
||||
"SOMETHING IS LIKELY WRONG WITH YOUR XML SYNTAX.\n"
|
||||
"IF YOU DON'T HAVE A CUSTOM SYSTEMS FILE, THEN THE\n"
|
||||
"EMULATIONSTATION INSTALLATION IS BROKEN. SEE THE\n"
|
||||
"APPLICATION LOG FILE es_log.txt FOR ADDITIONAL INFO.";
|
||||
std::string errorMessage = "COULDN'T PARSE THE SYSTEMS CONFIGURATION FILE.\n"
|
||||
"IF YOU HAVE A CUSTOMIZED es_systems.xml FILE, THEN\n"
|
||||
"SOMETHING IS LIKELY WRONG WITH YOUR XML SYNTAX.\n"
|
||||
"IF YOU DON'T HAVE A CUSTOM SYSTEMS FILE, THEN THE\n"
|
||||
"EMULATIONSTATION INSTALLATION IS BROKEN. SEE THE\n"
|
||||
"APPLICATION LOG FILE es_log.txt FOR ADDITIONAL INFO.";
|
||||
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
errorMessage.c_str(),
|
||||
"QUIT", [] {
|
||||
SDL_Event quit;
|
||||
quit.type = SDL_QUIT;
|
||||
SDL_PushEvent(&quit);
|
||||
}, "", nullptr, "", nullptr, true));
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, HelpStyle(), errorMessage.c_str(), "QUIT",
|
||||
[] {
|
||||
SDL_Event quit;
|
||||
quit.type = SDL_QUIT;
|
||||
SDL_PushEvent(&quit);
|
||||
},
|
||||
"", nullptr, "", nullptr, true));
|
||||
}
|
||||
|
||||
void ViewController::noGamesDialog()
|
||||
{
|
||||
mNoGamesErrorMessage =
|
||||
"NO GAME FILES WERE FOUND. EITHER PLACE YOUR GAMES IN\n"
|
||||
"THE CURRENTLY CONFIGURED ROM DIRECTORY OR CHANGE\n"
|
||||
"ITS PATH USING THE BUTTON BELOW. OPTIONALLY THE ROM\n"
|
||||
"DIRECTORY STRUCTURE CAN BE GENERATED WHICH WILL\n"
|
||||
"CREATE A TEXT FILE FOR EACH SYSTEM PROVIDING SOME\n"
|
||||
"INFORMATION SUCH AS THE SUPPORTED FILE EXTENSIONS.\n"
|
||||
"THIS IS THE CURRENTLY CONFIGURED ROM DIRECTORY:\n";
|
||||
mNoGamesErrorMessage = "NO GAME FILES WERE FOUND. EITHER PLACE YOUR GAMES IN\n"
|
||||
"THE CURRENTLY CONFIGURED ROM DIRECTORY OR CHANGE\n"
|
||||
"ITS PATH USING THE BUTTON BELOW. OPTIONALLY THE ROM\n"
|
||||
"DIRECTORY STRUCTURE CAN BE GENERATED WHICH WILL\n"
|
||||
"CREATE A TEXT FILE FOR EACH SYSTEM PROVIDING SOME\n"
|
||||
"INFORMATION SUCH AS THE SUPPORTED FILE EXTENSIONS.\n"
|
||||
"THIS IS THE CURRENTLY CONFIGURED ROM DIRECTORY:\n";
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
|
||||
#else
|
||||
#else
|
||||
mRomDirectory = FileData::getROMDirectory();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mNoGamesMessageBox = new GuiMsgBox(mWindow, HelpStyle(), mNoGamesErrorMessage + mRomDirectory,
|
||||
"CHANGE ROM DIRECTORY", [this] {
|
||||
std::string currentROMDirectory;
|
||||
#if defined(_WIN64)
|
||||
currentROMDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
|
||||
#else
|
||||
currentROMDirectory = FileData::getROMDirectory();
|
||||
#endif
|
||||
mNoGamesMessageBox = new GuiMsgBox(
|
||||
mWindow, HelpStyle(), mNoGamesErrorMessage + mRomDirectory, "CHANGE ROM DIRECTORY",
|
||||
[this] {
|
||||
std::string currentROMDirectory;
|
||||
#if defined(_WIN64)
|
||||
currentROMDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
|
||||
#else
|
||||
currentROMDirectory = FileData::getROMDirectory();
|
||||
#endif
|
||||
|
||||
mWindow->pushGui(new GuiComplexTextEditPopup(
|
||||
mWindow,
|
||||
HelpStyle(),
|
||||
"ENTER ROM DIRECTORY PATH",
|
||||
"Currently configured path:",
|
||||
currentROMDirectory,
|
||||
currentROMDirectory,
|
||||
mWindow->pushGui(new GuiComplexTextEditPopup(
|
||||
mWindow, HelpStyle(), "ENTER ROM DIRECTORY PATH",
|
||||
"Currently configured path:", currentROMDirectory, currentROMDirectory,
|
||||
[this](const std::string& newROMDirectory) {
|
||||
Settings::getInstance()->setString("ROMDirectory", newROMDirectory);
|
||||
Settings::getInstance()->saveFile();
|
||||
#if defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
|
||||
#else
|
||||
#else
|
||||
mRomDirectory = FileData::getROMDirectory();
|
||||
#endif
|
||||
#endif
|
||||
mNoGamesMessageBox->changeText(mNoGamesErrorMessage + mRomDirectory);
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
"ROM DIRECTORY SETTING SAVED, RESTART\n"
|
||||
"THE APPLICATION TO RESCAN THE SYSTEMS",
|
||||
"OK", nullptr, "", nullptr, "", nullptr, true));
|
||||
"ROM DIRECTORY SETTING SAVED, RESTART\n"
|
||||
"THE APPLICATION TO RESCAN THE SYSTEMS",
|
||||
"OK", nullptr, "", nullptr, "", nullptr, true));
|
||||
},
|
||||
false,
|
||||
"SAVE",
|
||||
"SAVE CHANGES?",
|
||||
"LOAD CURRENT",
|
||||
"LOAD CURRENTLY CONFIGURED VALUE",
|
||||
"CLEAR",
|
||||
"CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)",
|
||||
false));
|
||||
},
|
||||
"CREATE DIRECTORIES", [this] {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
false, "SAVE", "SAVE CHANGES?", "LOAD CURRENT", "LOAD CURRENTLY CONFIGURED VALUE",
|
||||
"CLEAR", "CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)", false));
|
||||
},
|
||||
"CREATE DIRECTORIES",
|
||||
[this] {
|
||||
mWindow->pushGui(new GuiMsgBox(
|
||||
mWindow, HelpStyle(),
|
||||
"THIS WILL CREATE DIRECTORIES FOR ALL THE\n"
|
||||
"GAME SYSTEMS DEFINED IN es_systems.xml\n\n"
|
||||
"THIS MAY CREATE A LOT OF FOLDERS SO IT'S\n"
|
||||
"ADVICED TO REMOVE THE ONES YOU DON'T NEED\n\n"
|
||||
"PROCEED?",
|
||||
"YES", [this] {
|
||||
if (!SystemData::createSystemDirectories()) {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
"THE SYSTEM DIRECTORIES WERE SUCCESSFULLY\n"
|
||||
"GENERATED, EXIT THE APPLICATION AND PLACE\n"
|
||||
"YOUR GAMES IN THE NEWLY CREATED FOLDERS", "OK", nullptr,
|
||||
"", nullptr, "", nullptr, true));
|
||||
}
|
||||
else {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
"ERROR CREATING THE SYSTEM DIRECTORIES,\n"
|
||||
"PERMISSION PROBLEMS OR DISK FULL?\n\n"
|
||||
"SEE THE LOG FILE FOR MORE DETAILS", "OK", nullptr,
|
||||
"", nullptr, "", nullptr, true));
|
||||
}
|
||||
}, "NO", nullptr, "", nullptr, true));
|
||||
},
|
||||
"QUIT", [] {
|
||||
SDL_Event quit;
|
||||
quit.type = SDL_QUIT;
|
||||
SDL_PushEvent(&quit);
|
||||
}, true, false);
|
||||
"YES",
|
||||
[this] {
|
||||
if (!SystemData::createSystemDirectories()) {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
"THE SYSTEM DIRECTORIES WERE SUCCESSFULLY\n"
|
||||
"GENERATED, EXIT THE APPLICATION AND PLACE\n"
|
||||
"YOUR GAMES IN THE NEWLY CREATED FOLDERS",
|
||||
"OK", nullptr, "", nullptr, "", nullptr,
|
||||
true));
|
||||
}
|
||||
else {
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
|
||||
"ERROR CREATING THE SYSTEM DIRECTORIES,\n"
|
||||
"PERMISSION PROBLEMS OR DISK FULL?\n\n"
|
||||
"SEE THE LOG FILE FOR MORE DETAILS",
|
||||
"OK", nullptr, "", nullptr, "", nullptr,
|
||||
true));
|
||||
}
|
||||
},
|
||||
"NO", nullptr, "", nullptr, true));
|
||||
},
|
||||
"QUIT",
|
||||
[] {
|
||||
SDL_Event quit;
|
||||
quit.type = SDL_QUIT;
|
||||
SDL_PushEvent(&quit);
|
||||
},
|
||||
true, false);
|
||||
|
||||
mWindow->pushGui(mNoGamesMessageBox);
|
||||
}
|
||||
|
@ -205,8 +203,8 @@ void ViewController::goToStart()
|
|||
// If a specific system is requested, go directly to its game list.
|
||||
auto requestedSystem = Settings::getInstance()->getString("StartupSystem");
|
||||
if ("" != requestedSystem && "retropie" != requestedSystem) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
if ((*it)->getName() == requestedSystem) {
|
||||
goToGameList(*it);
|
||||
return;
|
||||
|
@ -237,7 +235,7 @@ bool ViewController::isCameraMoving()
|
|||
{
|
||||
if (mCurrentView) {
|
||||
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 false;
|
||||
|
@ -322,7 +320,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
|
|||
|
||||
auto systemList = getSystemListView();
|
||||
systemList->setPosition(getSystemId(system) * static_cast<float>(Renderer::getScreenWidth()),
|
||||
systemList->getPosition().y());
|
||||
systemList->getPosition().y());
|
||||
|
||||
systemList->goToSystem(system, false);
|
||||
mCurrentView = systemList;
|
||||
|
@ -333,7 +331,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
|
|||
mCamera.translation() = -mCurrentView->getPosition();
|
||||
if (Settings::getInstance()->getString("TransitionStyle") == "slide") {
|
||||
if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL ||
|
||||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
|
||||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
|
||||
mCamera.translation().y() += Renderer::getScreenHeight();
|
||||
else
|
||||
mCamera.translation().x() -= Renderer::getScreenWidth();
|
||||
|
@ -341,7 +339,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
|
|||
}
|
||||
else if (Settings::getInstance()->getString("TransitionStyle") == "fade") {
|
||||
if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL ||
|
||||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
|
||||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
|
||||
mCamera.translation().y() += Renderer::getScreenHeight();
|
||||
else
|
||||
mCamera.translation().x() += Renderer::getScreenWidth();
|
||||
|
@ -396,8 +394,9 @@ void ViewController::goToGameList(SystemData* system)
|
|||
restoreViewPosition();
|
||||
|
||||
if (mPreviousView && Settings::getInstance()->getString("TransitionStyle") == "fade" &&
|
||||
isAnimationPlaying(0))
|
||||
isAnimationPlaying(0)) {
|
||||
mPreviousView->onHide();
|
||||
}
|
||||
|
||||
if (mPreviousView) {
|
||||
mSkipView = mPreviousView;
|
||||
|
@ -449,7 +448,7 @@ void ViewController::goToGameList(SystemData* system)
|
|||
int sysId = getSystemId(system);
|
||||
|
||||
sysList->setPosition(sysId * static_cast<float>(Renderer::getScreenWidth()),
|
||||
sysList->getPosition().y());
|
||||
sysList->getPosition().y());
|
||||
offsetX = sysList->getPosition().x() - offsetX;
|
||||
mCamera.translation().x() -= offsetX;
|
||||
}
|
||||
|
@ -464,7 +463,7 @@ void ViewController::goToGameList(SystemData* system)
|
|||
float offsetX = getGameListView(system)->getPosition().x();
|
||||
// This is needed to move the camera in the correct direction if there are only two systems.
|
||||
if (SystemData::sSystemVector.size() == 2 && mNextSystem)
|
||||
offsetX -= Renderer::getScreenWidth();
|
||||
offsetX -= Renderer::getScreenWidth();
|
||||
else
|
||||
offsetX += Renderer::getScreenWidth();
|
||||
currentPosition.x() = offsetX;
|
||||
|
@ -541,11 +540,13 @@ void ViewController::playViewTransition(bool instant)
|
|||
std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
|
||||
|
||||
if (instant || transition_style == "instant") {
|
||||
setAnimation(new LambdaAnimation([this, target](float /*t*/) {
|
||||
this->mCamera.translation() = -target;
|
||||
if (mPreviousView)
|
||||
mPreviousView->onHide();
|
||||
}, 1));
|
||||
setAnimation(new LambdaAnimation(
|
||||
[this, target](float /*t*/) {
|
||||
this->mCamera.translation() = -target;
|
||||
if (mPreviousView)
|
||||
mPreviousView->onHide();
|
||||
},
|
||||
1));
|
||||
updateHelpPrompts();
|
||||
}
|
||||
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
|
||||
// finishedCallback is calling this function.
|
||||
if (!mCancelledTransition)
|
||||
mFadeOpacity = Math::lerp(0, 1, t);
|
||||
mFadeOpacity = Math::lerp(0.0f, 1.0f, t);
|
||||
};
|
||||
|
||||
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_WAIT = 200; // Time to wait between in/out.
|
||||
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0,
|
||||
[this, fadeFunc, fadeCallback, target] {
|
||||
this->mCamera.translation() = -target;
|
||||
updateHelpPrompts();
|
||||
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION),
|
||||
FADE_WAIT, fadeCallback, true);
|
||||
});
|
||||
[this, fadeFunc, fadeCallback, target] {
|
||||
this->mCamera.translation() = -target;
|
||||
updateHelpPrompts();
|
||||
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT,
|
||||
fadeCallback, true);
|
||||
});
|
||||
|
||||
// Fast-forward animation if we're partway faded.
|
||||
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
|
||||
// shut down before it would resume. I.e. it would not be enough to just stop the game.
|
||||
if (system->hasPlatformId(PlatformIds::VALVE_STEAM) ||
|
||||
Settings::getInstance()->getBool("RunInBackground"))
|
||||
Settings::getInstance()->getBool("RunInBackground"))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
@ -646,8 +647,9 @@ void ViewController::launch(FileData* game)
|
|||
if (durationString == "disabled") {
|
||||
// If the game launch screen has been set as disabled, show a simple info popup
|
||||
// notification instead.
|
||||
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "LAUNCHING GAME '" +
|
||||
Utils::String::toUpper(game->metadata.get("name") + "'"), 10000);
|
||||
GuiInfoPopup* s = new GuiInfoPopup(
|
||||
mWindow, "LAUNCHING GAME '" + Utils::String::toUpper(game->metadata.get("name") + "'"),
|
||||
10000);
|
||||
mWindow->setInfoPopup(s);
|
||||
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
|
||||
// to be displayed briefly, and for the navigation sound playing to be able to complete.
|
||||
// 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);
|
||||
// If the launch screen is disabled then this will do nothing.
|
||||
mWindow->closeLaunchScreen();
|
||||
onFileChanged(game, true);
|
||||
// This is a workaround so that any keys or button presses used for exiting the emulator
|
||||
// are not captured upon returning.
|
||||
setAnimation(new LambdaAnimation([](float t){}, 1), 0, [this] {
|
||||
mLockInput = false;
|
||||
});
|
||||
setAnimation(new LambdaAnimation([](float t) {}, 1), 0, [this] { mLockInput = false; });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -733,38 +733,41 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
|
|||
}
|
||||
|
||||
// Create the view.
|
||||
switch (selectedViewStyle)
|
||||
{
|
||||
case VIDEO:
|
||||
switch (selectedViewStyle) {
|
||||
case VIDEO: {
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new VideoGameListView(mWindow, system->getRootFolder()));
|
||||
new VideoGameListView(mWindow, system->getRootFolder()));
|
||||
mState.viewstyle = VIDEO;
|
||||
break;
|
||||
case DETAILED:
|
||||
}
|
||||
case DETAILED: {
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new DetailedGameListView(mWindow, system->getRootFolder()));
|
||||
new DetailedGameListView(mWindow, system->getRootFolder()));
|
||||
mState.viewstyle = DETAILED;
|
||||
break;
|
||||
case GRID:
|
||||
}
|
||||
case GRID: {
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new GridGameListView(mWindow, system->getRootFolder()));
|
||||
new GridGameListView(mWindow, system->getRootFolder()));
|
||||
mState.viewstyle = GRID;
|
||||
break;
|
||||
case BASIC:
|
||||
default:
|
||||
}
|
||||
case BASIC: {
|
||||
}
|
||||
default: {
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new BasicGameListView(mWindow, system->getRootFolder()));
|
||||
new BasicGameListView(mWindow, system->getRootFolder()));
|
||||
mState.viewstyle = BASIC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
view->setTheme(system->getTheme());
|
||||
|
||||
std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
|
||||
int id = static_cast<int>(
|
||||
std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
|
||||
int id = static_cast<int>(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
|
||||
view->setPosition(id * static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight() * 2));
|
||||
static_cast<float>(Renderer::getScreenHeight() * 2));
|
||||
|
||||
addChild(view.get());
|
||||
|
||||
|
@ -799,8 +802,8 @@ bool ViewController::input(InputConfig* config, Input input)
|
|||
|
||||
// Open the main menu.
|
||||
if (!(UIModeController::getInstance()->isUIModeKid() &&
|
||||
!Settings::getInstance()->getBool("EnableMenuKidMode")) &&
|
||||
config->isMappedTo("start", input) && input.value != 0) {
|
||||
!Settings::getInstance()->getBool("EnableMenuKidMode")) &&
|
||||
config->isMappedTo("start", input) && input.value != 0) {
|
||||
// If we don't stop the scrolling here, it will continue to
|
||||
// run after closing the menu.
|
||||
if (mSystemListView->isScrolling())
|
||||
|
@ -854,7 +857,7 @@ void ViewController::render(const Transform4x4f& parentTrans)
|
|||
// Camera position, position + size.
|
||||
Vector3f viewStart = transInverse.translation();
|
||||
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.
|
||||
UIModeController::getInstance()->monitorUIMode();
|
||||
|
@ -870,11 +873,11 @@ void ViewController::render(const Transform4x4f& parentTrans)
|
|||
if (it->second == mCurrentView || (it->second == mPreviousView && isCameraMoving())) {
|
||||
// Clipping.
|
||||
Vector3f guiStart = it->second->getPosition();
|
||||
Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(),
|
||||
it->second->getSize().y(), 0);
|
||||
Vector3f guiEnd = it->second->getPosition() +
|
||||
Vector3f(it->second->getSize().x(), it->second->getSize().y(), 0);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -887,7 +890,7 @@ void ViewController::render(const Transform4x4f& parentTrans)
|
|||
unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mFadeOpacity * 255);
|
||||
Renderer::setMatrix(parentTrans);
|
||||
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());
|
||||
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it ++) {
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend();
|
||||
it++) {
|
||||
if (Settings::getInstance()->getBool("SplashScreen") &&
|
||||
Settings::getInstance()->getBool("SplashScreenProgress")) {
|
||||
mWindow->renderLoadingScreen("Loading '" + (*it)->getFullName() + "' (" +
|
||||
std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it)+1) +
|
||||
"/" + std::to_string(systemCount) + ")");
|
||||
Settings::getInstance()->getBool("SplashScreenProgress")) {
|
||||
mWindow->renderLoadingScreen(
|
||||
"Loading '" + (*it)->getFullName() + "' (" +
|
||||
std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it) + 1) + "/" +
|
||||
std::to_string(systemCount) + ")");
|
||||
}
|
||||
(*it)->getIndex()->resetFilters();
|
||||
getGameListView(*it);
|
||||
|
@ -1028,7 +1032,7 @@ std::vector<HelpPrompt> ViewController::getHelpPrompts()
|
|||
|
||||
prompts = mCurrentView->getHelpPrompts();
|
||||
if (!(UIModeController::getInstance()->isUIModeKid() &&
|
||||
!Settings::getInstance()->getBool("EnableMenuKidMode")))
|
||||
!Settings::getInstance()->getBool("EnableMenuKidMode")))
|
||||
prompts.push_back(HelpPrompt("start", "menu"));
|
||||
return prompts;
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
#ifndef 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/GuiMsgBox.h"
|
||||
#include "renderers/Renderer.h"
|
||||
#include "FileData.h"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -46,8 +46,10 @@ public:
|
|||
// If a basic view detected a metadata change, it can request to recreate
|
||||
// the current gamelist view (as it may change to be detailed).
|
||||
void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false);
|
||||
inline void reloadGameListView(SystemData* system, bool reloadTheme = false)
|
||||
{ reloadGameListView(getGameListView(system).get(), reloadTheme); }
|
||||
void reloadGameListView(SystemData* system, bool reloadTheme = false)
|
||||
{
|
||||
reloadGameListView(getGameListView(system).get(), reloadTheme);
|
||||
}
|
||||
// Reload everything with a theme.
|
||||
// Used when the "ThemeSet" setting changes.
|
||||
void reloadAll();
|
||||
|
@ -67,22 +69,26 @@ public:
|
|||
void stopScrolling();
|
||||
|
||||
void onFileChanged(FileData* file, bool reloadGameList);
|
||||
void triggerGameLaunch(FileData* game) { mGameToLaunch = game; mLockInput = true; };
|
||||
bool getGameLaunchTriggered() { return (mGameToLaunch != nullptr); };
|
||||
void triggerGameLaunch(FileData* game)
|
||||
{
|
||||
mGameToLaunch = game;
|
||||
mLockInput = true;
|
||||
};
|
||||
bool getGameLaunchTriggered() { return (mGameToLaunch != nullptr); }
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
|
||||
enum ViewMode {
|
||||
NOTHING,
|
||||
NOTHING, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
START_SCREEN,
|
||||
SYSTEM_SELECT,
|
||||
GAME_LIST
|
||||
};
|
||||
|
||||
enum GameListViewStyle {
|
||||
AUTOMATIC,
|
||||
AUTOMATIC, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||
BASIC,
|
||||
DETAILED,
|
||||
GRID,
|
||||
|
@ -93,18 +99,18 @@ public:
|
|||
ViewMode viewing;
|
||||
GameListViewStyle viewstyle;
|
||||
|
||||
inline SystemData* getSystem() const
|
||||
SystemData* getSystem() const
|
||||
{
|
||||
assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT);
|
||||
return system;
|
||||
}
|
||||
|
||||
private:
|
||||
friend ViewController;
|
||||
SystemData* system;
|
||||
private:
|
||||
friend ViewController;
|
||||
SystemData* system;
|
||||
};
|
||||
|
||||
inline const State& getState() const { return mState; }
|
||||
const State& getState() const { return mState; }
|
||||
|
||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
virtual HelpStyle getHelpStyle() override;
|
||||
|
@ -143,6 +149,9 @@ private:
|
|||
std::map<SystemData*, std::shared_ptr<IGameListView>> mGameListViews;
|
||||
std::shared_ptr<SystemView> mSystemListView;
|
||||
|
||||
FileData* mGameToLaunch;
|
||||
State mState;
|
||||
|
||||
Transform4x4f mCamera;
|
||||
bool mSystemViewTransition;
|
||||
bool mWrappedViews;
|
||||
|
@ -151,9 +160,6 @@ private:
|
|||
bool mCancelledTransition; // Needed only for the Fade transition style.
|
||||
bool mLockInput;
|
||||
bool mNextSystem;
|
||||
FileData* mGameToLaunch;
|
||||
|
||||
State mState;
|
||||
};
|
||||
|
||||
#endif // ES_APP_VIEWS_VIEW_CONTROLLER_H
|
||||
|
|
|
@ -8,15 +8,16 @@
|
|||
|
||||
#include "views/gamelist/BasicGameListView.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "CollectionSystemsManager.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
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.setPosition(0, mSize.y() * 0.2f);
|
||||
|
@ -89,16 +90,16 @@ void BasicGameListView::populateList(const std::vector<FileData*>& files, FileDa
|
|||
}
|
||||
|
||||
if ((*it)->getFavorite() && favoriteStar &&
|
||||
mRoot->getSystem()->getName() != "favorites") {
|
||||
mRoot->getSystem()->getName() != "favorites") {
|
||||
if (Settings::getInstance()->getBool("SpecialCharsASCII"))
|
||||
mList.add(inCollectionPrefix + "* " +
|
||||
(*it)->getName(), *it, ((*it)->getType() == FOLDER));
|
||||
mList.add(inCollectionPrefix + "* " + (*it)->getName(), *it,
|
||||
((*it)->getType() == FOLDER));
|
||||
else
|
||||
mList.add(inCollectionPrefix + ViewController::FAVORITE_CHAR + " " +
|
||||
(*it)->getName(), *it, ((*it)->getType() == FOLDER));
|
||||
(*it)->getName(),
|
||||
*it, ((*it)->getType() == FOLDER));
|
||||
}
|
||||
else if ((*it)->getType() == FOLDER &&
|
||||
mRoot->getSystem()->getName() != "collections") {
|
||||
else if ((*it)->getType() == FOLDER && mRoot->getSystem()->getName() != "collections") {
|
||||
if (Settings::getInstance()->getBool("SpecialCharsASCII"))
|
||||
mList.add("# " + (*it)->getName(), *it, true);
|
||||
else
|
||||
|
@ -117,11 +118,6 @@ void BasicGameListView::populateList(const std::vector<FileData*>& files, FileDa
|
|||
generateFirstLetterIndex(files);
|
||||
}
|
||||
|
||||
FileData* BasicGameListView::getCursor()
|
||||
{
|
||||
return mList.getSelected();
|
||||
}
|
||||
|
||||
void BasicGameListView::setCursor(FileData* cursor)
|
||||
{
|
||||
if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) {
|
||||
|
@ -133,6 +129,7 @@ void BasicGameListView::setCursor(FileData* cursor)
|
|||
if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) {
|
||||
std::stack<FileData*> tmp;
|
||||
FileData* ptr = cursor->getParent();
|
||||
|
||||
while (ptr && ptr != mRoot) {
|
||||
tmp.push(ptr);
|
||||
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)
|
||||
{
|
||||
// Empty list, add a placeholder.
|
||||
|
@ -186,18 +158,9 @@ void BasicGameListView::addPlaceholder(FileData* firstEntry)
|
|||
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)
|
||||
{
|
||||
// This triggers ViewController to launch the game.
|
||||
ViewController::get()->triggerGameLaunch(game);
|
||||
}
|
||||
|
||||
|
@ -235,7 +198,7 @@ void BasicGameListView::remove(FileData* game, bool deleteFile)
|
|||
|
||||
if (deleteFile) {
|
||||
parent->sort(parent->getSortTypeFromString(parent->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
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
|
||||
// the directory too. Remove any empty parent directories as well.
|
||||
auto removeEmptyDirFunc = []
|
||||
(std::string systemMediaDir, std::string mediaType, std::string path) {
|
||||
auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType,
|
||||
std::string path) {
|
||||
std::string parentPath = Utils::FileSystem::getParent(path);
|
||||
while (parentPath != systemMediaDir + "/" + mediaType) {
|
||||
if (Utils::FileSystem::getDirContent(parentPath).size() == 0) {
|
||||
|
@ -321,13 +284,13 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
|
|||
std::vector<HelpPrompt> prompts;
|
||||
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect") &&
|
||||
SystemData::sSystemVector.size() > 1)
|
||||
SystemData::sSystemVector.size() > 1)
|
||||
prompts.push_back(HelpPrompt("left/right", "system"));
|
||||
|
||||
prompts.push_back(HelpPrompt("up/down", "choose"));
|
||||
|
||||
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"));
|
||||
else
|
||||
prompts.push_back(HelpPrompt("a", "launch"));
|
||||
|
@ -341,24 +304,24 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
|
|||
prompts.push_back(HelpPrompt("thumbstickclick", "random"));
|
||||
|
||||
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
!CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() &&
|
||||
ViewController::get()->getState().viewing == ViewController::GAME_LIST &&
|
||||
ViewController::get()->getState().viewstyle != ViewController::BASIC) {
|
||||
!CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() &&
|
||||
ViewController::get()->getState().viewing == ViewController::GAME_LIST &&
|
||||
ViewController::get()->getState().viewstyle != ViewController::BASIC) {
|
||||
prompts.push_back(HelpPrompt("y", "jump to game"));
|
||||
}
|
||||
else if (mRoot->getSystem()->isGameSystem() &&
|
||||
(mRoot->getSystem()->getThemeFolder() != "custom-collections" ||
|
||||
!mCursorStack.empty()) &&
|
||||
!UIModeController::getInstance()->isUIModeKid() &&
|
||||
!UIModeController::getInstance()->isUIModeKiosk() &&
|
||||
(Settings::getInstance()->getBool("FavoritesAddButton") ||
|
||||
CollectionSystemsManager::get()->isEditing())) {
|
||||
(mRoot->getSystem()->getThemeFolder() != "custom-collections" ||
|
||||
!mCursorStack.empty()) &&
|
||||
!UIModeController::getInstance()->isUIModeKid() &&
|
||||
!UIModeController::getInstance()->isUIModeKiosk() &&
|
||||
(Settings::getInstance()->getBool("FavoritesAddButton") ||
|
||||
CollectionSystemsManager::get()->isEditing())) {
|
||||
std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
}
|
||||
else if (mRoot->getSystem()->isGameSystem() &&
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
CollectionSystemsManager::get()->isEditing()) {
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
CollectionSystemsManager::get()->isEditing()) {
|
||||
std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
}
|
||||
|
|
|
@ -21,30 +21,33 @@ public:
|
|||
virtual void onFileChanged(FileData* file, bool reloadGameList) override;
|
||||
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||
|
||||
virtual FileData* getCursor() 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 FileData* getCursor() override { return mList.getSelected(); }
|
||||
virtual FileData* getNextEntry() override { return mList.getNext(); }
|
||||
virtual FileData* getPreviousEntry() override { return mList.getPrevious(); }
|
||||
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::vector<HelpPrompt> getHelpPrompts() override;
|
||||
virtual void launch(FileData* game) override;
|
||||
|
||||
virtual bool isListScrolling() override { return mList.isScrolling(); };
|
||||
virtual void stopListScrolling() override { mList.stopScrolling(); };
|
||||
virtual bool isListScrolling() override { return mList.isScrolling(); }
|
||||
virtual void stopListScrolling() override { mList.stopScrolling(); }
|
||||
|
||||
virtual const std::vector<std::string>& getFirstLetterIndex() override
|
||||
{ return mFirstLetterIndex; };
|
||||
{
|
||||
return mFirstLetterIndex;
|
||||
}
|
||||
|
||||
virtual void addPlaceholder(FileData* firstEntry = nullptr) override;
|
||||
virtual void launch(FileData* game) override;
|
||||
|
||||
protected:
|
||||
virtual std::string getQuickSystemSelectRightButton() override;
|
||||
virtual std::string getQuickSystemSelectLeftButton() override;
|
||||
virtual std::string getQuickSystemSelectRightButton() override { return "right"; }
|
||||
virtual std::string getQuickSystemSelectLeftButton() override { return "left"; }
|
||||
virtual void populateList(const std::vector<FileData*>& files, FileData* firstEntry) override;
|
||||
virtual void remove(FileData* game, bool deleteFile) override;
|
||||
virtual void removeMedia(FileData* game) override;
|
||||
|
|
|
@ -8,45 +8,40 @@
|
|||
|
||||
#include "views/gamelist/DetailedGameListView.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "CollectionSystemsManager.h"
|
||||
#include "SystemData.h"
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#define FADE_IN_START_OPACITY 0.5f
|
||||
#define FADE_IN_TIME 650
|
||||
|
||||
DetailedGameListView::DetailedGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: BasicGameListView(window, root),
|
||||
mDescContainer(window),
|
||||
mDescription(window),
|
||||
mGamelistInfo(window),
|
||||
|
||||
mThumbnail(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
|
||||
mLblRating(window),
|
||||
mLblReleaseDate(window),
|
||||
mLblDeveloper(window),
|
||||
mLblPublisher(window),
|
||||
mLblGenre(window),
|
||||
mLblPlayers(window),
|
||||
mLblLastPlayed(window),
|
||||
mLblPlayCount(window),
|
||||
|
||||
mRating(window),
|
||||
mReleaseDate(window),
|
||||
mDeveloper(window),
|
||||
mPublisher(window),
|
||||
mGenre(window),
|
||||
mPlayers(window),
|
||||
mLastPlayed(window),
|
||||
mPlayCount(window),
|
||||
mName(window),
|
||||
mLastUpdated(nullptr)
|
||||
DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
|
||||
: BasicGameListView(window, root)
|
||||
, mDescContainer(window)
|
||||
, mDescription(window)
|
||||
, mGamelistInfo(window)
|
||||
, mThumbnail(window)
|
||||
, mMarquee(window)
|
||||
, mImage(window)
|
||||
, mLblRating(window)
|
||||
, mLblReleaseDate(window)
|
||||
, mLblDeveloper(window)
|
||||
, mLblPublisher(window)
|
||||
, mLblGenre(window)
|
||||
, mLblPlayers(window)
|
||||
, mLblLastPlayed(window)
|
||||
, mLblPlayCount(window)
|
||||
, mRating(window)
|
||||
, mReleaseDate(window)
|
||||
, mDeveloper(window)
|
||||
, mPublisher(window)
|
||||
, mGenre(window)
|
||||
, mPlayers(window)
|
||||
, mLastPlayed(window)
|
||||
, mPlayCount(window)
|
||||
, mName(window)
|
||||
, mLastUpdated(nullptr)
|
||||
{
|
||||
const float padding = 0.01f;
|
||||
|
||||
|
@ -114,8 +109,8 @@ DetailedGameListView::DetailedGameListView(
|
|||
addChild(&mName);
|
||||
|
||||
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
|
||||
mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setAutoScroll(true);
|
||||
mDescContainer.setDefaultZIndex(40);
|
||||
addChild(&mDescContainer);
|
||||
|
@ -140,20 +135,20 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
|
|||
|
||||
using namespace ThemeFlags;
|
||||
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",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
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);
|
||||
|
||||
initMDLabels();
|
||||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
assert(labels.size() == 8);
|
||||
std::vector<std::string> lblElements = {
|
||||
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
|
||||
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||
};
|
||||
std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
|
||||
"md_lbl_developer", "md_lbl_publisher",
|
||||
"md_lbl_genre", "md_lbl_players",
|
||||
"md_lbl_lastplayed", "md_lbl_playcount" };
|
||||
|
||||
for (unsigned int i = 0; i < labels.size(); i++)
|
||||
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||
|
@ -161,19 +156,19 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
|
|||
initMDValues();
|
||||
std::vector<GuiComponent*> values = getMDValues();
|
||||
assert(values.size() == 8);
|
||||
std::vector<std::string> valElements = {
|
||||
"md_rating", "md_releasedate", "md_developer", "md_publisher",
|
||||
"md_genre", "md_players", "md_lastplayed", "md_playcount"
|
||||
};
|
||||
std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
|
||||
"md_publisher", "md_genre", "md_players",
|
||||
"md_lastplayed", "md_playcount" };
|
||||
|
||||
for (unsigned int i = 0; i < values.size(); i++)
|
||||
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
|
||||
|
||||
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.applyTheme(theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
mDescription.applyTheme(
|
||||
theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
|
||||
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
|
||||
// If there is no position defined in the theme for gamelistInfo, then hide it.
|
||||
|
@ -205,7 +200,7 @@ void DetailedGameListView::initMDLabels()
|
|||
}
|
||||
else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -232,13 +227,13 @@ void DetailedGameListView::initMDValues()
|
|||
|
||||
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++) {
|
||||
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() +
|
||||
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]->setDefaultZIndex(40);
|
||||
values[i]->setDefaultZIndex(40.0f);
|
||||
|
||||
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.setSize(mDescContainer.getSize().x(), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(),
|
||||
mSize.y() - mDescContainer.getPosition().y());
|
||||
}
|
||||
|
||||
void DetailedGameListView::updateInfoPanel()
|
||||
|
@ -267,7 +262,7 @@ void DetailedGameListView::updateInfoPanel()
|
|||
if (file) {
|
||||
// Always hide the metadata fields if browsing grouped custom collections.
|
||||
if (file->getSystem()->isCustomCollection() &&
|
||||
file->getPath() == file->getSystem()->getName())
|
||||
file->getPath() == file->getSystem()->getName())
|
||||
hideMetaDataFields = true;
|
||||
else
|
||||
hideMetaDataFields = (file->metadata.get("hidemetadata") == "true");
|
||||
|
@ -283,9 +278,9 @@ void DetailedGameListView::updateInfoPanel()
|
|||
// or if we're in the grouped custom collection view.
|
||||
if (mList.isScrolling())
|
||||
if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") ||
|
||||
(mLastUpdated->getSystem()->isCustomCollection() &&
|
||||
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
|
||||
hideMetaDataFields = true;
|
||||
(mLastUpdated->getSystem()->isCustomCollection() &&
|
||||
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
|
||||
hideMetaDataFields = true;
|
||||
|
||||
if (hideMetaDataFields) {
|
||||
mLblRating.setVisible(false);
|
||||
|
@ -333,9 +328,9 @@ void DetailedGameListView::updateInfoPanel()
|
|||
// 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.
|
||||
if (file->getSystem()->isCustomCollection() &&
|
||||
file->getPath() == file->getSystem()->getName()) {
|
||||
mRandomGame = CollectionSystemsManager::get()->
|
||||
updateCollectionFolderMetadata(file->getSystem());
|
||||
file->getPath() == file->getSystem()->getName()) {
|
||||
mRandomGame =
|
||||
CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem());
|
||||
if (mRandomGame) {
|
||||
mThumbnail.setImage(mRandomGame->getThumbnailPath());
|
||||
mMarquee.setImage(mRandomGame->getMarqueePath());
|
||||
|
@ -366,21 +361,21 @@ void DetailedGameListView::updateInfoPanel()
|
|||
if (mIsFiltered) {
|
||||
if (mFilteredGameCountAll == mFilteredGameCount)
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " " +
|
||||
std::to_string(mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
std::to_string(mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
else
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " " +
|
||||
std::to_string(mFilteredGameCount) + " + " +
|
||||
std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
std::to_string(mFilteredGameCount) + " + " +
|
||||
std::to_string(mFilteredGameCountAll - mFilteredGameCount) +
|
||||
" / " + std::to_string(mGameCount);
|
||||
}
|
||||
else {
|
||||
gamelistInfoString += ViewController::CONTROLLER_CHAR + " " +
|
||||
std::to_string(mGameCount);
|
||||
gamelistInfoString +=
|
||||
ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
|
||||
if (!(file->getSystem()->isCollection() &&
|
||||
file->getSystem()->getFullName() == "favorites"))
|
||||
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " "
|
||||
+ std::to_string(mFavoritesGameCount);
|
||||
file->getSystem()->getFullName() == "favorites"))
|
||||
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
|
||||
std::to_string(mFavoritesGameCount);
|
||||
}
|
||||
|
||||
if (mIsFolder && infoAlign != ALIGN_RIGHT)
|
||||
|
@ -390,9 +385,9 @@ void DetailedGameListView::updateInfoPanel()
|
|||
|
||||
// Fade in the game image.
|
||||
auto func = [this](float t) {
|
||||
mImage.setOpacity(static_cast<unsigned char>(Math::lerp(
|
||||
static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
|
||||
};
|
||||
mImage.setOpacity(static_cast<unsigned char>(
|
||||
Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
|
||||
};
|
||||
mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
|
||||
|
||||
mDescription.setText(file->metadata.get("desc"));
|
||||
|
|
|
@ -8,48 +8,42 @@
|
|||
|
||||
#include "views/gamelist/GridGameListView.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "CollectionSystemsManager.h"
|
||||
#include "Settings.h"
|
||||
#include "Sound.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_TIME 650
|
||||
|
||||
GridGameListView::GridGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: ISimpleGameListView(window, root),
|
||||
|
||||
mGrid(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
|
||||
mDescContainer(window),
|
||||
mDescription(window),
|
||||
mGamelistInfo(window),
|
||||
|
||||
mLblRating(window),
|
||||
mLblReleaseDate(window),
|
||||
mLblDeveloper(window),
|
||||
mLblPublisher(window),
|
||||
mLblGenre(window),
|
||||
mLblPlayers(window),
|
||||
mLblLastPlayed(window),
|
||||
mLblPlayCount(window),
|
||||
|
||||
mRating(window),
|
||||
mReleaseDate(window),
|
||||
mDeveloper(window),
|
||||
mPublisher(window),
|
||||
mGenre(window),
|
||||
mPlayers(window),
|
||||
mLastPlayed(window),
|
||||
mPlayCount(window),
|
||||
mName(window)
|
||||
GridGameListView::GridGameListView(Window* window, FileData* root)
|
||||
: ISimpleGameListView(window, root)
|
||||
, mGrid(window)
|
||||
, mMarquee(window)
|
||||
, mImage(window)
|
||||
, mDescContainer(window)
|
||||
, mDescription(window)
|
||||
, mGamelistInfo(window)
|
||||
, mLblRating(window)
|
||||
, mLblReleaseDate(window)
|
||||
, mLblDeveloper(window)
|
||||
, mLblPublisher(window)
|
||||
, mLblGenre(window)
|
||||
, mLblPlayers(window)
|
||||
, mLblLastPlayed(window)
|
||||
, mLblPlayCount(window)
|
||||
, mRating(window)
|
||||
, mReleaseDate(window)
|
||||
, mDeveloper(window)
|
||||
, mPublisher(window)
|
||||
, mGenre(window)
|
||||
, mPlayers(window)
|
||||
, mLastPlayed(window)
|
||||
, mPlayCount(window)
|
||||
, mName(window)
|
||||
{
|
||||
const float padding = 0.01f;
|
||||
|
||||
|
@ -95,8 +89,8 @@ GridGameListView::GridGameListView(
|
|||
addChild(&mName);
|
||||
|
||||
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
|
||||
mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setAutoScroll(true);
|
||||
mDescContainer.setDefaultZIndex(40);
|
||||
addChild(&mDescContainer);
|
||||
|
@ -107,7 +101,7 @@ GridGameListView::GridGameListView(
|
|||
|
||||
mMarquee.setOrigin(0.5f, 0.5f);
|
||||
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.setVisible(false);
|
||||
addChild(&mMarquee);
|
||||
|
@ -130,10 +124,6 @@ GridGameListView::GridGameListView(
|
|||
updateInfoPanel();
|
||||
}
|
||||
|
||||
GridGameListView::~GridGameListView()
|
||||
{
|
||||
}
|
||||
|
||||
void GridGameListView::onFileChanged(FileData* file, bool reloadGameList)
|
||||
{
|
||||
if (reloadGameList) {
|
||||
|
@ -145,11 +135,6 @@ void GridGameListView::onFileChanged(FileData* file, bool reloadGameList)
|
|||
ISimpleGameListView::onFileChanged(file, reloadGameList);
|
||||
}
|
||||
|
||||
FileData* GridGameListView::getCursor()
|
||||
{
|
||||
return mGrid.getSelected();
|
||||
}
|
||||
|
||||
void GridGameListView::setCursor(FileData* cursor)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (input.value == 0 && (config->isMappedLike("left", input) ||
|
||||
config->isMappedLike("right", input) ||
|
||||
(config->isMappedLike("up", input)) ||
|
||||
(config->isMappedLike("down", input)) ))
|
||||
if (input.value == 0 &&
|
||||
(config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
|
||||
(config->isMappedLike("up", input)) || (config->isMappedLike("down", input))))
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
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);
|
||||
mName.applyTheme(theme, getName(), "md_name", ALL);
|
||||
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",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
|
||||
initMDLabels();
|
||||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
assert(labels.size() == 8);
|
||||
std::vector<std::string> lblElements = {
|
||||
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
|
||||
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||
};
|
||||
std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
|
||||
"md_lbl_developer", "md_lbl_publisher",
|
||||
"md_lbl_genre", "md_lbl_players",
|
||||
"md_lbl_lastplayed", "md_lbl_playcount" };
|
||||
|
||||
for (unsigned int i = 0; i < labels.size(); i++)
|
||||
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||
|
@ -306,19 +255,19 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
initMDValues();
|
||||
std::vector<GuiComponent*> values = getMDValues();
|
||||
assert(values.size() == 8);
|
||||
std::vector<std::string> valElements = {
|
||||
"md_rating", "md_releasedate", "md_developer", "md_publisher",
|
||||
"md_genre", "md_players", "md_lastplayed", "md_playcount"
|
||||
};
|
||||
std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
|
||||
"md_publisher", "md_genre", "md_players",
|
||||
"md_lastplayed", "md_playcount" };
|
||||
|
||||
for (unsigned int i = 0; i < values.size(); i++)
|
||||
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
|
||||
|
||||
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.applyTheme(theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
mDescription.applyTheme(
|
||||
theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
|
||||
// Repopulate list in case a new theme is displaying a different image.
|
||||
// Preserve selection.
|
||||
|
@ -336,6 +285,12 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
sortChildren();
|
||||
}
|
||||
|
||||
void GridGameListView::onShow()
|
||||
{
|
||||
GuiComponent::onShow();
|
||||
updateInfoPanel();
|
||||
}
|
||||
|
||||
void GridGameListView::initMDLabels()
|
||||
{
|
||||
std::vector<TextComponent*> components = getMDLabels();
|
||||
|
@ -356,7 +311,7 @@ void GridGameListView::initMDLabels()
|
|||
}
|
||||
else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -383,11 +338,11 @@ void GridGameListView::initMDValues()
|
|||
|
||||
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++) {
|
||||
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() +
|
||||
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]->setDefaultZIndex(40);
|
||||
|
||||
|
@ -397,8 +352,8 @@ void GridGameListView::initMDValues()
|
|||
}
|
||||
|
||||
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(),
|
||||
mSize.y() - mDescContainer.getPosition().y());
|
||||
}
|
||||
|
||||
void GridGameListView::updateInfoPanel()
|
||||
|
@ -465,21 +420,22 @@ void GridGameListView::updateInfoPanel()
|
|||
|
||||
if (mIsFiltered) {
|
||||
if (mFilteredGameCountAll == mFilteredGameCount)
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " "
|
||||
+ std::to_string(mFilteredGameCount) + " / " + std::to_string(mGameCount);
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " " +
|
||||
std::to_string(mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
else
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " " +
|
||||
std::to_string(mFilteredGameCount) + " + " +
|
||||
std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
std::to_string(mFilteredGameCount) + " + " +
|
||||
std::to_string(mFilteredGameCountAll - mFilteredGameCount) +
|
||||
" / " + std::to_string(mGameCount);
|
||||
}
|
||||
else {
|
||||
gamelistInfoString += ViewController::CONTROLLER_CHAR + " " +
|
||||
std::to_string(mGameCount);
|
||||
gamelistInfoString +=
|
||||
ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
|
||||
if (!(file->getSystem()->isCollection() &&
|
||||
file->getSystem()->getFullName() == "favorites"))
|
||||
file->getSystem()->getFullName() == "favorites"))
|
||||
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
|
||||
std::to_string(mFavoritesGameCount);
|
||||
std::to_string(mFavoritesGameCount);
|
||||
}
|
||||
|
||||
if (mIsFolder && infoAlign != ALIGN_RIGHT)
|
||||
|
@ -489,9 +445,9 @@ void GridGameListView::updateInfoPanel()
|
|||
|
||||
// Fade in the game image.
|
||||
auto func = [this](float t) {
|
||||
mImage.setOpacity(static_cast<unsigned char>(Math::lerp(
|
||||
static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
|
||||
};
|
||||
mImage.setOpacity(static_cast<unsigned char>(
|
||||
Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
|
||||
};
|
||||
mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
|
||||
|
||||
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 not playing, then animate if opacity != our target opacity.
|
||||
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) {
|
||||
// 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));
|
||||
// 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->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
|
||||
}
|
||||
|
@ -560,6 +516,7 @@ void GridGameListView::addPlaceholder(FileData* firstEntry)
|
|||
|
||||
void GridGameListView::launch(FileData* game)
|
||||
{
|
||||
// This triggers ViewController to launch the 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
|
||||
// the directory too. Remove any empty parent directories as well.
|
||||
auto removeEmptyDirFunc = []
|
||||
(std::string systemMediaDir, std::string mediaType, std::string path) {
|
||||
auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType,
|
||||
std::string path) {
|
||||
std::string parentPath = Utils::FileSystem::getParent(path);
|
||||
while (parentPath != systemMediaDir + "/" + mediaType) {
|
||||
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"));
|
||||
|
||||
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"));
|
||||
else
|
||||
prompts.push_back(HelpPrompt("a", "launch"));
|
||||
|
@ -715,32 +672,30 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
|
|||
prompts.push_back(HelpPrompt("b", "back"));
|
||||
|
||||
if (mRoot->getSystem()->isGameSystem() &&
|
||||
mRoot->getSystem()->getThemeFolder() != "custom-collections")
|
||||
mRoot->getSystem()->getThemeFolder() != "custom-collections")
|
||||
prompts.push_back(HelpPrompt("x", "view media"));
|
||||
|
||||
if (mRoot->getSystem()->isGameSystem() && !mCursorStack.empty() &&
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections")
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections")
|
||||
prompts.push_back(HelpPrompt("x", "view media"));
|
||||
|
||||
if (!UIModeController::getInstance()->isUIModeKid())
|
||||
prompts.push_back(HelpPrompt("back", "options"));
|
||||
if (mRoot->getSystem()->isGameSystem() &&
|
||||
Settings::getInstance()->getBool("RandomAddButton"))
|
||||
if (mRoot->getSystem()->isGameSystem() && Settings::getInstance()->getBool("RandomAddButton"))
|
||||
prompts.push_back(HelpPrompt("thumbstickclick", "random"));
|
||||
|
||||
if (mRoot->getSystem()->isGameSystem() &&
|
||||
(mRoot->getSystem()->getThemeFolder() != "custom-collections" ||
|
||||
!mCursorStack.empty()) &&
|
||||
!UIModeController::getInstance()->isUIModeKid() &&
|
||||
!UIModeController::getInstance()->isUIModeKiosk() &&
|
||||
(Settings::getInstance()->getBool("FavoritesAddButton") ||
|
||||
CollectionSystemsManager::get()->isEditing())) {
|
||||
(mRoot->getSystem()->getThemeFolder() != "custom-collections" || !mCursorStack.empty()) &&
|
||||
!UIModeController::getInstance()->isUIModeKid() &&
|
||||
!UIModeController::getInstance()->isUIModeKiosk() &&
|
||||
(Settings::getInstance()->getBool("FavoritesAddButton") ||
|
||||
CollectionSystemsManager::get()->isEditing())) {
|
||||
std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
}
|
||||
else if (mRoot->getSystem()->isGameSystem() &&
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
CollectionSystemsManager::get()->isEditing()) {
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
CollectionSystemsManager::get()->isEditing()) {
|
||||
std::string prompt = CollectionSystemsManager::get()->getEditingCollection();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
}
|
||||
|
@ -749,11 +704,6 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
|
|||
|
||||
void GridGameListView::update(int deltaTime)
|
||||
{
|
||||
// Update.
|
||||
ISimpleGameListView::update(deltaTime);
|
||||
}
|
||||
|
||||
void GridGameListView::onShow()
|
||||
{
|
||||
GuiComponent::onShow();
|
||||
updateInfoPanel();
|
||||
}
|
||||
|
|
|
@ -20,48 +20,50 @@ class GridGameListView : public ISimpleGameListView
|
|||
{
|
||||
public:
|
||||
GridGameListView(Window* window, FileData* root);
|
||||
virtual ~GridGameListView();
|
||||
virtual ~GridGameListView() {}
|
||||
|
||||
// Called when a FileData* is added, has its metadata changed, or is removed.
|
||||
virtual void onFileChanged(FileData* file, bool reloadGameList) override;
|
||||
|
||||
virtual void onShow() override;
|
||||
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||
|
||||
virtual FileData* getCursor() 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 bool input(InputConfig* config, Input input) override;
|
||||
|
||||
virtual std::vector<HelpPrompt> getHelpPrompts() 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
|
||||
{
|
||||
mGrid.stopAllAnimations();
|
||||
mGrid.stopScrolling();
|
||||
};
|
||||
}
|
||||
|
||||
virtual const std::vector<std::string>& getFirstLetterIndex() override
|
||||
{ return mFirstLetterIndex; };
|
||||
{
|
||||
return mFirstLetterIndex;
|
||||
}
|
||||
|
||||
virtual void addPlaceholder(FileData* firstEntry = nullptr) override;
|
||||
|
||||
protected:
|
||||
virtual void update(int deltaTime) override;
|
||||
virtual std::string getQuickSystemSelectRightButton() override;
|
||||
virtual std::string getQuickSystemSelectLeftButton() override;
|
||||
virtual std::string getQuickSystemSelectRightButton() override { return "rightshoulder"; }
|
||||
virtual std::string getQuickSystemSelectLeftButton() override { return "leftshoulder"; }
|
||||
virtual void populateList(const std::vector<FileData*>& files, FileData* firstEntry) override;
|
||||
virtual void remove(FileData* game, bool deleteFile) override;
|
||||
virtual void removeMedia(FileData* game) override;
|
||||
virtual void update(int deltaTime) override;
|
||||
|
||||
ImageGridComponent<FileData*> mGrid;
|
||||
// Points to the first game in the list, i.e. the first entry which is of the type 'GAME'.
|
||||
|
|
|
@ -8,18 +8,32 @@
|
|||
|
||||
#include "views/gamelist/IGameListView.h"
|
||||
|
||||
#include "guis/GuiGamelistOptions.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "AudioManager.h"
|
||||
#include "Sound.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)
|
||||
{
|
||||
// Select button opens GuiGamelistOptions.
|
||||
if (!UIModeController::getInstance()->isUIModeKid() &&
|
||||
config->isMappedTo("back", input) && input.value) {
|
||||
if (!UIModeController::getInstance()->isUIModeKid() && // Line break.
|
||||
config->isMappedTo("back", input) && input.value) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
stopListScrolling();
|
||||
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.
|
||||
else if (Settings::getInstance()->getBool("Debug") &&
|
||||
config->getDeviceId() == DEVICE_KEYBOARD &&
|
||||
(SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) &&
|
||||
input.id == SDLK_r && input.value != 0) {
|
||||
config->getDeviceId() == DEVICE_KEYBOARD &&
|
||||
(SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && input.id == SDLK_r &&
|
||||
input.value != 0) {
|
||||
LOG(LogDebug) << "IGameListView::input(): Reloading view";
|
||||
ViewController::get()->reloadGameListView(this, true);
|
||||
return true;
|
||||
|
@ -39,12 +53,6 @@ bool IGameListView::input(InputConfig* config, Input input)
|
|||
return GuiComponent::input(config, input);
|
||||
}
|
||||
|
||||
void IGameListView::setTheme(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
mTheme = theme;
|
||||
onThemeChanged(theme);
|
||||
}
|
||||
|
||||
HelpStyle IGameListView::getHelpStyle()
|
||||
{
|
||||
HelpStyle style;
|
||||
|
@ -60,9 +68,9 @@ void IGameListView::render(const Transform4x4f& parentTrans)
|
|||
float scaleY = trans.r1().y();
|
||||
|
||||
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)),
|
||||
static_cast<int>(std::round(mSize.y() * scaleY)));
|
||||
static_cast<int>(std::round(mSize.y() * scaleY)));
|
||||
|
||||
Renderer::pushClipRect(pos, size);
|
||||
renderChildren(trans);
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#ifndef 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 "GuiComponent.h"
|
||||
#include "renderers/Renderer.h"
|
||||
|
||||
class ThemeData;
|
||||
class Window;
|
||||
|
@ -20,12 +20,7 @@ class Window;
|
|||
class IGameListView : public GuiComponent
|
||||
{
|
||||
public:
|
||||
IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root)
|
||||
{
|
||||
setSize(static_cast<float>(Renderer::getScreenWidth()),
|
||||
static_cast<float>(Renderer::getScreenHeight()));
|
||||
}
|
||||
|
||||
IGameListView(Window* window, FileData* root);
|
||||
virtual ~IGameListView() {}
|
||||
|
||||
// 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;
|
||||
|
||||
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 void setCursor(FileData*) = 0;
|
||||
|
|
|
@ -8,26 +8,24 @@
|
|||
|
||||
#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 "FileFilterIndex.h"
|
||||
#include "Settings.h"
|
||||
#include "Sound.h"
|
||||
#include "SystemData.h"
|
||||
#include "guis/GuiInfoPopup.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
ISimpleGameListView::ISimpleGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: IGameListView(window, root),
|
||||
mHeaderText(window),
|
||||
mHeaderImage(window),
|
||||
mBackground(window),
|
||||
mRandomGame(nullptr)
|
||||
ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root)
|
||||
: IGameListView(window, root)
|
||||
, mHeaderText(window)
|
||||
, mHeaderImage(window)
|
||||
, mBackground(window)
|
||||
, mRandomGame(nullptr)
|
||||
{
|
||||
mHeaderText.setText("Logo Text");
|
||||
mHeaderText.setSize(mSize.x(), 0);
|
||||
|
@ -124,10 +122,10 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
std::vector<FileData*> listEntries = cursor->getChildrenListToDisplay();
|
||||
// Check if there is an entry in the cursor stack history matching any entry
|
||||
// in the currect folder. If so, select that entry.
|
||||
for (auto it = mCursorStackHistory.begin();
|
||||
it != mCursorStackHistory.end(); it++) {
|
||||
for (auto it = mCursorStackHistory.begin(); // Line break.
|
||||
it != mCursorStackHistory.end(); it++) {
|
||||
if (std::find(listEntries.begin(), listEntries.end(), *it) !=
|
||||
listEntries.end()) {
|
||||
listEntries.end()) {
|
||||
newCursor = *it;
|
||||
mCursorStackHistory.erase(it);
|
||||
break;
|
||||
|
@ -155,7 +153,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
mCursorStackHistory.push_back(getCursor());
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(BACKSOUND);
|
||||
populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay(),
|
||||
mCursorStack.top()->getParent());
|
||||
mCursorStack.top()->getParent());
|
||||
setCursor(mCursorStack.top());
|
||||
if (mCursorStack.size() > 0)
|
||||
mCursorStack.pop();
|
||||
|
@ -169,9 +167,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
stopListScrolling();
|
||||
SystemData* systemToView = getCursor()->getSystem();
|
||||
if (systemToView->isCustomCollection() &&
|
||||
systemToView->getRootFolder()->getParent())
|
||||
systemToView->getRootFolder()->getParent())
|
||||
ViewController::get()->goToSystemView(
|
||||
systemToView->getRootFolder()->getParent()->getSystem(), true);
|
||||
systemToView->getRootFolder()->getParent()->getSystem(), true);
|
||||
else
|
||||
ViewController::get()->goToSystemView(systemToView, true);
|
||||
}
|
||||
|
@ -184,9 +182,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
return true;
|
||||
}
|
||||
else if (config->isMappedTo("x", input) &&
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
mCursorStack.empty() && ViewController::get()->getState().viewing ==
|
||||
ViewController::GAME_LIST) {
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
mCursorStack.empty() &&
|
||||
ViewController::get()->getState().viewing == ViewController::GAME_LIST) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
// Jump to the randomly selected game.
|
||||
if (mRandomGame) {
|
||||
|
@ -206,7 +204,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) {
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect") &&
|
||||
SystemData::sSystemVector.size() > 1) {
|
||||
SystemData::sSystemVector.size() > 1) {
|
||||
onPauseVideo();
|
||||
onFocusLost();
|
||||
stopListScrolling();
|
||||
|
@ -216,7 +214,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) {
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect") &&
|
||||
SystemData::sSystemVector.size() > 1) {
|
||||
SystemData::sSystemVector.size() > 1) {
|
||||
onPauseVideo();
|
||||
onFocusLost();
|
||||
stopListScrolling();
|
||||
|
@ -225,8 +223,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
}
|
||||
else if (Settings::getInstance()->getBool("RandomAddButton") &&
|
||||
(config->isMappedTo("leftthumbstickclick", input) ||
|
||||
config->isMappedTo("rightthumbstickclick", input))) {
|
||||
(config->isMappedTo("leftthumbstickclick", input) ||
|
||||
config->isMappedTo("rightthumbstickclick", input))) {
|
||||
if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER) {
|
||||
stopListScrolling();
|
||||
// Jump to a random game.
|
||||
|
@ -238,21 +236,19 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
}
|
||||
else if (config->isMappedTo("y", input) &&
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
!CollectionSystemsManager::get()->isEditing() &&
|
||||
mCursorStack.empty() && ViewController::get()->getState().viewing ==
|
||||
ViewController::GAME_LIST) {
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
!CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() &&
|
||||
ViewController::get()->getState().viewing == ViewController::GAME_LIST) {
|
||||
// Jump to the randomly selected game.
|
||||
if (mRandomGame) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND);
|
||||
// If there is already an mCursorStackHistory entry for the collection, then
|
||||
// remove it so we don't get multiple entries.
|
||||
std::vector<FileData*> listEntries =
|
||||
mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay();
|
||||
for (auto it = mCursorStackHistory.begin();
|
||||
it != mCursorStackHistory.end(); it++) {
|
||||
mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay();
|
||||
for (auto it = mCursorStackHistory.begin(); it != mCursorStackHistory.end(); it++) {
|
||||
if (std::find(listEntries.begin(), listEntries.end(), *it) !=
|
||||
listEntries.end()) {
|
||||
listEntries.end()) {
|
||||
mCursorStackHistory.erase(it);
|
||||
break;
|
||||
}
|
||||
|
@ -265,34 +261,33 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
}
|
||||
else if (config->isMappedTo("y", input) &&
|
||||
!Settings::getInstance()->getBool("FavoritesAddButton") &&
|
||||
!CollectionSystemsManager::get()->isEditing()) {
|
||||
!Settings::getInstance()->getBool("FavoritesAddButton") &&
|
||||
!CollectionSystemsManager::get()->isEditing()) {
|
||||
return true;
|
||||
}
|
||||
else if (config->isMappedTo("y", input) &&
|
||||
!UIModeController::getInstance()->isUIModeKid() &&
|
||||
!UIModeController::getInstance()->isUIModeKiosk()) {
|
||||
!UIModeController::getInstance()->isUIModeKid() &&
|
||||
!UIModeController::getInstance()->isUIModeKiosk()) {
|
||||
// Notify the user if attempting to add a custom collection to a custom collection.
|
||||
if (CollectionSystemsManager::get()->isEditing() &&
|
||||
mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
|
||||
getCursor()->getParent()->getPath() == "collections") {
|
||||
mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
|
||||
getCursor()->getParent()->getPath() == "collections") {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
|
||||
GuiInfoPopup* s;
|
||||
s = new GuiInfoPopup(mWindow,
|
||||
"CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS", 4000);
|
||||
s = new GuiInfoPopup(mWindow, "CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS",
|
||||
4000);
|
||||
mWindow->setInfoPopup(s);
|
||||
}
|
||||
// Notify the user if attempting to add a placeholder to a custom collection.
|
||||
if (CollectionSystemsManager::get()->isEditing() &&
|
||||
mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) {
|
||||
mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
|
||||
GuiInfoPopup* s;
|
||||
s = new GuiInfoPopup(mWindow,
|
||||
"CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000);
|
||||
s = new GuiInfoPopup(mWindow, "CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000);
|
||||
mWindow->setInfoPopup(s);
|
||||
}
|
||||
else if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
|
||||
getCursor()->getParent()->getPath() != "collections") {
|
||||
getCursor()->getParent()->getPath() != "collections") {
|
||||
if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER)
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
|
||||
// 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();
|
||||
|
||||
if (mRoot->getSystem()->isCustomCollection() ||
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections")
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections")
|
||||
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
||||
else
|
||||
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
||||
|
||||
if (favoritesSorting && static_cast<std::string>(
|
||||
mRoot->getSystem()->getName()) != "recent" && !isEditing) {
|
||||
if (favoritesSorting &&
|
||||
static_cast<std::string>(mRoot->getSystem()->getName()) != "recent" &&
|
||||
!isEditing) {
|
||||
FileData* entryToSelect;
|
||||
// Add favorite flag.
|
||||
if (!getCursor()->getFavorite()) {
|
||||
|
@ -331,7 +327,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
entryToSelect = getNextEntry();
|
||||
}
|
||||
else if (getCursor() == getLastEntry() &&
|
||||
getPreviousEntry()->getFavorite()) {
|
||||
getPreviousEntry()->getFavorite()) {
|
||||
entryToSelect = getLastEntry();
|
||||
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
|
||||
// favorite, then select this entry if they are of the same type.
|
||||
else if (getPreviousEntry() == getFirstEntry() &&
|
||||
getCursor()->getType() == getPreviousEntry()->getType()) {
|
||||
getCursor()->getType() == getPreviousEntry()->getType()) {
|
||||
entryToSelect = getPreviousEntry();
|
||||
}
|
||||
// For all other scenarios try to select the next entry, and if it doesn't
|
||||
// exist, select the previous entry.
|
||||
else {
|
||||
entryToSelect = getCursor() != getNextEntry() ?
|
||||
getNextEntry() : getPreviousEntry();
|
||||
entryToSelect =
|
||||
getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry();
|
||||
}
|
||||
}
|
||||
// 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,
|
||||
// unless folders are sorted on top and the previous entry is a folder.
|
||||
else if (foldersOnTop &&
|
||||
getCursor()->getFavorite() != getNextEntry()->getFavorite()) {
|
||||
getCursor()->getFavorite() != getNextEntry()->getFavorite()) {
|
||||
entryToSelect = getPreviousEntry()->getType() == FOLDER ?
|
||||
getCursor() : getPreviousEntry();
|
||||
getCursor() :
|
||||
getPreviousEntry();
|
||||
}
|
||||
// If we are on the favorite marking boundary, select the previous entry.
|
||||
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
|
||||
// exist, select the previous entry.
|
||||
else {
|
||||
entryToSelect = getCursor() != getNextEntry() ?
|
||||
getNextEntry() : getPreviousEntry();
|
||||
entryToSelect =
|
||||
getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry();
|
||||
}
|
||||
|
||||
// If we removed the last favorite marking, set the flag to jump to the
|
||||
// first list entry after the sorting has been performed.
|
||||
if (foldersOnTop && getCursor() == getFirstGameEntry() &&
|
||||
!getNextEntry()->getFavorite())
|
||||
!getNextEntry()->getFavorite())
|
||||
removedLastFavorite = true;
|
||||
else if (getCursor() == getFirstEntry() && !getNextEntry()->getFavorite())
|
||||
removedLastFavorite = true;
|
||||
removedLastFavorite = true;
|
||||
}
|
||||
|
||||
setCursor(entryToSelect);
|
||||
|
@ -399,22 +396,30 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
if (entryToUpdate->getType() == FOLDER) {
|
||||
GuiInfoPopup* s;
|
||||
if (isEditing) {
|
||||
s = new GuiInfoPopup(mWindow,
|
||||
"CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS", 4000);
|
||||
s = new GuiInfoPopup(mWindow, "CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS",
|
||||
4000);
|
||||
}
|
||||
else {
|
||||
MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata;
|
||||
if (md->get("favorite") == "false") {
|
||||
md->set("favorite", "true");
|
||||
s = new GuiInfoPopup(mWindow, "MARKED FOLDER '" +
|
||||
s = new GuiInfoPopup(
|
||||
mWindow,
|
||||
"MARKED FOLDER '" +
|
||||
Utils::String::toUpper(Utils::String::removeParenthesis(
|
||||
entryToUpdate->getName())) + "' AS FAVORITE", 4000);
|
||||
entryToUpdate->getName())) +
|
||||
"' AS FAVORITE",
|
||||
4000);
|
||||
}
|
||||
else {
|
||||
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(
|
||||
entryToUpdate->getName())) + "'", 4000);
|
||||
entryToUpdate->getName())) +
|
||||
"'",
|
||||
4000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,8 +427,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint();
|
||||
|
||||
getCursor()->getParent()->sort(
|
||||
mRoot->getSortTypeFromString(mRoot->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
mRoot->getSortTypeFromString(mRoot->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
|
||||
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
|
||||
// sorted yet.
|
||||
if (removedLastFavorite) {
|
||||
ViewController::get()->getGameListView(entryToUpdate->
|
||||
getSystem())->setCursor(ViewController::get()->
|
||||
getGameListView(entryToUpdate->getSystem())->getFirstEntry());
|
||||
ViewController::get()
|
||||
->getGameListView(entryToUpdate->getSystem())
|
||||
->setCursor(ViewController::get()
|
||||
->getGameListView(entryToUpdate->getSystem())
|
||||
->getFirstEntry());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (isEditing && entryToUpdate->metadata.get("nogamecount") == "true") {
|
||||
GuiInfoPopup* s = new GuiInfoPopup(mWindow,
|
||||
"CAN'T ADD ENTRIES THAT ARE NOT COUNTED "
|
||||
"AS GAMES TO CUSTOM COLLECTIONS", 4000);
|
||||
"CAN'T ADD ENTRIES THAT ARE NOT COUNTED "
|
||||
"AS GAMES TO CUSTOM COLLECTIONS",
|
||||
4000);
|
||||
mWindow->setInfoPopup(s);
|
||||
}
|
||||
else if (CollectionSystemsManager::get()->toggleGameInCollection(entryToUpdate)) {
|
||||
|
@ -450,13 +458,15 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
IGameListView* view = ViewController::get()->getGameListView(system).get();
|
||||
// Jump to the first entry in the gamelist if the last favorite was unmarked.
|
||||
if (foldersOnTop && removedLastFavorite &&
|
||||
!entryToUpdate->getSystem()->isCustomCollection()) {
|
||||
ViewController::get()->getGameListView(entryToUpdate->getSystem())->
|
||||
setCursor(ViewController::get()->getGameListView(entryToUpdate->
|
||||
getSystem())->getFirstGameEntry());
|
||||
!entryToUpdate->getSystem()->isCustomCollection()) {
|
||||
ViewController::get()
|
||||
->getGameListView(entryToUpdate->getSystem())
|
||||
->setCursor(ViewController::get()
|
||||
->getGameListView(entryToUpdate->getSystem())
|
||||
->getFirstGameEntry());
|
||||
}
|
||||
else if (removedLastFavorite &&
|
||||
!entryToUpdate->getSystem()->isCustomCollection()) {
|
||||
!entryToUpdate->getSystem()->isCustomCollection()) {
|
||||
view->setCursor(view->getFirstEntry());
|
||||
}
|
||||
else if (selectLastEntry) {
|
||||
|
@ -467,10 +477,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
// onFileChanged() which will trigger populateList().
|
||||
if (isEditing) {
|
||||
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))->
|
||||
getCursor(), false);
|
||||
ViewController::get()->getGameListView((*it))->getCursor(), false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -511,13 +520,13 @@ void ISimpleGameListView::generateGamelistInfo(FileData* cursor, FileData* first
|
|||
|
||||
if (idx->isFiltered()) {
|
||||
mIsFiltered = true;
|
||||
mFilteredGameCount = static_cast<unsigned int>(rootFolder->
|
||||
getFilesRecursive(GAME, true, false).size());
|
||||
mFilteredGameCount =
|
||||
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
|
||||
// apply to such entries as well and this will be indicated with a separate '+ XX'
|
||||
// in the GamelistInfo field.
|
||||
mFilteredGameCountAll = static_cast<unsigned int>(rootFolder->
|
||||
getFilesRecursive(GAME, true, true).size());
|
||||
mFilteredGameCountAll =
|
||||
static_cast<unsigned int>(rootFolder->getFilesRecursive(GAME, true, true).size());
|
||||
}
|
||||
|
||||
if (firstEntry->getParent() && firstEntry->getParent()->getType() == FOLDER)
|
||||
|
@ -541,7 +550,7 @@ void ISimpleGameListView::generateFirstLetterIndex(const std::vector<FileData*>&
|
|||
else
|
||||
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.
|
||||
for (auto it = files.begin(); it != files.end(); it++) {
|
||||
|
@ -553,16 +562,20 @@ void ISimpleGameListView::generateFirstLetterIndex(const std::vector<FileData*>&
|
|||
|
||||
// Build the index.
|
||||
for (auto it = files.begin(); it != files.end(); it++) {
|
||||
if ((*it)->getType() == FOLDER && (*it)->getFavorite() &&
|
||||
favoritesSorting && !onlyFavorites)
|
||||
if ((*it)->getType() == FOLDER && (*it)->getFavorite() && favoritesSorting &&
|
||||
!onlyFavorites) {
|
||||
hasFavorites = true;
|
||||
else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders)
|
||||
}
|
||||
else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders) {
|
||||
hasFolders = true;
|
||||
else if ((*it)->getType() == GAME && (*it)->getFavorite() &&
|
||||
favoritesSorting && !onlyFavorites)
|
||||
}
|
||||
else if ((*it)->getType() == GAME && (*it)->getFavorite() && favoritesSorting &&
|
||||
!onlyFavorites) {
|
||||
hasFavorites = true;
|
||||
else
|
||||
}
|
||||
else {
|
||||
mFirstLetterIndex.push_back(Utils::String::getFirstCharacter((*it)->getSortName()));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and make each entry unique.
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#ifndef 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/TextComponent.h"
|
||||
#include "views/gamelist/IGameListView.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
|
@ -39,9 +39,13 @@ public:
|
|||
// These functions are used to retain the folder cursor history, for instance
|
||||
// during a view reload. The calling function stores the history temporarily.
|
||||
void copyCursorHistory(std::vector<FileData*>& cursorHistory) override
|
||||
{ cursorHistory = mCursorStackHistory; };
|
||||
{
|
||||
cursorHistory = mCursorStackHistory;
|
||||
};
|
||||
void populateCursorHistory(std::vector<FileData*>& cursorHistory) override
|
||||
{ mCursorStackHistory = cursorHistory; };
|
||||
{
|
||||
mCursorStackHistory = cursorHistory;
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual std::string getQuickSystemSelectRightButton() = 0;
|
||||
|
|
|
@ -16,67 +16,62 @@
|
|||
#if defined(BUILD_VLC_PLAYER)
|
||||
#include "components/VideoVlcComponent.h"
|
||||
#endif
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "AudioManager.h"
|
||||
#include "CollectionSystemsManager.h"
|
||||
#include "SystemData.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#define FADE_IN_START_OPACITY 0.5f
|
||||
#define FADE_IN_TIME 650
|
||||
|
||||
VideoGameListView::VideoGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: BasicGameListView(window, root),
|
||||
mDescContainer(window),
|
||||
mDescription(window),
|
||||
mGamelistInfo(window),
|
||||
|
||||
mThumbnail(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
mVideo(nullptr),
|
||||
mVideoPlaying(false),
|
||||
|
||||
mLblRating(window),
|
||||
mLblReleaseDate(window),
|
||||
mLblDeveloper(window),
|
||||
mLblPublisher(window),
|
||||
mLblGenre(window),
|
||||
mLblPlayers(window),
|
||||
mLblLastPlayed(window),
|
||||
mLblPlayCount(window),
|
||||
|
||||
mRating(window),
|
||||
mReleaseDate(window),
|
||||
mDeveloper(window),
|
||||
mPublisher(window),
|
||||
mGenre(window),
|
||||
mPlayers(window),
|
||||
mLastPlayed(window),
|
||||
mPlayCount(window),
|
||||
mName(window),
|
||||
mLastUpdated(nullptr)
|
||||
VideoGameListView::VideoGameListView(Window* window, FileData* root)
|
||||
: BasicGameListView(window, root)
|
||||
, mDescContainer(window)
|
||||
, mDescription(window)
|
||||
, mGamelistInfo(window)
|
||||
, mThumbnail(window)
|
||||
, mMarquee(window)
|
||||
, mImage(window)
|
||||
, mVideo(nullptr)
|
||||
, mVideoPlaying(false)
|
||||
, mLblRating(window)
|
||||
, mLblReleaseDate(window)
|
||||
, mLblDeveloper(window)
|
||||
, mLblPublisher(window)
|
||||
, mLblGenre(window)
|
||||
, mLblPlayers(window)
|
||||
, mLblLastPlayed(window)
|
||||
, mLblPlayCount(window)
|
||||
, mRating(window)
|
||||
, mReleaseDate(window)
|
||||
, mDeveloper(window)
|
||||
, mPublisher(window)
|
||||
, mGenre(window)
|
||||
, mPlayers(window)
|
||||
, mLastPlayed(window)
|
||||
, mPlayCount(window)
|
||||
, mName(window)
|
||||
, mLastUpdated(nullptr)
|
||||
{
|
||||
const float padding = 0.01f;
|
||||
|
||||
// Create the correct type of video window.
|
||||
#if defined(_RPI_)
|
||||
// Create the correct type of video window.
|
||||
#if defined(_RPI_)
|
||||
if (Settings::getInstance()->getBool("VideoOmxPlayer"))
|
||||
mVideo = new VideoOmxComponent(window);
|
||||
else if (Settings::getInstance()->getString("VideoPlayer") == "vlc")
|
||||
mVideo = new VideoVlcComponent(window);
|
||||
else
|
||||
mVideo = new VideoFFmpegComponent(window);
|
||||
#elif defined(BUILD_VLC_PLAYER)
|
||||
#elif defined(BUILD_VLC_PLAYER)
|
||||
if (Settings::getInstance()->getString("VideoPlayer") == "vlc")
|
||||
mVideo = new VideoVlcComponent(window);
|
||||
else
|
||||
mVideo = new VideoFFmpegComponent(window);
|
||||
#else
|
||||
#else
|
||||
mVideo = new VideoFFmpegComponent(window);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
|
||||
mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y());
|
||||
|
@ -87,21 +82,21 @@ VideoGameListView::VideoGameListView(
|
|||
mThumbnail.setOrigin(0.5f, 0.5f);
|
||||
mThumbnail.setPosition(2.0f, 2.0f);
|
||||
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);
|
||||
addChild(&mThumbnail);
|
||||
|
||||
// Marquee.
|
||||
mMarquee.setOrigin(0.5f, 0.5f);
|
||||
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);
|
||||
addChild(&mMarquee);
|
||||
|
||||
// Video.
|
||||
mVideo->setOrigin(0.5f, 0.5f);
|
||||
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);
|
||||
addChild(mVideo);
|
||||
|
||||
|
@ -140,8 +135,8 @@ VideoGameListView::VideoGameListView(
|
|||
addChild(&mName);
|
||||
|
||||
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
|
||||
mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setAutoScroll(true);
|
||||
mDescContainer.setDefaultZIndex(40);
|
||||
addChild(&mDescContainer);
|
||||
|
@ -160,10 +155,7 @@ VideoGameListView::VideoGameListView(
|
|||
initMDValues();
|
||||
}
|
||||
|
||||
VideoGameListView::~VideoGameListView()
|
||||
{
|
||||
delete mVideo;
|
||||
}
|
||||
VideoGameListView::~VideoGameListView() { delete mVideo; }
|
||||
|
||||
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;
|
||||
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",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
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",
|
||||
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);
|
||||
|
||||
initMDLabels();
|
||||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
assert(labels.size() == 8);
|
||||
std::vector<std::string> lblElements = {
|
||||
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
|
||||
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||
};
|
||||
std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
|
||||
"md_lbl_developer", "md_lbl_publisher",
|
||||
"md_lbl_genre", "md_lbl_players",
|
||||
"md_lbl_lastplayed", "md_lbl_playcount" };
|
||||
|
||||
for (unsigned int i = 0; i < labels.size(); i++)
|
||||
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||
|
@ -194,19 +187,19 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
initMDValues();
|
||||
std::vector<GuiComponent*> values = getMDValues();
|
||||
assert(values.size() == 8);
|
||||
std::vector<std::string> valElements = {
|
||||
"md_rating", "md_releasedate", "md_developer", "md_publisher",
|
||||
"md_genre", "md_players", "md_lastplayed", "md_playcount"
|
||||
};
|
||||
std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
|
||||
"md_publisher", "md_genre", "md_players",
|
||||
"md_lastplayed", "md_playcount" };
|
||||
|
||||
for (unsigned int i = 0; i < values.size(); i++)
|
||||
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
|
||||
|
||||
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.applyTheme(theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
mDescription.applyTheme(
|
||||
theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
|
||||
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
|
||||
// If there is no position defined in the theme for gamelistInfo, then hide it.
|
||||
|
@ -238,7 +231,7 @@ void VideoGameListView::initMDLabels()
|
|||
}
|
||||
else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -265,11 +258,11 @@ void VideoGameListView::initMDValues()
|
|||
|
||||
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++) {
|
||||
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() +
|
||||
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]->setDefaultZIndex(40);
|
||||
|
||||
|
@ -280,8 +273,8 @@ void VideoGameListView::initMDValues()
|
|||
}
|
||||
|
||||
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(),
|
||||
mSize.y() - mDescContainer.getPosition().y());
|
||||
}
|
||||
|
||||
void VideoGameListView::updateInfoPanel()
|
||||
|
@ -300,7 +293,7 @@ void VideoGameListView::updateInfoPanel()
|
|||
if (file) {
|
||||
// Always hide the metadata fields if browsing grouped custom collections.
|
||||
if (file->getSystem()->isCustomCollection() &&
|
||||
file->getPath() == file->getSystem()->getName())
|
||||
file->getPath() == file->getSystem()->getName())
|
||||
hideMetaDataFields = true;
|
||||
else
|
||||
hideMetaDataFields = (file->metadata.get("hidemetadata") == "true");
|
||||
|
@ -316,9 +309,9 @@ void VideoGameListView::updateInfoPanel()
|
|||
// or if we're in the grouped custom collection view.
|
||||
if (mList.isScrolling())
|
||||
if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") ||
|
||||
(mLastUpdated->getSystem()->isCustomCollection() &&
|
||||
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
|
||||
hideMetaDataFields = true;
|
||||
(mLastUpdated->getSystem()->isCustomCollection() &&
|
||||
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
|
||||
hideMetaDataFields = true;
|
||||
|
||||
if (hideMetaDataFields) {
|
||||
mLblRating.setVisible(false);
|
||||
|
@ -367,9 +360,9 @@ void VideoGameListView::updateInfoPanel()
|
|||
// 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.
|
||||
if (file->getSystem()->isCustomCollection() &&
|
||||
file->getPath() == file->getSystem()->getName()) {
|
||||
mRandomGame = CollectionSystemsManager::get()->
|
||||
updateCollectionFolderMetadata(file->getSystem());
|
||||
file->getPath() == file->getSystem()->getName()) {
|
||||
mRandomGame =
|
||||
CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem());
|
||||
if (mRandomGame) {
|
||||
mThumbnail.setImage(mRandomGame->getThumbnailPath());
|
||||
mMarquee.setImage(mRandomGame->getMarqueePath());
|
||||
|
@ -398,7 +391,6 @@ void VideoGameListView::updateInfoPanel()
|
|||
mVideo->setImage(file->getImagePath());
|
||||
mVideo->onHide();
|
||||
|
||||
|
||||
if (!mVideo->setVideo(file->getVideoPath()))
|
||||
mVideo->setDefaultVideo();
|
||||
}
|
||||
|
@ -418,21 +410,21 @@ void VideoGameListView::updateInfoPanel()
|
|||
if (mIsFiltered) {
|
||||
if (mFilteredGameCountAll == mFilteredGameCount)
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " " +
|
||||
std::to_string(mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
std::to_string(mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
else
|
||||
gamelistInfoString += ViewController::FILTER_CHAR + " " +
|
||||
std::to_string(mFilteredGameCount) + " + " +
|
||||
std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " +
|
||||
std::to_string(mGameCount);
|
||||
std::to_string(mFilteredGameCount) + " + " +
|
||||
std::to_string(mFilteredGameCountAll - mFilteredGameCount) +
|
||||
" / " + std::to_string(mGameCount);
|
||||
}
|
||||
else {
|
||||
gamelistInfoString += ViewController::CONTROLLER_CHAR + " " +
|
||||
std::to_string(mGameCount);
|
||||
gamelistInfoString +=
|
||||
ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
|
||||
if (!(file->getSystem()->isCollection() &&
|
||||
file->getSystem()->getFullName() == "favorites"))
|
||||
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " "
|
||||
+ std::to_string(mFavoritesGameCount);
|
||||
file->getSystem()->getFullName() == "favorites"))
|
||||
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
|
||||
std::to_string(mFavoritesGameCount);
|
||||
}
|
||||
|
||||
if (mIsFolder && infoAlign != ALIGN_RIGHT)
|
||||
|
@ -442,9 +434,9 @@ void VideoGameListView::updateInfoPanel()
|
|||
|
||||
// Fade in the game image.
|
||||
auto func = [this](float t) {
|
||||
mVideo->setOpacity(static_cast<unsigned char>(Math::lerp(
|
||||
static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
|
||||
};
|
||||
mVideo->setOpacity(static_cast<unsigned char>(
|
||||
Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
|
||||
};
|
||||
mVideo->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
|
||||
|
||||
mDescription.setText(file->metadata.get("desc"));
|
||||
|
@ -498,10 +490,7 @@ void VideoGameListView::updateInfoPanel()
|
|||
}
|
||||
}
|
||||
|
||||
void VideoGameListView::launch(FileData* game)
|
||||
{
|
||||
ViewController::get()->triggerGameLaunch(game);
|
||||
}
|
||||
void VideoGameListView::launch(FileData* game) { ViewController::get()->triggerGameLaunch(game); }
|
||||
|
||||
std::vector<TextComponent*> VideoGameListView::getMDLabels()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue