diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index 0f0add7a4..ca0f5dfb4 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -23,6 +23,7 @@ #include "utils/PlatformUtil.h" #include "utils/StringUtil.h" #include "utils/TimeUtil.h" +#include "views/GamelistView.h" #include "views/ViewController.h" #include @@ -1233,12 +1234,19 @@ void FileData::launchGame() // Update number of times the game has been launched. FileData* gameToUpdate = getSourceFileData(); - int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1; + int timesPlayed {gameToUpdate->metadata.getInt("playcount") + 1}; gameToUpdate->metadata.set("playcount", std::to_string(static_cast(timesPlayed))); // Update last played time. gameToUpdate->metadata.set("lastplayed", Utils::Time::DateTime(Utils::Time::now())); + // If the cursor is a folder, the "launch file" functionality must have been used, so set + // the lastplayed timestamp for this folder to the same as the launched game. + FileData* cursor { + ViewController::getInstance()->getGamelistView(gameToUpdate->getSystem())->getCursor()}; + if (cursor->getType() == FOLDER) + cursor->metadata.set("lastplayed", gameToUpdate->metadata.get("lastplayed")); + // If the parent is a folder and it's not the root of the system, then update its lastplayed // timestamp to the same time as the game that was just launched. if (gameToUpdate->getParent()->getType() == FOLDER && diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index c96d75f76..a6b693609 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -20,7 +20,7 @@ namespace // The statistic entries must be placed at the bottom or otherwise there will be problems with // saving the values in GuiMetaDataEd. MetaDataDecl gameDecls[] { - // key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd, shouldScrape + // Key Type Default value Statistic Name in GuiMetaDataEd Prompt in GuiMetaDataEd Scrape {"name", MD_STRING, "", false, "name", "enter name", true}, {"sortname", MD_STRING, "", false, "sortname", "enter sortname", false}, {"collectionsortname", MD_STRING, "", false, "custom collections sortname", "enter collections sortname", false}, @@ -46,6 +46,7 @@ namespace }; MetaDataDecl folderDecls[] { + // Key Type Default value Statistic Name in GuiMetaDataEd Prompt in GuiMetaDataEd Scrape {"name", MD_STRING, "", false, "name", "enter name", true}, {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true}, {"rating", MD_RATING, "0", false, "rating", "enter rating", true}, @@ -62,6 +63,7 @@ namespace {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"controller", MD_CONTROLLER, "", false, "controller", "select controller", true}, + {"launchfile", MD_LAUNCH_FILE, "", false, "launch file", "select launch file", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; // clang-format on diff --git a/es-app/src/MetaData.h b/es-app/src/MetaData.h index e7867e9e4..67c4ff821 100644 --- a/es-app/src/MetaData.h +++ b/es-app/src/MetaData.h @@ -34,6 +34,7 @@ enum MetaDataType { MD_MULTILINE_STRING, MD_CONTROLLER, MD_ALT_EMULATOR, + MD_LAUNCH_FILE, MD_PATH, MD_RATING, MD_DATE, diff --git a/es-app/src/Screensaver.cpp b/es-app/src/Screensaver.cpp index e5270df50..d9c521e76 100644 --- a/es-app/src/Screensaver.cpp +++ b/es-app/src/Screensaver.cpp @@ -212,12 +212,28 @@ void Screensaver::nextGame() void Screensaver::launchGame() { if (mCurrentGame != nullptr) { + // If the game is inside a folder where a "launch file" entry is present, then jump to + // that folder instead of to the actual game file. Also check the complete hierarchy in + // case launch file entries are set on multiple levels of the folder structure. + FileData* entry {mCurrentGame}; + FileData* selectGame {mCurrentGame}; + FileData* launchFolder {nullptr}; + + while (entry != nullptr) { + entry = entry->getParent(); + if (entry != nullptr && entry->metadata.get("launchfile") != "") + launchFolder = entry; + } + + if (launchFolder != nullptr) + selectGame = launchFolder; + // Launching game ViewController::getInstance()->triggerGameLaunch(mCurrentGame); ViewController::getInstance()->goToGamelist(mCurrentGame->getSystem()); GamelistView* view { ViewController::getInstance()->getGamelistView(mCurrentGame->getSystem()).get()}; - view->setCursor(mCurrentGame); + view->setCursor(selectGame); view->stopListScrolling(); ViewController::getInstance()->cancelViewTransitions(); ViewController::getInstance()->pauseViewVideos(); @@ -227,6 +243,18 @@ void Screensaver::launchGame() void Screensaver::goToGame() { if (mCurrentGame != nullptr) { + FileData* entry {mCurrentGame}; + FileData* launchFolder {nullptr}; + + while (entry != nullptr) { + entry = entry->getParent(); + if (entry != nullptr && entry->metadata.get("launchfile") != "") + launchFolder = entry; + } + + if (launchFolder != nullptr) + mCurrentGame = launchFolder; + // Go to the game in the gamelist view, but don't launch it. ViewController::getInstance()->goToGamelist(mCurrentGame->getSystem()); GamelistView* view { diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 37a6692d7..282194ee5 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -10,6 +10,11 @@ // metadata edit interface is covered by GuiMetaDataEd. // +#if defined(_WIN64) +// Why this is needed here is anyone's guess but without it the compilation fails. +#include +#endif + #include "GuiGamelistOptions.h" #include "CollectionSystemsManager.h" @@ -33,6 +38,7 @@ GuiGamelistOptions::GuiGamelistOptions(SystemData* system) , mCancelled {false} , mIsCustomCollection {false} , mIsCustomCollectionGroup {false} + , mLaunchFileOverride {false} , mCustomCollectionSystem {nullptr} { addChild(&mMenu); @@ -228,6 +234,19 @@ GuiGamelistOptions::GuiGamelistOptions(SystemData* system) } } + if (file->getType() == FOLDER && file->metadata.get("launchfile") != "") { + row.elements.clear(); + row.addElement(std::make_shared("ENTER FOLDER (OVERRIDE LAUNCH FILE)", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); + row.makeAcceptInputHandler([this, file] { + mLaunchFileOverride = true; + getGamelist()->enterDirectory(file); + delete this; + }); + mMenu.addRow(row); + } + // Buttons. The logic to apply or cancel settings are handled by the destructor. if ((!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() == 0) || system->getName() == "recent") { @@ -308,7 +327,8 @@ GuiGamelistOptions::~GuiGamelistOptions() } } - if (mSystem->getRootFolder()->getChildren().size() != 0 && mSystem->getName() != "recent") + if (mSystem->getRootFolder()->getChildren().size() != 0 && mSystem->getName() != "recent" && + !mLaunchFileOverride) NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); } diff --git a/es-app/src/guis/GuiGamelistOptions.h b/es-app/src/guis/GuiGamelistOptions.h index cf17493d3..e51a1e50e 100644 --- a/es-app/src/guis/GuiGamelistOptions.h +++ b/es-app/src/guis/GuiGamelistOptions.h @@ -59,6 +59,7 @@ private: bool mCancelled; bool mIsCustomCollection; bool mIsCustomCollectionGroup; + bool mLaunchFileOverride; SystemData* mCustomCollectionSystem; std::vector mFirstLetterIndex; std::string mCurrentFirstCharacter; diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 529afd396..c166f1b45 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -52,6 +52,7 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, , mMediaFilesUpdated {false} , mSavedMediaAndAborted {false} , mInvalidEmulatorEntry {false} + , mInvalidLaunchFileEntry {false} { if (ViewController::getInstance()->getState().getSystem()->isCustomCollection() || ViewController::getInstance()->getState().getSystem()->getThemeFolder() == @@ -70,9 +71,9 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, mGrid.setEntry(mTitle, glm::ivec2 {0, 0}, false, true, glm::ivec2 {2, 2}); // Extract possible subfolders from the path. - std::string folderPath = + std::string folderPath { Utils::String::replace(Utils::FileSystem::getParent(scraperParams.game->getPath()), - scraperParams.system->getSystemEnvData()->mStartPath, ""); + scraperParams.system->getSystemEnvData()->mStartPath, "")}; if (folderPath.size() >= 2) { folderPath.erase(0, 1); @@ -112,8 +113,8 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, // Populate list. for (auto it = mdd.cbegin(); it != mdd.cend(); ++it) { std::shared_ptr ed; - std::string currentKey = it->key; - std::string originalValue = mMetaData->get(it->key); + std::string currentKey {it->key}; + std::string originalValue {mMetaData->get(it->key)}; std::string gamePath; // Only display the custom collections sortname entry if we're editing the game @@ -162,7 +163,7 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, ed = std::make_shared(true); ed->setChangedColor(ICONCOLOR_USERMARKED); - const float height = lbl->getSize().y * 0.71f; + const float height {lbl->getSize().y * 0.71f}; ed->setSize(0.0f, height); row.addElement(ed, false, true); @@ -200,7 +201,7 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, bracket->setResize(glm::vec2 {0.0f, lbl->getFont()->getLetterHeight()}); row.addElement(bracket, false); - const std::string title = it->displayPrompt; + const std::string title {it->displayPrompt}; // OK callback (apply new value to ed). auto updateVal = [ed, originalValue](const std::string& newVal) { @@ -212,15 +213,15 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, }; row.makeAcceptInputHandler([this, title, ed, updateVal] { - GuiSettings* s = new GuiSettings(title); + GuiSettings* s {new GuiSettings(title)}; for (auto controller : mControllerBadges) { - std::string selectedLabel = ed->getValue(); + std::string selectedLabel {ed->getValue()}; std::string label; ComponentListRow row; - std::shared_ptr labelText = std::make_shared( - label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + std::shared_ptr labelText {std::make_shared( + label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF)}; labelText->setSelectable(true); labelText->setValue(controller.displayName); @@ -243,9 +244,9 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, // If a value is set, then display "Clear entry" as the last entry. if (ed->getValue() != "") { ComponentListRow row; - std::shared_ptr clearText = std::make_shared( + std::shared_ptr clearText {std::make_shared( ViewController::CROSSEDCIRCLE_CHAR + " CLEAR ENTRY", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF)}; clearText->setSelectable(true); row.addElement(clearText, true); row.makeAcceptInputHandler([s, ed] { @@ -282,7 +283,7 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, bracket->setResize(glm::vec2 {0.0f, lbl->getFont()->getLetterHeight()}); row.addElement(bracket, false); - const std::string title = it->displayPrompt; + const std::string title {it->displayPrompt}; // OK callback (apply new value to ed). auto updateVal = [this, ed, originalValue](const std::string& newVal) { @@ -313,10 +314,10 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, scraperParams.system->getSystemEnvData()->mLaunchCommands.size() > 1) { row.makeAcceptInputHandler([this, title, scraperParams, ed, updateVal, originalValue] { - GuiSettings* s = nullptr; + GuiSettings* s {nullptr}; - bool singleEntry = - scraperParams.system->getSystemEnvData()->mLaunchCommands.size() == 1; + bool singleEntry { + scraperParams.system->getSystemEnvData()->mLaunchCommands.size() == 1}; if (mInvalidEmulatorEntry && singleEntry) s = new GuiSettings("CLEAR INVALID ENTRY"); @@ -326,8 +327,8 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, if (!mInvalidEmulatorEntry && ed->getValue() == "" && singleEntry) return; - std::vector> launchCommands = - scraperParams.system->getSystemEnvData()->mLaunchCommands; + std::vector> launchCommands { + scraperParams.system->getSystemEnvData()->mLaunchCommands}; if (ed->getValue() != "" && mInvalidEmulatorEntry && singleEntry) launchCommands.push_back(std::make_pair( @@ -342,7 +343,7 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, ViewController::EXCLAMATION_CHAR + " " + originalValue) continue; - std::string selectedLabel = ed->getValue(); + std::string selectedLabel {ed->getValue()}; std::string label; ComponentListRow row; @@ -351,9 +352,9 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, else label = entry.second; - std::shared_ptr labelText = + std::shared_ptr labelText { std::make_shared(label, Font::get(FONT_SIZE_MEDIUM), - 0x777777FF); + 0x777777FF)}; labelText->setSelectable(true); if (scraperParams.system->getAlternativeEmulator() == "" && @@ -404,6 +405,110 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, } break; } + case MD_LAUNCH_FILE: { + ed = std::make_shared( + "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + row.addElement(ed, true); + + auto spacer = std::make_shared(); + spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f); + row.addElement(spacer, false); + + auto bracket = std::make_shared(); + bracket->setImage(":/graphics/arrow.svg"); + bracket->setResize(glm::vec2 {0.0f, lbl->getFont()->getLetterHeight()}); + row.addElement(bracket, false); + + const std::string title {it->displayPrompt}; + + std::vector children; + if (originalValue != "") + mInvalidLaunchFileEntry = true; + + for (auto child : mScraperParams.game->getChildrenRecursive()) { + if (child->getType() == GAME && child->getCountAsGame() && + !child->getHidden()) { + children.emplace_back(child); + std::string filePath {child->getPath()}; + std::string systemPath {child->getSystem()->getRootFolder()->getPath() + + "/" + mScraperParams.game->getFileName() + "/"}; + if (Utils::String::replace(filePath, systemPath, "") == originalValue) + mInvalidLaunchFileEntry = false; + } + } + + // OK callback (apply new value to ed). + auto updateVal = [this, ed, originalValue](const std::string& newVal) { + mInvalidLaunchFileEntry = false; + ed->setValue(newVal); + if (newVal == originalValue) + ed->setColor(DEFAULT_TEXTCOLOR); + else + ed->setColor(TEXTCOLOR_USERMARKED); + }; + + row.makeAcceptInputHandler([this, children, title, ed, updateVal] { + GuiSettings* s {new GuiSettings(title)}; + + for (auto child : children) { + std::string selectedLabel {ed->getValue()}; + std::string label; + ComponentListRow row; + + std::string filePath {child->getPath()}; + std::string systemPath {child->getSystem()->getRootFolder()->getPath() + + "/" + mScraperParams.game->getFileName() + "/"}; + + filePath = Utils::String::replace(filePath, systemPath, ""); + + std::shared_ptr labelText {std::make_shared( + label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF)}; + labelText->setSelectable(true); + labelText->setValue(filePath); + + label = filePath; + + row.addElement(labelText, true); + + row.makeAcceptInputHandler([s, updateVal, filePath] { + updateVal(filePath); + delete s; + }); + + // Select the row that corresponds to the selected label. + if (selectedLabel == label) + s->addRow(row, true); + else + s->addRow(row, false); + } + + // If a value is set, then display "Clear entry" as the last entry. + if (ed->getValue() != "") { + ComponentListRow row; + std::shared_ptr clearText {std::make_shared( + ViewController::CROSSEDCIRCLE_CHAR + " CLEAR ENTRY", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF)}; + clearText->setSelectable(true); + row.addElement(clearText, true); + row.makeAcceptInputHandler([this, s, ed] { + mInvalidLaunchFileEntry = false; + ed->setValue(""); + delete s; + }); + s->addRow(row, false); + } + + float aspectValue {1.778f / Renderer::getScreenAspectRatio()}; + float maxWidthModifier {glm::clamp(0.64f * aspectValue, 0.42f, 0.92f)}; + float maxWidth {Renderer::getScreenWidth() * maxWidthModifier}; + + s->setMenuSize(glm::vec2 {maxWidth, s->getMenuSize().y}); + s->setMenuPosition( + glm::vec3 {(s->getSize().x - maxWidth) / 2.0f, mPosition.y, mPosition.z}); + mWindow->pushGui(s); + }); + break; + } case MD_MULTILINE_STRING: default: { // MD_STRING. @@ -420,8 +525,8 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, bracket->setResize(glm::vec2 {0.0f, lbl->getFont()->getLetterHeight()}); row.addElement(bracket, false); - bool multiLine = it->type == MD_MULTILINE_STRING; - const std::string title = it->displayPrompt; + bool multiLine {it->type == MD_MULTILINE_STRING}; + const std::string title {it->displayPrompt}; gamePath = Utils::FileSystem::getStem(mScraperParams.game->getPath()); @@ -488,11 +593,14 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, assert(ed); mList->addRow(row); - if (it->type == MD_ALT_EMULATOR && mInvalidEmulatorEntry == true) { + if (it->type == MD_ALT_EMULATOR && mInvalidEmulatorEntry) { + ed->setValue(ViewController::EXCLAMATION_CHAR + " " + originalValue); + } + else if (it->type == MD_LAUNCH_FILE && mInvalidLaunchFileEntry) { ed->setValue(ViewController::EXCLAMATION_CHAR + " " + originalValue); } else if (it->type == MD_CONTROLLER && mMetaData->get(it->key) != "") { - std::string displayName = BadgeComponent::getDisplayName(mMetaData->get(it->key)); + std::string displayName {BadgeComponent::getDisplayName(mMetaData->get(it->key))}; if (displayName != "unknown") ed->setValue(displayName); else @@ -639,6 +747,9 @@ void GuiMetaDataEd::save() if (key == "altemulator" && mInvalidEmulatorEntry == true) continue; + if (key == "launchfile" && mInvalidLaunchFileEntry) + continue; + if (key == "controller" && mEditors.at(i)->getValue() != "") { std::string shortName = BadgeComponent::getShortName(mEditors.at(i)->getValue()); if (shortName != "unknown") @@ -807,7 +918,10 @@ void GuiMetaDataEd::close() const std::string& key {mMetaDataDecl.at(i + offset).key}; - if (key == "altemulator" && mInvalidEmulatorEntry == true) + if (key == "altemulator" && mInvalidEmulatorEntry) + continue; + + if (key == "launchfile" && mInvalidLaunchFileEntry) continue; std::string mMetaDataValue {mMetaData->get(key)}; diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index c3c25307a..d2cd26dac 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -73,6 +73,7 @@ private: bool mMediaFilesUpdated; bool mSavedMediaAndAborted; bool mInvalidEmulatorEntry; + bool mInvalidLaunchFileEntry; }; #endif // ES_APP_GUIS_GUI_META_DATA_ED_H diff --git a/es-app/src/views/GamelistBase.cpp b/es-app/src/views/GamelistBase.cpp index 47a4b73ce..06a5ccfd9 100644 --- a/es-app/src/views/GamelistBase.cpp +++ b/es-app/src/views/GamelistBase.cpp @@ -6,6 +6,11 @@ // Gamelist base class with utility functions and other low-level logic. // +#if defined(_WIN64) +// Why this is needed here is anyone's guess but without it the compilation fails. +#include +#endif + #include "views/GamelistBase.h" #include "CollectionSystemsManager.h" @@ -73,12 +78,33 @@ bool GamelistBase::input(InputConfig* config, Input input) // It's a folder. if (cursor->getChildren().size() > 0) { ViewController::getInstance()->cancelViewTransitions(); + // If a "launch file" entry has been set on the folder, then check if it + // corresponds to an actual child entry, and if so then launch this child + // instead of entering the folder. + if (!CollectionSystemsManager::getInstance()->isEditing() && + cursor->metadata.get("launchfile") != "") { + std::string launchFile; + launchFile.append(cursor->getPath()) + .append("/") + .append(Utils::String::replace(cursor->metadata.get("launchfile"), "\\", + "/")); + for (auto child : cursor->getChildrenRecursive()) { + if (child->getPath() == launchFile) { + pauseViewVideos(); + ViewController::getInstance()->cancelViewTransitions(); + stopListScrolling(); + launch(child); + return true; + } + } + } + NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND); mCursorStack.push(cursor); populateList(cursor->getChildrenListToDisplay(), cursor); FileData* newCursor {nullptr}; - std::vector listEntries = cursor->getChildrenListToDisplay(); + std::vector listEntries {cursor->getChildrenListToDisplay()}; // Check if there is an entry in the cursor stack history matching any entry // in the currect folder. If so, select that entry. for (auto it = mCursorStackHistory.begin(); // Line break. @@ -469,6 +495,41 @@ bool GamelistBase::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } +void GamelistBase::enterDirectory(FileData* cursor) +{ + assert(cursor->getType() == FOLDER); + + if (cursor->getChildren().size() > 0) { + ViewController::getInstance()->cancelViewTransitions(); + NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND); + mCursorStack.push(cursor); + populateList(cursor->getChildrenListToDisplay(), cursor); + + FileData* newCursor {nullptr}; + std::vector listEntries = cursor->getChildrenListToDisplay(); + // Check if there is an entry in the cursor stack history matching any entry + // in the currect folder. If so, select that entry. + for (auto it = mCursorStackHistory.begin(); it != mCursorStackHistory.end(); ++it) { + if (std::find(listEntries.begin(), listEntries.end(), *it) != listEntries.end()) { + newCursor = *it; + mCursorStackHistory.erase(it); + break; + } + } + + // If there was no match in the cursor history, simply select the first entry. + if (!newCursor) + newCursor = getCursor(); + setCursor(newCursor); + stopListScrolling(); + if (mRoot->getSystem()->getThemeFolder() == "custom-collections") + updateHelpPrompts(); + } + else { + NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); + } +} + void GamelistBase::populateList(const std::vector& files, FileData* firstEntry) { mFirstGameEntry = nullptr; @@ -537,10 +598,18 @@ void GamelistBase::populateList(const std::vector& files, FileData* f } else if ((*it)->getType() == FOLDER && mRoot->getSystem()->getName() != "collections") { - if (Settings::getInstance()->getBool("SpecialCharsASCII")) - name = "# " + (*it)->getName(); - else - name = ViewController::FOLDER_CHAR + " " + (*it)->getName(); + if (Settings::getInstance()->getBool("SpecialCharsASCII")) { + if ((*it)->metadata.get("launchfile") != "") + name = "> " + (*it)->getName(); + else + name = "# " + (*it)->getName(); + } + else { + if ((*it)->metadata.get("launchfile") != "") + name = ViewController::FOLDERLINK_CHAR + " " + (*it)->getName(); + else + name = ViewController::FOLDER_CHAR + " " + (*it)->getName(); + } } else { name = inCollectionPrefix + (*it)->getName(); diff --git a/es-app/src/views/GamelistBase.h b/es-app/src/views/GamelistBase.h index c9341c3f5..939be1861 100644 --- a/es-app/src/views/GamelistBase.h +++ b/es-app/src/views/GamelistBase.h @@ -34,6 +34,7 @@ public: void setCursor(FileData*); bool input(InputConfig* config, Input input) override; + void enterDirectory(FileData* cursor); FileData* getNextEntry() { return mPrimary->getNext(); } FileData* getPreviousEntry() { return mPrimary->getPrevious(); } diff --git a/es-app/src/views/GamelistView.h b/es-app/src/views/GamelistView.h index dd81fd584..99248f730 100644 --- a/es-app/src/views/GamelistView.h +++ b/es-app/src/views/GamelistView.h @@ -10,8 +10,6 @@ #define ES_APP_VIEWS_GAMELIST_VIEW_H #include "views/GamelistBase.h" - -#include "renderers/Renderer.h" #include "views/ViewController.h" class GamelistView : public GamelistBase diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index 9c9ad067b..3071805d3 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -132,6 +132,7 @@ public: static inline const std::string FAVORITE_CHAR {Utils::String::wideStringToString(L"\uf005")}; static inline const std::string FILTER_CHAR {Utils::String::wideStringToString(L"\uf0b0")}; static inline const std::string FOLDER_CHAR {Utils::String::wideStringToString(L"\uf07C")}; + static inline const std::string FOLDERLINK_CHAR {Utils::String::wideStringToString(L"\uf090")}; static inline const std::string GEAR_CHAR {Utils::String::wideStringToString(L"\uf013")}; static inline const std::string KEYBOARD_CHAR {Utils::String::wideStringToString(L"\uf11c")}; static inline const std::string TICKMARK_CHAR {Utils::String::wideStringToString(L"\uf14A")}; @@ -142,6 +143,7 @@ public: static inline const std::string FAVORITE_CHAR {"\uf005"}; static inline const std::string FILTER_CHAR {"\uf0b0"}; static inline const std::string FOLDER_CHAR {"\uf07C"}; + static inline const std::string FOLDERLINK_CHAR {"\uf090"}; static inline const std::string GEAR_CHAR {"\uf013"}; static inline const std::string KEYBOARD_CHAR {"\uf11c"}; static inline const std::string TICKMARK_CHAR {"\uf14a"}; diff --git a/es-core/src/ThemeData.h b/es-core/src/ThemeData.h index 5193f2026..af815c8a9 100644 --- a/es-core/src/ThemeData.h +++ b/es-core/src/ThemeData.h @@ -23,11 +23,6 @@ #include #include -#if defined(_WIN64) -// Why this is needed here is anyone's guess but without it the compilation fails. -#include -#endif - namespace pugi { class xml_node;