2012-07-20 16:14:09 +00:00
# include "SystemData.h"
# include "GameData.h"
2012-08-08 00:50:45 +00:00
# include "XMLReader.h"
2012-07-20 16:14:09 +00:00
# include <boost/filesystem.hpp>
2012-07-21 19:06:24 +00:00
# include <fstream>
# include <stdlib.h>
2012-07-23 23:53:33 +00:00
# include <SDL/SDL_joystick.h>
2012-08-29 18:53:53 +00:00
# include "Renderer.h"
2012-10-13 18:29:53 +00:00
# include "AudioManager.h"
2012-07-20 16:14:09 +00:00
2012-07-21 20:57:53 +00:00
std : : vector < SystemData * > SystemData : : sSystemVector ;
2012-07-20 16:14:09 +00:00
namespace fs = boost : : filesystem ;
2012-08-08 00:50:45 +00:00
extern bool PARSEGAMELISTONLY ;
2012-08-11 04:17:52 +00:00
extern bool IGNOREGAMELIST ;
2012-08-08 00:50:45 +00:00
std : : string SystemData : : getStartPath ( ) { return mStartPath ; }
std : : string SystemData : : getExtension ( ) { return mSearchExtension ; }
2012-07-21 19:06:24 +00:00
SystemData : : SystemData ( std : : string name , std : : string startPath , std : : string extension , std : : string command )
2012-07-20 16:14:09 +00:00
{
mName = name ;
2012-07-27 16:58:27 +00:00
//expand home symbol if the startpath contains it
if ( startPath [ 0 ] = = ' ~ ' )
{
startPath . erase ( 0 , 1 ) ;
std : : string home = getenv ( " HOME " ) ;
if ( home . empty ( ) )
{
std : : cerr < < " ERROR - System start path contains ~ but $HOME is not set! \n " ;
return ;
} else {
startPath . insert ( 0 , home ) ;
}
}
2012-07-20 16:14:09 +00:00
mStartPath = startPath ;
mSearchExtension = extension ;
2012-07-21 19:06:24 +00:00
mLaunchCommand = command ;
2012-07-27 16:58:27 +00:00
mRootFolder = new FolderData ( this , mStartPath , " Search Root " ) ;
2012-08-08 00:50:45 +00:00
if ( ! PARSEGAMELISTONLY )
populateFolder ( mRootFolder ) ;
2012-08-11 04:17:52 +00:00
if ( ! IGNOREGAMELIST )
parseGamelist ( this ) ;
2012-08-08 00:50:45 +00:00
mRootFolder - > sort ( ) ;
2012-07-20 16:14:09 +00:00
}
SystemData : : ~ SystemData ( )
2012-07-21 19:06:24 +00:00
{
2012-07-27 16:58:27 +00:00
delete mRootFolder ;
2012-07-21 19:06:24 +00:00
}
std : : string strreplace ( std : : string & str , std : : string replace , std : : string with )
{
size_t pos = str . find ( replace ) ;
2012-09-23 21:01:56 +00:00
if ( pos ! = std : : string : : npos )
return str . replace ( pos , replace . length ( ) , with . c_str ( ) , with . length ( ) ) ;
else
return str ;
2012-07-21 19:06:24 +00:00
}
2012-07-27 16:58:27 +00:00
void SystemData : : launchGame ( GameData * game )
2012-07-21 19:06:24 +00:00
{
std : : cout < < " Attempting to launch game... \n " ;
2012-07-23 23:53:33 +00:00
//suspend SDL joystick events (these'll pile up even while something else is running)
SDL_JoystickEventState ( 0 ) ;
2012-10-13 18:29:53 +00:00
AudioManager : : deinit ( ) ;
2012-08-29 18:53:53 +00:00
Renderer : : deinit ( ) ;
2012-07-21 19:06:24 +00:00
std : : string command = mLaunchCommand ;
2012-08-02 01:43:55 +00:00
command = strreplace ( command , " %ROM% " , game - > getBashPath ( ) ) ;
2012-07-21 19:06:24 +00:00
std : : cout < < " " < < command < < " \n " ;
std : : cout < < " ===================================================== \n " ;
2012-10-28 23:07:05 +00:00
int exitCode = system ( command . c_str ( ) ) ;
2012-07-21 19:06:24 +00:00
std : : cout < < " ===================================================== \n " ;
2012-10-28 23:07:05 +00:00
if ( exitCode ! = 0 )
std : : cout < < " ...launch terminated with nonzero exit code! \n " ;
2012-07-23 23:53:33 +00:00
2012-08-29 18:53:53 +00:00
Renderer : : init ( 0 , 0 ) ;
2012-10-13 18:29:53 +00:00
AudioManager : : init ( ) ;
2012-08-29 18:53:53 +00:00
2012-07-23 23:53:33 +00:00
//re-enable SDL joystick events
SDL_JoystickEventState ( 1 ) ;
2012-07-21 19:06:24 +00:00
}
2012-07-27 16:58:27 +00:00
void SystemData : : populateFolder ( FolderData * folder )
2012-07-20 16:14:09 +00:00
{
2012-07-27 16:58:27 +00:00
std : : string folderPath = folder - > getPath ( ) ;
if ( ! fs : : is_directory ( folderPath ) )
2012-07-20 16:14:09 +00:00
{
2012-07-27 16:58:27 +00:00
std : : cerr < < " Error - folder with path \" " < < folderPath < < " \" is not a directory! \n " ;
2012-07-20 16:14:09 +00:00
return ;
}
2012-07-27 16:58:27 +00:00
for ( fs : : directory_iterator end , dir ( folderPath ) ; dir ! = end ; + + dir )
2012-07-20 16:14:09 +00:00
{
2012-07-27 16:58:27 +00:00
fs : : path filePath = ( * dir ) . path ( ) ;
2012-07-20 16:14:09 +00:00
2012-08-11 01:29:51 +00:00
if ( filePath . stem ( ) . string ( ) . empty ( ) )
continue ;
2012-07-27 16:58:27 +00:00
if ( fs : : is_directory ( filePath ) )
2012-07-20 16:14:09 +00:00
{
2012-07-27 16:58:27 +00:00
FolderData * newFolder = new FolderData ( this , filePath . string ( ) , filePath . stem ( ) . string ( ) ) ;
populateFolder ( newFolder ) ;
2012-08-11 04:17:52 +00:00
//ignore folders that do not contain games
if ( newFolder - > getFileCount ( ) = = 0 )
delete newFolder ;
else
folder - > pushFileData ( newFolder ) ;
2012-07-27 16:58:27 +00:00
} else {
2012-09-08 18:17:36 +00:00
//this is a little complicated because we allow a list of extensions to be defined (delimited with a space)
//we first get the extension of the file itself:
std : : string extension = filePath . extension ( ) . string ( ) ;
std : : string chkExt ;
size_t extPos = 0 ;
2012-09-10 18:10:59 +00:00
2012-09-08 18:17:36 +00:00
do {
//now we loop through every extension in the list
size_t cpos = extPos ;
extPos = mSearchExtension . find ( " " , extPos ) ;
chkExt = mSearchExtension . substr ( cpos , ( ( extPos = = std : : string : : npos ) ? mSearchExtension . length ( ) - cpos : extPos - cpos ) ) ;
//if it matches, add it
2012-09-10 18:10:59 +00:00
if ( chkExt = = extension )
2012-09-08 18:17:36 +00:00
{
GameData * newGame = new GameData ( this , filePath . string ( ) , filePath . stem ( ) . string ( ) ) ;
folder - > pushFileData ( newGame ) ;
break ;
} else if ( extPos ! = std : : string : : npos ) //if not, add one to the "next position" marker to skip the space when reading the next extension
{
extPos + + ;
}
2012-09-10 18:10:59 +00:00
} while ( extPos ! = std : : string : : npos & & chkExt ! = " " & & chkExt . find ( " . " ) ! = std : : string : : npos ) ;
2012-07-20 16:14:09 +00:00
}
}
}
std : : string SystemData : : getName ( )
{
return mName ;
}
2012-07-21 19:06:24 +00:00
2012-07-21 20:57:53 +00:00
//creates systems from information located in a config file
2012-07-23 23:53:33 +00:00
void SystemData : : loadConfig ( )
2012-07-21 19:06:24 +00:00
{
2012-07-21 20:57:53 +00:00
deleteSystems ( ) ;
2012-07-21 19:06:24 +00:00
2012-07-23 23:53:33 +00:00
std : : string path = getConfigPath ( ) ;
2012-07-21 20:57:53 +00:00
std : : cout < < " Loading system config file \" " < < path < < " \" ... \n " ;
2012-07-21 19:06:24 +00:00
std : : ifstream file ( path . c_str ( ) ) ;
if ( file . is_open ( ) )
{
std : : string line ;
std : : string sysName , sysPath , sysExtension , sysCommand ;
while ( file . good ( ) )
{
std : : getline ( file , line ) ;
2012-07-21 20:57:53 +00:00
//skip blank lines and comments
if ( line . empty ( ) | | line [ 0 ] = = * " # " )
2012-07-21 19:06:24 +00:00
continue ;
//find the name (left of the equals sign) and the value (right of the equals sign)
bool lineValid = false ;
std : : string varName , varValue ;
for ( unsigned int i = 0 ; i < line . length ( ) ; i + + )
{
if ( line [ i ] = = * " = " )
{
lineValid = true ;
varName = line . substr ( 0 , i ) ;
varValue = line . substr ( i + 1 , line . length ( ) - 1 ) ;
break ;
}
}
if ( lineValid )
{
2012-07-21 20:57:53 +00:00
//map the value to the appropriate variable
2012-07-21 19:06:24 +00:00
if ( varName = = " NAME " )
sysName = varValue ;
else if ( varName = = " PATH " )
2012-09-23 21:01:56 +00:00
{
if ( varValue [ varValue . length ( ) - 1 ] = = ' / ' )
sysPath = varValue . substr ( 0 , varValue . length ( ) - 1 ) ;
else
sysPath = varValue ;
} else if ( varName = = " EXTENSION " )
2012-07-21 19:06:24 +00:00
sysExtension = varValue ;
else if ( varName = = " COMMAND " )
sysCommand = varValue ;
//we have all our variables - create the system object
if ( ! sysName . empty ( ) & & ! sysPath . empty ( ) & & ! sysExtension . empty ( ) & & ! sysCommand . empty ( ) )
{
2012-08-08 00:50:45 +00:00
SystemData * newSystem = new SystemData ( sysName , sysPath , sysExtension , sysCommand ) ;
if ( newSystem - > getRootFolder ( ) - > getFileCount ( ) = = 0 )
{
2012-09-07 21:44:07 +00:00
std : : cout < < " System \" " < < sysName < < " \" has no games! Deleting. \n " ;
2012-08-08 00:50:45 +00:00
delete newSystem ;
} else {
sSystemVector . push_back ( newSystem ) ;
}
2012-07-21 19:06:24 +00:00
//reset the variables for the next block (should there be one)
2012-07-21 20:57:53 +00:00
sysName = " " ; sysPath = " " ; sysExtension = " " ; sysCommand = " " ;
2012-07-21 19:06:24 +00:00
}
} else {
std : : cerr < < " Error reading config file \" " < < path < < " \" - no equals sign found on line \" " < < line < < " \" ! \n " ;
2012-07-21 20:57:53 +00:00
return ;
2012-07-21 19:06:24 +00:00
}
}
} else {
std : : cerr < < " Error - could not load config file \" " < < path < < " \" ! \n " ;
2012-07-21 20:57:53 +00:00
return ;
2012-07-21 19:06:24 +00:00
}
2012-07-21 20:57:53 +00:00
std : : cout < < " Finished loading config file - created " < < sSystemVector . size ( ) < < " systems. \n " ;
return ;
}
2012-07-23 23:53:33 +00:00
void SystemData : : writeExampleConfig ( )
{
std : : string path = getConfigPath ( ) ;
std : : ofstream file ( path . c_str ( ) ) ;
file < < " # This is the EmulationStation Systems configuration file. " < < std : : endl ;
file < < " # Lines that begin with a hash (#) are ignored, as are empty lines. " < < std : : endl ;
file < < " # A sample system might look like this: " < < std : : endl ;
file < < " #NAME=Nintendo Entertainment System " < < std : : endl ;
file < < " #PATH=~/ROMs/nes/ " < < std : : endl ;
2012-09-08 18:17:36 +00:00
file < < " #EXTENSION=.nes .NES " < < std : : endl ;
2012-07-23 23:53:33 +00:00
file < < " #COMMAND=retroarch -L ~/cores/libretro-fceumm.so %ROM% " < < std : : endl < < std : : endl ;
file < < " #NAME is just a name to identify the system. " < < std : : endl ;
file < < " #PATH is the path to start the recursive search for ROMs in. ~ will be expanded into the $HOME variable. " < < std : : endl ;
2012-09-08 18:17:36 +00:00
file < < " #EXTENSION is a list of extensions to search for, separated by spaces. You MUST include the period, and it must be exact - it's case sensitive, and no wildcards. " < < std : : endl ;
file < < " #COMMAND is the shell command to execute when a game is selected. %ROM% will be replaced with the (bash special-character escaped) path to the ROM. " < < std : : endl < < std : : endl ;
2012-07-23 23:53:33 +00:00
file < < " #Now try your own! " < < std : : endl ;
file < < " NAME= " < < std : : endl ;
file < < " PATH= " < < std : : endl ;
file < < " EXTENSION= " < < std : : endl ;
file < < " COMMAND= " < < std : : endl ;
file . close ( ) ;
}
2012-07-21 20:57:53 +00:00
void SystemData : : deleteSystems ( )
{
for ( unsigned int i = 0 ; i < sSystemVector . size ( ) ; i + + )
{
delete sSystemVector . at ( i ) ;
}
sSystemVector . clear ( ) ;
2012-07-21 19:06:24 +00:00
}
2012-07-23 23:53:33 +00:00
std : : string SystemData : : getConfigPath ( )
{
std : : string home = getenv ( " HOME " ) ;
if ( home . empty ( ) )
{
std : : cerr < < " FATAL ERROR - $HOME environment variable empty or nonexistant! \n " ;
exit ( 1 ) ;
return " " ;
}
2012-08-02 02:37:07 +00:00
return ( home + " /.emulationstation/es_systems.cfg " ) ;
2012-07-23 23:53:33 +00:00
}
2012-07-27 16:58:27 +00:00
FolderData * SystemData : : getRootFolder ( )
{
return mRootFolder ;
}
2012-08-08 00:50:45 +00:00
bool SystemData : : hasGamelist ( )
{
return fs : : exists ( mRootFolder - > getPath ( ) + " /gamelist.xml " ) ;
}