Integrated new wxSavestates. Features:

* Savestates are now 'proper' zip archives, which can be opened by any zip tool such as 7zip or winrar.  PS2 virtual machine memory components are stored as individual files (such as eemem.bin, iopmem.bin, etc), and can be extracted, modified, and re-packed easily (maybe fun for hacking!)
 * Savestate filenames are now based on a combination of serial code and CRC, ex: SLUS-12345_(0D386A2).00.p2s
 * Savestates made during the BIOS will have meaningful CRC codes now, instead of 0000000.
 * Minor improvements to error handling.
 * Better support for unicode and internationalized windows installs.
 * Prep work for eventual screenshots embedded into savestates (WIP)
 * Changed i18n macros around a bit to help differentiate out some of the lesser needed translation items. gettext po/pot file updates will be forthcoming.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4132 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-12-23 06:03:49 +00:00
commit 718b04a20d
46 changed files with 1472 additions and 947 deletions

View File

@ -171,8 +171,9 @@
<Unit filename="../../include/Utilities/TlsVariable.inl" />
<Unit filename="../../include/Utilities/TraceLog.h" />
<Unit filename="../../include/Utilities/lnx_memzero.h" />
<Unit filename="../../include/Utilities/pxCheckBox.h" />
<Unit filename="../../include/Utilities/pxEvents.h" />
<Unit filename="../../include/Utilities/pxForwardDefs.h" />
<Unit filename="../../include/Utilities/pxCheckBox.h" />
<Unit filename="../../include/Utilities/pxEvents.h" />
<Unit filename="../../include/Utilities/pxRadioPanel.h" />
<Unit filename="../../include/Utilities/pxStaticText.h" />
<Unit filename="../../include/Utilities/pxStreams.h" />

View File

@ -445,6 +445,10 @@
RelativePath="..\..\include\Utilities\pxEvents.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\pxForwardDefs.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\pxRadioPanel.h"
>

View File

@ -18,36 +18,7 @@
// Dependencies.h : Contains classes required by all Utilities headers.
// This file is included by most .h files provided by the Utilities class.
// --------------------------------------------------------------------------------------
// Forward Declarations Section
// --------------------------------------------------------------------------------------
class wxOutputStream;
class wxFileOutputStream;
class wxFFileOutputStream;
class wxInputStream;
class wxFileInputStream;
class wxFFileInputStream;
class wxPoint;
class wxRect;
class wxSize;
extern const wxSize wxDefaultSize;
extern const wxPoint wxDefaultPosition;
namespace Threading
{
class Mutex;
class Semaphore;
class pxThread;
}
namespace Exception
{
class BaseException;
}
#include "pxForwardDefs.h"
// This should prove useful....
#define wxsFormat wxString::Format
@ -164,6 +135,50 @@ public:
}
};
// --------------------------------------------------------------------------------------
// _(x) / _t(x) / _d(x) / pxL(x) / pxLt(x) [macros]
// --------------------------------------------------------------------------------------
// Define pxWex's own i18n helpers. These override the wxWidgets helpers and provide
// additional functionality. Define them FIRST THING, to make sure that wx's own gettext
// macros aren't in place.
//
// _ is for standard translations
// _t is for tertiary low priority translations
// _d is for debug/devel build translations
#define WXINTL_NO_GETTEXT_MACRO
#ifndef _
# define _(s) pxGetTranslation(_T(s))
#endif
#ifndef _t
# define _t(s) pxGetTranslation(_T(s))
#endif
#ifndef _d
# define _d(s) pxGetTranslation(_T(s))
#endif
// pxL / pxLt / pxDt -- macros provided for tagging translation strings, without actually running
// them through the translator (which the _() does automatically, and sometimes we don't
// want that). This is a shorthand replacement for wxTRANSLATE. pxL is a standard translation
// moniker. pxLt is for tertiary strings that have a very low translation priority. pxDt is for
// debug/devel specific translations.
//
#ifndef pxL
# define pxL(a) wxT(a)
#endif
#ifndef pxLt
# define pxLt(a) wxT(a)
#endif
#ifndef pxDt
# define pxDt(a) wxT(a)
#endif
#include <wx/string.h>
#include <wx/intl.h>
#include <wx/log.h>
@ -177,6 +192,9 @@ public:
#include "Pcsx2Defs.h"
// --------------------------------------------------------------------------------------
// Handy Human-readable constants for common immediate values (_16kb -> _4gb)
static const sptr _64kb = 0x10000;
static const sptr _16kb = _64kb / 4;
static const sptr _128kb = _64kb * 2;
@ -191,27 +209,6 @@ static const s64 _256mb = _1mb * 256;
static const s64 _1gb = _256mb * 4;
static const s64 _4gb = _1gb * 4;
// ===========================================================================================
// i18n/Translation Feature Set!
// ===========================================================================================
extern const wxChar* __fastcall pxExpandMsg( const wxChar* key, const wxChar* englishContent );
extern const wxChar* __fastcall pxGetTranslation( const wxChar* message );
extern bool pxIsEnglish( int id );
extern wxString fromUTF8( const char* src );
extern wxString fromAscii( const char* src );
// --------------------------------------------------------------------------------------
// wxLt(x) [macro]
// --------------------------------------------------------------------------------------
// macro provided for tagging translation strings, without actually running them through the
// translator (which the _() does automatically, and sometimes we don't want that). This is
// a shorthand replacement for wxTRANSLATE.
//
#ifndef wxLt
# define wxLt(a) wxT(a)
#endif
// --------------------------------------------------------------------------------------
// pxE(key, msg) and pxEt(key, msg) [macros]
@ -249,6 +246,18 @@ extern wxString fromAscii( const char* src );
// For use with tertiary translations (low priority).
#define pxEt(key, english) pxExpandMsg( wxT(key), english )
// For use with Dev/debug build translations (low priority).
#define pxE_dev(key, english) pxExpandMsg( wxT(key), english )
extern const wxChar* __fastcall pxExpandMsg( const wxChar* key, const wxChar* englishContent );
extern const wxChar* __fastcall pxGetTranslation( const wxChar* message );
extern bool pxIsEnglish( int id );
extern wxString fromUTF8( const char* src );
extern wxString fromAscii( const char* src );
#include "Utilities/Assertions.h"
#include "Utilities/Exceptions.h"
#include "Utilities/ScopedPtr.h"

View File

@ -184,7 +184,7 @@ public: \
// an App message loop we'll still want it to be handled in a reasonably graceful manner.
class CancelEvent : public RuntimeError
{
DEFINE_RUNTIME_EXCEPTION( CancelEvent, RuntimeError, wxLt("No reason given.") )
DEFINE_RUNTIME_EXCEPTION( CancelEvent, RuntimeError, pxLt("No reason given.") )
public:
explicit CancelEvent( const wxString& logmsg )
@ -223,7 +223,7 @@ public: \
class ParseError : public RuntimeError
{
DEFINE_RUNTIME_EXCEPTION( ParseError, RuntimeError, wxLt("Parse error") );
DEFINE_RUNTIME_EXCEPTION( ParseError, RuntimeError, pxL("Parse error") );
};
// ---------------------------------------------------------------------------------------
@ -247,7 +247,7 @@ public: \
class HardwareDeficiency : public RuntimeError
{
public:
DEFINE_RUNTIME_EXCEPTION( HardwareDeficiency, RuntimeError, wxLt("Your machine's hardware is incapable of running PCSX2. Sorry dood.") );
DEFINE_RUNTIME_EXCEPTION( HardwareDeficiency, RuntimeError, pxL("Your machine's hardware is incapable of running PCSX2. Sorry dood.") );
};
// ---------------------------------------------------------------------------------------

View File

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

View File

@ -176,8 +176,10 @@ public:
template< typename T >
class ScopedAlloc : public BaseScopedAlloc<T>
{
typedef BaseScopedAlloc<T> _parent;
public:
ScopedAlloc( size_t size=0 ) : BaseScopedAlloc<T>()
ScopedAlloc( size_t size=0 ) : _parent()
{
Alloc(size);
}
@ -195,17 +197,19 @@ public:
this->m_buffer = (T*)malloc( this->m_size * sizeof(T) );
if (!this->m_buffer)
throw Exception::OutOfMemory("ScopedAlloc");
throw Exception::OutOfMemory(L"ScopedAlloc");
}
virtual void Resize( size_t newsize )
{
this->m_size = newsize;
this->m_buffer = (T*)realloc(this->m_buffer * sizeof(T), newsize);
this->m_buffer = (T*)realloc(this->m_buffer, this->m_size * sizeof(T));
if (!this->m_buffer)
throw Exception::OutOfMemory("ScopedAlloc::Resize");
throw Exception::OutOfMemory(L"ScopedAlloc::Resize");
}
using _parent::operator[];
};
// --------------------------------------------------------------------------------------
@ -220,8 +224,10 @@ public:
template< typename T, uint align >
class ScopedAlignedAlloc : public BaseScopedAlloc<T>
{
typedef BaseScopedAlloc<T> _parent;
public:
ScopedAlignedAlloc( size_t size=0 ) : BaseScopedAlloc<T>()
ScopedAlignedAlloc( size_t size=0 ) : _parent()
{
Alloc(size);
}
@ -250,4 +256,6 @@ public:
if (!this->m_buffer)
throw Exception::OutOfMemory(L"ScopedAlignedAlloc::Resize");
}
using _parent::operator[];
};

View File

@ -0,0 +1,51 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 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
// --------------------------------------------------------------------------------------
// Forward Declarations Section
// --------------------------------------------------------------------------------------
class wxOutputStream;
class wxFileOutputStream;
class wxFFileOutputStream;
class wxStreamBase;
class wxInputStream;
class wxFileInputStream;
class wxFFileInputStream;
class wxPoint;
class wxRect;
class wxSize;
class pxInputStream;
class pxOutputStream;
extern const wxSize wxDefaultSize;
extern const wxPoint wxDefaultPosition;
namespace Threading
{
class Mutex;
class Semaphore;
class pxThread;
}
namespace Exception
{
class BaseException;
}

View File

@ -13,68 +13,104 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "wx/filefn.h"
// --------------------------------------------------------------------------------------
// 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;
virtual wxFileOffset Tell() const=0;
virtual wxFileOffset Seek( wxFileOffset ofs, wxSeekMode mode = wxFromStart )=0;
virtual wxFileOffset Length() const;
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 )
{
Write( &data, sizeof(data) );
}
wxFileOffset Tell() const;
wxFileOffset Seek( wxFileOffset ofs, wxSeekMode mode = wxFromStart );
};
// --------------------------------------------------------------------------------------
// 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 )
{
Read( &dest, sizeof(dest) );
}
wxFileOffset Tell() const;
wxFileOffset Seek( wxFileOffset ofs, wxSeekMode mode = wxFromStart );
};

View File

@ -150,6 +150,7 @@ set(UtilitiesHeaders
../../include/Utilities/Path.h
../../include/Utilities/PageFaultSource.h
../../include/Utilities/pxCheckBox.h
../../include/Utilities/pxForwardDefs.h
../../include/Utilities/pxRadioPanel.h
../../include/Utilities/pxStaticText.h
../../include/Utilities/pxStreams.h

View File

@ -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())
@ -331,6 +331,7 @@ wxString Exception::CannotCreateStream::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("A file could not be created."));
retval.Write("\n");
_formatUserMsg(retval);
return retval;
}
@ -341,7 +342,7 @@ wxString Exception::CannotCreateStream::FormatDisplayMessage() const
wxString Exception::FileNotFound::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("File not found.");
retval.Write("File not found.\n");
_formatDiagMsg(retval);
return retval;
}
@ -350,6 +351,7 @@ wxString Exception::FileNotFound::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("File not found."));
retval.Write("\n");
_formatUserMsg(retval);
return retval;
}
@ -360,7 +362,7 @@ wxString Exception::FileNotFound::FormatDisplayMessage() const
wxString Exception::AccessDenied::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("Permission denied to file.");
retval.Write("Permission denied to file.\n");
_formatDiagMsg(retval);
return retval;
}
@ -369,6 +371,7 @@ wxString Exception::AccessDenied::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("Permission denied while trying to open file, likely due to insufficient user account rights."));
retval.Write("\n");
_formatUserMsg(retval);
return retval;
}
@ -379,7 +382,7 @@ wxString Exception::AccessDenied::FormatDisplayMessage() const
wxString Exception::EndOfStream::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("Unexpected end of file or stream.");
retval.Write("Unexpected end of file or stream.\n");
_formatDiagMsg(retval);
return retval;
}
@ -388,6 +391,7 @@ wxString Exception::EndOfStream::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("Unexpected end of file or stream encountered. File is probably truncated or corrupted."));
retval.Write("\n");
_formatUserMsg(retval);
return retval;
}

View File

@ -38,7 +38,7 @@ ConsoleLogSource_Threading::ConsoleLogSource_Threading()
static const TraceLogDescriptor myDesc =
{
L"pxThread", L"pxThread",
wxLt("Threading activity: start, detach, sync, deletion, etc.")
pxLt("Threading activity: start, detach, sync, deletion, etc.")
};
m_Descriptor = &myDesc;

View File

@ -22,39 +22,71 @@
#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();
}
wxFileOffset pxStreamBase::Length() const
{
if (!GetWxStreamBase()) return 0;
return GetWxStreamBase()->GetLength();
}
// --------------------------------------------------------------------------------------
// 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(); }
wxFileOffset pxInputStream::Tell() const
{
return m_stream_in->TellI();
}
wxFileOffset pxInputStream::Seek( wxFileOffset ofs, wxSeekMode mode )
{
return m_stream_in->SeekI(ofs, mode);
}
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 +101,55 @@ 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(); }
wxFileOffset pxOutputStream::Tell() const
{
return m_stream_out->TellO();
}
wxFileOffset pxOutputStream::Seek( wxFileOffset ofs, wxSeekMode mode )
{
return m_stream_out->SeekO( ofs, mode );
}
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)

View File

@ -33,7 +33,7 @@ ConsoleLogSource_App::ConsoleLogSource_App()
static const TraceLogDescriptor myDesc =
{
L"AppEvents", L"App Events",
wxLt("Includes idle event processing and some other uncommon event usages.")
pxLt("Includes idle event processing and some other uncommon event usages.")
};
m_Descriptor = &myDesc;

View File

@ -411,7 +411,9 @@ void isoFile::Open( const wxString& srcfile )
// (and if numparts is incremented, elsn will get assigned accordingly)
if (!Detect())
throw Exception::BadStream().SetUserMsg(wxLt("Unrecognized ISO file format."));
throw Exception::BadStream()
.SetUserMsg(L"Unrecognized ISO image file format")
.SetDiagMsg(_("ISO mounting failed: PCSX2 is unable to identify the ISO image type."));
if (!(m_flags & ISOFLAGS_BLOCKDUMP_V2))
{

View File

@ -260,16 +260,26 @@ void ElfObject::readFile()
if (rsize < data.GetSizeInBytes()) throw Exception::EndOfStream(filename);
}
static wxString GetMsg_InvalidELF()
{
return
_("Cannot load ELF binary image. The file may be corrupt or incomplete.") +
wxString(L"\n\n") +
_("If loading from an ISO image, this error may be caused by an unsupported ISO image type or bug in PCSX2 ISO image support.");
}
void ElfObject::checkElfSize(s64 elfsize)
{
if (elfsize > 0xfffffff)
throw Exception::BadStream(filename).SetBothMsgs(wxLt("Illegal ELF file size over 2GB!"));
const wxChar* diagMsg = NULL;
if (elfsize > 0xfffffff) diagMsg = L"Illegal ELF file size over 2GB!";
else if (elfsize == -1) diagMsg = L"ELF file does not exist!";
else if (elfsize == 0) diagMsg = L"Unexpected end of ELF file.";
if (elfsize == -1)
throw Exception::BadStream(filename).SetBothMsgs(wxLt("ELF file does not exist!"));
if (elfsize == 0)
throw Exception::BadStream(filename).SetBothMsgs(wxLt("Unexpected end of ELF file."));
if (diagMsg)
throw Exception::BadStream(filename)
.SetDiagMsg(diagMsg)
.SetUserMsg(GetMsg_InvalidELF());
}
u32 ElfObject::getCRC()

View File

@ -99,7 +99,7 @@ extern bool SIGNAL_IMR_Pending;
__fi void gsInterrupt()
{
GIF_LOG("gsInterrupt: %8.8x", cpuRegs.cycle);
GIF_LOG("gsInterrupt caught!");
if (dmacRegs.ctrl.MFD == MFD_GIF) // GIF MFIFO
{

View File

@ -806,7 +806,7 @@ void SysMtgsThread::WaitForOpen()
// emulator forcefully, or to continue waiting on the GS.
throw Exception::PluginOpenError( PluginId_GS )
.SetBothMsgs(wxLt("The MTGS thread has become unresponsive while waiting for the GS plugin to open."));
.SetBothMsgs(pxLt("The MTGS thread has become unresponsive while waiting for the GS plugin to open."));
}
}

View File

@ -23,6 +23,7 @@
#include "CDVD/CDVDisoReader.h"
#include "Utilities/ScopedPtr.h"
#include "Utilities/pxStreams.h"
#if _MSC_VER
# include "svnrev.h"
@ -680,7 +681,7 @@ SysCorePlugins *g_plugins = NULL;
wxString Exception::SaveStateLoadError::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("Savestate is corrupt or incomplete!");
retval.Write("Savestate is corrupt or incomplete!\n");
_formatDiagMsg(retval);
return retval;
}
@ -689,6 +690,7 @@ wxString Exception::SaveStateLoadError::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("The savestate cannot be loaded, as it appears to be corrupt or incomplete."));
retval.Write("\n");
_formatUserMsg(retval);
return retval;
}
@ -810,11 +812,11 @@ SysCorePlugins::PluginStatus_t::PluginStatus_t( PluginsEnum_t _pid, const wxStri
if( !wxFile::Exists( Filename ) )
throw Exception::PluginLoadError( pid ).SetStreamName(srcfile)
.SetBothMsgs(wxLt("The configured %s plugin file was not found"));
.SetBothMsgs(pxL("The configured %s plugin file was not found"));
if( !Lib.Load( Filename ) )
throw Exception::PluginLoadError( pid ).SetStreamName(Filename)
.SetBothMsgs(wxLt("The configured %s plugin file is not a valid dynamic library"));
.SetBothMsgs(pxL("The configured %s plugin file is not a valid dynamic library"));
// Try to enumerate the new v2.0 plugin interface first.
@ -1324,7 +1326,7 @@ bool SysCorePlugins::Init()
{
// fixme: use plugin's GetLastError (not implemented yet!)
throw Exception::PluginInitError( PluginId_Mcd )
.SetBothMsgs(wxLt("Internal Memorycard Plugin failed to initialize."));
.SetBothMsgs(pxLt("Internal Memorycard Plugin failed to initialize."));
}
}
@ -1402,9 +1404,6 @@ void SysCorePlugins::Freeze( PluginsEnum_t pid, SaveStateBase& state )
// No locking leeded -- DoFreeze locks as needed, and this avoids MTGS deadlock.
//ScopedLock lock( m_mtx_PluginStatus );
Console.Indent().WriteLn( "%s %s", state.IsSaving() ? "Saving" : "Loading",
tbl_PluginInfo[pid].shortname );
freezeData fP = { 0, NULL };
if( !DoFreeze( pid, FREEZE_SIZE, &fP ) )
fP.size = 0;
@ -1412,6 +1411,9 @@ void SysCorePlugins::Freeze( PluginsEnum_t pid, SaveStateBase& state )
int fsize = fP.size;
state.Freeze( fsize );
Console.Indent().WriteLn( "%s %s", state.IsSaving() ? "Saving" : "Loading",
tbl_PluginInfo[pid].shortname );
if( state.IsLoading() && (fsize == 0) )
{
// no state data to read, but the plugin expects some state data.
@ -1446,6 +1448,82 @@ void SysCorePlugins::Freeze( PluginsEnum_t pid, SaveStateBase& state )
state.CommitBlock( fP.size );
}
size_t SysCorePlugins::GetFreezeSize( PluginsEnum_t pid )
{
freezeData fP = { 0, NULL };
if (!DoFreeze( pid, FREEZE_SIZE, &fP)) return 0;
return fP.size;
}
void SysCorePlugins::FreezeOut( PluginsEnum_t pid, void* dest )
{
// No locking needed -- DoFreeze locks as needed, and this avoids MTGS deadlock.
//ScopedLock lock( m_mtx_PluginStatus );
freezeData fP = { 0, (s8*)dest };
if (!DoFreeze( pid, FREEZE_SIZE, &fP)) return;
if (!fP.size) return;
Console.Indent().WriteLn( "Saving %s", tbl_PluginInfo[pid].shortname );
if (!DoFreeze(pid, FREEZE_SAVE, &fP))
throw Exception::FreezePluginFailure( pid );
}
void SysCorePlugins::FreezeOut( PluginsEnum_t pid, pxOutputStream& outfp )
{
// No locking needed -- DoFreeze locks as needed, and this avoids MTGS deadlock.
//ScopedLock lock( m_mtx_PluginStatus );
freezeData fP = { 0, NULL };
if (!DoFreeze( pid, FREEZE_SIZE, &fP)) return;
if (!fP.size) return;
Console.Indent().WriteLn( "Saving %s", tbl_PluginInfo[pid].shortname );
ScopedAlloc<s8> data( fP.size );
fP.data = data.GetPtr();
if (!DoFreeze(pid, FREEZE_SAVE, &fP))
throw Exception::FreezePluginFailure( pid );
outfp.Write( fP.data, fP.size );
}
void SysCorePlugins::FreezeIn( PluginsEnum_t pid, pxInputStream& infp )
{
// No locking needed -- DoFreeze locks as needed, and this avoids MTGS deadlock.
//ScopedLock lock( m_mtx_PluginStatus );
freezeData fP = { 0, NULL };
if (!DoFreeze( pid, FREEZE_SIZE, &fP ))
fP.size = 0;
Console.Indent().WriteLn( "Loading %s", tbl_PluginInfo[pid].shortname );
if (!infp.IsOk() || !infp.Length())
{
// no state data to read, but the plugin expects some state data?
// Issue a warning to console...
if( fP.size != 0 )
Console.Indent().Warning( "Warning: No data for this plugin was found. Plugin status may be unpredictable." );
return;
// Note: Size mismatch check could also be done here on loading, but
// some plugins may have built-in version support for non-native formats or
// older versions of a different size... or could give different sizes depending
// on the status of the plugin when loading, so let's ignore it.
}
ScopedAlloc<s8> data( fP.size );
fP.data = data.GetPtr();
infp.Read( fP.data, fP.size );
if (!DoFreeze(pid, FREEZE_LOAD, &fP))
throw Exception::ThawPluginFailure( pid );
}
bool SysCorePlugins::KeyEvent( const keyEvent& evt )
{
ScopedLock lock( m_mtx_PluginStatus );

View File

@ -326,6 +326,10 @@ public:
virtual bool IsInitialized( PluginsEnum_t pid ) const;
virtual bool IsLoaded( PluginsEnum_t pid ) const;
virtual size_t GetFreezeSize( PluginsEnum_t pid );
virtual void FreezeOut( PluginsEnum_t pid, void* dest );
virtual void FreezeOut( PluginsEnum_t pid, pxOutputStream& outfp );
virtual void FreezeIn( PluginsEnum_t pid, pxInputStream& infp );
virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state );
virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data );

View File

@ -16,7 +16,7 @@
#ifndef PCSX2_PRECOMPILED_HEADER
#define PCSX2_PRECOMPILED_HEADER
//#pragma once // no dice, causes problems in GCC PCH (which doesn't really work very well
//#pragma once // no dice, causes problems in GCC PCH (which doesn't really work very well anyway)
// Disable some pointless warnings...
#ifdef _MSC_VER
@ -24,20 +24,7 @@
# pragma warning(disable:4996) //ignore the stricmp deprecated warning
#endif
//////////////////////////////////////////////////////////////////////////////////////////
// Define PCSX2's own i18n helpers. These override the wxWidgets helpers and provide
// additional functionality.
//
#define WXINTL_NO_GETTEXT_MACRO
#undef _
#define _(s) pxGetTranslation(_T(s))
// macro provided for tagging translation strings, without actually running them through the
// translator (which the _() does automatically, and sometimes we don't want that). This is
// a shorthand replacement for wxTRANSLATE.
#ifndef wxLt
# define wxLt(a) wxT(a)
#endif
#include "Utilities/Dependencies.h"
#define NOMINMAX // Disables other libs inclusion of their own min/max macros (we use std instead)
@ -84,7 +71,6 @@ typedef int BOOL;
// unchanged for long periods of time, or happen to be used by almost everything, so they
// need a full recompile anyway, when modified (etc)
#include "zlib.h"
#include "Pcsx2Defs.h"
#include "i18n.h"

View File

@ -20,6 +20,7 @@
#include "ps2/BiosTools.h"
#include "COP0.h"
#include "VUmicro.h"
#include "Cache.h"
#include "AppConfig.h"
@ -45,10 +46,19 @@ static void PostLoadPrep()
UpdateVSyncRate();
}
// --------------------------------------------------------------------------------------
// SaveStateBase (implementations)
// --------------------------------------------------------------------------------------
wxString SaveStateBase::GetFilename( int slot )
{
wxString serialName( DiscSerial );
if (serialName.IsEmpty()) serialName = L"BIOS";
return (g_Conf->Folders.Savestates +
pxsFmt( L"%08X.%03d", ElfCRC, slot )).GetFullPath();
pxsFmt( L"%s (%08X).%02d.p2s", serialName.c_str(), ElfCRC, slot )).GetFullPath();
//return (g_Conf->Folders.Savestates +
// pxsFmt( L"%08X.%03d", ElfCRC, slot )).GetFullPath();
}
SaveStateBase::SaveStateBase( SafeArray<u8>& memblock )
@ -66,8 +76,6 @@ void SaveStateBase::Init( SafeArray<u8>* memblock )
m_memory = memblock;
m_version = g_SaveVersion;
m_idx = 0;
m_sectid = FreezeId_Unknown;
m_pid = PluginId_GS;
m_DidBios = false;
}
@ -88,7 +96,7 @@ void SaveStateBase::PrepBlock( int size )
void SaveStateBase::FreezeTag( const char* src )
{
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 );
strcpy( m_tagspace, src );
@ -102,66 +110,70 @@ void SaveStateBase::FreezeTag( const char* src )
}
}
void SaveStateBase::FreezeBios()
SaveStateBase& SaveStateBase::FreezeBios()
{
FreezeTag( "BIOS" );
// Check the BIOS, and issue a warning if the bios for this state
// doesn't match the bios currently being used (chances are it'll still
// work fine, but some games are very picky).
u32 bioscheck = BiosChecksum;
char biosdesc[256];
char descin[128], desccmp[128];
wxString descout;
IsBIOS( g_Conf->FullpathToBios(), descout );
memzero( descin );
memzero( desccmp );
pxToUTF8 utf8(BiosDescription);
pxToUTF8 utf8(descout);
memcpy_fast( descin, utf8, utf8.Length() );
memcpy_fast( desccmp, utf8, utf8.Length() );
memzero( biosdesc );
memcpy_fast( biosdesc, utf8, std::min( sizeof(biosdesc), utf8.Length() ) );
Freeze( bioscheck );
Freeze( biosdesc );
// ... and only freeze bios info once per state, since the user msg could
// become really annoying on a corrupted state or something. (have to always
// load though, so that we advance past the duplicated info, if present)
if( IsLoading() || !m_DidBios )
Freeze( descin );
if( !m_DidBios )
if (bioscheck != BiosChecksum)
{
if( memcmp( descin, desccmp, 128 ) != 0 )
{
Console.Newline();
Console.Indent(1).Error( "Warning: BIOS Version Mismatch, savestate may be unstable!" );
Console.Indent(2).Error(
"Current Version: %s\n"
"Savestate Version: %s\n",
utf8.data(), descin
);
}
Console.Newline();
Console.Indent(1).Error( "Warning: BIOS Version Mismatch, savestate may be unstable!" );
Console.Indent(2).Error(
"Current BIOS: %ls (crc=0x%08x)\n"
"Savestate BIOS: %s (crc=0x%08x)\n",
BiosDescription.c_str(), BiosChecksum,
biosdesc, bioscheck
);
}
m_DidBios = true;
return *this;
}
static const int MainMemorySizeInBytes =
Ps2MemSize::MainRam + Ps2MemSize::Scratch + Ps2MemSize::Hardware +
Ps2MemSize::IopRam + Ps2MemSize::IopHardware + 0x0100;
static const uint MainMemorySizeInBytes =
Ps2MemSize::MainRam + Ps2MemSize::Scratch + Ps2MemSize::Hardware +
Ps2MemSize::IopRam + Ps2MemSize::IopHardware;
void SaveStateBase::FreezeMainMemory()
SaveStateBase& SaveStateBase::FreezeMainMemory()
{
if( IsLoading() )
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(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(iopMem->Sif, 0x000100); // iop's sif 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;
}
void SaveStateBase::FreezeRegisters()
SaveStateBase& SaveStateBase::FreezeInternals()
{
if( IsLoading() )
PreLoadPrep();
@ -201,6 +213,8 @@ void SaveStateBase::FreezeRegisters()
// Fifth Block - iop-related systems
// ---------------------------------
FreezeTag( "IOP-Subsystems" );
FreezeMem(iopMem->Sif, sizeof(iopMem->Sif)); // iop's sif memory (not really needed, but oh well)
#ifdef ENABLE_NEW_IOPDMA
iopDmacFreeze();
#endif
@ -216,165 +230,35 @@ void SaveStateBase::FreezeRegisters()
if( IsLoading() )
PostLoadPrep();
return *this;
}
void SaveStateBase::WritebackSectionLength( int seekpos, int sectlen, const wxChar* sectname )
SaveStateBase& SaveStateBase::FreezePlugins()
{
int realsectsize = m_idx - seekpos;
if( IsSaving() )
for (uint i=0; i<PluginId_Count; ++i)
{
// write back the section length...
*((u32*)m_memory->GetPtr(seekpos-4)) = realsectsize;
}
else // IsLoading!!
{
if( sectlen != realsectsize ) // if they don't match then we have a problem, jim.
{
throw Exception::SaveStateLoadError()
.SetDiagMsg(wxsFormat(L"Invalid size encountered on section '%s'.", sectname ))
.SetUserMsg(_("The savestate data is invalid or corrupted."));
}
FreezeTag( FastFormatAscii().Write("Plugin:%s", tbl_PluginInfo[i].shortname) );
GetCorePlugins().Freeze( (PluginsEnum_t)i, *this );
}
return *this;
}
bool SaveStateBase::FreezeSection( int seek_section )
SaveStateBase& SaveStateBase::FreezeAll()
{
const bool isSeeking = (seek_section != FreezeId_NotSeeking );
if( IsSaving() ) pxAssertDev( !isSeeking, "Cannot seek on a saving-mode savestate stream." );
Freeze( m_sectid );
if( seek_section == m_sectid ) return false;
switch( m_sectid )
{
case FreezeId_End:
return false;
case FreezeId_Bios:
{
int sectlen = 128;
FreezeTag( "BiosVersion" );
Freeze( sectlen );
if( sectlen != 128 )
{
throw Exception::SaveStateLoadError()
.SetDiagMsg(L"Invalid size encountered on BiosVersion section.")
.SetUserMsg(_("The savestate data is invalid or corrupted."));
}
if( isSeeking )
m_idx += sectlen;
else
FreezeBios();
m_sectid++;
}
break;
case FreezeId_Memory:
{
FreezeTag( "MainMemory" );
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 );
m_sectid++;
}
break;
case FreezeId_Registers:
{
FreezeTag( "HardwareRegisters" );
int seekpos = m_idx+4;
int sectlen = 0xdead; // gets written back over with "real" data in IsSaving() mode
Freeze( sectlen );
FreezeRegisters();
WritebackSectionLength( seekpos, sectlen, L"HardwareRegisters" );
m_sectid++;
}
break;
case FreezeId_Plugin:
{
FreezeTag( "Plugin" );
int seekpos = m_idx+4;
int sectlen = 0xdead; // gets written back over with "real" data in IsSaving() mode
Freeze( sectlen );
Freeze( m_pid );
if( isSeeking )
m_idx += sectlen;
else
GetCorePlugins().Freeze( (PluginsEnum_t)m_pid, *this );
WritebackSectionLength( seekpos, sectlen, L"Plugins" );
// following increments only affect Saving mode, which needs to be sure to save all
// plugins (order doesn't matter but sequential is easy enough. (ignored by Loading mode)
m_pid++;
if( m_pid >= PluginId_Count )
m_sectid = FreezeId_End;
}
break;
case FreezeId_Unknown:
default:
pxAssert( IsSaving() );
// Skip unknown sections with a warning log.
// Maybe it'll work! (haha?)
int size;
Freeze( m_tagspace );
Freeze( size );
m_tagspace[sizeof(m_tagspace)-1] = 0;
Console.Warning(
"Warning: Unknown tag encountered while loading savestate; going to ignore it!\n"
"\tTagname: %s, Size: %d", m_tagspace, size
);
m_idx += size;
break;
}
if( wxThread::IsMain() )
wxSafeYield( NULL, true );
return true;
FreezeMainMemory();
FreezeBios();
FreezeInternals();
FreezePlugins();
return *this;
}
void SaveStateBase::FreezeAll()
{
if( IsSaving() )
{
// Loading mode streams will assign these, but saving mode reads them so better
// do some setup first.
m_sectid = (int)FreezeId_End+1;
m_pid = PluginId_GS;
}
while( FreezeSection() );
}
//////////////////////////////////////////////////////////////////////////////////
// --------------------------------------------------------------------------------------
// memSavingState (implementations)
// --------------------------------------------------------------------------------------
// uncompressed to/from memory state saves implementation
memSavingState::memSavingState( SafeArray<u8>& save_to )
@ -390,22 +274,32 @@ memSavingState::memSavingState( SafeArray<u8>* save_to )
// Saving of state data
void memSavingState::FreezeMem( void* data, int size )
{
m_memory->MakeRoomFor( m_idx+size );
if (!size) return;
m_memory->MakeRoomFor( m_idx + size );
memcpy_fast( m_memory->GetPtr(m_idx), data, size );
m_idx += size;
}
void memSavingState::FreezeAll()
void memSavingState::MakeRoomForData()
{
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->MakeRoomFor( MemoryBaseAllocSize );
_parent::FreezeAll();
m_memory->MakeRoomFor( m_idx + MemoryBaseAllocSize );
}
// Saving of state data to a memory buffer
memSavingState& memSavingState::FreezeAll()
{
MakeRoomForData();
_parent::FreezeAll();
return *this;
}
// --------------------------------------------------------------------------------------
// memLoadingState (implementations)
// --------------------------------------------------------------------------------------
memLoadingState::memLoadingState( const SafeArray<u8>& load_from )
: SaveStateBase( const_cast<SafeArray<u8>&>(load_from) )
{
@ -418,7 +312,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);
@ -426,25 +320,6 @@ void memLoadingState::FreezeMem( void* data, int size )
memcpy_fast( data, src, size );
}
bool memLoadingState::SeekToSection( PluginsEnum_t pid )
{
m_idx = 0; // start from the beginning
do
{
while( FreezeSection( FreezeId_Plugin ) );
if( m_sectid == FreezeId_End ) return false;
FreezeTag( "Plugin" );
int sectlen = 0xdead;
Freeze( sectlen );
Freeze( m_pid );
} while( m_pid != pid );
return true;
}
// --------------------------------------------------------------------------------------
// SaveState Exception Messages
// --------------------------------------------------------------------------------------
@ -452,22 +327,21 @@ bool memLoadingState::SeekToSection( PluginsEnum_t pid )
wxString Exception::UnsupportedStateVersion::FormatDiagnosticMessage() const
{
// 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
{
// m_message_user contains a recoverable savestate error which is helpful to the user.
return wxsFormat(
return
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
{
// Note: no stacktrace needed for this one...
return wxsFormat(
return pxsFmt(
L"Game/CDVD does not match the savestate CRC.\n"
L"\tCdvd CRC: 0x%X\n\tGame CRC: 0x%X\n",
Crc_Savestate, Crc_Cdvd
@ -476,11 +350,10 @@ wxString Exception::StateCrcMismatch::FormatDiagnosticMessage() const
wxString Exception::StateCrcMismatch::FormatDisplayMessage() const
{
return wxsFormat(
return
m_message_user + L"\n\n" +
wxsFormat(
pxsFmt(
L"Savestate game/crc mismatch. Cdvd CRC: 0x%X Game CRC: 0x%X\n",
Crc_Savestate, Crc_Cdvd
)
);
);
}

View File

@ -24,31 +24,13 @@
// 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.
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
// between the GS saving function and the MTGS's needs. :)
extern s32 CALLBACK gsSafeFreeze( int mode, freezeData *data );
enum FreezeSectionId
{
FreezeId_NotSeeking = -2,
FreezeId_End,
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
// but can be skipped if the savestate has only plugins.
FreezeId_Bios,
FreezeId_Memory,
FreezeId_Registers,
FreezeId_Plugin,
// anything here and beyond we can skip, with a warning
FreezeId_Unknown,
};
namespace Exception
{
// ---------------------------------------------------------------------------------------
@ -121,9 +103,7 @@ protected:
u32 m_version; // version of the savestate being loaded.
int m_idx; // current read/write index of the allocation
int m_sectid;
int m_pid;
int m_idx; // current read/write index of the allocation
bool m_DidBios;
@ -144,7 +124,12 @@ public:
// Loads or saves the entire emulation state.
// Note: The Cpu state must be reset, and plugins *open*, prior to Defrosting
// (loading) a state!
virtual void FreezeAll();
virtual SaveStateBase& FreezeAll();
virtual SaveStateBase& FreezeMainMemory();
virtual SaveStateBase& FreezeBios();
virtual SaveStateBase& FreezeInternals();
virtual SaveStateBase& FreezePlugins();
// Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays.
// For dynamically allocated pointers use FreezeMem instead.
@ -164,19 +149,26 @@ 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;
}
void WritebackSectionLength( int seekpos, int sectlen, const wxChar* sectname );
bool FreezeSection( int seek_section = FreezeId_NotSeeking );
// Freezes an identifier value into the savestate for troubleshooting purposes.
// Identifiers can be used to determine where in a savestate that data has become
// skewed (if the value does not match then the error occurs somewhere prior to that
@ -201,10 +193,6 @@ protected:
// Load/Save functions for the various components of our glorious emulator!
void FreezeBios();
void FreezeMainMemory();
void FreezeRegisters();
void rcntFreeze();
void vuMicroFreeze();
void vif0Freeze();
@ -238,17 +226,18 @@ class memSavingState : public SaveStateBase
typedef SaveStateBase _parent;
protected:
static const int ReallocThreshold = 0x200000; // 256k reallocation block size.
static const int MemoryBaseAllocSize = 0x02b00000; // 45 meg base alloc
static const int ReallocThreshold = _1mb / 4; // 256k reallocation block size.
static const int MemoryBaseAllocSize = _8mb; // 8 meg base alloc when PS2 main memory is excluded
public:
virtual ~memSavingState() throw() { }
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();
memSavingState& FreezeAll();
bool IsSaving() const { return true; }
};
@ -261,9 +250,7 @@ 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 );
bool IsSaving() const { return false; }
bool IsFinished() const { return m_idx >= m_memory->GetSizeInBytes(); }

View File

@ -93,27 +93,27 @@ static const TraceLogDescriptor
TLD_ELF = {
L"ELF", L"ELF",
wxLt("Dumps detailed information for PS2 executables (ELFs).")
pxDt("Dumps detailed information for PS2 executables (ELFs).")
},
TLD_eeRecPerf = {
L"EErecPerf", L"EErec Performance",
wxLt("Logs manual protection, split blocks, and other things that might impact performance.")
pxDt("Logs manual protection, split blocks, and other things that might impact performance.")
},
TLD_eeConsole = {
L"EEout", L"EE Console",
wxLt("Shows the game developer's logging text (EE processor)")
pxDt("Shows the game developer's logging text (EE processor)")
},
TLD_iopConsole = {
L"IOPout", L"IOP Console",
wxLt("Shows the game developer's logging text (IOP processor)")
pxDt("Shows the game developer's logging text (IOP processor)")
},
TLD_deci2 = {
L"DECI2", L"DECI2 Console",
wxLt("Shows DECI2 debugging logs (EE processor)")
pxDt("Shows DECI2 debugging logs (EE processor)")
};
SysConsoleLogPack::SysConsoleLogPack()
@ -131,7 +131,7 @@ SysConsoleLogPack::SysConsoleLogPack()
static const SysTraceLogDescriptor
TLD_SIF = {
L"SIF", L"SIF (EE <-> IOP)",
wxLt(""),
pxDt(""),
"SIF"
};
@ -142,109 +142,109 @@ TLD_SIF = {
static const SysTraceLogDescriptor
TLD_EE_Bios = {
L"Bios", L"Bios",
wxLt("SYSCALL and DECI2 activity."),
pxDt("SYSCALL and DECI2 activity."),
"EE"
},
TLD_EE_Memory = {
L"Memory", L"Memory",
wxLt("Direct memory accesses to unknown or unmapped EE memory space."),
pxDt("Direct memory accesses to unknown or unmapped EE memory space."),
"eMem"
},
TLD_EE_R5900 = {
L"R5900", L"R5900 Core",
wxLt("Disasm of executing core instructions (excluding COPs and CACHE)."),
pxDt("Disasm of executing core instructions (excluding COPs and CACHE)."),
"eDis"
},
TLD_EE_COP0 = {
L"COP0", L"COP0",
wxLt("Disasm of COP0 instructions (MMU, cpu and dma status, etc)."),
pxDt("Disasm of COP0 instructions (MMU, cpu and dma status, etc)."),
"eDis"
},
TLD_EE_COP1 = {
L"FPU", L"COP1/FPU",
wxLt("Disasm of the EE's floating point unit (FPU) only."),
pxDt("Disasm of the EE's floating point unit (FPU) only."),
"eDis"
},
TLD_EE_COP2 = {
L"VUmacro", L"COP2/VUmacro",
wxLt("Disasm of the EE's VU0macro co-processor instructions."),
pxDt("Disasm of the EE's VU0macro co-processor instructions."),
"eDis"
},
TLD_EE_Cache = {
L"Cache", L"Cache",
wxLt("Execution of EE cache instructions."),
pxDt("Execution of EE cache instructions."),
"eDis"
},
TLD_EE_KnownHw = {
L"HwRegs", L"Hardware Regs",
wxLt("All known hardware register accesses (very slow!); not including sub filter options below."),
pxDt("All known hardware register accesses (very slow!); not including sub filter options below."),
"eReg"
},
TLD_EE_UnknownHw = {
L"UnknownRegs", L"Unknown Regs",
wxLt("Logs only unknown, unmapped, or unimplemented register accesses."),
pxDt("Logs only unknown, unmapped, or unimplemented register accesses."),
"eReg"
},
TLD_EE_DMAhw = {
L"DmaRegs", L"DMA Regs",
wxLt("Logs only DMA-related registers."),
pxDt("Logs only DMA-related registers."),
"eReg"
},
TLD_EE_IPU = {
L"IPU", L"IPU",
wxLt("IPU activity: hardware registers, decoding operations, DMA status, etc."),
pxDt("IPU activity: hardware registers, decoding operations, DMA status, etc."),
"IPU"
},
TLD_EE_GIFtag = {
L"GIFtags", L"GIFtags",
wxLt("All GIFtag parse activity; path index, tag type, etc."),
pxDt("All GIFtag parse activity; path index, tag type, etc."),
"GIF"
},
TLD_EE_VIFcode = {
L"VIFcodes", L"VIFcodes",
wxLt("All VIFcode processing; command, tag style, interrupts."),
pxDt("All VIFcode processing; command, tag style, interrupts."),
"VIF"
},
TLD_EE_SPR = {
L"MFIFO", L"Scratchpad MFIFO",
wxLt("Scratchpad's MFIFO activity."),
pxDt("Scratchpad's MFIFO activity."),
"SPR"
},
TLD_EE_DMAC = {
L"DmaCtrl", L"DMA Controller",
wxLt("Actual data transfer logs, bus right arbitration, stalls, etc."),
pxDt("Actual data transfer logs, bus right arbitration, stalls, etc."),
"eDmaC"
},
TLD_EE_Counters = {
L"Counters", L"Counters",
wxLt("Tracks all EE counters events and some counter register activity."),
pxDt("Tracks all EE counters events and some counter register activity."),
"eCnt"
},
TLD_EE_VIF = {
L"VIF", L"VIF",
wxLt("Dumps various VIF and VIFcode processing data."),
pxDt("Dumps various VIF and VIFcode processing data."),
"VIF"
},
TLD_EE_GIF = {
L"GIF", L"GIF",
wxLt("Dumps various GIF and GIFtag parsing data."),
pxDt("Dumps various GIF and GIFtag parsing data."),
"GIF"
};
@ -255,73 +255,73 @@ TLD_EE_GIF = {
static const SysTraceLogDescriptor
TLD_IOP_Bios = {
L"Bios", L"Bios",
wxLt("SYSCALL and IRX activity."),
pxDt("SYSCALL and IRX activity."),
"IOP"
},
TLD_IOP_Memory = {
L"Memory", L"Memory",
wxLt("Direct memory accesses to unknown or unmapped IOP memory space."),
pxDt("Direct memory accesses to unknown or unmapped IOP memory space."),
"iMem"
},
TLD_IOP_R3000A = {
L"R3000A", L"R3000A Core",
wxLt("Disasm of executing core instructions (excluding COPs and CACHE)."),
pxDt("Disasm of executing core instructions (excluding COPs and CACHE)."),
"iDis"
},
TLD_IOP_COP2 = {
L"COP2/GPU", L"COP2",
wxLt("Disasm of the IOP's GPU co-processor instructions."),
pxDt("Disasm of the IOP's GPU co-processor instructions."),
"iDis"
},
TLD_IOP_KnownHw = {
L"HwRegs", L"Hardware Regs",
wxLt("All known hardware register accesses, not including the sub-filters below."),
pxDt("All known hardware register accesses, not including the sub-filters below."),
"iReg"
},
TLD_IOP_UnknownHw = {
L"UnknownRegs", L"Unknown Regs",
wxLt("Logs only unknown, unmapped, or unimplemented register accesses."),
pxDt("Logs only unknown, unmapped, or unimplemented register accesses."),
"iReg"
},
TLD_IOP_DMAhw = {
L"DmaRegs", L"DMA Regs",
wxLt("Logs only DMA-related registers."),
pxDt("Logs only DMA-related registers."),
"iReg"
},
TLD_IOP_Memcards = {
L"Memorycards", L"Memorycards",
wxLt("Memorycard reads, writes, erases, terminators, and other processing."),
pxDt("Memorycard reads, writes, erases, terminators, and other processing."),
"Mcd"
},
TLD_IOP_PAD = {
L"Pad", L"Pad",
wxLt("Gamepad activity on the SIO."),
pxDt("Gamepad activity on the SIO."),
"Pad"
},
TLD_IOP_DMAC = {
L"DmaCrl", L"DMA Controller",
wxLt("Actual DMA event processing and data transfer logs."),
pxDt("Actual DMA event processing and data transfer logs."),
"iDmaC"
},
TLD_IOP_Counters = {
L"Counters", L"Counters",
wxLt("Tracks all IOP counters events and some counter register activity."),
pxDt("Tracks all IOP counters events and some counter register activity."),
"iCnt"
},
TLD_IOP_CDVD = {
L"CDVD", L"CDVD",
wxLt("Detailed logging of CDVD hardware."),
pxDt("Detailed logging of CDVD hardware."),
"CDVD"
};

View File

@ -602,9 +602,11 @@ wxString SysGetDiscID()
if( !ElfCRC )
{
// FIXME: If the system is currently running the BIOS, it should return a serial based on
// FIXME: system is currently running the BIOS, so it should return a serial based on
// the BIOS being run (either a checksum of the BIOS roms, and/or a string based on BIOS
// region and revision).
return wxEmptyString;
}
return pxsFmt( L"%08x", ElfCRC );

View File

@ -150,8 +150,8 @@ void SysCoreThread::UploadStateCopy( const VmStateBuffer& copy )
{
if( !pxAssertDev( IsPaused(), "CoreThread is not paused; new VM state cannot be uploaded." ) ) return;
SysClearExecutionCache();
memLoadingState( copy ).FreezeAll();
memLoadingState loadme( copy );
loadme.FreezeAll();
m_resetVirtualMachine = false;
}

View File

@ -79,22 +79,10 @@ void vuMemoryReserve::Reset()
void SaveStateBase::vuMicroFreeze()
{
FreezeTag( "vuMicro" );
pxAssume( VU0.Mem != NULL );
pxAssume( VU1.Mem != NULL );
FreezeTag( "vuMicroRegs" );
Freeze(VU0.ACC);
// Seemingly silly and pointless use of temp var: GCC is unable to bind packed fields
// (appears to be a bug, tracked here: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36566 ).
// Dereferencing outside the context of the function (via a temp var) seems to circumvent it. --air
u32& temp_vu0_code = VU0.code;
Freeze(temp_vu0_code);
FreezeMem(VU0.Mem, 4*1024);
FreezeMem(VU0.Micro, 4*1024);
Freeze(VU0.code);
Freeze(VU0.VF);
Freeze(VU0.VI);
@ -104,9 +92,6 @@ void SaveStateBase::vuMicroFreeze()
u32& temp_vu1_code = VU1.code;
Freeze(temp_vu1_code);
FreezeMem(VU1.Mem, 16*1024);
FreezeMem(VU1.Micro, 16*1024);
Freeze(VU1.VF);
Freeze(VU1.VI);
}

View File

@ -16,92 +16,192 @@
#pragma once
#include "Utilities/PersistentThread.h"
//#include <zlib/zlib.h>
#include "Utilities/pxStreams.h"
#include "wx/zipstrm.h"
using namespace Threading;
class IStreamWriter
// --------------------------------------------------------------------------------------
// ArchiveEntry
// --------------------------------------------------------------------------------------
class ArchiveEntry
{
protected:
wxString m_filename;
uptr m_dataidx;
size_t m_datasize;
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 )
ArchiveEntry( const wxString& filename=wxEmptyString )
: m_filename( filename )
{
Write( &data, sizeof(data) );
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;
}
};
class IStreamReader
typedef SafeArray< u8 > ArchiveDataBuffer;
// --------------------------------------------------------------------------------------
// ArchiveEntryList
// --------------------------------------------------------------------------------------
class ArchiveEntryList
{
DeclareNoncopyableObject( ArchiveEntryList );
protected:
std::vector<ArchiveEntry> m_list;
ScopedPtr<ArchiveDataBuffer> m_data;
public:
virtual ~IStreamReader() throw() {}
ArchiveEntryList() {}
virtual void Read( void* dest, size_t size )=0;
virtual wxString GetStreamName() const=0;
template< typename T >
void Read( T& dest )
ArchiveEntryList( ArchiveDataBuffer* data )
{
Read( &dest, sizeof(dest) );
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];
}
};
typedef void FnType_WriteCompressedHeader( IStreamWriter& thr );
typedef void FnType_ReadCompressedHeader( IStreamReader& thr );
// --------------------------------------------------------------------------------------
// BaseCompressThread
// --------------------------------------------------------------------------------------
class BaseCompressThread
: public pxThread
, public IStreamWriter
{
typedef pxThread _parent;
protected:
FnType_WriteCompressedHeader* m_WriteHeaderInThread;
const wxString m_filename;
ScopedPtr< SafeArray< u8 > > m_src_buffer;
ScopedPtr< ArchiveEntryList > m_src_list;
ScopedPtr< pxOutputStream > m_gzfp;
bool m_PendingSaveFlag;
wxString m_final_filename;
BaseCompressThread( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL)
: m_filename( file )
, m_src_buffer( srcdata )
public:
virtual ~BaseCompressThread() throw();
BaseCompressThread& SetSource( ArchiveEntryList* srcdata )
{
m_WriteHeaderInThread = writeHeader;
m_PendingSaveFlag = false;
m_src_list = srcdata;
return *this;
}
virtual ~BaseCompressThread() throw();
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;
}
wxString GetStreamName() const { return m_gzfp->GetStreamName(); }
BaseCompressThread& SetTargetFilename(const wxString& filename)
{
m_final_filename = filename;
return *this;
}
protected:
BaseCompressThread()
{
m_PendingSaveFlag = false;
}
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 OnCleanupInThread();
};

View File

@ -19,10 +19,12 @@
#include "SaveState.h"
#include "ThreadedZipTools.h"
#include "Utilities/SafeArray.inl"
#include "wx/wfstream.h"
BaseCompressThread::~BaseCompressThread() throw()
{
_parent::Cancel();
if( m_PendingSaveFlag )
{
wxGetApp().ClearPendingSave();
@ -36,83 +38,54 @@ void BaseCompressThread::SetPendingSave()
m_PendingSaveFlag = true;
}
CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeheader )
: 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()
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 );
// Safeguard against corruption by writing to a temp file, and then
// copying the final result over the original:
wxString tempfile( m_filename + L".tmp" );
uint listlen = m_src_list->GetLength();
for( uint i=0; i<listlen; ++i )
{
const ArchiveEntry& entry = (*m_src_list)[i];
if (!entry.GetDataSize()) continue;
if( !(m_gzfp = gzopen(tempfile.ToUTF8(), "wb")) )
throw Exception::CannotCreateStream( m_filename );
wxArchiveOutputStream& woot = *(wxArchiveOutputStream*)m_gzfp->GetWxStreamBase();
woot.PutNextEntry( entry.GetFilename() );
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
static const uint BlockSize = 0x64000;
uint curidx = 0;
#if defined(ZLIB_VERNUM) && (ZLIB_VERNUM >= 0x1240)
gzbuffer(m_gzfp, 0x100000); // 1mb buffer size for less file fragments (Windows/NTFS)
#endif
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();
}
if( m_WriteHeaderInThread )
m_WriteHeaderInThread( *this );
m_gzfp->Close();
static const int BlockSize = 0x64000;
int curidx = 0;
do {
int thisBlockSize = std::min( BlockSize, m_src_buffer->GetSizeInBytes() - curidx );
if( gzwrite( m_gzfp, m_src_buffer->GetPtr(curidx), thisBlockSize ) < thisBlockSize )
throw Exception::BadStream( m_filename );
curidx += thisBlockSize;
Yield( 2 );
} while( curidx < m_src_buffer->GetSizeInBytes() );
gzclose( m_gzfp );
m_gzfp = NULL;
if( !wxRenameFile( tempfile, m_filename, true ) )
throw Exception::BadStream( m_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."));
if( !wxRenameFile( m_gzfp->GetStreamName(), m_final_filename, true ) )
throw Exception::BadStream( m_final_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."));
Console.WriteLn( "(gzipThread) Data saved to disk without error." );
}
void CompressThread_gzip::OnCleanupInThread()
void BaseCompressThread::OnCleanupInThread()
{
_parent::OnCleanupInThread();
wxGetApp().DeleteThread( this );

View File

@ -540,6 +540,7 @@ public:
void WipeUserModeSettings();
void ReadUserModeSettings();
bool HasPendingSaves() const;
void StartPendingSave();
void ClearPendingSave();

View File

@ -765,7 +765,8 @@ void AppConfig_OnChangedSettingsFolder( bool overwrite )
if( overwrite )
{
if( wxFileExists( iniFilename ) && !wxRemoveFile( iniFilename ) )
throw Exception::AccessDenied(iniFilename).SetBothMsgs(wxLt("Failed to overwrite existing settings file; permission was denied."));
throw Exception::AccessDenied(iniFilename)
.SetBothMsgs(pxL("Failed to overwrite existing settings file; permission was denied."));
}
// Bind into wxConfigBase to allow wx to use our config internally, and delete whatever

View File

@ -298,8 +298,7 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
wxString gameFixes;
wxString gameCheats;
// [TODO] : Fix this so that it recognizes and reports BIOS-booting status!
wxString gameName (L"Unknown");
wxString gameName;
wxString gameCompat;
if (ElfCRC) gameCRC.Printf( L"%8.8x", ElfCRC );
@ -309,27 +308,39 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
const bool verbose( newGameKey != curGameKey );
curGameKey = newGameKey;
if (IGameDatabase* GameDB = AppHost_GetGameDatabase() )
if (!curGameKey.IsEmpty())
{
Game_Data game;
if (GameDB->findGame(game, curGameKey)) {
int compat = game.getInt("Compat");
gameName = game.getString("Name");
gameName += L" (" + game.getString("Region") + L")";
gameCompat = L" [Status = "+compatToStringWX(compat)+L"]";
}
if (IGameDatabase* GameDB = AppHost_GetGameDatabase() )
{
Game_Data game;
if (GameDB->findGame(game, curGameKey)) {
int compat = game.getInt("Compat");
gameName = game.getString("Name");
gameName += L" (" + game.getString("Region") + L")";
gameCompat = L" [Status = "+compatToStringWX(compat)+L"]";
}
if (EmuConfig.EnablePatches) {
if (int patches = InitPatches(gameCRC, game)) {
gamePatch.Printf(L" [%d Patches]", patches);
if (verbose) Console.WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", patches);
}
if (int fixes = loadGameSettings(fixup, game, verbose)) {
gameFixes.Printf(L" [%d Fixes]", fixes);
if (EmuConfig.EnablePatches) {
if (int patches = InitPatches(gameCRC, game)) {
gamePatch.Printf(L" [%d Patches]", patches);
if (verbose) Console.WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", patches);
}
if (int fixes = loadGameSettings(fixup, game, verbose)) {
gameFixes.Printf(L" [%d Fixes]", fixes);
}
}
}
}
if (gameName.IsEmpty() && gameSerial.IsEmpty() && gameCRC.IsEmpty())
{
// if all these conditions are met, it should mean that we're currently running BIOS code.
// Chances are the BiosChecksum value is still zero or out of date, however -- because
// the BIos isn't loaded until after initial calls to ApplySettings.
gameName = L"Booting PS2 BIOS... ";
}
if (EmuConfig.EnableCheats) {
if (int cheats = InitCheats(gameCRC)) {
gameCheats.Printf(L" [%d Cheats]", cheats);

View File

@ -504,9 +504,6 @@ class GameDatabaseLoaderThread : public pxThread
{
typedef pxThread _parent;
protected:
gzFile m_gzfp;
public:
GameDatabaseLoaderThread()
: pxThread( L"GameDatabaseLoader" )

View File

@ -605,6 +605,12 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
bool Pcsx2App::HasPendingSaves() const
{
AffinityAssert_AllowFrom_MainUI();
return !!m_PendingSaves;
}
// A call to this method informs the app that there is a pending save operation that must be
// finished prior to exiting the app, or else data loss will occur. Any call to this method
// should be matched by a call to ClearPendingSave().

View File

@ -52,7 +52,7 @@ namespace Exception
public:
explicit CannotApplySettings( BaseApplicableConfigPanel* thispanel )
{
SetBothMsgs(wxLt("Cannot apply new settings, one of the settings is invalid."));
SetBothMsgs(pxL("Cannot apply new settings, one of the settings is invalid."));
m_Panel = thispanel;
IsVerbose = true;
}

View File

@ -409,22 +409,22 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
// create Appearance menu and submenus
menuFontSizes.Append( MenuId_FontSize_Small, _("Small"), _("Fits a lot of log in a microcosmically small area."),
menuFontSizes.Append( MenuId_FontSize_Small, _("Small"), _t("Fits a lot of log in a microcosmically small area."),
wxITEM_RADIO )->Check( options.FontSize == 7 );
menuFontSizes.Append( MenuId_FontSize_Normal, _("Normal"),_("It's what I use (the programmer guy)."),
menuFontSizes.Append( MenuId_FontSize_Normal, _("Normal"),_t("It's what I use (the programmer guy)."),
wxITEM_RADIO )->Check( options.FontSize == 8 );
menuFontSizes.Append( MenuId_FontSize_Large, _("Large"), _("Its nice and readable."),
menuFontSizes.Append( MenuId_FontSize_Large, _("Large"), _t("Its nice and readable."),
wxITEM_RADIO )->Check( options.FontSize == 10 );
menuFontSizes.Append( MenuId_FontSize_Huge, _("Huge"), _("In case you have a really high res display."),
menuFontSizes.Append( MenuId_FontSize_Huge, _("Huge"), _t("In case you have a really high res display."),
wxITEM_RADIO )->Check( options.FontSize == 12 );
menuFontSizes.AppendSeparator();
menuFontSizes.Append( MenuId_ColorScheme_Light, _("Light theme"), _("Default soft-tone color scheme."), wxITEM_RADIO );
menuFontSizes.Append( MenuId_ColorScheme_Dark, _("Dark theme"), _("Classic black color scheme for people who enjoy having text seared into their optic nerves."), wxITEM_RADIO );
menuFontSizes.Append( MenuId_ColorScheme_Light, _("Light theme"), _t("Default soft-tone color scheme."), wxITEM_RADIO );
menuFontSizes.Append( MenuId_ColorScheme_Dark, _("Dark theme"), _t("Classic black color scheme for people who enjoy having text seared into their optic nerves."), wxITEM_RADIO );
menuAppear.AppendSeparator();
menuAppear.Append( wxID_ANY, _("Always on Top"),
_("When checked the log window will be visible over other foreground windows."), wxITEM_CHECK );
_t("When checked the log window will be visible over other foreground windows."), wxITEM_CHECK );
menuLog.Append(wxID_SAVE, _("&Save..."), _("Save log contents to file"));
menuLog.Append(wxID_CLEAR, _("C&lear"), _("Clear the log window contents"));

View File

@ -161,19 +161,19 @@ void Dialogs::CreateMemoryCardDialog::CreateControls()
const RadioPanelItem tbl_CardSizes[] =
{
RadioPanelItem(_("8 MB [most compatible]"), _("This is the standard Sony-provisioned size, and is supported by all games and BIOS versions."))
. SetToolTip(_("Always use this option if you want the safest and surest memory card behavior."))
. SetToolTip(_t("Always use this option if you want the safest and surest memory card behavior."))
. SetInt(8),
RadioPanelItem(_("16 MB"), _("A typical size for 3rd-party memory cards which should work with most games."))
. SetToolTip(_("16 and 32 MB cards have roughly the same compatibility factor."))
. SetToolTip(_t("16 and 32 MB cards have roughly the same compatibility factor."))
. SetInt(16),
RadioPanelItem(_("32 MB"), _("A typical size for 3rd-party memory cards which should work with most games."))
. SetToolTip(_("16 and 32 MB cards have roughly the same compatibility factor."))
. SetToolTip(_t("16 and 32 MB cards have roughly the same compatibility factor."))
. SetInt(32),
RadioPanelItem(_("64 MB"), _("Low compatibility warning: Yes it's very big, but may not work with many games."))
. SetToolTip(_("Use at your own risk. Erratic memory card behavior is possible (though unlikely)."))
. SetToolTip(_t("Use at your own risk. Erratic memory card behavior is possible (though unlikely)."))
. SetInt(64)
};

View File

@ -99,9 +99,6 @@ Dialogs::McdConfigDialog::McdConfigDialog( wxWindow* parent )
// [TODO] : Plan here is to add an advanced tab which gives the user the ability
// to configure the names of each memory card slot.
//AddPage<McdConfigPanel_Toggles> ( wxLt("Settings"), cfgid.MemoryCard );
//AddPage<McdConfigPanel_Standard> ( wxLt("Slots 1/2"), cfgid.MemoryCard );
*this += Heading(_("Drag items over other items in the list to swap or copy memory cards.")) | StdExpand();
*this += StdPadding;

View File

@ -73,13 +73,12 @@ Dialogs::SysConfigDialog::SysConfigDialog(wxWindow* parent)
CreateListbook( wxGetApp().GetImgList_Config() );
const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config );
AddPage<CpuPanelEE> ( wxLt("EE/IOP"), cfgid.Cpu );
AddPage<CpuPanelVU> ( wxLt("VUs"), cfgid.Cpu );
AddPage<VideoPanel> ( wxLt("GS"), cfgid.Cpu );
AddPage<GSWindowSettingsPanel> ( wxLt("GS Window"), cfgid.Video );
AddPage<SpeedHacksPanel> ( wxLt("Speedhacks"), cfgid.Speedhacks );
AddPage<GameFixesPanel> ( wxLt("Game Fixes"), cfgid.Gamefixes );
//AddPage<GameDatabasePanel> ( wxLt("Game Database"),cfgid.Plugins );
AddPage<CpuPanelEE> ( pxL("EE/IOP"), cfgid.Cpu );
AddPage<CpuPanelVU> ( pxL("VUs"), cfgid.Cpu );
AddPage<VideoPanel> ( pxL("GS"), cfgid.Cpu );
AddPage<GSWindowSettingsPanel> ( pxL("GS Window"), cfgid.Video );
AddPage<SpeedHacksPanel> ( pxL("Speedhacks"), cfgid.Speedhacks );
AddPage<GameFixesPanel> ( pxL("Game Fixes"), cfgid.Gamefixes );
AddListbook();
AddOkCancel();
@ -96,8 +95,8 @@ Dialogs::ComponentsConfigDialog::ComponentsConfigDialog(wxWindow* parent)
CreateListbook( wxGetApp().GetImgList_Config() );
const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config );
AddPage<PluginSelectorPanel> ( wxLt("Plugins"), cfgid.Plugins );
AddPage<BiosSelectorPanel> ( wxLt("BIOS"), cfgid.Cpu );
AddPage<PluginSelectorPanel> ( pxL("Plugins"), cfgid.Plugins );
AddPage<BiosSelectorPanel> ( pxL("BIOS"), cfgid.Cpu );
AddListbook();
AddOkCancel();
@ -114,12 +113,12 @@ Dialogs::InterfaceConfigDialog::InterfaceConfigDialog(wxWindow *parent)
CreateListbook( wxGetApp().GetImgList_Config() );
const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config );
AddPage<StandardPathsPanel> ( wxLt("Appearance"), cfgid.Appearance );
AddPage<StandardPathsPanel> ( wxLt("Folders"), cfgid.Paths );
AddPage<StandardPathsPanel> ( pxL("Appearance"), cfgid.Appearance );
AddPage<StandardPathsPanel> ( pxL("Folders"), cfgid.Paths );
AddListbook();
AddOkCancel();
//*this += new Panels::LanguageSelectionPanel( this ) | pxCenter;
//wxDialogWithHelpers::AddOkCancel( NULL, false );
}
}

View File

@ -37,7 +37,7 @@ ConsoleLogSource_Event::ConsoleLogSource_Event()
static const TraceLogDescriptor myDesc =
{
L"SysEvents", L"SysVM Control Events",
wxLt("Logs events as they are passed to the PS2 virtual machine."),
pxLt("Logs events as they are passed to the PS2 virtual machine."),
};
m_Descriptor = &myDesc;

View File

@ -240,26 +240,26 @@ static const GlobalCommandDescriptor CommandDeclarations[] =
{
{ "States_FreezeCurrentSlot",
States_FreezeCurrentSlot,
wxLt( "Save state" ),
wxLt( "Saves the virtual machine state to the current slot." ),
pxL( "Save state" ),
pxL( "Saves the virtual machine state to the current slot." ),
},
{ "States_DefrostCurrentSlot",
States_DefrostCurrentSlot,
wxLt( "Load state" ),
wxLt( "Loads a virtual machine state from the current slot." ),
pxL( "Load state" ),
pxL( "Loads a virtual machine state from the current slot." ),
},
{ "States_CycleSlotForward",
States_CycleSlotForward,
wxLt( "Cycle to next slot" ),
wxLt( "Cycles the current save slot in +1 fashion!" ),
pxL( "Cycle to next slot" ),
pxL( "Cycles the current save slot in +1 fashion!" ),
},
{ "States_CycleSlotBackward",
States_CycleSlotBackward,
wxLt( "Cycle to prev slot" ),
wxLt( "Cycles the current save slot in -1 fashion!" ),
pxL( "Cycle to prev slot" ),
pxL( "Cycles the current save slot in -1 fashion!" ),
},
{ "Frameskip_Toggle",

View File

@ -273,7 +273,9 @@ void SysExecEvent_ApplyPlugins::InvokeEvent()
// FIXME : We only actually have to save plugins here, except the recovery code
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
memSavingState( *(buffer.Reassign(new VmStateBuffer(L"StateBuffer_ApplyNewPlugins"))) ).FreezeAll();
memSavingState saveme( *(buffer.Reassign(new VmStateBuffer(L"StateBuffer_ApplyNewPlugins"))) );
saveme.FreezeAll();
}
ScopedCoreThreadClose closed_core;

View File

@ -145,11 +145,11 @@ void Panels::FramelimiterPanel::Apply()
catch( Exception::ParseError& )
{
throw Exception::CannotApplySettings( this )
.SetDiagMsg(wxsFormat(
.SetDiagMsg(pxsFmt(
L"Error while parsing either NTSC or PAL framerate settings.\n\tNTSC Input = %s\n\tPAL Input = %s",
m_text_BaseNtsc->GetValue().c_str(), m_text_BasePal->GetValue().c_str()
) )
.SetUserMsg(_("Error while parsing either NTSC or PAL framerate settings. Settings must be valid floating point numerics."));
.SetUserMsg(_t("Error while parsing either NTSC or PAL framerate settings. Settings must be valid floating point numerics."));
}
appfps.SanityCheck();
@ -289,11 +289,11 @@ Panels::VideoPanel::VideoPanel( wxWindow* parent ) :
wxPanelWithHelpers* right = new wxPanelWithHelpers( this, wxVERTICAL );
m_check_SynchronousGS = new pxCheckBox( right, _("Use Synchronized MTGS"),
_("For troubleshooting potential bugs in the MTGS only, as it is potentially very slow.")
_t("For troubleshooting potential bugs in the MTGS only, as it is potentially very slow.")
);
m_check_DisableOutput = new pxCheckBox( right, _("Disable all GS output"),
_("Completely disables all GS plugin activity; ideal for benchmarking EEcore components.")
_t("Completely disables all GS plugin activity; ideal for benchmarking EEcore components.")
);
m_check_SynchronousGS->SetToolTip( pxEt( "!ContextTip:GS:SyncMTGS",

View File

@ -37,6 +37,8 @@ bool States_isSlotUsed(int num)
return wxFileExists( SaveStateBase::GetFilename( num ) );
}
// FIXME : Use of the IsSavingOrLoading flag is mostly a hack until we implement a
// complete thread to manage queuing savestate tasks, and zipping states to disk. --air
static volatile u32 IsSavingOrLoading = false;
class SysExecEvent_ClearSavingLoadingFlag : public SysExecEvent
@ -60,7 +62,10 @@ protected:
void States_FreezeCurrentSlot()
{
if( AtomicExchange(IsSavingOrLoading, true) )
// FIXME : Use of the IsSavingOrLoading flag is mostly a hack until we implement a
// complete thread to manage queuing savestate tasks, and zipping states to disk. --air
if( wxGetApp().HasPendingSaves() || AtomicExchange(IsSavingOrLoading, true) )
{
Console.WriteLn( "Load or save action is already pending." );
return;
@ -84,8 +89,6 @@ void States_DefrostCurrentSlot()
StateCopy_LoadFromSlot( StatesC );
GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() );
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
}
static void OnSlotChanged()

View File

@ -14,36 +14,264 @@
*/
#include "PrecompiledHeader.h"
#include "MemoryTypes.h"
#include "App.h"
#include "System/SysThreads.h"
#include "SaveState.h"
#include "VUmicro.h"
#include "ZipTools/ThreadedZipTools.h"
#include "Utilities/pxStreams.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" );
static const char SavestateIdentString[] = "PCSX2 Savestate";
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
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.dat";
static void SaveStateFile_WriteHeader( IStreamWriter& thr )
// --------------------------------------------------------------------------------------
// BaseSavestateEntry
// --------------------------------------------------------------------------------------
class BaseSavestateEntry
{
thr.Write( SavestateIdentString );
thr.Write( g_SaveVersion );
protected:
BaseSavestateEntry() {}
virtual ~BaseSavestateEntry() throw() {}
public:
virtual wxString GetFilename() const=0;
virtual void FreezeIn( pxInputStream& reader ) const=0;
virtual void FreezeOut( SaveStateBase& writer ) const=0;
virtual bool IsRequired() const=0;
};
class MemorySavestateEntry : public BaseSavestateEntry
{
protected:
MemorySavestateEntry() {}
virtual ~MemorySavestateEntry() throw() {}
public:
virtual void FreezeIn( pxInputStream& reader ) const;
virtual void FreezeOut( SaveStateBase& writer ) const;
virtual bool IsRequired() const { return true; }
protected:
virtual u8* GetDataPtr() const=0;
virtual uint GetDataSize() const=0;
};
class PluginSavestateEntry : public BaseSavestateEntry
{
protected:
PluginsEnum_t m_pid;
public:
PluginSavestateEntry( PluginsEnum_t pid )
{
m_pid = pid;
}
virtual ~PluginSavestateEntry() throw() {}
virtual wxString GetFilename() const;
virtual void FreezeIn( pxInputStream& reader ) const;
virtual void FreezeOut( SaveStateBase& writer ) const;
virtual bool IsRequired() const { return false; }
protected:
virtual PluginsEnum_t GetPluginId() const { return m_pid; }
};
void MemorySavestateEntry::FreezeIn( pxInputStream& reader ) const
{
const uint entrySize = reader.Length();
const uint expectedSize = GetDataSize();
if (entrySize < expectedSize)
{
Console.WriteLn( Color_Yellow, " '%s' is incomplete (expected 0x%x bytes, loading only 0x%x bytes)",
GetFilename(), expectedSize, entrySize );
}
uint copylen = std::min(entrySize, expectedSize);
reader.Read( GetDataPtr(), copylen );
}
static void SaveStateFile_ReadHeader( IStreamReader& thr )
void MemorySavestateEntry::FreezeOut( SaveStateBase& writer ) const
{
char ident[SavestateIdentLen] = {0};
writer.FreezeMem( GetDataPtr(), GetDataSize() );
}
thr.Read( ident );
wxString PluginSavestateEntry::GetFilename() const
{
return pxsFmt( "Plugin %s.dat", tbl_PluginInfo[m_pid].shortname );
}
if( strcmp(SavestateIdentString, ident) )
throw Exception::SaveStateLoadError( thr.GetStreamName() )
.SetDiagMsg(wxsFormat( L"Unrecognized file signature while loading savestate."))
.SetUserMsg(_("This is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2."));
void PluginSavestateEntry::FreezeIn( pxInputStream& reader ) const
{
GetCorePlugins().FreezeIn( GetPluginId(), reader );
}
void PluginSavestateEntry::FreezeOut( SaveStateBase& writer ) const
{
if (uint size = GetCorePlugins().GetFreezeSize( GetPluginId() ))
{
writer.PrepBlock( size );
GetCorePlugins().FreezeOut( GetPluginId(), writer.GetBlockPtr() );
writer.CommitBlock( size );
}
}
// --------------------------------------------------------------------------------------
// 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 MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"eeMemory.bin"; }
u8* GetDataPtr() const { return eeMem->Main; }
uint GetDataSize() const { return sizeof(eeMem->Main); }
virtual void FreezeIn( pxInputStream& reader ) const
{
SysClearExecutionCache();
MemorySavestateEntry::FreezeIn( reader );
}
};
class SavestateEntry_IopMemory : public MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"iopMemory.bin"; }
u8* GetDataPtr() const { return iopMem->Main; }
uint GetDataSize() const { return sizeof(iopMem->Main); }
};
class SavestateEntry_HwRegs : public MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"eeHwRegs.bin"; }
u8* GetDataPtr() const { return eeHw; }
uint GetDataSize() const { return sizeof(eeHw); }
};
class SavestateEntry_IopHwRegs : public MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"iopHwRegs.bin"; }
u8* GetDataPtr() const { return iopHw; }
uint GetDataSize() const { return sizeof(iopHw); }
};
class SavestateEntry_Scratchpad : public MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"Scratchpad.bin"; }
u8* GetDataPtr() const { return eeMem->Scratch; }
uint GetDataSize() const { return sizeof(eeMem->Scratch); }
};
class SavestateEntry_VU0mem : public MemorySavestateEntry
{
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 MemorySavestateEntry
{
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 MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"vu0MicroMem.bin"; }
u8* GetDataPtr() const { return vuRegs[0].Micro; }
uint GetDataSize() const { return VU0_PROGSIZE; }
};
class SavestateEntry_VU1prog : public MemorySavestateEntry
{
public:
wxString GetFilename() const { return L"vu1MicroMem.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?
// * 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 uint NumSavestateEntries = 9 + PluginId_Count;
class SavestateEntryPack : public ScopedAlloc<const BaseSavestateEntry*>
{
typedef ScopedAlloc<const BaseSavestateEntry*> _parent;
public:
SavestateEntryPack()
: _parent( NumSavestateEntries )
{
uint i = 0; // more convenient in case we re-arrange anything...
this->operator[](i++) = new SavestateEntry_EmotionMemory;
this->operator[](i++) = new SavestateEntry_IopMemory;
this->operator[](i++) = new SavestateEntry_HwRegs;
this->operator[](i++) = new SavestateEntry_IopHwRegs;
this->operator[](i++) = new SavestateEntry_Scratchpad;
this->operator[](i++) = new SavestateEntry_VU0mem;
this->operator[](i++) = new SavestateEntry_VU1mem;
this->operator[](i++) = new SavestateEntry_VU0prog;
this->operator[](i++) = new SavestateEntry_VU1prog;
this->operator[](i++) = new PluginSavestateEntry( PluginId_GS );
this->operator[](i++) = new PluginSavestateEntry( PluginId_PAD );
this->operator[](i++) = new PluginSavestateEntry( PluginId_SPU2 );
this->operator[](i++) = new PluginSavestateEntry( PluginId_CDVD );
this->operator[](i++) = new PluginSavestateEntry( PluginId_USB );
this->operator[](i++) = new PluginSavestateEntry( PluginId_FW );
this->operator[](i++) = new PluginSavestateEntry( PluginId_DEV9 );
}
using _parent::operator[];
};
static const SavestateEntryPack 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( pxInputStream& thr )
{
u32 savever;
thr.Read( savever );
@ -51,82 +279,37 @@ static void SaveStateFile_ReadHeader( IStreamReader& thr )
// was removed entirely.
if( savever > g_SaveVersion )
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 ))
.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."));
.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."));
// 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(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."));
};
// --------------------------------------------------------------------------------------
// 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
// --------------------------------------------------------------------------------------
// Pauses core emulation and downloads the savestate into the state_buffer.
// 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
{
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; }
@ -142,47 +325,50 @@ 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(m_dest_buffer).FreezeAll();
memSavingState saveme( m_dest_list->GetBuffer() );
ArchiveEntry internals( EntryFilename_InternalStructures );
internals.SetDataIndex( saveme.GetCurrentPos() );
saveme.FreezeBios();
saveme.FreezeInternals();
internals.SetDataSize( saveme.GetCurrentPos() - internals.GetDataIndex() );
m_dest_list->Add( internals );
for (uint i=0; i<SavestateEntries.GetSize(); ++i)
{
uint startpos = saveme.GetCurrentPos();
SavestateEntries[i]->FreezeOut( saveme );
m_dest_list->Add( ArchiveEntry( SavestateEntries[i]->GetFilename() )
.SetDataIndex( startpos )
.SetDataSize( saveme.GetCurrentPos() - startpos )
);
}
UI_EnableStateActions();
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
// --------------------------------------------------------------------------------------
class VmStateZipThread : public CompressThread_gzip
class VmStateCompressThread : public BaseCompressThread
{
typedef CompressThread_gzip _parent;
typedef BaseCompressThread _parent;
protected:
ScopedLock m_lock_Compress;
public:
VmStateZipThread( const wxString& file, VmStateBuffer* srcdata )
: _parent( file, srcdata, SaveStateFile_WriteHeader )
VmStateCompressThread()
{
m_lock_Compress.Assign(mtx_CompressToDisk);
}
VmStateZipThread( const wxString& file, ScopedPtr<VmStateBuffer>& srcdata )
: _parent( file, srcdata, SaveStateFile_WriteHeader )
virtual ~VmStateCompressThread() throw()
{
m_lock_Compress.Assign(mtx_CompressToDisk);
}
virtual ~VmStateZipThread() throw()
{
}
protected:
@ -205,7 +391,7 @@ protected:
class SysExecEvent_ZipToDisk : public SysExecEvent
{
protected:
VmStateBuffer* m_src_buffer;
ArchiveEntryList* m_src_list;
wxString m_filename;
public:
@ -213,21 +399,20 @@ public:
virtual ~SysExecEvent_ZipToDisk() throw()
{
delete m_src_buffer;
}
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; }
@ -236,8 +421,50 @@ public:
protected:
void InvokeEvent()
{
(new VmStateZipThread( m_filename, m_src_buffer ))->Start();
m_src_buffer = NULL;
// 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);
if (!woot->IsOk())
throw Exception::CannotCreateStream(tempfile);
// Write the version and screenshot:
ScopedPtr<pxOutputStream> out( new pxOutputStream(tempfile, new wxZipOutputStream(woot)) );
wxZipOutputStream* gzfp = (wxZipOutputStream*)out->GetWxStreamBase();
{
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();
}
(*new VmStateCompressThread())
.SetSource(m_src_list)
.SetOutStream(out)
.SetFinishedPath(m_filename)
.Start();
// No errors? Release cleanup handlers:
elist.DetachPtr();
}
void CleanupEvent()
{
}
};
@ -251,8 +478,8 @@ protected:
class SysExecEvent_UnzipFromDisk : public SysExecEvent
{
protected:
wxString m_filename;
wxString m_filename;
public:
wxString GetEventName() const { return L"VM_UnzipFromDisk"; }
@ -269,42 +496,124 @@ protected:
void InvokeEvent()
{
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<pxInputStream> reader( new pxInputStream(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->GetWxStreamBase();
// 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( 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 = entry.DetachPtr();
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<NumSavestateEntries; ++i)
{
if (entry->GetName().CmpNoCase(SavestateEntries[i]->GetFilename()) == 0)
{
DevCon.WriteLn( Color_Green, L" ... found '%s'", SavestateEntries[i]->GetFilename().c_str() );
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;
if (SavestateEntries[i]->IsRequired())
{
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
// *ALWAYS* start execution after the new savestate is loaded.
GetCoreThread().Pause();
SysClearExecutionCache();
// fixme: should start initially with the file size, and then grow from there.
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& )
for (uint i=0; i<NumSavestateEntries; ++i)
{
// This exception actually means success! Any others we let get sent
// to the main event handler/thread for handling.
if (!foundEntry[i]) continue;
Threading::pxTestCancel();
gzreader->OpenEntry( *foundEntry[i] );
SavestateEntries[i]->FreezeIn( *reader );
}
// Optional shutdown of plugins when loading states? I'm not implementing it yet because some
// things, like the SPU2-recovery trick, rely on not resetting the plugins prior to loading
// the new savestate data.
//if( ShutdownOnStateLoad ) GetCoreThread().Cancel();
// Load all the internal data
gzreader->OpenEntry( *foundInternal );
GetCoreThread().UploadStateCopy( buffer );
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();
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
}
};
@ -317,9 +626,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 )

View File

@ -17,9 +17,14 @@
#include "Common.h"
#include "BiosTools.h"
#include "wx/file.h"
#include "Utilities/pxStreams.h"
#include "wx/ffile.h"
// FIXME: Temporary hack until we remove dependence on Pcsx2App.
#include "AppConfig.h"
#include "wx/mstream.h"
#include "wx/wfstream.h"
#define DIRENTRY_SIZE 16
@ -36,189 +41,67 @@ struct romdir
char fileName[10];
u16 extInfoSize;
u32 fileSize;
} __packed; // +22
} __packed; // +16
#ifdef _MSC_VER
# pragma pack()
#endif
u32 BiosVersion; // Used in Memory, Misc, CDVD
C_ASSERT( sizeof(romdir) == DIRENTRY_SIZE );
u32 BiosVersion;
u32 BiosChecksum;
wxString BiosDescription;
// --------------------------------------------------------------------------------------
// Exception::BiosLoadFailed (implementations)
// --------------------------------------------------------------------------------------
Exception::BiosLoadFailed::BiosLoadFailed( const wxString& filename )
{
m_message_diag = L"BIOS has not been configured, or the configuration has been corrupted.";
m_message_user = _("The PS2 BIOS could not be loaded. The BIOS has not been configured, or the configuration has been corrupted. Please re-configure.");
StreamName = filename;
}
// Returns the version information of the bios currently loaded into memory.
static u32 GetBiosVersion()
// This method throws a BadStream exception if the bios information chould not be obtained.
// (indicating that the file is invalid, incomplete, corrupted, or plain naughty).
static void LoadBiosVersion( pxInputStream& fp, u32& version, wxString& description, wxString& zoneStr )
{
int i;
uint i;
romdir rd;
for (i=0; i<512*1024; i++)
{
if( strncmp( (char*)&psRu8(i), "RESET", 5 ) == 0 )
fp.Read( rd );
if (strncmp( rd.fileName, "RESET", 5 ) == 0)
break; /* found romdir */
}
if (i == 512*1024) return -1;
const romdir* rd = (romdir*)&psRu8(i);
if (i == 512*1024)
{
throw Exception::BadStream( fp.GetStreamName() )
.SetDiagMsg(L"BIOS version check failed: 'RESET' tag could not be found.")
.SetUserMsg(_("The selected BIOS file is not a valid PS2 BIOS. Please re-configure."));
}
uint fileOffset = 0;
while(strlen(rd->fileName) > 0)
{
if (strcmp(rd->fileName, "ROMVER") == 0)
{
char vermaj[8];
char vermin[8];
u32 version;
const s8 *ROMVER = &psRs8(fileOffset);
strncpy(vermaj, (char *)(ROMVER+ 0), 2); vermaj[2] = 0;
strncpy(vermin, (char *)(ROMVER+ 2), 2); vermin[2] = 0;
version = strtol(vermaj, (char**)NULL, 0) << 8;
version|= strtol(vermin, (char**)NULL, 0);
return version;
}
if ((rd->fileSize % 0x10)==0)
fileOffset += rd->fileSize;
else
fileOffset += (rd->fileSize + 0x10) & 0xfffffff0;
rd++;
}
return -1;
}
// Attempts to load a BIOS rom sub-component, by trying multiple combinations of base
// filename and extension. The bios specified in the user's configuration is used as
// the base.
//
// Parameters:
// ext - extension of the sub-component to load. Valid options are rom1, rom2, AND erom.
//
static void loadBiosRom( const wxChar *ext, u8 *dest, s64 maxSize )
{
wxString Bios1;
s64 filesize;
// Try first a basic extension concatenation (normally results in something like name.bin.rom1)
const wxString Bios( g_Conf->FullpathToBios() );
Bios1.Printf( L"%s.%s", Bios.c_str(), ext);
if( (filesize=Path::GetFileSize( Bios1 ) ) <= 0 )
{
// Try the name properly extensioned next (name.rom1)
Bios1 = Path::ReplaceExtension( Bios, ext );
if( (filesize=Path::GetFileSize( Bios1 ) ) <= 0 )
{
Console.Warning( L"Load Bios Warning: %s module not found, skipping... (this is not an error)", ext );
return;
}
}
// if we made it this far, we have a successful file found:
wxFile fp( Bios1 );
fp.Read( dest, min( maxSize, filesize ) );
}
// Loads the configured bios rom file into PS2 memory. PS2 memory must be allocated prior to
// this method being called.
//
// Remarks:
// This function does not fail if rom1, rom2, or erom files are missing, since none are
// explicitly required for most emulation tasks.
//
// Exceptions:
// FileNotFound - Thrown if the primary bios file (usually .bin) is not found.
//
void LoadBIOS()
{
pxAssertDev( eeMem->ROM != NULL, "PS2 system memory has not been initialized yet." );
wxString Bios( g_Conf->FullpathToBios() );
if( !g_Conf->BaseFilenames.Bios.IsOk() || g_Conf->BaseFilenames.Bios.IsDir() )
throw Exception::BiosLoadFailed( Bios );
s64 filesize = Path::GetFileSize( Bios );
if( filesize <= 0 )
{
throw Exception::BiosLoadFailed( Bios )
.SetDiagMsg(L"Configured BIOS file does not exist.")
.SetUserMsg(_("The configured BIOS file does not exist. Please re-configure."));
}
wxFile fp( Bios );
fp.Read( eeMem->ROM, min( (s64)Ps2MemSize::Rom, filesize ) );
BiosVersion = GetBiosVersion();
if( BiosVersion == -1 )
{
throw Exception::BiosLoadFailed( Bios )
.SetDiagMsg(L"Configured BIOS file is not a valid PS2 BIOS.")
.SetUserMsg(_("The configured BIOS file is not a valid PS2 BIOS. Please re-configure."));
}
Console.WriteLn("Bios Version %d.%d", BiosVersion >> 8, BiosVersion & 0xff);
//injectIRX("host.irx"); //not fully tested; still buggy
loadBiosRom( L"rom1", eeMem->ROM1, Ps2MemSize::Rom1 );
loadBiosRom( L"rom2", eeMem->ROM2, Ps2MemSize::Rom2 );
loadBiosRom( L"erom", eeMem->EROM, Ps2MemSize::ERom );
}
bool IsBIOS(const wxString& filename, wxString& description)
{
uint fileOffset=0;
romdir rd;
wxFileName Bios( g_Conf->Folders.Bios + filename );
wxFile fp( Bios.GetFullPath() );
if( !fp.IsOpened() ) return FALSE;
int biosFileSize = fp.Length();
if( biosFileSize < 1024*4096) return FALSE; // valid bios must be at least 4mb.
while( (fp.Tell() < 512*1024) && (fp.Read( &rd, DIRENTRY_SIZE ) == DIRENTRY_SIZE) )
{
if (strcmp(rd.fileName, "RESET") == 0)
break; // found romdir
}
if ((strcmp(rd.fileName, "RESET") != 0) || (rd.fileSize == 0))
return FALSE; //Unable to locate ROMDIR structure in file or a ioprpXXX.img
bool found = false;
while(strlen(rd.fileName) > 0)
{
if (strcmp(rd.fileName, "ROMVER") == 0) // found romver
if (strcmp(rd.fileName, "ROMVER") == 0)
{
char aROMVER[14+1]; // ascii version loaded from disk.
char romver[14+1]; // ascii version loaded from disk.
uint filepos = fp.Tell();
wxFileOffset filetablepos = fp.Tell();
fp.Seek( fileOffset );
if( fp.Read( &aROMVER, 14 ) == 0 ) break;
fp.Seek( filepos ); //go back
fp.Read( &romver, 14 );
fp.Seek( filetablepos ); //go back
aROMVER[14] = 0;
romver[14] = 0;
const char zonefail[2] = { aROMVER[4], '\0' }; // the default "zone" (unknown code)
const char zonefail[2] = { romver[4], '\0' }; // the default "zone" (unknown code)
const char* zone = zonefail;
switch(aROMVER[4])
switch(romver[4])
{
case 'T': zone = "T10K"; break;
case 'X': zone = "Test"; break;
@ -230,39 +113,188 @@ bool IsBIOS(const wxString& filename, wxString& description)
case 'C': zone = "China"; break;
}
const wxString romver( fromUTF8(aROMVER) );
char vermaj[3] = { romver[0], romver[1], 0 };
char vermin[3] = { romver[2], romver[3], 0 };
description.Printf( L"%-7s v%c%c.%c%c(%c%c/%c%c/%c%c%c%c) %s",
fromUTF8(zone).c_str(),
romver[0], romver[1], // ver major
romver[2], romver[3], // ver minor
FastFormatUnicode result;
result.Write( "%-7s v%s.%s(%c%c/%c%c/%c%c%c%c) %s",
zone,
vermaj, vermin,
romver[12], romver[13], // day
romver[10], romver[11], // month
romver[6], romver[7], romver[8], romver[9], // year!
(aROMVER[5]=='C') ? L"Console" : (aROMVER[5]=='D') ? L"Devel" : wxEmptyString
(romver[5]=='C') ? "Console" : (romver[5]=='D') ? "Devel" : ""
);
found = true;
version = strtol(vermaj, (char**)NULL, 0) << 8;
version|= strtol(vermin, (char**)NULL, 0);
Console.WriteLn(L"Bios Found: %s", result.c_str());
description = result.c_str();
zoneStr = fromUTF8(zone);
}
if ((rd.fileSize % 0x10)==0)
if ((rd.fileSize % 0x10) == 0)
fileOffset += rd.fileSize;
else
fileOffset += (rd.fileSize + 0x10) & 0xfffffff0;
if (fp.Read( &rd, DIRENTRY_SIZE ) != DIRENTRY_SIZE) break;
fp.Read( rd );
}
fileOffset -= ((rd.fileSize + 0x10) & 0xfffffff0) - rd.fileSize;
if (found)
if (description.IsEmpty())
throw Exception::BadStream( fp.GetStreamName() )
.SetDiagMsg(L"BIOS version check failed: 'ROMDIR' tag could not be found.")
.SetUserMsg(_("The selected BIOS file is not a valid PS2 BIOS. Please re-configure."));
wxFileOffset fileSize = fp.Length();
if (fileSize < (int)fileOffset)
{
if ( biosFileSize < (int)fileOffset)
{
description += wxsFormat( L" %d%%", ((biosFileSize*100) / (int)fileOffset) );
// we force users to have correct bioses,
// not that lame scph10000 of 513KB ;-)
}
return true;
description += pxsFmt( L" %d%%", ((fileSize*100) / (int)fileOffset) );
// we force users to have correct bioses,
// not that lame scph10000 of 513KB ;-)
}
return false; //fail quietly
}
static void LoadBiosVersion( pxInputStream& fp, u32& version, wxString& description )
{
wxString zoneStr;
LoadBiosVersion( fp,version, description, zoneStr );
}
template< size_t _size >
void ChecksumIt( u32& result, const u8 (&srcdata)[_size] )
{
pxAssume( (_size & 3) == 0 );
for( size_t i=0; i<_size/4; ++i )
result ^= ((u32*)srcdata)[i];
}
// Attempts to load a BIOS rom sub-component, by trying multiple combinations of base
// filename and extension. The bios specified in the user's configuration is used as
// the base.
//
// Parameters:
// ext - extension of the sub-component to load. Valid options are rom1, rom2, AND erom.
//
template< size_t _size >
static void LoadExtraRom( const wxChar* ext, u8 (&dest)[_size] )
{
wxString Bios1;
s64 filesize;
// Try first a basic extension concatenation (normally results in something like name.bin.rom1)
const wxString Bios( g_Conf->FullpathToBios() );
Bios1.Printf( L"%s.%s", Bios.c_str(), ext);
try
{
if( (filesize=Path::GetFileSize( Bios1 ) ) <= 0 )
{
// Try the name properly extensioned next (name.rom1)
Bios1 = Path::ReplaceExtension( Bios, ext );
if( (filesize=Path::GetFileSize( Bios1 ) ) <= 0 )
{
Console.WriteLn( Color_Gray, L"BIOS %s module not found, skipping...", ext );
return;
}
}
wxFile fp( Bios1 );
fp.Read( dest, std::min<s64>( _size, filesize ) );
// Checksum for ROM1, ROM2, EROM? Rama says no, Gigaherz says yes. I'm not sure either way. --air
//ChecksumIt( BiosChecksum, dest );
}
catch (Exception::BadStream& ex)
{
// If any of the secondary roms fail,its a non-critical error.
// Log it, but don't make a big stink. 99% of games and stuff will
// still work fine.
Console.Warning(L"BIOS Warning: %s could not be read (permission denied?)", ext);
Console.Indent().WriteLn(L"Details: %s", ex.FormatDiagnosticMessage());
Console.Indent().WriteLn(L"File size: %llu", filesize);
}
}
// Loads the configured bios rom file into PS2 memory. PS2 memory must be allocated prior to
// this method being called.
//
// Remarks:
// This function does not fail if rom1, rom2, or erom files are missing, since none are
// explicitly required for most emulation tasks.
//
// Exceptions:
// BadStream - Thrown if the primary bios file (usually .bin) is not found, corrupted, etc.
//
void LoadBIOS()
{
pxAssertDev( eeMem->ROM != NULL, "PS2 system memory has not been initialized yet." );
try
{
wxString Bios( g_Conf->FullpathToBios() );
if( !g_Conf->BaseFilenames.Bios.IsOk() || g_Conf->BaseFilenames.Bios.IsDir() )
throw Exception::FileNotFound( Bios )
.SetDiagMsg(L"BIOS has not been configured, or the configuration has been corrupted.")
.SetUserMsg(_("The PS2 BIOS could not be loaded. The BIOS has not been configured, or the configuration has been corrupted. Please re-configure."));
s64 filesize = Path::GetFileSize( Bios );
if( filesize <= 0 )
{
throw Exception::FileNotFound( Bios )
.SetDiagMsg(L"Configured BIOS file does not exist, or has a file size of zero.")
.SetUserMsg(_("The configured BIOS file does not exist. Please re-configure."));
}
BiosChecksum = 0;
wxString biosZone;
wxFFile fp( Bios );
fp.Read( eeMem->ROM, std::min<s64>( Ps2MemSize::Rom, filesize ) );
ChecksumIt( BiosChecksum, eeMem->ROM );
pxInputStream memfp( Bios, new wxMemoryInputStream( eeMem->ROM, sizeof(eeMem->ROM) ) );
LoadBiosVersion( memfp, BiosVersion, BiosDescription, biosZone );
Console.SetTitle( pxsFmt( L"Running BIOS (%s v%u.%u)",
biosZone.c_str(), BiosVersion >> 8, BiosVersion & 0xff
));
//injectIRX("host.irx"); //not fully tested; still buggy
LoadExtraRom( L"rom1", eeMem->ROM1 );
LoadExtraRom( L"rom2", eeMem->ROM2 );
LoadExtraRom( L"erom", eeMem->EROM );
}
catch (Exception::BadStream& ex)
{
// Rethrow as a Bios Load Failure, so that the user interface handling the exceptions
// can respond to it appropriately.
throw Exception::BiosLoadFailed( ex.StreamName )
.SetDiagMsg( ex.DiagMsg() )
.SetUserMsg( ex.UserMsg() );
}
}
bool IsBIOS(const wxString& filename, wxString& description)
{
wxFileName Bios( g_Conf->Folders.Bios + filename );
pxInputStream inway( filename, new wxFFileInputStream( filename ) );
if (!inway.IsOk()) return false;
if (inway.Length() < 512*1024) return false;
try {
u32 version;
LoadBiosVersion( inway, version, description );
return true;
} catch( Exception::BadStream& ) { }
return false; // fail quietly
}

View File

@ -17,7 +17,7 @@
namespace Exception
{
class BiosLoadFailed : public FileNotFound
class BiosLoadFailed : public BadStream
{
DEFINE_EXCEPTION_COPYTORS( BiosLoadFailed, FileNotFound )
DEFINE_EXCEPTION_MESSAGES( BiosLoadFailed )
@ -29,6 +29,8 @@ namespace Exception
}
extern u32 BiosVersion; // Used by CDVD
extern u32 BiosChecksum;
extern wxString BiosDescription;
extern void LoadBIOS();
extern bool IsBIOS(const wxString& filename, wxString& description);