From 37db651ffa04479faaae1b1092d7ef83b916d37b Mon Sep 17 00:00:00 2001 From: Aloshi Date: Mon, 2 Jun 2014 13:13:30 -0500 Subject: [PATCH] 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. --- README.md | 14 ++++++++++++- src/Gamelist.cpp | 53 +++++++++++++++++++++++++++--------------------- src/MetaData.cpp | 2 +- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 57695ee04..38c366234 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,15 @@ An example gamelist.xml: ``` -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: + +`/home/pi/ROMs/nes/mm2.nes` + +or + +`./mm2.nes` + +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 `--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 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)*. Themes diff --git a/src/Gamelist.cpp b/src/Gamelist.cpp index 8eb4d5488..c4c3eeba3 100644 --- a/src/Gamelist.cpp +++ b/src/Gamelist.cpp @@ -7,6 +7,21 @@ 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" 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]; 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)) { @@ -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) { //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); }else{ //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; } - fs::path nodePath(pathNode.text().get()); + fs::path nodePath = resolvePath(pathNode.text().get(), system->getStartPath()); fs::path gamePath((*fit)->getPath()); if(nodePath == gamePath || (fs::exists(nodePath) && fs::exists(gamePath) && fs::equivalent(nodePath, gamePath))) { diff --git a/src/MetaData.cpp b/src/MetaData.cpp index f812b7be9..1d63808ad 100644 --- a/src/MetaData.cpp +++ b/src/MetaData.cpp @@ -8,7 +8,7 @@ MetaDataDecl gameDecls[] = { {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description"}, {"image", MD_IMAGE_PATH, "", false, "image", "enter path to image"}, {"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"}, {"developer", MD_STRING, "unknown", false, "developer", "enter game developer"}, {"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher"},