diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 60e5975b1..29157cae5 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -18,7 +18,7 @@ std::vector SystemData::sSystemVector; namespace fs = boost::filesystem; SystemData::SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector& extensions, - const std::string& command, const std::vector& platformIds, const std::string& themeFolder) + const std::string& command, const std::vector& platformIds, const std::string& themeFolder, bool directLaunch) { mName = name; mFullName = fullName; @@ -36,6 +36,8 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con mPlatformIds = platformIds; mThemeFolder = themeFolder; + mDirectLaunch = directLaunch; + mRootFolder = new FileData(FOLDER, mStartPath, this); mRootFolder->metadata.set("name", mFullName); @@ -106,7 +108,12 @@ std::string escapePath(const boost::filesystem::path& path) void SystemData::launchGame(Window* window, FileData* game) { - LOG(LogInfo) << "Attempting to launch game..."; + if ( game ) + { + LOG(LogInfo) << "Attempting to launch game..."; + }else{ + LOG(LogInfo) << "Attempting to launch command..."; + } AudioManager::getInstance()->deinit(); VolumeControl::getInstance()->deinit(); @@ -114,13 +121,16 @@ void SystemData::launchGame(Window* window, FileData* game) std::string command = mLaunchCommand; - const std::string rom = escapePath(game->getPath()); - const std::string basename = game->getPath().stem().string(); - const std::string rom_raw = fs::path(game->getPath()).make_preferred().string(); + if ( game ) + { + const std::string rom = escapePath(game->getPath()); + const std::string basename = game->getPath().stem().string(); + const std::string rom_raw = fs::path(game->getPath()).make_preferred().string(); - command = strreplace(command, "%ROM%", rom); - command = strreplace(command, "%BASENAME%", basename); - command = strreplace(command, "%ROM_RAW%", rom_raw); + command = strreplace(command, "%ROM%", rom); + command = strreplace(command, "%BASENAME%", basename); + command = strreplace(command, "%ROM_RAW%", rom_raw); + } LOG(LogInfo) << " " << command; int exitCode = runSystemCommand(command); @@ -135,17 +145,26 @@ void SystemData::launchGame(Window* window, FileData* game) AudioManager::getInstance()->init(); window->normalizeNextUpdate(); - //update number of times the game has been launched - int timesPlayed = game->metadata.getInt("playcount") + 1; - game->metadata.set("playcount", std::to_string(static_cast(timesPlayed))); + if ( game ) + { + //update number of times the game has been launched + int timesPlayed = game->metadata.getInt("playcount") + 1; + game->metadata.set("playcount", std::to_string(static_cast(timesPlayed))); - //update last played time - boost::posix_time::ptime time = boost::posix_time::second_clock::universal_time(); - game->metadata.setTime("lastplayed", time); + //update last played time + boost::posix_time::ptime time = boost::posix_time::second_clock::universal_time(); + game->metadata.setTime("lastplayed", time); + } } void SystemData::populateFolder(FileData* folder) { + if (mDirectLaunch) + { + LOG(LogInfo) << "System " << mName << " is a direct launch item, not building game lists."; + return; + } + const fs::path& folderPath = folder->getPath(); if(!fs::is_directory(folderPath)) { @@ -261,11 +280,13 @@ bool SystemData::loadConfig() for(pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system")) { std::string name, fullname, path, cmd, themeFolder; + bool directLaunch; PlatformIds::PlatformId platformId = PlatformIds::PLATFORM_UNKNOWN; name = system.child("name").text().get(); fullname = system.child("fullname").text().get(); path = system.child("path").text().get(); + directLaunch = ( strcmp( system.child("directlaunch").text().get(), "true" ) == 0); // convert extensions list from a string into a vector of strings std::vector extensions = readList(system.child("extension").text().get()); @@ -299,19 +320,29 @@ bool SystemData::loadConfig() // theme folder themeFolder = system.child("theme").text().as_string(name.c_str()); - //validate - if(name.empty() || path.empty() || extensions.empty() || cmd.empty()) + //validate game system + if( (name.empty() || path.empty() || extensions.empty() || cmd.empty() ) && directLaunch == false ) { LOG(LogError) << "System \"" << name << "\" is missing name, path, extension, or command!"; continue; } + + //validate direct launch item + if( (name.empty() || cmd.empty() ) && directLaunch == true ) + { + LOG(LogError) << "Direct Launch item \"" << name << "\" is missing name or command!"; + continue; + } - //convert path to generic directory seperators - boost::filesystem::path genericPath(path); - path = genericPath.generic_string(); + if (!directLaunch) + { + //convert path to generic directory seperators + boost::filesystem::path genericPath(path); + path = genericPath.generic_string(); + } - SystemData* newSys = new SystemData(name, fullname, path, extensions, cmd, platformIds, themeFolder); - if(newSys->getRootFolder()->getChildren().size() == 0) + SystemData* newSys = new SystemData(name, fullname, path, extensions, cmd, platformIds, themeFolder, directLaunch); + if(newSys->getRootFolder()->getChildren().size() == 0 && !directLaunch) { LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it."; delete newSys; @@ -361,6 +392,10 @@ void SystemData::writeExampleConfig(const std::string& path) " \n" " nes\n" + "\n" + " \n" + " false\n" " \n" "\n"; diff --git a/es-app/src/SystemData.h b/es-app/src/SystemData.h index 2a0ac8188..6629d9f7e 100644 --- a/es-app/src/SystemData.h +++ b/es-app/src/SystemData.h @@ -12,7 +12,7 @@ class SystemData { public: SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector& extensions, - const std::string& command, const std::vector& platformIds, const std::string& themeFolder); + const std::string& command, const std::vector& platformIds, const std::string& themeFolder, bool directLaunch=false); ~SystemData(); inline FileData* getRootFolder() const { return mRootFolder; }; @@ -21,6 +21,7 @@ public: inline const std::string& getStartPath() const { return mStartPath; } inline const std::vector& getExtensions() const { return mSearchExtensions; } inline const std::string& getThemeFolder() const { return mThemeFolder; } + inline const bool getDirectLaunch() const { return mDirectLaunch; } inline const std::vector& getPlatformIds() const { return mPlatformIds; } inline bool hasPlatformId(PlatformIds::PlatformId id) { return std::find(mPlatformIds.begin(), mPlatformIds.end(), id) != mPlatformIds.end(); } @@ -73,6 +74,7 @@ private: std::vector mPlatformIds; std::string mThemeFolder; std::shared_ptr mTheme; + bool mDirectLaunch; void populateFolder(FileData* folder); diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 54987fdf8..9810cd316 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -119,7 +119,16 @@ bool SystemView::input(InputConfig* config, Input input) if(config->isMappedTo("a", input)) { stopScrolling(); - ViewController::get()->goToGameList(getSelected()); + + SystemData *systemData = getSelected(); + + // decide whether to show game list or launch the command directly + if ( !systemData->getDirectLaunch() ) + { + ViewController::get()->goToGameList(getSelected()); + }else{ + systemData->launchGame( mWindow, nullptr ); + } return true; } }else{ diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 5a94d97c2..9716cfd8b 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -72,7 +72,15 @@ void ViewController::goToNextGameList() assert(mState.viewing == GAME_LIST); SystemData* system = getState().getSystem(); assert(system); - goToGameList(system->getNext()); + + // skip systems that don't have a game list, this will always end since it is called + // from a system with a game list and the iterator is cyclic + do + { + system = system->getNext(); + } while ( system->getDirectLaunch() ); + + goToGameList(system); } void ViewController::goToPrevGameList() @@ -80,7 +88,15 @@ void ViewController::goToPrevGameList() assert(mState.viewing == GAME_LIST); SystemData* system = getState().getSystem(); assert(system); - goToGameList(system->getPrev()); + + // skip systems that don't have a game list, this will always end since it is called + // from a system with a game list and the iterator is cyclic + do + { + system = system->getPrev(); + } while ( system->getDirectLaunch() ); + + goToGameList(system); } void ViewController::goToGameList(SystemData* system) diff --git a/es-app/src/views/gamelist/BasicGameListView.cpp b/es-app/src/views/gamelist/BasicGameListView.cpp index d3cbc017a..5438c889f 100644 --- a/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/es-app/src/views/gamelist/BasicGameListView.cpp @@ -38,6 +38,12 @@ void BasicGameListView::onFileChanged(FileData* file, FileChangeType change) void BasicGameListView::populateList(const std::vector& files) { mList.clear(); + + // file list can be empty if direct launch item + if (files.size()==0) + { + return; + } mHeaderText.setText(files.at(0)->getSystem()->getFullName());