mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
System: Add Error to boot/load/save state
This commit is contained in:
parent
f75a5605eb
commit
1b1e42d003
|
@ -923,7 +923,12 @@ void FullscreenUI::DoStartPath(std::string path, std::string state, std::optiona
|
|||
if (System::IsValid())
|
||||
return;
|
||||
|
||||
System::BootSystem(std::move(params));
|
||||
Error error;
|
||||
if (!System::BootSystem(std::move(params), &error))
|
||||
{
|
||||
Host::ReportErrorAsync(TRANSLATE_SV("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to boot system: {}"), error.GetDescription()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -964,13 +969,7 @@ void FullscreenUI::DoStartFile()
|
|||
|
||||
void FullscreenUI::DoStartBIOS()
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
if (System::IsValid())
|
||||
return;
|
||||
|
||||
SystemBootParameters params;
|
||||
System::BootSystem(std::move(params));
|
||||
});
|
||||
DoStartDisc(std::string());
|
||||
}
|
||||
|
||||
void FullscreenUI::DoStartDisc(std::string path)
|
||||
|
@ -979,9 +978,14 @@ void FullscreenUI::DoStartDisc(std::string path)
|
|||
if (System::IsValid())
|
||||
return;
|
||||
|
||||
Error error;
|
||||
SystemBootParameters params;
|
||||
params.filename = std::move(path);
|
||||
System::BootSystem(std::move(params));
|
||||
if (!System::BootSystem(std::move(params), &error))
|
||||
{
|
||||
Host::ReportErrorAsync(TRANSLATE_SV("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to boot system: {}"), error.GetDescription()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5923,14 +5927,16 @@ void FullscreenUI::DoLoadState(std::string path)
|
|||
|
||||
if (System::IsValid())
|
||||
{
|
||||
System::LoadState(path.c_str());
|
||||
Error error;
|
||||
if (!System::LoadState(path.c_str(), &error))
|
||||
{
|
||||
Host::ReportErrorAsync(TRANSLATE_SV("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to load state: {}"), error.GetDescription()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemBootParameters params;
|
||||
params.filename = std::move(boot_path);
|
||||
params.save_state = std::move(path);
|
||||
System::BootSystem(std::move(params));
|
||||
DoStartPath(std::move(boot_path), std::move(path));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -5944,7 +5950,12 @@ void FullscreenUI::DoSaveState(s32 slot, bool global)
|
|||
|
||||
std::string filename(global ? System::GetGlobalSaveStateFileName(slot) :
|
||||
System::GetGameSaveStateFileName(System::GetGameSerial(), slot));
|
||||
System::SaveState(filename.c_str(), g_settings.create_save_state_backups);
|
||||
Error error;
|
||||
if (!System::SaveState(filename.c_str(), &error, g_settings.create_save_state_backups))
|
||||
{
|
||||
Host::ReportErrorAsync(TRANSLATE_SV("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to save state: {}"), error.GetDescription()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "achievements.h"
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "util/input_manager.h"
|
||||
#include "util/postprocessing.h"
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
@ -67,7 +68,7 @@ static void HotkeyLoadStateSlot(bool global, s32 slot)
|
|||
|
||||
if (!global && System::GetGameSerial().empty())
|
||||
{
|
||||
Host::AddKeyedOSDMessage("LoadState", TRANSLATE_NOOP("OSDMessage", "Cannot load state for game without serial."),
|
||||
Host::AddKeyedOSDMessage("LoadState", TRANSLATE_STR("OSDMessage", "Cannot load state for game without serial."),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
return;
|
||||
}
|
||||
|
@ -77,12 +78,19 @@ static void HotkeyLoadStateSlot(bool global, s32 slot)
|
|||
if (!FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
Host::AddKeyedOSDMessage("LoadState",
|
||||
fmt::format(TRANSLATE_NOOP("OSDMessage", "No save state found in slot {}."), slot),
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "No save state found in slot {}."), slot),
|
||||
Host::OSD_INFO_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
System::LoadState(path.c_str());
|
||||
Error error;
|
||||
if (!System::LoadState(path.c_str(), &error))
|
||||
{
|
||||
Host::AddKeyedOSDMessage(
|
||||
"LoadState",
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to load state from slot {0}:\n{1}"), slot, error.GetDescription()),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
static void HotkeySaveStateSlot(bool global, s32 slot)
|
||||
|
@ -92,14 +100,21 @@ static void HotkeySaveStateSlot(bool global, s32 slot)
|
|||
|
||||
if (!global && System::GetGameSerial().empty())
|
||||
{
|
||||
Host::AddKeyedOSDMessage("LoadState", TRANSLATE_NOOP("OSDMessage", "Cannot save state for game without serial."),
|
||||
Host::AddKeyedOSDMessage("SaveState", TRANSLATE_STR("OSDMessage", "Cannot save state for game without serial."),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string path(global ? System::GetGlobalSaveStateFileName(slot) :
|
||||
System::GetGameSaveStateFileName(System::GetGameSerial(), slot));
|
||||
System::SaveState(path.c_str(), g_settings.create_save_state_backups);
|
||||
Error error;
|
||||
if (!System::SaveState(path.c_str(), &error, g_settings.create_save_state_backups))
|
||||
{
|
||||
Host::AddKeyedOSDMessage(
|
||||
"SaveState",
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save state to slot {0}:\n{1}"), slot, error.GetDescription()),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
@ -26,6 +26,7 @@
|
|||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/easing.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/intrin.h"
|
||||
#include "common/log.h"
|
||||
|
@ -1114,7 +1115,14 @@ void SaveStateSelectorUI::LoadCurrentSlot()
|
|||
{
|
||||
if (FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
System::LoadState(path.c_str());
|
||||
Error error;
|
||||
if (!System::LoadState(path.c_str(), &error))
|
||||
{
|
||||
Host::AddKeyedOSDMessage("LoadState",
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to load state from slot {0}:\n{1}"),
|
||||
GetCurrentSlot(), error.GetDescription()),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1133,7 +1141,16 @@ void SaveStateSelectorUI::LoadCurrentSlot()
|
|||
void SaveStateSelectorUI::SaveCurrentSlot()
|
||||
{
|
||||
if (std::string path = GetCurrentSlotPath(); !path.empty())
|
||||
System::SaveState(path.c_str(), g_settings.create_save_state_backups);
|
||||
{
|
||||
Error error;
|
||||
if (!System::SaveState(path.c_str(), &error, g_settings.create_save_state_backups))
|
||||
{
|
||||
Host::AddKeyedOSDMessage("SaveState",
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save state to slot {0}:\n{1}"),
|
||||
GetCurrentSlot(), error.GetDescription()),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "system.h"
|
||||
|
@ -1164,28 +1164,35 @@ void System::PauseSystem(bool paused)
|
|||
}
|
||||
}
|
||||
|
||||
bool System::LoadState(const char* filename)
|
||||
bool System::LoadState(const char* filename, Error* error)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
Error::SetStringView(error, "System is not booted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
{
|
||||
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("Achievements", "Loading state"),
|
||||
[filename = std::string(filename)](bool approved) {
|
||||
if (approved)
|
||||
LoadState(filename.c_str());
|
||||
LoadState(filename.c_str(), nullptr);
|
||||
});
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Timer load_timer;
|
||||
|
||||
std::unique_ptr<ByteStream> stream = ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||
std::unique_ptr<ByteStream> stream =
|
||||
ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, error);
|
||||
if (!stream)
|
||||
{
|
||||
Error::AddPrefixFmt(error, "Failed to open '{}': ", Path::GetFileName(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Loading state from '%s'...", filename);
|
||||
Log_InfoFmt("Loading state from '{}'...", filename);
|
||||
|
||||
{
|
||||
const std::string display_name(FileSystem::GetDisplayNameFromPath(filename));
|
||||
|
@ -1196,11 +1203,8 @@ bool System::LoadState(const char* filename)
|
|||
|
||||
SaveUndoLoadState();
|
||||
|
||||
if (!LoadStateFromStream(stream.get(), true))
|
||||
if (!LoadStateFromStream(stream.get(), error, true))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Load State Error",
|
||||
TRANSLATE("OSDMessage", "Loading state from '%s' failed. Resetting."), filename);
|
||||
|
||||
if (m_undo_load_state)
|
||||
UndoLoadState();
|
||||
|
||||
|
@ -1217,7 +1221,7 @@ bool System::LoadState(const char* filename)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool System::SaveState(const char* filename, bool backup_existing_save)
|
||||
bool System::SaveState(const char* filename, Error* error, bool backup_existing_save)
|
||||
{
|
||||
if (backup_existing_save && FileSystem::FileExists(filename))
|
||||
{
|
||||
|
@ -1229,21 +1233,24 @@ bool System::SaveState(const char* filename, bool backup_existing_save)
|
|||
Common::Timer save_timer;
|
||||
|
||||
std::unique_ptr<ByteStream> stream =
|
||||
ByteStream::OpenFile(filename, BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE |
|
||||
BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED);
|
||||
ByteStream::OpenFile(filename,
|
||||
BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE |
|
||||
BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED,
|
||||
error);
|
||||
if (!stream)
|
||||
{
|
||||
Error::AddPrefixFmt(error, "Failed to save state to '{}': ", Path::GetFileName(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Saving state to '%s'...", filename);
|
||||
|
||||
const u32 screenshot_size = 256;
|
||||
const bool result = SaveStateToStream(stream.get(), screenshot_size,
|
||||
const bool result = SaveStateToStream(stream.get(), error, screenshot_size,
|
||||
g_settings.compress_save_states ? SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD :
|
||||
SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE);
|
||||
if (!result)
|
||||
{
|
||||
Host::ReportFormattedErrorAsync(TRANSLATE("OSDMessage", "Save State"),
|
||||
TRANSLATE("OSDMessage", "Saving state to '%s' failed."), filename);
|
||||
stream->Discard();
|
||||
}
|
||||
else
|
||||
|
@ -1259,16 +1266,19 @@ bool System::SaveState(const char* filename, bool backup_existing_save)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool System::SaveResumeState()
|
||||
bool System::SaveResumeState(Error* error)
|
||||
{
|
||||
if (s_running_game_serial.empty())
|
||||
{
|
||||
Error::SetStringView(error, "Cannot save resume state without serial.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string path(GetGameSaveStateFileName(s_running_game_serial, -1));
|
||||
return SaveState(path.c_str(), false);
|
||||
return SaveState(path.c_str(), error, false);
|
||||
}
|
||||
|
||||
bool System::BootSystem(SystemBootParameters parameters)
|
||||
bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
||||
{
|
||||
if (!parameters.save_state.empty())
|
||||
{
|
||||
|
@ -1279,9 +1289,9 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
}
|
||||
|
||||
if (parameters.filename.empty())
|
||||
Log_InfoPrintf("Boot Filename: <BIOS/Shell>");
|
||||
Log_InfoPrint("Boot Filename: <BIOS/Shell>");
|
||||
else
|
||||
Log_InfoPrintf("Boot Filename: %s", parameters.filename.c_str());
|
||||
Log_InfoFmt("Boot Filename: {}", parameters.filename);
|
||||
|
||||
Assert(s_state == State::Shutdown);
|
||||
s_state = State::Starting;
|
||||
|
@ -1291,7 +1301,6 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
Host::OnSystemStarting();
|
||||
|
||||
// Load CD image up and detect region.
|
||||
Error error;
|
||||
std::unique_ptr<CDImage> disc;
|
||||
DiscRegion disc_region = DiscRegion::NonPS1;
|
||||
std::string exe_boot;
|
||||
|
@ -1317,11 +1326,10 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
else
|
||||
{
|
||||
Log_InfoPrintf("Loading CD image '%s'...", parameters.filename.c_str());
|
||||
disc = CDImage::Open(parameters.filename.c_str(), g_settings.cdrom_load_image_patches, &error);
|
||||
disc = CDImage::Open(parameters.filename.c_str(), g_settings.cdrom_load_image_patches, error);
|
||||
if (!disc)
|
||||
{
|
||||
Host::ReportErrorAsync("Error", fmt::format("Failed to load CD image '{}': {}",
|
||||
Path::GetFileName(parameters.filename), error.GetDescription()));
|
||||
Error::AddPrefixFmt(error, "Failed to open CD image '{}':\n", Path::GetFileName(parameters.filename));
|
||||
s_state = State::Shutdown;
|
||||
Host::OnSystemDestroyed();
|
||||
Host::OnIdleStateChanged();
|
||||
|
@ -1357,11 +1365,10 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
Log_InfoPrintf("Console Region: %s", Settings::GetConsoleRegionDisplayName(s_region));
|
||||
|
||||
// Switch subimage.
|
||||
if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, &error))
|
||||
if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, error))
|
||||
{
|
||||
Host::ReportErrorAsync("Error",
|
||||
fmt::format("Failed to switch to subimage {} in '{}': {}", parameters.media_playlist_index,
|
||||
parameters.filename, error.GetDescription()));
|
||||
Error::AddPrefixFmt(error, "Failed to switch to subimage {} in '{}':\n", parameters.media_playlist_index,
|
||||
Path::GetFileName(parameters.filename));
|
||||
s_state = State::Shutdown;
|
||||
Host::OnSystemDestroyed();
|
||||
Host::OnIdleStateChanged();
|
||||
|
@ -1375,8 +1382,8 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
{
|
||||
if (!FileSystem::FileExists(parameters.override_exe.c_str()) || !IsExeFileName(parameters.override_exe))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", "File '%s' is not a valid executable to boot.",
|
||||
parameters.override_exe.c_str());
|
||||
Error::SetStringFmt(error, "File '{}' is not a valid executable to boot.",
|
||||
Path::GetFileName(parameters.override_exe));
|
||||
s_state = State::Shutdown;
|
||||
Host::OnSystemDestroyed();
|
||||
Host::OnIdleStateChanged();
|
||||
|
@ -1410,7 +1417,7 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
if (approved)
|
||||
{
|
||||
parameters.disable_achievements_hardcore_mode = true;
|
||||
BootSystem(std::move(parameters));
|
||||
BootSystem(std::move(parameters), nullptr);
|
||||
}
|
||||
});
|
||||
cancelled = true;
|
||||
|
@ -1462,13 +1469,13 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
// Load EXE late after BIOS.
|
||||
if (!exe_boot.empty() && !LoadEXE(exe_boot.c_str()))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", "Failed to load EXE file '%s'", exe_boot.c_str());
|
||||
Error::SetStringFmt(error, "Failed to load EXE file '{}'", Path::GetFileName(exe_boot));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
else if (!psf_boot.empty() && !PSFLoader::Load(psf_boot.c_str()))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", "Failed to load PSF file '%s'", psf_boot.c_str());
|
||||
Error::SetStringFmt(error, "Failed to load PSF file '{}'", Path::GetFileName(psf_boot));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
@ -1509,17 +1516,16 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
if (!parameters.save_state.empty())
|
||||
{
|
||||
std::unique_ptr<ByteStream> stream =
|
||||
ByteStream::OpenFile(parameters.save_state.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||
ByteStream::OpenFile(parameters.save_state.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, error);
|
||||
if (!stream)
|
||||
{
|
||||
Host::ReportErrorAsync(
|
||||
TRANSLATE("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to load save state file '{}' for booting."), parameters.save_state));
|
||||
Error::AddPrefixFmt(error, "Failed to load save state file '{}' for booting:\n",
|
||||
Path::GetFileName(parameters.save_state));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadStateFromStream(stream.get(), true))
|
||||
if (!LoadStateFromStream(stream.get(), error, true))
|
||||
{
|
||||
DestroySystem();
|
||||
return false;
|
||||
|
@ -1974,13 +1980,16 @@ void System::IncrementInternalFrameNumber()
|
|||
|
||||
void System::RecreateSystem()
|
||||
{
|
||||
Error error;
|
||||
Assert(!IsShutdown());
|
||||
|
||||
const bool was_paused = System::IsPaused();
|
||||
std::unique_ptr<ByteStream> stream = ByteStream::CreateGrowableMemoryStream(nullptr, 8 * 1024);
|
||||
if (!System::SaveStateToStream(stream.get(), 0, SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE) || !stream->SeekAbsolute(0))
|
||||
if (!System::SaveStateToStream(stream.get(), &error, 0, SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE) ||
|
||||
!stream->SeekAbsolute(0))
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to save state before system recreation. Shutting down.");
|
||||
Host::ReportErrorAsync(
|
||||
"Error", fmt::format("Failed to save state before system recreation. Shutting down:\n", error.GetDescription()));
|
||||
DestroySystem();
|
||||
return;
|
||||
}
|
||||
|
@ -1988,13 +1997,13 @@ void System::RecreateSystem()
|
|||
DestroySystem();
|
||||
|
||||
SystemBootParameters boot_params;
|
||||
if (!BootSystem(std::move(boot_params)))
|
||||
if (!BootSystem(std::move(boot_params), &error))
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to boot system after recreation.");
|
||||
Host::ReportErrorAsync("Error", fmt::format("Failed to boot system after recreation:\n{}", error.GetDescription()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LoadStateFromStream(stream.get(), false))
|
||||
if (!LoadStateFromStream(stream.get(), &error, false))
|
||||
{
|
||||
DestroySystem();
|
||||
return;
|
||||
|
@ -2265,36 +2274,35 @@ std::string System::GetMediaPathFromSaveState(const char* path)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ignore_media)
|
||||
bool System::LoadStateFromStream(ByteStream* state, Error* error, bool update_display, bool ignore_media)
|
||||
{
|
||||
Assert(IsValid());
|
||||
|
||||
SAVE_STATE_HEADER header;
|
||||
if (!state->Read2(&header, sizeof(header)))
|
||||
return false;
|
||||
|
||||
if (header.magic != SAVE_STATE_MAGIC)
|
||||
if (!state->Read2(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC)
|
||||
{
|
||||
Error::SetStringView(error, "Incorrect file format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.version < SAVE_STATE_MINIMUM_VERSION)
|
||||
{
|
||||
Host::ReportFormattedErrorAsync(
|
||||
"Error", TRANSLATE("System", "Save state is incompatible: minimum version is %u but state is version %u."),
|
||||
Error::SetStringFmt(
|
||||
error, TRANSLATE_FS("System", "Save state is incompatible: minimum version is {0} but state is version {1}."),
|
||||
SAVE_STATE_MINIMUM_VERSION, header.version);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.version > SAVE_STATE_VERSION)
|
||||
{
|
||||
Host::ReportFormattedErrorAsync(
|
||||
"Error", TRANSLATE("System", "Save state is incompatible: maximum version is %u but state is version %u."),
|
||||
Error::SetStringFmt(
|
||||
error, TRANSLATE_FS("System", "Save state is incompatible: maximum version is {0} but state is version {1}."),
|
||||
SAVE_STATE_VERSION, header.version);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ignore_media)
|
||||
{
|
||||
Error error;
|
||||
std::string media_filename;
|
||||
std::unique_ptr<CDImage> media;
|
||||
if (header.media_filename_length > 0)
|
||||
|
@ -2314,7 +2322,9 @@ bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ig
|
|||
}
|
||||
else
|
||||
{
|
||||
media = CDImage::Open(media_filename.c_str(), g_settings.cdrom_load_image_patches, &error);
|
||||
Error local_error;
|
||||
media =
|
||||
CDImage::Open(media_filename.c_str(), g_settings.cdrom_load_image_patches, error ? error : &local_error);
|
||||
if (!media)
|
||||
{
|
||||
if (old_media)
|
||||
|
@ -2322,17 +2332,16 @@ bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ig
|
|||
Host::AddOSDMessage(
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to open CD image from save state '{}': {}.\nUsing "
|
||||
"existing image '{}', this may result in instability."),
|
||||
media_filename, error.GetDescription(), old_media->GetFileName()),
|
||||
media_filename, error ? error->GetDescription() : local_error.GetDescription(),
|
||||
old_media->GetFileName()),
|
||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
media = std::move(old_media);
|
||||
header.media_subimage_index = media->GetCurrentSubImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::ReportErrorAsync(
|
||||
TRANSLATE_SV("OSDMessage", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to open CD image '{}' used by save state: {}."),
|
||||
media_filename, error.GetDescription()));
|
||||
Error::AddPrefixFmt(error, TRANSLATE_FS("System", "Failed to open CD image '{}' used by save state:\n"),
|
||||
Path::GetFileName(media_filename));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2346,18 +2355,16 @@ bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ig
|
|||
const u32 num_subimages = media->HasSubImages() ? media->GetSubImageCount() : 1;
|
||||
if (header.media_subimage_index >= num_subimages ||
|
||||
(media->HasSubImages() && media->GetCurrentSubImage() != header.media_subimage_index &&
|
||||
!media->SwitchSubImage(header.media_subimage_index, &error)))
|
||||
!media->SwitchSubImage(header.media_subimage_index, error)))
|
||||
{
|
||||
Host::ReportErrorAsync(
|
||||
TRANSLATE_SV("OSDMessage", "Error"),
|
||||
fmt::format(
|
||||
TRANSLATE_FS("System", "Failed to switch to subimage {} in CD image '{}' used by save state: {}."),
|
||||
header.media_subimage_index + 1u, media_filename, error.GetDescription()));
|
||||
Error::AddPrefixFmt(
|
||||
error, TRANSLATE_FS("System", "Failed to switch to subimage {} in CD image '{}' used by save state:\n"),
|
||||
header.media_subimage_index + 1u, Path::GetFileName(media_filename));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_InfoPrintf("Switched to subimage %u in '%s'", header.media_subimage_index, media_filename.c_str());
|
||||
Log_InfoFmt("Switched to subimage {} in '{}'", header.media_subimage_index, media_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2391,18 +2398,24 @@ bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ig
|
|||
{
|
||||
StateWrapper sw(state, StateWrapper::Mode::Read, header.version);
|
||||
if (!DoState(sw, nullptr, update_display, false))
|
||||
{
|
||||
Error::SetStringView(error, "Save state stream is corrupted.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (header.data_compression_type == SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD)
|
||||
{
|
||||
std::unique_ptr<ByteStream> dstream(ByteStream::CreateZstdDecompressStream(state, header.data_compressed_size));
|
||||
StateWrapper sw(dstream.get(), StateWrapper::Mode::Read, header.version);
|
||||
if (!DoState(sw, nullptr, update_display, false))
|
||||
{
|
||||
Error::SetStringView(error, "Save state stream is corrupted.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", "Unknown save state compression type %u", header.data_compression_type);
|
||||
Error::SetStringFmt(error, "Unknown save state compression type {}", header.data_compression_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2415,7 +2428,7 @@ bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ig
|
|||
return true;
|
||||
}
|
||||
|
||||
bool System::SaveStateToStream(ByteStream* state, u32 screenshot_size /* = 256 */,
|
||||
bool System::SaveStateToStream(ByteStream* state, Error* error, u32 screenshot_size /* = 256 */,
|
||||
u32 compression_method /* = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE*/,
|
||||
bool ignore_media /* = false*/)
|
||||
{
|
||||
|
@ -4210,7 +4223,15 @@ void System::ShutdownSystem(bool save_resume_state)
|
|||
return;
|
||||
|
||||
if (save_resume_state)
|
||||
SaveResumeState();
|
||||
{
|
||||
Error error;
|
||||
if (!SaveResumeState(&error))
|
||||
{
|
||||
Host::ReportErrorAsync(
|
||||
TRANSLATE_SV("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to save resume state: {}"), error.GetDescription()));
|
||||
}
|
||||
}
|
||||
|
||||
s_state = State::Stopping;
|
||||
if (!s_system_executing)
|
||||
|
@ -4245,10 +4266,12 @@ bool System::UndoLoadState()
|
|||
|
||||
Assert(IsValid());
|
||||
|
||||
Error error;
|
||||
m_undo_load_state->SeekAbsolute(0);
|
||||
if (!LoadStateFromStream(m_undo_load_state.get(), true))
|
||||
if (!LoadStateFromStream(m_undo_load_state.get(), &error, true))
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to load undo state, resetting system.");
|
||||
Host::ReportErrorAsync("Error",
|
||||
fmt::format("Failed to load undo state, resetting system:\n", error.GetDescription()));
|
||||
m_undo_load_state.reset();
|
||||
ResetSystem();
|
||||
return false;
|
||||
|
@ -4264,10 +4287,13 @@ bool System::SaveUndoLoadState()
|
|||
if (m_undo_load_state)
|
||||
m_undo_load_state.reset();
|
||||
|
||||
Error error;
|
||||
m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE);
|
||||
if (!SaveStateToStream(m_undo_load_state.get()))
|
||||
if (!SaveStateToStream(m_undo_load_state.get(), &error))
|
||||
{
|
||||
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to save undo load state."), 15.0f);
|
||||
Host::AddOSDMessage(
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save undo load state:\n{}"), error.GetDescription()),
|
||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
m_undo_load_state.reset();
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -241,14 +241,14 @@ void ApplySettings(bool display_osd_messages);
|
|||
/// Reloads game specific settings, and applys any changes present.
|
||||
bool ReloadGameSettings(bool display_osd_messages);
|
||||
|
||||
bool BootSystem(SystemBootParameters parameters);
|
||||
bool BootSystem(SystemBootParameters parameters, Error* error);
|
||||
void PauseSystem(bool paused);
|
||||
void ResetSystem();
|
||||
|
||||
/// Loads state from the specified filename.
|
||||
bool LoadState(const char* filename);
|
||||
bool SaveState(const char* filename, bool backup_existing_save);
|
||||
bool SaveResumeState();
|
||||
bool LoadState(const char* filename, Error* error);
|
||||
bool SaveState(const char* filename, Error* error, bool backup_existing_save);
|
||||
bool SaveResumeState(Error* error);
|
||||
|
||||
/// Memory save states - only for internal use.
|
||||
struct MemorySaveState
|
||||
|
@ -258,8 +258,8 @@ struct MemorySaveState
|
|||
};
|
||||
bool SaveMemoryState(MemorySaveState* mss);
|
||||
bool LoadMemoryState(const MemorySaveState& mss);
|
||||
bool LoadStateFromStream(ByteStream* stream, bool update_display, bool ignore_media = false);
|
||||
bool SaveStateToStream(ByteStream* state, u32 screenshot_size = 256, u32 compression_method = 0,
|
||||
bool LoadStateFromStream(ByteStream* stream, Error* error, bool update_display, bool ignore_media = false);
|
||||
bool SaveStateToStream(ByteStream* state, Error* error, u32 screenshot_size = 256, u32 compression_method = 0,
|
||||
bool ignore_media = false);
|
||||
|
||||
/// Runs the VM until the CPU execution is canceled.
|
||||
|
|
|
@ -722,7 +722,12 @@ void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
|
|||
|
||||
setInitialState(params->override_fullscreen);
|
||||
|
||||
System::BootSystem(std::move(*params));
|
||||
Error error;
|
||||
if (!System::BootSystem(std::move(*params), &error))
|
||||
{
|
||||
emit errorReported(tr("Error"),
|
||||
tr("Failed to boot system: %1").arg(QString::fromStdString(error.GetDescription())));
|
||||
}
|
||||
}
|
||||
|
||||
void EmuThread::bootOrLoadState(std::string path)
|
||||
|
@ -731,7 +736,12 @@ void EmuThread::bootOrLoadState(std::string path)
|
|||
|
||||
if (System::IsValid())
|
||||
{
|
||||
System::LoadState(path.c_str());
|
||||
Error error;
|
||||
if (!System::LoadState(path.c_str(), &error))
|
||||
{
|
||||
emit errorReported(tr("Error"),
|
||||
tr("Failed to load state: %1").arg(QString::fromStdString(error.GetDescription())));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1241,7 +1251,9 @@ void EmuThread::saveState(const QString& filename, bool block_until_done /* = fa
|
|||
if (!System::IsValid())
|
||||
return;
|
||||
|
||||
System::SaveState(filename.toUtf8().data(), g_settings.create_save_state_backups);
|
||||
Error error;
|
||||
if (!System::SaveState(filename.toUtf8().data(), &error, g_settings.create_save_state_backups))
|
||||
emit errorReported(tr("Error"), tr("Failed to save state: %1").arg(QString::fromStdString(error.GetDescription())));
|
||||
}
|
||||
|
||||
void EmuThread::saveState(bool global, qint32 slot, bool block_until_done /* = false */)
|
||||
|
@ -1256,10 +1268,14 @@ void EmuThread::saveState(bool global, qint32 slot, bool block_until_done /* = f
|
|||
if (!global && System::GetGameSerial().empty())
|
||||
return;
|
||||
|
||||
System::SaveState((global ? System::GetGlobalSaveStateFileName(slot) :
|
||||
System::GetGameSaveStateFileName(System::GetGameSerial(), slot))
|
||||
.c_str(),
|
||||
g_settings.create_save_state_backups);
|
||||
Error error;
|
||||
if (!System::SaveState((global ? System::GetGlobalSaveStateFileName(slot) :
|
||||
System::GetGameSaveStateFileName(System::GetGameSerial(), slot))
|
||||
.c_str(),
|
||||
&error, g_settings.create_save_state_backups))
|
||||
{
|
||||
emit errorReported(tr("Error"), tr("Failed to save state: %1").arg(QString::fromStdString(error.GetDescription())));
|
||||
}
|
||||
}
|
||||
|
||||
void EmuThread::undoLoadState()
|
||||
|
|
|
@ -662,11 +662,12 @@ int main(int argc, char* argv[])
|
|||
|
||||
RegTestHost::HookSignals();
|
||||
|
||||
Error error;
|
||||
int result = -1;
|
||||
Log_InfoPrintf("Trying to boot '%s'...", autoboot->filename.c_str());
|
||||
if (!System::BootSystem(std::move(autoboot.value())))
|
||||
if (!System::BootSystem(std::move(autoboot.value()), &error))
|
||||
{
|
||||
Log_ErrorPrint("Failed to boot system.");
|
||||
Log_ErrorFmt("Failed to boot system: {}", error.GetDescription());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ std::unique_ptr<CDImage> CDImage::Open(const char* filename, bool allow_patches,
|
|||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Invalid filename: '%s'", filename);
|
||||
Error::SetStringFmt(error, "Invalid filename: '{}'", Path::GetFileName(filename));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ std::unique_ptr<CDImage> CDImage::Open(const char* filename, bool allow_patches,
|
|||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Unknown extension '%s' from filename '%s'", extension, filename);
|
||||
Error::SetStringFmt(error, "Unknown extension '{}' from filename '{}'", extension, Path::GetFileName(filename));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue