diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 7d593a0ad..9c01df79e 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -432,7 +432,7 @@ bool HostInterface::LoadState(const char* filename) return true; } -bool HostInterface::LoadState(bool global, u32 slot) +bool HostInterface::LoadState(bool global, s32 slot) { if (!global && (!m_system || m_system->GetRunningCode().empty())) { @@ -468,7 +468,7 @@ bool HostInterface::SaveState(const char* filename) return result; } -bool HostInterface::SaveState(bool global, u32 slot) +bool HostInterface::SaveState(bool global, s32 slot) { const std::string& code = m_system->GetRunningCode(); if (!global && code.empty()) @@ -481,6 +481,48 @@ bool HostInterface::SaveState(bool global, u32 slot) return SaveState(save_path.c_str()); } +bool HostInterface::ResumeSystemFromState(const char* filename, bool boot_on_failure) +{ + if (!BootSystemFromFile(filename)) + return false; + + const bool global = m_system->GetRunningCode().empty(); + const std::string path = + global ? GetGlobalSaveStateFileName(-1) : GetGameSaveStateFileName(m_system->GetRunningCode().c_str(), -1); + if (FileSystem::FileExists(path.c_str())) + { + if (!LoadState(path.c_str()) && !boot_on_failure) + { + DestroySystem(); + return false; + } + } + else + { + ReportFormattedError("Resume save state not found for '%s' ('%s').", m_system->GetRunningCode().c_str(), + m_system->GetRunningTitle().c_str()); + if (!boot_on_failure) + { + DestroySystem(); + return false; + } + } + + return true; +} + +bool HostInterface::ResumeSystemFromMostRecentState() +{ + const std::string path = GetMostRecentResumeSaveStatePath(); + if (path.empty()) + { + ReportError("No resume save state found."); + return false; + } + + return LoadState(path.c_str()); +} + void HostInterface::UpdateSpeedLimiterState() { m_speed_limiter_enabled = m_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled; @@ -660,7 +702,7 @@ std::vector HostInterface::GetAvailableSaveStates( std::vector si; std::string path; - auto add_path = [&si](std::string path, s32 slot, bool global) { + auto add_path = [&si](const std::string& path, s32 slot, bool global) { FILESYSTEM_STAT_DATA sd; if (!FileSystem::StatFile(path.c_str(), &sd)) return; @@ -670,6 +712,7 @@ std::vector HostInterface::GetAvailableSaveStates( if (game_code && std::strlen(game_code) > 0) { + add_path(GetGameSaveStateFileName(game_code, -1), -1, false); for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++) add_path(GetGameSaveStateFileName(game_code, i), i, false); } @@ -680,6 +723,26 @@ std::vector HostInterface::GetAvailableSaveStates( return si; } +std::string HostInterface::GetMostRecentResumeSaveStatePath() const +{ + std::vector files; + if (!FileSystem::FindFiles(GetUserDirectoryRelativePath("savestates").c_str(), "*resume.sav", FILESYSTEM_FIND_FILES, + &files) || + files.empty()) + { + return {}; + } + + FILESYSTEM_FIND_DATA* most_recent = &files[0]; + for (FILESYSTEM_FIND_DATA& file : files) + { + if (file.ModificationTime > most_recent->ModificationTime) + most_recent = &file; + } + + return std::move(most_recent->FileName); +} + void HostInterface::SetDefaultSettings() { m_settings.region = ConsoleRegion::Auto; @@ -856,3 +919,12 @@ void HostInterface::SetTimerResolutionIncreased(bool enabled) timeEndPeriod(1); #endif } + +bool HostInterface::SaveResumeSaveState() +{ + if (!m_system) + return false; + + const bool global = m_system->GetRunningCode().empty(); + return SaveState(global, -1); +} diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 217cd9b8e..4ca44b5ec 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -46,13 +46,23 @@ public: void DestroySystem(); /// Loads the current emulation state from file. Specifying a slot of -1 loads the "resume" game state. - bool LoadState(bool global, u32 slot); + bool LoadState(bool global, s32 slot); bool LoadState(const char* filename); /// Saves the current emulation state to a file. Specifying a slot of -1 saves the "resume" save state. - bool SaveState(bool global, u32 slot); + bool SaveState(bool global, s32 slot); bool SaveState(const char* filename); + /// Loads the resume save state for the given game. Optionally boots the game anyway if loading fails. + bool ResumeSystemFromState(const char* filename, bool boot_on_failure); + + /// Loads the most recent resume save state. This may be global or per-game. + bool ResumeSystemFromMostRecentState(); + + /// Saves the resume save state, call when shutting down. Not called automatically on DestroySystem() since that can + /// be called as a result of an error. + bool SaveResumeSaveState(); + virtual void ReportError(const char* message); virtual void ReportMessage(const char* message); @@ -135,6 +145,9 @@ protected: /// Returns a list of save states for the specified game code. std::vector GetAvailableSaveStates(const char* game_code) const; + /// Returns the most recent resume save state. + std::string GetMostRecentResumeSaveStatePath() const; + /// Loads the BIOS image for the specified region. std::optional> GetBIOSImage(ConsoleRegion region);