Better gamelist.xml support. See changelog.txt (August 7) for more details.

This commit is contained in:
Aloshi 2012-08-07 19:50:45 -05:00
parent 6bc7fdf371
commit 6cfe83e8fe
8 changed files with 148 additions and 39 deletions

View file

@ -36,8 +36,8 @@ EmulationStation will return once your system's command terminates (i.e. your em
gamelist.xml
============
If a file named gamelist.xml is found, it will be parsed and the detailed GuiGameList will be used. This means you can define screenshots, descriptions, and alternate names for files.
Screenshots are meant to be 256x256, but ES won't stop you from using other sizes - they'll just be placed wrong.
If a file named gamelist.xml is found in the root of a system's search directory, it will be parsed and the detailed GuiGameList will be used. This means you can define images, descriptions, and different names for files.
Images are meant to be 256x256, but ES won't stop you from using other sizes - they'll just be placed wrong. I'd like to add automatic scaling (with SDL_gfx) in a future update.
An example gamelist.xml:
```
<gameList>
@ -50,8 +50,9 @@ An example gamelist.xml:
</gameList>
```
The path element should be the absolute path of the ROM. Special characters SHOULD NOT be escaped. The image element is the absolute path to an image to use (like a screenshot or boxart). Most formats can be used (including png, jpg, gif, etc.). Look up the SDL_image library for a full list. Not all elements need to be used.
The path element should be the absolute path of the ROM. Special characters SHOULD NOT be escaped. The image element is the absolute 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 switch `--gamelist-only` can be used to skip automatic searching, and only display games defined in the system's gamelist.xml.
-Aloshi
http://www.aloshi.com

View file

@ -1,3 +1,10 @@
August 7
-gamelist.xml files are now read from each system's individual search directory.
-The switch --gamelist-only was added. Use it to skip automatic searching and only use files defined in gamelist.xml.
-A gamelist.xml file can now specify a file that wasn't previously found by the automatic search.
-Fixed alphabetizing uppercase and lowercase letters differently (woops!)
-When loading the system config file, if a system doesn't contain any games, it will be automatically deleted.
August 4
-Moved configuration files to $HOME/.emulationstation/
-Renderer::loadFonts() will now fall back to /usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf if LinLibertine.ttf is not found.

View file

@ -1,6 +1,7 @@
#include "FolderData.h"
#include "SystemData.h"
#include <algorithm>
#include <iostream>
bool FolderData::isFolder() { return true; }
std::string FolderData::getName() { return mName; }
@ -38,9 +39,9 @@ bool filesort(FileData* file1, FileData* file2)
for(unsigned int i = 0; i < name1.length(); i++)
{
if(name1[i] != name2[i])
if(toupper(name1[i]) != toupper(name2[i]))
{
if(name1[i] < name2[i])
if(toupper(name1[i]) < toupper(name2[i]))
{
return true;
}else{
@ -55,7 +56,14 @@ bool filesort(FileData* file1, FileData* file2)
return false;
}
//sort this folder and any subfolders
void FolderData::sort()
{
std::sort(mFileVector.begin(), mFileVector.end(), filesort);
for(unsigned int i = 0; i < mFileVector.size(); i++)
{
if(mFileVector.at(i)->isFolder())
((FolderData*)mFileVector.at(i))->sort();
}
}

View file

@ -1,5 +1,6 @@
#include "SystemData.h"
#include "GameData.h"
#include "XMLReader.h"
#include <boost/filesystem.hpp>
#include <fstream>
#include <stdlib.h>
@ -9,6 +10,11 @@ std::vector<SystemData*> SystemData::sSystemVector;
namespace fs = boost::filesystem;
extern bool PARSEGAMELISTONLY;
std::string SystemData::getStartPath() { return mStartPath; }
std::string SystemData::getExtension() { return mSearchExtension; }
SystemData::SystemData(std::string name, std::string startPath, std::string extension, std::string command)
{
mName = name;
@ -34,7 +40,13 @@ SystemData::SystemData(std::string name, std::string startPath, std::string exte
mRootFolder = new FolderData(this, mStartPath, "Search Root");
populateFolder(mRootFolder);
if(!PARSEGAMELISTONLY)
populateFolder(mRootFolder);
parseGamelist(this);
mRootFolder->sort();
}
SystemData::~SystemData()
@ -97,8 +109,6 @@ void SystemData::populateFolder(FolderData* folder)
}
}
}
folder->sort();
}
@ -164,7 +174,14 @@ void SystemData::loadConfig()
//we have all our variables - create the system object
if(!sysName.empty() && !sysPath.empty() &&!sysExtension.empty() && !sysCommand.empty())
{
sSystemVector.push_back(new SystemData(sysName, sysPath, sysExtension, sysCommand));
SystemData* newSystem = new SystemData(sysName, sysPath, sysExtension, sysCommand);
if(newSystem->getRootFolder()->getFileCount() == 0)
{
std::cerr << "Error - system \"" << sysName << "\" has no games! Deleting.\n";
delete newSystem;
}else{
sSystemVector.push_back(newSystem);
}
//reset the variables for the next block (should there be one)
sysName = ""; sysPath = ""; sysExtension = ""; sysCommand = "";
@ -237,3 +254,8 @@ FolderData* SystemData::getRootFolder()
{
return mRootFolder;
}
bool SystemData::hasGamelist()
{
return fs::exists(mRootFolder->getPath() + "/gamelist.xml");
}

View file

@ -15,8 +15,12 @@ public:
FolderData* getRootFolder();
std::string getName();
std::string getStartPath();
std::string getExtension();
bool hasGamelist();
void launchGame(GameData* game);
static void deleteSystems();
static void loadConfig();
static void writeExampleConfig();

View file

@ -27,8 +27,78 @@ GameData* searchFolderByPath(FolderData* folder, std::string const& path)
return NULL;
}
void parseXMLFile(std::string xmlpath)
GameData* createGameFromPath(std::string gameAbsPath, SystemData* system)
{
std::string gamePath = gameAbsPath;
std::string sysPath = system->getStartPath();
//strip out the system path stuff so it's relative to the system root folder
for(unsigned int i = 0; i < gamePath.length(); i++)
{
if(gamePath[i] != sysPath[i])
{
gamePath = gamePath.substr(i, gamePath.length() - i);
break;
}
}
if(gamePath[0] != '/')
gamePath.insert(0, "/");
//make our way through the directory tree finding each folder in our path or creating it if it doesn't exist
FolderData* folder = system->getRootFolder();
unsigned int separator = 0;
unsigned int nextSeparator = 0;
while(nextSeparator != std::string::npos)
{
//determine which chunk of the path we're testing right now
nextSeparator = gamePath.find('/', separator + 1);
if(nextSeparator == std::string::npos)
break;
std::string checkName = gamePath.substr(separator + 1, nextSeparator - separator - 1);
separator = nextSeparator;
//see if the folder already exists
bool foundFolder = false;
for(unsigned int i = 0; i < folder->getFileCount(); i++)
{
FileData* checkFolder = folder->getFile(i);
if(checkFolder->isFolder() && checkFolder->getName() == checkName)
{
folder = (FolderData*)checkFolder;
foundFolder = true;
break;
}
}
//the folder didn't already exist, so create it
if(!foundFolder)
{
FolderData* newFolder = new FolderData(system, folder->getPath() + "/" + checkName, checkName);
folder->pushFileData(newFolder);
folder = newFolder;
}
}
//find gameName
std::string gameName = gamePath.substr(separator + 1, gamePath.find(system->getExtension(), separator) - separator - 1);
GameData* game = new GameData(system, gameAbsPath, gameName);
folder->pushFileData(game);
return game;
}
void parseGamelist(SystemData* system)
{
if(!system->hasGamelist())
return;
std::string xmlpath = system->getRootFolder()->getPath() + "/gamelist.xml";
std::cout << "Parsing XML file \"" << xmlpath << "\"...\n";
pugi::xml_document doc;
@ -59,20 +129,13 @@ void parseXMLFile(std::string xmlpath)
std::string path = pathNode.text().get();
GameData* game = NULL;
SystemData* system = NULL;
for(unsigned int i = 0; i < SystemData::sSystemVector.size(); i++)
if(boost::filesystem::exists(path))
{
system = SystemData::sSystemVector.at(i);
game = searchFolderByPath(system->getRootFolder(), path);
if(game != NULL)
break;
}
GameData* game = searchFolderByPath(system->getRootFolder(), path);
if(game == NULL)
game = createGameFromPath(path, system);
if(game == NULL)
{
std::cerr << "Error - game of path \"" << path << "\" was not found by the system's search. Ignoring.\n";
}else{
//actually gather the information in the XML doc, then pass it to the game's set method
std::string newName, newDesc, newImage;
@ -84,16 +147,11 @@ void parseXMLFile(std::string xmlpath)
newImage = gameNode.child("image").text().get();
game->set(newName, newDesc, newImage);
}else{
std::cerr << "Game at \"" << path << "\" does not exist!\n";
}
}
std::cout << "XML parsing complete.\n";
//sort all systems
for(unsigned int i = 0; i < SystemData::sSystemVector.size(); i++)
{
SystemData::sSystemVector.at(i)->getRootFolder()->sort();
}
}

View file

@ -4,6 +4,6 @@
#include <string>
class SystemData;
void parseXMLFile(std::string xmlpath);
void parseGamelist(SystemData* system);
#endif

View file

@ -6,7 +6,8 @@
#include "SystemData.h"
#include <boost/filesystem.hpp>
#include "components/GuiInputConfig.h"
#include "XMLReader.h"
bool PARSEGAMELISTONLY = false;
int main(int argc, char* argv[])
{
@ -41,6 +42,9 @@ int main(int argc, char* argv[])
{
height = atoi(argv[i + 1]);
i++;
}else if(strcmp(argv[i], "--gamelist-only") == 0)
{
PARSEGAMELISTONLY = true;
}
}
}
@ -88,8 +92,8 @@ int main(int argc, char* argv[])
if(!boost::filesystem::exists(SystemData::getConfigPath()))
//try loading the system config file
if(!boost::filesystem::exists(SystemData::getConfigPath())) //if it doesn't exist, create the example and quit
{
std::cerr << "A system config file in " << SystemData::getConfigPath() << " was not found. An example will be created.\n";
SystemData::writeExampleConfig();
@ -98,7 +102,7 @@ int main(int argc, char* argv[])
}else{
SystemData::loadConfig();
if(SystemData::sSystemVector.size() == 0)
if(SystemData::sSystemVector.size() == 0) //if it exists but was empty, notify the user and quit
{
std::cerr << "A system config file in " << SystemData::getConfigPath() << " was found, but contained no systems.\n";
std::cerr << "You should probably go read that, or delete it.\n";
@ -107,13 +111,18 @@ int main(int argc, char* argv[])
bool useDetail = false;
//loaded system config, so try parsing the test XML doc
if(boost::filesystem::exists("gamelist.xml"))
//see if any systems had gamelists present, if so we'll use the detailed GuiGameList
for(unsigned int i = 0; i < SystemData::sSystemVector.size(); i++)
{
parseXMLFile("gamelist.xml");
useDetail = true;
if(SystemData::sSystemVector.at(i)->hasGamelist())
{
useDetail = true;
break;
}
}
//choose which Gui to open up
if(boost::filesystem::exists(InputManager::getConfigPath()))
{
InputManager::loadConfig();