mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
SaveState: Rework error handling when saving states
This commit is contained in:
parent
bfdb435573
commit
afe6dac472
@ -1266,6 +1266,51 @@ void MainWindow::reportStateLoadError(const QString& message, std::optional<s32>
|
|||||||
delete message_box;
|
delete message_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::reportStateSaveError(const QString& message, std::optional<s32> slot)
|
||||||
|
{
|
||||||
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
|
if (!prompt_on_error)
|
||||||
|
{
|
||||||
|
SaveState_ReportSaveErrorOSD(message.toStdString(), slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
if (slot.has_value())
|
||||||
|
title = tr("Failed to Save State To Slot %1").arg(*slot);
|
||||||
|
else
|
||||||
|
title = tr("Failed to Save State");
|
||||||
|
|
||||||
|
VMLock lock(pauseAndLockVM());
|
||||||
|
|
||||||
|
QCheckBox* do_not_show_again = new QCheckBox(tr("Do not show again"));
|
||||||
|
|
||||||
|
QPointer<QMessageBox> message_box = new QMessageBox(this);
|
||||||
|
message_box->setWindowTitle(title);
|
||||||
|
message_box->setText(message);
|
||||||
|
message_box->setIcon(QMessageBox::Critical);
|
||||||
|
message_box->addButton(QMessageBox::Ok);
|
||||||
|
message_box->setDefaultButton(QMessageBox::Ok);
|
||||||
|
message_box->setCheckBox(do_not_show_again);
|
||||||
|
|
||||||
|
message_box->exec();
|
||||||
|
if (message_box.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (do_not_show_again->isChecked())
|
||||||
|
{
|
||||||
|
Host::SetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", false);
|
||||||
|
Host::CommitBaseSettingChanges();
|
||||||
|
if (m_settings_window)
|
||||||
|
{
|
||||||
|
InterfaceSettingsWidget* interface_settings = m_settings_window->getInterfaceSettingsWidget();
|
||||||
|
interface_settings->updatePromptOnStateLoadSaveFailureCheckbox(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete message_box;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::runOnUIThread(const std::function<void()>& func)
|
void MainWindow::runOnUIThread(const std::function<void()>& func)
|
||||||
{
|
{
|
||||||
func();
|
func();
|
||||||
|
|||||||
@ -121,6 +121,7 @@ public Q_SLOTS:
|
|||||||
bool confirmMessage(const QString& title, const QString& message);
|
bool confirmMessage(const QString& title, const QString& message);
|
||||||
void onStatusMessage(const QString& message);
|
void onStatusMessage(const QString& message);
|
||||||
void reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup);
|
void reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup);
|
||||||
|
void reportStateSaveError(const QString& message, std::optional<s32> slot);
|
||||||
|
|
||||||
void runOnUIThread(const std::function<void()>& func);
|
void runOnUIThread(const std::function<void()>& func);
|
||||||
void requestReset();
|
void requestReset();
|
||||||
|
|||||||
@ -337,11 +337,11 @@ void EmuThread::saveState(const QString& filename)
|
|||||||
if (!VMManager::HasValidVM())
|
if (!VMManager::HasValidVM())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!VMManager::SaveState(filename.toUtf8().constData()))
|
VMManager::SaveState(filename.toUtf8().constData(), true, false, [](const std::string& error) {
|
||||||
{
|
QtHost::RunOnUIThread([message = QString::fromStdString(error)]() {
|
||||||
// this one is usually the result of a user-chosen path, so we can display a message box safely here
|
g_main_window->reportStateSaveError(message, std::nullopt);
|
||||||
Console.Error("Failed to save state");
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::saveStateToSlot(qint32 slot)
|
void EmuThread::saveStateToSlot(qint32 slot)
|
||||||
@ -355,7 +355,11 @@ void EmuThread::saveStateToSlot(qint32 slot)
|
|||||||
if (!VMManager::HasValidVM())
|
if (!VMManager::HasValidVM())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VMManager::SaveStateToSlot(slot);
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
QtHost::RunOnUIThread([message = QString::fromStdString(error), slot]() {
|
||||||
|
g_main_window->reportStateSaveError(message, slot);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::run()
|
void EmuThread::run()
|
||||||
|
|||||||
@ -112,7 +112,9 @@ static void HotkeyLoadStateSlot(s32 slot)
|
|||||||
|
|
||||||
static void HotkeySaveStateSlot(s32 slot)
|
static void HotkeySaveStateSlot(s32 slot)
|
||||||
{
|
{
|
||||||
VMManager::SaveStateToSlot(slot);
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
FullscreenUI::ReportStateSaveError(error, slot);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CanPause()
|
static bool CanPause()
|
||||||
|
|||||||
@ -671,6 +671,7 @@ namespace FullscreenUI
|
|||||||
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
|
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
|
||||||
static void DrawResumeStateSelector();
|
static void DrawResumeStateSelector();
|
||||||
static void DoLoadState(std::string path, std::optional<s32> slot, bool backup);
|
static void DoLoadState(std::string path, std::optional<s32> slot, bool backup);
|
||||||
|
static void DoSaveState(s32 slot);
|
||||||
|
|
||||||
static std::vector<SaveStateListEntry> s_save_state_selector_slots;
|
static std::vector<SaveStateListEntry> s_save_state_selector_slots;
|
||||||
static std::string s_save_state_selector_game_path;
|
static std::string s_save_state_selector_game_path;
|
||||||
@ -7347,7 +7348,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
|||||||
if (is_loading)
|
if (is_loading)
|
||||||
DoLoadState(std::move(entry.path), entry.slot, false);
|
DoLoadState(std::move(entry.path), entry.slot, false);
|
||||||
else
|
else
|
||||||
Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); });
|
DoSaveState(entry.slot);
|
||||||
|
|
||||||
CloseSaveStateSelector();
|
CloseSaveStateSelector();
|
||||||
ReturnToMainWindow();
|
ReturnToMainWindow();
|
||||||
@ -7485,7 +7486,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
|||||||
if (is_loading)
|
if (is_loading)
|
||||||
DoLoadState(entry.path, entry.slot, false);
|
DoLoadState(entry.path, entry.slot, false);
|
||||||
else
|
else
|
||||||
Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); });
|
DoSaveState(entry.slot);
|
||||||
|
|
||||||
CloseSaveStateSelector();
|
CloseSaveStateSelector();
|
||||||
ReturnToMainWindow();
|
ReturnToMainWindow();
|
||||||
@ -7665,6 +7666,15 @@ void FullscreenUI::DoLoadState(std::string path, std::optional<s32> slot, bool b
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FullscreenUI::DoSaveState(s32 slot)
|
||||||
|
{
|
||||||
|
Host::RunOnCPUThread([slot]() {
|
||||||
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
ReportStateSaveError(error, slot);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void FullscreenUI::PopulateGameListEntryList()
|
void FullscreenUI::PopulateGameListEntryList()
|
||||||
{
|
{
|
||||||
const int sort = Host::GetBaseIntSettingValue("UI", "FullscreenUIGameSort", 0);
|
const int sort = Host::GetBaseIntSettingValue("UI", "FullscreenUIGameSort", 0);
|
||||||
@ -9166,9 +9176,9 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
|
|||||||
EndMenuButtons();
|
EndMenuButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullscreenUI::ReportStateLoadError(std::string message, std::optional<s32> slot, bool backup)
|
void FullscreenUI::ReportStateLoadError(const std::string& message, std::optional<s32> slot, bool backup)
|
||||||
{
|
{
|
||||||
MTGS::RunOnGSThread([message = std::move(message), slot, backup]() {
|
MTGS::RunOnGSThread([message, slot, backup]() {
|
||||||
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
if (!prompt_on_error || !ImGuiManager::InitializeFullscreenUI())
|
if (!prompt_on_error || !ImGuiManager::InitializeFullscreenUI())
|
||||||
{
|
{
|
||||||
@ -9204,6 +9214,37 @@ void FullscreenUI::ReportStateLoadError(std::string message, std::optional<s32>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FullscreenUI::ReportStateSaveError(const std::string& message, std::optional<s32> slot)
|
||||||
|
{
|
||||||
|
MTGS::RunOnGSThread([message, slot]() {
|
||||||
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
|
if (!prompt_on_error || !ImGuiManager::InitializeFullscreenUI())
|
||||||
|
{
|
||||||
|
SaveState_ReportSaveErrorOSD(message, slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string title;
|
||||||
|
if (slot.has_value())
|
||||||
|
title = fmt::format(FSUI_FSTR("Failed to Save State To Slot {}"), *slot);
|
||||||
|
else
|
||||||
|
title = FSUI_STR("Failed to Save State");
|
||||||
|
|
||||||
|
ImGuiFullscreen::InfoMessageDialogCallback callback;
|
||||||
|
if (VMManager::GetState() == VMState::Running)
|
||||||
|
{
|
||||||
|
Host::RunOnCPUThread([]() { VMManager::SetPaused(true); });
|
||||||
|
callback = []() {
|
||||||
|
Host::RunOnCPUThread([]() { VMManager::SetPaused(false); });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFullscreen::OpenInfoMessageDialog(
|
||||||
|
fmt::format("{} {}", ICON_FA_TRIANGLE_EXCLAMATION, title),
|
||||||
|
std::move(message), std::move(callback));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Translation String Area
|
// Translation String Area
|
||||||
// To avoid having to type T_RANSLATE("FullscreenUI", ...) everywhere, we use the shorter macros at the top
|
// To avoid having to type T_RANSLATE("FullscreenUI", ...) everywhere, we use the shorter macros at the top
|
||||||
|
|||||||
@ -27,7 +27,8 @@ namespace FullscreenUI
|
|||||||
void OpenPauseMenu();
|
void OpenPauseMenu();
|
||||||
bool OpenAchievementsWindow();
|
bool OpenAchievementsWindow();
|
||||||
bool OpenLeaderboardsWindow();
|
bool OpenLeaderboardsWindow();
|
||||||
void ReportStateLoadError(std::string message, std::optional<s32> slot, bool backup);
|
void ReportStateLoadError(const std::string& message, std::optional<s32> slot, bool backup);
|
||||||
|
void ReportStateSaveError(const std::string& message, std::optional<s32> slot);
|
||||||
|
|
||||||
// NOTE: Only call from GS thread.
|
// NOTE: Only call from GS thread.
|
||||||
bool IsAchievementsWindowOpen();
|
bool IsAchievementsWindowOpen();
|
||||||
|
|||||||
@ -1400,7 +1400,9 @@ void SaveStateSelectorUI::LoadCurrentBackupSlot()
|
|||||||
void SaveStateSelectorUI::SaveCurrentSlot()
|
void SaveStateSelectorUI::SaveCurrentSlot()
|
||||||
{
|
{
|
||||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||||
VMManager::SaveStateToSlot(slot);
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
FullscreenUI::ReportStateSaveError(error, slot);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -646,7 +646,11 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(std::span<u8> buf, std::vector<u8
|
|||||||
goto error;
|
goto error;
|
||||||
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size)) [[unlikely]]
|
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size)) [[unlikely]]
|
||||||
goto error;
|
goto error;
|
||||||
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] { VMManager::SaveStateToSlot(slot); });
|
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] {
|
||||||
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
SaveState_ReportSaveErrorOSD(error, slot);
|
||||||
|
});
|
||||||
|
});
|
||||||
buf_cnt += 1;
|
buf_cnt += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,8 +56,9 @@ bool InputRecording::create(const std::string& fileName, const bool fromSaveStat
|
|||||||
m_initial_load_complete = true;
|
m_initial_load_complete = true;
|
||||||
m_watching_for_rerecords = true;
|
m_watching_for_rerecords = true;
|
||||||
setStartingFrame(g_FrameCount);
|
setStartingFrame(g_FrameCount);
|
||||||
// TODO - error handling
|
VMManager::SaveState(savestatePath.c_str(), true, false, [](const std::string& error) {
|
||||||
VMManager::SaveState(savestatePath.c_str());
|
SaveState_ReportSaveErrorOSD(error, std::nullopt);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -395,8 +396,7 @@ void InputRecording::InformGSThread()
|
|||||||
TinyString frame_data_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Frame: {}/{} ({})"), g_InputRecording.getFrameCounter(), g_InputRecording.getData().getTotalFrames(), g_InputRecording.getFrameCounterStateless());
|
TinyString frame_data_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Frame: {}/{} ({})"), g_InputRecording.getFrameCounter(), g_InputRecording.getData().getTotalFrames(), g_InputRecording.getFrameCounterStateless());
|
||||||
TinyString undo_count_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Undo Count: {}"), g_InputRecording.getData().getUndoCount());
|
TinyString undo_count_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Undo Count: {}"), g_InputRecording.getData().getUndoCount());
|
||||||
|
|
||||||
MTGS::RunOnGSThread([recording_active_message, frame_data_message, undo_count_message](bool is_recording = g_InputRecording.getControls().isRecording())
|
MTGS::RunOnGSThread([recording_active_message, frame_data_message, undo_count_message](bool is_recording = g_InputRecording.getControls().isRecording()) {
|
||||||
{
|
|
||||||
g_InputRecordingData.is_recording = is_recording;
|
g_InputRecordingData.is_recording = is_recording;
|
||||||
g_InputRecordingData.recording_active_message = recording_active_message;
|
g_InputRecordingData.recording_active_message = recording_active_message;
|
||||||
g_InputRecordingData.frame_data_message = frame_data_message;
|
g_InputRecordingData.frame_data_message = frame_data_message;
|
||||||
|
|||||||
@ -1253,3 +1253,16 @@ void SaveState_ReportLoadErrorOSD(const std::string& message, std::optional<s32>
|
|||||||
Host::AddIconOSDMessage("LoadState", ICON_FA_TRIANGLE_EXCLAMATION,
|
Host::AddIconOSDMessage("LoadState", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||||
full_message, Host::OSD_WARNING_DURATION);
|
full_message, Host::OSD_WARNING_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaveState_ReportSaveErrorOSD(const std::string& message, std::optional<s32> slot)
|
||||||
|
{
|
||||||
|
std::string full_message;
|
||||||
|
if (slot.has_value())
|
||||||
|
full_message = fmt::format(
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to save state to slot {}: {}"), *slot, message);
|
||||||
|
else
|
||||||
|
full_message = fmt::format(TRANSLATE_FS("SaveState", "Failed to save state: {}"), message);
|
||||||
|
|
||||||
|
Host::AddIconOSDMessage("SaveState", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||||
|
full_message, Host::OSD_WARNING_DURATION);
|
||||||
|
}
|
||||||
|
|||||||
@ -356,3 +356,4 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void SaveState_ReportLoadErrorOSD(const std::string& message, std::optional<s32> slot, bool backup);
|
void SaveState_ReportLoadErrorOSD(const std::string& message, std::optional<s32> slot, bool backup);
|
||||||
|
void SaveState_ReportSaveErrorOSD(const std::string& message, std::optional<s32> slot);
|
||||||
|
|||||||
@ -115,13 +115,13 @@ namespace VMManager
|
|||||||
|
|
||||||
static std::string GetCurrentSaveStateFileName(s32 slot, bool backup = false);
|
static std::string GetCurrentSaveStateFileName(s32 slot, bool backup = false);
|
||||||
static bool DoLoadState(const char* filename, Error* error = nullptr);
|
static bool DoLoadState(const char* filename, Error* error = nullptr);
|
||||||
static bool DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state);
|
static void DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state, std::function<void(const std::string&)> error_callback);
|
||||||
static void ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
static void ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename,
|
||||||
s32 slot_for_message);
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback);
|
||||||
static void ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
static void ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename,
|
||||||
s32 slot_for_message);
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback);
|
||||||
|
|
||||||
static void LoadSettings();
|
static void LoadSettings();
|
||||||
static void LoadCoreSettings(SettingsInterface& si);
|
static void LoadCoreSettings(SettingsInterface& si);
|
||||||
@ -1617,8 +1617,13 @@ void VMManager::Shutdown(bool save_resume_state)
|
|||||||
if (!GSDumpReplayer::IsReplayingDump() && save_resume_state)
|
if (!GSDumpReplayer::IsReplayingDump() && save_resume_state)
|
||||||
{
|
{
|
||||||
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
||||||
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1, true, false))
|
if (!resume_file_name.empty())
|
||||||
Console.Error("Failed to save resume state");
|
{
|
||||||
|
DoSaveState(resume_file_name.c_str(), -1, true, false, [](const std::string& error) {
|
||||||
|
Host::AddIconOSDMessage("SaveResumeState", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||||
|
fmt::format(TRANSLATE_FS("VMManager", "Failed to save resume state: {}"), error), Host::OSD_QUICK_DURATION);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// end input recording before clearing state
|
// end input recording before clearing state
|
||||||
@ -1829,7 +1834,7 @@ bool VMManager::DoLoadState(const char* filename, Error* error)
|
|||||||
{
|
{
|
||||||
if (GSDumpReplayer::IsReplayingDump())
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
{
|
{
|
||||||
Error::SetString(error, TRANSLATE_STR("VMManager", "Cannot load save state while replaying GS dump."));
|
Error::SetString(error, TRANSLATE_STR("VMManager", "Cannot load state while replaying GS dump."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1849,21 +1854,21 @@ bool VMManager::DoLoadState(const char* filename, Error* error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state)
|
void VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
if (GSDumpReplayer::IsReplayingDump())
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
return false;
|
{
|
||||||
|
error_callback(TRANSLATE_STR("VMManager", "Cannot save state while replaying GS dump."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string osd_key(fmt::format("SaveStateSlot{}", slot_for_message));
|
|
||||||
Error error;
|
Error error;
|
||||||
|
|
||||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
||||||
if (!elist)
|
if (!elist)
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(fmt::format(
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state: {}."), error.GetDescription()),
|
TRANSLATE_FS("VMManager", "Failed to save state: {}."), error.GetDescription()));
|
||||||
Host::OSD_ERROR_DURATION);
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot = SaveState_SaveScreenshot();
|
std::unique_ptr<SaveStateScreenshotData> screenshot = SaveState_SaveScreenshot();
|
||||||
@ -1874,10 +1879,10 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
|
|||||||
Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename));
|
Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename));
|
||||||
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(osd_key, ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(fmt::format(
|
||||||
fmt::format(
|
TRANSLATE_FS("VMManager", "Cannot back up old save state '{}'."),
|
||||||
TRANSLATE_FS("VMManager", "Failed to back up old save state {}."), Path::GetFileName(filename)),
|
Path::GetFileName(filename)));
|
||||||
Host::OSD_ERROR_DURATION);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1886,21 +1891,22 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
|
|||||||
// lock order here is important; the thread could exit before we resume here.
|
// lock order here is important; the thread could exit before we resume here.
|
||||||
std::unique_lock lock(s_save_state_threads_mutex);
|
std::unique_lock lock(s_save_state_threads_mutex);
|
||||||
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
|
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
|
||||||
std::move(osd_key), std::string(filename), slot_for_message);
|
std::string(filename), slot_for_message, std::move(error_callback));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename, slot_for_message);
|
ZipSaveState(
|
||||||
|
std::move(elist), std::move(screenshot), filename, slot_for_message, std::move(error_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
Host::OnSaveStateSaved(filename);
|
Host::OnSaveStateSaved(filename);
|
||||||
MemcardBusy::CheckSaveStateDependency();
|
MemcardBusy::CheckSaveStateDependency();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename,
|
||||||
s32 slot_for_message)
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
Common::Timer timer;
|
Common::Timer timer;
|
||||||
|
|
||||||
@ -1908,26 +1914,26 @@ void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
|||||||
{
|
{
|
||||||
if (slot_for_message >= 0 && VMManager::HasValidVM())
|
if (slot_for_message >= 0 && VMManager::HasValidVM())
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_FLOPPY_DISK,
|
Host::AddIconOSDMessage("SaveState", ICON_FA_FLOPPY_DISK,
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "State saved to slot {}."), slot_for_message),
|
fmt::format(TRANSLATE_FS("VMManager", "State saved to slot {}."), slot_for_message),
|
||||||
Host::OSD_QUICK_DURATION);
|
Host::OSD_QUICK_DURATION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(fmt::format(
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {}."), slot_for_message,
|
TRANSLATE_FS("VMManager", "Failed to save state to slot {}."), slot_for_message));
|
||||||
Host::OSD_ERROR_DURATION));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DevCon.WriteLn("Zipping save state to '%s' took %.2f ms", filename, timer.GetTimeMilliseconds());
|
DevCon.WriteLn("Zipping save state to '%s' took %.2f ms", filename, timer.GetTimeMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename,
|
||||||
s32 slot_for_message)
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename.c_str(), slot_for_message);
|
ZipSaveState(
|
||||||
|
std::move(elist), std::move(screenshot), filename.c_str(), slot_for_message, std::move(error_callback));
|
||||||
|
|
||||||
// remove ourselves from the thread list. if we're joining, we might not be in there.
|
// remove ourselves from the thread list. if we're joining, we might not be in there.
|
||||||
const auto this_id = std::this_thread::get_id();
|
const auto this_id = std::this_thread::get_id();
|
||||||
@ -2046,37 +2052,45 @@ bool VMManager::LoadStateFromSlot(s32 slot, bool backup, Error* error)
|
|||||||
return DoLoadState(filename.c_str(), error);
|
return DoLoadState(filename.c_str(), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::SaveState(const char* filename, bool zip_on_thread, bool backup_old_state)
|
void VMManager::SaveState(
|
||||||
|
const char* filename, bool zip_on_thread, bool backup_old_state, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
if (MemcardBusy::IsBusy())
|
if (MemcardBusy::IsBusy())
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(TRANSLATE_STR("VMManager", "Failed to save state (memory card is busy)."));
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state (Memory card is busy)")),
|
return;
|
||||||
Host::OSD_QUICK_DURATION);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DoSaveState(filename, -1, zip_on_thread, backup_old_state);
|
DoSaveState(filename, -1, zip_on_thread, backup_old_state, std::move(error_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread)
|
void VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
const std::string filename(GetCurrentSaveStateFileName(slot));
|
const std::string filename(GetCurrentSaveStateFileName(slot));
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
return false;
|
{
|
||||||
|
error_callback(TRANSLATE_STR("VMManager", "Failed to generate filename for save state."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (MemcardBusy::IsBusy())
|
if (MemcardBusy::IsBusy())
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(fmt::format(
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {} (Memory card is busy)"), slot),
|
TRANSLATE_FS("VMManager", "Failed to save state to slot {} (Memory card is busy)"), slot));
|
||||||
Host::OSD_QUICK_DURATION);
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it takes more than a minute.. well.. wtf.
|
// if it takes more than a minute.. well.. wtf.
|
||||||
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_FLOPPY_DISK,
|
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_FLOPPY_DISK,
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Saving state to slot {}..."), slot), 60.0f);
|
fmt::format(TRANSLATE_FS("VMManager", "Saving state to slot {}..."), slot), 60.0f);
|
||||||
return DoSaveState(filename.c_str(), slot, zip_on_thread, EmuConfig.BackupSavestate);
|
|
||||||
|
auto callback = [error_callback = std::move(error_callback), slot](const std::string& error) {
|
||||||
|
Host::RemoveKeyedOSDMessage(fmt::format("SaveStateSlot{}", slot));
|
||||||
|
error_callback(error);
|
||||||
|
};
|
||||||
|
|
||||||
|
return DoSaveState(
|
||||||
|
filename.c_str(), slot, zip_on_thread, EmuConfig.BackupSavestate, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
LimiterModeType VMManager::GetLimiterMode()
|
LimiterModeType VMManager::GetLimiterMode()
|
||||||
|
|||||||
@ -165,10 +165,11 @@ namespace VMManager
|
|||||||
bool LoadStateFromSlot(s32 slot, bool backup = false, Error* error = nullptr);
|
bool LoadStateFromSlot(s32 slot, bool backup = false, Error* error = nullptr);
|
||||||
|
|
||||||
/// Saves state to the specified filename.
|
/// Saves state to the specified filename.
|
||||||
bool SaveState(const char* filename, bool zip_on_thread = true, bool backup_old_state = false);
|
void SaveState(const char* filename, bool zip_on_thread, bool backup_old_state,
|
||||||
|
std::function<void(const std::string&)> error_callback);
|
||||||
|
|
||||||
/// Saves state to the specified slot.
|
/// Saves state to the specified slot.
|
||||||
bool SaveStateToSlot(s32 slot, bool zip_on_thread = true);
|
void SaveStateToSlot(s32 slot, bool zip_on_thread, std::function<void(const std::string&)> error_callback);
|
||||||
|
|
||||||
/// Waits until all compressing save states have finished saving to disk.
|
/// Waits until all compressing save states have finished saving to disk.
|
||||||
void WaitForSaveStateFlush();
|
void WaitForSaveStateFlush();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user