From 3a3471cfe8fdf4d64f7a893db9f7a67728bbefa6 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Tue, 5 Nov 2013 19:41:49 -0600 Subject: [PATCH] Combined FolderData and GameData into one class, FileData. You don't need to dynamic_cast everywhere to check things anymore. Folders can have metadata now (currently not set up). Metadata is now a public member variable instead of a function that returns a pointer to make actually using const possible. --- CMakeLists.txt | 7 +- src/FileData.cpp | 98 +++++++++++++++ src/FileData.h | 65 ++++++++-- src/FileSorts.cpp | 62 +++++++++ src/FileSorts.h | 14 +++ src/FolderData.cpp | 196 ----------------------------- src/FolderData.h | 60 --------- src/GameData.cpp | 84 ------------- src/GameData.h | 36 ------ src/ScraperCmdLine.cpp | 28 ++--- src/SystemData.cpp | 122 ++++++++++-------- src/SystemData.h | 29 ++--- src/XMLReader.cpp | 74 +++++------ src/components/GuiFastSelect.cpp | 13 +- src/components/GuiFastSelect.h | 4 +- src/components/GuiGameList.cpp | 121 ++++-------------- src/components/GuiGameList.h | 15 +-- src/components/GuiGameScraper.cpp | 6 +- src/components/GuiMetaDataEd.h | 1 - src/components/GuiScraperLog.cpp | 6 +- src/components/GuiScraperStart.cpp | 10 +- src/components/GuiScraperStart.h | 2 +- src/scrapers/GamesDBScraper.cpp | 2 +- src/scrapers/Scraper.cpp | 6 +- src/scrapers/Scraper.h | 3 +- src/scrapers/TheArchiveScraper.cpp | 2 +- 26 files changed, 416 insertions(+), 650 deletions(-) create mode 100644 src/FileData.cpp create mode 100644 src/FileSorts.cpp create mode 100644 src/FileSorts.h delete mode 100644 src/FolderData.cpp delete mode 100644 src/FolderData.h delete mode 100644 src/GameData.cpp delete mode 100644 src/GameData.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 343cc0931..31fad76de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,8 +138,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.h ${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/FileSorts.h ${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/HttpReq.h ${CMAKE_CURRENT_SOURCE_DIR}/src/ImageIO.h @@ -198,8 +197,8 @@ set(ES_HEADERS ) set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/FileSorts.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/HttpReq.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ImageIO.cpp diff --git a/src/FileData.cpp b/src/FileData.cpp new file mode 100644 index 000000000..958ecf499 --- /dev/null +++ b/src/FileData.cpp @@ -0,0 +1,98 @@ +#include "FileData.h" +#include + +namespace fs = boost::filesystem; + +std::string getCleanFileName(const fs::path& path) +{ + return regex_replace(path.stem().generic_string(), boost::regex("\\((.*)\\)|\\[(.*)\\]"), ""); +} + + +FileData::FileData(FileType type, const fs::path& path) + : mType(type), mPath(path), mParent(NULL), metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) // metadata is REALLY set in the constructor! +{ + // metadata needs at least a name field (since that's what getName() will return) + if(metadata.get("name").empty()) + metadata.set("name", getCleanFileName(mPath)); +} + +FileData::~FileData() +{ + if(mParent) + mParent->removeChild(this); +} + +const std::string& FileData::getThumbnailPath() const +{ + if(!metadata.get("thumbnail").empty()) + return metadata.get("thumbnail"); + else + return metadata.get("image"); +} + + +std::vector FileData::getFilesRecursive(unsigned int typeMask) const +{ + std::vector out; + + for(auto it = mChildren.begin(); it != mChildren.end(); it++) + { + if((*it)->getType() & typeMask) + out.push_back(*it); + + if((*it)->getChildren().size() > 0) + { + std::vector subchildren = (*it)->getFilesRecursive(typeMask); + out.insert(out.end(), subchildren.cbegin(), subchildren.cend()); + } + } + + return out; +} + +void FileData::addChild(FileData* file) +{ + assert(mType == FOLDER); + assert(file->getParent() == NULL); + + mChildren.push_back(file); + file->mParent = this; +} + +void FileData::removeChild(FileData* file) +{ + assert(mType == FOLDER); + assert(file->getParent() == this); + + for(auto it = mChildren.begin(); it != mChildren.end(); it++) + { + if(*it == file) + { + mChildren.erase(it); + return; + } + } + + // File somehow wasn't in our children. + assert(false); +} + +void FileData::sort(ComparisonFunction& comparator, bool ascending) +{ + std::sort(mChildren.begin(), mChildren.end(), comparator); + + for(auto it = mChildren.begin(); it != mChildren.end(); it++) + { + if((*it)->getChildren().size() > 0) + (*it)->sort(comparator, ascending); + } + + if(!ascending) + std::reverse(mChildren.begin(), mChildren.end()); +} + +void FileData::sort(const SortType& type) +{ + sort(*type.comparisonFunction, type.ascending); +} diff --git a/src/FileData.h b/src/FileData.h index de927a081..2ef987dc7 100644 --- a/src/FileData.h +++ b/src/FileData.h @@ -1,17 +1,62 @@ -#ifndef _FILEDATA_H_ -#define _FILEDATA_H_ +#pragma once +#include #include +#include +#include "MetaData.h" -//This is a really basic class that the GameData and FolderData subclass from. -//This lets us keep everything in one vector and not have to differentiate between files and folders when we just want to check the name, etc. +enum FileType +{ + GAME = 1, // Cannot have children. + FOLDER = 2 +}; + +// Used for loading/saving gamelist.xml. +const char* fileTypeToString(FileType type); +FileType stringToFileType(const char* str); + +std::string getCleanFileName(const boost::filesystem::path& path); + +// A tree node that holds information for a file. class FileData { public: - virtual ~FileData() { }; - virtual bool isFolder() const = 0; - virtual const std::string& getName() const = 0; - virtual const std::string& getPath() const = 0; -}; + FileData(FileType type, const boost::filesystem::path& path); + virtual ~FileData(); -#endif + inline const std::string& getName() const { return metadata.get("name"); } + inline FileType getType() const { return mType; } + inline const boost::filesystem::path& getPath() const { return mPath; } + inline FileData* getParent() const { return mParent; } + inline const std::vector& getChildren() const { return mChildren; } + + virtual const std::string& getThumbnailPath() const; + + std::vector getFilesRecursive(unsigned int typeMask) const; + + void addChild(FileData* file); // Error if mType != FOLDER + void removeChild(FileData* file); //Error if mType != FOLDER + + + typedef bool ComparisonFunction(const FileData* a, const FileData* b); + struct SortType + { + ComparisonFunction* comparisonFunction; + bool ascending; + std::string description; + + SortType(ComparisonFunction* sortFunction, bool sortAscending, const std::string & sortDescription) + : comparisonFunction(sortFunction), ascending(sortAscending), description(sortDescription) {} + }; + + void sort(ComparisonFunction& comparator, bool ascending = true); + void sort(const SortType& type); + + MetaDataList metadata; + +private: + FileType mType; + boost::filesystem::path mPath; + FileData* mParent; + std::vector mChildren; +}; diff --git a/src/FileSorts.cpp b/src/FileSorts.cpp new file mode 100644 index 000000000..5f5b02085 --- /dev/null +++ b/src/FileSorts.cpp @@ -0,0 +1,62 @@ +#include "FileSorts.h" + +namespace FileSorts +{ + const FileData::SortType typesArr[] = { + FileData::SortType(&compareFileName, true, "file name, ascending") + }; + + const std::vector SortTypes(typesArr, typesArr + sizeof(typesArr)/sizeof(typesArr[0])); + + //returns if file1 should come before file2 + bool compareFileName(const FileData* file1, const FileData* file2) + { + std::string name1 = file1->getName(); + std::string name2 = file2->getName(); + + //min of name1/name2 .length()s + unsigned int count = name1.length() > name2.length() ? name2.length() : name1.length(); + for(unsigned int i = 0; i < count; i++) + { + if(toupper(name1[i]) != toupper(name2[i])) + { + return toupper(name1[i]) < toupper(name2[i]); + } + } + + return name1.length() < name2.length(); + } + + bool compareRating(const FileData* file1, const FileData* file2) + { + //only games have rating metadata + if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) + { + return file1->metadata.getFloat("rating") < file2->metadata.getFloat("rating"); + } + + return false; + } + + bool compareTimesPlayed(const FileData* file1, const FileData* file2) + { + //only games have playcount metadata + if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) + { + return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); + } + + return false; + } + + bool compareLastPlayed(const FileData* file1, const FileData* file2) + { + //only games have lastplayed metadata + if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) + { + return (file1)->metadata.getTime("lastplayed") < (file2)->metadata.getTime("lastplayed"); + } + + return false; + } +}; diff --git a/src/FileSorts.h b/src/FileSorts.h new file mode 100644 index 000000000..e9f662437 --- /dev/null +++ b/src/FileSorts.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "FileData.h" + +namespace FileSorts +{ + bool compareFileName(const FileData* file1, const FileData* file2); + bool compareRating(const FileData* file1, const FileData* file2); + bool compareTimesPlayed(const FileData* file1, const FileData* fil2); + bool compareLastPlayed(const FileData* file1, const FileData* file2); + + extern const std::vector SortTypes; +}; diff --git a/src/FolderData.cpp b/src/FolderData.cpp deleted file mode 100644 index a94c7d47b..000000000 --- a/src/FolderData.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "FolderData.h" -#include "SystemData.h" -#include "GameData.h" -#include -#include - - -std::map FolderData::sortStateNameMap; - -bool FolderData::isFolder() const { return true; } -const std::string & FolderData::getName() const { return mName; } -const std::string & FolderData::getPath() const { return mPath; } -unsigned int FolderData::getFileCount() { return mFileVector.size(); } - - -FolderData::FolderData(SystemData* system, std::string path, std::string name) - : mSystem(system), mPath(path), mName(name) -{ - //first created folder data initializes the list - if (sortStateNameMap.empty()) { - sortStateNameMap[compareFileName] = "file name"; - sortStateNameMap[compareRating] = "rating"; - sortStateNameMap[compareTimesPlayed] = "times played"; - sortStateNameMap[compareLastPlayed] = "last time played"; - } -} - -FolderData::~FolderData() -{ - for(unsigned int i = 0; i < mFileVector.size(); i++) - { - delete mFileVector.at(i); - } - - mFileVector.clear(); -} - -void FolderData::pushFileData(FileData* file) -{ - mFileVector.push_back(file); -} - -//sort this folder and any subfolders -void FolderData::sort(ComparisonFunction & comparisonFunction, bool ascending) -{ - std::sort(mFileVector.begin(), mFileVector.end(), comparisonFunction); - - for(unsigned int i = 0; i < mFileVector.size(); i++) - { - if(mFileVector.at(i)->isFolder()) - ((FolderData*)mFileVector.at(i))->sort(comparisonFunction, ascending); - } - - if (!ascending) { - std::reverse(mFileVector.begin(), mFileVector.end()); - } -} - -//returns if file1 should come before file2 -bool FolderData::compareFileName(const FileData* file1, const FileData* file2) -{ - std::string name1 = file1->getName(); - std::string name2 = file2->getName(); - - //min of name1/name2 .length()s - unsigned int count = name1.length() > name2.length() ? name2.length() : name1.length(); - for(unsigned int i = 0; i < count; i++) - { - if(toupper(name1[i]) != toupper(name2[i])) - { - return toupper(name1[i]) < toupper(name2[i]); - } - } - - return name1.length() < name2.length(); -} - -bool FolderData::compareRating(const FileData* file1, const FileData* file2) -{ - //we need game data. try to cast - const GameData * game1 = dynamic_cast(file1); - const GameData * game2 = dynamic_cast(file2); - if (game1 != nullptr && game2 != nullptr) { - return const_cast(game1)->metadata()->getFloat("rating") < const_cast(game2)->metadata()->getFloat("rating"); - } - return false; -} - -bool FolderData::compareTimesPlayed(const FileData* file1, const FileData* file2) -{ - //we need game data. try to cast - const GameData * game1 = dynamic_cast(file1); - const GameData * game2 = dynamic_cast(file2); - if (game1 != nullptr && game2 != nullptr) { - return const_cast(game1)->metadata()->getInt("playcount") < const_cast(game2)->metadata()->getInt("playcount"); - } - return false; -} - -bool FolderData::compareLastPlayed(const FileData* file1, const FileData* file2) -{ - //we need game data. try to cast - const GameData * game1 = dynamic_cast(file1); - const GameData * game2 = dynamic_cast(file2); - if (game1 != nullptr && game2 != nullptr) { - return const_cast(game1)->metadata()->getTime("lastplayed") < const_cast(game2)->metadata()->getTime("lastplayed"); - } - return false; -} - -std::string FolderData::getSortStateName(ComparisonFunction & comparisonFunction, bool ascending) -{ - std::string temp = sortStateNameMap[comparisonFunction]; - if (ascending) { - temp.append(" (ascending)"); - } - else { - temp.append(" (descending)"); - } - return temp; -} - -FileData* FolderData::getFile(unsigned int i) const -{ - return mFileVector.at(i); -} - -std::vector FolderData::getFiles(bool onlyFiles) const -{ - std::vector temp; - //now check if a child is a folder and get those children in turn - std::vector::const_iterator fdit = mFileVector.cbegin(); - while(fdit != mFileVector.cend()) { - //dynamically try to cast to FolderData type - FolderData * folder = dynamic_cast(*fdit); - if (folder != nullptr) { - //add this only when user wanted it - if (!onlyFiles) { - temp.push_back(*fdit); - } - } - else { - temp.push_back(*fdit); - } - ++fdit; - } - return temp; -} - -std::vector FolderData::getFilesRecursive(bool onlyFiles) const -{ - std::vector temp; - //now check if a child is a folder and get those children in turn - std::vector::const_iterator fdit = mFileVector.cbegin(); - while(fdit != mFileVector.cend()) { - //dynamically try to cast to FolderData type - FolderData * folder = dynamic_cast(*fdit); - if (folder != nullptr) { - //add this onyl when user wanted it - if (!onlyFiles) { - temp.push_back(*fdit); - } - //worked. Is actual folder data. recurse - std::vector children = folder->getFilesRecursive(onlyFiles); - //insert children into return vector - temp.insert(temp.end(), children.cbegin(), children.cend()); - } - else { - temp.push_back(*fdit); - } - ++fdit; - } - return temp; -} - -void FolderData::removeFileRecursive(FileData* f) -{ - auto iter = mFileVector.begin(); - while(iter != mFileVector.end()) - { - if(*iter == f) - { - delete *iter; - iter = mFileVector.erase(iter); - }else{ - - FolderData* folder = dynamic_cast(*iter); - if(folder) - { - folder->removeFileRecursive(f); - } - - iter++; - } - } -} diff --git a/src/FolderData.h b/src/FolderData.h deleted file mode 100644 index 2d073b074..000000000 --- a/src/FolderData.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _FOLDER_H_ -#define _FOLDER_H_ - -#include -#include - -#include "FileData.h" - - -class SystemData; - -//This class lets us hold a vector of FileDatas within under a common name. -class FolderData : public FileData -{ -public: - typedef bool ComparisonFunction(const FileData* a, const FileData* b); - struct SortState - { - ComparisonFunction & comparisonFunction; - bool ascending; - std::string description; - - SortState(ComparisonFunction & sortFunction, bool sortAscending, const std::string & sortDescription) : comparisonFunction(sortFunction), ascending(sortAscending), description(sortDescription) {} - }; - -private: - static std::map sortStateNameMap; - -public: - FolderData(SystemData* system, std::string path, std::string name); - ~FolderData(); - - bool isFolder() const; - const std::string & getName() const; - const std::string & getPath() const; - - unsigned int getFileCount(); - FileData* getFile(unsigned int i) const; - std::vector getFiles(bool onlyFiles = false) const; - std::vector getFilesRecursive(bool onlyFiles = false) const; - - void removeFileRecursive(FileData* file); - - void pushFileData(FileData* file); - - void sort(ComparisonFunction & comparisonFunction = compareFileName, bool ascending = true); - static bool compareFileName(const FileData* file1, const FileData* file2); - static bool compareRating(const FileData* file1, const FileData* file2); - static bool compareTimesPlayed(const FileData* file1, const FileData* file2); - static bool compareLastPlayed(const FileData* file1, const FileData* file2); - static std::string getSortStateName(ComparisonFunction & comparisonFunction = compareFileName, bool ascending = true); - -private: - SystemData* mSystem; - std::string mPath; - std::string mName; - std::vector mFileVector; -}; - -#endif diff --git a/src/GameData.cpp b/src/GameData.cpp deleted file mode 100644 index d679784b1..000000000 --- a/src/GameData.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "GameData.h" -#include -#include -#include -#include -#include - -GameData::GameData(const std::string& path, const MetaDataList& metadata) - : mPath(path), mBaseName(boost::filesystem::path(path).stem().string()), mMetaData(metadata) -{ - if(mMetaData.get("name").empty()) - mMetaData.set("name", mBaseName); -} - -bool GameData::isFolder() const -{ - return false; -} - -const std::string& GameData::getName() const -{ - return mMetaData.get("name"); -} - -const std::string& GameData::getPath() const -{ - return mPath; -} - -std::string GameData::getBashPath() const -{ - //a quick and dirty way to insert a backslash before most characters that would mess up a bash path - std::string path = getPath(); - - const char* invalidChars = " '\"\\!$^&*(){}[]?;<>"; - for(unsigned int i = 0; i < path.length(); i++) - { - char c; - unsigned int charNum = 0; - do { - c = invalidChars[charNum]; - if(path[i] == c) - { - path.insert(i, "\\"); - i++; - break; - } - charNum++; - } while(c != '\0'); - } - - return path; -} - -//returns the boost::filesystem stem of our path - e.g. for "/foo/bar.rom" returns "bar" -std::string GameData::getBaseName() const -{ - return mBaseName; -} - -std::string GameData::getCleanName() const -{ - return regex_replace(mBaseName, boost::regex("\\((.*)\\)|\\[(.*)\\]"), ""); -} - -void GameData::incTimesPlayed() -{ - int timesPlayed = metadata()->getInt("playcount"); - timesPlayed++; - - std::stringstream ss; - metadata()->set("playcount", std::to_string(static_cast(timesPlayed))); -} - -void GameData::lastPlayedNow() -{ - boost::posix_time::ptime time = boost::posix_time::second_clock::universal_time(); - metadata()->setTime("lastplayed", time); -} - -MetaDataList* GameData::metadata() -{ - return &mMetaData; -} diff --git a/src/GameData.h b/src/GameData.h deleted file mode 100644 index 27ce4f099..000000000 --- a/src/GameData.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef _GAMEDATA_H_ -#define _GAMEDATA_H_ - -#include - -#include "FileData.h" -#include "MetaData.h" - -//This class holds information about a game: at the least, its name, system, and path. Additional information is optional and read by other classes. -class GameData : public FileData -{ -public: - GameData(const std::string& path, const MetaDataList& metadata); - - const std::string& getName() const override; - const std::string& getPath() const override; - - void incTimesPlayed(); - void lastPlayedNow(); - - std::string getBashPath() const; - std::string getBaseName() const; - std::string getCleanName() const; - - bool isFolder() const override; - - MetaDataList* metadata(); - -private: - const std::string mPath; - const std::string mBaseName; - - MetaDataList mMetaData; -}; - -#endif diff --git a/src/ScraperCmdLine.cpp b/src/ScraperCmdLine.cpp index 53fce16c5..4fd535e78 100644 --- a/src/ScraperCmdLine.cpp +++ b/src/ScraperCmdLine.cpp @@ -141,7 +141,7 @@ int run_scraper_cmdline() std::shared_ptr scraper = Settings::getInstance()->getScraper(); for(auto sysIt = systems.begin(); sysIt != systems.end(); sysIt++) { - std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(true); + std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(GAME); ScraperSearchParams params; params.system = (*sysIt); @@ -149,15 +149,15 @@ int run_scraper_cmdline() for(auto gameIt = files.begin(); gameIt != files.end(); gameIt++) { params.nameOverride = ""; - params.game = (GameData*)(*gameIt); + params.game = *gameIt; //print original search term - out << params.game->getCleanName() << "...\n"; + out << getCleanFileName(params.game->getPath()) << "...\n"; //need to take into account filter_choice if(filter_choice == FILTER_MISSING_IMAGES) { - if(!params.game->metadata()->get("image").empty()) //maybe should also check if the image file exists/is a URL + if(!params.game->metadata.get("image").empty()) //maybe should also check if the image file exists/is a URL { out << " Skipping, metadata \"image\" entry is not empty.\n"; continue; @@ -212,7 +212,7 @@ int run_scraper_cmdline() if(choice >= 0 && choice < (int)mdls.size()) { - *params.game->metadata() = mdls.at(choice); + params.game->metadata = mdls.at(choice); break; }else{ out << "Invalid choice.\n"; @@ -223,7 +223,7 @@ int run_scraper_cmdline() //automatic mode //always choose the first choice out << " name -> " << mdls.at(0).get("name") << "\n"; - *params.game->metadata() = mdls.at(0); + params.game->metadata = mdls.at(0); break; } @@ -238,32 +238,32 @@ int run_scraper_cmdline() for(auto sysIt = systems.begin(); sysIt != systems.end(); sysIt++) { - std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(true); + std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(GAME); for(auto gameIt = files.begin(); gameIt != files.end(); gameIt++) { - GameData* game = (GameData*)(*gameIt); - const std::vector& mdd = game->metadata()->getMDD(); + FileData* game = *gameIt; + const std::vector& mdd = game->metadata.getMDD(); for(auto i = mdd.begin(); i != mdd.end(); i++) { std::string key = i->key; - std::string url = game->metadata()->get(key); + std::string url = game->metadata.get(key); if(i->type == MD_IMAGE_PATH && HttpReq::isUrl(url)) { std::string urlShort = url.substr(0, url.length() > 35 ? 35 : url.length()); if(url.length() != urlShort.length()) urlShort += "..."; - out << " " << game->metadata()->get("name") << " [from: " << urlShort << "]...\n"; + out << " " << game->metadata.get("name") << " [from: " << urlShort << "]...\n"; ScraperSearchParams p; p.game = game; p.system = *sysIt; - game->metadata()->set(key, downloadImage(url, getSaveAsPath(p, key, url))); - if(game->metadata()->get(key).empty()) + game->metadata.set(key, downloadImage(url, getSaveAsPath(p, key, url))); + if(game->metadata.get(key).empty()) { out << " FAILED! Skipping.\n"; - game->metadata()->set(key, url); //result URL to what it was if download failed, retry some other time + game->metadata.set(key, url); //result URL to what it was if download failed, retry some other time } } } diff --git a/src/SystemData.cpp b/src/SystemData.cpp index 84b3e7ddd..d162a4b00 100644 --- a/src/SystemData.cpp +++ b/src/SystemData.cpp @@ -1,5 +1,4 @@ #include "SystemData.h" -#include "GameData.h" #include "XMLReader.h" #include #include @@ -12,14 +11,12 @@ #include "InputManager.h" #include #include "Settings.h" +#include "FileSorts.h" std::vector SystemData::sSystemVector; namespace fs = boost::filesystem; -std::string SystemData::getStartPath() { return mStartPath; } -std::vector SystemData::getExtensions() { return mSearchExtensions; } - SystemData::SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector& extensions, const std::string& command, PlatformIds::PlatformId platformId) { @@ -38,7 +35,8 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con mLaunchCommand = command; mPlatformId = platformId; - mRootFolder = new FolderData(this, mStartPath, "Search Root"); + mRootFolder = new FileData(FOLDER, mStartPath); + mRootFolder->metadata.set("name", "Search Root"); if(!Settings::getInstance()->getBool("PARSEGAMELISTONLY")) populateFolder(mRootFolder); @@ -46,15 +44,17 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) parseGamelist(this); - mRootFolder->sort(); + mRootFolder->sort(FileSorts::SortTypes.at(0)); } SystemData::~SystemData() { //save changed game data back to xml - if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) { + if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) + { updateGamelist(this); } + delete mRootFolder; } @@ -68,7 +68,33 @@ std::string strreplace(std::string& str, std::string replace, std::string with) return str; } -void SystemData::launchGame(Window* window, GameData* game) +std::string escapePath(const boost::filesystem::path& path) +{ + // a quick and dirty way to insert a backslash before most characters that would mess up a bash path; + // someone with regex knowledge should make this into a one-liner + std::string pathStr = path.generic_string(); + + const char* invalidChars = " '\"\\!$^&*(){}[]?;<>"; + for(unsigned int i = 0; i < pathStr.length(); i++) + { + char c; + unsigned int charNum = 0; + do { + c = invalidChars[charNum]; + if(pathStr[i] == c) + { + pathStr.insert(i, "\\"); + i++; + break; + } + charNum++; + } while(c != '\0'); + } + + return pathStr; +} + +void SystemData::launchGame(Window* window, FileData* game) { LOG(LogInfo) << "Attempting to launch game..."; @@ -78,9 +104,13 @@ void SystemData::launchGame(Window* window, GameData* game) std::string command = mLaunchCommand; - command = strreplace(command, "%ROM%", game->getBashPath()); - command = strreplace(command, "%BASENAME%", game->getBaseName()); - command = strreplace(command, "%ROM_RAW%", game->getPath()); + const std::string rom = escapePath(game->getPath()); + const std::string basename = game->getPath().stem().string(); + const std::string rom_raw = game->getPath().string(); + + command = strreplace(command, "%ROM%", rom); + command = strreplace(command, "%BASENAME%", basename); + command = strreplace(command, "%ROM_RAW%", rom_raw); LOG(LogInfo) << " " << command; std::cout << "==============================================\n"; @@ -97,25 +127,31 @@ void SystemData::launchGame(Window* window, GameData* game) AudioManager::getInstance()->init(); window->normalizeNextUpdate(); - //update number of times the game has been launched and the time - game->incTimesPlayed(); - game->lastPlayedNow(); + //update number of times the game has been launched + int timesPlayed = game->metadata.getInt("playcount") + 1; + game->metadata.set("playcount", std::to_string(static_cast(timesPlayed))); + + //update last played time + boost::posix_time::ptime time = boost::posix_time::second_clock::universal_time(); + game->metadata.setTime("lastplayed", time); } -void SystemData::populateFolder(FolderData* folder) +void SystemData::populateFolder(FileData* folder) { - std::string folderPath = folder->getPath(); + const fs::path& folderPath = folder->getPath(); if(!fs::is_directory(folderPath)) { LOG(LogWarning) << "Error - folder with path \"" << folderPath << "\" is not a directory!"; return; } + const std::string folderStr = folderPath.generic_string(); + //make sure that this isn't a symlink to a thing we already have if(fs::is_symlink(folderPath)) { //if this symlink resolves to somewhere that's at the beginning of our path, it's gonna recurse - if(folderPath.find(fs::canonical(folderPath).string()) == 0) + if(folderStr.find(fs::canonical(folderPath).generic_string()) == 0) { LOG(LogWarning) << "Skipping infinitely recursive symlink \"" << folderPath << "\""; return; @@ -142,39 +178,26 @@ void SystemData::populateFolder(FolderData* folder) isGame = false; if(std::find(mSearchExtensions.begin(), mSearchExtensions.end(), extension) != mSearchExtensions.end()) { - GameData* newGame = new GameData(filePath.generic_string(), MetaDataList(GAME_METADATA)); - folder->pushFileData(newGame); + FileData* newGame = new FileData(GAME, filePath.generic_string()); + folder->addChild(newGame); isGame = true; } //add directories that also do not match an extension as folders if(!isGame && fs::is_directory(filePath)) { - FolderData* newFolder = new FolderData(this, filePath.generic_string(), filePath.stem().string()); + FileData* newFolder = new FileData(FOLDER, filePath.generic_string()); populateFolder(newFolder); //ignore folders that do not contain games - if(newFolder->getFileCount() == 0) + if(newFolder->getChildren().size() == 0) delete newFolder; else - folder->pushFileData(newFolder); + folder->addChild(newFolder); } } } -std::string SystemData::getName() -{ - return mName; -} - -std::string SystemData::getFullName() -{ - if(mFullName.empty()) - return mName; - else - return mFullName; -} - //creates systems from information located in a config file bool SystemData::loadConfig(const std::string& path, bool writeExample) { @@ -241,7 +264,7 @@ bool SystemData::loadConfig(const std::string& path, bool writeExample) path = genericPath.generic_string(); SystemData* newSys = new SystemData(name, fullname, path, extensions, cmd, platformId); - if(newSys->getRootFolder()->getFileCount() == 0) + if(newSys->getRootFolder()->getChildren().size() == 0) { LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it."; delete newSys; @@ -312,22 +335,16 @@ std::string SystemData::getConfigPath() return(home + "/.emulationstation/es_systems.cfg"); } -FolderData* SystemData::getRootFolder() +std::string SystemData::getGamelistPath() const { - return mRootFolder; -} + fs::path filePath; -std::string SystemData::getGamelistPath() -{ - std::string filePath; - - filePath = mRootFolder->getPath() + "/gamelist.xml"; + filePath = mRootFolder->getPath() / "gamelist.xml"; if(fs::exists(filePath)) - return filePath; + return filePath.generic_string(); - filePath = getHomePath(); - filePath += "/.emulationstation/"+ getName() + "/gamelist.xml"; - return filePath; + filePath = getHomePath() + "/.emulationstation/"+ getName() + "/gamelist.xml"; + return filePath.generic_string(); } bool SystemData::hasGamelist() @@ -335,12 +352,7 @@ bool SystemData::hasGamelist() return (fs::exists(getGamelistPath())); } -PlatformIds::PlatformId SystemData::getPlatformId() +unsigned int SystemData::getGameCount() const { - return mPlatformId; -} - -unsigned int SystemData::getGameCount() -{ - return mRootFolder->getFilesRecursive(true).size(); + return mRootFolder->getFilesRecursive(GAME).size(); } diff --git a/src/SystemData.h b/src/SystemData.h index 6040d2e61..c0f2b0e92 100644 --- a/src/SystemData.h +++ b/src/SystemData.h @@ -3,13 +3,11 @@ #include #include -#include "FolderData.h" +#include "FileData.h" #include "Window.h" #include "MetaData.h" #include "PlatformId.h" -class GameData; - class SystemData { public: @@ -17,20 +15,19 @@ public: const std::string& command, PlatformIds::PlatformId platformId = PlatformIds::PLATFORM_UNKNOWN); ~SystemData(); - FolderData* getRootFolder(); + 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 mStartPath; } + inline const std::vector& getExtensions() const { return mSearchExtensions; } + inline PlatformIds::PlatformId getPlatformId() const { return mPlatformId; } - std::string getName(); - std::string getFullName(); - std::string getStartPath(); - std::vector getExtensions(); - PlatformIds::PlatformId getPlatformId(); - - std::string getGamelistPath(); + std::string getGamelistPath() const; bool hasGamelist(); - unsigned int getGameCount(); + unsigned int getGameCount() const; - void launchGame(Window* window, GameData* game); + void launchGame(Window* window, FileData* game); static void deleteSystems(); static bool loadConfig(const std::string& path, bool writeExampleIfNonexistant = true); //Load the system config file at getConfigPath(). Returns true if no errors were encountered. An example can be written if the file doesn't exist. @@ -38,6 +35,7 @@ public: static std::string getConfigPath(); static std::vector sSystemVector; + private: std::string mName; std::string mFullName; @@ -46,10 +44,9 @@ private: std::string mLaunchCommand; PlatformIds::PlatformId mPlatformId; - void populateFolder(FolderData* folder); + void populateFolder(FileData* folder); - FolderData* mRootFolder; + FileData* mRootFolder; }; #endif - diff --git a/src/XMLReader.cpp b/src/XMLReader.cpp index 35f8fdcda..fbdf19bd6 100644 --- a/src/XMLReader.cpp +++ b/src/XMLReader.cpp @@ -1,6 +1,5 @@ #include "XMLReader.h" #include "SystemData.h" -#include "GameData.h" #include "pugiXML/pugixml.hpp" #include #include "Log.h" @@ -8,27 +7,25 @@ //this is obviously an incredibly inefficient way to go about searching //but I don't think it'll matter too much with the size of most collections -GameData* searchFolderByPath(FolderData* folder, std::string const& path) +FileData* searchFolderByPath(FileData* folder, std::string const& path) { - for(unsigned int i = 0; i < folder->getFileCount(); i++) + for(auto it = folder->getChildren().begin(); it != folder->getChildren().end(); it++) { - FileData* file = folder->getFile(i); - - if(file->isFolder()) + if((*it)->getType() == FOLDER) { - GameData* result = searchFolderByPath((FolderData*)file, path); + FileData* result = searchFolderByPath(*it, path); if(result) - return (GameData*)result; + return result; }else{ - if(file->getPath() == path) - return (GameData*)file; + if((*it)->getPath().generic_string() == path) + return *it; } } return NULL; } -GameData* createGameFromPath(std::string gameAbsPath, SystemData* system) +FileData* createGameFromPath(std::string gameAbsPath, SystemData* system) { std::string gamePath = gameAbsPath; std::string sysPath = system->getStartPath(); @@ -46,11 +43,10 @@ GameData* createGameFromPath(std::string gameAbsPath, SystemData* system) //make our way through the directory tree finding each folder in our path or creating it if it doesn't exist - FolderData* folder = system->getRootFolder(); + FileData* folder = system->getRootFolder(); size_t separator = 0; size_t nextSeparator = 0; - unsigned int loops = 0; while(nextSeparator != std::string::npos) { //determine which chunk of the path we're testing right now @@ -63,12 +59,11 @@ GameData* createGameFromPath(std::string gameAbsPath, SystemData* system) //see if the folder already exists bool foundFolder = false; - for(unsigned int i = 0; i < folder->getFileCount(); i++) + for(auto it = folder->getChildren().begin(); it != folder->getChildren().end(); it++) { - FileData* checkFolder = folder->getFile(i); - if(checkFolder->isFolder() && checkFolder->getName() == checkName) + if((*it)->getType() == FOLDER && (*it)->getName() == checkName) { - folder = (FolderData*)checkFolder; + folder = *it; foundFolder = true; break; } @@ -77,22 +72,14 @@ GameData* createGameFromPath(std::string gameAbsPath, SystemData* system) //the folder didn't already exist, so create it if(!foundFolder) { - FolderData* newFolder = new FolderData(system, folder->getPath() + "/" + checkName, checkName); - folder->pushFileData(newFolder); + FileData* newFolder = new FileData(FOLDER, folder->getPath() / checkName); + folder->addChild(newFolder); folder = newFolder; } - - //if for some reason this function is broken, break out of this while instead of freezing - if(loops > gamePath.length() * 2) - { - LOG(LogError) << "createGameFromPath breaking out of loop for path \"" << gamePath << "\" to prevent infinite loop (please report this)"; - break; - } - loops++; } - GameData* game = new GameData(gameAbsPath, MetaDataList(GAME_METADATA)); - folder->pushFileData(game); + FileData* game = new FileData(GAME, gameAbsPath); + folder->addChild(game); return game; } @@ -150,40 +137,41 @@ void parseGamelist(SystemData* system) if(boost::filesystem::exists(path)) { - GameData* game = searchFolderByPath(system->getRootFolder(), path); + FileData* game = searchFolderByPath(system->getRootFolder(), path); if(game == NULL) game = createGameFromPath(path, system); //load the metadata - *(game->metadata()) = MetaDataList::createFromXML(GAME_METADATA, gameNode); + std::string defaultName = game->metadata.get("name"); + game->metadata = MetaDataList::createFromXML(GAME_METADATA, gameNode); //make sure name gets set if one didn't exist - if(game->metadata()->get("name").empty()) - game->metadata()->set("name", game->getBaseName()); + if(game->metadata.get("name").empty()) + game->metadata.set("name", defaultName); }else{ LOG(LogWarning) << "Game at \"" << path << "\" does not exist!"; } } } -void addGameDataNode(pugi::xml_node& parent, const GameData* game, SystemData* system) +void addGameDataNode(pugi::xml_node& parent, const FileData* game, SystemData* system) { //create game and add to parent node pugi::xml_node newGame = parent.append_child("game"); //write metadata - const_cast(game)->metadata()->appendToXML(newGame, true); + game->metadata.appendToXML(newGame, true); if(newGame.children().begin() == newGame.child("name") //first element is name && ++newGame.children().begin() == newGame.children().end() //theres only one element - && newGame.child("name").text().get() == game->getBaseName()) //the name is the default + && newGame.child("name").text().get() == getCleanFileName(game->getPath())) //the name is the default { //if the only info is the default name, don't bother with this node parent.remove_child(newGame); }else{ //there's something useful in there so we'll keep the node, add the path - newGame.prepend_child("path").text().set(game->getPath().c_str()); + newGame.prepend_child("path").text().set(game->getPath().generic_string().c_str()); } } @@ -230,18 +218,16 @@ void updateGamelist(SystemData* system) } //now we have all the information from the XML. now iterate through all our games and add information from there - FolderData * rootFolder = system->getRootFolder(); + FileData* rootFolder = system->getRootFolder(); if (rootFolder != nullptr) { //get only files, no folders - std::vector files = rootFolder->getFilesRecursive(true); + std::vector files = rootFolder->getFilesRecursive(GAME); //iterate through all files, checking if they're already in the XML std::vector::const_iterator fit = files.cbegin(); while(fit != files.cend()) { - //try to cast to gamedata - const GameData * game = dynamic_cast(*fit); - if (game != nullptr) + if((*fit)->getType() == GAME) { //worked. check if this games' path can be found somewhere in the XML for(pugi::xml_node gameNode = root.child("game"); gameNode; gameNode = gameNode.next_sibling("game")) @@ -256,7 +242,7 @@ void updateGamelist(SystemData* system) //check paths. use the same directory separators boost::filesystem::path nodePath(pathNode.text().get()); - boost::filesystem::path gamePath(game->getPath()); + boost::filesystem::path gamePath((*fit)->getPath()); if (nodePath.generic_string() == gamePath.generic_string()) { //found the game. remove it. it will be added again later with updated values @@ -268,7 +254,7 @@ void updateGamelist(SystemData* system) //either the game content was removed, because it needs to be updated, //or didn't exist in the first place, so just add it - addGameDataNode(root, game, system); + addGameDataNode(root, *fit, system); } ++fit; } diff --git a/src/components/GuiFastSelect.cpp b/src/components/GuiFastSelect.cpp index 5adaec2b9..3a59398c5 100644 --- a/src/components/GuiFastSelect.cpp +++ b/src/components/GuiFastSelect.cpp @@ -2,6 +2,7 @@ #include "../Renderer.h" #include #include "GuiGameList.h" +#include "../FileSorts.h" #define DEFAULT_FS_IMAGE ":/frame.png" @@ -9,6 +10,8 @@ const std::string GuiFastSelect::LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const int GuiFastSelect::SCROLLSPEED = 100; const int GuiFastSelect::SCROLLDELAY = 507; +int GuiFastSelect::sortTypeId = 0; + GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent* list, char startLetter, ThemeComponent * theme) : GuiComponent(window), mParent(parent), mList(list), mTheme(theme), mBox(mWindow, "") { @@ -60,7 +63,7 @@ void GuiFastSelect::render(const Eigen::Affine3f& parentTrans) letterFont->drawCenteredText(LETTERS.substr(mLetterID, 1), 0, sh * 0.5f - (letterFont->getHeight() * 0.5f), mTextColor); subtextFont->drawCenteredText("Sort order:", 0, sh * 0.6f - (subtextFont->getHeight() * 0.5f), mTextColor); - std::string sortString = "<- " + mParent->getSortState().description + " ->"; + std::string sortString = "<- " + FileSorts::SortTypes.at(sortTypeId).description + " ->"; subtextFont->drawCenteredText(sortString, 0, sh * 0.6f + (subtextFont->getHeight() * 0.5f), mTextColor); } @@ -82,13 +85,16 @@ bool GuiFastSelect::input(InputConfig* config, Input input) if(config->isMappedTo("left", input) && input.value != 0) { - mParent->setPreviousSortIndex(); + sortTypeId--; + if(sortTypeId < 0) + sortTypeId = FileSorts::SortTypes.size() - 1; + mScrollSound->play(); return true; } else if(config->isMappedTo("right", input) && input.value != 0) { - mParent->setNextSortIndex(); + sortTypeId = (sortTypeId + 1) % FileSorts::SortTypes.size(); mScrollSound->play(); return true; } @@ -104,6 +110,7 @@ bool GuiFastSelect::input(InputConfig* config, Input input) if(config->isMappedTo("select", input) && input.value == 0) { setListPos(); + mParent->sort(FileSorts::SortTypes.at(sortTypeId)); delete this; return true; } diff --git a/src/components/GuiFastSelect.h b/src/components/GuiFastSelect.h index b2ee4befc..02cec1c03 100644 --- a/src/components/GuiFastSelect.h +++ b/src/components/GuiFastSelect.h @@ -3,7 +3,6 @@ #include "../GuiComponent.h" #include "../SystemData.h" -#include "../FolderData.h" #include "../Sound.h" #include "ThemeComponent.h" #include "TextListComponent.h" @@ -11,6 +10,7 @@ class GuiGameList; + class GuiFastSelect : public GuiComponent { public: @@ -26,6 +26,8 @@ private: static const int SCROLLSPEED; static const int SCROLLDELAY; + static int sortTypeId; + void setListPos(); void scroll(); void setLetterID(int id); diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index 88908c6d7..59e21c545 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -10,8 +10,6 @@ #include "GuiMetaDataEd.h" #include "GuiScraperStart.h" -std::vector GuiGameList::sortStates; - Eigen::Vector3f GuiGameList::getImagePos() { return Eigen::Vector3f(Renderer::getScreenWidth() * mTheme->getFloat("gameImageOffsetX"), Renderer::getScreenHeight() * mTheme->getFloat("gameImageOffsetY"), 0.0f); @@ -23,16 +21,12 @@ bool GuiGameList::isDetailed() const return false; //return true if any game has an image specified - for(unsigned int i = 0; i < mFolder->getFileCount(); i++) + for(auto it = mFolder->getChildren().begin(); it != mFolder->getChildren().end(); it++) { - if(!mFolder->getFile(i)->isFolder()) - { - GameData* game = (GameData*)(mFolder->getFile(i)); - if(!game->metadata()->get("image").empty()) - return true; - } + if(!(*it)->getThumbnailPath().empty()) + return true; } - + return false; } @@ -47,22 +41,9 @@ GuiGameList::GuiGameList(Window* window) : GuiComponent(window), mDescContainer(window), mTransitionImage(window, 0.0f, 0.0f, "", (float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight(), true), mHeaderText(mWindow), - sortStateIndex(Settings::getInstance()->getInt("GameListSortIndex")), mLockInput(false), mEffectFunc(NULL), mEffectTime(0), mGameLaunchEffectLength(700) { - //first object initializes the vector - if (sortStates.empty()) { - sortStates.push_back(FolderData::SortState(FolderData::compareFileName, true, "file name, ascending")); - sortStates.push_back(FolderData::SortState(FolderData::compareFileName, false, "file name, descending")); - sortStates.push_back(FolderData::SortState(FolderData::compareRating, true, "rating, ascending")); - sortStates.push_back(FolderData::SortState(FolderData::compareRating, false, "rating, descending")); - sortStates.push_back(FolderData::SortState(FolderData::compareTimesPlayed, true, "played least often")); - sortStates.push_back(FolderData::SortState(FolderData::compareTimesPlayed, false, "played most often")); - sortStates.push_back(FolderData::SortState(FolderData::compareLastPlayed, true, "played least recently")); - sortStates.push_back(FolderData::SortState(FolderData::compareLastPlayed, false, "played most recently")); - } - mImageAnimation.addChild(&mScreenshot); mDescContainer.addChild(&mReleaseDateLabel); mDescContainer.addChild(&mReleaseDate); @@ -142,18 +123,18 @@ bool GuiGameList::input(InputConfig* config, Input input) if(input.id == SDLK_F3) { - GameData* game = dynamic_cast(mList.getSelectedObject()); - if(game) + FileData* game = mList.getSelectedObject(); + if(game->getType() == GAME) { - FolderData* root = mSystem->getRootFolder(); + FileData* root = mSystem->getRootFolder(); ScraperSearchParams searchParams; searchParams.game = game; searchParams.system = mSystem; - mWindow->pushGui(new GuiMetaDataEd(mWindow, game->metadata(), game->metadata()->getMDD(), searchParams, game->getBaseName(), + mWindow->pushGui(new GuiMetaDataEd(mWindow, &game->metadata, game->metadata.getMDD(), searchParams, game->getPath().stem().string(), [&] { updateDetailData(); }, [game, root, this] { boost::filesystem::remove(game->getPath()); - root->removeFileRecursive(game); + root->removeChild(game); updateList(); })); } @@ -166,16 +147,16 @@ bool GuiGameList::input(InputConfig* config, Input input) return true; } - if(config->isMappedTo("a", input) && mFolder->getFileCount() > 0 && input.value != 0) + if(config->isMappedTo("a", input) && input.value != 0) { //play select sound mTheme->getSound("menuSelect")->play(); FileData* file = mList.getSelectedObject(); - if(file->isFolder()) //if you selected a folder, add this directory to the stack, and use the selected one + if(file->getType() == FOLDER) //if you selected a folder, add this directory to the stack, and use the selected one { mFolderStack.push(mFolder); - mFolder = (FolderData*)file; + mFolder = file; updateList(); updateDetailData(); return true; @@ -225,16 +206,6 @@ bool GuiGameList::input(InputConfig* config, Input input) } } - //change sort order - if(config->isMappedTo("sortordernext", input) && input.value != 0) { - setNextSortIndex(); - //std::cout << "Sort order is " << FolderData::getSortStateName(sortStates.at(sortStateIndex).comparisonFunction, sortStates.at(sortStateIndex).ascending) << std::endl; - } - else if(config->isMappedTo("sortorderprevious", input) && input.value != 0) { - setPreviousSortIndex(); - //std::cout << "Sort order is " << FolderData::getSortStateName(sortStates.at(sortStateIndex).comparisonFunction, sortStates.at(sortStateIndex).ascending) << std::endl; - } - //open the "start menu" if(config->isMappedTo("menu", input) && input.value != 0) { @@ -264,48 +235,10 @@ bool GuiGameList::input(InputConfig* config, Input input) return false; } -const FolderData::SortState & GuiGameList::getSortState() const -{ - return sortStates.at(sortStateIndex); -} - -void GuiGameList::setSortIndex(size_t index) -{ - //make the index valid - if (index >= sortStates.size()) { - index = 0; - } - if (index != sortStateIndex) { - //get sort state from vector and sort list - sortStateIndex = index; - sort(sortStates.at(sortStateIndex).comparisonFunction, sortStates.at(sortStateIndex).ascending); - } - //save new index to settings - Settings::getInstance()->setInt("GameListSortIndex", sortStateIndex); -} - -void GuiGameList::setNextSortIndex() -{ - //make the index wrap around - if ((sortStateIndex - 1) >= sortStates.size()) { - setSortIndex(0); - } - setSortIndex(sortStateIndex + 1); -} - -void GuiGameList::setPreviousSortIndex() -{ - //make the index wrap around - if (((int)sortStateIndex - 1) < 0) { - setSortIndex(sortStates.size() - 1); - } - setSortIndex(sortStateIndex - 1); -} - -void GuiGameList::sort(FolderData::ComparisonFunction & comparisonFunction, bool ascending) +void GuiGameList::sort(const FileData::SortType& type) { //resort list and update it - mFolder->sort(comparisonFunction, ascending); + mFolder->sort(type); updateList(); updateDetailData(); } @@ -314,14 +247,12 @@ void GuiGameList::updateList() { mList.clear(); - for(unsigned int i = 0; i < mFolder->getFileCount(); i++) + for(auto it = mFolder->getChildren().begin(); it != mFolder->getChildren().end(); it++) { - FileData* file = mFolder->getFile(i); - - if(file->isFolder()) - mList.addObject(file->getName(), file, mTheme->getColor("secondary")); + if((*it)->getType() == FOLDER) + mList.addObject((*it)->getName(), *it, mTheme->getColor("secondary")); else - mList.addObject(file->getName(), file, mTheme->getColor("primary")); + mList.addObject((*it)->getName(), *it, mTheme->getColor("primary")); } } @@ -391,17 +322,17 @@ void GuiGameList::updateTheme() void GuiGameList::updateDetailData() { - if(!isDetailed() || !mList.getSelectedObject() || mList.getSelectedObject()->isFolder()) + if(!isDetailed() || !mList.getSelectedObject() || mList.getSelectedObject()->getType() == FOLDER) { hideDetailData(); }else{ if(mDescContainer.getParent() != this) addChild(&mDescContainer); - GameData* game = (GameData*)mList.getSelectedObject(); + FileData* game = mList.getSelectedObject(); //set image to either "not found" image or metadata image - if(!boost::filesystem::exists(game->metadata()->get("image"))) + if(!boost::filesystem::exists(game->metadata.get("image"))) { //image doesn't exist if(mTheme->getString("imageNotFoundPath").empty()) @@ -413,7 +344,7 @@ void GuiGameList::updateDetailData() mScreenshot.setImage(mTheme->getString("imageNotFoundPath")); } }else{ - mScreenshot.setImage(game->metadata()->get("image")); + mScreenshot.setImage(game->metadata.get("image")); } Eigen::Vector3f imgOffset = Eigen::Vector3f(Renderer::getScreenWidth() * 0.10f, 0, 0); @@ -434,14 +365,14 @@ void GuiGameList::updateDetailData() mReleaseDateLabel.setPosition(0, 0); mReleaseDateLabel.setText("Released: "); mReleaseDate.setPosition(mReleaseDateLabel.getPosition().x() + mReleaseDateLabel.getSize().x(), mReleaseDateLabel.getPosition().y()); - mReleaseDate.setValue(game->metadata()->get("releasedate")); + mReleaseDate.setValue(game->metadata.get("releasedate")); mRating.setPosition(colwidth - mRating.getSize().x() - 12, 0); - mRating.setValue(game->metadata()->get("rating")); + mRating.setValue(game->metadata.get("rating")); mDescription.setPosition(0, mRating.getSize().y()); mDescription.setSize(Eigen::Vector2f(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03f), 0)); - mDescription.setText(game->metadata()->get("desc")); + mDescription.setText(game->metadata.get("desc")); } } @@ -557,7 +488,7 @@ void GuiGameList::updateGameLaunchEffect(int t) { //effect done mTransitionImage.setImage(""); //fixes "tried to bind uninitialized texture!" since copyScreen()'d textures don't reinit - mSystem->launchGame(mWindow, (GameData*)mList.getSelectedObject()); + mSystem->launchGame(mWindow, mList.getSelectedObject()); mEffectFunc = &GuiGameList::updateGameReturnEffect; mEffectTime = 0; mGameLaunchEffectLength = 700; diff --git a/src/components/GuiGameList.h b/src/components/GuiGameList.h index a080d55e1..7d29923b3 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -10,8 +10,6 @@ #include #include #include "../SystemData.h" -#include "../GameData.h" -#include "../FolderData.h" #include "ScrollableContainer.h" #include "RatingComponent.h" #include "DateTimeComponent.h" @@ -20,9 +18,6 @@ //It has a TextListComponent child that handles the game list, a ThemeComponent that handles the theming system, and an ImageComponent for game images. class GuiGameList : public GuiComponent { - static std::vector sortStates; - size_t sortStateIndex; - public: GuiGameList(Window* window); virtual ~GuiGameList(); @@ -35,11 +30,7 @@ public: void updateDetailData(); - const FolderData::SortState & getSortState() const; - void setSortIndex(size_t index); - void setNextSortIndex(); - void setPreviousSortIndex(); - void sort(FolderData::ComparisonFunction & comparisonFunction = FolderData::compareFileName, bool ascending = true); + void sort(const FileData::SortType& type); static GuiGameList* create(Window* window); @@ -55,8 +46,8 @@ private: std::string getThemeFile(); SystemData* mSystem; - FolderData* mFolder; - std::stack mFolderStack; + FileData* mFolder; + std::stack mFolderStack; int mSystemId; TextListComponent mList; diff --git a/src/components/GuiGameScraper.cpp b/src/components/GuiGameScraper.cpp index 973110d9b..fbae61f38 100644 --- a/src/components/GuiGameScraper.cpp +++ b/src/components/GuiGameScraper.cpp @@ -7,7 +7,7 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::function doneFunc, std::function skipFunc) : GuiComponent(window), mList(window, Eigen::Vector2i(2, 7 + MAX_SCRAPER_RESULTS)), mBox(window, ":/frame.png"), - mHeader(window, params.game->getBaseName(), Font::get(FONT_SIZE_MEDIUM)), + mHeader(window, getCleanFileName(params.game->getPath()), Font::get(FONT_SIZE_MEDIUM)), mResultName(window, "", Font::get(FONT_SIZE_MEDIUM)), mResultInfo(window), mResultDesc(window, "", Font::get(FONT_SIZE_SMALL)), @@ -56,7 +56,7 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std:: mResultName.setColor(0x3B56CCFF); mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), &mResultName, false, ComponentListComponent::AlignLeft); - mResultDesc.setText(params.game->metadata()->get("desc")); + mResultDesc.setText(params.game->metadata.get("desc")); mResultDesc.setSize(colWidth, 0); mResultInfo.addChild(&mResultDesc); mResultInfo.setSize(mResultDesc.getSize().x(), mResultDesc.getFont()->getHeight() * 3.0f); @@ -69,7 +69,7 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std:: //y = 3 is a spacer row mList.setEntry(Vector2i(0, 4), Vector2i(1, 1), &mSearchLabel, false, ComponentListComponent::AlignLeft); - mSearchText.setValue(!params.nameOverride.empty() ? params.nameOverride : params.game->getCleanName()); + mSearchText.setValue(!params.nameOverride.empty() ? params.nameOverride : getCleanFileName(params.game->getPath())); mSearchText.setSize(colWidth * 2 - mSearchLabel.getSize().x() - 20, mSearchText.getSize().y()); mList.setEntry(Vector2i(1, 4), Vector2i(1, 1), &mSearchText, true, ComponentListComponent::AlignRight); diff --git a/src/components/GuiMetaDataEd.h b/src/components/GuiMetaDataEd.h index 1c33394f5..396d2d95c 100644 --- a/src/components/GuiMetaDataEd.h +++ b/src/components/GuiMetaDataEd.h @@ -4,7 +4,6 @@ #include "ComponentListComponent.h" #include "../MetaData.h" #include "TextComponent.h" -#include "../GameData.h" #include "NinePatchComponent.h" #include "ButtonComponent.h" #include diff --git a/src/components/GuiScraperLog.cpp b/src/components/GuiScraperLog.cpp index 66c5b4dbf..ce1b840d5 100644 --- a/src/components/GuiScraperLog.cpp +++ b/src/components/GuiScraperLog.cpp @@ -52,7 +52,7 @@ void GuiScraperLog::next() ScraperSearchParams search = mSearches.front(); mSearches.pop(); - writeLine(search.game->getCleanName(), 0x0000FFFF); + writeLine(getCleanFileName(search.game->getPath()), 0x0000FFFF); if(mManualMode) { @@ -87,7 +87,7 @@ void GuiScraperLog::resultFetched(ScraperSearchParams params, MetaDataList mdl) void GuiScraperLog::resultResolved(ScraperSearchParams params, MetaDataList mdl) { //apply new metadata - *params.game->metadata() = mdl; + params.game->metadata = mdl; writeLine(" Success!", 0x00FF00FF); @@ -106,7 +106,7 @@ void GuiScraperLog::resultEmpty(ScraperSearchParams search) else writeLine(" NO RESULTS, skipping", 0xFF0000FF); - LOG(LogInfo) << "Scraper skipping [" << search.game->getCleanName() << "]"; + LOG(LogInfo) << "Scraper skipping [" << getCleanFileName(search.game->getPath()) << "]"; mSkippedCount++; diff --git a/src/components/GuiScraperStart.cpp b/src/components/GuiScraperStart.cpp index dfbc1ef4d..6cf43762b 100644 --- a/src/components/GuiScraperStart.cpp +++ b/src/components/GuiScraperStart.cpp @@ -24,9 +24,9 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), //add filters (with first one selected) mFiltersOpt.addEntry(mFiltersOpt.makeEntry("All Games", - [](SystemData*, GameData*) -> bool { return true; }, true)); + [](SystemData*, FileData*) -> bool { return true; }, true)); mFiltersOpt.addEntry(mFiltersOpt.makeEntry("Missing Image", - [](SystemData*, GameData* g) -> bool { return g->metadata()->get("image").empty(); })); + [](SystemData*, FileData* g) -> bool { return g->metadata.get("image").empty(); })); mList.setEntry(Vector2i(0, 0), Vector2i(1, 1), &mFilterLabel, false, ComponentListComponent::AlignRight); mList.setEntry(Vector2i(1, 0), Vector2i(1, 1), &mFiltersOpt, true, ComponentListComponent::AlignLeft); @@ -85,13 +85,13 @@ std::queue GuiScraperStart::getSearches(std::vector queue; for(auto sys = systems.begin(); sys != systems.end(); sys++) { - std::vector games = (*sys)->getRootFolder()->getFilesRecursive(true); + std::vector games = (*sys)->getRootFolder()->getFilesRecursive(GAME); for(auto game = games.begin(); game != games.end(); game++) { - if(selector((*sys), (GameData*)(*game))) + if(selector((*sys), (*game))) { ScraperSearchParams search; - search.game = (GameData*)(*game); + search.game = *game; search.system = *sys; queue.push(search); diff --git a/src/components/GuiScraperStart.h b/src/components/GuiScraperStart.h index 02514cb3b..117433f33 100644 --- a/src/components/GuiScraperStart.h +++ b/src/components/GuiScraperStart.h @@ -10,7 +10,7 @@ #include "../scrapers/Scraper.h" #include -typedef std::function GameFilterFunc; +typedef std::function GameFilterFunc; //The starting point for a multi-game scrape. //Allows the user to set various parameters (to set filters, to set which systems to scrape, to enable manual mode). diff --git a/src/scrapers/GamesDBScraper.cpp b/src/scrapers/GamesDBScraper.cpp index 10be80824..16d40a571 100644 --- a/src/scrapers/GamesDBScraper.cpp +++ b/src/scrapers/GamesDBScraper.cpp @@ -60,7 +60,7 @@ std::shared_ptr GamesDBScraper::makeHttpReq(ScraperSearchParams params) std::string cleanName = params.nameOverride; if(cleanName.empty()) - cleanName = params.game->getCleanName(); + cleanName = getCleanFileName(params.game->getPath()); path += "name=" + HttpReq::urlEncode(cleanName); diff --git a/src/scrapers/Scraper.cpp b/src/scrapers/Scraper.cpp index 4f5d0a7af..ddfe6350e 100644 --- a/src/scrapers/Scraper.cpp +++ b/src/scrapers/Scraper.cpp @@ -143,7 +143,7 @@ std::string downloadImage(const std::string& url, const std::string& saveAs) std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url) { const std::string subdirectory = params.system->getName(); - const std::string name = params.game->getCleanName() + "-" + suffix; + const std::string name = getCleanFileName(params.game->getPath()) + "-" + suffix; std::string path = getHomePath() + "/.emulationstation/downloaded_images/"; @@ -177,7 +177,7 @@ std::shared_ptr createScraperByName(const std::string& name) void resolveMetaDataAssetsAsync(Window* window, const ScraperSearchParams& params, MetaDataList mdl, std::function returnFunc) { - const std::vector& mdd = params.game->metadata()->getMDD(); + const std::vector& mdd = params.game->metadata.getMDD(); for(auto it = mdd.begin(); it != mdd.end(); it++) { std::string key = it->key; @@ -190,7 +190,7 @@ void resolveMetaDataAssetsAsync(Window* window, const ScraperSearchParams& param if(savedAs.empty()) { //error - LOG(LogError) << "Could not resolve image for [" << params.game->getCleanName() << "]! Skipping."; + LOG(LogError) << "Could not resolve image for [" << getCleanFileName(params.game->getPath()) << "]! Skipping."; } mdl.set(key, savedAs); diff --git a/src/scrapers/Scraper.h b/src/scrapers/Scraper.h index 854aa3a11..df9b8a42d 100644 --- a/src/scrapers/Scraper.h +++ b/src/scrapers/Scraper.h @@ -2,7 +2,6 @@ #include "../MetaData.h" #include "../SystemData.h" -#include "../GameData.h" #include "../HttpReq.h" #include #include @@ -12,7 +11,7 @@ class Window; struct ScraperSearchParams { SystemData* system; - GameData* game; + FileData* game; std::string nameOverride; }; diff --git a/src/scrapers/TheArchiveScraper.cpp b/src/scrapers/TheArchiveScraper.cpp index 4ad80a161..2429bf448 100644 --- a/src/scrapers/TheArchiveScraper.cpp +++ b/src/scrapers/TheArchiveScraper.cpp @@ -12,7 +12,7 @@ std::shared_ptr TheArchiveScraper::makeHttpReq(ScraperSearchParams para std::string cleanName = params.nameOverride; if(cleanName.empty()) - cleanName = params.game->getCleanName(); + cleanName = getCleanFileName(params.game->getPath()); path += HttpReq::urlEncode(cleanName); //platform TODO, should use some params.system get method