diff --git a/CMakeLists.txt b/CMakeLists.txt index 56b6b4c8c..343cc0931 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.h @@ -236,6 +237,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.cpp diff --git a/src/Settings.cpp b/src/Settings.cpp index 89f28e312..f6b428c5f 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -34,9 +34,10 @@ void Settings::setDefaults() mBoolMap["WINDOWED"] = false; mBoolMap["DISABLESOUNDS"] = false; mBoolMap["DisableGamelistWrites"] = false; + mBoolMap["ScrapeRatings"] = true; mIntMap["DIMTIME"] = 30*1000; - mIntMap["ScraperResizeWidth"] = 450; + mIntMap["ScraperResizeWidth"] = 400; mIntMap["ScraperResizeHeight"] = 0; mIntMap["GameListSortIndex"] = 0; diff --git a/src/XMLReader.cpp b/src/XMLReader.cpp index ab7077ef8..d8cf5da28 100644 --- a/src/XMLReader.cpp +++ b/src/XMLReader.cpp @@ -198,20 +198,26 @@ void updateGamelist(SystemData* system) return; std::string xmlpath = system->getGamelistPath(); - if(!boost::filesystem::exists(xmlpath)) - return; - - LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\" before writing..."; pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); - if(!result) + if(boost::filesystem::exists(xmlpath)) { - LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description(); - return; + //parse an existing file first + LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\" before writing..."; + + pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); + if(!result) + { + LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description(); + return; + } + }else{ + //set up an empty gamelist to append to + doc.append_child("gameList"); } + pugi::xml_node root = doc.child("gameList"); if(!root) { diff --git a/src/components/GuiMsgBoxOk.cpp b/src/components/GuiMsgBoxOk.cpp index 049890021..4cb08bca2 100644 --- a/src/components/GuiMsgBoxOk.cpp +++ b/src/components/GuiMsgBoxOk.cpp @@ -1,6 +1,9 @@ #include "GuiMsgBoxOk.h" #include "../Renderer.h" +#define MSG_WIDTH 0.8f +#define MSG_PADDING ((1 - MSG_WIDTH) / 2) + GuiMsgBoxOk::GuiMsgBoxOk(Window* window, const std::string& text, std::function callback) : GuiComponent(window), mCallback(callback), mText(window), @@ -8,17 +11,17 @@ GuiMsgBoxOk::GuiMsgBoxOk(Window* window, const std::string& text, std::function< { mText.setCentered(true); mText.setColor(0x00BB00FF); - mText.setSize((float)Renderer::getScreenWidth(), 0); + mText.setSize(Renderer::getScreenWidth() * MSG_WIDTH, 0); mText.setText(text); mOkText.setCentered(true); mOkText.setColor(0x0044BBFF); mOkText.setFont(Font::get(FONT_SIZE_SMALL)); - mOkText.setSize((float)Renderer::getScreenWidth(), 0); + mOkText.setSize(Renderer::getScreenWidth() * MSG_WIDTH, 0); mOkText.setText("[A]"); - mText.setPosition(0, (Renderer::getScreenHeight() - mText.getSize().y() - mOkText.getSize().y()) / 2); - mOkText.setPosition(0, mText.getPosition().y() + mText.getSize().y()); + mText.setPosition(Renderer::getScreenWidth() * MSG_PADDING, (Renderer::getScreenHeight() - mText.getSize().y() - mOkText.getSize().y()) / 2); + mOkText.setPosition(Renderer::getScreenWidth() * MSG_PADDING, mText.getPosition().y() + mText.getSize().y()); } bool GuiMsgBoxOk::input(InputConfig* config, Input input) diff --git a/src/components/GuiMsgBoxOk.h b/src/components/GuiMsgBoxOk.h index 4898c4e5d..66232adfc 100644 --- a/src/components/GuiMsgBoxOk.h +++ b/src/components/GuiMsgBoxOk.h @@ -4,6 +4,8 @@ #include "TextComponent.h" #include +//A simple popup message box with callbacks for when the user dismisses it. +//Make sure you remember to push it onto the window! class GuiMsgBoxOk : public GuiComponent { public: diff --git a/src/components/GuiMsgBoxYesNo.cpp b/src/components/GuiMsgBoxYesNo.cpp new file mode 100644 index 000000000..84ba96617 --- /dev/null +++ b/src/components/GuiMsgBoxYesNo.cpp @@ -0,0 +1,59 @@ +#include "GuiMsgBoxYesNo.h" +#include "../Renderer.h" + +#define MSG_WIDTH 0.8f +#define MSG_PADDING ((1 - MSG_WIDTH) / 2) + +GuiMsgBoxYesNo::GuiMsgBoxYesNo(Window* window, const std::string& text, std::function yesCallback, std::function noCallback) : GuiComponent(window), + mYesCallback(yesCallback), + mNoCallback(noCallback), + mText(window), + mInputText(window) +{ + mText.setCentered(true); + mText.setColor(0x00BB00FF); + mText.setSize(Renderer::getScreenWidth() * MSG_WIDTH, 0); + mText.setText(text); + + mInputText.setCentered(true); + mInputText.setColor(0x0044BBFF); + mInputText.setFont(Font::get(FONT_SIZE_SMALL)); + mInputText.setSize(Renderer::getScreenWidth() * MSG_WIDTH, 0); + mInputText.setText("[A - yes] [B - no]"); + + mText.setPosition(Renderer::getScreenWidth() * MSG_PADDING, (Renderer::getScreenHeight() - mText.getSize().y() - mInputText.getSize().y()) / 2); + mInputText.setPosition(Renderer::getScreenWidth() * MSG_PADDING, mText.getPosition().y() + mText.getSize().y()); +} + +bool GuiMsgBoxYesNo::input(InputConfig* config, Input input) +{ + if(input.value != 0) + { + if(config->isMappedTo("a", input)) + { + if(mYesCallback) + mYesCallback(); + + delete this; + return true; + }else if(config->isMappedTo("b", input)) + { + if(mNoCallback) + mNoCallback(); + + delete this; + return true; + } + } + + return false; +} + +void GuiMsgBoxYesNo::render(const Eigen::Affine3f& parentTrans) +{ + float height = mText.getSize().y() + mInputText.getSize().y(); + Renderer::setMatrix(parentTrans); + Renderer::drawRect(0, (int)((Renderer::getScreenHeight() - height) / 2), Renderer::getScreenWidth(), (int)height, 0x111111FF); + mText.render(parentTrans); + mInputText.render(parentTrans); +} diff --git a/src/components/GuiMsgBoxYesNo.h b/src/components/GuiMsgBoxYesNo.h new file mode 100644 index 000000000..9a17c92b3 --- /dev/null +++ b/src/components/GuiMsgBoxYesNo.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../GuiComponent.h" +#include "TextComponent.h" +#include + +//A simple "yes or no" popup box with callbacks for yes or no. +//Make sure you remember to push it onto the window! +class GuiMsgBoxYesNo : public GuiComponent +{ +public: + GuiMsgBoxYesNo(Window* window, const std::string& msg, std::function yesCallback = nullptr, std::function noCallback = nullptr); + + bool input(InputConfig* config, Input input) override; + void render(const Eigen::Affine3f& parentTrans) override; + +private: + std::function mYesCallback, mNoCallback; + + TextComponent mText; + TextComponent mInputText; +}; diff --git a/src/components/GuiScraperLog.h b/src/components/GuiScraperLog.h index 7674d880c..ca0b03b22 100644 --- a/src/components/GuiScraperLog.h +++ b/src/components/GuiScraperLog.h @@ -7,6 +7,8 @@ #include #include "TextComponent.h" +//A "terminal" of sorts for scraping. +//Doesn't accept input, but renders log-style messages and handles the callback chain for multi-game scraping. class GuiScraperLog : public GuiComponent { public: diff --git a/src/components/GuiScraperStart.cpp b/src/components/GuiScraperStart.cpp index a54eef47f..dfbc1ef4d 100644 --- a/src/components/GuiScraperStart.cpp +++ b/src/components/GuiScraperStart.cpp @@ -1,5 +1,6 @@ #include "GuiScraperStart.h" #include "GuiScraperLog.h" +#include "GuiMsgBoxYesNo.h" GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), mBox(window, ":/frame.png"), @@ -44,7 +45,7 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), mList.setEntry(Vector2i(1, 2), Vector2i(1, 1), &mManualSwitch, true, ComponentListComponent::AlignLeft); mStartButton.setText("GO GO GO GO", 0x00FF00FF); - mStartButton.setPressedFunc(std::bind(&GuiScraperStart::start, this)); + mStartButton.setPressedFunc(std::bind(&GuiScraperStart::pressedStart, this)); mList.setEntry(Vector2i(0, 3), Vector2i(2, 1), &mStartButton, true, ComponentListComponent::AlignCenter); mList.setPosition(Renderer::getScreenWidth() / 2 - mList.getSize().x() / 2, Renderer::getScreenHeight() / 2 - mList.getSize().y() / 2); @@ -53,6 +54,22 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), mBox.fitTo(mList.getSize(), mList.getPosition(), Eigen::Vector2f(8, 8)); } +void GuiScraperStart::pressedStart() +{ + std::vector sys = mSystemsOpt.getSelectedObjects(); + for(auto it = sys.begin(); it != sys.end(); it++) + { + if((*it)->getPlatformId() == PlatformIds::PLATFORM_UNKNOWN) + { + mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Warning: some of your selected systems do not have a platform ID set. Results may be even more inaccurate than usual!\nContinue anyway?", + std::bind(&GuiScraperStart::start, this))); + return; + } + } + + start(); +} + void GuiScraperStart::start() { std::queue searches = getSearches(mSystemsOpt.getSelectedObjects(), mFiltersOpt.getSelectedObjects()[0]); diff --git a/src/components/GuiScraperStart.h b/src/components/GuiScraperStart.h index e5f4250fb..02514cb3b 100644 --- a/src/components/GuiScraperStart.h +++ b/src/components/GuiScraperStart.h @@ -12,6 +12,9 @@ typedef std::function GameFilterFunc; +//The starting point for a multi-game scrape. +//Allows the user to set various parameters (to set filters, to set which systems to scrape, to enable manual mode). +//Generates a list of "searches" that will be carried out by GuiScraperLog. class GuiScraperStart : public GuiComponent { public: @@ -20,6 +23,7 @@ public: bool input(InputConfig* config, Input input) override; private: + void pressedStart(); void start(); std::queue getSearches(std::vector systems, GameFilterFunc selector); diff --git a/src/components/RatingComponent.cpp b/src/components/RatingComponent.cpp index 6a827cb78..25c97d482 100644 --- a/src/components/RatingComponent.cpp +++ b/src/components/RatingComponent.cpp @@ -13,6 +13,12 @@ RatingComponent::RatingComponent(Window* window) : GuiComponent(window) void RatingComponent::setValue(const std::string& value) { + if(value.empty()) + { + mValue = 0.0f; + return; + } + mValue = stof(value); if(mValue > 1.0f) mValue = 1.0f; diff --git a/src/scrapers/GamesDBScraper.cpp b/src/scrapers/GamesDBScraper.cpp index 8f1b94649..00dfe1765 100644 --- a/src/scrapers/GamesDBScraper.cpp +++ b/src/scrapers/GamesDBScraper.cpp @@ -106,6 +106,14 @@ std::vector GamesDBScraper::parseReq(ScraperSearchParams params, s boost::posix_time::ptime rd = string_to_ptime(game.child("ReleaseDate").text().get(), "%m/%d/%Y"); mdl.back().setTime("releasedate", rd); + if(Settings::getInstance()->getBool("ScrapeRatings") && game.child("Rating")) + { + float ratingVal = (game.child("Rating").text().as_int() / 10.0f); + std::stringstream ss; + ss << ratingVal; + mdl.back().set("rating", ss.str()); + } + pugi::xml_node images = game.child("Images"); if(images) diff --git a/src/scrapers/TheArchiveScraper.cpp b/src/scrapers/TheArchiveScraper.cpp index 1effa418e..efa542a77 100644 --- a/src/scrapers/TheArchiveScraper.cpp +++ b/src/scrapers/TheArchiveScraper.cpp @@ -47,7 +47,9 @@ std::vector TheArchiveScraper::parseReq(ScraperSearchParams params mdl.push_back(MetaDataList(params.system->getGameMDD())); mdl.back().set("name", game.child("title").text().get()); mdl.back().set("desc", game.child("description").text().get()); - + + //Archive.search does not return ratings + pugi::xml_node image = game.child("box_front"); pugi::xml_node thumbnail = game.child("box_front_small");