diff --git a/README.md b/README.md
index 69902e7b5..84f4f5451 100644
--- a/README.md
+++ b/README.md
@@ -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:
```
@@ -50,8 +50,9 @@ 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 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
diff --git a/changelog.txt b/changelog.txt
index 003b63224..d836b4397 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -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.
diff --git a/src/FolderData.cpp b/src/FolderData.cpp
index 0e21aa109..8d0d43596 100644
--- a/src/FolderData.cpp
+++ b/src/FolderData.cpp
@@ -1,6 +1,7 @@
#include "FolderData.h"
#include "SystemData.h"
#include
+#include
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();
+ }
}
diff --git a/src/SystemData.cpp b/src/SystemData.cpp
index 62869537e..90c8524e7 100644
--- a/src/SystemData.cpp
+++ b/src/SystemData.cpp
@@ -1,5 +1,6 @@
#include "SystemData.h"
#include "GameData.h"
+#include "XMLReader.h"
#include
#include
#include
@@ -9,6 +10,11 @@ std::vector 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");
+}
diff --git a/src/SystemData.h b/src/SystemData.h
index c8926339b..4d8f8aca9 100644
--- a/src/SystemData.h
+++ b/src/SystemData.h
@@ -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();
diff --git a/src/XMLReader.cpp b/src/XMLReader.cpp
index 1c9f37744..48c20d113 100644
--- a/src/XMLReader.cpp
+++ b/src/XMLReader.cpp
@@ -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();
- }
}
diff --git a/src/XMLReader.h b/src/XMLReader.h
index 90d0390a8..00e4d2fa1 100644
--- a/src/XMLReader.h
+++ b/src/XMLReader.h
@@ -4,6 +4,6 @@
#include
class SystemData;
-void parseXMLFile(std::string xmlpath);
+void parseGamelist(SystemData* system);
#endif
diff --git a/src/main.cpp b/src/main.cpp
index 34fbeed8a..f405284b2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,7 +6,8 @@
#include "SystemData.h"
#include
#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();