New generic metadata backend.

This commit is contained in:
Aloshi 2013-08-14 07:16:49 -05:00
parent dbcb9aed37
commit 421797929d
14 changed files with 350 additions and 265 deletions

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 override;
const std::string& getPath() const override;
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);
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

View file

@ -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 "";
}

View file

@ -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
View 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
View 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

View file

@ -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;

View file

@ -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());
//write metadata
const_cast<GameData*>(game)->metadata()->appendToXML(newGame, MetaDataList::getDefaultGameMDD());
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());
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() << "\"!";
}
}

View file

@ -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("");

View file

@ -93,3 +93,13 @@ void TextComponent::calculateExtent()
}
}
}
void TextComponent::setValue(const std::string& value)
{
setText(value);
}
std::string TextComponent::getValue() const
{
return mText;
}

View file

@ -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;