diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 02d713c42..2e71a64d6 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -741,6 +741,12 @@ void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result) // Update the list with the scraped metadata values. for (unsigned int i = 0; i < mEditors.size(); i++) { const std::string& key = mMetaDataDecl.at(i).key; + if (key == "controller" && metadata->get(key) != "") { + std::string displayName = BadgeComponent::getDisplayName(metadata->get(key)); + if (displayName != "unknown") + metadata->set(key, displayName); + } + if (mEditors.at(i)->getValue() != metadata->get(key)) { if (key == "rating") mEditors.at(i)->setOriginalColor(ICONCOLOR_SCRAPERMARKED); diff --git a/es-app/src/guis/GuiScraperMenu.cpp b/es-app/src/guis/GuiScraperMenu.cpp index 7507b0a73..8a9692332 100644 --- a/es-app/src/guis/GuiScraperMenu.cpp +++ b/es-app/src/guis/GuiScraperMenu.cpp @@ -249,6 +249,28 @@ void GuiScraperMenu::openContentOptions() ->setOpacity(DISABLED_OPACITY); } + // Scrape controllers (arcade systems only). + auto scrapeControllers = std::make_shared(mWindow); + scrapeControllers->setState(Settings::getInstance()->getBool("ScrapeControllers")); + s->addWithLabel("SCRAPE CONTROLLERS (ARCADE SYSTEMS ONLY)", scrapeControllers); + s->addSaveFunc([scrapeControllers, s] { + if (scrapeControllers->getState() != + Settings::getInstance()->getBool("ScrapeControllers")) { + Settings::getInstance()->setBool("ScrapeControllers", scrapeControllers->getState()); + s->setNeedsSaving(); + } + }); + + // Controllers are not supported by TheGamesDB, so gray out the option if this scraper is + // selected. + if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { + scrapeControllers->setEnabled(false); + scrapeControllers->setOpacity(DISABLED_OPACITY); + scrapeControllers->getParent() + ->getChild(scrapeControllers->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); + } + // Scrape other metadata. auto scrape_metadata = std::make_shared(mWindow); scrape_metadata->setState(Settings::getInstance()->getBool("ScrapeMetadata")); @@ -845,6 +867,11 @@ void GuiScraperMenu::start() contentToScrape = true; break; } + if (scraperService == "screenscraper" && + Settings::getInstance()->getBool("ScrapeControllers")) { + contentToScrape = true; + break; + } if (Settings::getInstance()->getBool("ScrapeMetadata")) { contentToScrape = true; break; diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index bf7aba7b9..83f0cb6fe 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -873,15 +873,19 @@ bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result, const std::string& key = mMetaDataDecl.at(i).key; // Skip element if the setting to not scrape metadata has been set, - // unless its type is rating or name. + // unless its type is rating, controller or name. if (!Settings::getInstance()->getBool("ScrapeMetadata") && - (key != "rating" && key != "name")) + (key != "rating" && key != "controller" && key != "name")) continue; - // Skip saving of rating if the corresponding option has been set to false. + // Skip saving of rating metadata if the corresponding option has been set to false. if (key == "rating" && !Settings::getInstance()->getBool("ScrapeRatings")) continue; + // Skip saving of controller metadata if the corresponding option has been set to false. + if (key == "controller" && !Settings::getInstance()->getBool("ScrapeControllers")) + continue; + // Skip saving of game name if the corresponding option has been set to false. if (key == "name" && !Settings::getInstance()->getBool("ScrapeGameNames")) continue; diff --git a/es-app/src/scrapers/ScreenScraper.cpp b/es-app/src/scrapers/ScreenScraper.cpp index ea4cb1cab..d6caa4885 100644 --- a/es-app/src/scrapers/ScreenScraper.cpp +++ b/es-app/src/scrapers/ScreenScraper.cpp @@ -426,6 +426,97 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, << result.mdl.get("players"); } + // Controller (only for the Arcade and SNK Neo Geo systems). + pugi::xml_node system = game.child("systeme"); + int platformID = system.attribute("parentid").as_int(); + + if (platformID == 75 || platformID == 142) { + std::string controller = Utils::String::toLower(game.child("controles").text().get()); + if (!controller.empty()) { + std::string controllerDescription = "Other"; + // Place the steering wheel entry first as some games support both joysticks and + // and steering wheels and it's likely more interesting to capture the steering + // wheel option in this case. + if (controller.find("steering wheel") != std::string::npos || + controller.find("paddle") != std::string::npos || + controller.find("pedal") != std::string::npos) { + result.mdl.set("controller", "steering_wheel_generic"); + controllerDescription = "Steering wheel"; + } + else if (controller.find("control type=\"joy") != std::string::npos || + controller.find("joystick") != std::string::npos) { + std::string buttonEntry; + std::string buttonCount; + if (controller.find("p1numbuttons=") != std::string::npos) + buttonEntry = controller.substr(controller.find("p1numbuttons=") + 13, 4); + else if (controller.find("buttons=") != std::string::npos) + buttonEntry = controller.substr(controller.find("buttons=") + 8, 5); + + bool foundDigit = false; + for (unsigned char character : buttonEntry) { + if (std::isdigit(character)) { + buttonCount.push_back(character); + foundDigit = true; + } + else if (foundDigit == true) { + break; + } + } + + if (buttonCount == "0") { + result.mdl.set("controller", "joystick_arcade_no_buttons"); + controllerDescription = "Joystick (no buttons)"; + } + else if (buttonCount == "1") { + result.mdl.set("controller", "joystick_arcade_1_button"); + controllerDescription = "Joystick (1 button)"; + } + else if (buttonCount == "2") { + result.mdl.set("controller", "joystick_arcade_2_buttons"); + controllerDescription = "Joystick (2 buttons)"; + } + else if (buttonCount == "3") { + result.mdl.set("controller", "joystick_arcade_3_buttons"); + controllerDescription = "Joystick (3 buttons)"; + } + else if (buttonCount == "4") { + result.mdl.set("controller", "joystick_arcade_4_buttons"); + controllerDescription = "Joystick (4 buttons)"; + } + else if (buttonCount == "5") { + result.mdl.set("controller", "joystick_arcade_5_buttons"); + controllerDescription = "Joystick (5 buttons)"; + } + else if (buttonCount == "6") { + result.mdl.set("controller", "joystick_arcade_6_buttons"); + controllerDescription = "Joystick (6 buttons)"; + } + else { + controllerDescription = "Joystick (other)"; + } + } + else if (controller.find("spinner") != std::string::npos) { + result.mdl.set("controller", "spinner_generic"); + controllerDescription = "Spinner"; + } + else if (controller.find("trackball") != std::string::npos) { + result.mdl.set("controller", "trackball_generic"); + controllerDescription = "Trackball"; + } + else if (controller.find("gun") != std::string::npos) { + result.mdl.set("controller", "lightgun_generic"); + controllerDescription = "Lightgun"; + } + else if (controller.find("stick") != std::string::npos) { + result.mdl.set("controller", "flight_stick_generic"); + controllerDescription = "Flight stick"; + } + + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Controller: " + << controllerDescription; + } + } + // Media super-node. pugi::xml_node media_list = game.child("medias"); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 34c3a8c8b..2d4d406c8 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -104,6 +104,7 @@ void Settings::setDefaults() mBoolMap["ScrapeGameNames"] = {true, true}; mBoolMap["ScrapeRatings"] = {true, true}; + mBoolMap["ScrapeControllers"] = {true, true}; mBoolMap["ScrapeMetadata"] = {true, true}; mBoolMap["ScrapeVideos"] = {true, true}; mBoolMap["ScrapeScreenshots"] = {true, true};