From 2805bf376a0691145c6212c6ce47e0f6bed9a007 Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Mon, 10 Nov 2025 22:28:34 +0000 Subject: [PATCH] VMManager: Improve error handling for initialization functions --- pcsx2-qt/QtHost.cpp | 5 +- pcsx2/GS/GSLzma.cpp | 29 ++++++------ pcsx2/GSDumpReplayer.cpp | 12 ++--- pcsx2/GSDumpReplayer.h | 4 +- pcsx2/ImGui/FullscreenUI.cpp | 18 ++++++-- pcsx2/VMManager.cpp | 90 ++++++++++++++++++------------------ pcsx2/VMManager.h | 4 +- 7 files changed, 89 insertions(+), 73 deletions(-) diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 770296497c..df88a2ebed 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -221,9 +221,12 @@ void EmuThread::startVM(std::shared_ptr boot_params) }); }; - auto done_callback = [](VMBootResult result) { + auto done_callback = [](VMBootResult result, const Error& error) { if (result != VMBootResult::StartupSuccess) + { + Host::ReportErrorAsync(TRANSLATE_STR("QtHost", "Startup Error"), error.GetDescription()); return; + } if (!Host::GetBoolSettingValue("UI", "StartPaused", false)) { diff --git a/pcsx2/GS/GSLzma.cpp b/pcsx2/GS/GSLzma.cpp index 01ea3f567d..af77fc168f 100644 --- a/pcsx2/GS/GSLzma.cpp +++ b/pcsx2/GS/GSLzma.cpp @@ -13,6 +13,8 @@ #include "GS/GSLzma.h" #include "GS/GSExtra.h" +#include "Host.h" + #include #include <7zCrc.h> #include @@ -73,14 +75,14 @@ bool GSDumpFile::ReadFile(Error* error) u32 ss; if (Read(&m_crc, sizeof(m_crc)) != sizeof(m_crc) || Read(&ss, sizeof(ss)) != sizeof(ss)) { - Error::SetString(error, "Failed to read header"); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read header.")); return false; } m_state_data.resize(ss); if (Read(m_state_data.data(), ss) != ss) { - Error::SetString(error, "Failed to read state data"); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read state data.")); return false; } @@ -90,7 +92,7 @@ bool GSDumpFile::ReadFile(Error* error) GSDumpHeader header; if (m_state_data.size() < sizeof(header)) { - Error::SetString(error, "GSDump header is corrupted."); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "GSDump header is corrupted.")); return false; } @@ -102,7 +104,7 @@ bool GSDumpFile::ReadFile(Error* error) { if (header.serial_offset > ss || (static_cast(header.serial_offset) + header.serial_size) > ss) { - Error::SetString(error, "GSDump header is corrupted."); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "GSDump header is corrupted.")); return false; } @@ -114,7 +116,7 @@ bool GSDumpFile::ReadFile(Error* error) m_state_data.resize(header.state_size); if (Read(m_state_data.data(), header.state_size) != header.state_size) { - Error::SetString(error, "Failed to read real state data"); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read real state data")); return false; } } @@ -122,7 +124,7 @@ bool GSDumpFile::ReadFile(Error* error) m_regs_data.resize(8192); if (Read(m_regs_data.data(), m_regs_data.size()) != m_regs_data.size()) { - Error::SetString(error, "Failed to read regs data"); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read regs data.")); return false; } @@ -139,7 +141,7 @@ bool GSDumpFile::ReadFile(Error* error) { if (!IsEof()) { - Error::SetString(error, "Failed to read packet"); + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read packet.")); return false; } @@ -157,7 +159,7 @@ bool GSDumpFile::ReadFile(Error* error) { \ if (remaining < sizeof(u8)) \ { \ - Error::SetString(error, "Failed to read byte"); \ + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read byte.")); \ return false; \ } \ std::memcpy(dst, data, sizeof(u8)); \ @@ -169,7 +171,7 @@ bool GSDumpFile::ReadFile(Error* error) { \ if (remaining < sizeof(u32)) \ { \ - Error::SetString(error, "Failed to read word"); \ + Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read word.")); \ return false; \ } \ std::memcpy(dst, data, sizeof(u32)); \ @@ -199,7 +201,8 @@ bool GSDumpFile::ReadFile(Error* error) packet.length = 8192; break; default: - Error::SetString(error, fmt::format("Unknown packet type {}", static_cast(packet.id))); + Error::SetStringFmt(error, + TRANSLATE_FS("GSDumpFile", "Unknown packet type {}"), static_cast(packet.id)); return false; } @@ -331,7 +334,7 @@ namespace look_stream.buf = static_cast(ISzAlloc_Alloc(&g_Alloc, kInputBufSize)); if (!look_stream.buf) { - Error::SetString(error, "Failed to allocate lookahead buffer"); + Error::SetString(error, TRANSLATE_STR("GSDumpLzma", "Failed to allocate lookahead buffer.")); return false; } ScopedGuard guard = [&look_stream]() { @@ -351,14 +354,14 @@ namespace SRes res = Xzs_ReadBackward(&xzs, &look_stream.vt, &start_pos, nullptr, &g_Alloc); if (res != SZ_OK) { - Error::SetString(error, fmt::format("Xzs_ReadBackward() failed: {}", res)); + Error::SetStringFmt(error, TRANSLATE_FS("GSDumpLzma", "Xzs_ReadBackward() failed: {}."), res); return false; } const size_t num_blocks = Xzs_GetNumBlocks(&xzs); if (num_blocks == 0) { - Error::SetString(error, "Stream has no blocks."); + Error::SetString(error, TRANSLATE_STR("GSDumpLzma", "Stream has no blocks.")); return false; } diff --git a/pcsx2/GSDumpReplayer.cpp b/pcsx2/GSDumpReplayer.cpp index 7b253c544c..dd4fd4f9b0 100644 --- a/pcsx2/GSDumpReplayer.cpp +++ b/pcsx2/GSDumpReplayer.cpp @@ -85,17 +85,17 @@ int GSDumpReplayer::GetLoopCount() return s_dump_loop_count; } -bool GSDumpReplayer::Initialize(const char* filename) +bool GSDumpReplayer::Initialize(const char* filename, Error* error) { Common::Timer timer; Console.WriteLn("(GSDumpReplayer) Reading file '%s'...", filename); - Error error; - s_dump_file = GSDumpFile::OpenGSDump(filename, &error); - if (!s_dump_file || !s_dump_file->ReadFile(&error)) + Error dump_error; + s_dump_file = GSDumpFile::OpenGSDump(filename, &dump_error); + if (!s_dump_file || !s_dump_file->ReadFile(&dump_error)) { - Host::ReportErrorAsync("GSDumpReplayer", fmt::format("Failed to open or read '{}': {}", - Path::GetFileName(filename), error.GetDescription())); + Error::SetStringFmt(error, TRANSLATE_FS("GSDumpReplayer", "Failed to open or read '{}': {}"), + Path::GetFileName(filename), dump_error.GetDescription()); s_dump_file.reset(); return false; } diff --git a/pcsx2/GSDumpReplayer.h b/pcsx2/GSDumpReplayer.h index 5fab8d4b4a..e436461a15 100644 --- a/pcsx2/GSDumpReplayer.h +++ b/pcsx2/GSDumpReplayer.h @@ -3,6 +3,8 @@ #pragma once +#include "common/Error.h" + #include #include @@ -16,7 +18,7 @@ namespace GSDumpReplayer bool IsRunner(); void SetIsDumpRunner(bool is_runner); - bool Initialize(const char* filename); + bool Initialize(const char* filename, Error* error = nullptr); bool ChangeDump(const char* filename); void Shutdown(); diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index ed76d1e9f5..23d2b35b8e 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -1461,11 +1461,19 @@ void FullscreenUI::DoVMInitialize(const VMBootParameters& boot_params, bool swit }); }; - auto done_callback = [switch_to_landing_on_failure](VMBootResult result) { - if (result == VMBootResult::StartupSuccess) - VMManager::SetState(VMState::Running); - else if (switch_to_landing_on_failure) - MTGS::RunOnGSThread(SwitchToLanding); + auto done_callback = [switch_to_landing_on_failure](VMBootResult result, const Error& error) { + if (result != VMBootResult::StartupSuccess) + { + ImGuiFullscreen::OpenInfoMessageDialog( + FSUI_ICONSTR(ICON_FA_TRIANGLE_EXCLAMATION, "Startup Error"), error.GetDescription()); + + if (switch_to_landing_on_failure) + MTGS::RunOnGSThread(SwitchToLanding); + + return; + } + + VMManager::SetState(VMState::Running); }; VMManager::InitializeAsync(boot_params, std::move(hardcore_disable_callback), std::move(done_callback)); diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index a9e7d42aac..7f6ec2cf9b 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -102,7 +102,7 @@ namespace VMManager static void LogUnsafeSettingsToConsole(const std::string& messages); static void WarnAboutUnsafeSettings(); - static bool AutoDetectSource(const std::string& filename); + static bool AutoDetectSource(const std::string& filename, Error* error = nullptr); static void UpdateDiscDetails(bool booting); static void ClearDiscDetails(); static void HandleELFChange(bool verbose_patches_if_changed); @@ -1197,20 +1197,20 @@ bool VMManager::HasBootedELF() return s_current_crc != 0 && s_elf_executed; } -bool VMManager::AutoDetectSource(const std::string& filename) +bool VMManager::AutoDetectSource(const std::string& filename, Error* error) { if (!filename.empty()) { if (!FileSystem::FileExists(filename.c_str())) { - Host::ReportErrorAsync("Error", fmt::format("Requested filename '{}' does not exist.", filename)); + Error::SetStringFmt(error, TRANSLATE_FS("VMManager", "Requested filename '{}' does not exist."), filename); return false; } if (IsGSDumpFileName(filename)) { CDVDsys_ChangeSource(CDVD_SourceType::NoDisc); - return GSDumpReplayer::Initialize(filename.c_str()); + return GSDumpReplayer::Initialize(filename.c_str(), error); } else if (IsElfFileName(filename)) { @@ -1272,7 +1272,8 @@ void VMManager::InitializeAsync( VMBootHardcoreDisableCallback hardcore_disable_callback, VMBootDoneCallback done_callback) { - VMBootResult result = VMManager::Initialize(boot_params); + Error error; + VMBootResult result = VMManager::Initialize(boot_params, &error); if (result == VMBootResult::PromptDisableHardcoreMode) { @@ -1286,21 +1287,24 @@ void VMManager::InitializeAsync( [boot_params, done_callback = std::move(done_callback)]() { VMBootParameters new_boot_params = std::move(boot_params); new_boot_params.disable_achievements_hardcore_mode = true; - done_callback(VMManager::Initialize(new_boot_params)); + + Error error; + VMBootResult result = VMManager::Initialize(new_boot_params, &error); + done_callback(result, error); }); return; } - done_callback(result); + done_callback(result, error); } -VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) +VMBootResult VMManager::Initialize(const VMBootParameters& boot_params, Error* error) { const Common::Timer init_timer; if (s_state.load(std::memory_order_acquire) != VMState::Shutdown) { - Host::ReportErrorAsync("Error", "The virtual machine is already running."); + Error::SetString(error, TRANSLATE_STR("VMManager", "The virtual machine is already running.")); return VMBootResult::StartupFailure; } @@ -1344,24 +1348,22 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) { if (boot_params.filename.empty()) { - Host::ReportErrorAsync("Error", "Cannot load an indexed save state without a boot filename."); + Error::SetString(error, + TRANSLATE_STR("VMManager", "Cannot load an indexed save state without a boot filename.")); return VMBootResult::StartupFailure; } state_to_load = GetSaveStateFileName(boot_params.filename.c_str(), boot_params.state_index.value()); if (state_to_load.empty()) { - Host::ReportErrorAsync("Error", "Could not resolve path indexed save state load."); + Error::SetString(error, + TRANSLATE_STR("VMManager", "Could not resolve path for indexed save state load.")); return VMBootResult::StartupFailure; } } - Error cdvd_lock_error; - if (!cdvdLock(&cdvd_lock_error)) - { - Host::ReportErrorAsync("Startup Error", cdvd_lock_error.GetDescription()); + if (!cdvdLock(error)) return VMBootResult::StartupFailure; - } ScopedGuard unlock_cdvd = &cdvdUnlock; @@ -1371,8 +1373,8 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) if (boot_params.source_type.value() == CDVD_SourceType::Iso && !FileSystem::FileExists(boot_params.filename.c_str())) { - Host::ReportErrorAsync( - "Error", fmt::format("Requested filename '{}' does not exist.", boot_params.filename)); + Error::SetStringFmt(error, + TRANSLATE_FS("VMManager", "Requested filename '{}' does not exist."), boot_params.filename); return VMBootResult::StartupFailure; } @@ -1383,7 +1385,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) else { // Automatic type detection of boot parameter based on filename. - if (!AutoDetectSource(boot_params.filename)) + if (!AutoDetectSource(boot_params.filename, error)) return VMBootResult::StartupFailure; } @@ -1395,15 +1397,14 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Loading BIOS..."); if (!LoadBIOS()) { - Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Error – No BIOS Present"), - fmt::format( - TRANSLATE_FS("VMManager", - "PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n" - "For legal reasons, you will need to obtain this BIOS from a PlayStation 2 unit which you own.\n\n" - "For step-by-step help with this process, please consult the setup guide at {}.\n\n" - "PCSX2 will be able to run once you've placed your BIOS image inside the folder named \"bios\" within the data directory " - "(Tools Menu -> Open Data Directory)."), - PCSX2_DOCUMENTATION_BIOS_URL_SHORTENED)); + Error::SetStringFmt(error, + TRANSLATE_FS("VMManager", + "PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n" + "For legal reasons, you will need to obtain this BIOS from a PlayStation 2 unit which you own.\n\n" + "For step-by-step help with this process, please consult the setup guide at {}.\n\n" + "PCSX2 will be able to run once you've placed your BIOS image inside the folder named \"bios\" within the data directory " + "(Tools Menu -> Open Data Directory)."), + PCSX2_DOCUMENTATION_BIOS_URL_SHORTENED); return VMBootResult::StartupFailure; } @@ -1411,13 +1412,13 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) cdvdLoadNVRAM(); } - Error error; + Error cdvd_error; Console.WriteLn("Opening CDVD..."); - if (!DoCDVDopen(&error)) + if (!DoCDVDopen(&cdvd_error)) { - Host::ReportErrorAsync("Startup Error", fmt::format("Failed to open CDVD '{}': {}.", - Path::GetFileName(CDVDsys_GetFile(CDVDsys_GetSourceType())), - error.GetDescription())); + Error::SetStringFmt(error, TRANSLATE_FS("VMManager", "Failed to open CDVD '{}': {}."), + Path::GetFileName(CDVDsys_GetFile(CDVDsys_GetSourceType())), + cdvd_error.GetDescription()); return VMBootResult::StartupFailure; } ScopedGuard close_cdvd(&DoCDVDclose); @@ -1438,7 +1439,8 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) { if (!FileSystem::FileExists(s_elf_override.c_str())) { - Host::ReportErrorAsync("Error", fmt::format("Requested boot ELF '{}' does not exist.", s_elf_override)); + Error::SetStringFmt(error, + TRANSLATE_FS("VMManager", "Requested boot ELF '{}' does not exist."), s_elf_override); return VMBootResult::StartupFailure; } @@ -1484,7 +1486,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) if (!s_gs_open_on_initialize && !MTGS::WaitForOpen()) { // we assume GS is going to report its own error - Console.WriteLn("Failed to open GS."); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize GS.")); return VMBootResult::StartupFailure; } @@ -1496,7 +1498,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Opening SPU2..."); if (!SPU2::Open()) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize SPU2."); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize SPU2.")); return VMBootResult::StartupFailure; } ScopedGuard close_spu2(&SPU2::Close); @@ -1505,7 +1507,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Initializing Pad..."); if (!Pad::Initialize()) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD"); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize PAD.")); return VMBootResult::StartupFailure; } ScopedGuard close_pad = &Pad::Shutdown; @@ -1513,7 +1515,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Initializing SIO2..."); if (!g_Sio2.Initialize()) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize SIO2"); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize SIO2.")); return VMBootResult::StartupFailure; } ScopedGuard close_sio2 = []() { @@ -1523,7 +1525,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Initializing SIO0..."); if (!g_Sio0.Initialize()) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize SIO0"); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize SIO0.")); return VMBootResult::StartupFailure; } ScopedGuard close_sio0 = []() { @@ -1533,7 +1535,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Opening DEV9..."); if (DEV9init() != 0 || DEV9open() != 0) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize DEV9."); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize DEV9.")); return VMBootResult::StartupFailure; } ScopedGuard close_dev9 = []() { @@ -1544,7 +1546,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Opening USB..."); if (!USBopen()) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize USB."); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize USB.")); return VMBootResult::StartupFailure; } ScopedGuard close_usb = []() { USBclose(); }; @@ -1552,7 +1554,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) Console.WriteLn("Opening FW..."); if (FWopen() != 0) { - Host::ReportErrorAsync("Startup Error", "Failed to initialize FW."); + Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize FW.")); return VMBootResult::StartupFailure; } ScopedGuard close_fw = []() { FWclose(); }; @@ -1588,10 +1590,8 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params) // do we want to load state? if (!GSDumpReplayer::IsReplayingDump() && !state_to_load.empty()) { - Error state_error; - if (!DoLoadState(state_to_load.c_str(), &state_error)) + if (!DoLoadState(state_to_load.c_str(), error)) { - Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Failed to load save state."), state_error.GetDescription()); Shutdown(false); return VMBootResult::StartupFailure; } diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index 957e2b9654..27e87bdc83 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -61,7 +61,7 @@ using VMBootHardcoreDisableCallback = std::function; +using VMBootDoneCallback = std::function; namespace VMManager { @@ -116,7 +116,7 @@ namespace VMManager VMBootDoneCallback done_callback); /// Initializes all system components. Will not attempt to restart itself. - VMBootResult Initialize(const VMBootParameters& boot_params); + VMBootResult Initialize(const VMBootParameters& boot_params, Error* error = nullptr); /// Destroys all system components. void Shutdown(bool save_resume_state);