CommonHostInterface: Add undo load state feature

This commit is contained in:
Connor McLaughlin 2021-07-03 15:58:29 +10:00
parent af7dc40f5f
commit 762ab3ff43
4 changed files with 85 additions and 0 deletions

View file

@ -993,6 +993,9 @@ void QtHostInterface::populateSaveStateMenus(const char* game_code, QMenu* load_
loadState(path); loadState(path);
}); });
QAction* load_from_state = load_menu->addAction(tr("Undo Load State"));
load_from_state->setEnabled(CanUndoLoadState());
connect(load_from_state, &QAction::triggered, this, &QtHostInterface::undoLoadState);
load_menu->addSeparator(); load_menu->addSeparator();
connect(save_menu->addAction(tr("Save To File...")), &QAction::triggered, [this]() { connect(save_menu->addAction(tr("Save To File...")), &QAction::triggered, [this]() {
@ -1341,6 +1344,17 @@ void QtHostInterface::saveState(bool global, qint32 slot, bool block_until_done
SaveState(global, slot); SaveState(global, slot);
} }
void QtHostInterface::undoLoadState()
{
if (!isOnWorkerThread())
{
QMetaObject::invokeMethod(this, "undoLoadState", Qt::QueuedConnection);
return;
}
UndoLoadState();
}
void QtHostInterface::setAudioOutputVolume(int volume, int fast_forward_volume) void QtHostInterface::setAudioOutputVolume(int volume, int fast_forward_volume)
{ {
if (!isOnWorkerThread()) if (!isOnWorkerThread())

View file

@ -165,6 +165,7 @@ public Q_SLOTS:
void loadState(bool global, qint32 slot); void loadState(bool global, qint32 slot);
void saveState(const QString& filename, bool block_until_done = false); void saveState(const QString& filename, bool block_until_done = false);
void saveState(bool global, qint32 slot, bool block_until_done = false); void saveState(bool global, qint32 slot, bool block_until_done = false);
void undoLoadState();
void setAudioOutputVolume(int volume, int fast_forward_volume); void setAudioOutputVolume(int volume, int fast_forward_volume);
void setAudioOutputMuted(bool muted); void setAudioOutputMuted(bool muted);
void startDumpingAudio(); void startDumpingAudio();

View file

@ -215,6 +215,7 @@ bool CommonHostInterface::BootSystem(std::shared_ptr<SystemBootParameters> param
void CommonHostInterface::DestroySystem() void CommonHostInterface::DestroySystem()
{ {
m_undo_load_state.reset();
SetTimerResolutionIncreased(false); SetTimerResolutionIncreased(false);
m_save_state_selector_ui->Close(); m_save_state_selector_ui->Close();
m_display->SetPostProcessingChain({}); m_display->SetPostProcessingChain({});
@ -700,9 +701,57 @@ void CommonHostInterface::UpdateControllerInterface()
} }
} }
bool CommonHostInterface::UndoLoadState()
{
if (!m_undo_load_state)
return false;
Assert(System::IsValid());
m_undo_load_state->SeekAbsolute(0);
if (!System::LoadState(m_undo_load_state.get()))
{
ReportError("Failed to load undo state, resetting system.");
m_undo_load_state.reset();
ResetSystem();
return false;
}
System::ResetPerformanceCounters();
System::ResetThrottler();
#ifdef WITH_CHEEVOS
Cheevos::Reset();
#endif
Log_InfoPrintf("Loaded undo save state.");
m_undo_load_state.reset();
return true;
}
bool CommonHostInterface::SaveUndoLoadState()
{
if (m_undo_load_state)
m_undo_load_state.reset();
m_undo_load_state = ByteStream_CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE);
if (!System::SaveState(m_undo_load_state.get(), 0))
{
AddOSDMessage(TranslateStdString("OSDMessage", "Failed to save undo load state."), 15.0f);
m_undo_load_state.reset();
return false;
}
Log_InfoPrintf("Saved undo load state: % " PRIu64 " bytes", m_undo_load_state->GetSize());
return true;
}
bool CommonHostInterface::LoadState(const char* filename) bool CommonHostInterface::LoadState(const char* filename)
{ {
const bool system_was_valid = System::IsValid(); const bool system_was_valid = System::IsValid();
if (system_was_valid)
SaveUndoLoadState();
const bool result = HostInterface::LoadState(filename); const bool result = HostInterface::LoadState(filename);
if (system_was_valid || !result) if (system_was_valid || !result)
{ {
@ -711,6 +760,9 @@ bool CommonHostInterface::LoadState(const char* filename)
#endif #endif
} }
if (!result && CanUndoLoadState())
UndoLoadState();
return result; return result;
} }
@ -2320,6 +2372,12 @@ void CommonHostInterface::RegisterSaveStateHotkeys()
m_save_state_selector_ui->SelectNextSlot(); m_save_state_selector_ui->SelectNextSlot();
}); });
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Save States")), StaticString("UndoLoadState"),
StaticString(TRANSLATABLE("Hotkeys", "Undo Load State")), [this](bool pressed) {
if (pressed)
UndoLoadState();
});
for (u32 slot = 1; slot <= PER_GAME_SAVE_STATE_SLOTS; slot++) for (u32 slot = 1; slot <= PER_GAME_SAVE_STATE_SLOTS; slot++)
{ {
RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Save States")), RegisterHotkey(StaticString(TRANSLATABLE("Hotkeys", "Save States")),

View file

@ -155,6 +155,9 @@ public:
/// Returns true if the fullscreen UI is enabled. /// Returns true if the fullscreen UI is enabled.
ALWAYS_INLINE bool IsFullscreenUIEnabled() const { return m_fullscreen_ui_enabled; } ALWAYS_INLINE bool IsFullscreenUIEnabled() const { return m_fullscreen_ui_enabled; }
/// Returns true if an undo load state exists.
ALWAYS_INLINE bool CanUndoLoadState() const { return static_cast<bool>(m_undo_load_state); }
/// Parses command line parameters for all frontends. /// Parses command line parameters for all frontends.
bool ParseCommandLineParameters(int argc, char* argv[], std::unique_ptr<SystemBootParameters>* out_boot_params); bool ParseCommandLineParameters(int argc, char* argv[], std::unique_ptr<SystemBootParameters>* out_boot_params);
@ -176,6 +179,9 @@ public:
/// Powers off the system, optionally saving the resume state. /// Powers off the system, optionally saving the resume state.
void PowerOffSystem(bool save_resume_state); void PowerOffSystem(bool save_resume_state);
/// Undoes a load state, i.e. restores the state prior to the load.
bool UndoLoadState();
/// Loads state from the specified filename. /// Loads state from the specified filename.
bool LoadState(const char* filename); bool LoadState(const char* filename);
@ -352,6 +358,9 @@ protected:
/// Executes per-frame tasks such as controller polling. /// Executes per-frame tasks such as controller polling.
virtual void PollAndUpdate(); virtual void PollAndUpdate();
/// Saves the undo load state, so it can be restored.
bool SaveUndoLoadState();
virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override; virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
virtual s32 GetAudioOutputVolume() const override; virtual s32 GetAudioOutputVolume() const override;
virtual void UpdateControllerInterface(); virtual void UpdateControllerInterface();
@ -548,6 +557,9 @@ private:
}; };
std::vector<ControllerAutoFireState> m_controller_autofires; std::vector<ControllerAutoFireState> m_controller_autofires;
// temporary save state, created when loading, used to undo load state
std::unique_ptr<ByteStream> m_undo_load_state;
#ifdef WITH_DISCORD_PRESENCE #ifdef WITH_DISCORD_PRESENCE
// discord rich presence // discord rich presence
bool m_discord_presence_enabled = false; bool m_discord_presence_enabled = false;