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) { \ static inline bool operator &(const T &lhs, const T &rhs) { \
return ((int)lhs & (int)rhs) != 0; \ 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 #endif

View File

@ -147,7 +147,7 @@ void WavData::Read(RIFFReader &file_) {
numFrames = numBytes / raw_bytes_per_frame; // numFrames numFrames = numBytes / raw_bytes_per_frame; // numFrames
// It seems the atrac3 codec likes to read a little bit outside. // 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 = (uint8_t *)malloc(numBytes + padding);
raw_data_size = numBytes; raw_data_size = numBytes;
@ -359,8 +359,8 @@ void BackgroundAudio::Update() {
return; return;
// Grab some audio from the current game and play it. // Grab some audio from the current game and play it.
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GAMEINFO_WANTSND); std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GameInfoFlags::SND);
if (!gameInfo || gameInfo->pending) { if (!gameInfo->Ready(GameInfoFlags::SND)) {
// Should try again shortly.. // Should try again shortly..
return; return;
} }

View File

@ -48,9 +48,9 @@ CwCheatScreen::~CwCheatScreen() {
} }
bool CwCheatScreen::TryLoadCheatInfo() { 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; std::string gameID;
if (info && info->paramSFOLoaded) { if (info && info->Ready(GameInfoFlags::PARAM_SFO)) {
gameID = info->paramSFO.GetValueString("DISC_ID"); gameID = info->paramSFO.GetValueString("DISC_ID");
} else { } else {
return false; return false;

View File

@ -269,8 +269,8 @@ void EmuScreen::bootGame(const Path &filename) {
invalid_ = true; invalid_ = true;
// We don't want to boot with the wrong game specific config, so wait until info is ready. // 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); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, filename, GameInfoFlags::PARAM_SFO);
if (!info || info->pending) if (!info || info->Ready(GameInfoFlags::PARAM_SFO))
return; return;
auto sc = GetI18NCategory(I18NCat::SCREEN); auto sc = GetI18NCategory(I18NCat::SCREEN);
@ -952,11 +952,11 @@ public:
void Draw(UIContext &dc) override { void Draw(UIContext &dc) override {
// Should only be called when visible. // 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(); dc.Flush();
// PIC1 is the loading image, so let's only draw if it's available. // 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; Draw::Texture *texture = ginfo->pic1.texture;
if (texture) { if (texture) {
dc.GetDrawContext()->BindTexture(0, texture); dc.GetDrawContext()->BindTexture(0, texture);

View File

@ -54,8 +54,9 @@ void GameInfoTex::Clear() {
} }
} }
GameInfo::GameInfo() : fileType(IdentifiedFileType::UNKNOWN) { GameInfo::GameInfo() {
pending = true; // here due to a forward decl.
fileType = IdentifiedFileType::UNKNOWN;
} }
GameInfo::~GameInfo() { GameInfo::~GameInfo() {
@ -275,6 +276,7 @@ void GameInfo::DisposeFileLoader() {
} }
bool GameInfo::DeleteAllSaveData() { bool GameInfo::DeleteAllSaveData() {
_assert_(hasFlags & GameInfoFlags::PARAM_SFO); // so we know we have the ID.
std::vector<Path> saveDataDir = GetSaveDataDirectories(); std::vector<Path> saveDataDir = GetSaveDataDirectories();
for (size_t j = 0; j < saveDataDir.size(); j++) { for (size_t j = 0; j < saveDataDir.size(); j++) {
std::vector<File::FileInfo> fileInfo; std::vector<File::FileInfo> fileInfo;
@ -324,13 +326,15 @@ void GameInfo::ParseParamSFO() {
region = GAMEREGION_CHINA; region = GAMEREGION_CHINA;
}*/ }*/
} }
paramSFOLoaded = true;
} }
std::string GameInfo::GetTitle() { std::string GameInfo::GetTitle() {
std::lock_guard<std::mutex> guard(lock); std::lock_guard<std::mutex> guard(lock);
return title; if (hasFlags & GameInfoFlags::PARAM_SFO) {
return title;
} else {
return filePath_.GetFilename();
}
} }
void GameInfo::SetTitle(const std::string &newTitle) { 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) { void GameInfo::SetupTexture(Draw::DrawContext *thin3d, GameInfoTex &tex) {
using namespace Draw; using namespace Draw;
if (tex.data.size()) { // TODO: Use TempImage to semi-load the image in the worker task, then here we
if (!tex.texture) { // could just call CreateTextureFromTempImage.
// TODO: Use TempImage to semi-load the image in the worker task, then here we tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT, false, GetTitle().c_str());
// could just call CreateTextureFromTempImage. if (tex.texture) {
tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT, false, GetTitle().c_str()); tex.timeLoaded = time_now_d();
if (tex.texture) { } else {
tex.timeLoaded = time_now_d(); ERROR_LOG(G3D, "Failed creating texture (%s) from %d-byte file", GetTitle().c_str(), (int)tex.data.size());
} 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;
}
} }
} }
@ -434,15 +430,11 @@ static bool ReadVFSToString(const char *filename, std::string *contents, std::mu
class GameInfoWorkItem : public Task { class GameInfoWorkItem : public Task {
public: public:
GameInfoWorkItem(const Path &gamePath, std::shared_ptr<GameInfo> &info) GameInfoWorkItem(const Path &gamePath, std::shared_ptr<GameInfo> &info, GameInfoFlags flags)
: gamePath_(gamePath), info_(info) { : gamePath_(gamePath), info_(info), flags_(flags) {}
}
~GameInfoWorkItem() { ~GameInfoWorkItem() {
info_->pending.store(false);
info_->working.store(false);
info_->DisposeFileLoader(); info_->DisposeFileLoader();
info_->readyEvent.Notify();
} }
TaskType Type() const override { TaskType Type() const override {
@ -475,8 +467,11 @@ public:
std::string errorString; std::string errorString;
info_->working = true; if (flags_ & GameInfoFlags::FILE_TYPE) {
info_->fileType = Identify_File(info_->GetFileLoader().get(), &errorString); info_->fileType = Identify_File(info_->GetFileLoader().get(), &errorString);
info_->hasConfig = g_Config.hasGameConfig(info_->id);
}
switch (info_->fileType) { switch (info_->fileType) {
case IdentifiedFileType::PSP_PBP: case IdentifiedFileType::PSP_PBP:
case IdentifiedFileType::PSP_PBP_DIRECTORY: case IdentifiedFileType::PSP_PBP_DIRECTORY:
@ -499,41 +494,45 @@ public:
} }
// First, PARAM.SFO. // First, PARAM.SFO.
std::vector<u8> sfoData; if (flags_ & GameInfoFlags::PARAM_SFO) {
if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) { std::vector<u8> sfoData;
std::lock_guard<std::mutex> lock(info_->lock); if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) {
info_->paramSFO.ReadSFO(sfoData); std::lock_guard<std::mutex> lock(info_->lock);
info_->ParseParamSFO(); info_->paramSFO.ReadSFO(sfoData);
info_->ParseParamSFO();
// Assuming PSP_PBP_DIRECTORY without ID or with disc_total < 1 in GAME dir must be 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) if ((info_->id.empty() || !info_->disc_total)
&& gamePath_.FilePathContainsNoCase("PSP/GAME/") && gamePath_.FilePathContainsNoCase("PSP/GAME/")
&& info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) { && info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) {
info_->id = g_paramSFO.GenerateFakeID(gamePath_); info_->id = g_paramSFO.GenerateFakeID(gamePath_);
info_->id_version = info_->id + "_1.00"; info_->id_version = info_->id + "_1.00";
info_->region = GAMEREGION_MAX + 1; // Homebrew info_->region = GAMEREGION_MAX + 1; // Homebrew
}
} }
} }
// Then, ICON0.PNG. // Then, ICON0.PNG.
if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) { if (flags_ & GameInfoFlags::ICON) {
std::lock_guard<std::mutex> lock(info_->lock); if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) {
pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data); std::lock_guard<std::mutex> lock(info_->lock);
} else { pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data);
Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); } else {
Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg");
// Try using png/jpg screenshots first Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png");
if (File::Exists(screenshot_png)) // Try using png/jpg screenshots first
ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); if (File::Exists(screenshot_png))
else if (File::Exists(screenshot_jpg)) ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock);
ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock); else if (File::Exists(screenshot_jpg))
else ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock);
// Read standard icon else
ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); // 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) { if (pbp.GetSubFileSize(PBP_PIC0_PNG) > 0) {
std::string data; std::string data;
pbp.GetSubFileAsString(PBP_PIC0_PNG, &data); pbp.GetSubFileAsString(PBP_PIC0_PNG, &data);
@ -549,7 +548,7 @@ public:
info_->pic1.dataLoaded = true; info_->pic1.dataLoaded = true;
} }
} }
if (info_->wantFlags & GAMEINFO_WANTSND) { if (flags_ & GameInfoFlags::SND) {
if (pbp.GetSubFileSize(PBP_SND0_AT3) > 0) { if (pbp.GetSubFileSize(PBP_SND0_AT3) > 0) {
std::string data; std::string data;
pbp.GetSubFileAsString(PBP_SND0_AT3, &data); pbp.GetSubFileAsString(PBP_SND0_AT3, &data);
@ -564,17 +563,17 @@ public:
case IdentifiedFileType::PSP_ELF: case IdentifiedFileType::PSP_ELF:
handleELF: handleELF:
// An elf on its own has no usable information, no icons, no nothing. // An elf on its own has no usable information, no icons, no nothing.
{ if (flags_ & GameInfoFlags::PARAM_SFO) {
std::lock_guard<std::mutex> lock(info_->lock);
info_->id = g_paramSFO.GenerateFakeID(gamePath_); info_->id = g_paramSFO.GenerateFakeID(gamePath_);
info_->id_version = info_->id + "_1.00"; info_->id_version = info_->id + "_1.00";
info_->region = GAMEREGION_MAX + 1; // Homebrew info_->region = GAMEREGION_MAX + 1; // Homebrew
info_->paramSFOLoaded = true;
} }
{
Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); if (flags_ & GameInfoFlags::ICON) {
Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); 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 // Try using png/jpg screenshots first
if (File::Exists(screenshot_png)) { if (File::Exists(screenshot_png)) {
ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock);
@ -594,17 +593,20 @@ handleELF:
SequentialHandleAllocator handles; SequentialHandleAllocator handles;
VirtualDiscFileSystem umd(&handles, gamePath_); VirtualDiscFileSystem umd(&handles, gamePath_);
// Alright, let's fetch the PARAM.SFO. if (flags_ & GameInfoFlags::PARAM_SFO) {
std::string paramSFOcontents; // Alright, let's fetch the PARAM.SFO.
if (ReadFileToString(&umd, "/PARAM.SFO", &paramSFOcontents, 0)) { std::string paramSFOcontents;
std::lock_guard<std::mutex> lock(info_->lock); if (ReadFileToString(&umd, "/PARAM.SFO", &paramSFOcontents, 0)) {
info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); std::lock_guard<std::mutex> lock(info_->lock);
info_->ParseParamSFO(); info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size());
info_->ParseParamSFO();
}
} }
if (flags_ & GameInfoFlags::ICON) {
ReadFileToString(&umd, "/ICON0.PNG", &info_->icon.data, &info_->lock); ReadFileToString(&umd, "/ICON0.PNG", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true; info_->icon.dataLoaded = true;
if (info_->wantFlags & GAMEINFO_WANTBG) { }
if (flags_ & GameInfoFlags::BG) {
ReadFileToString(&umd, "/PIC1.PNG", &info_->pic1.data, &info_->lock); ReadFileToString(&umd, "/PIC1.PNG", &info_->pic1.data, &info_->lock);
info_->pic1.dataLoaded = true; info_->pic1.dataLoaded = true;
} }
@ -613,34 +615,30 @@ handleELF:
case IdentifiedFileType::PPSSPP_SAVESTATE: case IdentifiedFileType::PPSSPP_SAVESTATE:
{ {
Path screenshotPath; if (flags_ & GameInfoFlags::PARAM_SFO) {
{
info_->SetTitle(SaveState::GetTitle(gamePath_)); 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. // Let's use the screenshot as an icon, too.
if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) { if (flags_ & GameInfoFlags::ICON) {
info_->icon.dataLoaded = true; Path screenshotPath = gamePath_.WithReplacedExtension(".ppst", ".jpg");
} else { if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) {
ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str()); info_->icon.dataLoaded = true;
} else {
ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str());
}
} }
break; break;
} }
case IdentifiedFileType::PPSSPP_GE_DUMP: case IdentifiedFileType::PPSSPP_GE_DUMP:
{ {
Path screenshotPath; if (flags_ & GameInfoFlags::ICON) {
Path screenshotPath = gamePath_.WithReplacedExtension(".ppdmp", ".png");
{ // Let's use the comparison screenshot as an icon, if it exists.
std::lock_guard<std::mutex> guard(info_->lock); if (ReadLocalFileToString(screenshotPath, &info_->icon.data, &info_->lock)) {
screenshotPath = gamePath_.WithReplacedExtension(".ppdmp", ".png"); info_->icon.dataLoaded = true;
} }
// 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; break;
} }
@ -651,22 +649,24 @@ handleELF:
VirtualDiscFileSystem umd(&handles, gamePath_); VirtualDiscFileSystem umd(&handles, gamePath_);
// Alright, let's fetch the PARAM.SFO. // Alright, let's fetch the PARAM.SFO.
std::string paramSFOcontents; if (flags_ & GameInfoFlags::PARAM_SFO) {
if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents, 0)) { std::string paramSFOcontents;
std::lock_guard<std::mutex> lock(info_->lock); if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents, 0)) {
info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); std::lock_guard<std::mutex> lock(info_->lock);
info_->ParseParamSFO(); info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size());
info_->ParseParamSFO();
}
} }
ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock); ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true; info_->icon.dataLoaded = true;
if (info_->wantFlags & GAMEINFO_WANTBG) { if (flags_ & GameInfoFlags::BG) {
ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, &info_->lock); ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, &info_->lock);
info_->pic0.dataLoaded = true; info_->pic0.dataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, &info_->lock); ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, &info_->lock);
info_->pic1.dataLoaded = true; info_->pic1.dataLoaded = true;
} }
if (info_->wantFlags & GAMEINFO_WANTSND) { if (flags_ & GameInfoFlags::SND) {
ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock); ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock);
info_->pic1.dataLoaded = true; info_->pic1.dataLoaded = true;
} }
@ -691,60 +691,63 @@ handleELF:
ISOFileSystem umd(&handles, bd); ISOFileSystem umd(&handles, bd);
// Alright, let's fetch the PARAM.SFO. // Alright, let's fetch the PARAM.SFO.
std::string paramSFOcontents; if (flags_ & GameInfoFlags::PARAM_SFO) {
if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents, nullptr)) { std::string paramSFOcontents;
{ if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents, nullptr)) {
std::lock_guard<std::mutex> lock(info_->lock); {
info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); std::lock_guard<std::mutex> lock(info_->lock);
info_->ParseParamSFO(); 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 // 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)) { if (flags_ & GameInfoFlags::ICON) {
Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); if (!ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock)) {
Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg");
// Try using png/jpg screenshots first Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png");
if (File::Exists(screenshot_png)) // Try using png/jpg screenshots first
info_->icon.dataLoaded = ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock); if (File::Exists(screenshot_png))
else if (File::Exists(screenshot_jpg)) info_->icon.dataLoaded = ReadLocalFileToString(screenshot_png, &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock); else if (File::Exists(screenshot_jpg))
else { info_->icon.dataLoaded = ReadLocalFileToString(screenshot_jpg, &info_->icon.data, &info_->lock);
DEBUG_LOG(LOADER, "Loading unknown.png because no icon was found"); else {
info_->icon.dataLoaded = ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); 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; break;
} }
case IdentifiedFileType::ARCHIVE_ZIP: case IdentifiedFileType::ARCHIVE_ZIP:
info_->paramSFOLoaded = true; if (flags_ & GameInfoFlags::ICON) {
{
ReadVFSToString("zip.png", &info_->icon.data, &info_->lock); ReadVFSToString("zip.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true; info_->icon.dataLoaded = true;
} }
break; break;
case IdentifiedFileType::ARCHIVE_RAR: case IdentifiedFileType::ARCHIVE_RAR:
info_->paramSFOLoaded = true; if (flags_ & GameInfoFlags::ICON) {
{
ReadVFSToString("rargray.png", &info_->icon.data, &info_->lock); ReadVFSToString("rargray.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true; info_->icon.dataLoaded = true;
} }
break; break;
case IdentifiedFileType::ARCHIVE_7Z: case IdentifiedFileType::ARCHIVE_7Z:
info_->paramSFOLoaded = true; if (flags_ & GameInfoFlags::ICON) {
{
ReadVFSToString("7z.png", &info_->icon.data, &info_->lock); ReadVFSToString("7z.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true; info_->icon.dataLoaded = true;
} }
@ -752,28 +755,31 @@ handleELF:
case IdentifiedFileType::NORMAL_DIRECTORY: case IdentifiedFileType::NORMAL_DIRECTORY:
default: default:
info_->paramSFOLoaded = true;
break; break;
} }
info_->hasConfig = g_Config.hasGameConfig(info_->id); if (flags_ & GameInfoFlags::SIZE) {
if (info_->wantFlags & GAMEINFO_WANTSIZE) {
std::lock_guard<std::mutex> lock(info_->lock); std::lock_guard<std::mutex> lock(info_->lock);
info_->gameSizeOnDisk = info_->GetGameSizeOnDiskInBytes(); info_->gameSizeOnDisk = info_->GetGameSizeOnDiskInBytes();
info_->saveDataSize = info_->GetSaveDataSizeInBytes(); info_->saveDataSize = info_->GetSaveDataSizeInBytes();
info_->installDataSize = info_->GetInstallDataSizeInBytes(); info_->installDataSize = info_->GetInstallDataSizeInBytes();
} }
if (info_->wantFlags & GAMEINFO_WANTUNCOMPRESSEDSIZE) { if (flags_ & GameInfoFlags::UNCOMPRESSED_SIZE) {
info_->gameSizeUncompressed = info_->GetGameSizeUncompressedInBytes(); 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()); // INFO_LOG(SYSTEM, "Completed writing info for %s", info_->GetTitle().c_str());
} }
private: private:
Path gamePath_; Path gamePath_;
std::shared_ptr<GameInfo> info_; std::shared_ptr<GameInfo> info_;
GameInfoFlags flags_{};
DISALLOW_COPY_AND_ASSIGN(GameInfoWorkItem); DISALLOW_COPY_AND_ASSIGN(GameInfoWorkItem);
}; };
@ -820,14 +826,18 @@ void GameInfoCache::FlushBGs() {
iter->second->sndFileData.clear(); iter->second->sndFileData.clear();
iter->second->sndDataLoaded = false; 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) { void GameInfoCache::PurgeType(IdentifiedFileType fileType) {
for (auto iter = info_.begin(); iter != info_.end();) { for (auto iter = info_.begin(); iter != info_.end();) {
auto &info = iter->second; 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) { if (info->fileType == fileType) {
iter = info_.erase(iter); iter = info_.erase(iter);
} else { } else {
@ -836,51 +846,48 @@ void GameInfoCache::PurgeType(IdentifiedFileType fileType) {
} }
} }
void GameInfoCache::WaitUntilDone(std::shared_ptr<GameInfo> &info) { // Call on the main thread ONLY - that is from stuff called from NativeFrame.
info->readyEvent.Wait();
}
// Runs on the main thread. Only call from render() and similar, not update()!
// Can also be called from the audio thread for menu background music, but that cannot request images! // 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> GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags) {
std::shared_ptr<GameInfo> info;
const std::string &pathStr = gamePath.ToString(); 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); auto iter = info_.find(pathStr);
if (iter != info_.end()) { 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->FinishPendingTextureLoads(draw);
info->lastAccessedTime = time_now_d(); info->lastAccessedTime = time_now_d();
GameInfoFlags wanted = (GameInfoFlags)0;
{
// 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;
}
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; return info;
} }
if (!info) { std::shared_ptr<GameInfo> info = std::make_shared<GameInfo>();
info = std::make_shared<GameInfo>(); info->pendingFlags = wantFlags;
} info->lastAccessedTime = time_now_d();
info_.insert(std::make_pair(pathStr, info));
mapLock_.unlock();
if (info->working) { // Just get all the stuff we wanted.
// Uh oh, it's currently in process. It could mark pending = false with the wrong wantFlags. GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wantFlags);
// Let's wait it out, then queue.
// NOTE: This is bad because we're likely on the UI thread....
WaitUntilDone(info);
}
{
std::lock_guard<std::mutex> lock(info->lock);
info->wantFlags |= wantFlags;
info->pending = true;
}
GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info);
g_threadManager.EnqueueTask(item); g_threadManager.EnqueueTask(item);
// Don't re-insert if we already have it.
if (info_.find(pathStr) == info_.end())
info_[pathStr] = info;
return info; return info;
} }

View File

@ -50,13 +50,16 @@ enum GameRegion {
GAMEREGION_MAX, GAMEREGION_MAX,
}; };
enum GameInfoWantFlags { enum class GameInfoFlags {
GAMEINFO_WANTBG = 0x01, FILE_TYPE = 0x01, // Don't need to specify this, always included.
GAMEINFO_WANTSIZE = 0x02, PARAM_SFO = 0x02,
GAMEINFO_WANTSND = 0x04, ICON = 0x04,
GAMEINFO_WANTBGDATA = 0x08, // Use with WANTBG. BG = 0x08,
GAMEINFO_WANTUNCOMPRESSEDSIZE = 0x10, SND = 0x10,
SIZE = 0x20,
UNCOMPRESSED_SIZE = 0x40,
}; };
ENUM_CLASS_BITOPS(GameInfoFlags);
class FileLoader; class FileLoader;
enum class IdentifiedFileType; enum class IdentifiedFileType;
@ -102,6 +105,11 @@ public:
std::string GetTitle(); std::string GetTitle();
void SetTitle(const std::string &newTitle); void SetTitle(const std::string &newTitle);
bool Ready(GameInfoFlags flags) {
std::unique_lock<std::mutex> guard(lock);
return (hasFlags & flags) != 0;
}
GameInfoTex *GetBGPic() { GameInfoTex *GetBGPic() {
if (pic1.texture) if (pic1.texture)
return &pic1; return &pic1;
@ -119,6 +127,11 @@ public:
// Controls access to the fileLoader pointer. // Controls access to the fileLoader pointer.
std::mutex loaderLock; 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;
std::string id_version; std::string id_version;
int disc_total = 0; int disc_total = 0;
@ -126,7 +139,6 @@ public:
int region = -1; int region = -1;
IdentifiedFileType fileType; IdentifiedFileType fileType;
ParamSFOData paramSFO; ParamSFOData paramSFO;
bool paramSFOLoaded = false;
bool hasConfig = false; bool hasConfig = false;
// Pre read the data, create a texture the next time (GL thread..) // Pre read the data, create a texture the next time (GL thread..)
@ -137,8 +149,6 @@ public:
std::string sndFileData; std::string sndFileData;
std::atomic<bool> sndDataLoaded{}; std::atomic<bool> sndDataLoaded{};
int wantFlags = 0;
double lastAccessedTime = 0.0; double lastAccessedTime = 0.0;
u64 gameSizeUncompressed = 0; u64 gameSizeUncompressed = 0;
@ -146,11 +156,6 @@ public:
u64 saveDataSize = 0; u64 saveDataSize = 0;
u64 installDataSize = 0; u64 installDataSize = 0;
std::atomic<bool> pending{};
std::atomic<bool> working{};
Event readyEvent;
protected: protected:
// Note: this can change while loading, use GetTitle(). // Note: this can change while loading, use GetTitle().
std::string title; std::string title;
@ -178,7 +183,7 @@ public:
// but filled in later asynchronously in the background. So keep calling this, // 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 // 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. // 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 FlushBGs(); // Gets rid of all BG textures. Also gets rid of bg sounds.
void CancelAll(); void CancelAll();
@ -191,6 +196,7 @@ private:
// Maps ISO path to info. Need to use shared_ptr as we can return these pointers - // 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. // and if they get destructed while being in use, that's bad.
std::map<std::string, std::shared_ptr<GameInfo> > info_; std::map<std::string, std::shared_ptr<GameInfo> > info_;
std::mutex mapLock_;
}; };
// This one can be global, no good reason not to. // This one can be global, no good reason not to.

View File

@ -86,7 +86,7 @@ void GameScreen::update() {
} }
void GameScreen::CreateViews() { 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()) { if (info && !info->id.empty()) {
saveDirs = info->GetSaveDataDirectories(); // Get's very heavy, let's not do it in update() 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); rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Delete Game"))))->OnClick.Handle(this, &GameScreen::OnDeleteGame);
if (System_GetPropertyBool(SYSPROP_CAN_CREATE_SHORTCUT)) { if (System_GetPropertyBool(SYSPROP_CAN_CREATE_SHORTCUT)) {
rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Create Shortcut"))))->OnClick.Add([=](UI::EventParams &e) { 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); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO);
if (info) { if (info->Ready(GameInfoFlags::PARAM_SFO)) {
// TODO: Should we block on Ready?
System_CreateGameShortcut(gamePath_, info->GetTitle()); System_CreateGameShortcut(gamePath_, info->GetTitle());
} }
return UI::EVENT_DONE; return UI::EVENT_DONE;
@ -241,8 +242,8 @@ UI::Choice *GameScreen::AddOtherChoice(UI::Choice *choice) {
} }
UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) { UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) {
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
if (!info) { if (!info->Ready(GameInfoFlags::PARAM_SFO)) {
return UI::EVENT_SKIPPED; return UI::EVENT_SKIPPED;
} }
g_Config.createGameConfig(info->id); g_Config.createGameConfig(info->id);
@ -255,8 +256,8 @@ UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) {
void GameScreen::CallbackDeleteConfig(bool yes) { void GameScreen::CallbackDeleteConfig(bool yes) {
if (yes) { if (yes) {
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
if (!info) { if (!info->Ready(GameInfoFlags::PARAM_SFO)) {
return; return;
} }
g_Config.deleteGameConfig(info->id); g_Config.deleteGameConfig(info->id);
@ -283,7 +284,7 @@ ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) {
Draw::DrawContext *draw = screenManager()->getDrawContext(); 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_) { if (tvTitle_) {
tvTitle_->SetText(info->GetTitle()); 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. // At this point, the above buttons won't become visible. We can show these now.
for (UI::Choice *choice : otherChoices_) { for (UI::Choice *choice : otherChoices_) {
choice->SetVisibility(UI::V_VISIBLE); choice->SetVisibility(UI::V_VISIBLE);
@ -448,8 +449,8 @@ UI::EventReturn GameScreen::OnPlay(UI::EventParams &e) {
} }
UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { UI::EventReturn GameScreen::OnGameSettings(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 && info->paramSFOLoaded) { if (info && info->Ready(GameInfoFlags::PARAM_SFO)) {
std::string discID = info->paramSFO.GetValueString("DISC_ID"); std::string discID = info->paramSFO.GetValueString("DISC_ID");
if ((discID.empty() || !info->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/")) if ((discID.empty() || !info->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/"))
discID = g_paramSFO.GenerateFakeID(gamePath_); discID = g_paramSFO.GenerateFakeID(gamePath_);
@ -459,7 +460,7 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) {
} }
UI::EventReturn GameScreen::OnDeleteSaveData(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) { if (info) {
// Check that there's any savedata to delete // Check that there's any savedata to delete
if (saveDirs.size()) { if (saveDirs.size()) {
@ -470,14 +471,13 @@ UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) {
std::bind(&GameScreen::CallbackDeleteSaveData, this, std::placeholders::_1))); std::bind(&GameScreen::CallbackDeleteSaveData, this, std::placeholders::_1)));
} }
} }
RecreateViews(); RecreateViews();
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
void GameScreen::CallbackDeleteSaveData(bool yes) { void GameScreen::CallbackDeleteSaveData(bool yes) {
if (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->DeleteAllSaveData();
info->saveDataSize = 0; info->saveDataSize = 0;
info->installDataSize = 0; info->installDataSize = 0;
@ -485,21 +485,20 @@ void GameScreen::CallbackDeleteSaveData(bool yes) {
} }
UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) { UI::EventReturn GameScreen::OnDeleteGame(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) { if (info && info->Ready(GameInfoFlags::PARAM_SFO)) {
auto di = GetI18NCategory(I18NCat::DIALOG); auto di = GetI18NCategory(I18NCat::DIALOG);
auto ga = GetI18NCategory(I18NCat::GAME); auto ga = GetI18NCategory(I18NCat::GAME);
screenManager()->push( 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"), 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))); std::bind(&GameScreen::CallbackDeleteGame, this, std::placeholders::_1)));
} }
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
void GameScreen::CallbackDeleteGame(bool yes) { void GameScreen::CallbackDeleteGame(bool yes) {
if (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(); info->Delete();
g_gameInfoCache->Clear(); g_gameInfoCache->Clear();
screenManager()->switchScreen(new MainScreen()); screenManager()->switchScreen(new MainScreen());
@ -560,8 +559,8 @@ void SetBackgroundPopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
void SetBackgroundPopupScreen::update() { void SetBackgroundPopupScreen::update() {
PopupScreen::update(); PopupScreen::update();
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTBGDATA); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::BG);
if (status_ == Status::PENDING && info && !info->pending) { if (status_ == Status::PENDING && info && info->Ready(GameInfoFlags::BG)) {
GameInfoTex *pic = nullptr; GameInfoTex *pic = nullptr;
if (info->pic1.dataLoaded && info->pic1.data.size()) { if (info->pic1.dataLoaded && info->pic1.data.size()) {
pic = &info->pic1; pic = &info->pic1;

View File

@ -221,7 +221,7 @@ void GameSettingsScreen::PreCreateViews() {
ReloadAllThemeInfo(); ReloadAllThemeInfo();
if (editThenRestore_) { 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()); g_Config.loadGameConfig(gameID_, info->GetTitle());
} }
@ -1454,7 +1454,7 @@ void GameSettingsScreen::onFinish(DialogResult result) {
if (editThenRestore_) { if (editThenRestore_) {
// In case we didn't have the title yet before, try again. // 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.changeGameSpecific(gameID_, info->GetTitle());
g_Config.unloadGameConfig(); g_Config.unloadGameConfig();
} }

View File

@ -226,12 +226,12 @@ private:
}; };
void GameButton::Draw(UIContext &dc) { 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; Draw::Texture *texture = 0;
u32 color = 0, shadowColor = 0; u32 color = 0, shadowColor = 0;
using namespace UI; using namespace UI;
if (ginfo->icon.texture) { if (ginfo->Ready(GameInfoFlags::ICON) && ginfo->icon.texture) {
texture = ginfo->icon.texture; texture = ginfo->icon.texture;
} }
@ -427,10 +427,9 @@ void GameButton::Draw(UIContext &dc) {
} }
std::string GameButton::DescribeText() const { std::string GameButton::DescribeText() const {
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
if (ginfo->pending) if (!ginfo->Ready(GameInfoFlags::PARAM_SFO))
return "..."; return "...";
auto u = GetI18NCategory(I18NCat::UI_ELEMENTS); auto u = GetI18NCategory(I18NCat::UI_ELEMENTS);
return ApplySafeSubstitutions(u->T("%1 button"), ginfo->GetTitle()); 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; std::shared_ptr<GameInfo> ginfo;
if (!gamePath.empty()) { 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. // Loading texture data may bind a texture.
dc.RebindTexture(); dc.RebindTexture();
// Let's not bother if there's no picture. // 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; return false;
} }
} else { } else {
@ -1471,11 +1470,10 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const Path &gamePath, float pr
UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) { UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) {
g_Config.Save("MainScreen::OnGameSelected"); g_Config.Save("MainScreen::OnGameSelected");
Path path(e.s); Path path(e.s);
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, path, GAMEINFO_WANTBG); std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, path, GameInfoFlags::FILE_TYPE);
if (ginfo && ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { if (ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) {
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
if (g_GameManager.GetState() == GameManagerState::INSTALLING) if (g_GameManager.GetState() == GameManagerState::INSTALLING)
return UI::EVENT_DONE; return UI::EVENT_DONE;

View File

@ -240,7 +240,7 @@ private:
} }
std::shared_ptr<GameInfo> ginfo = GetInfo(dc, index); 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. // Wait for it to load. It might be the next one.
break; break;
} }
@ -261,7 +261,7 @@ private:
const auto recentIsos = g_Config.RecentIsos(); const auto recentIsos = g_Config.RecentIsos();
if (index >= (int)recentIsos.size()) if (index >= (int)recentIsos.size())
return nullptr; 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) { 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) { void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z) {
using namespace Draw; using namespace Draw;
using namespace UI; using namespace UI;
std::shared_ptr<GameInfo> ginfo;
if (!gamePath.empty())
ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG);
dc.Flush(); 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) { if (pic) {
dc.GetDrawContext()->BindTexture(0, pic->texture); dc.GetDrawContext()->BindTexture(0, pic->texture);
uint32_t color = whiteAlpha(ease((time_now_d() - pic->timeLoaded) * 3)) & 0xFFc0c0c0; uint32_t color = whiteAlpha(ease((time_now_d() - pic->timeLoaded) * 3)) & 0xFFc0c0c0;

View File

@ -531,26 +531,29 @@ UI::EventReturn GamePauseScreen::OnLastSaveUndo(UI::EventParams &e) {
void GamePauseScreen::CallbackDeleteConfig(bool yes) void GamePauseScreen::CallbackDeleteConfig(bool yes)
{ {
if (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);
g_Config.unloadGameConfig(); if (info->Ready(GameInfoFlags::PARAM_SFO)) {
g_Config.deleteGameConfig(info->id); g_Config.unloadGameConfig();
info->hasConfig = false; g_Config.deleteGameConfig(info->id);
screenManager()->RecreateAllViews(); info->hasConfig = false;
screenManager()->RecreateAllViews();
}
} }
} }
UI::EventReturn GamePauseScreen::OnCreateConfig(UI::EventParams &e) 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);
std::string gameId = g_paramSFO.GetDiscID(); if (info->Ready(GameInfoFlags::PARAM_SFO)) {
g_Config.createGameConfig(gameId); std::string gameId = g_paramSFO.GetDiscID();
g_Config.changeGameSpecific(gameId, info->GetTitle()); g_Config.createGameConfig(gameId);
g_Config.saveGameConfig(gameId, info->GetTitle()); g_Config.changeGameSpecific(gameId, info->GetTitle());
if (info) { g_Config.saveGameConfig(gameId, info->GetTitle());
info->hasConfig = true; if (info) {
info->hasConfig = true;
}
screenManager()->topScreen()->RecreateViews();
} }
screenManager()->topScreen()->RecreateViews();
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }

View File

@ -87,8 +87,8 @@ public:
UIContext &dc = *screenManager()->getUIContext(); UIContext &dc = *screenManager()->getUIContext();
const Style &textStyle = dc.theme->popupStyle; const Style &textStyle = dc.theme->popupStyle;
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE);
if (!ginfo) if (!ginfo->Ready(GameInfoFlags::PARAM_SFO))
return; return;
ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); 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) { 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(); ginfo->Delete();
TriggerFinish(DR_NO); TriggerFinish(DR_NO);
return UI::EVENT_DONE; return UI::EVENT_DONE;
@ -274,8 +274,8 @@ static std::string CleanSaveString(const std::string &str) {
} }
bool SavedataButton::UpdateText() { bool SavedataButton::UpdateText() {
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);
if (!ginfo->pending) { if (ginfo->Ready(GameInfoFlags::PARAM_SFO)) {
UpdateText(ginfo); UpdateText(ginfo);
return true; return true;
} }
@ -294,7 +294,7 @@ void SavedataButton::UpdateText(const std::shared_ptr<GameInfo> &ginfo) {
} }
void SavedataButton::Draw(UIContext &dc) { 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; Draw::Texture *texture = 0;
u32 color = 0, shadowColor = 0; u32 color = 0, shadowColor = 0;
using namespace UI; using namespace UI;
@ -682,7 +682,10 @@ UI::EventReturn SavedataScreen::OnSearch(UI::EventParams &e) {
} }
UI::EventReturn SavedataScreen::OnSavedataButtonClick(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()); SavedataPopupScreen *popupScreen = new SavedataPopupScreen(e.s, ginfo->GetTitle());
if (e.v) { if (e.v) {
popupScreen->SetPopupOrigin(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) { void GameIconView::Draw(UIContext &dc) {
using namespace UI; using namespace UI;
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GameInfoFlags::ICON);
if (!info->Ready(GameInfoFlags::ICON) || !info->icon.texture) {
if (!info->icon.texture) {
return; return;
} }
textureWidth_ = info->icon.texture->Width() * scale_; Draw::Texture *texture = info->icon.texture;
textureHeight_ = info->icon.texture->Height() * scale_;
textureWidth_ = texture->Width() * scale_;
textureHeight_ = texture->Height() * scale_;
// Fade icon with the backgrounds. // Fade icon with the backgrounds.
double loadTime = info->icon.timeLoaded; 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); float nw = std::min(bounds_.h * textureWidth_ / textureHeight_, (float)bounds_.w);
dc.Flush(); 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.Draw()->Rect(bounds_.x, bounds_.y, nw, bounds_.h, color);
dc.Flush(); dc.Flush();
dc.RebindTexture(); dc.RebindTexture();