From fd7da08bf9f5ddac486d3f92d43b1c579ee33818 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 May 2020 18:34:33 +0200 Subject: [PATCH] Large code cleanup and code documentation update, mostly related to the scraper. Only cosmetic changes in this commit. --- es-app/src/components/AsyncReqComponent.cpp | 6 + es-app/src/components/AsyncReqComponent.h | 6 + .../src/components/ScraperSearchComponent.cpp | 305 +++++++++--------- .../src/components/ScraperSearchComponent.h | 38 ++- es-app/src/guis/GuiGameScraper.cpp | 78 +++-- es-app/src/guis/GuiGameScraper.h | 13 +- es-app/src/guis/GuiGamelistFilter.cpp | 59 ++-- es-app/src/guis/GuiGamelistFilter.h | 9 + es-app/src/guis/GuiGamelistOptions.cpp | 3 + es-app/src/guis/GuiGamelistOptions.h | 3 + es-app/src/guis/GuiMetaDataEd.cpp | 213 ++++++------ es-app/src/guis/GuiMetaDataEd.h | 18 +- es-app/src/guis/GuiScraperMulti.cpp | 85 +++-- es-app/src/guis/GuiScraperMulti.h | 15 +- es-app/src/guis/GuiScraperStart.cpp | 74 +++-- es-app/src/guis/GuiScraperStart.h | 17 +- es-app/src/main.cpp | 2 + es-app/src/scrapers/GamesDBJSONScraper.cpp | 203 +++++------- es-app/src/scrapers/GamesDBJSONScraper.h | 37 ++- .../scrapers/GamesDBJSONScraperResources.cpp | 137 ++++---- .../scrapers/GamesDBJSONScraperResources.h | 26 +- es-app/src/scrapers/Scraper.cpp | 152 ++++----- es-app/src/scrapers/Scraper.h | 91 +++--- es-app/src/scrapers/ScreenScraper.cpp | 205 ++++++------ es-app/src/scrapers/ScreenScraper.h | 86 +++-- es-core/src/AsyncHandle.h | 17 +- es-core/src/HttpReq.cpp | 111 +++---- es-core/src/HttpReq.h | 69 ++-- es-core/src/Settings.cpp | 5 +- es-core/src/Settings.h | 5 +- 30 files changed, 1153 insertions(+), 935 deletions(-) diff --git a/es-app/src/components/AsyncReqComponent.cpp b/es-app/src/components/AsyncReqComponent.cpp index 7693ae6b4..ed3d7fc7c 100644 --- a/es-app/src/components/AsyncReqComponent.cpp +++ b/es-app/src/components/AsyncReqComponent.cpp @@ -1,3 +1,9 @@ +// +// AsyncReqComponent.cpp +// +// Deprecated, not in use any longer? +// + #include "components/AsyncReqComponent.h" #include "renderers/Renderer.h" diff --git a/es-app/src/components/AsyncReqComponent.h b/es-app/src/components/AsyncReqComponent.h index 6b051157b..0ca934fdb 100644 --- a/es-app/src/components/AsyncReqComponent.h +++ b/es-app/src/components/AsyncReqComponent.h @@ -1,3 +1,9 @@ +// +// AsyncReqComponent.h +// +// Deprecated, not in use any longer? +// + #pragma once #ifndef ES_APP_COMPONENTS_ASYNC_REQ_COMPONENT_H #define ES_APP_COMPONENTS_ASYNC_REQ_COMPONENT_H diff --git a/es-app/src/components/ScraperSearchComponent.cpp b/es-app/src/components/ScraperSearchComponent.cpp index 752c4a8e6..ffd638641 100644 --- a/es-app/src/components/ScraperSearchComponent.cpp +++ b/es-app/src/components/ScraperSearchComponent.cpp @@ -1,3 +1,15 @@ +// +// ScraperSearchComponent.cpp +// +// User interface component for the scraper where the user is able to see an overview +// of the game being scraped and an option to override the game search string. +// Used by both single-game scraping from the GuiMetaDataEd menu as well as +// to resolve scraping conflicts when run from GuiScraperStart. +// +// This component is called from GuiGameScraper for single-game scraping and +// from GuiScraperMulti for multi-game scraping. +// + #include "components/ScraperSearchComponent.h" #include "components/ComponentList.h" @@ -14,32 +26,39 @@ #include "Log.h" #include "Window.h" -ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) : GuiComponent(window), - mGrid(window, Vector2i(4, 3)), mBusyAnim(window), - mSearchType(type) +ScraperSearchComponent::ScraperSearchComponent( + Window* window, + SearchType type) + : GuiComponent(window), + mGrid(window, Vector2i(4, 3)), + mBusyAnim(window), + mSearchType(type) { addChild(&mGrid); mBlockAccept = false; - // left spacer (empty component, needed for borders) - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + // Left spacer (empty component, needed for borders). + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), + false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); - // selected result name - mResultName = std::make_shared(mWindow, "Result name", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + // Selected result name. + mResultName = std::make_shared(mWindow, "Result name", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); - // selected result thumbnail + // Selected result thumbnail. mResultThumbnail = std::make_shared(mWindow); mGrid.setEntry(mResultThumbnail, Vector2i(1, 1), false, false, Vector2i(1, 1)); - // selected result desc + container + // Selected result description and container. mDescContainer = std::make_shared(mWindow); - mResultDesc = std::make_shared(mWindow, "Result desc", Font::get(FONT_SIZE_SMALL), 0x777777FF); + mResultDesc = std::make_shared(mWindow, "Result desc", + Font::get(FONT_SIZE_SMALL), 0x777777FF); mDescContainer->addChild(mResultDesc.get()); mDescContainer->setAutoScroll(true); - // metadata - auto font = Font::get(FONT_SIZE_SMALL); // this gets replaced in onSizeChanged() so its just a placeholder + // Metadata. + auto font = Font::get(FONT_SIZE_SMALL); // Placeholder, gets replaced in onSizeChanged(). const unsigned int mdColor = 0x777777FF; const unsigned int mdLblColor = 0x666666FF; mMD_Rating = std::make_shared(mWindow); @@ -50,17 +69,23 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) mMD_Genre = std::make_shared(mWindow, "", font, mdColor); mMD_Players = std::make_shared(mWindow, "", font, mdColor); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "RATING:", font, mdLblColor), mMD_Rating, false)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared + (mWindow, "RATING:", font, mdLblColor), mMD_Rating, false)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared + (mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared + (mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared + (mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared + (mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared + (mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); - mMD_Grid = std::make_shared(mWindow, Vector2i(2, (int)mMD_Pairs.size()*2 - 1)); + mMD_Grid = std::make_shared(mWindow, + Vector2i(2, (int)mMD_Pairs.size()*2 - 1)); unsigned int i = 0; - for(auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) - { + for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) { mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true); mMD_Grid->setEntry(it->second, Vector2i(1, i), false, it->resize); i += 2; @@ -68,9 +93,10 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) mGrid.setEntry(mMD_Grid, Vector2i(2, 1), false, false); - // result list + // Result list. mResultList = std::make_shared(mWindow); - mResultList->setCursorChangedCallback([this](CursorState state) { if(state == CURSOR_STOPPED) updateInfoPane(); }); + mResultList->setCursorChangedCallback([this](CursorState state) { + if (state == CURSOR_STOPPED) updateInfoPane(); }); updateViewStyle(); } @@ -79,46 +105,49 @@ void ScraperSearchComponent::onSizeChanged() { mGrid.setSize(mSize); - if(mSize.x() == 0 || mSize.y() == 0) + if (mSize.x() == 0 || mSize.y() == 0) return; - // column widths - if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) - mGrid.setColWidthPerc(0, 0.02f); // looks better when this is higher in auto mode + // Column widths. + if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) + mGrid.setColWidthPerc(0, 0.02f); // Looks better when this is higher in auto mode. else mGrid.setColWidthPerc(0, 0.01f); mGrid.setColWidthPerc(1, 0.25f); mGrid.setColWidthPerc(2, 0.25f); - // row heights - if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // show name - mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) / mGrid.getSize().y()); // result name + // Row heights. + if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name. + mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) / + mGrid.getSize().y()); // Result name. else - mGrid.setRowHeightPerc(0, 0.0825f); // hide name but do padding + mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding. - if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) - { + if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) mGrid.setRowHeightPerc(2, 0.2f); - }else{ + else mGrid.setRowHeightPerc(1, 0.505f); - } const float boxartCellScale = 0.9f; - // limit thumbnail size using setMaxHeight - we do this instead of letting mGrid call setSize because it maintains the aspect ratio - // we also pad a little so it doesn't rub up against the metadata labels + // Limit thumbnail size using setMaxHeight - we do this instead of letting mGrid + // call setSize because it maintains the aspect ratio. + // We also pad a little so it doesn't rub up against the metadata labels. mResultThumbnail->setMaxSize(mGrid.getColWidth(1) * boxartCellScale, mGrid.getRowHeight(1)); - // metadata + // Metadata. resizeMetadata(); - if(mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) - mDescContainer->setSize(mGrid.getColWidth(1)*boxartCellScale + mGrid.getColWidth(2), mResultDesc->getFont()->getHeight() * 3); + if (mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) + mDescContainer->setSize(mGrid.getColWidth(1)*boxartCellScale + + mGrid.getColWidth(2), mResultDesc->getFont()->getHeight() * 3); else - mDescContainer->setSize(mGrid.getColWidth(3)*boxartCellScale, mResultDesc->getFont()->getHeight() * 8); + mDescContainer->setSize(mGrid.getColWidth(3)*boxartCellScale, + mResultDesc->getFont()->getHeight() * 8); - mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container + // Make description text wrap at edge of container. + mResultDesc->setSize(mDescContainer->getSize().x(), 0); mGrid.onSizeChanged(); @@ -128,28 +157,25 @@ void ScraperSearchComponent::onSizeChanged() void ScraperSearchComponent::resizeMetadata() { mMD_Grid->setSize(mGrid.getColWidth(2), mGrid.getRowHeight(1)); - if(mMD_Grid->getSize().y() > mMD_Pairs.size()) - { + if (mMD_Grid->getSize().y() > mMD_Pairs.size()) { const int fontHeight = (int)(mMD_Grid->getSize().y() / mMD_Pairs.size() * 0.8f); auto fontLbl = Font::get(fontHeight, FONT_PATH_REGULAR); auto fontComp = Font::get(fontHeight, FONT_PATH_LIGHT); - // update label fonts + // Update label fonts. float maxLblWidth = 0; - for(auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) - { + for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) { it->first->setFont(fontLbl); it->first->setSize(0, 0); - if(it->first->getSize().x() > maxLblWidth) + if (it->first->getSize().x() > maxLblWidth) maxLblWidth = it->first->getSize().x() + 6; } - for(unsigned int i = 0; i < mMD_Pairs.size(); i++) - { - mMD_Grid->setRowHeightPerc(i*2, (fontLbl->getLetterHeight() + 2) / mMD_Grid->getSize().y()); - } + for (unsigned int i = 0; i < mMD_Pairs.size(); i++) + mMD_Grid->setRowHeightPerc(i*2, (fontLbl->getLetterHeight() + 2) / + mMD_Grid->getSize().y()); - // update component fonts + // Update component fonts. mMD_ReleaseDate->setFont(fontComp); mMD_Developer->setFont(fontComp); mMD_Publisher->setFont(fontComp); @@ -158,44 +184,52 @@ void ScraperSearchComponent::resizeMetadata() mMD_Grid->setColWidthPerc(0, maxLblWidth / mMD_Grid->getSize().x()); - // rating is manually sized + // Rating is manually sized. mMD_Rating->setSize(mMD_Grid->getColWidth(1), fontLbl->getHeight() * 0.65f); mMD_Grid->onSizeChanged(); - // make result font follow label font + // Make result font follow label font. mResultDesc->setFont(Font::get(fontHeight, FONT_PATH_REGULAR)); } } void ScraperSearchComponent::updateViewStyle() { - // unlink description and result list and result name + // Unlink description, result list and result name. mGrid.removeEntry(mResultName); mGrid.removeEntry(mResultDesc); mGrid.removeEntry(mResultList); - // add them back depending on search type - if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) - { - // show name - mGrid.setEntry(mResultName, Vector2i(1, 0), false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); + // Add them back depending on search type. + if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { + // Show name. + mGrid.setEntry(mResultName, Vector2i(1, 0), false, true, Vector2i(2, 1), + GridFlags::BORDER_TOP); - // need a border on the bottom left - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 2), false, false, Vector2i(3, 1), GridFlags::BORDER_BOTTOM); + // Need a border on the bottom left. + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 2), + false, false, Vector2i(3, 1), GridFlags::BORDER_BOTTOM); - // show description on the right - mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); - mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container - }else{ - // fake row where name would be - mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 0), false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); + // Show description on the right. + mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3), + GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + // Make description text wrap at edge of container. + mResultDesc->setSize(mDescContainer->getSize().x(), 0); + } + else { + // Fake row where name would be. + mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 0), + false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); - // show result list on the right - mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3), GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + // Show result list on the right. + mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3), + GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); - // show description under image/info - mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1), GridFlags::BORDER_BOTTOM); - mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container + // Show description under image/info. + mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1), + GridFlags::BORDER_BOTTOM); + // Make description text wrap at edge of container. + mResultDesc->setSize(mDescContainer->getSize().x(), 0); } } @@ -229,31 +263,31 @@ void ScraperSearchComponent::onSearchDone(const std::vector auto font = Font::get(FONT_SIZE_MEDIUM); unsigned int color = 0x777777FF; - if(results.empty()) - { - // Check if the scraper used is still valid - if (!isValidConfiguredScraper()) - { - mWindow->pushGui(new GuiMsgBox(mWindow, Utils::String::toUpper("Configured scraper is no longer available.\nPlease change the scraping source in the settings."), + if (results.empty()) { + // Check if the scraper used is still valid. + if (!isValidConfiguredScraper()) { + mWindow->pushGui(new GuiMsgBox(mWindow, Utils::String::toUpper("Configured scraper " + "is no longer available.\nPlease change the scraping source in the settings."), "FINISH", mSkipCallback)); } - else - { + else { ComponentListRow row; - row.addElement(std::make_shared(mWindow, "NO GAMES FOUND - SKIP", font, color), true); + row.addElement(std::make_shared(mWindow, "NO GAMES FOUND - SKIP", + font, color), true); - if(mSkipCallback) + if (mSkipCallback) row.makeAcceptInputHandler(mSkipCallback); mResultList->addRow(row); mGrid.resetCursor(); } - }else{ + } + else { ComponentListRow row; - for(size_t i = 0; i < results.size(); i++) - { + for (size_t i = 0; i < results.size(); i++) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true); + row.addElement(std::make_shared(mWindow, + Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true); row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); }); mResultList->addRow(row); } @@ -263,14 +297,13 @@ void ScraperSearchComponent::onSearchDone(const std::vector mBlockAccept = false; updateInfoPane(); - if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) - { - if(mScraperResults.size() == 0) + if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { + if (mScraperResults.size() == 0) mSkipCallback(); else returnResult(mScraperResults.front()); - }else if(mSearchType == ALWAYS_ACCEPT_MATCHING_CRC) - { + } + else if (mSearchType == ALWAYS_ACCEPT_MATCHING_CRC) { // TODO } } @@ -286,7 +319,7 @@ void ScraperSearchComponent::onSearchError(const std::string& error) int ScraperSearchComponent::getSelectedIndex() { - if(!mScraperResults.size() || mGrid.getSelectedComponent() != mResultList) + if (!mScraperResults.size() || mGrid.getSelectedComponent() != mResultList) return -1; return mResultList->getCursorId(); @@ -295,13 +328,10 @@ int ScraperSearchComponent::getSelectedIndex() void ScraperSearchComponent::updateInfoPane() { int i = getSelectedIndex(); - if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT && mScraperResults.size()) - { + if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT && mScraperResults.size()) i = 0; - } - if(i != -1 && (int)mScraperResults.size() > i) - { + if (i != -1 && (int)mScraperResults.size() > i) { ScraperSearchResult& res = mScraperResults.at(i); mResultName->setText(Utils::String::toUpper(res.mdl.get("name"))); mResultDesc->setText(Utils::String::toUpper(res.mdl.get("desc"))); @@ -309,14 +339,12 @@ void ScraperSearchComponent::updateInfoPane() mResultThumbnail->setImage(""); const std::string& thumb = res.thumbnailUrl.empty() ? res.imageUrl : res.thumbnailUrl; - if(!thumb.empty()) - { + if (!thumb.empty()) mThumbnailReq = std::unique_ptr(new HttpReq(thumb)); - }else{ + else mThumbnailReq.reset(); - } - // metadata + // Metadata. mMD_Rating->setValue(Utils::String::toUpper(res.mdl.get("rating"))); mMD_ReleaseDate->setValue(Utils::String::toUpper(res.mdl.get("releasedate"))); mMD_Developer->setText(Utils::String::toUpper(res.mdl.get("developer"))); @@ -324,12 +352,13 @@ void ScraperSearchComponent::updateInfoPane() mMD_Genre->setText(Utils::String::toUpper(res.mdl.get("genre"))); mMD_Players->setText(Utils::String::toUpper(res.mdl.get("players"))); mGrid.onSizeChanged(); - }else{ + } + else { mResultName->setText(""); mResultDesc->setText(""); mResultThumbnail->setImage(""); - // metadata + // Metadata. mMD_Rating->setValue(""); mMD_ReleaseDate->setValue(""); mMD_Developer->setText(""); @@ -341,9 +370,8 @@ void ScraperSearchComponent::updateInfoPane() bool ScraperSearchComponent::input(InputConfig* config, Input input) { - if(config->isMappedTo("a", input) && input.value != 0) - { - if(mBlockAccept) + if (config->isMappedTo("a", input) && input.value != 0) { + if (mBlockAccept) return true; } @@ -356,8 +384,7 @@ void ScraperSearchComponent::render(const Transform4x4f& parentTrans) renderChildren(trans); - if(mBlockAccept) - { + if (mBlockAccept) { Renderer::setMatrix(trans); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x00000011, 0x00000011); @@ -369,9 +396,8 @@ void ScraperSearchComponent::returnResult(ScraperSearchResult result) { mBlockAccept = true; - // resolve metadata image before returning - if(!result.imageUrl.empty()) - { + // Resolve metadata image before returning. + if (!result.imageUrl.empty()) { mMDResolveHandle = resolveMetaDataAssets(result, mLastSearch); return; } @@ -383,46 +409,36 @@ void ScraperSearchComponent::update(int deltaTime) { GuiComponent::update(deltaTime); - if(mBlockAccept) - { + if (mBlockAccept) mBusyAnim.update(deltaTime); - } - if(mThumbnailReq && mThumbnailReq->status() != HttpReq::REQ_IN_PROGRESS) - { + if (mThumbnailReq && mThumbnailReq->status() != HttpReq::REQ_IN_PROGRESS) updateThumbnail(); - } - if(mSearchHandle && mSearchHandle->status() != ASYNC_IN_PROGRESS) - { + if (mSearchHandle && mSearchHandle->status() != ASYNC_IN_PROGRESS) { auto status = mSearchHandle->status(); auto results = mSearchHandle->getResults(); auto statusString = mSearchHandle->getStatusString(); - // we reset here because onSearchDone in auto mode can call mSkipCallback() which can call - // another search() which will set our mSearchHandle to something important + // We reset here because onSearchDone in auto mode can call mSkipCallback() which + // can call another search() which will set our mSearchHandle to something important. mSearchHandle.reset(); - if(status == ASYNC_DONE) - { + if (status == ASYNC_DONE) onSearchDone(results); - }else if(status == ASYNC_ERROR) - { + else if (status == ASYNC_ERROR) onSearchError(statusString); - } } - if(mMDResolveHandle && mMDResolveHandle->status() != ASYNC_IN_PROGRESS) - { - if(mMDResolveHandle->status() == ASYNC_DONE) - { + if (mMDResolveHandle && mMDResolveHandle->status() != ASYNC_IN_PROGRESS) { + if (mMDResolveHandle->status() == ASYNC_DONE) { ScraperSearchResult result = mMDResolveHandle->getResult(); mMDResolveHandle.reset(); - - // this might end in us being deleted, depending on mAcceptCallback - so make sure this is the last thing we do in update() + // This might end in us being deleted, depending on mAcceptCallback - + // so make sure this is the last thing we do in update(). returnResult(result); - }else if(mMDResolveHandle->status() == ASYNC_ERROR) - { + } + else if (mMDResolveHandle->status() == ASYNC_ERROR) { onSearchError(mMDResolveHandle->getStatusString()); mMDResolveHandle.reset(); } @@ -431,12 +447,12 @@ void ScraperSearchComponent::update(int deltaTime) void ScraperSearchComponent::updateThumbnail() { - if(mThumbnailReq && mThumbnailReq->status() == HttpReq::REQ_SUCCESS) - { + if (mThumbnailReq && mThumbnailReq->status() == HttpReq::REQ_SUCCESS) { std::string content = mThumbnailReq->getContent(); mResultThumbnail->setImage(content.data(), content.length()); - mGrid.onSizeChanged(); // a hack to fix the thumbnail position since its size changed - }else{ + mGrid.onSizeChanged(); // A hack to fix the thumbnail position since its size changed. + } + else { LOG(LogWarning) << "thumbnail req failed: " << mThumbnailReq->getErrorMsg(); mResultThumbnail->setImage(""); } @@ -446,15 +462,14 @@ void ScraperSearchComponent::updateThumbnail() void ScraperSearchComponent::openInputScreen(ScraperSearchParams& params) { - auto searchForFunc = [&](const std::string& name) - { + auto searchForFunc = [&](const std::string& name) { params.nameOverride = name; search(params); }; stop(); mWindow->pushGui(new GuiTextEditPopup(mWindow, "SEARCH FOR", - // initial value is last search if there was one, otherwise the clean path name + // Initial value is last search if there was one, otherwise the clean path name. params.nameOverride.empty() ? params.game->getCleanName() : params.nameOverride, searchForFunc, false, "SEARCH")); } @@ -462,7 +477,7 @@ void ScraperSearchComponent::openInputScreen(ScraperSearchParams& params) std::vector ScraperSearchComponent::getHelpPrompts() { std::vector prompts = mGrid.getHelpPrompts(); - if(getSelectedIndex() != -1) + if (getSelectedIndex() != -1) prompts.push_back(HelpPrompt("a", "accept result")); return prompts; diff --git a/es-app/src/components/ScraperSearchComponent.h b/es-app/src/components/ScraperSearchComponent.h index 6af36e795..af0776454 100644 --- a/es-app/src/components/ScraperSearchComponent.h +++ b/es-app/src/components/ScraperSearchComponent.h @@ -1,3 +1,15 @@ +// +// ScraperSearchComponent.h +// +// User interface component for the scraper where the user is able to see an overview +// of the game being scraped and an option to override the game search string. +// Used by both single-game scraping from the GuiMetaDataEd menu as well as +// to resolve scraping conflicts when run from GuiScraperStart. +// +// This component is called from GuiGameScraper for single-game scraping and +// from GuiScraperMulti for multi-game scraping. +// + #pragma once #ifndef ES_APP_COMPONENTS_SCRAPER_SEARCH_COMPONENT_H #define ES_APP_COMPONENTS_SCRAPER_SEARCH_COMPONENT_H @@ -17,8 +29,7 @@ class TextComponent; class ScraperSearchComponent : public GuiComponent { public: - enum SearchType - { + enum SearchType { ALWAYS_ACCEPT_FIRST_RESULT, ALWAYS_ACCEPT_MATCHING_CRC, NEVER_AUTO_ACCEPT @@ -31,10 +42,14 @@ public: void stop(); inline SearchType getSearchType() const { return mSearchType; } - // Metadata assets will be resolved before calling the accept callback (e.g. result.mdl's "image" is automatically downloaded and properly set). - inline void setAcceptCallback(const std::function& acceptCallback) { mAcceptCallback = acceptCallback; } - inline void setSkipCallback(const std::function& skipCallback) { mSkipCallback = skipCallback; }; - inline void setCancelCallback(const std::function& cancelCallback) { mCancelCallback = cancelCallback; } + // Metadata assets will be resolved before calling the accept callback + // (e.g. result.mdl's "image" is automatically downloaded and properly set). + inline void setAcceptCallback(const std::function& + acceptCallback) { mAcceptCallback = acceptCallback; } + inline void setSkipCallback(const std::function& + skipCallback) { mSkipCallback = skipCallback; }; + inline void setCancelCallback(const std::function& + cancelCallback) { mCancelCallback = cancelCallback; } bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; @@ -56,7 +71,7 @@ private: int getSelectedIndex(); - // resolve any metadata assets that need to be downloaded and return + // Resolve any metadata assets that need to be downloaded and return. void returnResult(ScraperSearchResult result); ComponentGrid mGrid; @@ -75,14 +90,15 @@ private: std::shared_ptr mMD_Genre; std::shared_ptr mMD_Players; - // label-component pair - struct MetaDataPair - { + // Label-component pair. + struct MetaDataPair { std::shared_ptr first; std::shared_ptr second; bool resize; - MetaDataPair(const std::shared_ptr& f, const std::shared_ptr& s, bool r = true) : first(f), second(s), resize(r) {}; + MetaDataPair(const std::shared_ptr& f, + const std::shared_ptr& s, bool r = true) + : first(f), second(s), resize(r) {}; }; std::vector mMD_Pairs; diff --git a/es-app/src/guis/GuiGameScraper.cpp b/es-app/src/guis/GuiGameScraper.cpp index d8ebcb716..66f8e674a 100644 --- a/es-app/src/guis/GuiGameScraper.cpp +++ b/es-app/src/guis/GuiGameScraper.cpp @@ -1,3 +1,11 @@ +// +// GuiGameScraper.cpp +// +// Single game scraping user interface. +// This interface is triggered from GuiMetaDataEd. +// ScraperSearchComponent is called from here. +// + #include "guis/GuiGameScraper.h" #include "components/ButtonComponent.h" @@ -7,48 +15,57 @@ #include "PowerSaver.h" #include "SystemData.h" -GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::function doneFunc) : GuiComponent(window), - mGrid(window, Vector2i(1, 7)), - mBox(window, ":/frame.png"), - mSearchParams(params), - mClose(false) +GuiGameScraper::GuiGameScraper( + Window* window, + ScraperSearchParams params, + std::function doneFunc) + : GuiComponent(window), + mGrid(window, Vector2i(1, 7)), + mBox(window, ":/frame.png"), + mSearchParams(params), + mClose(false) { PowerSaver::pause(); addChild(&mBox); addChild(&mGrid); - // row 0 is a spacer + // Row 0 is a spacer. - mGameName = std::make_shared(mWindow, Utils::String::toUpper(Utils::FileSystem::getFileName(mSearchParams.game->getPath())), - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); + mGameName = std::make_shared(mWindow, Utils::String::toUpper( + Utils::FileSystem::getFileName(mSearchParams.game->getPath())), + Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mGameName, Vector2i(0, 1), false, true); - // row 2 is a spacer + // Row 2 is a spacer. - mSystemName = std::make_shared(mWindow, Utils::String::toUpper(mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL), - 0x888888FF, ALIGN_CENTER); + mSystemName = std::make_shared(mWindow, Utils::String::toUpper( + mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL), + 0x888888FF, ALIGN_CENTER); mGrid.setEntry(mSystemName, Vector2i(0, 3), false, true); - // row 4 is a spacer + // Row 4 is a spacer. - // ScraperSearchComponent - mSearch = std::make_shared(window, ScraperSearchComponent::NEVER_AUTO_ACCEPT); + // ScraperSearchComponent. + mSearch = std::make_shared(window, + ScraperSearchComponent::NEVER_AUTO_ACCEPT); mGrid.setEntry(mSearch, Vector2i(0, 5), true); - // buttons + // Buttons std::vector< std::shared_ptr > buttons; buttons.push_back(std::make_shared(mWindow, "INPUT", "search", [&] { mSearch->openInputScreen(mSearchParams); mGrid.resetCursor(); })); - buttons.push_back(std::make_shared(mWindow, "CANCEL", "cancel", [&] { delete this; })); + buttons.push_back(std::make_shared( + mWindow, "CANCEL", "cancel", [&] { delete this; })); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); - // we call this->close() instead of just delete this; in the accept callback: - // this is because of how GuiComponent::update works. if it was just delete this, this would happen when the metadata resolver is done: + // We call this->close() instead of just 'delete this' in the accept callback. + // This is because of how GuiComponent::update works. If it was just 'delete this', + // the following would happen when the metadata resolver is done: // GuiGameScraper::update() // GuiComponent::update() // it = mChildren.cbegin(); @@ -56,9 +73,9 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std:: // it++; // mSearchComponent::update() // acceptCallback -> delete this - // it++; // error, mChildren has been deleted because it was part of this + // it++; // Error, mChildren has been deleted because it was part of 'this'. - // so instead we do this: + // So instead we do this: // GuiGameScraper::update() // GuiComponent::update() // it = mChildren.cbegin(); @@ -66,17 +83,19 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std:: // it++; // mSearchComponent::update() // acceptCallback -> close() -> mClose = true - // it++; // ok + // it++; // OK. // if(mClose) // delete this; - mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { doneFunc(result); close(); }); + mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { + doneFunc(result); close(); }); mSearch->setCancelCallback([&] { delete this; }); setSize(Renderer::getScreenWidth() * 0.95f, Renderer::getScreenHeight() * 0.747f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - + mSize.y()) / 2); mGrid.resetCursor(); - mSearch->search(params); // start the search + mSearch->search(params); // Start the search. } void GuiGameScraper::onSizeChanged() @@ -84,18 +103,19 @@ void GuiGameScraper::onSizeChanged() mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); mGrid.setRowHeightPerc(0, 0.04f, false); - mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y(), false); // game name + mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / + mSize.y(), false); // Game name. mGrid.setRowHeightPerc(2, 0.04f, false); - mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y(), false); // system name + mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / + mSize.y(), false); // System name. mGrid.setRowHeightPerc(4, 0.04f, false); - mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // buttons + mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // Buttons. mGrid.setSize(mSize); } bool GuiGameScraper::input(InputConfig* config, Input input) { - if(config->isMappedTo("b", input) && input.value) - { + if(config->isMappedTo("b", input) && input.value) { PowerSaver::resume(); delete this; return true; diff --git a/es-app/src/guis/GuiGameScraper.h b/es-app/src/guis/GuiGameScraper.h index 2f3da8b47..adbad4dd1 100644 --- a/es-app/src/guis/GuiGameScraper.h +++ b/es-app/src/guis/GuiGameScraper.h @@ -1,3 +1,11 @@ +// +// GuiGameScraper.h +// +// Single game scraping user interface. +// This interface is triggered from GuiMetaDataEd. +// ScraperSearchComponent is called from here. +// + #pragma once #ifndef ES_APP_GUIS_GUI_GAME_SCRAPER_H #define ES_APP_GUIS_GUI_GAME_SCRAPER_H @@ -9,7 +17,10 @@ class GuiGameScraper : public GuiComponent { public: - GuiGameScraper(Window* window, ScraperSearchParams params, std::function doneFunc); + GuiGameScraper( + Window* window, + ScraperSearchParams params, + std::function doneFunc); void onSizeChanged() override; diff --git a/es-app/src/guis/GuiGamelistFilter.cpp b/es-app/src/guis/GuiGamelistFilter.cpp index f7a68b67c..f68c0ddd5 100644 --- a/es-app/src/guis/GuiGamelistFilter.cpp +++ b/es-app/src/guis/GuiGamelistFilter.cpp @@ -1,10 +1,23 @@ +// +// GuiGamelistFilter.cpp +// +// User interface for the gamelist filters. +// Triggered from the GuiGamelistOptions menu. +// Actual filter logic is covered by FileFilterIndex. +// + #include "guis/GuiGamelistFilter.h" #include "components/OptionListComponent.h" #include "views/UIModeController.h" #include "SystemData.h" -GuiGamelistFilter::GuiGamelistFilter(Window* window, SystemData* system) : GuiComponent(window), mMenu(window, "FILTER GAMELIST BY"), mSystem(system) +GuiGamelistFilter::GuiGamelistFilter( + Window* window, + SystemData* system) + : GuiComponent(window), + mMenu(window, "FILTER GAMELIST BY"), + mSystem(system) { initializeMenu(); } @@ -13,15 +26,15 @@ void GuiGamelistFilter::initializeMenu() { addChild(&mMenu); - // get filters from system - + // Get filters from system. mFilterIndex = mSystem->getIndex(); ComponentListRow row; - // show filtered menu + // Show filtered menu. row.elements.clear(); - row.addElement(std::make_shared(mWindow, "RESET ALL FILTERS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "RESET ALL FILTERS", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.makeAcceptInputHandler(std::bind(&GuiGamelistFilter::resetAllFilters, this)); mMenu.addRow(row); row.elements.clear(); @@ -30,13 +43,15 @@ void GuiGamelistFilter::initializeMenu() mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this)); - mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, + Renderer::getScreenHeight() * 0.15f); } void GuiGamelistFilter::resetAllFilters() { mFilterIndex->resetFilters(); - for (std::map >>::const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); ++it ) { + for (std::map + >>::const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); ++it ) { std::shared_ptr< OptionListComponent > optionList = it->second; optionList->selectNone(); } @@ -57,23 +72,22 @@ void GuiGamelistFilter::addFiltersToMenu() if (UIModeController::getInstance()->isUIModeKid()) skip = 2; - for (std::vector::const_iterator it = decls.cbegin(); it != decls.cend()-skip; ++it ) { + for (std::vector::const_iterator it = decls.cbegin(); + it != decls.cend()-skip; ++it ) { - FilterIndexType type = (*it).type; // type of filter - std::map* allKeys = (*it).allIndexKeys; // all possible filters for this type - std::string menuLabel = (*it).menuLabel; // text to show in menu + FilterIndexType type = (*it).type; // Type of filter. + // All possible filters for this type. + std::map* allKeys = (*it).allIndexKeys; + std::string menuLabel = (*it).menuLabel; // Text to show in menu. std::shared_ptr< OptionListComponent > optionList; - - // add filters (with first one selected) + // Add filters (with first one selected). ComponentListRow row; - // add genres + // Add genres. optionList = std::make_shared< OptionListComponent >(mWindow, menuLabel, true); - for(auto it: *allKeys) - { + for (auto it: *allKeys) optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type)); - } if (allKeys->size() > 0) mMenu.addWithLabel(menuLabel, optionList); @@ -84,27 +98,24 @@ void GuiGamelistFilter::addFiltersToMenu() void GuiGamelistFilter::applyFilters() { std::vector decls = mFilterIndex->getFilterDataDecls(); - for (std::map >>::const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); ++it ) { + for (std::map + >>::const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); ++it ) { std::shared_ptr< OptionListComponent > optionList = it->second; std::vector filters = optionList->getSelectedObjects(); mFilterIndex->setFilter(it->first, &filters); } delete this; - } bool GuiGamelistFilter::input(InputConfig* config, Input input) { bool consumed = GuiComponent::input(config, input); - if(consumed) + if (consumed) return true; - if(config->isMappedTo("b", input) && input.value != 0) - { + if (config->isMappedTo("b", input) && input.value != 0) applyFilters(); - } - return false; } diff --git a/es-app/src/guis/GuiGamelistFilter.h b/es-app/src/guis/GuiGamelistFilter.h index 003b72e2a..b6475e6ca 100644 --- a/es-app/src/guis/GuiGamelistFilter.h +++ b/es-app/src/guis/GuiGamelistFilter.h @@ -1,3 +1,11 @@ +// +// GuiGamelistFilter.h +// +// User interface for the gamelist filters. +// Triggered from the GuiGamelistOptions menu. +// Actual filter logic is covered by FileFilterIndex. +// + #pragma once #ifndef ES_APP_GUIS_GUI_GAME_LIST_FILTER_H #define ES_APP_GUIS_GUI_GAME_LIST_FILTER_H @@ -14,6 +22,7 @@ class GuiGamelistFilter : public GuiComponent { public: GuiGamelistFilter(Window* window, SystemData* system); + ~GuiGamelistFilter(); bool input(InputConfig* config, Input input) override; diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 3cfa17ff2..4813ef22b 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -4,6 +4,9 @@ // Gamelist options menu for the 'Jump to...' quick selector, // game sorting, game filters, and metadata edit. // +// The filter interface is covered by GuiGamelistFilter and the +// metadata edit interface is covered by GuiMetaDataEd. +// #include "GuiGamelistOptions.h" diff --git a/es-app/src/guis/GuiGamelistOptions.h b/es-app/src/guis/GuiGamelistOptions.h index 8e47c8b63..0d591dafd 100644 --- a/es-app/src/guis/GuiGamelistOptions.h +++ b/es-app/src/guis/GuiGamelistOptions.h @@ -4,6 +4,9 @@ // Gamelist options menu for the 'Jump to...' quick selector, // game sorting, game filters, and metadata edit. // +// The filter interface is covered by GuiGamelistFilter and the +// metadata edit interface is covered by GuiMetaDataEd. +// #pragma once #ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index a05008f35..f3701b63d 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -1,3 +1,12 @@ +// +// GuiMetaDataEd.cpp +// +// Game metadata edit user interface. +// This interface is triggered from the GuiGamelistOptions menu. +// The scraping interface is handled by GuiGameScraper which calls +// ScraperSearchComponent. +// + #include "guis/GuiMetaDataEd.h" #include "components/ButtonComponent.h" @@ -20,25 +29,35 @@ #include "SystemData.h" #include "Window.h" -GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector& mdd, ScraperSearchParams scraperParams, - const std::string& /*header*/, std::function saveCallback, std::function deleteFunc) : GuiComponent(window), - mScraperParams(scraperParams), +GuiMetaDataEd::GuiMetaDataEd( + Window* window, + MetaDataList* md, + const std::vector& mdd, + ScraperSearchParams scraperParams, + const std::string& /*header*/, + std::function saveCallback, + std::function deleteFunc) + : GuiComponent(window), + mScraperParams(scraperParams), - mBackground(window, ":/frame.png"), - mGrid(window, Vector2i(1, 3)), + mBackground(window, ":/frame.png"), + mGrid(window, Vector2i(1, 3)), - mMetaDataDecl(mdd), - mMetaData(md), - mSavedCallback(saveCallback), mDeleteFunc(deleteFunc) + mMetaDataDecl(mdd), + mMetaData(md), + mSavedCallback(saveCallback), + mDeleteFunc(deleteFunc) { addChild(&mBackground); addChild(&mGrid); mHeaderGrid = std::make_shared(mWindow, Vector2i(1, 5)); - mTitle = std::make_shared(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); - mSubtitle = std::make_shared(mWindow, Utils::String::toUpper(Utils::FileSystem::getFileName(scraperParams.game->getPath())), - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); + mTitle = std::make_shared(mWindow, "EDIT METADATA", + Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + mSubtitle = std::make_shared(mWindow, + Utils::String::toUpper(Utils::FileSystem::getFileName(scraperParams.game-> + getPath())), Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), false, true); mHeaderGrid->setEntry(mSubtitle, Vector2i(0, 3), false, true); @@ -47,19 +66,19 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector mList = std::make_shared(mWindow); mGrid.setEntry(mList, Vector2i(0, 1), true, true); - // populate list - for(auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) - { + // Populate list. + for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) { std::shared_ptr ed; - // don't add statistics - if(iter->isStatistic) + // Don't add statistics. + if (iter->isStatistic) continue; - // Don't show the launch string override entry if this option has been disabled in the settings - if(!Settings::getInstance()->getBool("LaunchstringOverride") && iter->type == MD_LAUNCHSTRING) - { - ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + // Don't show the launch string override entry if this option has been disabled. + if (!Settings::getInstance()->getBool("LaunchstringOverride") && + iter->type == MD_LAUNCHSTRING) { + ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, + FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); assert(ed); ed->setValue(mMetaData->get(iter->key)); mEditors.push_back(ed); @@ -67,22 +86,20 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector continue; } - // create ed and add it (and any related components) to mMenu - // ed's value will be set below + // Create ed and add it (and any related components) to mMenu. + // ed's value will be set below. ComponentListRow row; - auto lbl = std::make_shared(mWindow, Utils::String::toUpper(iter->displayName), Font::get(FONT_SIZE_SMALL), 0x777777FF); - row.addElement(lbl, true); // label + auto lbl = std::make_shared(mWindow, + Utils::String::toUpper(iter->displayName), Font::get(FONT_SIZE_SMALL), 0x777777FF); + row.addElement(lbl, true); // Label. - switch(iter->type) - { - case MD_BOOL: - { + switch (iter->type) { + case MD_BOOL: { ed = std::make_shared(window); row.addElement(ed, false, true); break; } - case MD_RATING: - { + case MD_RATING: { ed = std::make_shared(window); const float height = lbl->getSize().y() * 0.71f; ed->setSize(0, height); @@ -92,13 +109,12 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0); row.addElement(spacer, false); - // pass input to the actual RatingComponent instead of the spacer - row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, std::placeholders::_2); - + // Pass input to the actual RatingComponent instead of the spacer. + row.input_handler = std::bind(&GuiComponent::input, + ed.get(), std::placeholders::_1, std::placeholders::_2); break; } - case MD_DATE: - { + case MD_DATE: { ed = std::make_shared(window); row.addElement(ed, false); @@ -106,20 +122,20 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0); row.addElement(spacer, false); - // pass input to the actual DateTimeEditComponent instead of the spacer - row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, std::placeholders::_2); - + // Pass input to the actual DateTimeEditComponent instead of the spacer. + row.input_handler = std::bind(&GuiComponent::input, ed.get(), + std::placeholders::_1, std::placeholders::_2); break; } - case MD_TIME: - { - ed = std::make_shared(window, DateTimeEditComponent::DISP_RELATIVE_TO_NOW); + case MD_TIME: { + ed = std::make_shared(window, + DateTimeEditComponent::DISP_RELATIVE_TO_NOW); row.addElement(ed, false); break; } - case MD_LAUNCHSTRING: - { - ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + case MD_LAUNCHSTRING: { + ed = std::make_shared(window, "", + Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); row.addElement(ed, true); auto spacer = std::make_shared(mWindow); @@ -133,21 +149,26 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector bool multiLine = false; const std::string title = iter->displayPrompt; - auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); }; // ok callback (apply new value to ed) + auto updateVal = [ed](const std::string& newVal) { + ed->setValue(newVal); }; // OK callback (apply new value to ed). std::string staticTextString = "Default value from es_systems.cfg:"; - std::string defaultLaunchString = scraperParams.system->getSystemEnvData()->mLaunchCommand; + std::string defaultLaunchString = scraperParams.system-> + getSystemEnvData()->mLaunchCommand; - row.makeAcceptInputHandler([this, title, staticTextString, defaultLaunchString, ed, updateVal, multiLine] { - mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, title, staticTextString, defaultLaunchString, ed->getValue(), updateVal, multiLine)); + row.makeAcceptInputHandler([this, title, staticTextString, + defaultLaunchString, ed, updateVal, multiLine] { + mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, title, + staticTextString, defaultLaunchString, ed->getValue(), + updateVal, multiLine)); }); break; } case MD_MULTILINE_STRING: - default: - { - // MD_STRING - ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + default: { + // MD_STRING. + ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, + FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); row.addElement(ed, true); auto spacer = std::make_shared(mWindow); @@ -161,9 +182,12 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector bool multiLine = iter->type == MD_MULTILINE_STRING; const std::string title = iter->displayPrompt; - auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); }; // ok callback (apply new value to ed) + + // OK callback (apply new value to ed). + auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); }; row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] { - mWindow->pushGui(new GuiTextEditPopup(mWindow, title, ed->getValue(), updateVal, multiLine)); + mWindow->pushGui(new GuiTextEditPopup(mWindow, title, ed->getValue(), + updateVal, multiLine)); }); break; } @@ -177,26 +201,33 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector std::vector< std::shared_ptr > buttons; - if(!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) - buttons.push_back(std::make_shared(mWindow, "SCRAPE", "scrape", std::bind(&GuiMetaDataEd::fetch, this))); + if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) + buttons.push_back(std::make_shared(mWindow, "SCRAPE", "scrape", + std::bind(&GuiMetaDataEd::fetch, this))); - buttons.push_back(std::make_shared(mWindow, "SAVE", "save", [&] { save(); delete this; })); - buttons.push_back(std::make_shared(mWindow, "CANCEL", "cancel", [&] { delete this; })); + buttons.push_back(std::make_shared(mWindow, "SAVE", "save", + [&] { save(); delete this; })); + buttons.push_back(std::make_shared(mWindow, "CANCEL", "cancel", + [&] { delete this; })); - if(mDeleteFunc) - { + if (mDeleteFunc) { auto deleteFileAndSelf = [&] { mDeleteFunc(); delete this; }; - auto deleteBtnFunc = [this, deleteFileAndSelf] { mWindow->pushGui(new GuiMsgBox(mWindow, "THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?", "YES", deleteFileAndSelf, "NO", nullptr)); }; - buttons.push_back(std::make_shared(mWindow, "DELETE", "delete", deleteBtnFunc)); + auto deleteBtnFunc = [this, deleteFileAndSelf] { mWindow->pushGui( + new GuiMsgBox(mWindow, "THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE " + "YOU SURE?", "YES", deleteFileAndSelf, "NO", nullptr)); }; + buttons.push_back(std::make_shared(mWindow, "DELETE", + "delete", deleteBtnFunc)); } mButtons = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtons, Vector2i(0, 2), true, false); - // resize + center - float width = (float)Math::min(Renderer::getScreenHeight(), (int)(Renderer::getScreenWidth() * 0.90f)); + // Resize + center. + float width = (float)Math::min(Renderer::getScreenHeight(), + (int)(Renderer::getScreenWidth() * 0.90f)); setSize(width, Renderer::getScreenHeight() * 0.82f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, + (Renderer::getScreenHeight() - mSize.y()) / 2); } void GuiMetaDataEd::onSizeChanged() @@ -209,7 +240,8 @@ void GuiMetaDataEd::onSizeChanged() const float subtitleHeight = mSubtitle->getFont()->getLetterHeight(); const float titleSubtitleSpacing = mSize.y() * 0.03f; - mGrid.setRowHeightPerc(0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / mSize.y()); + mGrid.setRowHeightPerc(0, (titleHeight + titleSubtitleSpacing + subtitleHeight + + TITLE_VERT_PADDING) / mSize.y()); mGrid.setRowHeightPerc(2, mButtons->getSize().y() / mSize.y()); mHeaderGrid->setRowHeightPerc(1, titleHeight / mHeaderGrid->getSize().y()); @@ -219,24 +251,23 @@ void GuiMetaDataEd::onSizeChanged() void GuiMetaDataEd::save() { - // remove game from index + // Remove game from index. mScraperParams.system->getIndex()->removeFromIndex(mScraperParams.game); - for(unsigned int i = 0; i < mEditors.size(); i++) - { - if(mMetaDataDecl.at(i).isStatistic) + for (unsigned int i = 0; i < mEditors.size(); i++) { + if (mMetaDataDecl.at(i).isStatistic) continue; mMetaData->set(mMetaDataDecl.at(i).key, mEditors.at(i)->getValue()); } - // enter game in index + // Enter game in index. mScraperParams.system->getIndex()->addToIndex(mScraperParams.game); - if(mSavedCallback) + if (mSavedCallback) mSavedCallback(); - // update respective Collection Entries + // Update respective Collection Entries. CollectionSystemManager::get()->refreshCollectionSystems(mScraperParams.game); mScraperParams.system->onMetaDataSavePoint(); @@ -244,15 +275,15 @@ void GuiMetaDataEd::save() void GuiMetaDataEd::fetch() { - GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); + GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, + std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); mWindow->pushGui(scr); } void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result) { - for(unsigned int i = 0; i < mEditors.size(); i++) - { - if(mMetaDataDecl.at(i).isStatistic) + for (unsigned int i = 0; i < mEditors.size(); i++) { + if (mMetaDataDecl.at(i).isStatistic) continue; const std::string& key = mMetaDataDecl.at(i).key; @@ -262,52 +293,50 @@ void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result) void GuiMetaDataEd::close(bool closeAllWindows) { - // find out if the user made any changes + // Find out if the user made any changes. bool dirty = false; - for(unsigned int i = 0; i < mEditors.size(); i++) - { + for (unsigned int i = 0; i < mEditors.size(); i++) { const std::string& key = mMetaDataDecl.at(i).key; - if(mMetaData->get(key) != mEditors.at(i)->getValue()) - { + if (mMetaData->get(key) != mEditors.at(i)->getValue()) { dirty = true; break; } } std::function closeFunc; - if(!closeAllWindows) - { + if (!closeAllWindows) { closeFunc = [this] { delete this; }; - }else{ + } + else { Window* window = mWindow; closeFunc = [window, this] { - while(window->peekGui() != ViewController::get()) + while (window->peekGui() != ViewController::get()) delete window->peekGui(); }; } - if(dirty) + if (dirty) { - // changes were made, ask if the user wants to save them + // Changes were made, ask if the user wants to save them. mWindow->pushGui(new GuiMsgBox(mWindow, "SAVE CHANGES?", "YES", [this, closeFunc] { save(); closeFunc(); }, "NO", closeFunc )); - }else{ + } + else { closeFunc(); } } bool GuiMetaDataEd::input(InputConfig* config, Input input) { - if(GuiComponent::input(config, input)) + if (GuiComponent::input(config, input)) return true; const bool isStart = config->isMappedTo("start", input); - if(input.value != 0 && (config->isMappedTo("b", input) || isStart)) - { + if (input.value != 0 && (config->isMappedTo("b", input) || isStart)) { close(isStart); return true; } diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index b7b7665be..dcc520a38 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -1,3 +1,12 @@ +// +// GuiMetaDataEd.h +// +// Game metadata edit user interface. +// This interface is triggered from the GuiGamelistOptions menu. +// The scraping interface is handled by GuiGameScraper which calls +// ScraperSearchComponent. +// + #pragma once #ifndef ES_APP_GUIS_GUI_META_DATA_ED_H #define ES_APP_GUIS_GUI_META_DATA_ED_H @@ -14,8 +23,13 @@ class TextComponent; class GuiMetaDataEd : public GuiComponent { public: - GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector& mdd, ScraperSearchParams params, - const std::string& header, std::function savedCallback, std::function deleteFunc); + GuiMetaDataEd( + Window* window, + MetaDataList* md, const std::vector&mdd, + ScraperSearchParams params, + const std::string& header, + std::function savedCallback, + std::function deleteFunc); bool input(InputConfig* config, Input input) override; void onSizeChanged() override; diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index 28b87db40..8cfc0383b 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -1,3 +1,12 @@ +// +// GuiScraperMulti.cpp +// +// Multiple game scraping user interface. +// Shows the progress for the scraping as it's running. +// This interface is triggered from the GuiScraperStart menu. +// ScraperSearchComponent is called from here. +// + #include "guis/GuiScraperMulti.h" #include "components/ButtonComponent.h" @@ -11,9 +20,14 @@ #include "SystemData.h" #include "Window.h" -GuiScraperMulti::GuiScraperMulti(Window* window, const std::queue& searches, bool approveResults) : - GuiComponent(window), mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 5)), - mSearchQueue(searches) +GuiScraperMulti::GuiScraperMulti( + Window* window, + const std::queue& searches, + bool approveResults) + : GuiComponent(window), + mBackground(window, ":/frame.png"), + mGrid(window, Vector2i(1, 5)), + mSearchQueue(searches) { assert(mSearchQueue.size()); @@ -28,27 +42,32 @@ GuiScraperMulti::GuiScraperMulti(Window* window, const std::queue(mWindow, "SCRAPING IN PROGRESS", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + // Set up grid. + mTitle = std::make_shared(mWindow, "SCRAPING IN PROGRESS", + Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); - mSystem = std::make_shared(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); + mSystem = std::make_shared(mWindow, "SYSTEM", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mSystem, Vector2i(0, 1), false, true); - mSubtitle = std::make_shared(mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); + mSubtitle = std::make_shared(mWindow, "subtitle text", + Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true); mSearchComp = std::make_shared(mWindow, - approveResults ? ScraperSearchComponent::ALWAYS_ACCEPT_MATCHING_CRC : ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT); - mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1)); + approveResults ? ScraperSearchComponent::ALWAYS_ACCEPT_MATCHING_CRC + : ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT); + mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult, + this, std::placeholders::_1)); mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this)); mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this)); - mGrid.setEntry(mSearchComp, Vector2i(0, 3), mSearchComp->getSearchType() != ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT, true); + mGrid.setEntry(mSearchComp, Vector2i(0, 3), mSearchComp->getSearchType() != + ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT, true); std::vector< std::shared_ptr > buttons; - if(approveResults) - { + if (approveResults) { buttons.push_back(std::make_shared(mWindow, "INPUT", "search", [&] { mSearchComp->openInputScreen(mSearchQueue.front()); mGrid.resetCursor(); @@ -60,21 +79,24 @@ GuiScraperMulti::GuiScraperMulti(Window* window, const std::queue(mWindow, "STOP", "stop (progress saved)", std::bind(&GuiScraperMulti::finish, this))); + buttons.push_back(std::make_shared(mWindow, "STOP", + "stop (progress saved)", std::bind(&GuiScraperMulti::finish, this))); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); setSize(Renderer::getScreenWidth() * 0.95f, Renderer::getScreenHeight() * 0.849f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - + mSize.y()) / 2); doNextSearch(); } GuiScraperMulti::~GuiScraperMulti() { - // view type probably changed (basic -> detailed) - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) + // View type probably changed (basic -> detailed). + for (auto it = SystemData::sSystemVector.cbegin(); + it !=SystemData::sSystemVector.cend(); it++) ViewController::get()->reloadGameListView(*it, false); } @@ -91,19 +113,20 @@ void GuiScraperMulti::onSizeChanged() void GuiScraperMulti::doNextSearch() { - if(mSearchQueue.empty()) - { + if (mSearchQueue.empty()) { finish(); return; } - // update title + // Update title. std::stringstream ss; mSystem->setText(Utils::String::toUpper(mSearchQueue.front().system->getFullName())); - // update subtitle - ss.str(""); // clear - ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << Utils::String::toUpper(Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath())); + // Update subtitle. + ss.str(""); // Clear. + ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << + Utils::String::toUpper(Utils::FileSystem::getFileName( + mSearchQueue.front().game->getPath())); mSubtitle->setText(ss.str()); mSearchComp->search(mSearchQueue.front()); @@ -133,18 +156,20 @@ void GuiScraperMulti::skip() void GuiScraperMulti::finish() { std::stringstream ss; - if(mTotalSuccessful == 0) - { + if (mTotalSuccessful == 0) { ss << "NO GAMES WERE SCRAPED."; - }else{ - ss << mTotalSuccessful << " GAME" << ((mTotalSuccessful > 1) ? "S" : "") << " SUCCESSFULLY SCRAPED!"; + } + else { + ss << mTotalSuccessful << " GAME" << ((mTotalSuccessful > 1) ? "S" : "") << + " SUCCESSFULLY SCRAPED!"; - if(mTotalSkipped > 0) - ss << "\n" << mTotalSkipped << " GAME" << ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED."; + if (mTotalSkipped > 0) + ss << "\n" << mTotalSkipped << " GAME" << ((mTotalSkipped > 1) ? "S" : "") << + " SKIPPED."; } - mWindow->pushGui(new GuiMsgBox(mWindow, ss.str(), - "OK", [&] { delete this; })); + mWindow->pushGui(new GuiMsgBox(mWindow, ss.str(), "OK", [&] { + delete this; })); mIsProcessing = false; PowerSaver::resume(); diff --git a/es-app/src/guis/GuiScraperMulti.h b/es-app/src/guis/GuiScraperMulti.h index 6622f0b8c..e10d1217f 100644 --- a/es-app/src/guis/GuiScraperMulti.h +++ b/es-app/src/guis/GuiScraperMulti.h @@ -1,3 +1,12 @@ +// +// GuiScraperMulti.h +// +// Multiple game scraping user interface. +// Shows the progress for the scraping as it's running. +// This interface is triggered from the GuiScraperStart menu. +// ScraperSearchComponent is called from here. +// + #pragma once #ifndef ES_APP_GUIS_GUI_SCRAPER_MULTI_H #define ES_APP_GUIS_GUI_SCRAPER_MULTI_H @@ -13,7 +22,11 @@ class TextComponent; class GuiScraperMulti : public GuiComponent { public: - GuiScraperMulti(Window* window, const std::queue& searches, bool approveResults); + GuiScraperMulti( + Window* window, + const std::queue& searches, + bool approveResults); + virtual ~GuiScraperMulti(); void onSizeChanged() override; diff --git a/es-app/src/guis/GuiScraperStart.cpp b/es-app/src/guis/GuiScraperStart.cpp index 224204690..e6f63ce39 100644 --- a/es-app/src/guis/GuiScraperStart.cpp +++ b/es-app/src/guis/GuiScraperStart.cpp @@ -1,3 +1,10 @@ +// +// GuiScraperStart.cpp +// +// Submenu to the GuiMenu main menu. +// Configuration options for the scraper and start button to intiate the scraping. +// + #include "guis/GuiScraperStart.h" #include "components/OptionListComponent.h" @@ -13,19 +20,22 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), { addChild(&mMenu); - // add filters (with first one selected) - mFilters = std::make_shared< OptionListComponent >(mWindow, "SCRAPE THESE GAMES", false); + // Add filters (with first one selected). + mFilters = std::make_shared< OptionListComponent + >(mWindow, "SCRAPE THESE GAMES", false); mFilters->add("All Games", [](SystemData*, FileData*) -> bool { return true; }, false); mFilters->add("Only missing image", - [](SystemData*, FileData* g) -> bool { return g->metadata.get("image").empty(); }, true); + [](SystemData*, FileData* g) -> bool { + return g->metadata.get("image").empty(); }, true); mMenu.addWithLabel("Filter", mFilters); - //add systems (all with a platformid specified selected) - mSystems = std::make_shared< OptionListComponent >(mWindow, "SCRAPE THESE SYSTEMS", true); - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) - { - if(!(*it)->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) + // Add systems (all systems with an existing platform ID are listed). + mSystems = std::make_shared< OptionListComponent + >(mWindow, "SCRAPE THESE SYSTEMS", true); + for (auto it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); it++) { + if (!(*it)->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) mSystems->add((*it)->getFullName(), *it, !(*it)->getPlatformIds().empty()); } mMenu.addWithLabel("Systems", mSystems); @@ -37,18 +47,19 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), mMenu.addButton("START", "start", std::bind(&GuiScraperStart::pressedStart, this)); mMenu.addButton("BACK", "back", [&] { delete this; }); - mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, + Renderer::getScreenHeight() * 0.15f); } void GuiScraperStart::pressedStart() { std::vector sys = mSystems->getSelectedObjects(); - for(auto it = sys.cbegin(); it != sys.cend(); it++) - { - if((*it)->getPlatformIds().empty()) - { + for (auto it = sys.cbegin(); it != sys.cend(); it++) { + if ((*it)->getPlatformIds().empty()) { mWindow->pushGui(new GuiMsgBox(mWindow, - Utils::String::toUpper("Warning: some of your selected systems do not have a platform set. Results may be even more inaccurate than usual!\nContinue anyway?"), + Utils::String::toUpper("Warning: some of your selected systems do not " + "have a platform set. Results may be even more inaccurate than " + "usual!\nContinue anyway?"), "YES", std::bind(&GuiScraperStart::start, this), "NO", nullptr)); return; @@ -60,29 +71,28 @@ void GuiScraperStart::pressedStart() void GuiScraperStart::start() { - std::queue searches = getSearches(mSystems->getSelectedObjects(), mFilters->getSelected()); + std::queue searches = getSearches(mSystems->getSelectedObjects(), + mFilters->getSelected()); - if(searches.empty()) - { + if (searches.empty()) { mWindow->pushGui(new GuiMsgBox(mWindow, "NO GAMES FIT THAT CRITERIA.")); - }else{ + } + else { GuiScraperMulti* gsm = new GuiScraperMulti(mWindow, searches, mApproveResults->getState()); mWindow->pushGui(gsm); delete this; } } -std::queue GuiScraperStart::getSearches(std::vector systems, GameFilterFunc selector) +std::queue GuiScraperStart::getSearches( + std::vector systems, GameFilterFunc selector) { std::queue queue; - for(auto sys = systems.cbegin(); sys != systems.cend(); sys++) - { + for (auto sys = systems.cbegin(); sys != systems.cend(); sys++) { std::vector games = (*sys)->getRootFolder()->getFilesRecursive(GAME); - for(auto game = games.cbegin(); game != games.cend(); game++) - { - if(selector((*sys), (*game))) - { + for (auto game = games.cbegin(); game != games.cend(); game++) { + if (selector((*sys), (*game))) { ScraperSearchParams search; search.game = *game; search.system = *sys; @@ -91,31 +101,27 @@ std::queue GuiScraperStart::getSearches(std::vectorisMappedTo("b", input)) - { + if (input.value != 0 && config->isMappedTo("b", input)) { delete this; return true; } - if(config->isMappedTo("start", input) && input.value != 0) - { - // close everything + if (config->isMappedTo("start", input) && input.value != 0) { + // Close everything. Window* window = mWindow; - while(window->peekGui() && window->peekGui() != ViewController::get()) + while (window->peekGui() && window->peekGui() != ViewController::get()) delete window->peekGui(); } - return false; } diff --git a/es-app/src/guis/GuiScraperStart.h b/es-app/src/guis/GuiScraperStart.h index 6c442b9fe..66cb4a0d3 100644 --- a/es-app/src/guis/GuiScraperStart.h +++ b/es-app/src/guis/GuiScraperStart.h @@ -1,3 +1,10 @@ +// +// GuiScraperStart.h +// +// Submenu to the GuiMenu main menu. +// Configuration options for the scraper and start button to intiate the scraping. +// + #pragma once #ifndef ES_APP_GUIS_GUI_SCRAPER_START_H #define ES_APP_GUIS_GUI_SCRAPER_START_H @@ -13,9 +20,10 @@ class SystemData; 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. +// The starting point for a multi-game scrape. +// Allows the user to set various parameters (filters, which systems to scrape and +// whether to use manual mode). Generates a list of "searches" that will be carried +// out by GuiScraperLog. class GuiScraperStart : public GuiComponent { public: @@ -28,7 +36,8 @@ public: private: void pressedStart(); void start(); - std::queue getSearches(std::vector systems, GameFilterFunc selector); + std::queue getSearches( + std::vector systems, GameFilterFunc selector); std::shared_ptr< OptionListComponent > mFilters; std::shared_ptr< OptionListComponent > mSystems; diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 4ced577d1..43096ff7d 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -7,6 +7,8 @@ // Improved and extended by the RetroPie community. // Desktop Edition fork by Leon Styhre. // +// The line length limit is 100 characters. +// // main.cpp // // Main program loop. Interprets command-line arguments, checks for the diff --git a/es-app/src/scrapers/GamesDBJSONScraper.cpp b/es-app/src/scrapers/GamesDBJSONScraper.cpp index bf591a9ab..2d8293ed7 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.cpp +++ b/es-app/src/scrapers/GamesDBJSONScraper.cpp @@ -1,3 +1,10 @@ +// +// GamesDBJSONScraper.cpp +// +// Functions specifically for scraping from thegamesdb.net +// Called from Scraper. +// + #include #include @@ -12,15 +19,14 @@ #include "utils/TimeUtil.h" #include -/* When raspbian will get an up to date version of rapidjson we'll be - able to have it throw in case of error with the following: -#ifndef RAPIDJSON_ASSERT -#define RAPIDJSON_ASSERT(x) \ - if (!(x)) { \ - throw std::runtime_error("rapidjson internal assertion failure: " #x); \ - } -#endif // RAPIDJSON_ASSERT -*/ +// When raspbian will get an up to date version of rapidjson we'll be +// able to have it throw in case of error with the following: +//ifndef RAPIDJSON_ASSERT +//#define RAPIDJSON_ASSERT(x) \ +// if (!(x)) { \ +// throw std::runtime_error("rapidjson internal assertion failure: " #x); \ +// } +//#endif // RAPIDJSON_ASSERT #include #include @@ -28,12 +34,11 @@ using namespace PlatformIds; using namespace rapidjson; -namespace -{ +namespace { TheGamesDBJSONRequestResources resources; } -const std::map gamesdb_new_platformid_map{ +const std::map gamesdb_new_platformid_map { { THREEDO, "25" }, { AMIGA, "4911" }, { AMSTRAD_CPC, "4914" }, @@ -88,8 +93,8 @@ const std::map gamesdb_new_platformid_map{ { PLAYSTATION_VITA, "39" }, { PLAYSTATION_PORTABLE, "13" }, { SUPER_NINTENDO, "6" }, - { TURBOGRAFX_16, "34" }, // HuCards only - { TURBOGRAFX_CD, "4955" }, // CD-ROMs only + { TURBOGRAFX_16, "34" }, // HuCards only. + { TURBOGRAFX_CD, "4955" }, // CD-ROMs only. { WONDERSWAN, "4925" }, { WONDERSWAN_COLOR, "4926" }, { ZX_SPECTRUM, "4913" }, @@ -99,16 +104,17 @@ const std::map gamesdb_new_platformid_map{ { TANDY, "4941" }, }; -void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, - std::queue>& requests, std::vector& results) +void thegamesdb_generate_json_scraper_requests( + const ScraperSearchParams& params, + std::queue>& requests, + std::vector& results) { resources.prepare(); std::string path = "https://api.thegamesdb.net/v1"; bool usingGameID = false; const std::string apiKey = std::string("apikey=") + resources.getApiKey(); std::string cleanName = params.nameOverride; - if (!cleanName.empty() && cleanName.substr(0, 3) == "id:") - { + if (!cleanName.empty() && cleanName.substr(0, 3) == "id:") { std::string gameID = cleanName.substr(3); path += "/Games/ByGameID?" + apiKey + "&fields=players,publishers,genres,overview,last_updated,rating," @@ -116,42 +122,37 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params "include=boxart&id=" + HttpReq::urlEncode(gameID); usingGameID = true; - } else - { + } + else { if (cleanName.empty()) cleanName = params.game->getCleanName(); - path += "/Games/ByGameName?" + apiKey + - "&fields=players,publishers,genres,overview,last_updated,rating," - "platform,coop,youtube,os,processor,ram,hdd,video,sound,alternates&" - "include=boxart&name=" + - HttpReq::urlEncode(cleanName); + path += "/Games/ByGameName?" + apiKey + + "&fields=players,publishers,genres,overview,last_updated,rating," + "platform,coop,youtube,os,processor,ram,hdd,video,sound,alternates&" + "include=boxart&name=" + + HttpReq::urlEncode(cleanName); } - if (usingGameID) - { - // if we have the ID already, we don't need the GetGameList request + if (usingGameID) { + // Ff we have the ID already, we don't need the GetGameList request. requests.push(std::unique_ptr(new TheGamesDBJSONRequest(results, path))); - } else - { + } + else { std::string platformQueryParam; auto& platforms = params.system->getPlatformIds(); - if (!platforms.empty()) - { + if (!platforms.empty()) { bool first = true; platformQueryParam += "&filter%5Bplatform%5D="; - for (auto platformIt = platforms.cbegin(); platformIt != platforms.cend(); platformIt++) - { + for (auto platformIt = platforms.cbegin(); + platformIt != platforms.cend(); platformIt++) { auto mapIt = gamesdb_new_platformid_map.find(*platformIt); - if (mapIt != gamesdb_new_platformid_map.cend()) - { + if (mapIt != gamesdb_new_platformid_map.cend()) { if (!first) - { platformQueryParam += ","; - } platformQueryParam += HttpReq::urlEncode(mapIt->second); first = false; - } else - { + } + else { LOG(LogWarning) << "TheGamesDB scraper warning - no support for platform " << getPlatformName(*platformIt); } @@ -159,7 +160,8 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params path += platformQueryParam; } - requests.push(std::unique_ptr(new TheGamesDBJSONRequest(requests, results, path))); + requests.push(std::unique_ptr + (new TheGamesDBJSONRequest(requests, results, path))); } } @@ -168,26 +170,25 @@ namespace std::string getStringOrThrow(const Value& v, const std::string& key) { - if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) - { - throw std::runtime_error("rapidjson internal assertion failure: missing or non string key:" + key); + if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) { + throw std::runtime_error( + "rapidjson internal assertion failure: missing or non string key:" + key); } return v[key.c_str()].GetString(); } int getIntOrThrow(const Value& v, const std::string& key) { - if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) - { - throw std::runtime_error("rapidjson internal assertion failure: missing or non int key:" + key); + if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) { + throw std::runtime_error( + "rapidjson internal assertion failure: missing or non int key:" + key); } return v[key.c_str()].GetInt(); } int getIntOrThrow(const Value& v) { - if (!v.IsInt()) - { + if (!v.IsInt()) { throw std::runtime_error("rapidjson internal assertion failure: not an int"); } return v.GetInt(); @@ -196,18 +197,14 @@ int getIntOrThrow(const Value& v) std::string getBoxartImage(const Value& v) { if (!v.IsArray() || v.Size() == 0) - { return ""; - } - for (int i = 0; i < (int)v.Size(); ++i) - { + + for (int i = 0; i < (int)v.Size(); ++i) { auto& im = v[i]; std::string type = getStringOrThrow(im, "type"); std::string side = getStringOrThrow(im, "side"); if (type == "boxart" && side == "front") - { return getStringOrThrow(im, "filename"); - } } return getStringOrThrow(v[0], "filename"); } @@ -215,22 +212,19 @@ std::string getBoxartImage(const Value& v) std::string getDeveloperString(const Value& v) { if (!v.IsArray()) - { return ""; - } + std::string out = ""; bool first = true; - for (int i = 0; i < (int)v.Size(); ++i) - { + for (int i = 0; i < (int)v.Size(); ++i) { auto mapIt = resources.gamesdb_new_developers_map.find(getIntOrThrow(v[i])); + if (mapIt == resources.gamesdb_new_developers_map.cend()) - { continue; - } + if (!first) - { out += ", "; - } + out += mapIt->second; first = false; } @@ -240,22 +234,19 @@ std::string getDeveloperString(const Value& v) std::string getPublisherString(const Value& v) { if (!v.IsArray()) - { return ""; - } + std::string out = ""; bool first = true; - for (int i = 0; i < (int)v.Size(); ++i) - { + for (int i = 0; i < (int)v.Size(); ++i) { auto mapIt = resources.gamesdb_new_publishers_map.find(getIntOrThrow(v[i])); + if (mapIt == resources.gamesdb_new_publishers_map.cend()) - { continue; - } + if (!first) - { out += ", "; - } + out += mapIt->second; first = false; } @@ -265,22 +256,18 @@ std::string getPublisherString(const Value& v) std::string getGenreString(const Value& v) { if (!v.IsArray()) - { return ""; - } + std::string out = ""; bool first = true; - for (int i = 0; i < (int)v.Size(); ++i) - { + for (int i = 0; i < (int)v.Size(); ++i) { auto mapIt = resources.gamesdb_new_genres_map.find(getIntOrThrow(v[i])); if (mapIt == resources.gamesdb_new_genres_map.cend()) - { continue; - } + if (!first) - { out += ", "; - } + out += mapIt->second; first = false; } @@ -296,38 +283,27 @@ void processGame(const Value& game, const Value& boxart, std::vector& req, std::vector& results) +void TheGamesDBJSONRequest::process(const std::unique_ptr& req, + std::vector& results) { assert(req->status() == HttpReq::REQ_SUCCESS); Document doc; doc.Parse(req->getContent().c_str()); - if (doc.HasParseError()) - { + if (doc.HasParseError()) { std::string err = - std::string("TheGamesDBJSONRequest - Error parsing JSON. \n\t") + GetParseError_En(doc.GetParseError()); + std::string("TheGamesDBJSONRequest - Error parsing JSON. \n\t") + + GetParseError_En(doc.GetParseError()); setError(err); LOG(LogError) << err; return; } - if (!doc.HasMember("data") || !doc["data"].HasMember("games") || !doc["data"]["games"].IsArray()) - { + if (!doc.HasMember("data") || !doc["data"].HasMember("games") || + !doc["data"]["games"].IsArray()) { std::string warn = "TheGamesDBJSONRequest - Response had no game data.\n"; LOG(LogWarning) << warn; return; } const Value& games = doc["data"]["games"]; - if (!doc.HasMember("include") || !doc["include"].HasMember("boxart")) - { + if (!doc.HasMember("include") || !doc["include"].HasMember("boxart")) { std::string warn = "TheGamesDBJSONRequest - Response had no include boxart data.\n"; LOG(LogWarning) << warn; return; @@ -371,8 +347,7 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr& req, std::ve const Value& boxart = doc["include"]["boxart"]; - if (!boxart.HasMember("base_url") || !boxart.HasMember("data") || !boxart.IsObject()) - { + if (!boxart.HasMember("base_url") || !boxart.HasMember("data") || !boxart.IsObject()) { std::string warn = "TheGamesDBJSONRequest - Response include had no usable boxart data.\n"; LOG(LogWarning) << warn; return; @@ -380,16 +355,12 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr& req, std::ve resources.ensureResources(); - - for (int i = 0; i < (int)games.Size(); ++i) - { + for (int i = 0; i < (int)games.Size(); ++i) { auto& v = games[i]; - try - { + try { processGame(v, boxart, results); } - catch (std::runtime_error& e) - { + catch (std::runtime_error& e) { LOG(LogError) << "Error while processing game: " << e.what(); } } diff --git a/es-app/src/scrapers/GamesDBJSONScraper.h b/es-app/src/scrapers/GamesDBJSONScraper.h index 505dbcf2b..b6bd449bb 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.h +++ b/es-app/src/scrapers/GamesDBJSONScraper.h @@ -1,34 +1,49 @@ +// +// GamesDBJSONScraper.h +// +// Functions specifically for scraping from thegamesdb.net +// Called from Scraper. +// + #pragma once #ifndef ES_APP_SCRAPERS_GAMES_DB_JSON_SCRAPER_H #define ES_APP_SCRAPERS_GAMES_DB_JSON_SCRAPER_H #include "scrapers/Scraper.h" -namespace pugi -{ +namespace pugi { class xml_document; } -void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, - std::queue>& requests, std::vector& results); +void thegamesdb_generate_json_scraper_requests( + const ScraperSearchParams& params, + std::queue>& requests, + std::vector& results); class TheGamesDBJSONRequest : public ScraperHttpRequest { public: - // ctor for a GetGameList request - TheGamesDBJSONRequest(std::queue>& requestsWrite, - std::vector& resultsWrite, const std::string& url) - : ScraperHttpRequest(resultsWrite, url), mRequestQueue(&requestsWrite) + // ctor for a GetGameList request. + TheGamesDBJSONRequest( + std::queue>& requestsWrite, + std::vector& resultsWrite, + const std::string& url) + : ScraperHttpRequest(resultsWrite, url), + mRequestQueue(&requestsWrite) { } // ctor for a GetGame request - TheGamesDBJSONRequest(std::vector& resultsWrite, const std::string& url) - : ScraperHttpRequest(resultsWrite, url), mRequestQueue(nullptr) + TheGamesDBJSONRequest( + std::vector& resultsWrite, + const std::string& url) + : ScraperHttpRequest(resultsWrite, url), + mRequestQueue(nullptr) { } protected: - void process(const std::unique_ptr& req, std::vector& results) override; + void process(const std::unique_ptr& req, + std::vector& results) override; bool isGameRequest() { return !mRequestQueue; } std::queue>* mRequestQueue; diff --git a/es-app/src/scrapers/GamesDBJSONScraperResources.cpp b/es-app/src/scrapers/GamesDBJSONScraperResources.cpp index 517fd3369..ae3606ec1 100644 --- a/es-app/src/scrapers/GamesDBJSONScraperResources.cpp +++ b/es-app/src/scrapers/GamesDBJSONScraperResources.cpp @@ -1,3 +1,10 @@ +// +// GamesDBJSONScraperResources.cpp +// +// Functions specifically for scraping from thegamesdb.net +// Called from GamesDBJSONScraper. +// + #include #include #include @@ -8,17 +15,14 @@ #include "scrapers/GamesDBJSONScraperResources.h" #include "utils/FileSystemUtil.h" - #include #include using namespace rapidjson; - -namespace -{ -constexpr char GamesDBAPIKey[] = "445fcbc3f32bb2474bc27016b99eb963d318ee3a608212c543b9a79de1041600"; - +namespace { +constexpr char GamesDBAPIKey[] = + "445fcbc3f32bb2474bc27016b99eb963d318ee3a608212c543b9a79de1041600"; constexpr int MAX_WAIT_MS = 90000; constexpr int POLL_TIME_MS = 500; @@ -46,73 +50,57 @@ void ensureScrapersResourcesDir() } // namespace - std::string getScrapersResouceDir() { return Utils::FileSystem::getGenericPath( - Utils::FileSystem::getHomePath() + "/.emulationstation/" + SCRAPER_RESOURCES_DIR); + Utils::FileSystem::getHomePath() + "/.emulationstation/" + SCRAPER_RESOURCES_DIR); } std::string TheGamesDBJSONRequestResources::getApiKey() const { return GamesDBAPIKey; } - void TheGamesDBJSONRequestResources::prepare() { if (checkLoaded()) - { return; - } - if (loadResource(gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE)) && - !gamesdb_developers_resource_request) - { + if (loadResource(gamesdb_new_developers_map, "developers", + genFilePath(DEVELOPERS_JSON_FILE)) && !gamesdb_developers_resource_request) gamesdb_developers_resource_request = fetchResource(DEVELOPERS_ENDPOINT); - } - if (loadResource(gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE)) && - !gamesdb_publishers_resource_request) - { + + if (loadResource(gamesdb_new_publishers_map, "publishers", + genFilePath(PUBLISHERS_JSON_FILE)) && !gamesdb_publishers_resource_request) gamesdb_publishers_resource_request = fetchResource(PUBLISHERS_ENDPOINT); - } - if (loadResource(gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE)) && !gamesdb_genres_resource_request) - { + + if (loadResource(gamesdb_new_genres_map, "genres", + genFilePath(GENRES_JSON_FILE)) && !gamesdb_genres_resource_request) gamesdb_genres_resource_request = fetchResource(GENRES_ENDPOINT); - } } void TheGamesDBJSONRequestResources::ensureResources() { - if (checkLoaded()) - { return; - } + for (int i = 0; i < MAX_WAIT_ITER; ++i) { - for (int i = 0; i < MAX_WAIT_ITER; ++i) - { if (gamesdb_developers_resource_request && - saveResource(gamesdb_developers_resource_request.get(), gamesdb_new_developers_map, "developers", - genFilePath(DEVELOPERS_JSON_FILE))) - { - + saveResource(gamesdb_developers_resource_request.get(), + gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE))) gamesdb_developers_resource_request.reset(nullptr); - } - if (gamesdb_publishers_resource_request && - saveResource(gamesdb_publishers_resource_request.get(), gamesdb_new_publishers_map, "publishers", - genFilePath(PUBLISHERS_JSON_FILE))) - { - gamesdb_publishers_resource_request.reset(nullptr); - } - if (gamesdb_genres_resource_request && saveResource(gamesdb_genres_resource_request.get(), gamesdb_new_genres_map, - "genres", genFilePath(GENRES_JSON_FILE))) - { - gamesdb_genres_resource_request.reset(nullptr); - } - if (!gamesdb_developers_resource_request && !gamesdb_publishers_resource_request && !gamesdb_genres_resource_request) - { + if (gamesdb_publishers_resource_request && + saveResource(gamesdb_publishers_resource_request.get(), + gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE))) + gamesdb_publishers_resource_request.reset(nullptr); + + if (gamesdb_genres_resource_request && saveResource(gamesdb_genres_resource_request.get(), + gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE))) + gamesdb_genres_resource_request.reset(nullptr); + + if (!gamesdb_developers_resource_request && !gamesdb_publishers_resource_request && + !gamesdb_genres_resource_request) return; - } + std::this_thread::sleep_for(std::chrono::milliseconds(POLL_TIME_MS)); } LOG(LogError) << "Timed out while waiting for resources\n"; @@ -120,26 +108,28 @@ void TheGamesDBJSONRequestResources::ensureResources() bool TheGamesDBJSONRequestResources::checkLoaded() { - return !gamesdb_new_genres_map.empty() && !gamesdb_new_developers_map.empty() && !gamesdb_new_publishers_map.empty(); + return !gamesdb_new_genres_map.empty() && !gamesdb_new_developers_map.empty() && + !gamesdb_new_publishers_map.empty(); } -bool TheGamesDBJSONRequestResources::saveResource(HttpReq* req, std::unordered_map& resource, - const std::string& resource_name, const std::string& file_name) +bool TheGamesDBJSONRequestResources::saveResource( + HttpReq* req, + std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name) { - if (req == nullptr) - { + if (req == nullptr) { LOG(LogError) << "Http request pointer was null\n"; return true; } - if (req->status() == HttpReq::REQ_IN_PROGRESS) - { - return false; // Not ready: wait some more + if (req->status() == HttpReq::REQ_IN_PROGRESS) { + return false; // Not ready: wait some more. } - if (req->status() != HttpReq::REQ_SUCCESS) - { - LOG(LogError) << "Resource request for " << file_name << " failed:\n\t" << req->getErrorMsg(); - return true; // Request failed, resetting request. + if (req->status() != HttpReq::REQ_SUCCESS) { + LOG(LogError) << "Resource request for " << file_name << + " failed:\n\t" << req->getErrorMsg(); + return true; // Request failed, resetting request.. } ensureScrapersResourcesDir(); @@ -160,47 +150,42 @@ std::unique_ptr TheGamesDBJSONRequestResources::fetchResource(const std return std::unique_ptr(new HttpReq(path)); } - int TheGamesDBJSONRequestResources::loadResource( - std::unordered_map& resource, const std::string& resource_name, const std::string& file_name) + std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name) { - - std::ifstream fin(file_name); if (!fin.good()) - { return 1; - } + std::stringstream buffer; buffer << fin.rdbuf(); Document doc; doc.Parse(buffer.str().c_str()); - if (doc.HasParseError()) - { - std::string err = std::string("TheGamesDBJSONRequest - Error parsing JSON for resource file ") + file_name + - ":\n\t" + GetParseError_En(doc.GetParseError()); + if (doc.HasParseError()) { + std::string err = std::string("TheGamesDBJSONRequest - " + "Error parsing JSON for resource file ") + file_name + + ":\n\t" + GetParseError_En(doc.GetParseError()); LOG(LogError) << err; return 1; } if (!doc.HasMember("data") || !doc["data"].HasMember(resource_name.c_str()) || - !doc["data"][resource_name.c_str()].IsObject()) - { + !doc["data"][resource_name.c_str()].IsObject()) { std::string err = "TheGamesDBJSONRequest - Response had no resource data.\n"; LOG(LogError) << err; return 1; } auto& data = doc["data"][resource_name.c_str()]; - for (Value::ConstMemberIterator itr = data.MemberBegin(); itr != data.MemberEnd(); ++itr) - { + for (Value::ConstMemberIterator itr = data.MemberBegin(); itr != data.MemberEnd(); ++itr) { auto& entry = itr->value; - if (!entry.IsObject() || !entry.HasMember("id") || !entry["id"].IsInt() || !entry.HasMember("name") || - !entry["name"].IsString()) - { + if (!entry.IsObject() || !entry.HasMember("id") || !entry["id"].IsInt() || + !entry.HasMember("name") || !entry["name"].IsString()) continue; - } + resource[entry["id"].GetInt()] = entry["name"].GetString(); } return resource.empty(); diff --git a/es-app/src/scrapers/GamesDBJSONScraperResources.h b/es-app/src/scrapers/GamesDBJSONScraperResources.h index 4cbbcc587..da45eb3ee 100644 --- a/es-app/src/scrapers/GamesDBJSONScraperResources.h +++ b/es-app/src/scrapers/GamesDBJSONScraperResources.h @@ -1,3 +1,10 @@ +// +// GamesDBJSONScraperResources.h +// +// Functions specifically for scraping from thegamesdb.net +// Called from GamesDBJSONScraper. +// + #pragma once #ifndef ES_APP_SCRAPERS_GAMES_DB_JSON_SCRAPER_RESOURCES_H #define ES_APP_SCRAPERS_GAMES_DB_JSON_SCRAPER_RESOURCES_H @@ -9,9 +16,7 @@ #include "HttpReq.h" - -struct TheGamesDBJSONRequestResources -{ +struct TheGamesDBJSONRequestResources { TheGamesDBJSONRequestResources() = default; void prepare(); @@ -25,12 +30,17 @@ struct TheGamesDBJSONRequestResources private: bool checkLoaded(); - bool saveResource(HttpReq* req, std::unordered_map& resource, const std::string& resource_name, - const std::string& file_name); - std::unique_ptr fetchResource(const std::string& endpoint); + bool saveResource( + HttpReq* req, + std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name); + std::unique_ptr fetchResource(const std::string& endpoint); int loadResource( - std::unordered_map& resource, const std::string& resource_name, const std::string& file_name); + std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name); std::unique_ptr gamesdb_developers_resource_request; std::unique_ptr gamesdb_publishers_resource_request; @@ -39,4 +49,4 @@ struct TheGamesDBJSONRequestResources std::string getScrapersResouceDir(); -#endif // ES_APP_SCRAPERS_GAMES_DB_JSON_SCRAPER_H +#endif // ES_APP_SCRAPERS_GAMES_DB_JSON_SCRAPER_RESOURCES_H diff --git a/es-app/src/scrapers/Scraper.cpp b/es-app/src/scrapers/Scraper.cpp index bc3e6b00d..852cb1073 100644 --- a/es-app/src/scrapers/Scraper.cpp +++ b/es-app/src/scrapers/Scraper.cpp @@ -1,3 +1,11 @@ +// +// Scraper.cpp +// +// Main scraper logic. +// Called from ScraperSearchComponent. +// Calls either GamesDBJSONScraper or ScreenScraper. +// + #include "scrapers/Scraper.h" #include "FileData.h" @@ -19,15 +27,11 @@ std::unique_ptr startScraperSearch(const ScraperSearchParam const std::string& name = Settings::getInstance()->getString("Scraper"); std::unique_ptr handle(new ScraperSearchHandle()); - // Check if the Scraper in the settings still exists as a registered scraping source. + // Check if the scraper in the settings still exists as a registered scraping source. if (scraper_request_funcs.find(name) == scraper_request_funcs.end()) - { LOG(LogWarning) << "Configured scraper (" << name << ") unavailable, scraping aborted."; - } else - { scraper_request_funcs.at(name)(params, handle->mRequestQueue, handle->mResults); - } return handle; } @@ -36,9 +40,7 @@ std::vector getScraperList() { std::vector list; for(auto it = scraper_request_funcs.cbegin(); it != scraper_request_funcs.cend(); it++) - { list.push_back(it->first); - } return list; } @@ -49,7 +51,7 @@ bool isValidConfiguredScraper() return scraper_request_funcs.find(name) != scraper_request_funcs.end(); } -// ScraperSearchHandle +// ScraperSearchHandle. ScraperSearchHandle::ScraperSearchHandle() { setStatus(ASYNC_IN_PROGRESS); @@ -62,51 +64,45 @@ void ScraperSearchHandle::update() if(!mRequestQueue.empty()) { - // a request can add more requests to the queue while running, - // so be careful with references into the queue + // A request can add more requests to the queue while running, + // so be careful with references into the queue. auto& req = *(mRequestQueue.front()); AsyncHandleStatus status = req.status(); - if(status == ASYNC_ERROR) - { - // propegate error + if(status == ASYNC_ERROR) { + // Propagate error. setError(req.getStatusString()); - // empty our queue + // Empty our queue. while(!mRequestQueue.empty()) mRequestQueue.pop(); return; } - // finished this one, see if we have any more + // Finished this one, see if we have any more. if(status == ASYNC_DONE) - { mRequestQueue.pop(); - } - // status == ASYNC_IN_PROGRESS + // Status == ASYNC_IN_PROGRESS. } - // we finished without any errors! - if(mRequestQueue.empty()) - { + // We finished without any errors! + if(mRequestQueue.empty()) { setStatus(ASYNC_DONE); return; } } - - -// ScraperRequest -ScraperRequest::ScraperRequest(std::vector& resultsWrite) : mResults(resultsWrite) +// ScraperRequest. +ScraperRequest::ScraperRequest(std::vector& resultsWrite) + : mResults(resultsWrite) { } - -// ScraperHttpRequest -ScraperHttpRequest::ScraperHttpRequest(std::vector& resultsWrite, const std::string& url) - : ScraperRequest(resultsWrite) +// ScraperHttpRequest. +ScraperHttpRequest::ScraperHttpRequest(std::vector& + resultsWrite, const std::string& url) : ScraperRequest(resultsWrite) { setStatus(ASYNC_IN_PROGRESS); mReq = std::unique_ptr(new HttpReq(url)); @@ -117,41 +113,43 @@ void ScraperHttpRequest::update() HttpReq::Status status = mReq->status(); if(status == HttpReq::REQ_SUCCESS) { - setStatus(ASYNC_DONE); // if process() has an error, status will be changed to ASYNC_ERROR + // If process() has an error, status will be changed to ASYNC_ERROR. + setStatus(ASYNC_DONE); process(mReq, mResults); return; } - // not ready yet + // Not ready yet. if(status == HttpReq::REQ_IN_PROGRESS) return; - // everything else is some sort of error - LOG(LogError) << "ScraperHttpRequest network error (status: " << status << ") - " << mReq->getErrorMsg(); + // Everything else is some sort of error. + LOG(LogError) << "ScraperHttpRequest network error (status: " << status<< ") - " + << mReq->getErrorMsg(); setError(mReq->getErrorMsg()); } +// Metadata resolving stuff. -// metadata resolving stuff - -std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search) +std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, + const ScraperSearchParams& search) { return std::unique_ptr(new MDResolveHandle(result, search)); } -MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search) : mResult(result) +MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, + const ScraperSearchParams& search) : mResult(result) { - if(!result.imageUrl.empty()) - { + if(!result.imageUrl.empty()) { std::string ext; // If we have a file extension returned by the scraper, then use it. // Otherwise, try to guess it by the name of the URL, which point to an image. - if (!result.imageType.empty()) - { + if (!result.imageType.empty()) { ext = result.imageType; - }else{ + } + else { size_t dot = result.imageUrl.find_last_of('.'); if (dot != std::string::npos) @@ -160,8 +158,8 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, const Scrape std::string imgPath = getSaveAsPath(search, "image", ext); - mFuncs.push_back(ResolvePair(downloadImageAsync(result.imageUrl, imgPath), [this, imgPath] - { + mFuncs.push_back(ResolvePair(downloadImageAsync(result.imageUrl, imgPath), + [this, imgPath] { mResult.mdl.set("image", imgPath); mResult.imageUrl = ""; })); @@ -174,14 +172,12 @@ void MDResolveHandle::update() return; auto it = mFuncs.cbegin(); - while(it != mFuncs.cend()) - { - if(it->first->status() == ASYNC_ERROR) - { + while(it != mFuncs.cend()) { + if(it->first->status() == ASYNC_ERROR) { setError(it->first->getStatusString()); return; - }else if(it->first->status() == ASYNC_DONE) - { + } + else if(it->first->status() == ASYNC_DONE) { it->second(); it = mFuncs.erase(it); continue; @@ -193,14 +189,17 @@ void MDResolveHandle::update() setStatus(ASYNC_DONE); } -std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs) +std::unique_ptr downloadImageAsync(const std::string& url, + const std::string& saveAs) { return std::unique_ptr(new ImageDownloadHandle(url, saveAs, - Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight"))); + Settings::getInstance()->getInt("ScraperResizeWidth"), + Settings::getInstance()->getInt("ScraperResizeHeight"))); } -ImageDownloadHandle::ImageDownloadHandle(const std::string& url, const std::string& path, int maxWidth, int maxHeight) : - mSavePath(path), mMaxWidth(maxWidth), mMaxHeight(maxHeight), mReq(new HttpReq(url)) +ImageDownloadHandle::ImageDownloadHandle(const std::string& url, + const std::string& path, int maxWidth, int maxHeight) : mSavePath(path), + mMaxWidth(maxWidth), mMaxHeight(maxHeight), mReq(new HttpReq(url)) { } @@ -209,18 +208,16 @@ void ImageDownloadHandle::update() if(mReq->status() == HttpReq::REQ_IN_PROGRESS) return; - if(mReq->status() != HttpReq::REQ_SUCCESS) - { + if(mReq->status() != HttpReq::REQ_SUCCESS) { std::stringstream ss; ss << "Network error: " << mReq->getErrorMsg(); setError(ss.str()); return; } - // download is done, save it to disk + // Download is done, save it to disk. std::ofstream stream(mSavePath, std::ios_base::out | std::ios_base::binary); - if(stream.bad()) - { + if(stream.bad()) { setError("Failed to open image path to write. Permission error? Disk full?"); return; } @@ -228,15 +225,13 @@ void ImageDownloadHandle::update() const std::string& content = mReq->getContent(); stream.write(content.data(), content.length()); stream.close(); - if(stream.bad()) - { + if(stream.bad()) { setError("Failed to save image. Disk full?"); return; } - // resize it - if(!resizeImage(mSavePath, mMaxWidth, mMaxHeight)) - { + // Resize it. + if(!resizeImage(mSavePath, mMaxWidth, mMaxHeight)) { setError("Error saving resized image. Out of memory? Disk full?"); return; } @@ -244,31 +239,30 @@ void ImageDownloadHandle::update() setStatus(ASYNC_DONE); } -//you can pass 0 for width or height to keep aspect ratio +// You can pass 0 for width or height to keep aspect ratio. bool resizeImage(const std::string& path, int maxWidth, int maxHeight) { - // nothing to do + // Nothing to do. if(maxWidth == 0 && maxHeight == 0) return true; FREE_IMAGE_FORMAT format = FIF_UNKNOWN; FIBITMAP* image = NULL; - //detect the filetype + // Detect the filetype. format = FreeImage_GetFileType(path.c_str(), 0); if(format == FIF_UNKNOWN) format = FreeImage_GetFIFFromFilename(path.c_str()); - if(format == FIF_UNKNOWN) - { + if(format == FIF_UNKNOWN) { LOG(LogError) << "Error - could not detect filetype for image \"" << path << "\"!"; return false; } - //make sure we can read this filetype first, then load it - if(FreeImage_FIFSupportsReading(format)) - { + // Make sure we can read this filetype first, then load it. + if(FreeImage_FIFSupportsReading(format)) { image = FreeImage_Load(format, path.c_str()); - }else{ + } + else { LOG(LogError) << "Error - file format reading not supported for image \"" << path << "\"!"; return false; } @@ -277,18 +271,14 @@ bool resizeImage(const std::string& path, int maxWidth, int maxHeight) float height = (float)FreeImage_GetHeight(image); if(maxWidth == 0) - { maxWidth = (int)((maxHeight / height) * width); - }else if(maxHeight == 0) - { + else if(maxHeight == 0) maxHeight = (int)((maxWidth / width) * height); - } FIBITMAP* imageRescaled = FreeImage_Rescale(image, maxWidth, maxHeight, FILTER_BILINEAR); FreeImage_Unload(image); - if(imageRescaled == NULL) - { + if(imageRescaled == NULL) { LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)"; return false; } @@ -302,7 +292,8 @@ bool resizeImage(const std::string& path, int maxWidth, int maxHeight) return saved; } -std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& extension) +std::string getSaveAsPath(const ScraperSearchParams& params, + const std::string& suffix, const std::string& extension) { const std::string subdirectory = params.system->getName(); const std::string name = Utils::FileSystem::getStem(params.game->getPath()) + "-" + suffix; @@ -317,7 +308,6 @@ std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& if(!Utils::FileSystem::exists(path)) Utils::FileSystem::createDirectory(path); - path += name + extension; return path; } diff --git a/es-app/src/scrapers/Scraper.h b/es-app/src/scrapers/Scraper.h index b86fc9080..eea7eee05 100644 --- a/es-app/src/scrapers/Scraper.h +++ b/es-app/src/scrapers/Scraper.h @@ -1,3 +1,11 @@ +// +// Scraper.h +// +// Main scraper logic. +// Called from ScraperSearchComponent. +// Calls either GamesDBJSONScraper or ScreenScraper. +// + #pragma once #ifndef ES_APP_SCRAPERS_SCRAPER_H #define ES_APP_SCRAPERS_SCRAPER_H @@ -16,31 +24,29 @@ class FileData; class SystemData; -struct ScraperSearchParams -{ +struct ScraperSearchParams { SystemData* system; FileData* game; std::string nameOverride; }; -struct ScraperSearchResult -{ +struct ScraperSearchResult { ScraperSearchResult() : mdl(GAME_METADATA) {}; MetaDataList mdl; std::string imageUrl; std::string thumbnailUrl; - // Needed to pre-set the image type + // Needed to pre-set the image type. std::string imageType; }; // So let me explain why I've abstracted this so heavily. // There are two ways I can think of that you'd want to write a scraper. -// 1. Do some HTTP request(s) -> process it -> return the results -// 2. Do some local filesystem queries (an offline scraper) -> return the results +// 1. Do some HTTP request(s) -> process it -> return the results. +// 2. Do some local filesystem queries (an offline scraper) -> return the results. // The first way needs to be asynchronous while it's waiting for the HTTP request to return. // The second doesn't. @@ -51,25 +57,26 @@ struct ScraperSearchResult // ... process search ... // return results; -// We could do this if we used threads. Right now ES doesn't because I'm pretty sure I'll fuck it up, -// and I'm not sure of the performance of threads on the Pi (single-core ARM). +// We could do this if we used threads. Right now ES doesn't because I'm pretty sure I'll +// fuck it up, and I'm not sure of the performance of threads on the Pi (single-core ARM). // We could also do this if we used coroutines. // I can't find a really good cross-platform coroutine library (x86/64/ARM Linux + Windows), // and I don't want to spend more time chasing libraries than just writing it the long way once. // So, I did it the "long" way. -// ScraperSearchHandle - one logical search, e.g. "search for mario" -// ScraperRequest - encapsulates some sort of asynchronous request that will ultimately return some results -// ScraperHttpRequest - implementation of ScraperRequest that waits on an HttpReq, then processes it with some processing function. +// ScraperSearchHandle - one logical search, e.g. "search for mario". +// ScraperRequest - encapsulates some sort of asynchronous request that will ultimately +// return some results. +// ScraperHttpRequest - implementation of ScraperRequest that waits on an HttpReq, then +// processes it with some processing function. - -// a scraper search gathers results from (potentially multiple) ScraperRequests +// A scraper search gathers results from (potentially multiple) ScraperRequests. class ScraperRequest : public AsyncHandle { public: ScraperRequest(std::vector& resultsWrite); - // returns "true" once we're done + // Returns "true" once we're done. virtual void update() = 0; protected: @@ -77,7 +84,7 @@ protected: }; -// a single HTTP request that needs to be processed to get the results +// A single HTTP request that needs to be processed to get the results. class ScraperHttpRequest : public ScraperRequest { public: @@ -85,43 +92,46 @@ public: virtual void update() override; protected: - virtual void process(const std::unique_ptr& req, std::vector& results) = 0; + virtual void process(const std::unique_ptr& req, + std::vector& results) = 0; private: std::unique_ptr mReq; }; -// a request to get a list of results +// A request to get a list of results. class ScraperSearchHandle : public AsyncHandle { public: ScraperSearchHandle(); void update(); - inline const std::vector& getResults() const { assert(mStatus != ASYNC_IN_PROGRESS); return mResults; } + inline const std::vector& getResults() const { + assert(mStatus != ASYNC_IN_PROGRESS); return mResults; } protected: - friend std::unique_ptr startScraperSearch(const ScraperSearchParams& params); + friend std::unique_ptr + startScraperSearch(const ScraperSearchParams& params); std::queue< std::unique_ptr > mRequestQueue; std::vector mResults; }; -// will use the current scraper settings to pick the result source +// Will use the current scraper settings to pick the result source. std::unique_ptr startScraperSearch(const ScraperSearchParams& params); -// returns a list of valid scraper names +// Returns a list of valid scraper names. std::vector getScraperList(); -// returns true if the scraper configured in the settings is still valid +// Returns true if the scraper configured in the settings is still valid. bool isValidConfiguredScraper(); -typedef void (*generate_scraper_requests_func)(const ScraperSearchParams& params, std::queue< std::unique_ptr >& requests, std::vector& results); +typedef void (*generate_scraper_requests_func)(const ScraperSearchParams& params, + std::queue< std::unique_ptr >& requests, + std::vector& results); // ------------------------------------------------------------------------- - - // Meta data asset downloading stuff. class MDResolveHandle : public AsyncHandle { @@ -129,7 +139,8 @@ public: MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search); void update() override; - inline const ScraperSearchResult& getResult() const { assert(mStatus == ASYNC_DONE); return mResult; } + inline const ScraperSearchResult& getResult() const + { assert(mStatus == ASYNC_DONE); return mResult; } private: ScraperSearchResult mResult; @@ -141,7 +152,8 @@ private: class ImageDownloadHandle : public AsyncHandle { public: - ImageDownloadHandle(const std::string& url, const std::string& path, int maxWidth, int maxHeight); + ImageDownloadHandle(const std::string& url, const std::string& path, + int maxWidth, int maxHeight); void update() override; @@ -152,19 +164,24 @@ private: int mMaxHeight; }; -//About the same as "~/.emulationstation/downloaded_images/[system_name]/[game_name].[url's extension]". -//Will create the "downloaded_images" and "subdirectory" directories if they do not exist. -std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url); +// About the same as: +// "~/.emulationstation/downloaded_images/[system_name]/[game_name].[url's extension]". +// Will create the "downloaded_images" and "subdirectory" directories if they do not exist. +std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, + const std::string& url); -//Will resize according to Settings::getInt("ScraperResizeWidth") and Settings::getInt("ScraperResizeHeight"). -std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs); +// Will resize according to Settings::getInt("ScraperResizeWidth") and +// Settings::getInt("ScraperResizeHeight"). +std::unique_ptr downloadImageAsync(const std::string& url, + const std::string& saveAs); // Resolves all metadata assets that need to be downloaded. -std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search); +std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, + const ScraperSearchParams& search); -//You can pass 0 for maxWidth or maxHeight to automatically keep the aspect ratio. -//Will overwrite the image at [path] with the new resized one. -//Returns true if successful, false otherwise. +// You can pass 0 for maxWidth or maxHeight to automatically keep the aspect ratio. +// It will overwrite the image at [path] with the new resized one. +// Returns true if successful, false otherwise. bool resizeImage(const std::string& path, int maxWidth, int maxHeight); #endif // ES_APP_SCRAPERS_SCRAPER_H diff --git a/es-app/src/scrapers/ScreenScraper.cpp b/es-app/src/scrapers/ScreenScraper.cpp index ac7a7617b..fda737dee 100644 --- a/es-app/src/scrapers/ScreenScraper.cpp +++ b/es-app/src/scrapers/ScreenScraper.cpp @@ -1,3 +1,10 @@ +// +// ScreenScraper.cpp +// +// Functions specifically for scraping from screenscraper.fr +// Called from Scraper. +// + #include "scrapers/ScreenScraper.h" #include "utils/TimeUtil.h" @@ -12,17 +19,15 @@ using namespace PlatformIds; -/** - List of systems and their IDs from - https://www.screenscraper.fr/api/systemesListe.php?devid=xxx&devpassword=yyy&softname=zzz&output=XML -**/ -const std::map screenscraper_platformid_map{ +// List of systems and their IDs from: +// https://www.screenscraper.fr/api/systemesListe.php?devid=xxx&devpassword=yyy&softname=zzz&output=XML +const std::map screenscraper_platformid_map { { THREEDO, 29 }, { AMIGA, 64 }, { AMSTRAD_CPC, 65 }, { APPLE_II, 86 }, { ARCADE, 75 }, - { ATARI_800, 26 }, // Use ATARI_2600 as an alias for atari 800 + { ATARI_800, 26 }, // Use ATARI_2600 as an alias for atari 800. { ATARI_2600, 26 }, { ATARI_5200, 40 }, { ATARI_7800, 41 }, @@ -30,7 +35,7 @@ const std::map screenscraper_platformid_map{ { ATARI_JAGUAR_CD, 171 }, { ATARI_LYNX, 28 }, { ATARI_ST, 42}, - // missing Atari XE ? + // Missing Atari XE ? { COLECOVISION, 48 }, { COMMODORE_64, 66 }, { INTELLIVISION, 115 }, @@ -72,7 +77,7 @@ const std::map screenscraper_platformid_map{ { PLAYSTATION, 57 }, { PLAYSTATION_2, 58 }, { PLAYSTATION_3, 59 }, - // missing Sony Playstation 4 ? + // Missing Sony Playstation 4 ? { PLAYSTATION_VITA, 62 }, { PLAYSTATION_PORTABLE, 61 }, { SUPER_NINTENDO, 4 }, @@ -88,12 +93,11 @@ const std::map screenscraper_platformid_map{ { TANDY, 144 } }; - // Helper XML parsing method, finding a node-by-name recursively. -pugi::xml_node find_node_by_name_re(const pugi::xml_node& node, const std::vector node_names) { +pugi::xml_node find_node_by_name_re(const pugi::xml_node& node, + const std::vector node_names) { - for (const std::string& _val : node_names) - { + for (const std::string& _val : node_names) { pugi::xpath_query query_node_name((static_cast("//") + _val).c_str()); pugi::xpath_node_set results = node.select_nodes(query_node_name); @@ -104,26 +108,25 @@ pugi::xml_node find_node_by_name_re(const pugi::xml_node& node, const std::vecto return pugi::xml_node(); } -// Help XML parsing method, finding an direct child XML node starting from the parent and filtering by an attribute value list. -pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent, const std::string& node_name, const std::string& attribute_name, const std::vector attribute_values) +// Help XML parsing method, finding an direct child XML node starting from the parent and +// filtering by an attribute value list. +pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent, + const std::string& node_name, const std::string& attribute_name, + const std::vector attribute_values) { - for (auto _val : attribute_values) - { - for (pugi::xml_node node : node_parent.children(node_name.c_str())) - { - + for (auto _val : attribute_values) { + for (pugi::xml_node node : node_parent.children(node_name.c_str())) { if (strcmp(node.attribute(attribute_name.c_str()).value(), _val.c_str()) == 0) return node; } } return pugi::xml_node(NULL); - } void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, - std::queue< std::unique_ptr >& requests, - std::vector& results) + std::queue< std::unique_ptr >& requests, + std::vector& results) { std::string path; @@ -133,22 +136,23 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, auto& platforms = params.system->getPlatformIds(); std::vector p_ids; - // Get the IDs of each platform from the ScreenScraper list - for (auto platformIt = platforms.cbegin(); platformIt != platforms.cend(); platformIt++) - { + // Get the IDs of each platform from the ScreenScraper list. + for (auto platformIt = platforms.cbegin(); platformIt != platforms.cend(); platformIt++) { auto mapIt = screenscraper_platformid_map.find(*platformIt); - if (mapIt != screenscraper_platformid_map.cend()) - { + if (mapIt != screenscraper_platformid_map.cend()) { p_ids.push_back(mapIt->second); - }else{ - LOG(LogWarning) << "ScreenScraper: no support for platform " << getPlatformName(*platformIt); - // Add the scrape request without a platform/system ID - requests.push(std::unique_ptr(new ScreenScraperRequest(requests, results, path))); + } + else { + LOG(LogWarning) << "ScreenScraper: no support for platform " << + getPlatformName(*platformIt); + // Add the scrape request without a platform/system ID. + requests.push(std::unique_ptr + (new ScreenScraperRequest(requests, results, path))); } } - // Sort the platform IDs and remove duplicates + // Sort the platform IDs and remove duplicates. std::sort(p_ids.begin(), p_ids.end()); auto last = std::unique(p_ids.begin(), p_ids.end()); p_ids.erase(last, p_ids.end()); @@ -157,22 +161,24 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, { path += "&systemeid="; path += HttpReq::urlEncode(std::to_string(*platform)); - requests.push(std::unique_ptr(new ScreenScraperRequest(requests, results, path))); + requests.push(std::unique_ptr + (new ScreenScraperRequest(requests, results, path))); } } -void ScreenScraperRequest::process(const std::unique_ptr& req, std::vector& results) +void ScreenScraperRequest::process(const std::unique_ptr& req, + std::vector& results) { assert(req->status() == HttpReq::REQ_SUCCESS); pugi::xml_document doc; pugi::xml_parse_result parseResult = doc.load_string(req->getContent().c_str()); - if (!parseResult) - { + if (!parseResult) { std::stringstream ss; - ss << "ScreenScraperRequest - Error parsing XML." << std::endl << parseResult.description() << ""; + ss << "ScreenScraperRequest - Error parsing XML." << std::endl << + parseResult.description() << ""; std::string err = ss.str(); setError(err); @@ -182,17 +188,15 @@ void ScreenScraperRequest::process(const std::unique_ptr& req, std::vec } processGame(doc, results); - } - -void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, std::vector& out_results) +void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, + std::vector& out_results) { pugi::xml_node data = xmldoc.child("Data"); pugi::xml_node game = data.child("jeu"); - if (game) - { + if (game) { ScraperSearchResult result; ScreenScraperRequest::ScreenScraperConfig ssConfig; @@ -200,114 +204,116 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, std::ve std::string language = Utils::String::toLower(ssConfig.language).c_str(); // Name fallback: US, WOR(LD). ( Xpath: Data/jeu[0]/noms/nom[*] ). - result.mdl.set("name", find_child_by_attribute_list(game.child("noms"), "nom", "region", { region, "wor", "us" , "ss", "eu", "jp" }).text().get()); + result.mdl.set("name", find_child_by_attribute_list(game.child("noms"), + "nom", "region", { region, "wor", "us" , "ss", "eu", "jp" }).text().get()); - // Description fallback language: EN, WOR(LD) - std::string description = find_child_by_attribute_list(game.child("synopsis"), "synopsis", "langue", { language, "en", "wor" }).text().get(); + // Description fallback language: EN, WOR(LD). + std::string description = find_child_by_attribute_list(game.child("synopsis"), + "synopsis", "langue", { language, "en", "wor" }).text().get(); - if (!description.empty()) { + if (!description.empty()) result.mdl.set("desc", Utils::String::replace(description, " ", " ")); - } - // Genre fallback language: EN. ( Xpath: Data/jeu[0]/genres/genre[*] ) - result.mdl.set("genre", find_child_by_attribute_list(game.child("genres"), "genre", "langue", { language, "en" }).text().get()); + // Genre fallback language: EN. ( Xpath: Data/jeu[0]/genres/genre[*] ). + result.mdl.set("genre", find_child_by_attribute_list(game.child("genres"), + "genre", "langue", { language, "en" }).text().get()); LOG(LogDebug) << "Genre: " << result.mdl.get("genre"); - // Get the date proper. The API returns multiple 'date' children nodes to the 'dates' main child of 'jeu'. - // Date fallback: WOR(LD), US, SS, JP, EU - std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region", { region, "wor", "us", "ss", "jp", "eu" }).text().get(); + // Get the date proper. The API returns multiple 'date' children nodes to the 'dates' + // main child of 'jeu'. + // Date fallback: WOR(LD), US, SS, JP, EU. + std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region", + { region, "wor", "us", "ss", "jp", "eu" }).text().get(); LOG(LogDebug) << "Release Date (unparsed): " << _date; // Date can be YYYY-MM-DD or just YYYY. - if (_date.length() > 4) - { - result.mdl.set("releasedate", Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y-%m-%d"))); - } else if (_date.length() > 0) - { - result.mdl.set("releasedate", Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y"))); + if (_date.length() > 4) { + result.mdl.set("releasedate", Utils::Time::DateTime( + Utils::Time::stringToTime(_date, "%Y-%m-%d"))); + } + else if (_date.length() > 0) { + result.mdl.set("releasedate", Utils::Time::DateTime( + Utils::Time::stringToTime(_date, "%Y"))); } LOG(LogDebug) << "Release Date (parsed): " << result.mdl.get("releasedate"); - /// Developer for the game( Xpath: Data/jeu[0]/developpeur ) + /// Developer for the game( Xpath: Data/jeu[0]/developpeur ). std::string developer = game.child("developpeur").text().get(); if (!developer.empty()) result.mdl.set("developer", Utils::String::replace(developer, " ", " ")); - // Publisher for the game ( Xpath: Data/jeu[0]/editeur ) + // Publisher for the game ( Xpath: Data/jeu[0]/editeur ). std::string publisher = game.child("editeur").text().get(); if (!publisher.empty()) result.mdl.set("publisher", Utils::String::replace(publisher, " ", " ")); - // Players + // Players. result.mdl.set("players", game.child("joueurs").text().get()); - // TODO: Validate rating - if (Settings::getInstance()->getBool("ScrapeRatings") && game.child("note")) - { + // TODO: Validate rating. + if (Settings::getInstance()->getBool("ScrapeRatings") && game.child("note")) { float ratingVal = (game.child("note").text().as_int() / 20.0f); std::stringstream ss; ss << ratingVal; result.mdl.set("rating", ss.str()); } - // Media super-node + // Media super-node. pugi::xml_node media_list = game.child("medias"); - if (media_list) - { + if (media_list) { pugi::xml_node art = pugi::xml_node(NULL); - // Do an XPath query for media[type='$media_type'], then filter by region + // Do an XPath query for media[type='$media_type'], then filter by region. // We need to do this because any child of 'medias' has the form // // and we need to find the right media for the region. - pugi::xpath_node_set results = media_list.select_nodes((static_cast("media[@type='") + ssConfig.media_name + "']").c_str()); + pugi::xpath_node_set results = media_list.select_nodes((static_cast + ("media[@type='") + ssConfig.media_name + "']").c_str()); - if (results.size()) - { - // Region fallback: WOR(LD), US, CUS(TOM?), JP, EU - for (auto _region : std::vector{ region, "wor", "us", "cus", "jp", "eu" }) - { + if (results.size()) { + // Region fallback: WOR(LD), US, CUS(TOM?), JP, EU. + for (auto _region : std::vector{ region, + "wor", "us", "cus", "jp", "eu" }) { if (art) break; - for (auto node : results) - { - if (node.node().attribute("region").value() == _region) - { + for (auto node : results) { + if (node.node().attribute("region").value() == _region) { art = node.node(); break; } } } - } // results + } // Results. - if (art) - { - // Sending a 'softname' containing space will make the image URLs returned by the API also contain the space. - // Escape any spaces in the URL here + if (art) { + // Sending a 'softname' containing space will make the image URLs returned + // by the API also contain the space. Escape any spaces in the URL here result.imageUrl = Utils::String::replace(art.text().get(), " ", "%20"); - // Get the media type returned by ScreenScraper + // Get the media type returned by ScreenScraper. std::string media_type = art.attribute("format").value(); if (!media_type.empty()) result.imageType = "." + media_type; - // Ask for the same image, but with a smaller size, for the thumbnail displayed during scraping + // Ask for the same image, but with a smaller size, for the thumbnail + // displayed during scraping. result.thumbnailUrl = result.imageUrl + "&maxheight=250"; - }else{ + } + else { LOG(LogDebug) << "Failed to find media XML node with name=" << ssConfig.media_name; } - } out_results.push_back(result); - } // game + } // Game. } -// Currently not used in this module -void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, std::vector& results) +// Currently not used in this module. +void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, + std::vector& results) { assert(mRequestQueue != nullptr); @@ -321,26 +327,26 @@ void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, std::ve ScreenScraperRequest::ScreenScraperConfig ssConfig; - // limit the number of results per platform, not in total. - // otherwise if the first platform returns >= 7 games + // Limit the number of results per platform, not in total. + // Otherwise if the first platform returns >= 7 games // but the second platform contains the relevant game, // the relevant result would not be shown. - for (int i = 0; game && i < MAX_SCRAPER_RESULTS; i++) - { + for (int i = 0; game && i < MAX_SCRAPER_RESULTS; i++) { std::string id = game.child("id").text().get(); std::string name = game.child("nom").text().get(); std::string platformId = game.child("systemeid").text().get(); - std::string path = ssConfig.getGameSearchUrl(name) + "&systemeid=" + platformId + "&gameid=" + id; + std::string path = ssConfig.getGameSearchUrl(name) + "&systemeid=" + + platformId + "&gameid=" + id; - mRequestQueue->push(std::unique_ptr(new ScreenScraperRequest(results, path))); + mRequestQueue->push(std::unique_ptr + (new ScreenScraperRequest(results, path))); game = game.next_sibling("jeu"); } - - } -std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(const std::string gameName) const +std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( + const std::string gameName) const { return API_URL_BASE + "/jeuInfos.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY) @@ -348,5 +354,4 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl(const st + "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" + "&romnom=" + HttpReq::urlEncode(gameName); - } diff --git a/es-app/src/scrapers/ScreenScraper.h b/es-app/src/scrapers/ScreenScraper.h index c27f0c61a..a168b4434 100644 --- a/es-app/src/scrapers/ScreenScraper.h +++ b/es-app/src/scrapers/ScreenScraper.h @@ -1,3 +1,10 @@ +// +// ScreenScraper.h +// +// Functions specifically for scraping from screenscraper.fr +// Called from Scraper. +// + #pragma once #ifndef ES_APP_SCRAPERS_SCREEN_SCRAPER_H #define ES_APP_SCRAPERS_SCREEN_SCRAPER_H @@ -7,67 +14,76 @@ namespace pugi { class xml_document; } - -void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, std::queue< std::unique_ptr >& requests, - std::vector& results); +void screenscraper_generate_scraper_requests( + const ScraperSearchParams& params, + std::queue< std::unique_ptr >& requests, + std::vector& results); class ScreenScraperRequest : public ScraperHttpRequest { public: - // ctor for a GetGameList request - ScreenScraperRequest(std::queue< std::unique_ptr >& requestsWrite, std::vector& resultsWrite, const std::string& url) : ScraperHttpRequest(resultsWrite, url), mRequestQueue(&requestsWrite) {} - // ctor for a GetGame request - ScreenScraperRequest(std::vector& resultsWrite, const std::string& url) : ScraperHttpRequest(resultsWrite, url), mRequestQueue(nullptr) {} + // ctor for a GetGameList request. + ScreenScraperRequest(std::queue< std::unique_ptr >& requestsWrite, + std::vector& resultsWrite, + const std::string& url) : ScraperHttpRequest(resultsWrite, url), + mRequestQueue(&requestsWrite) {} - // Settings for the scraper + // ctor for a GetGame request. + ScreenScraperRequest(std::vector& resultsWrite, + const std::string& url) : ScraperHttpRequest(resultsWrite, url), + mRequestQueue(nullptr) {} + + // Settings for the scraper. static const struct ScreenScraperConfig { std::string getGameSearchUrl(const std::string gameName) const; - // Access to the API - const std::string API_DEV_U = { 91, 32, 7, 17 }; - const std::string API_DEV_P = { 108, 28, 54, 55, 83, 43, 91, 44, 30, 22, 41, 12, 0, 108, 38, 29 }; - const std::string API_DEV_KEY = { 54, 73, 115, 100, 101, 67, 111, 107, 79, 66, 68, 66, 67, 56, 118, 77, 54, 88, 101, 54 }; + // Access to the API. + const std::string API_DEV_U = + { 91, 32, 7, 17 }; + const std::string API_DEV_P = + { 108, 28, 54, 55, 83, 43, 91, 44, 30, 22, 41, 12, 0, 108, 38, 29 }; + const std::string API_DEV_KEY = + { 54, 73, 115, 100, 101, 67, 111, 107, 79, 66, 68, 66, 67, 56, 118, 77, 54, 88, 101, 54 }; const std::string API_URL_BASE = "https://www.screenscraper.fr/api2"; - const std::string API_SOFT_NAME = "Emulationstation-DE " + static_cast(PROGRAM_VERSION_STRING); + const std::string API_SOFT_NAME = "Emulationstation-DE " + + static_cast(PROGRAM_VERSION_STRING); - /** Which type of image artwork we need. Possible values (not a comprehensive list): - - ss: in-game screenshot - - box-3D: 3D boxart - - box-2D: 2D boxart (default) - - screenmarque : marquee - - sstitle: in-game start screenshot - - steamgrid: Steam artwork - - wheel: spine - - support-2D: media showing the 2d boxart on the cart - - support-3D: media showing the 3d boxart on the cart - - Note that no all games contain values for these, so we default to "box-2D" since it's the most common. - **/ + // Which type of image artwork we need. Possible values (not a comprehensive list): + // - ss: in-game screenshot + // - box-3D: 3D boxart + // - box-2D: 2D boxart (default) + // - screenmarque : marquee + // - sstitle: in-game start screenshot + // - steamgrid: Steam artwork + // - wheel: spine + // - support-2D: media showing the 2d boxart on the cart + // - support-3D: media showing the 3d boxart on the cart + // + // Note that not all games contain values for all these, so we default to "box-2D" + // since it's the most common. + // std::string media_name = "box-2D"; - // Which Region to use when selecting the artwork - // Applies to: artwork, name of the game, date of release + // Which Region to use when selecting the artwork. + // Applies to: artwork, name of the game, date of release. std::string region = "US"; - // Which Language to use when selecting the textual information - // Applies to: description, genre + // Which Language to use when selecting the textual information. + // Applies to: description, genre. std::string language = "EN"; ScreenScraperConfig() {}; } configuration; protected: - void process(const std::unique_ptr& req, std::vector& results) override; + void process(const std::unique_ptr& req, + std::vector& results) override; void processList(const pugi::xml_document& xmldoc, std::vector& results); void processGame(const pugi::xml_document& xmldoc, std::vector& results); bool isGameRequest() { return !mRequestQueue; } std::queue< std::unique_ptr >* mRequestQueue; - - }; - - #endif // ES_APP_SCRAPERS_SCREEN_SCRAPER_H diff --git a/es-core/src/AsyncHandle.h b/es-core/src/AsyncHandle.h index 65ffb5317..8042d3ede 100644 --- a/es-core/src/AsyncHandle.h +++ b/es-core/src/AsyncHandle.h @@ -1,17 +1,22 @@ +// +// AsyncHandle.h +// +// Asynchronous operations used by ScraperSearchComponent and Scraper. +// + #pragma once #ifndef ES_CORE_ASYNC_HANDLE_H #define ES_CORE_ASYNC_HANDLE_H #include -enum AsyncHandleStatus -{ +enum AsyncHandleStatus { ASYNC_IN_PROGRESS, ASYNC_ERROR, ASYNC_DONE }; -// Handle for some asynchronous operation. +// Handle for some asynchronous operations. class AsyncHandle { public: @@ -23,11 +28,11 @@ public: // Update and return the latest status. inline AsyncHandleStatus status() { update(); return mStatus; } - // User-friendly string of our current status. Will return error message if status() == SEARCH_ERROR. + // User-friendly string of our current status. + // Will return error message if status() == SEARCH_ERROR. inline std::string getStatusString() { - switch(mStatus) - { + switch (mStatus) { case ASYNC_IN_PROGRESS: return "in progress"; case ASYNC_ERROR: diff --git a/es-core/src/HttpReq.cpp b/es-core/src/HttpReq.cpp index feaea8a3c..48d50d908 100644 --- a/es-core/src/HttpReq.cpp +++ b/es-core/src/HttpReq.cpp @@ -1,3 +1,11 @@ +// +// HttpReq.cpp +// +// HTTP request functions. +// Used by Scraper, GamesDBJSONScraper, GamesDBJSONScraperResources and +// ScreenScraper to download game information and media files. +// + #include "HttpReq.h" #include "utils/FileSystemUtil.h" @@ -10,17 +18,15 @@ std::map HttpReq::s_requests; std::string HttpReq::urlEncode(const std::string &s) { - const std::string unreserved = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; + const std::string unreserved = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; std::string escaped=""; - for(size_t i=0; imsg == CURLMSG_DONE) - { + while ((msg = curl_multi_info_read(s_multi_handle, &msgs_left)) != nullptr) { + if (msg->msg == CURLMSG_DONE) { HttpReq* req = s_requests[msg->easy_handle]; - if(req == NULL) - { + if (req == NULL) { LOG(LogError) << "Cannot find easy handle!"; continue; } - if(msg->data.result == CURLE_OK) - { + if (msg->data.result == CURLE_OK) { req->mStatus = REQ_SUCCESS; - }else{ + } + else { req->mStatus = REQ_IO_ERROR; req->onError(curl_easy_strerror(msg->data.result)); } @@ -187,9 +182,9 @@ std::string HttpReq::getErrorMsg() return mErrorMsg; } -//used as a curl callback -//size = size of an element, nmemb = number of elements -//return value is number of elements successfully read +// Used as a curl callback. +// size = size of an element, nmemb = number of elements. +// Return value is number of elements successfully read. size_t HttpReq::write_content(void* buff, size_t size, size_t nmemb, void* req_ptr) { std::stringstream& ss = ((HttpReq*)req_ptr)->mContent; @@ -198,8 +193,8 @@ size_t HttpReq::write_content(void* buff, size_t size, size_t nmemb, void* req_p return nmemb; } -//used as a curl callback -/*int HttpReq::update_progress(void* req_ptr, double dlTotal, double dlNow, double ulTotal, double ulNow) -{ - -}*/ +// Used as a curl callback. +//int HttpReq::update_progress(void* req_ptr, double dlTotal, +// double dlNow, double ulTotal, double ulNow) +//{ +//} diff --git a/es-core/src/HttpReq.h b/es-core/src/HttpReq.h index f6032a3c7..dde0bac6b 100644 --- a/es-core/src/HttpReq.h +++ b/es-core/src/HttpReq.h @@ -1,3 +1,11 @@ +// +// HttpReq.h +// +// HTTP request functions. +// Used by Scraper, GamesDBJSONScraper, GamesDBJSONScraperResources and +// ScreenScraper to download game information and media files. +// + #pragma once #ifndef ES_CORE_HTTP_REQ_H #define ES_CORE_HTTP_REQ_H @@ -6,22 +14,27 @@ #include #include -/* Usage: - * HttpReq myRequest("www.google.com", "/index.html"); - * //for blocking behavior: while(myRequest.status() == HttpReq::REQ_IN_PROGRESS); - * //for non-blocking behavior: check if(myRequest.status() != HttpReq::REQ_IN_PROGRESS) in some sort of update method - * - * //once one of those completes, the request is ready - * if(myRequest.status() != REQ_SUCCESS) - * { - * //an error occured - * LOG(LogError) << "HTTP request error - " << myRequest.getErrorMessage(); - * return; - * } - * - * std::string content = myRequest.getContent(); - * //process contents... -*/ +// Usage: +// HttpReq myRequest("www.duckduckgo.com", "/index.html"); +// +// For blocking behavior: +// while (myRequest.status() == HttpReq::REQ_IN_PROGRESS); +// +// For non-blocking behavior: +// Check 'if(myRequest.status() != HttpReq::REQ_IN_PROGRESS)' in some sort of update method. +// +// Once one of those calls complete, the request is ready. +// +// Do something like this to capture errors: +// if(myRequest.status() != REQ_SUCCESS) +// { +// // An error occured. +// LOG(LogError) << "HTTP request error - " << myRequest.getErrorMessage(); +// return; +// } +// +// This is how to read the returned content: +// std::string content = myRequest.getContent(); class HttpReq { @@ -30,31 +43,31 @@ public: ~HttpReq(); - enum Status - { - REQ_IN_PROGRESS, //request is in progress - REQ_SUCCESS, //request completed successfully, get it with getContent() + enum Status { + REQ_IN_PROGRESS, // Request is in progress. + REQ_SUCCESS, // Request completed successfully, get it with getContent(). - REQ_IO_ERROR, //some error happened, get it with getErrorMsg() - REQ_BAD_STATUS_CODE, //some invalid HTTP response status code happened (non-200) - REQ_INVALID_RESPONSE //the HTTP response was invalid + REQ_IO_ERROR, // Some error happened, get it with getErrorMsg(). + REQ_BAD_STATUS_CODE, // Some invalid HTTP response status code happened (non-200). + REQ_INVALID_RESPONSE // The HTTP response was invalid. }; - Status status(); //process any received data and return the status afterwards + Status status(); // Process any received data and return the status afterwards. std::string getErrorMsg(); - std::string getContent() const; // mStatus must be REQ_SUCCESS + std::string getContent() const; // mStatus must be REQ_SUCCESS. static std::string urlEncode(const std::string &s); static bool isUrl(const std::string& s); private: static size_t write_content(void* buff, size_t size, size_t nmemb, void* req_ptr); - //static int update_progress(void* req_ptr, double dlTotal, double dlNow, double ulTotal, double ulNow); + //static int update_progress(void* req_ptr, double dlTotal, double dlNow, + // double ulTotal, double ulNow); - //god dammit libcurl why can't you have some way to check the status of an individual handle - //why do I have to handle ALL messages at once + // God dammit libcurl why can't you have some way to check the status of an + // individual handle why do I have to handle ALL messages at once. static std::map s_requests; static CURLM* s_multi_handle; diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index fd8b8a1ee..b0812cedf 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -1,9 +1,8 @@ // // Settings.cpp // -// Functions to read from and write to the configuration file es_settings.cfg -// are included here. The default values for the application settings are -// defined here as well. +// Functions to read from and write to the configuration file es_settings.cfg. +// The default values for the application settings are defined here as well. // #include "Settings.h" diff --git a/es-core/src/Settings.h b/es-core/src/Settings.h index a3240cb5f..5b452a5e2 100644 --- a/es-core/src/Settings.h +++ b/es-core/src/Settings.h @@ -1,9 +1,8 @@ // // Settings.h // -// Functions to read from and write to the configuration file es_settings.cfg -// are included here. The default values for the application settings are -// defined here as well. +// Functions to read from and write to the configuration file es_settings.cfg. +// The default values for the application settings are defined here as well. // #pragma once