SaveState: Use libzip instead of wx

This commit is contained in:
Connor McLaughlin 2022-04-11 22:15:44 +10:00 committed by refractionpcsx2
parent 9aa2c52b20
commit dd8a645986
10 changed files with 383 additions and 370 deletions

115
common/ZipHelpers.h Normal file
View File

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

View File

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

View File

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

View File

@ -1598,6 +1598,7 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
fmt::fmt
ryml
chdr-static
zip
wxWidgets::all
ZLIB::ZLIB
PkgConfig::SOUNDTOUCH

View File

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

View File

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

View File

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

View File

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

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

View File

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