diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index d83678ba09..9d0e8ed5d2 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -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() diff --git a/pcsx2/CDVD/Ps1CD.cpp b/pcsx2/CDVD/Ps1CD.cpp index 8f2a9973e9..6f4a480e54 100644 --- a/pcsx2/CDVD/Ps1CD.cpp +++ b/pcsx2/CDVD/Ps1CD.cpp @@ -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(); } diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index fee3d2f36f..2b3ad8b1e4 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -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(); } diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 721ccd60fc..c52a9e5eed 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -372,9 +372,10 @@ void gsPostVsyncStart() MTGS::PostVsyncStart(registers_written); } -void SaveStateBase::gsFreeze() +bool SaveStateBase::gsFreeze() { FreezeMem(PS2MEM_GS, 0x2000); Freeze(gsVideoMode); + return IsOkay(); } diff --git a/pcsx2/Gif.cpp b/pcsx2/Gif.cpp index b6e631c8f7..628fbbed51 100644 --- a/pcsx2/Gif.cpp +++ b/pcsx2/Gif.cpp @@ -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(); } diff --git a/pcsx2/Gif_Unit.cpp b/pcsx2/Gif_Unit.cpp index 46b057d848..3ca7c67d33 100644 --- a/pcsx2/Gif_Unit.cpp +++ b/pcsx2/Gif_Unit.cpp @@ -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; } diff --git a/pcsx2/IPU/IPU.cpp b/pcsx2/IPU/IPU.cpp index 5c8bcde575..55d245d063 100644 --- a/pcsx2/IPU/IPU.cpp +++ b/pcsx2/IPU/IPU.cpp @@ -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 diff --git a/pcsx2/IPU/IPUdma.cpp b/pcsx2/IPU/IPUdma.cpp index eebc4f6779..44053159ff 100644 --- a/pcsx2/IPU/IPUdma.cpp +++ b/pcsx2/IPU/IPUdma.cpp @@ -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() { diff --git a/pcsx2/IopCounters.cpp b/pcsx2/IopCounters.cpp index 55ce15b98f..9216e8517e 100644 --- a/pcsx2/IopCounters.cpp +++ b/pcsx2/IopCounters.cpp @@ -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; } diff --git a/pcsx2/MTVU.cpp b/pcsx2/MTVU.cpp index f5721e7fd7..96727fe4c9 100644 --- a/pcsx2/MTVU.cpp +++ b/pcsx2/MTVU.cpp @@ -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() diff --git a/pcsx2/R5900OpcodeImpl.cpp b/pcsx2/R5900OpcodeImpl.cpp index ae17ff77f2..5773cf5268 100644 --- a/pcsx2/R5900OpcodeImpl.cpp +++ b/pcsx2/R5900OpcodeImpl.cpp @@ -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(); } /* diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index 255abac645..fe5a63c810 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -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" diff --git a/pcsx2/SPR.cpp b/pcsx2/SPR.cpp index fd36baa4de..7da082f97e 100644 --- a/pcsx2/SPR.cpp +++ b/pcsx2/SPR.cpp @@ -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(); } diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index 82fdb78e27..468257818e 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -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* 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* 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(fP.size); - fP.data = data.get(); - - if (zip_fread(zf, data.get(), fP.size) != static_cast(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 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(fP.size); + fP.data = data.get(); + + if (zip_fread(zf, data.get(), fP.size) != static_cast(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 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(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(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> 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 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(data.size())); } + + return writer.IsOkay(); } bool IsRequired() const override { return false; } @@ -676,7 +714,7 @@ static const std::unique_ptr SavestateEntries[] = { #endif }; -std::unique_ptr SaveState_DownloadState() +std::unique_ptr SaveState_DownloadState(Error* error) { std::unique_ptr destlist = std::make_unique(new VmStateBuffer("Zippable Savestate")); @@ -684,8 +722,17 @@ std::unique_ptr 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 SaveState_DownloadState() for (const std::unique_ptr& 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; } diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 39dc63f175..81700b0979 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -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 SaveState_DownloadState(); +extern std::unique_ptr SaveState_DownloadState(Error* error); extern std::unique_ptr SaveState_SaveScreenshot(); extern bool SaveState_ZipToDisk(std::unique_ptr srclist, std::unique_ptr screenshot, const char* filename); extern bool SaveState_ReadScreenshot(const std::string& filename, u32* out_width, u32* out_height, std::vector* 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 diff --git a/pcsx2/Sif.cpp b/pcsx2/Sif.cpp index 68ce5fef5e..652c7ef534 100644 --- a/pcsx2/Sif.cpp +++ b/pcsx2/Sif.cpp @@ -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(); } diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index 1c28de650a..69e5f2326b 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -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 sioConvertPadToPortAndSlot(u32 index) diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 7d1eaa0fcd..222ede1c80 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -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 elist(SaveState_DownloadState()); - std::unique_ptr 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 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 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 elist, diff --git a/pcsx2/VUmicroMem.cpp b/pcsx2/VUmicroMem.cpp index 89f7d421d7..cde1914ae5 100644 --- a/pcsx2/VUmicroMem.cpp +++ b/pcsx2/VUmicroMem.cpp @@ -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(); } diff --git a/pcsx2/Vif.cpp b/pcsx2/Vif.cpp index f80f2b46e6..ab8e2d1a76 100644 --- a/pcsx2/Vif.cpp +++ b/pcsx2/Vif.cpp @@ -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(); } //------------------------------------------------------------------ diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 1d57d5a984..9df88c01e7 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -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