(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.
//
// 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
struct u128
{
u64 lo;
u64 hi;
// Implicit conversion from u64
u128( u64 src ) :
lo( src )
, hi( 0 ) {}
// Explicit conversion from u64
static u128 From64( u64 src )
{
u128 retval = { src, 0 };
return retval;
}
// Implicit conversion from u32
u128( u32 src ) :
lo( src )
, hi( 0 ) {}
u128() {}
// Explicit conversion from u32
static u128 From32( u32 src )
{
u128 retval = { src, 0 };
return retval;
}
};
struct s128
@ -129,17 +136,19 @@ struct s128
s64 lo;
s64 hi;
// Implicit conversion from u64
s128( s64 src ) :
lo( src )
, hi( 0 ) {}
// explicit conversion from s64, with sign extension.
static s128 From64( s64 src )
{
s128 retval = { src, (src < 0) ? -1 : 0 };
return retval;
}
// Implicit conversion from u32
s128( s32 src ) :
lo( src )
, hi( 0 ) {}
s128() {}
// explicit conversion from s32, with sign extension.
static s128 From64( s32 src )
{
s128 retval = { src, (src < 0) ? -1 : 0 };
return retval;
}
};
#else

View File

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

View File

@ -33,27 +33,33 @@ extern void pcsx2_aligned_free(void* pmem);
// pointer to null after deallocation.
#define safe_delete( ptr ) \
((void) (delete ptr), ptr = NULL)
((void) (delete (ptr)), (ptr) = NULL)
#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
// 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
// all ported platforms.
#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
// NULL status (our implementation under GCC, and microsoft's under MSVC), so no need to
// do it here.
#define safe_aligned_free( ptr ) \
((void) ( _aligned_free( ptr ), ptr = NULL ))
((void) ( _aligned_free( ptr ), (ptr) = NULL ))
#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

View File

@ -1,4 +1,210 @@
#pragma once
#include <wx/scopedptr.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 "Pcsx2Defs.h"
#include "ScopedPtr.h"
namespace Exception
{
@ -87,6 +88,7 @@ namespace Threading
void Lock();
void Unlock();
bool TryLock();
};
// 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.
extern void Sleep( int ms );
//////////////////////////////////////////////////////////////////////////////////////////
// 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
// --------------------------------------------------------------------------------------
// IThread - Interface for the public access to PersistentThread.
// --------------------------------------------------------------------------------------
// Class usage: Can be used for allowing safe nullification of a thread handle. Rather
// than being NULL'd, the handle can be mapped to an IThread implementation which acts
// as a do-nothing placebo or an assertion generator.
//
class IThread
{
DeclareNoncopyableObject(IThread);
public:
IThread() {}
virtual ~IThread() throw() {}
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);
protected:
typedef int (*PlainJoeFP)();
wxString m_name; // diagnostic name for our thread.
pthread_t m_thread;
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
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.
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.
// 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:
virtual ~PersistentThread() throw();
PersistentThread();
PersistentThread( const char* name );
virtual void Start();
virtual void Cancel( bool isBlocking = true );
virtual void 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 bool Detach();
virtual sptr Block();
virtual int GetReturnCode() const;
virtual void RethrowException() const;
bool IsRunning() const;
bool IsSelf() const;
wxString GetName() const;
virtual void DoThreadCleanup();
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.
// (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!
virtual sptr ExecuteTask()=0;
};
//////////////////////////////////////////////////////////////////////////////////////////
// ScopedLock: Helper class for using Mutexes.
// Using this class provides an exception-safe (and generally clean) method of locking

View File

@ -17,17 +17,19 @@
#include "Dependencies.h"
// ----------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
// wxBaseTools.h
//
// 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
// you don't need to include or link against wxCore (GUI) to build them. For tools
// which require wxCore, see wxGuiTools.h
// ----------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////
// wxDoNotLogInThisScope
// --------------------------------------------------------------------------------------
// wxDoNotLogInThisScope
// --------------------------------------------------------------------------------------
// 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
// 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 "Threading.h"
#include "wxBaseTools.h"
#include <wx/datetime.h>
#include <wx/thread.h>
@ -37,11 +38,13 @@ namespace Threading
}
PersistentThread::PersistentThread() :
m_thread()
m_name( L"PersistentThread" )
, m_thread()
, m_sem_event()
, m_sem_finished()
, m_lock_start( true ) // recursive mutexing!
, m_returncode( 0 )
, m_detached( false )
, m_detached( true ) // start out with m_thread in detached/invalid state
, m_running( false )
{
}
@ -53,40 +56,42 @@ namespace Threading
// your sister, and then cheating on her with your daughter.
PersistentThread::~PersistentThread() throw()
{
if( !m_running ) return;
wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( !_InterlockedExchange( &m_detached, true ) )
if( m_running )
{
#if wxUSE_GUI
m_sem_finished.WaitGui();
#else
m_sem_finished.Wait();
#endif
m_running = false;
}
Detach();
}
// This function should not be called from the owner thread.
void PersistentThread::Start()
{
ScopedLock startlock( m_lock_start ); // Prevents sudden parallel startup
if( m_running ) return;
Detach(); // clean up previous thread, if one exists.
m_sem_finished.Reset();
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
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.
void PersistentThread::Detach()
bool PersistentThread::Detach()
{
if( !m_running ) return;
if( _InterlockedExchange( &m_detached, true ) ) return;
wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( _InterlockedExchange( &m_detached, true ) ) return false;
pthread_detach( m_thread );
return true;
}
// Remarks:
@ -102,15 +107,15 @@ namespace Threading
//
void PersistentThread::Cancel( bool isBlocking )
{
wxASSERT( !IsSelf() );
if( !m_running ) return;
if( _InterlockedExchange( &m_detached, true ) )
if( m_detached )
{
Console::Notice( "Threading Warning: Attempted to cancel detached thread; Ignoring..." );
return;
}
wxASSERT( !IsSelf() );
pthread_cancel( m_thread );
if( isBlocking )
@ -121,8 +126,6 @@ namespace Threading
m_sem_finished.Wait();
#endif
}
else
pthread_detach( m_thread );
}
// Blocks execution of the calling thread until this thread completes its task. The
@ -153,14 +156,10 @@ namespace Threading
bool PersistentThread::IsRunning() const
{
if (!m_running) return false;
if( !!m_detached )
return !!m_running;
else
return ( ESRCH != pthread_kill( m_thread, 0 ) );
return !!m_running;
}
// Gets the return code of the thread.
// Exceptions:
// InvalidOperation - thrown if the thread is still running or has never been started.
//
@ -171,32 +170,95 @@ namespace Threading
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.
void PersistentThread::DoThreadCleanup()
{
wxASSERT( IsSelf() ); // only allowed from our own thread, thanks.
_InterlockedExchange( &m_running, false );
m_running = false;
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 )
{
jASSUME( itsme != NULL );
PersistentThread& owner = *((PersistentThread*)itsme);
pthread_cleanup_push( _pt_callback_cleanup, itsme );
owner.m_returncode = owner.ExecuteTask();
owner._internal_execute();
pthread_cleanup_pop( true );
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.
#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;
#pragma pack(push,8)
@ -210,10 +272,10 @@ namespace Threading
#pragma pack(pop)
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0;
__try
{
@ -480,6 +542,11 @@ namespace Threading
pthread_mutex_unlock( &mutex );
}
bool MutexLock::TryLock()
{
return EBUSY != pthread_mutex_trylock( &mutex );
}
// --------------------------------------------------------------------------------------
// InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions)
// --------------------------------------------------------------------------------------

View File

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

View File

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

View File

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

View File

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

View File

@ -48,6 +48,7 @@ namespace Exception
public:
DEFINE_EXCEPTION_COPYTORS( PluginError )
PluginError() {}
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
// management facilities)
//
class EmuPluginBindings
class SysPluginBindings
{
protected:
PS2E_ComponentAPI_Mcd* Mcd;
public:
EmuPluginBindings() :
SysPluginBindings() :
Mcd( NULL )
{
@ -204,7 +205,7 @@ public:
friend class PluginManager;
};
extern EmuPluginBindings EmuPlugins;
extern SysPluginBindings SysPlugins;
// --------------------------------------------------------------------------------------

View File

@ -20,129 +20,268 @@
#include "HostGui.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()
{
if( !g_RecoveryState ) return;
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." );
Console::Status( "Resuming execution from full memory state..." );
memLoadingState( *g_RecoveryState ).FreezeAll();
StateRecovery::Clear();
SysClearExecutionCache();
Start();
sys_resume_lock = true;
}
SafeArray<u8> gzSavingBuffer;
class gzThreadClass : public PersistentThread
protected:
sptr ExecuteTask()
{
typedef PersistentThread _parent;
protected:
gzFile m_file;
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 )
memSavingState( state_buffer ).FreezeAll();
return 0;
}
void DoThreadCleanup()
{
SysSuspend( false );
gzThread.reset( NULL ); // blocks on any existing gzipping business.
wxCommandEvent evt( pxEVT_FreezeFinished );
evt.SetClientData( this );
wxGetApp().AddPendingEvent( evt );
memSavingState( gzSavingBuffer ).FreezeAll();
_parent::DoThreadCleanup();
}
};
// start that encoding thread:
gzThread.reset( new gzThreadClass( file ) );
SysResume();
class StateThread_ZipToDisk : public PersistentThread
{
typedef PersistentThread _parent;
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
// (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 )
~StateThread_ZipToDisk() throw()
{
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).
// 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()
protected:
sptr ExecuteTask()
{
if( g_RecoveryState ) return;
if( !EmulationInProgress() ) return;
Sleep( 2 );
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" ) );
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.reset();
gzclose( m_gzfp ); m_gzfp = NULL;
throw;
}
sys_resume_lock = true;
}
// Clears and deallocates any recovery states.
void Clear()
~StateThread_UnzipFromDisk() throw()
{
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(); }
};
namespace StateRecovery
{
extern bool HasState();
extern void Recover();
extern void SaveToFile( const wxString& file );
extern void SaveToSlot( uint num );
extern void MakeFull();
extern void Clear();
}
extern bool StateCopy_IsValid();
extern bool StateCopy_HasFullState();
extern bool StateCopy_HasPartialState();
extern void StateCopy_FreezeToMem();
extern void StateCopy_ThawFromMem();
extern void StateCopy_SaveToFile( const wxString& file );
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)
{
EmuPlugins.McdRead(
SysPlugins.McdRead(
sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()],
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)
{
EmuPlugins.McdSave(
SysPlugins.McdSave(
sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()],
data, adr, size
);
@ -69,7 +69,7 @@ static void _SaveMcd(const u8 *data, u32 adr, int size)
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 )
@ -601,7 +601,7 @@ void InitializeSIO(u8 value)
const uint port = sio.GetMemcardIndex();
const uint slot = sio.activeMemcardSlot[port];
if( EmuPlugins.McdIsPresent( port, slot ) )
if( SysPlugins.McdIsPresent( port, slot ) )
{
sio2.packet.recvVal1 = 0x1100;
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 slot=0; slot<8; ++slot )
m_mcdCRCs[port][slot] = EmuPlugins.McdGetCRC( port, slot );
m_mcdCRCs[port][slot] = SysPlugins.McdGetCRC( port, slot );
}
}
Freeze( m_mcdCRCs );
@ -689,7 +689,7 @@ void SaveStateBase::sioFreeze()
{
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] )
{
m_mcdCRCs[port][slot] = newCRC;

View File

@ -252,40 +252,11 @@ void SysClearExecutionCache()
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 )
{
SafeArray<u8> buf;
gzFile gzfp = gzopen( srcfile.ToUTF8().data(), "rb" );
if( gzfp == NULL )
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();
//SysClearExecutionCache();
//cpuReset();
//joe.FreezeAll();
}
// 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_plugins( plugins )
{
m_name = L"EE Core";
}
SysCoreThread::~SysCoreThread() throw()
@ -280,13 +281,13 @@ void SysCoreThread::CpuInitializeMess()
cpuReset();
SysClearExecutionCache();
if( StateRecovery::HasState() )
if( StateCopy_IsValid() )
{
// no need to boot bios or detect CDs when loading savestates.
// [TODO] : It might be useful to detect game SLUS/CRC and compare it against
// the savestate info, and issue a warning to the user since, chances are, they
// don't really want to run a game with the wrong ISO loaded into the emu.
StateRecovery::Recover();
StateCopy_ThawFromMem();
}
else
{
@ -354,7 +355,6 @@ static void _cet_callback_cleanup( void* handle )
sptr SysCoreThread::ExecuteTask()
{
SetName( "EE Core" );
tls_coreThread = this;
StateCheck();

View File

@ -19,7 +19,19 @@
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;

View File

@ -26,6 +26,7 @@ class MainEmuFrame;
class GSFrame;
class ConsoleLogFrame;
class PipeRedirectionBase;
class AppCoreThread;
#include "Utilities/HashMap.h"
#include "Utilities/wxGuiTools.h"
@ -39,6 +40,9 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 )
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -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()
// ------------------------------------------------------------------------
@ -73,11 +77,11 @@ enum MenuIdentifiers
MenuId_SkipBiosToggle, // enables the Bios Skip speedhack
MenuId_Emu_SuspendResume, // suspends/resumes active emulation, retains plugin states
MenuId_Emu_Close, // Closes the emulator (states are preserved)
MenuId_Emu_Reset, // Issues a complete reset (wipes preserved states)
MenuId_Emu_LoadStates, // Opens load states submenu
MenuId_Emu_SaveStates, // Opens save states submenu
MenuId_Sys_SuspendResume, // suspends/resumes active emulation, retains plugin states
MenuId_Sys_Close, // Closes the emulator (states are preserved)
MenuId_Sys_Reset, // Issues a complete reset (wipes preserved states)
MenuId_Sys_LoadStates, // Opens load states submenu
MenuId_Sys_SaveStates, // Opens save states submenu
MenuId_EnablePatches,
MenuId_State_Load,
@ -298,15 +302,15 @@ public:
protected:
wxImageList m_ConfigImages;
wxScopedPtr<wxImageList> m_ToolbarImages;
wxScopedPtr<wxBitmap> m_Bitmap_Logo;
wxScopedPtr<PipeRedirectionBase>m_StdoutRedirHandle;
wxScopedPtr<PipeRedirectionBase>m_StderrRedirHandle;
ScopedPtr<wxImageList> m_ToolbarImages;
ScopedPtr<wxBitmap> m_Bitmap_Logo;
ScopedPtr<PipeRedirectionBase> m_StdoutRedirHandle;
ScopedPtr<PipeRedirectionBase> m_StderrRedirHandle;
public:
wxScopedPtr<SysCoreAllocations> m_CoreAllocs;
wxScopedPtr<PluginManager> m_CorePlugins;
wxScopedPtr<SysCoreThread> m_CoreThread;
ScopedPtr<SysCoreAllocations> m_CoreAllocs;
ScopedPtr<PluginManager> m_CorePlugins;
ScopedPtr<SysCoreThread> m_CoreThread;
protected:
// Note: Pointers to frames should not be scoped because wxWidgets handles deletion
@ -333,16 +337,8 @@ public:
void SysExecute();
void SysExecute( CDVD_SourceType cdvdsrc );
void SysReset()
{
m_CoreThread.reset();
m_CorePlugins.reset();
}
bool EmuInProgress() const
{
return m_CoreThread && m_CoreThread->IsRunning();
}
void SysReset();
bool SysIsActive() const;
const wxBitmap& GetLogoBitmap();
wxImageList& GetImgList_Config();
@ -350,28 +346,13 @@ public:
const AppImageIds& GetImgId() const { return m_ImageId; }
MainEmuFrame& GetMainFrame() const
{
wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
wxASSERT( m_MainFrame != NULL );
return *m_MainFrame;
}
bool HasMainFrame() const { return m_MainFrame != NULL; }
bool HasCoreThread() const { return m_CoreThread != NULL; }
MainEmuFrame& GetMainFrameOrExcept() const
{
if( m_MainFrame == NULL )
throw Exception::ObjectIsNull( "main application frame" );
return *m_MainFrame;
}
SysCoreThread& GetCoreThreadOrExcept() const
{
if( !m_CoreThread )
throw Exception::ObjectIsNull( "core emulation thread" );
return *m_CoreThread;
}
MainEmuFrame& GetMainFrame() const;
SysCoreThread& GetCoreThread() const;
MainEmuFrame& GetMainFrameOrExcept() const;
SysCoreThread& GetCoreThreadOrExcept() const;
void OpenGsFrame();
void OnGsFrameClosed();
@ -412,9 +393,14 @@ protected:
void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnSemaphorePing( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt );
void OnCoreThreadTerminated( wxCommandEvent& evt );
void OnFreezeFinished( wxCommandEvent& evt );
void OnThawFinished( wxCommandEvent& evt );
void OnMessageBox( pxMessageBoxEvent& evt );
void OnEmuKeyDown( wxKeyEvent& evt );
// ----------------------------------------------------------------------------
// 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;
@ -443,15 +429,16 @@ protected:
wxKeyEvent m_kevt;
public:
AppEmuThread( PluginManager& plugins );
virtual ~AppEmuThread() throw();
AppCoreThread( PluginManager& plugins );
virtual ~AppCoreThread() throw();
virtual void Suspend( bool isBlocking=true );
virtual void StateCheck( bool isCancelable=true );
virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnResumeReady();
protected:
virtual void OnResumeReady();
virtual void DoThreadCleanup();
sptr ExecuteTask();
};
@ -470,6 +457,7 @@ public:
bool IsReentrant() const { return Counter > 1; }
};
extern bool sys_resume_lock;
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
extern void LoadPluginsPassive();
@ -477,13 +465,13 @@ extern void LoadPluginsImmediate();
extern void UnloadPlugins();
extern bool HandlePluginError( Exception::PluginError& ex );
extern bool EmulationInProgress();
extern bool SysHasValidState();
extern void AppLoadSettings();
extern void AppSaveSettings();
extern void AppApplySettings( const AppConfig* oldconf=NULL );
extern bool SysHasValidState();
extern void SysStatus( const wxString& text );
extern void SysSuspend( bool closePlugins = true );
extern void SysResume();
@ -491,36 +479,8 @@ extern void SysReset();
extern void SysExecute();
extern void SysExecute( CDVD_SourceType cdvdsrc );
extern bool HasMainFrame();
extern bool HasCoreThread();
// --------------------------------------------------------------------------------------
// AppInvoke macro
// --------------------------------------------------------------------------------------
// 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 )
extern MainEmuFrame& GetMainFrame();
extern SysCoreThread& GetCoreThread();

View File

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

View File

@ -178,4 +178,4 @@ extern ConfigOverrides OverrideOptions;
extern wxFileConfig* OpenFileConfig( const wxString& filename );
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_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated );
DEFINE_EVENT_TYPE( pxEVT_FreezeFinished );
DEFINE_EVENT_TYPE( pxEVT_ThawFinished );
bool UseAdminMode = false;
wxDirName SettingsFolder;
bool UseDefaultSettingsFolder = true;
wxScopedPtr<AppConfig> g_Conf;
ScopedPtr<AppConfig> g_Conf;
ConfigOverrides OverrideOptions;
namespace Exception
@ -54,7 +57,7 @@ namespace Exception
class StartupAborted : public BaseException
{
public:
virtual ~StartupAborted() throw() {}
DEFINE_EXCEPTION_COPYTORS( StartupAborted )
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 )
, m_kevt()
{
}
AppEmuThread::~AppEmuThread() throw()
AppCoreThread::~AppCoreThread() throw()
{
AppInvoke( MainFrame, ApplySettings() );
}
void AppEmuThread::Suspend( bool isBlocking )
void AppCoreThread::Suspend( bool 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
// plugin is suspended.
@ -88,7 +91,7 @@ void AppEmuThread::Suspend( bool isBlocking )
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;
@ -99,7 +102,19 @@ void AppEmuThread::OnResumeReady()
if( GSopen2 != NULL )
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
@ -111,14 +126,14 @@ static const int pxID_PadHandler_Keydown = 8030;
extern int TranslateGDKtoWXK( u32 keysym );
#endif
void AppEmuThread::StateCheck( bool isCancelable )
void AppCoreThread::StateCheck( bool isCancelable )
{
_parent::StateCheck( isCancelable );
const keyEvent* ev = PADkeyEvent();
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 );
const bool isDown = (ev->evt == KEYPRESS);
@ -141,8 +156,13 @@ void AppEmuThread::StateCheck( bool isCancelable )
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
// which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is
// usually the desired behavior) will be ignored.
@ -153,17 +173,14 @@ void AppEmuThread::ApplySettings( const Pcsx2Config& src )
SysCoreThread::ApplySettings( src );
}
__forceinline bool EmulationInProgress()
{
return wxGetApp().EmuInProgress();
}
// 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.
__forceinline bool SysHasValidState()
{
return wxGetApp().EmuInProgress() || StateRecovery::HasState();
bool isRunning = HasCoreThread() ? GetCoreThread().IsRunning() : false;
return isRunning || StateCopy_HasFullState();
}
bool HandlePluginError( Exception::PluginError& ex )
{
if( pxDialogExists( DialogId_CoreSettings ) ) return true;
@ -183,7 +200,7 @@ bool HandlePluginError( Exception::PluginError& ex )
return result;
}
sptr AppEmuThread::ExecuteTask()
sptr AppCoreThread::ExecuteTask()
{
try
{
@ -195,7 +212,7 @@ sptr AppEmuThread::ExecuteTask()
m_plugins.Close();
if( ex.StreamName == g_Conf->FullpathToBios() )
{
GetPluginManager().Close();
m_plugins.Close();
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the BIOS Configuration Panel.") );
@ -270,7 +287,7 @@ void Pcsx2App::ReadUserModeSettings()
wxFileName usermodefile( FilenameDefs::GetUsermodeConfig() );
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 ) );
@ -401,10 +418,10 @@ bool Pcsx2App::OnInit()
wxInitAllImageHandlers();
if( !wxApp::OnInit() ) return false;
g_Conf.reset( new AppConfig() );
g_Conf = new AppConfig();
m_StdoutRedirHandle.reset( NewPipeRedir(stdout) );
m_StderrRedirHandle.reset( NewPipeRedir(stderr) );
m_StdoutRedirHandle = NewPipeRedir(stdout);
m_StderrRedirHandle = NewPipeRedir(stderr);
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
#define pxMessageBoxEventThing(func) \
@ -416,11 +433,14 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
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 ) );
// 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
// 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
@ -456,7 +476,7 @@ bool Pcsx2App::OnInit()
SysDetect();
AppApplySettings();
m_CoreAllocs.reset( new SysCoreAllocations() );
m_CoreAllocs = new SysCoreAllocations();
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{
@ -578,6 +598,14 @@ void Pcsx2App::Ping() const
// 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 )
{
((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.
bool Pcsx2App::PrepForExit()
{
m_CoreThread.reset();
m_CoreThread = NULL;
CleanupMess();
return true;
@ -750,6 +778,36 @@ Pcsx2App::~Pcsx2App()
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 )
{
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.
AppInvoke( MainFrame, ApplySettings() );
AppInvoke( CoreThread, ApplySettings( g_Conf->EmuOptions ) );
if( HasMainFrame() )
GetMainFrame().ApplySettings();
if( HasCoreThread() )
GetCoreThread().ApplySettings( g_Conf->EmuOptions );
}
void AppLoadSettings()
@ -793,7 +852,8 @@ void AppLoadSettings()
IniLoader loader( *conf );
g_Conf->LoadSave( loader );
AppInvoke( MainFrame, LoadRecentIsoList( *conf ) );
if( HasMainFrame() )
GetMainFrame().LoadRecentIsoList( *conf );
}
void AppSaveSettings()
@ -804,7 +864,8 @@ void AppSaveSettings()
IniSaver saver( *conf );
g_Conf->LoadSave( saver );
AppInvoke( MainFrame, SaveRecentIsoList( *conf ) );
if( HasMainFrame() )
GetMainFrame().SaveRecentIsoList( *conf );
}
// --------------------------------------------------------------------------------------
@ -815,9 +876,15 @@ void AppSaveSettings()
// configured CDVD source device.
void Pcsx2App::SysExecute()
{
if( sys_resume_lock )
{
Console::WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
return;
}
SysReset();
LoadPluginsImmediate();
m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) );
m_CoreThread = new AppCoreThread( *m_CorePlugins );
m_CoreThread->Resume();
}
@ -826,15 +893,35 @@ void Pcsx2App::SysExecute()
// sources.
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
{
if( sys_resume_lock )
{
Console::WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
return;
}
SysReset();
LoadPluginsImmediate();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
CDVDsys_ChangeSource( cdvdsrc );
m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) );
m_CoreThread = new AppCoreThread( *m_CorePlugins );
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()
{
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.
// FIXME: This probably isn't thread safe. >_<
void SysStatus( const wxString& text )
{
// mirror output to the console!
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
@ -878,18 +967,48 @@ void SysExecute( CDVD_SourceType cdvdsrc )
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 )
{
if( !HasCoreThread() ) return;
if( closePlugins )
AppInvoke( CoreThread, Suspend(closePlugins) );
GetCoreThread().Suspend();
else
AppInvoke( CoreThread, ShortSuspend() );
GetCoreThread().ShortSuspend();
}
void 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()
{
if( m_Bitmap_Logo != NULL )
if( m_Bitmap_Logo )
return *m_Bitmap_Logo;
wxFileName mess;
@ -90,7 +90,7 @@ const wxBitmap& Pcsx2App::GetLogoBitmap()
wxImage img;
EmbeddedImage<res_BackgroundLogo> temp; // because gcc can't allow non-const temporaries.
LoadImageAny( img, useTheme, mess, L"BackgroundLogo", temp );
m_Bitmap_Logo.reset( new wxBitmap( img ) );
m_Bitmap_Logo = new wxBitmap( img );
return *m_Bitmap_Logo;
}
@ -137,10 +137,10 @@ wxImageList& Pcsx2App::GetImgList_Config()
// ------------------------------------------------------------------------
wxImageList& Pcsx2App::GetImgList_Toolbars()
{
if( m_ToolbarImages == NULL )
if( !m_ToolbarImages )
{
const int imgSize = g_Conf->Toolbar_ImageSize ? 64 : 32;
m_ToolbarImages.reset( new wxImageList( imgSize, imgSize ) );
m_ToolbarImages = new wxImageList( imgSize, imgSize );
wxFileName mess;
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(
"Contributors"
"\n\n"
"Hiryu and Sjeep for libcvd (the iso parsing and"
"filesystem driver code), nneeve (fpu and vu help)"
"Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"
"\n\n"
"Plugin Specialists: ChickenLiver (Lilypad), Efp (efp),"
"Gabest (Gsdx, Cdvdolio, Xpad), Zeydlitz (ZZogl)"
"\n\n"
"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 );

View File

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

View File

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

View File

@ -211,8 +211,8 @@ void MainEmuFrame::ConnectMenus()
ConnectMenu( MenuId_SkipBiosToggle, Menu_SkipBiosToggle_Click );
ConnectMenu( MenuId_Exit, Menu_Exit_Click );
ConnectMenu( MenuId_Emu_SuspendResume, Menu_SuspendResume_Click );
ConnectMenu( MenuId_Emu_Reset, Menu_EmuReset_Click );
ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click );
ConnectMenu( MenuId_Sys_Reset, Menu_EmuReset_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"));
// ------------------------------------------------------------------------
m_menuEmu.Append(MenuId_Emu_SuspendResume, _("Suspend"),
m_menuEmu.Append(MenuId_Sys_SuspendResume, _("Suspend"),
_("Stops emulation dead in its tracks") )->Enable( SysHasValidState() );
m_menuEmu.AppendSeparator();
//m_menuEmu.Append(MenuId_Emu_Close, _("Close"),
//m_menuEmu.Append(MenuId_Sys_Close, _("Close"),
// _("Stops emulation and closes the GS window."));
m_menuEmu.Append(MenuId_Emu_LoadStates, _("Load state"), &m_LoadStatesSubmenu);
m_menuEmu.Append(MenuId_Emu_SaveStates, _("Save state"), &m_SaveStatesSubmenu);
m_menuEmu.Append(MenuId_Sys_LoadStates, _("Load state"), &m_LoadStatesSubmenu);
m_menuEmu.Append(MenuId_Sys_SaveStates, _("Save state"), &m_SaveStatesSubmenu);
m_menuEmu.AppendSeparator();
m_menuEmu.Append(MenuId_EnablePatches, _("Enable Patches"),
wxEmptyString, wxITEM_CHECK);
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"));
// ------------------------------------------------------------------------
@ -470,9 +470,7 @@ void MainEmuFrame::ReloadRecentLists()
if( m_RecentIsoList )
m_RecentIsoList->Save( *cfg );
m_RecentIsoList.reset();
m_RecentIsoList.reset( new wxFileHistory( g_Conf->RecentFileCount ) );
m_RecentIsoList->Load( *cfg );
m_RecentIsoList.Reassign( new wxFileHistory(g_Conf->RecentFileCount) )->Load( *cfg );
UpdateIsoSrcFile();
cfg->Flush();
}
@ -484,12 +482,11 @@ void MainEmuFrame::ApplySettings()
GetMenuBar()->Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled );
GetMenuBar()->Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled );
GetMenuBar()->Enable( MenuId_Emu_SuspendResume, SysHasValidState() );
bool result = false;
AppInvokeBool( CoreThread, IsSuspended(), result );
GetMenuBar()->SetLabel( MenuId_Emu_SuspendResume, result ? _("Resume") :_("Suspend") );
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() );
if( HasCoreThread() )
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, GetCoreThread().IsSuspended() ? _("Resume") :_("Suspend") );
if( m_RecentIsoList )
{
if( m_RecentIsoList->GetMaxFiles() != g_Conf->RecentFileCount )

View File

@ -43,7 +43,7 @@ class MainEmuFrame : public wxFrame
// ------------------------------------------------------------------------
protected:
wxScopedPtr<wxFileHistory> m_RecentIsoList;
ScopedPtr<wxFileHistory> m_RecentIsoList;
wxStatusBar& m_statusbar;
wxStaticBitmap m_background;
@ -74,7 +74,7 @@ public:
void OnLogBoxHidden();
bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Emu_SuspendResume ); }
bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Sys_SuspendResume ); }
void UpdateIsoSrcFile();
void UpdateIsoSrcSelection();
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."
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 )
{
@ -190,11 +190,9 @@ void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
{
if( !SysHasValidState() ) return;
if( !HasCoreThread() ) return;
bool result = false;
AppInvokeBool( CoreThread, IsSuspended(), result );
if( result )
if( GetCoreThread().IsSuspended() )
SysResume();
else
SysSuspend();
@ -202,15 +200,13 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event)
{
bool wasInProgress = EmulationInProgress();
bool wasSuspended;
AppInvokeBool( CoreThread, IsSuspended(), wasSuspended );
if( !SysHasValidState() ) return;
bool wasSuspended = HasCoreThread() ? GetCoreThread().IsSuspended() : true;
SysReset();
if( !wasInProgress || wasSuspended ) return;
SysExecute();
if( !wasSuspended )
SysExecute();
}
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
// occurs during file enumeration.
wxScopedPtr<wxArrayString> bioslist( new wxArrayString() );
ScopedPtr<wxArrayString> bioslist( new wxArrayString() );
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) )
validated = false;
m_BiosList.swap( bioslist );
m_BiosList.SwapPtr( bioslist );
return validated;
}

View File

@ -55,7 +55,7 @@ namespace Exception
Panels::BaseApplicableConfigPanel* m_Panel;
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.") )
{
@ -351,7 +351,7 @@ namespace Panels
class BiosSelectorPanel : public BaseSelectorPanel
{
protected:
wxScopedPtr<wxArrayString> m_BiosList;
ScopedPtr<wxArrayString> m_BiosList;
wxListBox& m_ComboBox;
DirPickerPanel& m_FolderPicker;
@ -454,8 +454,8 @@ namespace Panels
ComboBoxPanel& m_ComponentBoxes;
bool m_Canceled;
wxScopedPtr<wxArrayString> m_FileList; // list of potential plugin files
wxScopedPtr<EnumThread> m_EnumeratorThread;
ScopedPtr<wxArrayString> m_FileList; // list of potential plugin files
ScopedPtr<EnumThread> m_EnumeratorThread;
public:
virtual ~PluginSelectorPanel();

View File

@ -364,9 +364,7 @@ void Panels::PluginSelectorPanel::DoRefresh()
wxCommandEvent evt( pxEVT_ShowStatusBar );
GetEventHandler()->AddPendingEvent( evt );
// Use a thread to load plugins.
m_EnumeratorThread.reset( NULL );
m_EnumeratorThread.reset( new EnumThread( *this ) );
m_EnumeratorThread.Delete() = new EnumThread( *this );
if( DisableThreading )
m_EnumeratorThread->DoNextPlugin( 0 );
@ -376,7 +374,7 @@ void Panels::PluginSelectorPanel::DoRefresh()
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;
@ -385,20 +383,20 @@ bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
// Impl Note: ScopedPtr used so that resources get cleaned up if an exception
// 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) )
validated = false;
if( pluggers == 0 )
{
m_FileList.reset();
m_FileList = NULL;
return validated;
}
m_FileList.swap( pluginlist );
m_FileList.SwapPtr( pluginlist );
m_StatusPanel.SetGaugeLength( pluggers );
@ -428,7 +426,7 @@ void Panels::PluginSelectorPanel::OnShowStatusBar( 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?
// (for now we just force it to selection zero if nothing's selected)

View File

@ -63,13 +63,15 @@ protected:
int ExecuteTask();
};
static wxScopedPtr<LoadPluginsTask> _loadTask;
static ScopedPtr<LoadPluginsTask> _loadTask;
LoadPluginsTask::~LoadPluginsTask() throw()
{
_loadTask.release();
if( _loadTask )
_loadTask.DetachPtr(); // avoids recursive deletion
PersistentThread::Cancel();
_loadTask.reset();
_loadTask = NULL;
}
int LoadPluginsTask::ExecuteTask()
@ -111,10 +113,10 @@ int LoadPluginsTask::ExecuteTask()
int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
{
wxScopedPtr<wxArrayString> placebo;
ScopedPtr<wxArrayString> placebo;
wxArrayString* realdest = dest;
if( realdest == NULL )
placebo.reset( realdest = new wxArrayString() );
placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done
#ifdef __WXMSW__
// 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 )
{
// scoped ptr ensures the thread object is cleaned up even on exception:
wxScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
m_CorePlugins.reset( killTask->Result );
ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
m_CorePlugins = killTask->Result;
if( !m_CorePlugins )
{
@ -153,8 +155,8 @@ void Pcsx2App::ReloadPlugins()
{
if( _loadTask ) return;
m_CoreThread.reset();
m_CorePlugins.reset();
m_CoreThread = NULL;
m_CorePlugins = NULL;
wxString passins[PluginId_Count];
@ -166,7 +168,7 @@ void Pcsx2App::ReloadPlugins()
passins[pi->id] = g_Conf->FullpathTo( pi->id );
} 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. :)
}
@ -203,5 +205,5 @@ void LoadPluginsImmediate()
void UnloadPlugins()
{
wxGetApp().m_CorePlugins.reset();
wxGetApp().m_CorePlugins = NULL;
}

View File

@ -23,16 +23,6 @@
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.
void States_Load( const wxString& file )
@ -55,7 +45,7 @@ void States_Load( const wxString& file )
catch( Exception::BaseException& )
{
// VM state is probably ruined. We'll need to recover from the in-memory backup.
StateRecovery::Recover();
//StateRecovery::Recover();
}
SysExecute();
@ -64,7 +54,7 @@ void States_Load( const wxString& file )
// Save state save-to-file (or slot) helpers.
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.") );
return;
@ -73,7 +63,7 @@ void States_Save( const wxString& file )
try
{
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() ) );
}
catch( Exception::BaseException& ex )
@ -103,6 +93,14 @@ void States_Save( const wxString& file )
static int StatesC = 0;
static const int StateSlotsCount = 10;
bool States_isSlotUsed(int num)
{
if (ElfCRC == 0)
return false;
else
return wxFileExists( SaveStateBase::GetFilename( num ) );
}
void States_FreezeCurrentSlot()
{
Console::Status( "Saving savestate to slot %d...", StatesC );

View File

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

View File

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