Commit 42179792 authored by Aloshi's avatar Aloshi

New generic metadata backend.

parent dbcb9aed
......@@ -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;
}
\ No newline at end of file
}
#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;
......
#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;
}
#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;
......
This diff is collapsed.
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment