diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h index cb82652473..6d9b0e39bf 100644 --- a/common/include/Utilities/Exceptions.h +++ b/common/include/Utilities/Exceptions.h @@ -300,7 +300,7 @@ namespace Exception class BadStream : public virtual Stream { public: - DEFINE_STREAM_EXCEPTION( BadStream, wxLt("File data is corrupted or incomplete, or the stream connection closed unexpectedly") ) + DEFINE_STREAM_EXCEPTION( BadStream, wxLt("File data is corrupted or incomplete, or the stream connection closed unexpectedly.") ) }; // A generic exception for odd-ball stream creation errors. @@ -308,7 +308,7 @@ namespace Exception class CreateStream : public virtual Stream { public: - DEFINE_STREAM_EXCEPTION( CreateStream, wxLt("File could not be created or opened") ) + DEFINE_STREAM_EXCEPTION( CreateStream, wxLt("File could not be created or opened.") ) }; // Exception thrown when an attempt to open a non-existent file is made. @@ -317,13 +317,13 @@ namespace Exception class FileNotFound : public virtual CreateStream { public: - DEFINE_STREAM_EXCEPTION( FileNotFound, wxLt("File not found") ) + DEFINE_STREAM_EXCEPTION( FileNotFound, wxLt("File not found.") ) }; class AccessDenied : public virtual CreateStream { public: - DEFINE_STREAM_EXCEPTION( AccessDenied, wxLt("Permission denied to file") ) + DEFINE_STREAM_EXCEPTION( AccessDenied, wxLt("Permission denied to file.") ) }; // EndOfStream can be used either as an error, or used just as a shortcut for manual @@ -332,7 +332,7 @@ namespace Exception class EndOfStream : public virtual Stream { public: - DEFINE_STREAM_EXCEPTION( EndOfStream, wxLt("Unexpected end of file") ); + DEFINE_STREAM_EXCEPTION( EndOfStream, wxLt("Unexpected end of file or stream.") ); }; // --------------------------------------------------------------------------------------- @@ -346,29 +346,7 @@ namespace Exception class BadSavedState : public virtual BadStream { public: - DEFINE_STREAM_EXCEPTION( BadSavedState, wxLt("Savestate data is corrupted or incomplete") ) - }; - - // Exception thrown by SaveState class when a plugin returns an error during state - // load or save. - // - class FreezePluginFailure : public virtual RuntimeError - { - public: - wxString PluginName; // name of the plugin - wxString FreezeAction; - - public: - DEFINE_EXCEPTION_COPYTORS( FreezePluginFailure ) - - explicit FreezePluginFailure( const char* plugin, const char* action ) - { - PluginName = wxString::FromUTF8( plugin ); - FreezeAction = wxString::FromUTF8( action ); - } - - virtual wxString FormatDiagnosticMessage() const; - virtual wxString FormatDisplayMessage() const; + DEFINE_STREAM_EXCEPTION( BadSavedState, wxLt("Savestate data is corrupted or incomplete.") ) }; // thrown when the savestate being loaded isn't supported. diff --git a/common/include/Utilities/HashMap.h b/common/include/Utilities/HashMap.h index 544bd84498..dd1aa22ef5 100644 --- a/common/include/Utilities/HashMap.h +++ b/common/include/Utilities/HashMap.h @@ -550,6 +550,9 @@ template< class Key, class T > class HashMap : public google::dense_hash_map { public: + using dense_hash_map::operator[]; + using dense_hash_map::const_iterator; + virtual ~HashMap() {} /// @@ -574,13 +577,13 @@ public: /// parameter. This is a more favorable alternative to the indexer operator since the /// indexer implementation can and will create new entries for every request that /// - /*void TryGetValue( const Key& key, T& outval ) const + void TryGetValue( const Key& key, T& outval ) const { // See above class for notes on why this is commented out. const_iterator iter = find( key ); if( iter != end() ) outval = iter->second; - }*/ + } const T& GetValue( Key key ) const { diff --git a/common/include/Utilities/SafeArray.h b/common/include/Utilities/SafeArray.h index 68ffe44f6e..27cf276436 100644 --- a/common/include/Utilities/SafeArray.h +++ b/common/include/Utilities/SafeArray.h @@ -248,7 +248,7 @@ protected: } public: - virtual ~SafeList() + virtual ~SafeList() throw() { safe_free( m_ptr ); } @@ -287,6 +287,11 @@ public: // The actual size of the allocation may differ. int GetSizeInBytes() const { return m_length * sizeof(T); } + void MatchLengthToAllocatedSize() + { + m_length = m_allocsize; + } + // Ensures that the allocation is large enough to fit data of the // amount requested. The memory allocation is not resized smaller. void MakeRoomFor( int blockSize ) @@ -398,7 +403,9 @@ protected: } }; -////////////////////////////////////////////////////////////////////////////////////////// +// -------------------------------------------------------------------------------------- +// SafeAlignedArray class +// -------------------------------------------------------------------------------------- // Handy little class for allocating a resizable memory block, complete with // exception-based error handling and automatic cleanup. // This one supports aligned data allocations too! diff --git a/common/include/Utilities/StringHelpers.h b/common/include/Utilities/StringHelpers.h index 9a0a6d52d8..0e60e8f79e 100644 --- a/common/include/Utilities/StringHelpers.h +++ b/common/include/Utilities/StringHelpers.h @@ -29,6 +29,9 @@ extern const wxRect wxDefaultRect; // This should prove useful.... #define wxsFormat wxString::Format +extern void SplitString( wxArrayString& dest, const wxString& src, const wxString& delims ); +extern void JoinString( wxString& dest, const wxArrayString& src, const wxString& separator ); + extern wxString ToString( const wxPoint& src, const wxString& separator=L"," ); extern wxString ToString( const wxSize& src, const wxString& separator=L"," ); extern wxString ToString( const wxRect& src, const wxString& separator=L"," ); diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index 0717a01217..4d64b104ba 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -130,7 +130,7 @@ namespace Threading volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc. public: - virtual ~PersistentThread(); + virtual ~PersistentThread() throw(); PersistentThread(); virtual void Start(); @@ -172,7 +172,7 @@ namespace Threading bool m_IsLocked; public: - virtual ~ScopedLock() + virtual ~ScopedLock() throw() { if( m_IsLocked ) m_lock.Unlock(); @@ -239,77 +239,35 @@ namespace Threading { protected: volatile bool m_Done; - volatile bool m_TaskComplete; + volatile bool m_TaskPending; Semaphore m_post_TaskComplete; + MutexLock m_lock_TaskComplete; public: - virtual ~BaseTaskThread() {} + virtual ~BaseTaskThread() throw() {} BaseTaskThread() : m_Done( false ) - , m_TaskComplete( false ) + , m_TaskPending( false ) , m_post_TaskComplete() { } - // Tells the thread to exit and then waits for thread termination. - sptr Block() - { - if( !m_running ) return m_returncode; - m_Done = true; - m_sem_event.Post(); - return PersistentThread::Block(); - } - - // Initiates the new task. This should be called after your own StartTask has - // initialized internal variables / preparations for task execution. - void PostTask() - { - jASSUME( m_running ); - m_TaskComplete = false; - m_post_TaskComplete.Reset(); - m_sem_event.Post(); - } - - // Blocks current thread execution pending the completion of the parallel task. - void WaitForResult() - { - if( !m_running ) return; - if( !m_TaskComplete ) - m_post_TaskComplete.Wait(); - else - m_post_TaskComplete.Reset(); - } + sptr Block(); + void PostTask(); + void WaitForResult(); protected: // Abstract method run when a task has been posted. Implementing classes should do // all your necessary processing work here. virtual void Task()=0; - sptr ExecuteTask() - { - do - { - // Wait for a job! - m_sem_event.Wait(); - - if( m_Done ) break; - Task(); - m_TaskComplete = true; - m_post_TaskComplete.Post(); - } while( !m_Done ); - - return 0; - } + sptr ExecuteTask(); }; ////////////////////////////////////////////////////////////////////////////////////////// // Our fundamental interlocking functions. All other useful interlocks can be derived // from these little beasties! - extern long pcsx2_InterlockedExchange(volatile long* Target, long srcval); - extern long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp ); - extern long pcsx2_InterlockedExchangeAdd( volatile long* target, long addval ); - extern void AtomicExchange( volatile u32& Target, u32 value ); extern void AtomicExchangeAdd( volatile u32& Target, u32 value ); extern void AtomicIncrement( volatile u32& Target ); diff --git a/common/src/Utilities/Console.cpp b/common/src/Utilities/Console.cpp index 177fe586e1..b1afd1ca14 100644 --- a/common/src/Utilities/Console.cpp +++ b/common/src/Utilities/Console.cpp @@ -52,8 +52,7 @@ namespace Console { std::string m_format_buffer; vssprintf( m_format_buffer, fmt, args ); - m_format_buffer += "\n"; - Write( wxString::FromUTF8( m_format_buffer.c_str() ) ); + WriteLn( wxString::FromUTF8( m_format_buffer.c_str() ) ); } __forceinline void __fastcall _WriteLn( Colors color, const char* fmt, va_list args ) diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp index 0a1b6b819a..bbfe5d0d2d 100644 --- a/common/src/Utilities/Exceptions.cpp +++ b/common/src/Utilities/Exceptions.cpp @@ -104,29 +104,15 @@ namespace Exception wxString Stream::FormatDiagnosticMessage() const { return wxsFormat( - L"Stream exception: %s\n\tObject name: %s", + L"Stream exception: %s\n\tFile/Object: %s", m_message_diag.c_str(), StreamName.c_str() ) + m_stacktrace; } wxString Stream::FormatDisplayMessage() const { - return m_message_user + L"\n\n" + StreamName; - } - - // ------------------------------------------------------------------------ - wxString FreezePluginFailure::FormatDiagnosticMessage() const - { - return wxsFormat( - L"%s plugin returned an error while %s the state.\n\n", - PluginName.c_str(), - FreezeAction.c_str() - ) + m_stacktrace; - } - - wxString FreezePluginFailure::FormatDisplayMessage() const - { - return m_message_user; + return m_message_user + L"\n\n" + + wxsFormat( _("Name: %s"), StreamName.c_str() ); } // ------------------------------------------------------------------------ diff --git a/common/src/Utilities/StringHelpers.cpp b/common/src/Utilities/StringHelpers.cpp index 80084e4d71..a6eb8b01c2 100644 --- a/common/src/Utilities/StringHelpers.cpp +++ b/common/src/Utilities/StringHelpers.cpp @@ -17,7 +17,6 @@ const wxRect wxDefaultRect( wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, wxDefaultCoord ); -////////////////////////////////////////////////////////////////////////////////////////// // Splits a string into parts and adds the parts into the given SafeList. // This list is not cleared, so concatenating many splits into a single large list is // the 'default' behavior, unless you manually clear the SafeList prior to subsequent calls. @@ -31,7 +30,13 @@ void SplitString( SafeList& dest, const wxString& src, const wxString& dest.Add( parts.GetNextToken() ); } -////////////////////////////////////////////////////////////////////////////////////////// +void SplitString( wxArrayString& dest, const wxString& src, const wxString& delims ) +{ + wxStringTokenizer parts( src, delims ); + while( parts.HasMoreTokens() ) + dest.Add( parts.GetNextToken() ); +} + // Joins a list of strings into one larger string, using the given string concatenation // character as a separator. If you want to be able to split the string later then the // concatenation string needs to be a single character. @@ -48,7 +53,16 @@ void JoinString( wxString& dest, const SafeList& src, const wxString& } } -////////////////////////////////////////////////////////////////////////////////////////// +void JoinString( wxString& dest, const wxArrayString& src, const wxString& separator ) +{ + for( int i=0, len=src.GetCount(); i 0 ) + { + multiple--; + sem_post( &sema ); + } #endif + } #if wxUSE_GUI // This is a wxApp-safe implementation of Wait, which makes sure and executes the App's @@ -278,7 +348,7 @@ namespace Threading // to handle messages. do { - wxTheApp->ProcessPendingEvents(); + wxTheApp->Yield(); } while( !Wait( ts_msec_250 ) ); } } @@ -297,7 +367,7 @@ namespace Threading // to handle messages. do { - wxTheApp->ProcessPendingEvents(); + wxTheApp->Yield(); if( Wait( ts_msec_250 ) ) break; countdown -= ts_msec_250; } while( countdown.GetMilliseconds() > 0 ); @@ -342,6 +412,10 @@ namespace Threading return retval; } +// -------------------------------------------------------------------------------------- +// MutexLock Implementations +// -------------------------------------------------------------------------------------- + MutexLock::MutexLock() { int err = 0; @@ -383,74 +457,58 @@ namespace Threading pthread_mutex_unlock( &mutex ); } - ////////////////////////////////////////////////////////////////////// - // define some overloads for InterlockedExchanges - // for commonly used types, like u32 and s32. - - __forceinline long pcsx2_InterlockedExchange( volatile long* target, long srcval ) - { - return _InterlockedExchange( target, srcval ); - } - - __forceinline long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp ) - { - // Use the pthreads-win32 implementation... - return _InterlockedCompareExchange( target, srcval, comp ); - } - - __forceinline long pcsx2_InterlockedExchangeAdd( volatile long* target, long srcval ) - { - return _InterlockedExchangeAdd( target, srcval ); - } +// -------------------------------------------------------------------------------------- +// InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions) +// -------------------------------------------------------------------------------------- +// define some overloads for InterlockedExchanges for commonly used types, like u32 and s32. __forceinline void AtomicExchange( volatile u32& Target, u32 value ) { - pcsx2_InterlockedExchange( (volatile long*)&Target, value ); + _InterlockedExchange( (volatile long*)&Target, value ); } __forceinline void AtomicExchangeAdd( volatile u32& Target, u32 value ) { - pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value ); + _InterlockedExchangeAdd( (volatile long*)&Target, value ); } __forceinline void AtomicIncrement( volatile u32& Target ) { - pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 ); + _InterlockedExchangeAdd( (volatile long*)&Target, 1 ); } __forceinline void AtomicDecrement( volatile u32& Target ) { - pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 ); + _InterlockedExchangeAdd( (volatile long*)&Target, -1 ); } __forceinline void AtomicExchange( volatile s32& Target, s32 value ) { - pcsx2_InterlockedExchange( (volatile long*)&Target, value ); + _InterlockedExchange( (volatile long*)&Target, value ); } __forceinline void AtomicExchangeAdd( s32& Target, u32 value ) { - pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value ); + _InterlockedExchangeAdd( (volatile long*)&Target, value ); } __forceinline void AtomicIncrement( volatile s32& Target ) { - pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 ); + _InterlockedExchangeAdd( (volatile long*)&Target, 1 ); } __forceinline void AtomicDecrement( volatile s32& Target ) { - pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 ); + _InterlockedExchangeAdd( (volatile long*)&Target, -1 ); } __forceinline void _AtomicExchangePointer( const void ** target, const void* value ) { - pcsx2_InterlockedExchange( (volatile long*)target, (long)value ); + _InterlockedExchange( (volatile long*)target, (long)value ); } __forceinline void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand ) { - pcsx2_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand ); + _InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand ); } - } diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index 9b14f0934f..9a0dc887c2 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -483,7 +483,7 @@ struct Freeze_v10Compat bool Spinning; }; -void SaveState::cdvdFreeze() +void SaveStateBase::cdvdFreeze() { FreezeTag( "cdvd" ); Freeze( cdvd ); diff --git a/pcsx2/CDVD/CdRom.cpp b/pcsx2/CDVD/CdRom.cpp index b05817e12c..af671ee5f8 100644 --- a/pcsx2/CDVD/CdRom.cpp +++ b/pcsx2/CDVD/CdRom.cpp @@ -961,7 +961,7 @@ void cdrReset() { cdReadTime = (PSXCLK / 1757) * BIAS; } -void SaveState::cdrFreeze() +void SaveStateBase::cdrFreeze() { FreezeTag( "cdrom" ); Freeze(cdr); diff --git a/pcsx2/Common.h b/pcsx2/Common.h index 9494ddafee..3179b9dced 100644 --- a/pcsx2/Common.h +++ b/pcsx2/Common.h @@ -40,6 +40,5 @@ // Some homeless externs. This is as good a spot as any for now... -extern bool EmulationInProgress(); extern void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR); extern u32 g_sseVUMXCSR, g_sseMXCSR; diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 8a10ad1338..69f89b5112 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -46,13 +46,15 @@ enum PluginsEnum_t u32 bitset; \ struct { +#define BITFIELD_END }; }; + //------------ DEFAULT sseMXCSR VALUES --------------- #define DEFAULT_sseMXCSR 0xffc0 //FPU rounding > DaZ, FtZ, "chop" #define DEFAULT_sseVUMXCSR 0xffc0 //VU rounding > DaZ, FtZ, "chop" -///////////////////////////////////////////////////////////////////////////////////////// -// Pcsx2Config -// +// -------------------------------------------------------------------------------------- +// Pcsx2Config class +// -------------------------------------------------------------------------------------- // This is intended to be a public class library between the core emulator and GUI only. // It is *not* meant to be shared data between core emulation and plugins, due to issues // with version incompatibilities if the structure formats are changed. @@ -75,7 +77,7 @@ public: RecBlocks_IOP:1, // Enables per-block profiling for the IOP recompiler [unimplemented] RecBlocks_VU0:1, // Enables per-block profiling for the VU0 recompiler [unimplemented] RecBlocks_VU1:1; // Enables per-block profiling for the VU1 recompiler [unimplemented] - }; }; + BITFIELD_END // Default is Disabled, with all recs enabled underneath. ProfilerOptions() : bitset( 0xfffffffe ) {} @@ -105,7 +107,7 @@ public: bool UseMicroVU0:1, UseMicroVU1:1; - }; }; + BITFIELD_END // All recs are enabled by default. RecompilerOptions() : bitset( 0xffffffff ) { } @@ -142,7 +144,7 @@ public: fpuOverflow:1, fpuExtraOverflow:1, fpuFullMode:1; - }; }; + BITFIELD_END CpuOptions(); void LoadSave( IniInterface& conf ); @@ -213,7 +215,7 @@ public: DMAExeHack:1, // Fix for Fatal Frame; breaks Gust and Tri-Ace games. XgKickHack:1, // Fix for Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics, but breaks Tri-ace games and others. MpegHack:1; // Fix for Mana Khemia 1, breaks Digital Devil Saga. - }; }; + BITFIELD_END // all gamefixes are disabled by default. GamefixOptions() : bitset( 0 ) {} @@ -240,7 +242,7 @@ public: BIFC0:1, // enables BIFC0 detection and fast-forwarding vuFlagHack:1, // microVU specific flag hack; Can cause Infinite loops, SPS, etc... vuMinMax:1; // microVU specific MinMax hack; Can cause SPS, Black Screens, etc... - }; }; + BITFIELD_END u8 EECycleRate; // EE cycle rate selector (1.0, 1.5, 2.0) u8 VUCycleSteal; // VU Cycle Stealer factor (0, 1, 2, or 3) @@ -275,7 +277,7 @@ public: MultitapPort0_Enabled:1, MultitapPort1_Enabled:1; - }; }; + BITFIELD_END CpuOptions Cpu; VideoOptions Video; diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 2c3b3eb690..6ed274e5d4 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -798,7 +798,7 @@ __forceinline u32 rcntCycle(int index) return counters[index].count; } -void SaveState::rcntFreeze() +void SaveStateBase::rcntFreeze() { Freeze( counters ); Freeze( hsyncCounter ); diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 7a0013031f..eda419d6fb 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -40,57 +40,6 @@ static s64 m_iSlowTicks=0; static bool m_justSkipped = false; static bool m_StrictSkipping = false; -#ifdef PCSX2_DEVBUILD - -// GS Playback -int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save -int g_nLeftGSFrames = 0; // when saving, number of frames left -gzSavingState* g_fGSSave; - -// fixme - need to take this concept and make it MTGS friendly. -#ifdef _STGS_GSSTATE_CODE -void GSGIFTRANSFER1(u32 *pMem, u32 addr) { - if( g_SaveGSStream == 2) { - u32 type = GSRUN_TRANS1; - u32 size = (0x4000-(addr))/16; - g_fGSSave->Freeze( type ); - g_fGSSave->Freeze( size ); - g_fGSSave->FreezeMem( ((u8*)pMem)+(addr), size*16 ); - } - GSgifTransfer1(pMem, addr); -} - -void GSGIFTRANSFER2(u32 *pMem, u32 size) { - if( g_SaveGSStream == 2) { - u32 type = GSRUN_TRANS2; - u32 _size = size; - g_fGSSave->Freeze( type ); - g_fGSSave->Freeze( size ); - g_fGSSave->FreezeMem( pMem, _size*16 ); - } - GSgifTransfer2(pMem, size); -} - -void GSGIFTRANSFER3(u32 *pMem, u32 size) { - if( g_SaveGSStream == 2 ) { - u32 type = GSRUN_TRANS3; - u32 _size = size; - g_fGSSave->Freeze( type ); - g_fGSSave->Freeze( size ); - g_fGSSave->FreezeMem( pMem, _size*16 ); - } - GSgifTransfer3(pMem, size); -} - -__forceinline void GSVSYNC(void) { - if( g_SaveGSStream == 2 ) { - u32 type = GSRUN_VSYNC; - g_fGSSave->Freeze( type ); - } -} -#endif -#endif - void _gs_ChangeTimings( u32 framerate, u32 iTicks ) { m_iSlowStart = GetCPUTicks(); @@ -615,7 +564,7 @@ void gsDynamicSkipEnable() frameLimitReset(); } -void SaveState::gsFreeze() +void SaveStateBase::gsFreeze() { FreezeMem(PS2MEM_GS, 0x2000); Freeze(CSRw); diff --git a/pcsx2/GS.h b/pcsx2/GS.h index 23a36609e5..b9a78e1e19 100644 --- a/pcsx2/GS.h +++ b/pcsx2/GS.h @@ -135,9 +135,16 @@ enum GS_RINGTYPE , GS_RINGTYPE_QUIT }; + +struct MTGS_FreezeData +{ + freezeData* fdata; + s32 retval; // value returned from the call, valid only after an mtgsWaitGS() +}; + class mtgsThreadObject : public Threading::PersistentThread { - friend class SaveState; + friend class SaveStateBase; protected: // Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s. @@ -192,6 +199,10 @@ protected: #endif // the MTGS "dummy" GIFtag info! + // fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely + // separate paths. But for that to work properly we need also interlocked path sources. + // That is, when the GIF selects a source, it sticks to that source until an EOP. Currently + // this is not emulated! GIFPath m_path[3]; // contains aligned memory allocations for gs and Ringbuffer. @@ -203,7 +214,7 @@ protected: public: mtgsThreadObject(); - virtual ~mtgsThreadObject(); + virtual ~mtgsThreadObject() throw(); void Start(); void Cancel(); @@ -222,13 +233,11 @@ public: void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 ); u8* GetDataPacketPtr() const; - void Freeze( SaveState& state ); + void Freeze( SaveStateBase& state ); void SetEvent(); void PostVsyncEnd( bool updategs ); - void _close_gs(); - protected: // Saves MMX/XMM regs, posts an event to the mtgsThread flag and releases a timeslice. // For use in surrounding loops that wait on the mtgs. @@ -331,11 +340,10 @@ enum gsrun extern int g_SaveGSStream; extern int g_nLeftGSFrames; -extern gzSavingState* g_fGSSave; #endif extern void SaveGSState(const wxString& file); extern void LoadGSState(const wxString& file); -extern void RunGSState(gzLoadingState& f); +extern void RunGSState( memLoadingState& f ); diff --git a/pcsx2/GSState.cpp b/pcsx2/GSState.cpp index 6236e78748..e4be0e704a 100644 --- a/pcsx2/GSState.cpp +++ b/pcsx2/GSState.cpp @@ -19,6 +19,55 @@ #include #ifdef PCSX2_DEVBUILD + +// GS Playback +int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save +int g_nLeftGSFrames = 0; // when saving, number of frames left +static wxScopedPtr g_fGSSave; + +// fixme - need to take this concept and make it MTGS friendly. +#ifdef _STGS_GSSTATE_CODE +void GSGIFTRANSFER1(u32 *pMem, u32 addr) { + if( g_SaveGSStream == 2) { + u32 type = GSRUN_TRANS1; + u32 size = (0x4000-(addr))/16; + g_fGSSave->Freeze( type ); + g_fGSSave->Freeze( size ); + g_fGSSave->FreezeMem( ((u8*)pMem)+(addr), size*16 ); + } + GSgifTransfer1(pMem, addr); +} + +void GSGIFTRANSFER2(u32 *pMem, u32 size) { + if( g_SaveGSStream == 2) { + u32 type = GSRUN_TRANS2; + u32 _size = size; + g_fGSSave->Freeze( type ); + g_fGSSave->Freeze( size ); + g_fGSSave->FreezeMem( pMem, _size*16 ); + } + GSgifTransfer2(pMem, size); +} + +void GSGIFTRANSFER3(u32 *pMem, u32 size) { + if( g_SaveGSStream == 2 ) { + u32 type = GSRUN_TRANS3; + u32 _size = size; + g_fGSSave->Freeze( type ); + g_fGSSave->Freeze( size ); + g_fGSSave->FreezeMem( pMem, _size*16 ); + } + GSgifTransfer3(pMem, size); +} + +__forceinline void GSVSYNC(void) { + if( g_SaveGSStream == 2 ) { + u32 type = GSRUN_VSYNC; + g_fGSSave->Freeze( type ); + } +} +#endif + void SaveGSState(const wxString& file) { if( g_SaveGSStream ) return; @@ -26,7 +75,8 @@ void SaveGSState(const wxString& file) Console::WriteLn( "Saving GS State..." ); Console::WriteLn( wxsFormat( L"\t%s", file.c_str() ) ); - g_fGSSave = new gzSavingState( file ); + SafeArray buf; + g_fGSSave.reset( new memSavingState( buf ) ); g_SaveGSStream = 1; g_nLeftGSFrames = 2; @@ -37,46 +87,35 @@ void SaveGSState(const wxString& file) void LoadGSState(const wxString& file) { int ret; - gzLoadingState* f; Console::Status( "Loading GS State..." ); - try - { - f = new gzLoadingState( file ); - } - catch( Exception::FileNotFound& ) - { - // file not found? try prefixing with sstates folder: - if( !Path::IsRelative( file ) ) - { - //f = new gzLoadingState( Path::Combine( g_Conf->Folders.Savestates, file ) ); + wxString src( file ); - // If this load attempt fails, then let the exception bubble up to - // the caller to deal with... - } - } + /*if( !wxFileName::FileExists( src ) ) + src = Path::Combine( g_Conf->Folders.Savestates, src );*/ + + if( !wxFileName::FileExists( src ) ) + return; + + SafeArray buf; + memLoadingState f( buf ); // Always set gsIrq callback -- GS States are always exclusionary of MTGS mode GSirqCallback( gsIrq ); ret = GSopen(&pDsp, "PCSX2", 0); if (ret != 0) - { - delete f; throw Exception::PluginOpenError( PluginId_GS ); - } ret = PADopen((void *)&pDsp); - f->Freeze(g_nLeftGSFrames); - f->gsFreeze(); + f.Freeze(g_nLeftGSFrames); + f.gsFreeze(); - f->FreezePlugin( "GS", gsSafeFreeze ); + GetPluginManager().Freeze( PluginId_GS, f ); - RunGSState( *f ); - - delete( f ); + RunGSState( f ); g_plugins->Close( PluginId_GS ); g_plugins->Close( PluginId_PAD ); @@ -90,12 +129,12 @@ struct GSStatePacket // runs the GS // (this should really be part of the AppGui) -void RunGSState( gzLoadingState& f ) +void RunGSState( memLoadingState& f ) { u32 newfield; std::list< GSStatePacket > packets; - while( !f.Finished() ) + while( !f.IsFinished() ) { int type, size; f.Freeze( type ); @@ -152,3 +191,77 @@ void RunGSState( gzLoadingState& f ) } } #endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +void vSyncDebugStuff( uint frame ) +{ +#ifdef OLD_TESTBUILD_STUFF + if( g_TestRun.enabled && g_TestRun.frame > 0 ) { + if( frame > g_TestRun.frame ) { + // take a snapshot + if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) { + if( g_TestRun.snapdone ) { + g_TestRun.curimage++; + g_TestRun.snapdone = 0; + g_TestRun.frame += 20; + if( g_TestRun.curimage >= g_TestRun.numimages ) { + // exit + g_EmuThread->Cancel(); + } + } + else { + // query for the image + GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture); + } + } + else { + // exit + g_EmuThread->Cancel(); + } + } + } + + GSVSYNC(); + + if( g_SaveGSStream == 1 ) { + freezeData fP; + + g_SaveGSStream = 2; + g_fGSSave->gsFreeze(); + + if (GSfreeze(FREEZE_SIZE, &fP) == -1) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + } + else { + fP.data = (s8*)malloc(fP.size); + if (fP.data == NULL) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + } + else { + if (GSfreeze(FREEZE_SAVE, &fP) == -1) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + } + else { + g_fGSSave->Freeze( fP.size ); + if (fP.size) { + g_fGSSave->FreezeMem( fP.data, fP.size ); + free(fP.data); + } + } + } + } + } + else if( g_SaveGSStream == 2 ) { + + if( --g_nLeftGSFrames <= 0 ) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + Console::WriteLn("Done saving GS stream"); + } + } +#endif +} diff --git a/pcsx2/Gif.cpp b/pcsx2/Gif.cpp index 91711176ff..3498b088f4 100644 --- a/pcsx2/Gif.cpp +++ b/pcsx2/Gif.cpp @@ -101,15 +101,6 @@ static u32 WRITERING_DMA(u32 *pMem, u32 qwc) int size = mtgsThread->PrepDataPacket(GIF_PATH_3, pMem, qwc); u8* pgsmem = mtgsThread->GetDataPacketPtr(); - /* check if page of endmem is valid (dark cloud2) */ - // fixme: this hack makes no sense, because the giftagDummy will - // process the full length of bytes regardess of how much we copy. - // So you'd think if we're truncating the copy to prevent DEPs, we - // should truncate the gif packet size too.. (air) - - // fixed? PrepDataPacket now returns the actual size of the packet. - // VIF handles scratchpad wrapping also, so this code shouldn't be needed anymore. - memcpy_aligned(pgsmem, pMem, size<<4); mtgsThread->SendDataPacket(); @@ -572,7 +563,7 @@ void gifMFIFOInterrupt() clearFIFOstuff(false); } -void SaveState::gifFreeze() +void SaveStateBase::gifFreeze() { FreezeTag( "GIFdma" ); diff --git a/pcsx2/IPU/IPU.cpp b/pcsx2/IPU/IPU.cpp index 7a4b4c8ca7..42d4dc0c12 100644 --- a/pcsx2/IPU/IPU.cpp +++ b/pcsx2/IPU/IPU.cpp @@ -195,7 +195,7 @@ void ReportIPU() } // fixme - ipuFreeze looks fairly broken. Should probably take a closer look at some point. -void SaveState::ipuFreeze() +void SaveStateBase::ipuFreeze() { IPUProcessInterrupt(); diff --git a/pcsx2/IopCounters.cpp b/pcsx2/IopCounters.cpp index f3520ca448..33a194dae1 100644 --- a/pcsx2/IopCounters.cpp +++ b/pcsx2/IopCounters.cpp @@ -748,7 +748,7 @@ void psxRcntSetGates() psxvblankgate &= ~(1<<3); } -void SaveState::psxRcntFreeze() +void SaveStateBase::psxRcntFreeze() { FreezeTag( "iopCounters" ); diff --git a/pcsx2/IopSio2.cpp b/pcsx2/IopSio2.cpp index 1c28f259ca..89328a07cc 100644 --- a/pcsx2/IopSio2.cpp +++ b/pcsx2/IopSio2.cpp @@ -199,7 +199,7 @@ u8 sio2_fifoOut(){ return 0; // No Data } -void SaveState::sio2Freeze() +void SaveStateBase::sio2Freeze() { FreezeTag( "sio2" ); Freeze(sio2); diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 09b9d08542..d2e53cadf7 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -78,7 +78,7 @@ __forceinline void GIFPath::SetTag(const void* mem) curreg = 0; } -static void _mtgsFreezeGIF( SaveState& state, GIFPath (&paths)[3] ) +static void _mtgsFreezeGIF( SaveStateBase& state, GIFPath (&paths)[3] ) { for(int i=0; i<3; i++ ) { @@ -92,7 +92,7 @@ static void _mtgsFreezeGIF( SaveState& state, GIFPath (&paths)[3] ) } } -void SaveState::mtgsFreeze() +void SaveStateBase::mtgsFreeze() { FreezeTag( "mtgs" ); mtgsThread->Freeze( *this ); @@ -388,17 +388,12 @@ struct PacketTagType extern bool renderswitch; -void mtgsThreadObject::_close_gs() +static void _clean_close_gs( void* obj ) { if( g_plugins != NULL ) g_plugins->m_info[PluginId_GS].CommonBindings.Close(); } -static void _clean_close_gs( void* obj ) -{ - ((mtgsThreadObject*)obj)->_close_gs(); -} - void mtgsThreadObject::_RingbufferLoop() { pthread_cleanup_push( _clean_close_gs, this ); @@ -510,9 +505,9 @@ void mtgsThreadObject::_RingbufferLoop() case GS_RINGTYPE_FREEZE: { - freezeData* data = (freezeData*)(*(uptr*)&tag.data[1]); + MTGS_FreezeData* data = (MTGS_FreezeData*)(*(uptr*)&tag.data[1]); int mode = tag.data[0]; - GetPluginManager().Freeze( PluginId_GS, mode, data ); + data->retval = GetPluginManager().DoFreeze( PluginId_GS, mode, data->fdata ); break; } @@ -576,11 +571,17 @@ void mtgsThreadObject::_RingbufferLoop() pthread_cleanup_pop( true ); } +static void dummyIrqCallback() +{ + // dummy, because MTGS doesn't need this mess! + // (and zerogs does >_<) +} + sptr mtgsThreadObject::ExecuteTask() { memcpy_aligned( m_gsMem, PS2MEM_GS, sizeof(PS2MEM_GS) ); GSsetBaseMem( m_gsMem ); - GSirqCallback( NULL ); + GSirqCallback( dummyIrqCallback ); Console::WriteLn( (wxString)L"\t\tForced software switch: " + (renderswitch ? L"Enabled" : L"Disabled") ); m_returncode = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 ); @@ -781,14 +782,6 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s jASSUME( size < m_RingBufferSize ); jASSUME( writepos < m_RingBufferSize ); - //fixme: Vif sometimes screws up and size is unaligned, try this then (rama) - //Is this still a problem? It should be fixed on the specific VIF command now. (air) - //It seems to be fixed in Fatal Frame, leaving the code here still in case we get that again (rama) - /*if( (size&15) != 0){ - Console::Error( "MTGS problem, size unaligned"); - size = (size+15)&(~15); - }*/ - m_packet_size = gifTransferDummy(pathidx, srcdata, size); size = m_packet_size + 1; // takes into account our command qword. @@ -1009,7 +1002,7 @@ void mtgsThreadObject::GIFSoftReset( int mask ) mtgsThread->SendSimplePacket( GS_RINGTYPE_SOFTRESET, mask, 0, 0 ); } -void mtgsThreadObject::Freeze( SaveState& state ) +void mtgsThreadObject::Freeze( SaveStateBase& state ) { _mtgsFreezeGIF( state, this->m_path ); } diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp index 88ab7dcb61..74693e5e39 100644 --- a/pcsx2/PluginManager.cpp +++ b/pcsx2/PluginManager.cpp @@ -593,6 +593,34 @@ wxString Exception::PluginError::FormatDisplayMessage() const return wxsFormat( m_message_user, tbl_PluginInfo[PluginId].GetShortname().c_str() ); } +wxString Exception::FreezePluginFailure::FormatDiagnosticMessage() const +{ + return wxsFormat( + L"%s plugin returned an error while saving the state.\n\n", + tbl_PluginInfo[PluginId].shortname + ) + m_stacktrace; +} + +wxString Exception::FreezePluginFailure::FormatDisplayMessage() const +{ + // [TODO] + return m_message_user; +} + +wxString Exception::ThawPluginFailure::FormatDiagnosticMessage() const +{ + return wxsFormat( + L"%s plugin returned an error while loading the state.\n\n", + tbl_PluginInfo[PluginId].shortname + ) + m_stacktrace; +} + +wxString Exception::ThawPluginFailure::FormatDisplayMessage() const +{ + // [TODO] + return m_message_user; +} + // -------------------------------------------------------------------------------------- // PCSX2 Callbacks passed to Plugins // -------------------------------------------------------------------------------------- @@ -702,6 +730,8 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] ) // fixme: use plugin's GetLastError (not implemented yet!) throw Exception::PluginLoadError( PluginId_Mcd, wxEmptyString, "Internal Memorycard Plugin failed to load." ); } + + g_plugins = this; } PluginManager::~PluginManager() @@ -713,6 +743,9 @@ PluginManager::~PluginManager() } DESTRUCTOR_CATCHALL // All library unloading done automatically. + + if( g_plugins == this ) + g_plugins = NULL; } void PluginManager::BindCommon( PluginsEnum_t pid ) @@ -890,7 +923,7 @@ void PluginManager::Open() void PluginManager::Close( PluginsEnum_t pid ) { if( !m_info[pid].IsOpened ) return; - DevCon::Status( "\tClosing %s", tbl_PluginInfo[pid].shortname ); + Console::Status( "\tClosing %s", tbl_PluginInfo[pid].shortname ); if( pid == PluginId_GS ) { @@ -910,7 +943,7 @@ void PluginManager::Close( PluginsEnum_t pid ) void PluginManager::Close( bool closegs ) { - Console::Status( "Closing plugins..." ); + DbgCon::Status( "Closing plugins..." ); // Close plugins in reverse order of the initialization procedure. @@ -920,7 +953,7 @@ void PluginManager::Close( bool closegs ) Close( tbl_PluginInfo[i].id ); } - Console::Status( "Plugins closed successfully." ); + DbgCon::Status( "Plugins closed successfully." ); } // Initializes all plugins. Plugin initialization should be done once for every new emulation @@ -974,8 +1007,8 @@ void PluginManager::Init() void PluginManager::Shutdown() { Close(); + DbgCon::Status( "Shutting down plugins..." ); - Console::Status( "Shutting down plugins..." ); // Shutdown plugins in reverse order (probably doesn't matter... // ... but what the heck, right?) @@ -990,52 +1023,82 @@ void PluginManager::Shutdown() // More memorycard hacks!! - if( EmuPlugins.Mcd != NULL && m_mcdPlugin != NULL ) + if( (EmuPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) ) { m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)EmuPlugins.Mcd ); EmuPlugins.Mcd = NULL; } - Console::Status( "Plugins shutdown successfully." ); + DbgCon::Status( "Plugins shutdown successfully." ); } -void PluginManager::Freeze( PluginsEnum_t pid, int mode, freezeData* data ) +// For internal use only, unless you're the MTGS. Then it's for you too! +// Returns false if the plugin returned an error. +bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data ) { - m_info[pid].CommonBindings.Freeze( mode, data ); -} - -// ---------------------------------------------------------------------------- -// Thread Safety: -// This function should only be called by the Main GUI thread and the GS thread (for GS states only), -// as it has special handlers to ensure that GS freeze commands are executed appropriately on the -// GS thread. -// -void PluginManager::Freeze( PluginsEnum_t pid, SaveState& state ) -{ - if( pid == PluginId_GS && wxThread::IsMain() ) + if( (pid == PluginId_GS) && wxThread::IsMain() ) { - // Need to send the GS freeze request on the GS thread. + MTGS_FreezeData woot = { data, 0 }; + // GS needs some thread safety love... + mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &woot ); + mtgsWaitGS(); + return woot.retval != -1; } else { - state.FreezePlugin( tbl_PluginInfo[pid].shortname, m_info[pid].CommonBindings.Freeze ); + return m_info[pid].CommonBindings.Freeze( mode, data ) != -1; } } -// ---------------------------------------------------------------------------- -// This overload of Freeze performs savestate freeze operation on *all* plugins, -// as according to the order in PluignsEnum_t. -// // Thread Safety: // This function should only be called by the Main GUI thread and the GS thread (for GS states only), // as it has special handlers to ensure that GS freeze commands are executed appropriately on the // GS thread. // -void PluginManager::Freeze( SaveState& state ) +void PluginManager::Freeze( PluginsEnum_t pid, SaveStateBase& state ) { - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) - Freeze( pi->id, state ); + Console::WriteLn( "\t%s %s", state.IsSaving() ? "Saving" : "Loading", + tbl_PluginInfo[pid].shortname ); + + freezeData fP = { 0, NULL }; + if( !DoFreeze( pid, FREEZE_SIZE, &fP ) ) + fP.size = 0; + + int fsize = fP.size; + state.Freeze( fsize ); + + if( state.IsLoading() && (fsize == 0) ) + { + // no state data to read, but the plugin expects some state data. + // Issue a warning to console... + if( fP.size != 0 ) + Console::Notice( "\tWarning: 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. + } + + fP.size = fsize; + if( fP.size == 0 ) return; + + state.PrepBlock( fP.size ); + fP.data = (s8*)state.GetBlockPtr(); + + if( state.IsSaving() ) + { + if( !DoFreeze(pid, FREEZE_SAVE, &fP) ) + throw Exception::FreezePluginFailure( pid ); + } + else + { + if( !DoFreeze(pid, FREEZE_LOAD, &fP) ) + throw Exception::ThawPluginFailure( pid ); + } + + state.CommitBlock( fP.size ); } bool PluginManager::KeyEvent( const keyEvent& evt ) @@ -1062,9 +1125,7 @@ void PluginManager::Configure( PluginsEnum_t pid ) // PluginManager* PluginManager_Create( const wxString (&folders)[PluginId_Count] ) { - PluginManager* retval = new PluginManager( folders ); - retval->Init(); - return retval; + return new PluginManager( folders ); } PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] ) diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h index 3d28dca955..d07682d1f4 100644 --- a/pcsx2/Plugins.h +++ b/pcsx2/Plugins.h @@ -106,6 +106,39 @@ namespace Exception PluginId = pid; } }; + + // This exception is thrown when a plugin returns an error while trying to save itself. + // Typically this should be a very rare occurance since a plugin typically shoudn't + // be doing memory allocations or file access during state saving. + // + class FreezePluginFailure : public virtual PluginError + { + public: + DEFINE_EXCEPTION_COPYTORS( FreezePluginFailure ) + + explicit FreezePluginFailure( PluginsEnum_t pid) + { + PluginId = pid; + } + + virtual wxString FormatDiagnosticMessage() const; + virtual wxString FormatDisplayMessage() const; + }; + + class ThawPluginFailure : public virtual PluginError, public virtual BadSavedState + { + public: + DEFINE_EXCEPTION_COPYTORS( ThawPluginFailure ) + + explicit ThawPluginFailure( PluginsEnum_t pid ) + { + PluginId = pid; + } + + virtual wxString FormatDiagnosticMessage() const; + virtual wxString FormatDisplayMessage() const; + }; + }; // -------------------------------------------------------------------------------------- @@ -140,7 +173,7 @@ struct LegacyPluginAPI_Common } }; -class SaveState; +class SaveStateBase; class mtgsThreadObject; // -------------------------------------------------------------------------------------- @@ -201,9 +234,12 @@ public: virtual void Close( PluginsEnum_t pid ) {} virtual void Close( bool closegs=true ) {} - virtual void Freeze( PluginsEnum_t pid, int mode, freezeData* data ) { wxASSERT_MSG( false, L"Null PluginManager!" ); } - virtual void Freeze( PluginsEnum_t pid, SaveState& state ) { wxASSERT_MSG( false, L"Null PluginManager!" ); } - virtual void Freeze( SaveState& state ) { wxASSERT_MSG( false, L"Null PluginManager!" ); } + virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state ) { wxASSERT_MSG( false, L"Null PluginManager!" ); } + virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data ) + { + wxASSERT_MSG( false, L"Null PluginManager!" ); + return false; + } virtual bool KeyEvent( const keyEvent& evt ) { return false; } }; @@ -238,9 +274,11 @@ protected: bool m_initialized; - PluginStatus_t m_info[PluginId_Count]; const PS2E_LibraryAPI* m_mcdPlugin; +public: // hack until we unsuck plugins... + PluginStatus_t m_info[PluginId_Count]; + public: virtual ~PluginManager(); @@ -251,9 +289,8 @@ public: void Close( PluginsEnum_t pid ); void Close( bool closegs=true ); - void Freeze( PluginsEnum_t pid, int mode, freezeData* data ); - void Freeze( PluginsEnum_t pid, SaveState& state ); - void Freeze( SaveState& state ); + void Freeze( PluginsEnum_t pid, SaveStateBase& state ); + bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data ); bool KeyEvent( const keyEvent& evt ); void Configure( PluginsEnum_t pid ); diff --git a/pcsx2/PrecompiledHeader.h b/pcsx2/PrecompiledHeader.h index 8241c83ea2..a6cc27a5f9 100644 --- a/pcsx2/PrecompiledHeader.h +++ b/pcsx2/PrecompiledHeader.h @@ -83,6 +83,7 @@ typedef int BOOL; #include "i18n.h" #include "Config.h" #include "Utilities/wxBaseTools.h" +#include "Utilities/ScopedPtr.h" #include "Utilities/Path.h" #include "Utilities/Console.h" #include "Utilities/Exceptions.h" diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp index 0139988600..34fbce2aef 100644 --- a/pcsx2/RecoverySystem.cpp +++ b/pcsx2/RecoverySystem.cpp @@ -16,128 +16,33 @@ #include "PrecompiledHeader.h" -#include "Common.h" -#include "ps2/CoreEmuThread.h" +#include "App.h" #include "HostGui.h" - -////////////////////////////////////////////////////////////////////////////////////////// -// RecoverySystem.cpp -- houses code for recovering from on-the-fly changes to the emu -// configuration, and for saving/restoring the GS state (for more seamless exiting of -// fullscreen GS operation). -// -// The following handful of local classes are implemented att he bottom of this file. - -static SafeArray* g_RecoveryState = NULL; -static SafeArray* g_gsRecoveryState = NULL; - -// This class type creates a memory savestate using the existing Recovery information -// (if present) to generate the savestate material. If no recovery data is present, -// the current emulation state is used instead. -class RecoveryMemSavingState : public memSavingState, Sealed -{ -public: - virtual ~RecoveryMemSavingState() { } - RecoveryMemSavingState(); - - void gsFreeze(); - void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) ); -}; - -// This class type creates an on-disk (zipped) savestate using the existing Recovery -// information (if present) to generate the savestate material. If no recovery data is -// present, the current emulation state is used instead. -class RecoveryZipSavingState : public gzSavingState, Sealed -{ -public: - virtual ~RecoveryZipSavingState() { } - RecoveryZipSavingState( const wxString& filename ); - - void gsFreeze(); - void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) ); -}; - -// Special helper class used to save *just* the GS-relevant state information. -class JustGsSavingState : public memSavingState, Sealed -{ -public: - virtual ~JustGsSavingState() { } - JustGsSavingState(); - - // This special override saves the gs info to m_idx+4, and then goes back and - // writes in the length of data saved. - void gsFreeze(); -}; +static wxScopedPtr> g_RecoveryState; namespace StateRecovery { bool HasState() { - return g_RecoveryState != NULL || g_gsRecoveryState != NULL; + return g_RecoveryState; } void Recover() { - wxASSERT( g_EmuThread != NULL ); - wxASSERT( g_EmuThread->IsSelf() ); + if( !g_RecoveryState ) return; - if( g_RecoveryState != NULL ) - { - Console::Status( "Resuming execution from full memory state..." ); - memLoadingState( *g_RecoveryState ).FreezeAll(); - } - else if( g_gsRecoveryState != NULL ) - { - s32 dummylen; - - Console::Status( "Resuming execution from gsState..." ); - memLoadingState eddie( *g_gsRecoveryState ); - eddie.FreezePlugin( "GS", gsSafeFreeze ); - eddie.Freeze( dummylen ); // reads the length value recorded earlier. - eddie.gsFreeze(); - } + Console::Status( "Resuming execution from full memory state..." ); + memLoadingState( *g_RecoveryState ).FreezeAll(); StateRecovery::Clear(); SysClearExecutionCache(); - - if( GSsetGameCRC != NULL ) - GSsetGameCRC(ElfCRC, g_ZeroGSOptions); } - // Saves recovery state info to the given filename, or saves the active emulation state - // (if one exists) and no recovery data was found. This is needed because when a recovery - // state is made, the emulation state is usually reset so the only persisting state is - // the one in the memory save. :) - // - // Threading Notes: - // This function can be invoked by any thread. However, if it is run outside the context - // of a CoreEmuThred then it void SaveToFile( const wxString& file ) { - SysSuspend(); - if( g_RecoveryState == NULL ) - { - RecoveryMemSavingState().FreezeAll(); - } - - SysResume(); - - if( g_RecoveryState != NULL ) - { - gzFile fileptr = gzopen( file.ToAscii().data(), "wb" ); - if( fileptr == NULL ) - throw Exception::CreateStream( file, "General savestate file creation error." ); - - gzwrite( fileptr, &g_SaveVersion, sizeof( u32 ) ); - gzwrite( fileptr, g_RecoveryState->GetPtr(), g_RecoveryState->GetSizeInBytes() ); - gzclose( fileptr ); - } - else - { - if( !EmulationInProgress() ) return; - - RecoveryZipSavingState( file ).FreezeAll(); - } + SafeArray buf; + memSavingState( buf ).FreezeAll(); } // Saves recovery state info to the given saveslot, or saves the active emulation state @@ -146,40 +51,22 @@ namespace StateRecovery { // the one in the memory save. :) void SaveToSlot( uint num ) { - SaveToFile( SaveState::GetFilename( num ) ); + SaveToFile( SaveStateBase::GetFilename( num ) ); } - // This method will override any existing recovery states, so call it with caution, if you - // think that there could be existing important state info in the recovery buffers (but - // really there shouldn't be, unless you're calling this function when it's not intended - // to be called). - void MakeGsOnly() - { - StateRecovery::Clear(); - if( !EmulationInProgress() ) return; - - g_gsRecoveryState = new SafeArray(); - JustGsSavingState eddie; - eddie.FreezePlugin( "GS", gsSafeFreeze ) ; - eddie.gsFreeze(); - } - // Creates a full recovery of the entire emulation state (CPU and all plugins). // If a current recovery state is already present, then nothing is done (the // existing recovery state takes precedence since if it were out-of-date it'd be // deleted!). void MakeFull() { - if( g_RecoveryState != NULL ) return; + if( g_RecoveryState ) return; if( !EmulationInProgress() ) return; - SysSuspend(); - try { - g_RecoveryState = new SafeArray( L"Memory Savestate Recovery" ); - RecoveryMemSavingState().FreezeAll(); - safe_delete( g_gsRecoveryState ); + g_RecoveryState.reset( new SafeArray( L"Memory Savestate Recovery" ) ); + memSavingState( *g_RecoveryState ).FreezeAll(); } catch( Exception::RuntimeError& ex ) { @@ -189,101 +76,13 @@ namespace StateRecovery { L"able to recover if you make changes to your PCSX2 configuration.\n\n" L"Details: %s", ex.FormatDisplayMessage().c_str() ) ); - safe_delete( g_RecoveryState ); + g_RecoveryState.reset(); } } // Clears and deallocates any recovery states. void Clear() { - safe_delete( g_RecoveryState ); - safe_delete( g_gsRecoveryState ); - } -} - -RecoveryMemSavingState::RecoveryMemSavingState() : memSavingState( *g_RecoveryState ) -{ -} - -void RecoveryMemSavingState::gsFreeze() -{ - if( g_gsRecoveryState != NULL ) - { - // just copy the data from src to dst. - // the normal savestate doesn't expect a length prefix for internal structures, - // so don't copy that part. - const u32 pluginlen = *((u32*)g_gsRecoveryState->GetPtr()); - const u32 gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4)); - memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(pluginlen+8), gslen ); - m_idx += gslen; - } - else - memSavingState::gsFreeze(); -} - -void RecoveryMemSavingState::FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) ) -{ - if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) ) - { - // Gs data is already in memory, so just copy from src to dest: - // length of the GS data is stored as the first u32, so use that to run the copy: - const u32 len = *((u32*)g_gsRecoveryState->GetPtr()); - memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(), len+4 ); - m_idx += len+4; - } - else - memSavingState::FreezePlugin( name, freezer ); -} - -RecoveryZipSavingState::RecoveryZipSavingState( const wxString& filename ) : gzSavingState( filename ) -{ -} - -void RecoveryZipSavingState::gsFreeze() -{ - if( g_gsRecoveryState != NULL ) - { - // read data from the gsRecoveryState allocation instead of the GS, since the gs - // info was invalidated when the plugin was shut down. - - // the normal savestate doesn't expect a length prefix for internal structures, - // so don't copy that part. - - u32& pluginlen = *((u32*)g_gsRecoveryState->GetPtr(0)); - u32& gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4)); - gzwrite( m_file, g_gsRecoveryState->GetPtr(pluginlen+4), gslen ); - } - else - gzSavingState::gsFreeze(); -} - -void RecoveryZipSavingState::FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) ) -{ - if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) ) - { - // Gs data is already in memory, so just copy from there into the gzip file. - // length of the GS data is stored as the first u32, so use that to run the copy: - u32& len = *((u32*)g_gsRecoveryState->GetPtr()); - gzwrite( m_file, g_gsRecoveryState->GetPtr(), len+4 ); - } - else - gzSavingState::FreezePlugin( name, freezer ); -} - -JustGsSavingState::JustGsSavingState() : memSavingState( *g_gsRecoveryState ) -{ -} - -// This special override saves the gs info to m_idx+4, and then goes back and -// writes in the length of data saved. -void JustGsSavingState::gsFreeze() -{ - int oldmidx = m_idx; - m_idx += 4; - memSavingState::gsFreeze(); - if( IsSaving() ) - { - s32& len = *((s32*)m_memory.GetPtr( oldmidx )); - len = (m_idx - oldmidx)-4; + g_RecoveryState.reset(); } } diff --git a/pcsx2/SPR.cpp b/pcsx2/SPR.cpp index 88898a363f..91fbc5887e 100644 --- a/pcsx2/SPR.cpp +++ b/pcsx2/SPR.cpp @@ -437,7 +437,7 @@ void SPRTOinterrupt() hwDmacIrq(DMAC_TO_SPR); } -void SaveState::sprFreeze() +void SaveStateBase::sprFreeze() { FreezeTag("SPRdma"); diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index aa7b5e6335..2c5581053e 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -34,9 +34,6 @@ using namespace R5900; -extern void recResetEE(); -extern void recResetIOP(); - static void PreLoadPrep() { SysClearExecutionCache(); @@ -49,38 +46,45 @@ static void PostLoadPrep() for(int i=0; i<48; i++) MapTLB(i); } -wxString SaveState::GetFilename( int slot ) +wxString SaveStateBase::GetFilename( int slot ) { return (g_Conf->Folders.Savestates + wxsFormat( L"%8.8X.%3.3d", ElfCRC, slot )).GetFullPath(); } -SaveState::SaveState( const char* msg, const wxString& destination ) : - m_version( g_SaveVersion ) -, m_tagspace( 128 ) +SaveStateBase::SaveStateBase( SafeArray& memblock ) : + m_memory( memblock ) +, m_version( g_SaveVersion ) +, m_idx( 0 ) +, m_sectid( FreezeId_Unknown ) +, m_pid( PluginId_GS ) +, m_DidBios( false ) { - Console::WriteLn( "%s %s", msg, destination.ToAscii().data() ); } -s32 CALLBACK gsSafeFreeze( int mode, freezeData *data ) +void SaveStateBase::PrepBlock( int size ) { - // have to call in the GS thread, otherwise weird stuff will start happening - mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, data ); - mtgsWaitGS(); - return 0; -} - -void SaveState::FreezeTag( const char* src ) -{ - const int length = strlen( src ); - m_tagspace.MakeRoomFor( length+1 ); - - strcpy( m_tagspace.GetPtr(), src ); - FreezeMem( m_tagspace.GetPtr(), length ); - - if( strcmp( m_tagspace.GetPtr(), src ) != 0 ) + const int end = m_idx+size; + if( IsSaving() ) + m_memory.MakeRoomFor( end ); + else { - assert( 0 ); + if( m_memory.GetSizeInBytes() <= end ) + throw Exception::BadSavedState(); + } +} + +void SaveStateBase::FreezeTag( const char* src ) +{ + wxASSERT( strlen(src) < (sizeof( m_tagspace )-1) ); + + memzero_obj( m_tagspace ); + strcpy( m_tagspace, src ); + Freeze( m_tagspace ); + + if( strcmp( m_tagspace, src ) != 0 ) + { + wxASSERT_MSG( false, L"Savestate data corruption detected while reading tag" ); throw Exception::BadSavedState( // Untranslated diagnostic msg (use default msg for translation) L"Savestate data corruption detected while reading tag: " + wxString::FromAscii(src) @@ -88,48 +92,68 @@ void SaveState::FreezeTag( const char* src ) } } -void SaveState::FreezeAll() +void SaveStateBase::FreezeBios() { - if( IsLoading() ) - PreLoadPrep(); - // 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). - + char descin[128]; wxString descout; IsBIOS( g_Conf->FullpathToBios(), descout ); memcpy_fast( descin, descout.ToAscii().data(), 128 ); - Freeze( descin ); - - if( memcmp( descin, descout, 128 ) != 0 ) - { - Console::Error( - "\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n" - "\t\tCurrent BIOS: %s\n" - "\t\tSavestate BIOS: %s\n", - descout.ToAscii().data(), descin - ); - } + // ... 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( memcmp( descin, descout, 128 ) != 0 ) + { + Console::Error( + "\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n" + "\t\tCurrent Version: %s\n" + "\t\tSavestate Version: %s\n", + descout.ToAscii().data(), descin + ); + } + } + m_DidBios = true; +} + +static const int MainMemorySizeInBytes = + Ps2MemSize::Base + Ps2MemSize::Scratch + Ps2MemSize::Hardware + + Ps2MemSize::IopRam + Ps2MemSize::IopHardware + 0x0100; + +void SaveStateBase::FreezeMainMemory() +{ // First Block - Memory Dumps // --------------------------- - FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory - FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad - FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory + FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory + FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad + FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory FreezeMem(psxM, Ps2MemSize::IopRam); // 2 MB main memory FreezeMem(psxH, Ps2MemSize::IopHardware); // hardware memory FreezeMem(psxS, 0x000100); // iop's sif memory +} + +void SaveStateBase::FreezeRegisters() +{ + if( IsLoading() ) + PreLoadPrep(); // Second Block - Various CPU Registers and States // ----------------------------------------------- FreezeTag( "cpuRegs" ); - Freeze(cpuRegs); // cpu regs + COP0 - Freeze(psxRegs); // iop regs + Freeze(cpuRegs); // cpu regs + COP0 + Freeze(psxRegs); // iop regs Freeze(fpuRegs); - Freeze(tlb); // tlbs + Freeze(tlb); // tlbs // Third Block - Cycle Timers and Events // ------------------------------------- @@ -143,6 +167,7 @@ void SaveState::FreezeAll() // Fourth Block - EE-related systems // --------------------------------- + FreezeTag( "EE-Subsystems" ); rcntFreeze(); gsFreeze(); vuMicroFreeze(); @@ -155,154 +180,172 @@ void SaveState::FreezeAll() // Fifth Block - iop-related systems // --------------------------------- + FreezeTag( "IOP-Subsystems" ); psxRcntFreeze(); sioFreeze(); sio2Freeze(); cdrFreeze(); cdvdFreeze(); - // Sixth Block - Plugins Galore! - // ----------------------------- - FreezePlugin( "GS", gsSafeFreeze ); - - g_plugins->Freeze( *this ); - if( IsLoading() ) PostLoadPrep(); } -///////////////////////////////////////////////////////////////////////////// -// gzipped to/from disk state saves implementation - -gzBaseStateInfo::gzBaseStateInfo( const char* msg, const wxString& filename ) : - SaveState( msg, filename ) -, m_filename( filename ) -, m_file( NULL ) +bool SaveStateBase::FreezeSection() { -} + Freeze( m_sectid ); -gzBaseStateInfo::~gzBaseStateInfo() -{ - if( m_file != NULL ) + switch( m_sectid ) { - gzclose( m_file ); - m_file = NULL; + case FreezeId_End: + return false; + + case FreezeId_Bios: + { + int sectlen = 128; + FreezeTag( "BiosVersion" ); + Freeze( sectlen ); + + if( sectlen != MainMemorySizeInBytes ) + { + throw Exception::BadSavedState( wxEmptyString, + L"Invalid size encountered on BiosVersion section.", + _("The savestate data is invalid or corrupted.") + ); + } + + FreezeBios(); + m_sectid++; + } + break; + + case FreezeId_Memory: + { + FreezeTag( "MainMemory" ); + + int sectlen = MainMemorySizeInBytes; + Freeze( sectlen ); + if( sectlen != MainMemorySizeInBytes ) + { + throw Exception::BadSavedState( wxEmptyString, + L"Invalid size encountered on MainMemory section.", + _("The savestate data is invalid or corrupted.") + ); + } + + FreezeMainMemory(); + m_sectid++; + } + break; + + case FreezeId_Registers: + { + FreezeTag( "HardwareRegisters" ); + int seekpos = m_idx; + int sectsize; + Freeze( sectsize ); + + FreezeRegisters(); + + int realsectsize = m_idx - seekpos; + if( IsSaving() ) + { + // write back the section length... + *((u32*)m_memory.GetPtr(seekpos)) = realsectsize - 4; + } + else // IsLoading!! + { + if( sectsize != realsectsize ) // if they don't match then we have a problem, jim. + { + throw Exception::BadSavedState( wxEmptyString, + L"Invalid size encountered on HardwareRegisters section.", + _("The savestate data is invalid or corrupted.") + ); + } + } + } + break; + + case FreezeId_Plugin: + { + FreezeTag( "Plugin" ); + int seekpos = m_idx; + int sectsize; + Freeze( sectsize ); + + Freeze( m_pid ); + g_plugins->Freeze( (PluginsEnum_t)m_pid, *this ); + + int realsectsize = m_idx - seekpos; + if( IsSaving() ) + { + // write back the section length... + *((u32*)m_memory.GetPtr(seekpos)) = realsectsize - 4; + } + else + { + if( sectsize != realsectsize ) // if they don't match then we have a problem, jim. + { + throw Exception::BadSavedState( wxEmptyString, + L"Invalid size encountered on Plugin section.", + _("The savestate data is invalid or corrupted.") + ); + } + } + + + // following increments only affect Saving mode, are ignored by Loading mode. + m_pid++; + if( m_pid > PluginId_Count ) + m_sectid++; + } + break; + + case FreezeId_Unknown: + default: + if( IsSaving() ) + m_sectid = FreezeId_End; + else + { + // 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::Notice( + "Warning: Unknown tag encountered while loading savestate; going to ignore it!\n" + "\tTagname: %s, Size: %d", m_tagspace, size + ); + m_idx += size; + } + break; } + + return true; } - -gzSavingState::gzSavingState( const wxString& filename ) : - gzBaseStateInfo( "Saving state to: ", filename ) +void SaveStateBase::FreezeAll() { - m_file = gzopen(filename.ToAscii().data(), "wb"); - if( m_file == NULL ) - throw Exception::FileNotFound(); + m_sectid = (int)FreezeId_End+1; + m_pid = PluginId_GS; - gzsetparams( m_file, Z_BEST_SPEED, Z_DEFAULT_STRATEGY ); - Freeze( m_version ); -} - - -gzLoadingState::gzLoadingState( const wxString& filename ) : - gzBaseStateInfo( "Loading state from: ", filename ) -{ - m_file = gzopen(filename.ToAscii().data(), "rb"); - if( m_file == NULL ) - throw Exception::FileNotFound(); - - gzread( m_file, &m_version, 4 ); - - if( (m_version >> 16) != (g_SaveVersion >> 16) ) - { - Console::Error( - "Savestate load aborted:\n" - "\tUnknown or invalid savestate identifier, either from a (very!) old version of\n" - "\tPcsx2, or the file is corrupted" - ); - throw Exception::UnsupportedStateVersion( m_version ); - } - else if( m_version > g_SaveVersion ) - { - Console::Error( - "Savestate load aborted:\n" - "\tThe savestate was created with a newer version of Pcsx2. I don't know how to load it!" ); - throw Exception::UnsupportedStateVersion( m_version ); - } -} - -gzLoadingState::~gzLoadingState() { } - - -void gzSavingState::FreezeMem( void* data, int size ) -{ - gzwrite( m_file, data, size ); -} - -void gzLoadingState::FreezeMem( void* data, int size ) -{ - if( gzread( m_file, data, size ) != size ) - throw Exception::BadSavedState( m_filename ); -} - -void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) ) -{ - freezeData fP = { 0, NULL }; - Console::WriteLn( "\tSaving %s", name ); - - FreezeTag( name ); - - if (freezer(FREEZE_SIZE, &fP) == -1) - throw Exception::FreezePluginFailure( name, "saving" ); - - Freeze( fP.size ); - if( fP.size == 0 ) return; - - SafeArray buffer( fP.size ); - fP.data = buffer.GetPtr(); - - if(freezer(FREEZE_SAVE, &fP) == -1) - throw Exception::FreezePluginFailure( name, "saving" ); - - FreezeMem( fP.data, fP.size ); -} - -void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) ) -{ - freezeData fP = { 0, NULL }; - Console::WriteLn( "\tLoading %s", name ); - - FreezeTag( name ); - Freeze( fP.size ); - if( fP.size == 0 ) return; - - SafeArray buffer( fP.size ); - fP.data = buffer.GetPtr(); - - FreezeMem( fP.data, fP.size ); - - if(freezer(FREEZE_LOAD, &fP) == -1) - throw Exception::FreezePluginFailure( name, "loading" ); + while( FreezeSection() ) ; } ////////////////////////////////////////////////////////////////////////////////// // uncompressed to/from memory state saves implementation -memBaseStateInfo::memBaseStateInfo( SafeArray& memblock, const char* msg ) : - SaveState( msg, L"Memory") -, m_memory( memblock ) -, m_idx( 0 ) -{ - // Always clear the MTGS thread state. - mtgsWaitGS(); -} - -memSavingState::memSavingState( SafeArray& save_to ) : memBaseStateInfo( save_to, "Saving state to: " ) +memSavingState::memSavingState( SafeArray& save_to ) : + SaveStateBase( save_to ) { save_to.ChunkSize = ReallocThreshold; save_to.MakeRoomFor( MemoryBaseAllocSize ); } -// Saving of state data to a memory buffer +// Saving of state data void memSavingState::FreezeMem( void* data, int size ) { const int end = m_idx+size; @@ -315,14 +358,14 @@ void memSavingState::FreezeMem( void* data, int size ) dest[m_idx] = *src; } -memLoadingState::memLoadingState(SafeArray& load_from ) : - memBaseStateInfo( load_from, "Loading state from: " ) +memLoadingState::memLoadingState( const SafeArray& load_from ) : + SaveStateBase( const_cast&>(load_from) ) { } memLoadingState::~memLoadingState() { } -// Loading of state data from a memory buffer... +// Loading of state data void memLoadingState::FreezeMem( void* data, int size ) { const int end = m_idx+size; @@ -332,45 +375,3 @@ void memLoadingState::FreezeMem( void* data, int size ) for( ; m_idx m_memory.GetSizeInBytes() ) - { - assert(0); - throw Exception::BadSavedState( L"memory"); - } - - fP.data = ((s8*)m_memory.GetPtr()) + m_idx; - if(freezer(FREEZE_LOAD, &fP) == -1) - throw Exception::FreezePluginFailure( name, "loading" ); - - m_idx += fP.size; -} diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 1da2ff747d..22eb3e8100 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -13,8 +13,7 @@ * If not, see . */ -#ifndef _SAVESTATE_H_ -#define _SAVESTATE_H_ +#pragma once // This shouldn't break Win compiles, but it does. #ifdef __LINUX__ @@ -28,25 +27,53 @@ // 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 = 0x8b410001; +static const u32 g_SaveVersion = 0x8b410002; // 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 ); -// This class provides the base API for both loading and saving savestates. -// Normally you'll want to use one of the four "functional" derived classes rather -// than this class directly: gzLoadingState, gzSavingState (gzipped disk-saved + +enum FreezeSectionId +{ + FreezeId_End, + + FreezeId_Memory, + FreezeId_Registers, + + // 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_Plugin, + + // anything here and beyond we can skip, with a warning + FreezeId_Unknown, +}; + +// -------------------------------------------------------------------------------------- +// SaveStateBase class +// -------------------------------------------------------------------------------------- +// Provides the base API for both loading and saving savestates. Normally you'll want to +// use one of the four "functional" derived classes rather than this class directly: gzLoadingState, gzSavingState (gzipped disk-saved // states), and memLoadingState, memSavingState (uncompressed memory states). -class SaveState +class SaveStateBase { protected: + SafeArray& m_memory; + char m_tagspace[32]; + u32 m_version; // version of the savestate being loaded. - SafeArray m_tagspace; + + int m_idx; // current read/write index of the allocation + int m_sectid; + int m_pid; + + bool m_DidBios; public: - SaveState( const char* msg, const wxString& destination ); - virtual ~SaveState() { } + SaveStateBase( SafeArray& memblock ); + virtual ~SaveStateBase() { } static wxString GetFilename( int slot ); @@ -78,14 +105,28 @@ public: FreezeMem( &data, sizeof( T ) - sizeOfNewStuff ); } + void PrepBlock( int size ); + + u8* GetBlockPtr() + { + return &m_memory[m_idx]; + } + + void CommitBlock( int size ) + { + m_idx += size; + } + + bool FreezeSection(); + // 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 // position). void FreezeTag( const char* src ); - // Loads or saves a plugin. Plugin name is for console logging purposes. - virtual void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )=0; + // Returns true if this object is a StateLoading type object. + bool IsLoading() const { return !IsSaving(); } // Loads or saves a memory block. virtual void FreezeMem( void* data, int size )=0; @@ -93,18 +134,18 @@ public: // Returns true if this object is a StateSaving type object. virtual bool IsSaving() const=0; - // Returns true if this object is a StateLoading type object. - bool IsLoading() const { return !IsSaving(); } - - // note: gsFreeze() needs to be public because of the GSState recorder. - public: - virtual void gsFreeze(); + // note: gsFreeze() needs to be public because of the GSState recorder. + void gsFreeze(); protected: // Load/Save functions for the various components of our glorious emulator! + void FreezeBios(); + void FreezeMainMemory(); + void FreezeRegisters(); + void rcntFreeze(); void vuMicroFreeze(); void vif0Freeze(); @@ -125,57 +166,11 @@ protected: }; -///////////////////////////////////////////////////////////////////////////////// -// Class Declarations for Savestates using zlib +// -------------------------------------------------------------------------------------- +// Saving and Loading Specialized Implementations... +// -------------------------------------------------------------------------------------- -class gzBaseStateInfo : public SaveState -{ -protected: - const wxString m_filename; - gzFile m_file; // used for reading/writing disk saves - -public: - gzBaseStateInfo( const char* msg, const wxString& filename ); - - virtual ~gzBaseStateInfo(); -}; - -class gzSavingState : public gzBaseStateInfo -{ -public: - virtual ~gzSavingState() {} - gzSavingState( const wxString& filename ) ; - void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); - void FreezeMem( void* data, int size ); - bool IsSaving() const { return true; } -}; - -class gzLoadingState : public gzBaseStateInfo -{ -public: - virtual ~gzLoadingState(); - gzLoadingState( const wxString& filename ); - - void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); - void FreezeMem( void* data, int size ); - bool IsSaving() const { return false; } - bool Finished() const { return !!gzeof( m_file ); } -}; - -////////////////////////////////////////////////////////////////////////////////// - -class memBaseStateInfo : public SaveState -{ -protected: - SafeArray& m_memory; - int m_idx; // current read/write index of the allocation - -public: - virtual ~memBaseStateInfo() { } - memBaseStateInfo( SafeArray& memblock, const char* msg ); -}; - -class memSavingState : public memBaseStateInfo +class memSavingState : public SaveStateBase { protected: static const int ReallocThreshold = 0x200000; // 256k reallocation block size. @@ -185,22 +180,21 @@ public: virtual ~memSavingState() { } memSavingState( SafeArray& save_to ); - void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); // Saving of state data to a memory buffer void FreezeMem( void* data, int size ); bool IsSaving() const { return true; } }; -class memLoadingState : public memBaseStateInfo +class memLoadingState : public SaveStateBase { public: virtual ~memLoadingState(); - memLoadingState(SafeArray& load_from ); + memLoadingState( const SafeArray& load_from ); - void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); // Loading of state data from a memory buffer... void FreezeMem( void* data, int size ); bool IsSaving() const { return false; } + bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); } }; namespace StateRecovery @@ -209,10 +203,7 @@ namespace StateRecovery extern void Recover(); extern void SaveToFile( const wxString& file ); extern void SaveToSlot( uint num ); - extern void MakeGsOnly(); extern void MakeFull(); extern void Clear(); } -#endif - diff --git a/pcsx2/Sif.cpp b/pcsx2/Sif.cpp index 98b4aa7f49..e43fa45fc8 100644 --- a/pcsx2/Sif.cpp +++ b/pcsx2/Sif.cpp @@ -499,7 +499,7 @@ __forceinline void dmaSIF2() } -void SaveState::sifFreeze() +void SaveStateBase::sifFreeze() { FreezeTag("SIFdma"); diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index d0c11948bd..bc62fa32b7 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -645,7 +645,7 @@ void SIO_FORCEINLINE sioInterrupt() { psxHu32(0x1070)|=0x80; } -void SaveState::sioFreeze() +void SaveStateBase::sioFreeze() { // CRCs for memory cards. u64 m_mcdCRCs[2][8]; diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 7ef9d4b4c6..496a2909e5 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -39,25 +39,16 @@ Pcsx2Config EmuConfig; // disable all session overrides by default... SessionOverrideFlags g_Session = {false}; -CoreEmuThread* g_EmuThread; -bool sysInitialized = false; - -// ----------------------------------------------------------------------- // This function should be called once during program execution. -// void SysDetect() { using namespace Console; -#ifdef __LINUX__ - // Haven't rigged up getting the svn version yet... --arcum42 - Notice("PCSX2 %d.%d.%d - compiled on " __DATE__, PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo); -#else Notice("PCSX2 %d.%d.%d.r%d %s - compiled on " __DATE__, PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo, SVN_REV, SVN_MODS ? "(modded)" : "" ); -#endif + Notice("Savestate version: %x", g_SaveVersion); cpudetectInit(); @@ -82,49 +73,51 @@ void SysDetect() x86caps.Flags, x86caps.Flags2, x86caps.EFlags ) ); + + wxArrayString features[2]; // 2 lines, for readability! + + if( x86caps.hasMultimediaExtensions ) features[0].Add( L"MMX" ); + if( x86caps.hasStreamingSIMDExtensions ) features[0].Add( L"SSE" ); + if( x86caps.hasStreamingSIMD2Extensions ) features[0].Add( L"SSE2" ); + if( x86caps.hasStreamingSIMD3Extensions ) features[0].Add( L"SSE3" ); + if( x86caps.hasSupplementalStreamingSIMD3Extensions ) features[0].Add( L"SSSE3" ); + if( x86caps.hasStreamingSIMD4Extensions ) features[0].Add( L"SSE4.1" ); + if( x86caps.hasStreamingSIMD4Extensions2 ) features[0].Add( L"SSE4.2" ); - WriteLn( "Features:" ); - WriteLn( - "\t%sDetected MMX\n" - "\t%sDetected SSE\n" - "\t%sDetected SSE2\n" - "\t%sDetected SSE3\n" - "\t%sDetected SSSE3\n" - "\t%sDetected SSE4.1\n" - "\t%sDetected SSE4.2\n", - x86caps.hasMultimediaExtensions ? "" : "Not ", - x86caps.hasStreamingSIMDExtensions ? "" : "Not ", - x86caps.hasStreamingSIMD2Extensions ? "" : "Not ", - x86caps.hasStreamingSIMD3Extensions ? "" : "Not ", - x86caps.hasSupplementalStreamingSIMD3Extensions ? "" : "Not ", - x86caps.hasStreamingSIMD4Extensions ? "" : "Not ", - x86caps.hasStreamingSIMD4Extensions2 ? "" : "Not " - ); + if( x86caps.hasMultimediaExtensionsExt ) features[1].Add( L"MMX2 " ); + if( x86caps.has3DNOWInstructionExtensions ) features[1].Add( L"3DNOW " ); + if( x86caps.has3DNOWInstructionExtensionsExt ) features[1].Add( L"3DNOW2" ); + if( x86caps.hasStreamingSIMD4ExtensionsA ) features[1].Add( L"SSE4a " ); - if ( x86caps.VendorName[0] == 'A' ) //AMD cpu - { - WriteLn( " Extended AMD Features:" ); - WriteLn( - "\t%sDetected MMX2\n" - "\t%sDetected 3DNOW\n" - "\t%sDetected 3DNOW2\n" - "\t%sDetected SSE4a\n", - x86caps.hasMultimediaExtensionsExt ? "" : "Not ", - x86caps.has3DNOWInstructionExtensions ? "" : "Not ", - x86caps.has3DNOWInstructionExtensionsExt ? "" : "Not ", - x86caps.hasStreamingSIMD4ExtensionsA ? "" : "Not " - ); - } + wxString result[2]; + JoinString( result[0], features[0], L".. " ); + JoinString( result[1], features[1], L".. " ); + + WriteLn( L"Features Detected:\n\t" + result[0] + (result[1].IsEmpty() ? wxEmptyString : (L"\n\t" + result[1])) + L"\n" ); + + //if ( x86caps.VendorName[0] == 'A' ) //AMD cpu Console::ClearColor(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Allocates memory for all PS2 systems. -bool SysAllocateMem() +// returns the translated error message for the Virtual Machine failing to allocate! +static wxString GetMemoryErrorVM() { - // Allocate PS2 system ram space (required by interpreters and recompilers both) + return pxE( ".Popup Error:EmuCore::MemoryForVM", + L"PCSX2 is unable to allocate memory needed for the PS2 virtual machine. " + L"Close out some memory hogging background tasks and try again." + ); +} +EmuCoreAllocations::EmuCoreAllocations() +{ + Console::Status( "Initializing PS2 virtual machine..." ); + + RecSuccess_EE = false; + RecSuccess_IOP = false; + RecSuccess_VU0 = false; + RecSuccess_VU1 = false; + try { vtlb_Core_Alloc(); @@ -132,147 +125,116 @@ bool SysAllocateMem() psxMemAlloc(); vuMicroMemAlloc(); } - catch( Exception::OutOfMemory& ) + // ---------------------------------------------------------------------------- + catch( Exception::OutOfMemory& ex ) { - // TODO : Should this error be handled here or allowed to be handled by the main - // exception handler? + wxString newmsg( ex.UserMsg() + L"\n\n" + GetMemoryErrorVM() ); + ex.UserMsg() = newmsg; + CleanupMess(); + throw; + } + catch( std::bad_alloc& ex ) + { + CleanupMess(); - // Failures on the core initialization of memory is bad, since it means the emulator is - // completely non-functional. + // re-throw std::bad_alloc as something more friendly. - //Msgbox::Alert( "Failed to allocate memory needed to run pcsx2.\n\nError: %s", ex.cMessage() ); - SysShutdownMem(); - return false; + throw Exception::OutOfMemory( + wxsFormat( // Diagnostic (english) + L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n" + L"Error Details: " + wxString::FromUTF8( ex.what() ) + ), + + GetMemoryErrorVM() // translated + ); } - return true; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Allocates memory for all recompilers, and force-disables any recs that fail to initialize. -// This should be done asap, since the recompilers tend to demand a lot of system resources, -// and prefer to have those resources at specific address ranges. The sooner memory is -// allocated, the better. -// -// Returns FALSE on *critical* failure (GUI should issue a msg and exit). -void SysAllocateDynarecs() -{ - // Attempt to initialize the recompilers. - // Most users want to use recs anyway, and if they are using interpreters I don't think the - // extra few megs of allocation is going to be an issue. + Console::Status( "Allocating memory for recompilers..." ); try { - // R5900 and R3000a must be rec-enabled together for now so if either fails they both fail. recCpu.Allocate(); - psxRec.Allocate(); + RecSuccess_EE = true; } - catch( Exception::BaseException& ) + catch( Exception::BaseException& ex ) { - // TODO : Fix this message. It should respond according to the user's - // currently configured recompiler.interpreter options, for example. - - /*Msgbox::Alert( - "The EE/IOP recompiler failed to initialize with the following error:\n\n" - "%s" - "\n\nThe EE/IOP interpreter will be used instead (slow!).", params - ex.cMessage() - );*/ - - g_Session.ForceDisableEErec = true; - + Console::Error( L"EE Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() ); recCpu.Shutdown(); + } + + try + { + psxRec.Allocate(); + RecSuccess_IOP = true; + } + catch( Exception::BaseException& ex ) + { + Console::Error( L"IOP Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() ); psxRec.Shutdown(); } + // hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :( + try { VU0micro::recAlloc(); + RecSuccess_VU0 = true; } - catch( Exception::BaseException& ) + catch( Exception::BaseException& ex ) { - - // TODO : Fix this message. It should respond according to the user's - // currently configured recompiler.interpreter options, for example. -/* - Msgbox::Alert( - "The VU0 recompiler failed to initialize with the following error:\n\n" - "%s" - "\n\nThe VU0 interpreter will be used for this session (may slow down some games).", params - ex.cMessage() - ); -*/ - - g_Session.ForceDisableVU0rec = true; + Console::Error( L"VU0 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() ); VU0micro::recShutdown(); } try { VU1micro::recAlloc(); + RecSuccess_VU1 = true; } - catch( Exception::BaseException& ) + catch( Exception::BaseException& ex ) { - - // TODO : Fix this message. It should respond according to the user's - // currently configured recompiler.interpreter options, for example. -/* - Msgbox::Alert( - "The VU1 recompiler failed to initialize with the following error:\n\n" - "%s" - "\n\nThe VU1 interpreter will be used for this session (will slow down most games).", params - ex.cMessage() - ); -*/ - - g_Session.ForceDisableVU1rec = true; + Console::Error( L"VU1 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() ); VU1micro::recShutdown(); } - // If both VUrecs failed, then make sure the SuperVU is totally closed out: - if( !CHECK_VU0REC && !CHECK_VU1REC) + // If both VUrecs failed, then make sure the SuperVU is totally closed out, because it + // actually initializes everything once and then shares it between both VU recs. + if( !RecSuccess_VU0 && !RecSuccess_VU1 ) + SuperVUDestroy( -1 ); +} + +void EmuCoreAllocations::CleanupMess() throw() +{ + try + { + // Special SuperVU "complete" terminator. SuperVUDestroy( -1 ); + VU1micro::recShutdown(); + VU0micro::recShutdown(); + + psxRec.Shutdown(); + recCpu.Shutdown(); + + vuMicroMemShutdown(); + psxMemShutdown(); + memShutdown(); + vtlb_Core_Shutdown(); + } + DESTRUCTOR_CATCHALL } -// This should be called last thing before PCSX2 exits. -// -void SysShutdownMem() +EmuCoreAllocations::~EmuCoreAllocations() throw() { - if( sysInitialized ) - SysShutdown(); - - vuMicroMemShutdown(); - psxMemShutdown(); - memShutdown(); - vtlb_Core_Shutdown(); + CleanupMess(); } -// This should generally be called right before calling SysShutdownMem(), although you can optionally -// use it in conjunction with SysAllocDynarecs to allocate/free the dynarec resources on the fly (as -// risky as it might be, since dynarecs could very well fail on the second attempt). -void SysShutdownDynarecs() +bool EmuCoreAllocations::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const { - // Special SuperVU "complete" terminator. - SuperVUDestroy( -1 ); - - VU0micro::recShutdown(); - VU1micro::recShutdown(); - - psxRec.Shutdown(); - recCpu.Shutdown(); -} - -void SysShutdown() -{ - sysInitialized = false; - - Console::Status( "Shutting down PS2 virtual machine..." ); - SysEndExecution(); - safe_delete( g_plugins ); - - SysShutdownDynarecs(); - SysShutdownMem(); + return (recOpts.EnableEE && !RecSuccess_EE) || + (recOpts.EnableIOP && !RecSuccess_IOP) || + (recOpts.EnableVU0 && !RecSuccess_VU0) || + (recOpts.EnableVU1 && !RecSuccess_VU1); } // Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition. @@ -291,63 +253,6 @@ void SysClearExecutionCache() vuMicroCpuReset(); } -bool EmulationInProgress() -{ - return (g_EmuThread != NULL) && g_EmuThread->IsRunning(); -} - -// Executes the specified cdvd source and optional elf file. This command performs a -// full closure of any existing VM state and starts a fresh VM with the requested -// sources. -void SysExecute( CoreEmuThread* newThread, CDVD_SourceType cdvdsrc ) -{ - wxASSERT( newThread != NULL ); - safe_delete( g_EmuThread ); - - CDVDsys_ChangeSource( cdvdsrc ); - g_EmuThread = newThread; - g_EmuThread->Resume(); -} - -// Executes the emulator using a saved/existing virtual machine state and currently -// configured CDVD source device. -// Debug assertions: -void SysExecute( CoreEmuThread* newThread ) -{ - wxASSERT( newThread != NULL ); - safe_delete( g_EmuThread ); - - g_EmuThread = newThread; - g_EmuThread->Resume(); -} - -// Once execution has been ended no action can be taken on the Virtual Machine (such as -// saving states). No assertions or exceptions. -void SysEndExecution() -{ - safe_delete( g_EmuThread ); - GetPluginManager().Shutdown(); -} - -void SysSuspend() -{ - if( g_EmuThread != NULL ) - g_EmuThread->Suspend(); -} - -void SysResume() -{ - if( g_EmuThread != NULL ) - g_EmuThread->Resume(); -} - - -void SysRestorableReset() -{ - if( !EmulationInProgress() ) return; - StateRecovery::MakeFull(); -} - // The calling function should trap and handle exceptions as needed. // Exceptions: // Exception::StateLoadError - thrown when a fully recoverable exception ocurred. The @@ -355,35 +260,19 @@ void SysRestorableReset() // // Any other exception means the Virtual Memory state is indeterminate and probably // invalid. -void SysLoadState( const wxString& file ) +void SysLoadState( const wxString& srcfile ) { + SafeArray buf; + memLoadingState joe( buf ); // this could throw n StateLoadError. + // we perform a full backup to memory first so that we can restore later if the // load fails. fixme: should this be made optional? It could have significant // speed impact on state loads on slower machines with low ram. >_< StateRecovery::MakeFull(); - gzLoadingState joe( file ); // this'll throw an StateLoadError. - - GetPluginManager().Open(); - cpuReset(); SysClearExecutionCache(); - + cpuReset(); joe.FreezeAll(); - - if( GSsetGameCRC != NULL ) - GSsetGameCRC(ElfCRC, g_ZeroGSOptions); -} - -void SysReset() -{ - Console::Status( "Resetting PS2 virtual machine..." ); - - SysEndExecution(); - StateRecovery::Clear(); - ElfCRC = 0; - - // Note : No need to call cpuReset() here. It gets called automatically before the - // emulator resumes execution. } // Maps a block of memory for use as a recompiled code buffer, and ensures that the @@ -414,29 +303,3 @@ u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller) } return Mem; } - -// Ensures existence of necessary folders, and performs error handling if the -// folders fail to create. -static void InitFolderStructure() -{ - -} - -// Returns FALSE if the core/recompiler memory allocations failed. -bool SysInit() -{ - if( sysInitialized ) return true; - sysInitialized = true; - - SysDetect(); - - PCSX2_MEM_PROTECT_BEGIN(); - Console::Status( "Initializing PS2 virtual machine..." ); - if( !SysAllocateMem() ) - return false; // critical memory allocation failure; - - SysAllocateDynarecs(); - PCSX2_MEM_PROTECT_END(); - - return true; -} diff --git a/pcsx2/System.h b/pcsx2/System.h index 7301bffbef..96127805be 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -23,36 +23,46 @@ static const int PCSX2_VersionHi = 0; static const int PCSX2_VersionMid = 9; static const int PCSX2_VersionLo = 7; - class CoreEmuThread; -extern bool SysInit(); +// -------------------------------------------------------------------------------------- +// EmuCoreAllocations class +// -------------------------------------------------------------------------------------- +class EmuCoreAllocations +{ +public: + // This set of booleans defaults to false and are only set TRUE if the corresponding + // recompilers succeeded to initialize/allocate. The host application should honor + // these booleans when selecting between recompiler or interpreter, since recompilers + // will fail to operate if these are "false." + + bool RecSuccess_EE:1, + RecSuccess_IOP:1, + RecSuccess_VU0:1, + RecSuccess_VU1:1; + +protected: + +public: + EmuCoreAllocations(); + virtual ~EmuCoreAllocations() throw(); + + bool HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const; + +protected: + void CleanupMess() throw(); +}; + + extern void SysDetect(); // Detects cpu type and fills cpuInfo structs. -extern void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers. - -extern void SysExecute( CoreEmuThread* newThread ); -extern void SysExecute( CoreEmuThread* newThread, CDVD_SourceType cdvdsrc ); -extern void SysEndExecution(); - -extern void SysSuspend(); -extern void SysResume(); - -extern bool SysAllocateMem(); // allocates memory for all PS2 systems; returns FALSe on critical error. -extern void SysAllocateDynarecs(); // allocates memory for all dynarecs, and force-disables any failures. -extern void SysShutdownDynarecs(); -extern void SysShutdownMem(); -extern void SysShutdown(); extern void SysLoadState( const wxString& file ); -extern void SysRestorableReset(); // Saves the current emulation state prior to spu reset. extern void SysClearExecutionCache(); // clears recompiled execution caches! extern u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed"); extern void vSyncDebugStuff( uint frame ); -extern CoreEmuThread* g_EmuThread; - ////////////////////////////////////////////////////////////////////////////////////////// // #ifdef __LINUX__ diff --git a/pcsx2/VUmicroMem.cpp b/pcsx2/VUmicroMem.cpp index fc76153743..1d9aa6f3a2 100644 --- a/pcsx2/VUmicroMem.cpp +++ b/pcsx2/VUmicroMem.cpp @@ -138,7 +138,7 @@ void vuMicroMemReset() VU1.vifRegs = vif1Regs; } -void SaveState::vuMicroFreeze() +void SaveStateBase::vuMicroFreeze() { FreezeTag( "vuMicro" ); diff --git a/pcsx2/VifDma.cpp b/pcsx2/VifDma.cpp index c2891e6011..a545e9db69 100644 --- a/pcsx2/VifDma.cpp +++ b/pcsx2/VifDma.cpp @@ -1658,7 +1658,7 @@ void vif0Reset() vif0Regs->stat &= ~VIF0_STAT_FQC; // FQC=0 } -void SaveState::vif0Freeze() +void SaveStateBase::vif0Freeze() { FreezeTag("VIFdma"); @@ -2803,7 +2803,7 @@ void vif1Reset() vif1Regs->stat &= ~VIF1_STAT_FQC; // FQC=0 } -void SaveState::vif1Freeze() +void SaveStateBase::vif1Freeze() { Freeze(vif1); diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 0b99523804..f68bc871bc 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -30,6 +30,12 @@ class IniInterface; +BEGIN_DECLARE_EVENT_TYPES() + DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 ) + DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 ) + DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 ) +END_DECLARE_EVENT_TYPES() + // ------------------------------------------------------------------------ // All Menu Options for the Main Window! :D // ------------------------------------------------------------------------ @@ -119,6 +125,14 @@ enum MenuIdentifiers MenuId_Debug_Usermode, }; +enum DialogIdentifiers +{ + DialogId_CoreSettings = 0x800, + DialogId_BiosSelector, + DialogId_LogOptions, + DialogId_About, +}; + ////////////////////////////////////////////////////////////////////////////////////////// // ScopedWindowDisable // @@ -197,53 +211,89 @@ struct AppImageIds } Toolbars; }; -////////////////////////////////////////////////////////////////////////////////////////// -// -class pxAppTraits : public wxGUIAppTraits + +struct MsgboxEventResult { -#ifdef __WXDEBUG__ -public: - virtual bool ShowAssertDialog(const wxString& msg); - -protected: - virtual wxString GetAssertStackTrace(); -#endif + Semaphore WaitForMe; + int result; + MsgboxEventResult() : + WaitForMe(), result( 0 ) + { + } }; -////////////////////////////////////////////////////////////////////////////////////////// -// +// -------------------------------------------------------------------------------------- +// Pcsx2App - main wxApp class +// -------------------------------------------------------------------------------------- + class Pcsx2App : public wxApp { protected: + wxImageList m_ConfigImages; + + wxScopedPtr m_ToolbarImages; + wxScopedPtr m_Bitmap_Logo; + + wxScopedPtr m_CoreAllocs; + +public: + wxScopedPtr m_CorePlugins; + wxScopedPtr m_CoreThread; + +protected: + // Note: Pointers to frames should not be scoped because wxWidgets handles deletion + // of these objects internally. MainEmuFrame* m_MainFrame; ConsoleLogFrame* m_ProgramLogBox; - wxBitmap* m_Bitmap_Logo; - wxImageList m_ConfigImages; - bool m_ConfigImagesAreLoaded; - - wxImageList* m_ToolbarImages; // dynamic (pointer) to allow for large/small redefinition. - AppImageIds m_ImageId; + bool m_ConfigImagesAreLoaded; + AppImageIds m_ImageId; public: Pcsx2App(); virtual ~Pcsx2App(); - wxFrame* GetMainWindow() const; + void ReloadPlugins(); - bool OnInit(); - int OnExit(); - void CleanUp(); + void ApplySettings( const AppConfig* oldconf = NULL ); + void LoadSettings(); + void SaveSettings(); + + void PostMenuAction( MenuIdentifiers menu_id ) const; + int ThreadedModalDialog( DialogIdentifiers dialogId ); + void Ping() const; - void OnInitCmdLine( wxCmdLineParser& parser ); - bool OnCmdLineParsed( wxCmdLineParser& parser ); - bool OnCmdLineError( wxCmdLineParser& parser ); bool PrepForExit(); + + // Executes the emulator using a saved/existing virtual machine state and currently + // configured CDVD source device. + // Debug assertions: + void SysExecute(); + void SysExecute( CDVD_SourceType cdvdsrc ); -#ifdef __WXDEBUG__ - void OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ); -#endif + void SysResume() + { + if( !m_CoreThread ) return; + m_CoreThread->Resume(); + } + + void SysSuspend() + { + if( !m_CoreThread ) return; + m_CoreThread->Suspend(); + } + + void SysReset() + { + m_CoreThread.reset(); + m_CorePlugins.reset(); + } + + bool EmuInProgress() const + { + return m_CoreThread && m_CoreThread->IsRunning(); + } const wxBitmap& GetLogoBitmap(); wxImageList& GetImgList_Config(); @@ -253,16 +303,24 @@ public: MainEmuFrame& GetMainFrame() const { + wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) ); wxASSERT( m_MainFrame != NULL ); return *m_MainFrame; } - void PostMenuAction( MenuIdentifiers menu_id ) const; - void Ping() const; + // -------------------------------------------------------------------------- + // Overrides of wxApp virtuals: + // -------------------------------------------------------------------------- + bool OnInit(); + int OnExit(); - void ApplySettings( const AppConfig& newconf ); - void LoadSettings(); - void SaveSettings(); + void OnInitCmdLine( wxCmdLineParser& parser ); + bool OnCmdLineParsed( wxCmdLineParser& parser ); + bool OnCmdLineError( wxCmdLineParser& parser ); + +#ifdef __WXDEBUG__ + void OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ); +#endif // ---------------------------------------------------------------------------- // Console / Program Logging Helpers @@ -303,7 +361,9 @@ protected: void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const; + void OnReloadPlugins( wxCommandEvent& evt ); void OnSemaphorePing( wxCommandEvent& evt ); + void OnOpenModalDialog( wxCommandEvent& evt ); void OnMessageBox( pxMessageBoxEvent& evt ); void OnEmuKeyDown( wxKeyEvent& evt ); @@ -322,15 +382,18 @@ protected: void OnUnhandledException() { throw; } }; -////////////////////////////////////////////////////////////////////////////////////////// -// + +// -------------------------------------------------------------------------------------- +// AppEmuThread class +// -------------------------------------------------------------------------------------- + class AppEmuThread : public CoreEmuThread { protected: wxKeyEvent m_kevt; public: - AppEmuThread(); + AppEmuThread( PluginManager& plugins ); virtual ~AppEmuThread() { } virtual void Resume(); @@ -340,14 +403,29 @@ protected: sptr ExecuteTask(); }; - - DECLARE_APP(Pcsx2App) +class EntryGuard +{ +public: + int& Counter; + EntryGuard( int& counter ) : Counter( counter ) + { ++Counter; } + + virtual ~EntryGuard() throw() + { --Counter; } + + bool IsReentrant() const { return Counter > 1; } +}; + extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest ); -extern void LoadPlugins(); -extern void InitPlugins(); -extern void OpenPlugins(); +extern void LoadPluginsPassive(); +extern void LoadPluginsImmediate(); +extern void UnloadPlugins(); extern wxRect wxGetDisplayArea(); extern bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos ); + +extern bool HandlePluginError( Exception::PluginError& ex ); +extern bool EmulationInProgress(); + diff --git a/pcsx2/gui/AppAssert.cpp b/pcsx2/gui/AppAssert.cpp index 93c7409413..618c917f32 100644 --- a/pcsx2/gui/AppAssert.cpp +++ b/pcsx2/gui/AppAssert.cpp @@ -68,10 +68,6 @@ static wxString pxGetStackTrace() static __threadlocal bool _reentrant_lock = false; #ifdef __WXDEBUG__ -wxString pxAppTraits::GetAssertStackTrace() -{ - return pxGetStackTrace(); -} // This override of wx's implementation provides thread safe assertion message reporting. If we aren't // on the main gui thread then the assertion message box needs to be passed off to the main gui thread @@ -101,8 +97,8 @@ void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func // make life easier for people using VC++ IDE by using this format, which allows double-click // response times from the Output window... dbgmsg.Printf( L"%s(%d) : assertion failed%s%s: %s", file, line, - (func==NULL) ? L"" : L" in ", - (func==NULL) ? L"" : func, + (func==NULL) ? wxEmptyString : L" in ", + (func==NULL) ? wxEmptyString : func, message.c_str() ); diff --git a/pcsx2/gui/AppConfig.cpp b/pcsx2/gui/AppConfig.cpp index d5ed445936..eeaea5c106 100644 --- a/pcsx2/gui/AppConfig.cpp +++ b/pcsx2/gui/AppConfig.cpp @@ -94,7 +94,10 @@ namespace PathDefs // share with other programs: screenshots, memory cards, and savestates. wxDirName GetDocuments() { - return (wxDirName)g_Conf->GetDefaultDocumentsFolder(); + if( UseAdminMode ) + return (wxDirName)wxGetCwd(); + else + return (wxDirName)Path::Combine( wxStandardPaths::Get().GetDocumentsDir(), wxGetApp().GetAppName() ); } wxDirName GetSnapshots() @@ -155,20 +158,12 @@ namespace PathDefs } }; -wxString AppConfig::GetDefaultDocumentsFolder() -{ - if( UseAdminMode ) - return wxGetCwd(); - else - return Path::Combine( wxStandardPaths::Get().GetDocumentsDir(), wxGetApp().GetAppName() ); -} - const wxDirName& AppConfig::FolderOptions::operator[]( FoldersEnum_t folderidx ) const { switch( folderidx ) { case FolderId_Plugins: return Plugins; - case FolderId_Settings: return Settings; + case FolderId_Settings: return SettingsFolder; case FolderId_Bios: return Bios; case FolderId_Snapshots: return Snapshots; case FolderId_Savestates: return Savestates; @@ -185,7 +180,7 @@ const bool AppConfig::FolderOptions::IsDefault( FoldersEnum_t folderidx ) const switch( folderidx ) { case FolderId_Plugins: return UseDefaultPlugins; - case FolderId_Settings: return UseDefaultSettings; + case FolderId_Settings: return UseDefaultSettingsFolder; case FolderId_Bios: return UseDefaultBios; case FolderId_Snapshots: return UseDefaultSnapshots; case FolderId_Savestates: return UseDefaultSavestates; @@ -207,8 +202,8 @@ void AppConfig::FolderOptions::Set( FoldersEnum_t folderidx, const wxString& src break; case FolderId_Settings: - Settings = src; - UseDefaultSettings = useDefault; + SettingsFolder = src; + UseDefaultSettingsFolder = useDefault; break; case FolderId_Bios: @@ -240,8 +235,9 @@ void AppConfig::FolderOptions::Set( FoldersEnum_t folderidx, const wxString& src } } -////////////////////////////////////////////////////////////////////////////////////////// -// +// -------------------------------------------------------------------------------------- +// Default Filenames +// -------------------------------------------------------------------------------------- namespace FilenameDefs { wxFileName GetConfig() @@ -290,9 +286,14 @@ wxString AppConfig::FullpathTo( PluginsEnum_t pluginidx ) const return Path::Combine( Folders.Plugins, BaseFilenames[pluginidx] ); } -wxString AppConfig::FullPathToConfig() const +wxDirName GetSettingsFolder() { - return g_Conf->Folders.Settings.Combine( FilenameDefs::GetConfig() ).GetFullPath(); + return UseDefaultSettingsFolder ? PathDefs::GetSettings() : SettingsFolder; +} + +wxString GetSettingsFilename() +{ + return GetSettingsFolder().Combine( FilenameDefs::GetConfig() ).GetFullPath(); } @@ -349,7 +350,8 @@ void AppConfig::LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash ) ini.GetConfig().Write( L"Timestamp", timestamp_now );*/ ini.Entry( L"UseAdminMode", UseAdminMode, false ); - ini.Entry( L"SettingsPath", Folders.Settings, PathDefs::GetSettings() ); + ini.Entry( L"UseDefaultSettingsFolder", UseDefaultSettingsFolder, true ); + ini.Entry( L"SettingsFolder", SettingsFolder, PathDefs::GetSettings() ); ini.Flush(); } @@ -404,40 +406,6 @@ void AppConfig::LoadSave( IniInterface& ini ) ini.Flush(); } -// ------------------------------------------------------------------------ -// Performs necessary operations to ensure that the current g_Conf settings (and other config-stored -// globals) are applied to the pcsx2 main window and primary emulation subsystems (if active). -// -void AppConfig::Apply() -{ - Folders.ApplyDefaults(); - - // Ensure existence of necessary documents folders. Plugins and other parts - // of PCSX2 rely on them. - - Folders.MemoryCards.Mkdir(); - Folders.Savestates.Mkdir(); - Folders.Snapshots.Mkdir(); - - EmuOptions.BiosFilename = FullpathToBios(); - - // Update the compression attribute on the Memcards folder. - // Memcards generally compress very well via NTFS compression. - - NTFS_CompressFile( Folders.MemoryCards.ToString(), McdEnableNTFS ); - - { - wxDoNotLogInThisScope please; - if( !i18n_SetLanguage( LanguageId ) ) - { - if( !i18n_SetLanguage( wxLANGUAGE_DEFAULT ) ) - { - i18n_SetLanguage( wxLANGUAGE_ENGLISH ); - } - } - } -} - // ------------------------------------------------------------------------ AppConfig::ConsoleLogOptions::ConsoleLogOptions() : Visible( false ) @@ -463,7 +431,6 @@ void AppConfig::ConsoleLogOptions::LoadSave( IniInterface& ini, const wxChar* lo void AppConfig::FolderOptions::ApplyDefaults() { if( UseDefaultPlugins ) Plugins = PathDefs::GetPlugins(); - if( UseDefaultSettings ) Settings = PathDefs::GetSettings(); if( UseDefaultBios ) Bios = PathDefs::GetBios(); if( UseDefaultSnapshots ) Snapshots = PathDefs::GetSnapshots(); if( UseDefaultSavestates ) Savestates = PathDefs::GetSavestates(); @@ -475,7 +442,6 @@ void AppConfig::FolderOptions::ApplyDefaults() AppConfig::FolderOptions::FolderOptions() : bitset( 0xffffffff ) , Plugins( PathDefs::GetPlugins() ) -, Settings( PathDefs::GetSettings() ) , Bios( PathDefs::GetBios() ) , Snapshots( PathDefs::GetSnapshots() ) , Savestates( PathDefs::GetSavestates() ) @@ -503,7 +469,6 @@ void AppConfig::FolderOptions::LoadSave( IniInterface& ini ) IniBitBool( UseDefaultLogs ); IniEntry( Plugins ); - IniEntry( Settings ); IniEntry( Bios ); IniEntry( Snapshots ); IniEntry( Savestates ); @@ -552,13 +517,13 @@ void AppConfig_ReloadGlobalSettings( bool overwrite ) PathDefs::GetSettings().Mkdir(); // Allow wx to use our config, and enforces auto-cleanup as well - delete wxConfigBase::Set( OpenFileConfig( g_Conf->FullPathToConfig() ) ); + delete wxConfigBase::Set( OpenFileConfig( GetSettingsFilename() ) ); wxConfigBase::Get()->SetRecordDefaults(); if( !overwrite ) wxGetApp().LoadSettings(); - wxGetApp().ApplySettings( *g_Conf ); + wxGetApp().ApplySettings(); g_Conf->Folders.Logs.Mkdir(); wxString newlogname( Path::Combine( g_Conf->Folders.Logs.ToString(), L"emuLog.txt" ) ); diff --git a/pcsx2/gui/AppConfig.h b/pcsx2/gui/AppConfig.h index 87abb9b7cc..30776fc07a 100644 --- a/pcsx2/gui/AppConfig.h +++ b/pcsx2/gui/AppConfig.h @@ -21,7 +21,12 @@ class IniInterface; class wxFileConfig; -extern bool UseAdminMode; // dictates if the program uses /home/user or /cwd for the program data +extern bool UseAdminMode; // dictates if the program uses /home/user or /cwd for the program data +extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE. +extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the UseAdminMode + +wxDirName GetSettingsFolder(); +wxString GetSettingsFilename(); ////////////////////////////////////////////////////////////////////////////////////////// // Pcsx2 Application Configuration. @@ -33,15 +38,15 @@ public: // ------------------------------------------------------------------------ struct ConsoleLogOptions { - bool Visible; + bool Visible; // if true, DisplayPos is ignored and the console is automatically docked to the main window. - bool AutoDock; + bool AutoDock; // Display position used if AutoDock is false (ignored otherwise) - wxPoint DisplayPosition; - wxSize DisplaySize; + wxPoint DisplayPosition; + wxSize DisplaySize; // Size of the font in points. - int FontSize; + int FontSize; ConsoleLogOptions(); void LoadSave( IniInterface& conf, const wxChar* title ); @@ -59,11 +64,10 @@ public: UseDefaultSavestates:1, UseDefaultMemoryCards:1, UseDefaultLogs:1; - }; }; + BITFIELD_END wxDirName Plugins, - Settings, Bios, Snapshots, Savestates, @@ -156,14 +160,10 @@ public: wxString FullpathToBios() const; wxString FullpathToMcd( uint port, uint slot ) const; wxString FullpathTo( PluginsEnum_t pluginId ) const; - wxString FullPathToConfig() const; void LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash ); - wxString GetDefaultDocumentsFolder(); - protected: - void Apply(); void LoadSave( IniInterface& ini ); void LoadSaveMemcards( IniInterface& ini ); @@ -181,4 +181,4 @@ extern ConfigOverrides OverrideOptions; extern wxFileConfig* OpenFileConfig( const wxString& filename ); extern void AppConfig_ReloadGlobalSettings( bool overwrite = false ); -extern AppConfig* g_Conf; +extern wxScopedPtr g_Conf; diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 89d7429ad6..d2a0bf9f46 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -19,6 +19,9 @@ #include "Plugins.h" #include "Dialogs/ModalPopups.h" +#include "Dialogs/ConfigurationDialog.h" +#include "Dialogs/LogOptionsDialog.h" + #include "Utilities/ScopedPtr.h" #include "Utilities/HashMap.h" @@ -28,15 +31,16 @@ IMPLEMENT_APP(Pcsx2App) -BEGIN_DECLARE_EVENT_TYPES() - DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 ) -END_DECLARE_EVENT_TYPES() +DEFINE_EVENT_TYPE( pxEVT_SemaphorePing ); +DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog ); +DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins ); -DEFINE_EVENT_TYPE( pxEVT_SemaphorePing ) +bool UseAdminMode = false; +wxDirName SettingsFolder; +bool UseDefaultSettingsFolder = true; -bool UseAdminMode = false; -AppConfig* g_Conf = NULL; -ConfigOverrides OverrideOptions; +wxScopedPtr g_Conf; +ConfigOverrides OverrideOptions; namespace Exception { @@ -57,8 +61,8 @@ namespace Exception }; } -AppEmuThread::AppEmuThread() : - CoreEmuThread() +AppEmuThread::AppEmuThread( PluginManager& plugins ) : + CoreEmuThread( plugins ) , m_kevt() { } @@ -127,19 +131,50 @@ void AppEmuThread::StateCheck() } } -static bool HandlePluginError( Exception::PluginError& ex ) +// Executes the emulator using a saved/existing virtual machine state and currently +// configured CDVD source device. +void Pcsx2App::SysExecute() { + SysReset(); + LoadPluginsImmediate(); + m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) ); + m_CoreThread->Resume(); +} + +// Executes the specified cdvd source and optional elf file. This command performs a +// full closure of any existing VM state and starts a fresh VM with the requested +// sources. +void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc ) +{ + SysReset(); + LoadPluginsImmediate(); + CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); + CDVDsys_ChangeSource( cdvdsrc ); + m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) ); + m_CoreThread->Resume(); +} + +__forceinline bool EmulationInProgress() +{ + return wxGetApp().EmuInProgress(); +} + + +bool HandlePluginError( Exception::PluginError& ex ) +{ + if( pxDialogExists( DialogId_CoreSettings ) ) return true; + bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() + - _("\n\nPress Ok to go to the Plugin Configuration Panel.") ); + _("\n\nPress Ok to go to the Plugin Configuration Panel.") + ); if( result ) { g_Conf->SettingsTabName = L"Plugins"; - wxGetApp().PostMenuAction( MenuId_Config_Settings ); - wxGetApp().Ping(); // fixme: Send a message to the panel to select the failed plugin. - // fixme: handle case where user cancels the settings dialog. (should return FALSE). + if( Dialogs::ConfigurationDialog().ShowModal() == wxID_CANCEL ) + return false; } return result; } @@ -162,11 +197,14 @@ sptr AppEmuThread::ExecuteTask() if( result ) { - wxGetApp().PostMenuAction( MenuId_Config_BIOS ); - wxGetApp().Ping(); - - // fixme: handle case where user cancels the settings dialog. (should return FALSE). - // fixme: automatically re-try emu startup here... + if( wxGetApp().ThreadedModalDialog( DialogId_BiosSelector ) == wxID_CANCEL ) + { + // fixme: handle case where user cancels the settings dialog. (should return FALSE). + } + else + { + // fixme: automatically re-try emu startup here... + } } } } @@ -174,10 +212,13 @@ sptr AppEmuThread::ExecuteTask() catch( Exception::PluginError& ex ) { GetPluginManager().Close(); - if( HandlePluginError( ex ) ) + Console::Error( ex.FormatDiagnosticMessage() ); + Msgbox::Alert( ex.FormatDisplayMessage(), _("Plugin Open Error") ); + + /*if( HandlePluginError( ex ) ) { // fixme: automatically re-try emu startup here... - } + }*/ } // ---------------------------------------------------------------------------- // [TODO] : Add exception handling here for debuggable PS2 exceptions that allows @@ -194,8 +235,6 @@ sptr AppEmuThread::ExecuteTask() } -wxFrame* Pcsx2App::GetMainWindow() const { return m_MainFrame; } - void Pcsx2App::OpenWizardConsole() { if( !IsDebugBuild ) return; @@ -254,7 +293,7 @@ void Pcsx2App::ReadUserModeSettings() IniLoader loader( *conf_usermode ); g_Conf->LoadSaveUserMode( loader, groupname ); - if( !wxFile::Exists( g_Conf->FullPathToConfig() ) ) + if( !wxFile::Exists( GetSettingsFilename() ) ) { // user wiped their pcsx2.ini -- needs a reconfiguration via wizard! // (we skip the first page since it's a usermode.ini thing) @@ -361,7 +400,7 @@ bool Pcsx2App::OnInit() wxInitAllImageHandlers(); if( !wxApp::OnInit() ) return false; - g_Conf = new AppConfig(); + g_Conf.reset( new AppConfig() ); wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); @@ -371,6 +410,8 @@ bool Pcsx2App::OnInit() Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) ); Connect( pxEVT_CallStackBox, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) ); Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) ); + Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); + Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); Connect( pxID_Window_GS, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) ); @@ -406,9 +447,51 @@ bool Pcsx2App::OnInit() SetExitOnFrameDelete( true ); // but being explicit doesn't hurt... m_MainFrame->Show(); - SysInit(); - ApplySettings( *g_Conf ); - InitPlugins(); + SysDetect(); + ApplySettings(); + + m_CoreAllocs.reset( new EmuCoreAllocations() ); + + if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) ) + { + wxString message( _("The following cpu recompilers failed to initialize and will not be available:\n\n") ); + + if( !m_CoreAllocs->RecSuccess_EE ) + { + message += L"\t* R5900 (EE)\n"; + g_Session.ForceDisableEErec = true; + } + + if( !m_CoreAllocs->RecSuccess_IOP ) + { + message += L"\t* R3000A (IOP)\n"; + g_Session.ForceDisableIOPrec = true; + } + + if( !m_CoreAllocs->RecSuccess_VU0 ) + { + message += L"\t* VU0\n"; + g_Session.ForceDisableVU0rec = true; + } + + if( !m_CoreAllocs->RecSuccess_VU1 ) + { + message += L"\t* VU1\n"; + g_Session.ForceDisableVU1rec = true; + } + + message += pxE( ".Popup Error:EmuCore:MemoryForRecs", + L"These errors are the result of memory allocation failures (see the program log for details). " + L"Closing out some memory hogging background tasks may resolve this error.\n\n" + L"These recompilers have been disabled and interpreters will be used in their place. " + L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2." + ); + + if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) ) + return false; + } + + LoadPluginsPassive(); } // ---------------------------------------------------------------------------- catch( Exception::StartupAborted& ex ) @@ -417,10 +500,16 @@ bool Pcsx2App::OnInit() return false; } // ---------------------------------------------------------------------------- - catch( Exception::PluginError& ex ) + // Failures on the core initialization procedure (typically OutOfMemory errors) are bad, + // since it means the emulator is completely non-functional. Let's pop up an error and + // exit gracefully-ish. + // + catch( Exception::RuntimeError& ex ) { - if( !HandlePluginError( ex ) ) - return false; + Console::Error( ex.FormatDiagnosticMessage() ); + Msgbox::Alert( ex.FormatDisplayMessage() + L"\n\nPress OK to close PCSX2.", + _("PCSX2 Critical Error"), wxICON_ERROR ); + return false; } return true; } @@ -433,11 +522,27 @@ void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const if( m_MainFrame == NULL ) return; wxCommandEvent joe( wxEVT_COMMAND_MENU_SELECTED, menu_id ); - m_MainFrame->GetEventHandler()->AddPendingEvent( joe ); + if( wxThread::IsMain() ) + m_MainFrame->GetEventHandler()->ProcessEvent( joe ); + else + m_MainFrame->GetEventHandler()->AddPendingEvent( joe ); +} + +int Pcsx2App::ThreadedModalDialog( DialogIdentifiers dialogId ) +{ + wxASSERT( !wxThread::IsMain() ); // don't call me from MainThread! + + MsgboxEventResult result; + wxCommandEvent joe( pxEVT_OpenModalDialog, dialogId ); + joe.SetClientData( &result ); + AddPendingEvent( joe ); + result.WaitForMe.WaitNoCancel(); + return result.result; } // Waits for the main GUI thread to respond. If run from the main GUI thread, returns -// immediately without error. +// immediately without error. Use this on non-GUI threads to have them sleep until +// the GUI has processed all its pending messages. void Pcsx2App::Ping() const { if( wxThread::IsMain() ) return; @@ -458,6 +563,53 @@ void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt ) ((Semaphore*)evt.GetClientData())->Post(); } +void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt ) +{ + using namespace Dialogs; + + MsgboxEventResult& evtres( *((MsgboxEventResult*)evt.GetClientData()) ); + switch( evt.GetId() ) + { + case DialogId_CoreSettings: + { + static int _guard = 0; + EntryGuard guard( _guard ); + if( guard.IsReentrant() ) return; + evtres.result = ConfigurationDialog().ShowModal(); + } + break; + + case DialogId_BiosSelector: + { + static int _guard = 0; + EntryGuard guard( _guard ); + if( guard.IsReentrant() ) return; + evtres.result = BiosSelectorDialog().ShowModal(); + } + break; + + case DialogId_LogOptions: + { + static int _guard = 0; + EntryGuard guard( _guard ); + if( guard.IsReentrant() ) return; + evtres.result = LogOptionsDialog().ShowModal(); + } + break; + + case DialogId_About: + { + static int _guard = 0; + EntryGuard guard( _guard ); + if( guard.IsReentrant() ) return; + evtres.result = AboutBoxDialog().ShowModal(); + } + break; + } + + evtres.WaitForMe.Post(); +} + void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt ) { Msgbox::OnEvent( evt ); @@ -467,16 +619,9 @@ void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt ) void Pcsx2App::CleanupMess() { - safe_delete( m_Bitmap_Logo ); - safe_delete( g_Conf ); -} - -// This cleanup procedure is issued by wxWidgets prior to destroying base windows and window -// classes. -void Pcsx2App::CleanUp() -{ - SysShutdown(); - wxApp::CleanUp(); + m_CorePlugins.reset(); + m_ProgramLogBox = NULL; + m_MainFrame = NULL; } void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const @@ -489,7 +634,11 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& catch( Exception::PluginError& ex ) { Console::Error( ex.FormatDiagnosticMessage() ); - Msgbox::Alert( ex.FormatDisplayMessage() ); + if( !HandlePluginError( ex ) ) + { + Console::Error( L"User-canceled plugin configuration after load failure. Plugins not loaded!" ); + Msgbox::Alert( _("Warning! Plugins have not been loaded. PCSX2 will be inoperable.") ); + } } // ---------------------------------------------------------------------------- catch( Exception::RuntimeError& ex ) @@ -505,15 +654,15 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& // Common exit handler which can be called from any event (though really it should // be called only from CloseWindow handlers since that's the more appropriate way -// to handle window closures) +// to handle cancelable window closures) +// +// returns true if the app can close, or false if the close event was canceled by +// the glorious user, whomever (s)he-it might be. bool Pcsx2App::PrepForExit() { - SysShutdown(); + m_CoreThread.reset(); CleanupMess(); - m_ProgramLogBox = NULL; - m_MainFrame = NULL; - return true; } @@ -521,9 +670,12 @@ int Pcsx2App::OnExit() { PrepForExit(); - if( g_Conf != NULL ) + if( g_Conf ) SaveSettings(); + while( wxGetLocale() != NULL ) + delete wxGetLocale(); + return wxApp::OnExit(); } @@ -540,47 +692,48 @@ Pcsx2App::Pcsx2App() : Pcsx2App::~Pcsx2App() { + // Typically OnExit cleans everything up before we get here, *unless* we cancel + // out of program startup in OnInit (return false) -- then remaning cleanup needs + // to happen here in the destructor. + CleanupMess(); - while( wxGetLocale() != NULL ) - delete wxGetLocale(); } - -void Pcsx2App::ApplySettings( const AppConfig& newconf ) +void Pcsx2App::ApplySettings( const AppConfig* oldconf ) { + DevAssert( wxThread::IsMain(), "ApplySettings valid from the GUI thread only." ); - if( &newconf != g_Conf ) + // Ensure existence of necessary documents folders. Plugins and other parts + // of PCSX2 rely on them. + + g_Conf->Folders.MemoryCards.Mkdir(); + g_Conf->Folders.Savestates.Mkdir(); + g_Conf->Folders.Snapshots.Mkdir(); + + g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios(); + + // Update the compression attribute on the Memcards folder. + // Memcards generally compress very well via NTFS compression. + + NTFS_CompressFile( g_Conf->Folders.MemoryCards.ToString(), g_Conf->McdEnableNTFS ); + + if( (oldconf == NULL) || (oldconf->LanguageId != g_Conf->LanguageId) ) { - // Need to unload the current emulation state if the user changed plugins, because - // the whole plugin system needs to be re-loaded. - - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) + wxDoNotLogInThisScope please; + if( !i18n_SetLanguage( g_Conf->LanguageId ) ) { - if( newconf.FullpathTo( pi->id ) != g_Conf->FullpathTo( pi->id ) ) - break; - } - if( pi->shortname != NULL ) - { - // [TODO] : Post notice that this shuts down existing emulation. - SysEndExecution(); - safe_delete( g_plugins ); - // Think safe to do this earlier, but not positive. - // Have to update the config *before* loading the new plugins. - *g_Conf = newconf; - LoadPlugins(); - } - else { - *g_Conf = newconf; + if( !i18n_SetLanguage( wxLANGUAGE_DEFAULT ) ) + { + i18n_SetLanguage( wxLANGUAGE_ENGLISH ); + } } } - g_Conf->Apply(); if( m_MainFrame != NULL ) m_MainFrame->ApplySettings(); - if( g_EmuThread != NULL ) - g_EmuThread->ApplySettings( g_Conf->EmuOptions ); + if( m_CoreThread ) + m_CoreThread->ApplySettings( g_Conf->EmuOptions ); } void Pcsx2App::LoadSettings() diff --git a/pcsx2/gui/AppRes.cpp b/pcsx2/gui/AppRes.cpp index aee5260a2b..67773c3c45 100644 --- a/pcsx2/gui/AppRes.cpp +++ b/pcsx2/gui/AppRes.cpp @@ -90,7 +90,7 @@ const wxBitmap& Pcsx2App::GetLogoBitmap() wxImage img; EmbeddedImage temp; // because gcc can't allow non-const temporaries. LoadImageAny( img, useTheme, mess, L"BackgroundLogo", temp ); - m_Bitmap_Logo = new wxBitmap( img ); + m_Bitmap_Logo.reset( new wxBitmap( img ) ); return *m_Bitmap_Logo; } @@ -140,7 +140,7 @@ wxImageList& Pcsx2App::GetImgList_Toolbars() if( m_ToolbarImages == NULL ) { const int imgSize = g_Conf->Toolbar_ImageSize ? 64 : 32; - m_ToolbarImages = new wxImageList( imgSize, imgSize ); + m_ToolbarImages.reset( new wxImageList( imgSize, imgSize ) ); wxFileName mess; bool useTheme = (g_Conf->DeskTheme != L"default"); diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp index c62c0b62da..667cb2d9da 100644 --- a/pcsx2/gui/ConsoleLogger.cpp +++ b/pcsx2/gui/ConsoleLogger.cpp @@ -686,17 +686,6 @@ static int pxCallstackDialog( const wxString& content, const wxString& caption, return wxMessageDialog( NULL, content, caption, flags ).ShowModal(); } -struct MsgboxEventResult -{ - Semaphore WaitForMe; - int result; - - MsgboxEventResult() : - WaitForMe(), result( 0 ) - { - } -}; - class pxMessageBoxEvent : public wxEvent { protected: diff --git a/pcsx2/gui/ConsoleLogger.h b/pcsx2/gui/ConsoleLogger.h index 9cf90e2844..db9012fe23 100644 --- a/pcsx2/gui/ConsoleLogger.h +++ b/pcsx2/gui/ConsoleLogger.h @@ -58,7 +58,7 @@ public: { } - ~ConsoleTestThread() + ~ConsoleTestThread() throw() { m_done = true; } diff --git a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp index 4001c9527d..36742ca277 100644 --- a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp +++ b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp @@ -26,23 +26,27 @@ using namespace wxHelpers; -namespace Dialogs { - -////////////////////////////////////////////////////////////////////////////////////////// -// Helper class for creating wxStaticText labels which are aligned to center. -// (automates the passing of wxDefaultSize and wxDefaultPosition) -// -class StaticTextCentered : public wxStaticText +namespace Dialogs { -public: - StaticTextCentered( wxWindow* parent, const wxString& text, int id=wxID_ANY ) : - wxStaticText( parent, id, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER ) + // Helper class for creating wxStaticText labels which are aligned to center. + // (automates the passing of wxDefaultSize and wxDefaultPosition) + // + class StaticTextCentered : public wxStaticText { - } -}; + public: + StaticTextCentered( wxWindow* parent, const wxString& text, int id=wxID_ANY ) : + wxStaticText( parent, id, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER ) + { + } + }; +} -AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): +// -------------------------------------------------------------------------------------- +// AboutBoxDialog Implementation +// -------------------------------------------------------------------------------------- + +Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): wxDialog( parent, id, _("About PCSX2"), parent->GetPosition()-wxSize( 32, 32 ) ), m_bitmap_logo( this, wxID_ANY, wxGetApp().GetLogoBitmap(), wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN ), @@ -120,5 +124,3 @@ AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): mainSizer.Add( new wxButton( this, wxID_OK, L"I've seen enough"), SizerFlags::StdCenter() ); SetSizerAndFit( &mainSizer ); } - -} // end namespace Dialogs diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp index 3421880418..0bd6a9596c 100644 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp +++ b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp @@ -144,8 +144,8 @@ void Dialogs::ConfigurationDialog::OnApply_Click( wxCommandEvent& evt ) // ---------------------------------------------------------------------------- -Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent ) : - wxDialogWithHelpers( parent, wxID_ANY, _("BIOS Selector"), false ) +Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent, int id ) : + wxDialogWithHelpers( parent, id, _("BIOS Selector"), false ) { wxBoxSizer& bleh( *new wxBoxSizer( wxVERTICAL ) ); diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.h b/pcsx2/gui/Dialogs/ConfigurationDialog.h index 83c7ba097a..0c8830f5e8 100644 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.h +++ b/pcsx2/gui/Dialogs/ConfigurationDialog.h @@ -33,7 +33,7 @@ namespace Dialogs public: virtual ~ConfigurationDialog(); - ConfigurationDialog(wxWindow* parent, int id=wxID_ANY); + ConfigurationDialog(wxWindow* parent=NULL, int id=DialogId_CoreSettings); protected: template< typename T > @@ -60,7 +60,7 @@ namespace Dialogs public: virtual ~BiosSelectorDialog() {} - BiosSelectorDialog(wxWindow* parent ); + BiosSelectorDialog( wxWindow* parent=NULL, int id=DialogId_BiosSelector ); protected: void OnOk_Click( wxCommandEvent& evt ); diff --git a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp index 6fdd047a5d..7e355fe2b9 100644 --- a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp +++ b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp @@ -76,10 +76,7 @@ FirstTimeWizard::UsermodePage::UsermodePage( wxWizard* parent ) : void FirstTimeWizard::UsermodePage::OnUsermodeChanged( wxCommandEvent& evt ) { - m_panel_UserSel.Apply( *g_Conf ); // this assigns the current user mode to g_ApplyState.UseAdminMode - if( g_ApplyState.UseAdminMode == UseAdminMode ) return; - - UseAdminMode = g_ApplyState.UseAdminMode; + m_panel_UserSel.Apply(); g_Conf->Folders.ApplyDefaults(); m_dirpick_settings.Reset(); } @@ -161,7 +158,7 @@ void FirstTimeWizard::OnPageChanging( wxWizardEvent& evt ) if( page == 0 ) { - if( wxFile::Exists( g_Conf->FullPathToConfig() ) ) + if( wxFile::Exists( GetSettingsFilename() ) ) { // Asks the user if they want to import or overwrite the existing settings. diff --git a/pcsx2/gui/Dialogs/LogOptionsDialog.cpp b/pcsx2/gui/Dialogs/LogOptionsDialog.cpp index 6f71f556ca..c4267a40a8 100644 --- a/pcsx2/gui/Dialogs/LogOptionsDialog.cpp +++ b/pcsx2/gui/Dialogs/LogOptionsDialog.cpp @@ -112,8 +112,8 @@ LogOptionsDialog::iopLogOptionsPanel::iopLogOptionsPanel( wxWindow* parent ) : ////////////////////////////////////////////////////////////////////////////////////////// // -LogOptionsDialog::LogOptionsDialog(wxWindow* parent, int id, const wxPoint& pos, const wxSize& size): - wxDialogWithHelpers( parent, id, _("Logging"), true, pos, size ) +LogOptionsDialog::LogOptionsDialog(wxWindow* parent, int id): + wxDialogWithHelpers( parent, id, _("Logging"), true ) { eeLogOptionsPanel& eeBox = *new eeLogOptionsPanel( this ); iopLogOptionsPanel& iopSizer = *new iopLogOptionsPanel( this ); diff --git a/pcsx2/gui/Dialogs/LogOptionsDialog.h b/pcsx2/gui/Dialogs/LogOptionsDialog.h index 749bbad13b..85df195937 100644 --- a/pcsx2/gui/Dialogs/LogOptionsDialog.h +++ b/pcsx2/gui/Dialogs/LogOptionsDialog.h @@ -14,9 +14,8 @@ */ #pragma once - -#include -#include + +#include "App.h" #include "wxHelpers.h" #include "CheckedStaticBox.h" @@ -26,7 +25,7 @@ namespace Dialogs { class LogOptionsDialog: public wxDialogWithHelpers { public: - LogOptionsDialog( wxWindow* parent, int id, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize ); + LogOptionsDialog( wxWindow* parent=NULL, int id=DialogId_LogOptions ); protected: diff --git a/pcsx2/gui/Dialogs/ModalPopups.h b/pcsx2/gui/Dialogs/ModalPopups.h index 4c8f2823fe..b741ee84d9 100644 --- a/pcsx2/gui/Dialogs/ModalPopups.h +++ b/pcsx2/gui/Dialogs/ModalPopups.h @@ -15,6 +15,7 @@ #pragma once +#include "App.h" #include "Panels/ConfigurationPanels.h" #include @@ -68,7 +69,7 @@ namespace Dialogs class AboutBoxDialog: public wxDialog { public: - AboutBoxDialog( wxWindow* parent, int id ); + AboutBoxDialog( wxWindow* parent=NULL, int id=DialogId_About ); protected: wxStaticBitmap m_bitmap_logo; diff --git a/pcsx2/gui/HostGui.cpp b/pcsx2/gui/HostGui.cpp index 416ed95325..c85398e6bd 100644 --- a/pcsx2/gui/HostGui.cpp +++ b/pcsx2/gui/HostGui.cpp @@ -33,7 +33,7 @@ namespace HostGui // Sets the status bar message without mirroring the output to the console. void SetStatusMsg( const wxString& text ) { - wxGetApp().GetMainWindow()->SetStatusText( text ); + wxGetApp().GetMainFrame().SetStatusText( text ); } void Notice( const wxString& text ) @@ -63,10 +63,7 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt ) switch( evt.GetKeyCode() ) { case WXK_ESCAPE: - if( g_Conf->CloseGSonEsc ) - StateRecovery::MakeGsOnly(); - - SysEndExecution(); + m_CoreThread->Suspend(); break; case WXK_F1: @@ -82,7 +79,7 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt ) Console::Notice( " > Selected savestate slot %d", StatesC); if( GSchangeSaveState != NULL ) - GSchangeSaveState(StatesC, SaveState::GetFilename(StatesC).mb_str()); + GSchangeSaveState(StatesC, SaveStateBase::GetFilename(StatesC).mb_str()); break; case WXK_F3: @@ -100,15 +97,10 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt ) break; case WXK_F9: //gsdx "on the fly" renderer switching - - SysSuspend(); - StateRecovery::MakeGsOnly(); - - GetPluginManager().Close( PluginId_GS ); + m_CoreThread->Suspend(); + m_CorePlugins->Close( PluginId_GS ); renderswitch = !renderswitch; - - StateRecovery::Recover(); - SysResume(); + m_CoreThread->Resume(); break; #ifdef PCSX2_DEVBUILD diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index c91b2910d0..99d38ecd0a 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -126,10 +126,12 @@ void MainEmuFrame::PopulatePadMenu() // void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt) { - // Note Closure Veting would be handled here (user prompt confirmation + // Note Closure Vetoing would be handled here (user prompt confirmation // of closing the app) - wxGetApp().PrepForExit(); + if( !wxGetApp().PrepForExit() ) + evt.Veto( true ); + evt.Skip(); } diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 75f0c884fb..260c3e92c0 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -82,13 +82,13 @@ bool MainEmuFrame::_DoSelectIsoBrowser() void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event ) { - SysSuspend(); + wxGetApp().SysSuspend(); if( !wxFileExists( g_Conf->CurrentIso ) ) { if( !_DoSelectIsoBrowser() ) { - SysResume(); + wxGetApp().SysResume(); return; } } @@ -100,66 +100,37 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event ) if( !result ) { - SysResume(); + wxGetApp().SysResume(); return; } } - SysEndExecution(); - g_Conf->EmuOptions.SkipBiosSplash = GetMenuBar()->IsChecked( MenuId_SkipBiosToggle ); wxGetApp().SaveSettings(); - InitPlugins(); - - CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); - SysExecute( new AppEmuThread(), g_Conf->CdvdSource ); + wxGetApp().SysExecute( g_Conf->CdvdSource ); } void MainEmuFrame::Menu_IsoBrowse_Click( wxCommandEvent &event ) { - SysSuspend(); + wxGetApp().SysSuspend(); _DoSelectIsoBrowser(); - SysResume(); + wxGetApp().SysResume(); } void MainEmuFrame::Menu_RunIso_Click( wxCommandEvent &event ) { - SysSuspend(); + wxGetApp().SysSuspend(); if( !_DoSelectIsoBrowser() ) { - SysResume(); + wxGetApp().SysResume(); return; } - SysEndExecution(); - - InitPlugins(); - SysExecute( new AppEmuThread(), CDVDsrc_Iso ); + wxGetApp().SysExecute( CDVDsrc_Iso ); } -/*void MainEmuFrame::Menu_RunWithoutDisc_Click(wxCommandEvent &event) -{ - if( EmulationInProgress() ) - { - SysSuspend(); - - // [TODO] : Add one of 'dems checkboxes that read like "[x] don't show this stupid shit again, kthx." - bool result = Msgbox::OkCancel( pxE( ".Popup:ConfirmEmuReset", L"This will reset the emulator and your current emulation session will be lost. Are you sure?") ); - - if( !result ) - { - SysResume(); - return; - } - } - - SysEndExecution(); - InitPlugins(); - SysExecute( new AppEmuThread(), CDVDsrc_NoDisc ); -}*/ - void MainEmuFrame::Menu_IsoRecent_Click(wxCommandEvent &event) { //Console::Status( "%d", event.GetId() - g_RecentIsoList->GetBaseId() ); @@ -194,34 +165,32 @@ void MainEmuFrame::Menu_SaveStateOther_Click(wxCommandEvent &event) void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event) { - SysReset(); Close(); } void MainEmuFrame::Menu_EmuClose_Click(wxCommandEvent &event) { - SysEndExecution(); + //wxGetApp()-> GetMenuBar()->Check( MenuId_Emu_Pause, false ); } void MainEmuFrame::Menu_EmuPause_Click(wxCommandEvent &event) { if( event.IsChecked() ) - SysSuspend(); + wxGetApp().SysSuspend(); else - SysResume(); + wxGetApp().SysResume(); } void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event) { bool wasRunning = EmulationInProgress(); - SysReset(); + wxGetApp().SysReset(); GetMenuBar()->Check( MenuId_Emu_Pause, false ); if( !wasRunning ) return; - InitPlugins(); - SysExecute( new AppEmuThread() ); + wxGetApp().SysExecute(); } void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event) @@ -229,8 +198,11 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event) typedef void (CALLBACK* PluginConfigureFnptr)(); const PluginsEnum_t pid = (PluginsEnum_t)( event.GetId() - MenuId_Config_GS ); - LoadPlugins(); - ScopedWindowDisable disabler( this ); + LoadPluginsImmediate(); + if( g_plugins == NULL ) return; + + wxWindowDisabler disabler; + //ScopedWindowDisable disabler( this ); g_plugins->Configure( pid ); } diff --git a/pcsx2/gui/MemoryCardFile.cpp b/pcsx2/gui/MemoryCardFile.cpp index c334de445f..00cccc61d0 100644 --- a/pcsx2/gui/MemoryCardFile.cpp +++ b/pcsx2/gui/MemoryCardFile.cpp @@ -32,10 +32,6 @@ struct Component_FileMcd; #include -#ifdef _WIN32 - extern void NTFS_CompressFile( const wxString& file, bool compressMode ); -#endif - static const int MCD_SIZE = 1024 * 8 * 16; static const int MC2_SIZE = 1024 * 528 * 16; @@ -106,9 +102,7 @@ FileMemoryCard::FileMemoryCard() // [TODO] : Add memcard size detection and report it to the console log. // (8MB, 256Mb, whatever) - #ifdef _WIN32 NTFS_CompressFile( str, g_Conf->McdEnableNTFS ); - #endif if( !m_file[port][slot].Open( str.c_str(), wxFile::read_write ) ) { diff --git a/pcsx2/gui/Panels/BiosSelectorPanel.cpp b/pcsx2/gui/Panels/BiosSelectorPanel.cpp index 53d22fa61a..09962e50df 100644 --- a/pcsx2/gui/Panels/BiosSelectorPanel.cpp +++ b/pcsx2/gui/Panels/BiosSelectorPanel.cpp @@ -70,7 +70,6 @@ Panels::BiosSelectorPanel::BiosSelectorPanel( wxWindow& parent, int idealWidth ) _("BIOS Search Path:"), // static box label _("Select folder with PS2 BIOS roms") // dir picker popup label ) ) -, m_BiosList( NULL ) { m_ComboBox.SetFont( wxFont( m_ComboBox.GetFont().GetPointSize()+1, wxFONTFAMILY_MODERN, wxNORMAL, wxNORMAL, false, L"Lucida Console" ) ); m_ComboBox.SetMinSize( wxSize( wxDefaultCoord, std::max( m_ComboBox.GetMinSize().GetHeight(), 96 ) ) ); @@ -88,7 +87,6 @@ Panels::BiosSelectorPanel::BiosSelectorPanel( wxWindow& parent, int idealWidth ) Panels::BiosSelectorPanel::~BiosSelectorPanel() { - safe_delete( m_BiosList ); } bool Panels::BiosSelectorPanel::ValidateEnumerationStatus() @@ -102,11 +100,10 @@ bool Panels::BiosSelectorPanel::ValidateEnumerationStatus() if( m_FolderPicker.GetPath().Exists() ) wxDir::GetAllFiles( m_FolderPicker.GetPath().ToString(), bioslist.get(), L"*.bin", wxDIR_FILES ); - if( (m_BiosList == NULL) || (*bioslist != *m_BiosList) ) + if( !m_BiosList || (*bioslist != *m_BiosList) ) validated = false; - delete m_BiosList; - m_BiosList = bioslist.release(); + m_BiosList.swap( bioslist ); return validated; } @@ -116,7 +113,7 @@ void Panels::BiosSelectorPanel::ReloadSettings() m_FolderPicker.Reset(); } -void Panels::BiosSelectorPanel::Apply( AppConfig& conf ) +void Panels::BiosSelectorPanel::Apply() { int sel = m_ComboBox.GetSelection(); if( sel == wxNOT_FOUND ) @@ -133,11 +130,13 @@ void Panels::BiosSelectorPanel::Apply( AppConfig& conf ) ); } - conf.BaseFilenames.Bios = (*m_BiosList)[(int)m_ComboBox.GetClientData(sel)]; + g_Conf->BaseFilenames.Bios = (*m_BiosList)[(int)m_ComboBox.GetClientData(sel)]; } void Panels::BiosSelectorPanel::DoRefresh() { + if( !m_BiosList ) return; + wxFileName right( g_Conf->FullpathToBios() ); right.MakeAbsolute(); diff --git a/pcsx2/gui/Panels/ConfigurationPanels.h b/pcsx2/gui/Panels/ConfigurationPanels.h index f923f72c78..eb8ec32f40 100644 --- a/pcsx2/gui/Panels/ConfigurationPanels.h +++ b/pcsx2/gui/Panels/ConfigurationPanels.h @@ -93,10 +93,6 @@ namespace Panels // TODO : Rename me to CurOwnerBook, or rename the one above to ParentPage. wxBookCtrlBase* ParentBook; - // Crappy hack to handle the UseAdminMode option, which can't be part of AppConfig - // because AppConfig depends on this value to initialize itself. - bool UseAdminMode; - StaticApplyState() : PanelList() , CurOwnerPage( wxID_NONE ) @@ -168,7 +164,7 @@ namespace Panels // configuration structure (which is typically a copy of g_Conf). If validation // of form contents fails, the function should throw Exception::CannotApplySettings. // If no exceptions are thrown, then the operation is assumed a success. :) - virtual void Apply( AppConfig& conf )=0; + virtual void Apply()=0; }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -183,7 +179,7 @@ namespace Panels virtual ~UsermodeSelectionPanel() { } UsermodeSelectionPanel( wxWindow& parent, int idealWidth=wxDefaultCoord, bool isFirstTime = true ); - void Apply( AppConfig& conf ); + void Apply(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -198,7 +194,7 @@ namespace Panels virtual ~LanguageSelectionPanel() { } LanguageSelectionPanel( wxWindow& parent, int idealWidth=wxDefaultCoord ); - void Apply( AppConfig& conf ); + void Apply(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -216,7 +212,7 @@ namespace Panels public: CpuPanel( wxWindow& parent, int idealWidth ); - void Apply( AppConfig& conf ); + void Apply(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -227,7 +223,7 @@ namespace Panels public: VideoPanel( wxWindow& parent, int idealWidth ); - void Apply( AppConfig& conf ); + void Apply(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -248,7 +244,7 @@ namespace Panels public: SpeedHacksPanel( wxWindow& parent, int idealWidth ); - void Apply( AppConfig& conf ); + void Apply(); protected: const wxChar* GetEEcycleSliderMsg( int val ); @@ -267,7 +263,7 @@ namespace Panels public: GameFixesPanel( wxWindow& parent, int idealWidth ); - void Apply( AppConfig& conf ); + void Apply(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -286,7 +282,7 @@ namespace Panels DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid, const wxString& label, const wxString& dialogLabel ); virtual ~DirPickerPanel() { } - void Apply( AppConfig& conf ); + void Apply(); void Reset(); wxDirName GetPath() const { return wxDirName( m_pickerCtrl->GetPath() ); } @@ -354,9 +350,9 @@ namespace Panels class BiosSelectorPanel : public BaseSelectorPanel { protected: + wxScopedPtr m_BiosList; wxListBox& m_ComboBox; DirPickerPanel& m_FolderPicker; - wxArrayString* m_BiosList; public: BiosSelectorPanel( wxWindow& parent, int idealWidth ); @@ -364,7 +360,7 @@ namespace Panels void ReloadSettings(); protected: - virtual void Apply( AppConfig& conf ); + virtual void Apply(); virtual void DoRefresh(); virtual bool ValidateEnumerationStatus(); }; @@ -399,16 +395,18 @@ namespace Panels class EnumThread : public Threading::PersistentThread { public: - EnumeratedPluginInfo* Results; // array of plugin results. + SafeList Results; // array of plugin results. protected: PluginSelectorPanel& m_master; - volatile bool m_cancel; public: - virtual ~EnumThread(); + virtual ~EnumThread() throw() + { + PersistentThread::Cancel(); + } + EnumThread( PluginSelectorPanel& master ); - void Cancel(); void DoNextPlugin( int evtidx ); protected: @@ -447,25 +445,27 @@ namespace Panels }; // ------------------------------------------------------------------------ - // PluginSelectorPanel Members + // PluginSelectorPanel Members // ------------------------------------------------------------------------ protected: - wxArrayString* m_FileList; // list of potential plugin files StatusPanel& m_StatusPanel; ComboBoxPanel& m_ComponentBoxes; - EnumThread* m_EnumeratorThread; + + wxScopedPtr m_FileList; // list of potential plugin files + wxScopedPtr m_EnumeratorThread; public: virtual ~PluginSelectorPanel(); PluginSelectorPanel( wxWindow& parent, int idealWidth ); void CancelRefresh(); // used from destructor, stays non-virtual - void Apply( AppConfig& conf ); + void Apply(); void ReloadSettings(); protected: void OnConfigure_Clicked( wxCommandEvent& evt ); + void OnShowStatusBar( wxCommandEvent& evt ); virtual void OnProgress( wxCommandEvent& evt ); virtual void OnEnumComplete( wxCommandEvent& evt ); diff --git a/pcsx2/gui/Panels/CpuPanel.cpp b/pcsx2/gui/Panels/CpuPanel.cpp index 804c470ac9..86a2380517 100644 --- a/pcsx2/gui/Panels/CpuPanel.cpp +++ b/pcsx2/gui/Panels/CpuPanel.cpp @@ -78,9 +78,9 @@ Panels::CpuPanel::CpuPanel( wxWindow& parent, int idealWidth ) : m_Option_sVU1->SetValue( recOps.EnableVU1 ); } -void Panels::CpuPanel::Apply( AppConfig& conf ) +void Panels::CpuPanel::Apply() { - Pcsx2Config::RecompilerOptions& recOps( conf.EmuOptions.Cpu.Recompiler ); + Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler ); recOps.EnableEE = m_Option_RecEE->GetValue(); recOps.EnableIOP = m_Option_RecIOP->GetValue(); recOps.EnableVU0 = m_Option_mVU0->GetValue() || m_Option_sVU0->GetValue(); diff --git a/pcsx2/gui/Panels/DirPickerPanel.cpp b/pcsx2/gui/Panels/DirPickerPanel.cpp index 21deb6bf37..8471c93459 100644 --- a/pcsx2/gui/Panels/DirPickerPanel.cpp +++ b/pcsx2/gui/Panels/DirPickerPanel.cpp @@ -93,8 +93,7 @@ Panels::DirPickerPanel::DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid m_checkCtrl = &AddCheckBox( s_lower, _("Use default setting") ); m_checkCtrl->SetToolTip( pxE( ".Tooltip:DirPicker:UseDefault", - L"When checked this folder will automatically reflect the default associated with PCSX2's current usermode setting. " - L"" ) + L"When checked this folder will automatically reflect the default associated with PCSX2's current usermode setting. " ) ); #ifndef __WXGTK__ @@ -134,7 +133,7 @@ void Panels::DirPickerPanel::Reset() m_pickerCtrl->SetPath( GetNormalizedConfigFolder( m_FolderId ) ); } -void Panels::DirPickerPanel::Apply( AppConfig& conf ) +void Panels::DirPickerPanel::Apply() { - conf.Folders.Set( m_FolderId, m_pickerCtrl->GetPath(), m_checkCtrl->GetValue() ); + g_Conf->Folders.Set( m_FolderId, m_pickerCtrl->GetPath(), m_checkCtrl->GetValue() ); } diff --git a/pcsx2/gui/Panels/GameFixesPanel.cpp b/pcsx2/gui/Panels/GameFixesPanel.cpp index 4129fcd30a..9964eb5512 100644 --- a/pcsx2/gui/Panels/GameFixesPanel.cpp +++ b/pcsx2/gui/Panels/GameFixesPanel.cpp @@ -85,9 +85,9 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow& parent, int idealWidth ) : } // I could still probably get rid of the for loop, but I think this is clearer. -void Panels::GameFixesPanel::Apply( AppConfig& conf ) +void Panels::GameFixesPanel::Apply() { - Pcsx2Config::GamefixOptions& opts( conf.EmuOptions.Gamefixes ); + Pcsx2Config::GamefixOptions& opts( g_Conf->EmuOptions.Gamefixes ); for (int i = 0; i < NUM_OF_GAME_FIXES; i++) { if (m_checkbox[i]->GetValue()) diff --git a/pcsx2/gui/Panels/MiscPanelStuff.cpp b/pcsx2/gui/Panels/MiscPanelStuff.cpp index 4ba040e80a..d3e06149dd 100644 --- a/pcsx2/gui/Panels/MiscPanelStuff.cpp +++ b/pcsx2/gui/Panels/MiscPanelStuff.cpp @@ -60,31 +60,40 @@ void Panels::StaticApplyState::StartWizard() bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess ) { bool retval = true; + + // Save these settings so we can restore them if the Apply fails. + + bool oldAdminMode = UseAdminMode; + wxDirName oldSettingsFolder = SettingsFolder; + bool oldUseDefSet = UseDefaultSettingsFolder; + + AppConfig confcopy( *g_Conf ); + try { - AppConfig confcopy( *g_Conf ); - - g_ApplyState.UseAdminMode = UseAdminMode; - PanelApplyList_t::iterator yay = PanelList.begin(); while( yay != PanelList.end() ) { //DbgCon::Status( L"Writing settings for: " + (*yay)->GetLabel() ); if( (pageid < 0) || (*yay)->IsOnPage( pageid ) ) - (*yay)->Apply( confcopy ); + (*yay)->Apply(); yay++; } // If an exception is thrown above, this code below won't get run. // (conveniently skipping any option application! :D) - UseAdminMode = g_ApplyState.UseAdminMode; - wxGetApp().ApplySettings( confcopy ); + wxGetApp().ApplySettings( &confcopy ); if( saveOnSuccess ) wxGetApp().SaveSettings(); } catch( Exception::CannotApplySettings& ex ) { + UseAdminMode = oldAdminMode; + SettingsFolder = oldSettingsFolder; + UseDefaultSettingsFolder = oldUseDefSet; + *g_Conf = confcopy; + wxMessageBox( ex.FormatDisplayMessage(), _("Cannot apply settings...") ); if( ex.GetPanel() != NULL ) @@ -92,6 +101,15 @@ bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess ) retval = false; } + catch( ... ) + { + UseAdminMode = oldAdminMode; + SettingsFolder = oldSettingsFolder; + UseDefaultSettingsFolder = oldUseDefSet; + *g_Conf = confcopy; + + throw; + } return retval; } @@ -133,12 +151,12 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow& parent, int id SetSizer( &s_boxer ); } -void Panels::UsermodeSelectionPanel::Apply( AppConfig& conf ) +void Panels::UsermodeSelectionPanel::Apply() { if( !m_radio_cwd->GetValue() && !m_radio_user->GetValue() ) throw Exception::CannotApplySettings( this, wxLt( "You must select one of the available user modes before proceeding." ) ); - g_ApplyState.UseAdminMode = m_radio_cwd->GetValue(); + UseAdminMode = m_radio_cwd->GetValue(); } // ----------------------------------------------------------------------- @@ -173,20 +191,20 @@ Panels::LanguageSelectionPanel::LanguageSelectionPanel( wxWindow& parent, int id SetSizer( &s_lang ); } -void Panels::LanguageSelectionPanel::Apply( AppConfig& conf ) +void Panels::LanguageSelectionPanel::Apply() { // The combo box's order is sorted and may not match our m_langs order, so // we have to do a string comparison to find a match: wxString sel( m_picker->GetString( m_picker->GetSelection() ) ); - conf.LanguageId = wxLANGUAGE_DEFAULT; // use this if no matches found + g_Conf->LanguageId = wxLANGUAGE_DEFAULT; // use this if no matches found int size = m_langs.size(); for( int i=0; iLanguageId = m_langs[i].wxLangId; break; } } diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index 28875379da..53a733c4ae 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -34,12 +34,14 @@ using namespace wxHelpers; using namespace Threading; BEGIN_DECLARE_EVENT_TYPES() - DECLARE_EVENT_TYPE(wxEVT_EnumeratedNext, -1) - DECLARE_EVENT_TYPE(wxEVT_EnumerationFinished, -1) + DECLARE_EVENT_TYPE(pxEVT_EnumeratedNext, -1) + DECLARE_EVENT_TYPE(pxEVT_EnumerationFinished, -1) + DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1) END_DECLARE_EVENT_TYPES() -DEFINE_EVENT_TYPE(wxEVT_EnumeratedNext) -DEFINE_EVENT_TYPE(wxEVT_EnumerationFinished); +DEFINE_EVENT_TYPE(pxEVT_EnumeratedNext) +DEFINE_EVENT_TYPE(pxEVT_EnumerationFinished); +DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar); typedef s32 (CALLBACK* PluginTestFnptr)(); typedef void (CALLBACK* PluginConfigureFnptr)(); @@ -55,8 +57,10 @@ namespace Exception }; } -////////////////////////////////////////////////////////////////////////////////////////// -// +// -------------------------------------------------------------------------------------- +// PluginEnumerator class +// -------------------------------------------------------------------------------------- + class PluginEnumerator { protected: @@ -74,15 +78,15 @@ public: // Constructor! // // Possible Exceptions: - // BadStream - thrown if the provided file is simply not a loadable DLL. - // NotPcsxPlugin - thrown if the DLL is not a PCSX2 plugin, or if it's of an unsupported version. + // BadStream - thrown if the provided file is simply not a loadable DLL. + // NotEnumerablePlugin - thrown if the DLL is not a PCSX2 plugin, or if it's of an unsupported version. // PluginEnumerator( const wxString& plugpath ) : m_plugpath( plugpath ) , m_plugin() { if( !m_plugin.Load( m_plugpath ) ) - throw Exception::BadStream( m_plugpath, "File is not a valid dynamic library" ); + throw Exception::BadStream( m_plugpath, "File is not a valid dynamic library." ); wxDoNotLogInThisScope please; m_GetLibType = (_PS2EgetLibType)m_plugin.GetSymbol( L"PS2EgetLibType" ); @@ -90,9 +94,8 @@ public: m_GetLibVersion2 = (_PS2EgetLibVersion2)m_plugin.GetSymbol( L"PS2EgetLibVersion2" ); if( m_GetLibType == NULL || m_GetLibName == NULL || m_GetLibVersion2 == NULL ) - { throw Exception::NotEnumerablePlugin( m_plugpath ); - } + m_type = m_GetLibType(); } @@ -134,7 +137,10 @@ public: static const wxString failed_separator( L"-------- Unsupported Plugins --------" ); -// ------------------------------------------------------------------------ +// -------------------------------------------------------------------------------------- +// PluginSelectorPanel implementations +// -------------------------------------------------------------------------------------- + Panels::PluginSelectorPanel::StatusPanel::StatusPanel( wxWindow* parent ) : wxPanelWithHelpers( parent ) , m_gauge( *new wxGauge( this, wxID_ANY, 10 ) ) @@ -221,10 +227,8 @@ void Panels::PluginSelectorPanel::ComboBoxPanel::Reset() // ------------------------------------------------------------------------ Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow& parent, int idealWidth ) : BaseSelectorPanel( parent, idealWidth ) -, m_FileList( NULL ) , m_StatusPanel( *new StatusPanel( this ) ) , m_ComponentBoxes( *new ComboBoxPanel( this ) ) -, m_EnumeratorThread( NULL ) { // note: the status panel is a floating window, so that it can be positioned in the // center of the dialog after it's been fitted to the contents. @@ -242,8 +246,9 @@ Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow& parent, int idealWid SetSizer( &s_main ); - Connect( wxEVT_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) ); - Connect( wxEVT_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) ); + Connect( pxEVT_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) ); + Connect( pxEVT_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) ); + Connect( pxEVT_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) ); Connect( ButtonId_Configure, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnConfigure_Clicked ) ); } @@ -257,10 +262,19 @@ void Panels::PluginSelectorPanel::ReloadSettings() m_ComponentBoxes.GetDirPicker().Reset(); } -void Panels::PluginSelectorPanel::Apply( AppConfig& conf ) +static wxString GetApplyFailedMsg() +{ + return pxE( ".Popup Error:PluginSelector:ApplyFailed", + L"All plugins must have valid selections for PCSX2 to run. If you are unable to make\n" + L"a valid selection due to missing plugins or an incomplete install of PCSX2, then\n" + L"press cancel to close the Configuration panel." + ); +} + +void Panels::PluginSelectorPanel::Apply() { // user never entered plugins panel? Skip application since combo boxes are invalid/uninitialized. - if( m_FileList == NULL ) return; + if( !m_FileList ) return; for( int i=0; iBaseFilenames.Plugins[tbl_PluginInfo[i].id] = GetFilename((int)m_ComponentBoxes.Get(i).GetClientData(sel)); } + + // ---------------------------------------------------------------------------- + // Make sure folders are up to date, and try to load/reload plugins if needed... + + g_Conf->Folders.ApplyDefaults(); + + // Need to unload the current emulation state if the user changed plugins, because + // the whole plugin system needs to be re-loaded. + + const PluginInfo* pi = tbl_PluginInfo-1; + while( ++pi, pi->shortname != NULL ) + { + if( g_Conf->FullpathTo( pi->id ) != g_Conf->FullpathTo( pi->id ) ) + break; + } + + if( pi->shortname != NULL ) + { + if( wxGetApp().m_CoreThread ) + { + // [TODO] : Post notice that this shuts down existing emulation, and may not safely recover. + } + + wxGetApp().SysReset(); + } + + if( !wxGetApp().m_CorePlugins ) + { + try + { + LoadPluginsImmediate(); + } + catch( Exception::PluginError& ex ) + { + // Rethrow PluginLoadErrors as a failure to Apply... + + wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() ); + + throw Exception::CannotApplySettings( this, + // English Log + ex.FormatDiagnosticMessage(), + + // Translated + wxsFormat( L"The selected %s plugin failed to load.", plugname.c_str() ) + L"\n\n" + GetApplyFailedMsg() + ); + } + } } void Panels::PluginSelectorPanel::CancelRefresh() { - safe_delete( m_EnumeratorThread ); - safe_delete( m_FileList ); } void Panels::PluginSelectorPanel::DoRefresh() { m_ComponentBoxes.Reset(); - if( m_FileList == NULL ) + if( !m_FileList ) { wxCommandEvent evt; OnEnumComplete( evt ); @@ -304,21 +358,17 @@ void Panels::PluginSelectorPanel::DoRefresh() } // Disable all controls until enumeration is complete. - // Show status bar for plugin enumeration. - - // fixme: the status bar doesn't always fit itself to the window correctly because - // sometimes this gets called before the parent window has been fully created. I'm - // not quite sure how to fix (a delayed event/message might work tho implementing it - // may not be trivial) -- air + // Show status bar for plugin enumeration. Use a pending event so that + // the window's size can get initialized properly before trying to custom- + // fit the status panel to it. m_ComponentBoxes.Hide(); - m_StatusPanel.SetSize( m_ComponentBoxes.GetSize().GetWidth() - 8, wxDefaultCoord ); - m_StatusPanel.CentreOnParent(); - m_StatusPanel.Show(); + wxCommandEvent evt( pxEVT_ShowStatusBar ); + GetEventHandler()->AddPendingEvent( evt ); // Use a thread to load plugins. - safe_delete( m_EnumeratorThread ); - m_EnumeratorThread = new EnumThread( *this ); + m_EnumeratorThread.reset( NULL ); + m_EnumeratorThread.reset( new EnumThread( *this ) ); if( DisableThreading ) m_EnumeratorThread->DoNextPlugin( 0 ); @@ -328,6 +378,8 @@ void Panels::PluginSelectorPanel::DoRefresh() bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() { + m_EnumeratorThread.reset(); // make sure the thread is STOPPED, just in case... + bool validated = true; // re-enumerate plugins, and if anything changed then we need to wipe @@ -339,17 +391,16 @@ bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes.GetPluginsPath(), pluginlist.get() ); - if( (m_FileList == NULL) || (*pluginlist != *m_FileList) ) + if( !m_FileList || (*pluginlist != *m_FileList) ) validated = false; if( pluggers == 0 ) { - safe_delete( m_FileList ); + m_FileList.reset(); return validated; } - delete m_FileList; - m_FileList = pluginlist.release(); + m_FileList.swap( pluginlist ); m_StatusPanel.SetGaugeLength( pluggers ); @@ -370,9 +421,16 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt ) } } +void Panels::PluginSelectorPanel::OnShowStatusBar( wxCommandEvent& evt ) +{ + m_StatusPanel.SetSize( m_ComponentBoxes.GetSize().GetWidth() - 8, wxDefaultCoord ); + m_StatusPanel.CentreOnParent(); + m_StatusPanel.Show(); +} + void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt ) { - safe_delete( m_EnumeratorThread ); + m_EnumeratorThread.reset(); // fixme: Default plugins should be picked based on the timestamp of the DLL or something? // (for now we just force it to selection zero if nothing's selected) @@ -395,7 +453,7 @@ void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt ) void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt ) { - if( m_FileList == NULL ) return; + if( !m_FileList ) return; const size_t evtidx = evt.GetExtraLong(); @@ -404,7 +462,7 @@ void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt ) const int nextidx = evtidx+1; if( nextidx == m_FileList->Count() ) { - wxCommandEvent done( wxEVT_EnumerationFinished ); + wxCommandEvent done( pxEVT_EnumerationFinished ); GetEventHandler()->AddPendingEvent( done ); } else @@ -447,28 +505,16 @@ void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt ) } -////////////////////////////////////////////////////////////////////////////////////////// -// EnumThread Method Implementations +// -------------------------------------------------------------------------------------- +// EnumThread method implementations +// -------------------------------------------------------------------------------------- Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master ) : PersistentThread() -, Results( new EnumeratedPluginInfo[master.FileCount()] ) +, Results( master.FileCount(), L"PluginSelectorResults" ) , m_master( master ) -, m_cancel( false ) { -} - -Panels::PluginSelectorPanel::EnumThread::~EnumThread() -{ - EnumThread::Cancel(); - safe_delete_array( Results ); -} - -void Panels::PluginSelectorPanel::EnumThread::Cancel() -{ - m_cancel = true; - Sleep( 2 ); - PersistentThread::Cancel(); + Results.MatchLengthToAllocatedSize(); } void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx ) @@ -499,7 +545,7 @@ void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx ) Console::Status( ex.FormatDiagnosticMessage() ); } - wxCommandEvent yay( wxEVT_EnumeratedNext ); + wxCommandEvent yay( pxEVT_EnumeratedNext ); yay.SetExtraLong( curidx ); m_master.GetEventHandler()->AddPendingEvent( yay ); } @@ -509,15 +555,15 @@ sptr Panels::PluginSelectorPanel::EnumThread::ExecuteTask() DevCon::Status( "Plugin Enumeration Thread started..." ); wxGetApp().Ping(); // gives the gui thread some time to refresh + Sleep( 3 ); for( int curidx=0; curidx < m_master.FileCount(); ++curidx ) { - if( m_cancel ) return 0; DoNextPlugin( curidx ); pthread_testcancel(); } - wxCommandEvent done( wxEVT_EnumerationFinished ); + wxCommandEvent done( pxEVT_EnumerationFinished ); m_master.GetEventHandler()->AddPendingEvent( done ); DevCon::Status( "Plugin Enumeration Thread complete!" ); diff --git a/pcsx2/gui/Panels/SpeedhacksPanel.cpp b/pcsx2/gui/Panels/SpeedhacksPanel.cpp index af2d2b3a04..22e8052651 100644 --- a/pcsx2/gui/Panels/SpeedhacksPanel.cpp +++ b/pcsx2/gui/Panels/SpeedhacksPanel.cpp @@ -219,9 +219,9 @@ Panels::SpeedHacksPanel::SpeedHacksPanel( wxWindow& parent, int idealWidth ) : Connect( m_slider_vustealer->GetId(), wxEVT_SCROLL_CHANGED, wxScrollEventHandler( SpeedHacksPanel::VUCycleRate_Scroll ) ); } -void Panels::SpeedHacksPanel::Apply( AppConfig& conf ) +void Panels::SpeedHacksPanel::Apply() { - Pcsx2Config::SpeedhackOptions& opts( conf.EmuOptions.Speedhacks ); + Pcsx2Config::SpeedhackOptions& opts( g_Conf->EmuOptions.Speedhacks ); opts.EECycleRate = m_slider_eecycle->GetValue()-1; opts.VUCycleSteal = m_slider_vustealer->GetValue(); diff --git a/pcsx2/gui/Panels/VideoPanel.cpp b/pcsx2/gui/Panels/VideoPanel.cpp index 82b9caab8e..72b1dc7cbd 100644 --- a/pcsx2/gui/Panels/VideoPanel.cpp +++ b/pcsx2/gui/Panels/VideoPanel.cpp @@ -25,6 +25,6 @@ Panels::VideoPanel::VideoPanel( wxWindow& parent, int idealWidth ) : // MTGS Forced Synchronization } -void Panels::VideoPanel::Apply( AppConfig& conf ) +void Panels::VideoPanel::Apply() { } diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp index e86e56795a..65da250122 100644 --- a/pcsx2/gui/Plugins.cpp +++ b/pcsx2/gui/Plugins.cpp @@ -14,7 +14,7 @@ */ #include "PrecompiledHeader.h" -#include "Utilities/ScopedPtr.h" +#include "App.h" #include #include @@ -44,9 +44,73 @@ int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0; } -void LoadPlugins() +using namespace Threading; + +void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt ) { - if( g_plugins != NULL ) return; + ReloadPlugins(); +} + +void Pcsx2App::ReloadPlugins() +{ + class LoadPluginsTask : public Threading::BaseTaskThread + { + public: + PluginManager* Result; + Exception::PluginError* Ex_PluginError; + Exception::RuntimeError* Ex_RuntimeError; + + protected: + const wxString (&m_folders)[PluginId_Count]; + + public: + LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : + Result( NULL ) + , Ex_PluginError( NULL ) + , Ex_RuntimeError( NULL ) + , m_folders( folders ) + { + } + + virtual ~LoadPluginsTask() throw() + { + BaseTaskThread::Cancel(); + } + + protected: + void Task() + { + wxGetApp().Ping(); + Sleep(3); + + try + { + //throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" ); + Result = PluginManager_Create( m_folders ); + } + catch( Exception::PluginError& ex ) + { + Result = NULL; + Ex_PluginError = new Exception::PluginError( ex ); + } + catch( Exception::RuntimeError& innerEx ) + { + // Runtime errors are typically recoverable, so handle them here + // and prep them for re-throw on the main thread. + Result = NULL; + Ex_RuntimeError = new Exception::RuntimeError( + L"A runtime error occurred on the LoadPlugins thread.\n" + innerEx.FormatDiagnosticMessage(), + innerEx.FormatDisplayMessage() + ); + } + + // anything else leave unhandled so that the debugger catches it! + } + }; + + m_CoreThread.reset(); + m_CorePlugins.reset(); + wxString passins[PluginId_Count]; const PluginInfo* pi = tbl_PluginInfo-1; @@ -57,29 +121,45 @@ void LoadPlugins() if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) ) passins[pi->id] = g_Conf->FullpathTo( pi->id ); } + + LoadPluginsTask task( passins ); + task.Start(); + task.PostTask(); + task.WaitForResult(); + + if( task.Result == NULL ) + { + if( task.Ex_PluginError != NULL ) + throw *task.Ex_PluginError; + if( task.Ex_RuntimeError != NULL ) + throw *task.Ex_RuntimeError; // Re-Throws generic threaded errors + } - g_plugins = PluginManager_Create( passins ); + m_CorePlugins.reset( task.Result ); } -void InitPlugins() +// Posts a message to the App to reload plugins. Plugins are loaded via a background thread +// which is started on a pending event, so don't expect them to be ready "right now." +void LoadPluginsPassive() { - if( g_plugins == NULL ) - LoadPlugins(); - - GetPluginManager().Init(); + wxCommandEvent evt( pxEVT_ReloadPlugins ); + wxGetApp().AddPendingEvent( evt ); } -// All plugins except the CDVD are opened here. The CDVD must be opened manually by -// the GUI, depending on the user's menu/config in use. -// -// Exceptions: -// PluginFailure - if any plugin fails to initialize or open. -// ThreadCreationError - If the MTGS thread fails to be created. -// -void OpenPlugins() +// Blocks until plugins have been successfully loaded, or throws an exception if +// the user cancels the loading procedure after error. +void LoadPluginsImmediate() { - if( g_plugins == NULL ) - InitPlugins(); + wxASSERT( wxThread::IsMain() ); - GetPluginManager().Open(); + static int _reentrant = 0; + EntryGuard guard( _reentrant ); + + wxASSERT( !guard.IsReentrant() ); + wxGetApp().ReloadPlugins(); +} + +void UnloadPlugins() +{ + wxGetApp().m_CorePlugins.reset(); } diff --git a/pcsx2/gui/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp index 07a3ad5303..9c46dbaaec 100644 --- a/pcsx2/gui/Saveslots.cpp +++ b/pcsx2/gui/Saveslots.cpp @@ -33,7 +33,7 @@ bool States_isSlotUsed(int num) if (ElfCRC == 0) return false; else - return wxFileExists( SaveState::GetFilename( num ) ); + return wxFileExists( SaveStateBase::GetFilename( num ) ); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -62,12 +62,12 @@ void States_Load( const wxString& file ) StateRecovery::Recover(); } - SysExecute( new AppEmuThread() ); + wxGetApp().SysExecute(); } void States_Load(int num) { - wxString file( SaveState::GetFilename( num ) ); + wxString file( SaveStateBase::GetFilename( num ) ); if( !wxFileExists( file ) ) { @@ -120,79 +120,5 @@ void States_Save( const wxString& file ) void States_Save(int num) { Console::Status( "Saving savestate to slot %d...", num ); - States_Save( SaveState::GetFilename( num ) ); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// -void vSyncDebugStuff( uint frame ) -{ -#ifdef OLD_TESTBUILD_STUFF - if( g_TestRun.enabled && g_TestRun.frame > 0 ) { - if( frame > g_TestRun.frame ) { - // take a snapshot - if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) { - if( g_TestRun.snapdone ) { - g_TestRun.curimage++; - g_TestRun.snapdone = 0; - g_TestRun.frame += 20; - if( g_TestRun.curimage >= g_TestRun.numimages ) { - // exit - g_EmuThread->Cancel(); - } - } - else { - // query for the image - GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture); - } - } - else { - // exit - g_EmuThread->Cancel(); - } - } - } - - GSVSYNC(); - - if( g_SaveGSStream == 1 ) { - freezeData fP; - - g_SaveGSStream = 2; - g_fGSSave->gsFreeze(); - - if (GSfreeze(FREEZE_SIZE, &fP) == -1) { - safe_delete( g_fGSSave ); - g_SaveGSStream = 0; - } - else { - fP.data = (s8*)malloc(fP.size); - if (fP.data == NULL) { - safe_delete( g_fGSSave ); - g_SaveGSStream = 0; - } - else { - if (GSfreeze(FREEZE_SAVE, &fP) == -1) { - safe_delete( g_fGSSave ); - g_SaveGSStream = 0; - } - else { - g_fGSSave->Freeze( fP.size ); - if (fP.size) { - g_fGSSave->FreezeMem( fP.data, fP.size ); - free(fP.data); - } - } - } - } - } - else if( g_SaveGSStream == 2 ) { - - if( --g_nLeftGSFrames <= 0 ) { - safe_delete( g_fGSSave ); - g_SaveGSStream = 0; - Console::WriteLn("Done saving GS stream"); - } - } -#endif + States_Save( SaveStateBase::GetFilename( num ) ); } diff --git a/pcsx2/gui/wxHelpers.cpp b/pcsx2/gui/wxHelpers.cpp index fa410f1327..9fac1d5ded 100644 --- a/pcsx2/gui/wxHelpers.cpp +++ b/pcsx2/gui/wxHelpers.cpp @@ -14,6 +14,7 @@ */ #include "PrecompiledHeader.h" +#include "Utilities/HashMap.h" #include "wxHelpers.h" #include @@ -276,12 +277,21 @@ void pxTextWrapperBase::Wrap( const wxWindow *win, const wxString& text, int wid } } -////////////////////////////////////////////////////////////////////////////////////////// -// +HashTools::HashMap< wxWindowID, int > m_DialogIdents( 0, wxID_ANY ); + +bool pxDialogExists( wxWindowID id ) +{ + int dest = 0; + m_DialogIdents.TryGetValue( id, dest ); + return (dest > 0); +} + wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos, const wxSize& size ) : wxDialog( parent, id, title, pos, size , wxDEFAULT_DIALOG_STYLE), //, (wxCAPTION | wxMAXIMIZE | wxCLOSE_BOX | wxRESIZE_BORDER) ), // flags for resizable dialogs, currently unused. m_hasContextHelp( hasContextHelp ) { + ++m_DialogIdents[GetId()]; + if( hasContextHelp ) wxHelpProvider::Set( new wxSimpleHelpProvider() ); @@ -291,6 +301,12 @@ wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxStr // any good. } +wxDialogWithHelpers::~wxDialogWithHelpers() +{ + --m_DialogIdents[GetId()]; + wxASSERT( m_DialogIdents[GetId()] >= 0 ); +} + // ------------------------------------------------------------------------ // Creates a new checkbox and adds it to the specified sizer/parent combo, with optional tooltip. // Uses the default spacer setting for adding checkboxes, and the tooltip (if specified) is applied diff --git a/pcsx2/gui/wxHelpers.h b/pcsx2/gui/wxHelpers.h index befdc76524..a9f92f579c 100644 --- a/pcsx2/gui/wxHelpers.h +++ b/pcsx2/gui/wxHelpers.h @@ -55,6 +55,7 @@ protected: public: wxDialogWithHelpers(wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize ); + virtual ~wxDialogWithHelpers() throw(); wxCheckBox& AddCheckBox( wxSizer& sizer, const wxString& label, const wxString& subtext=wxEmptyString, const wxString& tooltip=wxEmptyString ); wxStaticText& AddStaticText(wxSizer& sizer, const wxString& label, int alignFlags=wxALIGN_CENTRE, int size=wxDefaultCoord ); @@ -88,3 +89,6 @@ protected: m_StartNewRadioGroup = true; } }; + +extern bool pxDialogExists( wxWindowID id ); + diff --git a/pcsx2/pcsxAbout.bmp b/pcsx2/pcsxAbout.bmp deleted file mode 100644 index 3d6accb181..0000000000 Binary files a/pcsx2/pcsxAbout.bmp and /dev/null differ diff --git a/pcsx2/ps2/BiosTools.cpp b/pcsx2/ps2/BiosTools.cpp index b089f1986b..413a545399 100644 --- a/pcsx2/ps2/BiosTools.cpp +++ b/pcsx2/ps2/BiosTools.cpp @@ -235,7 +235,7 @@ bool IsBIOS(const wxString& filename, wxString& description) 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" : L"" + (aROMVER[5]=='C') ? L"Console" : (aROMVER[5]=='D') ? L"Devel" : wxEmptyString ); found = true; } diff --git a/pcsx2/ps2/CoreEmuThread.cpp b/pcsx2/ps2/CoreEmuThread.cpp index cb30fbf08a..016847616e 100644 --- a/pcsx2/ps2/CoreEmuThread.cpp +++ b/pcsx2/ps2/CoreEmuThread.cpp @@ -35,10 +35,10 @@ CoreEmuThread& CoreEmuThread::Get() void CoreEmuThread::CpuInitializeMess() { - GetPluginManager().Open(); + m_plugins.Open(); cpuReset(); SysClearExecutionCache(); - GetPluginManager().Open(); + m_plugins.Open(); if( StateRecovery::HasState() ) { @@ -133,53 +133,61 @@ sptr CoreEmuThread::ExecuteTask() void CoreEmuThread::StateCheck() { + ScopedLock locker( m_lock_ExecMode ); + switch( m_ExecMode ) { + case ExecMode_NoThreadYet: case ExecMode_Idle: // threads should never have an idle execution state set while the // thread is in any way active or alive. DevAssert( false, "Invalid execution state detected." ); break; - // These are not the case statements you're looking for. Move along. case ExecMode_Running: pthread_testcancel(); break; case ExecMode_Suspending: { - ScopedLock locker( m_lock_ExecMode ); + m_plugins.Close(); m_ExecMode = ExecMode_Suspended; m_SuspendEvent.Post(); - } + } + // fall through... case ExecMode_Suspended: + m_lock_ExecMode.Unlock(); while( m_ExecMode == ExecMode_Suspended ) m_ResumeEvent.WaitGui(); + + m_plugins.Open(); break; } } -CoreEmuThread::CoreEmuThread() : - m_ExecMode( ExecMode_Idle ) -, m_ResumeEvent() -, m_SuspendEvent() +CoreEmuThread::CoreEmuThread( PluginManager& plugins ) : + m_ExecMode( ExecMode_NoThreadYet ) +, m_lock_ExecMode() + , m_resetRecompilers( false ) , m_resetProfilers( false ) -, m_lock_ExecMode() +, m_plugins( plugins ) +, m_ResumeEvent() +, m_SuspendEvent() + { - PersistentThread::Start(); } // Invoked by the pthread_exit or pthread_cancel void CoreEmuThread::DoThreadCleanup() { - GetPluginManager().Close(); + m_plugins.Shutdown(); PersistentThread::DoThreadCleanup(); } -CoreEmuThread::~CoreEmuThread() +CoreEmuThread::~CoreEmuThread() throw() { PersistentThread::Cancel(); } @@ -187,13 +195,26 @@ CoreEmuThread::~CoreEmuThread() // Resumes the core execution state, or does nothing is the core is already running. If // settings were changed, resets will be performed as needed and emulation state resumed from // memory savestates. +// +// Exceptions (can occur on first call only): +// PluginInitError - thrown if a plugin fails init (init is performed on the current thread +// on the first time the thread is resumed from it's initial idle state) +// ThreadCreationError - Insufficient system resources to create thread. +// void CoreEmuThread::Resume() { - if( IsSelf() || !IsRunning() ) return; - + if( IsSelf() ) return; + { ScopedLock locker( m_lock_ExecMode ); + if( m_ExecMode == ExecMode_NoThreadYet ) + { + m_plugins.Init(); + Start(); + m_ExecMode = ExecMode_Idle; + } + if( m_ExecMode == ExecMode_Running ) return; diff --git a/pcsx2/ps2/CoreEmuThread.h b/pcsx2/ps2/CoreEmuThread.h index bcd4cf4215..c9e1a811fc 100644 --- a/pcsx2/ps2/CoreEmuThread.h +++ b/pcsx2/ps2/CoreEmuThread.h @@ -19,14 +19,15 @@ using namespace Threading; -///////////////////////////////////////////////////////////////////////////////////////// -// CoreEmuThread -// +// -------------------------------------------------------------------------------------- +// CoreEmuThread class +// -------------------------------------------------------------------------------------- class CoreEmuThread : public PersistentThread { -public: +protected: enum ExecutionMode { + ExecMode_NoThreadYet, ExecMode_Idle, ExecMode_Running, ExecMode_Suspending, @@ -34,22 +35,22 @@ public: }; protected: - volatile ExecutionMode m_ExecMode; + volatile ExecutionMode m_ExecMode; + MutexLock m_lock_ExecMode; - Semaphore m_ResumeEvent; - Semaphore m_SuspendEvent; - - bool m_resetRecompilers; - bool m_resetProfilers; + bool m_resetRecompilers; + bool m_resetProfilers; - MutexLock m_lock_ExecMode; + PluginManager& m_plugins; + Semaphore m_ResumeEvent; + Semaphore m_SuspendEvent; public: static CoreEmuThread& Get(); public: - explicit CoreEmuThread(); - virtual ~CoreEmuThread(); + explicit CoreEmuThread( PluginManager& plugins ); + virtual ~CoreEmuThread() throw(); bool IsSuspended() const { return (m_ExecMode == ExecMode_Suspended); } virtual void Suspend( bool isBlocking = true ); diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 42c759b160..d827e88b81 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -20,7 +20,6 @@ @@ -119,7 +120,6 @@ /> @@ -245,7 +245,7 @@ /> @@ -308,6 +308,7 @@ /> @@ -371,7 +372,6 @@ />