mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-13 23:07:30 +00:00
Qt: Backport undo load state timestamps
This commit is contained in:
parent
29e55a2e5b
commit
cb2dfabeeb
@ -137,6 +137,11 @@ struct SaveStateBuffer
|
|||||||
size_t state_size;
|
size_t state_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UndoSaveStateBuffer : public SaveStateBuffer
|
||||||
|
{
|
||||||
|
time_t timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static void CheckCacheLineSize();
|
static void CheckCacheLineSize();
|
||||||
@ -318,7 +323,7 @@ struct ALIGN_TO_CACHE_LINE StateVars
|
|||||||
Threading::ThreadHandle cpu_thread_handle;
|
Threading::ThreadHandle cpu_thread_handle;
|
||||||
|
|
||||||
// temporary save state, created when loading, used to undo load state
|
// temporary save state, created when loading, used to undo load state
|
||||||
std::optional<System::SaveStateBuffer> undo_load_state;
|
std::optional<UndoSaveStateBuffer> undo_load_state;
|
||||||
|
|
||||||
// Used to track play time. We use a monotonic timer here, in case of clock changes.
|
// Used to track play time. We use a monotonic timer here, in case of clock changes.
|
||||||
u64 session_start_time = 0;
|
u64 session_start_time = 0;
|
||||||
@ -2024,7 +2029,7 @@ void System::ClearRunningGame()
|
|||||||
s_state.running_game_entry = nullptr;
|
s_state.running_game_entry = nullptr;
|
||||||
s_state.running_game_hash = 0;
|
s_state.running_game_hash = 0;
|
||||||
|
|
||||||
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
|
Host::OnSystemGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
|
||||||
s_state.running_game_hash);
|
s_state.running_game_hash);
|
||||||
|
|
||||||
UpdateRichPresence(true);
|
UpdateRichPresence(true);
|
||||||
@ -4212,7 +4217,7 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
|
|||||||
FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
|
FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
|
||||||
s_state.running_game_hash);
|
s_state.running_game_hash);
|
||||||
|
|
||||||
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
|
Host::OnSystemGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
|
||||||
s_state.running_game_hash);
|
s_state.running_game_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5282,12 +5287,14 @@ bool System::UndoLoadState()
|
|||||||
Host::ReportErrorAsync("Error",
|
Host::ReportErrorAsync("Error",
|
||||||
fmt::format("Failed to load undo state, resetting system:\n", error.GetDescription()));
|
fmt::format("Failed to load undo state, resetting system:\n", error.GetDescription()));
|
||||||
s_state.undo_load_state.reset();
|
s_state.undo_load_state.reset();
|
||||||
|
Host::OnSystemUndoStateAvailabilityChanged(false, 0);
|
||||||
ResetSystem();
|
ResetSystem();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO_LOG("Loaded undo save state.");
|
INFO_LOG("Loaded undo save state.");
|
||||||
s_state.undo_load_state.reset();
|
s_state.undo_load_state.reset();
|
||||||
|
Host::OnSystemUndoStateAvailabilityChanged(false, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5303,10 +5310,13 @@ bool System::SaveUndoLoadState()
|
|||||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save undo load state:\n{}"), error.GetDescription()),
|
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save undo load state:\n{}"), error.GetDescription()),
|
||||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||||
s_state.undo_load_state.reset();
|
s_state.undo_load_state.reset();
|
||||||
|
Host::OnSystemUndoStateAvailabilityChanged(false, 0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO_LOG("Saved undo load state: {} bytes", s_state.undo_load_state->state_size);
|
INFO_LOG("Saved undo load state: {} bytes", s_state.undo_load_state->state_size);
|
||||||
|
s_state.undo_load_state->timestamp = std::time(nullptr);
|
||||||
|
Host::OnSystemUndoStateAvailabilityChanged(true, static_cast<u64>(s_state.undo_load_state->timestamp));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,8 +115,11 @@ void OnSystemAbnormalShutdown(const std::string_view reason);
|
|||||||
void OnPerformanceCountersUpdated(const GPUBackend* gpu_backend);
|
void OnPerformanceCountersUpdated(const GPUBackend* gpu_backend);
|
||||||
|
|
||||||
/// Provided by the host; called when the running executable changes.
|
/// Provided by the host; called when the running executable changes.
|
||||||
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name,
|
void OnSystemGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name,
|
||||||
GameHash game_hash);
|
GameHash game_hash);
|
||||||
|
|
||||||
|
/// Provided by the host; called when the undo save state availability changes.
|
||||||
|
void OnSystemUndoStateAvailabilityChanged(bool available, u64 timestamp);
|
||||||
|
|
||||||
/// Called when media capture starts/stops.
|
/// Called when media capture starts/stops.
|
||||||
void OnMediaCaptureStarted();
|
void OnMediaCaptureStarted();
|
||||||
|
@ -1185,8 +1185,8 @@ void MiniHost::WarnAboutInterface()
|
|||||||
Host::AddIconOSDWarning("MiniWarning", ICON_EMOJI_WARNING, message, Host::OSD_INFO_DURATION);
|
Host::AddIconOSDWarning("MiniWarning", ICON_EMOJI_WARNING, message, Host::OSD_INFO_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name,
|
void Host::OnSystemGameChanged(const std::string& disc_path, const std::string& game_serial,
|
||||||
GameHash game_hash)
|
const std::string& game_name, GameHash game_hash)
|
||||||
{
|
{
|
||||||
using namespace MiniHost;
|
using namespace MiniHost;
|
||||||
|
|
||||||
@ -1195,6 +1195,11 @@ void Host::OnGameChanged(const std::string& disc_path, const std::string& game_s
|
|||||||
SDL_SetWindowTitle(s_state.sdl_window, GetWindowTitle(game_name).c_str());
|
SDL_SetWindowTitle(s_state.sdl_window, GetWindowTitle(game_name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host::OnSystemUndoStateAvailabilityChanged(bool available, u64 timestamp)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */)
|
void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */)
|
||||||
{
|
{
|
||||||
using namespace MiniHost;
|
using namespace MiniHost;
|
||||||
|
@ -113,6 +113,7 @@ static QString s_current_game_title;
|
|||||||
static QString s_current_game_serial;
|
static QString s_current_game_serial;
|
||||||
static QString s_current_game_path;
|
static QString s_current_game_path;
|
||||||
static QIcon s_current_game_icon;
|
static QIcon s_current_game_icon;
|
||||||
|
static std::optional<std::time_t> s_undo_state_timestamp;
|
||||||
|
|
||||||
bool QtHost::IsSystemPaused()
|
bool QtHost::IsSystemPaused()
|
||||||
{
|
{
|
||||||
@ -576,6 +577,7 @@ void MainWindow::onSystemDestroyed()
|
|||||||
s_system_starting = false;
|
s_system_starting = false;
|
||||||
s_system_valid = false;
|
s_system_valid = false;
|
||||||
s_system_paused = false;
|
s_system_paused = false;
|
||||||
|
s_undo_state_timestamp.reset();
|
||||||
|
|
||||||
// If we're closing or in batch mode, quit the whole application now.
|
// If we're closing or in batch mode, quit the whole application now.
|
||||||
if (m_is_closing || QtHost::InBatchMode())
|
if (m_is_closing || QtHost::InBatchMode())
|
||||||
@ -593,7 +595,7 @@ void MainWindow::onSystemDestroyed()
|
|||||||
switchToGameListView();
|
switchToGameListView();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title)
|
void MainWindow::onSystemGameChanged(const QString& filename, const QString& game_serial, const QString& game_title)
|
||||||
{
|
{
|
||||||
s_current_game_path = filename;
|
s_current_game_path = filename;
|
||||||
s_current_game_title = game_title;
|
s_current_game_title = game_title;
|
||||||
@ -603,6 +605,14 @@ void MainWindow::onRunningGameChanged(const QString& filename, const QString& ga
|
|||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onSystemUndoStateAvailabilityChanged(bool available, quint64 timestamp)
|
||||||
|
{
|
||||||
|
if (!available)
|
||||||
|
s_undo_state_timestamp.reset();
|
||||||
|
else
|
||||||
|
s_undo_state_timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onMediaCaptureStarted()
|
void MainWindow::onMediaCaptureStarted()
|
||||||
{
|
{
|
||||||
QSignalBlocker sb(m_ui.actionMediaCapture);
|
QSignalBlocker sb(m_ui.actionMediaCapture);
|
||||||
@ -891,7 +901,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString FormatTimestampForSaveStateMenu(u64 timestamp)
|
QString MainWindow::formatTimestampForSaveStateMenu(u64 timestamp)
|
||||||
{
|
{
|
||||||
const QDateTime qtime(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(timestamp)));
|
const QDateTime qtime(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(timestamp)));
|
||||||
return qtime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
return qtime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
||||||
@ -904,7 +914,7 @@ void MainWindow::populateLoadStateMenu(std::string_view game_serial, QMenu* menu
|
|||||||
std::optional<SaveStateInfo> ssi = System::GetSaveStateInfo(serial, slot);
|
std::optional<SaveStateInfo> ssi = System::GetSaveStateInfo(serial, slot);
|
||||||
|
|
||||||
const QString menu_title =
|
const QString menu_title =
|
||||||
ssi.has_value() ? title.arg(slot).arg(FormatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot);
|
ssi.has_value() ? title.arg(slot).arg(formatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot);
|
||||||
|
|
||||||
QAction* load_action = menu->addAction(menu_title);
|
QAction* load_action = menu->addAction(menu_title);
|
||||||
load_action->setEnabled(ssi.has_value());
|
load_action->setEnabled(ssi.has_value());
|
||||||
@ -925,8 +935,11 @@ void MainWindow::populateLoadStateMenu(std::string_view game_serial, QMenu* menu
|
|||||||
|
|
||||||
g_emu_thread->loadState(path);
|
g_emu_thread->loadState(path);
|
||||||
});
|
});
|
||||||
QAction* load_from_state = menu->addAction(tr("Undo Load State"));
|
QAction* load_from_state =
|
||||||
load_from_state->setEnabled(System::CanUndoLoadState());
|
menu->addAction(s_undo_state_timestamp.has_value() ?
|
||||||
|
tr("Undo Load State (%1)").arg(formatTimestampForSaveStateMenu(s_undo_state_timestamp.value())) :
|
||||||
|
tr("Undo Load State"));
|
||||||
|
load_from_state->setEnabled(s_undo_state_timestamp.has_value());
|
||||||
connect(load_from_state, &QAction::triggered, g_emu_thread, &EmuThread::undoLoadState);
|
connect(load_from_state, &QAction::triggered, g_emu_thread, &EmuThread::undoLoadState);
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
@ -949,7 +962,7 @@ void MainWindow::populateSaveStateMenu(std::string_view game_serial, QMenu* menu
|
|||||||
std::optional<SaveStateInfo> ssi = System::GetSaveStateInfo(serial, slot);
|
std::optional<SaveStateInfo> ssi = System::GetSaveStateInfo(serial, slot);
|
||||||
|
|
||||||
const QString menu_title =
|
const QString menu_title =
|
||||||
ssi.has_value() ? title.arg(slot).arg(FormatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot);
|
ssi.has_value() ? title.arg(slot).arg(formatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot);
|
||||||
|
|
||||||
QAction* save_action = menu->addAction(menu_title);
|
QAction* save_action = menu->addAction(menu_title);
|
||||||
connect(save_action, &QAction::triggered,
|
connect(save_action, &QAction::triggered,
|
||||||
@ -2123,7 +2136,9 @@ void MainWindow::connectSignals()
|
|||||||
connect(g_emu_thread, &EmuThread::systemDestroyed, this, &MainWindow::onSystemDestroyed);
|
connect(g_emu_thread, &EmuThread::systemDestroyed, this, &MainWindow::onSystemDestroyed);
|
||||||
connect(g_emu_thread, &EmuThread::systemPaused, this, &MainWindow::onSystemPaused);
|
connect(g_emu_thread, &EmuThread::systemPaused, this, &MainWindow::onSystemPaused);
|
||||||
connect(g_emu_thread, &EmuThread::systemResumed, this, &MainWindow::onSystemResumed);
|
connect(g_emu_thread, &EmuThread::systemResumed, this, &MainWindow::onSystemResumed);
|
||||||
connect(g_emu_thread, &EmuThread::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
connect(g_emu_thread, &EmuThread::systemGameChanged, this, &MainWindow::onSystemGameChanged);
|
||||||
|
connect(g_emu_thread, &EmuThread::systemUndoStateAvailabilityChanged, this,
|
||||||
|
&MainWindow::onSystemUndoStateAvailabilityChanged);
|
||||||
connect(g_emu_thread, &EmuThread::mediaCaptureStarted, this, &MainWindow::onMediaCaptureStarted);
|
connect(g_emu_thread, &EmuThread::mediaCaptureStarted, this, &MainWindow::onMediaCaptureStarted);
|
||||||
connect(g_emu_thread, &EmuThread::mediaCaptureStopped, this, &MainWindow::onMediaCaptureStopped);
|
connect(g_emu_thread, &EmuThread::mediaCaptureStopped, this, &MainWindow::onMediaCaptureStopped);
|
||||||
connect(g_emu_thread, &EmuThread::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
|
connect(g_emu_thread, &EmuThread::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
|
||||||
|
@ -146,7 +146,8 @@ private Q_SLOTS:
|
|||||||
void onSystemDestroyed();
|
void onSystemDestroyed();
|
||||||
void onSystemPaused();
|
void onSystemPaused();
|
||||||
void onSystemResumed();
|
void onSystemResumed();
|
||||||
void onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
void onSystemGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
||||||
|
void onSystemUndoStateAvailabilityChanged(bool available, quint64 timestamp);
|
||||||
void onMediaCaptureStarted();
|
void onMediaCaptureStarted();
|
||||||
void onMediaCaptureStopped();
|
void onMediaCaptureStopped();
|
||||||
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||||
@ -215,7 +216,6 @@ private Q_SLOTS:
|
|||||||
void onDebugLogChannelsMenuAboutToShow();
|
void onDebugLogChannelsMenuAboutToShow();
|
||||||
void openCPUDebugger();
|
void openCPUDebugger();
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
@ -296,6 +296,8 @@ private:
|
|||||||
void startFileOrChangeDisc(const QString& path);
|
void startFileOrChangeDisc(const QString& path);
|
||||||
void promptForDiscChange(const QString& path);
|
void promptForDiscChange(const QString& path);
|
||||||
|
|
||||||
|
static QString formatTimestampForSaveStateMenu(u64 timestamp);
|
||||||
|
|
||||||
Ui::MainWindow m_ui;
|
Ui::MainWindow m_ui;
|
||||||
|
|
||||||
GameListWidget* m_game_list_widget = nullptr;
|
GameListWidget* m_game_list_widget = nullptr;
|
||||||
|
@ -2517,11 +2517,16 @@ void Host::OnPerformanceCountersUpdated(const GPUBackend* gpu_backend)
|
|||||||
g_emu_thread->updatePerformanceCounters(gpu_backend);
|
g_emu_thread->updatePerformanceCounters(gpu_backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name,
|
void Host::OnSystemGameChanged(const std::string& disc_path, const std::string& game_serial,
|
||||||
GameHash hash)
|
const std::string& game_name, GameHash hash)
|
||||||
{
|
{
|
||||||
emit g_emu_thread->runningGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial),
|
emit g_emu_thread->systemGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial),
|
||||||
QString::fromStdString(game_name));
|
QString::fromStdString(game_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::OnSystemUndoStateAvailabilityChanged(bool available, u64 timestamp)
|
||||||
|
{
|
||||||
|
emit g_emu_thread->systemUndoStateAvailabilityChanged(available, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::OnMediaCaptureStarted()
|
void Host::OnMediaCaptureStarted()
|
||||||
|
@ -140,6 +140,8 @@ Q_SIGNALS:
|
|||||||
void systemDestroyed();
|
void systemDestroyed();
|
||||||
void systemPaused();
|
void systemPaused();
|
||||||
void systemResumed();
|
void systemResumed();
|
||||||
|
void systemGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
||||||
|
void systemUndoStateAvailabilityChanged(bool available, quint64 timestamp);
|
||||||
void gameListRefreshed();
|
void gameListRefreshed();
|
||||||
void gameListRowsChanged(const QList<int>& rows_changed);
|
void gameListRowsChanged(const QList<int>& rows_changed);
|
||||||
std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen,
|
std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen,
|
||||||
@ -148,7 +150,6 @@ Q_SIGNALS:
|
|||||||
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
||||||
void onReleaseRenderWindowRequested();
|
void onReleaseRenderWindowRequested();
|
||||||
void focusDisplayWidgetRequested();
|
void focusDisplayWidgetRequested();
|
||||||
void runningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
|
||||||
void inputProfileLoaded();
|
void inputProfileLoaded();
|
||||||
void mouseModeRequested(bool relative, bool hide_cursor);
|
void mouseModeRequested(bool relative, bool hide_cursor);
|
||||||
void fullscreenUIStartedOrStopped(bool running);
|
void fullscreenUIStartedOrStopped(bool running);
|
||||||
|
@ -316,14 +316,19 @@ void Host::OnPerformanceCountersUpdated(const GPUBackend* gpu_backend)
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name,
|
void Host::OnSystemGameChanged(const std::string& disc_path, const std::string& game_serial,
|
||||||
GameHash hash)
|
const std::string& game_name, GameHash hash)
|
||||||
{
|
{
|
||||||
INFO_LOG("Disc Path: {}", disc_path);
|
INFO_LOG("Disc Path: {}", disc_path);
|
||||||
INFO_LOG("Game Serial: {}", game_serial);
|
INFO_LOG("Game Serial: {}", game_serial);
|
||||||
INFO_LOG("Game Name: {}", game_name);
|
INFO_LOG("Game Name: {}", game_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host::OnSystemUndoStateAvailabilityChanged(bool available, u64 timestamp)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
void Host::OnMediaCaptureStarted()
|
void Host::OnMediaCaptureStarted()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@ -890,7 +895,7 @@ bool RegTestHost::ParseCommandLineParameters(int argc, char* argv[], std::option
|
|||||||
|
|
||||||
INFO_LOG("Setting CPU execution mode to {}.", Settings::GetCPUExecutionModeName(cpu.value()));
|
INFO_LOG("Setting CPU execution mode to {}.", Settings::GetCPUExecutionModeName(cpu.value()));
|
||||||
s_base_settings_interface.SetStringValue("CPU", "ExecutionMode",
|
s_base_settings_interface.SetStringValue("CPU", "ExecutionMode",
|
||||||
Settings::GetCPUExecutionModeName(cpu.value()));
|
Settings::GetCPUExecutionModeName(cpu.value()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (CHECK_ARG("-pgxp"))
|
else if (CHECK_ARG("-pgxp"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user