diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h index b37358903c..e0d4e18375 100644 --- a/common/include/Utilities/Exceptions.h +++ b/common/include/Utilities/Exceptions.h @@ -67,25 +67,30 @@ namespace Exception // Translation Note: These exceptions are never translated, except to issue a general // error message to the user (which is specified below). // - class Ps2Generic : public BaseException + class Ps2Generic { + protected: + const wxString m_message_eng; // (untranslated) a "detailed" message of what disastrous thing has occurred! + public: virtual ~Ps2Generic() throw() {} - explicit Ps2Generic( const char* msg="Ps2/MIPS cpu caused a general exception" ) : - BaseException( msg ) { } - explicit Ps2Generic( const wxString& msg_eng, const wxString& msg_xlt=_("Ps2/MIPS cpu caused a general exception") ) : - BaseException( msg_eng, msg_xlt ) { } + explicit Ps2Generic( const char* msg ) : + m_message_eng( wxString::FromUTF8( msg ) ) { } + + explicit Ps2Generic( const wxString& msg=L"Ps2/MIPS cpu caused a general exception" ) : + m_message_eng( msg ) { } virtual u32 GetPc() const=0; virtual bool IsDelaySlot() const=0; + virtual wxString Message() const { return m_message_eng; } }; // --------------------------------------------------------------------------------------- // Generalized Exceptions: RuntimeError / LogicError / AssertionFailure // --------------------------------------------------------------------------------------- - class RuntimeError : public BaseException + class RuntimeError : public virtual BaseException { public: virtual ~RuntimeError() throw() {} @@ -99,7 +104,7 @@ namespace Exception BaseException( msg_eng, msg_xlt ) { } }; - class LogicError : public BaseException + class LogicError : public virtual BaseException { public: virtual ~LogicError() throw() {} @@ -112,15 +117,17 @@ namespace Exception explicit LogicError( const wxString& msg_eng, const wxString& msg_xlt ) : BaseException( msg_eng, msg_xlt ) { } }; - - class AssertionFailure : public LogicError + + class AssertionFailure : public virtual LogicError { public: explicit AssertionFailure( const char* msg=wxLt("Assertion Failure") ) : - LogicError( msg ) {} + LogicError( msg ) + , BaseException( msg ) {} explicit AssertionFailure( const wxString& msg_eng, const wxString& msg_xlt ) : - LogicError( msg_eng, msg_xlt ) { } + LogicError( msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) {} virtual ~AssertionFailure() throw() {} }; @@ -129,40 +136,45 @@ namespace Exception // OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError // --------------------------------------------------------------------------------------- - class OutOfMemory : public RuntimeError + class OutOfMemory : public virtual RuntimeError { public: virtual ~OutOfMemory() throw() {} explicit OutOfMemory( const char* msg=wxLt("Out of memory") ) : - RuntimeError( msg ) {} + RuntimeError( msg ) + , BaseException( msg ) {} explicit OutOfMemory( const wxString& msg_eng, const wxString& msg_xlt=_("Out of memory") ) : - RuntimeError( msg_eng, msg_xlt ) { } + RuntimeError( msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) {} }; // This exception thrown any time an operation is attempted when an object // is in an uninitialized state. // - class InvalidOperation : public LogicError + class InvalidOperation : public virtual LogicError { public: virtual ~InvalidOperation() throw() {} explicit InvalidOperation( const char* msg="Attempted method call is invalid for the current object or program state." ) : - LogicError( msg ) {} + LogicError( msg ) + , BaseException( msg ) {} explicit InvalidOperation( const wxString& msg_eng, const wxString& msg_xlt ) : - LogicError( msg_eng, msg_xlt ) { } + LogicError( msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) {} }; // This exception thrown any time an operation is attempted when an object // is in an uninitialized state. // - class InvalidArgument : public LogicError + class InvalidArgument : public virtual LogicError { public: virtual ~InvalidArgument() throw() {} explicit InvalidArgument( const char* msg="Invalid argument passed to a function." ) : LogicError( msg ) + , BaseException( msg ) { // assertions make debugging easier sometimes. :) wxASSERT_MSG_A( false, msg ); @@ -172,7 +184,7 @@ namespace Exception // Keep those array indexers in bounds when using the SafeArray type, or you'll be // seeing these. // - class IndexBoundsFault : public LogicError + class IndexBoundsFault : public virtual AssertionFailure { public: const wxString ArrayName; @@ -182,14 +194,11 @@ namespace Exception public: virtual ~IndexBoundsFault() throw() {} explicit IndexBoundsFault( const wxString& objname, int index, int arrsize ) : - LogicError( "Index is outside the bounds of an array." ), + AssertionFailure( "Index is outside the bounds of an array." ), ArrayName( objname ), ArrayLength( arrsize ), BadIndex( index ) - { - // assertions make debugging easier sometimes. :) - wxASSERT_MSG( false, L"Index is outside the bounds of an array" ); - } + , BaseException( "Index is outside the bounds of an array." ) {} virtual wxString LogMessage() const; virtual wxString DisplayMessage() const; @@ -200,7 +209,8 @@ namespace Exception public: virtual ~ParseError() throw() {} explicit ParseError( const char* msg="Parse error" ) : - RuntimeError( msg ) {} + RuntimeError( msg ) + , BaseException( msg ) {} }; // --------------------------------------------------------------------------------------- @@ -208,50 +218,22 @@ namespace Exception // HardwareDeficiency / CpuStateShutdown / PluginFailure / ThreadCreationError // --------------------------------------------------------------------------------------- - class HardwareDeficiency : public RuntimeError + class HardwareDeficiency : public virtual RuntimeError { public: explicit HardwareDeficiency( const char* msg=wxLt("Your machine's hardware is incapable of running Pcsx2. Sorry dood.") ) : - RuntimeError( msg ) {} + RuntimeError( msg ) + , BaseException( msg ) {} virtual ~HardwareDeficiency() throw() {} }; - // This exception is thrown by the PS2 emulation (R5900, etc) when bad things happen - // that force the emulation state to terminate. The GUI should handle them by returning - // the user to the GUI. - // - class CpuStateShutdown : public RuntimeError - { - public: - virtual ~CpuStateShutdown() throw() {} - explicit CpuStateShutdown( const char* msg="Unexpected emulation shutdown" ) : - RuntimeError( msg ) {} - - explicit CpuStateShutdown( const wxString& msg_eng, const wxString& msg_xlt=wxString() ) : - RuntimeError( msg_eng, msg_xlt.IsEmpty() ? L"Unexpected emulation shutdown" : msg_xlt ) { } - }; - - class PluginFailure : public RuntimeError - { - public: - wxString plugin_name; // name of the plugin - - virtual ~PluginFailure() throw() {} - - explicit PluginFailure( const char* plugin, const char* msg="%s plugin encountered a critical error" ) : - RuntimeError( msg ) - , plugin_name( wxString::FromAscii(plugin) ) {} - - virtual wxString LogMessage() const; - virtual wxString DisplayMessage() const; - }; - - class ThreadCreationError : public RuntimeError + class ThreadCreationError : public virtual RuntimeError { public: virtual ~ThreadCreationError() throw() {} explicit ThreadCreationError( const char* msg="Thread could not be created." ) : - RuntimeError( msg ) {} + RuntimeError( msg ) + , BaseException( msg ) {} }; // --------------------------------------------------------------------------------------- @@ -263,7 +245,7 @@ namespace Exception // This exception is usually thrown via derived classes, except in the (rare) case of a // generic / unknown error. // - class Stream : public RuntimeError + class Stream : public virtual RuntimeError { public: wxString StreamName; // name of the stream (if applicable) @@ -272,19 +254,22 @@ namespace Exception // copy construct! Stream( const Stream& src ) : - RuntimeError( src ), - StreamName( src.StreamName ) {} + RuntimeError( src ) + , BaseException( src ) + , StreamName( src.StreamName ) {} explicit Stream( const wxString& objname=wxString(), const char* msg="General file operation error" // general error while accessing or operating on a file or stream ) : - RuntimeError( msg ), - StreamName( objname ) {} + RuntimeError( msg ) + , BaseException( msg ) + , StreamName( objname ) {} explicit Stream( const wxString& objname, const wxString& msg_eng, const wxString& msg_xlt=_("General file operation error") ) : - RuntimeError( msg_eng, msg_xlt ), - StreamName( objname ) {} + RuntimeError( msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) + , StreamName( objname ) {} virtual wxString LogMessage() const; virtual wxString DisplayMessage() const; @@ -294,7 +279,7 @@ namespace Exception // connection, or anything else that would indicate a failure to read the data after the // stream was successfully opened. // - class BadStream : public Stream + class BadStream : public virtual Stream { public: virtual ~BadStream() throw() {} @@ -302,12 +287,17 @@ namespace Exception const wxString& objname=wxString(), const char* msg=wxLt("File data is corrupted or incomplete, or the stream connection closed unexpectedly") ) : - Stream( objname, msg ) {} + Stream( objname, msg ) + , BaseException( msg ) {} + + explicit BadStream( const wxString& objname, const wxString& msg_eng, const wxString& msg_xlt ) : + Stream( objname, msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) {} }; // A generic exception for odd-ball stream creation errors. // - class CreateStream : public Stream + class CreateStream : public virtual Stream { public: virtual ~CreateStream() throw() {} @@ -315,55 +305,63 @@ namespace Exception explicit CreateStream( const char* objname, const char* msg=wxLt("File could not be created or opened") ) : - Stream( wxString::FromAscii( objname ), msg ) {} + Stream( wxString::FromAscii( objname ), msg ) + , BaseException( msg ) {} explicit CreateStream( const wxString& objname=wxString(), const char* msg=wxLt("File could not be created or opened") ) : - Stream( objname, msg ) {} + Stream( objname, msg ) + , BaseException( msg ) {} explicit CreateStream( const wxString& objname, const wxString& msg_eng, const wxString& msg_xlt ) : - Stream( objname, msg_eng, msg_xlt ) {} + Stream( objname, msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) {} }; // Exception thrown when an attempt to open a non-existent file is made. // (this exception can also mean file permissions are invalid) // - class FileNotFound : public CreateStream + class FileNotFound : public virtual CreateStream { public: virtual ~FileNotFound() throw() {} explicit FileNotFound( const wxString& objname=wxString(), - const char* msg="File not found" ) : - CreateStream( objname, msg ) {} - - explicit FileNotFound( const wxString& objname, const wxString& msg_eng, const wxString& msg_xlt ) : - CreateStream( objname, msg_eng, msg_xlt ) {} + const char* msg="File not found" ) + : + CreateStream( objname, msg ) + , BaseException( msg ) {} + explicit FileNotFound( const wxString& objname, const wxString& msg_eng, const wxString& msg_xlt ) : + CreateStream( objname, msg_eng, msg_xlt ) + , BaseException( msg_eng, msg_xlt ) {} }; - class AccessDenied : public CreateStream + class AccessDenied : public virtual CreateStream { public: virtual ~AccessDenied() throw() {} explicit AccessDenied( const wxString& objname=wxString(), - const char* msg="Permission denied to file" ) : - CreateStream( objname, msg ) {} + const char* msg="Permission denied to file" ) + : + CreateStream( objname, msg ) + , BaseException( msg ) {} }; // EndOfStream can be used either as an error, or used just as a shortcut for manual // feof checks. // - class EndOfStream : public Stream + class EndOfStream : public virtual Stream { public: virtual ~EndOfStream() throw() {} explicit EndOfStream( const wxString& objname, const char* msg=wxLt("Unexpected end of file") ) : - Stream( objname, msg ) {} + Stream( objname, msg ) + , BaseException( msg ) {} }; // --------------------------------------------------------------------------------------- @@ -374,19 +372,20 @@ namespace Exception // Exception thrown when a corrupted or truncated savestate is encountered. // - class BadSavedState : public BadStream + class BadSavedState : public virtual BadStream { public: virtual ~BadSavedState() throw() {} explicit BadSavedState( const wxString& objname=wxString(), const char* msg="Savestate data is corrupted" ) : // or incomplete - BadStream( objname, msg ) {} + BadStream( objname, msg ) + , BaseException( msg ) {} }; // Exception thrown by SaveState class when a critical plugin or gzread // - class FreezePluginFailure : public RuntimeError + class FreezePluginFailure : public virtual RuntimeError { public: wxString plugin_name; // name of the plugin @@ -396,39 +395,28 @@ namespace Exception explicit FreezePluginFailure( const char* plugin, const char* action, const wxString& msg_xlt=_("Plugin error occurred while loading/saving state") ) : - RuntimeError( wxString(), msg_xlt ) // LogMessage / DisplayMessage build their own messages + RuntimeError( wxEmptyString, msg_xlt ) // LogMessage / DisplayMessage build their own messages + , BaseException( wxEmptyString, msg_xlt ) , plugin_name( wxString::FromAscii(plugin) ) - , freeze_action( wxString::FromAscii(action) ){} - + , freeze_action( wxString::FromAscii(action) ) {} + virtual wxString LogMessage() const; virtual wxString DisplayMessage() const; }; - // A recoverable error thrown when a savestate load fails prior to actually modifying - // the current emulation state. Recoverable errors are always thrown from the SaveState - // object construction (and never from Freeze methods). - // - class StateLoadError : public RuntimeError - { - public: - virtual ~StateLoadError() throw() {} - explicit StateLoadError( const char* msg="Recoverable savestate load error" ) : - RuntimeError( msg ) {} - }; - // A recoverable exception thrown when the savestate being loaded isn't supported. // - class UnsupportedStateVersion : public StateLoadError + class UnsupportedStateVersion : public virtual BadSavedState { public: u32 Version; // version number of the unsupported state. public: virtual ~UnsupportedStateVersion() throw() {} - explicit UnsupportedStateVersion( int version ) : - StateLoadError(), - Version( version ) - {} + explicit UnsupportedStateVersion( int version, const wxString& objname=wxEmptyString ) : + BadSavedState( objname ) + , BaseException( wxEmptyString, wxEmptyString ) + , Version( version ) {} virtual wxString LogMessage() const; virtual wxString DisplayMessage() const; @@ -438,7 +426,7 @@ namespace Exception // CRC returned by the Cdvd driver. // [feature not implemented yet] // - class StateCrcMismatch : public StateLoadError + class StateCrcMismatch : public virtual BadSavedState { public: u32 Crc_Savestate; @@ -446,8 +434,9 @@ namespace Exception public: virtual ~StateCrcMismatch() throw() {} - explicit StateCrcMismatch( u32 crc_save, u32 crc_cdvd ) - : StateLoadError() + explicit StateCrcMismatch( u32 crc_save, u32 crc_cdvd, const wxString& objname=wxEmptyString ) : + BadSavedState( objname ) + , BaseException( wxEmptyString, wxEmptyString ) , Crc_Savestate( crc_save ) , Crc_Cdvd( crc_cdvd ) {} diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index fda1c78468..9e48b4cf67 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -146,6 +146,8 @@ namespace Threading virtual bool IsRunning() const; virtual sptr Block(); + + bool IsSelf() const; protected: // Used to dispatch the thread callback function. diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp index 390c826779..b0af49dad3 100644 --- a/common/src/Utilities/Exceptions.cpp +++ b/common/src/Utilities/Exceptions.cpp @@ -83,20 +83,6 @@ namespace Exception return m_message + L"\n\n" + StreamName; } - // ------------------------------------------------------------------------ - wxString PluginFailure::LogMessage() const - { - return wxsFormat( - L"%s plugin has encountered an error.\n\n", - plugin_name.c_str() - ) + m_stacktrace; - } - - wxString PluginFailure::DisplayMessage() const - { - return wxsFormat( m_message, plugin_name.c_str() ); - } - // ------------------------------------------------------------------------ wxString FreezePluginFailure::LogMessage() const { @@ -124,7 +110,7 @@ namespace Exception // m_message contains a recoverable savestate error which is helpful to the user. return wxsFormat( m_message + L"\n\n" + - wxsFormat( L"Unknown savestate version: 0x%x", Version ) + wxsFormat( _("Cannot load savestate. It is of an unknown or unsupported version."), Version ) ); } diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index 23a2fb8065..43c0923ef1 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -45,6 +45,8 @@ namespace Threading void PersistentThread::Start() { + if( m_running ) return; + if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) throw Exception::ThreadCreationError(); @@ -89,8 +91,13 @@ namespace Threading pthread_join( m_thread, (void**)&m_returncode ); return m_returncode; } + + bool PersistentThread::IsSelf() const + { + return pthread_self() == m_thread; + } - bool Exists( pthread_t pid ) + bool PersistentThread::Exists( pthread_t pid ) { // passing 0 to pthread_kill is a NOP, and returns the status of the thread only. return ( ESRCH != pthread_kill( pid, 0 ) ); diff --git a/pcsx2/CDVD/IsoFStools.h b/pcsx2/CDVD/IsoFStools.h index 93e38000bc..5d461fc670 100644 --- a/pcsx2/CDVD/IsoFStools.h +++ b/pcsx2/CDVD/IsoFStools.h @@ -33,8 +33,7 @@ int IsoFS_getDirectories(TocEntry tocEntry[], int req_entries); #define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */ #if defined(_MSC_VER) -#pragma pack(1) -#pragma warning(disable:4996) //ignore the stricmp deprecated warning +# pragma pack(1) #endif struct rootDirTocHeader diff --git a/pcsx2/Common.h b/pcsx2/Common.h index 0b6b44dc58..e37d3289f1 100644 --- a/pcsx2/Common.h +++ b/pcsx2/Common.h @@ -16,8 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef __COMMON_H__ -#define __COMMON_H__ +#pragma once #include "Pcsx2Defs.h" @@ -42,4 +41,5 @@ #include "Elfheader.h" #include "Patch.h" -#endif /* __COMMON_H__ */ +extern bool EmulationInProgress(); + diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index c1d416f291..07b20f68ab 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -442,16 +442,22 @@ __forceinline void rcntUpdate_vSync() s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle); if( diff < vsyncCounter.CycleT ) return; - //iopBranchAction = 1; if (vsyncCounter.Mode == MODE_VSYNC) { + eeRecIsReset = false; + CoreEmuThread::Get().StateCheck(); + if( eeRecIsReset ) + { + eeRecIsReset = false; + cpuSetBranch(); + throw Exception::RecompilerReset(); + } + VSyncEnd(vsyncCounter.sCycle); vsyncCounter.sCycle += vSyncInfo.Blank; vsyncCounter.CycleT = vSyncInfo.Render; vsyncCounter.Mode = MODE_VRENDER; - - CoreEmuThread::Get().StateCheck(); } else // VSYNC end / VRENDER begin { diff --git a/pcsx2/Elfheader.cpp b/pcsx2/Elfheader.cpp index c84f7cad7e..d138fb148d 100644 --- a/pcsx2/Elfheader.cpp +++ b/pcsx2/Elfheader.cpp @@ -23,10 +23,6 @@ using namespace std; -#ifdef _MSC_VER -#pragma warning(disable:4996) //ignore the stricmp deprecated warning -#endif - u32 ElfCRC; struct ELF_HEADER { diff --git a/pcsx2/HostGui.h b/pcsx2/HostGui.h index 7005389755..05e6506161 100644 --- a/pcsx2/HostGui.h +++ b/pcsx2/HostGui.h @@ -68,8 +68,6 @@ extern void States_Load( int num ); extern void States_Save( int num ); extern bool States_isSlotUsed(int num); -extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (set to false on reset) - ////////////////////////////////////////////////////////////////////////////////////////// // External Gui APIs (platform specific) // @@ -87,14 +85,6 @@ extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (se // as keyboard events, menu/status updates, and cpu execution invocation. namespace HostGui { - // Signal for informing the GUI that the saveslot status has been altered. - // The guis hould re-enumerate the slot information displayed in the menu, or wherever. - extern void ResetMenuSlots(); - - // Signals to the GUI that execution of the emulator should begin. This can be either - // a blocking or non-blocking (threaded) action. - extern void BeginExecution(); - // Signals the gui with a keystroke. Handle or discard or dispatch, or enjoy its // pleasant flavor. extern void __fastcall KeyEvent( keyEvent* ev ); diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 773fd86164..479a0ab2c3 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -1088,7 +1088,6 @@ void mtgsWaitGS() void mtgsOpen() { // better not be a thread already running, yo! - assert( mtgsThread == NULL ); if( mtgsThread != NULL ) return; mtgsThread = new mtgsThreadObject(); diff --git a/pcsx2/MemoryCard.cpp b/pcsx2/MemoryCard.cpp index d8d4100d74..c76b952cb3 100644 --- a/pcsx2/MemoryCard.cpp +++ b/pcsx2/MemoryCard.cpp @@ -73,7 +73,7 @@ void MemoryCard::Load( uint mcd ) if( !fname.FileExists() ) Create( str ); -#ifdef WIN32 +#ifdef _WIN32 NTFS_CompressFile( str, g_Conf->McdEnableNTFS ); #endif diff --git a/pcsx2/Misc.cpp b/pcsx2/Misc.cpp index 1b64c31e0e..cbc7b09eee 100644 --- a/pcsx2/Misc.cpp +++ b/pcsx2/Misc.cpp @@ -41,21 +41,13 @@ #include "COP0.h" #include "Cache.h" -#include "Dump.h" #include "AppConfig.h" using namespace std; using namespace R5900; -static int g_Pcsx2Recording = 0; // true 1 if recording video and sound - -// renderswitch - tells GSdx to go into dx9 sw if "renderswitch" is set. -bool renderswitch = false; - struct KeyModifiers keymodifiers = {false, false, false, false}; -#define NUM_STATES 10 -int StatesC = 0; extern wxString strgametitle; @@ -245,166 +237,6 @@ char* mystrlwr( char* string ) return string; } -static wxString GetGSStateFilename() -{ - return Path::Combine( g_Conf->Folders.Savestates, wxsFormat( L"/%8.8X.%d.gs", ElfCRC, StatesC ) ); -} - -void ProcessFKeys(int fkey, struct KeyModifiers *keymod) -{ - assert(fkey >= 1 && fkey <= 12 ); - - switch(fkey) - { - case 1: - try - { - gzSavingState( SaveState::GetFilename( StatesC ) ).FreezeAll(); - HostGui::ResetMenuSlots(); - } - catch( Exception::BaseException& ex ) - { - // 99% of the time this is a file permission error and the - // cpu state is intact so just display a passive msg to console without - // raising an exception. - - Console::Error( "Error! Could not save state to slot %d", params StatesC ); - Console::Error( ex.LogMessage() ); - } - break; - - case 2: - if( keymod->shift ) - StatesC = (StatesC+NUM_STATES-1) % NUM_STATES; - else - StatesC = (StatesC+1) % NUM_STATES; - - Console::Notice( " > Selected savestate slot %d", params StatesC); - - if( GSchangeSaveState != NULL ) - GSchangeSaveState(StatesC, SaveState::GetFilename(StatesC).mb_str()); - break; - - case 3: - try - { - gzLoadingState joe( SaveState::GetFilename( StatesC ) ); // throws exception on version mismatch - cpuReset(); - SysClearExecutionCache(); - joe.FreezeAll(); - } - catch( Exception::StateLoadError& ) - { - // At this point the cpu hasn't been reset, so we can return - // control to the user safely... (and silently) - } - catch( Exception::FileNotFound& ) - { - Console::Notice( "Saveslot %d cannot be loaded; slot does not exist (file not found)", params StatesC ); - } - catch( Exception::RuntimeError& ex ) - { - // This is the bad one. Chances are the cpu has been reset, so emulation has - // to be aborted. Sorry user! We'll give you some info for your trouble: - - ClosePlugins( true ); - - throw Exception::CpuStateShutdown( - // english log message: - wxsFormat( L"Error! Could not load from saveslot %d\n", StatesC ) + ex.LogMessage(), - - // translated message: - wxsFormat( _("Error loading saveslot %d. Emulator reset."), StatesC ) - ); - } - break; - - case 4: - // FIXME : Reimplement framelimiting using new oolean system - //CycleFrameLimit(keymod->shift ? -1 : 1); - break; - - // note: VK_F5-VK_F7 are reserved for GS - case 8: - GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() ); - break; - - case 9: //gsdx "on the fly" renderer switching - StateRecovery::MakeGsOnly(); - g_EmulationInProgress = false; - - // close GS and PAD together, because the PAD depends on the GS window. - g_plugins->Close( PluginId_PAD ); - safe_delete( mtgsThread ); - - renderswitch = !renderswitch; - StateRecovery::Recover(); - HostGui::BeginExecution(); // also sets g_EmulationInProgress to true later - break; - -#ifdef PCSX2_DEVBUILD - case 10: - // There's likely a better way to implement this, but this seemed useful. - // I might add turning EE, VU0, and VU1 recs on and off by hotkey at some point, too. - // --arcum42 - enableLogging = !enableLogging; - - if (enableLogging) - GSprintf(10, "Logging Enabled."); - else - GSprintf(10,"Logging Disabled."); - - break; - - case 11: - Console::Notice( "Cannot make gsstates in MTGS mode" ); - - // fixme : fix up gsstate mess and make it mtgs compatible -- air -#ifdef _STGS_GSSTATE_CODE - wxString Text; - if( strgametitle[0] != 0 ) - { - // only take the first two words - wxString gsText; - - wxStringTokenizer parts( strgametitle, L" " ); - - wxString name( parts.GetNextToken() ); // first part - wxString part2( parts.GetNextToken() ); - - if( !!part2 ) - name += L"_" + part2; - - gsText.Printf( L"%s.%d.gs", name.c_str(), StatesC ); - Text = Path::Combine( g_Conf->Folders.Savestates, gsText ); - } - else - { - Text = GetGSStateFilename(); - } - break; -#endif -#endif - - case 12: - if( keymod->shift ) - { -#ifdef PCSX2_DEVBUILD - iDumpRegisters(cpuRegs.pc, 0); - Console::Notice("hardware registers dumped EE:%x, IOP:%x\n", params cpuRegs.pc, psxRegs.pc); -#endif - } - else - { - g_Pcsx2Recording ^= 1; - - mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0); - if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL); - } - break; - } -} - void _memset16_unaligned( void* dest, u16 data, size_t size ) { jASSUME( (size & 0x1) == 0 ); diff --git a/pcsx2/Misc.h b/pcsx2/Misc.h index 31cfac4368..9ea1bde7e0 100644 --- a/pcsx2/Misc.h +++ b/pcsx2/Misc.h @@ -37,9 +37,6 @@ extern int GetPS2ElfName( wxString& dest ); // Used in Misc, System, Linux, CDVD void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR); extern u32 g_sseVUMXCSR, g_sseMXCSR; -// Used in Misc,and Windows/Linux files. -extern void ProcessFKeys(int fkey, struct KeyModifiers *keymod); // processes fkey related commands value 1-12 - void SaveGSState(const wxString& file); void LoadGSState(const wxString& file); diff --git a/pcsx2/Patch.cpp b/pcsx2/Patch.cpp index ec05cd6dfa..8e17d8f25f 100644 --- a/pcsx2/Patch.cpp +++ b/pcsx2/Patch.cpp @@ -27,10 +27,6 @@ #include "Patch.h" #include "VU.h" -#ifdef _MSC_VER -#pragma warning(disable:4996) //ignore the stricmp deprecated warning -#endif - IniPatch patch[ MAX_PATCH ]; u32 SkipCount=0, IterationCount=0; diff --git a/pcsx2/Plugins.cpp b/pcsx2/PluginManager.cpp similarity index 76% rename from pcsx2/Plugins.cpp rename to pcsx2/PluginManager.cpp index 814c31c1ea..534b35e008 100644 --- a/pcsx2/Plugins.cpp +++ b/pcsx2/PluginManager.cpp @@ -17,7 +17,6 @@ */ #include "PrecompiledHeader.h" -#include "Utilities/RedtapeWindows.h" #include "Utilities/ScopedPtr.h" #include @@ -27,7 +26,6 @@ #include "GS.h" #include "HostGui.h" #include "CDVD/CDVDisoReader.h" -#include "AppConfig.h" // ---------------------------------------------------------------------------- // Yay, order of this array shouldn't be important. :) @@ -49,24 +47,6 @@ const PluginInfo tbl_PluginInfo[] = }; -int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) -{ - wxScopedPtr placebo; - wxArrayString* realdest = dest; - if( realdest == NULL ) - placebo.reset( realdest = new wxArrayString() ); - -#ifdef _WIN32 - return searchpath.Exists() ? - wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( L"*%s", wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0; -#else - return searchpath.Exists() ? - wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( L"*%s*", wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0; - -#endif -} - - typedef void CALLBACK VoidMethod(); typedef void CALLBACK vMeth(); // shorthand for VoidMethod @@ -166,21 +146,7 @@ _PADkeyEvent PADkeyEvent; _PADsetSlot PADsetSlot; _PADqueryMtap PADqueryMtap; -void PAD_update( u32 padslot ) { } - -// SIO[2] -/* -_SIOinit SIOinit[2][9]; -_SIOopen SIOopen[2][9]; -_SIOclose SIOclose[2][9]; -_SIOshutdown SIOshutdown[2][9]; -_SIOstartPoll SIOstartPoll[2][9]; -_SIOpoll SIOpoll[2][9]; -_SIOquery SIOquery[2][9]; - -_SIOconfigure SIOconfigure[2][9]; -_SIOtest SIOtest[2][9]; -_SIOabout SIOabout[2][9];*/ +static void PAD_update( u32 padslot ) { } // SPU2 _SPU2open SPU2open; @@ -559,12 +525,35 @@ static const LegacyApi_OptMethod* const s_MethMessOpt[] = PluginManager *g_plugins = NULL; -Exception::NotPcsxPlugin::NotPcsxPlugin( const wxString& objname ) : - Stream( objname, wxLt("File is not a PCSX2 plugin") ) {} +////////////////////////////////////////////////////////////////////////////////////////// -Exception::NotPcsxPlugin::NotPcsxPlugin( const PluginsEnum_t& pid ) : - Stream( wxString::FromUTF8( tbl_PluginInfo[pid].shortname ), wxLt("File is not a PCSX2 plugin") ) {} +Exception::InvalidPluginConfigured::InvalidPluginConfigured( const PluginsEnum_t& pid, const wxString& objname, const char* eng ) : + BadStream( objname, eng ) +, PluginError( pid ) +, BaseException( eng ) +{} +Exception::InvalidPluginConfigured::InvalidPluginConfigured( const PluginsEnum_t& pid, const wxString& objname, + const wxString& eng_msg, const wxString& xlt_msg ) : + BadStream( objname, eng_msg, xlt_msg ) +, PluginError( pid ) +, BaseException( eng_msg, xlt_msg ) +{} + +wxString Exception::PluginFailure::LogMessage() const +{ + return wxsFormat( + L"%s plugin has encountered an error.\n\n", + plugin_name.c_str() + ) + m_stacktrace; +} + +wxString Exception::PluginFailure::DisplayMessage() const +{ + return wxsFormat( m_message, plugin_name.c_str() ); +} + +////////////////////////////////////////////////////////////////////////////////////////// PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] ) { @@ -574,15 +563,20 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] ) const PluginsEnum_t pid = pi->id; if( folders[pid].IsEmpty() ) - throw Exception::InvalidArgument( "Invalid argument in PluginManager::ctor: Empty plugin filename." ); + throw Exception::InvalidArgument( "Empty plugin filename." ); m_info[pid].Filename = folders[pid]; if( !wxFile::Exists( folders[pid] ) ) - throw Exception::FileNotFound( folders[pid] ); + throw Exception::InvalidPluginConfigured( pid, folders[pid], + L"Plugin file not found", + _("The configured plugin file was not found") + ); if( !m_info[pid].Lib.Load( folders[pid] ) ) - throw Exception::NotPcsxPlugin( folders[pid] ); + throw Exception::InvalidPluginConfigured( pid, folders[pid], + wxLt("Configured plugin file is not a valid dynamic library") + ); // Try to enumerate the new v2.0 plugin interface first. // If that fails, fall back on the old style interface. @@ -599,7 +593,7 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] ) // Bind Optional Functions // (leave pointer null and do not generate error) } - + // Hack for PAD's stupid parameter passed on Init PADinit = (_PADinit)m_info[PluginId_PAD].CommonBindings.Init; m_info[PluginId_PAD].CommonBindings.Init = _hack_PADinit; @@ -609,7 +603,7 @@ PluginManager::~PluginManager() { Close(); Shutdown(); - + // All library unloading done automatically. } @@ -623,13 +617,16 @@ void PluginManager::BindCommon( PluginsEnum_t pid ) while( current->MethodName != NULL ) { *target = (VoidMethod*)m_info[pid].Lib.GetSymbol( current->GetMethodName( pid ) ); - + if( *target == NULL ) *target = current->Fallback; if( *target == NULL ) - throw Exception::NotPcsxPlugin( pid ); - + { + throw Exception::InvalidPluginConfigured( pid, m_info[pid].Filename, + wxLt( "Configured plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2." ) ); + } + target++; current++; } @@ -650,7 +647,10 @@ void PluginManager::BindRequired( PluginsEnum_t pid ) *(current->Dest) = current->Fallback; if( *(current->Dest) == NULL ) - throw Exception::NotPcsxPlugin( pid ); + { + throw Exception::InvalidPluginConfigured( pid, m_info[pid].Filename, + wxLt( "Configured plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2." ) ); + } current++; } @@ -688,8 +688,18 @@ static bool OpenPlugin_CDVD() static bool OpenPlugin_GS() { - // Abusing the isMultiThread parameter for that so we don't need a new callback - return !GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 ); + if( !mtgsThread->IsSelf() ) + { + if( mtgsThread == NULL ) + mtgsOpen(); // mtgsOpen raises its own exception on error + + return true; + } + else + return !GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 ); + + // Note: rederswitch is us abusing the isMultiThread parameter for that so + // we don't need a new callback } static bool OpenPlugin_PAD() @@ -739,7 +749,7 @@ static bool OpenPlugin_FW() void PluginManager::Open( PluginsEnum_t pid ) { if( m_info[pid].IsOpened ) return; - + // Each Open needs to be called explicitly. >_< bool result = true; @@ -759,19 +769,44 @@ void PluginManager::Open( PluginsEnum_t pid ) m_info[pid].IsOpened = true; } +void PluginManager::Open() +{ + const PluginInfo* pi = tbl_PluginInfo-1; + while( ++pi, pi->shortname != NULL ) + g_plugins->Open( pi->id ); +} + void PluginManager::Close( PluginsEnum_t pid ) { if( !m_info[pid].IsOpened ) return; + if( pid == PluginId_GS ) + { + if( mtgsThread == NULL ) return; + + if( !mtgsThread->IsSelf() ) + { + // force-close PAD before GS, because the PAD depends on the GS window. + Close( PluginId_PAD ); + + safe_delete( mtgsThread ); + return; + } + } + m_info[pid].IsOpened = false; m_info[pid].CommonBindings.Close(); } -void PluginManager::Close() +void PluginManager::Close( bool closegs ) { - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) - Close( pi->id ); + // Close plugins in reverse order of the initialization procedure. + + for( int i=PluginId_Count-1; i>=0; --i ) + { + if( closegs || (tbl_PluginInfo[i].id != PluginId_GS) ) + Close( tbl_PluginInfo[i].id ); + } } // Initializes all plugins. Plugin initialization should be done once for every new emulation @@ -806,10 +841,12 @@ void PluginManager::Shutdown() { Close(); - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) + // Shutdown plugins in reverse order (probably doesn't matter... + // ... but what the heck, right?) + + for( int i=PluginId_Count-1; i>=0; --i ) { - const PluginsEnum_t pid = pi->id; + const PluginsEnum_t pid = tbl_PluginInfo[i].id; if( !m_info[pid].IsInitialized ) continue; m_info[pid].IsInitialized = false; m_info[pid].CommonBindings.Shutdown(); @@ -869,7 +906,7 @@ PluginManager* PluginManager_Create( const wxString (&folders)[PluginId_Count] ) PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] ) { wxString passins[PluginId_Count]; - + const PluginInfo* pi = tbl_PluginInfo-1; while( ++pi, pi->shortname != NULL ) passins[pi->id] = folders[pi->id]; @@ -877,305 +914,12 @@ PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] ) return PluginManager_Create( passins ); } -void LoadPlugins() +static PluginManagerBase s_pluginman_placebo; + +// retrieves a handle to the current plugin manager. Plugin manager is assumed to be valid, +// and debug-level assertions are performed on the validity of the handle. +PluginManagerBase& GetPluginManager() { - wxString passins[PluginId_Count]; - - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) - passins[pi->id] = g_Conf->FullpathTo( pi->id ); - - g_plugins = PluginManager_Create( passins ); + if( g_plugins == NULL ) return s_pluginman_placebo; + return *g_plugins; } - -void InitPlugins() -{ - if( g_plugins == NULL ) - LoadPlugins(); - - g_plugins->Init(); -} - -// 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() -{ - if( g_plugins == NULL ) - InitPlugins(); - - mtgsOpen(); - - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) - g_plugins->Open( pi->id ); -} - - -void ClosePlugins( bool closegs ) -{ - if( g_plugins == NULL ) return; - safe_delete( mtgsThread ); - - const PluginInfo* pi = tbl_PluginInfo-1; - while( ++pi, pi->shortname != NULL ) - g_plugins->Close( pi->id ); -} - -void ShutdownPlugins() -{ - if( g_plugins == NULL ) return; - ClosePlugins( true ); - g_plugins->Shutdown(); -} - -void ReleasePlugins() -{ - safe_delete( g_plugins ); -} - -#ifdef _not_wxWidgets_Land_ - - -bool OpenGS() -{ - GSdriverInfo info; - - if (!OpenStatus.GS) - { - if (ReportError2(gsOpen(), "GS")) - { - ClosePlugins(true); - return false; - } - - //Get the user input. - if (GSgetDriverInfo) - { - GSgetDriverInfo(&info); - if (PAD1gsDriverInfo) PAD1gsDriverInfo(&info); - if (PAD2gsDriverInfo) PAD2gsDriverInfo(&info); - } - OpenStatus.GS = true; - } - return true; -} - -bool OpenCDVD( const char* pTitleFilename ) -{ - // if this assertion fails it means you didn't call CDVDsys_ChangeSource. You should. - // You really should. Really. - jASSUME( CDVD != NULL ); - - // Don't repetitively open the CDVD plugin if directly loading an elf file and open failed once already. - if (!OpenStatus.CDVD) - { - CDVD->newDiskCB( cdvdNewDiskCB ); - - if( (pTitleFilename == NULL) && !cdvd_FileNameParam.IsEmpty() ) - pTitleFilename = cdvd_FileNameParam.c_str(); - - if (DoCDVDopen(pTitleFilename) != 0) - { - Msgbox::Alert("Error Opening CDVD Plugin"); - ClosePlugins(true); - return false; - } - - if( cdvd_FileNameParam.IsEmpty() && (pTitleFilename != NULL) ) - cdvd_FileNameParam = pTitleFilename; - - OpenStatus.CDVD = true; - } - return true; -} - -bool OpenPAD1() -{ - if (!OpenStatus.PAD1) - { - if (ReportError2(PAD1open((void *)&pDsp), "PAD1")) - { - ClosePlugins(true); - return false; - } - OpenStatus.PAD1 = true; - } - return true; -} - -bool OpenPAD2() -{ - if (!OpenStatus.PAD2) - { - if (ReportError2(PAD2open((void *)&pDsp), "PAD2")) - { - ClosePlugins(true); - return false; - } - OpenStatus.PAD2 = true; - } - return true; -} - -bool OpenSPU2() -{ - if (!OpenStatus.SPU2) - { - SPU2irqCallback(spu2Irq,spu2DMA4Irq,spu2DMA7Irq); - - if (SPU2setDMABaseAddr != NULL) SPU2setDMABaseAddr((uptr)psxM); - if (SPU2setClockPtr != NULL) SPU2setClockPtr(&psxRegs.cycle); - - if (ReportError2(SPU2open((void*)&pDsp), "SPU2")) - { - ClosePlugins(true); - return false; - } - OpenStatus.SPU2 = true; - } - return true; -} - -bool OpenDEV9() -{ - if (!OpenStatus.DEV9) - { - DEV9irqCallback(dev9Irq); - dev9Handler = DEV9irqHandler(); - - if (ReportError2(DEV9open(&psxRegs.pc)/*((void *)&pDsp)*/, "DEV9")) - { - ClosePlugins(true); - return false; - } - OpenStatus.DEV9 = true; - } - return true; -} -bool OpenUSB() -{ - if (!OpenStatus.USB) - { - USBirqCallback(usbIrq); - usbHandler = USBirqHandler(); - USBsetRAM(psxM); - - if (ReportError2(USBopen((void *)&pDsp), "USB")) - { - ClosePlugins(true); - return false; - } - OpenStatus.USB = true; - } - return true; -} - -bool OpenFW() -{ - if (!OpenStatus.FW) - { - FWirqCallback(fwIrq); - - if (ReportError2(FWopen((void *)&pDsp), "FW")) - { - ClosePlugins(true); - return false; - } - OpenStatus.FW = true; - } - return true; -} - -// Note: If the CDVD has not already been manually opened, then it will be opened here -// using NULL as the source file (defaults to whatever's been previously configured into -// the CDVD plugin, which is typically a drive letter) -int OpenPlugins() -{ - if( InitPlugins() == -1 ) return -1; - - if( !OpenGS() || !OpenPAD1() || !OpenPAD2() || !OpenCDVD(NULL) || - !OpenSPU2() || !OpenDEV9() || !OpenUSB() || !OpenFW() - ) - return -1; - - cdvdDetectDisk(); - return 0; -} - - -#define CLOSE_PLUGIN( name ) \ - if( OpenStatus.name ) { \ - name##close(); \ - OpenStatus.name = false; \ - } - -#define CLOSE_PLUGIN2( name ) \ - if( OpenStatus.name ) { \ - name.close(); \ - OpenStatus.name = false; \ - } - - -void ClosePlugins( bool closegs ) -{ - // Close pads first since they attach to the GS's window. - - CLOSE_PLUGIN( PAD1 ); - CLOSE_PLUGIN( PAD2 ); - - // GS plugin is special and is not always closed during emulation pauses. - // (that's because the GS is the most complicated plugin and to close it would - // require we save the GS state) - - if( OpenStatus.GS ) - { - if( closegs ) - { - gsClose(); - OpenStatus.GS = false; - } - else - { - mtgsWaitGS(); - } - } - - CloseCDVD(); - - if( OpenStatus.CDVD ) - { - DoCDVDclose(); - OpenStatus.CDVD = false; - } - - CLOSE_PLUGIN( DEV9 ); - CLOSE_PLUGIN( USB ); - CLOSE_PLUGIN( FW ); - CLOSE_PLUGIN( SPU2 ); -} - -void PluginsResetGS() -{ - // PADs are tied to the GS window, so shut them down together with the GS. - - CLOSE_PLUGIN( PAD1 ); - CLOSE_PLUGIN( PAD2 ); - - if( OpenStatus.GS ) - { - gsClose(); - OpenStatus.GS = false; - } - - GSshutdown(); - - int ret = GSinit(); - if (ret != 0) { Msgbox::Alert("GSinit error: %d", params ret); } -} - -#endif diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h index e0b08106ea..3ed2228317 100644 --- a/pcsx2/Plugins.h +++ b/pcsx2/Plugins.h @@ -39,12 +39,45 @@ struct PluginInfo namespace Exception { - class NotPcsxPlugin : public Stream + class PluginError : public virtual RuntimeError { public: - virtual ~NotPcsxPlugin() throw() {} - explicit NotPcsxPlugin( const wxString& objname ); - explicit NotPcsxPlugin( const PluginsEnum_t& pid ); + const PluginsEnum_t PluginId; + + public: + PluginError( PluginsEnum_t pid ) : + RuntimeError( "Generic plugin error" ) + , BaseException( "Generic plugin error" ) + , PluginId( pid ) + { + } + }; + + class PluginFailure : public virtual RuntimeError + { + public: + wxString plugin_name; // name of the plugin + + virtual ~PluginFailure() throw() {} + + explicit PluginFailure( const char* plugin, const char* msg="%s plugin encountered a critical error" ) : + RuntimeError( msg ) + , BaseException( msg ) + , plugin_name( wxString::FromAscii(plugin) ) {} + + virtual wxString LogMessage() const; + virtual wxString DisplayMessage() const; + }; + + class InvalidPluginConfigured : public virtual PluginError, public virtual BadStream + { + public: + virtual ~InvalidPluginConfigured() throw() {} + + explicit InvalidPluginConfigured( const PluginsEnum_t& pid, const wxString& objname, + const char* eng ); + explicit InvalidPluginConfigured( const PluginsEnum_t& pid, const wxString& objname, + const wxString& eng_msg, const wxString& xlt_msg ); }; }; @@ -80,8 +113,40 @@ struct LegacyPluginAPI_Common class SaveState; ////////////////////////////////////////////////////////////////////////////////////////// +// IPluginManager +// Provides a basic placebo "do-nothing" interface for plugin management. This is used +// to avoid NULL pointer exceptions/segfaults when referencing the plugin manager global +// handle. // -class PluginManager +// Note: The Init and Freeze methods of this class will cause debug assertions, but Close +// methods fail silently, on the premise that Close and Shutdown are typically run from +// exception handlers or cleanup code, and null pointers should be silently ignored in +// favor of continuing cleanup. +// +class PluginManagerBase +{ + DeclareNoncopyableObject( PluginManagerBase ) + +public: + PluginManagerBase() {} + virtual ~PluginManagerBase() {} + + virtual void Init() { wxASSERT_MSG( false, L"Null PluginManager!" ); } + virtual void Shutdown() {} + virtual void Open() { } + virtual void Open( PluginsEnum_t pid ) { wxASSERT_MSG( false, L"Null PluginManager!" ); } + 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!" ); } + +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +class PluginManager : public PluginManagerBase { DeclareNoncopyableObject( PluginManager ) @@ -110,13 +175,14 @@ protected: PluginStatus_t m_info[PluginId_Count]; public: - ~PluginManager(); + virtual ~PluginManager(); void Init(); void Shutdown(); + void Open(); void Open( PluginsEnum_t pid ); void Close( PluginsEnum_t pid ); - void Close(); + void Close( bool closegs=true ); void Freeze( PluginsEnum_t pid, int mode, freezeData* data ); void Freeze( PluginsEnum_t pid, SaveState& state ); @@ -137,15 +203,8 @@ protected: extern const PluginInfo tbl_PluginInfo[]; extern PluginManager* g_plugins; -extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest ); +extern PluginManager* PluginManager_Create( const wxString (&folders)[PluginId_Count] ); +extern PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] ); - -extern void LoadPlugins(); -extern void ReleasePlugins(); - -extern void InitPlugins(); -extern void ShutdownPlugins(); - -extern void OpenPlugins(); -extern void ClosePlugins( bool closegs ); +extern PluginManagerBase& GetPluginManager(); diff --git a/pcsx2/PrecompiledHeader.h b/pcsx2/PrecompiledHeader.h index 44c67272a6..fcd40d1c6b 100644 --- a/pcsx2/PrecompiledHeader.h +++ b/pcsx2/PrecompiledHeader.h @@ -3,6 +3,12 @@ //#pragma once // no dice, causes problems in GCC PCH (which doesn't really work very well +// Disable some pointless warnings... +#ifdef _MSC_VER +# pragma warning(disable:4250) //'class' inherits 'method' via dominance +# pragma warning(disable:4996) //ignore the stricmp deprecated warning +#endif + ////////////////////////////////////////////////////////////////////////////////////////// // Define PCSX2's own i18n helpers. These override the wxWidgets helpers and provide // additional functionality. diff --git a/pcsx2/R3000A.cpp b/pcsx2/R3000A.cpp index 5f62935a17..af02bc1e99 100644 --- a/pcsx2/R3000A.cpp +++ b/pcsx2/R3000A.cpp @@ -69,7 +69,6 @@ void psxReset() void psxShutdown() { psxBiosShutdown(); - psxSIOShutdown(); //psxCpu->Shutdown(); } diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 51d350d265..8e7b27d315 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -89,16 +89,6 @@ void cpuReset() psxReset(); } -void cpuShutdown() -{ - mtgsWaitGS(); - - hwShutdown(); -// biosShutdown(); - psxShutdown(); - disR5900FreeSyms(); -} - __releaseinline void cpuException(u32 code, u32 bd) { cpuRegs.branch = 0; // Tells the interpreter that an exception occurred during a branch. @@ -367,14 +357,10 @@ __forceinline void _cpuBranchTest_Shared() eeEventTestIsActive = true; g_nextBranchCycle = cpuRegs.cycle + eeWaitCycles; - EEsCycle += cpuRegs.cycle - EEoCycle; - EEoCycle = cpuRegs.cycle; - - if( EEsCycle > 0 ) - iopBranchAction = true; - // ---- Counters ------------- - rcntUpdate_hScanline(); + // Important: the vsync counter must be the first to be checked. It includes emulation + // escape/suspend hooks, and it's really a good idea to suspend/resume emulation before + // doing any actual meaninful branchtest logic. if( cpuTestCycle( nextsCounter, nextCounter ) ) { @@ -382,6 +368,8 @@ __forceinline void _cpuBranchTest_Shared() _cpuTestPERF(); } + rcntUpdate_hScanline(); + _cpuTestTIMR(); // ---- Interrupts ------------- @@ -399,6 +387,12 @@ __forceinline void _cpuBranchTest_Shared() // * The IOP cannot always be run. If we run IOP code every time through the // cpuBranchTest, the IOP generally starts to run way ahead of the EE. + EEsCycle += cpuRegs.cycle - EEoCycle; + EEoCycle = cpuRegs.cycle; + + if( EEsCycle > 0 ) + iopBranchAction = true; + psxBranchTest(); if( iopBranchAction ) diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index d849fe8a26..6314972c19 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -16,11 +16,33 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef __R5900_H__ -#define __R5900_H__ +#pragma once + +////////////////////////////////////////////////////////////////////////////////////////// +#pragma region Recompiler Stuffs + +// This code section contains recompiler vars that are used in "shared" code. Placing +// them in iR5900.h would mean having to include that into more files than I care to +// right now, so we're sticking them here for now until a better solution comes along. extern bool g_EEFreezeRegs; +extern bool g_ExecBiosHack; +extern volatile bool eeRecIsReset; +namespace Exception +{ + // Implementation Note: this exception has no meaningful type information and we don't + // care to have it be caught by any BaseException handlers lying about, so let's not + // derive from BaseException :D + class RecompilerReset + { + public: + explicit RecompilerReset() { } + }; +} +#pragma endregion + +////////////////////////////////////////////////////////////////////////////////////////// // EE Bios function name tables. namespace R5900 { extern const char* const bios[256]; @@ -28,7 +50,6 @@ extern const char* const bios[256]; extern s32 EEsCycle; extern u32 EEoCycle; -extern bool g_ExecBiosHack; union GPR_reg { // Declare union type GPR register u64 UD[2]; //128 bits @@ -254,7 +275,6 @@ extern R5900cpu recCpu; extern void cpuInit(); extern void cpuReset(); // can throw Exception::FileNotFound. -extern void cpuShutdown(); extern void cpuExecuteBios(); extern void cpuException(u32 code, u32 bd); extern void cpuTlbMissR(u32 addr, u32 bd); @@ -295,5 +315,3 @@ extern void cpuTestTIMRInts(); #define EXC_CODE_WATCH EXC_CODE(23) #define EXC_CODE__MASK 0x0000007c #define EXC_CODE__SHIFT 2 - -#endif /* __R5900_H__ */ diff --git a/pcsx2/R5900Exceptions.h b/pcsx2/R5900Exceptions.h index 602a455c84..eae3463e92 100644 --- a/pcsx2/R5900Exceptions.h +++ b/pcsx2/R5900Exceptions.h @@ -30,7 +30,7 @@ namespace R5900Exception // Translation note: EE Emulation exceptions are untranslated only. There's really no // point in providing translations for this hardcore mess. :) // - class BaseExcept : public Ps2Generic + class BaseExcept : public virtual Ps2Generic { public: const cpuRegisters cpuState; @@ -39,8 +39,8 @@ namespace R5900Exception virtual ~BaseExcept() throw()=0; explicit BaseExcept( const wxString& msg ) : - Exception::Ps2Generic( L"(EE) " + msg ), - cpuState( cpuRegs ) + Ps2Generic( L"(EE) " + msg ) + , cpuState( cpuRegs ) { } @@ -103,18 +103,6 @@ namespace R5900Exception {} }; - ////////////////////////////////////////////////////////////////////////////////// - // - class SystemCall : public BaseExcept - { - public: - virtual ~SystemCall() throw() {} - - explicit SystemCall() : - BaseExcept( L"SystemCall [SYSCALL]" ) - {} - }; - ////////////////////////////////////////////////////////////////////////////////// // class Trap : public BaseExcept @@ -139,30 +127,6 @@ namespace R5900Exception {} }; - ////////////////////////////////////////////////////////////////////////////////// - // - class Break : public BaseExcept - { - public: - virtual ~Break() throw() {} - - explicit Break() : - BaseExcept( L"Break Instruction" ) - {} - }; - - ////////////////////////////////////////////////////////////////////////////////// - // - class Overflow : public BaseExcept - { - public: - virtual ~Overflow() throw() {} - - explicit Overflow() : - BaseExcept( L"Overflow" ) - {} - }; - ////////////////////////////////////////////////////////////////////////////////// // class DebugBreakpoint : public BaseExcept diff --git a/pcsx2/R5900OpcodeImpl.cpp b/pcsx2/R5900OpcodeImpl.cpp index 800e907dff..be3e2b9358 100644 --- a/pcsx2/R5900OpcodeImpl.cpp +++ b/pcsx2/R5900OpcodeImpl.cpp @@ -34,12 +34,12 @@ static __forceinline s64 _add64_Overflow( s64 x, s64 y ) // other method below is like 5-10 times slower). if( ((~(x^y))&(x^result)) < 0 ) - throw R5900Exception::Overflow(); + cpuException(0x30, cpuRegs.branch); // fixme: is 0x30 right for overflow?? // the not-as-fast style! //if( ((x >= 0) && (y >= 0) && (result < 0)) || // ((x < 0) && (y < 0) && (result >= 0)) ) - // throw R5900Exception::Overflow(); + // cpuException(0x30, cpuRegs.branch); return result; } @@ -54,7 +54,7 @@ static __forceinline s64 _add32_Overflow( s32 x, s32 y ) // If bit32 != bit31 then we have an overflow. if( (result.UL[0]>>31) != (result.UL[1] & 1) ) - throw R5900Exception::Overflow(); + cpuException(0x30, cpuRegs.branch); return result.SD[0]; } diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp index 0c8664e575..a4d34cdfca 100644 --- a/pcsx2/RecoverySystem.cpp +++ b/pcsx2/RecoverySystem.cpp @@ -19,6 +19,7 @@ #include "PrecompiledHeader.h" #include "Common.h" +#include "ps2/CoreEmuThread.h" #include "HostGui.h" @@ -79,8 +80,8 @@ namespace StateRecovery { void Recover() { - // Just in case they weren't initialized earlier (no harm in calling this multiple times) - OpenPlugins(); + wxASSERT( g_EmuThread != NULL ); + wxASSERT( g_EmuThread->IsSelf() ); if( g_RecoveryState != NULL ) { @@ -99,9 +100,7 @@ namespace StateRecovery { } StateRecovery::Clear(); - - // this needs to be called for every new game! - // (note: sometimes launching games through bios will give a crc of 0) + SysClearExecutionCache(); if( GSsetGameCRC != NULL ) GSsetGameCRC(ElfCRC, g_ZeroGSOptions); @@ -111,37 +110,35 @@ namespace StateRecovery { // (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 ) { - // State is already saved into memory, and the emulator (and in-progress flag) - // have likely been cleared out. So save from the Recovery buffer instead of - // doing a "standard" save: - gzFile fileptr = gzopen( file.ToAscii().data(), "wb" ); if( fileptr == NULL ) - { - Msgbox::Alert( wxsFormat( _("Error while trying to save to file: %s"), file.c_str() ) ); - return; - } + 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( g_gsRecoveryState != NULL ) - { - RecoveryZipSavingState( file ).FreezeAll(); - } else { - if( !g_EmulationInProgress ) - { - Msgbox::Alert( _("No emulation state to save") ); // translate: You need to start a game first before you can save it's state - return; - } - - States_Save( file ); + if( !EmulationInProgress() ) return; + + RecoveryZipSavingState( file ).FreezeAll(); } } @@ -161,8 +158,7 @@ namespace StateRecovery { void MakeGsOnly() { StateRecovery::Clear(); - - if( !g_EmulationInProgress ) return; + if( !EmulationInProgress() ) return; g_gsRecoveryState = new SafeArray(); JustGsSavingState eddie; @@ -172,23 +168,28 @@ namespace StateRecovery { // 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). + // existing recovery state takes precedence since if it were out-of-date it'd be + // deleted!). void MakeFull() { if( g_RecoveryState != NULL ) return; + if( !EmulationInProgress() ) return; + + SysSuspend(); try { g_RecoveryState = new SafeArray( L"Memory Savestate Recovery" ); RecoveryMemSavingState().FreezeAll(); safe_delete( g_gsRecoveryState ); - g_EmulationInProgress = false; } catch( Exception::RuntimeError& ex ) { - Msgbox::Alert( wxsFormat( // fixme: this error needs proper translation stuffs. - L"Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" - L"Error: %s", ex.DisplayMessage().c_str() ) + Msgbox::Alert( wxsFormat( // fixme: needs proper translation + L"PCSX2 encountered an error while trying to backup/suspend the PS2 VirtualMachine state. " + L"You may resume emulation without losing any data, however the machine state will not be " + L"able to recover if you make changes to your PCSX2 configuration.\n\n" + L"Details: %s", ex.DisplayMessage().c_str() ) ); safe_delete( g_RecoveryState ); } diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index 2f63bc46a9..2826dd6eeb 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -80,16 +80,11 @@ void sioInit() // Transfer(?) Ready and the Buffer is Empty sio.StatReg = TX_RDY | TX_EMPTY; sio.packetsize = 0; - sio.terminator =0x55; // Command terminator 'U' + sio.terminator = 0x55; // Command terminator 'U' MemoryCard::Init(); } -void psxSIOShutdown() -{ - MemoryCard::Shutdown(); -} - u8 sioRead8() { u8 ret = 0xFF; @@ -597,7 +592,7 @@ void InitializeSIO(u8 value) if( sio.activeMemcardSlot[mcidx] != 0 ) { - // Might want to more agressively declare a card's non-existence here. + // Might want to more aggressively declare a card's non-existence here. // As non-zero slots always report a failure, and have to read // the FAT before writing, think this should be fine. sio2.packet.recvVal1 = 0x1D100; @@ -607,7 +602,7 @@ void InitializeSIO(u8 value) { m_PostSavestateCards[mcidx]--; sio2.packet.recvVal1 = 0x1D100; - PAD_LOG( "START MEMCARD[%d] - post-savestate ejection - reported as missing!", sio.GetMemcardIndex() ); + PAD_LOG( "START MEMCARD[%d] - post-savestate ejection - reporting as missing!", sio.GetMemcardIndex() ); } else { diff --git a/pcsx2/Sio.h b/pcsx2/Sio.h index 541f583143..1bfa37202b 100644 --- a/pcsx2/Sio.h +++ b/pcsx2/Sio.h @@ -60,8 +60,6 @@ struct _sio { extern _sio sio; extern void sioInit(); -extern void sioShutdown(); -extern void psxSIOShutdown(); extern u8 sioRead8(); extern void sioWrite8(u8 value); extern void sioWriteCtrl16(u16 value); diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 202296a223..11f5cedf55 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -18,9 +18,9 @@ #include "PrecompiledHeader.h" -#include "Common.h" #include "HostGui.h" +#include "Common.h" #include "VUmicro.h" #include "iR5900.h" #include "R3000A.h" @@ -30,13 +30,15 @@ #include "R5900Exceptions.h" #include "CDVD/CDVD.h" +#include "ps2/CoreEmuThread.h" using namespace std; Pcsx2Config EmuConfig; // disable all session overrides by default... -SessionOverrideFlags g_Session = {false}; +SessionOverrideFlags g_Session = {false}; +CoreEmuThread* g_EmuThread; bool sysInitialized = false; @@ -230,10 +232,9 @@ void SysAllocateDynarecs() ////////////////////////////////////////////////////////////////////////////////////////// // This should be called last thing before Pcsx2 exits. +// void SysShutdownMem() { - cpuShutdown(); - vuMicroMemShutdown(); psxMemShutdown(); memShutdown(); @@ -254,9 +255,6 @@ void SysShutdownDynarecs() } -bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI -bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset) - ////////////////////////////////////////////////////////////////////////////////////////// // Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition. // This can be called at any time outside the context of a Cpu->Execute() block without @@ -265,16 +263,8 @@ bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (s // Use this method to reset the recs when important global pointers like the MTGS are re-assigned. void SysClearExecutionCache() { - if( CHECK_EEREC ) - { - Cpu = &recCpu; - psxCpu = &psxRec; - } - else - { - Cpu = &intCpu; - psxCpu = &psxInt; - } + Cpu = CHECK_EEREC ? &recCpu : &intCpu; + psxCpu = CHECK_IOPREC ? &psxRec : &psxInt; Cpu->Reset(); psxCpu->Reset(); @@ -287,83 +277,118 @@ __forceinline void SysUpdate() HostGui::KeyEvent( PADkeyEvent() ); } -void SysExecute() +bool EmulationInProgress() { - g_EmulationInProgress = true; - g_ReturnToGui = false; - - // Optimization: We hardcode two versions of the EE here -- one for recs and one for ints. - // This is because recs are performance critical, and being able to inline them into the - // function here helps a small bit (not much but every small bit counts!). - - try - { - if( CHECK_EEREC ) - { - while( !g_ReturnToGui ) - { - recExecute(); - SysUpdate(); - } - } - else - { - while( !g_ReturnToGui ) - { - Cpu->Execute(); - SysUpdate(); - } - } - } - catch( R5900Exception::BaseExcept& ex ) - { - Console::Error( ex.LogMessage() ); - Console::Error( fmt_string( "(EE) PC: 0x%8.8x \tCycle: 0x%8.8x", ex.cpuState.pc, ex.cpuState.cycle ).c_str() ); - } + return (g_EmuThread != NULL) && g_EmuThread->IsRunning(); } -// Function provided to escape the emulation state, by shutting down plugins and saving -// the GS state. The execution state is effectively preserved, and can be resumed with a -// call to SysExecute. +// 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() { if( EmuConfig.closeGSonEsc ) StateRecovery::MakeGsOnly(); - ClosePlugins( EmuConfig.closeGSonEsc ); - g_ReturnToGui = true; + safe_delete( g_EmuThread ); } +void SysSuspend() +{ + if( g_EmuThread != NULL ) + g_EmuThread->Suspend(); +} + +void SysResume() +{ + if( g_EmuThread != NULL ) + g_EmuThread->Resume(); +} + + +// Function provided to escape the emulation state, by shutting down plugins and saving +// the GS state. The execution state is effectively preserved, and can be resumed with a +// call to SysExecute. +/*void SysEndExecution() +{ + if( EmuConfig.closeGSonEsc ) + StateRecovery::MakeGsOnly(); + + ClosePlugins( EmuConfig.closeGSonEsc ); +}*/ + void SysRestorableReset() { - if( !g_EmulationInProgress ) return; + 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 +// virtual machine memory state is fully intact. +// +// Any other exception means the Virtual Memory state is indeterminate and probably +// invalid. +void SysLoadState( const wxString& file ) +{ + // 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(); + + joe.FreezeAll(); + + if( GSsetGameCRC != NULL ) + GSsetGameCRC(ElfCRC, g_ZeroGSOptions); +} + void SysReset() { - // fixme - this code sets the statusbar but never returns control to the window message pump - // so the status bar won't receive the WM_PAINT messages needed to update itself anyway. - // Oops! (air) - - HostGui::Notice( _("Resetting...") ); - Console::SetTitle( _("Resetting...") ); - - g_EmulationInProgress = false; - StateRecovery::Clear(); - - cpuShutdown(); - ShutdownPlugins(); + Console::Status( _("Resetting...") ); + safe_delete( g_EmuThread ); + GetPluginManager().Shutdown(); ElfCRC = 0; // Note : No need to call cpuReset() here. It gets called automatically before the // emulator resumes execution. - - HostGui::Notice( _("Ready") ); - Console::SetTitle( _("Emulation state is reset.") ); } +// Maps a block of memory for use as a recompiled code buffer, and ensures that the +// allocation is below a certain memory address (specified in "bounds" parameter). +// The allocated block has code execution privileges. +// Returns NULL on allocation failure. u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller) { u8 *Mem = (u8*)HostSys::Mmap( base, size ); diff --git a/pcsx2/System.h b/pcsx2/System.h index 7836f77d1b..8daf4a2ee4 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -23,34 +23,38 @@ #include "Utilities/Threading.h" // to use threading stuff, include the Threading namespace in your file. #include "Misc.h" +class CoreEmuThread; +enum CDVD_SourceType; + extern bool SysInit(); extern void SysDetect(); // Detects cpu type and fills cpuInfo structs. extern void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers. extern void SysUpdate(); // Called on VBlank (to update i.e. pads) +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 SysLoadState( const wxString& file ); extern void SysRestorableReset(); // Saves the current emulation state prior to spu reset. extern void SysClearExecutionCache(); // clears recompiled execution caches! -extern void SysEndExecution(); // terminates plugins, saves GS state (if enabled), and signals emulation loop to end. - -// initiates high-speed execution of the emulation state. This function is currently -// designed to be run from an event loop, but will eventually be re-tooled with threading -// in mindunder the new GUI (assuming Linux approves!), which will make life easier *and* -// improve overall performance too! -extern void SysExecute(); -// Maps a block of memory for use as a recompiled code buffer, and ensures that the -// allocation is below a certain memory address (specified in "bounds" parameter). -// The allocated block has code execution privileges. -// Returns NULL on allocation failure. extern u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed"); extern void vSyncDebugStuff( uint frame ); +extern CoreEmuThread* g_EmuThread; + +////////////////////////////////////////////////////////////////////////////////////////// +// #ifdef __LINUX__ # include @@ -91,7 +95,7 @@ namespace Msgbox extern bool OkCancel( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=0 ); extern bool YesNo( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_QUESTION ); - extern int Assertion( const wxString& text, const wxString& stacktrace ); + extern int Assertion( const wxString& text, const wxString& stacktrace ); extern void Except( const Exception::BaseException& src ); } @@ -99,4 +103,3 @@ BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 ); DECLARE_EVENT_TYPE( pxEVT_CallStackBox, -1 ); END_DECLARE_EVENT_TYPES() - diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index c913053b1f..9be91b2f3e 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -28,14 +28,10 @@ #include "AppConfig.h" #include "System.h" #include "ConsoleLogger.h" +#include "ps2/CoreEmuThread.h" class IniInterface; -extern wxFileHistory* g_RecentIsoList; - -extern wxRect wxGetDisplayArea(); -extern bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos ); - ////////////////////////////////////////////////////////////////////////////////////////// // struct AppImageIds @@ -183,8 +179,32 @@ protected: bool TryOpenConfigCwd(); void OnMessageBox( pxMessageBoxEvent& evt ); void CleanupMess(); - - }; +////////////////////////////////////////////////////////////////////////////////////////// +// +class AppEmuThread : public CoreEmuThread +{ +public: + AppEmuThread( const wxString& elf_file=wxEmptyString ); + virtual ~AppEmuThread() { } + + virtual void Resume(); + +protected: + sptr ExecuteTask(); +}; + + + DECLARE_APP(Pcsx2App) + +extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest ); +extern void LoadPlugins(); +extern void InitPlugins(); +extern void OpenPlugins(); + +extern wxRect wxGetDisplayArea(); +extern bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos ); + +extern wxFileHistory* g_RecentIsoList; diff --git a/pcsx2/gui/HostGui.cpp b/pcsx2/gui/HostGui.cpp index 2e9a6c12c6..7e3e8a3502 100644 --- a/pcsx2/gui/HostGui.cpp +++ b/pcsx2/gui/HostGui.cpp @@ -17,13 +17,28 @@ */ #include "PrecompiledHeader.h" +#include "HostGui.h" #include "MainFrame.h" +#include "Dump.h" +#include "Elfheader.h" +#include "SaveState.h" +#include "GS.h" + // This API is likely obsolete for the most part, so I've just included a few dummies // to keep things compiling until I can get to the point of tying up loose ends. +// renderswitch - tells GSdx to go into dx9 sw if "renderswitch" is set. +bool renderswitch = false; + namespace HostGui { + static const int NUM_STATES = 10; + + static int StatesC = 0; + static int g_Pcsx2Recording = 0; // true 1 if recording video and sound + + // Sets the status bar message without mirroring the output to the console. void SetStatusMsg( const wxString& text ) { @@ -37,15 +52,120 @@ namespace HostGui SetStatusMsg( text ); } - void ResetMenuSlots() + static wxString GetGSStateFilename() { - // Probably obsolete if we do a savestate dialog. + return Path::Combine( g_Conf->Folders.Savestates, wxsFormat( L"/%8.8X.%d.gs", ElfCRC, StatesC ) ); } - void BeginExecution() + static void ProcessFKeys(int fkey, struct KeyModifiers *keymod) { - wxASSERT( g_EmuThread != NULL ); - g_EmuThread->Resume(); + assert(fkey >= 1 && fkey <= 12 ); + + switch(fkey) + { + case 1: + States_Save( StatesC ); + break; + + case 2: + if( keymod->shift ) + StatesC = (StatesC+NUM_STATES-1) % NUM_STATES; + else + StatesC = (StatesC+1) % NUM_STATES; + + Console::Notice( " > Selected savestate slot %d", params StatesC); + + if( GSchangeSaveState != NULL ) + GSchangeSaveState(StatesC, SaveState::GetFilename(StatesC).mb_str()); + break; + + case 3: + States_Load( StatesC ); + break; + + case 4: + // FIXME : Reimplement framelimiting using new oolean system + //CycleFrameLimit(keymod->shift ? -1 : 1); + break; + + // note: VK_F5-VK_F7 are reserved for GS + case 8: + GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() ); + break; + + case 9: //gsdx "on the fly" renderer switching + + SysSuspend(); + StateRecovery::MakeGsOnly(); + + g_plugins->Close( PluginId_GS ); + renderswitch = !renderswitch; + + StateRecovery::Recover(); + SysResume(); + break; + + #ifdef PCSX2_DEVBUILD + case 10: + // There's likely a better way to implement this, but this seemed useful. + // I might add turning EE, VU0, and VU1 recs on and off by hotkey at some point, too. + // --arcum42 + enableLogging = !enableLogging; + + if (enableLogging) + GSprintf(10, "Logging Enabled."); + else + GSprintf(10,"Logging Disabled."); + + break; + + case 11: + Console::Notice( "Cannot make gsstates in MTGS mode" ); + + // fixme : fix up gsstate mess and make it mtgs compatible -- air + #ifdef _STGS_GSSTATE_CODE + wxString Text; + if( strgametitle[0] != 0 ) + { + // only take the first two words + wxString gsText; + + wxStringTokenizer parts( strgametitle, L" " ); + + wxString name( parts.GetNextToken() ); // first part + wxString part2( parts.GetNextToken() ); + + if( !!part2 ) + name += L"_" + part2; + + gsText.Printf( L"%s.%d.gs", name.c_str(), StatesC ); + Text = Path::Combine( g_Conf->Folders.Savestates, gsText ); + } + else + { + Text = GetGSStateFilename(); + } + break; + #endif + #endif + + case 12: + if( keymod->shift ) + { + #ifdef PCSX2_DEVBUILD + iDumpRegisters(cpuRegs.pc, 0); + Console::Notice("hardware registers dumped EE:%x, IOP:%x\n", params cpuRegs.pc, psxRegs.pc); + #endif + } + else + { + g_Pcsx2Recording ^= 1; + + mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0); + if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL); + } + break; + } } void __fastcall KeyEvent( keyEvent* ev ) diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h index 46d1ab8a03..332e858441 100644 --- a/pcsx2/gui/MainFrame.h +++ b/pcsx2/gui/MainFrame.h @@ -23,9 +23,6 @@ #include #include "App.h" -#include "ps2/CoreEmuThread.h" - -extern CoreEmuThread* g_EmuThread; ////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 7eb766618b..0bb77450d2 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -49,23 +49,29 @@ static const wxChar* isoFilterTypes = void MainEmuFrame::Menu_RunIso_Click(wxCommandEvent &event) { - g_EmuThread->Suspend(); + SysSuspend(); Console::Status( L"Default Folder: " + g_Conf->Folders.RunIso.ToString() ); wxFileDialog ctrl( this, _("Run PS2 Iso..."), g_Conf->Folders.RunIso.ToString(), wxEmptyString, isoFilterTypes, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); - if( ctrl.ShowModal() == wxID_CANCEL ) return; - g_Conf->Folders.RunIso = ctrl.GetPath(); - - //g_Conf->Save(); + if( ctrl.ShowModal() == wxID_CANCEL ) + { + SysResume(); + return; + } + SysEndExecution(); + + g_Conf->Folders.RunIso = ctrl.GetPath(); + g_Conf->Save(); + + wxString elf_file; if( EmuConfig.SkipBiosSplash ) { // Fetch the ELF filename and CD type from the CDVD provider. wxString ename( ctrl.GetFilename() ); int result = GetPS2ElfName( ename ); - g_EmuThread->SetElfFile( wxEmptyString ); switch( result ) { case 0: @@ -78,34 +84,32 @@ void MainEmuFrame::Menu_RunIso_Click(wxCommandEvent &event) case 2: // PS2 game. Valid! - g_EmuThread->SetElfFile( ename ); + elf_file = ename; break; } } + + SysExecute( new AppEmuThread( elf_file ), CDVDsrc_Iso ); } void MainEmuFrame::Menu_RunWithoutDisc_Click(wxCommandEvent &event) { - if( g_EmuThread->IsRunning() ) + if( EmulationInProgress() ) { - g_EmuThread->Suspend(); + 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 ) { - if( !IsPaused() ) - g_EmuThread->Resume(); + SysResume(); return; } } - g_EmuThread->Reset(); - CDVDsys_ChangeSource( CDVDsrc_NoDisc ); - g_EmuThread->Resume(); - - //HostGui::BeginExecution(); + SysEndExecution(); + SysExecute( new AppEmuThread(), CDVDsrc_NoDisc ); } void MainEmuFrame::Menu_IsoRecent_Click(wxCommandEvent &event) diff --git a/pcsx2/gui/Makefile.am b/pcsx2/gui/Makefile.am deleted file mode 100644 index 3864c17ad5..0000000000 --- a/pcsx2/gui/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -AUTOMAKE_OPTIONS = foreign -INCLUDES = $(shell pkg-config --cflags gtk+-2.0) -I@srcdir@/../ -I@srcdir@/../Linux/ -I@srcdir@/../../common/include -I@srcdir@/../../3rdparty $(shell wx-config --cppflags) - -bin_PROGRAMS = pcsx2 - -# the application source, library search path, and link libraries -pcsx2_SOURCES = \ -CheckedStaticBox.cpp ConsoleLogger.cpp MainFrame.cpp wxHelpers.cpp AppConfig.cpp main.cpp \ -App.h CheckedStaticBox.h MainFrame.h wxHelpers.h \ -IniInterface.cpp AboutBoxDialog.cpp GameFixesDialog.cpp LogOptionsDialog.cpp \ -AboutBoxDialog.h GameFixesDialog.h LogOptionsDialog.h - -pcsx2_LDFLAGS = - -pcsx2_DEPENDENCIES = ../libpcsx2.a ../IPU/libIPU.a ../IPU/mpeg2lib/libmpeg2IPU.a ../RDebug/libRDebug.a ../tinyxml/libtinyxml.a -pcsx2_DEPENDENCIES += ../x86/libx86recomp.a ../x86/ix86/libix86.a -pcsx2_DEPENDENCIES += ../DebugTools/libDebugTools.a - -pcsx2_LDADD = ../libpcsx2.a ../IPU/libIPU.a ../IPU/mpeg2lib/libmpeg2IPU.a ../RDebug/libRDebug.a ../tinyxml/libtinyxml.a -pcsx2_LDADD += ../x86/libx86recomp.a ../x86/ix86/libix86.a -pcsx2_LDADD += ../DebugTools/libDebugTools.a diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index d52b20a819..a477f58c44 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -17,7 +17,7 @@ */ #include "PrecompiledHeader.h" -#include "System.h" +#include "App.h" #include "Plugins.h" #include "Utilities/ScopedPtr.h" #include "ConfigurationPanels.h" @@ -39,6 +39,21 @@ DEFINE_EVENT_TYPE(wxEVT_EnumerationFinished); typedef s32 (CALLBACK* PluginTestFnptr)(); typedef void (CALLBACK* PluginConfigureFnptr)(); +////////////////////////////////////////////////////////////////////////////////////////// +// +namespace Exception +{ + class NotEnumerablePlugin : public BadStream + { + public: + virtual ~NotEnumerablePlugin() throw() {} + explicit NotEnumerablePlugin( const wxString& objname=wxEmptyString ) : + BadStream( objname, "" ) + , BaseException( wxLt("File is not a PCSX2 plugin") ) + {} + }; +} + ////////////////////////////////////////////////////////////////////////////////////////// // class PluginEnumerator @@ -75,7 +90,7 @@ public: if( m_GetLibType == NULL || m_GetLibName == NULL || m_GetLibVersion2 == NULL ) { - throw Exception::NotPcsxPlugin( m_plugpath ); + throw Exception::NotEnumerablePlugin( m_plugpath ); } m_type = m_GetLibType(); } @@ -447,10 +462,6 @@ sptr Panels::PluginSelectorPanel::EnumThread::ExecuteTask() { Console::Status( ex.LogMessage() ); } - catch( Exception::NotPcsxPlugin& ex ) - { - Console::Status( ex.LogMessage() ); - } pthread_testcancel(); diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp new file mode 100644 index 0000000000..e41a8602a6 --- /dev/null +++ b/pcsx2/gui/Plugins.cpp @@ -0,0 +1,83 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2009 Pcsx2 Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "PrecompiledHeader.h" +#include "Utilities/ScopedPtr.h" + +#include +#include + +#include "Plugins.h" +#include "GS.h" +#include "HostGui.h" +#include "AppConfig.h" + +int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) +{ + wxScopedPtr placebo; + wxArrayString* realdest = dest; + if( realdest == NULL ) + placebo.reset( realdest = new wxArrayString() ); + +#ifdef __WXMSW__ + // Windows pretty well has a strict "must end in .dll" rule. + wxString pattern( L"*%s" ); +#else + // Other platforms seem to like to version their libs after the .so extension: + // blah.so.3.1.fail? + wxString pattern( L"*%s*" ); +#endif + + return searchpath.Exists() ? + wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0; +} + +void LoadPlugins() +{ + if( g_plugins != NULL ) return; + wxString passins[PluginId_Count]; + + const PluginInfo* pi = tbl_PluginInfo-1; + while( ++pi, pi->shortname != NULL ) + passins[pi->id] = g_Conf->FullpathTo( pi->id ); + + g_plugins = PluginManager_Create( passins ); +} + +void InitPlugins() +{ + if( g_plugins == NULL ) + LoadPlugins(); + + GetPluginManager().Init(); +} + +// 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() +{ + if( g_plugins == NULL ) + InitPlugins(); + + GetPluginManager().Open(); +} diff --git a/pcsx2/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp similarity index 58% rename from pcsx2/Saveslots.cpp rename to pcsx2/gui/Saveslots.cpp index 9284352209..a9ac93bb7a 100644 --- a/pcsx2/Saveslots.cpp +++ b/pcsx2/gui/Saveslots.cpp @@ -17,6 +17,8 @@ */ #include "PrecompiledHeader.h" +#include "App.h" + #include "Common.h" #include "HostGui.h" @@ -40,52 +42,30 @@ bool States_isSlotUsed(int num) ////////////////////////////////////////////////////////////////////////////////////////// // Save state load-from-file (or slot) helpers. -// Internal use state loading function which does not trap exceptions. -// The calling function should trap and handle exceptions as needed. -static void _loadStateOrExcept( const wxString& file ) -{ - gzLoadingState joe( file ); // this'll throw an StateLoadError. - - // Make sure the cpu and plugins are ready to be state-ified! - cpuReset(); - OpenPlugins(); - - joe.FreezeAll(); - - if( GSsetGameCRC != NULL ) - GSsetGameCRC(ElfCRC, g_ZeroGSOptions); -} - // returns true if the new state was loaded, or false if nothing happened. void States_Load( const wxString& file ) { try { - _loadStateOrExcept( file ); - HostGui::Notice( wxsFormat( _("Loaded State %s"), file.c_str() ) ); + SysLoadState( file ); + HostGui::Notice( wxsFormat( _("Loaded State %s"), + wxFileName( file ).GetFullName().c_str() ) ); } - catch( Exception::StateLoadError& ex) + catch( Exception::BadSavedState& ex) { + // At this point we can return control back to the user, no questions asked. + // StateLoadErrors are only thorwn if the load failed prior to any virtual machine + // memory contents being changed. (usually missing file errors) + Console::Notice( ex.LogMessage() ); - - // At this point the cpu hasn't been reset, so we can return - // control to the user safely... (that's why we use a console notice instead of a popup) - - return; } catch( Exception::BaseException& ex ) { - // The emulation state is ruined. Might as well give them a popup and start the gui. - // Translation Tip: Since the savestate load was incomplete, the emulator has been reset. - - Msgbox::Alert( - wxsFormat( _("Error loading savestate from file: %s"), file.c_str() ) + - L"\n\n" + _("Error details:") + ex.DisplayMessage() - ); - SysReset(); - return; + // VM state is probably ruined. We'll need to recover from the in-memory backup. + StateRecovery::Recover(); } - HostGui::BeginExecution(); + + SysExecute( new AppEmuThread() ); } void States_Load(int num) @@ -94,39 +74,13 @@ void States_Load(int num) if( !Path::IsFile( file ) ) { - Console::Notice( "Saveslot %d is empty.", params num ); + Console::Notice( "Savestate slot %d is empty.", params num ); return; } - try - { - _loadStateOrExcept( file ); - HostGui::Notice( wxsFormat( _("Loaded State %d"), num ) ); - } - catch( Exception::StateLoadError& ex) - { - Console::Notice( wxsFormat( L"Could not load savestate slot %d.\n\n%s", num, ex.LogMessage().c_str() ) ); - - // At this point the cpu hasn't been reset, so we can return - // control to the user safely... (that's why we use a console notice instead of a popup) - - return; - } - catch( Exception::BaseException& ex ) - { - // The emulation state is ruined. Might as well give them a popup and start the gui. - // Translation Tip: Since the savestate load was incomplete, the emulator has been reset. - - Msgbox::Alert( - wxsFormat( _("Error loading savestate from slot %d"), file.c_str() ) + - L"\n\n" + _("Error details:") + ex.DisplayMessage() - ); - - SysEndExecution(); - return; - } - - HostGui::BeginExecution(); + Console::Status( "Loading savestate from slot %d...", params num ); + States_Load( file ); + HostGui::Notice( wxsFormat( _("Loaded State (slot %d)"), num ) ); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -134,10 +88,17 @@ void States_Load(int num) void States_Save( const wxString& file ) { + if( !EmulationInProgress() ) + { + Msgbox::Alert( _("You need to start emulation first before you can save it's state.") ); + return; + } + try { + Console::Status( wxsFormat( L"Saving savestate to file: %s", file.c_str() ) ); StateRecovery::SaveToFile( file ); - HostGui::Notice( wxsFormat( _("State saved to file: %s"), file.c_str() ) ); + HostGui::Notice( wxsFormat( _("State saved to file: %s"), wxFileName( file ).GetFullName().c_str() ) ); } catch( Exception::BaseException& ex ) { @@ -153,32 +114,16 @@ void States_Save( const wxString& file ) Console::Error( wxsFormat( L"An error occurred while trying to save to file %s\n", file.c_str() ) + - L"Your emulation state has not been saved!\n\nError: " + ex.LogMessage() + L"Your emulation state has not been saved!\n" + L"\nError: " + ex.LogMessage() ); } - - // Filename could be a valid slot, so still need to update - HostGui::ResetMenuSlots(); } void States_Save(int num) { - try - { - StateRecovery::SaveToSlot( num ); - HostGui::Notice( wxsFormat( _("State saved to slot %d"), num ) ); - } - catch( Exception::BaseException& ex ) - { - // TODO: Implement a "pause the action and issue a popup" thing here. - // *OR* some kind of GS overlay... [for now we use the console] - - Console::Error( wxsFormat( - L"An error occurred while trying to save to slot %d\n", num ) + - L"Your emulation state has not been saved!\n\nError: " + ex.LogMessage() - ); - } - HostGui::ResetMenuSlots(); + Console::Status( "Saving savestate to slot %d...", params num ); + States_Save( SaveState::GetFilename( num ) ); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -196,7 +141,7 @@ void vSyncDebugStuff( uint frame ) g_TestRun.frame += 20; if( g_TestRun.curimage >= g_TestRun.numimages ) { // exit - SysEndExecution(); + g_EmuThread->Cancel(); } } else { @@ -206,7 +151,7 @@ void vSyncDebugStuff( uint frame ) } else { // exit - SysEndExecution(); + g_EmuThread->Cancel(); } } } diff --git a/pcsx2/gui/main.cpp b/pcsx2/gui/main.cpp index b785ca4e99..13f880a475 100644 --- a/pcsx2/gui/main.cpp +++ b/pcsx2/gui/main.cpp @@ -19,6 +19,9 @@ #include "PrecompiledHeader.h" #include "IniInterface.h" #include "MainFrame.h" +#include "MemoryCard.h" +#include "Plugins.h" + #include "Dialogs/ModalPopups.h" #include "Utilities/ScopedPtr.h" @@ -34,7 +37,6 @@ IMPLEMENT_APP(Pcsx2App) bool UseAdminMode = false; AppConfig* g_Conf = NULL; wxFileHistory* g_RecentIsoList = NULL; -CoreEmuThread* g_EmuThread = NULL; namespace Exception { @@ -53,6 +55,70 @@ namespace Exception }; } +AppEmuThread::AppEmuThread( const wxString& elf_file ) : + CoreEmuThread( elf_file ) +{ + MemoryCard::Init(); +} + +void AppEmuThread::Resume() +{ + if( wxGetApp().GetMainFrame().IsPaused() ) return; + CoreEmuThread::Resume(); +} + +sptr AppEmuThread::ExecuteTask() +{ + try + { + CoreEmuThread::ExecuteTask(); + } + // ---------------------------------------------------------------------------- + catch( Exception::FileNotFound& ex ) + { + if( ex.StreamName == g_Conf->FullpathToBios() ) + { + Msgbox::OkCancel( ex.DisplayMessage() + + _("\n\nPress Ok to go to the BIOS Configuration Panel.") ); + } + else + { + // Probably a plugin. Find out which one! + + const PluginInfo* pi = tbl_PluginInfo-1; + while( ++pi, pi->shortname != NULL ) + { + const PluginsEnum_t pid = pi->id; + if( g_Conf->FullpathTo( pid ) == ex.StreamName ) break; + } + + if( pi->shortname == NULL ) + { + // Some other crap file failure >_< + } + + int result = Msgbox::OkCancel( ex.DisplayMessage() + + _("\n\nPress Ok to go to the Plugin Configuration Panel.") ); + + if( result == wxID_OK ) + { + } + } + } + // ---------------------------------------------------------------------------- + // [TODO] : Add exception handling here for debuggable PS2 exceptions that allows + // invocation of the PCSX2 debugger and such. + catch( Exception::BaseException& ex ) + { + // Sent the exception back to the main gui thread? + GetPluginManager().Close(); + Msgbox::Alert( ex.DisplayMessage() ); + } + + return 0; +} + + wxFrame* Pcsx2App::GetMainWindow() const { return m_MainFrame; } #include "HashMap.h" @@ -165,7 +231,6 @@ bool Pcsx2App::OnInit() wxApp::OnInit(); g_Conf = new AppConfig(); - g_EmuThread = new CoreEmuThread(); wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); @@ -236,6 +301,8 @@ bool Pcsx2App::PrepForExit() int Pcsx2App::OnExit() { + MemoryCard::Shutdown(); + if( g_Conf != NULL ) g_Conf->Save(); diff --git a/pcsx2/ps2/CoreEmuThread.cpp b/pcsx2/ps2/CoreEmuThread.cpp index ccb841440f..f166632cd8 100644 --- a/pcsx2/ps2/CoreEmuThread.cpp +++ b/pcsx2/ps2/CoreEmuThread.cpp @@ -38,43 +38,58 @@ CoreEmuThread& CoreEmuThread::Get() void CoreEmuThread::CpuInitializeMess() { - try - { - OpenPlugins(); - cpuReset(); - SysClearExecutionCache(); + GetPluginManager().Open(); + cpuReset(); + SysClearExecutionCache(); + if( GSsetGameCRC != NULL ) GSsetGameCRC( ElfCRC, 0 ); - if( StateRecovery::HasState() ) - { - // no need to boot bios or detect CDs when loading savestates. - // [TODO] : It might be useful to detect game SLUS/CRC and compare it against - // the savestate info, and issue a warning to the user since, chances are, they - // don't really want to run a game with the wrong ISO loaded into the emu. - StateRecovery::Recover(); - } - else - { - ScopedLock lock( m_lock_elf_file ); - if( !m_elf_file.IsEmpty() ) - { - // Skip Bios Hack -- Runs the PS2 BIOS stub, and then manually loads the ELF - // executable data, and injects the cpuRegs.pc with the address of the - // execution start point. - // - // This hack is necessary for non-CD ELF files, and is optional for game CDs - // (though not recommended for games because of rare ill side effects). - - cpuExecuteBios(); - loadElfFile( m_elf_file ); - } - } - } - catch( Exception::BaseException& ex ) + if( StateRecovery::HasState() ) { - Msgbox::Alert( ex.DisplayMessage() ); + // no need to boot bios or detect CDs when loading savestates. + // [TODO] : It might be useful to detect game SLUS/CRC and compare it against + // the savestate info, and issue a warning to the user since, chances are, they + // don't really want to run a game with the wrong ISO loaded into the emu. + StateRecovery::Recover(); } + else + { + if( !m_elf_file.IsEmpty() ) + { + // Skip Bios Hack -- Runs the PS2 BIOS stub, and then manually loads the ELF + // executable data, and injects the cpuRegs.pc with the address of the + // execution start point. + // + // This hack is necessary for non-CD ELF files, and is optional for game CDs + // (though not recommended for games because of rare ill side effects). + + cpuExecuteBios(); + loadElfFile( m_elf_file ); + } + } +} + +// special macro which disables inlining on functions that require their own function stackframe. +// This is due to how Win32 handles structured exception handling. Linux uses signals instead +// of SEH, and so these functions can be inlined. +#ifdef _WIN32 +# define __unique_stackframe __noinline +#endif + +// On Win32 this function invokes SEH, which requires it be in a function all by itself +// with inlining disabled. +__unique_stackframe +void CoreEmuThread::CpuExecute() +{ + PCSX2_MEM_PROTECT_BEGIN(); + Cpu->Execute(); + PCSX2_MEM_PROTECT_END(); +} + +static void _cet_callback_cleanup( void* handle ) +{ + ((CoreEmuThread*)handle)->DoThreadCleanup(); } sptr CoreEmuThread::ExecuteTask() @@ -86,13 +101,11 @@ sptr CoreEmuThread::ExecuteTask() m_ResumeEvent.Wait(); } + pthread_cleanup_push( _cet_callback_cleanup, this ); CpuInitializeMess(); - StateCheck(); - - PCSX2_MEM_PROTECT_BEGIN(); - Cpu->Execute(); - PCSX2_MEM_PROTECT_END(); + CpuExecute(); + pthread_cleanup_pop( true ); return 0; } @@ -127,28 +140,32 @@ void CoreEmuThread::StateCheck() } } -void CoreEmuThread::Start() +CoreEmuThread::CoreEmuThread( const wxString& elf_file ) : + m_ExecMode( ExecMode_Idle ) +, m_Done( false ) +, m_ResumeEvent() +, m_SuspendEvent() +, m_resetRecompilers( false ) +, m_resetProfilers( false ) + +, m_elf_file( elf_file ) +, m_lock_ExecMode() { - if( IsRunning() ) return; - - m_running = false; - m_ExecMode = ExecMode_Idle; - m_Done = false; - m_resetProfilers = false; - m_resetRecompilers = false; - m_elf_file = wxEmptyString; - - m_ResumeEvent.Reset(); - m_SuspendEvent.Reset(); PersistentThread::Start(); - pthread_detach( m_thread ); } -void CoreEmuThread::Reset() +// Invoked by the pthread_exit or pthread_cancel +void CoreEmuThread::DoThreadCleanup() +{ + wxASSERT( IsSelf() ); // only allowed from our own thread, thanks. + m_running = false; + GetPluginManager().Close(); +} + +CoreEmuThread::~CoreEmuThread() { Cancel(); - StateRecovery::Clear(); } // Resumes the core execution state, or does nothing is the core is already running. If @@ -156,7 +173,7 @@ void CoreEmuThread::Reset() // memory savestates. void CoreEmuThread::Resume() { - Start(); + if( IsSelf() || !IsRunning() ) return; { ScopedLock locker( m_lock_ExecMode ); @@ -208,6 +225,8 @@ void CoreEmuThread::Resume() // void CoreEmuThread::Suspend( bool isBlocking ) { + if( IsSelf() || !IsRunning() ) return; + { ScopedLock locker( m_lock_ExecMode ); @@ -232,3 +251,4 @@ void CoreEmuThread::ApplySettings( const Pcsx2Config& src ) m_resetProfilers = (src.Profiler != EmuConfig.Profiler ); EmuConfig = src; } + diff --git a/pcsx2/ps2/CoreEmuThread.h b/pcsx2/ps2/CoreEmuThread.h index 35dc03fe4a..766975af7a 100644 --- a/pcsx2/ps2/CoreEmuThread.h +++ b/pcsx2/ps2/CoreEmuThread.h @@ -46,46 +46,27 @@ protected: bool m_resetRecompilers; bool m_resetProfilers; - wxString m_elf_file; - - MutexLock m_lock_elf_file; + const wxString m_elf_file; + MutexLock m_lock_ExecMode; public: static CoreEmuThread& Get(); public: - CoreEmuThread() : - m_ExecMode( ExecMode_Idle ) - , m_Done( false ) - , m_ResumeEvent() - , m_SuspendEvent() - , m_resetRecompilers( false ) - , m_resetProfilers( false ) - - , m_elf_file() - , m_lock_elf_file() - , m_lock_ExecMode() - { - } - - void SetElfFile( const wxString& text ) - { - ScopedLock lock( m_lock_elf_file ); - m_elf_file = text; - } - - void Start(); - void Reset(); + explicit CoreEmuThread( const wxString& elf_file=wxEmptyString ); + virtual ~CoreEmuThread(); bool IsSuspended() const { return (m_ExecMode == ExecMode_Suspended); } - void Suspend( bool isBlocking = true ); - void Resume(); - void ApplySettings( const Pcsx2Config& src ); + virtual void Suspend( bool isBlocking = true ); + virtual void Resume(); + virtual void ApplySettings( const Pcsx2Config& src ); + virtual void StateCheck(); - void StateCheck(); + virtual void DoThreadCleanup(); protected: void CpuInitializeMess(); - sptr ExecuteTask(); + void CpuExecute(); + virtual sptr ExecuteTask(); }; diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 8d1b5904d0..c69b4a59dd 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -1950,6 +1950,10 @@ RelativePath="..\..\gui\MainMenuClicks.cpp" > + + @@ -2134,16 +2138,32 @@ > + + + + + + + + - - @@ -2166,14 +2182,6 @@ RelativePath="..\..\System.cpp" > - - - - diff --git a/pcsx2/x86/iR5900.h b/pcsx2/x86/iR5900.h index f9f33153f8..eca5b78db3 100644 --- a/pcsx2/x86/iR5900.h +++ b/pcsx2/x86/iR5900.h @@ -37,6 +37,9 @@ extern int branch; // set for branch (also used by the SuperVU! .. why extern u32 target; // branch target extern u32 s_nBlockCycles; // cycles of current block recompiling +////////////////////////////////////////////////////////////////////////////////////////// +// + #define REC_FUNC( f ) \ void rec##f( void ) \ { \ @@ -94,8 +97,6 @@ void iFlushCall(int flushtype); void recBranchCall( void (*func)() ); void recCall( void (*func)(), int delreg ); -extern void recExecute(); // same as recCpu.Execute(), but faster (can be inline'd) - namespace R5900{ namespace Dynarec { extern void recDoBranchImm( u32* jmpSkip, bool isLikely = false ); diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index f90374d4ab..66af986455 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -112,69 +112,6 @@ static void iBranchTest(u32 newpc = 0xffffffff); static void ClearRecLUT(BASEBLOCK* base, int count); static u32 eeScaleBlockCycles(); -#ifdef PCSX2_VM_COISSUE -static u8 _eeLoadWritesRs(u32 tempcode) -{ - switch(tempcode>>26) { - case 26: // ldl - case 27: // ldr - case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: - case 55: // LD - case 30: // lq - return ((tempcode>>21)&0x1f)==((tempcode>>16)&0x1f); // rs==rt - } - return 0; -} - -static u8 _eeIsLoadStoreCoIssue(u32 firstcode, u32 secondcode) -{ - switch(firstcode>>26) { - case 34: // lwl - return (secondcode>>26)==38; - case 38: // lwr - return (secondcode>>26)==34; - case 42: // swl - return (secondcode>>26)==46; - case 46: // swr - return (secondcode>>26)==42; - case 26: // ldl - return (secondcode>>26)==27; - case 27: // ldr - return (secondcode>>26)==26; - case 44: // sdl - return (secondcode>>26)==45; - case 45: // sdr - return (secondcode>>26)==44; - - case 32: case 33: case 35: case 36: case 37: case 39: - case 55: // LD - - // stores - case 40: case 41: case 43: - case 63: // sd - return (secondcode>>26)==(firstcode>>26); - - case 30: // lq - case 31: // sq - case 49: // lwc1 - case 57: // swc1 - case 54: // lqc2 - case 62: // sqc2 - return (secondcode>>26)==(firstcode>>26); - } - return 0; -} - -static u8 _eeIsLoadStoreCoX(u32 tempcode) -{ - switch( tempcode>>26 ) { - case 30: case 31: case 49: case 57: case 55: case 63: - return 1; - } - return 0; -} -#endif - void _eeFlushAllUnused() { int i; @@ -446,13 +383,22 @@ static void recAlloc() x86FpuState = FPU_STATE; } +struct ManualPageTracking +{ + u16 page; + u8 counter; +}; + PCSX2_ALIGNED16( static u16 manual_page[Ps2MemSize::Base >> 12] ); PCSX2_ALIGNED16( static u8 manual_counter[Ps2MemSize::Base >> 12] ); +volatile bool eeRecIsReset = false; + //////////////////////////////////////////////////// void recResetEE( void ) { Console::Status( "Issuing EE/iR5900-32 Recompiler Reset [mem/structure cleanup]" ); + eeRecIsReset = true; maxrecmem = 0; @@ -616,67 +562,71 @@ static void __naked DispatcherEvent() jmp DispatcherReg; } } +#endif -void recExecute() +static void recExecute() { - g_EEFreezeRegs = true; - - // Enter an endless loop, which is only escapable via C++ exception handling. + // Implementation Notes: + // This function enter an endless loop, which is only escapable via C++ exception handling. // The loop is needed because some things in the rec use "ret" as a shortcut to // invoking DispatcherReg. These things are code bits which are called infrequently, // such as dyna_block_discard and dyna_page_reset. + // Optimization note: + // Compared pushad against manually pushing the regs one-by-one. + // Manually pushing is faster, especially on Core2's and such. :) + + g_EEFreezeRegs = true; + while( true ) { - // Optimization note : Compared pushad against manually pushing the regs one-by-one. - // Manually pushing is faster, especially on Core2's and such. :) - - __asm + try { - push ebx - push esi - push edi - push ebp +#ifdef _MSC_VER - call DispatcherReg + __asm + { + push ebx + push esi + push edi + push ebp - pop ebp - pop edi - pop esi - pop ebx - } - } - g_EEFreezeRegs = false; -} + call DispatcherReg + + pop ebp + pop edi + pop esi + pop ebx + } #else // _MSC_VER -__forceinline void recExecute() -{ - // Optimization note : Compared pushad against manually pushing the regs one-by-one. - // Manually pushing is faster, especially on Core2's and such. :) - g_EEFreezeRegs = true; - __asm__ - ( - ".intel_syntax noprefix\n" - "push ebx\n" - "push esi\n" - "push edi\n" - "push ebp\n" + __asm__ + ( + ".intel_syntax noprefix\n" + "push ebx\n" + "push esi\n" + "push edi\n" + "push ebp\n" - "call DispatcherReg\n" + "call DispatcherReg\n" + + "pop ebp\n" + "pop edi\n" + "pop esi\n" + "pop ebx\n" + ".att_syntax\n" + ); +#endif + } + catch( Exception::RecompilerReset& ) + { + } + } - "pop ebp\n" - "pop edi\n" - "pop esi\n" - "pop ebx\n" - ".att_syntax\n" - ); g_EEFreezeRegs = false; } -#endif - namespace R5900 { namespace Dynarec { namespace OpcodeImpl { diff --git a/pcsx2/xmlpatchloader.cpp b/pcsx2/xmlpatchloader.cpp index 917d43cb73..e69762dbac 100644 --- a/pcsx2/xmlpatchloader.cpp +++ b/pcsx2/xmlpatchloader.cpp @@ -24,10 +24,6 @@ using namespace std; #include "Patch.h" #include "System.h" -#ifdef _MSC_VER -#pragma warning(disable:4996) //ignore the stricmp deprecated warning -#endif - #if !defined(_WIN32) #ifndef strnicmp #define strnicmp strncasecmp