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)
{
Host::ReportErrorAsync(TRANSLATE_STR("QtHost", "Startup Error"), error.GetDescription());
return;
}
if (!Host::GetBoolSettingValue("UI", "StartPaused", false))
{

View File

@ -13,6 +13,8 @@
#include "GS/GSLzma.h"
#include "GS/GSExtra.h"
#include "Host.h"
#include <Alloc.h>
#include <7zCrc.h>
#include <Xz.h>
@ -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<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;
}
@ -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<u32>(packet.id)));
Error::SetStringFmt(error,
TRANSLATE_FS("GSDumpFile", "Unknown packet type {}"), static_cast<u32>(packet.id));
return false;
}
@ -331,7 +334,7 @@ namespace
look_stream.buf = static_cast<Byte*>(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;
}

View File

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

View File

@ -3,6 +3,8 @@
#pragma once
#include "common/Error.h"
#include <string>
#include <vector>
@ -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();

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) {
if (result == VMBootResult::StartupSuccess)
VMManager::SetState(VMState::Running);
else if (switch_to_landing_on_failure)
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));

View File

@ -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(
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));
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 '{}': {}.",
Error::SetStringFmt(error, TRANSLATE_FS("VMManager", "Failed to open CDVD '{}': {}."),
Path::GetFileName(CDVDsys_GetFile(CDVDsys_GetSourceType())),
error.GetDescription()));
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;
}

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
/// 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
{
@ -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);