VMManager: Improve error handling for initialization functions

This commit is contained in:
chaoticgd 2025-11-10 22:28:34 +00:00 committed by Ty
parent eaa834d238
commit 2805bf376a
7 changed files with 89 additions and 73 deletions

View File

@ -221,9 +221,12 @@ void EmuThread::startVM(std::shared_ptr<VMBootParameters> boot_params)
}); });
}; };
auto done_callback = [](VMBootResult result) { auto done_callback = [](VMBootResult result, const Error& error) {
if (result != VMBootResult::StartupSuccess) if (result != VMBootResult::StartupSuccess)
{
Host::ReportErrorAsync(TRANSLATE_STR("QtHost", "Startup Error"), error.GetDescription());
return; return;
}
if (!Host::GetBoolSettingValue("UI", "StartPaused", false)) if (!Host::GetBoolSettingValue("UI", "StartPaused", false))
{ {

View File

@ -13,6 +13,8 @@
#include "GS/GSLzma.h" #include "GS/GSLzma.h"
#include "GS/GSExtra.h" #include "GS/GSExtra.h"
#include "Host.h"
#include <Alloc.h> #include <Alloc.h>
#include <7zCrc.h> #include <7zCrc.h>
#include <Xz.h> #include <Xz.h>
@ -73,14 +75,14 @@ bool GSDumpFile::ReadFile(Error* error)
u32 ss; u32 ss;
if (Read(&m_crc, sizeof(m_crc)) != sizeof(m_crc) || Read(&ss, sizeof(ss)) != sizeof(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; return false;
} }
m_state_data.resize(ss); m_state_data.resize(ss);
if (Read(m_state_data.data(), ss) != 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; return false;
} }
@ -90,7 +92,7 @@ bool GSDumpFile::ReadFile(Error* error)
GSDumpHeader header; GSDumpHeader header;
if (m_state_data.size() < sizeof(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; return false;
} }
@ -102,7 +104,7 @@ bool GSDumpFile::ReadFile(Error* error)
{ {
if (header.serial_offset > ss || (static_cast<u64>(header.serial_offset) + header.serial_size) > ss) if (header.serial_offset > ss || (static_cast<u64>(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; return false;
} }
@ -114,7 +116,7 @@ bool GSDumpFile::ReadFile(Error* error)
m_state_data.resize(header.state_size); m_state_data.resize(header.state_size);
if (Read(m_state_data.data(), header.state_size) != 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; return false;
} }
} }
@ -122,7 +124,7 @@ bool GSDumpFile::ReadFile(Error* error)
m_regs_data.resize(8192); m_regs_data.resize(8192);
if (Read(m_regs_data.data(), m_regs_data.size()) != m_regs_data.size()) 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; return false;
} }
@ -139,7 +141,7 @@ bool GSDumpFile::ReadFile(Error* error)
{ {
if (!IsEof()) if (!IsEof())
{ {
Error::SetString(error, "Failed to read packet"); Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read packet."));
return false; return false;
} }
@ -157,7 +159,7 @@ bool GSDumpFile::ReadFile(Error* error)
{ \ { \
if (remaining < sizeof(u8)) \ if (remaining < sizeof(u8)) \
{ \ { \
Error::SetString(error, "Failed to read byte"); \ Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read byte.")); \
return false; \ return false; \
} \ } \
std::memcpy(dst, data, sizeof(u8)); \ std::memcpy(dst, data, sizeof(u8)); \
@ -169,7 +171,7 @@ bool GSDumpFile::ReadFile(Error* error)
{ \ { \
if (remaining < sizeof(u32)) \ if (remaining < sizeof(u32)) \
{ \ { \
Error::SetString(error, "Failed to read word"); \ Error::SetString(error, TRANSLATE_STR("GSDumpFile", "Failed to read word.")); \
return false; \ return false; \
} \ } \
std::memcpy(dst, data, sizeof(u32)); \ std::memcpy(dst, data, sizeof(u32)); \
@ -199,7 +201,8 @@ bool GSDumpFile::ReadFile(Error* error)
packet.length = 8192; packet.length = 8192;
break; break;
default: default:
Error::SetString(error, fmt::format("Unknown packet type {}", static_cast<u32>(packet.id))); Error::SetStringFmt(error,
TRANSLATE_FS("GSDumpFile", "Unknown packet type {}"), static_cast<u32>(packet.id));
return false; return false;
} }
@ -331,7 +334,7 @@ namespace
look_stream.buf = static_cast<Byte*>(ISzAlloc_Alloc(&g_Alloc, kInputBufSize)); look_stream.buf = static_cast<Byte*>(ISzAlloc_Alloc(&g_Alloc, kInputBufSize));
if (!look_stream.buf) 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; return false;
} }
ScopedGuard guard = [&look_stream]() { ScopedGuard guard = [&look_stream]() {
@ -351,14 +354,14 @@ namespace
SRes res = Xzs_ReadBackward(&xzs, &look_stream.vt, &start_pos, nullptr, &g_Alloc); SRes res = Xzs_ReadBackward(&xzs, &look_stream.vt, &start_pos, nullptr, &g_Alloc);
if (res != SZ_OK) 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; return false;
} }
const size_t num_blocks = Xzs_GetNumBlocks(&xzs); const size_t num_blocks = Xzs_GetNumBlocks(&xzs);
if (num_blocks == 0) if (num_blocks == 0)
{ {
Error::SetString(error, "Stream has no blocks."); Error::SetString(error, TRANSLATE_STR("GSDumpLzma", "Stream has no blocks."));
return false; return false;
} }

View File

@ -85,17 +85,17 @@ int GSDumpReplayer::GetLoopCount()
return s_dump_loop_count; return s_dump_loop_count;
} }
bool GSDumpReplayer::Initialize(const char* filename) bool GSDumpReplayer::Initialize(const char* filename, Error* error)
{ {
Common::Timer timer; Common::Timer timer;
Console.WriteLn("(GSDumpReplayer) Reading file '%s'...", filename); Console.WriteLn("(GSDumpReplayer) Reading file '%s'...", filename);
Error error; Error dump_error;
s_dump_file = GSDumpFile::OpenGSDump(filename, &error); s_dump_file = GSDumpFile::OpenGSDump(filename, &dump_error);
if (!s_dump_file || !s_dump_file->ReadFile(&error)) if (!s_dump_file || !s_dump_file->ReadFile(&dump_error))
{ {
Host::ReportErrorAsync("GSDumpReplayer", fmt::format("Failed to open or read '{}': {}", Error::SetStringFmt(error, TRANSLATE_FS("GSDumpReplayer", "Failed to open or read '{}': {}"),
Path::GetFileName(filename), error.GetDescription())); Path::GetFileName(filename), dump_error.GetDescription());
s_dump_file.reset(); s_dump_file.reset();
return false; return false;
} }

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include "common/Error.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -16,7 +18,7 @@ namespace GSDumpReplayer
bool IsRunner(); bool IsRunner();
void SetIsDumpRunner(bool is_runner); void SetIsDumpRunner(bool is_runner);
bool Initialize(const char* filename); bool Initialize(const char* filename, Error* error = nullptr);
bool ChangeDump(const char* filename); bool ChangeDump(const char* filename);
void Shutdown(); void Shutdown();

View File

@ -1461,11 +1461,19 @@ void FullscreenUI::DoVMInitialize(const VMBootParameters& boot_params, bool swit
}); });
}; };
auto done_callback = [switch_to_landing_on_failure](VMBootResult result) { auto done_callback = [switch_to_landing_on_failure](VMBootResult result, const Error& error) {
if (result == VMBootResult::StartupSuccess) if (result != VMBootResult::StartupSuccess)
VMManager::SetState(VMState::Running); {
else if (switch_to_landing_on_failure) ImGuiFullscreen::OpenInfoMessageDialog(
MTGS::RunOnGSThread(SwitchToLanding); 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)); VMManager::InitializeAsync(boot_params, std::move(hardcore_disable_callback), std::move(done_callback));

View File

@ -102,7 +102,7 @@ namespace VMManager
static void LogUnsafeSettingsToConsole(const std::string& messages); static void LogUnsafeSettingsToConsole(const std::string& messages);
static void WarnAboutUnsafeSettings(); 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 UpdateDiscDetails(bool booting);
static void ClearDiscDetails(); static void ClearDiscDetails();
static void HandleELFChange(bool verbose_patches_if_changed); static void HandleELFChange(bool verbose_patches_if_changed);
@ -1197,20 +1197,20 @@ bool VMManager::HasBootedELF()
return s_current_crc != 0 && s_elf_executed; 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 (!filename.empty())
{ {
if (!FileSystem::FileExists(filename.c_str())) 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; return false;
} }
if (IsGSDumpFileName(filename)) if (IsGSDumpFileName(filename))
{ {
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc); CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
return GSDumpReplayer::Initialize(filename.c_str()); return GSDumpReplayer::Initialize(filename.c_str(), error);
} }
else if (IsElfFileName(filename)) else if (IsElfFileName(filename))
{ {
@ -1272,7 +1272,8 @@ void VMManager::InitializeAsync(
VMBootHardcoreDisableCallback hardcore_disable_callback, VMBootHardcoreDisableCallback hardcore_disable_callback,
VMBootDoneCallback done_callback) VMBootDoneCallback done_callback)
{ {
VMBootResult result = VMManager::Initialize(boot_params); Error error;
VMBootResult result = VMManager::Initialize(boot_params, &error);
if (result == VMBootResult::PromptDisableHardcoreMode) if (result == VMBootResult::PromptDisableHardcoreMode)
{ {
@ -1286,21 +1287,24 @@ void VMManager::InitializeAsync(
[boot_params, done_callback = std::move(done_callback)]() { [boot_params, done_callback = std::move(done_callback)]() {
VMBootParameters new_boot_params = std::move(boot_params); VMBootParameters new_boot_params = std::move(boot_params);
new_boot_params.disable_achievements_hardcore_mode = true; 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; 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; const Common::Timer init_timer;
if (s_state.load(std::memory_order_acquire) != VMState::Shutdown) 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; return VMBootResult::StartupFailure;
} }
@ -1344,24 +1348,22 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
{ {
if (boot_params.filename.empty()) 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; return VMBootResult::StartupFailure;
} }
state_to_load = GetSaveStateFileName(boot_params.filename.c_str(), boot_params.state_index.value()); state_to_load = GetSaveStateFileName(boot_params.filename.c_str(), boot_params.state_index.value());
if (state_to_load.empty()) 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; return VMBootResult::StartupFailure;
} }
} }
Error cdvd_lock_error; if (!cdvdLock(error))
if (!cdvdLock(&cdvd_lock_error))
{
Host::ReportErrorAsync("Startup Error", cdvd_lock_error.GetDescription());
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
}
ScopedGuard unlock_cdvd = &cdvdUnlock; ScopedGuard unlock_cdvd = &cdvdUnlock;
@ -1371,8 +1373,8 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
if (boot_params.source_type.value() == CDVD_SourceType::Iso && if (boot_params.source_type.value() == CDVD_SourceType::Iso &&
!FileSystem::FileExists(boot_params.filename.c_str())) !FileSystem::FileExists(boot_params.filename.c_str()))
{ {
Host::ReportErrorAsync( Error::SetStringFmt(error,
"Error", fmt::format("Requested filename '{}' does not exist.", boot_params.filename)); TRANSLATE_FS("VMManager", "Requested filename '{}' does not exist."), boot_params.filename);
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
@ -1383,7 +1385,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
else else
{ {
// Automatic type detection of boot parameter based on filename. // Automatic type detection of boot parameter based on filename.
if (!AutoDetectSource(boot_params.filename)) if (!AutoDetectSource(boot_params.filename, error))
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
@ -1395,15 +1397,14 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Loading BIOS..."); Console.WriteLn("Loading BIOS...");
if (!LoadBIOS()) if (!LoadBIOS())
{ {
Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Error No BIOS Present"), Error::SetStringFmt(error,
fmt::format( TRANSLATE_FS("VMManager",
TRANSLATE_FS("VMManager", "PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n"
"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 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"
"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 "
"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)."),
"(Tools Menu -> Open Data Directory)."), PCSX2_DOCUMENTATION_BIOS_URL_SHORTENED);
PCSX2_DOCUMENTATION_BIOS_URL_SHORTENED));
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
@ -1411,13 +1412,13 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
cdvdLoadNVRAM(); cdvdLoadNVRAM();
} }
Error error; Error cdvd_error;
Console.WriteLn("Opening CDVD..."); Console.WriteLn("Opening CDVD...");
if (!DoCDVDopen(&error)) if (!DoCDVDopen(&cdvd_error))
{ {
Host::ReportErrorAsync("Startup Error", fmt::format("Failed to open CDVD '{}': {}.", Error::SetStringFmt(error, TRANSLATE_FS("VMManager", "Failed to open CDVD '{}': {}."),
Path::GetFileName(CDVDsys_GetFile(CDVDsys_GetSourceType())), Path::GetFileName(CDVDsys_GetFile(CDVDsys_GetSourceType())),
error.GetDescription())); cdvd_error.GetDescription());
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
ScopedGuard close_cdvd(&DoCDVDclose); ScopedGuard close_cdvd(&DoCDVDclose);
@ -1438,7 +1439,8 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
{ {
if (!FileSystem::FileExists(s_elf_override.c_str())) 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; return VMBootResult::StartupFailure;
} }
@ -1484,7 +1486,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
if (!s_gs_open_on_initialize && !MTGS::WaitForOpen()) if (!s_gs_open_on_initialize && !MTGS::WaitForOpen())
{ {
// we assume GS is going to report its own error // 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; return VMBootResult::StartupFailure;
} }
@ -1496,7 +1498,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Opening SPU2..."); Console.WriteLn("Opening SPU2...");
if (!SPU2::Open()) if (!SPU2::Open())
{ {
Host::ReportErrorAsync("Startup Error", "Failed to initialize SPU2."); Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize SPU2."));
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
ScopedGuard close_spu2(&SPU2::Close); ScopedGuard close_spu2(&SPU2::Close);
@ -1505,7 +1507,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Initializing Pad..."); Console.WriteLn("Initializing Pad...");
if (!Pad::Initialize()) if (!Pad::Initialize())
{ {
Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD"); Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize PAD."));
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
ScopedGuard close_pad = &Pad::Shutdown; ScopedGuard close_pad = &Pad::Shutdown;
@ -1513,7 +1515,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Initializing SIO2..."); Console.WriteLn("Initializing SIO2...");
if (!g_Sio2.Initialize()) 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; return VMBootResult::StartupFailure;
} }
ScopedGuard close_sio2 = []() { ScopedGuard close_sio2 = []() {
@ -1523,7 +1525,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Initializing SIO0..."); Console.WriteLn("Initializing SIO0...");
if (!g_Sio0.Initialize()) 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; return VMBootResult::StartupFailure;
} }
ScopedGuard close_sio0 = []() { ScopedGuard close_sio0 = []() {
@ -1533,7 +1535,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Opening DEV9..."); Console.WriteLn("Opening DEV9...");
if (DEV9init() != 0 || DEV9open() != 0) 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; return VMBootResult::StartupFailure;
} }
ScopedGuard close_dev9 = []() { ScopedGuard close_dev9 = []() {
@ -1544,7 +1546,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Opening USB..."); Console.WriteLn("Opening USB...");
if (!USBopen()) if (!USBopen())
{ {
Host::ReportErrorAsync("Startup Error", "Failed to initialize USB."); Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize USB."));
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
ScopedGuard close_usb = []() { USBclose(); }; ScopedGuard close_usb = []() { USBclose(); };
@ -1552,7 +1554,7 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
Console.WriteLn("Opening FW..."); Console.WriteLn("Opening FW...");
if (FWopen() != 0) if (FWopen() != 0)
{ {
Host::ReportErrorAsync("Startup Error", "Failed to initialize FW."); Error::SetString(error, TRANSLATE_STR("VMManager", "Failed to initialize FW."));
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }
ScopedGuard close_fw = []() { FWclose(); }; ScopedGuard close_fw = []() { FWclose(); };
@ -1588,10 +1590,8 @@ VMBootResult VMManager::Initialize(const VMBootParameters& boot_params)
// do we want to load state? // do we want to load state?
if (!GSDumpReplayer::IsReplayingDump() && !state_to_load.empty()) if (!GSDumpReplayer::IsReplayingDump() && !state_to_load.empty())
{ {
Error state_error; if (!DoLoadState(state_to_load.c_str(), error))
if (!DoLoadState(state_to_load.c_str(), &state_error))
{ {
Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Failed to load save state."), state_error.GetDescription());
Shutdown(false); Shutdown(false);
return VMBootResult::StartupFailure; return VMBootResult::StartupFailure;
} }

View File

@ -61,7 +61,7 @@ using VMBootHardcoreDisableCallback = std::function<void(std::string reason, VMB
/// Callback used when the VM boot process has finished. This may be called /// Callback used when the VM boot process has finished. This may be called
/// asynchronously after the user has been prompted to disable hardcore mode. /// asynchronously after the user has been prompted to disable hardcore mode.
using VMBootDoneCallback = std::function<void(VMBootResult result)>; using VMBootDoneCallback = std::function<void(VMBootResult result, const Error& error)>;
namespace VMManager namespace VMManager
{ {
@ -116,7 +116,7 @@ namespace VMManager
VMBootDoneCallback done_callback); VMBootDoneCallback done_callback);
/// Initializes all system components. Will not attempt to restart itself. /// 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. /// Destroys all system components.
void Shutdown(bool save_resume_state); void Shutdown(bool save_resume_state);