SaveState: Remove exceptions

This commit is contained in:
Stenzek 2022-12-28 21:13:38 +10:00 committed by Connor McLaughlin
parent 52266d7ac0
commit 81236209db
21 changed files with 487 additions and 368 deletions

View File

@ -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()

View File

@ -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();
}

View File

@ -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();
}

View File

@ -372,9 +372,10 @@ void gsPostVsyncStart()
MTGS::PostVsyncStart(registers_written);
}
void SaveStateBase::gsFreeze()
bool SaveStateBase::gsFreeze()
{
FreezeMem(PS2MEM_GS, 0x2000);
Freeze(gsVideoMode);
return IsOkay();
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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

View File

@ -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() {

View File

@ -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;
}

View File

@ -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()

View File

@ -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();
}
/*

View File

@ -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"

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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)

View File

@ -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,

View File

@ -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();
}

View File

@ -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();
}
//------------------------------------------------------------------

View File

@ -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