mirror of https://github.com/PCSX2/pcsx2.git
SaveState: Use libzip instead of wx
This commit is contained in:
parent
9aa2c52b20
commit
dd8a645986
|
@ -0,0 +1,115 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "zip.h"
|
||||
|
||||
#include "Console.h"
|
||||
|
||||
static inline std::unique_ptr<zip_t, void (*)(zip_t*)> zip_open_managed(const char* filename, int flags, zip_error_t* ze)
|
||||
{
|
||||
zip_source_t* zs = zip_source_file_create(filename, 0, 0, ze);
|
||||
zip_t* zip = nullptr;
|
||||
if (zs && !(zip = zip_open_from_source(zs, flags, ze)))
|
||||
{
|
||||
// have to clean up source
|
||||
zip_source_free(zs);
|
||||
}
|
||||
|
||||
return std::unique_ptr<zip_t, void (*)(zip_t*)>(zip, [](zip_t* zf) {
|
||||
if (!zf)
|
||||
return;
|
||||
|
||||
int err = zip_close(zf);
|
||||
if (err != 0)
|
||||
{
|
||||
Console.Error("Failed to close zip file: %d", err);
|
||||
zip_discard(zf);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<zip_t, void (*)(zip_t*)> zip_open_buffer_managed(const void* buffer, size_t size, int flags, int freep, zip_error_t* ze)
|
||||
{
|
||||
zip_source_t* zs = zip_source_buffer_create(buffer, size, freep, ze);
|
||||
zip_t* zip = nullptr;
|
||||
if (zs && !(zip = zip_open_from_source(zs, flags, ze)))
|
||||
{
|
||||
// have to clean up source
|
||||
zip_source_free(zs);
|
||||
}
|
||||
|
||||
return std::unique_ptr<zip_t, void (*)(zip_t*)>(zip, [](zip_t* zf) {
|
||||
if (!zf)
|
||||
return;
|
||||
|
||||
int err = zip_close(zf);
|
||||
if (err != 0)
|
||||
{
|
||||
Console.Error("Failed to close zip file: %d", err);
|
||||
zip_discard(zf);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<zip_file_t, int (*)(zip_file_t*)> zip_fopen_managed(zip_t* zip, const char* filename, zip_flags_t flags)
|
||||
{
|
||||
return std::unique_ptr<zip_file_t, int (*)(zip_file_t*)>(zip_fopen(zip, filename, flags), zip_fclose);
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<zip_file_t, int (*)(zip_file_t*)> zip_fopen_index_managed(zip_t* zip, zip_uint64_t index, zip_flags_t flags)
|
||||
{
|
||||
return std::unique_ptr<zip_file_t, int (*)(zip_file_t*)>(zip_fopen_index(zip, index, flags), zip_fclose);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline std::optional<T> ReadFileInZipToContainer(zip_t* zip, const char* name)
|
||||
{
|
||||
std::optional<T> ret;
|
||||
const zip_int64_t file_index = zip_name_locate(zip, name, 0);
|
||||
if (file_index >= 0)
|
||||
{
|
||||
zip_stat_t zst;
|
||||
if (zip_stat_index(zip, file_index, 0, &zst) == 0)
|
||||
{
|
||||
zip_file_t* zf = zip_fopen_index(zip, file_index, 0);
|
||||
if (zf)
|
||||
{
|
||||
ret = T();
|
||||
ret->resize(static_cast<size_t>(zst.size));
|
||||
if (zip_fread(zf, ret->data(), ret->size()) != static_cast<zip_int64_t>(ret->size()))
|
||||
{
|
||||
ret.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline std::optional<std::string> ReadFileInZipToString(zip_t* zip, const char* name)
|
||||
{
|
||||
return ReadFileInZipToContainer<std::string>(zip, name);
|
||||
}
|
||||
|
||||
static inline std::optional<std::vector<u8>> ReadBinaryFileInZip(zip_t* zip, const char* name)
|
||||
{
|
||||
return ReadFileInZipToContainer<std::vector<u8>>(zip, name);
|
||||
}
|
|
@ -190,6 +190,7 @@
|
|||
<ClInclude Include="emitter\implement\simd_helpers.h" />
|
||||
<ClInclude Include="emitter\implement\simd_moremovs.h" />
|
||||
<ClInclude Include="emitter\implement\simd_shufflepack.h" />
|
||||
<ClInclude Include="ZipHelpers.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\3rdparty\glad\glad.vcxproj">
|
||||
|
@ -209,4 +210,4 @@
|
|||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -420,6 +420,9 @@
|
|||
<ClInclude Include="D3D11\ShaderCompiler.h">
|
||||
<Filter>Header Files\D3D11</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZipHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
|
|
|
@ -1598,6 +1598,7 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
|
|||
fmt::fmt
|
||||
ryml
|
||||
chdr-static
|
||||
zip
|
||||
wxWidgets::all
|
||||
ZLIB::ZLIB
|
||||
PkgConfig::SOUNDTOUCH
|
||||
|
|
|
@ -15,9 +15,14 @@
|
|||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "R3000A.h"
|
||||
#include "SaveState.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/SafeArray.inl"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/ZipHelpers.h"
|
||||
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "COP0.h"
|
||||
#include "VUmicro.h"
|
||||
|
@ -25,7 +30,7 @@
|
|||
#include "Cache.h"
|
||||
#include "Config.h"
|
||||
#include "CDVD/CDVD.h"
|
||||
|
||||
#include "R3000A.h"
|
||||
#include "Elfheader.h"
|
||||
#include "Counters.h"
|
||||
#include "Patch.h"
|
||||
|
@ -33,18 +38,11 @@
|
|||
#include "DebugTools/Breakpoints.h"
|
||||
#include "Host.h"
|
||||
#include "GS.h"
|
||||
|
||||
#include "common/pxStreams.h"
|
||||
#include "common/SafeArray.inl"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "GS/GS.h"
|
||||
#include "SPU2/spu2.h"
|
||||
#include "USB/USB.h"
|
||||
#include "PAD/Gamepad.h"
|
||||
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/App.h"
|
||||
#include "gui/ConsoleLogger.h"
|
||||
|
@ -52,19 +50,21 @@
|
|||
#include "VMManager.h"
|
||||
#endif
|
||||
|
||||
#include "common/pxStreams.h"
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#include <csetjmp>
|
||||
#include <png.h>
|
||||
|
||||
using namespace R5900;
|
||||
|
||||
|
||||
static void PreLoadPrep()
|
||||
{
|
||||
// ensure everything is in sync before we start overwriting stuff.
|
||||
if (THREAD_VU1)
|
||||
vu1Thread.WaitVU();
|
||||
GetMTGS().WaitGS(false);
|
||||
SysClearExecutionCache();
|
||||
#ifndef PCSX2_CORE
|
||||
PatchesVerboseReset();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PostLoadPrep()
|
||||
|
@ -215,42 +215,11 @@ SaveStateBase& SaveStateBase::FreezeBios()
|
|||
return *this;
|
||||
}
|
||||
|
||||
static const uint MainMemorySizeInBytes =
|
||||
Ps2MemSize::MainRam + Ps2MemSize::Scratch + Ps2MemSize::Hardware +
|
||||
Ps2MemSize::IopRam + Ps2MemSize::IopHardware;
|
||||
|
||||
SaveStateBase& SaveStateBase::FreezeMainMemory()
|
||||
{
|
||||
vu1Thread.WaitVU(); // Finish VU1 just in-case...
|
||||
if (IsLoading()) PreLoadPrep();
|
||||
else m_memory->MakeRoomFor( m_idx + MainMemorySizeInBytes );
|
||||
|
||||
// First Block - Memory Dumps
|
||||
// ---------------------------
|
||||
FreezeMem(eeMem->Main, Ps2MemSize::MainRam); // 32 MB main memory
|
||||
FreezeMem(eeMem->Scratch, Ps2MemSize::Scratch); // scratch pad
|
||||
FreezeMem(eeHw, Ps2MemSize::Hardware); // hardware memory
|
||||
|
||||
FreezeMem(iopMem->Main, Ps2MemSize::IopRam); // 2 MB main memory
|
||||
FreezeMem(iopHw, Ps2MemSize::IopHardware); // hardware memory
|
||||
|
||||
FreezeMem(vuRegs[0].Micro, VU0_PROGSIZE);
|
||||
FreezeMem(vuRegs[0].Mem, VU0_MEMSIZE);
|
||||
|
||||
FreezeMem(vuRegs[1].Micro, VU1_PROGSIZE);
|
||||
FreezeMem(vuRegs[1].Mem, VU1_MEMSIZE);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
SaveStateBase& SaveStateBase::FreezeInternals()
|
||||
{
|
||||
vu1Thread.WaitVU(); // Finish VU1 just in-case...
|
||||
// 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 (IsLoading()) PreLoadPrep();
|
||||
|
||||
// Second Block - Various CPU Registers and States
|
||||
// -----------------------------------------------
|
||||
FreezeTag( "cpuRegs" );
|
||||
|
@ -322,9 +291,6 @@ SaveStateBase& SaveStateBase::FreezeInternals()
|
|||
InputRecordingFreeze();
|
||||
#endif
|
||||
|
||||
if( IsLoading() )
|
||||
PostLoadPrep();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -405,9 +371,9 @@ wxString Exception::SaveStateLoadError::FormatDisplayMessage() const
|
|||
// Used to hold the current state backup (fullcopy of PS2 memory and subcomponents states).
|
||||
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
|
||||
|
||||
static const wxChar* EntryFilename_StateVersion = L"PCSX2 Savestate Version.id";
|
||||
static const wxChar* EntryFilename_Screenshot = L"Screenshot.png";
|
||||
static const wxChar* EntryFilename_InternalStructures = L"PCSX2 Internal Structures.dat";
|
||||
static const char* EntryFilename_StateVersion = "PCSX2 Savestate Version.id";
|
||||
static const char* EntryFilename_Screenshot = "Screenshot.png";
|
||||
static const char* EntryFilename_InternalStructures = "PCSX2 Internal Structures.dat";
|
||||
|
||||
struct SysState_Component
|
||||
{
|
||||
|
@ -448,7 +414,7 @@ static void SysState_ComponentFreezeOutRoot(void* dest, SysState_Component comp)
|
|||
throw std::runtime_error(std::string(" * ") + comp.name + std::string(": Error saving state!\n"));
|
||||
}
|
||||
|
||||
static void SysState_ComponentFreezeIn(pxInputStream& infp, SysState_Component comp)
|
||||
static void SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
|
||||
{
|
||||
freezeData fP = { 0, nullptr };
|
||||
if (comp.freeze(FreezeAction::Size, &fP) != 0)
|
||||
|
@ -456,21 +422,10 @@ static void SysState_ComponentFreezeIn(pxInputStream& infp, SysState_Component c
|
|||
|
||||
Console.Indent().WriteLn("Loading %s", comp.name);
|
||||
|
||||
if (!infp.IsOk() || !infp.Length())
|
||||
{
|
||||
// no state data to read, but component expects some state data?
|
||||
// Issue a warning to console...
|
||||
if (fP.size != 0)
|
||||
Console.Indent().Warning("Warning: No data for %s found. Status may be unpredictable.", comp.name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = std::make_unique<u8[]>(fP.size);
|
||||
fP.data = data.get();
|
||||
|
||||
infp.Read(fP.data, fP.size);
|
||||
if (comp.freeze(FreezeAction::Load, &fP) != 0)
|
||||
if (zip_fread(zf, data.get(), fP.size) != static_cast<zip_int64_t>(fP.size) || comp.freeze(FreezeAction::Load, &fP) != 0)
|
||||
throw std::runtime_error(std::string(" * ") + comp.name + std::string(": Error loading state!\n"));
|
||||
}
|
||||
|
||||
|
@ -498,8 +453,8 @@ protected:
|
|||
public:
|
||||
virtual ~BaseSavestateEntry() = default;
|
||||
|
||||
virtual wxString GetFilename() const = 0;
|
||||
virtual void FreezeIn(pxInputStream& reader) const = 0;
|
||||
virtual const char* GetFilename() const = 0;
|
||||
virtual void FreezeIn(zip_file_t* zf) const = 0;
|
||||
virtual void FreezeOut(SaveStateBase& writer) const = 0;
|
||||
virtual bool IsRequired() const = 0;
|
||||
};
|
||||
|
@ -511,28 +466,24 @@ protected:
|
|||
virtual ~MemorySavestateEntry() = default;
|
||||
|
||||
public:
|
||||
virtual void FreezeIn(pxInputStream& reader) const;
|
||||
virtual void FreezeIn(zip_file_t* zf) const;
|
||||
virtual void FreezeOut(SaveStateBase& writer) const;
|
||||
virtual bool IsRequired() const { return true; }
|
||||
|
||||
protected:
|
||||
virtual u8* GetDataPtr() const = 0;
|
||||
virtual uint GetDataSize() const = 0;
|
||||
virtual u32 GetDataSize() const = 0;
|
||||
};
|
||||
|
||||
void MemorySavestateEntry::FreezeIn(pxInputStream& reader) const
|
||||
void MemorySavestateEntry::FreezeIn(zip_file_t* zf) const
|
||||
{
|
||||
const uint entrySize = reader.Length();
|
||||
const uint expectedSize = GetDataSize();
|
||||
|
||||
if (entrySize < expectedSize)
|
||||
const u32 expectedSize = GetDataSize();
|
||||
const s64 bytesRead = zip_fread(zf, GetDataPtr(), expectedSize);
|
||||
if (bytesRead != static_cast<s64>(expectedSize))
|
||||
{
|
||||
Console.WriteLn(Color_Yellow, " '%s' is incomplete (expected 0x%x bytes, loading only 0x%x bytes)",
|
||||
WX_STR(GetFilename()), expectedSize, entrySize);
|
||||
GetFilename(), expectedSize, static_cast<u32>(bytesRead));
|
||||
}
|
||||
|
||||
uint copylen = std::min(entrySize, expectedSize);
|
||||
reader.Read(GetDataPtr(), copylen);
|
||||
}
|
||||
|
||||
void MemorySavestateEntry::FreezeOut(SaveStateBase& writer) const
|
||||
|
@ -554,14 +505,14 @@ class SavestateEntry_EmotionMemory : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_EmotionMemory() = default;
|
||||
|
||||
wxString GetFilename() const { return L"eeMemory.bin"; }
|
||||
const char* GetFilename() const { return "eeMemory.bin"; }
|
||||
u8* GetDataPtr() const { return eeMem->Main; }
|
||||
uint GetDataSize() const { return sizeof(eeMem->Main); }
|
||||
|
||||
virtual void FreezeIn(pxInputStream& reader) const
|
||||
virtual void FreezeIn(zip_file_t* zf) const
|
||||
{
|
||||
SysClearExecutionCache();
|
||||
MemorySavestateEntry::FreezeIn(reader);
|
||||
MemorySavestateEntry::FreezeIn(zf);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -570,7 +521,7 @@ class SavestateEntry_IopMemory : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_IopMemory() = default;
|
||||
|
||||
wxString GetFilename() const { return L"iopMemory.bin"; }
|
||||
const char* GetFilename() const { return "iopMemory.bin"; }
|
||||
u8* GetDataPtr() const { return iopMem->Main; }
|
||||
uint GetDataSize() const { return sizeof(iopMem->Main); }
|
||||
};
|
||||
|
@ -580,7 +531,7 @@ class SavestateEntry_HwRegs : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_HwRegs() = default;
|
||||
|
||||
wxString GetFilename() const { return L"eeHwRegs.bin"; }
|
||||
const char* GetFilename() const { return "eeHwRegs.bin"; }
|
||||
u8* GetDataPtr() const { return eeHw; }
|
||||
uint GetDataSize() const { return sizeof(eeHw); }
|
||||
};
|
||||
|
@ -590,7 +541,7 @@ class SavestateEntry_IopHwRegs : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_IopHwRegs() = default;
|
||||
|
||||
wxString GetFilename() const { return L"iopHwRegs.bin"; }
|
||||
const char* GetFilename() const { return "iopHwRegs.bin"; }
|
||||
u8* GetDataPtr() const { return iopHw; }
|
||||
uint GetDataSize() const { return sizeof(iopHw); }
|
||||
};
|
||||
|
@ -600,7 +551,7 @@ class SavestateEntry_Scratchpad : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_Scratchpad() = default;
|
||||
|
||||
wxString GetFilename() const { return L"Scratchpad.bin"; }
|
||||
const char* GetFilename() const { return "Scratchpad.bin"; }
|
||||
u8* GetDataPtr() const { return eeMem->Scratch; }
|
||||
uint GetDataSize() const { return sizeof(eeMem->Scratch); }
|
||||
};
|
||||
|
@ -610,7 +561,7 @@ class SavestateEntry_VU0mem : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_VU0mem() = default;
|
||||
|
||||
wxString GetFilename() const { return L"vu0Memory.bin"; }
|
||||
const char* GetFilename() const { return "vu0Memory.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[0].Mem; }
|
||||
uint GetDataSize() const { return VU0_MEMSIZE; }
|
||||
};
|
||||
|
@ -620,7 +571,7 @@ class SavestateEntry_VU1mem : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_VU1mem() = default;
|
||||
|
||||
wxString GetFilename() const { return L"vu1Memory.bin"; }
|
||||
const char* GetFilename() const { return "vu1Memory.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[1].Mem; }
|
||||
uint GetDataSize() const { return VU1_MEMSIZE; }
|
||||
};
|
||||
|
@ -630,7 +581,7 @@ class SavestateEntry_VU0prog : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_VU0prog() = default;
|
||||
|
||||
wxString GetFilename() const { return L"vu0MicroMem.bin"; }
|
||||
const char* GetFilename() const { return "vu0MicroMem.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[0].Micro; }
|
||||
uint GetDataSize() const { return VU0_PROGSIZE; }
|
||||
};
|
||||
|
@ -640,7 +591,7 @@ class SavestateEntry_VU1prog : public MemorySavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_VU1prog() = default;
|
||||
|
||||
wxString GetFilename() const { return L"vu1MicroMem.bin"; }
|
||||
const char* GetFilename() const { return "vu1MicroMem.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[1].Micro; }
|
||||
uint GetDataSize() const { return VU1_PROGSIZE; }
|
||||
};
|
||||
|
@ -650,8 +601,8 @@ class SavestateEntry_SPU2 : public BaseSavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_SPU2() = default;
|
||||
|
||||
wxString GetFilename() const { return L"SPU2.bin"; }
|
||||
void FreezeIn(pxInputStream& reader) const { return SysState_ComponentFreezeIn(reader, SPU2); }
|
||||
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; }
|
||||
};
|
||||
|
@ -661,8 +612,8 @@ class SavestateEntry_USB : public BaseSavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_USB() = default;
|
||||
|
||||
wxString GetFilename() const { return L"USB.bin"; }
|
||||
void FreezeIn(pxInputStream& reader) const { return SysState_ComponentFreezeIn(reader, USB); }
|
||||
const char* GetFilename() const { return "USB.bin"; }
|
||||
void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, USB); }
|
||||
void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, USB); }
|
||||
bool IsRequired() const { return false; }
|
||||
};
|
||||
|
@ -672,8 +623,8 @@ class SavestateEntry_PAD : public BaseSavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_PAD() = default;
|
||||
|
||||
wxString GetFilename() const { return L"PAD.bin"; }
|
||||
void FreezeIn(pxInputStream& reader) const { return SysState_ComponentFreezeIn(reader, PAD_); }
|
||||
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; }
|
||||
};
|
||||
|
@ -683,8 +634,8 @@ class SavestateEntry_GS : public BaseSavestateEntry
|
|||
public:
|
||||
virtual ~SavestateEntry_GS() = default;
|
||||
|
||||
wxString GetFilename() const { return L"GS.bin"; }
|
||||
void FreezeIn(pxInputStream& reader) const { return SysState_ComponentFreezeIn(reader, GS); }
|
||||
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 IsRequired() const { return true; }
|
||||
};
|
||||
|
@ -714,35 +665,7 @@ static const std::unique_ptr<BaseSavestateEntry> SavestateEntries[] = {
|
|||
std::unique_ptr<BaseSavestateEntry>(new SavestateEntry_GS),
|
||||
};
|
||||
|
||||
// It's bad mojo to have savestates trying to read and write from the same file at the
|
||||
// same time. To prevent that we use this mutex lock, which is used by both the
|
||||
// CompressThread and the UnzipFromDisk events. (note that CompressThread locks the
|
||||
// mutex during OnStartInThread, which ensures that the ZipToDisk event blocks; preventing
|
||||
// the SysExecutor's Idle Event from re-enabing savestates and slots.)
|
||||
//
|
||||
static Mutex mtx_CompressToDisk;
|
||||
|
||||
static void CheckVersion(pxInputStream& thr)
|
||||
{
|
||||
u32 savever;
|
||||
thr.Read(savever);
|
||||
|
||||
// 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(thr.GetStreamName())
|
||||
.SetDiagMsg(pxsFmt(L"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."));
|
||||
|
||||
// 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(thr.GetStreamName())
|
||||
.SetDiagMsg(pxsFmt(L"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."));
|
||||
};
|
||||
|
||||
void SaveState_DownloadState(ArchiveEntryList* destlist)
|
||||
std::unique_ptr<ArchiveEntryList> SaveState_DownloadState()
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
if (!GetCoreThread().HasActiveMachine())
|
||||
|
@ -751,6 +674,8 @@ void SaveState_DownloadState(ArchiveEntryList* destlist)
|
|||
.SetUserMsg(_("There is no active virtual machine state to download or save."));
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ArchiveEntryList> destlist = std::make_unique<ArchiveEntryList>(new VmStateBuffer(L"Zippable Savestate"));
|
||||
|
||||
memSavingState saveme(destlist->GetBuffer());
|
||||
ArchiveEntry internals(EntryFilename_InternalStructures);
|
||||
internals.SetDataIndex(saveme.GetCurrentPos());
|
||||
|
@ -770,6 +695,8 @@ void SaveState_DownloadState(ArchiveEntryList* destlist)
|
|||
.SetDataIndex(startpos)
|
||||
.SetDataSize(saveme.GetCurrentPos() - startpos));
|
||||
}
|
||||
|
||||
return destlist;
|
||||
}
|
||||
|
||||
std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot()
|
||||
|
@ -791,21 +718,29 @@ std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot()
|
|||
return data;
|
||||
}
|
||||
|
||||
static bool SaveState_CompressScreenshot(SaveStateScreenshotData* data, wxZipOutputStream* gzfp)
|
||||
static bool SaveState_CompressScreenshot(SaveStateScreenshotData* data, zip_t* zf)
|
||||
{
|
||||
zip_error_t ze = {};
|
||||
zip_source_t* const zs = zip_source_buffer_create(nullptr, 0, 0, &ze);
|
||||
if (!zs)
|
||||
return false;
|
||||
|
||||
if (zip_source_begin_write(zs) != 0)
|
||||
{
|
||||
zip_source_free(zs);
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedGuard zs_free([zs]() { zip_source_free(zs); });
|
||||
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
png_infop info_ptr = nullptr;
|
||||
if (!png_ptr)
|
||||
return false;
|
||||
|
||||
wxZipEntry* const vent = new wxZipEntry(EntryFilename_Screenshot);
|
||||
vent->SetMethod(wxZIP_METHOD_STORE);
|
||||
gzfp->PutNextEntry(vent);
|
||||
|
||||
ScopedGuard cleanup([&png_ptr, &info_ptr, gzfp]() {
|
||||
ScopedGuard cleanup([&png_ptr, &info_ptr]() {
|
||||
if (png_ptr)
|
||||
png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : nullptr);
|
||||
gzfp->CloseEntry();
|
||||
});
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
|
@ -815,8 +750,8 @@ static bool SaveState_CompressScreenshot(SaveStateScreenshotData* data, wxZipOut
|
|||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
return false;
|
||||
|
||||
png_set_write_fn(png_ptr, gzfp, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) {
|
||||
((wxZipOutputStream*)png_get_io_ptr(png_ptr))->Write(data_ptr, size);
|
||||
png_set_write_fn(png_ptr, zs, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) {
|
||||
zip_source_write(static_cast<zip_source_t*>(png_get_io_ptr(png_ptr)), data_ptr, size);
|
||||
}, [](png_structp png_ptr) {});
|
||||
png_set_compression_level(png_ptr, 5);
|
||||
png_set_IHDR(png_ptr, info_ptr, data->width, data->height, 8, PNG_COLOR_TYPE_RGBA,
|
||||
|
@ -834,213 +769,224 @@ static bool SaveState_CompressScreenshot(SaveStateScreenshotData* data, wxZipOut
|
|||
}
|
||||
|
||||
png_write_end(png_ptr, nullptr);
|
||||
|
||||
if (zip_source_commit_write(zs) != 0)
|
||||
return false;
|
||||
|
||||
const s64 file_index = zip_file_add(zf, EntryFilename_Screenshot, zs, 0);
|
||||
if (file_index < 0)
|
||||
return false;
|
||||
|
||||
// png is already compressed, no point doing it twice
|
||||
zip_set_file_compression(zf, file_index, ZIP_CM_STORE, 0);
|
||||
|
||||
// source is now owned by the zip file for later compression
|
||||
zs_free.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// CompressThread_VmState
|
||||
// --------------------------------------------------------------------------------------
|
||||
static void ZipStateToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::unique_ptr<wxFFileOutputStream> outbase, wxString filename, wxString tempfile, s32 slot_for_message)
|
||||
static void ZipStateToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot,
|
||||
std::string filename, s32 slot_for_message)
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
wxGetApp().StartPendingSave();
|
||||
#endif
|
||||
|
||||
std::unique_ptr<pxOutputStream> out(new pxOutputStream(tempfile, new wxZipOutputStream(outbase.release())));
|
||||
wxZipOutputStream* gzfp = (wxZipOutputStream*)out->GetWxStreamBase();
|
||||
|
||||
zip_error_t ze = {};
|
||||
auto zf = zip_open_managed(filename.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &ze);
|
||||
if (!zf)
|
||||
{
|
||||
wxZipEntry* vent = new wxZipEntry(EntryFilename_StateVersion);
|
||||
vent->SetMethod(wxZIP_METHOD_STORE);
|
||||
gzfp->PutNextEntry(vent);
|
||||
out->Write(g_SaveVersion);
|
||||
gzfp->CloseEntry();
|
||||
Console.Error("Failed to open zip file '%s' for save state: %s", filename.c_str(), zip_error_strerror(&ze));
|
||||
return;
|
||||
}
|
||||
|
||||
if (screenshot)
|
||||
SaveState_CompressScreenshot(screenshot.get(), gzfp);
|
||||
// discard zip file if we fail saving something
|
||||
ScopedGuard zip_discarder([&zf]() { zip_discard(zf.release()); });
|
||||
|
||||
uint listlen = srclist->GetLength();
|
||||
// use deflate compression by default
|
||||
// TODO: switch to zstd
|
||||
constexpr u32 compression = ZIP_CM_DEFLATE;
|
||||
constexpr u32 compression_level = 0;
|
||||
|
||||
// version indicator
|
||||
{
|
||||
zip_source_t* const zs = zip_source_buffer(zf.get(), &g_SaveVersion, sizeof(g_SaveVersion), 0);
|
||||
if (!zs)
|
||||
return;
|
||||
|
||||
// NOTE: Source should not be freed if successful.
|
||||
const s64 fi = zip_file_add(zf.get(), EntryFilename_StateVersion, zs, ZIP_FL_ENC_UTF_8);
|
||||
if (fi < 0)
|
||||
{
|
||||
zip_source_free(zs);
|
||||
return;
|
||||
}
|
||||
|
||||
zip_set_file_compression(zf.get(), fi, ZIP_CM_STORE, 0);
|
||||
}
|
||||
|
||||
const uint listlen = srclist->GetLength();
|
||||
for (uint i = 0; i < listlen; ++i)
|
||||
{
|
||||
const ArchiveEntry& entry = (*srclist)[i];
|
||||
if (!entry.GetDataSize())
|
||||
continue;
|
||||
|
||||
gzfp->PutNextEntry(entry.GetFilename());
|
||||
zip_source_t* const zs = zip_source_buffer(zf.get(), srclist->GetPtr(entry.GetDataIndex()), entry.GetDataSize(), 0);
|
||||
if (!zs)
|
||||
return;
|
||||
|
||||
static const uint BlockSize = 0x64000;
|
||||
uint curidx = 0;
|
||||
|
||||
do
|
||||
const s64 fi = zip_file_add(zf.get(), entry.GetFilename().c_str(), zs, ZIP_FL_ENC_UTF_8);
|
||||
if (fi < 0)
|
||||
{
|
||||
uint thisBlockSize = std::min(BlockSize, entry.GetDataSize() - curidx);
|
||||
gzfp->Write(srclist->GetPtr(entry.GetDataIndex() + curidx), thisBlockSize);
|
||||
curidx += thisBlockSize;
|
||||
} while (curidx < entry.GetDataSize());
|
||||
zip_source_free(zs);
|
||||
return;
|
||||
}
|
||||
|
||||
gzfp->CloseEntry();
|
||||
zip_set_file_compression(zf.get(), fi, compression, compression_level);
|
||||
}
|
||||
|
||||
gzfp->Close();
|
||||
if (screenshot)
|
||||
SaveState_CompressScreenshot(screenshot.get(), zf.get());
|
||||
|
||||
if (!wxRenameFile(out->GetStreamName(), filename, true))
|
||||
{
|
||||
Console.Error("Failed to rename save state '%s' to '%s'", static_cast<const char*>(out->GetStreamName().c_str()), static_cast<const char*>(filename.c_str()));
|
||||
#ifndef PCSX2_CORE
|
||||
Msgbox::Alert(_("The savestate was not properly saved. The temporary file was created successfully but could not be moved to its final resting place."));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("(gzipThread) Data saved to disk without error.");
|
||||
}
|
||||
// force the zip to close, this is the expensive part with libzip.
|
||||
zf.reset();
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
if (slot_for_message >= 0 && VMManager::HasValidVM())
|
||||
Host::AddKeyedFormattedOSDMessage(StringUtil::StdStringFromFormat("SaveStateSlot%d", slot_for_message), 10.0f, "State saved to slot %d.", slot_for_message);
|
||||
#endif
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#else
|
||||
Console.WriteLn("(gzipThread) Data saved to disk without error.");
|
||||
wxGetApp().ClearPendingSave();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SaveState_ZipToDisk(ArchiveEntryList* srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, const wxString& filename, s32 slot_for_message)
|
||||
void SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message)
|
||||
{
|
||||
// Provisionals for scoped cleanup, in case of exception:
|
||||
std::unique_ptr<ArchiveEntryList> elist(srclist);
|
||||
|
||||
wxString tempfile(filename + L".tmp");
|
||||
std::unique_ptr<wxFFileOutputStream> out = std::make_unique<wxFFileOutputStream>(tempfile);
|
||||
if (!out->IsOk())
|
||||
throw Exception::CannotCreateStream(tempfile);
|
||||
|
||||
std::thread threaded_save(ZipStateToDiskOnThread, std::move(elist), std::move(screenshot), std::move(out), filename, tempfile, slot_for_message);
|
||||
std::thread threaded_save(ZipStateToDiskOnThread, std::move(srclist), std::move(screenshot), std::move(filename), slot_for_message);
|
||||
threaded_save.detach();
|
||||
}
|
||||
|
||||
void SaveState_UnzipFromDisk(const wxString& filename)
|
||||
static void CheckVersion(const std::string& filename, zip_t* zf)
|
||||
{
|
||||
ScopedLock lock(mtx_CompressToDisk);
|
||||
u32 savever;
|
||||
|
||||
// Ugh. Exception handling made crappy because wxWidgets classes don't support scoped pointers yet.
|
||||
|
||||
std::unique_ptr<wxFFileInputStream> woot(new wxFFileInputStream(filename));
|
||||
if (!woot->IsOk())
|
||||
throw Exception::CannotCreateStream(filename).SetDiagMsg(L"Cannot open file for reading.");
|
||||
|
||||
std::unique_ptr<pxInputStream> reader(new pxInputStream(filename, new wxZipInputStream(woot.get())));
|
||||
woot.release();
|
||||
|
||||
if (!reader->IsOk())
|
||||
auto zff = zip_fopen_managed(zf, EntryFilename_StateVersion, 0);
|
||||
if (!zff || zip_fread(zff.get(), &savever, sizeof(savever)) != sizeof(savever))
|
||||
{
|
||||
throw Exception::SaveStateLoadError(filename)
|
||||
throw Exception::SaveStateLoadError(StringUtil::UTF8StringToWxString(filename))
|
||||
.SetDiagMsg(L"Savestate file does not contain version indicator.")
|
||||
.SetUserMsg(_("This file is not a valid PCSX2 savestate. See the logfile for details."));
|
||||
}
|
||||
|
||||
// Major version mismatch. Means we can't load this savestate at all. Support for it
|
||||
// was removed entirely.
|
||||
if (savever > g_SaveVersion)
|
||||
throw Exception::SaveStateLoadError(StringUtil::UTF8StringToWxString(filename))
|
||||
.SetDiagMsg(pxsFmt(L"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."));
|
||||
|
||||
// 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(StringUtil::UTF8StringToWxString(filename))
|
||||
.SetDiagMsg(pxsFmt(L"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."));
|
||||
}
|
||||
|
||||
static zip_int64_t CheckFileExistsInState(zip_t* zf, const char* name)
|
||||
{
|
||||
zip_int64_t index = zip_name_locate(zf, name, /*ZIP_FL_NOCASE*/ 0);
|
||||
if (index >= 0)
|
||||
{
|
||||
DevCon.WriteLn(Color_Green, " ... found '%s'", name);
|
||||
return index;
|
||||
}
|
||||
|
||||
Console.WriteLn(Color_Red, " ... not found '%s'!", name);
|
||||
return index;
|
||||
}
|
||||
|
||||
static bool LoadInternalStructuresState(zip_t* zf, s64 index)
|
||||
{
|
||||
zip_stat_t zst;
|
||||
if (zip_stat_index(zf, index, 0, &zst) != 0 || zst.size > std::numeric_limits<int>::max())
|
||||
return false;
|
||||
|
||||
// Load all the internal data
|
||||
auto zff = zip_fopen_index_managed(zf, index, 0);
|
||||
if (!zff)
|
||||
return false;
|
||||
|
||||
VmStateBuffer buffer(static_cast<int>(zst.size), L"StateBuffer_UnzipFromDisk"); // start with an 8 meg buffer to avoid frequent reallocation.
|
||||
if (zip_fread(zff.get(), buffer.GetPtr(), buffer.GetSizeInBytes()) != buffer.GetSizeInBytes())
|
||||
return false;
|
||||
|
||||
memLoadingState(buffer).FreezeBios().FreezeInternals();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveState_UnzipFromDisk(const std::string& filename)
|
||||
{
|
||||
zip_error_t ze = {};
|
||||
auto zf = zip_open_managed(filename.c_str(), ZIP_RDONLY, &ze);
|
||||
if (!zf)
|
||||
{
|
||||
Console.Error("Failed to open zip file '%s' for save state load: %s", filename.c_str(), zip_error_strerror(&ze));
|
||||
throw Exception::SaveStateLoadError(StringUtil::UTF8StringToWxString(filename))
|
||||
.SetDiagMsg(L"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."));
|
||||
}
|
||||
|
||||
wxZipInputStream* gzreader = (wxZipInputStream*)reader->GetWxStreamBase();
|
||||
|
||||
// look for version and screenshot information in the zip stream:
|
||||
CheckVersion(filename, zf.get());
|
||||
|
||||
bool foundVersion = false;
|
||||
//bool foundScreenshot = false;
|
||||
//bool foundEntry[ArraySize(SavestateEntries)] = false;
|
||||
|
||||
std::unique_ptr<wxZipEntry> foundInternal;
|
||||
std::unique_ptr<wxZipEntry> foundEntry[std::size(SavestateEntries)];
|
||||
|
||||
while (true)
|
||||
{
|
||||
Threading::pxTestCancel();
|
||||
|
||||
std::unique_ptr<wxZipEntry> entry(gzreader->GetNextEntry());
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
if (entry->GetName().CmpNoCase(EntryFilename_StateVersion) == 0)
|
||||
{
|
||||
DevCon.WriteLn(Color_Green, L" ... found '%s'", EntryFilename_StateVersion);
|
||||
foundVersion = true;
|
||||
CheckVersion(*reader);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry->GetName().CmpNoCase(EntryFilename_InternalStructures) == 0)
|
||||
{
|
||||
DevCon.WriteLn(Color_Green, L" ... found '%s'", EntryFilename_InternalStructures);
|
||||
foundInternal = std::move(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
// No point in finding screenshots when loading states -- the screenshots are
|
||||
// only useful for the UI savestate browser.
|
||||
/*if (entry->GetName().CmpNoCase(EntryFilename_Screenshot) == 0)
|
||||
{
|
||||
foundScreenshot = true;
|
||||
}*/
|
||||
|
||||
for (uint i = 0; i < std::size(SavestateEntries); ++i)
|
||||
{
|
||||
if (entry->GetName().CmpNoCase(SavestateEntries[i]->GetFilename()) == 0)
|
||||
{
|
||||
DevCon.WriteLn(Color_Green, L" ... found '%s'", WX_STR(SavestateEntries[i]->GetFilename()));
|
||||
foundEntry[i] = std::move(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundVersion || !foundInternal)
|
||||
{
|
||||
throw Exception::SaveStateLoadError(filename)
|
||||
.SetDiagMsg(pxsFmt(L"Savestate file does not contain '%s'",
|
||||
!foundVersion ? EntryFilename_StateVersion : EntryFilename_InternalStructures))
|
||||
.SetUserMsg(_("This file is not a valid PCSX2 savestate. See the logfile for details."));
|
||||
}
|
||||
// check that all parts are included
|
||||
const s64 internal_index = CheckFileExistsInState(zf.get(), EntryFilename_InternalStructures);
|
||||
s64 entryIndices[std::size(SavestateEntries)];
|
||||
|
||||
// Log any parts and pieces that are missing, and then generate an exception.
|
||||
bool throwIt = false;
|
||||
for (uint i = 0; i < std::size(SavestateEntries); ++i)
|
||||
bool throwIt = (internal_index < 0);
|
||||
for (u32 i = 0; i < std::size(SavestateEntries); i++)
|
||||
{
|
||||
if (foundEntry[i])
|
||||
continue;
|
||||
|
||||
if (SavestateEntries[i]->IsRequired())
|
||||
{
|
||||
entryIndices[i] = CheckFileExistsInState(zf.get(), SavestateEntries[i]->GetFilename());
|
||||
if (entryIndices[i] < 0 && SavestateEntries[i]->IsRequired())
|
||||
throwIt = true;
|
||||
Console.WriteLn(Color_Red, " ... not found '%s'!", WX_STR(SavestateEntries[i]->GetFilename()));
|
||||
}
|
||||
|
||||
if (!throwIt)
|
||||
{
|
||||
PreLoadPrep();
|
||||
throwIt = !LoadInternalStructuresState(zf.get(), internal_index);
|
||||
}
|
||||
|
||||
if (!throwIt)
|
||||
{
|
||||
for (u32 i = 0; i < std::size(SavestateEntries); ++i)
|
||||
{
|
||||
if (entryIndices[i] < 0)
|
||||
continue;
|
||||
|
||||
auto zff = zip_fopen_index_managed(zf.get(), entryIndices[i], 0);
|
||||
if (!zff)
|
||||
{
|
||||
throwIt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
SavestateEntries[i]->FreezeIn(zff.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (throwIt)
|
||||
throw Exception::SaveStateLoadError(filename)
|
||||
{
|
||||
throw Exception::SaveStateLoadError(StringUtil::UTF8StringToWxString(filename))
|
||||
.SetDiagMsg(L"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."));
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
PatchesVerboseReset();
|
||||
#endif
|
||||
SysClearExecutionCache();
|
||||
|
||||
for (uint i = 0; i < std::size(SavestateEntries); ++i)
|
||||
{
|
||||
if (!foundEntry[i])
|
||||
continue;
|
||||
|
||||
Threading::pxTestCancel();
|
||||
|
||||
gzreader->OpenEntry(*foundEntry[i]);
|
||||
SavestateEntries[i]->FreezeIn(*reader);
|
||||
}
|
||||
|
||||
// Load all the internal data
|
||||
|
||||
gzreader->OpenEntry(*foundInternal);
|
||||
|
||||
VmStateBuffer buffer(foundInternal->GetSize(), L"StateBuffer_UnzipFromDisk"); // start with an 8 meg buffer to avoid frequent reallocation.
|
||||
reader->Read(buffer.GetPtr(), foundInternal->GetSize());
|
||||
|
||||
memLoadingState(buffer).FreezeBios().FreezeInternals();
|
||||
PostLoadPrep();
|
||||
}
|
||||
|
|
|
@ -56,10 +56,10 @@ class ArchiveEntryList;
|
|||
|
||||
// Wrappers to generate a save state compatible across all frontends.
|
||||
// These functions assume that the caller has paused the core thread.
|
||||
extern void SaveState_DownloadState(ArchiveEntryList* destlist);
|
||||
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState();
|
||||
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
||||
extern void SaveState_ZipToDisk(ArchiveEntryList* srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, const wxString& filename, s32 slot_for_message);
|
||||
extern void SaveState_UnzipFromDisk(const wxString& filename);
|
||||
extern void SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message);
|
||||
extern void SaveState_UnzipFromDisk(const std::string& filename);
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SaveStateBase class
|
||||
|
@ -93,7 +93,6 @@ public:
|
|||
return (m_version & 0xffff);
|
||||
}
|
||||
|
||||
virtual SaveStateBase& FreezeMainMemory();
|
||||
virtual SaveStateBase& FreezeBios();
|
||||
virtual SaveStateBase& FreezeInternals();
|
||||
|
||||
|
@ -196,13 +195,13 @@ protected:
|
|||
class ArchiveEntry
|
||||
{
|
||||
protected:
|
||||
wxString m_filename;
|
||||
std::string m_filename;
|
||||
uptr m_dataidx;
|
||||
size_t m_datasize;
|
||||
|
||||
public:
|
||||
ArchiveEntry(const wxString& filename = wxEmptyString)
|
||||
: m_filename(filename)
|
||||
ArchiveEntry(std::string filename)
|
||||
: m_filename(std::move(filename))
|
||||
{
|
||||
m_dataidx = 0;
|
||||
m_datasize = 0;
|
||||
|
@ -222,7 +221,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
wxString GetFilename() const
|
||||
const std::string& GetFilename() const
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
|
|
|
@ -845,7 +845,7 @@ bool VMManager::DoLoadState(const char* filename)
|
|||
try
|
||||
{
|
||||
Host::OnSaveStateLoading(filename);
|
||||
SaveState_UnzipFromDisk(wxString::FromUTF8(filename));
|
||||
SaveState_UnzipFromDisk(filename);
|
||||
UpdateRunningGame(false);
|
||||
Host::OnSaveStateLoaded(filename, true);
|
||||
return true;
|
||||
|
@ -865,9 +865,8 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message)
|
|||
|
||||
try
|
||||
{
|
||||
std::unique_ptr<ArchiveEntryList> elist = std::make_unique<ArchiveEntryList>(new VmStateBuffer(L"Zippable Savestate"));
|
||||
SaveState_DownloadState(elist.get());
|
||||
SaveState_ZipToDisk(elist.release(), SaveState_SaveScreenshot(), wxString::FromUTF8(filename), slot_for_message);
|
||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState();
|
||||
SaveState_ZipToDisk(std::move(elist), SaveState_SaveScreenshot(), filename, slot_for_message);
|
||||
Host::InvalidateSaveStateCache();
|
||||
Host::OnSaveStateSaved(filename);
|
||||
return true;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "SaveState.h"
|
||||
#include "VUmicro.h"
|
||||
|
||||
#include "common/pxStreams.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "SPU2/spu2.h"
|
||||
#include "USB/USB.h"
|
||||
#include "PAD/Gamepad.h"
|
||||
|
@ -38,27 +38,18 @@
|
|||
#include "ps2/BiosTools.h"
|
||||
#include "Elfheader.h"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_DownloadState
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Pauses core emulation and downloads the savestate into a memory buffer. The memory buffer
|
||||
// is then mailed to another thread for zip archiving, while the main emulation process is
|
||||
// allowed to continue execution.
|
||||
//
|
||||
class SysExecEvent_DownloadState : public SysExecEvent
|
||||
class SysExecEvent_SaveState : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
ArchiveEntryList* m_dest_list;
|
||||
wxString m_filename;
|
||||
|
||||
public:
|
||||
wxString GetEventName() const { return L"VM_Download"; }
|
||||
|
||||
virtual ~SysExecEvent_DownloadState() = default;
|
||||
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState(*this); }
|
||||
SysExecEvent_DownloadState(ArchiveEntryList* dest_list = NULL)
|
||||
{
|
||||
m_dest_list = dest_list;
|
||||
}
|
||||
SysExecEvent_SaveState(const wxString& filename) : m_filename(filename) {}
|
||||
virtual ~SysExecEvent_SaveState() = default;
|
||||
|
||||
SysExecEvent_SaveState* Clone() const { return new SysExecEvent_SaveState(*this); }
|
||||
|
||||
bool IsCriticalEvent() const { return true; }
|
||||
bool AllowCancelOnExit() const { return false; }
|
||||
|
@ -67,54 +58,10 @@ protected:
|
|||
void InvokeEvent()
|
||||
{
|
||||
ScopedCoreThreadPause paused_core;
|
||||
SaveState_DownloadState(m_dest_list);
|
||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState();
|
||||
UI_EnableStateActions();
|
||||
paused_core.AllowResume();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_ZipToDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_ZipToDisk : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
ArchiveEntryList* m_src_list;
|
||||
wxString m_filename;
|
||||
|
||||
public:
|
||||
wxString GetEventName() const { return L"VM_ZipToDisk"; }
|
||||
|
||||
virtual ~SysExecEvent_ZipToDisk() = default;
|
||||
|
||||
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk(*this); }
|
||||
|
||||
SysExecEvent_ZipToDisk(ArchiveEntryList& srclist, const wxString& filename)
|
||||
: m_filename(filename)
|
||||
{
|
||||
m_src_list = &srclist;
|
||||
}
|
||||
|
||||
SysExecEvent_ZipToDisk(ArchiveEntryList* srclist, const wxString& filename)
|
||||
: m_filename(filename)
|
||||
{
|
||||
m_src_list = srclist;
|
||||
}
|
||||
|
||||
bool IsCriticalEvent() const { return true; }
|
||||
bool AllowCancelOnExit() const { return false; }
|
||||
|
||||
protected:
|
||||
void InvokeEvent()
|
||||
{
|
||||
SaveState_ZipToDisk(m_src_list, nullptr, m_filename, -1);
|
||||
}
|
||||
|
||||
void CleanupEvent()
|
||||
{
|
||||
SaveState_ZipToDisk(std::move(elist), nullptr, StringUtil::wxStringToUTF8String(m_filename), -1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -148,7 +95,7 @@ protected:
|
|||
// We use direct Suspend/Resume control here, since it's desirable that emulation
|
||||
// *ALWAYS* start execution after the new savestate is loaded.
|
||||
GetCoreThread().Pause({});
|
||||
SaveState_UnzipFromDisk(m_filename);
|
||||
SaveState_UnzipFromDisk(StringUtil::wxStringToUTF8String(m_filename));
|
||||
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
|
||||
}
|
||||
};
|
||||
|
@ -160,13 +107,7 @@ protected:
|
|||
void StateCopy_SaveToFile(const wxString& file)
|
||||
{
|
||||
UI_DisableStateActions();
|
||||
|
||||
std::unique_ptr<ArchiveEntryList> ziplist(new ArchiveEntryList(new VmStateBuffer(L"Zippable Savestate")));
|
||||
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState(ziplist.get()));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk(ziplist.get(), file));
|
||||
|
||||
ziplist.release();
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_SaveState(file));
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromFile(const wxString& file)
|
||||
|
|
|
@ -42,13 +42,14 @@
|
|||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\cubeb\cubeb\include;$(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\imgui\imgui;$(SolutionDir)3rdparty\imgui\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions>/Zc:externConstexpr %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;ZIP_STATIC;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
@ -1204,6 +1205,9 @@
|
|||
<ProjectReference Include="$(SolutionDir)3rdparty\imgui\imgui.vcxproj">
|
||||
<Project>{88fb34ec-845e-4f21-a552-f1573b9ed167}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdparty\libzip\libzip.vcxproj">
|
||||
<Project>{20b2e9fe-f020-42a0-b324-956f5b06ea68}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -1216,4 +1220,4 @@
|
|||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -46,13 +46,14 @@
|
|||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\imgui\imgui;$(SolutionDir)3rdparty\imgui\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\sdl2\include;$(SolutionDir)3rdparty\sdl2\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions>/Zc:externConstexpr %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;SDL_BUILD;PCSX2_CORE;DISABLE_RECORDING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;ZIP_STATIC;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;SDL_BUILD;PCSX2_CORE;DISABLE_RECORDING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
@ -790,6 +791,9 @@
|
|||
<ProjectReference Include="$(SolutionDir)3rdparty\imgui\imgui.vcxproj">
|
||||
<Project>{88fb34ec-845e-4f21-a552-f1573b9ed167}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdparty\libzip\libzip.vcxproj">
|
||||
<Project>{20b2e9fe-f020-42a0-b324-956f5b06ea68}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdparty\sdl2\SDL.vcxproj">
|
||||
<Project>{812b4434-fd6b-4cb2-8865-5fd8eb34b046}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -802,4 +806,4 @@
|
|||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue