diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index c4257a013..9b3b89a1b 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -746,7 +746,7 @@ bool CommonHostInterface::SaveUndoLoadState() 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)) + if (!System::SaveState(m_undo_load_state.get())) { AddOSDMessage(TranslateStdString("OSDMessage", "Failed to save undo load state."), 15.0f); m_undo_load_state.reset(); @@ -2836,30 +2836,13 @@ std::optional CommonHostInterface::GetSaveSt } std::optional -CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot) +CommonHostInterface::GetExtendedSaveStateInfo(ByteStream* stream) { - const bool global = (!game_code || game_code[0] == 0); - std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot); - - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(path.c_str(), &sd)) - return std::nullopt; - - std::unique_ptr stream = - FileSystem::OpenFile(path.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE); - if (!stream) - return std::nullopt; - SAVE_STATE_HEADER header; if (!stream->Read(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC) return std::nullopt; ExtendedSaveStateInfo ssi; - ssi.path = std::move(path); - ssi.timestamp = sd.ModificationTime.AsUnixTimestamp(); - ssi.slot = slot; - ssi.global = global; - if (header.version < SAVE_STATE_MINIMUM_VERSION || header.version > SAVE_STATE_VERSION) { ssi.title = StringUtil::StdStringFromFormat( @@ -2902,6 +2885,53 @@ CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot) return ssi; } +std::optional +CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot) +{ + const bool global = (!game_code || game_code[0] == 0); + std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot); + + FILESYSTEM_STAT_DATA sd; + if (!FileSystem::StatFile(path.c_str(), &sd)) + return std::nullopt; + + std::unique_ptr stream = + FileSystem::OpenFile(path.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE); + if (!stream) + return std::nullopt; + + std::optional ssi = GetExtendedSaveStateInfo(stream.get()); + if (!ssi) + return std::nullopt; + + ssi->path = std::move(path); + ssi->timestamp = sd.ModificationTime.AsUnixTimestamp(); + ssi->slot = slot; + ssi->global = global; + + return ssi; +} + +std::optional CommonHostInterface::GetUndoSaveStateInfo() +{ + std::optional ssi; + if (m_undo_load_state) + { + m_undo_load_state->SeekAbsolute(0); + ssi = GetExtendedSaveStateInfo(m_undo_load_state.get()); + m_undo_load_state->SeekAbsolute(0); + + if (ssi) + { + ssi->timestamp = 0; + ssi->slot = 0; + ssi->global = false; + } + } + + return ssi; +} + void CommonHostInterface::DeleteSaveStates(const char* game_code, bool resume) { const std::vector states(GetAvailableSaveStates(game_code)); diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h index 328310826..970d22cef 100644 --- a/src/frontend-common/common_host_interface.h +++ b/src/frontend-common/common_host_interface.h @@ -209,9 +209,15 @@ public: /// Returns save state info if present. If game_code is null or empty, assumes global state. std::optional GetSaveStateInfo(const char* game_code, s32 slot); + /// Returns save state info from opened save state stream. + std::optional GetExtendedSaveStateInfo(ByteStream* stream); + /// Returns save state info if present. If game_code is null or empty, assumes global state. std::optional GetExtendedSaveStateInfo(const char* game_code, s32 slot); + /// Returns save state info for the undo slot, if present. + std::optional GetUndoSaveStateInfo(); + /// Deletes save states for the specified game code. If resume is set, the resume state is deleted too. void DeleteSaveStates(const char* game_code, bool resume); diff --git a/src/frontend-common/fullscreen_ui.cpp b/src/frontend-common/fullscreen_ui.cpp index 61ed1fbe4..95161d061 100644 --- a/src/frontend-common/fullscreen_ui.cpp +++ b/src/frontend-common/fullscreen_ui.cpp @@ -2785,6 +2785,7 @@ void InitializeSaveStateListEntry(SaveStateListEntry* li, CommonHostInterface::E li->summary = StringUtil::StdStringFromFormat("%s - Saved %s", ssi->game_code.c_str(), Timestamp::FromUnixTimestamp(ssi->timestamp).ToString("%c").GetCharArray()); + li->slot = ssi->slot; li->global = ssi->global; li->path = std::move(ssi->path); @@ -2812,6 +2813,19 @@ void PopulateSaveStateListEntries() { s_save_state_selector_slots.clear(); + if (s_save_state_selector_loading) + { + std::optional ssi = s_host_interface->GetUndoSaveStateInfo(); + if (ssi) + { + SaveStateListEntry li; + InitializeSaveStateListEntry(&li, &ssi.value()); + li.title = "Undo Load State"; + li.summary = "Restores the state of the system prior to the last state loaded."; + s_save_state_selector_slots.push_back(std::move(li)); + } + } + if (!System::GetRunningCode().empty()) { for (s32 i = 1; i <= CommonHostInterface::PER_GAME_SAVE_STATE_SLOTS; i++) @@ -3025,7 +3039,11 @@ void DrawSaveStateSelector(bool is_loading, bool fullscreen) { const std::string& path = entry.path; s_host_interface->RunLater([path]() { - s_host_interface->LoadState(path.c_str()); + if (path.empty()) + s_host_interface->UndoLoadState(); + else + s_host_interface->LoadState(path.c_str()); + CloseSaveStateSelector(); }); }