void FeRomList::get_tags_list( FeRomInfo &rom, std::vector< std::pair<std::string, bool> > &tags_list ) const { std::string curr_tags = rom.get_info(FeRomInfo::Tags); std::set<std::string> my_set; size_t pos=0; do { std::string one_tag; const char sep[] = { FE_TAGS_SEP, 0 }; token_helper( curr_tags, pos, one_tag, sep ); if ( !one_tag.empty() ) { my_set.insert( one_tag ); } } while ( pos < curr_tags.size() ); for ( std::map<std::string, bool>::const_iterator itr=m_tags.begin(); itr!=m_tags.end(); ++itr ) { tags_list.push_back( std::pair<std::string, bool>((*itr).first, ( my_set.find( (*itr).first ) != my_set.end() ) ) ); } }
void FeRomList::get_tags_list( int filter_index, int rom_index, std::vector< std::pair<std::string, bool> > &tags_list ) const { if (( rom_index < 0 ) || ( rom_index >= filter_size( filter_index ) )) return; std::string curr_tags = (m_filtered_list[filter_index][rom_index])->get_info(FeRomInfo::Tags); std::set<std::string> my_set; size_t pos=0; do { std::string one_tag; const char sep[] = { FE_TAGS_SEP, 0 }; token_helper( curr_tags, pos, one_tag, sep ); if ( !one_tag.empty() ) { my_set.insert( one_tag ); } } while ( pos < curr_tags.size() ); for ( std::map<std::string, bool>::const_iterator itr=m_tags.begin(); itr!=m_tags.end(); ++itr ) { tags_list.push_back( std::pair<std::string, bool>((*itr).first, ( my_set.find( (*itr).first ) != my_set.end() ) ) ); } }
void string_to_vector( const std::string &input, std::vector< std::string > &vec, bool allow_empty ) { size_t pos=0; do { std::string val; token_helper( input, pos, val ); if ( ( !val.empty() ) || allow_empty ) vec.push_back( val ); } while ( pos < input.size() ); }
bool run_program( const std::string &prog, const::std::string &args, output_callback_fn callback, void *opaque, bool block, const std::string &exit_hotkey, int joy_thresh ) { const int POLL_FOR_EXIT_MS=100; std::string comstr( prog ); comstr += " "; comstr += args; #ifdef SFML_SYSTEM_WINDOWS HANDLE child_output_read=NULL; HANDLE child_output_write=NULL; SECURITY_ATTRIBUTES satts; satts.nLength = sizeof( SECURITY_ATTRIBUTES ); satts.bInheritHandle = TRUE; satts.lpSecurityDescriptor = NULL; STARTUPINFO si; PROCESS_INFORMATION pi; MSG msg; ZeroMemory( &si, sizeof(si) ); ZeroMemory( &pi, sizeof(pi) ); si.cb = sizeof(si); if ( NULL != callback ) { CreatePipe( &child_output_read, &child_output_write, &satts, 1024 ); si.hStdOutput = child_output_write; si.dwFlags |= STARTF_USESTDHANDLES; } LPSTR cmdline = new char[ comstr.length() + 1 ]; strncpy( cmdline, comstr.c_str(), comstr.length() + 1 ); LPSTR path = NULL; size_t pos = prog.find_last_of( "/\\" ); if ( pos != std::string::npos ) { path = new char[ pos + 1 ]; strncpy( path, prog.substr( 0, pos ).c_str(), pos + 1 ); } bool ret = CreateProcess( NULL, cmdline, NULL, NULL, ( NULL == callback ) ? FALSE : TRUE, 0, NULL, path, &si, &pi ); // Parent process - close the child write handle after child created if ( child_output_write ) CloseHandle( child_output_write ); // // Cleanup our allocated values now // if ( path ) delete [] path; delete [] cmdline; if ( ret == false ) { std::cerr << "Error executing command: '" << comstr << "'" << std::endl; return false; } if (( NULL != callback ) && ( block )) { char buffer[ 1024 ]; buffer[1023]=0; DWORD bytes_read; while ( ReadFile( child_output_read, buffer, 1023, &bytes_read, NULL ) != 0 ) { buffer[bytes_read]=0; if ( callback( buffer, opaque ) == false ) { TerminateProcess( pi.hProcess, 0 ); block=false; break; } } } if ( child_output_read ) CloseHandle( child_output_read ); DWORD timeout = exit_hotkey.empty() ? INFINITE : POLL_FOR_EXIT_MS; FeInputSource exit_is( exit_hotkey ); bool keep_wait=block; while (keep_wait) { switch (MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, timeout, QS_ALLINPUT)) { case WAIT_OBJECT_0: keep_wait=false; break; case WAIT_OBJECT_0 + 1: // we have a message - peek and dispatch it while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } break; case WAIT_TIMEOUT: // We should only ever get here if an exit_hotkey was provided // if ( exit_is.get_current_state( joy_thresh ) ) { TerminateProcess( pi.hProcess, 0 ); keep_wait=false; } break; default: std::cerr << "Unexpected failure waiting on process" << std::endl; keep_wait=false; break; } } CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); #else std::vector < std::string > string_list; size_t pos=0; while ( pos < args.size() ) { std::string val; token_helper( args, pos, val, FE_WHITESPACE ); string_list.push_back( val ); } char *arg_list[string_list.size() + 2]; arg_list[0] = (char *)prog.c_str(); for ( unsigned int i=0; i < string_list.size(); i++ ) arg_list[i+1] = (char *)string_list[i].c_str(); arg_list[string_list.size() + 1] = NULL; int mypipe[2] = { 0, 0 }; // mypipe[0] = read end, mypipe[1] = write end if (( NULL != callback ) && block && ( pipe( mypipe ) )) std::cerr << "Error, pipe() failed" << std::endl; pid_t pid = fork(); switch (pid) { case -1: std::cerr << "Error, fork() failed" << std::endl; if ( mypipe[0] ) { close( mypipe[0] ); close( mypipe[1] ); } return false; case 0: // child process if ( mypipe[0] ) { dup2( mypipe[1], fileno(stdout) ); close( mypipe[1] ); } { size_t pos = prog.find_last_of( "/" ); if ( pos != std::string::npos ) chdir( prog.substr( 0, pos ).c_str() ); } execvp( prog.c_str(), arg_list ); // execvp doesn't return unless there is an error. std::cerr << "Error executing: " << prog << " " << args << std::endl; _exit(127); default: // parent process if ( mypipe[0] ) { FILE *fp = fdopen( mypipe[0], "r" ); close( mypipe[1] ); char buffer[ 1024 ]; while( fgets( buffer, 1024, fp ) != NULL ) { if ( callback( buffer, opaque ) == false ) { // User cancelled kill( pid, SIGTERM ); block=false; break; } } fclose( fp ); close( mypipe[0] ); } if ( block ) { int status; int opt = exit_hotkey.empty() ? 0 : WNOHANG; // option for waitpid. 0= wait for process to complete, WNOHANG=return right away FeInputSource exit_is( exit_hotkey ); do { if ( waitpid( pid, &status, opt ) == 0 ) { // waitpid should only return 0 if WNOHANG is used and the child is still running, so we // should only ever get here if there is an exit_hotkey provided // if ( exit_is.get_current_state( joy_thresh ) ) { kill( pid, SIGTERM ); break; // leave do/while loop } sf::sleep( sf::milliseconds( POLL_FOR_EXIT_MS ) ); } } while ( !WIFEXITED( status ) && !WIFSIGNALED( status ) ); } } #endif // SFML_SYSTEM_WINDOWS return true; }
void FeSettings::apply_xml_import( FeImporterContext &c ) { std::string source = c.emulator.get_info( FeEmulatorInfo::Info_source ); if ( source.empty() ) return; std::string base_command = clean_path( c.emulator.get_info( FeEmulatorInfo::Executable ) ); if ( source.compare( "mame" ) == 0 ) { std::cout << " - Obtaining -listxml info..."; FeMameXMLParser mamep( c ); if ( !mamep.parse( base_command ) ) std::cout << "No XML output found, command: " << base_command << " -listxml" << std::endl; } else if ( source.compare( "mess" ) == 0 ) { const std::vector < std::string > &system_names = c.emulator.get_systems(); if ( system_names.empty() ) { std::cout << "Note: No system configured for emulator: " << c.emulator.get_info( FeEmulatorInfo::Name ) << ", unable to obtain -listsoftware info." << std::endl; return; } FeMessXMLParser messp( c ); messp.parse( base_command, system_names ); } else if ( source.compare( "steam" ) == 0 ) { const std::vector<std::string> &paths = c.emulator.get_paths(); const std::vector<std::string> &exts = c.emulator.get_extensions(); if ( paths.empty() || exts.empty() ) return; std::string path = clean_path( paths.front(), true ); const std::string &extension = exts.front(); // A bit mislabelled: the steam import isn't really xml. for ( FeRomInfoListType::iterator itr = c.romlist.begin(); itr != c.romlist.end(); ++itr ) { // We expect appmanifest_* entries in the romname field. Open each of these files and // extract the data we can use in our list const std::string &n = (*itr).get_info( FeRomInfo::Romname ); std::string fname = path + n + extension; // // First, Fix the Romname entry in case we don't find it in the manifest // size_t start_pos = n.find_last_of( "_" ); if ( start_pos != std::string::npos ) (*itr).set_info( FeRomInfo::Romname, n.substr( start_pos + 1 ) ); std::ifstream myfile( fname.c_str() ); int fields_left( 3 ); if ( myfile.is_open() ) { while ( myfile.good() && ( fields_left > 0 ) ) { std::string line, val; size_t pos( 0 ); getline( myfile, line ); token_helper( line, pos, val, FE_WHITESPACE ); if ( icompare( val, "appid" ) == 0 ) { std::string id; token_helper( line, pos, id, FE_WHITESPACE ); (*itr).set_info( FeRomInfo::Romname, id ); fields_left--; } else if ( icompare( val, "name" ) == 0 ) { std::string name; token_helper( line, pos, name, FE_WHITESPACE ); (*itr).set_info( FeRomInfo::Title, name ); fields_left--; } else if ( icompare( val, "installdir" ) == 0 ) { std::string altname; token_helper( line, pos, altname, FE_WHITESPACE ); (*itr).set_info( FeRomInfo::AltRomname, altname ); fields_left--; } } ASSERT( !fields_left ); } else std::cerr << "Error opening file: " << fname << std::endl; } } else if ( source.compare( "thegamesdb.net" ) == 0 ) thegamesdb_scraper( c ); else { std::cout << "Unrecognized import_source setting: " << source << std::endl; } }
void FeRomList::save_tags() { if (( !m_tags_changed ) || ( m_user_path.empty() ) || ( m_romlist_name.empty() )) return; // First construct a mapping of tags to rom entries // std::multimap< std::string, const char * > tag_to_rom_map; for ( std::multimap< std::string, const char * >::const_iterator ite = m_extra_tags.begin(); ite != m_extra_tags.end(); ++ite ) { tag_to_rom_map.insert( std::pair<std::string,const char *>( (*ite).second, (*ite).first.c_str() ) ); } for ( FeRomInfoListType::const_iterator itr = m_list.begin(); itr != m_list.end(); ++itr ) { const std::string &my_tags = (*itr).get_info( FeRomInfo::Tags ); size_t pos=0; do { std::string one_tag; const char sep[] = { FE_TAGS_SEP, 0 }; token_helper( my_tags, pos, one_tag, sep ); if ( !one_tag.empty() ) { tag_to_rom_map.insert( std::pair<std::string,const char *>( one_tag, ((*itr).get_info( FeRomInfo::Romname )).c_str() ) ); } } while ( pos < my_tags.size() ); } confirm_directory( m_user_path, m_romlist_name ); std::string my_path = m_user_path + m_romlist_name + "/"; // // Now save the tags // std::map<std::string,bool>::const_iterator itt; for ( itt = m_tags.begin(); itt != m_tags.end(); ++itt ) { if ( (*itt).second == false ) continue; std::string file_name = my_path + (*itt).first + FE_FAVOURITE_FILE_EXTENSION; std::pair<std::multimap<std::string,const char *>::const_iterator,std::multimap<std::string,const char *>::const_iterator> ret; ret = tag_to_rom_map.equal_range( (*itt).first ); if ( ret.first == ret.second ) { delete_file( file_name ); } else { std::ofstream outfile( file_name.c_str() ); if ( !outfile.is_open() ) continue; for ( std::multimap<std::string,const char *>::const_iterator ito = ret.first; ito != ret.second; ++ito ) outfile << (*ito).second << std::endl; outfile.close(); } } }
bool FeRomList::load_romlist( const std::string &path, const std::string &romlist_name, const std::string &user_path, const std::string &stat_path, FeDisplayInfo &display ) { m_user_path = user_path; m_romlist_name = romlist_name; m_list.clear(); m_filtered_list.clear(); m_availability_checked = false; m_global_filter_ptr = NULL; m_global_filtered_out_count = 0; FeFilter *first_filter = display.get_global_filter(); if ( first_filter ) { first_filter->init(); if ( !first_filter->test_for_target( FeRomInfo::FileIsAvailable ) && !first_filter->test_for_target( FeRomInfo::Favourite ) && !first_filter->test_for_target( FeRomInfo::Tags ) ) { // If the global filter doesn't care about file availability, // favourites or tags then we can apply it right up front when we // load the romlist. We signal this by setting m_global_filter_ptr m_global_filter_ptr = first_filter; first_filter = NULL; } } sf::Clock load_timer; bool retval = FeBaseConfigurable::load_from_file( path + m_romlist_name + FE_ROMLIST_FILE_EXTENSION, ";" ); // // Create rom name to romlist entry lookup map // std::map < std::string, FeRomInfo * > rom_map; std::map < std::string, FeRomInfo * >::iterator rm_itr; for ( FeRomInfoListType::iterator itr = m_list.begin(); itr != m_list.end(); ++itr ) rom_map[ (*itr).get_info( FeRomInfo::Romname ) ] = &(*itr); // // Load favourites // m_extra_favs.clear(); m_fav_changed=false; std::string load_name( m_user_path + m_romlist_name + FE_FAVOURITE_FILE_EXTENSION ); std::ifstream myfile( load_name.c_str() ); if ( myfile.is_open() ) { while ( myfile.good() ) { size_t pos=0; std::string line, name; getline( myfile, line ); token_helper( line, pos, name ); if ( !name.empty() ) { rm_itr = rom_map.find( name ); if ( rm_itr != rom_map.end() ) (*rm_itr).second->set_info( FeRomInfo::Favourite, "1" ); else m_extra_favs.insert( name ); } } myfile.close(); } // // Load tags // m_tags.clear(); m_extra_tags.clear(); m_tags_changed=false; load_name = m_user_path + m_romlist_name + "/"; if ( directory_exists( load_name ) ) { std::vector<std::string> temp_tags; get_basename_from_extension( temp_tags, load_name, FE_FAVOURITE_FILE_EXTENSION ); for ( std::vector<std::string>::iterator itr=temp_tags.begin(); itr!=temp_tags.end(); ++itr ) { if ( (*itr).empty() ) continue; std::ifstream myfile( std::string(load_name + (*itr) + FE_FAVOURITE_FILE_EXTENSION).c_str() ); if ( !myfile.is_open() ) continue; std::map<std::string, bool>::iterator itt = m_tags.begin(); itt = m_tags.insert( itt, std::pair<std::string,bool>( (*itr), false ) ); while ( myfile.good() ) { size_t pos=0; std::string line, rname; getline( myfile, line ); token_helper( line, pos, rname ); if ( !rname.empty() ) { rm_itr = rom_map.find( rname ); if ( rm_itr != rom_map.end() ) (*rm_itr).second->append_tag( (*itt).first.c_str() ); else m_extra_tags.insert( std::pair<std::string,const char*>(rname, (*itt).first.c_str() ) ); } } myfile.close(); } } // Apply global filter if it hasn't been applied already if ( first_filter ) { if ( first_filter->test_for_target( FeRomInfo::FileIsAvailable ) ) get_file_availability(); FeRomInfoListType::iterator last_it=m_list.begin(); for ( FeRomInfoListType::iterator it=m_list.begin(); it!=m_list.end(); ) { if ( first_filter->apply_filter( *it ) ) { if ( last_it != it ) it = m_list.erase( last_it, it ); else ++it; last_it = it; } else { // // This rom is being filtered out and we may need to keep track of a few things // // 1. Track if this is a favourite... // if ( !(*it).get_info( FeRomInfo::Favourite ).empty() ) m_extra_favs.insert( (*it).get_info( FeRomInfo::Romname ) ); // // 2. Track if this rom has tags we'll need to keep // if ( !(*it).get_info( FeRomInfo::Tags ).empty() ) { const std::string &name = (*it).get_info( FeRomInfo::Romname ); const std::string &tags = (*it).get_info( FeRomInfo::Tags ); const char sep[] = { FE_TAGS_SEP, 0 }; size_t pos=0; while ( pos < tags.size() ) { std::string one_tag; token_helper( tags, pos, one_tag, sep ); std::map<std::string, bool>::iterator itt = m_tags.find( one_tag ); if ( itt != m_tags.end() ) m_extra_tags.insert( std::pair<std::string,const char *>( name, (*itt).first.c_str() ) ); } } m_global_filtered_out_count++; ++it; } } if ( last_it != m_list.end() ) m_list.erase( last_it, m_list.end() ); } std::cout << " - Loaded master romlist '" << m_romlist_name << "' in " << load_timer.getElapsedTime().asMilliseconds() << " ms (" << m_list.size() << " entries kept, " << m_global_filtered_out_count << " discarded)" << std::endl; load_timer.restart(); // // Apply filters // int filters_count = display.get_filter_count(); // // If the display doesn't have any filters configured, we create a single "filter" in the romlist object // with every romlist entry in it // if ( filters_count == 0 ) filters_count = 1; m_filtered_list.reserve( filters_count ); for ( int i=0; i<filters_count; i++ ) { m_filtered_list.push_back( std::vector< FeRomInfo *>() ); FeFilter *f = display.get_filter( i ); if ( f ) { if ( f->get_size() > 0 ) // if this is non zero then we've loaded before and know how many to expect m_filtered_list[i].reserve( f->get_size() ); if ( f->test_for_target( FeRomInfo::FileIsAvailable ) ) get_file_availability(); f->init(); for ( FeRomInfoListType::iterator itr=m_list.begin(); itr!=m_list.end(); ++itr ) if ( f->apply_filter( *itr ) ) m_filtered_list[i].push_back( &( *itr ) ); } else // no filter situation, so we just add the entire list... { m_filtered_list[i].reserve( m_list.size() ); for ( FeRomInfoListType::iterator itr=m_list.begin(); itr!=m_list.end(); ++itr ) m_filtered_list[i].push_back( &( *itr ) ); } // // make sure stats are loaded for the roms we will be showing // if ( !stat_path.empty() ) { for ( std::vector< FeRomInfo * >::iterator itf=m_filtered_list[i].begin(); itf!=m_filtered_list[i].end(); ++itf ) (*itf)->load_stats( stat_path ); // this will just return if stats are already loaded for the rom } if ( f ) { // track the size of the filtered list in our filter info object f->set_size( m_filtered_list[i].size() ); // // Sort and/or prune now if configured for this filter // FeRomInfo::Index sort_by=f->get_sort_by(); bool rev = f->get_reverse_order(); int list_limit = f->get_list_limit(); if ( sort_by != FeRomInfo::LAST_INDEX ) { std::stable_sort( m_filtered_list[i].begin(), m_filtered_list[i].end(), FeRomListSorter2( sort_by, rev ) ); } else if ( rev != false ) std::reverse( m_filtered_list[i].begin(), m_filtered_list[i].end() ); if (( list_limit != 0 ) && ( (int)m_filtered_list[i].size() > abs( list_limit ) )) { if ( list_limit > 0 ) m_filtered_list[i].erase( m_filtered_list[i].begin() + list_limit, m_filtered_list[i].end() ); else m_filtered_list[i].erase( m_filtered_list[i].begin(), m_filtered_list[i].end() + list_limit ); } } } std::cout << " - Constructed " << filters_count << " filters in " << load_timer.getElapsedTime().asMilliseconds() << " ms (" << filters_count * m_list.size() << " comparisons)" << std::endl; return retval; }