diff --git a/externals/zstd b/externals/zstd index e47e674cd0..63779c7982 160000 --- a/externals/zstd +++ b/externals/zstd @@ -1 +1 @@ -Subproject commit e47e674cd09583ff0503f0f6defd6d23d8b718d3 +Subproject commit 63779c798237346c2b245c546c40b72a5a5913fe diff --git a/src/core/core.cpp b/src/core/core.cpp index 4151cb4c6f..97650b620b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -113,9 +113,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { case Signal::Shutdown: return ResultStatus::ShutdownRequested; case Signal::Load: { - LOG_INFO(Core, "Begin load"); + const u32 slot = param; + LOG_INFO(Core, "Begin load of slot {}", slot); try { - System::LoadState(param); + System::LoadState(slot); LOG_INFO(Core, "Load completed"); } catch (const std::exception& e) { LOG_ERROR(Core, "Error loading: {}", e.what()); @@ -126,9 +127,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { return ResultStatus::Success; } case Signal::Save: { - LOG_INFO(Core, "Begin save"); + const u32 slot = param; + LOG_INFO(Core, "Begin save to slot {}", slot); try { - System::SaveState(param); + System::SaveState(slot); LOG_INFO(Core, "Save completed"); } catch (const std::exception& e) { LOG_ERROR(Core, "Error saving: {}", e.what()); diff --git a/src/core/savestate.cpp b/src/core/savestate.cpp index 1ab2455708..efbbe3eb46 100644 --- a/src/core/savestate.cpp +++ b/src/core/savestate.cpp @@ -3,18 +3,15 @@ // Refer to the license.txt file included. #include -#include #include #include "common/archives.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/zstd_compression.h" -#include "core/cheats/cheats.h" #include "core/core.h" #include "core/movie.h" #include "core/savestate.h" #include "network/network.h" -#include "video_core/video_core.h" namespace Core { @@ -25,19 +22,14 @@ struct CSTHeader { std::array revision; /// Git hash of the revision this savestate was created with u64_le time; /// The time when this save state was created - std::array reserved; /// Make heading 256 bytes so it has consistent size - - template - void serialize(Archive& ar, const unsigned int) { - ar& boost::serialization::binary_object(this, sizeof(CSTHeader)); - } + std::array reserved{}; /// Make heading 256 bytes so it has consistent size }; static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes"); #pragma pack(pop) constexpr std::array header_magic_bytes{{'C', 'S', 'T', 0x1B}}; -std::string GetSaveStatePath(u64 program_id, u32 slot) { +static std::string GetSaveStatePath(u64 program_id, u32 slot) { const u64 movie_id = Movie::GetInstance().GetCurrentMovieID(); if (movie_id) { return fmt::format("{}{:016X}.movie{:016X}.{:02d}.cst", @@ -49,6 +41,30 @@ std::string GetSaveStatePath(u64 program_id, u32 slot) { } } +static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64 program_id, + u32 slot) { + const auto path = GetSaveStatePath(program_id, slot); + if (header.filetype != header_magic_bytes) { + LOG_WARNING(Core, "Invalid save state file {}", path); + return false; + } + info.time = header.time; + + if (header.program_id != program_id) { + LOG_WARNING(Core, "Save state file isn't for the current game {}", path); + return false; + } + const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, "")); + if (revision == Common::g_scm_rev) { + info.status = SaveStateInfo::ValidationStatus::OK; + } else { + LOG_WARNING(Core, "Save state file {} created from a different revision {}", path, + revision); + info.status = SaveStateInfo::ValidationStatus::RevisionDismatch; + } + return true; +} + std::vector ListSaveStates(u64 program_id) { std::vector result; for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) { @@ -74,24 +90,10 @@ std::vector ListSaveStates(u64 program_id) { LOG_ERROR(Core, "Could not read from file {}", path); continue; } - if (header.filetype != header_magic_bytes) { - LOG_WARNING(Core, "Invalid save state file {}", path); + if (!ValidateSaveState(header, info, program_id, slot)) { continue; } - info.time = header.time; - if (header.program_id != program_id) { - LOG_WARNING(Core, "Save state file isn't for the current game {}", path); - continue; - } - const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, "")); - if (revision == Common::g_scm_rev) { - info.status = SaveStateInfo::ValidationStatus::OK; - } else { - LOG_WARNING(Core, "Save state file {} created from a different revision {}", path, - revision); - info.status = SaveStateInfo::ValidationStatus::RevisionDismatch; - } result.emplace_back(std::move(info)); } return result; @@ -121,8 +123,8 @@ void System::SaveState(u32 slot) const { header.filetype = header_magic_bytes; header.program_id = title_id; std::string rev_bytes; - CryptoPP::StringSource(Common::g_scm_rev, true, - new CryptoPP::HexDecoder(new CryptoPP::StringSink(rev_bytes))); + CryptoPP::StringSource ss(Common::g_scm_rev, true, + new CryptoPP::HexDecoder(new CryptoPP::StringSink(rev_bytes))); std::memcpy(header.revision.data(), rev_bytes.data(), sizeof(header.revision)); header.time = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) @@ -146,7 +148,19 @@ void System::LoadState(u32 slot) { std::vector buffer(FileUtil::GetSize(path) - sizeof(CSTHeader)); FileUtil::IOFile file(path, "rb"); - file.Seek(sizeof(CSTHeader), SEEK_SET); // Skip header + + // load header + CSTHeader header; + if (file.ReadBytes(&header, sizeof(header)) != sizeof(header)) { + throw std::runtime_error("Could not read from file at " + path); + } + + // validate header + SaveStateInfo info; + if (!ValidateSaveState(header, info, title_id, slot)) { + throw std::runtime_error("Invalid savestate"); + } + if (file.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { throw std::runtime_error("Could not read from file at " + path); } diff --git a/src/core/savestate.h b/src/core/savestate.h index f67bee22f8..2a1d1db524 100644 --- a/src/core/savestate.h +++ b/src/core/savestate.h @@ -9,8 +9,6 @@ namespace Core { -struct CSTHeader; - struct SaveStateInfo { u32 slot; u64 time;