Cleanup a lot of the bootup state management.

This commit is contained in:
Henrik Rydgård 2025-03-30 14:02:29 +02:00
parent e6ea158dcc
commit 088a02bfdb
30 changed files with 239 additions and 257 deletions

View file

@ -1700,7 +1700,7 @@ Path Config::getGameConfigFile(const std::string &pGameId, bool *exists) {
return iniFileNameFull; return iniFileNameFull;
} }
bool Config::saveGameConfig(const std::string &pGameId, const std::string &title) { bool Config::saveGameConfig(const std::string &pGameId, const std::string &titleForComment) {
if (pGameId.empty()) { if (pGameId.empty()) {
return false; return false;
} }
@ -1711,7 +1711,7 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title
IniFile iniFile; IniFile iniFile;
Section *top = iniFile.GetOrCreateSection(""); Section *top = iniFile.GetOrCreateSection("");
top->AddComment(StringFromFormat("Game config for %s - %s", pGameId.c_str(), title.c_str())); top->AddComment(StringFromFormat("Game config for %s - %s", pGameId.c_str(), titleForComment.c_str()));
PreSaveCleanup(true); PreSaveCleanup(true);

View file

@ -196,7 +196,7 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
while (address < end && start <= address) while (address < end && start <= address)
{ {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return; return;
std::lock_guard<std::recursive_mutex> guard(entriesLock_); std::lock_guard<std::recursive_mutex> guard(entriesLock_);
@ -406,7 +406,7 @@ void DisassemblyManager::clear()
DisassemblyFunction::DisassemblyFunction(u32 _address, u32 _size): address(_address), size(_size) DisassemblyFunction::DisassemblyFunction(u32 _address, u32 _size): address(_address), size(_size)
{ {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return; return;
hash = computeHash(address,size); hash = computeHash(address,size);
@ -419,7 +419,7 @@ DisassemblyFunction::~DisassemblyFunction() {
void DisassemblyFunction::recheck() void DisassemblyFunction::recheck()
{ {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return; return;
HashType newHash = computeHash(address,size); HashType newHash = computeHash(address,size);
@ -872,14 +872,14 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo &dest, bool
DisassemblyData::DisassemblyData(u32 _address, u32 _size, DataType _type): address(_address), size(_size), type(_type) DisassemblyData::DisassemblyData(u32 _address, u32 _size, DataType _type): address(_address), size(_size), type(_type)
{ {
_dbg_assert_(PSP_IsInited()); _dbg_assert_(PSP_GetBootState() == BootState::Complete);
hash = computeHash(address,size); hash = computeHash(address,size);
createLines(); createLines();
} }
void DisassemblyData::recheck() void DisassemblyData::recheck()
{ {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return; return;
HashType newHash = computeHash(address,size); HashType newHash = computeHash(address,size);

View file

@ -99,10 +99,6 @@ static void UpdateConnected(int delta) {
} }
static void WebSocketNotifyLifecycle(CoreLifecycle stage) { static void WebSocketNotifyLifecycle(CoreLifecycle stage) {
// We'll likely already be locked during the reboot.
if (PSP_IsRebooting())
return;
switch (stage) { switch (stage) {
case CoreLifecycle::STARTING: case CoreLifecycle::STARTING:
case CoreLifecycle::STOPPING: case CoreLifecycle::STOPPING:

View file

@ -107,12 +107,15 @@ void WebSocketCPUResume(DebuggerRequest &req) {
// - ticks: number of CPU cycles into emulation. // - ticks: number of CPU cycles into emulation.
void WebSocketCPUStatus(DebuggerRequest &req) { void WebSocketCPUStatus(DebuggerRequest &req) {
JsonWriter &json = req.Respond(); JsonWriter &json = req.Respond();
json.writeBool("stepping", PSP_IsInited() && Core_IsStepping() && coreState != CORE_POWERDOWN);
const bool pspInited = PSP_GetBootState() == BootState::Complete;
json.writeBool("stepping", pspInited && Core_IsStepping() && coreState != CORE_POWERDOWN);
json.writeBool("paused", GetUIState() != UISTATE_INGAME); json.writeBool("paused", GetUIState() != UISTATE_INGAME);
// Avoid NULL deference. // Avoid NULL deference.
json.writeUint("pc", PSP_IsInited() ? currentMIPS->pc : 0); json.writeUint("pc", pspInited ? currentMIPS->pc : 0);
// A double ought to be good enough for a 156 day debug session. // A double ought to be good enough for a 156 day debug session.
json.writeFloat("ticks", PSP_IsInited() ? CoreTiming::GetTicks() : 0); json.writeFloat("ticks", pspInited ? CoreTiming::GetTicks() : 0);
} }
// Retrieve all regs and their values (cpu.getAllRegs) // Retrieve all regs and their values (cpu.getAllRegs)

View file

@ -58,7 +58,7 @@ WebSocketGPURecordState::~WebSocketGPURecordState() {
// //
// Note: recording may take a moment. // Note: recording may take a moment.
void WebSocketGPURecordState::Dump(DebuggerRequest &req) { void WebSocketGPURecordState::Dump(DebuggerRequest &req) {
if (!PSP_IsInited()) { if (PSP_GetBootState() != BootState::Complete) {
return req.Fail("CPU not started"); return req.Fail("CPU not started");
} }

View file

@ -150,7 +150,7 @@ void WebSocketGPUStatsState::FlipListener() {
// Note: stats are returned after the next flip completes (paused if CPU or GPU in break.) // Note: stats are returned after the next flip completes (paused if CPU or GPU in break.)
// Note: info and timing may not be accurate if certain settings are disabled. // Note: info and timing may not be accurate if certain settings are disabled.
void WebSocketGPUStatsState::Get(DebuggerRequest &req) { void WebSocketGPUStatsState::Get(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("CPU not started"); return req.Fail("CPU not started");
std::lock_guard<std::mutex> guard(pendingLock_); std::lock_guard<std::mutex> guard(pendingLock_);
@ -169,7 +169,7 @@ void WebSocketGPUStatsState::Get(DebuggerRequest &req) {
// //
// Note: info and timing will be accurate after the first frame. // Note: info and timing will be accurate after the first frame.
void WebSocketGPUStatsState::Feed(DebuggerRequest &req) { void WebSocketGPUStatsState::Feed(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("CPU not started"); return req.Fail("CPU not started");
bool enable = true; bool enable = true;
if (!req.ParamBool("enable", &enable, DebuggerParamType::OPTIONAL)) if (!req.ParamBool("enable", &enable, DebuggerParamType::OPTIONAL))

View file

@ -27,7 +27,7 @@ struct GameStatusEvent {
JsonWriter j; JsonWriter j;
j.begin(); j.begin();
j.writeString("event", ev); j.writeString("event", ev);
if (PSP_IsInited()) { if (PSP_GetBootState() == BootState::Complete) {
j.pushDict("game"); j.pushDict("game");
j.writeString("id", g_paramSFO.GetDiscID()); j.writeString("id", g_paramSFO.GetDiscID());
j.writeString("version", g_paramSFO.GetValueString("DISC_VERSION")); j.writeString("version", g_paramSFO.GetValueString("DISC_VERSION"));
@ -83,10 +83,10 @@ void GameBroadcaster::Broadcast(net::WebSocketServer *ws) {
} else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) { } else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) {
ws->Send(GameStatusEvent{"game.resume"}); ws->Send(GameStatusEvent{"game.resume"});
prevState_ = state; prevState_ = state;
} else if (state == UISTATE_INGAME && PSP_IsInited()) { } else if (state == UISTATE_INGAME && PSP_GetBootState() == BootState::Complete) {
ws->Send(GameStatusEvent{"game.start"}); ws->Send(GameStatusEvent{"game.start"});
prevState_ = state; prevState_ = state;
} else if (state == UISTATE_MENU && !PSP_IsInited() && !PSP_IsQuitting() && !PSP_IsRebooting()) { } else if (state == UISTATE_MENU && PSP_GetBootState() != BootState::Complete) {
ws->Send(GameStatusEvent{"game.quit"}); ws->Send(GameStatusEvent{"game.quit"});
prevState_ = state; prevState_ = state;
} }

View file

@ -39,7 +39,7 @@ DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) {
// //
// Response (same event name) with no extra data or error. // Response (same event name) with no extra data or error.
void WebSocketGameReset(DebuggerRequest &req) { void WebSocketGameReset(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("Game not running"); return req.Fail("Game not running");
bool needBreak = false; bool needBreak = false;
@ -50,7 +50,7 @@ void WebSocketGameReset(DebuggerRequest &req) {
PSP_CoreParameter().startBreak = true; PSP_CoreParameter().startBreak = true;
std::string resetError; std::string resetError;
if (!PSP_Reboot(&resetError)) { if (PSP_Reboot(&resetError) != BootState::Complete) {
ERROR_LOG(Log::Boot, "Error resetting: %s", resetError.c_str()); ERROR_LOG(Log::Boot, "Error resetting: %s", resetError.c_str());
return req.Fail("Could not reset"); return req.Fail("Could not reset");
} }
@ -73,7 +73,7 @@ void WebSocketGameReset(DebuggerRequest &req) {
// - paused: boolean, true when gameplay is paused (not the same as stepping.) // - paused: boolean, true when gameplay is paused (not the same as stepping.)
void WebSocketGameStatus(DebuggerRequest &req) { void WebSocketGameStatus(DebuggerRequest &req) {
JsonWriter &json = req.Respond(); JsonWriter &json = req.Respond();
if (PSP_IsInited()) { if (PSP_GetBootState() == BootState::Complete) {
json.pushDict("game"); json.pushDict("game");
json.writeString("id", g_paramSFO.GetDiscID()); json.writeString("id", g_paramSFO.GetDiscID());
json.writeString("version", g_paramSFO.GetValueString("DISC_VERSION")); json.writeString("version", g_paramSFO.GetValueString("DISC_VERSION"));

View file

@ -101,7 +101,7 @@ void WebSocketHLEThreadList(DebuggerRequest &req) {
} }
static bool ThreadInfoForStatus(DebuggerRequest &req, DebugThreadInfo *result) { static bool ThreadInfoForStatus(DebuggerRequest &req, DebugThreadInfo *result) {
if (!PSP_IsInited()) { if (PSP_GetBootState() != BootState::Complete) {
req.Fail("CPU not active"); req.Fail("CPU not active");
return false; return false;
} }

View file

@ -71,7 +71,7 @@ void WebSocketReplayAbort(DebuggerRequest &req) {
// - version: unsigned integer, version number of data. // - version: unsigned integer, version number of data.
// - base64: base64 encode of binary data. // - base64: base64 encode of binary data.
void WebSocketReplayFlush(DebuggerRequest &req) { void WebSocketReplayFlush(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("Game not running"); return req.Fail("Game not running");
std::vector<uint8_t> data; std::vector<uint8_t> data;
@ -90,7 +90,7 @@ void WebSocketReplayFlush(DebuggerRequest &req) {
// //
// Response (same event name) with no extra data. // Response (same event name) with no extra data.
void WebSocketReplayExecute(DebuggerRequest &req) { void WebSocketReplayExecute(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("Game not running"); return req.Fail("Game not running");
uint32_t version = -1; uint32_t version = -1;
@ -130,7 +130,7 @@ void WebSocketReplayStatus(DebuggerRequest &req) {
// Response (same event name): // Response (same event name):
// - value: unsigned integer, may have more than 32 integer bits. // - value: unsigned integer, may have more than 32 integer bits.
void WebSocketReplayTimeGet(DebuggerRequest &req) { void WebSocketReplayTimeGet(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("Game not running"); return req.Fail("Game not running");
JsonWriter &json = req.Respond(); JsonWriter &json = req.Respond();
@ -144,7 +144,7 @@ void WebSocketReplayTimeGet(DebuggerRequest &req) {
// //
// Response (same event name) with no extra data. // Response (same event name) with no extra data.
void WebSocketReplayTimeSet(DebuggerRequest &req) { void WebSocketReplayTimeSet(DebuggerRequest &req) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return req.Fail("Game not running"); return req.Fail("Game not running");
uint32_t value; uint32_t value;

View file

@ -57,7 +57,7 @@ private:
// //
// Sent unexpectedly with no other properties. // Sent unexpectedly with no other properties.
void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) {
if (PSP_IsInited()) { if (PSP_GetBootState() == BootState::Complete) {
int steppingCounter = Core_GetSteppingCounter(); int steppingCounter = Core_GetSteppingCounter();
// We ignore CORE_POWERDOWN as a stepping state. // We ignore CORE_POWERDOWN as a stepping state.
if (coreState == CORE_STEPPING_CPU && steppingCounter != lastCounter_) { if (coreState == CORE_STEPPING_CPU && steppingCounter != lastCounter_) {

View file

@ -326,9 +326,7 @@ std::string __KernelStateSummary() {
return __KernelThreadingSummary(); return __KernelThreadingSummary();
} }
void sceKernelExitGame() {
void sceKernelExitGame()
{
INFO_LOG(Log::sceKernel, "sceKernelExitGame"); INFO_LOG(Log::sceKernel, "sceKernelExitGame");
__KernelSwitchOffThread("game exited"); __KernelSwitchOffThread("game exited");
Core_Stop(); Core_Stop();

View file

@ -210,7 +210,7 @@ unsigned int MIPSDebugInterface::readMemory(unsigned int address) {
bool MIPSDebugInterface::isAlive() bool MIPSDebugInterface::isAlive()
{ {
return PSP_IsInited() && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN; return PSP_GetBootState() == BootState::Complete && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN;
} }
bool MIPSDebugInterface::isBreakpoint(unsigned int address) bool MIPSDebugInterface::isBreakpoint(unsigned int address)

View file

@ -313,7 +313,7 @@ bool Init() {
} }
void Reinit() { void Reinit() {
_assert_msg_(PSP_IsInited(), "Cannot reinit during startup/shutdown"); _assert_msg_(PSP_GetBootState() == BootState::Complete, "Cannot reinit during startup/shutdown");
Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITING); Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITING);
Shutdown(); Shutdown();
Init(); Init();

View file

@ -250,16 +250,6 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
return false; return false;
} }
// OK, pretty confident we have a PSP game.
if (g_paramSFO.IsValid()) {
std::string title = StringFromFormat("%s : %s", g_paramSFO.GetValueString("DISC_ID").c_str(), g_paramSFO.GetValueString("TITLE").c_str());
INFO_LOG(Log::Loader, "%s", title.c_str());
System_SetWindowTitle(title);
} else {
// Should have been loaded earlier in the process.
_dbg_assert_(false);
}
//in case we didn't go through EmuScreen::boot //in case we didn't go through EmuScreen::boot
g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE")); g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE"));
System_PostUIMessage(UIMessage::CONFIG_LOADED); System_PostUIMessage(UIMessage::CONFIG_LOADED);
@ -375,17 +365,10 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) {
homebrewName = homebrewName.substr(lslash + 1); homebrewName = homebrewName.substr(lslash + 1);
if (rslash != homebrewName.npos) if (rslash != homebrewName.npos)
homebrewName = homebrewName.substr(rslash + 1); homebrewName = homebrewName.substr(rslash + 1);
std::string homebrewTitle = g_paramSFO.GetValueString("TITLE");
if (homebrewTitle.empty())
homebrewTitle = homebrewName;
std::string discID = g_paramSFO.GetDiscID(); std::string discID = g_paramSFO.GetDiscID();
std::string discVersion = g_paramSFO.GetValueString("DISC_VERSION"); std::string discVersion = g_paramSFO.GetValueString("DISC_VERSION");
std::string madeUpID = g_paramSFO.GenerateFakeID(Path()); std::string madeUpID = g_paramSFO.GenerateFakeID(Path());
std::string title = StringFromFormat("%s : %s", discID.c_str(), homebrewTitle.c_str());
INFO_LOG(Log::Loader, "%s", title.c_str());
System_SetWindowTitle(title);
// Migrate old save states from old versions of fake game IDs. // Migrate old save states from old versions of fake game IDs.
// Ugh, this might actually be slow on Android. // Ugh, this might actually be slow on Android.
const Path savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE); const Path savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE);

View file

@ -381,7 +381,7 @@ namespace Reporting
void UpdateConfig() { void UpdateConfig() {
currentSupported = IsSupported(); currentSupported = IsSupported();
if (!currentSupported && PSP_IsInited()) if (!currentSupported && PSP_GetBootState() == BootState::Complete)
everUnsupported = true; everUnsupported = true;
} }
@ -438,7 +438,7 @@ namespace Reporting
void AddGameplayInfo(UrlEncoder &postdata) void AddGameplayInfo(UrlEncoder &postdata)
{ {
// Just to get an idea of how long they played. // Just to get an idea of how long they played.
if (PSP_IsInited()) if (PSP_GetBootState() == BootState::Complete)
postdata.Add("ticks", (const uint64_t)CoreTiming::GetTicks()); postdata.Add("ticks", (const uint64_t)CoreTiming::GetTicks());
float vps, fps; float vps, fps;
@ -536,7 +536,7 @@ namespace Reporting
// Don't report from games without a version ID (i.e. random hashed homebrew IDs.) // Don't report from games without a version ID (i.e. random hashed homebrew IDs.)
// The problem is, these aren't useful because the hashes end up different for different people. // The problem is, these aren't useful because the hashes end up different for different people.
// TODO: Should really hash the ELF instead of the path, but then that affects savestates/cheats. // TODO: Should really hash the ELF instead of the path, but then that affects savestates/cheats.
if (PSP_IsInited() && g_paramSFO.GetValueString("DISC_VERSION").empty()) if (PSP_GetBootState() == BootState::Complete && g_paramSFO.GetValueString("DISC_VERSION").empty())
return false; return false;
// Some users run the exe from a zip or something, and don't have fonts. // Some users run the exe from a zip or something, and don't have fonts.
@ -555,7 +555,7 @@ namespace Reporting
bool IsEnabled() bool IsEnabled()
{ {
if (g_Config.sReportHost.empty() || (!currentSupported && PSP_IsInited())) if (g_Config.sReportHost.empty() || (!currentSupported && PSP_GetBootState() == BootState::Complete))
return false; return false;
// Disabled by default for now. // Disabled by default for now.
if (g_Config.sReportHost.compare("default") == 0) if (g_Config.sReportHost.compare("default") == 0)

View file

@ -1147,15 +1147,10 @@ double g_lastSaveTime = -1.0;
} }
void Cleanup() { void Cleanup() {
// TODO: Handle this better.
if (needsRestart) { if (needsRestart) {
PSP_Shutdown(); std::string error_string;
std::string resetError; PSP_Reboot(&error_string);
if (!PSP_Init(PSP_CoreParameter(), &resetError)) {
ERROR_LOG(Log::Boot, "Error resetting: %s", resetError.c_str());
// TODO: This probably doesn't clean up well enough.
Core_Stop();
return;
}
System_Notify(SystemNotification::BOOT_DONE); System_Notify(SystemNotification::BOOT_DONE);
System_Notify(SystemNotification::DISASSEMBLY); System_Notify(SystemNotification::DISASSEMBLY);
needsRestart = false; needsRestart = false;

View file

@ -71,6 +71,7 @@
#include "GPU/GPUCommon.h" #include "GPU/GPUCommon.h"
#include "GPU/Debugger/Playback.h" #include "GPU/Debugger/Playback.h"
#include "GPU/Debugger/RecordFormat.h" #include "GPU/Debugger/RecordFormat.h"
#include "UI/DiscordIntegration.h"
enum CPUThreadState { enum CPUThreadState {
CPU_THREAD_NOT_RUNNING, CPU_THREAD_NOT_RUNNING,
@ -94,11 +95,11 @@ static volatile CPUThreadState cpuThreadState = CPU_THREAD_NOT_RUNNING;
static GPUBackend gpuBackend; static GPUBackend gpuBackend;
static std::string gpuBackendDevice; static std::string gpuBackendDevice;
// Ugly! static BootState g_bootState = BootState::Off;
static volatile bool pspIsInited = false;
static volatile bool pspIsIniting = false; BootState PSP_GetBootState() {
static volatile bool pspIsQuitting = false; return g_bootState;
static volatile bool pspIsRebooting = false; }
void ResetUIState() { void ResetUIState() {
globalUIState = UISTATE_MENU; globalUIState = UISTATE_MENU;
@ -131,21 +132,7 @@ std::string GetGPUBackendDevice() {
return gpuBackendDevice; return gpuBackendDevice;
} }
bool CPU_IsReady() { void CPU_Shutdown(bool success);
if (coreState == CORE_POWERUP)
return false;
return cpuThreadState == CPU_THREAD_RUNNING || cpuThreadState == CPU_THREAD_NOT_RUNNING;
}
bool CPU_IsShutdown() {
return cpuThreadState == CPU_THREAD_NOT_RUNNING;
}
bool CPU_HasPendingAction() {
return cpuThreadState != CPU_THREAD_RUNNING;
}
void CPU_Shutdown();
static Path SymbolMapFilename(const Path &currentFilename, const char *ext) { static Path SymbolMapFilename(const Path &currentFilename, const char *ext) {
File::FileInfo info{}; File::FileInfo info{};
@ -249,6 +236,24 @@ static void GetBootError(IdentifiedFileType type, std::string *errorString) {
} }
} }
static void ShowCompatWarnings(const Compatibility &compat) {
// UI changes are best done after PSP_InitStart.
if (compat.flags().RequireBufferedRendering && g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BufferedRenderingRequired", "Warning: This game requires Rendering Mode to be set to Buffered."), 10.0f);
}
if (compat.flags().RequireBlockTransfer && g_Config.iSkipGPUReadbackMode != (int)SkipGPUReadbackMode::NO_SKIP && !PSP_CoreParameter().compat.flags().ForceEnableGPUReadback) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BlockTransferRequired", "Warning: This game requires Skip GPU Readbacks be set to No."), 10.0f);
}
if (compat.flags().RequireDefaultCPUClock && g_Config.iLockedCPUSpeed != 0) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f);
}
}
// NOTE: The loader has already been fully resolved (ResolveFileLoaderTarget) and identified here. // NOTE: The loader has already been fully resolved (ResolveFileLoaderTarget) and identified here.
static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::string *errorString) { static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::string *errorString) {
// Default memory settings // Default memory settings
@ -263,6 +268,8 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
std::string geDumpDiscID; std::string geDumpDiscID;
std::string gameTitle = "Unidentified PSP title";
switch (type) { switch (type) {
case IdentifiedFileType::PSP_ISO: case IdentifiedFileType::PSP_ISO:
case IdentifiedFileType::PSP_ISO_NP: case IdentifiedFileType::PSP_ISO_NP:
@ -294,6 +301,7 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
if (DiscIDFromGEDumpPath(g_CoreParameter.fileToStart, fileLoader, &geDumpDiscID)) { if (DiscIDFromGEDumpPath(g_CoreParameter.fileToStart, fileLoader, &geDumpDiscID)) {
// Store in SFO, otherwise it'll generate a fake disc ID. // Store in SFO, otherwise it'll generate a fake disc ID.
g_paramSFO.SetValue("DISC_ID", geDumpDiscID, 16); g_paramSFO.SetValue("DISC_ID", geDumpDiscID, 16);
gameTitle = g_CoreParameter.fileToStart.GetFilename();
} }
break; break;
default: default:
@ -306,6 +314,15 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
} }
} }
// OK, pretty confident we have a PSP game.
if (g_paramSFO.IsValid()) {
gameTitle = SanitizeString(g_paramSFO.GetValueString("TITLE"), StringRestriction::NoLineBreaksOrSpecials);
}
std::string title = StringFromFormat("%s : %s", g_paramSFO.GetValueString("DISC_ID").c_str(), gameTitle.c_str());
INFO_LOG(Log::Loader, "%s", title.c_str());
System_SetWindowTitle(title);
currentMIPS = &mipsr4k; currentMIPS = &mipsr4k;
g_symbolMap = new SymbolMap(); g_symbolMap = new SymbolMap();
@ -319,6 +336,7 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
// Homebrew usually has an empty discID, and even if they do have a disc id, it's not // Homebrew usually has an empty discID, and even if they do have a disc id, it's not
// likely to collide with any commercial ones. // likely to collide with any commercial ones.
g_CoreParameter.compat.Load(g_paramSFO.GetDiscID()); g_CoreParameter.compat.Load(g_paramSFO.GetDiscID());
ShowCompatWarnings(g_CoreParameter.compat);
// Compat settings can override the software renderer, take care of that here. // Compat settings can override the software renderer, take care of that here.
if (g_Config.bSoftwareRendering || PSP_CoreParameter().compat.flags().ForceSoftwareRenderer) { if (g_Config.bSoftwareRendering || PSP_CoreParameter().compat.flags().ForceSoftwareRenderer) {
@ -329,7 +347,6 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
if (!Memory::Init()) { if (!Memory::Init()) {
// We're screwed. // We're screwed.
*errorString = "Memory init failed"; *errorString = "Memory init failed";
CPU_Shutdown();
return false; return false;
} }
@ -370,7 +387,10 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
dir = ResolvePBPDirectory(Path(dir)).ToString(); dir = ResolvePBPDirectory(Path(dir)).ToString();
pspFileSystem.SetStartingDirectory("ms0:/" + dir.substr(pos)); pspFileSystem.SetStartingDirectory("ms0:/" + dir.substr(pos));
} }
return Load_PSP_ELF_PBP(fileLoader, errorString); if (!Load_PSP_ELF_PBP(fileLoader, errorString)) {
return false;
}
break;
} }
// Looks like a wrong fall through but is not, both paths are handled above. // Looks like a wrong fall through but is not, both paths are handled above.
@ -378,17 +398,26 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
case IdentifiedFileType::PSP_ELF: case IdentifiedFileType::PSP_ELF:
{ {
INFO_LOG(Log::Loader, "File is an ELF or loose PBP! %s", fileLoader->GetPath().c_str()); INFO_LOG(Log::Loader, "File is an ELF or loose PBP! %s", fileLoader->GetPath().c_str());
return Load_PSP_ELF_PBP(fileLoader, errorString); if (!Load_PSP_ELF_PBP(fileLoader, errorString)) {
return false;
}
break;
} }
case IdentifiedFileType::PSP_ISO: case IdentifiedFileType::PSP_ISO:
case IdentifiedFileType::PSP_ISO_NP: case IdentifiedFileType::PSP_ISO_NP:
case IdentifiedFileType::PSP_DISC_DIRECTORY: // behaves the same as the mounting is already done by now case IdentifiedFileType::PSP_DISC_DIRECTORY: // behaves the same as the mounting is already done by now
pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR"); pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR");
return Load_PSP_ISO(fileLoader, errorString); if (!Load_PSP_ISO(fileLoader, errorString)) {
return false;
}
break;
case IdentifiedFileType::PPSSPP_GE_DUMP: case IdentifiedFileType::PPSSPP_GE_DUMP:
return Load_PSP_GE_Dump(fileLoader, errorString); if (!Load_PSP_GE_Dump(fileLoader, errorString)) {
return false;
}
break;
default: default:
GetBootError(type, errorString); GetBootError(type, errorString);
@ -405,20 +434,12 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
return true; return true;
} }
PSP_LoadingLock::PSP_LoadingLock() { void CPU_Shutdown(bool success) {
loadingLock.lock();
}
PSP_LoadingLock::~PSP_LoadingLock() {
loadingLock.unlock();
}
void CPU_Shutdown() {
UninstallExceptionHandler(); UninstallExceptionHandler();
GPURecord::Replay_Unload(); GPURecord::Replay_Unload();
if (g_Config.bAutoSaveSymbolMap) { if (g_Config.bAutoSaveSymbolMap && success) {
SaveSymbolMapIfSupported(); SaveSymbolMapIfSupported();
} }
@ -473,30 +494,32 @@ void PSP_ForceDebugStats(bool enable) {
} }
bool PSP_InitStart(const CoreParameter &coreParam) { bool PSP_InitStart(const CoreParameter &coreParam) {
if (pspIsIniting || pspIsQuitting) { if (g_bootState != BootState::Off) {
ERROR_LOG(Log::System, "Can't start loader thread - initing or quitting"); ERROR_LOG(Log::System, "Can't start loader thread - already on.");
return false; return false;
} }
_dbg_assert_(coreState != CORE_POWERUP);
coreState = CORE_POWERUP; coreState = CORE_POWERUP;
g_bootState = BootState::Booting;
GraphicsContext *temp = g_CoreParameter.graphicsContext; GraphicsContext *temp = g_CoreParameter.graphicsContext;
g_CoreParameter = coreParam; g_CoreParameter = coreParam;
if (g_CoreParameter.graphicsContext == nullptr) { if (g_CoreParameter.graphicsContext == nullptr) {
g_CoreParameter.graphicsContext = temp; g_CoreParameter.graphicsContext = temp;
} }
g_CoreParameter.errorString.clear(); g_CoreParameter.errorString.clear();
pspIsIniting = true;
std::string *error_string = &g_CoreParameter.errorString; std::string *error_string = &g_CoreParameter.errorString;
INFO_LOG(Log::System, "Starting loader thread..."); INFO_LOG(Log::System, "Starting loader thread...");
_dbg_assert_(!g_loadingThread.joinable());
g_loadingThread = std::thread([error_string]() { g_loadingThread = std::thread([error_string]() {
SetCurrentThreadName("ExecLoader"); SetCurrentThreadName("ExecLoader");
PSP_LoadingLock guard;
if (coreState != CORE_POWERUP)
return;
AndroidJNIThreadContext jniContext; AndroidJNIThreadContext jniContext;
@ -532,14 +555,14 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
// TODO: The reason we pass in g_CoreParameter.errorString here is that it's persistent - // TODO: The reason we pass in g_CoreParameter.errorString here is that it's persistent -
// it gets written to from the loader thread that gets spawned. // it gets written to from the loader thread that gets spawned.
if (!CPU_Init(loadedFile, type, &g_CoreParameter.errorString)) { if (!CPU_Init(loadedFile, type, &g_CoreParameter.errorString)) {
CPU_Shutdown(); CPU_Shutdown(false);
coreState = CORE_BOOT_ERROR; coreState = CORE_BOOT_ERROR;
g_CoreParameter.fileToStart.clear(); g_CoreParameter.fileToStart.clear();
*error_string = g_CoreParameter.errorString; *error_string = g_CoreParameter.errorString;
if (error_string->empty()) { if (error_string->empty()) {
*error_string = "Failed initializing CPU/Memory"; *error_string = "Failed initializing CPU/Memory";
} }
pspIsIniting = false; g_bootState = BootState::Failed;
return; return;
} }
@ -549,101 +572,84 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
} else { } else {
coreState = CORE_RUNNING_CPU; coreState = CORE_RUNNING_CPU;
} }
g_bootState = BootState::Complete;
}); });
return true; return true;
} }
bool PSP_InitUpdate(std::string *error_string) { BootState PSP_InitUpdate(std::string *error_string) {
if (pspIsInited || !pspIsIniting) { if (g_bootState == BootState::Booting || g_bootState == BootState::Off) {
return true; // We're done already.
return g_bootState;
} }
if (!CPU_IsReady()) { _dbg_assert_(g_bootState == BootState::Complete || g_bootState == BootState::Failed);
return false;
}
bool success = !g_CoreParameter.fileToStart.empty();
if (!g_CoreParameter.errorString.empty()) {
*error_string = g_CoreParameter.errorString;
}
// Since we load on a background thread, wait for startup to complete. // Since we load on a background thread, wait for startup to complete.
_dbg_assert_(g_loadingThread.joinable()); _dbg_assert_(g_loadingThread.joinable());
g_loadingThread.join(); g_loadingThread.join();
if (success && gpu == nullptr) { if (g_bootState == BootState::Failed) {
// Failed! (Note: PSP_Shutdown was already called on the loader thread).
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
*error_string = g_CoreParameter.errorString;
return g_bootState;
}
// Ok, async boot completed, let's finish up things on the main thread.
if (!gpu) { // should be!
INFO_LOG(Log::System, "Starting graphics..."); INFO_LOG(Log::System, "Starting graphics...");
Draw::DrawContext *draw = g_CoreParameter.graphicsContext ? g_CoreParameter.graphicsContext->GetDrawContext() : nullptr; Draw::DrawContext *draw = g_CoreParameter.graphicsContext ? g_CoreParameter.graphicsContext->GetDrawContext() : nullptr;
success = GPU_Init(g_CoreParameter.graphicsContext, draw); bool success = GPU_Init(g_CoreParameter.graphicsContext, draw);
if (!success) { if (!success) {
*error_string = "Unable to initialize rendering engine."; *error_string = "Unable to initialize rendering engine.";
PSP_Shutdown(false);
g_bootState = BootState::Failed;
return g_bootState;
} }
} }
if (!success) {
pspIsRebooting = false;
PSP_Shutdown();
return true;
}
pspIsInited = GPU_IsReady(); // TODO: This should all be checked during GPU_Init.
pspIsIniting = !pspIsInited;
if (pspIsInited) {
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
pspIsRebooting = false;
// If GPU init failed during IsReady checks, bail.
if (!GPU_IsStarted()) { if (!GPU_IsStarted()) {
*error_string = "Unable to initialize rendering engine."; *error_string = "Unable to initialize rendering engine.";
pspIsRebooting = false; PSP_Shutdown(false);
PSP_Shutdown(); g_bootState = BootState::Failed;
return true;
} }
}
return pspIsInited; Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
return g_bootState;
} }
// Most platforms should not use this one, they should call PSP_InitStart and then do their thing // Most platforms should not use this one, they should call PSP_InitStart and then do their thing
// while repeatedly calling PSP_InitUpdate. This is basically just for libretro convenience. // while repeatedly calling PSP_InitUpdate. This is basically just for libretro convenience.
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) { BootState PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
// InitStart doesn't really fail anymore. // InitStart doesn't really fail anymore.
if (!PSP_InitStart(coreParam)) if (!PSP_InitStart(coreParam))
return false; return BootState::Failed;
while (!PSP_InitUpdate(error_string)) while (true) {
sleep_ms(10, "psp-init-poll"); BootState state = PSP_InitUpdate(error_string);
return pspIsInited; if (state != BootState::Booting) {
return state;
}
sleep_ms(5, "psp-init-poll");
}
} }
bool PSP_IsIniting() { void PSP_Shutdown(bool success) {
return pspIsIniting;
}
bool PSP_IsInited() {
return pspIsInited && !pspIsQuitting && !pspIsRebooting;
}
bool PSP_IsRebooting() {
return pspIsRebooting;
}
bool PSP_IsQuitting() {
return pspIsQuitting;
}
void PSP_Shutdown() {
// Reduce the risk for weird races with the Windows GE debugger. // Reduce the risk for weird races with the Windows GE debugger.
gpuDebug = nullptr; gpuDebug = nullptr;
Achievements::UnloadGame(); Achievements::UnloadGame();
// Do nothing if we never inited. // Do nothing if we never inited.
if (!pspIsInited && !pspIsIniting && !pspIsQuitting) { if (g_bootState == BootState::Off) {
return; return;
} }
// Make sure things know right away that PSP memory, etc. is going away.
pspIsQuitting = !pspIsRebooting;
if (coreState == CORE_RUNNING_CPU) if (coreState == CORE_RUNNING_CPU)
Core_Stop(); Core_Stop();
@ -651,29 +657,42 @@ void PSP_Shutdown() {
MIPSAnalyst::StoreHashMap(); MIPSAnalyst::StoreHashMap();
} }
if (pspIsIniting) if (g_bootState == BootState::Booting) {
// This should only happen during failures.
Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE);
}
Core_NotifyLifecycle(CoreLifecycle::STOPPING); Core_NotifyLifecycle(CoreLifecycle::STOPPING);
CPU_Shutdown();
CPU_Shutdown(success);
GPU_Shutdown(); GPU_Shutdown();
g_paramSFO.Clear(); g_paramSFO.Clear();
System_SetWindowTitle(""); System_SetWindowTitle("");
currentMIPS = 0;
pspIsInited = false; currentMIPS = nullptr;
pspIsIniting = false;
pspIsQuitting = false;
g_Config.unloadGameConfig(); g_Config.unloadGameConfig();
Core_NotifyLifecycle(CoreLifecycle::STOPPED); Core_NotifyLifecycle(CoreLifecycle::STOPPED);
if (success) {
g_bootState = BootState::Off;
}
} }
bool PSP_Reboot(std::string *error_string) { // Call this after handling BootState::Failed.
if (!pspIsInited || pspIsQuitting) void PSP_CancelBoot() {
return false; _dbg_assert_(g_bootState == BootState::Failed);
g_bootState = BootState::Off;
}
BootState PSP_Reboot(std::string *error_string) {
if (g_bootState != BootState::Complete) {
return g_bootState;
}
pspIsRebooting = true;
Core_Stop(); Core_Stop();
Core_WaitInactive(); Core_WaitInactive();
PSP_Shutdown(); PSP_Shutdown(true);
std::string resetError; std::string resetError;
return PSP_Init(PSP_CoreParameter(), error_string); return PSP_Init(PSP_CoreParameter(), error_string);
} }

View file

@ -71,27 +71,41 @@ void SetGPUBackend(GPUBackend type, const std::string &device = "");
GPUBackend GetGPUBackend(); GPUBackend GetGPUBackend();
std::string GetGPUBackendDevice(); std::string GetGPUBackendDevice();
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string); enum class BootState {
Off,
Booting,
Complete,
Failed,
};
BootState PSP_GetBootState();
inline bool PSP_IsInited() {
return PSP_GetBootState() == BootState::Complete;
}
// Call this once, then call PSP_InitUpdate repeatedly to monitor progress.
bool PSP_InitStart(const CoreParameter &coreParam); bool PSP_InitStart(const CoreParameter &coreParam);
bool PSP_InitUpdate(std::string *error_string);
bool PSP_IsIniting(); // Check the return value of this - if Booting, keep calling.
bool PSP_IsInited(); // If Complete or Failed, handle as appropriate, and stop calling.
bool PSP_IsRebooting(); BootState PSP_InitUpdate(std::string *error_string);
bool PSP_IsQuitting();
void PSP_Shutdown(); // Blocking wrapper around the two above functions, used for convenience in a couple of places.
bool PSP_Reboot(std::string *error_string); // Should be avoided/removed eventually.
// Returns either BootState::Complete or BootState::Failed.
BootState PSP_Init(const CoreParameter &coreParam, std::string *error_string);
// Call this after handling BootState::Failed from PSP_Init.
void PSP_CancelBoot();
void PSP_Shutdown(bool success);
BootState PSP_Reboot(std::string *error_string);
void PSP_BeginHostFrame(); void PSP_BeginHostFrame();
void PSP_EndHostFrame(); void PSP_EndHostFrame();
void PSP_RunLoopWhileState(); void PSP_RunLoopWhileState();
void PSP_RunLoopFor(int cycles); void PSP_RunLoopFor(int cycles);
// Used to wait for background loading thread.
struct PSP_LoadingLock {
PSP_LoadingLock();
~PSP_LoadingLock();
};
// Call before PSP_BeginHostFrame() in order to not miss any GPU stats. // Call before PSP_BeginHostFrame() in order to not miss any GPU stats.
void PSP_UpdateDebugStats(bool collectStats); void PSP_UpdateDebugStats(bool collectStats);
// Increments or decrements an internal counter. Intended to be used by debuggers. // Increments or decrements an internal counter. Intended to be used by debuggers.

View file

@ -563,7 +563,7 @@ static void FormatVertColRawType(char *dest, size_t destSize, const void *data,
static void FormatVertColRawColor(char *dest, size_t destSize, const void *data, int type); static void FormatVertColRawColor(char *dest, size_t destSize, const void *data, int type);
void FormatVertColRaw(VertexDecoder *decoder, char *dest, size_t destSize, int row, int col) { void FormatVertColRaw(VertexDecoder *decoder, char *dest, size_t destSize, int row, int col) {
if (!PSP_IsInited()) { if (PSP_GetBootState() != BootState::Complete) {
truncate_cpy(dest, destSize, "Invalid"); truncate_cpy(dest, destSize, "Invalid");
return; return;
} }

View file

@ -53,10 +53,6 @@ static void SetGPU(T *obj) {
#undef new #undef new
#endif #endif
bool GPU_IsReady() {
return gpu != nullptr;
}
bool GPU_IsStarted() { bool GPU_IsStarted() {
if (gpu) if (gpu)
return gpu->IsStarted(); return gpu->IsStarted();

View file

@ -182,7 +182,6 @@ namespace Draw {
} }
bool GPU_Init(GraphicsContext *ctx, Draw::DrawContext *draw); bool GPU_Init(GraphicsContext *ctx, Draw::DrawContext *draw);
bool GPU_IsReady();
bool GPU_IsStarted(); bool GPU_IsStarted();
void GPU_Shutdown(); void GPU_Shutdown();

View file

@ -5,7 +5,7 @@
#include "ppsspp_config.h" #include "ppsspp_config.h"
#include "Common/Log.h" #include "Common/Log.h"
#include "Core/Config.h" #include "Core/Config.h"
#include "DiscordIntegration.h" #include "UI/DiscordIntegration.h"
#include "Common/Data/Text/I18n.h" #include "Common/Data/Text/I18n.h"
#if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(LINUX)) && !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(UWP) #if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(LINUX)) && !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(UWP)

View file

@ -120,7 +120,7 @@ private:
DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {} DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {}
void DisplayLayoutScreen::DrawBackground(UIContext &dc) { void DisplayLayoutScreen::DrawBackground(UIContext &dc) {
if (PSP_IsInited() && !g_Config.bSkipBufferEffects) { if (PSP_GetBootState() == BootState::Complete && !g_Config.bSkipBufferEffects) {
// We normally rely on the PSP screen showing through. // We normally rely on the PSP screen showing through.
} else { } else {
// But if it's not present (we're not in game, or skip buffer effects is used), // But if it's not present (we're not in game, or skip buffer effects is used),

View file

@ -229,7 +229,6 @@ bool EmuScreen::bootAllowStorage(const Path &filename) {
return false; return false;
case PERMISSION_STATUS_DENIED: case PERMISSION_STATUS_DENIED:
stopRender_ = true;
screenManager()->switchScreen(new MainScreen()); screenManager()->switchScreen(new MainScreen());
return false; return false;
@ -251,37 +250,42 @@ void EmuScreen::bootGame(const Path &filename) {
return; return;
} }
if (PSP_IsRebooting())
return;
if (PSP_IsInited()) {
bootPending_ = false;
bootComplete();
return;
}
if (PSP_IsIniting()) {
std::string error_string = "(unknown error)"; std::string error_string = "(unknown error)";
BootState state = PSP_InitUpdate(&error_string);
bootPending_ = !PSP_InitUpdate(&error_string); switch (state) {
case BootState::Booting:
if (!bootPending_) { // Keep trying.
if (!PSP_IsInited()) {
errorMessage_ = error_string;
ERROR_LOG(Log::Boot, "isIniting bootGame error: %s", errorMessage_.c_str());
return; return;
} case BootState::Failed:
bootComplete(); // Failure.
} PSP_CancelBoot();
return; _dbg_assert_(!error_string.empty());
}
g_BackgroundAudio.SetGame(Path()); g_BackgroundAudio.SetGame(Path());
bootPending_ = false;
errorMessage_ = error_string;
ERROR_LOG(Log::Boot, "Boot failed: %s", errorMessage_.c_str());
return;
case BootState::Complete:
// Done booting!
g_BackgroundAudio.SetGame(Path());
bootPending_ = false;
errorMessage_.clear();
bootComplete();
// Reset views in case controls are in a different place.
RecreateViews();
return;
case BootState::Off:
// Gotta start the boot process! Continue below.
break;
}
// Check permission status first, in case we came from a shortcut. // Check permission status first, in case we came from a shortcut.
if (!bootAllowStorage(filename)) if (!bootAllowStorage(filename))
return; return;
// 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.
// TODO: Actually, we read this info again during bootup, so this is not really necessary.
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, filename, GameInfoFlags::PARAM_SFO); std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, filename, GameInfoFlags::PARAM_SFO);
if (!info->Ready(GameInfoFlags::PARAM_SFO)) { if (!info->Ready(GameInfoFlags::PARAM_SFO)) {
return; return;
@ -303,10 +307,6 @@ void EmuScreen::bootGame(const Path &filename) {
SetAssertCancelCallback(&AssertCancelCallback, this); SetAssertCancelCallback(&AssertCancelCallback, this);
if (!info->id.empty()) { if (!info->id.empty()) {
g_Config.loadGameConfig(info->id, info->GetTitle());
// Reset views in case controls are in a different place.
RecreateViews();
g_Discord.SetPresenceGame(info->GetTitle()); g_Discord.SetPresenceGame(info->GetTitle());
} else { } else {
g_Discord.SetPresenceGame(sc->T("Untitled PSP game")); g_Discord.SetPresenceGame(sc->T("Untitled PSP game"));
@ -359,21 +359,7 @@ void EmuScreen::bootGame(const Path &filename) {
if (!PSP_InitStart(coreParam)) { if (!PSP_InitStart(coreParam)) {
bootPending_ = false; bootPending_ = false;
ERROR_LOG(Log::Boot, "InitStart bootGame error: %s", errorMessage_.c_str()); ERROR_LOG(Log::Boot, "InitStart bootGame error: %s", errorMessage_.c_str());
} return;
if (PSP_CoreParameter().compat.flags().RequireBufferedRendering && g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BufferedRenderingRequired", "Warning: This game requires Rendering Mode to be set to Buffered."), 10.0f);
}
if (PSP_CoreParameter().compat.flags().RequireBlockTransfer && g_Config.iSkipGPUReadbackMode != (int)SkipGPUReadbackMode::NO_SKIP && !PSP_CoreParameter().compat.flags().ForceEnableGPUReadback) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BlockTransferRequired", "Warning: This game requires Skip GPU Readbacks be set to No."), 10.0f);
}
if (PSP_CoreParameter().compat.flags().RequireDefaultCPUClock && g_Config.iLockedCPUSpeed != 0) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f);
} }
loadingViewColor_->Divert(0xFFFFFFFF, 0.75f); loadingViewColor_->Divert(0xFFFFFFFF, 0.75f);
@ -386,6 +372,7 @@ void EmuScreen::bootGame(const Path &filename) {
} }
} }
// Only call this on successful boot.
void EmuScreen::bootComplete() { void EmuScreen::bootComplete() {
UpdateUIState(UISTATE_INGAME); UpdateUIState(UISTATE_INGAME);
System_Notify(SystemNotification::BOOT_DONE); System_Notify(SystemNotification::BOOT_DONE);
@ -456,9 +443,8 @@ EmuScreen::~EmuScreen() {
std::string gameID = g_paramSFO.GetValueString("DISC_ID"); std::string gameID = g_paramSFO.GetValueString("DISC_ID");
g_Config.TimeTracker().Stop(gameID); g_Config.TimeTracker().Stop(gameID);
// If we were invalid, it would already be shutdown.
if (!bootPending_) { if (!bootPending_) {
PSP_Shutdown(); PSP_Shutdown(true);
} }
System_PostUIMessage(UIMessage::GAME_SELECTED, ""); System_PostUIMessage(UIMessage::GAME_SELECTED, "");
@ -543,17 +529,15 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) {
screenManager()->push(new GamePauseScreen(gamePath_)); screenManager()->push(new GamePauseScreen(gamePath_));
} else if (message == UIMessage::REQUEST_GAME_STOP) { } else if (message == UIMessage::REQUEST_GAME_STOP) {
// We will push MainScreen in update(). // We will push MainScreen in update().
PSP_Shutdown(); PSP_Shutdown(true);
bootPending_ = false; bootPending_ = false;
stopRender_ = true;
System_Notify(SystemNotification::DISASSEMBLY); System_Notify(SystemNotification::DISASSEMBLY);
} else if (message == UIMessage::REQUEST_GAME_RESET) { } else if (message == UIMessage::REQUEST_GAME_RESET) {
PSP_Shutdown(); PSP_Shutdown(true);
bootPending_ = true; bootPending_ = true;
System_Notify(SystemNotification::DISASSEMBLY); System_Notify(SystemNotification::DISASSEMBLY);
if (!PSP_InitStart(PSP_CoreParameter())) { if (!PSP_InitStart(PSP_CoreParameter())) {
ERROR_LOG(Log::Loader, "Error resetting"); ERROR_LOG(Log::Loader, "Error resetting");
stopRender_ = true;
screenManager()->switchScreen(new MainScreen()); screenManager()->switchScreen(new MainScreen());
return; return;
} }
@ -567,7 +551,7 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) {
if (ext != nullptr && !strcmp(ext, ".ppst")) { if (ext != nullptr && !strcmp(ext, ".ppst")) {
SaveState::Load(Path(value), -1, &AfterStateBoot); SaveState::Load(Path(value), -1, &AfterStateBoot);
} else { } else {
PSP_Shutdown(); PSP_Shutdown(true);
bootPending_ = true; bootPending_ = true;
gamePath_ = Path(value); gamePath_ = Path(value);
// Don't leave it on CORE_POWERDOWN, we'll sometimes aggressively bail. // Don't leave it on CORE_POWERDOWN, we'll sometimes aggressively bail.
@ -1433,14 +1417,11 @@ void EmuScreen::update() {
} }
bool EmuScreen::checkPowerDown() { bool EmuScreen::checkPowerDown() {
if (PSP_IsRebooting()) { // This is for handling things like sceKernelExitGame().
bootPending_ = true; if (coreState == CORE_POWERDOWN && PSP_GetBootState() == BootState::Complete) {
}
if (coreState == CORE_POWERDOWN && !PSP_IsIniting() && !PSP_IsRebooting()) {
bool shutdown = false; bool shutdown = false;
if (PSP_IsInited()) { if (PSP_IsInited()) {
PSP_Shutdown(); PSP_Shutdown(true);
shutdown = true; shutdown = true;
} }
INFO_LOG(Log::System, "SELF-POWERDOWN!"); INFO_LOG(Log::System, "SELF-POWERDOWN!");

View file

@ -101,7 +101,6 @@ private:
Path gamePath_; Path gamePath_;
bool quit_ = false; bool quit_ = false;
bool stopRender_ = false;
std::string errorMessage_; std::string errorMessage_;
// If set, pauses at the end of the frame. // If set, pauses at the end of the frame.

View file

@ -50,7 +50,7 @@ static ImColor scaleColor(ImColor color, float factor) {
} }
bool ImDisasmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) { bool ImDisasmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) {
if (!PSP_IsInited()) if (PSP_GetBootState() != BootState::Complete)
return false; return false;
return GetDisasmAddressText(address, dest, abbreviateLabels, showData, displaySymbols_); return GetDisasmAddressText(address, dest, abbreviateLabels, showData, displaySymbols_);

View file

@ -483,8 +483,7 @@ void GamePauseScreen::CreateViews() {
rightColumnItems->Add(new Spacer(20.0)); rightColumnItems->Add(new Spacer(20.0));
std::string gameId = g_paramSFO.GetDiscID(); if (g_paramSFO.IsValid() && g_Config.hasGameConfig(g_paramSFO.GetDiscID())) {
if (g_Config.hasGameConfig(gameId)) {
rightColumnItems->Add(new Choice(pa->T("Game Settings")))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings); rightColumnItems->Add(new Choice(pa->T("Game Settings")))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings);
rightColumnItems->Add(new Choice(pa->T("Delete Game Config")))->OnClick.Handle(this, &GamePauseScreen::OnDeleteConfig); rightColumnItems->Add(new Choice(pa->T("Delete Game Config")))->OnClick.Handle(this, &GamePauseScreen::OnDeleteConfig);
} else { } else {

View file

@ -120,7 +120,7 @@ bool RunTests() {
INFO_LOG(Log::System, "Preparing to execute '%s'", testName.c_str()); INFO_LOG(Log::System, "Preparing to execute '%s'", testName.c_str());
std::string error_string; std::string error_string;
output.clear(); output.clear();
if (!PSP_Init(coreParam, &error_string)) { if (BootState::Complete != PSP_Init(coreParam, &error_string)) {
ERROR_LOG(Log::System, "Failed to init unittest %s : %s", testsToRun[i], error_string.c_str()); ERROR_LOG(Log::System, "Failed to init unittest %s : %s", testsToRun[i], error_string.c_str());
PSP_CoreParameter().pixelWidth = g_display.pixel_xres; PSP_CoreParameter().pixelWidth = g_display.pixel_xres;
PSP_CoreParameter().pixelHeight = g_display.pixel_yres; PSP_CoreParameter().pixelHeight = g_display.pixel_yres;
@ -177,7 +177,7 @@ bool RunTests() {
break; break;
} }
} }
PSP_Shutdown(); PSP_Shutdown(true);
} }
PSP_CoreParameter().pixelWidth = g_display.pixel_xres; PSP_CoreParameter().pixelWidth = g_display.pixel_xres;
PSP_CoreParameter().pixelHeight = g_display.pixel_yres; PSP_CoreParameter().pixelHeight = g_display.pixel_yres;

View file

@ -199,7 +199,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const
headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError); headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError);
std::string error_string; std::string error_string;
while (!PSP_InitUpdate(&error_string)) while (PSP_InitUpdate(&error_string) == BootState::Booting)
sleep_ms(1, "auto-test"); sleep_ms(1, "auto-test");
if (!PSP_IsInited()) { if (!PSP_IsInited()) {
@ -265,7 +265,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const
draw->EndFrame(); draw->EndFrame();
} }
PSP_Shutdown(); PSP_Shutdown(true);
if (!opt.bench) if (!opt.bench)
headlessHost->FlushDebugOutput(); headlessHost->FlushDebugOutput();