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();
|
||||
}
|
||||
|
||||
void SaveStateBase::cdvdFreeze()
|
||||
bool SaveStateBase::cdvdFreeze()
|
||||
{
|
||||
FreezeTag("cdvd");
|
||||
if (!FreezeTag("cdvd"))
|
||||
return false;
|
||||
|
||||
Freeze(cdvd);
|
||||
if (!IsOkay())
|
||||
return false;
|
||||
|
||||
if (IsLoading())
|
||||
{
|
||||
|
@ -948,6 +952,8 @@ void SaveStateBase::cdvdFreeze()
|
|||
if (cdvd.Reading)
|
||||
cdvd.RErr = DoCDVDreadTrack(cdvd.Readed ? cdvd.Sector : cdvd.SeekToSector, cdvd.ReadMode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cdvdNewDiskCB()
|
||||
|
|
|
@ -1114,8 +1114,11 @@ void cdrReset()
|
|||
cdReadTime = (PSXCLK / 1757) * BIAS;
|
||||
}
|
||||
|
||||
void SaveStateBase::cdrFreeze()
|
||||
bool SaveStateBase::cdrFreeze()
|
||||
{
|
||||
FreezeTag("cdrom");
|
||||
if (!FreezeTag("cdrom"))
|
||||
return false;
|
||||
|
||||
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<0x01>( u32 mem, mem32_t& value );
|
||||
|
||||
void SaveStateBase::rcntFreeze()
|
||||
bool SaveStateBase::rcntFreeze()
|
||||
{
|
||||
Freeze( counters );
|
||||
Freeze( hsyncCounter );
|
||||
|
@ -1170,4 +1170,6 @@ void SaveStateBase::rcntFreeze()
|
|||
|
||||
if( IsLoading() )
|
||||
cpuRcntSet();
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
|
|
@ -372,9 +372,10 @@ void gsPostVsyncStart()
|
|||
MTGS::PostVsyncStart(registers_written);
|
||||
}
|
||||
|
||||
void SaveStateBase::gsFreeze()
|
||||
bool SaveStateBase::gsFreeze()
|
||||
{
|
||||
FreezeMem(PS2MEM_GS, 0x2000);
|
||||
Freeze(gsVideoMode);
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
|
|
|
@ -804,10 +804,14 @@ void gifMFIFOInterrupt()
|
|||
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.
|
||||
FreezeTag("GIFdma");
|
||||
if (!FreezeTag("GIFdma"))
|
||||
return false;
|
||||
|
||||
Freeze(gif);
|
||||
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];
|
||||
|
@ -220,14 +220,18 @@ void SaveStateBase::gifPathFreeze(u32 path)
|
|||
gifPath.readAmount = 0;
|
||||
gifPath.gsPack.readAmount = 0;
|
||||
}
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
void SaveStateBase::gifFreeze()
|
||||
bool SaveStateBase::gifFreeze()
|
||||
{
|
||||
bool mtvuMode = THREAD_VU1;
|
||||
pxAssert(vu1Thread.IsDone());
|
||||
MTGS::WaitGS();
|
||||
FreezeTag("Gif Unit");
|
||||
if (!FreezeTag("Gif Unit"))
|
||||
return false;
|
||||
|
||||
Freeze(mtvuMode);
|
||||
Freeze(gifUnit.stat);
|
||||
Freeze(gifUnit.gsSIGNAL);
|
||||
|
@ -244,4 +248,6 @@ void SaveStateBase::gifFreeze()
|
|||
// ToDo: gifUnit.SwitchMTVU(mtvuMode);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -134,11 +134,13 @@ void ReportIPU()
|
|||
Console.Newline();
|
||||
}
|
||||
|
||||
void SaveStateBase::ipuFreeze()
|
||||
bool SaveStateBase::ipuFreeze()
|
||||
{
|
||||
// Get a report of the status of the ipu variables when saving and loading savestates.
|
||||
//ReportIPU();
|
||||
FreezeTag("IPU");
|
||||
if (!FreezeTag("IPU"))
|
||||
return false;
|
||||
|
||||
Freeze(ipu_fifo);
|
||||
|
||||
Freeze(g_BP);
|
||||
|
@ -147,6 +149,8 @@ void SaveStateBase::ipuFreeze()
|
|||
Freeze(coded_block_pattern);
|
||||
Freeze(decoder);
|
||||
Freeze(ipu_cmd);
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
void tIPU_CMD_IDEC::log() const
|
||||
|
|
|
@ -31,11 +31,14 @@ void ipuDmaReset()
|
|||
ProcessedData = 0;
|
||||
}
|
||||
|
||||
void SaveStateBase::ipuDmaFreeze()
|
||||
bool SaveStateBase::ipuDmaFreeze()
|
||||
{
|
||||
FreezeTag( "IPUdma" );
|
||||
if (!FreezeTag("IPUdma"))
|
||||
return false;
|
||||
|
||||
Freeze(IPU1Status);
|
||||
Freeze(CommandExecuteQueued);
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
static __fi int IPU1chain() {
|
||||
|
|
|
@ -880,9 +880,10 @@ void psxRcntSetGates()
|
|||
psxvblankgate &= ~(1 << 3);
|
||||
}
|
||||
|
||||
void SaveStateBase::psxRcntFreeze()
|
||||
bool SaveStateBase::psxRcntFreeze()
|
||||
{
|
||||
FreezeTag("iopCounters");
|
||||
if (!FreezeTag("iopCounters"))
|
||||
return false;
|
||||
|
||||
Freeze(psxCounters);
|
||||
Freeze(psxNextCounter);
|
||||
|
@ -890,6 +891,11 @@ void SaveStateBase::psxRcntFreeze()
|
|||
Freeze(psxvblankgate);
|
||||
Freeze(psxhblankgate);
|
||||
|
||||
if (!IsOkay())
|
||||
return false;
|
||||
|
||||
if (IsLoading())
|
||||
psxRcntUpdate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -57,9 +57,11 @@ static void MTVU_Unpack(void* data, VIFregisters& vifRegs)
|
|||
}
|
||||
|
||||
// Called on Saving/Loading states...
|
||||
void SaveStateBase::mtvuFreeze()
|
||||
bool SaveStateBase::mtvuFreeze()
|
||||
{
|
||||
FreezeTag("MTVU");
|
||||
if (!FreezeTag("MTVU"))
|
||||
return false;
|
||||
|
||||
pxAssert(vu1Thread.IsDone());
|
||||
if (!IsSaving())
|
||||
{
|
||||
|
@ -88,6 +90,7 @@ void SaveStateBase::mtvuFreeze()
|
|||
vu1Thread.gsLabel.store(gsLabel);
|
||||
|
||||
Freeze(vu1Thread.vuCycleIdx);
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
VU_Thread::VU_Thread()
|
||||
|
|
|
@ -157,13 +157,16 @@ void Deci2Reset()
|
|||
std::memset(deci2buffer, 0, sizeof(deci2buffer));
|
||||
}
|
||||
|
||||
void SaveStateBase::deci2Freeze()
|
||||
bool SaveStateBase::deci2Freeze()
|
||||
{
|
||||
FreezeTag( "deci2" );
|
||||
if (!FreezeTag("deci2"))
|
||||
return false;
|
||||
|
||||
Freeze( deci2addr );
|
||||
Freeze( deci2handler );
|
||||
Freeze( deci2buffer );
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
#include "MTGS.h"
|
||||
#include "SaveState.h"
|
||||
|
||||
void SaveStateBase::InputRecordingFreeze()
|
||||
bool SaveStateBase::InputRecordingFreeze()
|
||||
{
|
||||
// NOTE - BE CAREFUL
|
||||
// CHANGING THIS WILL BREAK BACKWARDS COMPATIBILITY ON SAVESTATES
|
||||
FreezeTag("InputRecording");
|
||||
if (!FreezeTag("InputRecording"))
|
||||
return false;
|
||||
|
||||
Freeze(g_FrameCount);
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
#include "InputRecording.h"
|
||||
|
|
|
@ -541,11 +541,14 @@ void SPRTOinterrupt()
|
|||
hwDmacIrq(DMAC_TO_SPR);
|
||||
}
|
||||
|
||||
void SaveStateBase::sprFreeze()
|
||||
bool SaveStateBase::sprFreeze()
|
||||
{
|
||||
FreezeTag("SPRdma");
|
||||
if (!FreezeTag("SPRdma"))
|
||||
return false;
|
||||
|
||||
Freeze(spr0finished);
|
||||
Freeze(spr1finished);
|
||||
Freeze(mfifotransferred);
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "VUmicro.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/SafeArray.inl"
|
||||
|
@ -109,24 +110,35 @@ void SaveStateBase::Init( SafeArray<u8>* memblock )
|
|||
m_memory = memblock;
|
||||
m_version = g_SaveVersion;
|
||||
m_idx = 0;
|
||||
m_error = false;
|
||||
}
|
||||
|
||||
void SaveStateBase::PrepBlock( int size )
|
||||
{
|
||||
pxAssertDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
||||
if (m_error)
|
||||
return;
|
||||
|
||||
const int end = m_idx+size;
|
||||
if( IsSaving() )
|
||||
m_memory->MakeRoomFor( end );
|
||||
if (IsSaving())
|
||||
{
|
||||
m_memory->MakeRoomFor(end);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_memory->GetSizeInBytes() < end )
|
||||
throw Exception::SaveStateLoadError();
|
||||
if (m_memory->GetSizeInBytes() < end)
|
||||
{
|
||||
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];
|
||||
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)
|
||||
{
|
||||
std::string msg(fmt::format("Savestate data corruption detected while reading tag: {}", src));
|
||||
pxFail(msg.c_str());
|
||||
throw Exception::SaveStateLoadError().SetDiagMsg(std::move(msg));
|
||||
Console.Error(fmt::format("Savestate data corruption detected while reading tag: {}", src));
|
||||
m_error = true;
|
||||
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
|
||||
// doesn't match the bios currently being used (chances are it'll still
|
||||
|
@ -170,29 +185,35 @@ 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)
|
||||
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
|
||||
// -----------------------------------------------
|
||||
FreezeTag( "cpuRegs" );
|
||||
if (!FreezeTag("cpuRegs"))
|
||||
return false;
|
||||
|
||||
Freeze(cpuRegs); // cpu regs + COP0
|
||||
Freeze(psxRegs); // iop regs
|
||||
Freeze(fpuRegs);
|
||||
Freeze(tlb); // tlbs
|
||||
Freeze(AllowParams1); //OSDConfig written (Fast Boot)
|
||||
Freeze(AllowParams2);
|
||||
|
||||
|
||||
// Third Block - Cycle Timers and Events
|
||||
// -------------------------------------
|
||||
FreezeTag( "Cycles" );
|
||||
if (!FreezeTag("Cycles"))
|
||||
return false;
|
||||
|
||||
Freeze(EEsCycle);
|
||||
Freeze(EEoCycle);
|
||||
Freeze(nextCounter);
|
||||
|
@ -202,39 +223,45 @@ SaveStateBase& SaveStateBase::FreezeInternals()
|
|||
|
||||
// Fourth Block - EE-related systems
|
||||
// ---------------------------------
|
||||
FreezeTag( "EE-Subsystems" );
|
||||
rcntFreeze();
|
||||
gsFreeze();
|
||||
vuMicroFreeze();
|
||||
vuJITFreeze();
|
||||
vif0Freeze();
|
||||
vif1Freeze();
|
||||
sifFreeze();
|
||||
ipuFreeze();
|
||||
ipuDmaFreeze();
|
||||
gifFreeze();
|
||||
gifDmaFreeze();
|
||||
sprFreeze();
|
||||
mtvuFreeze();
|
||||
if (!FreezeTag("EE-Subsystems"))
|
||||
return false;
|
||||
|
||||
bool okay = rcntFreeze();
|
||||
okay = okay && gsFreeze();
|
||||
okay = okay && vuMicroFreeze();
|
||||
okay = okay && vuJITFreeze();
|
||||
okay = okay && vif0Freeze();
|
||||
okay = okay && vif1Freeze();
|
||||
okay = okay && sifFreeze();
|
||||
okay = okay && ipuFreeze();
|
||||
okay = okay && ipuDmaFreeze();
|
||||
okay = okay && gifFreeze();
|
||||
okay = okay && gifDmaFreeze();
|
||||
okay = okay && sprFreeze();
|
||||
okay = okay && mtvuFreeze();
|
||||
if (!okay)
|
||||
return false;
|
||||
|
||||
// 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)
|
||||
|
||||
psxRcntFreeze();
|
||||
sioFreeze();
|
||||
sio2Freeze();
|
||||
cdrFreeze();
|
||||
cdvdFreeze();
|
||||
okay = okay && psxRcntFreeze();
|
||||
okay = okay && sioFreeze();
|
||||
okay = okay && sio2Freeze();
|
||||
okay = okay && cdrFreeze();
|
||||
okay = okay && cdvdFreeze();
|
||||
|
||||
// technically this is HLE BIOS territory, but we don't have enough such stuff
|
||||
// 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...
|
||||
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);
|
||||
m_idx += 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).
|
||||
//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 GS{ "GS", SysState_MTGSFreeze };
|
||||
|
||||
|
||||
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)
|
||||
static bool SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
|
||||
{
|
||||
if (!zf)
|
||||
return;
|
||||
return true;
|
||||
|
||||
freezeData fP = { 0, nullptr };
|
||||
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);
|
||||
|
||||
auto data = std::make_unique<u8[]>(fP.size);
|
||||
fP.data = data.get();
|
||||
|
||||
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)
|
||||
std::unique_ptr<u8[]> data;
|
||||
if (fP.size > 0)
|
||||
{
|
||||
const int size = fP.size;
|
||||
writer.PrepBlock(size);
|
||||
SysState_ComponentFreezeOutRoot(writer.GetBlockPtr(), comp);
|
||||
writer.CommitBlock(size);
|
||||
data = std::make_unique<u8[]>(fP.size);
|
||||
fP.data = data.get();
|
||||
|
||||
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.
|
||||
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 sw(&stream, StateWrapper::Mode::Read, g_SaveVersion);
|
||||
|
||||
// TODO: Get rid of the bloody exceptions.
|
||||
if (!do_state_func(sw))
|
||||
throw std::runtime_error(fmt::format(" * {}: Error loading state!", name));
|
||||
return do_state_func(sw);
|
||||
}
|
||||
|
||||
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 sw(&stream, StateWrapper::Mode::Write, g_SaveVersion);
|
||||
|
||||
// TODO: Get rid of the bloody exceptions.
|
||||
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());
|
||||
if (size > 0)
|
||||
|
@ -413,6 +442,8 @@ static void SysState_ComponentFreezeOutNew(SaveStateBase& writer, const char* na
|
|||
std::memcpy(writer.GetBlockPtr(), stream.GetBuffer().data(), size);
|
||||
writer.CommitBlock(size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -427,8 +458,8 @@ public:
|
|||
virtual ~BaseSavestateEntry() = default;
|
||||
|
||||
virtual const char* GetFilename() const = 0;
|
||||
virtual void FreezeIn(zip_file_t* zf) const = 0;
|
||||
virtual void FreezeOut(SaveStateBase& writer) const = 0;
|
||||
virtual bool FreezeIn(zip_file_t* zf) const = 0;
|
||||
virtual bool FreezeOut(SaveStateBase& writer) const = 0;
|
||||
virtual bool IsRequired() const = 0;
|
||||
};
|
||||
|
||||
|
@ -439,8 +470,8 @@ protected:
|
|||
virtual ~MemorySavestateEntry() = default;
|
||||
|
||||
public:
|
||||
virtual void FreezeIn(zip_file_t* zf) const;
|
||||
virtual void FreezeOut(SaveStateBase& writer) const;
|
||||
virtual bool FreezeIn(zip_file_t* zf) const;
|
||||
virtual bool FreezeOut(SaveStateBase& writer) const;
|
||||
virtual bool IsRequired() const { return true; }
|
||||
|
||||
protected:
|
||||
|
@ -448,7 +479,7 @@ protected:
|
|||
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 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)",
|
||||
GetFilename(), expectedSize, static_cast<u32>(bytesRead));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemorySavestateEntry::FreezeOut(SaveStateBase& writer) const
|
||||
bool MemorySavestateEntry::FreezeOut(SaveStateBase& writer) const
|
||||
{
|
||||
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
|
||||
// and resolve the addresses on-demand instead... --air
|
||||
|
||||
class SavestateEntry_EmotionMemory : public MemorySavestateEntry
|
||||
class SavestateEntry_EmotionMemory final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_EmotionMemory() = default;
|
||||
~SavestateEntry_EmotionMemory() override = default;
|
||||
|
||||
const char* GetFilename() const { return "eeMemory.bin"; }
|
||||
u8* GetDataPtr() const { return eeMem->Main; }
|
||||
uint GetDataSize() const { return sizeof(eeMem->Main); }
|
||||
const char* GetFilename() const override { return "eeMemory.bin"; }
|
||||
u8* GetDataPtr() const override { return 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();
|
||||
MemorySavestateEntry::FreezeIn(zf);
|
||||
return MemorySavestateEntry::FreezeIn(zf);
|
||||
}
|
||||
};
|
||||
|
||||
class SavestateEntry_IopMemory : public MemorySavestateEntry
|
||||
class SavestateEntry_IopMemory final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_IopMemory() = default;
|
||||
~SavestateEntry_IopMemory() override = default;
|
||||
|
||||
const char* GetFilename() const { return "iopMemory.bin"; }
|
||||
u8* GetDataPtr() const { return iopMem->Main; }
|
||||
uint GetDataSize() const { return sizeof(iopMem->Main); }
|
||||
const char* GetFilename() const override { return "iopMemory.bin"; }
|
||||
u8* GetDataPtr() const override { return iopMem->Main; }
|
||||
uint GetDataSize() const override { return sizeof(iopMem->Main); }
|
||||
};
|
||||
|
||||
class SavestateEntry_HwRegs : public MemorySavestateEntry
|
||||
class SavestateEntry_HwRegs final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_HwRegs() = default;
|
||||
~SavestateEntry_HwRegs() override = default;
|
||||
|
||||
const char* GetFilename() const { return "eeHwRegs.bin"; }
|
||||
u8* GetDataPtr() const { return eeHw; }
|
||||
uint GetDataSize() const { return sizeof(eeHw); }
|
||||
const char* GetFilename() const override { return "eeHwRegs.bin"; }
|
||||
u8* GetDataPtr() const override { return eeHw; }
|
||||
uint GetDataSize() const override { return sizeof(eeHw); }
|
||||
};
|
||||
|
||||
class SavestateEntry_IopHwRegs : public MemorySavestateEntry
|
||||
class SavestateEntry_IopHwRegs final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_IopHwRegs() = default;
|
||||
~SavestateEntry_IopHwRegs() = default;
|
||||
|
||||
const char* GetFilename() const { return "iopHwRegs.bin"; }
|
||||
u8* GetDataPtr() const { return iopHw; }
|
||||
uint GetDataSize() const { return sizeof(iopHw); }
|
||||
const char* GetFilename() const override { return "iopHwRegs.bin"; }
|
||||
u8* GetDataPtr() const override { return iopHw; }
|
||||
uint GetDataSize() const override { return sizeof(iopHw); }
|
||||
};
|
||||
|
||||
class SavestateEntry_Scratchpad : public MemorySavestateEntry
|
||||
class SavestateEntry_Scratchpad final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_Scratchpad() = default;
|
||||
~SavestateEntry_Scratchpad() = default;
|
||||
|
||||
const char* GetFilename() const { return "Scratchpad.bin"; }
|
||||
u8* GetDataPtr() const { return eeMem->Scratch; }
|
||||
uint GetDataSize() const { return sizeof(eeMem->Scratch); }
|
||||
const char* GetFilename() const override { return "Scratchpad.bin"; }
|
||||
u8* GetDataPtr() const override { return eeMem->Scratch; }
|
||||
uint GetDataSize() const override { return sizeof(eeMem->Scratch); }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU0mem : public MemorySavestateEntry
|
||||
class SavestateEntry_VU0mem final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_VU0mem() = default;
|
||||
~SavestateEntry_VU0mem() = default;
|
||||
|
||||
const char* GetFilename() const { return "vu0Memory.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[0].Mem; }
|
||||
uint GetDataSize() const { return VU0_MEMSIZE; }
|
||||
const char* GetFilename() const override { return "vu0Memory.bin"; }
|
||||
u8* GetDataPtr() const override { return vuRegs[0].Mem; }
|
||||
uint GetDataSize() const override { return VU0_MEMSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU1mem : public MemorySavestateEntry
|
||||
class SavestateEntry_VU1mem final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_VU1mem() = default;
|
||||
~SavestateEntry_VU1mem() = default;
|
||||
|
||||
const char* GetFilename() const { return "vu1Memory.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[1].Mem; }
|
||||
uint GetDataSize() const { return VU1_MEMSIZE; }
|
||||
const char* GetFilename() const override { return "vu1Memory.bin"; }
|
||||
u8* GetDataPtr() const override { return vuRegs[1].Mem; }
|
||||
uint GetDataSize() const override { return VU1_MEMSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU0prog : public MemorySavestateEntry
|
||||
class SavestateEntry_VU0prog final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_VU0prog() = default;
|
||||
~SavestateEntry_VU0prog() = default;
|
||||
|
||||
const char* GetFilename() const { return "vu0MicroMem.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[0].Micro; }
|
||||
uint GetDataSize() const { return VU0_PROGSIZE; }
|
||||
const char* GetFilename() const override { return "vu0MicroMem.bin"; }
|
||||
u8* GetDataPtr() const override { return vuRegs[0].Micro; }
|
||||
uint GetDataSize() const override { return VU0_PROGSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU1prog : public MemorySavestateEntry
|
||||
class SavestateEntry_VU1prog final : public MemorySavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_VU1prog() = default;
|
||||
~SavestateEntry_VU1prog() = default;
|
||||
|
||||
const char* GetFilename() const { return "vu1MicroMem.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[1].Micro; }
|
||||
uint GetDataSize() const { return VU1_PROGSIZE; }
|
||||
const char* GetFilename() const override { return "vu1MicroMem.bin"; }
|
||||
u8* GetDataPtr() const override { return vuRegs[1].Micro; }
|
||||
uint GetDataSize() const override { return VU1_PROGSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_SPU2 : public BaseSavestateEntry
|
||||
class SavestateEntry_SPU2 final : public BaseSavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_SPU2() = default;
|
||||
~SavestateEntry_SPU2() override = default;
|
||||
|
||||
const char* GetFilename() const { return "SPU2.bin"; }
|
||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, SPU2_); }
|
||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, SPU2_); }
|
||||
bool IsRequired() const { return true; }
|
||||
const char* GetFilename() const override { return "SPU2.bin"; }
|
||||
bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeIn(zf, SPU2_); }
|
||||
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOut(writer, SPU2_); }
|
||||
bool IsRequired() const override { return true; }
|
||||
};
|
||||
|
||||
class SavestateEntry_USB : public BaseSavestateEntry
|
||||
class SavestateEntry_USB final : public BaseSavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_USB() = default;
|
||||
~SavestateEntry_USB() override = default;
|
||||
|
||||
const char* GetFilename() const { return "USB.bin"; }
|
||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeInNew(zf, "USB", &USB::DoState); }
|
||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOutNew(writer, "USB", 16 * 1024, &USB::DoState); }
|
||||
bool IsRequired() const { return false; }
|
||||
const char* GetFilename() const override { return "USB.bin"; }
|
||||
bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeInNew(zf, "USB", &USB::DoState); }
|
||||
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOutNew(writer, "USB", 16 * 1024, &USB::DoState); }
|
||||
bool IsRequired() const override { return false; }
|
||||
};
|
||||
|
||||
class SavestateEntry_PAD : public BaseSavestateEntry
|
||||
class SavestateEntry_PAD final : public BaseSavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_PAD() = default;
|
||||
~SavestateEntry_PAD() override = default;
|
||||
|
||||
const char* GetFilename() const { return "PAD.bin"; }
|
||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, PAD_); }
|
||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, PAD_); }
|
||||
bool IsRequired() const { return true; }
|
||||
const char* GetFilename() const override { return "PAD.bin"; }
|
||||
bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeIn(zf, PAD_); }
|
||||
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOut(writer, PAD_); }
|
||||
bool IsRequired() const override { return true; }
|
||||
};
|
||||
|
||||
class SavestateEntry_GS : public BaseSavestateEntry
|
||||
class SavestateEntry_GS final : public BaseSavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual ~SavestateEntry_GS() = default;
|
||||
~SavestateEntry_GS() = default;
|
||||
|
||||
const char* GetFilename() const { return "GS.bin"; }
|
||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, GS); }
|
||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, GS); }
|
||||
bool FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, GS); }
|
||||
bool FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, GS); }
|
||||
bool IsRequired() const { return true; }
|
||||
};
|
||||
|
||||
#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"; }
|
||||
void FreezeIn(zip_file_t* zf) const override
|
||||
bool FreezeIn(zip_file_t* zf) const override
|
||||
{
|
||||
if (!Achievements::IsActive())
|
||||
return;
|
||||
return true;
|
||||
|
||||
std::optional<std::vector<u8>> data;
|
||||
if (zf)
|
||||
|
@ -632,12 +666,14 @@ class SaveStateEntry_Achievements : public BaseSavestateEntry
|
|||
Achievements::LoadState(data->data(), data->size());
|
||||
else
|
||||
Achievements::LoadState(nullptr, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreezeOut(SaveStateBase& writer) const override
|
||||
bool FreezeOut(SaveStateBase& writer) const override
|
||||
{
|
||||
if (!Achievements::IsActive())
|
||||
return;
|
||||
return true;
|
||||
|
||||
std::vector<u8> data(Achievements::SaveState());
|
||||
if (!data.empty())
|
||||
|
@ -646,6 +682,8 @@ class SaveStateEntry_Achievements : public BaseSavestateEntry
|
|||
std::memcpy(writer.GetBlockPtr(), data.data(), data.size());
|
||||
writer.CommitBlock(static_cast<int>(data.size()));
|
||||
}
|
||||
|
||||
return writer.IsOkay();
|
||||
}
|
||||
|
||||
bool IsRequired() const override { return false; }
|
||||
|
@ -676,7 +714,7 @@ static const std::unique_ptr<BaseSavestateEntry> SavestateEntries[] = {
|
|||
#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"));
|
||||
|
||||
|
@ -684,8 +722,17 @@ std::unique_ptr<ArchiveEntryList> SaveState_DownloadState()
|
|||
ArchiveEntry internals(EntryFilename_InternalStructures);
|
||||
internals.SetDataIndex(saveme.GetCurrentPos());
|
||||
|
||||
saveme.FreezeBios();
|
||||
saveme.FreezeInternals();
|
||||
if (!saveme.FreezeBios())
|
||||
{
|
||||
Error::SetString(error, "FreezeBios() failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!saveme.FreezeInternals())
|
||||
{
|
||||
Error::SetString(error, "FreezeInternals() failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
internals.SetDataSize(saveme.GetCurrentPos() - internals.GetDataIndex());
|
||||
destlist->Add(internals);
|
||||
|
@ -693,7 +740,13 @@ std::unique_ptr<ArchiveEntryList> SaveState_DownloadState()
|
|||
for (const std::unique_ptr<BaseSavestateEntry>& entry : SavestateEntries)
|
||||
{
|
||||
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(
|
||||
ArchiveEntry(entry->GetFilename())
|
||||
.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);
|
||||
}
|
||||
|
||||
static void CheckVersion(const std::string& filename, zip_t* zf)
|
||||
static bool CheckVersion(const std::string& filename, zip_t* zf, Error* error)
|
||||
{
|
||||
u32 savever;
|
||||
|
||||
auto zff = zip_fopen_managed(zf, EntryFilename_StateVersion, 0);
|
||||
if (!zff || zip_fread(zff.get(), &savever, sizeof(savever)) != sizeof(savever))
|
||||
{
|
||||
throw Exception::SaveStateLoadError(filename)
|
||||
.SetDiagMsg("Savestate file does not contain version indicator.")
|
||||
.SetUserMsg("This file is not a valid PCSX2 savestate. See the logfile for details.");
|
||||
Error::SetString(error, "Savestate file does not contain version indicator.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Major version mismatch. Means we can't load this savestate at all. Support for it
|
||||
// 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
|
||||
// than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
|
||||
if ((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))
|
||||
.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.");}
|
||||
if (savever > g_SaveVersion || (savever >> 16) != (g_SaveVersion >> 16))
|
||||
{
|
||||
Error::SetString(error, fmt::format("The state is an unsupported version. (PCSX2 ver={:x}, state ver={:x}).\n"
|
||||
"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)
|
||||
{
|
||||
|
@ -1022,11 +1077,17 @@ static bool LoadInternalStructuresState(zip_t* zf, s64 index)
|
|||
if (zip_fread(zff.get(), buffer.GetPtr(), buffer.GetSizeInBytes()) != buffer.GetSizeInBytes())
|
||||
return false;
|
||||
|
||||
memLoadingState(buffer).FreezeBios().FreezeInternals();
|
||||
memLoadingState state(buffer);
|
||||
if (!state.FreezeBios())
|
||||
return false;
|
||||
|
||||
if (!state.FreezeInternals())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveState_UnzipFromDisk(const std::string& filename)
|
||||
bool SaveState_UnzipFromDisk(const std::string& filename, Error* error)
|
||||
{
|
||||
zip_error_t 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));
|
||||
if (zip_error_code_zip(&ze) == ZIP_ER_NOENT)
|
||||
{
|
||||
throw Exception::SaveStateLoadError(filename)
|
||||
.SetDiagMsg("Savestate file does not exist.")
|
||||
.SetUserMsg("This savestate cannot be loaded because the file does not exist.");
|
||||
}
|
||||
Error::SetString(error, "Savestate file does not exist.");
|
||||
else
|
||||
{
|
||||
throw Exception::SaveStateLoadError(filename)
|
||||
.SetDiagMsg("Savestate file is not a valid gzip archive.")
|
||||
.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.");
|
||||
}
|
||||
Error::SetString(error, fmt::format("Savestate zip error: {}", zip_error_strerror(&ze)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
const s64 internal_index = CheckFileExistsInState(zf.get(), EntryFilename_InternalStructures, true);
|
||||
s64 entryIndices[std::size(SavestateEntries)];
|
||||
|
||||
// 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++)
|
||||
{
|
||||
const bool required = SavestateEntries[i]->IsRequired();
|
||||
entryIndices[i] = CheckFileExistsInState(zf.get(), SavestateEntries[i]->GetFilename(), 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)
|
||||
{
|
||||
SavestateEntries[i]->FreezeIn(nullptr);
|
||||
continue;
|
||||
}
|
||||
allPresent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (!zff)
|
||||
{
|
||||
throwIt = true;
|
||||
break;
|
||||
}
|
||||
PreLoadPrep();
|
||||
|
||||
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();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
|
||||
#include "System.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Exceptions.h"
|
||||
|
||||
class Error;
|
||||
|
||||
enum class FreezeAction
|
||||
{
|
||||
|
@ -62,11 +63,11 @@ class ArchiveEntryList;
|
|||
|
||||
// Wrappers to generate a save state compatible across all frontends.
|
||||
// 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 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 void SaveState_UnzipFromDisk(const std::string& filename);
|
||||
extern bool SaveState_UnzipFromDisk(const std::string& filename, Error* error);
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SaveStateBase class
|
||||
|
@ -83,11 +84,16 @@ protected:
|
|||
|
||||
int m_idx; // current read/write index of the allocation
|
||||
|
||||
bool m_error; // error occurred while reading/writing
|
||||
|
||||
public:
|
||||
SaveStateBase( VmStateBuffer& memblock );
|
||||
SaveStateBase( VmStateBuffer* memblock );
|
||||
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.
|
||||
// The version refers to the low 16 bits only (high 16 bits classifies Pcsx2 build types)
|
||||
u32 GetVersion() const
|
||||
|
@ -95,8 +101,8 @@ public:
|
|||
return (m_version & 0xffff);
|
||||
}
|
||||
|
||||
virtual SaveStateBase& FreezeBios();
|
||||
virtual SaveStateBase& FreezeInternals();
|
||||
bool FreezeBios();
|
||||
bool FreezeInternals();
|
||||
|
||||
// Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays.
|
||||
// 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
|
||||
// skewed (if the value does not match then the error occurs somewhere prior to that
|
||||
// position).
|
||||
void FreezeTag( const char* src );
|
||||
bool FreezeTag( const char* src );
|
||||
|
||||
// Returns true if this object is a StateLoading type object.
|
||||
bool IsLoading() const { return !IsSaving(); }
|
||||
|
@ -195,41 +201,41 @@ public:
|
|||
|
||||
public:
|
||||
// note: gsFreeze() needs to be public because of the GSState recorder.
|
||||
void gsFreeze();
|
||||
bool gsFreeze();
|
||||
|
||||
protected:
|
||||
void Init( VmStateBuffer* memblock );
|
||||
|
||||
void vmFreeze();
|
||||
void mtvuFreeze();
|
||||
void rcntFreeze();
|
||||
void vuMicroFreeze();
|
||||
void vuJITFreeze();
|
||||
void vif0Freeze();
|
||||
void vif1Freeze();
|
||||
void sifFreeze();
|
||||
void ipuFreeze();
|
||||
void ipuDmaFreeze();
|
||||
void gifFreeze();
|
||||
void gifDmaFreeze();
|
||||
void gifPathFreeze(u32 path); // called by gifFreeze()
|
||||
bool vmFreeze();
|
||||
bool mtvuFreeze();
|
||||
bool rcntFreeze();
|
||||
bool vuMicroFreeze();
|
||||
bool vuJITFreeze();
|
||||
bool vif0Freeze();
|
||||
bool vif1Freeze();
|
||||
bool sifFreeze();
|
||||
bool ipuFreeze();
|
||||
bool ipuDmaFreeze();
|
||||
bool gifFreeze();
|
||||
bool gifDmaFreeze();
|
||||
bool gifPathFreeze(u32 path); // called by gifFreeze()
|
||||
|
||||
void sprFreeze();
|
||||
bool sprFreeze();
|
||||
|
||||
void sioFreeze();
|
||||
void cdrFreeze();
|
||||
void cdvdFreeze();
|
||||
void psxRcntFreeze();
|
||||
void sio2Freeze();
|
||||
bool sioFreeze();
|
||||
bool cdrFreeze();
|
||||
bool cdvdFreeze();
|
||||
bool psxRcntFreeze();
|
||||
bool sio2Freeze();
|
||||
|
||||
void deci2Freeze();
|
||||
bool deci2Freeze();
|
||||
|
||||
// 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
|
||||
// internal emulation frame count than what it was at the beginning of the
|
||||
// original recording
|
||||
void InputRecordingFreeze();
|
||||
bool InputRecordingFreeze();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -387,16 +393,3 @@ public:
|
|||
bool IsSaving() const { return false; }
|
||||
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));
|
||||
}
|
||||
|
||||
void SaveStateBase::sifFreeze()
|
||||
bool SaveStateBase::sifFreeze()
|
||||
{
|
||||
FreezeTag("SIFdma");
|
||||
if (!FreezeTag("SIFdma"))
|
||||
return false;
|
||||
|
||||
Freeze(sif0);
|
||||
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);
|
||||
FreezeDeque(fifoIn);
|
||||
FreezeDeque(fifoOut);
|
||||
|
||||
if (!IsOkay())
|
||||
return false;
|
||||
|
||||
// CRCs for memory cards.
|
||||
// If the memory card hasn't changed when loading state, we can safely skip ejecting it.
|
||||
u64 mcdCrcs[SIO::PORTS][SIO::SLOTS];
|
||||
|
@ -842,6 +846,8 @@ void SaveStateBase::sio2Freeze()
|
|||
}
|
||||
}
|
||||
Freeze(mcdCrcs);
|
||||
if (!IsOkay())
|
||||
return false;
|
||||
|
||||
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);
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
std::tuple<u32, u32> sioConvertPadToPortAndSlot(u32 index)
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "ps2/BiosTools.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/SettingsWrapper.h"
|
||||
|
@ -1427,7 +1428,7 @@ void VMManager::Reset()
|
|||
s_state.store(VMState::Running, std::memory_order_release);
|
||||
}
|
||||
|
||||
void SaveStateBase::vmFreeze()
|
||||
bool SaveStateBase::vmFreeze()
|
||||
{
|
||||
const u32 prev_crc = s_current_crc;
|
||||
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)
|
||||
VMManager::HandleELFChange(true);
|
||||
}
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
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())
|
||||
return false;
|
||||
|
||||
try
|
||||
Host::OnSaveStateLoading(filename);
|
||||
|
||||
Error error;
|
||||
if (!SaveState_UnzipFromDisk(filename, &error))
|
||||
{
|
||||
Host::OnSaveStateLoading(filename);
|
||||
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);
|
||||
Host::ReportErrorAsync("Failed to load save state", error.GetDescription());
|
||||
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)
|
||||
|
@ -1532,47 +1534,46 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
|
|||
return false;
|
||||
|
||||
std::string osd_key(fmt::format("SaveStateSlot{}", slot_for_message));
|
||||
Error error;
|
||||
|
||||
try
|
||||
{
|
||||
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)
|
||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
||||
if (!elist)
|
||||
{
|
||||
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);
|
||||
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,
|
||||
|
|
|
@ -84,12 +84,13 @@ void vuMemoryReserve::Reset()
|
|||
VU1.VI[0].UL = 0;
|
||||
}
|
||||
|
||||
void SaveStateBase::vuMicroFreeze()
|
||||
bool SaveStateBase::vuMicroFreeze()
|
||||
{
|
||||
if(IsSaving())
|
||||
vu1Thread.WaitVU();
|
||||
|
||||
FreezeTag( "vuMicroRegs" );
|
||||
if (!FreezeTag("vuMicroRegs"))
|
||||
return false;
|
||||
|
||||
// VU0 state information
|
||||
|
||||
|
@ -175,4 +176,6 @@ void SaveStateBase::vuMicroFreeze()
|
|||
Freeze(VU1.ialureadpos);
|
||||
Freeze(VU1.ialuwritepos);
|
||||
Freeze(VU1.ialucount);
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
|
|
@ -44,9 +44,10 @@ void vif1Reset()
|
|||
resetNewVif(1);
|
||||
}
|
||||
|
||||
void SaveStateBase::vif0Freeze()
|
||||
bool SaveStateBase::vif0Freeze()
|
||||
{
|
||||
FreezeTag("VIF0dma");
|
||||
if (!FreezeTag("VIF0dma"))
|
||||
return false;
|
||||
|
||||
Freeze(g_vif0Cycles);
|
||||
|
||||
|
@ -54,11 +55,14 @@ void SaveStateBase::vif0Freeze()
|
|||
|
||||
Freeze(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);
|
||||
|
||||
|
@ -66,6 +70,8 @@ void SaveStateBase::vif1Freeze()
|
|||
|
||||
Freeze(nVif[1].bSize);
|
||||
FreezeMem(nVif[1].buffer, nVif[1].bSize);
|
||||
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -462,13 +462,14 @@ void recMicroVU1::ResumeXGkick()
|
|||
((mVUrecCallXG)microVU1.startFunctXG)();
|
||||
}
|
||||
|
||||
void SaveStateBase::vuJITFreeze()
|
||||
bool SaveStateBase::vuJITFreeze()
|
||||
{
|
||||
if (IsSaving())
|
||||
vu1Thread.WaitVU();
|
||||
|
||||
Freeze(microVU0.prog.lpState);
|
||||
Freeze(microVU1.prog.lpState);
|
||||
return IsOkay();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
Loading…
Reference in New Issue