mirror of
				https://github.com/RetroDECK/ES-DE.git
				synced 2025-04-10 19:15:13 +00:00 
			
		
		
		
	Added an offline miximage generator.
This commit is contained in:
		
							parent
							
								
									c9cd282b7f
								
							
						
					
					
						commit
						a1fd0959c1
					
				|  | @ -24,6 +24,7 @@ set(ES_HEADERS | |||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMediaViewerOptions.h | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.h | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.h | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiOfflineGenerator.h | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMenu.h | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.h | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperSearch.h | ||||
|  | @ -75,6 +76,7 @@ set(ES_SOURCES | |||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.cpp | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMediaViewerOptions.cpp | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.cpp | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiOfflineGenerator.cpp | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMenu.cpp | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.cpp | ||||
|         ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperSearch.cpp | ||||
|  |  | |||
							
								
								
									
										339
									
								
								es-app/src/guis/GuiOfflineGenerator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								es-app/src/guis/GuiOfflineGenerator.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,339 @@ | |||
| //  SPDX-License-Identifier: MIT
 | ||||
| //
 | ||||
| //  EmulationStation Desktop Edition
 | ||||
| //  GuiOfflineGenerator.cpp
 | ||||
| //
 | ||||
| //  User interface for the miximage offline generator.
 | ||||
| //  Calls MiximageGenerator to do the actual work.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "guis/GuiOfflineGenerator.h" | ||||
| 
 | ||||
| #include "components/MenuComponent.h" | ||||
| #include "views/ViewController.h" | ||||
| #include "SystemData.h" | ||||
| 
 | ||||
| GuiOfflineGenerator::GuiOfflineGenerator( | ||||
|         Window* window, | ||||
|         const std::queue<FileData*>& gameQueue) | ||||
|         : GuiComponent(window), | ||||
|         mBackground(window, ":/graphics/frame.svg"), | ||||
|         mGrid(window, Vector2i(5, 13)), | ||||
|         mGameQueue(gameQueue) | ||||
| { | ||||
|     addChild(&mBackground); | ||||
|     addChild(&mGrid); | ||||
| 
 | ||||
|     mProcessing = false; | ||||
|     mPaused = false; | ||||
|     mOverwriting = false; | ||||
| 
 | ||||
|     mTotalGames = static_cast<int>(mGameQueue.size()); | ||||
|     mGamesProcessed = 0; | ||||
|     mImagesGenerated = 0; | ||||
|     mImagesOverwritten = 0; | ||||
|     mGamesSkipped = 0; | ||||
|     mGamesFailed = 0; | ||||
| 
 | ||||
|     mGame = nullptr; | ||||
| 
 | ||||
|     // Header.
 | ||||
|     mTitle = std::make_shared<TextComponent>(mWindow, "MIXIMAGE OFFLINE GENERATOR", | ||||
|             Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); | ||||
|     mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(5, 1)); | ||||
| 
 | ||||
|     mStatus = std::make_shared<TextComponent>(mWindow, "NOT STARTED", | ||||
|             Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); | ||||
|     mGrid.setEntry(mStatus, Vector2i(0, 1), false, true, Vector2i(5, 1)); | ||||
| 
 | ||||
|     mGameCounter = std::make_shared<TextComponent>(mWindow, | ||||
|             std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + | ||||
|             (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); | ||||
|     mGrid.setEntry(mGameCounter, Vector2i(0, 2), false, true, Vector2i(5, 1)); | ||||
| 
 | ||||
|     // Spacer row with top border.
 | ||||
|     mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 3), | ||||
|             false, false, Vector2i(5, 1), GridFlags::BORDER_TOP); | ||||
| 
 | ||||
|     // Left spacer.
 | ||||
|     mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 4), | ||||
|             false, false, Vector2i(1, 7)); | ||||
| 
 | ||||
|     // Generated label.
 | ||||
|     mGeneratedLbl = std::make_shared<TextComponent>(mWindow, "Generated:", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mGeneratedLbl, Vector2i(1, 4), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Generated value/counter.
 | ||||
|     mGeneratedVal = std::make_shared<TextComponent>(mWindow, | ||||
|             std::to_string(mGamesProcessed), | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mGeneratedVal, Vector2i(2, 4), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Overwritten label.
 | ||||
|     mOverwrittenLbl = std::make_shared<TextComponent>(mWindow, "Overwritten:", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mOverwrittenLbl, Vector2i(1, 5), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Overwritten value/counter.
 | ||||
|     mOverwrittenVal = std::make_shared<TextComponent>(mWindow, | ||||
|             std::to_string(mImagesOverwritten), | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mOverwrittenVal, Vector2i(2, 5), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Skipping label.
 | ||||
|     mSkippedLbl = std::make_shared<TextComponent>(mWindow, "Skipped (existing):", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mSkippedLbl, Vector2i(1, 6), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Skipping value/counter.
 | ||||
|     mSkippedVal= std::make_shared<TextComponent>(mWindow, | ||||
|             std::to_string(mGamesSkipped), | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mSkippedVal, Vector2i(2, 6), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Failed label.
 | ||||
|     mFailedLbl = std::make_shared<TextComponent>(mWindow, "Failed:", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mFailedLbl, Vector2i(1, 7), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Failed value/counter.
 | ||||
|     mFailedVal = std::make_shared<TextComponent>(mWindow, | ||||
|             std::to_string(mGamesFailed), | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mFailedVal, Vector2i(2, 7), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Processing label.
 | ||||
|     mProcessingLbl = std::make_shared<TextComponent>(mWindow, "Processing: ", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mProcessingLbl, Vector2i(3, 4), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Processing value.
 | ||||
|     mProcessingVal = std::make_shared<TextComponent>(mWindow, "", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mProcessingVal, Vector2i(4, 4), false, true, Vector2i(1, 1)); | ||||
| 
 | ||||
|     // Spacer row.
 | ||||
|     mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 8), | ||||
|             false, false, Vector2i(5, 1)); | ||||
| 
 | ||||
|     // Last error message label.
 | ||||
|     mLastErrorLbl = std::make_shared<TextComponent>(mWindow, "Last error message:", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mLastErrorLbl, Vector2i(1, 9), false, true, Vector2i(4, 1)); | ||||
| 
 | ||||
|     // Last error message value.
 | ||||
|     mLastErrorVal = std::make_shared<TextComponent>(mWindow, "", | ||||
|             Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); | ||||
|     mGrid.setEntry(mLastErrorVal, Vector2i(1, 10), false, true, Vector2i(4, 1)); | ||||
| 
 | ||||
|     // Spacer row with bottom border.
 | ||||
|     mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 11), | ||||
|             false, false, Vector2i(5, 1), GridFlags::BORDER_BOTTOM); | ||||
| 
 | ||||
|     // Buttons.
 | ||||
|     std::vector<std::shared_ptr<ButtonComponent>> buttons; | ||||
| 
 | ||||
|     mStartPauseButton = std::make_shared<ButtonComponent>(mWindow, "START", | ||||
|             "start processing", [this](){ | ||||
|         if (!mProcessing) { | ||||
|             mProcessing = true; | ||||
|             mPaused = false; | ||||
|             mStartPauseButton->setText("PAUSE", "pause processing"); | ||||
|             mCloseButton->setText("CLOSE", "close (abort processing)"); | ||||
|             mStatus->setText("RUNNING..."); | ||||
|             if (mGamesProcessed == 0) { | ||||
|                 LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games"; | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             if (mMiximageGeneratorThread.joinable()) | ||||
|                 mMiximageGeneratorThread.join(); | ||||
|             mPaused = true; | ||||
|             update(1); | ||||
|             mProcessing = false; | ||||
|             this->mStartPauseButton->setText("START", "start processing"); | ||||
|             this->mCloseButton->setText("CLOSE", "close (abort processing)"); | ||||
|             mStatus->setText("PAUSED"); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     buttons.push_back(mStartPauseButton); | ||||
| 
 | ||||
|     mCloseButton = std::make_shared<ButtonComponent>(mWindow, "CLOSE", "close", [this](){ | ||||
|         if (mGamesProcessed != 0 && mGamesProcessed != mTotalGames) { | ||||
|             LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " << | ||||
|                     mGamesProcessed << (mGamesProcessed == 1 ? " game (" : " games (") << | ||||
|                     mImagesGenerated << (mImagesGenerated == 1 ? " image " : " images ") << | ||||
|                     "generated, " << mGamesSkipped << | ||||
|                     (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << mGamesFailed << | ||||
|                     (mGamesFailed == 1 ? " game " : " games ") << "failed)"; | ||||
|         } | ||||
|         delete this; | ||||
|     }); | ||||
| 
 | ||||
|     buttons.push_back(mCloseButton); | ||||
|     mButtonGrid = makeButtonGrid(mWindow, buttons); | ||||
| 
 | ||||
|     mGrid.setEntry(mButtonGrid, Vector2i(0, 12), true, false, Vector2i(5, 1)); | ||||
| 
 | ||||
|     // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 95% of the screen
 | ||||
|     // width rather than the 85% allowed for wider displays.
 | ||||
|     float width = Renderer::getScreenWidth() * | ||||
|             ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f); | ||||
| 
 | ||||
|     setSize(width, Renderer::getScreenHeight() * 0.75f); | ||||
|     setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, | ||||
|             (Renderer::getScreenHeight() - mSize.y()) / 2.0f); | ||||
| } | ||||
| 
 | ||||
| GuiOfflineGenerator::~GuiOfflineGenerator() | ||||
| { | ||||
|     // Let the miximage generator thread complete.
 | ||||
|     if (mMiximageGeneratorThread.joinable()) | ||||
|         mMiximageGeneratorThread.join(); | ||||
| 
 | ||||
|     mMiximageGenerator.reset(); | ||||
| 
 | ||||
|     if (mImagesGenerated > 0) | ||||
|         ViewController::get()->reloadAll(); | ||||
| } | ||||
| 
 | ||||
| void GuiOfflineGenerator::onSizeChanged() | ||||
| { | ||||
|     mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); | ||||
| 
 | ||||
|     // Set row heights.
 | ||||
|     mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y(), false); | ||||
|     mGrid.setRowHeightPerc(1, (mStatus->getFont()->getLetterHeight() + 2.0f) / mSize.y(), false); | ||||
|     mGrid.setRowHeightPerc(2, mGameCounter->getFont()->getHeight() * 1.75f / mSize.y(), false); | ||||
|     mGrid.setRowHeightPerc(3, (mStatus->getFont()->getLetterHeight() + 3.0f) / mSize.y(), false); | ||||
|     mGrid.setRowHeightPerc(4, 0.07f, false); | ||||
|     mGrid.setRowHeightPerc(5, 0.07f, false); | ||||
|     mGrid.setRowHeightPerc(6, 0.07f, false); | ||||
|     mGrid.setRowHeightPerc(7, 0.07f, false); | ||||
|     mGrid.setRowHeightPerc(8, 0.02f, false); | ||||
|     mGrid.setRowHeightPerc(9, 0.07f, false); | ||||
|     mGrid.setRowHeightPerc(10, 0.07f, false); | ||||
|     mGrid.setRowHeightPerc(12, mButtonGrid->getSize().y() / mSize.y(), false); | ||||
| 
 | ||||
|     // Set column widths.
 | ||||
|     mGrid.setColWidthPerc(0, 0.03f); | ||||
|     mGrid.setColWidthPerc(1, 0.20f); | ||||
|     mGrid.setColWidthPerc(2, 0.145f); | ||||
| 
 | ||||
|     // Adjust the width slightly depending on the aspect ratio of the screen to make sure
 | ||||
|     // that the label does not get abbreviated.
 | ||||
|     if (Renderer::getScreenAspectRatio() <= 1.4f) | ||||
|         mGrid.setColWidthPerc(3, 0.13f); | ||||
|     else if (Renderer::getScreenAspectRatio() <= 1.6f) | ||||
|         mGrid.setColWidthPerc(3, 0.12f); | ||||
|     else | ||||
|         mGrid.setColWidthPerc(3, 0.113f); | ||||
| 
 | ||||
|     mGrid.setSize(mSize); | ||||
| } | ||||
| 
 | ||||
| void GuiOfflineGenerator::update(int deltaTime) | ||||
| { | ||||
|     if (!mProcessing) | ||||
|         return; | ||||
| 
 | ||||
|     // Check if a miximage generator thread was started, and if the processing has been completed.
 | ||||
|     if (mMiximageGenerator && mGeneratorFuture.valid()) { | ||||
|         // Only wait one millisecond as this update() function runs very frequently.
 | ||||
|         if (mGeneratorFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) { | ||||
|             // We always let the miximage generator thread complete.
 | ||||
|             if (mMiximageGeneratorThread.joinable()) | ||||
|                 mMiximageGeneratorThread.join(); | ||||
|             mMiximageGenerator.reset(); | ||||
|             if (!mGeneratorFuture.get()) { | ||||
|                 mImagesGenerated++; | ||||
|                 TextureResource::manualUnload(mGame->getMiximagePath(), false); | ||||
|                 mProcessingVal->setText(""); | ||||
|                 if (mOverwriting) { | ||||
|                     mImagesOverwritten++; | ||||
|                     mOverwriting = false; | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 std::string errorMessage = mResultMessage + " (" + mGameName + ")"; | ||||
|                 mLastErrorVal->setText(errorMessage); | ||||
|                 LOG(LogInfo) << "GuiOfflineGenerator: " << errorMessage; | ||||
|                 mGamesFailed++; | ||||
|             } | ||||
|             mGame = nullptr; | ||||
|             mGamesProcessed++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // This is simply to retain the name of the last processed game on-screen while paused.
 | ||||
|     if (mPaused) | ||||
|         mProcessingVal->setText(mGameName); | ||||
| 
 | ||||
|     if (!mPaused && !mGameQueue.empty() && !mMiximageGenerator) { | ||||
|         mGame = mGameQueue.front(); | ||||
|         mGameQueue.pop(); | ||||
| 
 | ||||
|         mGameName = mGame->getName() + " [" + | ||||
|                 Utils::String::toUpper(mGame->getSystem()->getName()) + "]"; | ||||
|         mProcessingVal->setText(mGameName); | ||||
| 
 | ||||
|         if (!Settings::getInstance()->getBool("MiximageOverwrite") && | ||||
|                 mGame->getMiximagePath() != "") { | ||||
|             mGamesProcessed++; | ||||
|             mGamesSkipped++; | ||||
|             mSkippedVal->setText(std::to_string(mGamesSkipped)); | ||||
|         } | ||||
|         else { | ||||
|             if (mGame->getMiximagePath() != "") | ||||
|                 mOverwriting = true; | ||||
| 
 | ||||
|             mMiximageGenerator = std::make_unique<MiximageGenerator>(mGame, mResultMessage); | ||||
| 
 | ||||
|             // The promise/future mechanism is used as signaling for the thread to indicate
 | ||||
|             // that processing has been completed.
 | ||||
|             std::promise<bool>().swap(mGeneratorPromise); | ||||
|             mGeneratorFuture = mGeneratorPromise.get_future(); | ||||
| 
 | ||||
|             mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread, | ||||
|                     mMiximageGenerator.get(), &mGeneratorPromise); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Update the statistics.
 | ||||
|     mStatus->setText("RUNNING..."); | ||||
|     mGameCounter->setText(std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + | ||||
|             (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED"); | ||||
| 
 | ||||
|     mGeneratedVal->setText(std::to_string(mImagesGenerated)); | ||||
|     mFailedVal->setText(std::to_string(mGamesFailed)); | ||||
|     mOverwrittenVal->setText(std::to_string(mImagesOverwritten)); | ||||
| 
 | ||||
|     if (mGamesProcessed == mTotalGames) { | ||||
|         mStatus->setText("COMPLETED"); | ||||
|         mStartPauseButton->setText("DONE", "done (close)"); | ||||
|         mStartPauseButton->setPressedFunc([this](){ delete this; }); | ||||
|         mCloseButton->setText("CLOSE", "close"); | ||||
|         mProcessingVal->setText(""); | ||||
|         LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated << | ||||
|                 (mImagesGenerated == 1 ? " image " : " images ") << "generated, " << | ||||
|                 mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << | ||||
|                 mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)"; | ||||
|         mProcessing = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::vector<HelpPrompt> GuiOfflineGenerator::getHelpPrompts() | ||||
| { | ||||
|     std::vector<HelpPrompt> prompts = mGrid.getHelpPrompts(); | ||||
|     return prompts; | ||||
| } | ||||
| 
 | ||||
| HelpStyle GuiOfflineGenerator::getHelpStyle() | ||||
| { | ||||
|     HelpStyle style = HelpStyle(); | ||||
|     style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); | ||||
|     return style; | ||||
| } | ||||
							
								
								
									
										87
									
								
								es-app/src/guis/GuiOfflineGenerator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								es-app/src/guis/GuiOfflineGenerator.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| //  SPDX-License-Identifier: MIT
 | ||||
| //
 | ||||
| //  EmulationStation Desktop Edition
 | ||||
| //  GuiOfflineGenerator.h
 | ||||
| //
 | ||||
| //  User interface for the miximage offline generator.
 | ||||
| //  Calls MiximageGenerator to do the actual work.
 | ||||
| //
 | ||||
| 
 | ||||
| #ifndef ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H | ||||
| #define ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H | ||||
| 
 | ||||
| #include "components/ButtonComponent.h" | ||||
| #include "components/ComponentGrid.h" | ||||
| #include "GuiComponent.h" | ||||
| #include "MiximageGenerator.h" | ||||
| 
 | ||||
| #include <queue> | ||||
| 
 | ||||
| class TextComponent; | ||||
| 
 | ||||
| class GuiOfflineGenerator : public GuiComponent | ||||
| { | ||||
| public: | ||||
|     GuiOfflineGenerator(Window* window, const std::queue<FileData*>& gameQueue); | ||||
|     ~GuiOfflineGenerator(); | ||||
| 
 | ||||
| private: | ||||
|     void onSizeChanged() override; | ||||
|     void update(int deltaTime) override; | ||||
| 
 | ||||
|     virtual std::vector<HelpPrompt> getHelpPrompts() override; | ||||
|     HelpStyle getHelpStyle() override; | ||||
| 
 | ||||
|     std::queue<FileData*> mGameQueue; | ||||
| 
 | ||||
|     std::unique_ptr<MiximageGenerator> mMiximageGenerator; | ||||
|     std::thread mMiximageGeneratorThread; | ||||
|     std::promise<bool> mGeneratorPromise; | ||||
|     std::future<bool> mGeneratorFuture; | ||||
| 
 | ||||
|     FileData* mGame; | ||||
| 
 | ||||
|     bool mProcessing; | ||||
|     bool mPaused; | ||||
|     bool mOverwriting; | ||||
|     std::string mResultMessage; | ||||
| 
 | ||||
|     unsigned int mTotalGames; | ||||
|     unsigned int mGamesProcessed; | ||||
|     unsigned int mImagesGenerated; | ||||
|     unsigned int mImagesOverwritten; | ||||
|     unsigned int mGamesSkipped; | ||||
|     unsigned int mGamesFailed; | ||||
| 
 | ||||
|     NinePatchComponent mBackground; | ||||
|     ComponentGrid mGrid; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mTitle; | ||||
|     std::shared_ptr<TextComponent> mStatus; | ||||
|     std::shared_ptr<TextComponent> mGameCounter; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mGeneratedLbl; | ||||
|     std::shared_ptr<TextComponent> mGeneratedVal; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mOverwrittenLbl; | ||||
|     std::shared_ptr<TextComponent> mOverwrittenVal; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mSkippedLbl; | ||||
|     std::shared_ptr<TextComponent> mSkippedVal; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mFailedLbl; | ||||
|     std::shared_ptr<TextComponent> mFailedVal; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mProcessingLbl; | ||||
|     std::shared_ptr<TextComponent> mProcessingVal; | ||||
|     std::string mGameName; | ||||
| 
 | ||||
|     std::shared_ptr<TextComponent> mLastErrorLbl; | ||||
|     std::shared_ptr<TextComponent> mLastErrorVal; | ||||
| 
 | ||||
|     std::shared_ptr<ComponentGrid> mButtonGrid; | ||||
|     std::shared_ptr<ButtonComponent> mStartPauseButton; | ||||
|     std::shared_ptr<ButtonComponent> mCloseButton; | ||||
| }; | ||||
| 
 | ||||
| #endif // ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H
 | ||||
|  | @ -13,10 +13,11 @@ | |||
| #include "components/OptionListComponent.h" | ||||
| #include "components/SwitchComponent.h" | ||||
| #include "guis/GuiMsgBox.h" | ||||
| #include "guis/GuiOfflineGenerator.h" | ||||
| #include "guis/GuiScraperMulti.h" | ||||
| #include "guis/GuiSettings.h" | ||||
| #include "views/ViewController.h" | ||||
| #include "FileData.h" | ||||
| #include "FileSorts.h" | ||||
| #include "SystemData.h" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -439,9 +440,53 @@ void GuiScraperMenu::openMiximageOptions() | |||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     // Miximage offline generator.
 | ||||
|     ComponentListRow offline_generator_row; | ||||
|     offline_generator_row.elements.clear(); | ||||
|     offline_generator_row.addElement(std::make_shared<TextComponent> | ||||
|             (mWindow, "OFFLINE GENERATOR", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); | ||||
|     offline_generator_row.addElement(makeArrow(mWindow), false); | ||||
|     offline_generator_row.makeAcceptInputHandler( | ||||
|             std::bind(&GuiScraperMenu::openOfflineGenerator, this, s)); | ||||
|     s->addRow(offline_generator_row); | ||||
| 
 | ||||
|     mWindow->pushGui(s); | ||||
| } | ||||
| 
 | ||||
| void GuiScraperMenu::openOfflineGenerator(GuiSettings* settings) | ||||
| { | ||||
|     if (mSystems->getSelectedObjects().empty()) { | ||||
|         mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), | ||||
|                 "THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n" | ||||
|                 "SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n" | ||||
|                 "AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR")); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Always save the settings before starting the generator, in case any of the
 | ||||
|     // miximage settings were modified.
 | ||||
|     settings->save(); | ||||
|     // Also unset the save flag so that a double saving does not take place when closing
 | ||||
|     // the miximage options menu later on.
 | ||||
|     settings->setNeedsSaving(false); | ||||
| 
 | ||||
|     // Build the queue of games to process.
 | ||||
|     std::queue<FileData*> gameQueue; | ||||
|     std::vector<SystemData*> systems = mSystems->getSelectedObjects(); | ||||
| 
 | ||||
|     for (auto sys = systems.cbegin(); sys != systems.cend(); sys++) { | ||||
|         std::vector<FileData*> games = (*sys)->getRootFolder()->getChildrenRecursive(); | ||||
| 
 | ||||
|         // Sort the games by "filename, ascending".
 | ||||
|         std::stable_sort(games.begin(), games.end(), FileSorts::SortTypes.at(0).comparisonFunction); | ||||
| 
 | ||||
|         for (FileData* game : games) | ||||
|             gameQueue.push(game); | ||||
|     } | ||||
| 
 | ||||
|     mWindow->pushGui(new GuiOfflineGenerator(mWindow, gameQueue)); | ||||
| } | ||||
| 
 | ||||
| void GuiScraperMenu::openOtherOptions() | ||||
| { | ||||
|     auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); | ||||
|  | @ -731,7 +776,7 @@ void GuiScraperMenu::start() | |||
| { | ||||
|     if (mSystems->getSelectedObjects().empty()) { | ||||
|         mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), | ||||
|             "PLEASE SELECT AT LEAST ONE SYSTEM TO SCRAPE")); | ||||
|                 "PLEASE SELECT AT LEAST ONE SYSTEM TO SCRAPE")); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| #define ES_APP_GUIS_GUI_SCRAPER_MENU_H | ||||
| 
 | ||||
| #include "components/MenuComponent.h" | ||||
| #include "guis/GuiSettings.h" | ||||
| #include "scrapers/Scraper.h" | ||||
| 
 | ||||
| class FileData; | ||||
|  | @ -42,6 +43,7 @@ private: | |||
|     void openAccountOptions(); | ||||
|     void openContentOptions(); | ||||
|     void openMiximageOptions(); | ||||
|     void openOfflineGenerator(GuiSettings* settings); | ||||
|     void openOtherOptions(); | ||||
| 
 | ||||
|     std::queue<ScraperSearchParams> getSearches( | ||||
|  |  | |||
|  | @ -662,7 +662,7 @@ void GuiScraperSearch::update(int deltaTime) | |||
| 
 | ||||
|     // Check if a miximage generator thread was started, and if the processing has been completed.
 | ||||
|     if (mMiximageGenerator && mGeneratorFuture.valid()) { | ||||
|         // Only wait one millisecond, this update() function runs very frequently.
 | ||||
|         // Only wait one millisecond as this update() function runs very frequently.
 | ||||
|         if (mGeneratorFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) { | ||||
|             mMDResolveHandle.reset(); | ||||
|             // We always let the miximage generator thread complete.
 | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ public: | |||
|             bool isPassword = false); | ||||
|     inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }; | ||||
| 
 | ||||
|     void setNeedsSaving() { mNeedsSaving = true; }; | ||||
|     void setNeedsSaving(bool state = true) { mNeedsSaving = state; }; | ||||
|     void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; }; | ||||
|     void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; }; | ||||
|     void setNeedsSorting() { mNeedsSorting = true; }; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Leon Styhre
						Leon Styhre