2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-21 12:25:28 +00:00
|
|
|
// MetaData.cpp
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// Static data for default metadata values as well as functions
|
|
|
|
// to read and write metadata from the gamelist files.
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "MetaData.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2018-01-26 18:53:19 +00:00
|
|
|
#include "utils/FileSystemUtil.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "Log.h"
|
2020-09-21 17:17:34 +00:00
|
|
|
|
2020-06-24 15:38:41 +00:00
|
|
|
#include <pugixml.hpp>
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2017-06-12 16:38:59 +00:00
|
|
|
MetaDataDecl gameDecls[] = {
|
2020-08-08 09:36:43 +00:00
|
|
|
// key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd, shouldScrape
|
|
|
|
{"name", MD_STRING, "", false, "name", "enter game name", true},
|
|
|
|
{"sortname", MD_STRING, "", false, "sortname", "enter game sort name", false},
|
|
|
|
{"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true},
|
|
|
|
{"rating", MD_RATING, "0", false, "rating", "enter rating", true},
|
|
|
|
{"releasedate", MD_DATE, "19700101T010000", false, "release date", "enter release date", true},
|
|
|
|
{"developer", MD_STRING, "unknown", false, "developer", "enter game developer", true},
|
|
|
|
{"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher", true},
|
|
|
|
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre", true},
|
2021-01-08 19:30:21 +00:00
|
|
|
{"players", MD_STRING, "unknown", false, "players", "enter number of players", true},
|
2020-08-08 09:36:43 +00:00
|
|
|
{"favorite", MD_BOOL, "false", false, "favorite", "enter favorite off/on", false},
|
|
|
|
{"completed", MD_BOOL, "false", false, "completed", "enter completed off/on", false},
|
|
|
|
{"kidgame", MD_BOOL, "false", false, "kidgame", "enter kidgame off/on", false},
|
|
|
|
{"hidden", MD_BOOL, "false", false, "hidden", "enter hidden off/on", false},
|
|
|
|
{"broken", MD_BOOL, "false", false, "broken/not working", "enter broken off/on", false},
|
|
|
|
{"nogamecount", MD_BOOL, "false", false, "exclude from game counter", "enter don't count as game off/on", false},
|
|
|
|
{"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false},
|
|
|
|
{"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false},
|
|
|
|
{"launchcommand", MD_LAUNCHCOMMAND, "", false, "launch command", "enter game launch command "
|
|
|
|
"(emulator override)", false},
|
|
|
|
{"playcount", MD_INT, "0", false, "play count", "enter number of times played", false},
|
|
|
|
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false}
|
2014-06-25 16:29:58 +00:00
|
|
|
};
|
2020-08-08 09:36:43 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
const std::vector<MetaDataDecl> gameMDD(gameDecls, gameDecls +
|
2020-06-21 12:25:28 +00:00
|
|
|
sizeof(gameDecls) / sizeof(gameDecls[0]));
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2017-06-12 16:38:59 +00:00
|
|
|
MetaDataDecl folderDecls[] = {
|
2020-08-08 09:36:43 +00:00
|
|
|
{"name", MD_STRING, "", false, "name", "enter game name", true},
|
|
|
|
{"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true},
|
|
|
|
{"rating", MD_RATING, "0", false, "rating", "enter rating", true},
|
|
|
|
{"releasedate", MD_DATE, "19700101T010000", false, "release date", "enter release date", true},
|
|
|
|
{"developer", MD_STRING, "unknown", false, "developer", "enter game developer", true},
|
|
|
|
{"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher", true},
|
|
|
|
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre", true},
|
2021-01-08 19:30:21 +00:00
|
|
|
{"players", MD_STRING, "unknown", false, "players", "enter number of players", true},
|
2020-08-08 09:36:43 +00:00
|
|
|
{"favorite", MD_BOOL, "false", false, "favorite", "enter favorite off/on", false},
|
|
|
|
{"completed", MD_BOOL, "false", false, "completed", "enter completed off/on", false},
|
|
|
|
{"hidden", MD_BOOL, "false", false, "hidden", "enter hidden off/on", false},
|
|
|
|
{"broken", MD_BOOL, "false", false, "broken/not working", "enter broken off/on", false},
|
|
|
|
{"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false},
|
|
|
|
{"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false},
|
|
|
|
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false}
|
2014-06-25 16:29:58 +00:00
|
|
|
};
|
2020-08-08 09:36:43 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
const std::vector<MetaDataDecl> folderMDD(folderDecls, folderDecls +
|
2020-06-21 12:25:28 +00:00
|
|
|
sizeof(folderDecls) / sizeof(folderDecls[0]));
|
2014-06-25 16:29:58 +00:00
|
|
|
|
|
|
|
const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
switch(type) {
|
2020-10-18 17:45:26 +00:00
|
|
|
case GAME_METADATA:
|
|
|
|
return gameMDD;
|
|
|
|
case FOLDER_METADATA:
|
|
|
|
return folderMDD;
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG(LogError) << "Invalid MDD type";
|
|
|
|
return gameMDD;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MetaDataList::MetaDataList(MetaDataListType type)
|
2020-06-21 12:25:28 +00:00
|
|
|
: mType(type), mWasChanged(false)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
const std::vector<MetaDataDecl>& mdd = getMDD();
|
|
|
|
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++)
|
|
|
|
set(iter->key, iter->defaultValue);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
MetaDataList MetaDataList::createFromXML(MetaDataListType type,
|
2020-06-21 12:25:28 +00:00
|
|
|
pugi::xml_node& node, const std::string& relativeTo)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
MetaDataList mdl(type);
|
|
|
|
|
|
|
|
const std::vector<MetaDataDecl>& mdd = mdl.getMDD();
|
|
|
|
|
|
|
|
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) {
|
|
|
|
pugi::xml_node md = node.child(iter->key.c_str());
|
|
|
|
if (md && !md.text().empty()) {
|
|
|
|
// If it's a path, resolve relative paths.
|
|
|
|
std::string value = md.text().get();
|
|
|
|
if (iter->type == MD_PATH)
|
|
|
|
value = Utils::FileSystem::resolveRelativePath(value, relativeTo, true);
|
|
|
|
mdl.set(iter->key, value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mdl.set(iter->key, iter->defaultValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mdl;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults,
|
2020-06-21 12:25:28 +00:00
|
|
|
const std::string& relativeTo) const
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
const std::vector<MetaDataDecl>& mdd = getMDD();
|
|
|
|
|
|
|
|
for (auto mddIter = mdd.cbegin(); mddIter != mdd.cend(); mddIter++) {
|
|
|
|
auto mapIter = mMap.find(mddIter->key);
|
|
|
|
if (mapIter != mMap.cend()) {
|
|
|
|
// We have this value!
|
|
|
|
// If it's just the default (and we ignore defaults), don't write it.
|
|
|
|
if (ignoreDefaults && mapIter->second == mddIter->defaultValue)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Try and make paths relative if we can.
|
|
|
|
std::string value = mapIter->second;
|
|
|
|
if (mddIter->type == MD_PATH)
|
|
|
|
value = Utils::FileSystem::createRelativePath(value, relativeTo, true);
|
|
|
|
|
|
|
|
parent.append_child(mapIter->first.c_str()).text().set(value.c_str());
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MetaDataList::set(const std::string& key, const std::string& value)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mMap[key] = value;
|
|
|
|
mWasChanged = true;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const std::string& MetaDataList::get(const std::string& key) const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Check that the key actually exists, otherwise return empty string.
|
|
|
|
if (mMap.count(key) > 0)
|
|
|
|
return mMap.at(key);
|
|
|
|
else
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return mNoResult;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int MetaDataList::getInt(const std::string& key) const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return atoi(get(key).c_str());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
float MetaDataList::getFloat(const std::string& key) const
|
|
|
|
{
|
2020-11-17 22:06:54 +00:00
|
|
|
return static_cast<float>(atof(get(key).c_str()));
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2016-12-19 15:59:40 +00:00
|
|
|
bool MetaDataList::wasChanged() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return mWasChanged;
|
2016-12-19 15:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MetaDataList::resetChangedFlag()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mWasChanged = false;
|
2016-12-19 15:59:40 +00:00
|
|
|
}
|