mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-21 21:55:38 +00:00
New generic metadata backend.
This commit is contained in:
parent
dbcb9aed37
commit
421797929d
|
@ -127,6 +127,7 @@ set(ES_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Log.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MathExp.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MetaData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/platform.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.h
|
||||
|
@ -169,6 +170,7 @@ set(ES_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/Log.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MathExp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MetaData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/platform.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer_draw_gl.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Renderer_init.cpp
|
||||
|
|
|
@ -10,8 +10,8 @@ class FileData
|
|||
public:
|
||||
virtual ~FileData() { };
|
||||
virtual bool isFolder() const = 0;
|
||||
virtual const std::string & getName() const = 0;
|
||||
virtual const std::string & getPath() const = 0;
|
||||
virtual const std::string& getName() const = 0;
|
||||
virtual const std::string& getPath() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -82,7 +82,7 @@ bool FolderData::compareRating(const FileData* file1, const FileData* file2)
|
|||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getRating() < game2->getRating();
|
||||
return const_cast<GameData*>(game1)->metadata()->getFloat("rating") < const_cast<GameData*>(game2)->metadata()->getFloat("rating");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ bool FolderData::compareUserRating(const FileData* file1, const FileData* file2)
|
|||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getUserRating() < game2->getUserRating();
|
||||
return const_cast<GameData*>(game1)->metadata()->getFloat("userrating") < const_cast<GameData*>(game2)->metadata()->getFloat("userrating");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ bool FolderData::compareTimesPlayed(const FileData* file1, const FileData* file2
|
|||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getTimesPlayed() < game2->getTimesPlayed();
|
||||
return const_cast<GameData*>(game1)->metadata()->getInt("playcount") < const_cast<GameData*>(game2)->metadata()->getInt("playcount");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ bool FolderData::compareLastPlayed(const FileData* file1, const FileData* file2)
|
|||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getLastPlayed() < game2->getLastPlayed();
|
||||
return const_cast<GameData*>(game1)->metadata()->getTime("lastplayed") < const_cast<GameData*>(game2)->metadata()->getTime("lastplayed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -183,4 +183,4 @@ std::vector<FileData*> FolderData::getFilesRecursive(bool onlyFiles) const
|
|||
++fdit;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
|
123
src/GameData.cpp
123
src/GameData.cpp
|
@ -1,23 +1,14 @@
|
|||
#include "GameData.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
const std::string GameData::xmlTagGameList = "gameList";
|
||||
const std::string GameData::xmlTagGame = "game";
|
||||
const std::string GameData::xmlTagName = "name";
|
||||
const std::string GameData::xmlTagPath = "path";
|
||||
const std::string GameData::xmlTagDescription = "desc";
|
||||
const std::string GameData::xmlTagImagePath = "image";
|
||||
const std::string GameData::xmlTagRating = "rating";
|
||||
const std::string GameData::xmlTagUserRating = "userrating";
|
||||
const std::string GameData::xmlTagTimesPlayed = "timesplayed";
|
||||
const std::string GameData::xmlTagLastPlayed = "lastplayed";
|
||||
|
||||
|
||||
GameData::GameData(SystemData* system, std::string path, std::string name)
|
||||
: mSystem(system), mPath(path), mName(name), mRating(0.0f), mUserRating(0.0f), mTimesPlayed(0), mLastPlayed(0)
|
||||
GameData::GameData(SystemData* system, std::string path)
|
||||
: mSystem(system), mPath(path), mBaseName(boost::filesystem::path(path).stem().string()), mMetaData(MetaDataList::getDefaultGameMDD())
|
||||
{
|
||||
if(mMetaData.get("name").empty())
|
||||
mMetaData.set("name", mBaseName);
|
||||
}
|
||||
|
||||
bool GameData::isFolder() const
|
||||
|
@ -25,90 +16,20 @@ bool GameData::isFolder() const
|
|||
return false;
|
||||
}
|
||||
|
||||
const std::string & GameData::getName() const
|
||||
const std::string& GameData::getName() const
|
||||
{
|
||||
return mName;
|
||||
return mMetaData.get("name");
|
||||
}
|
||||
|
||||
void GameData::setName(const std::string & name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
const std::string & GameData::getPath() const
|
||||
const std::string& GameData::getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
void GameData::setPath(const std::string & path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
const std::string & GameData::getDescription() const
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
void GameData::setDescription(const std::string & description)
|
||||
{
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
const std::string & GameData::getImagePath() const
|
||||
{
|
||||
return mImagePath;
|
||||
}
|
||||
|
||||
void GameData::setImagePath(const std::string & imagePath)
|
||||
{
|
||||
mImagePath = imagePath;
|
||||
}
|
||||
|
||||
float GameData::getRating() const
|
||||
{
|
||||
return mRating;
|
||||
}
|
||||
|
||||
void GameData::setRating(float rating)
|
||||
{
|
||||
mRating = rating;
|
||||
}
|
||||
|
||||
float GameData::getUserRating() const
|
||||
{
|
||||
return mUserRating;
|
||||
}
|
||||
|
||||
void GameData::setUserRating(float rating)
|
||||
{
|
||||
mUserRating = rating;
|
||||
}
|
||||
|
||||
size_t GameData::getTimesPlayed() const
|
||||
{
|
||||
return mTimesPlayed;
|
||||
}
|
||||
|
||||
void GameData::setTimesPlayed(size_t timesPlayed)
|
||||
{
|
||||
mTimesPlayed = timesPlayed;
|
||||
}
|
||||
|
||||
std::time_t GameData::getLastPlayed() const
|
||||
{
|
||||
return mLastPlayed;
|
||||
}
|
||||
|
||||
void GameData::setLastPlayed(std::time_t lastPlayed)
|
||||
{
|
||||
mLastPlayed = lastPlayed;
|
||||
}
|
||||
|
||||
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 = mPath;
|
||||
std::string path = getPath();
|
||||
|
||||
const char* invalidChars = " '\"\\!$^&*(){}[]?;<>";
|
||||
for(unsigned int i = 0; i < path.length(); i++)
|
||||
|
@ -133,6 +54,26 @@ std::string GameData::getBashPath() const
|
|||
//returns the boost::filesystem stem of our path - e.g. for "/foo/bar.rom" returns "bar"
|
||||
std::string GameData::getBaseName() const
|
||||
{
|
||||
boost::filesystem::path path(mPath);
|
||||
return path.stem().string();
|
||||
return mBaseName;
|
||||
}
|
||||
|
||||
void GameData::incTimesPlayed()
|
||||
{
|
||||
int timesPlayed = metadata()->getInt("playcount");
|
||||
timesPlayed++;
|
||||
|
||||
std::stringstream ss();
|
||||
metadata()->set("playcount", std::to_string(static_cast<long long>(timesPlayed)));
|
||||
}
|
||||
|
||||
void GameData::lastPlayedNow()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::time(nullptr);
|
||||
metadata()->set("lastplayed", ss.str());
|
||||
}
|
||||
|
||||
MetaDataList* GameData::metadata()
|
||||
{
|
||||
return &mMetaData;
|
||||
}
|
||||
|
|
|
@ -2,70 +2,44 @@
|
|||
#define _GAMEDATA_H_
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
#include "FileData.h"
|
||||
#include "SystemData.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:
|
||||
//static tag names for reading/writing XML documents. This might fail in PUGIXML_WCHAR_MODE
|
||||
//TODO: The class should have member to read fromXML() and write toXML() probably...
|
||||
static const std::string xmlTagGameList;
|
||||
static const std::string xmlTagGame;
|
||||
static const std::string xmlTagName;
|
||||
static const std::string xmlTagPath;
|
||||
static const std::string xmlTagDescription;
|
||||
static const std::string xmlTagImagePath;
|
||||
static const std::string xmlTagRating;
|
||||
static const std::string xmlTagUserRating;
|
||||
static const std::string xmlTagTimesPlayed;
|
||||
static const std::string xmlTagLastPlayed;
|
||||
GameData(SystemData* system, std::string path);
|
||||
|
||||
GameData(SystemData* system, std::string path, std::string name);
|
||||
|
||||
const std::string & getName() const;
|
||||
void setName(const std::string & name);
|
||||
|
||||
const std::string & getPath() const;
|
||||
void setPath(const std::string & path);
|
||||
|
||||
const std::string & getDescription() const;
|
||||
void setDescription(const std::string & description);
|
||||
|
||||
const std::string & getImagePath() const;
|
||||
void setImagePath(const std::string & imagePath);
|
||||
|
||||
float getRating() const;
|
||||
void setRating(float rating);
|
||||
|
||||
float getUserRating() const;
|
||||
void setUserRating(float rating);
|
||||
|
||||
size_t getTimesPlayed() const;
|
||||
void setTimesPlayed(size_t timesPlayed);
|
||||
|
||||
std::time_t getLastPlayed() const;
|
||||
void setLastPlayed(std::time_t lastPlayed);
|
||||
const std::string& getName() const override;
|
||||
const std::string& getPath() const override;
|
||||
|
||||
void incTimesPlayed();
|
||||
void lastPlayedNow();
|
||||
|
||||
std::string getBashPath() const;
|
||||
std::string getBaseName() const;
|
||||
|
||||
bool isFolder() const;
|
||||
bool isFolder() const override;
|
||||
|
||||
MetaDataList* metadata();
|
||||
|
||||
private:
|
||||
SystemData* mSystem;
|
||||
std::string mPath;
|
||||
std::string mName;
|
||||
const std::string mPath;
|
||||
const std::string mBaseName;
|
||||
|
||||
//extra data
|
||||
std::string mDescription;
|
||||
/*std::string mDescription;
|
||||
std::string mImagePath;
|
||||
float mRating;
|
||||
float mUserRating;
|
||||
size_t mTimesPlayed;
|
||||
std::time_t mLastPlayed;
|
||||
std::time_t mLastPlayed;*/
|
||||
|
||||
MetaDataList mMetaData;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -157,3 +157,12 @@ const Eigen::Affine3f GuiComponent::getTransform()
|
|||
mTransform.translate(mPosition);
|
||||
return mTransform;
|
||||
}
|
||||
|
||||
void GuiComponent::setValue(const std::string& value)
|
||||
{
|
||||
}
|
||||
|
||||
std::string GuiComponent::getValue() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ public:
|
|||
//Called when time passes. Default implementation also calls update(deltaTime) on children - so you should probably call GuiComponent::update(deltaTime) at some point.
|
||||
virtual void update(int deltaTime);
|
||||
|
||||
//Called when it's time to render. By default, just calls renderChildren(transform).
|
||||
//Called when it's time to render. By default, just calls renderChildren(parentTrans * getTransform()).
|
||||
//You probably want to override this like so:
|
||||
//1. Calculate the new transform that your control will draw at with Eigen::Affine3f t = parentTrans * getTransform().
|
||||
//2. Set the renderer to use that new transform as the model matrix - Renderer::setModelMatrix(t.data());
|
||||
//2. Set the renderer to use that new transform as the model matrix - Renderer::setMatrix(t);
|
||||
//3. Draw your component.
|
||||
//4. Tell your children to render, based on your component's transform - renderChildren(t).
|
||||
virtual void render(const Eigen::Affine3f& parentTrans);
|
||||
|
@ -51,6 +51,9 @@ public:
|
|||
|
||||
const Eigen::Affine3f getTransform();
|
||||
|
||||
virtual std::string getValue() const;
|
||||
virtual void setValue(const std::string& value);
|
||||
|
||||
protected:
|
||||
void renderChildren(const Eigen::Affine3f& transform) const;
|
||||
|
||||
|
|
126
src/MetaData.cpp
Normal file
126
src/MetaData.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include "MetaData.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "Log.h"
|
||||
|
||||
MetaDataList::MetaDataList()
|
||||
{
|
||||
}
|
||||
|
||||
MetaDataList::MetaDataList(const std::vector<MetaDataDecl>& mdd)
|
||||
{
|
||||
for(auto iter = mdd.begin(); iter != mdd.end(); iter++)
|
||||
set(iter->key, iter->defaultValue);
|
||||
}
|
||||
|
||||
std::vector<MetaDataDecl> MetaDataList::getDefaultGameMDD()
|
||||
{
|
||||
MetaDataDecl decls[] = {
|
||||
{"name", MD_STRING, ""},
|
||||
{"desc", MD_STRING, ""},
|
||||
{"image", MD_IMAGE_PATH, ""},
|
||||
{"rating", MD_RATING, "0"},
|
||||
{"timesplayed", MD_INT, "0"},
|
||||
{"playcount", MD_TIME, "0"}
|
||||
};
|
||||
|
||||
std::vector<MetaDataDecl> mdd(decls, decls + sizeof(decls) / sizeof(decls[0]));
|
||||
return mdd;
|
||||
}
|
||||
|
||||
MetaDataList MetaDataList::createFromXML(const std::vector<MetaDataDecl>& mdd, pugi::xml_node node)
|
||||
{
|
||||
MetaDataList mdl;
|
||||
|
||||
for(auto iter = mdd.begin(); iter != mdd.end(); iter++)
|
||||
{
|
||||
pugi::xml_node md = node.child(iter->key.c_str());
|
||||
if(md)
|
||||
{
|
||||
mdl.set(iter->key, md.text().get());
|
||||
}else{
|
||||
mdl.set(iter->key, iter->defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
return mdl;
|
||||
}
|
||||
|
||||
void MetaDataList::appendToXML(pugi::xml_node parent, const std::vector<MetaDataDecl>& ignoreDefaults) const
|
||||
{
|
||||
for(auto iter = mMap.begin(); iter != mMap.end(); iter++)
|
||||
{
|
||||
bool write = true;
|
||||
for(auto mddIter = ignoreDefaults.begin(); mddIter != ignoreDefaults.end(); mddIter++)
|
||||
{
|
||||
if(mddIter->key == iter->first)
|
||||
{
|
||||
if(iter->second == mddIter->defaultValue)
|
||||
write = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(write)
|
||||
parent.append_child(iter->first.c_str()).text().set(iter->second.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void MetaDataList::set(const std::string& key, const std::string& value)
|
||||
{
|
||||
mMap[key] = value;
|
||||
}
|
||||
|
||||
const std::string& MetaDataList::get(const std::string& key) const
|
||||
{
|
||||
return mMap.at(key);
|
||||
}
|
||||
|
||||
int MetaDataList::getInt(const std::string& key) const
|
||||
{
|
||||
return atoi(get(key).c_str());
|
||||
}
|
||||
|
||||
float MetaDataList::getFloat(const std::string& key) const
|
||||
{
|
||||
return (float)atof(get(key).c_str());
|
||||
}
|
||||
|
||||
std::time_t MetaDataList::getTime(const std::string& key) const
|
||||
{
|
||||
return (std::time_t) atoi(get(key).c_str());
|
||||
}
|
||||
|
||||
GuiComponent* MetaDataList::makeDisplay(Window* window, MetaDataType as)
|
||||
{
|
||||
switch(as)
|
||||
{
|
||||
default:
|
||||
TextComponent* comp = new TextComponent(window);
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
GuiComponent* MetaDataList::makeEditor(Window* window, MetaDataType as)
|
||||
{
|
||||
switch(as)
|
||||
{
|
||||
default:
|
||||
TextComponent* comp = new TextComponent(window);
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
GuiComponent* MetaDataList::makeDisplay(Window* window, MetaDataDecl from)
|
||||
{
|
||||
GuiComponent* comp = makeDisplay(window, from.type);
|
||||
comp->setValue(get(from.key));
|
||||
return comp;
|
||||
}
|
||||
|
||||
GuiComponent* MetaDataList::makeEditor(Window* window, MetaDataDecl from)
|
||||
{
|
||||
GuiComponent* comp = makeEditor(window, from.type);
|
||||
comp->setValue(get(from.key));
|
||||
return comp;
|
||||
}
|
73
src/MetaData.h
Normal file
73
src/MetaData.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include "pugiXML/pugixml.hpp"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "GuiComponent.h"
|
||||
#include <ctime>
|
||||
|
||||
enum MetaDataType
|
||||
{
|
||||
//generic types
|
||||
MD_STRING,
|
||||
MD_INT,
|
||||
MD_FLOAT,
|
||||
|
||||
//specialized types
|
||||
MD_IMAGE_PATH,
|
||||
MD_RATING,
|
||||
MD_TIME
|
||||
};
|
||||
|
||||
struct MetaDataDecl
|
||||
{
|
||||
std::string key;
|
||||
MetaDataType type;
|
||||
std::string defaultValue;
|
||||
};
|
||||
|
||||
class MetaDataList
|
||||
{
|
||||
public:
|
||||
static std::vector<MetaDataDecl> getDefaultGameMDD();
|
||||
|
||||
static MetaDataList createFromXML(const std::vector<MetaDataDecl>& mdd, pugi::xml_node node);
|
||||
|
||||
//MetaDataDecl required to set our defaults.
|
||||
MetaDataList(const std::vector<MetaDataDecl>& mdd);
|
||||
|
||||
void set(const std::string& key, const std::string& value);
|
||||
const std::string& get(const std::string& key) const;
|
||||
int getInt(const std::string& key) const;
|
||||
float getFloat(const std::string& key) const;
|
||||
std::time_t getTime(const std::string& key) const;
|
||||
|
||||
GuiComponent* makeDisplay(Window* window, MetaDataType as);
|
||||
GuiComponent* makeDisplay(Window* window, MetaDataDecl from);
|
||||
|
||||
GuiComponent* makeEditor(Window* window, MetaDataType as);
|
||||
GuiComponent* makeEditor(Window* window, MetaDataDecl from);
|
||||
|
||||
void appendToXML(pugi::xml_node parent, const std::vector<MetaDataDecl>& ignoreDefaults = std::vector<MetaDataDecl>()) const;
|
||||
|
||||
private:
|
||||
MetaDataList();
|
||||
|
||||
std::map<std::string, std::string> mMap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//options for storing metadata...
|
||||
//store internally everything as a string - this is all going to be read to/from XML anyway, after all
|
||||
//store using individual get/set functions ala Settings - this is a fair amount of work but the most explicit, for better or worse
|
||||
|
||||
//let's think about some of the special types we would like to support...
|
||||
//image paths, sound paths, ratings, play counts
|
||||
//these get represented behind-the-scenes as strings, floats, and integers, and are eventually saved as strings
|
||||
//the only specialty is how they're edited and viewed, really
|
||||
|
||||
//so we need...
|
||||
//to be able to iterate through the available metadata
|
||||
//create components designed to either DISPLAY or EDIT a given piece of metadata
|
||||
//save and load metadata
|
|
@ -96,8 +96,8 @@ void SystemData::launchGame(Window* window, GameData* game)
|
|||
window->normalizeNextUpdate();
|
||||
|
||||
//update number of times the game has been launched and the time
|
||||
game->setTimesPlayed(game->getTimesPlayed() + 1);
|
||||
game->setLastPlayed(std::time(nullptr));
|
||||
game->incTimesPlayed();
|
||||
game->lastPlayedNow();
|
||||
}
|
||||
|
||||
void SystemData::populateFolder(FolderData* folder)
|
||||
|
@ -145,7 +145,7 @@ void SystemData::populateFolder(FolderData* folder)
|
|||
//if it matches, add it
|
||||
if(chkExt == extension)
|
||||
{
|
||||
GameData* newGame = new GameData(this, filePath.generic_string(), filePath.stem().string());
|
||||
GameData* newGame = new GameData(this, filePath.generic_string());
|
||||
folder->pushFileData(newGame);
|
||||
isGame = true;
|
||||
break;
|
||||
|
|
|
@ -90,11 +90,7 @@ GameData* createGameFromPath(std::string gameAbsPath, SystemData* system)
|
|||
loops++;
|
||||
}
|
||||
|
||||
|
||||
//find gameName
|
||||
std::string gameName = gamePath.substr(separator + 1, gamePath.find(".", separator) - separator - 1);
|
||||
|
||||
GameData* game = new GameData(system, gameAbsPath, gameName);
|
||||
GameData* game = new GameData(system, gameAbsPath);
|
||||
folder->pushFileData(game);
|
||||
return game;
|
||||
}
|
||||
|
@ -117,19 +113,19 @@ void parseGamelist(SystemData* system)
|
|||
return;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child(GameData::xmlTagGameList.c_str());
|
||||
pugi::xml_node root = doc.child("gameList");
|
||||
if(!root)
|
||||
{
|
||||
LOG(LogError) << "Could not find <" << GameData::xmlTagGameList << "> node in gamelist \"" << xmlpath << "\"!";
|
||||
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlpath << "\"!";
|
||||
return;
|
||||
}
|
||||
|
||||
for(pugi::xml_node gameNode = root.child(GameData::xmlTagGame.c_str()); gameNode; gameNode = gameNode.next_sibling(GameData::xmlTagGame.c_str()))
|
||||
for(pugi::xml_node gameNode = root.child("game"); gameNode; gameNode = gameNode.next_sibling("game"))
|
||||
{
|
||||
pugi::xml_node pathNode = gameNode.child(GameData::xmlTagPath.c_str());
|
||||
pugi::xml_node pathNode = gameNode.child("path");
|
||||
if(!pathNode)
|
||||
{
|
||||
LOG(LogError) << "<" << GameData::xmlTagGame << "> node contains no <" << GameData::xmlTagPath << "> child!";
|
||||
LOG(LogError) << "<game> node contains no <path> child!";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -137,11 +133,18 @@ void parseGamelist(SystemData* system)
|
|||
boost::filesystem::path gamePath(pathNode.text().get());
|
||||
std::string path = gamePath.generic_string();
|
||||
|
||||
//expand "."
|
||||
//expand '.'
|
||||
if(path[0] == '.')
|
||||
{
|
||||
path.erase(0, 1);
|
||||
path.insert(0, system->getRootFolder()->getPath());
|
||||
path.insert(0, boost::filesystem::path(xmlpath).parent_path().generic_string());
|
||||
}
|
||||
|
||||
//expand '~'
|
||||
if(path[0] == '~')
|
||||
{
|
||||
path.erase(0, 1);
|
||||
path.insert(0, getHomePath());
|
||||
}
|
||||
|
||||
if(boost::filesystem::exists(path))
|
||||
|
@ -151,103 +154,36 @@ void parseGamelist(SystemData* system)
|
|||
if(game == NULL)
|
||||
game = createGameFromPath(path, system);
|
||||
|
||||
//actually gather the information in the XML doc, then pass it to the game's set method
|
||||
std::string newName, newDesc, newImage;
|
||||
//load the metadata
|
||||
*(game->metadata()) = MetaDataList::createFromXML(MetaDataList::getDefaultGameMDD(), gameNode);
|
||||
|
||||
if(gameNode.child(GameData::xmlTagName.c_str()))
|
||||
{
|
||||
game->setName(gameNode.child(GameData::xmlTagName.c_str()).text().get());
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagDescription.c_str()))
|
||||
{
|
||||
game->setDescription(gameNode.child(GameData::xmlTagDescription.c_str()).text().get());
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagImagePath.c_str()))
|
||||
{
|
||||
newImage = gameNode.child(GameData::xmlTagImagePath.c_str()).text().get();
|
||||
|
||||
//expand "."
|
||||
if(newImage[0] == '.')
|
||||
{
|
||||
newImage.erase(0, 1);
|
||||
boost::filesystem::path pathname(xmlpath);
|
||||
newImage.insert(0, pathname.parent_path().generic_string() );
|
||||
}
|
||||
|
||||
//if the image exist, set it
|
||||
if(boost::filesystem::exists(newImage))
|
||||
{
|
||||
game->setImagePath(newImage);
|
||||
}
|
||||
}
|
||||
|
||||
//get rating and the times played from the XML doc
|
||||
if(gameNode.child(GameData::xmlTagRating.c_str()))
|
||||
{
|
||||
float rating;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagRating.c_str()).text().get()) >> rating;
|
||||
game->setRating(rating);
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagUserRating.c_str()))
|
||||
{
|
||||
float userRating;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagUserRating.c_str()).text().get()) >> userRating;
|
||||
game->setUserRating(userRating);
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagTimesPlayed.c_str()))
|
||||
{
|
||||
size_t timesPlayed;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagTimesPlayed.c_str()).text().get()) >> timesPlayed;
|
||||
game->setTimesPlayed(timesPlayed);
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagLastPlayed.c_str()))
|
||||
{
|
||||
std::time_t lastPlayed;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagLastPlayed.c_str()).text().get()) >> lastPlayed;
|
||||
game->setLastPlayed(lastPlayed);
|
||||
}
|
||||
}
|
||||
else{
|
||||
//make sure name gets set if one didn't exist
|
||||
if(game->metadata()->get("name").empty())
|
||||
game->metadata()->set("name", game->getBaseName());
|
||||
}else{
|
||||
LOG(LogWarning) << "Game at \"" << path << "\" does not exist!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addGameDataNode(pugi::xml_node & parent, const GameData * game)
|
||||
void addGameDataNode(pugi::xml_node& parent, const GameData* game)
|
||||
{
|
||||
//create game and add to parent node
|
||||
pugi::xml_node newGame = parent.append_child(GameData::xmlTagGame.c_str());
|
||||
//add values
|
||||
if (!game->getPath().empty()) {
|
||||
pugi::xml_node pathNode = newGame.append_child(GameData::xmlTagPath.c_str());
|
||||
//store path with generic directory seperators
|
||||
boost::filesystem::path gamePath(game->getPath());
|
||||
pathNode.text().set(gamePath.generic_string().c_str());
|
||||
}
|
||||
if (!game->getName().empty()) {
|
||||
pugi::xml_node nameNode = newGame.append_child(GameData::xmlTagName.c_str());
|
||||
nameNode.text().set(game->getName().c_str());
|
||||
}
|
||||
if (!game->getDescription().empty()) {
|
||||
pugi::xml_node descriptionNode = newGame.append_child(GameData::xmlTagDescription.c_str());
|
||||
descriptionNode.text().set(game->getDescription().c_str());
|
||||
}
|
||||
if (!game->getImagePath().empty()) {
|
||||
pugi::xml_node imagePathNode = newGame.append_child(GameData::xmlTagImagePath.c_str());
|
||||
imagePathNode.text().set(game->getImagePath().c_str());
|
||||
}
|
||||
//all other values are added regardless of their value
|
||||
pugi::xml_node ratingNode = newGame.append_child(GameData::xmlTagRating.c_str());
|
||||
ratingNode.text().set(std::to_string((long double)game->getRating()).c_str());
|
||||
pugi::xml_node newGame = parent.append_child("game");
|
||||
|
||||
pugi::xml_node userRatingNode = newGame.append_child(GameData::xmlTagUserRating.c_str());
|
||||
userRatingNode.text().set(std::to_string((long double)game->getUserRating()).c_str());
|
||||
|
||||
pugi::xml_node timesPlayedNode = newGame.append_child(GameData::xmlTagTimesPlayed.c_str());
|
||||
timesPlayedNode.text().set(std::to_string((unsigned long long)game->getTimesPlayed()).c_str());
|
||||
|
||||
pugi::xml_node lastPlayedNode = newGame.append_child(GameData::xmlTagLastPlayed.c_str());
|
||||
lastPlayedNode.text().set(std::to_string((unsigned long long)game->getLastPlayed()).c_str());
|
||||
//write metadata
|
||||
const_cast<GameData*>(game)->metadata()->appendToXML(newGame, MetaDataList::getDefaultGameMDD());
|
||||
|
||||
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
|
||||
{
|
||||
//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());
|
||||
}
|
||||
}
|
||||
|
||||
void updateGamelist(SystemData* system)
|
||||
|
@ -258,56 +194,64 @@ void updateGamelist(SystemData* system)
|
|||
//we already have in the system from the XML, and then add it back from its GameData information...
|
||||
|
||||
std::string xmlpath = system->getGamelistPath();
|
||||
if(xmlpath.empty()) {
|
||||
if(xmlpath.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\" before writing...";
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
||||
|
||||
if(!result) {
|
||||
if(!result)
|
||||
{
|
||||
LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description();
|
||||
return;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child(GameData::xmlTagGameList.c_str());
|
||||
if(!root) {
|
||||
LOG(LogError) << "Could not find <" << GameData::xmlTagGameList << "> node in gamelist \"" << xmlpath << "\"!";
|
||||
pugi::xml_node root = doc.child("gameList");
|
||||
if(!root)
|
||||
{
|
||||
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlpath << "\"!";
|
||||
return;
|
||||
}
|
||||
|
||||
//now we have all the information from the XML. now iterate through all our games and add information from there
|
||||
FolderData * rootFolder = system->getRootFolder();
|
||||
if (rootFolder != nullptr) {
|
||||
if (rootFolder != nullptr)
|
||||
{
|
||||
//get only files, no folders
|
||||
std::vector<FileData*> files = rootFolder->getFilesRecursive(true);
|
||||
//iterate through all files, checking if they're already in the XML
|
||||
std::vector<FileData*>::const_iterator fit = files.cbegin();
|
||||
while(fit != files.cend()) {
|
||||
while(fit != files.cend())
|
||||
{
|
||||
//try to cast to gamedata
|
||||
const GameData * game = dynamic_cast<const GameData*>(*fit);
|
||||
if (game != nullptr) {
|
||||
if (game != nullptr)
|
||||
{
|
||||
//worked. check if this games' path can be found somewhere in the XML
|
||||
for(pugi::xml_node gameNode = root.child(GameData::xmlTagGame.c_str()); gameNode; gameNode = gameNode.next_sibling(GameData::xmlTagGame.c_str())) {
|
||||
for(pugi::xml_node gameNode = root.child("game"); gameNode; gameNode = gameNode.next_sibling("game"))
|
||||
{
|
||||
//get path from game node
|
||||
pugi::xml_node pathNode = gameNode.child(GameData::xmlTagPath.c_str());
|
||||
pugi::xml_node pathNode = gameNode.child("path");
|
||||
if(!pathNode)
|
||||
{
|
||||
LOG(LogError) << "<" << GameData::xmlTagGame << "> node contains no <" << GameData::xmlTagPath << "> child!";
|
||||
LOG(LogError) << "<game> node contains no <path> child!";
|
||||
continue;
|
||||
}
|
||||
|
||||
//check paths. use the same directory separators
|
||||
boost::filesystem::path nodePath(pathNode.text().get());
|
||||
boost::filesystem::path gamePath(game->getPath());
|
||||
if (nodePath.generic_string() == gamePath.generic_string()) {
|
||||
if (nodePath.generic_string() == gamePath.generic_string())
|
||||
{
|
||||
//found the game. remove it. it will be added again later with updated values
|
||||
root.remove_child(gameNode);
|
||||
//break node search loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
|
@ -316,10 +260,9 @@ void updateGamelist(SystemData* system)
|
|||
}
|
||||
//now write the file
|
||||
if (!doc.save_file(xmlpath.c_str())) {
|
||||
LOG(LogError) << "Error saving XML file \"" << xmlpath << "\"!";
|
||||
LOG(LogError) << "Error saving gamelist.xml file \"" << xmlpath << "\"!";
|
||||
}
|
||||
}
|
||||
else {
|
||||
}else{
|
||||
LOG(LogError) << "Found no root folder for system \"" << system->getName() << "\"!";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,11 +353,12 @@ void GuiGameList::updateDetailData()
|
|||
//if we've selected a game
|
||||
if(mList.getSelectedObject() && !mList.getSelectedObject()->isFolder())
|
||||
{
|
||||
GameData* game = (GameData*)mList.getSelectedObject();
|
||||
//set image to either "not found" image or metadata image
|
||||
if(((GameData*)mList.getSelectedObject())->getImagePath().empty())
|
||||
if(game->metadata()->get("image").empty())
|
||||
mScreenshot.setImage(mTheme->getString("imageNotFoundPath"));
|
||||
else
|
||||
mScreenshot.setImage(((GameData*)mList.getSelectedObject())->getImagePath());
|
||||
mScreenshot.setImage(game->metadata()->get("image"));
|
||||
|
||||
Eigen::Vector3f imgOffset = Eigen::Vector3f(Renderer::getScreenWidth() * 0.10f, 0, 0);
|
||||
mScreenshot.setPosition(getImagePos() - imgOffset);
|
||||
|
@ -372,7 +373,7 @@ void GuiGameList::updateDetailData()
|
|||
|
||||
mDescription.setPosition(0, 0);
|
||||
mDescription.setSize(Eigen::Vector2f(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03f), 0));
|
||||
mDescription.setText(((GameData*)mList.getSelectedObject())->getDescription());
|
||||
mDescription.setText(game->metadata()->get("desc"));
|
||||
}else{
|
||||
mScreenshot.setImage("");
|
||||
mDescription.setText("");
|
||||
|
|
|
@ -93,3 +93,13 @@ void TextComponent::calculateExtent()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextComponent::setValue(const std::string& value)
|
||||
{
|
||||
setText(value);
|
||||
}
|
||||
|
||||
std::string TextComponent::getValue() const
|
||||
{
|
||||
return mText;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ public:
|
|||
|
||||
void render(const Eigen::Affine3f& parentTrans) override;
|
||||
|
||||
std::string getValue() const override;
|
||||
void setValue(const std::string& value) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Font> getFont() const;
|
||||
|
||||
|
|
Loading…
Reference in a new issue