mirror of https://github.com/PCSX2/pcsx2.git
SaveState: Remove exceptions
This commit is contained in:
parent
52266d7ac0
commit
81236209db
|
@ -934,10 +934,14 @@ void cdvdReset()
|
||||||
cdvdCtrlTrayClose();
|
cdvdCtrlTrayClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::cdvdFreeze()
|
bool SaveStateBase::cdvdFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("cdvd");
|
if (!FreezeTag("cdvd"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(cdvd);
|
Freeze(cdvd);
|
||||||
|
if (!IsOkay())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (IsLoading())
|
if (IsLoading())
|
||||||
{
|
{
|
||||||
|
@ -948,6 +952,8 @@ void SaveStateBase::cdvdFreeze()
|
||||||
if (cdvd.Reading)
|
if (cdvd.Reading)
|
||||||
cdvd.RErr = DoCDVDreadTrack(cdvd.Readed ? cdvd.Sector : cdvd.SeekToSector, cdvd.ReadMode);
|
cdvd.RErr = DoCDVDreadTrack(cdvd.Readed ? cdvd.Sector : cdvd.SeekToSector, cdvd.ReadMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cdvdNewDiskCB()
|
void cdvdNewDiskCB()
|
||||||
|
|
|
@ -1114,8 +1114,11 @@ void cdrReset()
|
||||||
cdReadTime = (PSXCLK / 1757) * BIAS;
|
cdReadTime = (PSXCLK / 1757) * BIAS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::cdrFreeze()
|
bool SaveStateBase::cdrFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("cdrom");
|
if (!FreezeTag("cdrom"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(cdr);
|
Freeze(cdr);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1156,7 +1156,7 @@ template u16 rcntRead32<0x01>( u32 mem );
|
||||||
template bool rcntWrite32<0x00>( u32 mem, mem32_t& value );
|
template bool rcntWrite32<0x00>( u32 mem, mem32_t& value );
|
||||||
template bool rcntWrite32<0x01>( u32 mem, mem32_t& value );
|
template bool rcntWrite32<0x01>( u32 mem, mem32_t& value );
|
||||||
|
|
||||||
void SaveStateBase::rcntFreeze()
|
bool SaveStateBase::rcntFreeze()
|
||||||
{
|
{
|
||||||
Freeze( counters );
|
Freeze( counters );
|
||||||
Freeze( hsyncCounter );
|
Freeze( hsyncCounter );
|
||||||
|
@ -1170,4 +1170,6 @@ void SaveStateBase::rcntFreeze()
|
||||||
|
|
||||||
if( IsLoading() )
|
if( IsLoading() )
|
||||||
cpuRcntSet();
|
cpuRcntSet();
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,9 +372,10 @@ void gsPostVsyncStart()
|
||||||
MTGS::PostVsyncStart(registers_written);
|
MTGS::PostVsyncStart(registers_written);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::gsFreeze()
|
bool SaveStateBase::gsFreeze()
|
||||||
{
|
{
|
||||||
FreezeMem(PS2MEM_GS, 0x2000);
|
FreezeMem(PS2MEM_GS, 0x2000);
|
||||||
Freeze(gsVideoMode);
|
Freeze(gsVideoMode);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -804,10 +804,14 @@ void gifMFIFOInterrupt()
|
||||||
DMA_LOG("GIF MFIFO DMA End");
|
DMA_LOG("GIF MFIFO DMA End");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::gifDmaFreeze()
|
bool SaveStateBase::gifDmaFreeze()
|
||||||
{
|
{
|
||||||
// Note: mfifocycles is not a persistent var, so no need to save it here.
|
// Note: mfifocycles is not a persistent var, so no need to save it here.
|
||||||
FreezeTag("GIFdma");
|
if (!FreezeTag("GIFdma"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(gif);
|
Freeze(gif);
|
||||||
Freeze(gif_fifo);
|
Freeze(gif_fifo);
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ void Gif_FinishIRQ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::gifPathFreeze(u32 path)
|
bool SaveStateBase::gifPathFreeze(u32 path)
|
||||||
{
|
{
|
||||||
|
|
||||||
Gif_Path& gifPath = gifUnit.gifPath[path];
|
Gif_Path& gifPath = gifUnit.gifPath[path];
|
||||||
|
@ -220,14 +220,18 @@ void SaveStateBase::gifPathFreeze(u32 path)
|
||||||
gifPath.readAmount = 0;
|
gifPath.readAmount = 0;
|
||||||
gifPath.gsPack.readAmount = 0;
|
gifPath.gsPack.readAmount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::gifFreeze()
|
bool SaveStateBase::gifFreeze()
|
||||||
{
|
{
|
||||||
bool mtvuMode = THREAD_VU1;
|
bool mtvuMode = THREAD_VU1;
|
||||||
pxAssert(vu1Thread.IsDone());
|
pxAssert(vu1Thread.IsDone());
|
||||||
MTGS::WaitGS();
|
MTGS::WaitGS();
|
||||||
FreezeTag("Gif Unit");
|
if (!FreezeTag("Gif Unit"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(mtvuMode);
|
Freeze(mtvuMode);
|
||||||
Freeze(gifUnit.stat);
|
Freeze(gifUnit.stat);
|
||||||
Freeze(gifUnit.gsSIGNAL);
|
Freeze(gifUnit.gsSIGNAL);
|
||||||
|
@ -244,4 +248,6 @@ void SaveStateBase::gifFreeze()
|
||||||
// ToDo: gifUnit.SwitchMTVU(mtvuMode);
|
// ToDo: gifUnit.SwitchMTVU(mtvuMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,11 +134,13 @@ void ReportIPU()
|
||||||
Console.Newline();
|
Console.Newline();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::ipuFreeze()
|
bool SaveStateBase::ipuFreeze()
|
||||||
{
|
{
|
||||||
// Get a report of the status of the ipu variables when saving and loading savestates.
|
// Get a report of the status of the ipu variables when saving and loading savestates.
|
||||||
//ReportIPU();
|
//ReportIPU();
|
||||||
FreezeTag("IPU");
|
if (!FreezeTag("IPU"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(ipu_fifo);
|
Freeze(ipu_fifo);
|
||||||
|
|
||||||
Freeze(g_BP);
|
Freeze(g_BP);
|
||||||
|
@ -147,6 +149,8 @@ void SaveStateBase::ipuFreeze()
|
||||||
Freeze(coded_block_pattern);
|
Freeze(coded_block_pattern);
|
||||||
Freeze(decoder);
|
Freeze(decoder);
|
||||||
Freeze(ipu_cmd);
|
Freeze(ipu_cmd);
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tIPU_CMD_IDEC::log() const
|
void tIPU_CMD_IDEC::log() const
|
||||||
|
|
|
@ -31,11 +31,14 @@ void ipuDmaReset()
|
||||||
ProcessedData = 0;
|
ProcessedData = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::ipuDmaFreeze()
|
bool SaveStateBase::ipuDmaFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag( "IPUdma" );
|
if (!FreezeTag("IPUdma"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(IPU1Status);
|
Freeze(IPU1Status);
|
||||||
Freeze(CommandExecuteQueued);
|
Freeze(CommandExecuteQueued);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
static __fi int IPU1chain() {
|
static __fi int IPU1chain() {
|
||||||
|
|
|
@ -880,9 +880,10 @@ void psxRcntSetGates()
|
||||||
psxvblankgate &= ~(1 << 3);
|
psxvblankgate &= ~(1 << 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::psxRcntFreeze()
|
bool SaveStateBase::psxRcntFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("iopCounters");
|
if (!FreezeTag("iopCounters"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(psxCounters);
|
Freeze(psxCounters);
|
||||||
Freeze(psxNextCounter);
|
Freeze(psxNextCounter);
|
||||||
|
@ -890,6 +891,11 @@ void SaveStateBase::psxRcntFreeze()
|
||||||
Freeze(psxvblankgate);
|
Freeze(psxvblankgate);
|
||||||
Freeze(psxhblankgate);
|
Freeze(psxhblankgate);
|
||||||
|
|
||||||
|
if (!IsOkay())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (IsLoading())
|
if (IsLoading())
|
||||||
psxRcntUpdate();
|
psxRcntUpdate();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,11 @@ static void MTVU_Unpack(void* data, VIFregisters& vifRegs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called on Saving/Loading states...
|
// Called on Saving/Loading states...
|
||||||
void SaveStateBase::mtvuFreeze()
|
bool SaveStateBase::mtvuFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("MTVU");
|
if (!FreezeTag("MTVU"))
|
||||||
|
return false;
|
||||||
|
|
||||||
pxAssert(vu1Thread.IsDone());
|
pxAssert(vu1Thread.IsDone());
|
||||||
if (!IsSaving())
|
if (!IsSaving())
|
||||||
{
|
{
|
||||||
|
@ -88,6 +90,7 @@ void SaveStateBase::mtvuFreeze()
|
||||||
vu1Thread.gsLabel.store(gsLabel);
|
vu1Thread.gsLabel.store(gsLabel);
|
||||||
|
|
||||||
Freeze(vu1Thread.vuCycleIdx);
|
Freeze(vu1Thread.vuCycleIdx);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
VU_Thread::VU_Thread()
|
VU_Thread::VU_Thread()
|
||||||
|
|
|
@ -157,13 +157,16 @@ void Deci2Reset()
|
||||||
std::memset(deci2buffer, 0, sizeof(deci2buffer));
|
std::memset(deci2buffer, 0, sizeof(deci2buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::deci2Freeze()
|
bool SaveStateBase::deci2Freeze()
|
||||||
{
|
{
|
||||||
FreezeTag( "deci2" );
|
if (!FreezeTag("deci2"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze( deci2addr );
|
Freeze( deci2addr );
|
||||||
Freeze( deci2handler );
|
Freeze( deci2handler );
|
||||||
Freeze( deci2buffer );
|
Freeze( deci2buffer );
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -19,12 +19,15 @@
|
||||||
#include "MTGS.h"
|
#include "MTGS.h"
|
||||||
#include "SaveState.h"
|
#include "SaveState.h"
|
||||||
|
|
||||||
void SaveStateBase::InputRecordingFreeze()
|
bool SaveStateBase::InputRecordingFreeze()
|
||||||
{
|
{
|
||||||
// NOTE - BE CAREFUL
|
// NOTE - BE CAREFUL
|
||||||
// CHANGING THIS WILL BREAK BACKWARDS COMPATIBILITY ON SAVESTATES
|
// CHANGING THIS WILL BREAK BACKWARDS COMPATIBILITY ON SAVESTATES
|
||||||
FreezeTag("InputRecording");
|
if (!FreezeTag("InputRecording"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(g_FrameCount);
|
Freeze(g_FrameCount);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "InputRecording.h"
|
#include "InputRecording.h"
|
||||||
|
|
|
@ -541,11 +541,14 @@ void SPRTOinterrupt()
|
||||||
hwDmacIrq(DMAC_TO_SPR);
|
hwDmacIrq(DMAC_TO_SPR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::sprFreeze()
|
bool SaveStateBase::sprFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("SPRdma");
|
if (!FreezeTag("SPRdma"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(spr0finished);
|
Freeze(spr0finished);
|
||||||
Freeze(spr1finished);
|
Freeze(spr1finished);
|
||||||
Freeze(mfifotransferred);
|
Freeze(mfifotransferred);
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "VUmicro.h"
|
#include "VUmicro.h"
|
||||||
#include "ps2/BiosTools.h"
|
#include "ps2/BiosTools.h"
|
||||||
|
|
||||||
|
#include "common/Error.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
#include "common/SafeArray.inl"
|
#include "common/SafeArray.inl"
|
||||||
|
@ -109,24 +110,35 @@ void SaveStateBase::Init( SafeArray<u8>* memblock )
|
||||||
m_memory = memblock;
|
m_memory = memblock;
|
||||||
m_version = g_SaveVersion;
|
m_version = g_SaveVersion;
|
||||||
m_idx = 0;
|
m_idx = 0;
|
||||||
|
m_error = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::PrepBlock( int size )
|
void SaveStateBase::PrepBlock( int size )
|
||||||
{
|
{
|
||||||
pxAssertDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
pxAssertDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
||||||
|
if (m_error)
|
||||||
|
return;
|
||||||
|
|
||||||
const int end = m_idx+size;
|
const int end = m_idx+size;
|
||||||
if( IsSaving() )
|
if (IsSaving())
|
||||||
m_memory->MakeRoomFor( end );
|
{
|
||||||
|
m_memory->MakeRoomFor(end);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if( m_memory->GetSizeInBytes() < end )
|
if (m_memory->GetSizeInBytes() < end)
|
||||||
throw Exception::SaveStateLoadError();
|
{
|
||||||
|
Console.Error("(SaveStateBase) Buffer overflow in PrepBlock(), expected %d got %d", end, m_memory->GetSizeInBytes());
|
||||||
|
m_error = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::FreezeTag(const char* src)
|
bool SaveStateBase::FreezeTag(const char* src)
|
||||||
{
|
{
|
||||||
|
if (m_error)
|
||||||
|
return false;
|
||||||
|
|
||||||
char tagspace[32];
|
char tagspace[32];
|
||||||
pxAssertDev(std::strlen(src) < (sizeof(tagspace) - 1), "Tag name exceeds the allowed length");
|
pxAssertDev(std::strlen(src) < (sizeof(tagspace) - 1), "Tag name exceeds the allowed length");
|
||||||
|
|
||||||
|
@ -136,15 +148,18 @@ void SaveStateBase::FreezeTag(const char* src)
|
||||||
|
|
||||||
if (std::strcmp(tagspace, src) != 0)
|
if (std::strcmp(tagspace, src) != 0)
|
||||||
{
|
{
|
||||||
std::string msg(fmt::format("Savestate data corruption detected while reading tag: {}", src));
|
Console.Error(fmt::format("Savestate data corruption detected while reading tag: {}", src));
|
||||||
pxFail(msg.c_str());
|
m_error = true;
|
||||||
throw Exception::SaveStateLoadError().SetDiagMsg(std::move(msg));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveStateBase& SaveStateBase::FreezeBios()
|
bool SaveStateBase::FreezeBios()
|
||||||
{
|
{
|
||||||
FreezeTag( "BIOS" );
|
if (!FreezeTag("BIOS"))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Check the BIOS, and issue a warning if the bios for this state
|
// Check the BIOS, and issue a warning if the bios for this state
|
||||||
// doesn't match the bios currently being used (chances are it'll still
|
// doesn't match the bios currently being used (chances are it'll still
|
||||||
|
@ -170,19 +185,23 @@ SaveStateBase& SaveStateBase::FreezeBios()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveStateBase& SaveStateBase::FreezeInternals()
|
bool SaveStateBase::FreezeInternals()
|
||||||
{
|
{
|
||||||
// Print this until the MTVU problem in gifPathFreeze is taken care of (rama)
|
// Print this until the MTVU problem in gifPathFreeze is taken care of (rama)
|
||||||
if (THREAD_VU1) Console.Warning("MTVU speedhack is enabled, saved states may not be stable");
|
if (THREAD_VU1)
|
||||||
|
Console.Warning("MTVU speedhack is enabled, saved states may not be stable");
|
||||||
|
|
||||||
vmFreeze();
|
if (!vmFreeze())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Second Block - Various CPU Registers and States
|
// Second Block - Various CPU Registers and States
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
FreezeTag( "cpuRegs" );
|
if (!FreezeTag("cpuRegs"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(cpuRegs); // cpu regs + COP0
|
Freeze(cpuRegs); // cpu regs + COP0
|
||||||
Freeze(psxRegs); // iop regs
|
Freeze(psxRegs); // iop regs
|
||||||
Freeze(fpuRegs);
|
Freeze(fpuRegs);
|
||||||
|
@ -192,7 +211,9 @@ SaveStateBase& SaveStateBase::FreezeInternals()
|
||||||
|
|
||||||
// Third Block - Cycle Timers and Events
|
// Third Block - Cycle Timers and Events
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
FreezeTag( "Cycles" );
|
if (!FreezeTag("Cycles"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(EEsCycle);
|
Freeze(EEsCycle);
|
||||||
Freeze(EEoCycle);
|
Freeze(EEoCycle);
|
||||||
Freeze(nextCounter);
|
Freeze(nextCounter);
|
||||||
|
@ -202,39 +223,45 @@ SaveStateBase& SaveStateBase::FreezeInternals()
|
||||||
|
|
||||||
// Fourth Block - EE-related systems
|
// Fourth Block - EE-related systems
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
FreezeTag( "EE-Subsystems" );
|
if (!FreezeTag("EE-Subsystems"))
|
||||||
rcntFreeze();
|
return false;
|
||||||
gsFreeze();
|
|
||||||
vuMicroFreeze();
|
bool okay = rcntFreeze();
|
||||||
vuJITFreeze();
|
okay = okay && gsFreeze();
|
||||||
vif0Freeze();
|
okay = okay && vuMicroFreeze();
|
||||||
vif1Freeze();
|
okay = okay && vuJITFreeze();
|
||||||
sifFreeze();
|
okay = okay && vif0Freeze();
|
||||||
ipuFreeze();
|
okay = okay && vif1Freeze();
|
||||||
ipuDmaFreeze();
|
okay = okay && sifFreeze();
|
||||||
gifFreeze();
|
okay = okay && ipuFreeze();
|
||||||
gifDmaFreeze();
|
okay = okay && ipuDmaFreeze();
|
||||||
sprFreeze();
|
okay = okay && gifFreeze();
|
||||||
mtvuFreeze();
|
okay = okay && gifDmaFreeze();
|
||||||
|
okay = okay && sprFreeze();
|
||||||
|
okay = okay && mtvuFreeze();
|
||||||
|
if (!okay)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Fifth Block - iop-related systems
|
// Fifth Block - iop-related systems
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
FreezeTag( "IOP-Subsystems" );
|
if (!FreezeTag("IOP-Subsystems"))
|
||||||
|
return false;
|
||||||
|
|
||||||
FreezeMem(iopMem->Sif, sizeof(iopMem->Sif)); // iop's sif memory (not really needed, but oh well)
|
FreezeMem(iopMem->Sif, sizeof(iopMem->Sif)); // iop's sif memory (not really needed, but oh well)
|
||||||
|
|
||||||
psxRcntFreeze();
|
okay = okay && psxRcntFreeze();
|
||||||
sioFreeze();
|
okay = okay && sioFreeze();
|
||||||
sio2Freeze();
|
okay = okay && sio2Freeze();
|
||||||
cdrFreeze();
|
okay = okay && cdrFreeze();
|
||||||
cdvdFreeze();
|
okay = okay && cdvdFreeze();
|
||||||
|
|
||||||
// technically this is HLE BIOS territory, but we don't have enough such stuff
|
// technically this is HLE BIOS territory, but we don't have enough such stuff
|
||||||
// to merit an HLE Bios sub-section... yet.
|
// to merit an HLE Bios sub-section... yet.
|
||||||
deci2Freeze();
|
okay = okay && deci2Freeze();
|
||||||
|
|
||||||
InputRecordingFreeze();
|
okay = okay && InputRecordingFreeze();
|
||||||
|
|
||||||
return *this;
|
return okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -287,27 +314,17 @@ memLoadingState::memLoadingState( const SafeArray<u8>* load_from )
|
||||||
// Loading of state data from a memory buffer...
|
// Loading of state data from a memory buffer...
|
||||||
void memLoadingState::FreezeMem( void* data, int size )
|
void memLoadingState::FreezeMem( void* data, int size )
|
||||||
{
|
{
|
||||||
|
if (m_error)
|
||||||
|
{
|
||||||
|
std::memset(data, 0, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const u8* const src = m_memory->GetPtr(m_idx);
|
const u8* const src = m_memory->GetPtr(m_idx);
|
||||||
m_idx += size;
|
m_idx += size;
|
||||||
memcpy( data, src, size );
|
memcpy( data, src, size );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Exception::SaveStateLoadError::FormatDiagnosticMessage() const
|
|
||||||
{
|
|
||||||
std::string retval = "Savestate is corrupt or incomplete!\n";
|
|
||||||
Host::AddOSDMessage("Error: Savestate is corrupt or incomplete!", 15.0f);
|
|
||||||
_formatDiagMsg(retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Exception::SaveStateLoadError::FormatDisplayMessage() const
|
|
||||||
{
|
|
||||||
std::string retval = "The savestate cannot be loaded, as it appears to be corrupt or incomplete.\n";
|
|
||||||
Host::AddOSDMessage("Error: The savestate cannot be loaded, as it appears to be corrupt or incomplete.", 15.0f);
|
|
||||||
_formatUserMsg(retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to hold the current state backup (fullcopy of PS2 memory and subcomponents states).
|
// Used to hold the current state backup (fullcopy of PS2 memory and subcomponents states).
|
||||||
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
|
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
|
||||||
|
|
||||||
|
@ -332,25 +349,10 @@ static constexpr SysState_Component SPU2_{ "SPU2", SPU2freeze };
|
||||||
static constexpr SysState_Component PAD_{ "PAD", PADfreeze };
|
static constexpr SysState_Component PAD_{ "PAD", PADfreeze };
|
||||||
static constexpr SysState_Component GS{ "GS", SysState_MTGSFreeze };
|
static constexpr SysState_Component GS{ "GS", SysState_MTGSFreeze };
|
||||||
|
|
||||||
|
static bool SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
|
||||||
static void SysState_ComponentFreezeOutRoot(void* dest, SysState_Component comp)
|
|
||||||
{
|
|
||||||
freezeData fP = { 0, (u8*)dest };
|
|
||||||
if (comp.freeze(FreezeAction::Size, &fP) != 0)
|
|
||||||
return;
|
|
||||||
if (!fP.size)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Console.Indent().WriteLn("Saving %s", comp.name);
|
|
||||||
|
|
||||||
if (comp.freeze(FreezeAction::Save, &fP) != 0)
|
|
||||||
throw std::runtime_error(std::string(" * ") + comp.name + std::string(": Error saving state!\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
|
|
||||||
{
|
{
|
||||||
if (!zf)
|
if (!zf)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
freezeData fP = { 0, nullptr };
|
freezeData fP = { 0, nullptr };
|
||||||
if (comp.freeze(FreezeAction::Size, &fP) != 0)
|
if (comp.freeze(FreezeAction::Size, &fP) != 0)
|
||||||
|
@ -358,27 +360,57 @@ static void SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
|
||||||
|
|
||||||
Console.Indent().WriteLn("Loading %s", comp.name);
|
Console.Indent().WriteLn("Loading %s", comp.name);
|
||||||
|
|
||||||
auto data = std::make_unique<u8[]>(fP.size);
|
std::unique_ptr<u8[]> data;
|
||||||
fP.data = data.get();
|
if (fP.size > 0)
|
||||||
|
|
||||||
if (zip_fread(zf, data.get(), fP.size) != static_cast<zip_int64_t>(fP.size) || comp.freeze(FreezeAction::Load, &fP) != 0)
|
|
||||||
throw std::runtime_error(std::string(" * ") + comp.name + std::string(": Error loading state!\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SysState_ComponentFreezeOut(SaveStateBase& writer, SysState_Component comp)
|
|
||||||
{
|
|
||||||
freezeData fP = { 0, NULL };
|
|
||||||
if (comp.freeze(FreezeAction::Size, &fP) == 0)
|
|
||||||
{
|
{
|
||||||
const int size = fP.size;
|
data = std::make_unique<u8[]>(fP.size);
|
||||||
writer.PrepBlock(size);
|
fP.data = data.get();
|
||||||
SysState_ComponentFreezeOutRoot(writer.GetBlockPtr(), comp);
|
|
||||||
writer.CommitBlock(size);
|
if (zip_fread(zf, data.get(), fP.size) != static_cast<zip_int64_t>(fP.size))
|
||||||
|
{
|
||||||
|
Console.Error(fmt::format("* {}: Failed to decompress save data", comp.name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
if (comp.freeze(FreezeAction::Load, &fP) != 0)
|
||||||
|
{
|
||||||
|
Console.Error(fmt::format("* {}: Failed to load freeze data", comp.name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SysState_ComponentFreezeInNew(zip_file_t* zf, const char* name, bool(*do_state_func)(StateWrapper&))
|
static bool SysState_ComponentFreezeOut(SaveStateBase& writer, SysState_Component comp)
|
||||||
|
{
|
||||||
|
freezeData fP = {};
|
||||||
|
if (comp.freeze(FreezeAction::Size, &fP) != 0)
|
||||||
|
{
|
||||||
|
Console.Error(fmt::format("* {}: Failed to get freeze size", comp.name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fP.size == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const int size = fP.size;
|
||||||
|
writer.PrepBlock(size);
|
||||||
|
|
||||||
|
Console.Indent().WriteLn("Saving %s", comp.name);
|
||||||
|
|
||||||
|
fP.data = writer.GetBlockPtr();
|
||||||
|
if (comp.freeze(FreezeAction::Save, &fP) != 0)
|
||||||
|
{
|
||||||
|
Console.Error(fmt::format("* {}: Failed to save freeze data", comp.name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.CommitBlock(size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SysState_ComponentFreezeInNew(zip_file_t* zf, const char* name, bool(*do_state_func)(StateWrapper&))
|
||||||
{
|
{
|
||||||
// TODO: We could decompress on the fly here for a little bit more speed.
|
// TODO: We could decompress on the fly here for a little bit more speed.
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
@ -392,19 +424,16 @@ static void SysState_ComponentFreezeInNew(zip_file_t* zf, const char* name, bool
|
||||||
StateWrapper::ReadOnlyMemoryStream stream(data.empty() ? nullptr : data.data(), data.size());
|
StateWrapper::ReadOnlyMemoryStream stream(data.empty() ? nullptr : data.data(), data.size());
|
||||||
StateWrapper sw(&stream, StateWrapper::Mode::Read, g_SaveVersion);
|
StateWrapper sw(&stream, StateWrapper::Mode::Read, g_SaveVersion);
|
||||||
|
|
||||||
// TODO: Get rid of the bloody exceptions.
|
return do_state_func(sw);
|
||||||
if (!do_state_func(sw))
|
|
||||||
throw std::runtime_error(fmt::format(" * {}: Error loading state!", name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SysState_ComponentFreezeOutNew(SaveStateBase& writer, const char* name, u32 reserve, bool (*do_state_func)(StateWrapper&))
|
static bool SysState_ComponentFreezeOutNew(SaveStateBase& writer, const char* name, u32 reserve, bool (*do_state_func)(StateWrapper&))
|
||||||
{
|
{
|
||||||
StateWrapper::VectorMemoryStream stream(reserve);
|
StateWrapper::VectorMemoryStream stream(reserve);
|
||||||
StateWrapper sw(&stream, StateWrapper::Mode::Write, g_SaveVersion);
|
StateWrapper sw(&stream, StateWrapper::Mode::Write, g_SaveVersion);
|
||||||
|
|
||||||
// TODO: Get rid of the bloody exceptions.
|
|
||||||
if (!do_state_func(sw))
|
if (!do_state_func(sw))
|
||||||
throw std::runtime_error(fmt::format(" * {}: Error saving state!", name));
|
return false;
|
||||||
|
|
||||||
const int size = static_cast<int>(stream.GetBuffer().size());
|
const int size = static_cast<int>(stream.GetBuffer().size());
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
|
@ -413,6 +442,8 @@ static void SysState_ComponentFreezeOutNew(SaveStateBase& writer, const char* na
|
||||||
std::memcpy(writer.GetBlockPtr(), stream.GetBuffer().data(), size);
|
std::memcpy(writer.GetBlockPtr(), stream.GetBuffer().data(), size);
|
||||||
writer.CommitBlock(size);
|
writer.CommitBlock(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -427,8 +458,8 @@ public:
|
||||||
virtual ~BaseSavestateEntry() = default;
|
virtual ~BaseSavestateEntry() = default;
|
||||||
|
|
||||||
virtual const char* GetFilename() const = 0;
|
virtual const char* GetFilename() const = 0;
|
||||||
virtual void FreezeIn(zip_file_t* zf) const = 0;
|
virtual bool FreezeIn(zip_file_t* zf) const = 0;
|
||||||
virtual void FreezeOut(SaveStateBase& writer) const = 0;
|
virtual bool FreezeOut(SaveStateBase& writer) const = 0;
|
||||||
virtual bool IsRequired() const = 0;
|
virtual bool IsRequired() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -439,8 +470,8 @@ protected:
|
||||||
virtual ~MemorySavestateEntry() = default;
|
virtual ~MemorySavestateEntry() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void FreezeIn(zip_file_t* zf) const;
|
virtual bool FreezeIn(zip_file_t* zf) const;
|
||||||
virtual void FreezeOut(SaveStateBase& writer) const;
|
virtual bool FreezeOut(SaveStateBase& writer) const;
|
||||||
virtual bool IsRequired() const { return true; }
|
virtual bool IsRequired() const { return true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -448,7 +479,7 @@ protected:
|
||||||
virtual u32 GetDataSize() const = 0;
|
virtual u32 GetDataSize() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void MemorySavestateEntry::FreezeIn(zip_file_t* zf) const
|
bool MemorySavestateEntry::FreezeIn(zip_file_t* zf) const
|
||||||
{
|
{
|
||||||
const u32 expectedSize = GetDataSize();
|
const u32 expectedSize = GetDataSize();
|
||||||
const s64 bytesRead = zip_fread(zf, GetDataPtr(), expectedSize);
|
const s64 bytesRead = zip_fread(zf, GetDataPtr(), expectedSize);
|
||||||
|
@ -457,11 +488,14 @@ void MemorySavestateEntry::FreezeIn(zip_file_t* zf) const
|
||||||
Console.WriteLn(Color_Yellow, " '%s' is incomplete (expected 0x%x bytes, loading only 0x%x bytes)",
|
Console.WriteLn(Color_Yellow, " '%s' is incomplete (expected 0x%x bytes, loading only 0x%x bytes)",
|
||||||
GetFilename(), expectedSize, static_cast<u32>(bytesRead));
|
GetFilename(), expectedSize, static_cast<u32>(bytesRead));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemorySavestateEntry::FreezeOut(SaveStateBase& writer) const
|
bool MemorySavestateEntry::FreezeOut(SaveStateBase& writer) const
|
||||||
{
|
{
|
||||||
writer.FreezeMem(GetDataPtr(), GetDataSize());
|
writer.FreezeMem(GetDataPtr(), GetDataSize());
|
||||||
|
return writer.IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -473,156 +507,156 @@ void MemorySavestateEntry::FreezeOut(SaveStateBase& writer) const
|
||||||
// cannot use static struct member initializers -- we need virtual functions that compute
|
// cannot use static struct member initializers -- we need virtual functions that compute
|
||||||
// and resolve the addresses on-demand instead... --air
|
// and resolve the addresses on-demand instead... --air
|
||||||
|
|
||||||
class SavestateEntry_EmotionMemory : public MemorySavestateEntry
|
class SavestateEntry_EmotionMemory final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_EmotionMemory() = default;
|
~SavestateEntry_EmotionMemory() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "eeMemory.bin"; }
|
const char* GetFilename() const override { return "eeMemory.bin"; }
|
||||||
u8* GetDataPtr() const { return eeMem->Main; }
|
u8* GetDataPtr() const override { return eeMem->Main; }
|
||||||
uint GetDataSize() const { return sizeof(eeMem->Main); }
|
uint GetDataSize() const override { return sizeof(eeMem->Main); }
|
||||||
|
|
||||||
virtual void FreezeIn(zip_file_t* zf) const
|
virtual bool FreezeIn(zip_file_t* zf) const override
|
||||||
{
|
{
|
||||||
SysClearExecutionCache();
|
SysClearExecutionCache();
|
||||||
MemorySavestateEntry::FreezeIn(zf);
|
return MemorySavestateEntry::FreezeIn(zf);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_IopMemory : public MemorySavestateEntry
|
class SavestateEntry_IopMemory final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_IopMemory() = default;
|
~SavestateEntry_IopMemory() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "iopMemory.bin"; }
|
const char* GetFilename() const override { return "iopMemory.bin"; }
|
||||||
u8* GetDataPtr() const { return iopMem->Main; }
|
u8* GetDataPtr() const override { return iopMem->Main; }
|
||||||
uint GetDataSize() const { return sizeof(iopMem->Main); }
|
uint GetDataSize() const override { return sizeof(iopMem->Main); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_HwRegs : public MemorySavestateEntry
|
class SavestateEntry_HwRegs final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_HwRegs() = default;
|
~SavestateEntry_HwRegs() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "eeHwRegs.bin"; }
|
const char* GetFilename() const override { return "eeHwRegs.bin"; }
|
||||||
u8* GetDataPtr() const { return eeHw; }
|
u8* GetDataPtr() const override { return eeHw; }
|
||||||
uint GetDataSize() const { return sizeof(eeHw); }
|
uint GetDataSize() const override { return sizeof(eeHw); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_IopHwRegs : public MemorySavestateEntry
|
class SavestateEntry_IopHwRegs final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_IopHwRegs() = default;
|
~SavestateEntry_IopHwRegs() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "iopHwRegs.bin"; }
|
const char* GetFilename() const override { return "iopHwRegs.bin"; }
|
||||||
u8* GetDataPtr() const { return iopHw; }
|
u8* GetDataPtr() const override { return iopHw; }
|
||||||
uint GetDataSize() const { return sizeof(iopHw); }
|
uint GetDataSize() const override { return sizeof(iopHw); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_Scratchpad : public MemorySavestateEntry
|
class SavestateEntry_Scratchpad final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_Scratchpad() = default;
|
~SavestateEntry_Scratchpad() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "Scratchpad.bin"; }
|
const char* GetFilename() const override { return "Scratchpad.bin"; }
|
||||||
u8* GetDataPtr() const { return eeMem->Scratch; }
|
u8* GetDataPtr() const override { return eeMem->Scratch; }
|
||||||
uint GetDataSize() const { return sizeof(eeMem->Scratch); }
|
uint GetDataSize() const override { return sizeof(eeMem->Scratch); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_VU0mem : public MemorySavestateEntry
|
class SavestateEntry_VU0mem final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_VU0mem() = default;
|
~SavestateEntry_VU0mem() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "vu0Memory.bin"; }
|
const char* GetFilename() const override { return "vu0Memory.bin"; }
|
||||||
u8* GetDataPtr() const { return vuRegs[0].Mem; }
|
u8* GetDataPtr() const override { return vuRegs[0].Mem; }
|
||||||
uint GetDataSize() const { return VU0_MEMSIZE; }
|
uint GetDataSize() const override { return VU0_MEMSIZE; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_VU1mem : public MemorySavestateEntry
|
class SavestateEntry_VU1mem final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_VU1mem() = default;
|
~SavestateEntry_VU1mem() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "vu1Memory.bin"; }
|
const char* GetFilename() const override { return "vu1Memory.bin"; }
|
||||||
u8* GetDataPtr() const { return vuRegs[1].Mem; }
|
u8* GetDataPtr() const override { return vuRegs[1].Mem; }
|
||||||
uint GetDataSize() const { return VU1_MEMSIZE; }
|
uint GetDataSize() const override { return VU1_MEMSIZE; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_VU0prog : public MemorySavestateEntry
|
class SavestateEntry_VU0prog final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_VU0prog() = default;
|
~SavestateEntry_VU0prog() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "vu0MicroMem.bin"; }
|
const char* GetFilename() const override { return "vu0MicroMem.bin"; }
|
||||||
u8* GetDataPtr() const { return vuRegs[0].Micro; }
|
u8* GetDataPtr() const override { return vuRegs[0].Micro; }
|
||||||
uint GetDataSize() const { return VU0_PROGSIZE; }
|
uint GetDataSize() const override { return VU0_PROGSIZE; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_VU1prog : public MemorySavestateEntry
|
class SavestateEntry_VU1prog final : public MemorySavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_VU1prog() = default;
|
~SavestateEntry_VU1prog() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "vu1MicroMem.bin"; }
|
const char* GetFilename() const override { return "vu1MicroMem.bin"; }
|
||||||
u8* GetDataPtr() const { return vuRegs[1].Micro; }
|
u8* GetDataPtr() const override { return vuRegs[1].Micro; }
|
||||||
uint GetDataSize() const { return VU1_PROGSIZE; }
|
uint GetDataSize() const override { return VU1_PROGSIZE; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_SPU2 : public BaseSavestateEntry
|
class SavestateEntry_SPU2 final : public BaseSavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_SPU2() = default;
|
~SavestateEntry_SPU2() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "SPU2.bin"; }
|
const char* GetFilename() const override { return "SPU2.bin"; }
|
||||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, SPU2_); }
|
bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeIn(zf, SPU2_); }
|
||||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, SPU2_); }
|
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOut(writer, SPU2_); }
|
||||||
bool IsRequired() const { return true; }
|
bool IsRequired() const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_USB : public BaseSavestateEntry
|
class SavestateEntry_USB final : public BaseSavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_USB() = default;
|
~SavestateEntry_USB() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "USB.bin"; }
|
const char* GetFilename() const override { return "USB.bin"; }
|
||||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeInNew(zf, "USB", &USB::DoState); }
|
bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeInNew(zf, "USB", &USB::DoState); }
|
||||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOutNew(writer, "USB", 16 * 1024, &USB::DoState); }
|
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOutNew(writer, "USB", 16 * 1024, &USB::DoState); }
|
||||||
bool IsRequired() const { return false; }
|
bool IsRequired() const override { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_PAD : public BaseSavestateEntry
|
class SavestateEntry_PAD final : public BaseSavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_PAD() = default;
|
~SavestateEntry_PAD() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "PAD.bin"; }
|
const char* GetFilename() const override { return "PAD.bin"; }
|
||||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, PAD_); }
|
bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeIn(zf, PAD_); }
|
||||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, PAD_); }
|
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOut(writer, PAD_); }
|
||||||
bool IsRequired() const { return true; }
|
bool IsRequired() const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavestateEntry_GS : public BaseSavestateEntry
|
class SavestateEntry_GS final : public BaseSavestateEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SavestateEntry_GS() = default;
|
~SavestateEntry_GS() = default;
|
||||||
|
|
||||||
const char* GetFilename() const { return "GS.bin"; }
|
const char* GetFilename() const { return "GS.bin"; }
|
||||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, GS); }
|
bool FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, GS); }
|
||||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, GS); }
|
bool FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, GS); }
|
||||||
bool IsRequired() const { return true; }
|
bool IsRequired() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_ACHIEVEMENTS
|
#ifdef ENABLE_ACHIEVEMENTS
|
||||||
class SaveStateEntry_Achievements : public BaseSavestateEntry
|
class SaveStateEntry_Achievements final : public BaseSavestateEntry
|
||||||
{
|
{
|
||||||
virtual ~SaveStateEntry_Achievements() override = default;
|
~SaveStateEntry_Achievements() override = default;
|
||||||
|
|
||||||
const char* GetFilename() const override { return "Achievements.bin"; }
|
const char* GetFilename() const override { return "Achievements.bin"; }
|
||||||
void FreezeIn(zip_file_t* zf) const override
|
bool FreezeIn(zip_file_t* zf) const override
|
||||||
{
|
{
|
||||||
if (!Achievements::IsActive())
|
if (!Achievements::IsActive())
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
std::optional<std::vector<u8>> data;
|
std::optional<std::vector<u8>> data;
|
||||||
if (zf)
|
if (zf)
|
||||||
|
@ -632,12 +666,14 @@ class SaveStateEntry_Achievements : public BaseSavestateEntry
|
||||||
Achievements::LoadState(data->data(), data->size());
|
Achievements::LoadState(data->data(), data->size());
|
||||||
else
|
else
|
||||||
Achievements::LoadState(nullptr, 0);
|
Achievements::LoadState(nullptr, 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreezeOut(SaveStateBase& writer) const override
|
bool FreezeOut(SaveStateBase& writer) const override
|
||||||
{
|
{
|
||||||
if (!Achievements::IsActive())
|
if (!Achievements::IsActive())
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
std::vector<u8> data(Achievements::SaveState());
|
std::vector<u8> data(Achievements::SaveState());
|
||||||
if (!data.empty())
|
if (!data.empty())
|
||||||
|
@ -646,6 +682,8 @@ class SaveStateEntry_Achievements : public BaseSavestateEntry
|
||||||
std::memcpy(writer.GetBlockPtr(), data.data(), data.size());
|
std::memcpy(writer.GetBlockPtr(), data.data(), data.size());
|
||||||
writer.CommitBlock(static_cast<int>(data.size()));
|
writer.CommitBlock(static_cast<int>(data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return writer.IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRequired() const override { return false; }
|
bool IsRequired() const override { return false; }
|
||||||
|
@ -676,7 +714,7 @@ static const std::unique_ptr<BaseSavestateEntry> SavestateEntries[] = {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<ArchiveEntryList> SaveState_DownloadState()
|
std::unique_ptr<ArchiveEntryList> SaveState_DownloadState(Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<ArchiveEntryList> destlist = std::make_unique<ArchiveEntryList>(new VmStateBuffer("Zippable Savestate"));
|
std::unique_ptr<ArchiveEntryList> destlist = std::make_unique<ArchiveEntryList>(new VmStateBuffer("Zippable Savestate"));
|
||||||
|
|
||||||
|
@ -684,8 +722,17 @@ std::unique_ptr<ArchiveEntryList> SaveState_DownloadState()
|
||||||
ArchiveEntry internals(EntryFilename_InternalStructures);
|
ArchiveEntry internals(EntryFilename_InternalStructures);
|
||||||
internals.SetDataIndex(saveme.GetCurrentPos());
|
internals.SetDataIndex(saveme.GetCurrentPos());
|
||||||
|
|
||||||
saveme.FreezeBios();
|
if (!saveme.FreezeBios())
|
||||||
saveme.FreezeInternals();
|
{
|
||||||
|
Error::SetString(error, "FreezeBios() failed");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!saveme.FreezeInternals())
|
||||||
|
{
|
||||||
|
Error::SetString(error, "FreezeInternals() failed");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
internals.SetDataSize(saveme.GetCurrentPos() - internals.GetDataIndex());
|
internals.SetDataSize(saveme.GetCurrentPos() - internals.GetDataIndex());
|
||||||
destlist->Add(internals);
|
destlist->Add(internals);
|
||||||
|
@ -693,7 +740,13 @@ std::unique_ptr<ArchiveEntryList> SaveState_DownloadState()
|
||||||
for (const std::unique_ptr<BaseSavestateEntry>& entry : SavestateEntries)
|
for (const std::unique_ptr<BaseSavestateEntry>& entry : SavestateEntries)
|
||||||
{
|
{
|
||||||
uint startpos = saveme.GetCurrentPos();
|
uint startpos = saveme.GetCurrentPos();
|
||||||
entry->FreezeOut(saveme);
|
if (!entry->FreezeOut(saveme))
|
||||||
|
{
|
||||||
|
Error::SetString(error, fmt::format("FreezeOut() failed for {}.", entry->GetFilename()));
|
||||||
|
destlist.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
destlist->Add(
|
destlist->Add(
|
||||||
ArchiveEntry(entry->GetFilename())
|
ArchiveEntry(entry->GetFilename())
|
||||||
.SetDataIndex(startpos)
|
.SetDataIndex(startpos)
|
||||||
|
@ -965,30 +1018,32 @@ bool SaveState_ReadScreenshot(const std::string& filename, u32* out_width, u32*
|
||||||
return SaveState_ReadScreenshot(zf.get(), out_width, out_height, out_pixels);
|
return SaveState_ReadScreenshot(zf.get(), out_width, out_height, out_pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CheckVersion(const std::string& filename, zip_t* zf)
|
static bool CheckVersion(const std::string& filename, zip_t* zf, Error* error)
|
||||||
{
|
{
|
||||||
u32 savever;
|
u32 savever;
|
||||||
|
|
||||||
auto zff = zip_fopen_managed(zf, EntryFilename_StateVersion, 0);
|
auto zff = zip_fopen_managed(zf, EntryFilename_StateVersion, 0);
|
||||||
if (!zff || zip_fread(zff.get(), &savever, sizeof(savever)) != sizeof(savever))
|
if (!zff || zip_fread(zff.get(), &savever, sizeof(savever)) != sizeof(savever))
|
||||||
{
|
{
|
||||||
throw Exception::SaveStateLoadError(filename)
|
Error::SetString(error, "Savestate file does not contain version indicator.");
|
||||||
.SetDiagMsg("Savestate file does not contain version indicator.")
|
return false;
|
||||||
.SetUserMsg("This file is not a valid PCSX2 savestate. See the logfile for details.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Major version mismatch. Means we can't load this savestate at all. Support for it
|
// Major version mismatch. Means we can't load this savestate at all. Support for it
|
||||||
// was removed entirely.
|
// was removed entirely.
|
||||||
if (savever > g_SaveVersion)
|
|
||||||
throw Exception::SaveStateLoadError(filename)
|
|
||||||
.SetDiagMsg(fmt::format("Savestate uses an unsupported or unknown savestate version.\n(PCSX2 ver={:x}, state ver={:x})", g_SaveVersion, savever))
|
|
||||||
.SetUserMsg("Cannot load this savestate. The state is an unsupported version.\nOption 1: Download an older PCSX2 version from pcsx2.net and make a memcard save like on the physical PS2.\nOption 2: Delete the savestates.");
|
|
||||||
// check for a "minor" version incompatibility; which happens if the savestate being loaded is a newer version
|
// check for a "minor" version incompatibility; which happens if the savestate being loaded is a newer version
|
||||||
// than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
|
// than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
|
||||||
if ((savever >> 16) != (g_SaveVersion >> 16))
|
if (savever > g_SaveVersion || (savever >> 16) != (g_SaveVersion >> 16))
|
||||||
throw Exception::SaveStateLoadError(filename)
|
{
|
||||||
.SetDiagMsg(fmt::format("Savestate uses an unknown savestate version.\n(PCSX2 ver={:x}, state ver={:x})", g_SaveVersion, savever))
|
Error::SetString(error, fmt::format("The state is an unsupported version. (PCSX2 ver={:x}, state ver={:x}).\n"
|
||||||
.SetUserMsg("Cannot load this savestate. The state is an unsupported version.\nOption 1: Download an older PCSX2 version from pcsx2.net and make a memcard save like on the physical PS2.\nOption 2: Delete the savestates.");}
|
"Option 1: Download an older PCSX2 version from pcsx2.net and make a memcard save like on the physical PS2.\n"
|
||||||
|
"Option 2: Delete the savestates.",
|
||||||
|
g_SaveVersion, savever));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static zip_int64_t CheckFileExistsInState(zip_t* zf, const char* name, bool required)
|
static zip_int64_t CheckFileExistsInState(zip_t* zf, const char* name, bool required)
|
||||||
{
|
{
|
||||||
|
@ -1022,11 +1077,17 @@ static bool LoadInternalStructuresState(zip_t* zf, s64 index)
|
||||||
if (zip_fread(zff.get(), buffer.GetPtr(), buffer.GetSizeInBytes()) != buffer.GetSizeInBytes())
|
if (zip_fread(zff.get(), buffer.GetPtr(), buffer.GetSizeInBytes()) != buffer.GetSizeInBytes())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
memLoadingState(buffer).FreezeBios().FreezeInternals();
|
memLoadingState state(buffer);
|
||||||
|
if (!state.FreezeBios())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!state.FreezeInternals())
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveState_UnzipFromDisk(const std::string& filename)
|
bool SaveState_UnzipFromDisk(const std::string& filename, Error* error)
|
||||||
{
|
{
|
||||||
zip_error_t ze = {};
|
zip_error_t ze = {};
|
||||||
auto zf = zip_open_managed(filename.c_str(), ZIP_RDONLY, &ze);
|
auto zf = zip_open_managed(filename.c_str(), ZIP_RDONLY, &ze);
|
||||||
|
@ -1034,69 +1095,63 @@ void SaveState_UnzipFromDisk(const std::string& filename)
|
||||||
{
|
{
|
||||||
Console.Error("Failed to open zip file '%s' for save state load: %s", filename.c_str(), zip_error_strerror(&ze));
|
Console.Error("Failed to open zip file '%s' for save state load: %s", filename.c_str(), zip_error_strerror(&ze));
|
||||||
if (zip_error_code_zip(&ze) == ZIP_ER_NOENT)
|
if (zip_error_code_zip(&ze) == ZIP_ER_NOENT)
|
||||||
{
|
Error::SetString(error, "Savestate file does not exist.");
|
||||||
throw Exception::SaveStateLoadError(filename)
|
|
||||||
.SetDiagMsg("Savestate file does not exist.")
|
|
||||||
.SetUserMsg("This savestate cannot be loaded because the file does not exist.");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
Error::SetString(error, fmt::format("Savestate zip error: {}", zip_error_strerror(&ze)));
|
||||||
throw Exception::SaveStateLoadError(filename)
|
|
||||||
.SetDiagMsg("Savestate file is not a valid gzip archive.")
|
return false;
|
||||||
.SetUserMsg("This savestate cannot be loaded because it is not a valid gzip archive. It may have been created by an older unsupported version of PCSX2, or it may be corrupted.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for version and screenshot information in the zip stream:
|
// look for version and screenshot information in the zip stream:
|
||||||
CheckVersion(filename, zf.get());
|
if (!CheckVersion(filename, zf.get(), error))
|
||||||
|
return false;
|
||||||
|
|
||||||
// check that all parts are included
|
// check that all parts are included
|
||||||
const s64 internal_index = CheckFileExistsInState(zf.get(), EntryFilename_InternalStructures, true);
|
const s64 internal_index = CheckFileExistsInState(zf.get(), EntryFilename_InternalStructures, true);
|
||||||
s64 entryIndices[std::size(SavestateEntries)];
|
s64 entryIndices[std::size(SavestateEntries)];
|
||||||
|
|
||||||
// Log any parts and pieces that are missing, and then generate an exception.
|
// Log any parts and pieces that are missing, and then generate an exception.
|
||||||
bool throwIt = (internal_index < 0);
|
bool allPresent = (internal_index >= 0);
|
||||||
for (u32 i = 0; i < std::size(SavestateEntries); i++)
|
for (u32 i = 0; i < std::size(SavestateEntries); i++)
|
||||||
{
|
{
|
||||||
const bool required = SavestateEntries[i]->IsRequired();
|
const bool required = SavestateEntries[i]->IsRequired();
|
||||||
entryIndices[i] = CheckFileExistsInState(zf.get(), SavestateEntries[i]->GetFilename(), required);
|
entryIndices[i] = CheckFileExistsInState(zf.get(), SavestateEntries[i]->GetFilename(), required);
|
||||||
if (entryIndices[i] < 0 && required)
|
if (entryIndices[i] < 0 && required)
|
||||||
throwIt = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!throwIt)
|
|
||||||
{
|
|
||||||
PreLoadPrep();
|
|
||||||
throwIt = !LoadInternalStructuresState(zf.get(), internal_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!throwIt)
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < std::size(SavestateEntries); ++i)
|
|
||||||
{
|
{
|
||||||
if (entryIndices[i] < 0)
|
allPresent = false;
|
||||||
{
|
break;
|
||||||
SavestateEntries[i]->FreezeIn(nullptr);
|
}
|
||||||
continue;
|
}
|
||||||
}
|
if (!allPresent)
|
||||||
|
{
|
||||||
|
Error::SetString(error, "Some required components were not found or are incomplete.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto zff = zip_fopen_index_managed(zf.get(), entryIndices[i], 0);
|
PreLoadPrep();
|
||||||
if (!zff)
|
|
||||||
{
|
|
||||||
throwIt = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SavestateEntries[i]->FreezeIn(zff.get());
|
if (!LoadInternalStructuresState(zf.get(), internal_index))
|
||||||
|
{
|
||||||
|
Error::SetString(error, "Save state corruption in internal structures.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < std::size(SavestateEntries); ++i)
|
||||||
|
{
|
||||||
|
if (entryIndices[i] < 0)
|
||||||
|
{
|
||||||
|
SavestateEntries[i]->FreezeIn(nullptr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto zff = zip_fopen_index_managed(zf.get(), entryIndices[i], 0);
|
||||||
|
if (!zff || !SavestateEntries[i]->FreezeIn(zff.get()))
|
||||||
|
{
|
||||||
|
Error::SetString(error, fmt::format("Save state corruption in {}.", SavestateEntries[i]->GetFilename()));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throwIt)
|
|
||||||
{
|
|
||||||
throw Exception::SaveStateLoadError(filename)
|
|
||||||
.SetDiagMsg("Savestate cannot be loaded: some required components were not found or are incomplete.")
|
|
||||||
.SetUserMsg("This savestate cannot be loaded due to missing critical components. See the log file for details.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PostLoadPrep();
|
PostLoadPrep();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
|
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
#include "common/Exceptions.h"
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
enum class FreezeAction
|
enum class FreezeAction
|
||||||
{
|
{
|
||||||
|
@ -62,11 +63,11 @@ class ArchiveEntryList;
|
||||||
|
|
||||||
// Wrappers to generate a save state compatible across all frontends.
|
// Wrappers to generate a save state compatible across all frontends.
|
||||||
// These functions assume that the caller has paused the core thread.
|
// These functions assume that the caller has paused the core thread.
|
||||||
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState();
|
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState(Error* error);
|
||||||
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
||||||
extern bool SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename);
|
extern bool SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename);
|
||||||
extern bool SaveState_ReadScreenshot(const std::string& filename, u32* out_width, u32* out_height, std::vector<u32>* out_pixels);
|
extern bool SaveState_ReadScreenshot(const std::string& filename, u32* out_width, u32* out_height, std::vector<u32>* out_pixels);
|
||||||
extern void SaveState_UnzipFromDisk(const std::string& filename);
|
extern bool SaveState_UnzipFromDisk(const std::string& filename, Error* error);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// SaveStateBase class
|
// SaveStateBase class
|
||||||
|
@ -83,11 +84,16 @@ protected:
|
||||||
|
|
||||||
int m_idx; // current read/write index of the allocation
|
int m_idx; // current read/write index of the allocation
|
||||||
|
|
||||||
|
bool m_error; // error occurred while reading/writing
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SaveStateBase( VmStateBuffer& memblock );
|
SaveStateBase( VmStateBuffer& memblock );
|
||||||
SaveStateBase( VmStateBuffer* memblock );
|
SaveStateBase( VmStateBuffer* memblock );
|
||||||
virtual ~SaveStateBase() { }
|
virtual ~SaveStateBase() { }
|
||||||
|
|
||||||
|
__fi bool HasError() const { return m_error; }
|
||||||
|
__fi bool IsOkay() const { return !m_error; }
|
||||||
|
|
||||||
// Gets the version of savestate that this object is acting on.
|
// Gets the version of savestate that this object is acting on.
|
||||||
// The version refers to the low 16 bits only (high 16 bits classifies Pcsx2 build types)
|
// The version refers to the low 16 bits only (high 16 bits classifies Pcsx2 build types)
|
||||||
u32 GetVersion() const
|
u32 GetVersion() const
|
||||||
|
@ -95,8 +101,8 @@ public:
|
||||||
return (m_version & 0xffff);
|
return (m_version & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual SaveStateBase& FreezeBios();
|
bool FreezeBios();
|
||||||
virtual SaveStateBase& FreezeInternals();
|
bool FreezeInternals();
|
||||||
|
|
||||||
// Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays.
|
// Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays.
|
||||||
// For dynamically allocated pointers use FreezeMem instead.
|
// For dynamically allocated pointers use FreezeMem instead.
|
||||||
|
@ -182,7 +188,7 @@ public:
|
||||||
// Identifiers can be used to determine where in a savestate that data has become
|
// Identifiers can be used to determine where in a savestate that data has become
|
||||||
// skewed (if the value does not match then the error occurs somewhere prior to that
|
// skewed (if the value does not match then the error occurs somewhere prior to that
|
||||||
// position).
|
// position).
|
||||||
void FreezeTag( const char* src );
|
bool FreezeTag( const char* src );
|
||||||
|
|
||||||
// Returns true if this object is a StateLoading type object.
|
// Returns true if this object is a StateLoading type object.
|
||||||
bool IsLoading() const { return !IsSaving(); }
|
bool IsLoading() const { return !IsSaving(); }
|
||||||
|
@ -195,41 +201,41 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// note: gsFreeze() needs to be public because of the GSState recorder.
|
// note: gsFreeze() needs to be public because of the GSState recorder.
|
||||||
void gsFreeze();
|
bool gsFreeze();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void Init( VmStateBuffer* memblock );
|
void Init( VmStateBuffer* memblock );
|
||||||
|
|
||||||
void vmFreeze();
|
bool vmFreeze();
|
||||||
void mtvuFreeze();
|
bool mtvuFreeze();
|
||||||
void rcntFreeze();
|
bool rcntFreeze();
|
||||||
void vuMicroFreeze();
|
bool vuMicroFreeze();
|
||||||
void vuJITFreeze();
|
bool vuJITFreeze();
|
||||||
void vif0Freeze();
|
bool vif0Freeze();
|
||||||
void vif1Freeze();
|
bool vif1Freeze();
|
||||||
void sifFreeze();
|
bool sifFreeze();
|
||||||
void ipuFreeze();
|
bool ipuFreeze();
|
||||||
void ipuDmaFreeze();
|
bool ipuDmaFreeze();
|
||||||
void gifFreeze();
|
bool gifFreeze();
|
||||||
void gifDmaFreeze();
|
bool gifDmaFreeze();
|
||||||
void gifPathFreeze(u32 path); // called by gifFreeze()
|
bool gifPathFreeze(u32 path); // called by gifFreeze()
|
||||||
|
|
||||||
void sprFreeze();
|
bool sprFreeze();
|
||||||
|
|
||||||
void sioFreeze();
|
bool sioFreeze();
|
||||||
void cdrFreeze();
|
bool cdrFreeze();
|
||||||
void cdvdFreeze();
|
bool cdvdFreeze();
|
||||||
void psxRcntFreeze();
|
bool psxRcntFreeze();
|
||||||
void sio2Freeze();
|
bool sio2Freeze();
|
||||||
|
|
||||||
void deci2Freeze();
|
bool deci2Freeze();
|
||||||
|
|
||||||
// Save or load PCSX2's global frame counter (g_FrameCount) along with each savestate
|
// Save or load PCSX2's global frame counter (g_FrameCount) along with each savestate
|
||||||
//
|
//
|
||||||
// This is to prevent any inaccuracy issues caused by having a different
|
// This is to prevent any inaccuracy issues caused by having a different
|
||||||
// internal emulation frame count than what it was at the beginning of the
|
// internal emulation frame count than what it was at the beginning of the
|
||||||
// original recording
|
// original recording
|
||||||
void InputRecordingFreeze();
|
bool InputRecordingFreeze();
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -387,16 +393,3 @@ public:
|
||||||
bool IsSaving() const { return false; }
|
bool IsSaving() const { return false; }
|
||||||
bool IsFinished() const { return m_idx >= m_memory->GetSizeInBytes(); }
|
bool IsFinished() const { return m_idx >= m_memory->GetSizeInBytes(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace Exception
|
|
||||||
{
|
|
||||||
// Exception thrown when a corrupted or truncated savestate is encountered.
|
|
||||||
class SaveStateLoadError : public BadStream
|
|
||||||
{
|
|
||||||
DEFINE_STREAM_EXCEPTION(SaveStateLoadError, BadStream)
|
|
||||||
|
|
||||||
virtual std::string FormatDiagnosticMessage() const override;
|
|
||||||
virtual std::string FormatDisplayMessage() const override;
|
|
||||||
};
|
|
||||||
}; // namespace Exception
|
|
||||||
|
|
|
@ -27,10 +27,12 @@ void sifReset()
|
||||||
std::memset(&sif1, 0, sizeof(sif1));
|
std::memset(&sif1, 0, sizeof(sif1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::sifFreeze()
|
bool SaveStateBase::sifFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("SIFdma");
|
if (!FreezeTag("SIFdma"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(sif0);
|
Freeze(sif0);
|
||||||
Freeze(sif1);
|
Freeze(sif1);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
|
@ -822,14 +822,18 @@ void sioSetGameSerial( const std::string& serial ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::sio2Freeze()
|
bool SaveStateBase::sio2Freeze()
|
||||||
{
|
{
|
||||||
FreezeTag("sio2");
|
if (!FreezeTag("sio2"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(sio2);
|
Freeze(sio2);
|
||||||
FreezeDeque(fifoIn);
|
FreezeDeque(fifoIn);
|
||||||
FreezeDeque(fifoOut);
|
FreezeDeque(fifoOut);
|
||||||
|
|
||||||
|
if (!IsOkay())
|
||||||
|
return false;
|
||||||
|
|
||||||
// CRCs for memory cards.
|
// CRCs for memory cards.
|
||||||
// If the memory card hasn't changed when loading state, we can safely skip ejecting it.
|
// If the memory card hasn't changed when loading state, we can safely skip ejecting it.
|
||||||
u64 mcdCrcs[SIO::PORTS][SIO::SLOTS];
|
u64 mcdCrcs[SIO::PORTS][SIO::SLOTS];
|
||||||
|
@ -842,6 +846,8 @@ void SaveStateBase::sio2Freeze()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Freeze(mcdCrcs);
|
Freeze(mcdCrcs);
|
||||||
|
if (!IsOkay())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (IsLoading())
|
if (IsLoading())
|
||||||
{
|
{
|
||||||
|
@ -859,12 +865,17 @@ void SaveStateBase::sio2Freeze()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::sioFreeze()
|
bool SaveStateBase::sioFreeze()
|
||||||
{
|
{
|
||||||
FreezeTag("sio0");
|
if (!FreezeTag("sio0"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(sio0);
|
Freeze(sio0);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<u32, u32> sioConvertPadToPortAndSlot(u32 index)
|
std::tuple<u32, u32> sioConvertPadToPortAndSlot(u32 index)
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "ps2/BiosTools.h"
|
#include "ps2/BiosTools.h"
|
||||||
|
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
|
#include "common/Error.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/ScopedGuard.h"
|
#include "common/ScopedGuard.h"
|
||||||
#include "common/SettingsWrapper.h"
|
#include "common/SettingsWrapper.h"
|
||||||
|
@ -1427,7 +1428,7 @@ void VMManager::Reset()
|
||||||
s_state.store(VMState::Running, std::memory_order_release);
|
s_state.store(VMState::Running, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::vmFreeze()
|
bool SaveStateBase::vmFreeze()
|
||||||
{
|
{
|
||||||
const u32 prev_crc = s_current_crc;
|
const u32 prev_crc = s_current_crc;
|
||||||
const std::string prev_elf = s_elf_path;
|
const std::string prev_elf = s_elf_path;
|
||||||
|
@ -1458,6 +1459,8 @@ void SaveStateBase::vmFreeze()
|
||||||
if (s_current_crc != prev_crc || s_elf_path != prev_elf || s_elf_executed != prev_elf_executed)
|
if (s_current_crc != prev_crc || s_elf_path != prev_elf || s_elf_executed != prev_elf_executed)
|
||||||
VMManager::HandleELFChange(true);
|
VMManager::HandleELFChange(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string VMManager::GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot)
|
std::string VMManager::GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot)
|
||||||
|
@ -1506,24 +1509,23 @@ bool VMManager::DoLoadState(const char* filename)
|
||||||
if (GSDumpReplayer::IsReplayingDump())
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try
|
Host::OnSaveStateLoading(filename);
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
if (!SaveState_UnzipFromDisk(filename, &error))
|
||||||
{
|
{
|
||||||
Host::OnSaveStateLoading(filename);
|
Host::ReportErrorAsync("Failed to load save state", error.GetDescription());
|
||||||
SaveState_UnzipFromDisk(filename);
|
|
||||||
Host::OnSaveStateLoaded(filename, true);
|
|
||||||
if (g_InputRecording.isActive())
|
|
||||||
{
|
|
||||||
g_InputRecording.handleLoadingSavestate();
|
|
||||||
MTGS::PresentCurrentFrame();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception::BaseException& e)
|
|
||||||
{
|
|
||||||
Host::ReportErrorAsync("Failed to load save state", e.UserMsg());
|
|
||||||
Host::OnSaveStateLoaded(filename, false);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Host::OnSaveStateLoaded(filename, true);
|
||||||
|
if (g_InputRecording.isActive())
|
||||||
|
{
|
||||||
|
g_InputRecording.handleLoadingSavestate();
|
||||||
|
MTGS::PresentCurrentFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state)
|
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state)
|
||||||
|
@ -1532,47 +1534,46 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::string osd_key(fmt::format("SaveStateSlot{}", slot_for_message));
|
std::string osd_key(fmt::format("SaveStateSlot{}", slot_for_message));
|
||||||
|
Error error;
|
||||||
|
|
||||||
try
|
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
||||||
{
|
if (!elist)
|
||||||
std::unique_ptr<ArchiveEntryList> elist(SaveState_DownloadState());
|
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot(SaveState_SaveScreenshot());
|
|
||||||
|
|
||||||
if (FileSystem::FileExists(filename) && backup_old_state)
|
|
||||||
{
|
|
||||||
const std::string backup_filename(fmt::format("{}.backup", filename));
|
|
||||||
Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename));
|
|
||||||
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
|
||||||
{
|
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
|
|
||||||
fmt::format(
|
|
||||||
TRANSLATE_SV("VMManager", "Failed to back up old save state {}."), Path::GetFileName(filename)),
|
|
||||||
Host::OSD_ERROR_DURATION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zip_on_thread)
|
|
||||||
{
|
|
||||||
// lock order here is important; the thread could exit before we resume here.
|
|
||||||
std::unique_lock lock(s_save_state_threads_mutex);
|
|
||||||
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
|
|
||||||
std::move(osd_key), std::string(filename), slot_for_message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename, slot_for_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
Host::OnSaveStateSaved(filename);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception::BaseException& e)
|
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
|
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
|
||||||
fmt::format(TRANSLATE_SV("VMManager", "Failed to save save state: {}."), e.DiagMsg()),
|
fmt::format(TRANSLATE_SV("VMManager", "Failed to save save state: {}."), error.GetDescription()),
|
||||||
Host::OSD_ERROR_DURATION);
|
Host::OSD_ERROR_DURATION);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SaveStateScreenshotData> screenshot = SaveState_SaveScreenshot();
|
||||||
|
|
||||||
|
if (FileSystem::FileExists(filename) && backup_old_state)
|
||||||
|
{
|
||||||
|
const std::string backup_filename(fmt::format("{}.backup", filename));
|
||||||
|
Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename));
|
||||||
|
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
|
||||||
|
fmt::format(
|
||||||
|
TRANSLATE_SV("VMManager", "Failed to back up old save state {}."), Path::GetFileName(filename)),
|
||||||
|
Host::OSD_ERROR_DURATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zip_on_thread)
|
||||||
|
{
|
||||||
|
// lock order here is important; the thread could exit before we resume here.
|
||||||
|
std::unique_lock lock(s_save_state_threads_mutex);
|
||||||
|
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
|
||||||
|
std::move(osd_key), std::string(filename), slot_for_message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename, slot_for_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Host::OnSaveStateSaved(filename);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
|
|
|
@ -84,12 +84,13 @@ void vuMemoryReserve::Reset()
|
||||||
VU1.VI[0].UL = 0;
|
VU1.VI[0].UL = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::vuMicroFreeze()
|
bool SaveStateBase::vuMicroFreeze()
|
||||||
{
|
{
|
||||||
if(IsSaving())
|
if(IsSaving())
|
||||||
vu1Thread.WaitVU();
|
vu1Thread.WaitVU();
|
||||||
|
|
||||||
FreezeTag( "vuMicroRegs" );
|
if (!FreezeTag("vuMicroRegs"))
|
||||||
|
return false;
|
||||||
|
|
||||||
// VU0 state information
|
// VU0 state information
|
||||||
|
|
||||||
|
@ -175,4 +176,6 @@ void SaveStateBase::vuMicroFreeze()
|
||||||
Freeze(VU1.ialureadpos);
|
Freeze(VU1.ialureadpos);
|
||||||
Freeze(VU1.ialuwritepos);
|
Freeze(VU1.ialuwritepos);
|
||||||
Freeze(VU1.ialucount);
|
Freeze(VU1.ialucount);
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,10 @@ void vif1Reset()
|
||||||
resetNewVif(1);
|
resetNewVif(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::vif0Freeze()
|
bool SaveStateBase::vif0Freeze()
|
||||||
{
|
{
|
||||||
FreezeTag("VIF0dma");
|
if (!FreezeTag("VIF0dma"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(g_vif0Cycles);
|
Freeze(g_vif0Cycles);
|
||||||
|
|
||||||
|
@ -54,11 +55,14 @@ void SaveStateBase::vif0Freeze()
|
||||||
|
|
||||||
Freeze(nVif[0].bSize);
|
Freeze(nVif[0].bSize);
|
||||||
FreezeMem(nVif[0].buffer, nVif[0].bSize);
|
FreezeMem(nVif[0].buffer, nVif[0].bSize);
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::vif1Freeze()
|
bool SaveStateBase::vif1Freeze()
|
||||||
{
|
{
|
||||||
FreezeTag("VIF1dma");
|
if (!FreezeTag("VIF1dma"))
|
||||||
|
return false;
|
||||||
|
|
||||||
Freeze(g_vif1Cycles);
|
Freeze(g_vif1Cycles);
|
||||||
|
|
||||||
|
@ -66,6 +70,8 @@ void SaveStateBase::vif1Freeze()
|
||||||
|
|
||||||
Freeze(nVif[1].bSize);
|
Freeze(nVif[1].bSize);
|
||||||
FreezeMem(nVif[1].buffer, nVif[1].bSize);
|
FreezeMem(nVif[1].buffer, nVif[1].bSize);
|
||||||
|
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
|
@ -462,13 +462,14 @@ void recMicroVU1::ResumeXGkick()
|
||||||
((mVUrecCallXG)microVU1.startFunctXG)();
|
((mVUrecCallXG)microVU1.startFunctXG)();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::vuJITFreeze()
|
bool SaveStateBase::vuJITFreeze()
|
||||||
{
|
{
|
||||||
if (IsSaving())
|
if (IsSaving())
|
||||||
vu1Thread.WaitVU();
|
vu1Thread.WaitVU();
|
||||||
|
|
||||||
Freeze(microVU0.prog.lpState);
|
Freeze(microVU0.prog.lpState);
|
||||||
Freeze(microVU1.prog.lpState);
|
Freeze(microVU1.prog.lpState);
|
||||||
|
return IsOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
Loading…
Reference in New Issue