Added support for relative paths in gamelist.xmls.

Fixed default for the "rating" metadata value, so it no longer writes to the gamelist when the rating hasn't been set yet.
This commit is contained in:
Aloshi 2014-06-02 13:13:30 -05:00
parent 0c802a2107
commit 37db651ffa
3 changed files with 44 additions and 25 deletions

View file

@ -193,7 +193,15 @@ An example gamelist.xml:
</gameList> </gameList>
``` ```
The path element should be the *absolute path* of the ROM. Special characters SHOULD NOT be escaped. The image element is the path to an image to display above the description (like a screenshot or boxart). Most formats can be used (including png, jpg, gif, etc.). Not all elements need to be used. The path element should be either the absolute path of the ROM, or a path relative to the system games folder that starts with "./". For example:
`<path>/home/pi/ROMs/nes/mm2.nes</path>`
or
`<path>./mm2.nes</path>`
ES will attempt to encode paths as relative to the system's `path` setting whenever it writes a gamelist.xml. Special characters SHOULD NOT be escaped. The image element is the path to an image to display above the description (like a screenshot or boxart). Most popular image formats can be used (png, jpg, gif, etc.). Not all elements need to be used.
The switch `--gamelist-only` can be used to skip automatic searching, and only display games defined in the system's gamelist.xml. The switch `--gamelist-only` can be used to skip automatic searching, and only display games defined in the system's gamelist.xml.
The switch `--ignore-gamelist` can be used to ignore the gamelist and force ES to use the non-detailed view. The switch `--ignore-gamelist` can be used to ignore the gamelist and force ES to use the non-detailed view.
@ -203,6 +211,10 @@ The switch `--ignore-gamelist` can be used to ignore the gamelist and force ES t
* **If you want to scrape multiple games:** press start to open the menu and choose the "SCRAPER" option. Adjust your settings and press "SCRAPE NOW". * **If you want to scrape multiple games:** press start to open the menu and choose the "SCRAPER" option. Adjust your settings and press "SCRAPE NOW".
* **If you just want to scrape one game:** find the game on the game list in ES and press select. Choose "EDIT THIS GAME'S METADATA" and then press the "SCRAPE" button at the bottom of the metadata editor. * **If you just want to scrape one game:** find the game on the game list in ES and press select. Choose "EDIT THIS GAME'S METADATA" and then press the "SCRAPE" button at the bottom of the metadata editor.
You can also edit metadata within ES by using the metadata editor - just find the game you wish to edit on the gamelist, press select, and choose "EDIT THIS GAME'S METADATA."
If you're writing a tool to generate gamelist.xml files, you can see the complete list of possible elements at the top of `src/MetaData.cpp`.
A command-line version of the scraper is also provided - just run emulationstation with `--scrape` *(currently broken)*. A command-line version of the scraper is also provided - just run emulationstation with `--scrape` *(currently broken)*.
Themes Themes

View file

@ -7,6 +7,21 @@
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
// expands "./my/path.sfc" to "[relativeTo]/my/path.sfc"
fs::path resolvePath(const fs::path& path, const fs::path& relativeTo)
{
if(path.begin() != path.end() && *path.begin() == fs::path("."))
{
fs::path ret = relativeTo;
for(auto it = ++path.begin(); it != path.end(); ++it)
ret /= *it;
return ret;
}
return path;
}
// example: removeCommonPath("/home/pi/roms/nes/foo/bar.nes", "/home/pi/roms/nes/") returns "foo/bar.nes" // example: removeCommonPath("/home/pi/roms/nes/foo/bar.nes", "/home/pi/roms/nes/") returns "foo/bar.nes"
fs::path removeCommonPath(const fs::path& path, const fs::path& relativeTo, bool& contains) fs::path removeCommonPath(const fs::path& path, const fs::path& relativeTo, bool& contains)
{ {
@ -150,7 +165,7 @@ void parseGamelist(SystemData* system)
FileType type = typeList[i]; FileType type = typeList[i];
for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag)) for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag))
{ {
boost::filesystem::path path = boost::filesystem::path(fileNode.child("path").text().get()); fs::path path = resolvePath(fileNode.child("path").text().get(), system->getStartPath());
if(!boost::filesystem::exists(path)) if(!boost::filesystem::exists(path))
{ {
@ -176,26 +191,6 @@ void parseGamelist(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
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->getCleanName()) //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().generic_string().c_str());
}
}
void addFileDataNode(pugi::xml_node& parent, const FileData* file, const char* tag, SystemData* system) void addFileDataNode(pugi::xml_node& parent, const FileData* file, const char* tag, SystemData* system)
{ {
//create game and add to parent node //create game and add to parent node
@ -213,7 +208,19 @@ void addFileDataNode(pugi::xml_node& parent, const FileData* file, const char* t
parent.remove_child(newNode); parent.remove_child(newNode);
}else{ }else{
//there's something useful in there so we'll keep the node, add the path //there's something useful in there so we'll keep the node, add the path
newNode.prepend_child("path").text().set(file->getPath().generic_string().c_str());
// try and make the path relative if we can so things still work if we change the rom folder location in the future
fs::path relPath = file->getPath();
bool contains = false;
relPath = removeCommonPath(relPath, system->getStartPath(), contains);
if(contains)
{
// it's in the start path (which we just stripped off), so add a "./"
relPath = "." / relPath;
}
newNode.prepend_child("path").text().set(relPath.generic_string().c_str());
} }
} }
@ -277,7 +284,7 @@ void updateGamelist(SystemData* system)
continue; continue;
} }
fs::path nodePath(pathNode.text().get()); fs::path nodePath = resolvePath(pathNode.text().get(), system->getStartPath());
fs::path gamePath((*fit)->getPath()); fs::path gamePath((*fit)->getPath());
if(nodePath == gamePath || (fs::exists(nodePath) && fs::exists(gamePath) && fs::equivalent(nodePath, gamePath))) if(nodePath == gamePath || (fs::exists(nodePath) && fs::exists(gamePath) && fs::equivalent(nodePath, gamePath)))
{ {

View file

@ -8,7 +8,7 @@ MetaDataDecl gameDecls[] = {
{"desc", MD_MULTILINE_STRING, "", false, "description", "enter description"}, {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description"},
{"image", MD_IMAGE_PATH, "", false, "image", "enter path to image"}, {"image", MD_IMAGE_PATH, "", false, "image", "enter path to image"},
{"thumbnail", MD_IMAGE_PATH, "", false, "thumbnail", "enter path to thumbnail"}, {"thumbnail", MD_IMAGE_PATH, "", false, "thumbnail", "enter path to thumbnail"},
{"rating", MD_RATING, "0", false, "rating", "enter rating"}, {"rating", MD_RATING, "0.000000", false, "rating", "enter rating"},
{"releasedate", MD_DATE, "not-a-date-time", false, "release date", "enter release date"}, {"releasedate", MD_DATE, "not-a-date-time", false, "release date", "enter release date"},
{"developer", MD_STRING, "unknown", false, "developer", "enter game developer"}, {"developer", MD_STRING, "unknown", false, "developer", "enter game developer"},
{"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher"}, {"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher"},