mirror of https://github.com/PCSX2/pcsx2.git
wxSavestates branch: (WIP, does not compile yet)
* Preliminary implementation of wx-based zip support (using wxZipInputStream and wxZipOutputStream). git-svn-id: http://pcsx2.googlecode.com/svn/branches/wxSavestates@4033 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
e90fa1af8d
commit
5efe38b270
|
@ -95,7 +95,7 @@ extern bool SIGNAL_IMR_Pending;
|
||||||
|
|
||||||
__fi void gsInterrupt()
|
__fi void gsInterrupt()
|
||||||
{
|
{
|
||||||
GIF_LOG("gsInterrupt: %8.8x", cpuRegs.cycle);
|
GIF_LOG("gsInterrupt caught!");
|
||||||
|
|
||||||
if(SIGNAL_IMR_Pending == true)
|
if(SIGNAL_IMR_Pending == true)
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,7 +84,6 @@ typedef int BOOL;
|
||||||
// unchanged for long periods of time, or happen to be used by almost everything, so they
|
// unchanged for long periods of time, or happen to be used by almost everything, so they
|
||||||
// need a full recompile anyway, when modified (etc)
|
// need a full recompile anyway, when modified (etc)
|
||||||
|
|
||||||
#include "zlib.h"
|
|
||||||
#include "Pcsx2Defs.h"
|
#include "Pcsx2Defs.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ void SaveStateBase::PrepBlock( int size )
|
||||||
void SaveStateBase::FreezeTag( const char* src )
|
void SaveStateBase::FreezeTag( const char* src )
|
||||||
{
|
{
|
||||||
const uint allowedlen = sizeof( m_tagspace )-1;
|
const uint allowedlen = sizeof( m_tagspace )-1;
|
||||||
pxAssertDev( strlen(src) < allowedlen, wxsFormat( L"Tag name exceeds the allowed length of %d chars.", allowedlen) );
|
pxAssertDev( strlen(src) < allowedlen, pxsFmt( L"Tag name exceeds the allowed length of %d chars.", allowedlen) );
|
||||||
|
|
||||||
memzero( m_tagspace );
|
memzero( m_tagspace );
|
||||||
strcpy( m_tagspace, src );
|
strcpy( m_tagspace, src );
|
||||||
|
@ -141,24 +141,22 @@ void SaveStateBase::FreezeBios()
|
||||||
m_DidBios = true;
|
m_DidBios = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int MainMemorySizeInBytes =
|
static const uint MainMemorySizeInBytes =
|
||||||
Ps2MemSize::MainRam + Ps2MemSize::Scratch + Ps2MemSize::Hardware +
|
Ps2MemSize::MainRam + Ps2MemSize::Scratch + Ps2MemSize::Hardware +
|
||||||
Ps2MemSize::IopRam + Ps2MemSize::IopHardware + 0x0100;
|
Ps2MemSize::IopRam + Ps2MemSize::IopHardware;
|
||||||
|
|
||||||
void SaveStateBase::FreezeMainMemory()
|
void SaveStateBase::FreezeMainMemory()
|
||||||
{
|
{
|
||||||
if( IsLoading() )
|
if (IsLoading()) PreLoadPrep();
|
||||||
PreLoadPrep();
|
|
||||||
|
|
||||||
// First Block - Memory Dumps
|
// First Block - Memory Dumps
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
FreezeMem(eeMem->Main, Ps2MemSize::MainRam); // 32 MB main memory
|
FreezeMem(eeMem->Main, Ps2MemSize::MainRam); // 32 MB main memory
|
||||||
FreezeMem(eeMem->Scratch, Ps2MemSize::Scratch); // scratch pad
|
FreezeMem(eeMem->Scratch, Ps2MemSize::Scratch); // scratch pad
|
||||||
FreezeMem(eeHw, Ps2MemSize::Hardware); // hardware memory
|
FreezeMem(eeHw, Ps2MemSize::Hardware); // hardware memory
|
||||||
|
|
||||||
FreezeMem(iopMem->Main, Ps2MemSize::IopRam); // 2 MB main memory
|
FreezeMem(iopMem->Main, Ps2MemSize::IopRam); // 2 MB main memory
|
||||||
FreezeMem(iopHw, Ps2MemSize::IopHardware); // hardware memory
|
FreezeMem(iopHw, Ps2MemSize::IopHardware); // hardware memory
|
||||||
FreezeMem(iopMem->Sif, 0x000100); // iop's sif memory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::FreezeRegisters()
|
void SaveStateBase::FreezeRegisters()
|
||||||
|
@ -201,6 +199,8 @@ void SaveStateBase::FreezeRegisters()
|
||||||
// Fifth Block - iop-related systems
|
// Fifth Block - iop-related systems
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
FreezeTag( "IOP-Subsystems" );
|
FreezeTag( "IOP-Subsystems" );
|
||||||
|
FreezeMem(iopMem->Sif, sizeof(iopMem->Sif)); // iop's sif memory (not really needed, but oh well)
|
||||||
|
|
||||||
#ifdef ENABLE_NEW_IOPDMA
|
#ifdef ENABLE_NEW_IOPDMA
|
||||||
iopDmacFreeze();
|
iopDmacFreeze();
|
||||||
#endif
|
#endif
|
||||||
|
@ -231,13 +231,13 @@ void SaveStateBase::WritebackSectionLength( int seekpos, int sectlen, const wxCh
|
||||||
if( sectlen != realsectsize ) // if they don't match then we have a problem, jim.
|
if( sectlen != realsectsize ) // if they don't match then we have a problem, jim.
|
||||||
{
|
{
|
||||||
throw Exception::SaveStateLoadError()
|
throw Exception::SaveStateLoadError()
|
||||||
.SetDiagMsg(wxsFormat(L"Invalid size encountered on section '%s'.", sectname ))
|
.SetDiagMsg(pxsFmt(L"Invalid size encountered on section '%s'.", sectname ))
|
||||||
.SetUserMsg(_("The savestate data is invalid or corrupted."));
|
.SetUserMsg(_("The savestate data is invalid or corrupted."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SaveStateBase::FreezeSection( int seek_section )
|
bool SaveStateBase::FreezeSection( bool freezeMem, int seek_section )
|
||||||
{
|
{
|
||||||
const bool isSeeking = (seek_section != FreezeId_NotSeeking );
|
const bool isSeeking = (seek_section != FreezeId_NotSeeking );
|
||||||
if( IsSaving() ) pxAssertDev( !isSeeking, "Cannot seek on a saving-mode savestate stream." );
|
if( IsSaving() ) pxAssertDev( !isSeeking, "Cannot seek on a saving-mode savestate stream." );
|
||||||
|
@ -273,25 +273,28 @@ bool SaveStateBase::FreezeSection( int seek_section )
|
||||||
|
|
||||||
case FreezeId_Memory:
|
case FreezeId_Memory:
|
||||||
{
|
{
|
||||||
FreezeTag( "MainMemory" );
|
if (freezeMem)
|
||||||
|
|
||||||
int seekpos = m_idx+4;
|
|
||||||
int sectlen = MainMemorySizeInBytes;
|
|
||||||
Freeze( sectlen );
|
|
||||||
if( sectlen != MainMemorySizeInBytes )
|
|
||||||
{
|
{
|
||||||
throw Exception::SaveStateLoadError()
|
FreezeTag( "MainMemory" );
|
||||||
.SetDiagMsg(L"Invalid size encountered on MainMemory section.")
|
|
||||||
.SetUserMsg(_("The savestate data is invalid or corrupted."));
|
int seekpos = m_idx+4;
|
||||||
|
int sectlen = MainMemorySizeInBytes;
|
||||||
|
Freeze( sectlen );
|
||||||
|
if( sectlen != MainMemorySizeInBytes )
|
||||||
|
{
|
||||||
|
throw Exception::SaveStateLoadError()
|
||||||
|
.SetDiagMsg(L"Invalid size encountered on MainMemory section.")
|
||||||
|
.SetUserMsg(_("The savestate data is invalid or corrupted."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( isSeeking )
|
||||||
|
m_idx += sectlen;
|
||||||
|
else
|
||||||
|
FreezeMainMemory();
|
||||||
|
|
||||||
|
int realsectsize = m_idx - seekpos;
|
||||||
|
pxAssert( sectlen == realsectsize );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isSeeking )
|
|
||||||
m_idx += sectlen;
|
|
||||||
else
|
|
||||||
FreezeMainMemory();
|
|
||||||
|
|
||||||
int realsectsize = m_idx - seekpos;
|
|
||||||
pxAssert( sectlen == realsectsize );
|
|
||||||
m_sectid++;
|
m_sectid++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -360,7 +363,7 @@ bool SaveStateBase::FreezeSection( int seek_section )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStateBase::FreezeAll()
|
void SaveStateBase::FreezeAll( bool freezeMem )
|
||||||
{
|
{
|
||||||
if( IsSaving() )
|
if( IsSaving() )
|
||||||
{
|
{
|
||||||
|
@ -371,7 +374,7 @@ void SaveStateBase::FreezeAll()
|
||||||
m_pid = PluginId_GS;
|
m_pid = PluginId_GS;
|
||||||
}
|
}
|
||||||
|
|
||||||
while( FreezeSection() );
|
while( FreezeSection( freezeMem ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -395,15 +398,14 @@ void memSavingState::FreezeMem( void* data, int size )
|
||||||
m_idx += size;
|
m_idx += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void memSavingState::FreezeAll()
|
void memSavingState::FreezeAll( bool freezeMem )
|
||||||
{
|
{
|
||||||
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
||||||
|
|
||||||
// 90% of all savestates fit in under 45 megs (and require more than 43 megs, so might as well...)
|
|
||||||
m_memory->ChunkSize = ReallocThreshold;
|
m_memory->ChunkSize = ReallocThreshold;
|
||||||
m_memory->MakeRoomFor( MemoryBaseAllocSize );
|
m_memory->MakeRoomFor( MemoryBaseAllocSize + (freezeMem ? MainMemorySizeInBytes : 0) );
|
||||||
|
|
||||||
_parent::FreezeAll();
|
_parent::FreezeAll( freezeMem );
|
||||||
}
|
}
|
||||||
|
|
||||||
memLoadingState::memLoadingState( const SafeArray<u8>& load_from )
|
memLoadingState::memLoadingState( const SafeArray<u8>& load_from )
|
||||||
|
@ -452,22 +454,21 @@ bool memLoadingState::SeekToSection( PluginsEnum_t pid )
|
||||||
wxString Exception::UnsupportedStateVersion::FormatDiagnosticMessage() const
|
wxString Exception::UnsupportedStateVersion::FormatDiagnosticMessage() const
|
||||||
{
|
{
|
||||||
// Note: no stacktrace needed for this one...
|
// Note: no stacktrace needed for this one...
|
||||||
return wxsFormat( L"Unknown or unsupported savestate version: 0x%x", Version );
|
return pxsFmt( L"Unknown or unsupported savestate version: 0x%x", Version );
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString Exception::UnsupportedStateVersion::FormatDisplayMessage() const
|
wxString Exception::UnsupportedStateVersion::FormatDisplayMessage() const
|
||||||
{
|
{
|
||||||
// m_message_user contains a recoverable savestate error which is helpful to the user.
|
// m_message_user contains a recoverable savestate error which is helpful to the user.
|
||||||
return wxsFormat(
|
return
|
||||||
m_message_user + L"\n\n" +
|
m_message_user + L"\n\n" +
|
||||||
wxsFormat( _("Cannot load savestate. It is of an unknown or unsupported version."), Version )
|
pxsFmt( _("Cannot load savestate. It is of an unknown or unsupported version."), Version );
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString Exception::StateCrcMismatch::FormatDiagnosticMessage() const
|
wxString Exception::StateCrcMismatch::FormatDiagnosticMessage() const
|
||||||
{
|
{
|
||||||
// Note: no stacktrace needed for this one...
|
// Note: no stacktrace needed for this one...
|
||||||
return wxsFormat(
|
return pxsFmt(
|
||||||
L"Game/CDVD does not match the savestate CRC.\n"
|
L"Game/CDVD does not match the savestate CRC.\n"
|
||||||
L"\tCdvd CRC: 0x%X\n\tGame CRC: 0x%X\n",
|
L"\tCdvd CRC: 0x%X\n\tGame CRC: 0x%X\n",
|
||||||
Crc_Savestate, Crc_Cdvd
|
Crc_Savestate, Crc_Cdvd
|
||||||
|
@ -476,11 +477,10 @@ wxString Exception::StateCrcMismatch::FormatDiagnosticMessage() const
|
||||||
|
|
||||||
wxString Exception::StateCrcMismatch::FormatDisplayMessage() const
|
wxString Exception::StateCrcMismatch::FormatDisplayMessage() const
|
||||||
{
|
{
|
||||||
return wxsFormat(
|
return
|
||||||
m_message_user + L"\n\n" +
|
m_message_user + L"\n\n" +
|
||||||
wxsFormat(
|
pxsFmt(
|
||||||
L"Savestate game/crc mismatch. Cdvd CRC: 0x%X Game CRC: 0x%X\n",
|
L"Savestate game/crc mismatch. Cdvd CRC: 0x%X Game CRC: 0x%X\n",
|
||||||
Crc_Savestate, Crc_Cdvd
|
Crc_Savestate, Crc_Cdvd
|
||||||
)
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
// the lower 16 bit value. IF the change is breaking of all compatibility with old
|
// the lower 16 bit value. IF the change is breaking of all compatibility with old
|
||||||
// states, increment the upper 16 bit value, and clear the lower 16 bits to 0.
|
// states, increment the upper 16 bit value, and clear the lower 16 bits to 0.
|
||||||
|
|
||||||
static const u32 g_SaveVersion = 0x8b4c0000;
|
static const u32 g_SaveVersion = (0x9A01 << 16) | 0x0000;
|
||||||
|
|
||||||
// this function is meant to be used in the place of GSfreeze, and provides a safe layer
|
// this function is meant to be used in the place of GSfreeze, and provides a safe layer
|
||||||
// between the GS saving function and the MTGS's needs. :)
|
// between the GS saving function and the MTGS's needs. :)
|
||||||
|
@ -39,10 +39,8 @@ enum FreezeSectionId
|
||||||
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
|
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
|
||||||
// but can be skipped if the savestate has only plugins.
|
// but can be skipped if the savestate has only plugins.
|
||||||
FreezeId_Bios,
|
FreezeId_Bios,
|
||||||
|
|
||||||
FreezeId_Memory,
|
FreezeId_Memory,
|
||||||
FreezeId_Registers,
|
FreezeId_Registers,
|
||||||
|
|
||||||
FreezeId_Plugin,
|
FreezeId_Plugin,
|
||||||
|
|
||||||
// anything here and beyond we can skip, with a warning
|
// anything here and beyond we can skip, with a warning
|
||||||
|
@ -144,7 +142,7 @@ public:
|
||||||
// Loads or saves the entire emulation state.
|
// Loads or saves the entire emulation state.
|
||||||
// Note: The Cpu state must be reset, and plugins *open*, prior to Defrosting
|
// Note: The Cpu state must be reset, and plugins *open*, prior to Defrosting
|
||||||
// (loading) a state!
|
// (loading) a state!
|
||||||
virtual void FreezeAll();
|
virtual void FreezeAll( bool freezeMemory=true );
|
||||||
|
|
||||||
// Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays.
|
// Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays.
|
||||||
// For dynamically allocated pointers use FreezeMem instead.
|
// For dynamically allocated pointers use FreezeMem instead.
|
||||||
|
@ -175,7 +173,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void WritebackSectionLength( int seekpos, int sectlen, const wxChar* sectname );
|
void WritebackSectionLength( int seekpos, int sectlen, const wxChar* sectname );
|
||||||
bool FreezeSection( int seek_section = FreezeId_NotSeeking );
|
bool FreezeSection( bool freezeMem, int seek_section = FreezeId_NotSeeking );
|
||||||
|
|
||||||
// Freezes an identifier value into the savestate for troubleshooting purposes.
|
// Freezes an identifier value into the savestate for troubleshooting purposes.
|
||||||
// Identifiers can be used to determine where in a savestate that data has become
|
// Identifiers can be used to determine where in a savestate that data has become
|
||||||
|
@ -238,8 +236,8 @@ class memSavingState : public SaveStateBase
|
||||||
typedef SaveStateBase _parent;
|
typedef SaveStateBase _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const int ReallocThreshold = 0x200000; // 256k reallocation block size.
|
static const int ReallocThreshold = _1mb / 4; // 256k reallocation block size.
|
||||||
static const int MemoryBaseAllocSize = 0x02b00000; // 45 meg base alloc
|
static const int MemoryBaseAllocSize = _8mb; // 8 meg base alloc when PS2 main memory is excluded
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~memSavingState() throw() { }
|
virtual ~memSavingState() throw() { }
|
||||||
|
@ -248,7 +246,7 @@ public:
|
||||||
|
|
||||||
// Saving of state data to a memory buffer
|
// Saving of state data to a memory buffer
|
||||||
void FreezeMem( void* data, int size );
|
void FreezeMem( void* data, int size );
|
||||||
void FreezeAll();
|
void FreezeAll( bool freezeMemory );
|
||||||
|
|
||||||
bool IsSaving() const { return true; }
|
bool IsSaving() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -150,7 +150,6 @@ void SysCoreThread::UploadStateCopy( const VmStateBuffer& copy )
|
||||||
{
|
{
|
||||||
if( !pxAssertDev( IsPaused(), "CoreThread is not paused; new VM state cannot be uploaded." ) ) return;
|
if( !pxAssertDev( IsPaused(), "CoreThread is not paused; new VM state cannot be uploaded." ) ) return;
|
||||||
|
|
||||||
SysClearExecutionCache();
|
|
||||||
memLoadingState( copy ).FreezeAll();
|
memLoadingState( copy ).FreezeAll();
|
||||||
m_resetVirtualMachine = false;
|
m_resetVirtualMachine = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,92 +16,52 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Utilities/PersistentThread.h"
|
#include "Utilities/PersistentThread.h"
|
||||||
//#include <zlib/zlib.h>
|
#include "Utilities/pxStreams.h"
|
||||||
|
#include "wx/zipstrm.h"
|
||||||
|
|
||||||
using namespace Threading;
|
using namespace Threading;
|
||||||
|
|
||||||
class IStreamWriter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IStreamWriter() throw() {}
|
|
||||||
|
|
||||||
virtual void Write( const void* data, size_t size )=0;
|
|
||||||
virtual wxString GetStreamName() const=0;
|
|
||||||
|
|
||||||
template< typename T >
|
|
||||||
void Write( const T& data )
|
|
||||||
{
|
|
||||||
Write( &data, sizeof(data) );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class IStreamReader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IStreamReader() throw() {}
|
|
||||||
|
|
||||||
virtual void Read( void* dest, size_t size )=0;
|
|
||||||
virtual wxString GetStreamName() const=0;
|
|
||||||
|
|
||||||
template< typename T >
|
|
||||||
void Read( T& dest )
|
|
||||||
{
|
|
||||||
Read( &dest, sizeof(dest) );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void FnType_WriteCompressedHeader( IStreamWriter& thr );
|
|
||||||
typedef void FnType_ReadCompressedHeader( IStreamReader& thr );
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// BaseCompressThread
|
// BaseCompressThread
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
class BaseCompressThread
|
class BaseCompressThread
|
||||||
: public pxThread
|
: public pxThread
|
||||||
, public IStreamWriter
|
|
||||||
{
|
{
|
||||||
typedef pxThread _parent;
|
typedef pxThread _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FnType_WriteCompressedHeader* m_WriteHeaderInThread;
|
|
||||||
|
|
||||||
const wxString m_filename;
|
|
||||||
ScopedPtr< SafeArray< u8 > > m_src_buffer;
|
ScopedPtr< SafeArray< u8 > > m_src_buffer;
|
||||||
|
ScopedPtr< pxStreamWriter > m_gzfp;
|
||||||
bool m_PendingSaveFlag;
|
bool m_PendingSaveFlag;
|
||||||
|
|
||||||
|
wxString m_final_filename;
|
||||||
|
|
||||||
BaseCompressThread( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL)
|
BaseCompressThread( SafeArray<u8>* srcdata, pxStreamWriter* outarchive)
|
||||||
: m_filename( file )
|
: m_src_buffer( srcdata )
|
||||||
, m_src_buffer( srcdata )
|
|
||||||
{
|
{
|
||||||
m_WriteHeaderInThread = writeHeader;
|
m_gzfp = outarchive;
|
||||||
m_PendingSaveFlag = false;
|
m_PendingSaveFlag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseCompressThread( SafeArray<u8>* srcdata, ScopedPtr<pxStreamWriter>& outarchive)
|
||||||
|
: m_src_buffer( srcdata )
|
||||||
|
{
|
||||||
|
m_gzfp = outarchive.DetachPtr();
|
||||||
|
m_PendingSaveFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~BaseCompressThread() throw();
|
virtual ~BaseCompressThread() throw();
|
||||||
|
|
||||||
void SetPendingSave();
|
void SetPendingSave();
|
||||||
|
|
||||||
public:
|
|
||||||
wxString GetStreamName() const { return m_filename; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
// CompressThread_gzip
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
class CompressThread_gzip : public BaseCompressThread
|
|
||||||
{
|
|
||||||
typedef BaseCompressThread _parent;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
gzFile m_gzfp;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
|
|
||||||
CompressThread_gzip( const wxString& file, ScopedPtr<SafeArray<u8> >& srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
|
|
||||||
virtual ~CompressThread_gzip() throw();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void Write( const void* data, size_t size );
|
|
||||||
void ExecuteTaskInThread();
|
void ExecuteTaskInThread();
|
||||||
void OnCleanupInThread();
|
void OnCleanupInThread();
|
||||||
|
|
||||||
|
public:
|
||||||
|
wxString GetStreamName() const { return m_gzfp->GetStreamName(); }
|
||||||
|
|
||||||
|
BaseCompressThread& SetTargetFilename(const wxString& filename)
|
||||||
|
{
|
||||||
|
m_final_filename = filename;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
#include "SaveState.h"
|
#include "SaveState.h"
|
||||||
#include "ThreadedZipTools.h"
|
#include "ThreadedZipTools.h"
|
||||||
#include "Utilities/SafeArray.inl"
|
#include "Utilities/SafeArray.inl"
|
||||||
|
#include "wx/wfstream.h"
|
||||||
|
|
||||||
|
|
||||||
BaseCompressThread::~BaseCompressThread() throw()
|
BaseCompressThread::~BaseCompressThread() throw()
|
||||||
{
|
{
|
||||||
|
_parent::Cancel();
|
||||||
if( m_PendingSaveFlag )
|
if( m_PendingSaveFlag )
|
||||||
{
|
{
|
||||||
wxGetApp().ClearPendingSave();
|
wxGetApp().ClearPendingSave();
|
||||||
|
@ -36,33 +38,7 @@ void BaseCompressThread::SetPendingSave()
|
||||||
m_PendingSaveFlag = true;
|
m_PendingSaveFlag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeheader )
|
void BaseCompressThread::ExecuteTaskInThread()
|
||||||
: BaseCompressThread( file, srcdata, writeheader )
|
|
||||||
{
|
|
||||||
m_gzfp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Believe it or not, the space in > > is required.
|
|
||||||
CompressThread_gzip::CompressThread_gzip( const wxString& file, ScopedPtr< SafeArray<u8> >& srcdata, FnType_WriteCompressedHeader* writeheader )
|
|
||||||
: BaseCompressThread( file, srcdata.DetachPtr(), writeheader )
|
|
||||||
{
|
|
||||||
m_gzfp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompressThread_gzip::~CompressThread_gzip() throw()
|
|
||||||
{
|
|
||||||
_parent::Cancel();
|
|
||||||
|
|
||||||
if( m_gzfp ) gzclose( m_gzfp );
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompressThread_gzip::Write( const void* data, size_t size )
|
|
||||||
{
|
|
||||||
if( gzwrite( m_gzfp, data, size ) == 0 )
|
|
||||||
throw Exception::BadStream( m_filename ).SetDiagMsg(L"Write to zip file failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompressThread_gzip::ExecuteTaskInThread()
|
|
||||||
{
|
{
|
||||||
// TODO : Add an API to PersistentThread for this! :) --air
|
// TODO : Add an API to PersistentThread for this! :) --air
|
||||||
//SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
|
//SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
|
||||||
|
@ -73,46 +49,31 @@ void CompressThread_gzip::ExecuteTaskInThread()
|
||||||
|
|
||||||
Yield( 3 );
|
Yield( 3 );
|
||||||
|
|
||||||
// Safeguard against corruption by writing to a temp file, and then
|
|
||||||
// copying the final result over the original:
|
|
||||||
|
|
||||||
wxString tempfile( m_filename + L".tmp" );
|
|
||||||
|
|
||||||
if( !(m_gzfp = gzopen(tempfile.ToUTF8(), "wb")) )
|
|
||||||
throw Exception::CannotCreateStream( m_filename );
|
|
||||||
|
|
||||||
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
|
|
||||||
|
|
||||||
#if defined(ZLIB_VERNUM) && (ZLIB_VERNUM >= 0x1240)
|
|
||||||
gzbuffer(m_gzfp, 0x100000); // 1mb buffer size for less file fragments (Windows/NTFS)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if( m_WriteHeaderInThread )
|
|
||||||
m_WriteHeaderInThread( *this );
|
|
||||||
|
|
||||||
static const int BlockSize = 0x64000;
|
static const int BlockSize = 0x64000;
|
||||||
int curidx = 0;
|
int curidx = 0;
|
||||||
|
|
||||||
|
// Safeguard against corruption by writing to a temp file, and then
|
||||||
|
// copying the final result over the original:
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int thisBlockSize = std::min( BlockSize, m_src_buffer->GetSizeInBytes() - curidx );
|
int thisBlockSize = std::min( BlockSize, m_src_buffer->GetSizeInBytes() - curidx );
|
||||||
if( gzwrite( m_gzfp, m_src_buffer->GetPtr(curidx), thisBlockSize ) < thisBlockSize )
|
m_gzfp->Write(m_src_buffer->GetPtr(curidx), thisBlockSize);
|
||||||
throw Exception::BadStream( m_filename );
|
|
||||||
curidx += thisBlockSize;
|
curidx += thisBlockSize;
|
||||||
Yield( 2 );
|
Yield( 2 );
|
||||||
} while( curidx < m_src_buffer->GetSizeInBytes() );
|
} while( curidx < m_src_buffer->GetSizeInBytes() );
|
||||||
|
|
||||||
gzclose( m_gzfp );
|
//m_gzfp->CloseEntry();
|
||||||
m_gzfp = NULL;
|
m_gzfp->Close();
|
||||||
|
|
||||||
if( !wxRenameFile( tempfile, m_filename, true ) )
|
if( !wxRenameFile( m_gzfp->GetStreamName(), m_final_filename, true ) )
|
||||||
throw Exception::BadStream( m_filename )
|
throw Exception::BadStream( m_final_filename )
|
||||||
.SetDiagMsg(L"Failed to move or copy the temporary archive to the destination filename.")
|
.SetDiagMsg(L"Failed to move or copy the temporary archive to the destination filename.")
|
||||||
.SetUserMsg(_("The savestate was not properly saved. The temporary file was created successfully but could not be moved to its final resting place."));
|
.SetUserMsg(_("The savestate was not properly saved. The temporary file was created successfully but could not be moved to its final resting place."));
|
||||||
|
|
||||||
Console.WriteLn( "(gzipThread) Data saved to disk without error." );
|
Console.WriteLn( "(gzipThread) Data saved to disk without error." );
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressThread_gzip::OnCleanupInThread()
|
void BaseCompressThread::OnCleanupInThread()
|
||||||
{
|
{
|
||||||
_parent::OnCleanupInThread();
|
_parent::OnCleanupInThread();
|
||||||
wxGetApp().DeleteThread( this );
|
wxGetApp().DeleteThread( this );
|
||||||
|
|
|
@ -481,9 +481,6 @@ class GameDatabaseLoaderThread : public pxThread
|
||||||
{
|
{
|
||||||
typedef pxThread _parent;
|
typedef pxThread _parent;
|
||||||
|
|
||||||
protected:
|
|
||||||
gzFile m_gzfp;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameDatabaseLoaderThread()
|
GameDatabaseLoaderThread()
|
||||||
: pxThread( L"GameDatabaseLoader" )
|
: pxThread( L"GameDatabaseLoader" )
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
#include "MemoryTypes.h"
|
||||||
#include "App.h"
|
#include "App.h"
|
||||||
|
|
||||||
#include "System/SysThreads.h"
|
#include "System/SysThreads.h"
|
||||||
|
@ -21,29 +22,93 @@
|
||||||
|
|
||||||
#include "ZipTools/ThreadedZipTools.h"
|
#include "ZipTools/ThreadedZipTools.h"
|
||||||
|
|
||||||
|
#include "wx/wfstream.h"
|
||||||
|
|
||||||
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
|
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
|
||||||
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
|
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
|
||||||
|
|
||||||
static const char SavestateIdentString[] = "PCSX2 Savestate";
|
static const wxChar* EntryFilename_StateVersion = L"PCSX2 Savestate Version.id";
|
||||||
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
|
static const wxChar* EntryFilename_Screenshot = L"Screenshot.jpg";
|
||||||
|
static const wxChar* EntryFilename_InternalStructures = L"PCSX2 Internal Structures.bin";
|
||||||
|
|
||||||
static void SaveStateFile_WriteHeader( IStreamWriter& thr )
|
class BaseSavestateEntry
|
||||||
{
|
{
|
||||||
thr.Write( SavestateIdentString );
|
public:
|
||||||
thr.Write( g_SaveVersion );
|
virtual const wxChar* GetFilename() const=0;
|
||||||
}
|
virtual void* GetDataPtr() const=0;
|
||||||
|
virtual uint GetDataSize() const=0;
|
||||||
|
};
|
||||||
|
|
||||||
static void SaveStateFile_ReadHeader( IStreamReader& thr )
|
class SavestateEntry_EmotionMemory : public BaseSavestateEntry
|
||||||
{
|
{
|
||||||
char ident[SavestateIdentLen] = {0};
|
public:
|
||||||
|
const wxChar* GetFilename() const { return L"eeMemory.bin"; }
|
||||||
|
void* GetDataPtr() const { return eeMem->Main; }
|
||||||
|
uint GetDataSize() const { return sizeof(eeMem->Main); }
|
||||||
|
};
|
||||||
|
|
||||||
thr.Read( ident );
|
class SavestateEntry_IopMemory : public BaseSavestateEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const wxChar* GetFilename() const { return L"iopMemory.bin"; }
|
||||||
|
void* GetDataPtr() const { return iopMem->Main; }
|
||||||
|
uint GetDataSize() const { return sizeof(iopMem->Main); }
|
||||||
|
};
|
||||||
|
|
||||||
if( strcmp(SavestateIdentString, ident) )
|
class SavestateEntry_HwRegs : public BaseSavestateEntry
|
||||||
throw Exception::SaveStateLoadError( thr.GetStreamName() )
|
{
|
||||||
.SetDiagMsg(wxsFormat( L"Unrecognized file signature while loading savestate."))
|
public:
|
||||||
.SetUserMsg(_("This is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2."));
|
const wxChar* GetFilename() const { return L"eeHwRegs.bin"; }
|
||||||
|
void* GetDataPtr() const { return eeHw; }
|
||||||
|
uint GetDataSize() const { return sizeof(eeHw); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class SavestateEntry_IopHwRegs : public BaseSavestateEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const wxChar* GetFilename() const { return L"iopHwRegs.bin"; }
|
||||||
|
void* GetDataPtr() const { return iopHw; }
|
||||||
|
uint GetDataSize() const { return sizeof(iopHw); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class SavestateEntry_Scratchpad : public BaseSavestateEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const wxChar* GetFilename() const { return L"Scratchpad.bin"; }
|
||||||
|
void* GetDataPtr() const { return eeMem->Scratch; }
|
||||||
|
uint GetDataSize() const { return sizeof(eeMem->Scratch); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// [TODO] : Add other components as files to the savestate gzip?
|
||||||
|
// * VU0/VU1 memory banks? VU0prog, VU1prog, VU0data, VU1data.
|
||||||
|
// * GS register data?
|
||||||
|
// * Individual plugins?
|
||||||
|
// (cpuRegs, iopRegs, VPU/GIF/DMAC structures should all remain as part of a larger unified
|
||||||
|
// block, since they're all PCSX2-dependent and having separate files in the archie for them
|
||||||
|
// would not be useful).
|
||||||
|
//
|
||||||
|
|
||||||
|
static const BaseSavestateEntry* const SavestateEntries[] =
|
||||||
|
{
|
||||||
|
new SavestateEntry_EmotionMemory,
|
||||||
|
new SavestateEntry_IopMemory,
|
||||||
|
new SavestateEntry_HwRegs,
|
||||||
|
new SavestateEntry_IopHwRegs,
|
||||||
|
new SavestateEntry_Scratchpad,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint NumSavestateEntries = ArraySize(SavestateEntries);
|
||||||
|
|
||||||
|
// 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( pxStreamReader& thr )
|
||||||
|
{
|
||||||
u32 savever;
|
u32 savever;
|
||||||
thr.Read( savever );
|
thr.Read( savever );
|
||||||
|
|
||||||
|
@ -51,64 +116,17 @@ static void SaveStateFile_ReadHeader( IStreamReader& thr )
|
||||||
// was removed entirely.
|
// was removed entirely.
|
||||||
if( savever > g_SaveVersion )
|
if( savever > g_SaveVersion )
|
||||||
throw Exception::SaveStateLoadError( thr.GetStreamName() )
|
throw Exception::SaveStateLoadError( thr.GetStreamName() )
|
||||||
.SetDiagMsg(wxsFormat( L"Savestate uses an unsupported or unknown savestate version.\n(PCSX2 ver=%x, state ver=%x)", g_SaveVersion, savever ))
|
.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 from an incompatible edition of PCSX2 that is either newer than this version, or is no longer supported."));
|
.SetUserMsg(_("Cannot load this savestate. The state is from an incompatible edition of PCSX2 that is either newer than this version, or is no longer supported."));
|
||||||
|
|
||||||
// check for a "minor" version incompatibility; which happens if the savestate being loaded is a newer 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.
|
// than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
|
||||||
if( (savever >> 16) != (g_SaveVersion >> 16) )
|
if( (savever >> 16) != (g_SaveVersion >> 16) )
|
||||||
throw Exception::SaveStateLoadError( thr.GetStreamName() )
|
throw Exception::SaveStateLoadError( thr.GetStreamName() )
|
||||||
.SetDiagMsg(wxsFormat( L"Savestate uses an unknown (future?!) savestate version.\n(PCSX2 ver=%x, state ver=%x)", g_SaveVersion, savever ))
|
.SetDiagMsg(pxsFmt( L"Savestate uses an unknown (future?!) savestate version.\n(PCSX2 ver=%x, state ver=%x)", g_SaveVersion, savever ))
|
||||||
.SetUserMsg(_("Cannot load this savestate. The state is an unsupported version, likely created by a newer edition of PCSX2."));
|
.SetUserMsg(_("Cannot load this savestate. The state is an unsupported version, likely created by a newer edition of PCSX2."));
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
// gzipReader
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
// Interface for reading data from a gzip stream.
|
|
||||||
//
|
|
||||||
class gzipReader : public IStreamReader
|
|
||||||
{
|
|
||||||
DeclareNoncopyableObject(gzipReader);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
wxString m_filename;
|
|
||||||
gzFile m_gzfp;
|
|
||||||
|
|
||||||
public:
|
|
||||||
gzipReader( const wxString& filename )
|
|
||||||
: m_filename( filename )
|
|
||||||
{
|
|
||||||
if( NULL == (m_gzfp = gzopen( m_filename.ToUTF8(), "rb" )) )
|
|
||||||
throw Exception::CannotCreateStream( m_filename ).SetDiagMsg(L"Cannot open file for reading.");
|
|
||||||
|
|
||||||
#if defined(ZLIB_VERNUM) && (ZLIB_VERNUM >= 0x1240)
|
|
||||||
gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~gzipReader() throw ()
|
|
||||||
{
|
|
||||||
if( m_gzfp ) gzclose( m_gzfp );
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString GetStreamName() const { return m_filename; }
|
|
||||||
|
|
||||||
void Read( void* dest, size_t size )
|
|
||||||
{
|
|
||||||
int result = gzread( m_gzfp, dest, size );
|
|
||||||
if( result == -1)
|
|
||||||
//throw Exception::gzError( m_filename );
|
|
||||||
throw Exception::BadStream( m_filename ).SetBothMsgs(wxLt("Data read failed: Invalid or corrupted gzip archive."));
|
|
||||||
|
|
||||||
if( (size_t)result < size )
|
|
||||||
throw Exception::EndOfStream( m_filename );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//static bool IsSavingOrLoading = false;
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// SysExecEvent_DownloadState
|
// SysExecEvent_DownloadState
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -142,47 +160,39 @@ protected:
|
||||||
.SetDiagMsg(L"SysExecEvent_DownloadState: Cannot freeze/download an invalid VM state!")
|
.SetDiagMsg(L"SysExecEvent_DownloadState: Cannot freeze/download an invalid VM state!")
|
||||||
.SetUserMsg(L"There is no active virtual machine state to download or save." );
|
.SetUserMsg(L"There is no active virtual machine state to download or save." );
|
||||||
|
|
||||||
memSavingState(m_dest_buffer).FreezeAll();
|
memSavingState( m_dest_buffer ).FreezeAll( false );
|
||||||
|
|
||||||
UI_EnableStateActions();
|
UI_EnableStateActions();
|
||||||
paused_core.AllowResume();
|
paused_core.AllowResume();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// CompressThread_VmState
|
// CompressThread_VmState
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
class VmStateZipThread : public CompressThread_gzip
|
class VmStateCompressThread : public BaseCompressThread
|
||||||
{
|
{
|
||||||
typedef CompressThread_gzip _parent;
|
typedef BaseCompressThread _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ScopedLock m_lock_Compress;
|
ScopedLock m_lock_Compress;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VmStateZipThread( const wxString& file, VmStateBuffer* srcdata )
|
VmStateCompressThread( VmStateBuffer* srcdata, pxStreamWriter* outarchive )
|
||||||
: _parent( file, srcdata, SaveStateFile_WriteHeader )
|
: _parent( srcdata, outarchive )
|
||||||
{
|
{
|
||||||
m_lock_Compress.Assign(mtx_CompressToDisk);
|
m_lock_Compress.Assign(mtx_CompressToDisk);
|
||||||
}
|
}
|
||||||
|
|
||||||
VmStateZipThread( const wxString& file, ScopedPtr<VmStateBuffer>& srcdata )
|
VmStateCompressThread( ScopedPtr<VmStateBuffer>& srcdata, ScopedPtr<pxStreamWriter>& outarchive )
|
||||||
: _parent( file, srcdata, SaveStateFile_WriteHeader )
|
: _parent( srcdata, outarchive )
|
||||||
{
|
{
|
||||||
m_lock_Compress.Assign(mtx_CompressToDisk);
|
m_lock_Compress.Assign(mtx_CompressToDisk);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~VmStateZipThread() throw()
|
virtual ~VmStateCompressThread() throw()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -213,7 +223,6 @@ public:
|
||||||
|
|
||||||
virtual ~SysExecEvent_ZipToDisk() throw()
|
virtual ~SysExecEvent_ZipToDisk() throw()
|
||||||
{
|
{
|
||||||
delete m_src_buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
|
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
|
||||||
|
@ -236,8 +245,47 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void InvokeEvent()
|
void InvokeEvent()
|
||||||
{
|
{
|
||||||
(new VmStateZipThread( m_filename, m_src_buffer ))->Start();
|
wxString tempfile( m_filename + L".tmp" );
|
||||||
m_src_buffer = NULL;
|
|
||||||
|
wxFFileOutputStream* woot = new wxFFileOutputStream(tempfile);
|
||||||
|
if (!woot->IsOk())
|
||||||
|
throw Exception::CannotCreateStream(tempfile);
|
||||||
|
|
||||||
|
// Write the version and screenshot:
|
||||||
|
ScopedPtr<pxStreamWriter> out( new pxStreamWriter(tempfile, new wxZipOutputStream(woot)) );
|
||||||
|
wxZipOutputStream* gzfp = (wxZipOutputStream*)out->GetBaseStream();
|
||||||
|
|
||||||
|
{
|
||||||
|
wxZipEntry* vent = new wxZipEntry(EntryFilename_StateVersion);
|
||||||
|
vent->SetMethod( wxZIP_METHOD_STORE );
|
||||||
|
gzfp->PutNextEntry( vent );
|
||||||
|
out->Write(g_SaveVersion);
|
||||||
|
gzfp->CloseEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedPtr<wxImage> m_screenshot;
|
||||||
|
|
||||||
|
if (m_screenshot)
|
||||||
|
{
|
||||||
|
wxZipEntry* vent = new wxZipEntry(EntryFilename_Screenshot);
|
||||||
|
vent->SetMethod( wxZIP_METHOD_STORE );
|
||||||
|
gzfp->PutNextEntry( vent );
|
||||||
|
m_screenshot->SaveFile( *gzfp, wxBITMAP_TYPE_JPEG );
|
||||||
|
gzfp->CloseEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//m_gzfp->PutNextEntry(EntryFilename_Screenshot);
|
||||||
|
//m_gzfp->Write();
|
||||||
|
//m_gzfp->CloseEntry();
|
||||||
|
|
||||||
|
(new VmStateCompressThread( m_src_buffer, out ))->
|
||||||
|
SetTargetFilename(m_filename).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CleanupEvent()
|
||||||
|
{
|
||||||
|
m_src_buffer = NULL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,8 +299,8 @@ protected:
|
||||||
class SysExecEvent_UnzipFromDisk : public SysExecEvent
|
class SysExecEvent_UnzipFromDisk : public SysExecEvent
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
wxString m_filename;
|
wxString m_filename;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wxString GetEventName() const { return L"VM_UnzipFromDisk"; }
|
wxString GetEventName() const { return L"VM_UnzipFromDisk"; }
|
||||||
|
|
||||||
|
@ -269,42 +317,127 @@ protected:
|
||||||
void InvokeEvent()
|
void InvokeEvent()
|
||||||
{
|
{
|
||||||
ScopedLock lock( mtx_CompressToDisk );
|
ScopedLock lock( mtx_CompressToDisk );
|
||||||
gzipReader m_gzreader(m_filename );
|
|
||||||
SaveStateFile_ReadHeader( m_gzreader );
|
// Ugh. Exception handling made crappy because wxWidgets classes don't support scoped pointers yet.
|
||||||
|
|
||||||
|
ScopedPtr<wxFFileInputStream> woot( new wxFFileInputStream(m_filename) );
|
||||||
|
if (!woot->IsOk())
|
||||||
|
throw Exception::CannotCreateStream( m_filename ).SetDiagMsg(L"Cannot open file for reading.");
|
||||||
|
|
||||||
|
|
||||||
|
ScopedPtr<pxStreamReader> reader( new pxStreamReader(m_filename, new wxZipInputStream(woot)) );
|
||||||
|
woot.DetachPtr();
|
||||||
|
|
||||||
|
if (!reader->IsOk())
|
||||||
|
{
|
||||||
|
throw Exception::SaveStateLoadError( m_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->GetBaseStream();
|
||||||
|
|
||||||
|
// look for version and screenshot information in the zip stream:
|
||||||
|
|
||||||
|
bool foundVersion = false;
|
||||||
|
//bool foundScreenshot = false;
|
||||||
|
//bool foundEntry[NumSavestateEntries] = false;
|
||||||
|
|
||||||
|
ScopedPtr<wxZipEntry> foundInternal;
|
||||||
|
ScopedPtr<wxZipEntry> foundEntry[NumSavestateEntries];
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
Threading::pxTestCancel();
|
||||||
|
|
||||||
|
ScopedPtr<wxZipEntry> entry( gzreader->GetNextEntry() );
|
||||||
|
if (!entry) break;
|
||||||
|
|
||||||
|
if (entry->GetName().CmpNoCase(EntryFilename_StateVersion) == 0)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn(L" ... found '%s'", EntryFilename_StateVersion);
|
||||||
|
foundVersion = true;
|
||||||
|
CheckVersion(*reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->GetName().CmpNoCase(EntryFilename_InternalStructures) == 0)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn(L" ... found '%s'", EntryFilename_InternalStructures);
|
||||||
|
foundInternal = entry.DetachPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<NumSavestateEntries; ++i)
|
||||||
|
{
|
||||||
|
if (entry->GetName().CmpNoCase(SavestateEntries[i]->GetFilename()) == 0)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn( Color_Green, L" ... found '%s'", SavestateEntries[i]->GetFilename() );
|
||||||
|
foundEntry[i] = entry.DetachPtr();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundVersion || !foundInternal)
|
||||||
|
{
|
||||||
|
throw Exception::SaveStateLoadError( m_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."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log any parts and pieces that are missing, and then generate an exception.
|
||||||
|
bool throwIt = false;
|
||||||
|
for (uint i=0; i<NumSavestateEntries; ++i)
|
||||||
|
{
|
||||||
|
if (foundEntry[i]) continue;
|
||||||
|
throwIt = true;
|
||||||
|
Console.WriteLn( Color_Red, " ... not found '%s'!", SavestateEntries[i]->GetFilename() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (throwIt)
|
||||||
|
throw Exception::SaveStateLoadError( m_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."));
|
||||||
|
|
||||||
// We use direct Suspend/Resume control here, since it's desirable that emulation
|
// We use direct Suspend/Resume control here, since it's desirable that emulation
|
||||||
// *ALWAYS* start execution after the new savestate is loaded.
|
// *ALWAYS* start execution after the new savestate is loaded.
|
||||||
|
|
||||||
GetCoreThread().Pause();
|
GetCoreThread().Pause();
|
||||||
|
|
||||||
// fixme: should start initially with the file size, and then grow from there.
|
for (uint i=0; i<NumSavestateEntries; ++i)
|
||||||
|
|
||||||
static const int BlockSize = 0x100000;
|
|
||||||
|
|
||||||
VmStateBuffer buffer( 0x800000, L"StateBuffer_UnzipFromDisk" ); // start with an 8 meg buffer to avoid frequent reallocation.
|
|
||||||
int curidx = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
while(true) {
|
|
||||||
buffer.MakeRoomFor( curidx+BlockSize );
|
|
||||||
m_gzreader.Read( buffer.GetPtr(curidx), BlockSize );
|
|
||||||
curidx += BlockSize;
|
|
||||||
Threading::pxTestCancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( Exception::EndOfStream& )
|
|
||||||
{
|
{
|
||||||
// This exception actually means success! Any others we let get sent
|
Threading::pxTestCancel();
|
||||||
// to the main event handler/thread for handling.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional shutdown of plugins when loading states? I'm not implementing it yet because some
|
gzreader->OpenEntry( *foundEntry[i] );
|
||||||
// things, like the SPU2-recovery trick, rely on not resetting the plugins prior to loading
|
const uint entrySize = foundEntry[i]->GetSize();
|
||||||
// the new savestate data.
|
const uint expectedSize = SavestateEntries[i]->GetDataSize();
|
||||||
//if( ShutdownOnStateLoad ) GetCoreThread().Cancel();
|
|
||||||
|
|
||||||
|
if (entrySize < expectedSize)
|
||||||
|
{
|
||||||
|
Console.WriteLn( Color_Yellow, " '%s' is incomplete (expected 0x%x bytes, loading only 0x%x bytes)",
|
||||||
|
SavestateEntries[i]->GetFilename(), expectedSize, entrySize );
|
||||||
|
}
|
||||||
|
|
||||||
GetCoreThread().UploadStateCopy( buffer );
|
uint copylen = std::min(entrySize, expectedSize);
|
||||||
|
reader->Read( SavestateEntries[i]->GetDataPtr(), copylen );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() );
|
||||||
|
|
||||||
|
//GetCoreThread().UploadStateCopy( buffer );
|
||||||
|
memLoadingState( buffer ).FreezeAll( false );
|
||||||
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
|
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue