System: Expose memory save states

This commit is contained in:
Stenzek 2023-08-30 17:38:17 +10:00
parent ae9cf5b889
commit 15af10e82a
2 changed files with 99 additions and 92 deletions

View File

@ -92,18 +92,8 @@ SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std
SystemBootParameters::~SystemBootParameters() = default; SystemBootParameters::~SystemBootParameters() = default;
struct MemorySaveState
{
std::unique_ptr<GPUTexture> vram_texture;
std::unique_ptr<GrowableMemoryByteStream> state_stream;
};
namespace System { namespace System {
static std::optional<ExtendedSaveStateInfo> InternalGetExtendedSaveStateInfo(ByteStream* stream); static std::optional<ExtendedSaveStateInfo> InternalGetExtendedSaveStateInfo(ByteStream* stream);
static bool InternalSaveState(ByteStream* state, u32 screenshot_size = 256,
u32 compression_method = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE);
static bool SaveMemoryState(MemorySaveState* mss);
static bool LoadMemoryState(const MemorySaveState& mss);
static bool LoadEXE(const char* filename); static bool LoadEXE(const char* filename);
@ -118,7 +108,6 @@ static void InternalReset();
static void ClearRunningGame(); static void ClearRunningGame();
static void DestroySystem(); static void DestroySystem();
static std::string GetMediaPathFromSaveState(const char* path); static std::string GetMediaPathFromSaveState(const char* path);
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state); static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
static bool CreateGPU(GPURenderer renderer, bool is_switching); static bool CreateGPU(GPURenderer renderer, bool is_switching);
static bool SaveUndoLoadState(); static bool SaveUndoLoadState();
@ -230,14 +219,14 @@ static std::unique_ptr<ByteStream> m_undo_load_state;
static bool s_memory_saves_enabled = false; static bool s_memory_saves_enabled = false;
static std::deque<MemorySaveState> s_rewind_states; static std::deque<System::MemorySaveState> s_rewind_states;
static s32 s_rewind_load_frequency = -1; static s32 s_rewind_load_frequency = -1;
static s32 s_rewind_load_counter = -1; static s32 s_rewind_load_counter = -1;
static s32 s_rewind_save_frequency = -1; static s32 s_rewind_save_frequency = -1;
static s32 s_rewind_save_counter = -1; static s32 s_rewind_save_counter = -1;
static bool s_rewinding_first_save = false; static bool s_rewinding_first_save = false;
static std::deque<MemorySaveState> s_runahead_states; static std::deque<System::MemorySaveState> s_runahead_states;
static bool s_runahead_replay_pending = false; static bool s_runahead_replay_pending = false;
static u32 s_runahead_frames = 0; static u32 s_runahead_frames = 0;
static u32 s_runahead_replay_frames = 0; static u32 s_runahead_replay_frames = 0;
@ -1163,7 +1152,7 @@ bool System::LoadState(const char* filename)
SaveUndoLoadState(); SaveUndoLoadState();
if (!DoLoadState(stream.get(), false, true)) if (!LoadStateFromStream(stream.get(), true))
{ {
Host::ReportFormattedErrorAsync("Load State Error", Host::ReportFormattedErrorAsync("Load State Error",
TRANSLATE("OSDMessage", "Loading state from '%s' failed. Resetting."), filename); TRANSLATE("OSDMessage", "Loading state from '%s' failed. Resetting."), filename);
@ -1201,7 +1190,7 @@ bool System::SaveState(const char* filename, bool backup_existing_save)
Log_InfoPrintf("Saving state to '%s'...", filename); Log_InfoPrintf("Saving state to '%s'...", filename);
const u32 screenshot_size = 256; const u32 screenshot_size = 256;
const bool result = InternalSaveState(stream.get(), screenshot_size, const bool result = SaveStateToStream(stream.get(), screenshot_size,
g_settings.compress_save_states ? SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD : g_settings.compress_save_states ? SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD :
SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE); SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE);
if (!result) if (!result)
@ -1450,7 +1439,7 @@ bool System::BootSystem(SystemBootParameters parameters)
return false; return false;
} }
if (!DoLoadState(stream.get(), false, true)) if (!LoadStateFromStream(stream.get(), true))
{ {
DestroySystem(); DestroySystem();
return false; return false;
@ -1939,7 +1928,7 @@ void System::RecreateSystem()
const bool was_paused = System::IsPaused(); const bool was_paused = System::IsPaused();
std::unique_ptr<ByteStream> stream = ByteStream::CreateGrowableMemoryStream(nullptr, 8 * 1024); std::unique_ptr<ByteStream> stream = ByteStream::CreateGrowableMemoryStream(nullptr, 8 * 1024);
if (!System::InternalSaveState(stream.get(), 0, SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE) || !stream->SeekAbsolute(0)) if (!System::SaveStateToStream(stream.get(), 0, SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE) || !stream->SeekAbsolute(0))
{ {
Host::ReportErrorAsync("Error", "Failed to save state before system recreation. Shutting down."); Host::ReportErrorAsync("Error", "Failed to save state before system recreation. Shutting down.");
DestroySystem(); DestroySystem();
@ -1955,7 +1944,7 @@ void System::RecreateSystem()
return; return;
} }
if (!DoLoadState(stream.get(), false, false)) if (!LoadStateFromStream(stream.get(), false))
{ {
DestroySystem(); DestroySystem();
return; return;
@ -2235,7 +2224,7 @@ std::string System::GetMediaPathFromSaveState(const char* path)
return ret; return ret;
} }
bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool update_display) bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ignore_media)
{ {
Assert(IsValid()); Assert(IsValid());
@ -2262,6 +2251,8 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
return false; return false;
} }
if (!ignore_media)
{
Error error; Error error;
std::string media_filename; std::string media_filename;
std::unique_ptr<CDImage> media; std::unique_ptr<CDImage> media;
@ -2326,8 +2317,6 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
} }
} }
ClearMemorySaveStates();
CDROM::Reset(); CDROM::Reset();
if (media) if (media)
{ {
@ -2344,6 +2333,9 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
// ensure the correct card is loaded // ensure the correct card is loaded
if (g_settings.HasAnyPerGameMemoryCards()) if (g_settings.HasAnyPerGameMemoryCards())
UpdatePerGameMemoryCards(); UpdatePerGameMemoryCards();
}
ClearMemorySaveStates();
#ifdef WITH_CHEEVOS #ifdef WITH_CHEEVOS
// Updating game/loading settings can turn on hardcore mode. Catch this. // Updating game/loading settings can turn on hardcore mode. Catch this.
@ -2385,8 +2377,9 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
return true; return true;
} }
bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 */, bool System::SaveStateToStream(ByteStream* state, u32 screenshot_size /* = 256 */,
u32 compression_method /* = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE*/) u32 compression_method /* = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE*/,
bool ignore_media /* = false*/)
{ {
if (IsShutdown()) if (IsShutdown())
return false; return false;
@ -2403,7 +2396,7 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 *
StringUtil::Strlcpy(header.title, s_running_game_title.c_str(), sizeof(header.title)); StringUtil::Strlcpy(header.title, s_running_game_title.c_str(), sizeof(header.title));
StringUtil::Strlcpy(header.serial, s_running_game_serial.c_str(), sizeof(header.serial)); StringUtil::Strlcpy(header.serial, s_running_game_serial.c_str(), sizeof(header.serial));
if (CDROM::HasMedia()) if (CDROM::HasMedia() && !ignore_media)
{ {
const std::string& media_filename = CDROM::GetMediaFileName(); const std::string& media_filename = CDROM::GetMediaFileName();
header.offset_to_media_filename = static_cast<u32>(state->GetPosition()); header.offset_to_media_filename = static_cast<u32>(state->GetPosition());
@ -4052,7 +4045,7 @@ bool System::UndoLoadState()
Assert(IsValid()); Assert(IsValid());
m_undo_load_state->SeekAbsolute(0); m_undo_load_state->SeekAbsolute(0);
if (!DoLoadState(m_undo_load_state.get(), false, true)) if (!LoadStateFromStream(m_undo_load_state.get(), true))
{ {
Host::ReportErrorAsync("Error", "Failed to load undo state, resetting system."); Host::ReportErrorAsync("Error", "Failed to load undo state, resetting system.");
m_undo_load_state.reset(); m_undo_load_state.reset();
@ -4071,7 +4064,7 @@ bool System::SaveUndoLoadState()
m_undo_load_state.reset(); m_undo_load_state.reset();
m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE); m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE);
if (!InternalSaveState(m_undo_load_state.get())) if (!SaveStateToStream(m_undo_load_state.get()))
{ {
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to save undo load state."), 15.0f); Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to save undo load state."), 15.0f);
m_undo_load_state.reset(); m_undo_load_state.reset();

View File

@ -19,11 +19,13 @@ class Controller;
struct CheatCode; struct CheatCode;
class CheatList; class CheatList;
namespace BIOS class GPUTexture;
{ class GrowableMemoryByteStream;
namespace BIOS {
struct ImageInfo; struct ImageInfo;
struct Hash; struct Hash;
} } // namespace BIOS
namespace GameDatabase namespace GameDatabase
{ {
@ -240,6 +242,18 @@ bool LoadState(const char* filename);
bool SaveState(const char* filename, bool backup_existing_save); bool SaveState(const char* filename, bool backup_existing_save);
bool SaveResumeState(); bool SaveResumeState();
/// Memory save states - only for internal use.
struct MemorySaveState
{
std::unique_ptr<GPUTexture> vram_texture;
std::unique_ptr<GrowableMemoryByteStream> state_stream;
};
bool SaveMemoryState(MemorySaveState* mss);
bool LoadMemoryState(const MemorySaveState& mss);
bool LoadStateFromStream(ByteStream* stream, bool update_display, bool ignore_media = false);
bool SaveStateToStream(ByteStream* state, u32 screenshot_size = 256, u32 compression_method = 0,
bool ignore_media = false);
/// Runs the VM until the CPU execution is canceled. /// Runs the VM until the CPU execution is canceled.
void Execute(); void Execute();