From 9b3c14a93103ce883b7dea991719b7a013e85171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2024 14:50:16 +0100 Subject: [PATCH 1/4] GameInfoCache: Break out a couple functions --- UI/GameInfoCache.cpp | 62 +++++++++++++++++++++++--------------------- UI/GameInfoCache.h | 4 ++- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index c67e16643174..1e9759fcf034 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -338,6 +338,38 @@ void GameInfo::SetTitle(const std::string &newTitle) { title = newTitle; } +void GameInfo::FinishPendingTextureLoads(Draw::DrawContext *draw) { + if (draw && icon.dataLoaded && !icon.texture) { + SetupTexture(draw, icon); + } + if (draw && pic0.dataLoaded && !pic0.texture) { + SetupTexture(draw, pic0); + } + if (draw && pic1.dataLoaded && !pic1.texture) { + SetupTexture(draw, pic1); + } +} + +void GameInfo::SetupTexture(Draw::DrawContext *thin3d, GameInfoTex &tex) { + using namespace Draw; + if (tex.data.size()) { + if (!tex.texture) { + // TODO: Use TempImage to semi-load the image in the worker task, then here we + // could just call CreateTextureFromTempImage. + tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT, false, GetTitle().c_str()); + if (tex.texture) { + tex.timeLoaded = time_now_d(); + } else { + ERROR_LOG(G3D, "Failed creating texture (%s) from %d-byte file", GetTitle().c_str(), (int)tex.data.size()); + } + } + if ((wantFlags & GAMEINFO_WANTBGDATA) == 0) { + tex.data.clear(); + tex.dataLoaded = false; + } + } +} + static bool ReadFileToString(IFileSystem *fs, const char *filename, std::string *contents, std::mutex *mtx) { PSPFileInfo info = fs->GetFileInfo(filename); if (!info.exists) { @@ -822,15 +854,7 @@ std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const // If wantFlags don't match, we need to start over. We'll just queue the work item again. if (info && (info->wantFlags & wantFlags) == wantFlags) { - if (draw && info->icon.dataLoaded && !info->icon.texture) { - SetupTexture(info, draw, info->icon); - } - if (draw && info->pic0.dataLoaded && !info->pic0.texture) { - SetupTexture(info, draw, info->pic0); - } - if (draw && info->pic1.dataLoaded && !info->pic1.texture) { - SetupTexture(info, draw, info->pic1); - } + info->FinishPendingTextureLoads(draw); info->lastAccessedTime = time_now_d(); return info; } @@ -860,23 +884,3 @@ std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const info_[pathStr] = info; return info; } - -void GameInfoCache::SetupTexture(std::shared_ptr &info, Draw::DrawContext *thin3d, GameInfoTex &tex) { - using namespace Draw; - if (tex.data.size()) { - if (!tex.texture) { - // TODO: Use TempImage to semi-load the image in the worker task, then here we - // could just call CreateTextureFromTempImage. - tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT, false, info->GetTitle().c_str()); - if (tex.texture) { - tex.timeLoaded = time_now_d(); - } else { - ERROR_LOG(G3D, "Failed creating texture (%s) from %d-byte file", info->GetTitle().c_str(), (int)tex.data.size()); - } - } - if ((info->wantFlags & GAMEINFO_WANTBGDATA) == 0) { - tex.data.clear(); - tex.dataLoaded = false; - } - } -} diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index d40d32619647..0c2450093a4a 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -95,6 +95,7 @@ class GameInfo { u64 GetInstallDataSizeInBytes(); void ParseParamSFO(); + void FinishPendingTextureLoads(Draw::DrawContext *draw); std::vector GetSaveDataDirectories(); @@ -158,6 +159,8 @@ class GameInfo { std::shared_ptr fileLoader; Path filePath_; + void SetupTexture(Draw::DrawContext *draw, GameInfoTex &tex); + private: DISALLOW_COPY_AND_ASSIGN(GameInfo); }; @@ -184,7 +187,6 @@ class GameInfoCache { private: void Init(); void Shutdown(); - void SetupTexture(std::shared_ptr &info, Draw::DrawContext *draw, GameInfoTex &tex); // Maps ISO path to info. Need to use shared_ptr as we can return these pointers - // and if they get destructed while being in use, that's bad. From 884c366692902a58b5b8b36b39857435703d0aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2024 16:23:27 +0100 Subject: [PATCH 2/4] Fix some longstanding issues with the GameInfoCache and optimize away double work --- Common/Common.h | 7 + UI/BackgroundAudio.cpp | 6 +- UI/CwCheatScreen.cpp | 4 +- UI/EmuScreen.cpp | 8 +- UI/GameInfoCache.cpp | 349 +++++++++++++++++++------------------- UI/GameInfoCache.h | 36 ++-- UI/GameScreen.cpp | 39 +++-- UI/GameSettingsScreen.cpp | 4 +- UI/MainScreen.cpp | 18 +- UI/MiscScreens.cpp | 13 +- UI/PauseScreen.cpp | 31 ++-- UI/SavedataScreen.cpp | 30 ++-- 12 files changed, 285 insertions(+), 260 deletions(-) diff --git a/Common/Common.h b/Common/Common.h index c5cc45ebaa79..c24342f6a23c 100644 --- a/Common/Common.h +++ b/Common/Common.h @@ -47,6 +47,13 @@ } \ static inline bool operator &(const T &lhs, const T &rhs) { \ return ((int)lhs & (int)rhs) != 0; \ + } \ + static inline T &operator &= (T &lhs, const T &rhs) { \ + lhs = (T)((int)lhs & (int)rhs); \ + return lhs; \ + } \ + static inline T operator ~(const T &rhs) { \ + return (T)(~((int)rhs)); \ } #endif diff --git a/UI/BackgroundAudio.cpp b/UI/BackgroundAudio.cpp index 344d65ed68bb..c9ddd9c19031 100644 --- a/UI/BackgroundAudio.cpp +++ b/UI/BackgroundAudio.cpp @@ -147,7 +147,7 @@ void WavData::Read(RIFFReader &file_) { numFrames = numBytes / raw_bytes_per_frame; // numFrames // It seems the atrac3 codec likes to read a little bit outside. - int padding = 16; + const int padding = 32; // 32 is the value FFMPEG uses. raw_data = (uint8_t *)malloc(numBytes + padding); raw_data_size = numBytes; @@ -359,8 +359,8 @@ void BackgroundAudio::Update() { return; // Grab some audio from the current game and play it. - std::shared_ptr gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GAMEINFO_WANTSND); - if (!gameInfo || gameInfo->pending) { + std::shared_ptr gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GameInfoFlags::SND); + if (!gameInfo->Ready(GameInfoFlags::SND)) { // Should try again shortly.. return; } diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 12798f5c57d5..d03ed87a4a2b 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -48,9 +48,9 @@ CwCheatScreen::~CwCheatScreen() { } bool CwCheatScreen::TryLoadCheatInfo() { - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); std::string gameID; - if (info && info->paramSFOLoaded) { + if (info && info->Ready(GameInfoFlags::PARAM_SFO)) { gameID = info->paramSFO.GetValueString("DISC_ID"); } else { return false; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 2666260ca86c..e1158aff52dd 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -269,8 +269,8 @@ void EmuScreen::bootGame(const Path &filename) { invalid_ = true; // We don't want to boot with the wrong game specific config, so wait until info is ready. - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, filename, 0); - if (!info || info->pending) + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, filename, GameInfoFlags::PARAM_SFO); + if (!info || info->Ready(GameInfoFlags::PARAM_SFO)) return; auto sc = GetI18NCategory(I18NCat::SCREEN); @@ -952,11 +952,11 @@ class GameInfoBGView : public UI::InertView { void Draw(UIContext &dc) override { // Should only be called when visible. - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GAMEINFO_WANTBG); + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GameInfoFlags::BG); dc.Flush(); // PIC1 is the loading image, so let's only draw if it's available. - if (ginfo && ginfo->pic1.texture) { + if (ginfo && ginfo->Ready(GameInfoFlags::BG) && ginfo->pic1.texture) { Draw::Texture *texture = ginfo->pic1.texture; if (texture) { dc.GetDrawContext()->BindTexture(0, texture); diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 1e9759fcf034..7763ed37f49f 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -54,8 +54,9 @@ void GameInfoTex::Clear() { } } -GameInfo::GameInfo() : fileType(IdentifiedFileType::UNKNOWN) { - pending = true; +GameInfo::GameInfo() { + // here due to a forward decl. + fileType = IdentifiedFileType::UNKNOWN; } GameInfo::~GameInfo() { @@ -275,6 +276,7 @@ void GameInfo::DisposeFileLoader() { } bool GameInfo::DeleteAllSaveData() { + _assert_(hasFlags & GameInfoFlags::PARAM_SFO); // so we know we have the ID. std::vector saveDataDir = GetSaveDataDirectories(); for (size_t j = 0; j < saveDataDir.size(); j++) { std::vector fileInfo; @@ -324,13 +326,15 @@ void GameInfo::ParseParamSFO() { region = GAMEREGION_CHINA; }*/ } - - paramSFOLoaded = true; } std::string GameInfo::GetTitle() { std::lock_guard guard(lock); - return title; + if (hasFlags & GameInfoFlags::PARAM_SFO) { + return title; + } else { + return filePath_.GetFilename(); + } } void GameInfo::SetTitle(const std::string &newTitle) { @@ -352,21 +356,13 @@ void GameInfo::FinishPendingTextureLoads(Draw::DrawContext *draw) { void GameInfo::SetupTexture(Draw::DrawContext *thin3d, GameInfoTex &tex) { using namespace Draw; - if (tex.data.size()) { - if (!tex.texture) { - // TODO: Use TempImage to semi-load the image in the worker task, then here we - // could just call CreateTextureFromTempImage. - tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT, false, GetTitle().c_str()); - if (tex.texture) { - tex.timeLoaded = time_now_d(); - } else { - ERROR_LOG(G3D, "Failed creating texture (%s) from %d-byte file", GetTitle().c_str(), (int)tex.data.size()); - } - } - if ((wantFlags & GAMEINFO_WANTBGDATA) == 0) { - tex.data.clear(); - tex.dataLoaded = false; - } + // TODO: Use TempImage to semi-load the image in the worker task, then here we + // could just call CreateTextureFromTempImage. + tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT, false, GetTitle().c_str()); + if (tex.texture) { + tex.timeLoaded = time_now_d(); + } else { + ERROR_LOG(G3D, "Failed creating texture (%s) from %d-byte file", GetTitle().c_str(), (int)tex.data.size()); } } @@ -434,15 +430,11 @@ static bool ReadVFSToString(const char *filename, std::string *contents, std::mu class GameInfoWorkItem : public Task { public: - GameInfoWorkItem(const Path &gamePath, std::shared_ptr &info) - : gamePath_(gamePath), info_(info) { - } + GameInfoWorkItem(const Path &gamePath, std::shared_ptr &info, GameInfoFlags flags) + : gamePath_(gamePath), info_(info), flags_(flags) {} ~GameInfoWorkItem() { - info_->pending.store(false); - info_->working.store(false); info_->DisposeFileLoader(); - info_->readyEvent.Notify(); } TaskType Type() const override { @@ -475,8 +467,11 @@ class GameInfoWorkItem : public Task { std::string errorString; - info_->working = true; - info_->fileType = Identify_File(info_->GetFileLoader().get(), &errorString); + if (flags_ & GameInfoFlags::FILE_TYPE) { + info_->fileType = Identify_File(info_->GetFileLoader().get(), &errorString); + info_->hasConfig = g_Config.hasGameConfig(info_->id); + } + switch (info_->fileType) { case IdentifiedFileType::PSP_PBP: case IdentifiedFileType::PSP_PBP_DIRECTORY: @@ -499,41 +494,45 @@ class GameInfoWorkItem : public Task { } // First, PARAM.SFO. - std::vector sfoData; - if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) { - std::lock_guard lock(info_->lock); - info_->paramSFO.ReadSFO(sfoData); - info_->ParseParamSFO(); + if (flags_ & GameInfoFlags::PARAM_SFO) { + std::vector sfoData; + if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) { + std::lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO(sfoData); + info_->ParseParamSFO(); - // Assuming PSP_PBP_DIRECTORY without ID or with disc_total < 1 in GAME dir must be homebrew - if ((info_->id.empty() || !info_->disc_total) - && gamePath_.FilePathContainsNoCase("PSP/GAME/") - && info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) { - info_->id = g_paramSFO.GenerateFakeID(gamePath_); - info_->id_version = info_->id + "_1.00"; - info_->region = GAMEREGION_MAX + 1; // Homebrew + // Assuming PSP_PBP_DIRECTORY without ID or with disc_total < 1 in GAME dir must be homebrew + if ((info_->id.empty() || !info_->disc_total) + && gamePath_.FilePathContainsNoCase("PSP/GAME/") + && info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) { + info_->id = g_paramSFO.GenerateFakeID(gamePath_); + info_->id_version = info_->id + "_1.00"; + info_->region = GAMEREGION_MAX + 1; // Homebrew + } } } // Then, ICON0.PNG. - if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) { - std::lock_guard lock(info_->lock); - pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data); - } else { - Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); - Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); - // Try using png/jpg screenshots first - if (File::Exists(screenshot_png)) - ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); - else if (File::Exists(screenshot_jpg)) - ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock); - else - // Read standard icon - ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); + if (flags_ & GameInfoFlags::ICON) { + if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) { + std::lock_guard lock(info_->lock); + pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data); + } else { + Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); + Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); + // Try using png/jpg screenshots first + if (File::Exists(screenshot_png)) + ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); + else if (File::Exists(screenshot_jpg)) + ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock); + else + // Read standard icon + ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); + } + info_->icon.dataLoaded = true; } - info_->icon.dataLoaded = true; - if (info_->wantFlags & GAMEINFO_WANTBG) { + if (flags_ & GameInfoFlags::BG) { if (pbp.GetSubFileSize(PBP_PIC0_PNG) > 0) { std::string data; pbp.GetSubFileAsString(PBP_PIC0_PNG, &data); @@ -549,7 +548,7 @@ class GameInfoWorkItem : public Task { info_->pic1.dataLoaded = true; } } - if (info_->wantFlags & GAMEINFO_WANTSND) { + if (flags_ & GameInfoFlags::SND) { if (pbp.GetSubFileSize(PBP_SND0_AT3) > 0) { std::string data; pbp.GetSubFileAsString(PBP_SND0_AT3, &data); @@ -564,17 +563,17 @@ class GameInfoWorkItem : public Task { case IdentifiedFileType::PSP_ELF: handleELF: // An elf on its own has no usable information, no icons, no nothing. - { - std::lock_guard lock(info_->lock); + if (flags_ & GameInfoFlags::PARAM_SFO) { info_->id = g_paramSFO.GenerateFakeID(gamePath_); info_->id_version = info_->id + "_1.00"; info_->region = GAMEREGION_MAX + 1; // Homebrew - - info_->paramSFOLoaded = true; } - { - Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); - Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); + + if (flags_ & GameInfoFlags::ICON) { + std::string id = g_paramSFO.GenerateFakeID(gamePath_); + // Due to the dependency of the BASIC info, we fetch it already here. + Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (id + "_00000.jpg"); + Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (id + "_00000.png"); // Try using png/jpg screenshots first if (File::Exists(screenshot_png)) { ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); @@ -594,17 +593,20 @@ class GameInfoWorkItem : public Task { SequentialHandleAllocator handles; VirtualDiscFileSystem umd(&handles, gamePath_); - // Alright, let's fetch the PARAM.SFO. - std::string paramSFOcontents; - if (ReadFileToString(&umd, "/PARAM.SFO", ¶mSFOcontents, 0)) { - std::lock_guard lock(info_->lock); - info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); - info_->ParseParamSFO(); + if (flags_ & GameInfoFlags::PARAM_SFO) { + // Alright, let's fetch the PARAM.SFO. + std::string paramSFOcontents; + if (ReadFileToString(&umd, "/PARAM.SFO", ¶mSFOcontents, 0)) { + std::lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); + info_->ParseParamSFO(); + } } - - ReadFileToString(&umd, "/ICON0.PNG", &info_->icon.data, &info_->lock); - info_->icon.dataLoaded = true; - if (info_->wantFlags & GAMEINFO_WANTBG) { + if (flags_ & GameInfoFlags::ICON) { + ReadFileToString(&umd, "/ICON0.PNG", &info_->icon.data, &info_->lock); + info_->icon.dataLoaded = true; + } + if (flags_ & GameInfoFlags::BG) { ReadFileToString(&umd, "/PIC1.PNG", &info_->pic1.data, &info_->lock); info_->pic1.dataLoaded = true; } @@ -613,34 +615,30 @@ class GameInfoWorkItem : public Task { case IdentifiedFileType::PPSSPP_SAVESTATE: { - Path screenshotPath; - { + if (flags_ & GameInfoFlags::PARAM_SFO) { info_->SetTitle(SaveState::GetTitle(gamePath_)); - std::lock_guard guard(info_->lock); - screenshotPath = gamePath_.WithReplacedExtension(".ppst", ".jpg"); } // Let's use the screenshot as an icon, too. - if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) { - info_->icon.dataLoaded = true; - } else { - ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str()); + if (flags_ & GameInfoFlags::ICON) { + Path screenshotPath = gamePath_.WithReplacedExtension(".ppst", ".jpg"); + if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) { + info_->icon.dataLoaded = true; + } else { + ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str()); + } } break; } case IdentifiedFileType::PPSSPP_GE_DUMP: { - Path screenshotPath; - - { - std::lock_guard guard(info_->lock); - screenshotPath = gamePath_.WithReplacedExtension(".ppdmp", ".png"); - } - - // Let's use the comparison screenshot as an icon, if it exists. - if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) { - info_->icon.dataLoaded = true; + if (flags_ & GameInfoFlags::ICON) { + Path screenshotPath = gamePath_.WithReplacedExtension(".ppdmp", ".png"); + // Let's use the comparison screenshot as an icon, if it exists. + if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) { + info_->icon.dataLoaded = true; + } } break; } @@ -651,22 +649,24 @@ class GameInfoWorkItem : public Task { VirtualDiscFileSystem umd(&handles, gamePath_); // Alright, let's fetch the PARAM.SFO. - std::string paramSFOcontents; - if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents, 0)) { - std::lock_guard lock(info_->lock); - info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); - info_->ParseParamSFO(); + if (flags_ & GameInfoFlags::PARAM_SFO) { + std::string paramSFOcontents; + if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents, 0)) { + std::lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); + info_->ParseParamSFO(); + } } ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; - if (info_->wantFlags & GAMEINFO_WANTBG) { + if (flags_ & GameInfoFlags::BG) { ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, &info_->lock); info_->pic0.dataLoaded = true; ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, &info_->lock); info_->pic1.dataLoaded = true; } - if (info_->wantFlags & GAMEINFO_WANTSND) { + if (flags_ & GameInfoFlags::SND) { ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock); info_->pic1.dataLoaded = true; } @@ -691,60 +691,63 @@ class GameInfoWorkItem : public Task { ISOFileSystem umd(&handles, bd); // Alright, let's fetch the PARAM.SFO. - std::string paramSFOcontents; - if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents, nullptr)) { - { - std::lock_guard lock(info_->lock); - info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); - info_->ParseParamSFO(); - } - if (info_->wantFlags & GAMEINFO_WANTBG) { - info_->pic0.dataLoaded = ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, &info_->lock); - info_->pic1.dataLoaded = ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, &info_->lock); - } - if (info_->wantFlags & GAMEINFO_WANTSND) { - info_->sndDataLoaded = ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock); + if (flags_ & GameInfoFlags::PARAM_SFO) { + std::string paramSFOcontents; + if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents, nullptr)) { + { + std::lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); + info_->ParseParamSFO(); + } } } + if (flags_ & GameInfoFlags::BG) { + info_->pic0.dataLoaded = ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, &info_->lock); + info_->pic1.dataLoaded = ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, &info_->lock); + } + + if (flags_ & GameInfoFlags::SND) { + info_->sndDataLoaded = ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock); + } + // Fall back to unknown icon if ISO is broken/is a homebrew ISO, override is allowed though - if (!ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock)) { - Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); - Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); - // Try using png/jpg screenshots first - if (File::Exists(screenshot_png)) - info_->icon.dataLoaded = ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); - else if (File::Exists(screenshot_jpg)) - info_->icon.dataLoaded = ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock); - else { - DEBUG_LOG(LOADER, "Loading unknown.png because no icon was found"); - info_->icon.dataLoaded = ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); + if (flags_ & GameInfoFlags::ICON) { + if (!ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock)) { + Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); + Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); + // Try using png/jpg screenshots first + if (File::Exists(screenshot_png)) + info_->icon.dataLoaded = ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); + else if (File::Exists(screenshot_jpg)) + info_->icon.dataLoaded = ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock); + else { + DEBUG_LOG(LOADER, "Loading unknown.png because no icon was found"); + info_->icon.dataLoaded = ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); + } + } else { + info_->icon.dataLoaded = true; } - } else { - info_->icon.dataLoaded = true; } break; } case IdentifiedFileType::ARCHIVE_ZIP: - info_->paramSFOLoaded = true; - { + if (flags_ & GameInfoFlags::ICON) { ReadVFSToString("zip.png", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; } break; case IdentifiedFileType::ARCHIVE_RAR: - info_->paramSFOLoaded = true; - { + if (flags_ & GameInfoFlags::ICON) { ReadVFSToString("rargray.png", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; } break; case IdentifiedFileType::ARCHIVE_7Z: - info_->paramSFOLoaded = true; - { + if (flags_ & GameInfoFlags::ICON) { ReadVFSToString("7z.png", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; } @@ -752,28 +755,31 @@ class GameInfoWorkItem : public Task { case IdentifiedFileType::NORMAL_DIRECTORY: default: - info_->paramSFOLoaded = true; break; } - info_->hasConfig = g_Config.hasGameConfig(info_->id); - - if (info_->wantFlags & GAMEINFO_WANTSIZE) { + if (flags_ & GameInfoFlags::SIZE) { std::lock_guard lock(info_->lock); info_->gameSizeOnDisk = info_->GetGameSizeOnDiskInBytes(); info_->saveDataSize = info_->GetSaveDataSizeInBytes(); info_->installDataSize = info_->GetInstallDataSizeInBytes(); } - if (info_->wantFlags & GAMEINFO_WANTUNCOMPRESSEDSIZE) { + if (flags_ & GameInfoFlags::UNCOMPRESSED_SIZE) { info_->gameSizeUncompressed = info_->GetGameSizeUncompressedInBytes(); } + // Time to update the flags. + std::unique_lock lock(info_->lock); + info_->hasFlags |= flags_; + info_->pendingFlags &= ~flags_; // INFO_LOG(SYSTEM, "Completed writing info for %s", info_->GetTitle().c_str()); } private: Path gamePath_; std::shared_ptr info_; + GameInfoFlags flags_{}; + DISALLOW_COPY_AND_ASSIGN(GameInfoWorkItem); }; @@ -820,14 +826,18 @@ void GameInfoCache::FlushBGs() { iter->second->sndFileData.clear(); iter->second->sndDataLoaded = false; } - iter->second->wantFlags &= ~(GAMEINFO_WANTBG | GAMEINFO_WANTSND | GAMEINFO_WANTBGDATA); + iter->second->hasFlags &= ~(GameInfoFlags::BG | GameInfoFlags::SND); } } void GameInfoCache::PurgeType(IdentifiedFileType fileType) { for (auto iter = info_.begin(); iter != info_.end();) { auto &info = iter->second; - info->readyEvent.Wait(); + + // TODO: Find a better way to wait here. + while (info->pendingFlags != (GameInfoFlags)0) { + sleep_ms(1); + } if (info->fileType == fileType) { iter = info_.erase(iter); } else { @@ -836,51 +846,48 @@ void GameInfoCache::PurgeType(IdentifiedFileType fileType) { } } -void GameInfoCache::WaitUntilDone(std::shared_ptr &info) { - info->readyEvent.Wait(); -} - -// Runs on the main thread. Only call from render() and similar, not update()! +// Call on the main thread ONLY - that is from stuff called from NativeFrame. // Can also be called from the audio thread for menu background music, but that cannot request images! -std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, int wantFlags) { - std::shared_ptr info; - +std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags) { const std::string &pathStr = gamePath.ToString(); + // This is always needed to determine the method to get the other info, so make sure it's computed first. + wantFlags |= GameInfoFlags::FILE_TYPE; + + mapLock_.lock(); + auto iter = info_.find(pathStr); if (iter != info_.end()) { - info = iter->second; - } + // There's already a structure about this game. Let's check. + std::shared_ptr info = iter->second; + mapLock_.unlock(); - // If wantFlags don't match, we need to start over. We'll just queue the work item again. - if (info && (info->wantFlags & wantFlags) == wantFlags) { info->FinishPendingTextureLoads(draw); info->lastAccessedTime = time_now_d(); + GameInfoFlags wanted = (GameInfoFlags)0; + { + // Careful now! + std::unique_lock lock(info->lock); + GameInfoFlags hasFlags = info->hasFlags | info->pendingFlags; // We don't want to re-fetch data that we have, so or in pendingFlags. + wanted = (GameInfoFlags)((int)wantFlags & ~(int)hasFlags); // & is reserved for testing. ugh. + info->pendingFlags |= wanted; + } + if (wanted != (GameInfoFlags)0) { + // We're missing info that we want. Go get it! + GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wanted); + g_threadManager.EnqueueTask(item); + } return info; } - if (!info) { - info = std::make_shared(); - } - - if (info->working) { - // Uh oh, it's currently in process. It could mark pending = false with the wrong wantFlags. - // Let's wait it out, then queue. - // NOTE: This is bad because we're likely on the UI thread.... - WaitUntilDone(info); - } + std::shared_ptr info = std::make_shared(); + info->pendingFlags = wantFlags; + info->lastAccessedTime = time_now_d(); + info_.insert(std::make_pair(pathStr, info)); + mapLock_.unlock(); - { - std::lock_guard lock(info->lock); - info->wantFlags |= wantFlags; - info->pending = true; - } - - GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info); + // Just get all the stuff we wanted. + GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wantFlags); g_threadManager.EnqueueTask(item); - - // Don't re-insert if we already have it. - if (info_.find(pathStr) == info_.end()) - info_[pathStr] = info; return info; } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 0c2450093a4a..5550276a5b86 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -50,13 +50,16 @@ enum GameRegion { GAMEREGION_MAX, }; -enum GameInfoWantFlags { - GAMEINFO_WANTBG = 0x01, - GAMEINFO_WANTSIZE = 0x02, - GAMEINFO_WANTSND = 0x04, - GAMEINFO_WANTBGDATA = 0x08, // Use with WANTBG. - GAMEINFO_WANTUNCOMPRESSEDSIZE = 0x10, +enum class GameInfoFlags { + FILE_TYPE = 0x01, // Don't need to specify this, always included. + PARAM_SFO = 0x02, + ICON = 0x04, + BG = 0x08, + SND = 0x10, + SIZE = 0x20, + UNCOMPRESSED_SIZE = 0x40, }; +ENUM_CLASS_BITOPS(GameInfoFlags); class FileLoader; enum class IdentifiedFileType; @@ -102,6 +105,11 @@ class GameInfo { std::string GetTitle(); void SetTitle(const std::string &newTitle); + bool Ready(GameInfoFlags flags) { + std::unique_lock guard(lock); + return (hasFlags & flags) != 0; + } + GameInfoTex *GetBGPic() { if (pic1.texture) return &pic1; @@ -119,6 +127,11 @@ class GameInfo { // Controls access to the fileLoader pointer. std::mutex loaderLock; + // Keep track of what we have, or what we're processing. + // These are protected by the mutex. While pendingFlags != 0, something is being loaded. + GameInfoFlags hasFlags{}; + GameInfoFlags pendingFlags{}; + std::string id; std::string id_version; int disc_total = 0; @@ -126,7 +139,6 @@ class GameInfo { int region = -1; IdentifiedFileType fileType; ParamSFOData paramSFO; - bool paramSFOLoaded = false; bool hasConfig = false; // Pre read the data, create a texture the next time (GL thread..) @@ -137,8 +149,6 @@ class GameInfo { std::string sndFileData; std::atomic sndDataLoaded{}; - int wantFlags = 0; - double lastAccessedTime = 0.0; u64 gameSizeUncompressed = 0; @@ -146,11 +156,6 @@ class GameInfo { u64 saveDataSize = 0; u64 installDataSize = 0; - std::atomic pending{}; - std::atomic working{}; - - Event readyEvent; - protected: // Note: this can change while loading, use GetTitle(). std::string title; @@ -178,7 +183,7 @@ class GameInfoCache { // but filled in later asynchronously in the background. So keep calling this, // redrawing the UI often. Only set flags to GAMEINFO_WANTBG or WANTSND if you really want them // because they're big. bgTextures and sound may be discarded over time as well. - std::shared_ptr GetInfo(Draw::DrawContext *draw, const Path &gamePath, int wantFlags); + std::shared_ptr GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags); void FlushBGs(); // Gets rid of all BG textures. Also gets rid of bg sounds. void CancelAll(); @@ -191,6 +196,7 @@ class GameInfoCache { // Maps ISO path to info. Need to use shared_ptr as we can return these pointers - // and if they get destructed while being in use, that's bad. std::map > info_; + std::mutex mapLock_; }; // This one can be global, no good reason not to. diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 9ee83b9fe47e..010483e9e573 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -86,7 +86,7 @@ void GameScreen::update() { } void GameScreen::CreateViews() { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::BG); if (info && !info->id.empty()) { saveDirs = info->GetSaveDataDirectories(); // Get's very heavy, let's not do it in update() @@ -187,8 +187,9 @@ void GameScreen::CreateViews() { rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Delete Game"))))->OnClick.Handle(this, &GameScreen::OnDeleteGame); if (System_GetPropertyBool(SYSPROP_CAN_CREATE_SHORTCUT)) { rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Create Shortcut"))))->OnClick.Add([=](UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); - if (info) { + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); + if (info->Ready(GameInfoFlags::PARAM_SFO)) { + // TODO: Should we block on Ready? System_CreateGameShortcut(gamePath_, info->GetTitle()); } return UI::EVENT_DONE; @@ -241,8 +242,8 @@ UI::Choice *GameScreen::AddOtherChoice(UI::Choice *choice) { } UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); - if (!info) { + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); + if (!info->Ready(GameInfoFlags::PARAM_SFO)) { return UI::EVENT_SKIPPED; } g_Config.createGameConfig(info->id); @@ -255,8 +256,8 @@ UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) { void GameScreen::CallbackDeleteConfig(bool yes) { if (yes) { - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); - if (!info) { + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); + if (!info->Ready(GameInfoFlags::PARAM_SFO)) { return; } g_Config.deleteGameConfig(info->id); @@ -283,7 +284,7 @@ ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) { Draw::DrawContext *draw = screenManager()->getDrawContext(); - std::shared_ptr info = g_gameInfoCache->GetInfo(draw, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE | GAMEINFO_WANTUNCOMPRESSEDSIZE); + std::shared_ptr info = g_gameInfoCache->GetInfo(draw, gamePath_, GameInfoFlags::BG | GameInfoFlags::SIZE | GameInfoFlags::UNCOMPRESSED_SIZE); if (tvTitle_) { tvTitle_->SetText(info->GetTitle()); @@ -410,7 +411,7 @@ ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) { } } - if (!info->pending) { + if (info->Ready(GameInfoFlags::PARAM_SFO)) { // At this point, the above buttons won't become visible. We can show these now. for (UI::Choice *choice : otherChoices_) { choice->SetVisibility(UI::V_VISIBLE); @@ -448,8 +449,8 @@ UI::EventReturn GameScreen::OnPlay(UI::EventParams &e) { } UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); - if (info && info->paramSFOLoaded) { + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); + if (info && info->Ready(GameInfoFlags::PARAM_SFO)) { std::string discID = info->paramSFO.GetValueString("DISC_ID"); if ((discID.empty() || !info->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/")) discID = g_paramSFO.GenerateFakeID(gamePath_); @@ -459,7 +460,7 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { } UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); if (info) { // Check that there's any savedata to delete if (saveDirs.size()) { @@ -470,14 +471,13 @@ UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) { std::bind(&GameScreen::CallbackDeleteSaveData, this, std::placeholders::_1))); } } - RecreateViews(); return UI::EVENT_DONE; } void GameScreen::CallbackDeleteSaveData(bool yes) { if (yes) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); info->DeleteAllSaveData(); info->saveDataSize = 0; info->installDataSize = 0; @@ -485,21 +485,20 @@ void GameScreen::CallbackDeleteSaveData(bool yes) { } UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); - if (info) { + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); + if (info && info->Ready(GameInfoFlags::PARAM_SFO)) { auto di = GetI18NCategory(I18NCat::DIALOG); auto ga = GetI18NCategory(I18NCat::GAME); screenManager()->push( new PromptScreen(gamePath_, di->T("DeleteConfirmGame", "Do you really want to delete this game\nfrom your device? You can't undo this."), ga->T("ConfirmDelete"), di->T("Cancel"), std::bind(&GameScreen::CallbackDeleteGame, this, std::placeholders::_1))); } - return UI::EVENT_DONE; } void GameScreen::CallbackDeleteGame(bool yes) { if (yes) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); info->Delete(); g_gameInfoCache->Clear(); screenManager()->switchScreen(new MainScreen()); @@ -560,8 +559,8 @@ void SetBackgroundPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { void SetBackgroundPopupScreen::update() { PopupScreen::update(); - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTBGDATA); - if (status_ == Status::PENDING && info && !info->pending) { + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::BG); + if (status_ == Status::PENDING && info && info->Ready(GameInfoFlags::BG)) { GameInfoTex *pic = nullptr; if (info->pic1.dataLoaded && info->pic1.data.size()) { pic = &info->pic1; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 56ce793f6536..4f01eb3fcd5d 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -221,7 +221,7 @@ void GameSettingsScreen::PreCreateViews() { ReloadAllThemeInfo(); if (editThenRestore_) { - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); g_Config.loadGameConfig(gameID_, info->GetTitle()); } @@ -1454,7 +1454,7 @@ void GameSettingsScreen::onFinish(DialogResult result) { if (editThenRestore_) { // In case we didn't have the title yet before, try again. - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); g_Config.changeGameSpecific(gameID_, info->GetTitle()); g_Config.unloadGameConfig(); } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 3d8ef0932291..1087837d81d5 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -226,12 +226,12 @@ class GameButton : public UI::Clickable { }; void GameButton::Draw(UIContext &dc) { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, 0); + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON); Draw::Texture *texture = 0; u32 color = 0, shadowColor = 0; using namespace UI; - if (ginfo->icon.texture) { + if (ginfo->Ready(GameInfoFlags::ICON) && ginfo->icon.texture) { texture = ginfo->icon.texture; } @@ -427,10 +427,9 @@ void GameButton::Draw(UIContext &dc) { } std::string GameButton::DescribeText() const { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); - if (ginfo->pending) + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); + if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) return "..."; - auto u = GetI18NCategory(I18NCat::UI_ELEMENTS); return ApplySafeSubstitutions(u->T("%1 button"), ginfo->GetTitle()); } @@ -1443,12 +1442,12 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const Path &gamePath, float pr std::shared_ptr ginfo; if (!gamePath.empty()) { - ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG); + ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GameInfoFlags::BG); // Loading texture data may bind a texture. dc.RebindTexture(); // Let's not bother if there's no picture. - if (!ginfo || (!ginfo->pic1.texture && !ginfo->pic0.texture)) { + if (!ginfo->Ready(GameInfoFlags::BG) || (!ginfo->pic1.texture && !ginfo->pic0.texture)) { return false; } } else { @@ -1471,11 +1470,10 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const Path &gamePath, float pr UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) { g_Config.Save("MainScreen::OnGameSelected"); Path path(e.s); - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, path, GAMEINFO_WANTBG); - if (ginfo && ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, path, GameInfoFlags::FILE_TYPE); + if (ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { return UI::EVENT_DONE; } - if (g_GameManager.GetState() == GameManagerState::INSTALLING) return UI::EVENT_DONE; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 0301bd5fc49c..be909da8cb87 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -240,7 +240,7 @@ class RecentGamesAnimation : public Animation { } std::shared_ptr ginfo = GetInfo(dc, index); - if (ginfo && ginfo->pending) { + if (ginfo && !ginfo->Ready(GameInfoFlags::BG)) { // Wait for it to load. It might be the next one. break; } @@ -261,7 +261,7 @@ class RecentGamesAnimation : public Animation { const auto recentIsos = g_Config.RecentIsos(); if (index >= (int)recentIsos.size()) return nullptr; - return g_gameInfoCache->GetInfo(dc.GetDrawContext(), Path(recentIsos[index]), GAMEINFO_WANTBG); + return g_gameInfoCache->GetInfo(dc.GetDrawContext(), Path(recentIsos[index]), GameInfoFlags::BG); } void DrawTex(UIContext &dc, std::shared_ptr &ginfo, float amount) { @@ -376,13 +376,14 @@ uint32_t GetBackgroundColorWithAlpha(const UIContext &dc) { void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z) { using namespace Draw; using namespace UI; + dc.Flush(); std::shared_ptr ginfo; - if (!gamePath.empty()) - ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG); - dc.Flush(); + if (!gamePath.empty()) { + ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GameInfoFlags::BG); + } - GameInfoTex *pic = ginfo ? ginfo->GetBGPic() : nullptr; + GameInfoTex *pic = (ginfo && ginfo->Ready(GameInfoFlags::BG)) ? ginfo->GetBGPic() : nullptr; if (pic) { dc.GetDrawContext()->BindTexture(0, pic->texture); uint32_t color = whiteAlpha(ease((time_now_d() - pic->timeLoaded) * 3)) & 0xFFc0c0c0; diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index e8d69b7b6472..9ed84dc732ee 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -531,26 +531,29 @@ UI::EventReturn GamePauseScreen::OnLastSaveUndo(UI::EventParams &e) { void GamePauseScreen::CallbackDeleteConfig(bool yes) { if (yes) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); - g_Config.unloadGameConfig(); - g_Config.deleteGameConfig(info->id); - info->hasConfig = false; - screenManager()->RecreateAllViews(); + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); + if (info->Ready(GameInfoFlags::PARAM_SFO)) { + g_Config.unloadGameConfig(); + g_Config.deleteGameConfig(info->id); + info->hasConfig = false; + screenManager()->RecreateAllViews(); + } } } UI::EventReturn GamePauseScreen::OnCreateConfig(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); - std::string gameId = g_paramSFO.GetDiscID(); - g_Config.createGameConfig(gameId); - g_Config.changeGameSpecific(gameId, info->GetTitle()); - g_Config.saveGameConfig(gameId, info->GetTitle()); - if (info) { - info->hasConfig = true; + std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); + if (info->Ready(GameInfoFlags::PARAM_SFO)) { + std::string gameId = g_paramSFO.GetDiscID(); + g_Config.createGameConfig(gameId); + g_Config.changeGameSpecific(gameId, info->GetTitle()); + g_Config.saveGameConfig(gameId, info->GetTitle()); + if (info) { + info->hasConfig = true; + } + screenManager()->topScreen()->RecreateViews(); } - - screenManager()->topScreen()->RecreateViews(); return UI::EVENT_DONE; } diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 9b384db1003c..5669f55b52b1 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -87,8 +87,8 @@ class SavedataPopupScreen : public PopupScreen { UIContext &dc = *screenManager()->getUIContext(); const Style &textStyle = dc.theme->popupStyle; - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); - if (!ginfo) + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE); + if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) return; ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); @@ -260,7 +260,7 @@ void SavedataButton::UpdateDateSeconds() { } UI::EventReturn SavedataPopupScreen::OnDeleteButtonClick(UI::EventParams &e) { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GAMEINFO_WANTSIZE); + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GameInfoFlags::PARAM_SFO); ginfo->Delete(); TriggerFinish(DR_NO); return UI::EVENT_DONE; @@ -274,8 +274,8 @@ static std::string CleanSaveString(const std::string &str) { } bool SavedataButton::UpdateText() { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GAMEINFO_WANTSIZE); - if (!ginfo->pending) { + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GameInfoFlags::PARAM_SFO); + if (ginfo->Ready(GameInfoFlags::PARAM_SFO)) { UpdateText(ginfo); return true; } @@ -294,7 +294,7 @@ void SavedataButton::UpdateText(const std::shared_ptr &ginfo) { } void SavedataButton::Draw(UIContext &dc) { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), savePath_, GAMEINFO_WANTSIZE); + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE); Draw::Texture *texture = 0; u32 color = 0, shadowColor = 0; using namespace UI; @@ -682,7 +682,10 @@ UI::EventReturn SavedataScreen::OnSearch(UI::EventParams &e) { } UI::EventReturn SavedataScreen::OnSavedataButtonClick(UI::EventParams &e) { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), Path(e.s), 0); + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), Path(e.s), GameInfoFlags::PARAM_SFO); + if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) { + return UI::EVENT_DONE; + } SavedataPopupScreen *popupScreen = new SavedataPopupScreen(e.s, ginfo->GetTitle()); if (e.v) { popupScreen->SetPopupOrigin(e.v); @@ -714,14 +717,15 @@ void GameIconView::GetContentDimensions(const UIContext &dc, float &w, float &h) void GameIconView::Draw(UIContext &dc) { using namespace UI; - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); - - if (!info->icon.texture) { + std::shared_ptr info = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GameInfoFlags::ICON); + if (!info->Ready(GameInfoFlags::ICON) || !info->icon.texture) { return; } - textureWidth_ = info->icon.texture->Width() * scale_; - textureHeight_ = info->icon.texture->Height() * scale_; + Draw::Texture *texture = info->icon.texture; + + textureWidth_ = texture->Width() * scale_; + textureHeight_ = texture->Height() * scale_; // Fade icon with the backgrounds. double loadTime = info->icon.timeLoaded; @@ -736,7 +740,7 @@ void GameIconView::Draw(UIContext &dc) { float nw = std::min(bounds_.h * textureWidth_ / textureHeight_, (float)bounds_.w); dc.Flush(); - dc.GetDrawContext()->BindTexture(0, info->icon.texture); + dc.GetDrawContext()->BindTexture(0, texture); dc.Draw()->Rect(bounds_.x, bounds_.y, nw, bounds_.h, color); dc.Flush(); dc.RebindTexture(); From 7b4d2453e341b64229b472e0d59a5254ee712731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2024 16:37:59 +0100 Subject: [PATCH 3/4] Android buildfix --- android/jni/app-android.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9fc3faf0f1e1..04d69e06e7f7 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1678,11 +1678,13 @@ extern "C" jstring Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameName(JNIEnv std::string result = ""; GameInfoCache *cache = new GameInfoCache(); - std::shared_ptr info = cache->GetInfo(nullptr, path, 0); + std::shared_ptr info = cache->GetInfo(nullptr, path, GameInfoFlags::PARAM_SFO); // Wait until it's done: this is synchronous, unfortunately. if (info) { INFO_LOG(SYSTEM, "GetInfo successful, waiting"); - cache->WaitUntilDone(info); + while (!info->Ready(GameInfoFlags::PARAM_SFO)) { + sleep_ms(1); + } INFO_LOG(SYSTEM, "Done waiting"); if (info->fileType != IdentifiedFileType::UNKNOWN) { result = info->GetTitle(); From a5fafb8321bb9c5276ec08e664dd87a69113c379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2024 16:56:07 +0100 Subject: [PATCH 4/4] Fix logic, cleanup --- UI/CwCheatScreen.cpp | 5 ++--- UI/EmuScreen.cpp | 5 +++-- UI/GameScreen.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index d03ed87a4a2b..ef7ef6ab85cf 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -50,11 +50,10 @@ CwCheatScreen::~CwCheatScreen() { bool CwCheatScreen::TryLoadCheatInfo() { std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); std::string gameID; - if (info && info->Ready(GameInfoFlags::PARAM_SFO)) { - gameID = info->paramSFO.GetValueString("DISC_ID"); - } else { + if (!info->Ready(GameInfoFlags::PARAM_SFO)) { return false; } + gameID = info->paramSFO.GetValueString("DISC_ID"); if ((info->id.empty() || !info->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/")) { gameID = g_paramSFO.GenerateFakeID(gamePath_); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e1158aff52dd..209ca23b037d 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -270,8 +270,9 @@ void EmuScreen::bootGame(const Path &filename) { // We don't want to boot with the wrong game specific config, so wait until info is ready. std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, filename, GameInfoFlags::PARAM_SFO); - if (!info || info->Ready(GameInfoFlags::PARAM_SFO)) + if (!info->Ready(GameInfoFlags::PARAM_SFO)) { return; + } auto sc = GetI18NCategory(I18NCat::SCREEN); if (info->fileType == IdentifiedFileType::PSP_DISC_DIRECTORY) { @@ -956,7 +957,7 @@ class GameInfoBGView : public UI::InertView { dc.Flush(); // PIC1 is the loading image, so let's only draw if it's available. - if (ginfo && ginfo->Ready(GameInfoFlags::BG) && ginfo->pic1.texture) { + if (ginfo->Ready(GameInfoFlags::BG) && ginfo->pic1.texture) { Draw::Texture *texture = ginfo->pic1.texture; if (texture) { dc.GetDrawContext()->BindTexture(0, texture); diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 010483e9e573..6a8324adc7c5 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -486,7 +486,7 @@ void GameScreen::CallbackDeleteSaveData(bool yes) { UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) { std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); - if (info && info->Ready(GameInfoFlags::PARAM_SFO)) { + if (info->Ready(GameInfoFlags::PARAM_SFO)) { auto di = GetI18NCategory(I18NCat::DIALOG); auto ga = GetI18NCategory(I18NCat::GAME); screenManager()->push( @@ -560,7 +560,7 @@ void SetBackgroundPopupScreen::update() { PopupScreen::update(); std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::BG); - if (status_ == Status::PENDING && info && info->Ready(GameInfoFlags::BG)) { + if (status_ == Status::PENDING && info->Ready(GameInfoFlags::BG)) { GameInfoTex *pic = nullptr; if (info->pic1.dataLoaded && info->pic1.data.size()) { pic = &info->pic1;