* Call PADupdate from MTGS thread when using legacy GSopen -- Should fix Linux Pad instabilities.

* Fixed a savestate loading bug, when loading states made with different sets/versions of plugins.

DevNote:
 * Moved some old PS2E types to PS2Edefs, since the new v2 plugin API won't use them (freezeData, keyEvent).

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2380 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-12-22 03:15:30 +00:00
parent 203979c16b
commit 408dbd7256
11 changed files with 212 additions and 174 deletions

View File

@ -48,6 +48,29 @@
#include "Pcsx2Defs.h" #include "Pcsx2Defs.h"
///////////////////////////////////////////////////////////////////////
// freeze modes:
#define FREEZE_LOAD 0
#define FREEZE_SAVE 1
#define FREEZE_SIZE 2
// event values:
#define KEYPRESS 1
#define KEYRELEASE 2
typedef struct {
int size;
s8 *data;
} freezeData;
typedef struct _keyEvent {
u32 key;
u32 evt;
} keyEvent;
///////////////////////////////////////////////////////////////////////
#if defined(GSdefs) || defined(PADdefs) || defined(SIOdefs) || \ #if defined(GSdefs) || defined(PADdefs) || defined(SIOdefs) || \
defined(SPU2defs) || defined(CDVDdefs) || defined(DEV9defs) || \ defined(SPU2defs) || defined(CDVDdefs) || defined(DEV9defs) || \
defined(USBdefs) || defined(FWdefs) defined(USBdefs) || defined(FWdefs)
@ -172,11 +195,6 @@ typedef int (*DEV9handler)(void);
typedef void (*USBcallback)(int cycles); typedef void (*USBcallback)(int cycles);
typedef int (*USBhandler)(void); typedef int (*USBhandler)(void);
// freeze modes:
#define FREEZE_LOAD 0
#define FREEZE_SAVE 1
#define FREEZE_SIZE 2
typedef struct _GSdriverInfo { typedef struct _GSdriverInfo {
char name[8]; char name[8];
void *common; void *common;

View File

@ -287,20 +287,4 @@ static const int __pagesize = PCSX2_PAGESIZE;
#endif #endif
///////////////////////////////////////////////////////////////////////////////////////
typedef struct {
int size;
s8 *data;
} freezeData;
// event values:
#define KEYPRESS 1
#define KEYRELEASE 2
typedef struct _keyEvent {
u32 key;
u32 evt;
} keyEvent;
#endif #endif

View File

@ -337,62 +337,4 @@ namespace Exception
public: public:
DEFINE_STREAM_EXCEPTION( EndOfStream, wxLt("Unexpected end of file or stream.") ); DEFINE_STREAM_EXCEPTION( EndOfStream, wxLt("Unexpected end of file or stream.") );
}; };
// ---------------------------------------------------------------------------------------
// Savestate Exceptions:
// BadSavedState / FreezePluginFailure / StateLoadError / UnsupportedStateVersion /
// StateCrcMismatch
// ---------------------------------------------------------------------------------------
// Exception thrown when a corrupted or truncated savestate is encountered.
//
class BadSavedState : public virtual BadStream
{
public:
DEFINE_STREAM_EXCEPTION( BadSavedState, wxLt("Savestate data is corrupted or incomplete.") )
};
// thrown when the savestate being loaded isn't supported.
//
class UnsupportedStateVersion : public virtual BadSavedState
{
public:
u32 Version; // version number of the unsupported state.
public:
DEFINE_EXCEPTION_COPYTORS( UnsupportedStateVersion )
explicit UnsupportedStateVersion( int version, const wxString& objname=wxEmptyString )
{
StreamName = objname;
Version = version;
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
// A recoverable exception thrown when the CRC of the savestate does not match the
// CRC returned by the Cdvd driver.
// [feature not implemented yet]
//
class StateCrcMismatch : public virtual BadSavedState
{
public:
u32 Crc_Savestate;
u32 Crc_Cdvd;
public:
DEFINE_EXCEPTION_COPYTORS( StateCrcMismatch )
explicit StateCrcMismatch( u32 crc_save, u32 crc_cdvd, const wxString& objname=wxEmptyString )
{
StreamName = objname;
Crc_Savestate = crc_save;
Crc_Cdvd = crc_cdvd;
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
} }

View File

@ -190,41 +190,3 @@ wxString Exception::Stream::FormatDisplayMessage() const
wxsFormat( _("Name: %s"), StreamName.c_str() ); wxsFormat( _("Name: %s"), StreamName.c_str() );
} }
// ------------------------------------------------------------------------
wxString Exception::UnsupportedStateVersion::FormatDiagnosticMessage() const
{
// Note: no stacktrace needed for this one...
return wxsFormat( 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(
m_message_user + L"\n\n" +
wxsFormat( _("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(
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
);
}
wxString Exception::StateCrcMismatch::FormatDisplayMessage() const
{
return wxsFormat(
m_message_user + L"\n\n" +
wxsFormat(
L"Savestate game/crc mismatch. Cdvd CRC: 0x%X Game CRC: 0x%X\n",
Crc_Savestate, Crc_Cdvd
)
);
}

View File

@ -370,6 +370,11 @@ void SysMtgsThread::ExecuteTaskInThread()
GSvsync(tag.data[0]); GSvsync(tag.data[0]);
gsFrameSkip(); gsFrameSkip();
// if we're not using GSOpen2, then the GS window is on this thread (MTGS thread),
// so we need to call PADupdate from here.
if( (GSopen2 == NULL) && (PADupdate != NULL) )
PADupdate(0);
StateCheckInThread(); StateCheckInThread();
} }
break; break;

View File

@ -123,7 +123,12 @@ struct LegacyApi_OptMethod
}; };
static s32 CALLBACK fallback_freeze(int mode, freezeData *data) { data->size = 0; return 0; } static s32 CALLBACK fallback_freeze(int mode, freezeData *data)
{
if( mode == FREEZE_SIZE ) data->size = 0;
return 0;
}
static void CALLBACK fallback_keyEvent(keyEvent *ev) {} static void CALLBACK fallback_keyEvent(keyEvent *ev) {}
static void CALLBACK fallback_setSettingsDir(const char* dir) {} static void CALLBACK fallback_setSettingsDir(const char* dir) {}
static void CALLBACK fallback_configure() {} static void CALLBACK fallback_configure() {}

View File

@ -23,6 +23,17 @@
#include <wx/dynlib.h> #include <wx/dynlib.h>
#ifdef _MSC_VER
// Disabling C4673: throwing 'Exception::Blah' the following types will not be considered at the catch site
// The warning is bugged, and happens even though we're properly inheriting classes with
// 'virtual' qualifiers. But since the warning is potentially useful elsewhere, I disable
// it only for the scope of these exceptions.
# pragma warning(push)
# pragma warning(disable:4673)
#endif
struct PluginInfo struct PluginInfo
{ {
const char* shortname; const char* shortname;
@ -36,22 +47,18 @@ struct PluginInfo
} }
}; };
#ifdef _MSC_VER
// Disabling C4673: throwing 'Exception::Blah' the following types will not be considered at the catch site
// The warning is bugged, and happens even though we're properly inheriting classes with
// 'virtual' qualifiers. But since the warning is potentially useful elsewhere, I disable
// it only for the scope of these exceptions.
# pragma warning(push)
# pragma warning(disable:4673)
#endif
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Plugin-related Exceptions // Plugin-related Exceptions
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
namespace Exception namespace Exception
{ {
// Exception thrown when a corrupted or truncated savestate is encountered.
class SaveStateLoadError : public virtual BadStream
{
public:
DEFINE_STREAM_EXCEPTION( SaveStateLoadError, wxLt("Savestate data is corrupted or incomplete.") )
};
class PluginError : public virtual RuntimeError class PluginError : public virtual RuntimeError
{ {
public: public:
@ -137,7 +144,7 @@ namespace Exception
virtual wxString FormatDisplayMessage() const; virtual wxString FormatDisplayMessage() const;
}; };
class ThawPluginFailure : public virtual PluginError, public virtual BadSavedState class ThawPluginFailure : public virtual PluginError, public virtual SaveStateLoadError
{ {
public: public:
DEFINE_EXCEPTION_COPYTORS( ThawPluginFailure ) DEFINE_EXCEPTION_COPYTORS( ThawPluginFailure )

View File

@ -37,6 +37,13 @@ int sys_resume_lock = 0;
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL; static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
static bool StateCopy_ForceClear()
{
sys_resume_lock = 0;
state_buffer_lock.Release();
state_buffer.Dispose();
}
static void __evt_fastcall StateThread_OnAppStatus( void* thr, AppEventType& stat ) static void __evt_fastcall StateThread_OnAppStatus( void* thr, AppEventType& stat )
{ {
if( (thr == NULL) || (stat != AppStatus_Exiting) ) return; if( (thr == NULL) || (stat != AppStatus_Exiting) ) return;
@ -378,43 +385,58 @@ void StateCopy_FreezeToMem()
(new StateThread_Freeze( OnFinished_Resume ))->Start(); (new StateThread_Freeze( OnFinished_Resume ))->Start();
} }
static void _acquire_and_block() class Acquire_And_Block
{ {
if( state_buffer_lock.TryAcquire() ) return; protected:
bool m_DisposeWhenFinished;
bool m_Acquired;
/* public:
// If the state buffer is locked and we're being called from the main thread then we need Acquire_And_Block( bool dispose )
// to cancel the current action. This is needed because state_buffer_lock is only updated
// from events handled on the main thread.
if( wxThread::IsMain() )
throw Exception::CancelEvent( "Blocking ThawFromMem canceled due to existing state buffer lock." );
else*/
{ {
pxAssume( current_state_thread != NULL ); m_DisposeWhenFinished = dispose;
do { m_Acquired = false;
/*
// If the state buffer is locked and we're being called from the main thread then we need
// to cancel the current action. This is needed because state_buffer_lock is only updated
// from events handled on the main thread.
if( wxThread::IsMain() )
throw Exception::CancelEvent( "Blocking ThawFromMem canceled due to existing state buffer lock." );
else*/
while ( !state_buffer_lock.TryAcquire() )
{
pxAssume( current_state_thread != NULL );
current_state_thread->Block(); current_state_thread->Block();
wxGetApp().ProcessPendingEvents(); // Trying this for now, may or may not work due to recursive pitfalls (see above) wxGetApp().ProcessPendingEvents(); // Trying this for now, may or may not work due to recursive pitfalls (see above)
} while ( !state_buffer_lock.TryAcquire() ); };
m_Acquired = true;
} }
}
virtual ~Acquire_And_Block() throw()
{
if( m_DisposeWhenFinished )
state_buffer.Dispose();
if( m_Acquired )
state_buffer_lock.Release();
}
};
void StateCopy_FreezeToMem_Blocking() void StateCopy_FreezeToMem_Blocking()
{ {
_acquire_and_block(); Acquire_And_Block blocker( false );
memSavingState( state_buffer ).FreezeAll(); memSavingState( state_buffer ).FreezeAll();
state_buffer_lock.Release();
} }
// Copies the saved state into the active VM, and automatically free's the saved state data. // Copies the saved state into the active VM, and automatically free's the saved state data.
void StateCopy_ThawFromMem_Blocking() void StateCopy_ThawFromMem_Blocking()
{ {
_acquire_and_block(); Acquire_And_Block blocker( true );
memLoadingState( state_buffer ).FreezeAll(); memLoadingState( state_buffer ).FreezeAll();
state_buffer.Dispose();
state_buffer_lock.Release();
} }
void StateCopy_Clear() void StateCopy_Clear()
@ -427,4 +449,3 @@ bool StateCopy_IsBusy()
{ {
return state_buffer_lock.IsLocked(); return state_buffer_lock.IsLocked();
} }

View File

@ -27,6 +27,7 @@
using namespace R5900; using namespace R5900;
static void PreLoadPrep() static void PreLoadPrep()
{ {
SysClearExecutionCache(); SysClearExecutionCache();
@ -45,14 +46,14 @@ wxString SaveStateBase::GetFilename( int slot )
wxsFormat( L"%8.8X.%3.3d", ElfCRC, slot )).GetFullPath(); wxsFormat( L"%8.8X.%3.3d", ElfCRC, slot )).GetFullPath();
} }
SaveStateBase::SaveStateBase( SafeArray<u8>& memblock ) : SaveStateBase::SaveStateBase( SafeArray<u8>& memblock )
m_memory( memblock ) : m_memory( memblock )
, m_version( g_SaveVersion )
, m_idx( 0 )
, m_sectid( FreezeId_Unknown )
, m_pid( PluginId_GS )
, m_DidBios( false )
{ {
m_version = g_SaveVersion;
m_idx = 0;
m_sectid = FreezeId_Unknown;
m_pid = PluginId_GS;
m_DidBios = false;
} }
void SaveStateBase::PrepBlock( int size ) void SaveStateBase::PrepBlock( int size )
@ -63,7 +64,7 @@ void SaveStateBase::PrepBlock( int size )
else else
{ {
if( m_memory.GetSizeInBytes() < end ) if( m_memory.GetSizeInBytes() < end )
throw Exception::BadSavedState(); throw Exception::SaveStateLoadError();
} }
} }
@ -79,7 +80,7 @@ void SaveStateBase::FreezeTag( const char* src )
if( strcmp( m_tagspace, src ) != 0 ) if( strcmp( m_tagspace, src ) != 0 )
{ {
pxFail( "Savestate data corruption detected while reading tag" ); pxFail( "Savestate data corruption detected while reading tag" );
throw Exception::BadSavedState( throw Exception::SaveStateLoadError(
// Untranslated diagnostic msg (use default msg for translation) // Untranslated diagnostic msg (use default msg for translation)
L"Savestate data corruption detected while reading tag: " + fromUTF8(src) L"Savestate data corruption detected while reading tag: " + fromUTF8(src)
); );
@ -205,7 +206,7 @@ void SaveStateBase::WritebackSectionLength( int seekpos, int sectlen, const wxCh
{ {
if( sectlen != realsectsize ) // if they don't match then we have a problem, jim. if( sectlen != realsectsize ) // if they don't match then we have a problem, jim.
{ {
throw Exception::BadSavedState( wxEmptyString, throw Exception::SaveStateLoadError( wxEmptyString,
wxsFormat( L"Invalid size encountered on section '%s'.", sectname ), wxsFormat( L"Invalid size encountered on section '%s'.", sectname ),
_("The savestate data is invalid or corrupted.") _("The savestate data is invalid or corrupted.")
); );
@ -234,7 +235,7 @@ bool SaveStateBase::FreezeSection( int seek_section )
if( sectlen != 128 ) if( sectlen != 128 )
{ {
throw Exception::BadSavedState( wxEmptyString, throw Exception::SaveStateLoadError( wxEmptyString,
L"Invalid size encountered on BiosVersion section.", L"Invalid size encountered on BiosVersion section.",
_("The savestate data is invalid or corrupted.") _("The savestate data is invalid or corrupted.")
); );
@ -257,7 +258,7 @@ bool SaveStateBase::FreezeSection( int seek_section )
Freeze( sectlen ); Freeze( sectlen );
if( sectlen != MainMemorySizeInBytes ) if( sectlen != MainMemorySizeInBytes )
{ {
throw Exception::BadSavedState( wxEmptyString, throw Exception::SaveStateLoadError( wxEmptyString,
L"Invalid size encountered on MainMemory section.", L"Invalid size encountered on MainMemory section.",
_("The savestate data is invalid or corrupted.") _("The savestate data is invalid or corrupted.")
); );
@ -302,7 +303,7 @@ bool SaveStateBase::FreezeSection( int seek_section )
else else
g_plugins->Freeze( (PluginsEnum_t)m_pid, *this ); g_plugins->Freeze( (PluginsEnum_t)m_pid, *this );
WritebackSectionLength( seekpos, sectlen, L"HardwareRegisters" ); WritebackSectionLength( seekpos, sectlen, L"Plugins" );
// following increments only affect Saving mode, which needs to be sure to save all // 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) // plugins (order doesn't matter but sequential is easy enough. (ignored by Loading mode)
@ -411,3 +412,42 @@ bool memLoadingState::SeekToSection( PluginsEnum_t pid )
return true; return true;
} }
// --------------------------------------------------------------------------------------
// SaveState Exception Messages
// --------------------------------------------------------------------------------------
wxString Exception::UnsupportedStateVersion::FormatDiagnosticMessage() const
{
// Note: no stacktrace needed for this one...
return wxsFormat( 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(
m_message_user + L"\n\n" +
wxsFormat( _("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(
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
);
}
wxString Exception::StateCrcMismatch::FormatDisplayMessage() const
{
return wxsFormat(
m_message_user + L"\n\n" +
wxsFormat(
L"Savestate game/crc mismatch. Cdvd CRC: 0x%X Game CRC: 0x%X\n",
Crc_Savestate, Crc_Cdvd
)
);
}

View File

@ -50,6 +50,58 @@ enum FreezeSectionId
FreezeId_Unknown, FreezeId_Unknown,
}; };
namespace Exception
{
// ---------------------------------------------------------------------------------------
// Savestate Exceptions:
// UnsupportedStateVersion / StateCrcMismatch
// ---------------------------------------------------------------------------------------
// thrown when the savestate being loaded isn't supported.
//
class UnsupportedStateVersion : public virtual SaveStateLoadError
{
public:
u32 Version; // version number of the unsupported state.
public:
DEFINE_EXCEPTION_COPYTORS( UnsupportedStateVersion )
explicit UnsupportedStateVersion( int version, const wxString& objname=wxEmptyString )
{
StreamName = objname;
Version = version;
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
// A recoverable exception thrown when the CRC of the savestate does not match the
// CRC returned by the Cdvd driver.
// [feature not implemented yet]
//
class StateCrcMismatch : public virtual SaveStateLoadError
{
public:
u32 Crc_Savestate;
u32 Crc_Cdvd;
public:
DEFINE_EXCEPTION_COPYTORS( StateCrcMismatch )
explicit StateCrcMismatch( u32 crc_save, u32 crc_cdvd, const wxString& objname=wxEmptyString )
{
StreamName = objname;
Crc_Savestate = crc_save;
Crc_Cdvd = crc_cdvd;
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
}
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SaveStateBase class // SaveStateBase class
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -207,7 +207,11 @@ void Pcsx2App::OnLogicalVsync( wxCommandEvent& evt )
{ {
if( !SysHasValidState() || g_plugins == NULL ) return; if( !SysHasValidState() || g_plugins == NULL ) return;
if( PADupdate != NULL ) PADupdate(0); // Only call PADupdate here if we're using GSopen2. Legacy GSopen plugins have the
// GS window belonging to the MTGS thread.
if( (PADupdate != NULL) && (GSopen2 != NULL) && (m_gsFrame != NULL) )
PADupdate(0);
const keyEvent* ev = PADkeyEvent(); const keyEvent* ev = PADkeyEvent();
if( (ev != NULL) && (ev->key != 0) ) if( (ev != NULL) && (ev->key != 0) )
@ -369,6 +373,12 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
Console.Warning( "User canceled BIOS configuration." ); Console.Warning( "User canceled BIOS configuration." );
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::SaveStateLoadError& ex)
{
// Saved state load failed.
Console.Warning( ex.FormatDiagnosticMessage() );
CoreThread.Resume();
}
catch( Exception::PluginInitError& ex ) catch( Exception::PluginInitError& ex )
{ {
if( m_CorePlugins ) m_CorePlugins->Shutdown(); if( m_CorePlugins ) m_CorePlugins->Shutdown();
@ -430,14 +440,6 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
{ {
Console.Warning( ex.FormatDiagnosticMessage() ); Console.Warning( ex.FormatDiagnosticMessage() );
} }
// ----------------------------------------------------------------------------
catch( Exception::BadSavedState& ex)
{
// Saved state load failed.
Console.Warning( ex.FormatDiagnosticMessage() );
CoreThread.Resume();
}
// ----------------------------------------------------------------------------
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
// Runtime errors which have been unhandled should still be safe to recover from, // Runtime errors which have been unhandled should still be safe to recover from,