diff --git a/src/core/system.cpp b/src/core/system.cpp index e1cc4a0cf..0047f6f0e 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -195,9 +195,6 @@ static void UpdatePerGameMemoryCards(); static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType type); static void UpdateMultitaps(); -/// Returns the maximum size of a save state, considering the current configuration. -static size_t GetMaxSaveStateSize(); - static std::string GetMediaPathFromSaveState(const char* path); static bool SaveUndoLoadState(); static void UpdateMemorySaveStateSettings(); @@ -2929,7 +2926,18 @@ bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bo // Updating game/loading settings can turn on hardcore mode. Catch this. Achievements::DisableHardcoreMode(); - StateWrapper sw(buffer.state_data.cspan(0, buffer.state_size), StateWrapper::Mode::Read, buffer.version); + return LoadStateDataFromBuffer(buffer.state_data.cspan(0, buffer.state_size), buffer.version, error, update_display); +} + +bool System::LoadStateDataFromBuffer(std::span data, u32 version, Error* error, bool update_display) +{ + if (IsShutdown()) [[unlikely]] + { + Error::SetStringView(error, "System is invalid."); + return 0; + } + + StateWrapper sw(data, StateWrapper::Mode::Read, version); if (!DoState(sw, update_display)) { Error::SetStringView(error, "Save state stream is corrupted."); @@ -3197,12 +3205,6 @@ bool System::SaveState(std::string path, Error* error, bool backup_existing_save bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size /* = 256 */) { - if (IsShutdown()) [[unlikely]] - { - Error::SetStringView(error, "System is invalid."); - return 0; - } - buffer->title = s_state.running_game_title; buffer->serial = s_state.running_game_serial; buffer->version = SAVE_STATE_VERSION; @@ -3250,14 +3252,25 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen if (buffer->state_data.empty()) buffer->state_data.resize(GetMaxSaveStateSize()); - StateWrapper sw(buffer->state_data.span(), StateWrapper::Mode::Write, SAVE_STATE_VERSION); + return SaveStateDataToBuffer(buffer->state_data, &buffer->state_size, error); +} + +bool System::SaveStateDataToBuffer(std::span data, size_t* data_size, Error* error) +{ + if (IsShutdown()) [[unlikely]] + { + Error::SetStringView(error, "System is invalid."); + return 0; + } + + StateWrapper sw(data, StateWrapper::Mode::Write, SAVE_STATE_VERSION); if (!DoState(sw, false)) { Error::SetStringView(error, "DoState() failed"); return false; } - buffer->state_size = sw.GetPosition(); + *data_size = sw.GetPosition(); return true; } diff --git a/src/core/system.h b/src/core/system.h index 50e9e31e0..e41a78f82 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -260,11 +260,18 @@ bool BootSystem(SystemBootParameters parameters, Error* error); void PauseSystem(bool paused); void ResetSystem(); +/// Returns the maximum size of a save state, considering the current configuration. +size_t GetMaxSaveStateSize(); + /// Loads state from the specified path. bool LoadState(const char* path, Error* error, bool save_undo_state); bool SaveState(std::string path, Error* error, bool backup_existing_save, bool ignore_memcard_busy); bool SaveResumeState(Error* error); +/// State data access, use with care as the media path is not updated. +bool LoadStateDataFromBuffer(std::span data, u32 version, Error* error, bool update_display); +bool SaveStateDataToBuffer(std::span data, size_t* data_size, Error* error); + /// Runs the VM until the CPU execution is canceled. void Execute(); diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index 02c0feb6a..034728929 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -2,12 +2,15 @@ // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "core/achievements.h" +#include "core/bus.h" #include "core/controller.h" #include "core/fullscreen_ui.h" #include "core/game_list.h" +#include "core/gpu.h" #include "core/gpu_backend.h" #include "core/gpu_thread.h" #include "core/host.h" +#include "core/spu.h" #include "core/system.h" #include "core/system_private.h" @@ -27,6 +30,7 @@ #include "common/log.h" #include "common/memory_settings_interface.h" #include "common/path.h" +#include "common/sha256_digest.h" #include "common/string_util.h" #include "common/timer.h" @@ -38,6 +42,7 @@ LOG_CHANNEL(Host); namespace RegTestHost { + static bool ParseCommandLineParameters(int argc, char* argv[], std::optional& autoboot); static void PrintCommandLineVersion(); static void PrintCommandLineHelp(const char* progname); @@ -46,8 +51,10 @@ static void InitializeEarlyConsole(); static void HookSignals(); static bool SetFolders(); static bool SetNewDataRoot(const std::string& filename); +static void DumpSystemStateHashes(); static std::string GetFrameDumpFilename(u32 frame); static void GPUThreadEntryPoint(); + } // namespace RegTestHost static std::unique_ptr s_base_settings_interface; @@ -320,7 +327,10 @@ void Host::PumpMessagesOnCPUThread() { s_frames_remaining--; if (s_frames_remaining == 0) + { + RegTestHost::DumpSystemStateHashes(); System::ShutdownSystem(false); + } } void Host::RunOnCPUThread(std::function function, bool block /* = false */) @@ -528,6 +538,32 @@ void RegTestHost::GPUThreadEntryPoint() GPUThread::Internal::GPUThreadEntryPoint(); } +void RegTestHost::DumpSystemStateHashes() +{ + Error error; + + // don't save full state on gpu dump, it's not going to be complete... + if (!System::IsReplayingGPUDump()) + { + DynamicHeapArray state_data(System::GetMaxSaveStateSize()); + size_t state_data_size; + if (!System::SaveStateDataToBuffer(state_data, &state_data_size, &error)) + { + ERROR_LOG("Failed to save system state: {}", error.GetDescription()); + return; + } + + INFO_LOG("Save State Hash: {}", + SHA256Digest::DigestToString(SHA256Digest::GetDigest(state_data.cspan(0, state_data_size)))); + INFO_LOG("RAM Hash: {}", + SHA256Digest::DigestToString(SHA256Digest::GetDigest(std::span(Bus::g_ram, Bus::g_ram_size)))); + INFO_LOG("SPU RAM Hash: {}", SHA256Digest::DigestToString(SHA256Digest::GetDigest(SPU::GetRAM()))); + } + + INFO_LOG("VRAM Hash: {}", SHA256Digest::DigestToString(SHA256Digest::GetDigest( + std::span(reinterpret_cast(g_vram), VRAM_SIZE)))); +} + void RegTestHost::InitializeEarlyConsole() { const bool was_console_enabled = Log::IsConsoleOutputEnabled();