diff --git a/.gitignore b/.gitignore index af18655..af8f58e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.rpx build/ src/resources/filelist.h +*.save-failed diff --git a/Makefile b/Makefile index 3653eca..3601fcd 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ TARGET := $(notdir $(CURDIR)) BUILD := build SOURCES := src \ src/fs \ + src/game \ src/gui \ src/menu \ src/resources \ diff --git a/data/images/noGameIcon.png b/data/images/noGameIcon.png new file mode 100644 index 0000000..687efb3 Binary files /dev/null and b/data/images/noGameIcon.png differ diff --git a/src/common/common.h b/src/common/common.h index 7cda38b..3e36f03 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -6,6 +6,7 @@ extern "C" { #endif #define LAUNCHIINE_VERSION "v0.1" +#define META_PATH "/meta" #ifdef __cplusplus } diff --git a/src/game/GameList.cpp b/src/game/GameList.cpp new file mode 100644 index 0000000..1822928 --- /dev/null +++ b/src/game/GameList.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include + +#include "utils/AsyncExecutor.h" +#include "GameList.h" +#include "common/common.h" + +#include "fs/DirList.h" +#include "fs/FSUtils.h" +#include "utils/logger.h" +#include "utils/StringTools.h" + +void GameList::clear() { + for (auto const& x : fullGameList) { + if(x != NULL) { + if(x->imageData != NULL) { + DEBUG_FUNCTION_LINE("Delete the image data\n"); + delete x->imageData; + x->imageData = NULL; + } + delete x; + } + } + gameFilter.clear(); + fullGameList.clear(); + filteredList.clear(); + //! Clear memory of the vector completely + std::vector().swap(filteredList); + std::vector().swap(fullGameList); + titleListChanged(this); +} + + +gameInfo * GameList::getGameInfo(uint64_t titleId) const { + for (uint32_t i = 0; i < filteredList.size(); ++i) { + if(titleId == filteredList[i]->titleId) + return filteredList[i]; + } + + return NULL; +} + +extern "C" int ACPGetTitleMetaXml(uint64_t titleid, ACPMetaXml*); +int32_t GameList::readGameList() { + // Clear list + for (auto const& x : fullGameList) { + delete x; + } + + fullGameList.clear(); + //! Clear memory of the vector completely + std::vector().swap(fullGameList); + + int32_t cnt = 0; + + MCPError mcp = MCP_Open(); + if (mcp < 0) { + return 0; + } + + MCPError titleCount = MCP_TitleCount(mcp); + if (titleCount < 0) { + MCP_Close(mcp); + return 0; + } + + std::vector titles(titleCount); + uint32_t realTitleCount; + + MCPError err = MCP_TitleList(mcp, &realTitleCount, titles.data(), titles.size() * sizeof(decltype(titles)::value_type)); + if (err < 0) { + MCP_Close(mcp); + return 0; + } + if (realTitleCount != titles.size()) { + titles.resize(realTitleCount); + } + + for (auto title_candidate : titles) { + if(true || (title_candidate.titleId & 0xFFFFFFFF00000000L) == 0x0005000000000000L) { + gameInfo* newGameInfo = new gameInfo; + newGameInfo->titleId = title_candidate.titleId; + newGameInfo->gamePath = title_candidate.path; + newGameInfo->name = ""; + newGameInfo->imageData = NULL; + DCFlushRange(newGameInfo, sizeof(gameInfo)); + fullGameList.push_back(newGameInfo); + titleAdded(newGameInfo); + cnt++; + } + } + + return cnt; +} + +void GameList::loadIcons() { + for (int i = 0; i < this->size(); i++) { + gameInfo * newHeader = this->at(i); + + ACPMetaXml* meta = (ACPMetaXml*)calloc(1, 0x4000); //TODO fix wut + if(meta) { + auto acp = ACPGetTitleMetaXml(newHeader->titleId, meta); + if(acp >= 0) { + newHeader->name = meta->shortname_en; + } + free(meta); + } + + if(newHeader->imageData == NULL) { + std::string filepath = "fs:" + newHeader->gamePath + META_PATH + "/iconTex.tga"; + uint8_t *buffer = NULL; + uint32_t bufferSize = 0; + int iResult = FSUtils::LoadFileToMem(filepath.c_str(), &buffer, &bufferSize); + + if(iResult > 0) { + GuiImageData * imageData = new GuiImageData(buffer, bufferSize, GX2_TEX_CLAMP_MODE_MIRROR); + if(imageData) { + newHeader->imageData = imageData; + } + + //! free original image buffer which is converted to texture now and not needed anymore + free(buffer); + } + } + DCFlushRange(newHeader, sizeof(gameInfo)); + titleUpdated(newHeader); + } +} + +void GameList::internalFilterList(std::vector &fullList) { + for (uint32_t i = 0; i < fullList.size(); ++i) { + gameInfo *header = fullList[i]; + + //! TODO: do filtering as needed + + filteredList.push_back(header); + } +} + +int32_t GameList::filterList(const char * filter) { + if(filter) { + gameFilter = filter; + } + + if(fullGameList.size() == 0) { + readGameList(); + } + + filteredList.clear(); + + // Filter current game list if selected + internalFilterList(fullGameList); + + sortList(); + + titleListChanged(this); + + AsyncExecutor::execute([&] { loadIcons();}); + + return filteredList.size(); +} + +void GameList::internalLoadUnfiltered(std::vector & fullList) { + for (uint32_t i = 0; i < fullList.size(); ++i) { + gameInfo *header = fullList[i]; + + filteredList.push_back(header); + } +} + +int32_t GameList::loadUnfiltered() { + if(fullGameList.size() == 0) { + readGameList(); + } + + gameFilter.clear(); + filteredList.clear(); + + // Filter current game list if selected + internalLoadUnfiltered(fullGameList); + + sortList(); + + AsyncExecutor::execute([&] { loadIcons();}); + + titleListChanged(this); + + return filteredList.size(); +} + +void GameList::sortList() { + std::sort(filteredList.begin(), filteredList.end(), nameSortCallback); +} + +bool GameList::nameSortCallback(const gameInfo *a, const gameInfo *b) { + return (strcasecmp(((gameInfo *) a)->name.c_str(), ((gameInfo *) b)->name.c_str()) < 0); +} + diff --git a/src/game/GameList.h b/src/game/GameList.h new file mode 100644 index 0000000..b8c206d --- /dev/null +++ b/src/game/GameList.h @@ -0,0 +1,102 @@ +#ifndef GAME_LIST_H_ +#define GAME_LIST_H_ + +#include +#include +#include +#include +#include + +typedef struct _gameInfo { + uint64_t titleId; + std::string name; + std::string gamePath; + GuiImageData * imageData; +} gameInfo; + +class GameList { +public: + GameList() : selectedGame(0) { }; + ~GameList() { clear(); }; + + int32_t size() const { + return filteredList.size(); + } + int32_t gameCount() const { + return fullGameList.size(); + } + int32_t filterList(const char * gameFilter = NULL); + int32_t loadUnfiltered(); + + gameInfo * at(int32_t i) const { + return operator[](i); + } + gameInfo * operator[](int32_t i) const { + if (i < 0 || i >= (int32_t) filteredList.size()) + return NULL; + return filteredList[i]; + } + gameInfo * getGameInfo(uint64_t titleId) const; + + const char * getCurrentFilter() const { + return gameFilter.c_str(); + } + void sortList(); + void clear(); + bool operator!() const { + return (fullGameList.size() == 0); + } + + //! Gamelist scrolling operators + int32_t operator+=(int32_t i) { + return (selectedGame = (selectedGame+i) % filteredList.size()); + } + int32_t operator-=(int32_t i) { + return (selectedGame = (selectedGame-i+filteredList.size()) % filteredList.size()); + } + int32_t operator++() { + return (selectedGame = (selectedGame+1) % filteredList.size()); + } + int32_t operator--() { + return (selectedGame = (selectedGame-1+filteredList.size()) % filteredList.size()); + } + int32_t operator++(int32_t i) { + return operator++(); + } + int32_t operator--(int32_t i) { + return operator--(); + } + gameInfo * GetCurrentSelected() const { + return operator[](selectedGame); + } + + std::vector & getfilteredList(void) { + return filteredList; + } + std::vector & getFullGameList(void) { + return fullGameList; + } + + sigslot::signal1 titleListChanged; + sigslot::signal1 titleUpdated; + sigslot::signal1 titleAdded; +protected: + + int32_t readGameList(); + + void internalFilterList(std::vector & fullList); + void internalLoadUnfiltered(std::vector & fullList); + + void loadIcons(); + + static bool nameSortCallback(const gameInfo *a, const gameInfo *b); + + static GameList *gameListInstance; + + std::string gameFilter; + int32_t selectedGame; + std::vector filteredList; + std::vector fullGameList; +}; + +#endif diff --git a/src/gui/GuiIconGrid.cpp b/src/gui/GuiIconGrid.cpp index 116d638..8b19dad 100644 --- a/src/gui/GuiIconGrid.cpp +++ b/src/gui/GuiIconGrid.cpp @@ -16,31 +16,155 @@ ****************************************************************************/ #include #include +#include #include "common/common.h" #include "Application.h" #include +#include "utils/logger.h" -GuiIconGrid::GuiIconGrid(int32_t w, int32_t h, int32_t GameIndex) +GuiIconGrid::GuiIconGrid(int32_t w, int32_t h, uint64_t GameIndex) : GuiTitleBrowser(w, h, GameIndex), - particleBgImage(w, h, 50, 60.0f, 90.0f, 0.6f, 1.0f) { + particleBgImage(w, h, 50, 60.0f, 90.0f, 0.6f, 1.0f) + , touchTrigger(GuiTrigger::CHANNEL_1, GuiTrigger::VPAD_TOUCH) + , wpadTouchTrigger(GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5, GuiTrigger::BUTTON_A) + , noIcon(Resources::GetFile("noGameIcon.png"), Resources::GetFileSize("noGameIcon.png"), GX2_TEX_CLAMP_MODE_MIRROR) { append(&particleBgImage); - + selectedGame = GameIndex; + listOffset = selectedGame / (MAX_COLS * MAX_ROWS); + targetLeftPosition = -listOffset * getWidth(); + currentLeftPosition = targetLeftPosition; } GuiIconGrid::~GuiIconGrid() { - + containerMutex.lock(); + for (auto const& x : gameInfoContainers) { + remove(x.second->button); + delete x.second; + } + gameInfoContainers.clear(); + containerMutex.unlock(); } -void GuiIconGrid::setSelectedGame(int32_t idx) { +void GuiIconGrid::setSelectedGame(uint64_t idx) { this->selectedGame = idx; } -int32_t GuiIconGrid::getSelectedGame(void) { +uint64_t GuiIconGrid::getSelectedGame(void) { return selectedGame; } +void GuiIconGrid::OnGameTitleListUpdated(GameList * gameList) { + containerMutex.lock(); + for(int32_t i = 0; i < gameList->size(); i++) { + gameInfo * info = gameList->at(i); + GameInfoContainer * container = NULL; + + for (auto const& x : gameInfoContainers) { + if(info->titleId == x.first) { + container = x.second; + break; + } + } + + if(container == NULL) { + OnGameTitleAdded(info); + } + } + containerMutex.unlock(); + bUpdatePositions = true; +} + +void GuiIconGrid::OnLaunchClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) { + //! do not auto launch when wiimote is pointing to screen and presses A + //if((trigger == &buttonATrigger) && (controller->chan & (GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5)) && controller->data.validPointer) { + // return; + //} + DEBUG_FUNCTION_LINE("Tried to launch %s (%016llX)\n", gameInfoContainers[getSelectedGame()]->info->name.c_str(),getSelectedGame()); + gameLaunchClicked(this, getSelectedGame()); +} + + +void GuiIconGrid::OnGameButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) { + containerMutex.lock(); + for (auto const& x : gameInfoContainers) { + if(x.second->button == button) { + if(selectedGame == (x.second->info->titleId)) { + if(gameLaunchTimer < 30) + OnLaunchClick(button, controller, trigger); + } else { + setSelectedGame(x.second->info->titleId); + gameSelectionChanged(this, selectedGame); + } + gameLaunchTimer = 0; + break; + } + } + containerMutex.unlock(); +} + +void GuiIconGrid::OnGameTitleAdded(gameInfo * info) { + DEBUG_FUNCTION_LINE("Adding %016llX\n", info->titleId); + GuiImage * image = new GuiImage(&noIcon); + GuiButton * button = new GuiButton(noIcon.getWidth(), noIcon.getHeight()); + button->setImage(image); + button->setPosition(0, 0); + button->setEffectGrow(); + button->setTrigger(&touchTrigger); + button->setTrigger(&wpadTouchTrigger); + button->setSoundClick(buttonClickSound); + //button->setClickable( (idx < gameList->size()) ); + //button->setSelectable( (idx < gameList->size()) ); + button->clicked.connect(this, &GuiIconGrid::OnGameButtonClick); + + GameInfoContainer * container = new GameInfoContainer(button, image, info); + containerMutex.lock(); + gameInfoContainers[info->titleId] = container; + containerMutex.unlock(); + this->append(button); + + bUpdatePositions = true; +} +void GuiIconGrid::OnGameTitleUpdated(gameInfo * info) { + DEBUG_FUNCTION_LINE("Updating infos of %016llX\n", info->titleId); + GameInfoContainer * container = NULL; + containerMutex.lock(); + for (auto const& x : gameInfoContainers) { + if(info->titleId == x.first) { + container = x.second; + break; + } + } + + if(container != NULL) { + container->updateImageData(); + } + containerMutex.unlock(); + bUpdatePositions = true; +} void GuiIconGrid::process() { + if(currentLeftPosition < targetLeftPosition) { + currentLeftPosition += 35; + + if(currentLeftPosition > targetLeftPosition) + currentLeftPosition = targetLeftPosition; + + bUpdatePositions = true; + } else if(currentLeftPosition > targetLeftPosition) { + currentLeftPosition -= 35; + + if(currentLeftPosition < targetLeftPosition) + currentLeftPosition = targetLeftPosition; + + bUpdatePositions = true; + } + + if(bUpdatePositions) { + bUpdatePositions = false; + updateButtonPositions(); + } + gameLaunchTimer++; + GuiFrame::process(); } @@ -48,6 +172,37 @@ void GuiIconGrid::update(GuiController * c) { GuiFrame::update(c); } -void GuiIconGrid::draw(CVideo *pVideo) { - GuiFrame::draw(pVideo); +void GuiIconGrid::updateButtonPositions() { + int32_t col = 0, row = 0, listOff = 0; + + int i = 0; + containerMutex.lock(); + for (auto const& x : gameInfoContainers) { + + listOff = i / (MAX_COLS * MAX_ROWS); + + float posX = currentLeftPosition + listOff * width + ( col * (noIcon.getWidth() + noIcon.getWidth() * 0.5f) - (MAX_COLS * 0.5f - 0.5f) * (noIcon.getWidth() + noIcon.getWidth() * 0.5f) ); + float posY = -row * (noIcon.getHeight() + noIcon.getHeight() * 0.5f) + (MAX_ROWS * 0.5f - 0.5f) * (noIcon.getHeight() + noIcon.getHeight() * 0.5f) + 30.0f; + + if(x.second->button != NULL) { + x.second->button->setPosition(posX, posY); + } + + col++; + if(col >= MAX_COLS) { + col = 0; + row++; + } + if(row >= MAX_ROWS) + row = 0; + + i++; + } + containerMutex.unlock(); +} + +void GuiIconGrid::draw(CVideo *pVideo) { + containerMutex.lock(); + GuiFrame::draw(pVideo); + containerMutex.unlock(); } diff --git a/src/gui/GuiIconGrid.h b/src/gui/GuiIconGrid.h index 4335980..714c3c4 100644 --- a/src/gui/GuiIconGrid.h +++ b/src/gui/GuiIconGrid.h @@ -19,25 +19,77 @@ #include #include "gui/GuiTitleBrowser.h" #include +#include "utils/AsyncExecutor.h" +#include "utils/logger.h" class GuiIconGrid : public GuiTitleBrowser, public sigslot::has_slots<> { public: - GuiIconGrid(int32_t w, int32_t h, int32_t GameIndex); + GuiIconGrid(int32_t w, int32_t h, uint64_t selectedTitleId); virtual ~GuiIconGrid(); - void setSelectedGame(int32_t idx); - int32_t getSelectedGame(void); + void setSelectedGame(uint64_t idx); + uint64_t getSelectedGame(void); void update(GuiController * t); void draw(CVideo *pVideo); void process(); + + void OnGameTitleListUpdated(GameList * list); + void OnAddGameTitle(gameInfo * info); + void OnGameTitleUpdated(gameInfo * info); + void OnGameTitleAdded(gameInfo * info); private: static const int32_t MAX_ROWS = 3; static const int32_t MAX_COLS = 5; GuiSound *buttonClickSound; - int32_t selectedGame = 0; - GuiParticleImage particleBgImage; + + GuiTrigger touchTrigger; + GuiTrigger wpadTouchTrigger; + + GuiImageData noIcon; + + void OnLaunchClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + void OnGameButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + void updateButtonPositions(); + + int32_t listOffset; + uint64_t selectedGame; + int32_t currentLeftPosition; + int32_t targetLeftPosition; + uint32_t gameLaunchTimer; + bool bUpdatePositions = false; + + class GameInfoContainer { + public: + GameInfoContainer(GuiButton* button, GuiImage * image, gameInfo * info) { + this->image = image; + this->info = info; + this->button = button; + } + + ~GameInfoContainer() { + if(button != NULL) { + AsyncExecutor::pushForDelete(button); + } + if(image != NULL) { + AsyncExecutor::pushForDelete(image); + } + } + + void updateImageData() { + if(image != NULL && info != NULL && info->imageData != NULL) { + image->setImageData(info->imageData); + } + } + + GuiImage * image; + gameInfo * info; + GuiButton * button; + }; + + CMutex containerMutex; + std::map gameInfoContainers; }; diff --git a/src/gui/GuiTitleBrowser.h b/src/gui/GuiTitleBrowser.h index 8866340..cc72240 100644 --- a/src/gui/GuiTitleBrowser.h +++ b/src/gui/GuiTitleBrowser.h @@ -2,15 +2,20 @@ #include #include +#include "game/GameList.h" class GuiTitleBrowser : public GuiFrame { public: - GuiTitleBrowser(int32_t w, int32_t h, int32_t GameIndex) : GuiFrame(w, h) {} + GuiTitleBrowser(int32_t w, int32_t h, uint64_t GameIndex) : GuiFrame(w, h) {} virtual ~GuiTitleBrowser() {} - virtual void setSelectedGame(int32_t idx) = 0; - virtual int32_t getSelectedGame(void) = 0; + virtual void setSelectedGame(uint64_t idx) = 0; + virtual uint64_t getSelectedGame(void) = 0; - sigslot::signal2 gameLaunchClicked; - sigslot::signal2 gameSelectionChanged; + virtual void OnGameTitleListUpdated(GameList * list) = 0; + virtual void OnGameTitleUpdated(gameInfo * info) = 0; + virtual void OnGameTitleAdded(gameInfo * info) = 0; + + sigslot::signal2 gameLaunchClicked; + sigslot::signal2 gameSelectionChanged; }; diff --git a/src/menu/MainWindow.cpp b/src/menu/MainWindow.cpp index 1815a69..95d1aac 100644 --- a/src/menu/MainWindow.cpp +++ b/src/menu/MainWindow.cpp @@ -40,9 +40,15 @@ MainWindow::MainWindow(int32_t w, int32_t h) pointerValid[i] = false; } SetupMainView(); + gameList.titleListChanged.connect(this, &MainWindow::OnGameTitleListChanged); + gameList.titleUpdated.connect(this, &MainWindow::OnGameTitleUpdated); + gameList.titleAdded.connect(this, &MainWindow::OnGameTitleAdded); + AsyncExecutor::execute([&] {gameList.loadUnfiltered();}); + } MainWindow::~MainWindow() { + gameList.titleListChanged.disconnect(this); while(!tvElements.empty()) { delete tvElements[0]; remove(tvElements[0]); @@ -103,6 +109,31 @@ void MainWindow::process() { } } +void MainWindow::OnGameTitleListChanged(GameList * list) { + currentTvFrame->OnGameTitleListUpdated(list); + if(currentTvFrame != currentDrcFrame) { + currentDrcFrame->OnGameTitleListUpdated(list); + } +} + +void MainWindow::OnGameTitleUpdated(gameInfo * info) { + currentTvFrame->OnGameTitleUpdated(info); + if(currentTvFrame != currentDrcFrame) { + currentDrcFrame->OnGameTitleUpdated(info); + } +} + +void MainWindow::OnGameTitleAdded(gameInfo * info) { + DEBUG_FUNCTION_LINE("%08X\n", info); + if(info == NULL) { + return; + } + currentTvFrame->OnGameTitleAdded(info); + if(currentTvFrame != currentDrcFrame) { + currentDrcFrame->OnGameTitleAdded(info); + } +} + void MainWindow::update(GuiController *controller) { //! dont read behind the initial elements in case one was added //uint32_t tvSize = tvElements.size(); @@ -179,6 +210,7 @@ void MainWindow::SetupMainView() { currentTvFrame->setEffect(EFFECT_FADE, 10, 255); currentTvFrame->setState(GuiElement::STATE_DISABLED); currentTvFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); + appendTv(currentTvFrame); currentDrcFrame = new GuiIconGrid(width, height,0); @@ -186,7 +218,6 @@ void MainWindow::SetupMainView() { currentDrcFrame->setState(GuiElement::STATE_DISABLED); currentDrcFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); - if(currentTvFrame != currentDrcFrame) { currentDrcFrame->setEffect(EFFECT_FADE, 10, 255); currentDrcFrame->setState(GuiElement::STATE_DISABLED); @@ -288,11 +319,11 @@ void MainWindow::OnCloseEffectFinish(GuiElement *element) { AsyncExecutor::pushForDelete(element); } -void MainWindow::OnSettingsButtonClicked(GuiElement *element){ +void MainWindow::OnSettingsButtonClicked(GuiElement *element) { } -void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, int32_t selectedIdx) { +void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, uint64_t selectedIdx) { if(!currentDrcFrame || !currentTvFrame) return; @@ -303,6 +334,6 @@ void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, int32_t selecte } } -void MainWindow::OnGameLaunch(GuiTitleBrowser *element, int32_t selectedIdx) { +void MainWindow::OnGameLaunch(GuiTitleBrowser *element, uint64_t selectedIdx) { } diff --git a/src/menu/MainWindow.h b/src/menu/MainWindow.h index 5bb2f9a..c2387ae 100644 --- a/src/menu/MainWindow.h +++ b/src/menu/MainWindow.h @@ -20,6 +20,7 @@ #include #include #include +#include "game/GameList.h" #include "system/CMutex.h" #include "gui/GuiTitleBrowser.h" #include "MainDrcButtonsFrame.h" @@ -116,13 +117,17 @@ private: void OnOpenEffectFinish(GuiElement *element); void OnCloseEffectFinish(GuiElement *element); - void OnGameLaunch(GuiTitleBrowser *element, int32_t gameIdx); - void OnGameSelectionChange(GuiTitleBrowser *element, int32_t selectedIdx); + void OnGameLaunch(GuiTitleBrowser *element, uint64_t gameIdx); + void OnGameSelectionChange(GuiTitleBrowser *element, uint64_t selectedIdx); void OnSettingsButtonClicked(GuiElement *element); void OnLayoutSwitchClicked(GuiElement *element); void OnLayoutSwitchEffectFinish(GuiElement *element); + void OnGameTitleListChanged(GameList * list); + void OnGameTitleUpdated(gameInfo * info); + void OnGameTitleAdded(gameInfo * info); + int32_t width, height; std::vector drcElements; std::vector tvElements; @@ -138,6 +143,8 @@ private: GuiImage *pointerImg[4]; bool pointerValid[4]; + GameList gameList; + CMutex guiMutex; }; diff --git a/src/utils/AsyncExecutor.cpp b/src/utils/AsyncExecutor.cpp index 2458ae5..edec646 100644 --- a/src/utils/AsyncExecutor.cpp +++ b/src/utils/AsyncExecutor.cpp @@ -11,5 +11,10 @@ void AsyncExecutor::execute(std::function func) { if(!instance) { instance = new AsyncExecutor(); } - instance->elements.push_back(std::async(std::launch::async,func)); + instance->elements.push(std::async(std::launch::async,func)); + if(instance->elements.size() >= 25){ + //DEBUG_FUNCTION_LINE("Wait on queue %d\n",instance->elements.size()); + instance->elements.front().get(); + instance->elements.pop(); + } } diff --git a/src/utils/AsyncExecutor.h b/src/utils/AsyncExecutor.h index 4b70be3..599d6ec 100644 --- a/src/utils/AsyncExecutor.h +++ b/src/utils/AsyncExecutor.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -22,5 +22,5 @@ private: AsyncExecutor() {} ~AsyncExecutor() {} - std::vector> elements; + std::queue> elements; };