mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #6419 from vitor-k/states
Update zstd and improve savestates logging
This commit is contained in:
		
						commit
						5f81940e63
					
				
					 4 changed files with 49 additions and 35 deletions
				
			
		
							
								
								
									
										2
									
								
								externals/zstd
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/zstd
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | ||||||
| Subproject commit e47e674cd09583ff0503f0f6defd6d23d8b718d3 | Subproject commit 63779c798237346c2b245c546c40b72a5a5913fe | ||||||
|  | @ -113,9 +113,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { | ||||||
|     case Signal::Shutdown: |     case Signal::Shutdown: | ||||||
|         return ResultStatus::ShutdownRequested; |         return ResultStatus::ShutdownRequested; | ||||||
|     case Signal::Load: { |     case Signal::Load: { | ||||||
|         LOG_INFO(Core, "Begin load"); |         const u32 slot = param; | ||||||
|  |         LOG_INFO(Core, "Begin load of slot {}", slot); | ||||||
|         try { |         try { | ||||||
|             System::LoadState(param); |             System::LoadState(slot); | ||||||
|             LOG_INFO(Core, "Load completed"); |             LOG_INFO(Core, "Load completed"); | ||||||
|         } catch (const std::exception& e) { |         } catch (const std::exception& e) { | ||||||
|             LOG_ERROR(Core, "Error loading: {}", e.what()); |             LOG_ERROR(Core, "Error loading: {}", e.what()); | ||||||
|  | @ -126,9 +127,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { | ||||||
|         return ResultStatus::Success; |         return ResultStatus::Success; | ||||||
|     } |     } | ||||||
|     case Signal::Save: { |     case Signal::Save: { | ||||||
|         LOG_INFO(Core, "Begin save"); |         const u32 slot = param; | ||||||
|  |         LOG_INFO(Core, "Begin save to slot {}", slot); | ||||||
|         try { |         try { | ||||||
|             System::SaveState(param); |             System::SaveState(slot); | ||||||
|             LOG_INFO(Core, "Save completed"); |             LOG_INFO(Core, "Save completed"); | ||||||
|         } catch (const std::exception& e) { |         } catch (const std::exception& e) { | ||||||
|             LOG_ERROR(Core, "Error saving: {}", e.what()); |             LOG_ERROR(Core, "Error saving: {}", e.what()); | ||||||
|  |  | ||||||
|  | @ -3,18 +3,15 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <boost/serialization/binary_object.hpp> |  | ||||||
| #include <cryptopp/hex.h> | #include <cryptopp/hex.h> | ||||||
| #include "common/archives.h" | #include "common/archives.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/zstd_compression.h" | #include "common/zstd_compression.h" | ||||||
| #include "core/cheats/cheats.h" |  | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/movie.h" | #include "core/movie.h" | ||||||
| #include "core/savestate.h" | #include "core/savestate.h" | ||||||
| #include "network/network.h" | #include "network/network.h" | ||||||
| #include "video_core/video_core.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| 
 | 
 | ||||||
|  | @ -25,19 +22,14 @@ struct CSTHeader { | ||||||
|     std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
 |     std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
 | ||||||
|     u64_le time;                 /// The time when this save state was created
 |     u64_le time;                 /// The time when this save state was created
 | ||||||
| 
 | 
 | ||||||
|     std::array<u8, 216> reserved; /// Make heading 256 bytes so it has consistent size
 |     std::array<u8, 216> reserved{}; /// Make heading 256 bytes so it has consistent size
 | ||||||
| 
 |  | ||||||
|     template <class Archive> |  | ||||||
|     void serialize(Archive& ar, const unsigned int) { |  | ||||||
|         ar& boost::serialization::binary_object(this, sizeof(CSTHeader)); |  | ||||||
|     } |  | ||||||
| }; | }; | ||||||
| static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes"); | static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes"); | ||||||
| #pragma pack(pop) | #pragma pack(pop) | ||||||
| 
 | 
 | ||||||
| constexpr std::array<u8, 4> header_magic_bytes{{'C', 'S', 'T', 0x1B}}; | constexpr std::array<u8, 4> 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(); |     const u64 movie_id = Movie::GetInstance().GetCurrentMovieID(); | ||||||
|     if (movie_id) { |     if (movie_id) { | ||||||
|         return fmt::format("{}{:016X}.movie{:016X}.{:02d}.cst", |         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<SaveStateInfo> ListSaveStates(u64 program_id) { | std::vector<SaveStateInfo> ListSaveStates(u64 program_id) { | ||||||
|     std::vector<SaveStateInfo> result; |     std::vector<SaveStateInfo> result; | ||||||
|     for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) { |     for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) { | ||||||
|  | @ -74,24 +90,10 @@ std::vector<SaveStateInfo> ListSaveStates(u64 program_id) { | ||||||
|             LOG_ERROR(Core, "Could not read from file {}", path); |             LOG_ERROR(Core, "Could not read from file {}", path); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         if (header.filetype != header_magic_bytes) { |         if (!ValidateSaveState(header, info, program_id, slot)) { | ||||||
|             LOG_WARNING(Core, "Invalid save state file {}", path); |  | ||||||
|             continue; |             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)); |         result.emplace_back(std::move(info)); | ||||||
|     } |     } | ||||||
|     return result; |     return result; | ||||||
|  | @ -121,7 +123,7 @@ void System::SaveState(u32 slot) const { | ||||||
|     header.filetype = header_magic_bytes; |     header.filetype = header_magic_bytes; | ||||||
|     header.program_id = title_id; |     header.program_id = title_id; | ||||||
|     std::string rev_bytes; |     std::string rev_bytes; | ||||||
|     CryptoPP::StringSource(Common::g_scm_rev, true, |     CryptoPP::StringSource ss(Common::g_scm_rev, true, | ||||||
|                               new CryptoPP::HexDecoder(new CryptoPP::StringSink(rev_bytes))); |                               new CryptoPP::HexDecoder(new CryptoPP::StringSink(rev_bytes))); | ||||||
|     std::memcpy(header.revision.data(), rev_bytes.data(), sizeof(header.revision)); |     std::memcpy(header.revision.data(), rev_bytes.data(), sizeof(header.revision)); | ||||||
|     header.time = std::chrono::duration_cast<std::chrono::seconds>( |     header.time = std::chrono::duration_cast<std::chrono::seconds>( | ||||||
|  | @ -146,7 +148,19 @@ void System::LoadState(u32 slot) { | ||||||
|         std::vector<u8> buffer(FileUtil::GetSize(path) - sizeof(CSTHeader)); |         std::vector<u8> buffer(FileUtil::GetSize(path) - sizeof(CSTHeader)); | ||||||
| 
 | 
 | ||||||
|         FileUtil::IOFile file(path, "rb"); |         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()) { |         if (file.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { | ||||||
|             throw std::runtime_error("Could not read from file at " + path); |             throw std::runtime_error("Could not read from file at " + path); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -9,8 +9,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| 
 | 
 | ||||||
| struct CSTHeader; |  | ||||||
| 
 |  | ||||||
| struct SaveStateInfo { | struct SaveStateInfo { | ||||||
|     u32 slot; |     u32 slot; | ||||||
|     u64 time; |     u64 time; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue