mirror of https://github.com/PCSX2/pcsx2.git
wxSavestates:
* Basic savestate loading/saving working now (needs testing). * No support for screenshots embedded into the savestate (yet). git-svn-id: http://pcsx2.googlecode.com/svn/branches/wxSavestates@4043 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
1dfc69670d
commit
6dcdf9cfa9
|
@ -26,6 +26,7 @@ class wxOutputStream;
|
|||
class wxFileOutputStream;
|
||||
class wxFFileOutputStream;
|
||||
|
||||
class wxStreamBase;
|
||||
class wxInputStream;
|
||||
class wxFileInputStream;
|
||||
class wxFFileInputStream;
|
||||
|
|
|
@ -86,8 +86,14 @@ public:
|
|||
|
||||
// Gets a pointer to the requested allocation index.
|
||||
// DevBuilds : Generates assertion if the index is invalid.
|
||||
T *GetPtr( uint idx=0 ) { return _getPtr( idx ); }
|
||||
const T *GetPtr( uint idx=0 ) const { return _getPtr( idx ); }
|
||||
T* GetPtr( uint idx=0 ) { return _getPtr( idx ); }
|
||||
const T* GetPtr( uint idx=0 ) const { return _getPtr( idx ); }
|
||||
|
||||
// Gets a pointer to the element directly after the last element in the array.
|
||||
// This is equivalent to doing GetPtr(GetLength()), except that this call *avoids*
|
||||
// the out-of-bounds assertion check that typically occurs when you do that. :)
|
||||
T* GetPtrEnd( uint idx=0 ) { return &m_ptr[m_size]; }
|
||||
const T* GetPtrEnd( uint idx=0 ) const { return &m_ptr[m_size]; }
|
||||
|
||||
// Gets an element of this memory allocation much as if it were an array.
|
||||
// DevBuilds : Generates assertion if the index is invalid.
|
||||
|
|
|
@ -13,31 +13,58 @@
|
|||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxStreamWriter
|
||||
// pxStreamBase
|
||||
// --------------------------------------------------------------------------------------
|
||||
class pxStreamWriter
|
||||
class pxStreamBase
|
||||
{
|
||||
DeclareNoncopyableObject(pxStreamWriter);
|
||||
DeclareNoncopyableObject(pxStreamBase);
|
||||
|
||||
protected:
|
||||
wxString m_filename;
|
||||
ScopedPtr<wxOutputStream> m_outstream;
|
||||
// Filename of the stream, provided by the creator/caller. This is typically used *only*
|
||||
// for generating comprehensive error messages when an error occurs (the stream name is
|
||||
// passed to the exception handlers).
|
||||
wxString m_filename;
|
||||
|
||||
public:
|
||||
pxStreamWriter(const wxString& filename, ScopedPtr<wxOutputStream>& output);
|
||||
pxStreamWriter(const wxString& filename, wxOutputStream* output);
|
||||
pxStreamBase(const wxString& filename);
|
||||
virtual ~pxStreamBase() throw() {}
|
||||
|
||||
virtual ~pxStreamWriter() throw() {}
|
||||
// Implementing classes should return the base wxStream object (usually either a wxInputStream
|
||||
// or wxOputStream derivative).
|
||||
virtual wxStreamBase* GetWxStreamBase() const=0;
|
||||
virtual void Close()=0;
|
||||
|
||||
bool IsOk() const;
|
||||
wxString GetStreamName() const { return m_filename; }
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxOutputStream
|
||||
// --------------------------------------------------------------------------------------
|
||||
class pxOutputStream : public pxStreamBase
|
||||
{
|
||||
DeclareNoncopyableObject(pxOutputStream);
|
||||
|
||||
protected:
|
||||
ScopedPtr<wxOutputStream> m_stream_out;
|
||||
|
||||
public:
|
||||
pxOutputStream(const wxString& filename, ScopedPtr<wxOutputStream>& output);
|
||||
pxOutputStream(const wxString& filename, wxOutputStream* output);
|
||||
|
||||
virtual ~pxOutputStream() throw() {}
|
||||
virtual void Write( const void* data, size_t size );
|
||||
|
||||
void Close() { m_outstream.Delete(); }
|
||||
wxOutputStream* GetBaseStream() const { return m_outstream; }
|
||||
|
||||
void SetStream( const wxString& filename, ScopedPtr<wxOutputStream>& stream );
|
||||
void SetStream( const wxString& filename, wxOutputStream* stream );
|
||||
|
||||
wxString GetStreamName() const { return m_filename; }
|
||||
void Close() { m_stream_out.Delete(); }
|
||||
|
||||
virtual wxStreamBase* GetWxStreamBase() const;
|
||||
|
||||
template< typename T >
|
||||
void Write( const T& data )
|
||||
|
@ -47,30 +74,28 @@ public:
|
|||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxStreamReader
|
||||
// pxInputStream
|
||||
// --------------------------------------------------------------------------------------
|
||||
class pxStreamReader
|
||||
class pxInputStream : public pxStreamBase
|
||||
{
|
||||
DeclareNoncopyableObject(pxStreamReader);
|
||||
DeclareNoncopyableObject(pxInputStream);
|
||||
|
||||
protected:
|
||||
wxString m_filename;
|
||||
ScopedPtr<wxInputStream> m_stream;
|
||||
ScopedPtr<wxInputStream> m_stream_in;
|
||||
|
||||
public:
|
||||
pxStreamReader(const wxString& filename, ScopedPtr<wxInputStream>& input);
|
||||
pxStreamReader(const wxString& filename, wxInputStream* input);
|
||||
pxInputStream(const wxString& filename, ScopedPtr<wxInputStream>& input);
|
||||
pxInputStream(const wxString& filename, wxInputStream* input);
|
||||
|
||||
virtual ~pxStreamReader() throw() {}
|
||||
virtual ~pxInputStream() throw() {}
|
||||
virtual void Read( void* dest, size_t size );
|
||||
|
||||
void SetStream( const wxString& filename, ScopedPtr<wxInputStream>& stream );
|
||||
void SetStream( const wxString& filename, wxInputStream* stream );
|
||||
|
||||
wxInputStream* GetBaseStream() const { return m_stream; }
|
||||
void Close() { m_stream.Delete(); }
|
||||
void Close() { m_stream_in.Delete(); }
|
||||
|
||||
wxString GetStreamName() const { return m_filename; }
|
||||
virtual wxStreamBase* GetWxStreamBase() const;
|
||||
|
||||
template< typename T >
|
||||
void Read( T& dest )
|
||||
|
|
|
@ -292,7 +292,7 @@ wxString Exception::BadStream::FormatDisplayMessage() const
|
|||
return retval;
|
||||
}
|
||||
|
||||
void Exception::BadStream::_formatDiagMsg( FastFormatUnicode& dest ) const
|
||||
void Exception::BadStream::_formatDiagMsg( FastFormatUnicode& dest ) const
|
||||
{
|
||||
dest.Write( L"Path: " );
|
||||
if (!StreamName.IsEmpty())
|
||||
|
|
|
@ -22,39 +22,55 @@
|
|||
#include <errno.h>
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxStreamReader (implementations)
|
||||
// pxStreamBase (implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
pxStreamBase::pxStreamBase(const wxString& filename)
|
||||
: m_filename( filename )
|
||||
{
|
||||
}
|
||||
|
||||
bool pxStreamBase::IsOk() const
|
||||
{
|
||||
wxStreamBase* woot = GetWxStreamBase();
|
||||
return woot && woot->IsOk();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxInputStream (implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Interface for reading data from a gzip stream.
|
||||
//
|
||||
|
||||
pxStreamReader::pxStreamReader(const wxString& filename, ScopedPtr<wxInputStream>& input)
|
||||
: m_filename( filename )
|
||||
, m_stream( input.DetachPtr() )
|
||||
pxInputStream::pxInputStream(const wxString& filename, ScopedPtr<wxInputStream>& input)
|
||||
: pxStreamBase( filename )
|
||||
, m_stream_in( input.DetachPtr() )
|
||||
{
|
||||
}
|
||||
|
||||
pxStreamReader::pxStreamReader(const wxString& filename, wxInputStream* input)
|
||||
: m_filename( filename )
|
||||
, m_stream( input )
|
||||
pxInputStream::pxInputStream(const wxString& filename, wxInputStream* input)
|
||||
: pxStreamBase( filename )
|
||||
, m_stream_in( input )
|
||||
{
|
||||
}
|
||||
|
||||
void pxStreamReader::SetStream( const wxString& filename, ScopedPtr<wxInputStream>& stream )
|
||||
wxStreamBase* pxInputStream::GetWxStreamBase() const { return m_stream_in.GetPtr(); }
|
||||
|
||||
void pxInputStream::SetStream( const wxString& filename, ScopedPtr<wxInputStream>& stream )
|
||||
{
|
||||
m_filename = filename;
|
||||
m_stream = stream.DetachPtr();
|
||||
m_stream_in = stream.DetachPtr();
|
||||
}
|
||||
|
||||
void pxStreamReader::SetStream( const wxString& filename, wxInputStream* stream )
|
||||
void pxInputStream::SetStream( const wxString& filename, wxInputStream* stream )
|
||||
{
|
||||
m_filename = filename;
|
||||
m_stream = stream;
|
||||
m_stream_in = stream;
|
||||
}
|
||||
|
||||
void pxStreamReader::Read( void* dest, size_t size )
|
||||
void pxInputStream::Read( void* dest, size_t size )
|
||||
{
|
||||
m_stream->Read(dest, size);
|
||||
if (m_stream->GetLastError() == wxSTREAM_READ_ERROR)
|
||||
m_stream_in->Read(dest, size);
|
||||
if (m_stream_in->GetLastError() == wxSTREAM_READ_ERROR)
|
||||
{
|
||||
int err = errno;
|
||||
if (!err)
|
||||
|
@ -69,43 +85,46 @@ void pxStreamReader::Read( void* dest, size_t size )
|
|||
// must always use the explicit check against the number of bytes read to determine
|
||||
// end-of-stream conditions.
|
||||
|
||||
if ((size_t)m_stream->LastRead() < size)
|
||||
if ((size_t)m_stream_in->LastRead() < size)
|
||||
throw Exception::EndOfStream( m_filename );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxStreamWriter
|
||||
// pxOutputStream
|
||||
// --------------------------------------------------------------------------------------
|
||||
pxStreamWriter::pxStreamWriter(const wxString& filename, ScopedPtr<wxOutputStream>& output)
|
||||
: m_filename( filename )
|
||||
, m_outstream( output.DetachPtr() )
|
||||
pxOutputStream::pxOutputStream(const wxString& filename, ScopedPtr<wxOutputStream>& output)
|
||||
: pxStreamBase( filename )
|
||||
, m_stream_out( output.DetachPtr() )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
pxStreamWriter::pxStreamWriter(const wxString& filename, wxOutputStream* output)
|
||||
: m_filename( filename )
|
||||
, m_outstream( output )
|
||||
pxOutputStream::pxOutputStream(const wxString& filename, wxOutputStream* output)
|
||||
: pxStreamBase( filename )
|
||||
, m_stream_out( output )
|
||||
{
|
||||
}
|
||||
|
||||
void pxStreamWriter::SetStream( const wxString& filename, ScopedPtr<wxOutputStream>& stream )
|
||||
wxStreamBase* pxOutputStream::GetWxStreamBase() const { return m_stream_out.GetPtr(); }
|
||||
|
||||
|
||||
void pxOutputStream::SetStream( const wxString& filename, ScopedPtr<wxOutputStream>& stream )
|
||||
{
|
||||
m_filename = filename;
|
||||
m_outstream = stream.DetachPtr();
|
||||
m_stream_out = stream.DetachPtr();
|
||||
}
|
||||
|
||||
void pxStreamWriter::SetStream( const wxString& filename, wxOutputStream* stream )
|
||||
void pxOutputStream::SetStream( const wxString& filename, wxOutputStream* stream )
|
||||
{
|
||||
m_filename = filename;
|
||||
m_outstream = stream;
|
||||
m_stream_out = stream;
|
||||
}
|
||||
|
||||
|
||||
void pxStreamWriter::Write( const void* src, size_t size )
|
||||
void pxOutputStream::Write( const void* src, size_t size )
|
||||
{
|
||||
m_outstream->Write(src, size);
|
||||
if(m_outstream->GetLastError() == wxSTREAM_WRITE_ERROR)
|
||||
m_stream_out->Write(src, size);
|
||||
if(m_stream_out->GetLastError() == wxSTREAM_WRITE_ERROR)
|
||||
{
|
||||
int err = errno;
|
||||
if (!err)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "COP0.h"
|
||||
#include "VUmicro.h"
|
||||
#include "Cache.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
|
@ -163,6 +164,12 @@ void SaveStateBase::FreezeMainMemory()
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
void SaveStateBase::FreezeRegisters()
|
||||
|
@ -378,16 +385,24 @@ void memSavingState::FreezeMem( void* data, int size )
|
|||
m_idx += size;
|
||||
}
|
||||
|
||||
void memSavingState::FreezeAll()
|
||||
void memSavingState::MakeRoomForData()
|
||||
{
|
||||
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
||||
|
||||
m_memory->ChunkSize = ReallocThreshold;
|
||||
m_memory->MakeRoomFor( m_idx + MemoryBaseAllocSize );
|
||||
}
|
||||
|
||||
// Saving of state data to a memory buffer
|
||||
void memSavingState::FreezeAll()
|
||||
{
|
||||
MakeRoomForData();
|
||||
_parent::FreezeAll();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// memLoadingState (implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
memLoadingState::memLoadingState( const SafeArray<u8>& load_from )
|
||||
: SaveStateBase( const_cast<SafeArray<u8>&>(load_from) )
|
||||
{
|
||||
|
@ -400,7 +415,7 @@ memLoadingState::memLoadingState( const SafeArray<u8>* load_from )
|
|||
|
||||
memLoadingState::~memLoadingState() throw() { }
|
||||
|
||||
// Loading of state data
|
||||
// Loading of state data from a memory buffer...
|
||||
void memLoadingState::FreezeMem( void* data, int size )
|
||||
{
|
||||
const u8* const src = m_memory->GetPtr(m_idx);
|
||||
|
|
|
@ -118,7 +118,7 @@ protected:
|
|||
|
||||
u32 m_version; // version of the savestate being loaded.
|
||||
|
||||
int m_idx; // current read/write index of the allocation
|
||||
int m_idx; // current read/write index of the allocation
|
||||
int m_sectid;
|
||||
int m_pid;
|
||||
|
||||
|
@ -163,11 +163,21 @@ public:
|
|||
|
||||
void PrepBlock( int size );
|
||||
|
||||
uint GetCurrentPos() const
|
||||
{
|
||||
return m_idx;
|
||||
}
|
||||
|
||||
u8* GetBlockPtr()
|
||||
{
|
||||
return m_memory->GetPtr(m_idx);
|
||||
}
|
||||
|
||||
u8* GetPtrEnd() const
|
||||
{
|
||||
return m_memory->GetPtrEnd();
|
||||
}
|
||||
|
||||
void CommitBlock( int size )
|
||||
{
|
||||
m_idx += size;
|
||||
|
@ -244,7 +254,8 @@ public:
|
|||
memSavingState( VmStateBuffer& save_to );
|
||||
memSavingState( VmStateBuffer* save_to );
|
||||
|
||||
// Saving of state data to a memory buffer
|
||||
void MakeRoomForData();
|
||||
|
||||
void FreezeMem( void* data, int size );
|
||||
void FreezeAll();
|
||||
|
||||
|
@ -259,7 +270,6 @@ public:
|
|||
memLoadingState( const VmStateBuffer& load_from );
|
||||
memLoadingState( const VmStateBuffer* load_from );
|
||||
|
||||
// Loading of state data from a memory buffer...
|
||||
void FreezeMem( void* data, int size );
|
||||
bool SeekToSection( PluginsEnum_t pid );
|
||||
|
||||
|
|
|
@ -21,6 +21,140 @@
|
|||
|
||||
using namespace Threading;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// BaseArchiveEntry
|
||||
// --------------------------------------------------------------------------------------
|
||||
class BaseArchiveEntry
|
||||
{
|
||||
protected:
|
||||
BaseArchiveEntry() {}
|
||||
virtual ~BaseArchiveEntry() throw() {}
|
||||
|
||||
public:
|
||||
virtual wxString GetFilename() const=0;
|
||||
virtual u8* GetDataPtr() const=0;
|
||||
virtual uint GetDataSize() const=0;
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ArchiveEntry
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ArchiveEntry
|
||||
{
|
||||
protected:
|
||||
wxString m_filename;
|
||||
uptr m_dataidx;
|
||||
size_t m_datasize;
|
||||
|
||||
public:
|
||||
ArchiveEntry( const wxString& filename=wxEmptyString )
|
||||
: m_filename( filename )
|
||||
{
|
||||
m_dataidx = 0;
|
||||
m_datasize = 0;
|
||||
}
|
||||
|
||||
virtual ~ArchiveEntry() throw() {}
|
||||
|
||||
ArchiveEntry& SetDataIndex( uptr idx )
|
||||
{
|
||||
m_dataidx = idx;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ArchiveEntry& SetDataSize( size_t size )
|
||||
{
|
||||
m_datasize = size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
wxString GetFilename() const
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
uptr GetDataIndex() const
|
||||
{
|
||||
return m_dataidx;
|
||||
}
|
||||
|
||||
uint GetDataSize() const
|
||||
{
|
||||
return m_datasize;
|
||||
}
|
||||
};
|
||||
|
||||
typedef SafeArray< u8 > ArchiveDataBuffer;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ArchiveEntryList
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ArchiveEntryList
|
||||
{
|
||||
DeclareNoncopyableObject( ArchiveEntryList );
|
||||
|
||||
protected:
|
||||
std::vector<ArchiveEntry> m_list;
|
||||
ScopedPtr<ArchiveDataBuffer> m_data;
|
||||
|
||||
public:
|
||||
ArchiveEntryList() {}
|
||||
|
||||
ArchiveEntryList( ArchiveDataBuffer* data )
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
ArchiveEntryList( ScopedPtr<ArchiveDataBuffer>& data )
|
||||
{
|
||||
m_data = data.DetachPtr();
|
||||
}
|
||||
|
||||
virtual ~ArchiveEntryList() throw() {}
|
||||
|
||||
const VmStateBuffer* GetBuffer() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
VmStateBuffer* GetBuffer()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
u8* GetPtr( uint idx )
|
||||
{
|
||||
return &(*m_data)[idx];
|
||||
}
|
||||
|
||||
const u8* GetPtr( uint idx ) const
|
||||
{
|
||||
return &(*m_data)[idx];
|
||||
}
|
||||
|
||||
ArchiveEntryList& Add( const ArchiveEntry& src )
|
||||
{
|
||||
m_list.push_back( src );
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t GetLength() const
|
||||
{
|
||||
return m_list.size();
|
||||
}
|
||||
|
||||
ArchiveEntry& operator[](uint idx)
|
||||
{
|
||||
return m_list[idx];
|
||||
}
|
||||
|
||||
const ArchiveEntry& operator[](uint idx) const
|
||||
{
|
||||
return m_list[idx];
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// BaseCompressThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -30,33 +164,45 @@ class BaseCompressThread
|
|||
typedef pxThread _parent;
|
||||
|
||||
protected:
|
||||
ScopedPtr< SafeArray< u8 > > m_src_buffer;
|
||||
ScopedPtr< pxStreamWriter > m_gzfp;
|
||||
ScopedPtr< ArchiveEntryList > m_src_list;
|
||||
ScopedPtr< pxOutputStream > m_gzfp;
|
||||
bool m_PendingSaveFlag;
|
||||
|
||||
wxString m_final_filename;
|
||||
|
||||
BaseCompressThread( SafeArray<u8>* srcdata, pxStreamWriter* outarchive)
|
||||
: m_src_buffer( srcdata )
|
||||
{
|
||||
m_gzfp = outarchive;
|
||||
m_PendingSaveFlag = false;
|
||||
}
|
||||
|
||||
BaseCompressThread( SafeArray<u8>* srcdata, ScopedPtr<pxStreamWriter>& outarchive)
|
||||
: m_src_buffer( srcdata )
|
||||
{
|
||||
m_gzfp = outarchive.DetachPtr();
|
||||
m_PendingSaveFlag = false;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~BaseCompressThread() throw();
|
||||
|
||||
void SetPendingSave();
|
||||
void ExecuteTaskInThread();
|
||||
void OnCleanupInThread();
|
||||
BaseCompressThread& SetSource( ArchiveEntryList* srcdata )
|
||||
{
|
||||
m_src_list = srcdata;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseCompressThread& SetSource( ScopedPtr< ArchiveEntryList >& srcdata )
|
||||
{
|
||||
m_src_list = srcdata.DetachPtr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseCompressThread& SetOutStream( pxOutputStream* out )
|
||||
{
|
||||
m_gzfp = out;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseCompressThread& SetOutStream( ScopedPtr< pxOutputStream >& out )
|
||||
{
|
||||
m_gzfp = out.DetachPtr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseCompressThread& SetFinishedPath( const wxString& path )
|
||||
{
|
||||
m_final_filename = path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
wxString GetStreamName() const { return m_gzfp->GetStreamName(); }
|
||||
|
||||
BaseCompressThread& SetTargetFilename(const wxString& filename)
|
||||
|
@ -64,4 +210,14 @@ public:
|
|||
m_final_filename = filename;
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
BaseCompressThread()
|
||||
{
|
||||
m_PendingSaveFlag = false;
|
||||
}
|
||||
|
||||
void SetPendingSave();
|
||||
void ExecuteTaskInThread();
|
||||
void OnCleanupInThread();
|
||||
};
|
||||
|
|
|
@ -43,26 +43,38 @@ void BaseCompressThread::ExecuteTaskInThread()
|
|||
// TODO : Add an API to PersistentThread for this! :) --air
|
||||
//SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
|
||||
|
||||
if( !m_src_buffer ) return;
|
||||
// Notes:
|
||||
// * Safeguard against corruption by writing to a temp file, and then copying the final
|
||||
// result over the original.
|
||||
|
||||
if( !m_src_list ) return;
|
||||
|
||||
SetPendingSave();
|
||||
|
||||
Yield( 3 );
|
||||
|
||||
static const int BlockSize = 0x64000;
|
||||
int curidx = 0;
|
||||
uint listlen = m_src_list->GetLength();
|
||||
for( uint i=0; i<listlen; ++i )
|
||||
{
|
||||
const ArchiveEntry& entry = (*m_src_list)[i];
|
||||
|
||||
// Safeguard against corruption by writing to a temp file, and then
|
||||
// copying the final result over the original:
|
||||
wxArchiveOutputStream& woot = *(wxArchiveOutputStream*)m_gzfp->GetWxStreamBase();
|
||||
woot.PutNextEntry( entry.GetFilename() );
|
||||
|
||||
static const uint BlockSize = 0x64000;
|
||||
uint curidx = 0;
|
||||
|
||||
do {
|
||||
uint thisBlockSize = std::min( BlockSize, entry.GetDataSize() - curidx );
|
||||
m_gzfp->Write(m_src_list->GetPtr( entry.GetDataIndex() + curidx ), thisBlockSize);
|
||||
curidx += thisBlockSize;
|
||||
Yield( 2 );
|
||||
} while( curidx < entry.GetDataSize() );
|
||||
|
||||
woot.CloseEntry();
|
||||
}
|
||||
|
||||
do {
|
||||
int thisBlockSize = std::min( BlockSize, m_src_buffer->GetSizeInBytes() - curidx );
|
||||
m_gzfp->Write(m_src_buffer->GetPtr(curidx), thisBlockSize);
|
||||
curidx += thisBlockSize;
|
||||
Yield( 2 );
|
||||
} while( curidx < m_src_buffer->GetSizeInBytes() );
|
||||
|
||||
//m_gzfp->CloseEntry();
|
||||
m_gzfp->Close();
|
||||
|
||||
if( !wxRenameFile( m_gzfp->GetStreamName(), m_final_filename, true ) )
|
||||
|
|
|
@ -19,10 +19,11 @@
|
|||
|
||||
#include "System/SysThreads.h"
|
||||
#include "SaveState.h"
|
||||
#include "VUmicro.h"
|
||||
|
||||
#include "ZipTools/ThreadedZipTools.h"
|
||||
|
||||
#include "wx/wfstream.h"
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
|
||||
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
|
||||
|
@ -31,54 +32,88 @@ static const wxChar* EntryFilename_StateVersion = L"PCSX2 Savestate Version.id
|
|||
static const wxChar* EntryFilename_Screenshot = L"Screenshot.jpg";
|
||||
static const wxChar* EntryFilename_InternalStructures = L"PCSX2 Internal Structures.bin";
|
||||
|
||||
class BaseSavestateEntry
|
||||
{
|
||||
public:
|
||||
virtual const wxChar* GetFilename() const=0;
|
||||
virtual void* GetDataPtr() const=0;
|
||||
virtual uint GetDataSize() const=0;
|
||||
};
|
||||
|
||||
class SavestateEntry_EmotionMemory : public BaseSavestateEntry
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SavestateEntry_* (EmotionMemory, IopMemory, etc)
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Implementation Rationale:
|
||||
// The address locations of PS2 virtual memory components is fully dynamic, so we need to
|
||||
// resolve the pointers at the time they are requested (eeMem, iopMem, etc). Thusly, we
|
||||
// cannot use static struct member initializers -- we need virtual functions that compute
|
||||
// and resolve the addresses on-demand instead... --air
|
||||
|
||||
class SavestateEntry_EmotionMemory : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
const wxChar* GetFilename() const { return L"eeMemory.bin"; }
|
||||
void* GetDataPtr() const { return eeMem->Main; }
|
||||
wxString GetFilename() const { return L"eeMemory.bin"; }
|
||||
u8* GetDataPtr() const { return eeMem->Main; }
|
||||
uint GetDataSize() const { return sizeof(eeMem->Main); }
|
||||
};
|
||||
|
||||
class SavestateEntry_IopMemory : public BaseSavestateEntry
|
||||
class SavestateEntry_IopMemory : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
const wxChar* GetFilename() const { return L"iopMemory.bin"; }
|
||||
void* GetDataPtr() const { return iopMem->Main; }
|
||||
wxString GetFilename() const { return L"iopMemory.bin"; }
|
||||
u8* GetDataPtr() const { return iopMem->Main; }
|
||||
uint GetDataSize() const { return sizeof(iopMem->Main); }
|
||||
};
|
||||
|
||||
class SavestateEntry_HwRegs : public BaseSavestateEntry
|
||||
class SavestateEntry_HwRegs : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
const wxChar* GetFilename() const { return L"eeHwRegs.bin"; }
|
||||
void* GetDataPtr() const { return eeHw; }
|
||||
wxString GetFilename() const { return L"eeHwRegs.bin"; }
|
||||
u8* GetDataPtr() const { return eeHw; }
|
||||
uint GetDataSize() const { return sizeof(eeHw); }
|
||||
};
|
||||
|
||||
class SavestateEntry_IopHwRegs : public BaseSavestateEntry
|
||||
class SavestateEntry_IopHwRegs : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
const wxChar* GetFilename() const { return L"iopHwRegs.bin"; }
|
||||
void* GetDataPtr() const { return iopHw; }
|
||||
wxString GetFilename() const { return L"iopHwRegs.bin"; }
|
||||
u8* GetDataPtr() const { return iopHw; }
|
||||
uint GetDataSize() const { return sizeof(iopHw); }
|
||||
};
|
||||
|
||||
class SavestateEntry_Scratchpad : public BaseSavestateEntry
|
||||
class SavestateEntry_Scratchpad : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
const wxChar* GetFilename() const { return L"Scratchpad.bin"; }
|
||||
void* GetDataPtr() const { return eeMem->Scratch; }
|
||||
wxString GetFilename() const { return L"Scratchpad.bin"; }
|
||||
u8* GetDataPtr() const { return eeMem->Scratch; }
|
||||
uint GetDataSize() const { return sizeof(eeMem->Scratch); }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU0mem : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
wxString GetFilename() const { return L"vu0Memory.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[0].Mem; }
|
||||
uint GetDataSize() const { return VU0_MEMSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU1mem : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
wxString GetFilename() const { return L"vu1Memory.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[1].Mem; }
|
||||
uint GetDataSize() const { return VU1_MEMSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU0prog : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
wxString GetFilename() const { return L"vu0Programs.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[0].Micro; }
|
||||
uint GetDataSize() const { return VU0_PROGSIZE; }
|
||||
};
|
||||
|
||||
class SavestateEntry_VU1prog : public BaseArchiveEntry
|
||||
{
|
||||
public:
|
||||
wxString GetFilename() const { return L"vu1Programs.bin"; }
|
||||
u8* GetDataPtr() const { return vuRegs[1].Micro; }
|
||||
uint GetDataSize() const { return VU1_PROGSIZE; }
|
||||
};
|
||||
|
||||
// [TODO] : Add other components as files to the savestate gzip?
|
||||
// * VU0/VU1 memory banks? VU0prog, VU1prog, VU0data, VU1data.
|
||||
// * GS register data?
|
||||
|
@ -88,13 +123,17 @@ public:
|
|||
// would not be useful).
|
||||
//
|
||||
|
||||
static const BaseSavestateEntry* const SavestateEntries[] =
|
||||
static const BaseArchiveEntry* const SavestateEntries[] =
|
||||
{
|
||||
new SavestateEntry_EmotionMemory,
|
||||
new SavestateEntry_IopMemory,
|
||||
new SavestateEntry_HwRegs,
|
||||
new SavestateEntry_IopHwRegs,
|
||||
new SavestateEntry_Scratchpad,
|
||||
new SavestateEntry_VU0mem,
|
||||
new SavestateEntry_VU1mem,
|
||||
new SavestateEntry_VU0prog,
|
||||
new SavestateEntry_VU1prog,
|
||||
};
|
||||
|
||||
static const uint NumSavestateEntries = ArraySize(SavestateEntries);
|
||||
|
@ -107,7 +146,7 @@ static const uint NumSavestateEntries = ArraySize(SavestateEntries);
|
|||
//
|
||||
static Mutex mtx_CompressToDisk;
|
||||
|
||||
static void CheckVersion( pxStreamReader& thr )
|
||||
static void CheckVersion( pxInputStream& thr )
|
||||
{
|
||||
u32 savever;
|
||||
thr.Read( savever );
|
||||
|
@ -135,16 +174,16 @@ static void CheckVersion( pxStreamReader& thr )
|
|||
class SysExecEvent_DownloadState : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
VmStateBuffer* m_dest_buffer;
|
||||
ArchiveEntryList* m_dest_list;
|
||||
|
||||
public:
|
||||
wxString GetEventName() const { return L"VM_Download"; }
|
||||
|
||||
virtual ~SysExecEvent_DownloadState() throw() {}
|
||||
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
|
||||
SysExecEvent_DownloadState( VmStateBuffer* dest=NULL )
|
||||
SysExecEvent_DownloadState( ArchiveEntryList* dest_list=NULL )
|
||||
{
|
||||
m_dest_buffer = dest;
|
||||
m_dest_list = dest_list;
|
||||
}
|
||||
|
||||
bool IsCriticalEvent() const { return true; }
|
||||
|
@ -160,9 +199,25 @@ protected:
|
|||
.SetDiagMsg(L"SysExecEvent_DownloadState: Cannot freeze/download an invalid VM state!")
|
||||
.SetUserMsg(L"There is no active virtual machine state to download or save." );
|
||||
|
||||
memSavingState saveme( m_dest_buffer );
|
||||
memSavingState saveme( m_dest_list->GetBuffer() );
|
||||
ArchiveEntry internals( EntryFilename_InternalStructures );
|
||||
internals.SetDataIndex( saveme.GetCurrentPos() );
|
||||
|
||||
saveme.FreezeAll();
|
||||
|
||||
internals.SetDataSize( saveme.GetCurrentPos() - internals.GetDataIndex() );
|
||||
m_dest_list->Add( internals );
|
||||
|
||||
for (uint i=0; i<NumSavestateEntries; ++i)
|
||||
{
|
||||
m_dest_list->Add( ArchiveEntry( SavestateEntries[i]->GetFilename() )
|
||||
.SetDataIndex( saveme.GetCurrentPos() )
|
||||
.SetDataSize( SavestateEntries[i]->GetDataSize() )
|
||||
);
|
||||
|
||||
saveme.FreezeMem( SavestateEntries[i]->GetDataPtr(), SavestateEntries[i]->GetDataSize() );
|
||||
}
|
||||
|
||||
UI_EnableStateActions();
|
||||
paused_core.AllowResume();
|
||||
}
|
||||
|
@ -180,14 +235,7 @@ protected:
|
|||
ScopedLock m_lock_Compress;
|
||||
|
||||
public:
|
||||
VmStateCompressThread( VmStateBuffer* srcdata, pxStreamWriter* outarchive )
|
||||
: _parent( srcdata, outarchive )
|
||||
{
|
||||
m_lock_Compress.Assign(mtx_CompressToDisk);
|
||||
}
|
||||
|
||||
VmStateCompressThread( ScopedPtr<VmStateBuffer>& srcdata, ScopedPtr<pxStreamWriter>& outarchive )
|
||||
: _parent( srcdata, outarchive )
|
||||
VmStateCompressThread()
|
||||
{
|
||||
m_lock_Compress.Assign(mtx_CompressToDisk);
|
||||
}
|
||||
|
@ -216,7 +264,7 @@ protected:
|
|||
class SysExecEvent_ZipToDisk : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
VmStateBuffer* m_src_buffer;
|
||||
ArchiveEntryList* m_src_list;
|
||||
wxString m_filename;
|
||||
|
||||
public:
|
||||
|
@ -228,16 +276,16 @@ public:
|
|||
|
||||
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
|
||||
|
||||
SysExecEvent_ZipToDisk( ScopedPtr<VmStateBuffer>& src, const wxString& filename )
|
||||
SysExecEvent_ZipToDisk( ScopedPtr<ArchiveEntryList>& srclist, const wxString& filename )
|
||||
: m_filename( filename )
|
||||
{
|
||||
m_src_buffer = src.DetachPtr();
|
||||
m_src_list = srclist.DetachPtr();
|
||||
}
|
||||
|
||||
SysExecEvent_ZipToDisk( VmStateBuffer* src, const wxString& filename )
|
||||
SysExecEvent_ZipToDisk( ArchiveEntryList* srclist, const wxString& filename )
|
||||
: m_filename( filename )
|
||||
{
|
||||
m_src_buffer = src;
|
||||
m_src_list = srclist;
|
||||
}
|
||||
|
||||
bool IsCriticalEvent() const { return true; }
|
||||
|
@ -246,6 +294,9 @@ public:
|
|||
protected:
|
||||
void InvokeEvent()
|
||||
{
|
||||
// Provisionals for scoped cleanup, in case of exception:
|
||||
ScopedPtr<ArchiveEntryList> elist( m_src_list );
|
||||
|
||||
wxString tempfile( m_filename + L".tmp" );
|
||||
|
||||
wxFFileOutputStream* woot = new wxFFileOutputStream(tempfile);
|
||||
|
@ -253,8 +304,8 @@ protected:
|
|||
throw Exception::CannotCreateStream(tempfile);
|
||||
|
||||
// Write the version and screenshot:
|
||||
ScopedPtr<pxStreamWriter> out( new pxStreamWriter(tempfile, new wxZipOutputStream(woot)) );
|
||||
wxZipOutputStream* gzfp = (wxZipOutputStream*)out->GetBaseStream();
|
||||
ScopedPtr<pxOutputStream> out( new pxOutputStream(tempfile, new wxZipOutputStream(woot)) );
|
||||
wxZipOutputStream* gzfp = (wxZipOutputStream*)out->GetWxStreamBase();
|
||||
|
||||
{
|
||||
wxZipEntry* vent = new wxZipEntry(EntryFilename_StateVersion);
|
||||
|
@ -280,13 +331,18 @@ protected:
|
|||
//m_gzfp->Write();
|
||||
//m_gzfp->CloseEntry();
|
||||
|
||||
(new VmStateCompressThread( m_src_buffer, out ))->
|
||||
SetTargetFilename(m_filename).Start();
|
||||
(*new VmStateCompressThread())
|
||||
.SetSource(m_src_list)
|
||||
.SetOutStream(out)
|
||||
.SetFinishedPath(m_filename)
|
||||
.Start();
|
||||
|
||||
// No errors? Release cleanup handlers:
|
||||
elist.DetachPtr();
|
||||
}
|
||||
|
||||
void CleanupEvent()
|
||||
{
|
||||
m_src_buffer = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -325,8 +381,7 @@ protected:
|
|||
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)) );
|
||||
ScopedPtr<pxInputStream> reader( new pxInputStream(m_filename, new wxZipInputStream(woot)) );
|
||||
woot.DetachPtr();
|
||||
|
||||
if (!reader->IsOk())
|
||||
|
@ -336,7 +391,7 @@ protected:
|
|||
.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();
|
||||
wxZipInputStream* gzreader = (wxZipInputStream*)reader->GetWxStreamBase();
|
||||
|
||||
// look for version and screenshot information in the zip stream:
|
||||
|
||||
|
@ -359,12 +414,14 @@ protected:
|
|||
DevCon.WriteLn(L" ... found '%s'", EntryFilename_StateVersion);
|
||||
foundVersion = true;
|
||||
CheckVersion(*reader);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry->GetName().CmpNoCase(EntryFilename_InternalStructures) == 0)
|
||||
{
|
||||
DevCon.WriteLn(L" ... found '%s'", EntryFilename_InternalStructures);
|
||||
foundInternal = entry.DetachPtr();
|
||||
continue;
|
||||
}
|
||||
|
||||
// No point in finding screenshots when loading states -- the screenshots are
|
||||
|
@ -411,6 +468,7 @@ protected:
|
|||
// *ALWAYS* start execution after the new savestate is loaded.
|
||||
|
||||
GetCoreThread().Pause();
|
||||
SysClearExecutionCache();
|
||||
|
||||
for (uint i=0; i<NumSavestateEntries; ++i)
|
||||
{
|
||||
|
@ -437,7 +495,6 @@ protected:
|
|||
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();
|
||||
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
|
||||
}
|
||||
|
@ -451,9 +508,10 @@ void StateCopy_SaveToFile( const wxString& file )
|
|||
{
|
||||
UI_DisableStateActions();
|
||||
|
||||
ScopedPtr<VmStateBuffer> zipbuf(new VmStateBuffer( L"Zippable Savestate" ));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf ));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file ));
|
||||
ScopedPtr<ArchiveEntryList> ziplist (new ArchiveEntryList( new VmStateBuffer( L"Zippable Savestate" ) ));
|
||||
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState ( ziplist ));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk ( ziplist, file ));
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromFile( const wxString& file )
|
||||
|
|
Loading…
Reference in New Issue