From a9e44df9031c5e42d08d77ff203b320342bbbff5 Mon Sep 17 00:00:00 2001 From: Andrew Mickelson Date: Sat, 1 Mar 2014 16:56:39 -0800 Subject: [PATCH] Issue #16 - Import romlists from other frontend formats - Added the "--import-romlist" command line option which allows for the import from Mamewah/Wahcade! .lst files, HyperSpin .xml files, and Attract-Mode .txt files. Multiple lists can be imported and combined into a single Attract-Mode romlist. --- Readme.md | 33 +++++-- src/fe_build.cpp | 210 +++++++++++++++++++++++++++++++++++++++----- src/fe_info.cpp | 6 ++ src/fe_info.hpp | 2 + src/fe_settings.hpp | 21 ++++- src/fe_xml.cpp | 190 ++++++++++++++++++++++++++++++--------- src/fe_xml.hpp | 17 +++- src/main.cpp | 98 +++++++++++++++++---- 8 files changed, 488 insertions(+), 89 deletions(-) diff --git a/Readme.md b/Readme.md index b4762f2ae..894a69ff8 100644 --- a/Readme.md +++ b/Readme.md @@ -72,9 +72,12 @@ be selected from the "Sound" menu when in config mode and mapped to an action or event. **ARTWORK:** Attract-Mode supports PNG, JPEG, GIF, BMP and TGA image formats. -When deciding what image file to use for a particular artwork type -(default artworks are "marquee" and "screen"), Attract-Mode will use the -artwork/movie selection order set out below. +When deciding what image file to use for a particular artwork type, Attract- +Mode will use the artwork/movie selection order set out below. The default +artworks in Attract-Mode are: "marquee" (for cabinet marquee images), +"snap" (for game screen shots), "flyer" (for game flyer/box art) and "wheel" +(for HyperSpin wheel art). You can add others as needed in the emulator +configuration menu. **MOVIE:** Attract-Mode should support any movie format supported by FFmpeg. The movie to play is decided in the order set out below. @@ -111,11 +114,31 @@ file that can be edited by most common spreadsheet programs (be sure to load it as "Text CSV"). The file has one game entry per line, with the very first line of the file specifying what each column represents. -In addition to the Romlist generation function available in config mode, +In addition to the romlist generation function available in config mode, Attract-Mode can generate a romlist for multiple emulators from the command line using the following command: - attract --build-rom-list [emulator names...] + attract --build-romlist + +You can also import romlists from MameWah/Wahcade! (.lst), Attract-Mode +(.txt) and HyperSpin (.xml) using the following command: + + attract --import-romlist [emulator name] + +The --build-romlist and --import-romlist options can be chained together in +all sorts of strange and wonderful ways to generate combined Attract-Mode +romlists. So: + +`attract --import-romlist mame.lst --import-romlist nintendo.lst nestopia` + +will combine the entries from the mame.lst and nintendo.lst files (located +in the current directory) into a single Attract-Mode romlist. The "mame" +emulator will be used for the mame.lst games, while the "nestopia" emulator +will be used for the nintendo.lst games. + +If you wish to specify the name of the created romlist at the command +line, you can do so with the `--output ` option. Beware that this will +overwrite any existing Attract-Mode romlist with the specified name. [Compile.md]: Compile.md [Layouts.md]: Layouts.md diff --git a/src/fe_build.cpp b/src/fe_build.cpp index 362456464..a48ed8e8b 100644 --- a/src/fe_build.cpp +++ b/src/fe_build.cpp @@ -28,6 +28,7 @@ #include #include #include +#include // strcasecmp() extern const char *FE_ROMLIST_SUBDIR; @@ -130,7 +131,7 @@ void write_romlist( const std::string &filename, // one line header showing what the columns represent // outfile << "#" << FeRomInfo::indexStrings[i++]; - while ( FeRomInfo::indexStrings[i] != NULL ) + while ( i < FeRomInfo::Favourite ) outfile << ";" << FeRomInfo::indexStrings[i++]; outfile << std::endl; @@ -144,12 +145,20 @@ void write_romlist( const std::string &filename, } } +struct myclasscmp +{ + bool operator() ( const std::string &lhs, const std::string &rhs ) + { + return ( strcasecmp( lhs.c_str(), rhs.c_str() ) < 0 ); + } +}; + void ini_import( const std::string &filename, std::list &romlist, FeRomInfo::Index index, const std::string &init_tag ) { - std::map my_map; + std::map my_map; // create entries in the map for each name we want to find for ( std::list::iterator itr=romlist.begin(); @@ -192,7 +201,7 @@ void ini_import( const std::string &filename, size_t pos=0; token_helper( line, pos, name, "=" ); - std::map::iterator itr; + std::map::iterator itr; itr = my_map.find( name ); if ( itr != my_map.end() ) { @@ -220,8 +229,8 @@ void ini_import( const std::string &filename, } } - std::cout << "Found info for " << count << " entries. File: " - << filename << std::endl; + std::cout << "[Import " << filename << "] - found info for " << count + << " entries." << std::endl; } void apply_import_extras( const FeEmulatorInfo &emulator, @@ -255,31 +264,184 @@ void apply_import_extras( const FeEmulatorInfo &emulator, } while ( pos < iextras.size() ); } +bool import_mamewah( const std::string &input_filename, + const std::string &emulator_name, + std::list &romlist ) +{ + // Map the lines of the Mamewah / Wahcade! romlist file to our RomInfo structure + // + const FeRomInfo::Index wah_map[] = + { + FeRomInfo::Romname, // Name + FeRomInfo::Title, // Description + FeRomInfo::Year, + FeRomInfo::Manufacturer, + FeRomInfo::Cloneof, + FeRomInfo::LAST_INDEX, // Romof + FeRomInfo::DisplayType, + FeRomInfo::Rotation, + FeRomInfo::Control, + FeRomInfo::Status, + FeRomInfo::LAST_INDEX, // Colour status + FeRomInfo::LAST_INDEX, // Sound status + FeRomInfo::Category + }; + + std::ifstream myfile( input_filename.c_str() ); + if ( !myfile.is_open() ) + { + std::cerr << "Error opening file: " << input_filename << std::endl; + return false; + } + + int count=0; + FeRomInfo current_rom; + + while ( myfile.good() ) + { + std::string line; + getline( myfile, line ); + + int index = ( count % 13 ); + + if ( wah_map[ index ] != FeRomInfo::LAST_INDEX ) + { + size_t l = line.find_last_not_of( FE_WHITESPACE ); + if ( l != std::string::npos ) + { + current_rom.set_info( + wah_map[ index ], + line.substr( 0, l+1 ) ); + } + } + + if ( index == 12 ) + { + current_rom.set_info( + FeRomInfo::Emulator, + emulator_name ); + + romlist.push_back( current_rom ); + current_rom.clear(); + count = 0; + } + else + count++; + } + + if ( count > 1 ) + { + std::cout << "Warning: Unexpected end of file encountered: " << input_filename + << ", this probably means the import failed." << std::endl; + } + + myfile.close(); + return true; +} + }; // end namespace -bool FeSettings::build_romlist( const std::vector &emu_names ) +bool FeSettings::build_romlist( const std::vector< FeImportTask > &task_list, + const std::string &output_name ) { std::list total_romlist; - std::string name, list_name, path; + std::string best_name, list_name, path; - for ( std::vector::const_iterator itr=emu_names.begin(); - itr < emu_names.end(); ++itr ) + for ( std::vector::const_iterator itr=task_list.begin(); + itr < task_list.end(); ++itr ) { - FeEmulatorInfo *emu = get_emulator( *itr ); - if ( emu == NULL ) + if ( (*itr).file_name.empty() ) { - std::cout << "Error: Invalid -build-rom-list target: " << (*itr) - << std::endl; + // Build romlist task + FeEmulatorInfo *emu = get_emulator( (*itr).emulator_name ); + if ( emu == NULL ) + { + std::cout << "Error: Invalid -build-rom-list target: " << (*itr).emulator_name + << std::endl; + } + else + { + std::list romlist; + + best_name = emu->get_info( FeEmulatorInfo::Name ); + + build_basic_romlist( *emu, romlist ); + + apply_listxml( *emu, romlist ); + apply_import_extras( *emu, romlist ); + + total_romlist.splice( total_romlist.end(), romlist ); + } } else { + // import romlist from file task std::list romlist; - name = emu->get_info( FeEmulatorInfo::Name ); + std::string emu_name; - build_basic_romlist( *emu, romlist ); + if ( (*itr).emulator_name.empty() ) + { + // deduce the emulator name from the filename provided + size_t my_start = (*itr).file_name.find_last_of( "\\/" ); + if ( my_start == std::string::npos ) // if there is no / we start at the beginning + my_start = 0; + else + my_start += 1; + + size_t my_end = (*itr).file_name.find_last_of( "." ); + if ( my_end != std::string::npos ) + emu_name = (*itr).file_name.substr( my_start, my_end - my_start ); + } + else + emu_name = (*itr).emulator_name; - apply_listxml( *emu, romlist ); - apply_import_extras( *emu, romlist ); + best_name = emu_name; + + if ( tail_compare( (*itr).file_name, ".txt" ) ) + { + // Attract-Mode format list + // + FeRomList temp_list; + temp_list.load_from_file( (*itr).file_name, ";" ); + + for ( int i=0; i< temp_list.size(); i++ ) + romlist.push_back( temp_list[i] ); + } + else if ( tail_compare( (*itr).file_name, ".lst" ) ) + { + // Mamewah/Wahcade! format list + // + import_mamewah( (*itr).file_name, emu_name, romlist ); + } + else if ( tail_compare( (*itr).file_name, ".xml" ) ) + { + // HyperSpin format list + // + FeHyperSpinXMLParser my_parser( romlist ); + if ( my_parser.parse( (*itr).file_name ) ) + { + // Update the emulator entries + for ( std::list::iterator itr=romlist.begin(); itr != romlist.end(); ++itr ) + (*itr).set_info( FeRomInfo::Emulator, emu_name ); + } + } + else + { + std::cerr << "Error: Unsupported --import-rom-list file: " + << (*itr).file_name << std::endl; + } + + std::cout << "[Import " << (*itr).file_name << "] - Imported " << romlist.size() << " entries." + << std::endl; + + FeEmulatorInfo *emu = get_emulator( emu_name ); + if ( emu == NULL ) + { + std::cout << "Warning: The emulator specified with --import-rom-list was not found: " + << emu_name << std::endl; + } + else + apply_import_extras( *emu, romlist ); total_romlist.splice( total_romlist.end(), romlist ); } @@ -291,14 +453,22 @@ bool FeSettings::build_romlist( const std::vector &emu_names ) total_romlist.sort( FeRomListCompare::cmp ); FeRomListCompare::close_rex(); - if ( emu_names.size() > 1 ) - name = "multi"; + if ( task_list.size() > 1 ) + best_name = "multi"; path = get_config_dir(); confirm_directory( path, FE_ROMLIST_SUBDIR ); path += FE_ROMLIST_SUBDIR; - get_available_filename( path, name, FE_ROMLIST_FILE_EXTENSION, list_name ); + + // if we weren't given a specific output name, then we come up with a name + // that doesn't exist already + // + if ( output_name.empty() ) + get_available_filename( path, best_name, FE_ROMLIST_FILE_EXTENSION, list_name ); + else + list_name = path + output_name + FE_ROMLIST_FILE_EXTENSION; + write_romlist( list_name, total_romlist ); return true; diff --git a/src/fe_info.cpp b/src/fe_info.cpp index 1b25fd4ee..7becaefaf 100644 --- a/src/fe_info.cpp +++ b/src/fe_info.cpp @@ -117,6 +117,12 @@ void FeRomInfo::dump( void ) const std::cout << std::endl; } +void FeRomInfo::clear() +{ + for ( int i=0; i < LAST_INDEX; i++ ) + m_info[i].clear(); +} + SQRex *FeRomListCompare::m_rex = NULL; void FeRomListCompare::init_rex( const std::string &re_mask ) diff --git a/src/fe_info.hpp b/src/fe_info.hpp index 0b097628a..c8a7411ff 100644 --- a/src/fe_info.hpp +++ b/src/fe_info.hpp @@ -69,6 +69,8 @@ class FeRomInfo : public FeBaseConfigurable void dump( void ) const; std::string as_output( void ) const; + void clear(); + private: FeRomInfo &operator=( const FeRomInfo & ); std::string get_info_escaped( int ) const; diff --git a/src/fe_settings.hpp b/src/fe_settings.hpp index 8c49c3b4f..5eae87ba9 100644 --- a/src/fe_settings.hpp +++ b/src/fe_settings.hpp @@ -35,6 +35,19 @@ extern const char *FE_EMULATOR_FILE_EXTENSION; extern const char *FE_LAYOUT_UI_KEY_FILE; +// A container for each task when importing/building romlists from the command line +class FeImportTask +{ +public: + FeImportTask( const std::string &fn, const std::string &en ) + : file_name( fn ), emulator_name( en ) + { + }; + + std::string file_name; // if empty, then we build romlist for specified emulator + std::string emulator_name; +}; + class FeSettings : public FeBaseConfigurable { public: @@ -220,10 +233,12 @@ class FeSettings : public FeBaseConfigurable bool confirm_favs() const { return m_confirm_favs; } // - // This function implements the command-line romlist generation - // A non-existing filename is chosen for the resulting romlist + // This function implements command-line romlist generation/imports + // If output_name is empty, then a non-existing filename is chosen for + // the resulting romlist file // - bool build_romlist( const std::vector< std::string > &emu_list ); + bool build_romlist( const std::vector< FeImportTask > &task_list, + const std::string &output_name ); // This function implements the config-mode romlist generation // A romlist named ".txt" is created in the romlist dir, diff --git a/src/fe_xml.cpp b/src/fe_xml.cpp index 1c814f735..c72eab500 100644 --- a/src/fe_xml.cpp +++ b/src/fe_xml.cpp @@ -21,12 +21,12 @@ */ #include "fe_xml.hpp" -#include "fe_info.hpp" #include "fe_util.hpp" #include #include #include #include +#include #include // @@ -38,8 +38,8 @@ void exp_handle_data( void *data, const char *content, int length ) ((FeXMLParser *)data)->handle_data( content, length ); } -void exp_start_element( void *data, - const char *element, +void exp_start_element( void *data, + const char *element, const char **attribute ) { ((FeXMLParser *)data)->start_element( element, attribute ); @@ -61,7 +61,7 @@ void FeXMLParser::handle_data( const char *content, int length ) m_current_data.append( content, length ); } -struct user_data_struct +struct user_data_struct { XML_Parser parser; bool parsed_xml; @@ -70,10 +70,10 @@ struct user_data_struct bool my_parse_callback( const char *buff, void *opaque ) { struct user_data_struct *ds = (struct user_data_struct *)opaque; - if ( XML_Parse( ds->parser, buff, + if ( XML_Parse( ds->parser, buff, strlen(buff), XML_FALSE ) == XML_STATUS_ERROR ) { - std::cout << "Error parsing xml output:" + std::cout << "Error parsing xml output:" << buff << std::endl; } else @@ -83,7 +83,7 @@ bool my_parse_callback( const char *buff, void *opaque ) return p->get_continue_parse(); // return false to cancel callback } -bool FeXMLParser::parse_internal( +bool FeXMLParser::parse_internal( const std::string &prog, const std::string &args ) { @@ -115,9 +115,9 @@ bool FeMapComp::operator()(const char *lhs, const char *rhs) const // // Mame XML Parser // -FeMameXMLParser::FeMameXMLParser( - std::list &romlist, - UiUpdate u, +FeMameXMLParser::FeMameXMLParser( + std::list &romlist, + UiUpdate u, void *d ) : FeXMLParser( u, d ), m_romlist( romlist ), @@ -126,8 +126,8 @@ FeMameXMLParser::FeMameXMLParser( { } -void FeMameXMLParser::start_element( - const char *element, +void FeMameXMLParser::start_element( + const char *element, const char **attribute ) { if ( strcmp( element, "game" ) == 0 ) @@ -207,7 +207,11 @@ void FeMameXMLParser::start_element( } else if ( strcmp( element, "control" ) == 0 ) { - std::string type, ways; + std::string type, ways, old_type; + + old_type = (*m_itr).get_info( FeRomInfo::Control ); + if ( !old_type.empty() ) + old_type += ","; for ( int i=0; attribute[i]; i+=2 ) { @@ -218,48 +222,46 @@ void FeMameXMLParser::start_element( } struct my_map_struct { const char *in; const char *out; }; - my_map_struct map[] = + my_map_struct my_map[] = { { "stick", "joystick (analog)" }, { "doublejoy", "double joystick" }, - { "paddle", "paddle" }, - { "trackball", "trackball" }, - { "dial", "dial" }, { NULL, NULL } }; if ( type.compare( "joy" ) == 0 ) { + // construct the joystick name + // type = "joystick ("; type += ways; type += "-way)"; - (*m_itr).set_info( FeRomInfo::Control, type ); } else { + // we also do a bit of name remapping + // int i=0; - while ( map[i].in != NULL ) + while ( my_map[i].in != NULL ) { - if ( type.compare( map[i].in ) == 0 ) + if ( type.compare( my_map[i].in ) == 0 ) { - (*m_itr).set_info( FeRomInfo::Control, map[i].out ); + type = my_map[i].out; break; } i++; } - - if ( (*m_itr).get_info( FeRomInfo::Control ).empty() ) - (*m_itr).set_info( FeRomInfo::Control, type ); } + (*m_itr).set_info( FeRomInfo::Control, old_type + type ); } else if (( strcmp( element, "description" ) == 0 ) || ( strcmp( element, "year" ) == 0 ) || ( strcmp( element, "manufacturer" ) == 0 )) { m_element_open=true; - } + } } -} +} void FeMameXMLParser::end_element( const char *element ) { @@ -269,7 +271,7 @@ void FeMameXMLParser::end_element( const char *element ) { m_collect_data = false; - if ( !m_keep_rom ) + if ( !m_keep_rom ) m_discarded.push_back( m_itr ); (*m_itr).set_info( FeRomInfo::DisplayCount, as_str( m_displays ) ); @@ -279,7 +281,7 @@ void FeMameXMLParser::end_element( const char *element ) if ( percent > m_percent ) { m_percent = percent; - std::cout << "\b\b\b\b" << std::setw(3) + std::cout << "\b\b\b\b" << std::setw(3) << m_percent << '%' << std::flush; if ( m_ui_update ) @@ -359,9 +361,9 @@ bool FeMameXMLParser::parse( const std::string &prog ) if ( !m_discarded.empty() ) { - std::cout << "Discarded " << m_discarded.size() + std::cout << "Discarded " << m_discarded.size() << " entries based on xml info: "; - std::vector::iterator>::iterator itr; + std::vector::iterator>::iterator itr; for ( itr = m_discarded.begin(); itr != m_discarded.end(); ++itr ) { std::cout << (*(*itr)).get_info( FeRomInfo::Romname ) << " "; @@ -376,9 +378,9 @@ bool FeMameXMLParser::parse( const std::string &prog ) // // Mess XML Parser // -FeMessXMLParser::FeMessXMLParser( - std::list &romlist, - UiUpdate u, +FeMessXMLParser::FeMessXMLParser( + std::list &romlist, + UiUpdate u, void *d ) : FeXMLParser( u, d ), m_romlist( romlist ) { @@ -394,8 +396,8 @@ void FeMessXMLParser::clear_parse_state() m_fuzzydesc.clear(); } -void FeMessXMLParser::start_element( - const char *element, +void FeMessXMLParser::start_element( + const char *element, const char **attribute ) { if ( strcmp( element, "software" ) == 0 ) @@ -412,7 +414,7 @@ void FeMessXMLParser::start_element( std::string romname = (*m_itr).get_info( FeRomInfo::Romname ); if ( romname.compare( attribute[i+1] ) == 0 ) { - m_keep_rom=true; + m_keep_rom=true; break; } } @@ -421,7 +423,7 @@ void FeMessXMLParser::start_element( } else if (( strcmp( element, "description" ) == 0 ) || ( strcmp( element, "year" ) == 0 ) - || ( strcmp( element, "publisher" ) == 0 )) + || ( strcmp( element, "publisher" ) == 0 )) { m_element_open=true; } @@ -443,16 +445,16 @@ void FeMessXMLParser::end_element( const char *element ) // otherwise try matching based on description for ( m_itr=m_romlist.begin(); m_itr!=m_romlist.end(); ++m_itr ) { - if ( m_description.compare( + if ( m_description.compare( (*m_itr).get_info( FeRomInfo::Romname ) ) == 0 ) - { + { (*m_itr).set_info( FeRomInfo::Title, m_description ); (*m_itr).set_info( FeRomInfo::Year, m_year ); (*m_itr).set_info( FeRomInfo::Manufacturer, m_man ); (*m_itr).set_info( FeRomInfo::Cloneof, m_name ); } else if (( (*m_itr).get_info( FeRomInfo::Cloneof ).empty() ) - && ( (*m_itr).get_info( FeRomInfo::Title ).find( + && ( (*m_itr).get_info( FeRomInfo::Title ).find( m_fuzzydesc ) != std::string::npos )) { (*m_itr).set_info( FeRomInfo::Year, m_year ); @@ -468,7 +470,7 @@ void FeMessXMLParser::end_element( const char *element ) if ( strcmp( element, "description" ) == 0 ) { m_description = m_current_data; - size_t pos=0; + size_t pos=0; token_helper( m_current_data, pos, m_fuzzydesc, "(" ); } else if ( strcmp( element, "year" ) == 0 ) @@ -481,9 +483,113 @@ void FeMessXMLParser::end_element( const char *element ) } } -bool FeMessXMLParser::parse( const std::string &prog, +bool FeMessXMLParser::parse( const std::string &prog, const std::string &args ) { return parse_internal( prog, args ); } +FeHyperSpinXMLParser::FeHyperSpinXMLParser( std::list & li ) + : m_romlist( li ), m_collect_data( false ) +{ +} + +void FeHyperSpinXMLParser::start_element( + const char *element, + const char **attribute ) +{ + if ( strcmp( element, "game" ) == 0 ) + { + m_current_rom.clear(); + m_collect_data = true; + + for ( int i=0; attribute[i]; i+=2 ) + { + if ( strcmp( attribute[i], "name" ) == 0 ) + { + m_current_rom.set_info( FeRomInfo::Romname, attribute[i+1] ); + break; + } + } + } + else if ( m_collect_data ) + { + if (( strcmp( element, "description" ) == 0 ) + || ( strcmp( element, "cloneof" ) == 0 ) + || ( strcmp( element, "year" ) == 0 ) + || ( strcmp( element, "manufacturer" ) == 0 ) + || ( strcmp( element, "genre" ) == 0 )) + { + m_element_open=true; + } + } +} + +void FeHyperSpinXMLParser::end_element( const char *element ) +{ + if ( strcmp( element, "game" ) == 0 ) + { + m_romlist.push_back( m_current_rom ); + m_collect_data = false; + } + else if ( m_element_open ) + { + if ( strcmp( element, "description" ) == 0 ) + m_current_rom.set_info( FeRomInfo::Title, m_current_data ); + else if ( strcmp( element, "cloneof" ) == 0 ) + m_current_rom.set_info( FeRomInfo::Cloneof, m_current_data ); + else if ( strcmp( element, "manufacturer" ) == 0 ) + m_current_rom.set_info( FeRomInfo::Manufacturer, m_current_data ); + else if ( strcmp( element, "year" ) == 0 ) + m_current_rom.set_info( FeRomInfo::Year, m_current_data ); + else if ( strcmp( element, "genre" ) == 0 ) + m_current_rom.set_info( FeRomInfo::Category, m_current_data ); + + m_current_data.clear(); + m_element_open=false; + } +} + +bool FeHyperSpinXMLParser::parse( const std::string &filename ) +{ + m_element_open=m_keep_rom=false; + m_continue_parse=true; + + XML_Parser parser = XML_ParserCreate( NULL ); + XML_SetUserData( parser, (void *)this ); + XML_SetElementHandler( parser, exp_start_element, exp_end_element ); + XML_SetCharacterDataHandler( parser, exp_handle_data ); + + std::ifstream myfile( filename.c_str() ); + if ( !myfile.is_open() ) + { + std::cerr << "Error opening file: " << filename << std::endl; + XML_ParserFree( parser ); + return false; + } + + bool ret_val = true; + + while ( myfile.good() ) + { + std::string line; + getline( myfile, line ); + + if ( XML_Parse( parser, line.c_str(), + line.size(), XML_FALSE ) == XML_STATUS_ERROR ) + { + std::cout << "Error parsing xml:" + << line << std::endl; + ret_val = false; + break; + } + } + + myfile.close(); + + // need to pass true to XML Parse on last line + XML_Parse( parser, 0, 0, XML_TRUE ); + XML_ParserFree( parser ); + + return ret_val; +} diff --git a/src/fe_xml.hpp b/src/fe_xml.hpp index f5f45e1a4..3fd99cf40 100644 --- a/src/fe_xml.hpp +++ b/src/fe_xml.hpp @@ -27,7 +27,7 @@ #include #include #include -class FeRomInfo; +#include "fe_info.hpp" class FeXMLParser { @@ -110,4 +110,19 @@ class FeMessXMLParser : private FeXMLParser void clear_parse_state(); }; +class FeHyperSpinXMLParser : private FeXMLParser +{ +public: + FeHyperSpinXMLParser( std::list & ); + bool parse( const std::string &filename ); + +private: + void start_element( const char *, const char ** ); + void end_element( const char * ); + + std::list &m_romlist; + FeRomInfo m_current_rom; + bool m_collect_data; +}; + #endif diff --git a/src/main.cpp b/src/main.cpp index 275fc84db..0db4ef8a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,12 +49,15 @@ void process_args( int argc, char *argv[], // // Deal with command line arguments // - std::vector romlist_emulators; + std::vector task_list; + std::string output_name; + int next_arg=1; while ( next_arg < argc ) { - if ( strcmp( argv[next_arg], "--config" ) == 0 ) + if (( strcmp( argv[next_arg], "-c" ) == 0 ) + || ( strcmp( argv[next_arg], "--config" ) == 0 )) { next_arg++; if ( next_arg < argc ) @@ -68,7 +71,8 @@ void process_args( int argc, char *argv[], exit(1); } } - else if ( strcmp( argv[next_arg], "--font" ) == 0 ) + else if (( strcmp( argv[next_arg], "-f" ) == 0 ) + || ( strcmp( argv[next_arg], "--font" ) == 0 )) { next_arg++; if ( next_arg < argc ) @@ -82,26 +86,70 @@ void process_args( int argc, char *argv[], exit(1); } } - else if ( strcmp( argv[next_arg], "--build-rom-list" ) == 0 ) + else if (( strcmp( argv[next_arg], "-b" ) == 0 ) + || ( strcmp( argv[next_arg], "--build-romlist" ) == 0 )) { next_arg++; + int first_cmd_arg = next_arg; for ( ; next_arg < argc; next_arg++ ) { if ( argv[next_arg][0] == '-' ) break; - romlist_emulators.push_back( argv[next_arg] ); + + task_list.push_back( FeImportTask( "", argv[next_arg] )); } - if ( romlist_emulators.empty() ) + if ( next_arg == first_cmd_arg ) { - std::cerr << "Error, no target emulators specified with --build-rom-list option." + std::cerr << "Error, no target emulators specified with --build-romlist option." << std::endl; exit(1); } + } + else if (( strcmp( argv[next_arg], "-i" ) == 0 ) + || ( strcmp( argv[next_arg], "--import-romlist" ) == 0 )) + { + std::string my_filename; + std::string my_emulator; + next_arg++; + if ( next_arg < argc ) + { + my_filename = argv[next_arg]; + next_arg++; + } + else + { + std::cerr << "Error, no filename specified with --import-romlist option." << std::endl; + exit(1); + } + + if ( ( next_arg < argc ) && ( argv[next_arg][0] != '-' ) ) + { + my_emulator = argv[ next_arg ]; + next_arg++; + } + + task_list.push_back( FeImportTask( my_filename, my_emulator )); + } + else if (( strcmp( argv[next_arg], "-o" ) == 0 ) + || ( strcmp( argv[next_arg], "--output" ) == 0 )) + { + next_arg++; + if ( next_arg < argc ) + { + output_name = argv[next_arg]; + next_arg++; + } + else + { + std::cerr << "Error, no output filename specified with --output option." << std::endl; + exit(1); + } } - else if ( strcmp( argv[next_arg], "--version" ) == 0 ) + else if (( strcmp( argv[next_arg], "-v" ) == 0 ) + || ( strcmp( argv[next_arg], "--version" ) == 0 )) { std::cout << FE_NAME << " " << FE_VERSION << " (" << get_OS_string() @@ -128,7 +176,8 @@ void process_args( int argc, char *argv[], else { int retval=1; - if ( strcmp( argv[next_arg], "--help" ) != 0 ) + if (( strcmp( argv[next_arg], "-h" ) != 0 ) + && ( strcmp( argv[next_arg], "--help" ) != 0 )) { std::cerr << "Unrecognized command line option: " << argv[next_arg] << std::endl; @@ -136,22 +185,35 @@ void process_args( int argc, char *argv[], } std::cout << FE_COPYRIGHT << std::endl - << "Usage: " << argv[0] << " [option...]" << std::endl - << "OPTIONS:" << std::endl - << "\t--build-rom-list : build rom list for specified emulators" << std::endl - << "\t--config : specify config directory" << std::endl - << "\t--font : specify default font name" << std::endl - << "\t--help: show this message" << std::endl - << "\t--version: show version information" << std::endl; + << "Usage: " << argv[0] << " [option...]" << std::endl << std::endl + << "ROMLIST IMPORT/BUILD OPTIONS:" << std::endl + << " -b, --build-romlist [emu(s)...]" << std::endl + << " Builds a romlist using the configuration for the specified emulator(s)" << std::endl + << " -i, --import-romlist [emu]" << std::endl + << " Import romlist from the specified file. Supported formats:" << std::endl + << " *.lst (Mamewah/Wahcade!)" << std::endl + << " *.txt (Attract-Mode)" << std::endl + << " *.xml (HyperSpin)" << std::endl + << " The emulator to use for list entries can be specified as well" << std::endl + << " -o, --output " << std::endl + << " Specify the name of the romlist to create, overwriting any existing" + << std::endl << std::endl + << "OTHER OPTIONS:" << std::endl + << " -c, --config " << std::endl + << " Specify the configuration to use" << std::endl + << " -f, --font " << std::endl + << " Specify the default font to use" << std::endl + << " -h, --help: Show this message" << std::endl + << " -v, --version: Show version information" << std::endl; exit( retval ); } } - if ( !romlist_emulators.empty() ) + if ( !task_list.empty() ) { FeSettings feSettings( config_path, cmdln_font ); feSettings.load(); - int retval = feSettings.build_romlist( romlist_emulators ); + int retval = feSettings.build_romlist( task_list, output_name ); exit( retval ); } }