Fix some longstanding issues with the GameInfoCache and optimize away double work

This commit is contained in:
Henrik Rydgård 2024-01-28 16:23:27 +01:00
parent 9b3c14a931
commit 884c366692
12 changed files with 286 additions and 261 deletions

View File

@ -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

View File

@ -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> gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GAMEINFO_WANTSND);
if (!gameInfo || gameInfo->pending) {
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GameInfoFlags::SND);
if (!gameInfo->Ready(GameInfoFlags::SND)) {
// Should try again shortly..
return;
}

View File

@ -48,9 +48,9 @@ CwCheatScreen::~CwCheatScreen() {
}
bool CwCheatScreen::TryLoadCheatInfo() {
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
std::shared_ptr<GameInfo> 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;

View File

@ -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<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, filename, 0);
if (!info || info->pending)
std::shared_ptr<GameInfo> 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 @@ public:
void Draw(UIContext &dc) override {
// Should only be called when visible.
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GAMEINFO_WANTBG);
std::shared_ptr<GameInfo> 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);

View File

@ -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<Path> saveDataDir = GetSaveDataDirectories();
for (size_t j = 0; j < saveDataDir.size(); j++) {
std::vector<File::FileInfo> fileInfo;
@ -324,13 +326,15 @@ void GameInfo::ParseParamSFO() {
region = GAMEREGION_CHINA;
}*/
}
paramSFOLoaded = true;
}
std::string GameInfo::GetTitle() {
std::lock_guard<std::mutex> guard(lock);
if (hasFlags & GameInfoFlags::PARAM_SFO) {
return title;
} else {
return filePath_.GetFilename();
}
}
void GameInfo::SetTitle(const std::string &newTitle) {
@ -352,8 +356,6 @@ 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());
@ -363,12 +365,6 @@ void GameInfo::SetupTexture(Draw::DrawContext *thin3d, GameInfoTex &tex) {
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);
@ -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<GameInfo> &info)
: gamePath_(gamePath), info_(info) {
}
GameInfoWorkItem(const Path &gamePath, std::shared_ptr<GameInfo> &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 @@ public:
std::string errorString;
info_->working = true;
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,6 +494,7 @@ public:
}
// First, PARAM.SFO.
if (flags_ & GameInfoFlags::PARAM_SFO) {
std::vector<u8> sfoData;
if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) {
std::lock_guard<std::mutex> lock(info_->lock);
@ -514,8 +510,10 @@ public:
info_->region = GAMEREGION_MAX + 1; // Homebrew
}
}
}
// Then, ICON0.PNG.
if (flags_ & GameInfoFlags::ICON) {
if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) {
std::lock_guard<std::mutex> lock(info_->lock);
pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data);
@ -532,8 +530,9 @@ public:
ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock);
}
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 @@ public:
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 @@ public:
case IdentifiedFileType::PSP_ELF:
handleELF:
// An elf on its own has no usable information, no icons, no nothing.
{
std::lock_guard<std::mutex> 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,6 +593,7 @@ handleELF:
SequentialHandleAllocator handles;
VirtualDiscFileSystem umd(&handles, gamePath_);
if (flags_ & GameInfoFlags::PARAM_SFO) {
// Alright, let's fetch the PARAM.SFO.
std::string paramSFOcontents;
if (ReadFileToString(&umd, "/PARAM.SFO", &paramSFOcontents, 0)) {
@ -601,10 +601,12 @@ handleELF:
info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size());
info_->ParseParamSFO();
}
}
if (flags_ & GameInfoFlags::ICON) {
ReadFileToString(&umd, "/ICON0.PNG", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
if (info_->wantFlags & GAMEINFO_WANTBG) {
}
if (flags_ & GameInfoFlags::BG) {
ReadFileToString(&umd, "/PIC1.PNG", &info_->pic1.data, &info_->lock);
info_->pic1.dataLoaded = true;
}
@ -613,35 +615,31 @@ handleELF:
case IdentifiedFileType::PPSSPP_SAVESTATE:
{
Path screenshotPath;
{
if (flags_ & GameInfoFlags::PARAM_SFO) {
info_->SetTitle(SaveState::GetTitle(gamePath_));
std::lock_guard<std::mutex> guard(info_->lock);
screenshotPath = gamePath_.WithReplacedExtension(".ppst", ".jpg");
}
// Let's use the screenshot as an icon, too.
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<std::mutex> guard(info_->lock);
screenshotPath = gamePath_.WithReplacedExtension(".ppdmp", ".png");
}
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 @@ handleELF:
VirtualDiscFileSystem umd(&handles, gamePath_);
// Alright, let's fetch the PARAM.SFO.
if (flags_ & GameInfoFlags::PARAM_SFO) {
std::string paramSFOcontents;
if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents, 0)) {
std::lock_guard<std::mutex> 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,6 +691,7 @@ handleELF:
ISOFileSystem umd(&handles, bd);
// Alright, let's fetch the PARAM.SFO.
if (flags_ & GameInfoFlags::PARAM_SFO) {
std::string paramSFOcontents;
if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents, nullptr)) {
{
@ -698,16 +699,20 @@ handleELF:
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::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 (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");
@ -723,28 +728,26 @@ handleELF:
} 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 @@ handleELF:
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<std::mutex> 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<std::mutex> 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<GameInfo> 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<GameInfo> &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<GameInfo> GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, int wantFlags) {
std::shared_ptr<GameInfo> info;
std::shared_ptr<GameInfo> 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<GameInfo> 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();
return info;
}
if (!info) {
info = std::make_shared<GameInfo>();
}
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);
}
GameInfoFlags wanted = (GameInfoFlags)0;
{
std::lock_guard<std::mutex> lock(info->lock);
info->wantFlags |= wantFlags;
info->pending = true;
// Careful now!
std::unique_lock<std::mutex> 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;
}
GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info);
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;
}
std::shared_ptr<GameInfo> info = std::make_shared<GameInfo>();
info->pendingFlags = wantFlags;
info->lastAccessedTime = time_now_d();
info_.insert(std::make_pair(pathStr, info));
mapLock_.unlock();
// 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;
}

View File

@ -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 @@ public:
std::string GetTitle();
void SetTitle(const std::string &newTitle);
bool Ready(GameInfoFlags flags) {
std::unique_lock<std::mutex> guard(lock);
return (hasFlags & flags) != 0;
}
GameInfoTex *GetBGPic() {
if (pic1.texture)
return &pic1;
@ -119,6 +127,11 @@ public:
// 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 @@ public:
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 @@ public:
std::string sndFileData;
std::atomic<bool> sndDataLoaded{};
int wantFlags = 0;
double lastAccessedTime = 0.0;
u64 gameSizeUncompressed = 0;
@ -146,11 +156,6 @@ public:
u64 saveDataSize = 0;
u64 installDataSize = 0;
std::atomic<bool> pending{};
std::atomic<bool> working{};
Event readyEvent;
protected:
// Note: this can change while loading, use GetTitle().
std::string title;
@ -178,7 +183,7 @@ public:
// 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<GameInfo> GetInfo(Draw::DrawContext *draw, const Path &gamePath, int wantFlags);
std::shared_ptr<GameInfo> 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 @@ private:
// 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<std::string, std::shared_ptr<GameInfo> > info_;
std::mutex mapLock_;
};
// This one can be global, no good reason not to.

View File

@ -86,7 +86,7 @@ void GameScreen::update() {
}
void GameScreen::CreateViews() {
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0);
if (info) {
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
if (!info) {
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
if (!info) {
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(draw, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE | GAMEINFO_WANTUNCOMPRESSEDSIZE);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
if (info && info->paramSFOLoaded) {
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
if (info) {
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTBGDATA);
if (status_ == Status::PENDING && info && !info->pending) {
std::shared_ptr<GameInfo> 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;

View File

@ -221,7 +221,7 @@ void GameSettingsScreen::PreCreateViews() {
ReloadAllThemeInfo();
if (editThenRestore_) {
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
g_Config.changeGameSpecific(gameID_, info->GetTitle());
g_Config.unloadGameConfig();
}

View File

@ -226,12 +226,12 @@ private:
};
void GameButton::Draw(UIContext &dc) {
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, 0);
std::shared_ptr<GameInfo> 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<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
if (ginfo->pending)
std::shared_ptr<GameInfo> 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<GameInfo> 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<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, path, GAMEINFO_WANTBG);
if (ginfo && ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) {
std::shared_ptr<GameInfo> 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;

View File

@ -240,7 +240,7 @@ private:
}
std::shared_ptr<GameInfo> 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 @@ private:
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<GameInfo> &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;
std::shared_ptr<GameInfo> ginfo;
if (!gamePath.empty())
ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG);
dc.Flush();
GameInfoTex *pic = ginfo ? ginfo->GetBGPic() : nullptr;
std::shared_ptr<GameInfo> ginfo;
if (!gamePath.empty()) {
ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GameInfoFlags::BG);
}
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;

View File

@ -531,17 +531,20 @@ UI::EventReturn GamePauseScreen::OnLastSaveUndo(UI::EventParams &e) {
void GamePauseScreen::CallbackDeleteConfig(bool yes)
{
if (yes) {
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0);
std::shared_ptr<GameInfo> 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());
@ -549,8 +552,8 @@ UI::EventReturn GamePauseScreen::OnCreateConfig(UI::EventParams &e)
if (info) {
info->hasConfig = true;
}
screenManager()->topScreen()->RecreateViews();
}
return UI::EVENT_DONE;
}

View File

@ -87,8 +87,8 @@ public:
UIContext &dc = *screenManager()->getUIContext();
const Style &textStyle = dc.theme->popupStyle;
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
if (!ginfo)
std::shared_ptr<GameInfo> 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<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GAMEINFO_WANTSIZE);
std::shared_ptr<GameInfo> 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<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GAMEINFO_WANTSIZE);
if (!ginfo->pending) {
std::shared_ptr<GameInfo> 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<GameInfo> &ginfo) {
}
void SavedataButton::Draw(UIContext &dc) {
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), savePath_, GAMEINFO_WANTSIZE);
std::shared_ptr<GameInfo> 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<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), Path(e.s), 0);
std::shared_ptr<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
if (!info->icon.texture) {
std::shared_ptr<GameInfo> 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();