mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 15:45:38 +00:00
Better gamelist.xml support. See changelog.txt (August 7) for more details.
This commit is contained in:
parent
6bc7fdf371
commit
6cfe83e8fe
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
#include <string>
|
||||
class SystemData;
|
||||
|
||||
void parseXMLFile(std::string xmlpath);
|
||||
void parseGamelist(SystemData* system);
|
||||
|
||||
#endif
|
||||
|
|
25
src/main.cpp
25
src/main.cpp
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue