(work in progress -- some of this stuff still doesn't quite work as it should)

* Rewrote Savestate code, fixed lots of stuff in PersistentThread, and renamed most "Emu"s to "Sys".
 * Removed wxScopedPtr and whipped up our own ScopedPtr that doesn't use boost's fail-style function names (and made it more thread-safe).
 * Handling exceptions across threads: Added Clone() and Rethrow() methods to BaseException, and added a Rethrow() to PersistentThread.
 * Go rid of AppInvoke macros (those were nasty!)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1929 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-09-29 19:16:00 +00:00
parent 893fcebf52
commit 15d2824d1b
36 changed files with 1073 additions and 509 deletions

View File

@ -105,23 +105,30 @@ typedef s32 sptr;
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// A rough-and-ready cross platform 128-bit datatype, Non-SSE style. // A rough-and-ready cross platform 128-bit datatype, Non-SSE style.
// //
// Note: These structs do not provide any additional constructors because C++ can't allow
// the use of dataypes with constructors in unions (and since unions are ont of the primary
// uses of these types, that measn we can't have constructors). Embedded functions for
// performing explicity conversion from 64 and 32 bit values is provided instead.
//
#ifdef __cplusplus #ifdef __cplusplus
struct u128 struct u128
{ {
u64 lo; u64 lo;
u64 hi; u64 hi;
// Implicit conversion from u64 // Explicit conversion from u64
u128( u64 src ) : static u128 From64( u64 src )
lo( src ) {
, hi( 0 ) {} u128 retval = { src, 0 };
return retval;
}
// Implicit conversion from u32 // Explicit conversion from u32
u128( u32 src ) : static u128 From32( u32 src )
lo( src ) {
, hi( 0 ) {} u128 retval = { src, 0 };
return retval;
u128() {} }
}; };
struct s128 struct s128
@ -129,17 +136,19 @@ struct s128
s64 lo; s64 lo;
s64 hi; s64 hi;
// Implicit conversion from u64 // explicit conversion from s64, with sign extension.
s128( s64 src ) : static s128 From64( s64 src )
lo( src ) {
, hi( 0 ) {} s128 retval = { src, (src < 0) ? -1 : 0 };
return retval;
}
// Implicit conversion from u32 // explicit conversion from s32, with sign extension.
s128( s32 src ) : static s128 From64( s32 src )
lo( src ) {
, hi( 0 ) {} s128 retval = { src, (src < 0) ? -1 : 0 };
return retval;
s128() {} }
}; };
#else #else

View File

@ -47,6 +47,8 @@ extern bool DevAssert( bool condition, const char* msg );
namespace Exception namespace Exception
{ {
int MakeNewType();
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// BaseException // BaseException
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -87,6 +89,9 @@ namespace Exception
// Returns a message suitable for end-user display. // Returns a message suitable for end-user display.
// This message is usually meant for display in a user popup or such. // This message is usually meant for display in a user popup or such.
virtual wxString FormatDisplayMessage() const { return m_message_user; } virtual wxString FormatDisplayMessage() const { return m_message_user; }
virtual void Rethrow() const=0;
virtual BaseException* Clone() const=0;
protected: protected:
// Construction using two pre-formatted pre-translated messages // Construction using two pre-formatted pre-translated messages
@ -129,7 +134,9 @@ namespace Exception
// it will be optionally translated. // it will be optionally translated.
// //
#define DEFINE_EXCEPTION_COPYTORS( classname ) \ #define DEFINE_EXCEPTION_COPYTORS( classname ) \
virtual ~classname() throw() {} virtual ~classname() throw() {} \
virtual void Rethrow() const { throw classname( *this ); } \
virtual BaseException* Clone() const { return new classname( *this ); }
#define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \ #define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
DEFINE_EXCEPTION_COPYTORS( classname ) \ DEFINE_EXCEPTION_COPYTORS( classname ) \
@ -161,7 +168,7 @@ namespace Exception
DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") ) DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") )
}; };
class ObjectIsNull : public RuntimeError class ObjectIsNull : public virtual RuntimeError
{ {
public: public:
wxString ObjectName; wxString ObjectName;

View File

@ -33,27 +33,33 @@ extern void pcsx2_aligned_free(void* pmem);
// pointer to null after deallocation. // pointer to null after deallocation.
#define safe_delete( ptr ) \ #define safe_delete( ptr ) \
((void) (delete ptr), ptr = NULL) ((void) (delete (ptr)), (ptr) = NULL)
#define safe_delete_array( ptr ) \ #define safe_delete_array( ptr ) \
((void) (delete[] ptr), ptr = NULL) ((void) (delete[] (ptr)), (ptr) = NULL)
// fixme: I'm pretty sure modern libc implementations under gcc and msvc check null status // fixme: I'm pretty sure modern libc implementations under gcc and msvc check null status
// inside free(), meaning we shouldn't have to do it ourselves. But legacy implementations // inside free(), meaning we shouldn't have to do it ourselves. But legacy implementations
// didn't always check, so best to be cautious unless absolutely certain it's being covered on // didn't always check, so best to be cautious unless absolutely certain it's being covered on
// all ported platforms. // all ported platforms.
#define safe_free( ptr ) \ #define safe_free( ptr ) \
((void) (( ( ptr != NULL ) && (free( ptr ), !!0) ), ptr = NULL)) ((void) (( ( (ptr) != NULL ) && (free( ptr ), !!0) ), (ptr) = NULL))
#define safe_fclose( ptr ) \
((void) (( ( (ptr) != NULL ) && (fclose( ptr ), !!0) ), (ptr) = NULL))
// Implementation note: all known implementations of _aligned_free check the pointer for // Implementation note: all known implementations of _aligned_free check the pointer for
// NULL status (our implementation under GCC, and microsoft's under MSVC), so no need to // NULL status (our implementation under GCC, and microsoft's under MSVC), so no need to
// do it here. // do it here.
#define safe_aligned_free( ptr ) \ #define safe_aligned_free( ptr ) \
((void) ( _aligned_free( ptr ), ptr = NULL )) ((void) ( _aligned_free( ptr ), (ptr) = NULL ))
#define SafeSysMunmap( ptr, size ) \ #define SafeSysMunmap( ptr, size ) \
((void) ( HostSys::Munmap( (uptr)ptr, size ), ptr = NULL )) ((void) ( HostSys::Munmap( (uptr)(ptr), size ), (ptr) = NULL ))
// Microsoft Windows only macro, useful for freeing out COM objects:
#define safe_release( ptr ) \
((void) (( ( (ptr) != NULL ) && ((ptr)->Release(), !!0) ), (ptr) = NULL))
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// Handy little class for allocating a resizable memory block, complete with // Handy little class for allocating a resizable memory block, complete with

View File

@ -1,4 +1,210 @@
#pragma once #pragma once
#include <wx/scopedptr.h>
#include <wx/scopedarray.h> #include <wx/scopedarray.h>
// --------------------------------------------------------------------------------------
// ScopedPtr
// --------------------------------------------------------------------------------------
template< typename T >
class ScopedPtr
{
DeclareNoncopyableObject(ScopedPtr);
protected:
T* m_ptr;
public:
typedef T element_type;
wxEXPLICIT ScopedPtr(T * ptr = NULL) : m_ptr(ptr) { }
~ScopedPtr()
{ Delete(); }
ScopedPtr& Reassign(T * ptr = NULL)
{
if ( ptr != m_ptr )
{
Delete();
m_ptr = ptr;
}
return *this;
}
ScopedPtr& Delete()
{
// Thread-safe deletion: Set the pointer to NULL first, and then issue
// the deletion. This allows pending Application messages that might be
// dependent on the current object to nullify their actions.
T* deleteme = m_ptr;
m_ptr = NULL;
delete deleteme;
return *this;
}
// Removes the pointer from scoped management, but does not delete!
T *DetachPtr()
{
T *ptr = m_ptr;
m_ptr = NULL;
return ptr;
}
// Returns the managed pointer. Can return NULL as a valid result if the ScopedPtr
// has no object in management.
T* GetPtr() const
{
return m_ptr;
}
void SwapPtr(ScopedPtr& other)
{
T * const tmp = other.m_ptr;
other.m_ptr = m_ptr;
m_ptr = tmp;
}
// ----------------------------------------------------------------------------
// ScopedPtr Operators
// ----------------------------------------------------------------------------
// I've decided to use the ATL's approach to pointer validity tests, opposed to
// the wx/boost approach (which uses some bizarre member method pointer crap, and can't
// allow the T* implicit casting.
bool operator!() const throw()
{
return m_ptr == NULL;
}
operator T*() const
{
return m_ptr;
}
// Equality
bool operator==(T* pT) const throw()
{
return m_ptr == pT;
}
// Inequality
bool operator!=(T* pT) const throw()
{
return !operator==(pT);
}
// Convenient assignment operator. ScopedPtr = NULL will issue an automatic deletion
// of the managed pointer.
ScopedPtr& operator=( T* src )
{
return Reassign( src );
}
// Dereference operator, returns a handle to the managed pointer.
// Generates a debug assertion if the object is NULL!
T& operator*() const
{
wxASSERT(m_ptr != NULL);
return *m_ptr;
}
T* operator->() const
{
wxASSERT(m_ptr != NULL);
return m_ptr;
}
};
// --------------------------------------------------------------------------------------
// pxObjPtr -- fancified version of wxScopedPtr
// --------------------------------------------------------------------------------------
// This class is a non-null scoped pointer container. What that means is that the object
// always resets itself to a valid "placebo" function rather than NULL, such that methods
// can be invoked safely without fear of NULL pointer exceptions. This system is useful
// for objects where most or all public methods can fail silently, and still allow program
// execution flow to continue.
//
// It also implements basic scoped pointer behavior: when the pxObjPtr class is deleted,
// it will automatically delete the pointer in its posession, if the pointer is valid.
//
// Notes:
// * This class intentionally does not implement the "release" API, because it doesn't
// really make sense within the context of a non-nullable pointer specification.
//
template< typename T, T& DefaultStaticInst >
class pxObjPtr
{
DeclareNoncopyableObject(pxObjPtr);
protected:
T * m_ptr;
public:
typedef T element_type;
explicit pxObjPtr(T * ptr = &DefaultStaticInst) : m_ptr(ptr) { }
bool IsEmpty() const
{
return m_ptr != &DefaultStaticInst;
}
~pxObjPtr()
{
if( !IsEmpty() ) delete m_ptr;
m_ptr = NULL;
}
// test for pointer validity: defining conversion to unspecified_bool_type
// and not more obvious bool to avoid implicit conversions to integer types
typedef T *(pxObjPtr<T,DefaultStaticInst>::*unspecified_bool_type)() const;
operator unspecified_bool_type() const
{
return ( !IsEmpty() ) ? &pxScopedPtr<T>::get : NULL;
}
void reset(T * ptr = &DefaultStaticInst)
{
if ( ptr != m_ptr )
{
if( !IsEmpty() )
delete m_ptr;
m_ptr = ptr;
}
}
T& operator*() const
{
wxASSERT(m_ptr != NULL);
return *m_ptr;
}
T* operator->() const
{
wxASSERT(m_ptr != NULL);
return m_ptr;
}
T* get() const
{
wxASSERT(m_ptr != NULL);
return m_ptr;
}
void swap(pxObjPtr& other)
{
// Neither pointer in either container should ever be NULL...
wxASSERT(m_ptr != NULL);
wxASSERT(other.m_ptr != NULL);
T * const tmp = other.m_ptr;
other.m_ptr = m_ptr;
m_ptr = tmp;
}
};

View File

@ -20,6 +20,7 @@
#include <semaphore.h> #include <semaphore.h>
#include "Pcsx2Defs.h" #include "Pcsx2Defs.h"
#include "ScopedPtr.h"
namespace Exception namespace Exception
{ {
@ -87,6 +88,7 @@ namespace Threading
void Lock(); void Lock();
void Unlock(); void Unlock();
bool TryLock();
}; };
// Returns the number of available logical CPUs (cores plus hyperthreaded cpus) // Returns the number of available logical CPUs (cores plus hyperthreaded cpus)
@ -101,55 +103,94 @@ namespace Threading
// sleeps the current thread for the given number of milliseconds. // sleeps the current thread for the given number of milliseconds.
extern void Sleep( int ms ); extern void Sleep( int ms );
////////////////////////////////////////////////////////////////////////////////////////// // --------------------------------------------------------------------------------------
// PersistentThread - Helper class for the basics of starting/managing persistent threads. // IThread - Interface for the public access to PersistentThread.
// // --------------------------------------------------------------------------------------
// Use this as a base class for your threaded procedure, and implement the 'int ExecuteTask()' // Class usage: Can be used for allowing safe nullification of a thread handle. Rather
// method. Use Start() and Cancel() to start and shutdown the thread, and use m_sem_event // than being NULL'd, the handle can be mapped to an IThread implementation which acts
// internally to post/receive events for the thread (make a public accessor for it in your // as a do-nothing placebo or an assertion generator.
// derived class if your thread utilizes the post). //
// class IThread
// Notes: {
// * Constructing threads as static global vars isn't recommended since it can potentially DeclareNoncopyableObject(IThread);
// confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers
// no dependency options for ensuring correct static var initializations). Use heap public:
// allocation to create thread objects instead. IThread() {}
// virtual ~IThread() throw() {}
class PersistentThread
virtual bool IsSelf() const { return false; }
virtual bool IsRunning() { return false; }
virtual int GetReturnCode() const
{
DevAssert( false, "Cannot obtain a return code from a placebo thread." );
return 0;
}
virtual void Start() {}
virtual void Cancel( bool isBlocking = true ) {}
virtual sptr Block() { return NULL; }
virtual bool Detach() { return false; }
};
// --------------------------------------------------------------------------------------
// PersistentThread - Helper class for the basics of starting/managing persistent threads.
// --------------------------------------------------------------------------------------
// Use this as a base class for your threaded procedure, and implement the 'int ExecuteTask()'
// method. Use Start() and Cancel() to start and shutdown the thread, and use m_sem_event
// internally to post/receive events for the thread (make a public accessor for it in your
// derived class if your thread utilizes the post).
//
// Notes:
// * Constructing threads as static global vars isn't recommended since it can potentially
// confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers
// no dependency options for ensuring correct static var initializations). Use heap
// allocation to create thread objects instead.
//
class PersistentThread : public virtual IThread
{ {
DeclareNoncopyableObject(PersistentThread); DeclareNoncopyableObject(PersistentThread);
protected: protected:
typedef int (*PlainJoeFP)(); typedef int (*PlainJoeFP)();
wxString m_name; // diagnostic name for our thread.
pthread_t m_thread; pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads. Semaphore m_sem_event; // general wait event that's needed by most threads.
Semaphore m_sem_finished; // used for canceling and closing threads in a deadlock-safe manner Semaphore m_sem_finished; // used for canceling and closing threads in a deadlock-safe manner
MutexLock m_lock_start; // used to lock the Start() code from starting simutaneous threads accidentally.
sptr m_returncode; // value returned from the thread on close. sptr m_returncode; // value returned from the thread on close.
volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc. volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
// exception handle, set non-NULL if the thread terminated with an exception
// Use RethrowException() to re-throw the exception using its original exception type.
ScopedPtr<Exception::BaseException> m_except;
public: public:
virtual ~PersistentThread() throw(); virtual ~PersistentThread() throw();
PersistentThread(); PersistentThread();
PersistentThread( const char* name );
virtual void Start(); virtual void Start();
virtual void Cancel( bool isBlocking = true ); virtual void Cancel( bool isBlocking = true );
virtual void Detach(); virtual bool Detach();
// Gets the return code of the thread.
// Throws std::logic_error if the thread has not terminated.
virtual int GetReturnCode() const;
virtual bool IsRunning() const;
virtual sptr Block(); virtual sptr Block();
virtual int GetReturnCode() const;
virtual void RethrowException() const;
bool IsRunning() const;
bool IsSelf() const; bool IsSelf() const;
wxString GetName() const;
virtual void DoThreadCleanup(); virtual void DoThreadCleanup();
protected: protected:
void SetName( __unused const char* name ); void DoSetThreadName( const wxString& name );
void DoSetThreadName( __unused const char* name );
void _internal_execute();
// Used to dispatch the thread callback function. // Used to dispatch the thread callback function.
// (handles some thread cleanup on Win32, and is basically a typecast // (handles some thread cleanup on Win32, and is basically a typecast
@ -159,7 +200,7 @@ namespace Threading
// Implemented by derived class to handle threading actions! // Implemented by derived class to handle threading actions!
virtual sptr ExecuteTask()=0; virtual sptr ExecuteTask()=0;
}; };
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// ScopedLock: Helper class for using Mutexes. // ScopedLock: Helper class for using Mutexes.
// Using this class provides an exception-safe (and generally clean) method of locking // Using this class provides an exception-safe (and generally clean) method of locking

View File

@ -17,17 +17,19 @@
#include "Dependencies.h" #include "Dependencies.h"
// ---------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// wxBaseTools.h // wxBaseTools.h
// //
// This file is meant to contain utility classes for users of the wxWidgets library. // This file is meant to contain utility classes for users of the wxWidgets library.
// All classes in this file are strictly dependent on wxBase libraries only, meaning // All classes in this file are strictly dependent on wxBase libraries only, meaning
// you don't need to include or link against wxCore (GUI) to build them. For tools // you don't need to include or link against wxCore (GUI) to build them. For tools
// which require wxCore, see wxGuiTools.h // which require wxCore, see wxGuiTools.h
// ---------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////
// wxDoNotLogInThisScope // --------------------------------------------------------------------------------------
// wxDoNotLogInThisScope
// --------------------------------------------------------------------------------------
// This class is used to disable wx's sometimes inappropriate amount of forced error logging // This class is used to disable wx's sometimes inappropriate amount of forced error logging
// during specific activities. For example, when using wxDynamicLibrary to detect the // during specific activities. For example, when using wxDynamicLibrary to detect the
// validity of DLLs, wx will log errors for missing symbols. (sigh) // validity of DLLs, wx will log errors for missing symbols. (sigh)
@ -54,3 +56,35 @@ public:
} }
}; };
// --------------------------------------------------------------------------------------
// wxToUTF8 - shortcut for str.ToUTF8().data()
// --------------------------------------------------------------------------------------
class wxToUTF8
{
DeclareNoncopyableObject( wxToUTF8 );
protected:
wxCharBuffer m_charbuffer;
public:
wxToUTF8( const wxString& str ) : m_charbuffer( str.ToUTF8().data() ) { }
virtual ~wxToUTF8() throw() {}
operator const char*() { return m_charbuffer.data(); }
};
// This class provided for completeness sake. You probably should use ToUTF8 instead.
class wxToAscii
{
DeclareNoncopyableObject( wxToAscii );
protected:
wxCharBuffer m_charbuffer;
public:
wxToAscii( const wxString& str ) : m_charbuffer( str.ToAscii().data() ) { }
virtual ~wxToAscii() throw() {}
operator const char*() { return m_charbuffer.data(); }
};

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "Threading.h" #include "Threading.h"
#include "wxBaseTools.h"
#include <wx/datetime.h> #include <wx/datetime.h>
#include <wx/thread.h> #include <wx/thread.h>
@ -37,11 +38,13 @@ namespace Threading
} }
PersistentThread::PersistentThread() : PersistentThread::PersistentThread() :
m_thread() m_name( L"PersistentThread" )
, m_thread()
, m_sem_event() , m_sem_event()
, m_sem_finished() , m_sem_finished()
, m_lock_start( true ) // recursive mutexing!
, m_returncode( 0 ) , m_returncode( 0 )
, m_detached( false ) , m_detached( true ) // start out with m_thread in detached/invalid state
, m_running( false ) , m_running( false )
{ {
} }
@ -53,40 +56,42 @@ namespace Threading
// your sister, and then cheating on her with your daughter. // your sister, and then cheating on her with your daughter.
PersistentThread::~PersistentThread() throw() PersistentThread::~PersistentThread() throw()
{ {
if( !m_running ) return; if( m_running )
wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( !_InterlockedExchange( &m_detached, true ) )
{ {
#if wxUSE_GUI #if wxUSE_GUI
m_sem_finished.WaitGui(); m_sem_finished.WaitGui();
#else #else
m_sem_finished.Wait(); m_sem_finished.Wait();
#endif #endif
m_running = false;
} }
Detach();
} }
// This function should not be called from the owner thread. // This function should not be called from the owner thread.
void PersistentThread::Start() void PersistentThread::Start()
{ {
ScopedLock startlock( m_lock_start ); // Prevents sudden parallel startup
if( m_running ) return; if( m_running ) return;
Detach(); // clean up previous thread, if one exists.
m_sem_finished.Reset(); m_sem_finished.Reset();
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
throw Exception::ThreadCreationError(); throw Exception::ThreadCreationError();
m_running = true; m_detached = false;
} }
// Returns: TRUE if the detachment was performed, or FALSE if the thread was
// already detached or isn't running at all.
// This function should not be called from the owner thread. // This function should not be called from the owner thread.
void PersistentThread::Detach() bool PersistentThread::Detach()
{ {
if( !m_running ) return;
if( _InterlockedExchange( &m_detached, true ) ) return;
wxASSERT( !IsSelf() ); // not allowed from our own thread. wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( _InterlockedExchange( &m_detached, true ) ) return false;
pthread_detach( m_thread ); pthread_detach( m_thread );
return true;
} }
// Remarks: // Remarks:
@ -102,15 +107,15 @@ namespace Threading
// //
void PersistentThread::Cancel( bool isBlocking ) void PersistentThread::Cancel( bool isBlocking )
{ {
wxASSERT( !IsSelf() );
if( !m_running ) return; if( !m_running ) return;
if( _InterlockedExchange( &m_detached, true ) ) if( m_detached )
{ {
Console::Notice( "Threading Warning: Attempted to cancel detached thread; Ignoring..." ); Console::Notice( "Threading Warning: Attempted to cancel detached thread; Ignoring..." );
return; return;
} }
wxASSERT( !IsSelf() );
pthread_cancel( m_thread ); pthread_cancel( m_thread );
if( isBlocking ) if( isBlocking )
@ -121,8 +126,6 @@ namespace Threading
m_sem_finished.Wait(); m_sem_finished.Wait();
#endif #endif
} }
else
pthread_detach( m_thread );
} }
// Blocks execution of the calling thread until this thread completes its task. The // Blocks execution of the calling thread until this thread completes its task. The
@ -153,14 +156,10 @@ namespace Threading
bool PersistentThread::IsRunning() const bool PersistentThread::IsRunning() const
{ {
if (!m_running) return false; return !!m_running;
if( !!m_detached )
return !!m_running;
else
return ( ESRCH != pthread_kill( m_thread, 0 ) );
} }
// Gets the return code of the thread.
// Exceptions: // Exceptions:
// InvalidOperation - thrown if the thread is still running or has never been started. // InvalidOperation - thrown if the thread is still running or has never been started.
// //
@ -171,32 +170,95 @@ namespace Threading
return m_returncode; return m_returncode;
} }
// Throws an exception if the thread encountered one. Uses the BaseException's Rethrow() method,
// which ensures the exception type remains consistent. Debuggable stacktraces will be lost, since
// the thread will have allowed itself to terminate properly.
void PersistentThread::RethrowException() const
{
if( !m_except ) return;
m_except->Rethrow();
}
// invoked when canceling or exiting the thread. // invoked when canceling or exiting the thread.
void PersistentThread::DoThreadCleanup() void PersistentThread::DoThreadCleanup()
{ {
wxASSERT( IsSelf() ); // only allowed from our own thread, thanks. wxASSERT( IsSelf() ); // only allowed from our own thread, thanks.
_InterlockedExchange( &m_running, false ); m_running = false;
m_sem_finished.Post(); m_sem_finished.Post();
} }
wxString PersistentThread::GetName() const
{
return m_name;
}
void PersistentThread::_internal_execute()
{
m_running = true;
DoSetThreadName( m_name );
try {
m_returncode = ExecuteTask();
}
catch( std::logic_error& ex )
{
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
GetName().c_str(), wxString::FromUTF8( ex.what() ) )
);
}
catch( Exception::LogicError& ex )
{
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName() ) + m_except->DiagMsg();
ex.Rethrow();
}
catch( std::runtime_error& ex )
{
m_except = new Exception::RuntimeError(
// Diagnostic message:
wxsFormat( L"(thread: %s) STL Runtime Error: %s\n\t%s",
GetName().c_str(), wxString::FromUTF8( ex.what() )
),
// User Message (not translated, std::exception doesn't have that kind of fancy!
wxsFormat( L"A runtime error occurred in %s:\n\n%s (STL)",
GetName().c_str(), wxString::FromUTF8( ex.what() )
)
);
}
catch( Exception::RuntimeError& ex )
{
m_except = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName() ) + m_except->DiagMsg();
}
}
void* PersistentThread::_internal_callback( void* itsme ) void* PersistentThread::_internal_callback( void* itsme )
{ {
jASSUME( itsme != NULL ); jASSUME( itsme != NULL );
PersistentThread& owner = *((PersistentThread*)itsme); PersistentThread& owner = *((PersistentThread*)itsme);
pthread_cleanup_push( _pt_callback_cleanup, itsme ); pthread_cleanup_push( _pt_callback_cleanup, itsme );
owner.m_returncode = owner.ExecuteTask(); owner._internal_execute();
pthread_cleanup_pop( true ); pthread_cleanup_pop( true );
return (void*)owner.m_returncode; return (void*)owner.m_returncode;
} }
void PersistentThread::DoSetThreadName( const wxString& name )
{
DoSetThreadName( wxToUTF8(name) );
}
void PersistentThread::SetName( __unused const char* name ) void PersistentThread::DoSetThreadName( __unused const char* name )
{ {
wxASSERT( IsSelf() ); // only allowed from our own thread, thanks. wxASSERT( IsSelf() ); // only allowed from our own thread, thanks.
#ifdef _WINDOWS_ #ifdef _WINDOWS_
// This code sample was borrowed form some obscure MSDN article.
// In a rare bout of sanity, it's an actual Micrsoft-published hack
// that actually works!
static const int MS_VC_EXCEPTION = 0x406D1388; static const int MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8) #pragma pack(push,8)
@ -210,10 +272,10 @@ namespace Threading
#pragma pack(pop) #pragma pack(pop)
THREADNAME_INFO info; THREADNAME_INFO info;
info.dwType = 0x1000; info.dwType = 0x1000;
info.szName = name; info.szName = name;
info.dwThreadID = GetCurrentThreadId(); info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0; info.dwFlags = 0;
__try __try
{ {
@ -480,6 +542,11 @@ namespace Threading
pthread_mutex_unlock( &mutex ); pthread_mutex_unlock( &mutex );
} }
bool MutexLock::TryLock()
{
return EBUSY != pthread_mutex_trylock( &mutex );
}
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions) // InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions)
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -85,12 +85,12 @@ __forceinline void xWrite64( u64 val )
xWrite( val ); xWrite( val );
} }
const xAddressIndexerBase ptr; const xAddressIndexerBase ptr;
const xAddressIndexer<u128> ptr128; const xAddressIndexer<u128> ptr128;
const xAddressIndexer<u64> ptr64; const xAddressIndexer<u64> ptr64;
const xAddressIndexer<u32> ptr32; const xAddressIndexer<u32> ptr32;
const xAddressIndexer<u16> ptr16; const xAddressIndexer<u16> ptr16;
const xAddressIndexer<u8> ptr8; const xAddressIndexer<u8> ptr8;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -23,7 +23,7 @@
// GS Playback // GS Playback
int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save
int g_nLeftGSFrames = 0; // when saving, number of frames left int g_nLeftGSFrames = 0; // when saving, number of frames left
static wxScopedPtr<memSavingState> g_fGSSave; static ScopedPtr<memSavingState> g_fGSSave;
// fixme - need to take this concept and make it MTGS friendly. // fixme - need to take this concept and make it MTGS friendly.
#ifdef _STGS_GSSTATE_CODE #ifdef _STGS_GSSTATE_CODE
@ -76,7 +76,7 @@ void SaveGSState(const wxString& file)
Console::WriteLn( wxsFormat( L"\t%s", file.c_str() ) ); Console::WriteLn( wxsFormat( L"\t%s", file.c_str() ) );
SafeArray<u8> buf; SafeArray<u8> buf;
g_fGSSave.reset( new memSavingState( buf ) ); g_fGSSave = new memSavingState( buf );
g_SaveGSStream = 1; g_SaveGSStream = 1;
g_nLeftGSFrames = 2; g_nLeftGSFrames = 2;

View File

@ -107,6 +107,7 @@ mtgsThreadObject::mtgsThreadObject() :
, m_lock_Stack() , m_lock_Stack()
#endif #endif
{ {
m_name = L"MTGS";
} }
void mtgsThreadObject::Start() void mtgsThreadObject::Start()
@ -139,6 +140,7 @@ void mtgsThreadObject::PollStatus()
mtgsThreadObject::~mtgsThreadObject() throw() mtgsThreadObject::~mtgsThreadObject() throw()
{ {
_parent::Cancel();
} }
void mtgsThreadObject::OnResumeReady() void mtgsThreadObject::OnResumeReady()
@ -244,8 +246,6 @@ void mtgsThreadObject::OpenPlugin()
sptr mtgsThreadObject::ExecuteTask() sptr mtgsThreadObject::ExecuteTask()
{ {
SetName( "MTGS" );
#ifdef RINGBUF_DEBUG_STACK #ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd; PacketTagType prevCmd;
#endif #endif

View File

@ -28,29 +28,29 @@
# include "svnrev.h" # include "svnrev.h"
#endif #endif
EmuPluginBindings EmuPlugins; SysPluginBindings SysPlugins;
bool EmuPluginBindings::McdIsPresent( uint port, uint slot ) bool SysPluginBindings::McdIsPresent( uint port, uint slot )
{ {
return !!Mcd->McdIsPresent( (PS2E_THISPTR) Mcd, port, slot ); return !!Mcd->McdIsPresent( (PS2E_THISPTR) Mcd, port, slot );
} }
void EmuPluginBindings::McdRead( uint port, uint slot, u8 *dest, u32 adr, int size ) void SysPluginBindings::McdRead( uint port, uint slot, u8 *dest, u32 adr, int size )
{ {
Mcd->McdRead( (PS2E_THISPTR) Mcd, port, slot, dest, adr, size ); Mcd->McdRead( (PS2E_THISPTR) Mcd, port, slot, dest, adr, size );
} }
void EmuPluginBindings::McdSave( uint port, uint slot, const u8 *src, u32 adr, int size ) void SysPluginBindings::McdSave( uint port, uint slot, const u8 *src, u32 adr, int size )
{ {
Mcd->McdSave( (PS2E_THISPTR) Mcd, port, slot, src, adr, size ); Mcd->McdSave( (PS2E_THISPTR) Mcd, port, slot, src, adr, size );
} }
void EmuPluginBindings::McdEraseBlock( uint port, uint slot, u32 adr ) void SysPluginBindings::McdEraseBlock( uint port, uint slot, u32 adr )
{ {
Mcd->McdEraseBlock( (PS2E_THISPTR) Mcd, port, slot, adr ); Mcd->McdEraseBlock( (PS2E_THISPTR) Mcd, port, slot, adr );
} }
u64 EmuPluginBindings::McdGetCRC( uint port, uint slot ) u64 SysPluginBindings::McdGetCRC( uint port, uint slot )
{ {
return Mcd->McdGetCRC( (PS2E_THISPTR) Mcd, port, slot ); return Mcd->McdGetCRC( (PS2E_THISPTR) Mcd, port, slot );
} }
@ -988,10 +988,10 @@ void PluginManager::Init()
throw Exception::PluginInitError( pid ); throw Exception::PluginInitError( pid );
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
if( EmuPlugins.Mcd == NULL ) if( SysPlugins.Mcd == NULL )
{ {
EmuPlugins.Mcd = (PS2E_ComponentAPI_Mcd*)m_mcdPlugin->NewComponentInstance( PS2E_TYPE_Mcd ); SysPlugins.Mcd = (PS2E_ComponentAPI_Mcd*)m_mcdPlugin->NewComponentInstance( PS2E_TYPE_Mcd );
if( EmuPlugins.Mcd == NULL ) if( SysPlugins.Mcd == NULL )
{ {
// fixme: use plugin's GetLastError (not implemented yet!) // fixme: use plugin's GetLastError (not implemented yet!)
throw Exception::PluginInitError( PluginId_Mcd, "Internal Memorycard Plugin failed to initialize." ); throw Exception::PluginInitError( PluginId_Mcd, "Internal Memorycard Plugin failed to initialize." );
@ -1029,10 +1029,10 @@ void PluginManager::Shutdown()
// More memorycard hacks!! // More memorycard hacks!!
if( (EmuPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) ) if( (SysPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) )
{ {
m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)EmuPlugins.Mcd ); m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)SysPlugins.Mcd );
EmuPlugins.Mcd = NULL; SysPlugins.Mcd = NULL;
} }
DbgCon::Status( "Plugins shutdown successfully." ); DbgCon::Status( "Plugins shutdown successfully." );

View File

@ -48,6 +48,7 @@ namespace Exception
public: public:
DEFINE_EXCEPTION_COPYTORS( PluginError ) DEFINE_EXCEPTION_COPYTORS( PluginError )
PluginError() {} PluginError() {}
PluginError( PluginsEnum_t pid, const char* msg="Generic plugin error" ) PluginError( PluginsEnum_t pid, const char* msg="Generic plugin error" )
{ {
@ -183,13 +184,13 @@ class mtgsThreadObject;
// make the current PluginManager largely obsolete (with the exception of the general Load/Unload // make the current PluginManager largely obsolete (with the exception of the general Load/Unload
// management facilities) // management facilities)
// //
class EmuPluginBindings class SysPluginBindings
{ {
protected: protected:
PS2E_ComponentAPI_Mcd* Mcd; PS2E_ComponentAPI_Mcd* Mcd;
public: public:
EmuPluginBindings() : SysPluginBindings() :
Mcd( NULL ) Mcd( NULL )
{ {
@ -204,7 +205,7 @@ public:
friend class PluginManager; friend class PluginManager;
}; };
extern EmuPluginBindings EmuPlugins; extern SysPluginBindings SysPlugins;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -20,129 +20,268 @@
#include "HostGui.h" #include "HostGui.h"
#include "zlib/zlib.h" #include "zlib/zlib.h"
using namespace Threading; static SafeArray<u8> state_buffer;
static wxScopedPtr< SafeArray<u8> > g_RecoveryState; // Simple lock boolean for the state buffer in use by a thread. This simple solution works because
// we are assured that state save/load actions will only be initiated from the main thread.
static bool state_buffer_lock = false;
namespace StateRecovery { // This boolean is to keep the system from resuming emulation until the current state has completely
// uploaded or downloaded itself. It is only modified from the main thread, and should only be read
// form the main thread.
bool sys_resume_lock = false;
bool HasState() class StateThread_Freeze : public PersistentThread
{
typedef PersistentThread _parent;
public:
StateThread_Freeze( const wxString& file )
{ {
return g_RecoveryState; m_name = L"SaveState::CopyAndZip";
}
void Recover() DevAssert( wxThread::IsMain(), "StateThread creation is allowed from the Main thread only." );
{ if( state_buffer_lock )
if( !g_RecoveryState ) return; throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
Console::Status( "Resuming execution from full memory state..." ); Start();
memLoadingState( *g_RecoveryState ).FreezeAll(); sys_resume_lock = true;
StateRecovery::Clear();
SysClearExecutionCache();
} }
SafeArray<u8> gzSavingBuffer; protected:
sptr ExecuteTask()
class gzThreadClass : public PersistentThread
{ {
typedef PersistentThread _parent; memSavingState( state_buffer ).FreezeAll();
return 0;
protected: }
gzFile m_file;
void DoThreadCleanup()
public:
gzThreadClass( const wxString& file ) :
m_file( gzopen( file.ToUTF8().data(), "wb" ) )
{
if( m_file == NULL )
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
Start();
}
virtual void DoThreadCleanup()
{
gzSavingBuffer.Dispose();
if( m_file != NULL )
{
gzclose( m_file );
m_file = NULL;
}
_parent::DoThreadCleanup();
}
virtual ~gzThreadClass() throw()
{
// fixme: something a little more graceful than Block, perhaps?
Block();
}
protected:
int ExecuteTask()
{
if( (m_file == NULL) || (gzSavingBuffer.GetSizeInBytes() == 0) ) return 0 ;
SetName( "Savestate::gzipper" );
gzwrite( m_file, gzSavingBuffer.GetPtr(), gzSavingBuffer.GetSizeInBytes() );
return 0;
}
};
wxScopedPtr<gzThreadClass> gzThread;
void SaveToFile( const wxString& file )
{ {
SysSuspend( false ); wxCommandEvent evt( pxEVT_FreezeFinished );
gzThread.reset( NULL ); // blocks on any existing gzipping business. evt.SetClientData( this );
wxGetApp().AddPendingEvent( evt );
memSavingState( gzSavingBuffer ).FreezeAll(); _parent::DoThreadCleanup();
}
};
// start that encoding thread: class StateThread_ZipToDisk : public PersistentThread
gzThread.reset( new gzThreadClass( file ) ); {
typedef PersistentThread _parent;
SysResume();
protected:
gzFile m_gzfp;
public:
StateThread_ZipToDisk( const wxString& file ) : m_gzfp( NULL )
{
m_name = L"SaveState::ZipToDisk";
DevAssert( wxThread::IsMain(), "StateThread creation is allowed from the Main thread only." );
if( state_buffer_lock )
throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
m_gzfp = gzopen( file.ToUTF8().data(), "wb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
try{ Start(); }
catch(...)
{
gzclose( m_gzfp ); m_gzfp = NULL;
throw;
}
sys_resume_lock = true;
} }
// Saves recovery state info to the given saveslot, or saves the active emulation state ~StateThread_ZipToDisk() throw()
// (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. :)
void SaveToSlot( uint num )
{ {
SaveToFile( SaveStateBase::GetFilename( num ) ); sys_resume_lock = false; // just in case;
if( m_gzfp != NULL ) gzclose( m_gzfp );
} }
// Creates a full recovery of the entire emulation state (CPU and all plugins). protected:
// If a current recovery state is already present, then nothing is done (the sptr ExecuteTask()
// existing recovery state takes precedence since if it were out-of-date it'd be
// deleted!).
void MakeFull()
{ {
if( g_RecoveryState ) return; Sleep( 2 );
if( !EmulationInProgress() ) return; if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
throw Exception::BadStream();
try return 0;
}
void DoThreadCleanup()
{
wxCommandEvent evt( pxEVT_FreezeFinished );
evt.SetClientData( this ); // tells message to clean us up.
wxGetApp().AddPendingEvent( evt );
_parent::DoThreadCleanup();
}
};
class StateThread_UnzipFromDisk : public PersistentThread
{
typedef PersistentThread _parent;
protected:
gzFile m_gzfp;
public:
StateThread_UnzipFromDisk( const wxString& file ) : m_gzfp( NULL )
{
m_name = L"SaveState::UnzipFromDisk";
DevAssert( wxThread::IsMain(), "StateThread creation is allowed from the Main thread only." );
if( state_buffer_lock )
throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
m_gzfp = gzopen( file.ToUTF8().data(), "wb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
try{ Start(); }
catch(...)
{ {
g_RecoveryState.reset( new SafeArray<u8>( L"Memory Savestate Recovery" ) ); gzclose( m_gzfp ); m_gzfp = NULL;
memSavingState( *g_RecoveryState ).FreezeAll(); throw;
}
catch( Exception::RuntimeError& ex )
{
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.FormatDisplayMessage().c_str() )
);
g_RecoveryState.reset();
} }
sys_resume_lock = true;
} }
// Clears and deallocates any recovery states. ~StateThread_UnzipFromDisk() throw()
void Clear()
{ {
g_RecoveryState.reset(); sys_resume_lock = false; // just in case;
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
protected:
sptr ExecuteTask()
{
// fixme: should start initially with the file size, and then grow from there.
static const int BlockSize = 327680;
int curidx = 0;
do
{
state_buffer.ExactAlloc( curidx+BlockSize );
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
curidx += BlockSize;
} while( !gzeof(m_gzfp) );
}
void DoThreadCleanup()
{
wxCommandEvent evt( pxEVT_ThawFinished );
evt.SetClientData( this ); // tells message to clean us up.
wxGetApp().AddPendingEvent( evt );
_parent::DoThreadCleanup();
}
};
void Pcsx2App::OnFreezeFinished( wxCommandEvent& evt )
{
state_buffer.Dispose();
state_buffer_lock = false;
SysClearExecutionCache();
SysResume();
if( PersistentThread* thread = (PersistentThread*)evt.GetClientData() )
{
delete thread;
} }
} }
void Pcsx2App::OnThawFinished( wxCommandEvent& evt )
{
state_buffer.Dispose();
state_buffer_lock = false;
SysClearExecutionCache();
SysResume();
if( PersistentThread* thread = (PersistentThread*)evt.GetClientData() )
{
delete thread;
}
}
void StateCopy_SaveToFile( const wxString& file )
{
if( state_buffer_lock ) return;
// [TODO] Implement optional 7zip compression here?
}
// Saves recovery state info to the given saveslot, or saves the active emulation state
// (if one exists) and no recovery data was found. This is needed because when a recovery
// state is made, the emulation state is usually reset so the only persisting state is
// the one in the memory save. :)
void StateCopy_SaveToSlot( uint num )
{
if( state_buffer_lock ) return;
StateCopy_SaveToFile( SaveStateBase::GetFilename( num ) );
}
bool StateCopy_IsValid()
{
return !state_buffer.IsDisposed();
}
bool StateCopy_HasFullState()
{
return false;
}
bool StateCopy_HasPartialState()
{
return false;
}
void StateCopy_FreezeToMem()
{
if( state_buffer_lock ) return;
}
void StateCopy_ThawFromMem()
{
if( state_buffer_lock ) return;
}
void StateCopy_Clear()
{
if( state_buffer_lock ) return;
state_buffer.Dispose();
}
// Creates a full recovery of the entire emulation state (CPU and all plugins).
// If a current recovery state is already present, then nothing is done (the
// existing recovery state takes precedence since if it were out-of-date it'd be
// deleted!).
void MakeFull()
{
//if( g_RecoveryState ) return;
//if( !SysHasValidState() ) return;
/*
try
{
g_RecoveryState = new SafeArray<u8>( L"Memory Savestate Recovery" );
memSavingState( *g_RecoveryState ).FreezeAll();
}
catch( Exception::RuntimeError& ex )
{
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.FormatDisplayMessage().c_str() )
);
g_RecoveryState = NULL;
}*/
}

View File

@ -193,13 +193,13 @@ public:
bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); } bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); }
}; };
namespace StateRecovery extern bool StateCopy_IsValid();
{ extern bool StateCopy_HasFullState();
extern bool HasState(); extern bool StateCopy_HasPartialState();
extern void Recover();
extern void SaveToFile( const wxString& file ); extern void StateCopy_FreezeToMem();
extern void SaveToSlot( uint num ); extern void StateCopy_ThawFromMem();
extern void MakeFull(); extern void StateCopy_SaveToFile( const wxString& file );
extern void Clear(); extern void StateCopy_SaveToSlot( uint num );
} extern void StateCopy_Clear();

View File

@ -53,7 +53,7 @@ static bool IsMtapPresent( uint port )
static void _ReadMcd(u8 *data, u32 adr, int size) static void _ReadMcd(u8 *data, u32 adr, int size)
{ {
EmuPlugins.McdRead( SysPlugins.McdRead(
sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()],
data, adr, size data, adr, size
); );
@ -61,7 +61,7 @@ static void _ReadMcd(u8 *data, u32 adr, int size)
static void _SaveMcd(const u8 *data, u32 adr, int size) static void _SaveMcd(const u8 *data, u32 adr, int size)
{ {
EmuPlugins.McdSave( SysPlugins.McdSave(
sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()],
data, adr, size data, adr, size
); );
@ -69,7 +69,7 @@ static void _SaveMcd(const u8 *data, u32 adr, int size)
static void _EraseMCDBlock(u32 adr) static void _EraseMCDBlock(u32 adr)
{ {
EmuPlugins.McdEraseBlock( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], adr ); SysPlugins.McdEraseBlock( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], adr );
} }
static u8 sio_xor( const u8 *buf, uint length ) static u8 sio_xor( const u8 *buf, uint length )
@ -601,7 +601,7 @@ void InitializeSIO(u8 value)
const uint port = sio.GetMemcardIndex(); const uint port = sio.GetMemcardIndex();
const uint slot = sio.activeMemcardSlot[port]; const uint slot = sio.activeMemcardSlot[port];
if( EmuPlugins.McdIsPresent( port, slot ) ) if( SysPlugins.McdIsPresent( port, slot ) )
{ {
sio2.packet.recvVal1 = 0x1100; sio2.packet.recvVal1 = 0x1100;
PAD_LOG("START MEMCARD [port:%d, slot:%d] - Present", port, slot ); PAD_LOG("START MEMCARD [port:%d, slot:%d] - Present", port, slot );
@ -665,7 +665,7 @@ void SaveStateBase::sioFreeze()
for( int port=0; port<2; ++port ) for( int port=0; port<2; ++port )
{ {
for( int slot=0; slot<8; ++slot ) for( int slot=0; slot<8; ++slot )
m_mcdCRCs[port][slot] = EmuPlugins.McdGetCRC( port, slot ); m_mcdCRCs[port][slot] = SysPlugins.McdGetCRC( port, slot );
} }
} }
Freeze( m_mcdCRCs ); Freeze( m_mcdCRCs );
@ -689,7 +689,7 @@ void SaveStateBase::sioFreeze()
{ {
for( int slot=0; slot<8; ++slot ) for( int slot=0; slot<8; ++slot )
{ {
u64 newCRC = EmuPlugins.McdGetCRC( port, slot ); u64 newCRC = SysPlugins.McdGetCRC( port, slot );
if( newCRC != m_mcdCRCs[port][slot] ) if( newCRC != m_mcdCRCs[port][slot] )
{ {
m_mcdCRCs[port][slot] = newCRC; m_mcdCRCs[port][slot] = newCRC;

View File

@ -252,40 +252,11 @@ void SysClearExecutionCache()
vuMicroCpuReset(); vuMicroCpuReset();
} }
// 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& srcfile ) void SysLoadState( const wxString& srcfile )
{ {
SafeArray<u8> buf; //SysClearExecutionCache();
gzFile gzfp = gzopen( srcfile.ToUTF8().data(), "rb" ); //cpuReset();
if( gzfp == NULL ) //joe.FreezeAll();
throw Exception::BadSavedState( srcfile, "File not found, or permission denied!" );
int curidx = 0;
do
{
buf.MakeRoomFor( curidx+327680 );
gzread( gzfp, buf.GetPtr(curidx), 327680 );
curidx += 327680;
} while( !gzeof(gzfp) );
gzclose( gzfp );
memLoadingState joe( buf ); // this could throw n StateLoadError.
// we perform a full backup to memory first so that we can restore later if the
// load fails. fixme: should this be made optional? It could have significant
// speed impact on state loads on slower machines with low ram. >_<
StateRecovery::MakeFull();
SysClearExecutionCache();
cpuReset();
joe.FreezeAll();
} }
// Maps a block of memory for use as a recompiled code buffer, and ensures that the // Maps a block of memory for use as a recompiled code buffer, and ensures that the

View File

@ -204,6 +204,7 @@ SysCoreThread::SysCoreThread( PluginManager& plugins ) :
, m_shortSuspend( false ) , m_shortSuspend( false )
, m_plugins( plugins ) , m_plugins( plugins )
{ {
m_name = L"EE Core";
} }
SysCoreThread::~SysCoreThread() throw() SysCoreThread::~SysCoreThread() throw()
@ -280,13 +281,13 @@ void SysCoreThread::CpuInitializeMess()
cpuReset(); cpuReset();
SysClearExecutionCache(); SysClearExecutionCache();
if( StateRecovery::HasState() ) if( StateCopy_IsValid() )
{ {
// no need to boot bios or detect CDs when loading savestates. // 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 // [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 // 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. // don't really want to run a game with the wrong ISO loaded into the emu.
StateRecovery::Recover(); StateCopy_ThawFromMem();
} }
else else
{ {
@ -354,7 +355,6 @@ static void _cet_callback_cleanup( void* handle )
sptr SysCoreThread::ExecuteTask() sptr SysCoreThread::ExecuteTask()
{ {
SetName( "EE Core" );
tls_coreThread = this; tls_coreThread = this;
StateCheck(); StateCheck();

View File

@ -19,7 +19,19 @@
using namespace Threading; using namespace Threading;
class SysSuspendableThread : public PersistentThread class ISysThread : public virtual IThread
{
public:
ISysThread() {}
virtual ~ISysThread() throw() {};
virtual bool IsSuspended() const { return false; }
virtual void Suspend( bool isBlocking = true ) { }
virtual void Resume() {}
};
class SysSuspendableThread : public PersistentThread, public virtual ISysThread
{ {
typedef PersistentThread _parent; typedef PersistentThread _parent;

View File

@ -26,6 +26,7 @@ class MainEmuFrame;
class GSFrame; class GSFrame;
class ConsoleLogFrame; class ConsoleLogFrame;
class PipeRedirectionBase; class PipeRedirectionBase;
class AppCoreThread;
#include "Utilities/HashMap.h" #include "Utilities/HashMap.h"
#include "Utilities/wxGuiTools.h" #include "Utilities/wxGuiTools.h"
@ -39,6 +40,9 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 ) DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 )
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 ) DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 ) DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
DECLARE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated, -1 )
DECLARE_EVENT_TYPE( pxEVT_FreezeFinished, -1 )
DECLARE_EVENT_TYPE( pxEVT_ThawFinished, -1 )
END_DECLARE_EVENT_TYPES() END_DECLARE_EVENT_TYPES()
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -73,11 +77,11 @@ enum MenuIdentifiers
MenuId_SkipBiosToggle, // enables the Bios Skip speedhack MenuId_SkipBiosToggle, // enables the Bios Skip speedhack
MenuId_Emu_SuspendResume, // suspends/resumes active emulation, retains plugin states MenuId_Sys_SuspendResume, // suspends/resumes active emulation, retains plugin states
MenuId_Emu_Close, // Closes the emulator (states are preserved) MenuId_Sys_Close, // Closes the emulator (states are preserved)
MenuId_Emu_Reset, // Issues a complete reset (wipes preserved states) MenuId_Sys_Reset, // Issues a complete reset (wipes preserved states)
MenuId_Emu_LoadStates, // Opens load states submenu MenuId_Sys_LoadStates, // Opens load states submenu
MenuId_Emu_SaveStates, // Opens save states submenu MenuId_Sys_SaveStates, // Opens save states submenu
MenuId_EnablePatches, MenuId_EnablePatches,
MenuId_State_Load, MenuId_State_Load,
@ -298,15 +302,15 @@ public:
protected: protected:
wxImageList m_ConfigImages; wxImageList m_ConfigImages;
wxScopedPtr<wxImageList> m_ToolbarImages; ScopedPtr<wxImageList> m_ToolbarImages;
wxScopedPtr<wxBitmap> m_Bitmap_Logo; ScopedPtr<wxBitmap> m_Bitmap_Logo;
wxScopedPtr<PipeRedirectionBase>m_StdoutRedirHandle; ScopedPtr<PipeRedirectionBase> m_StdoutRedirHandle;
wxScopedPtr<PipeRedirectionBase>m_StderrRedirHandle; ScopedPtr<PipeRedirectionBase> m_StderrRedirHandle;
public: public:
wxScopedPtr<SysCoreAllocations> m_CoreAllocs; ScopedPtr<SysCoreAllocations> m_CoreAllocs;
wxScopedPtr<PluginManager> m_CorePlugins; ScopedPtr<PluginManager> m_CorePlugins;
wxScopedPtr<SysCoreThread> m_CoreThread; ScopedPtr<SysCoreThread> m_CoreThread;
protected: protected:
// Note: Pointers to frames should not be scoped because wxWidgets handles deletion // Note: Pointers to frames should not be scoped because wxWidgets handles deletion
@ -333,16 +337,8 @@ public:
void SysExecute(); void SysExecute();
void SysExecute( CDVD_SourceType cdvdsrc ); void SysExecute( CDVD_SourceType cdvdsrc );
void SysReset() void SysReset();
{ bool SysIsActive() const;
m_CoreThread.reset();
m_CorePlugins.reset();
}
bool EmuInProgress() const
{
return m_CoreThread && m_CoreThread->IsRunning();
}
const wxBitmap& GetLogoBitmap(); const wxBitmap& GetLogoBitmap();
wxImageList& GetImgList_Config(); wxImageList& GetImgList_Config();
@ -350,28 +346,13 @@ public:
const AppImageIds& GetImgId() const { return m_ImageId; } const AppImageIds& GetImgId() const { return m_ImageId; }
MainEmuFrame& GetMainFrame() const bool HasMainFrame() const { return m_MainFrame != NULL; }
{ bool HasCoreThread() const { return m_CoreThread != NULL; }
wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
wxASSERT( m_MainFrame != NULL );
return *m_MainFrame;
}
MainEmuFrame& GetMainFrameOrExcept() const MainEmuFrame& GetMainFrame() const;
{ SysCoreThread& GetCoreThread() const;
if( m_MainFrame == NULL ) MainEmuFrame& GetMainFrameOrExcept() const;
throw Exception::ObjectIsNull( "main application frame" ); SysCoreThread& GetCoreThreadOrExcept() const;
return *m_MainFrame;
}
SysCoreThread& GetCoreThreadOrExcept() const
{
if( !m_CoreThread )
throw Exception::ObjectIsNull( "core emulation thread" );
return *m_CoreThread;
}
void OpenGsFrame(); void OpenGsFrame();
void OnGsFrameClosed(); void OnGsFrameClosed();
@ -412,9 +393,14 @@ protected:
void OnLoadPluginsComplete( wxCommandEvent& evt ); void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnSemaphorePing( wxCommandEvent& evt ); void OnSemaphorePing( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt ); void OnOpenModalDialog( wxCommandEvent& evt );
void OnCoreThreadTerminated( wxCommandEvent& evt );
void OnFreezeFinished( wxCommandEvent& evt );
void OnThawFinished( wxCommandEvent& evt );
void OnMessageBox( pxMessageBoxEvent& evt ); void OnMessageBox( pxMessageBoxEvent& evt );
void OnEmuKeyDown( wxKeyEvent& evt ); void OnEmuKeyDown( wxKeyEvent& evt );
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Override wx default exception handling behavior // Override wx default exception handling behavior
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -432,10 +418,10 @@ protected:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// AppEmuThread class // AppCoreThread class
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class AppEmuThread : public SysCoreThread class AppCoreThread : public SysCoreThread
{ {
typedef SysCoreThread _parent; typedef SysCoreThread _parent;
@ -443,15 +429,16 @@ protected:
wxKeyEvent m_kevt; wxKeyEvent m_kevt;
public: public:
AppEmuThread( PluginManager& plugins ); AppCoreThread( PluginManager& plugins );
virtual ~AppEmuThread() throw(); virtual ~AppCoreThread() throw();
virtual void Suspend( bool isBlocking=true ); virtual void Suspend( bool isBlocking=true );
virtual void StateCheck( bool isCancelable=true ); virtual void StateCheck( bool isCancelable=true );
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnResumeReady();
protected: protected:
virtual void OnResumeReady();
virtual void DoThreadCleanup();
sptr ExecuteTask(); sptr ExecuteTask();
}; };
@ -470,6 +457,7 @@ public:
bool IsReentrant() const { return Counter > 1; } bool IsReentrant() const { return Counter > 1; }
}; };
extern bool sys_resume_lock;
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest ); extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
extern void LoadPluginsPassive(); extern void LoadPluginsPassive();
@ -477,13 +465,13 @@ extern void LoadPluginsImmediate();
extern void UnloadPlugins(); extern void UnloadPlugins();
extern bool HandlePluginError( Exception::PluginError& ex ); extern bool HandlePluginError( Exception::PluginError& ex );
extern bool EmulationInProgress();
extern bool SysHasValidState();
extern void AppLoadSettings(); extern void AppLoadSettings();
extern void AppSaveSettings(); extern void AppSaveSettings();
extern void AppApplySettings( const AppConfig* oldconf=NULL ); extern void AppApplySettings( const AppConfig* oldconf=NULL );
extern bool SysHasValidState();
extern void SysStatus( const wxString& text ); extern void SysStatus( const wxString& text );
extern void SysSuspend( bool closePlugins = true ); extern void SysSuspend( bool closePlugins = true );
extern void SysResume(); extern void SysResume();
@ -491,36 +479,8 @@ extern void SysReset();
extern void SysExecute(); extern void SysExecute();
extern void SysExecute( CDVD_SourceType cdvdsrc ); extern void SysExecute( CDVD_SourceType cdvdsrc );
extern bool HasMainFrame();
extern bool HasCoreThread();
// -------------------------------------------------------------------------------------- extern MainEmuFrame& GetMainFrame();
// AppInvoke macro extern SysCoreThread& GetCoreThread();
// --------------------------------------------------------------------------------------
// This handy macro provides a safe way to invoke functions on objects that may or may not
// exist. If the object is null, the function is not called. Useful for calling things that
// are cosmetic optional, such as logging or status bars.
//
// Performance Note: This macro uses exception handling, and should not be used in the
// context of tight loops or performant code.
//
// Parameters:
// obj - name of the object. The name must have a matching accessor in Pcsx2App in the
// format of GetSomethingOrExcept(), where 'Something' would be the object name.
// runme - The function to call, complete with parameters. Note that parameters that
// perform actions (such as creating new objects or something) won't be run unless
// the 'obj' itself exists.
//
#define AppInvoke( obj, runme ) \
do { \
try { \
wxGetApp().Get##obj##OrExcept().runme; \
} \
catch( Exception::ObjectIsNull& ) { } \
} while( false )
#define AppInvokeBool( obj, runme, dest ) \
{ \
try { \
(dest) = wxGetApp().Get##obj##OrExcept().runme; \
} \
catch( Exception::ObjectIsNull& ) { } \
} while( false )

View File

@ -525,7 +525,8 @@ void AppConfig_OnChangedSettingsFolder( bool overwrite )
AppLoadSettings(); AppLoadSettings();
AppApplySettings(); AppApplySettings();
AppInvoke( MainFrame, ReloadRecentLists() ); if( HasMainFrame() )
GetMainFrame().ReloadRecentLists();
g_Conf->Folders.Logs.Mkdir(); g_Conf->Folders.Logs.Mkdir();

View File

@ -178,4 +178,4 @@ extern ConfigOverrides OverrideOptions;
extern wxFileConfig* OpenFileConfig( const wxString& filename ); extern wxFileConfig* OpenFileConfig( const wxString& filename );
extern void AppConfig_OnChangedSettingsFolder( bool overwrite = false ); extern void AppConfig_OnChangedSettingsFolder( bool overwrite = false );
extern wxScopedPtr<AppConfig> g_Conf; extern ScopedPtr<AppConfig> g_Conf;

View File

@ -37,12 +37,15 @@ DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog ); DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins ); DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete ); DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated );
DEFINE_EVENT_TYPE( pxEVT_FreezeFinished );
DEFINE_EVENT_TYPE( pxEVT_ThawFinished );
bool UseAdminMode = false; bool UseAdminMode = false;
wxDirName SettingsFolder; wxDirName SettingsFolder;
bool UseDefaultSettingsFolder = true; bool UseDefaultSettingsFolder = true;
wxScopedPtr<AppConfig> g_Conf; ScopedPtr<AppConfig> g_Conf;
ConfigOverrides OverrideOptions; ConfigOverrides OverrideOptions;
namespace Exception namespace Exception
@ -54,7 +57,7 @@ namespace Exception
class StartupAborted : public BaseException class StartupAborted : public BaseException
{ {
public: public:
virtual ~StartupAborted() throw() {} DEFINE_EXCEPTION_COPYTORS( StartupAborted )
StartupAborted( const wxString& msg_eng=L"Startup initialization was aborted by the user." ) StartupAborted( const wxString& msg_eng=L"Startup initialization was aborted by the user." )
{ {
@ -64,21 +67,21 @@ namespace Exception
}; };
} }
AppEmuThread::AppEmuThread( PluginManager& plugins ) : AppCoreThread::AppCoreThread( PluginManager& plugins ) :
SysCoreThread( plugins ) SysCoreThread( plugins )
, m_kevt() , m_kevt()
{ {
} }
AppEmuThread::~AppEmuThread() throw() AppCoreThread::~AppCoreThread() throw()
{ {
AppInvoke( MainFrame, ApplySettings() );
} }
void AppEmuThread::Suspend( bool isBlocking ) void AppCoreThread::Suspend( bool isBlocking )
{ {
_parent::Suspend( isBlocking ); _parent::Suspend( isBlocking );
AppInvoke( MainFrame, ApplySettings() ); if( HasMainFrame() )
GetMainFrame().ApplySettings();
// Clear the sticky key statuses, because hell knows what'll change while the PAD // Clear the sticky key statuses, because hell knows what'll change while the PAD
// plugin is suspended. // plugin is suspended.
@ -88,7 +91,7 @@ void AppEmuThread::Suspend( bool isBlocking )
m_kevt.m_altDown = false; m_kevt.m_altDown = false;
} }
void AppEmuThread::OnResumeReady() void AppCoreThread::OnResumeReady()
{ {
if( !DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." ) ) return; if( !DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." ) ) return;
@ -99,7 +102,19 @@ void AppEmuThread::OnResumeReady()
if( GSopen2 != NULL ) if( GSopen2 != NULL )
wxGetApp().OpenGsFrame(); wxGetApp().OpenGsFrame();
AppInvoke( MainFrame, ApplySettings() ); if( HasMainFrame() )
GetMainFrame().ApplySettings();
}
// Called whenever the thread has terminated, for either regular or irregular reasons.
// Typically the thread handles all its own errors, so there's no need to have error
// handling here. However it's a good idea to update the status of the GUI to reflect
// the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::DoThreadCleanup()
{
wxCommandEvent evt( pxEVT_AppCoreThread_Terminated );
wxGetApp().AddPendingEvent( evt );
_parent::DoThreadCleanup();
} }
// This is used when the GS plugin is handling its own window. Messages from the PAD // This is used when the GS plugin is handling its own window. Messages from the PAD
@ -111,14 +126,14 @@ static const int pxID_PadHandler_Keydown = 8030;
extern int TranslateGDKtoWXK( u32 keysym ); extern int TranslateGDKtoWXK( u32 keysym );
#endif #endif
void AppEmuThread::StateCheck( bool isCancelable ) void AppCoreThread::StateCheck( bool isCancelable )
{ {
_parent::StateCheck( isCancelable ); _parent::StateCheck( isCancelable );
const keyEvent* ev = PADkeyEvent(); const keyEvent* ev = PADkeyEvent();
if( ev == NULL || (ev->key == 0) ) return; if( ev == NULL || (ev->key == 0) ) return;
GetPluginManager().KeyEvent( *ev ); m_plugins.KeyEvent( *ev );
m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ); m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP );
const bool isDown = (ev->evt == KEYPRESS); const bool isDown = (ev->evt == KEYPRESS);
@ -141,8 +156,13 @@ void AppEmuThread::StateCheck( bool isCancelable )
wxGetApp().PostPadKey( m_kevt ); wxGetApp().PostPadKey( m_kevt );
} }
void AppEmuThread::ApplySettings( const Pcsx2Config& src ) // To simplify settings application rules and re-entry conditions, the main App's implementation
// of ApplySettings requires that the caller manually ensure that the thread has been properly
// suspended. If the thread has mot been suspended, this call will fail *silently*.
void AppCoreThread::ApplySettings( const Pcsx2Config& src )
{ {
if( !IsSuspended() ) return;
// Re-entry guard protects against cases where code wants to manually set core settings // Re-entry guard protects against cases where code wants to manually set core settings
// which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is // which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is
// usually the desired behavior) will be ignored. // usually the desired behavior) will be ignored.
@ -153,17 +173,14 @@ void AppEmuThread::ApplySettings( const Pcsx2Config& src )
SysCoreThread::ApplySettings( src ); SysCoreThread::ApplySettings( src );
} }
__forceinline bool EmulationInProgress() // Returns true if there is a "valid" virtual machine state from the user's perspective. This
{ // means the user has started the emulator and not issued a full reset.
return wxGetApp().EmuInProgress();
}
__forceinline bool SysHasValidState() __forceinline bool SysHasValidState()
{ {
return wxGetApp().EmuInProgress() || StateRecovery::HasState(); bool isRunning = HasCoreThread() ? GetCoreThread().IsRunning() : false;
return isRunning || StateCopy_HasFullState();
} }
bool HandlePluginError( Exception::PluginError& ex ) bool HandlePluginError( Exception::PluginError& ex )
{ {
if( pxDialogExists( DialogId_CoreSettings ) ) return true; if( pxDialogExists( DialogId_CoreSettings ) ) return true;
@ -183,7 +200,7 @@ bool HandlePluginError( Exception::PluginError& ex )
return result; return result;
} }
sptr AppEmuThread::ExecuteTask() sptr AppCoreThread::ExecuteTask()
{ {
try try
{ {
@ -195,7 +212,7 @@ sptr AppEmuThread::ExecuteTask()
m_plugins.Close(); m_plugins.Close();
if( ex.StreamName == g_Conf->FullpathToBios() ) if( ex.StreamName == g_Conf->FullpathToBios() )
{ {
GetPluginManager().Close(); m_plugins.Close();
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() + bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the BIOS Configuration Panel.") ); _("\n\nPress Ok to go to the BIOS Configuration Panel.") );
@ -270,7 +287,7 @@ void Pcsx2App::ReadUserModeSettings()
wxFileName usermodefile( FilenameDefs::GetUsermodeConfig() ); wxFileName usermodefile( FilenameDefs::GetUsermodeConfig() );
usermodefile.SetPath( usrlocaldir.ToString() ); usermodefile.SetPath( usrlocaldir.ToString() );
wxScopedPtr<wxFileConfig> conf_usermode( OpenFileConfig( usermodefile.GetFullPath() ) ); ScopedPtr<wxFileConfig> conf_usermode( OpenFileConfig( usermodefile.GetFullPath() ) );
wxString groupname( wxsFormat( L"CWD.%08x", hashres ) ); wxString groupname( wxsFormat( L"CWD.%08x", hashres ) );
@ -401,10 +418,10 @@ bool Pcsx2App::OnInit()
wxInitAllImageHandlers(); wxInitAllImageHandlers();
if( !wxApp::OnInit() ) return false; if( !wxApp::OnInit() ) return false;
g_Conf.reset( new AppConfig() ); g_Conf = new AppConfig();
m_StdoutRedirHandle.reset( NewPipeRedir(stdout) ); m_StdoutRedirHandle = NewPipeRedir(stdout);
m_StderrRedirHandle.reset( NewPipeRedir(stderr) ); m_StderrRedirHandle = NewPipeRedir(stderr);
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
#define pxMessageBoxEventThing(func) \ #define pxMessageBoxEventThing(func) \
@ -416,11 +433,14 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) ); Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnFreezeFinished ) );
Connect( pxEVT_ThawFinished, wxCommandEventHandler( Pcsx2App::OnThawFinished ) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) ); Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
// User/Admin Mode Dual Setup: // User/Admin Mode Dual Setup:
// Pcsx2 now supports two fundamental modes of operation. The default is Classic mode, // PCSX2 now supports two fundamental modes of operation. The default is Classic mode,
// which uses the Current Working Directory (CWD) for all user data files, and requires // which uses the Current Working Directory (CWD) for all user data files, and requires
// Admin access on Vista (and some Linux as well). The second mode is the Vista- // Admin access on Vista (and some Linux as well). The second mode is the Vista-
// compatible \documents folder usage. The mode is determined by the presence and // compatible \documents folder usage. The mode is determined by the presence and
@ -456,7 +476,7 @@ bool Pcsx2App::OnInit()
SysDetect(); SysDetect();
AppApplySettings(); AppApplySettings();
m_CoreAllocs.reset( new SysCoreAllocations() ); m_CoreAllocs = new SysCoreAllocations();
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) ) if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{ {
@ -578,6 +598,14 @@ void Pcsx2App::Ping() const
// Pcsx2App Event Handlers // Pcsx2App Event Handlers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Invoked by the AppCoreThread when the thread has terminated itself.
void Pcsx2App::OnCoreThreadTerminated( wxCommandEvent& evt )
{
if( HasMainFrame() )
GetMainFrame().ApplySettings();
m_CoreThread = NULL;
}
void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt ) void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt )
{ {
((Semaphore*)evt.GetClientData())->Post(); ((Semaphore*)evt.GetClientData())->Post();
@ -709,7 +737,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
// the glorious user, whomever (s)he-it might be. // the glorious user, whomever (s)he-it might be.
bool Pcsx2App::PrepForExit() bool Pcsx2App::PrepForExit()
{ {
m_CoreThread.reset(); m_CoreThread = NULL;
CleanupMess(); CleanupMess();
return true; return true;
@ -750,6 +778,36 @@ Pcsx2App::~Pcsx2App()
CleanupMess(); CleanupMess();
} }
MainEmuFrame& Pcsx2App::GetMainFrame() const
{
wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
wxASSERT( m_MainFrame != NULL );
return *m_MainFrame;
}
SysCoreThread& Pcsx2App::GetCoreThread() const
{
wxASSERT( m_CoreThread != NULL );
return *m_CoreThread;
}
MainEmuFrame& Pcsx2App::GetMainFrameOrExcept() const
{
if( m_MainFrame == NULL )
throw Exception::ObjectIsNull( "main application frame" );
return *m_MainFrame;
}
SysCoreThread& Pcsx2App::GetCoreThreadOrExcept() const
{
if( !m_CoreThread )
throw Exception::ObjectIsNull( "core emulation thread" );
return *m_CoreThread;
}
void AppApplySettings( const AppConfig* oldconf ) void AppApplySettings( const AppConfig* oldconf )
{ {
DevAssert( wxThread::IsMain(), "ApplySettings valid from the GUI thread only." ); DevAssert( wxThread::IsMain(), "ApplySettings valid from the GUI thread only." );
@ -780,9 +838,10 @@ void AppApplySettings( const AppConfig* oldconf )
} }
} }
// Both AppInvokes cause unhandled runtime errors in Linux. if( HasMainFrame() )
AppInvoke( MainFrame, ApplySettings() ); GetMainFrame().ApplySettings();
AppInvoke( CoreThread, ApplySettings( g_Conf->EmuOptions ) ); if( HasCoreThread() )
GetCoreThread().ApplySettings( g_Conf->EmuOptions );
} }
void AppLoadSettings() void AppLoadSettings()
@ -793,7 +852,8 @@ void AppLoadSettings()
IniLoader loader( *conf ); IniLoader loader( *conf );
g_Conf->LoadSave( loader ); g_Conf->LoadSave( loader );
AppInvoke( MainFrame, LoadRecentIsoList( *conf ) ); if( HasMainFrame() )
GetMainFrame().LoadRecentIsoList( *conf );
} }
void AppSaveSettings() void AppSaveSettings()
@ -804,7 +864,8 @@ void AppSaveSettings()
IniSaver saver( *conf ); IniSaver saver( *conf );
g_Conf->LoadSave( saver ); g_Conf->LoadSave( saver );
AppInvoke( MainFrame, SaveRecentIsoList( *conf ) ); if( HasMainFrame() )
GetMainFrame().SaveRecentIsoList( *conf );
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -815,9 +876,15 @@ void AppSaveSettings()
// configured CDVD source device. // configured CDVD source device.
void Pcsx2App::SysExecute() void Pcsx2App::SysExecute()
{ {
if( sys_resume_lock )
{
Console::WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
return;
}
SysReset(); SysReset();
LoadPluginsImmediate(); LoadPluginsImmediate();
m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) ); m_CoreThread = new AppCoreThread( *m_CorePlugins );
m_CoreThread->Resume(); m_CoreThread->Resume();
} }
@ -826,15 +893,35 @@ void Pcsx2App::SysExecute()
// sources. // sources.
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc ) void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
{ {
if( sys_resume_lock )
{
Console::WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
return;
}
SysReset(); SysReset();
LoadPluginsImmediate(); LoadPluginsImmediate();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
CDVDsys_ChangeSource( cdvdsrc ); CDVDsys_ChangeSource( cdvdsrc );
m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) ); m_CoreThread = new AppCoreThread( *m_CorePlugins );
m_CoreThread->Resume(); m_CoreThread->Resume();
} }
void Pcsx2App::SysReset()
{
m_CoreThread = NULL;
}
// Returns the state of the emulated system (Virtual Machine). Returns true if the
// system is active, or is in the process of reporting or handling an error. Returns
// false if there is no active system (thread is shutdown or hasn't been started).
bool Pcsx2App::SysIsActive() const
{
return m_CoreThread && m_CoreThread->IsRunning();
}
void Pcsx2App::OpenGsFrame() void Pcsx2App::OpenGsFrame()
{ {
if( m_gsFrame != NULL ) return; if( m_gsFrame != NULL ) return;
@ -857,11 +944,13 @@ void Pcsx2App::OnGsFrameClosed()
// Writes text to console and updates the window status bar and/or HUD or whateverness. // Writes text to console and updates the window status bar and/or HUD or whateverness.
// FIXME: This probably isn't thread safe. >_<
void SysStatus( const wxString& text ) void SysStatus( const wxString& text )
{ {
// mirror output to the console! // mirror output to the console!
Console::Status( text.c_str() ); Console::Status( text.c_str() );
AppInvoke( MainFrame, SetStatusText( text ) ); if( HasMainFrame() )
GetMainFrame().SetStatusText( text );
} }
// Executes the emulator using a saved/existing virtual machine state and currently // Executes the emulator using a saved/existing virtual machine state and currently
@ -878,18 +967,48 @@ void SysExecute( CDVD_SourceType cdvdsrc )
void SysResume() void SysResume()
{ {
AppInvoke( CoreThread, Resume() ); if( !HasCoreThread() ) return;
if( sys_resume_lock )
{
Console::WriteLn( "SysResume: State is locked, ignoring Resume request!" );
return;
}
GetCoreThread().Resume();
} }
void SysSuspend( bool closePlugins ) void SysSuspend( bool closePlugins )
{ {
if( !HasCoreThread() ) return;
if( closePlugins ) if( closePlugins )
AppInvoke( CoreThread, Suspend(closePlugins) ); GetCoreThread().Suspend();
else else
AppInvoke( CoreThread, ShortSuspend() ); GetCoreThread().ShortSuspend();
} }
void SysReset() void SysReset()
{ {
wxGetApp().SysReset(); wxGetApp().SysReset();
} }
bool HasMainFrame()
{
return wxGetApp().HasMainFrame();
}
bool HasCoreThread()
{
return wxGetApp().HasCoreThread();
}
MainEmuFrame& GetMainFrame()
{
return wxGetApp().GetMainFrame();
}
SysCoreThread& GetCoreThread()
{
return wxGetApp().GetCoreThread();
}

View File

@ -62,7 +62,7 @@ const wxImage& LoadImageAny(
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
const wxBitmap& Pcsx2App::GetLogoBitmap() const wxBitmap& Pcsx2App::GetLogoBitmap()
{ {
if( m_Bitmap_Logo != NULL ) if( m_Bitmap_Logo )
return *m_Bitmap_Logo; return *m_Bitmap_Logo;
wxFileName mess; wxFileName mess;
@ -90,7 +90,7 @@ const wxBitmap& Pcsx2App::GetLogoBitmap()
wxImage img; wxImage img;
EmbeddedImage<res_BackgroundLogo> temp; // because gcc can't allow non-const temporaries. EmbeddedImage<res_BackgroundLogo> temp; // because gcc can't allow non-const temporaries.
LoadImageAny( img, useTheme, mess, L"BackgroundLogo", temp ); LoadImageAny( img, useTheme, mess, L"BackgroundLogo", temp );
m_Bitmap_Logo.reset( new wxBitmap( img ) ); m_Bitmap_Logo = new wxBitmap( img );
return *m_Bitmap_Logo; return *m_Bitmap_Logo;
} }
@ -137,10 +137,10 @@ wxImageList& Pcsx2App::GetImgList_Config()
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
wxImageList& Pcsx2App::GetImgList_Toolbars() wxImageList& Pcsx2App::GetImgList_Toolbars()
{ {
if( m_ToolbarImages == NULL ) if( !m_ToolbarImages )
{ {
const int imgSize = g_Conf->Toolbar_ImageSize ? 64 : 32; const int imgSize = g_Conf->Toolbar_ImageSize ? 64 : 32;
m_ToolbarImages.reset( new wxImageList( imgSize, imgSize ) ); m_ToolbarImages = new wxImageList( imgSize, imgSize );
wxFileName mess; wxFileName mess;
bool useTheme = (g_Conf->DeskTheme != L"default"); bool useTheme = (g_Conf->DeskTheme != L"default");

View File

@ -72,14 +72,13 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
static const wxString LabelGreets = wxString::FromUTF8( static const wxString LabelGreets = wxString::FromUTF8(
"Contributors" "Contributors"
"\n\n" "\n\n"
"Hiryu and Sjeep for libcvd (the iso parsing and" "Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"
"filesystem driver code), nneeve (fpu and vu help)"
"\n\n" "\n\n"
"Plugin Specialists: ChickenLiver (Lilypad), Efp (efp)," "Plugin Specialists: ChickenLiver (Lilypad), Efp (efp),"
"Gabest (Gsdx, Cdvdolio, Xpad), Zeydlitz (ZZogl)" "Gabest (Gsdx, Cdvdolio, Xpad), Zeydlitz (ZZogl)"
"\n\n" "\n\n"
"Special thanks to: black_wd, Belmont, BGome, _Demo_, Dreamtime," "Special thanks to: black_wd, Belmont, BGome, _Demo_, Dreamtime,"
"F|RES, MrBrown, razorblade, Seta-san, Skarmeth" "F|RES, MrBrown, razorblade, Seta-san, Skarmeth, feal87"
); );
wxBoxSizer& mainSizer = *new wxBoxSizer( wxVERTICAL ); wxBoxSizer& mainSizer = *new wxBoxSizer( wxVERTICAL );

View File

@ -33,13 +33,13 @@ void GSFrame::InitDefaultAccelerators()
m_Accels.Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" ); m_Accels.Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" );
m_Accels.Map( AAC( WXK_TAB ).Shift(), "Framelimiter_MasterToggle" ); m_Accels.Map( AAC( WXK_TAB ).Shift(), "Framelimiter_MasterToggle" );
m_Accels.Map( AAC( WXK_ESCAPE ), "Emu_Suspend" ); m_Accels.Map( AAC( WXK_ESCAPE ), "Sys_Suspend" );
m_Accels.Map( AAC( WXK_F8 ), "Emu_TakeSnapshot" ); m_Accels.Map( AAC( WXK_F8 ), "Sys_TakeSnapshot" );
m_Accels.Map( AAC( WXK_F9 ), "Emu_RenderswitchToggle" ); m_Accels.Map( AAC( WXK_F9 ), "Sys_RenderswitchToggle" );
m_Accels.Map( AAC( WXK_F10 ), "Emu_LoggingToggle" ); m_Accels.Map( AAC( WXK_F10 ), "Sys_LoggingToggle" );
m_Accels.Map( AAC( WXK_F11 ), "Emu_FreezeGS" ); m_Accels.Map( AAC( WXK_F11 ), "Sys_FreezeGS" );
m_Accels.Map( AAC( WXK_F12 ), "Emu_RecordingToggle" ); m_Accels.Map( AAC( WXK_F12 ), "Sys_RecordingToggle" );
} }
GSFrame::GSFrame(wxWindow* parent, const wxString& title): GSFrame::GSFrame(wxWindow* parent, const wxString& title):
@ -54,7 +54,8 @@ GSFrame::GSFrame(wxWindow* parent, const wxString& title):
GSFrame::~GSFrame() throw() GSFrame::~GSFrame() throw()
{ {
AppInvoke( CoreThread, Suspend() ); // Just in case...! if( HasCoreThread() )
GetCoreThread().Suspend(); // Just in case...!
} }
void GSFrame::OnCloseWindow(wxCloseEvent& evt) void GSFrame::OnCloseWindow(wxCloseEvent& evt)

View File

@ -69,31 +69,32 @@ namespace Implementations
{ {
} }
void Emu_Suspend() void Sys_Suspend()
{ {
AppInvoke( CoreThread, Suspend() ); if( HasCoreThread() )
AppInvoke( MainFrame, ApplySettings() ); GetCoreThread().Suspend();
} }
void Emu_Resume() void Sys_Resume()
{ {
AppInvoke( CoreThread, Resume() ); if( HasCoreThread() )
AppInvoke( MainFrame, ApplySettings() ); GetCoreThread().Resume();
} }
void Emu_TakeSnapshot() void Sys_TakeSnapshot()
{ {
GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() ); GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() );
} }
void Emu_RenderToggle() void Sys_RenderToggle()
{ {
AppInvoke( CoreThread, Suspend() ); if( !HasCoreThread() ) return;
SysSuspend();
renderswitch = !renderswitch; renderswitch = !renderswitch;
AppInvoke( CoreThread, Resume() ); SysResume();
} }
void Emu_LoggingToggle() void Sys_LoggingToggle()
{ {
#ifdef PCSX2_DEVBUILD #ifdef PCSX2_DEVBUILD
// There's likely a better way to implement this, but this seemed useful. // There's likely a better way to implement this, but this seemed useful.
@ -108,7 +109,7 @@ namespace Implementations
#endif #endif
} }
void Emu_FreezeGS() void Sys_FreezeGS()
{ {
// fixme : fix up gsstate mess and make it mtgs compatible -- air // fixme : fix up gsstate mess and make it mtgs compatible -- air
#ifdef _STGS_GSSTATE_CODE #ifdef _STGS_GSSTATE_CODE
@ -137,7 +138,7 @@ namespace Implementations
} }
void Emu_RecordingToggle() void Sys_RecordingToggle()
{ {
g_Pcsx2Recording ^= 1; g_Pcsx2Recording ^= 1;
@ -206,37 +207,37 @@ static const GlobalCommandDescriptor CommandDeclarations[] =
NULL, NULL,
}, },
{ "Emu_Suspend", { "Sys_Suspend",
Implementations::Emu_Suspend, Implementations::Sys_Suspend,
NULL, NULL,
NULL, NULL,
}, },
{ "Emu_TakeSnapshot", { "Sys_TakeSnapshot",
Implementations::Emu_TakeSnapshot, Implementations::Sys_TakeSnapshot,
NULL, NULL,
NULL, NULL,
}, },
{ "Emu_RenderswitchToggle", { "Sys_RenderswitchToggle",
Implementations::Emu_RenderToggle, Implementations::Sys_RenderToggle,
NULL, NULL,
NULL, NULL,
}, },
{ "Emu_LoggingToggle", { "Sys_LoggingToggle",
Implementations::Emu_LoggingToggle, Implementations::Sys_LoggingToggle,
NULL, NULL,
NULL, NULL,
}, },
{ "Emu_FreezeGS", { "Sys_FreezeGS",
Implementations::Emu_FreezeGS, Implementations::Sys_FreezeGS,
NULL, NULL,
NULL, NULL,
}, },
{ "Emu_RecordingToggle", { "Sys_RecordingToggle",
Implementations::Emu_RecordingToggle, Implementations::Sys_RecordingToggle,
NULL, NULL,
NULL, NULL,
}, },
@ -304,11 +305,11 @@ void Pcsx2App::InitDefaultGlobalAccelerators()
GlobalAccels.Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" ); GlobalAccels.Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" );
GlobalAccels.Map( AAC( WXK_TAB ).Shift(), "Framelimiter_MasterToggle" ); GlobalAccels.Map( AAC( WXK_TAB ).Shift(), "Framelimiter_MasterToggle" );
GlobalAccels.Map( AAC( WXK_ESCAPE ), "Emu_Suspend"); GlobalAccels.Map( AAC( WXK_ESCAPE ), "Sys_Suspend");
GlobalAccels.Map( AAC( WXK_F8 ), "Emu_TakeSnapshot"); GlobalAccels.Map( AAC( WXK_F8 ), "Sys_TakeSnapshot");
GlobalAccels.Map( AAC( WXK_F9 ), "Emu_RenderswitchToggle"); GlobalAccels.Map( AAC( WXK_F9 ), "Sys_RenderswitchToggle");
GlobalAccels.Map( AAC( WXK_F10 ), "Emu_LoggingToggle"); GlobalAccels.Map( AAC( WXK_F10 ), "Sys_LoggingToggle");
GlobalAccels.Map( AAC( WXK_F11 ), "Emu_FreezeGS"); GlobalAccels.Map( AAC( WXK_F11 ), "Sys_FreezeGS");
GlobalAccels.Map( AAC( WXK_F12 ), "Emu_RecordingToggle"); GlobalAccels.Map( AAC( WXK_F12 ), "Sys_RecordingToggle");
} }

View File

@ -211,8 +211,8 @@ void MainEmuFrame::ConnectMenus()
ConnectMenu( MenuId_SkipBiosToggle, Menu_SkipBiosToggle_Click ); ConnectMenu( MenuId_SkipBiosToggle, Menu_SkipBiosToggle_Click );
ConnectMenu( MenuId_Exit, Menu_Exit_Click ); ConnectMenu( MenuId_Exit, Menu_Exit_Click );
ConnectMenu( MenuId_Emu_SuspendResume, Menu_SuspendResume_Click ); ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click );
ConnectMenu( MenuId_Emu_Reset, Menu_EmuReset_Click ); ConnectMenu( MenuId_Sys_Reset, Menu_EmuReset_Click );
ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click ); ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click );
@ -367,23 +367,23 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
_("Closing PCSX2 may be hazardous to your health")); _("Closing PCSX2 may be hazardous to your health"));
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
m_menuEmu.Append(MenuId_Emu_SuspendResume, _("Suspend"), m_menuEmu.Append(MenuId_Sys_SuspendResume, _("Suspend"),
_("Stops emulation dead in its tracks") )->Enable( SysHasValidState() ); _("Stops emulation dead in its tracks") )->Enable( SysHasValidState() );
m_menuEmu.AppendSeparator(); m_menuEmu.AppendSeparator();
//m_menuEmu.Append(MenuId_Emu_Close, _("Close"), //m_menuEmu.Append(MenuId_Sys_Close, _("Close"),
// _("Stops emulation and closes the GS window.")); // _("Stops emulation and closes the GS window."));
m_menuEmu.Append(MenuId_Emu_LoadStates, _("Load state"), &m_LoadStatesSubmenu); m_menuEmu.Append(MenuId_Sys_LoadStates, _("Load state"), &m_LoadStatesSubmenu);
m_menuEmu.Append(MenuId_Emu_SaveStates, _("Save state"), &m_SaveStatesSubmenu); m_menuEmu.Append(MenuId_Sys_SaveStates, _("Save state"), &m_SaveStatesSubmenu);
m_menuEmu.AppendSeparator(); m_menuEmu.AppendSeparator();
m_menuEmu.Append(MenuId_EnablePatches, _("Enable Patches"), m_menuEmu.Append(MenuId_EnablePatches, _("Enable Patches"),
wxEmptyString, wxITEM_CHECK); wxEmptyString, wxITEM_CHECK);
m_menuEmu.AppendSeparator(); m_menuEmu.AppendSeparator();
m_menuEmu.Append(MenuId_Emu_Reset, _("Reset"), m_menuEmu.Append(MenuId_Sys_Reset, _("Reset"),
_("Resets emulation state and re-runs current image")); _("Resets emulation state and re-runs current image"));
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -470,9 +470,7 @@ void MainEmuFrame::ReloadRecentLists()
if( m_RecentIsoList ) if( m_RecentIsoList )
m_RecentIsoList->Save( *cfg ); m_RecentIsoList->Save( *cfg );
m_RecentIsoList.reset(); m_RecentIsoList.Reassign( new wxFileHistory(g_Conf->RecentFileCount) )->Load( *cfg );
m_RecentIsoList.reset( new wxFileHistory( g_Conf->RecentFileCount ) );
m_RecentIsoList->Load( *cfg );
UpdateIsoSrcFile(); UpdateIsoSrcFile();
cfg->Flush(); cfg->Flush();
} }
@ -484,12 +482,11 @@ void MainEmuFrame::ApplySettings()
GetMenuBar()->Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled ); GetMenuBar()->Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled );
GetMenuBar()->Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled ); GetMenuBar()->Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled );
GetMenuBar()->Enable( MenuId_Emu_SuspendResume, SysHasValidState() ); GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() );
bool result = false;
AppInvokeBool( CoreThread, IsSuspended(), result );
GetMenuBar()->SetLabel( MenuId_Emu_SuspendResume, result ? _("Resume") :_("Suspend") );
if( HasCoreThread() )
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, GetCoreThread().IsSuspended() ? _("Resume") :_("Suspend") );
if( m_RecentIsoList ) if( m_RecentIsoList )
{ {
if( m_RecentIsoList->GetMaxFiles() != g_Conf->RecentFileCount ) if( m_RecentIsoList->GetMaxFiles() != g_Conf->RecentFileCount )

View File

@ -43,7 +43,7 @@ class MainEmuFrame : public wxFrame
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
protected: protected:
wxScopedPtr<wxFileHistory> m_RecentIsoList; ScopedPtr<wxFileHistory> m_RecentIsoList;
wxStatusBar& m_statusbar; wxStatusBar& m_statusbar;
wxStaticBitmap m_background; wxStaticBitmap m_background;
@ -74,7 +74,7 @@ public:
void OnLogBoxHidden(); void OnLogBoxHidden();
bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Emu_SuspendResume ); } bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Sys_SuspendResume ); }
void UpdateIsoSrcFile(); void UpdateIsoSrcFile();
void UpdateIsoSrcSelection(); void UpdateIsoSrcSelection();
void ApplySettings(); void ApplySettings();

View File

@ -93,10 +93,10 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
} }
} }
if( EmulationInProgress() ) if( SysHasValidState() )
{ {
// [TODO] : Add one of 'dems checkboxes that read like "[x] don't show this stupid shit again, kthx." // [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?") ); bool result = Msgbox::OkCancel( pxE( ".Popup:ConfirmSysReset", L"This will reset the emulator and your current emulation session will be lost. Are you sure?") );
if( !result ) if( !result )
{ {
@ -190,11 +190,9 @@ void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event) void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
{ {
if( !SysHasValidState() ) return; if( !SysHasValidState() ) return;
if( !HasCoreThread() ) return;
bool result = false; if( GetCoreThread().IsSuspended() )
AppInvokeBool( CoreThread, IsSuspended(), result );
if( result )
SysResume(); SysResume();
else else
SysSuspend(); SysSuspend();
@ -202,15 +200,13 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event) void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event)
{ {
bool wasInProgress = EmulationInProgress(); if( !SysHasValidState() ) return;
bool wasSuspended; bool wasSuspended = HasCoreThread() ? GetCoreThread().IsSuspended() : true;
AppInvokeBool( CoreThread, IsSuspended(), wasSuspended );
SysReset(); SysReset();
if( !wasInProgress || wasSuspended ) return; if( !wasSuspended )
SysExecute(); SysExecute();
} }
void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event) void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)

View File

@ -95,15 +95,15 @@ bool Panels::BiosSelectorPanel::ValidateEnumerationStatus()
// Impl Note: ScopedPtr used so that resources get cleaned up if an exception // Impl Note: ScopedPtr used so that resources get cleaned up if an exception
// occurs during file enumeration. // occurs during file enumeration.
wxScopedPtr<wxArrayString> bioslist( new wxArrayString() ); ScopedPtr<wxArrayString> bioslist( new wxArrayString() );
if( m_FolderPicker.GetPath().Exists() ) if( m_FolderPicker.GetPath().Exists() )
wxDir::GetAllFiles( m_FolderPicker.GetPath().ToString(), bioslist.get(), L"*.bin", wxDIR_FILES ); wxDir::GetAllFiles( m_FolderPicker.GetPath().ToString(), bioslist, L"*.bin", wxDIR_FILES );
if( !m_BiosList || (*bioslist != *m_BiosList) ) if( !m_BiosList || (*bioslist != *m_BiosList) )
validated = false; validated = false;
m_BiosList.swap( bioslist ); m_BiosList.SwapPtr( bioslist );
return validated; return validated;
} }

View File

@ -55,7 +55,7 @@ namespace Exception
Panels::BaseApplicableConfigPanel* m_Panel; Panels::BaseApplicableConfigPanel* m_Panel;
public: public:
virtual ~CannotApplySettings() throw() {} DEFINE_EXCEPTION_COPYTORS( CannotApplySettings )
explicit CannotApplySettings( Panels::BaseApplicableConfigPanel* thispanel, const char* msg=wxLt("Cannot apply new settings, one of the settings is invalid.") ) explicit CannotApplySettings( Panels::BaseApplicableConfigPanel* thispanel, const char* msg=wxLt("Cannot apply new settings, one of the settings is invalid.") )
{ {
@ -351,7 +351,7 @@ namespace Panels
class BiosSelectorPanel : public BaseSelectorPanel class BiosSelectorPanel : public BaseSelectorPanel
{ {
protected: protected:
wxScopedPtr<wxArrayString> m_BiosList; ScopedPtr<wxArrayString> m_BiosList;
wxListBox& m_ComboBox; wxListBox& m_ComboBox;
DirPickerPanel& m_FolderPicker; DirPickerPanel& m_FolderPicker;
@ -454,8 +454,8 @@ namespace Panels
ComboBoxPanel& m_ComponentBoxes; ComboBoxPanel& m_ComponentBoxes;
bool m_Canceled; bool m_Canceled;
wxScopedPtr<wxArrayString> m_FileList; // list of potential plugin files ScopedPtr<wxArrayString> m_FileList; // list of potential plugin files
wxScopedPtr<EnumThread> m_EnumeratorThread; ScopedPtr<EnumThread> m_EnumeratorThread;
public: public:
virtual ~PluginSelectorPanel(); virtual ~PluginSelectorPanel();

View File

@ -364,9 +364,7 @@ void Panels::PluginSelectorPanel::DoRefresh()
wxCommandEvent evt( pxEVT_ShowStatusBar ); wxCommandEvent evt( pxEVT_ShowStatusBar );
GetEventHandler()->AddPendingEvent( evt ); GetEventHandler()->AddPendingEvent( evt );
// Use a thread to load plugins. m_EnumeratorThread.Delete() = new EnumThread( *this );
m_EnumeratorThread.reset( NULL );
m_EnumeratorThread.reset( new EnumThread( *this ) );
if( DisableThreading ) if( DisableThreading )
m_EnumeratorThread->DoNextPlugin( 0 ); m_EnumeratorThread->DoNextPlugin( 0 );
@ -376,7 +374,7 @@ void Panels::PluginSelectorPanel::DoRefresh()
bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
{ {
m_EnumeratorThread.reset(); // make sure the thread is STOPPED, just in case... m_EnumeratorThread = NULL; // make sure the thread is STOPPED, just in case...
bool validated = true; bool validated = true;
@ -385,20 +383,20 @@ bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
// Impl Note: ScopedPtr used so that resources get cleaned up if an exception // Impl Note: ScopedPtr used so that resources get cleaned up if an exception
// occurs during file enumeration. // occurs during file enumeration.
wxScopedPtr<wxArrayString> pluginlist( new wxArrayString() ); ScopedPtr<wxArrayString> pluginlist( new wxArrayString() );
int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes.GetPluginsPath(), pluginlist.get() ); int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes.GetPluginsPath(), pluginlist );
if( !m_FileList || (*pluginlist != *m_FileList) ) if( !m_FileList || (*pluginlist != *m_FileList) )
validated = false; validated = false;
if( pluggers == 0 ) if( pluggers == 0 )
{ {
m_FileList.reset(); m_FileList = NULL;
return validated; return validated;
} }
m_FileList.swap( pluginlist ); m_FileList.SwapPtr( pluginlist );
m_StatusPanel.SetGaugeLength( pluggers ); m_StatusPanel.SetGaugeLength( pluggers );
@ -428,7 +426,7 @@ void Panels::PluginSelectorPanel::OnShowStatusBar( wxCommandEvent& evt )
void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt ) void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
{ {
m_EnumeratorThread.reset(); m_EnumeratorThread = NULL;
// fixme: Default plugins should be picked based on the timestamp of the DLL or something? // fixme: Default plugins should be picked based on the timestamp of the DLL or something?
// (for now we just force it to selection zero if nothing's selected) // (for now we just force it to selection zero if nothing's selected)

View File

@ -63,13 +63,15 @@ protected:
int ExecuteTask(); int ExecuteTask();
}; };
static wxScopedPtr<LoadPluginsTask> _loadTask; static ScopedPtr<LoadPluginsTask> _loadTask;
LoadPluginsTask::~LoadPluginsTask() throw() LoadPluginsTask::~LoadPluginsTask() throw()
{ {
_loadTask.release(); if( _loadTask )
_loadTask.DetachPtr(); // avoids recursive deletion
PersistentThread::Cancel(); PersistentThread::Cancel();
_loadTask.reset(); _loadTask = NULL;
} }
int LoadPluginsTask::ExecuteTask() int LoadPluginsTask::ExecuteTask()
@ -111,10 +113,10 @@ int LoadPluginsTask::ExecuteTask()
int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
{ {
wxScopedPtr<wxArrayString> placebo; ScopedPtr<wxArrayString> placebo;
wxArrayString* realdest = dest; wxArrayString* realdest = dest;
if( realdest == NULL ) if( realdest == NULL )
placebo.reset( realdest = new wxArrayString() ); placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done
#ifdef __WXMSW__ #ifdef __WXMSW__
// Windows pretty well has a strict "must end in .dll" rule. // Windows pretty well has a strict "must end in .dll" rule.
@ -137,8 +139,8 @@ void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt )
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt ) void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
{ {
// scoped ptr ensures the thread object is cleaned up even on exception: // scoped ptr ensures the thread object is cleaned up even on exception:
wxScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() ); ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
m_CorePlugins.reset( killTask->Result ); m_CorePlugins = killTask->Result;
if( !m_CorePlugins ) if( !m_CorePlugins )
{ {
@ -153,8 +155,8 @@ void Pcsx2App::ReloadPlugins()
{ {
if( _loadTask ) return; if( _loadTask ) return;
m_CoreThread.reset(); m_CoreThread = NULL;
m_CorePlugins.reset(); m_CorePlugins = NULL;
wxString passins[PluginId_Count]; wxString passins[PluginId_Count];
@ -166,7 +168,7 @@ void Pcsx2App::ReloadPlugins()
passins[pi->id] = g_Conf->FullpathTo( pi->id ); passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
_loadTask.reset( new LoadPluginsTask( passins ) ); _loadTask.Delete() = new LoadPluginsTask( passins );
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :) // ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
} }
@ -203,5 +205,5 @@ void LoadPluginsImmediate()
void UnloadPlugins() void UnloadPlugins()
{ {
wxGetApp().m_CorePlugins.reset(); wxGetApp().m_CorePlugins = NULL;
} }

View File

@ -23,16 +23,6 @@
StartupParams g_Startup; StartupParams g_Startup;
//////////////////////////////////////////////////////////////////////////////////////////
// Save Slot Detection System
bool States_isSlotUsed(int num)
{
if (ElfCRC == 0)
return false;
else
return wxFileExists( SaveStateBase::GetFilename( num ) );
}
// returns true if the new state was loaded, or false if nothing happened. // returns true if the new state was loaded, or false if nothing happened.
void States_Load( const wxString& file ) void States_Load( const wxString& file )
@ -55,7 +45,7 @@ void States_Load( const wxString& file )
catch( Exception::BaseException& ) catch( Exception::BaseException& )
{ {
// VM state is probably ruined. We'll need to recover from the in-memory backup. // VM state is probably ruined. We'll need to recover from the in-memory backup.
StateRecovery::Recover(); //StateRecovery::Recover();
} }
SysExecute(); SysExecute();
@ -64,7 +54,7 @@ void States_Load( const wxString& file )
// Save state save-to-file (or slot) helpers. // Save state save-to-file (or slot) helpers.
void States_Save( const wxString& file ) void States_Save( const wxString& file )
{ {
if( !EmulationInProgress() ) if( !SysHasValidState() )
{ {
Msgbox::Alert( _("You need to start emulation first before you can save it's state.") ); Msgbox::Alert( _("You need to start emulation first before you can save it's state.") );
return; return;
@ -73,7 +63,7 @@ void States_Save( const wxString& file )
try try
{ {
Console::Status( wxsFormat( L"Saving savestate to file: %s", file.c_str() ) ); Console::Status( wxsFormat( L"Saving savestate to file: %s", file.c_str() ) );
StateRecovery::SaveToFile( file ); StateCopy_SaveToFile( file );
SysStatus( wxsFormat( _("State saved to file: %s"), wxFileName( file ).GetFullName().c_str() ) ); SysStatus( wxsFormat( _("State saved to file: %s"), wxFileName( file ).GetFullName().c_str() ) );
} }
catch( Exception::BaseException& ex ) catch( Exception::BaseException& ex )
@ -103,6 +93,14 @@ void States_Save( const wxString& file )
static int StatesC = 0; static int StatesC = 0;
static const int StateSlotsCount = 10; static const int StateSlotsCount = 10;
bool States_isSlotUsed(int num)
{
if (ElfCRC == 0)
return false;
else
return wxFileExists( SaveStateBase::GetFilename( num ) );
}
void States_FreezeCurrentSlot() void States_FreezeCurrentSlot()
{ {
Console::Status( "Saving savestate to slot %d...", StatesC ); Console::Status( "Saving savestate to slot %d...", StatesC );

View File

@ -1938,10 +1938,6 @@
RelativePath="..\..\gui\GlobalCommands.cpp" RelativePath="..\..\gui\GlobalCommands.cpp"
> >
</File> </File>
<File
RelativePath="..\..\HostGui.h"
>
</File>
<File <File
RelativePath="..\..\gui\i18n.cpp" RelativePath="..\..\gui\i18n.cpp"
> >
@ -2519,6 +2515,10 @@
RelativePath="..\..\gui\Resources\EmbeddedImage.h" RelativePath="..\..\gui\Resources\EmbeddedImage.h"
> >
</File> </File>
<File
RelativePath="..\..\HostGui.h"
>
</File>
<File <File
RelativePath="..\..\gui\IniInterface.h" RelativePath="..\..\gui\IniInterface.h"
> >

View File

@ -153,6 +153,7 @@ public:
m_outpipe( outpipe ) m_outpipe( outpipe )
, m_color( color ) , m_color( color )
{ {
m_name = (m_color == Color_Red) ? L"Redirect_Stderr" : L"Redirect_Stdout";
} }
virtual ~WinPipeThread() throw() virtual ~WinPipeThread() throw()
@ -166,8 +167,6 @@ protected:
try try
{ {
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
SetName( (m_color == Color_Red) ? "Redirect_Stderr" :" Redirect_Stdout" );
while( true ) while( true )
{ {
Sleep( 100 ); Sleep( 100 );