mirror of https://github.com/PCSX2/pcsx2.git
Savestates work! Not much else does yet (settings, etc), but at least this monkey's off my back. I can work on tying up more loose ends now. :)
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1975 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
a2a495e842
commit
eda5d1d834
|
@ -429,6 +429,10 @@
|
||||||
RelativePath="..\..\include\intrin_x86.h"
|
RelativePath="..\..\include\intrin_x86.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\include\Utilities\Listeners.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\include\Utilities\lnx_memzero.h"
|
RelativePath="..\..\include\Utilities\lnx_memzero.h"
|
||||||
>
|
>
|
||||||
|
|
|
@ -191,7 +191,7 @@ typedef struct _PS2E_SessionInfo
|
||||||
// Sony's assigned serial number, valid only for CD/CDVD games (ASCII-Z string).
|
// Sony's assigned serial number, valid only for CD/CDVD games (ASCII-Z string).
|
||||||
// Ex: SLUS-2932 (if the running app is not a sony-registered game, the serial
|
// Ex: SLUS-2932 (if the running app is not a sony-registered game, the serial
|
||||||
// will be a zero length string).
|
// will be a zero length string).
|
||||||
const char Serial[16];
|
char Serial[16];
|
||||||
|
|
||||||
} PS2E_SessionInfo;
|
} PS2E_SessionInfo;
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
#if defined(PCSX2_DEBUG)
|
#if defined(PCSX2_DEBUG)
|
||||||
|
|
||||||
# define pxAssertMsg(cond, msg) ( (!!(cond)) || \
|
# define pxAssertMsg(cond, msg) ( (!!(cond)) || \
|
||||||
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), !!(cond)) )
|
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) )
|
||||||
|
|
||||||
# define pxAssertDev(cond,msg) pxAssertMsg(cond, msg)
|
# define pxAssertDev(cond,msg) pxAssertMsg(cond, msg)
|
||||||
|
|
||||||
|
@ -57,26 +57,28 @@
|
||||||
// Devel builds use __assume for standard assertions and call pxOnAssertDevel
|
// Devel builds use __assume for standard assertions and call pxOnAssertDevel
|
||||||
// for AssertDev brand assertions (which typically throws a LogicError exception).
|
// for AssertDev brand assertions (which typically throws a LogicError exception).
|
||||||
|
|
||||||
# define pxAssertMsg(cond, msg) (!!(cond))
|
# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
|
||||||
|
|
||||||
# define pxAssertDev(cond, msg) ( (!!(cond)) || \
|
# define pxAssertDev(cond, msg) ( (!!(cond)) || \
|
||||||
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), !!(cond)) )
|
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) )
|
||||||
|
|
||||||
# define pxFail(msg) __assume(false)
|
# define pxFail(msg) (__assume(false), false)
|
||||||
# define pxFailDev(msg ) pxAssertDev(false, msg)
|
# define pxFailDev(msg ) pxAssertDev(false, msg)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Release Builds just use __assume as an optimization, and always return 'true'
|
// Release Builds just use __assume as an optimization, and return the conditional
|
||||||
// indicating the assertion check succeeded (no actual check is performed).
|
// as a result (if .
|
||||||
|
|
||||||
# define pxAssertMsg(cond, msg) (__assume(cond), true)
|
# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
|
||||||
# define pxAssertDev(cond, msg) (__assume(cond), true)
|
# define pxAssertDev(cond, msg) (__assume(cond), likely(cond))
|
||||||
# define pxFail(msg) (__assume(false), true)
|
# define pxFail(msg) (__assume(false), false)
|
||||||
# define pxFailDev(msg) (__assume(false), true)
|
# define pxFailDev(msg) (__assume(false), false)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
__cold
|
||||||
|
|
||||||
#define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL)
|
#define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL)
|
||||||
|
|
||||||
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg);
|
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Dependencies.h"
|
#include "Dependencies.h"
|
||||||
|
#include "StringHelpers.h"
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// DESTRUCTOR_CATCHALL - safe destructor helper
|
// DESTRUCTOR_CATCHALL - safe destructor helper
|
||||||
|
@ -133,7 +134,7 @@ namespace Exception
|
||||||
//
|
//
|
||||||
#define DEFINE_EXCEPTION_COPYTORS( classname ) \
|
#define DEFINE_EXCEPTION_COPYTORS( classname ) \
|
||||||
virtual ~classname() throw() {} \
|
virtual ~classname() throw() {} \
|
||||||
virtual void Rethrow() const { throw classname( *this ); } \
|
virtual void Rethrow() const { throw *this; } \
|
||||||
virtual BaseException* Clone() const { return new classname( *this ); }
|
virtual BaseException* Clone() const { return new classname( *this ); }
|
||||||
|
|
||||||
#define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
|
#define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
|
||||||
|
@ -149,11 +150,13 @@ namespace Exception
|
||||||
explicit classname( const wxString& msg_eng ) { BaseException::InitBaseEx( msg_eng, wxEmptyString ); }
|
explicit classname( const wxString& msg_eng ) { BaseException::InitBaseEx( msg_eng, wxEmptyString ); }
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
// Generalized Exceptions: RuntimeError / LogicError / ObjectIsNull
|
// RuntimeError / LogicError - Generalized Exceptions
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class RuntimeError : public virtual BaseException
|
class RuntimeError : public virtual BaseException
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
bool IsSilent;
|
||||||
public:
|
public:
|
||||||
DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") )
|
DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") )
|
||||||
};
|
};
|
||||||
|
@ -166,7 +169,38 @@ 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 virtual RuntimeError
|
// --------------------------------------------------------------------------------------
|
||||||
|
// CancelAppEvent - Exception for canceling an event in a non-verbose fashion
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// Typically the PCSX2 interface issues popup dialogs for runtime errors. This exception
|
||||||
|
// instead issues a "silent" cancelation that is handled by the app gracefully (generates
|
||||||
|
// log, and resumes messages queue processing).
|
||||||
|
//
|
||||||
|
// I chose to have this exception derive from RuntimeError, since if one is thrown from outside
|
||||||
|
// an App message loop we'll still want it to be handled in a reasonably graceful manner.
|
||||||
|
class CancelEvent : public virtual RuntimeError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DEFINE_EXCEPTION_COPYTORS( CancelEvent )
|
||||||
|
|
||||||
|
explicit CancelEvent( const char* logmsg )
|
||||||
|
{
|
||||||
|
m_message_diag = fromUTF8( logmsg );
|
||||||
|
// overridden message formatters only use the diagnostic version...
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit CancelEvent( const wxString& logmsg=L"No reason given." )
|
||||||
|
{
|
||||||
|
m_message_diag = logmsg;
|
||||||
|
// overridden message formatters only use the diagnostic version...
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual wxString FormatDisplayMessage() const;
|
||||||
|
virtual wxString FormatDiagnosticMessage() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
class ObjectIsNull : public virtual CancelEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxString ObjectName;
|
wxString ObjectName;
|
||||||
|
@ -175,17 +209,16 @@ namespace Exception
|
||||||
|
|
||||||
explicit ObjectIsNull( const char* objname="unspecified" )
|
explicit ObjectIsNull( const char* objname="unspecified" )
|
||||||
{
|
{
|
||||||
m_message_diag = wxString::FromUTF8( objname );
|
m_message_diag = fromUTF8( objname );
|
||||||
// overridden message formatters only use the diagnostic version...
|
// overridden message formatters only use the diagnostic version...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual wxString FormatDisplayMessage() const;
|
virtual wxString FormatDisplayMessage() const;
|
||||||
virtual wxString FormatDiagnosticMessage() const;
|
virtual wxString FormatDiagnosticMessage() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
// OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
|
// OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class OutOfMemory : public virtual RuntimeError
|
class OutOfMemory : public virtual RuntimeError
|
||||||
|
@ -282,17 +315,17 @@ namespace Exception
|
||||||
explicit classname( const char* objname, const char* msg=defmsg ) \
|
explicit classname( const char* objname, const char* msg=defmsg ) \
|
||||||
{ \
|
{ \
|
||||||
BaseException::InitBaseEx( msg ); \
|
BaseException::InitBaseEx( msg ); \
|
||||||
StreamName = wxString::FromUTF8( objname ); \
|
StreamName = fromUTF8( objname ); \
|
||||||
} \
|
} \
|
||||||
explicit classname( const char* objname, const wxString& msg_eng, const wxString& msg_xlt ) \
|
explicit classname( const char* objname, const wxString& msg_eng, const wxString& msg_xlt ) \
|
||||||
{ \
|
{ \
|
||||||
BaseException::InitBaseEx( msg_eng, msg_xlt ); \
|
BaseException::InitBaseEx( msg_eng, msg_xlt ); \
|
||||||
StreamName = wxString::FromUTF8( objname ); \
|
StreamName = fromUTF8( objname ); \
|
||||||
} \
|
} \
|
||||||
explicit classname( const char* objname, const wxString& msg_eng ) \
|
explicit classname( const char* objname, const wxString& msg_eng ) \
|
||||||
{ \
|
{ \
|
||||||
BaseException::InitBaseEx( msg_eng, msg_eng ); \
|
BaseException::InitBaseEx( msg_eng, msg_eng ); \
|
||||||
StreamName = wxString::FromUTF8( objname ); \
|
StreamName = fromUTF8( objname ); \
|
||||||
} \
|
} \
|
||||||
explicit classname( const wxString& objname, const wxString& msg_eng ) \
|
explicit classname( const wxString& objname, const wxString& msg_eng ) \
|
||||||
{ \
|
{ \
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wx/event.h>
|
||||||
|
|
||||||
|
template< typename EvtType >
|
||||||
|
struct EventListener
|
||||||
|
{
|
||||||
|
typedef void FuncType( void* object, const EvtType& evt );
|
||||||
|
|
||||||
|
void* object;
|
||||||
|
FuncType* OnEvent;
|
||||||
|
|
||||||
|
EventListener( FuncType* fnptr )
|
||||||
|
{
|
||||||
|
object = NULL;
|
||||||
|
OnEvent = fnptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventListener( void* objhandle, FuncType* fnptr )
|
||||||
|
{
|
||||||
|
object = objhandle;
|
||||||
|
OnEvent = fnptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator ==( const EventListener& right ) const
|
||||||
|
{
|
||||||
|
return (object == right.object) && (OnEvent == right.OnEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator !=( const EventListener& right ) const
|
||||||
|
{
|
||||||
|
return this->operator ==(right);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename EvtType >
|
||||||
|
class EventSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename EventListener<EvtType> ListenerType;
|
||||||
|
typedef typename std::list< ListenerType > ListenerList;
|
||||||
|
typedef typename ListenerList::const_iterator Handle;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ListenerList m_listeners;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~EventSource() throw() {}
|
||||||
|
|
||||||
|
virtual void Remove( const ListenerType& listener )
|
||||||
|
{
|
||||||
|
m_listeners.remove( listener );
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Remove( const Handle& listenerHandle )
|
||||||
|
{
|
||||||
|
m_listeners.erase( listenerHandle );
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Handle AddFast( const ListenerType& listener )
|
||||||
|
{
|
||||||
|
m_listeners.push_front( listener );
|
||||||
|
return m_listeners.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks for duplicates before adding the event.
|
||||||
|
virtual inline void Add( const ListenerType& listener );
|
||||||
|
virtual inline void RemoveObject( const void* object );
|
||||||
|
|
||||||
|
Handle Add( void* objhandle, typename ListenerType::FuncType* fnptr )
|
||||||
|
{
|
||||||
|
return Add( Handle( objhandle, fnptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove( void* objhandle, typename ListenerType::FuncType* fnptr )
|
||||||
|
{
|
||||||
|
Remove( Handle( objhandle, fnptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dispatch( const wxCommandEvent& evt ) const
|
||||||
|
{
|
||||||
|
Handle iter = m_listeners.begin();
|
||||||
|
while( iter != m_listeners.end() )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
iter->OnEvent( iter->object, evt );
|
||||||
|
}
|
||||||
|
catch( Exception::RuntimeError& ex )
|
||||||
|
{
|
||||||
|
Console::Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
|
||||||
|
}
|
||||||
|
catch( BaseException& ex )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// EventListenerBinding
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// Encapsulated event listener binding, provides the "benefits" of object unwinding.
|
||||||
|
//
|
||||||
|
template< typename EvtType = wxCommandEvent >
|
||||||
|
class EventListenerBinding
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename EventSource<EvtType>::ListenerType ListenerHandle;
|
||||||
|
typedef typename EventSource<EvtType>::Handle ConstIterator;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EventSource<EvtType>& m_source;
|
||||||
|
const ListenerHandle m_listener;
|
||||||
|
ConstIterator m_iter;
|
||||||
|
bool m_attached;
|
||||||
|
|
||||||
|
public:
|
||||||
|
EventListenerBinding( EventSource<EvtType>& source, const ListenerHandle& listener, bool autoAttach=true ) :
|
||||||
|
m_source( source )
|
||||||
|
, m_listener( listener )
|
||||||
|
, m_attached( false )
|
||||||
|
{
|
||||||
|
// If you want to assert on null pointers, you'll need to do the check yourself. There's
|
||||||
|
// too many cases where silently ignoring null pointers is the desired behavior.
|
||||||
|
//if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return;
|
||||||
|
if( autoAttach ) Attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~EventListenerBinding() throw()
|
||||||
|
{
|
||||||
|
Detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Detach()
|
||||||
|
{
|
||||||
|
if( !m_attached ) return;
|
||||||
|
m_source.Remove( m_iter );
|
||||||
|
m_attached = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Attach()
|
||||||
|
{
|
||||||
|
if( m_attached || (m_listener.OnEvent == NULL) ) return;
|
||||||
|
m_iter = m_source.AddFast( m_listener );
|
||||||
|
m_attached = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef EventSource<wxCommandEvent> CmdEvt_Source;
|
||||||
|
typedef EventListener<wxCommandEvent> CmdEvt_Listener;
|
||||||
|
typedef EventListenerBinding<wxCommandEvent> CmdEvt_ListenerBinding;
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for duplicates before adding the event.
|
||||||
|
template< typename EvtType >
|
||||||
|
void EventSource<EvtType>::Add( const ListenerType& listener )
|
||||||
|
{
|
||||||
|
if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return;
|
||||||
|
|
||||||
|
Handle iter = m_listeners.begin();
|
||||||
|
while( iter != m_listeners.end() )
|
||||||
|
{
|
||||||
|
if( *iter == listener ) return;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
AddFast( listener );
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes all listeners which reference the given object. Use for assuring object deletion.
|
||||||
|
template< typename EvtType >
|
||||||
|
void EventSource<EvtType>::RemoveObject( const void* object )
|
||||||
|
{
|
||||||
|
class PredicatesAreTheThingsOfNightmares
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
const void* const m_object_match;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PredicatesAreTheThingsOfNightmares( const void* objmatch ) : m_object_match( objmatch ) { }
|
||||||
|
|
||||||
|
bool operator()( const ListenerType& src )
|
||||||
|
{
|
||||||
|
return src.object == m_object_match;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
m_listeners.remove_if( PredicatesAreTheThingsOfNightmares( object ) );
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
# include <wx/msw/wrapwin.h>
|
||||||
|
#else
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Windows Redtape! No windows.h should be included without it!
|
// Windows Redtape! No windows.h should be included without it!
|
||||||
//
|
//
|
||||||
|
@ -40,3 +44,4 @@
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
|
@ -15,25 +15,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <semaphore.h>
|
||||||
#include <errno.h> // EBUSY
|
#include <errno.h> // EBUSY
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <semaphore.h>
|
|
||||||
|
|
||||||
#include "Pcsx2Defs.h"
|
#include "Pcsx2Defs.h"
|
||||||
#include "ScopedPtr.h"
|
#include "ScopedPtr.h"
|
||||||
|
|
||||||
namespace Exception
|
#undef Yield // release th burden of windows.h global namespace spam.
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Thread termination exception, used to quickly terminate threads from anywhere in the
|
|
||||||
// thread's call stack. This exception is handled by the PCSX2 PersistentThread class. Threads
|
|
||||||
// not derived from that class will not handle this exception.
|
|
||||||
//
|
|
||||||
class ThreadTermination
|
|
||||||
{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class wxTimeSpan;
|
class wxTimeSpan;
|
||||||
|
|
||||||
namespace Threading
|
namespace Threading
|
||||||
|
@ -57,6 +46,38 @@ namespace Threading
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// NonblockingMutex
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// This is a very simple non-blocking mutex, which behaves similarly to pthread_mutex's
|
||||||
|
// trylock(), but without any of the extra overhead needed to set up a structure capable
|
||||||
|
// of blocking waits. It basically optimizes to a single InterlockedExchange.
|
||||||
|
//
|
||||||
|
// Simple use: if TryLock() returns false, the Bool is already interlocked by another thread.
|
||||||
|
// If TryLock() returns true, you've locked the object and are *responsible* for unlocking
|
||||||
|
// it later.
|
||||||
|
//
|
||||||
|
class NonblockingMutex
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
volatile long val;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NonblockingMutex() : val( false ) {}
|
||||||
|
virtual ~NonblockingMutex() throw() {};
|
||||||
|
|
||||||
|
bool TryLock() throw()
|
||||||
|
{
|
||||||
|
return !_InterlockedExchange( &val, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLocked()
|
||||||
|
{ return !!val; }
|
||||||
|
|
||||||
|
void Release()
|
||||||
|
{ val = false; }
|
||||||
|
};
|
||||||
|
|
||||||
struct Semaphore
|
struct Semaphore
|
||||||
{
|
{
|
||||||
sem_t sema;
|
sem_t sema;
|
||||||
|
@ -166,7 +187,7 @@ namespace Threading
|
||||||
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.
|
MutexLock m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
|
||||||
|
|
||||||
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.
|
||||||
|
@ -190,27 +211,49 @@ namespace Threading
|
||||||
bool IsSelf() const;
|
bool IsSelf() const;
|
||||||
wxString GetName() const;
|
wxString GetName() const;
|
||||||
|
|
||||||
void _ThreadCleanup();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// Extending classes should always implement your own OnStart(), which is called by
|
// Extending classes should always implement your own OnStart(), which is called by
|
||||||
// Start() once necessary locks have been obtained. Do not override Start() directly
|
// Start() once necessary locks have been obtained. Do not override Start() directly
|
||||||
// unless you're really sure that's what you need to do. ;)
|
// unless you're really sure that's what you need to do. ;)
|
||||||
virtual void OnStart()=0;
|
virtual void OnStart()=0;
|
||||||
virtual void OnThreadCleanup()=0;
|
virtual void OnThreadCleanup()=0;
|
||||||
|
|
||||||
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
|
|
||||||
// on linux).
|
|
||||||
static void* _internal_callback( void* func );
|
|
||||||
|
|
||||||
// Implemented by derived class to handle threading actions!
|
// Implemented by derived class to handle threading actions!
|
||||||
virtual void ExecuteTask()=0;
|
virtual void ExecuteTask()=0;
|
||||||
|
|
||||||
|
// Inserts a thread cancellation point. If the thread has received a cancel request, this
|
||||||
|
// function will throw an SEH exception designed to exit the thread (so make sure to use C++
|
||||||
|
// object encapsulation for anything that could leak resources, to ensure object unwinding
|
||||||
|
// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
|
||||||
|
void TestCancel();
|
||||||
|
|
||||||
|
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
|
||||||
|
// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()
|
||||||
|
// or better yet, disable cancellation of the thread completely with DisableCancellation().
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// ms - 'minimum' yield time in milliseconds (rough -- typically yields are longer by 1-5ms
|
||||||
|
// depending on operating system/platform). If ms is 0 or unspecified, then a single
|
||||||
|
// timeslice is yielded to other contending threads. If no threads are contending for
|
||||||
|
// time when ms==0, then no yield is done, but cancellation is still tested.
|
||||||
|
void Yield( int ms = 0 )
|
||||||
|
{
|
||||||
|
pxAssert( IsSelf() );
|
||||||
|
Threading::Sleep( ms );
|
||||||
|
TestCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Section of methods for internal use only.
|
||||||
|
|
||||||
|
void _DoSetThreadName( const wxString& name );
|
||||||
|
void _DoSetThreadName( __unused const char* name );
|
||||||
|
void _internal_execute();
|
||||||
|
void _try_virtual_invoke( void (PersistentThread::*method)() );
|
||||||
|
void _ThreadCleanup();
|
||||||
|
|
||||||
|
static void* _internal_callback( void* func );
|
||||||
|
static void _pt_callback_cleanup( void* handle );
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(disable:4063) // case '1' is not a valid value for switch()
|
||||||
|
#endif
|
||||||
|
|
||||||
// These functions are meant for memset operations of constant length only.
|
// These functions are meant for memset operations of constant length only.
|
||||||
// For dynamic length clears, use the C-compiler provided memset instead.
|
// For dynamic length clears, use the C-compiler provided memset instead.
|
||||||
|
|
||||||
|
|
|
@ -227,14 +227,14 @@ void IConsoleWriter::_Write( const char* fmt, va_list args ) const
|
||||||
{
|
{
|
||||||
std::string m_format_buffer;
|
std::string m_format_buffer;
|
||||||
vssprintf( m_format_buffer, fmt, args );
|
vssprintf( m_format_buffer, fmt, args );
|
||||||
Write( wxString::FromUTF8( m_format_buffer.c_str() ) );
|
Write( fromUTF8( m_format_buffer.c_str() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void IConsoleWriter::_WriteLn( const char* fmt, va_list args ) const
|
void IConsoleWriter::_WriteLn( const char* fmt, va_list args ) const
|
||||||
{
|
{
|
||||||
std::string m_format_buffer;
|
std::string m_format_buffer;
|
||||||
vssprintf( m_format_buffer, fmt, args );
|
vssprintf( m_format_buffer, fmt, args );
|
||||||
WriteLn( wxString::FromUTF8( m_format_buffer.c_str() ) );
|
WriteLn( fromUTF8( m_format_buffer.c_str() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void IConsoleWriter::_WriteLn( ConsoleColors color, const char* fmt, va_list args ) const
|
void IConsoleWriter::_WriteLn( ConsoleColors color, const char* fmt, va_list args ) const
|
||||||
|
|
|
@ -116,11 +116,23 @@ wxString Exception::BaseException::FormatDiagnosticMessage() const
|
||||||
return m_message_diag + L"\n\n" + m_stacktrace;
|
return m_message_diag + L"\n\n" + m_stacktrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
wxString Exception::CancelEvent::FormatDiagnosticMessage() const
|
||||||
|
{
|
||||||
|
// FIXME: This should probably just log a single line from the stacktrace.. ?
|
||||||
|
return L"Action canceled: " + m_message_diag;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString Exception::CancelEvent::FormatDisplayMessage() const
|
||||||
|
{
|
||||||
|
return L"Action canceled: " + m_message_diag;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
|
wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
|
||||||
{
|
{
|
||||||
return wxsFormat(
|
return wxsFormat(
|
||||||
L"An attempted reference to the %s has failed; the frame does not exist or it's handle is null.",
|
L"reference to '%s' failed : handle is null.",
|
||||||
m_message_diag.c_str()
|
m_message_diag.c_str()
|
||||||
) + m_stacktrace;
|
) + m_stacktrace;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +140,7 @@ wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
|
||||||
wxString Exception::ObjectIsNull::FormatDisplayMessage() const
|
wxString Exception::ObjectIsNull::FormatDisplayMessage() const
|
||||||
{
|
{
|
||||||
return wxsFormat(
|
return wxsFormat(
|
||||||
L"An attempted reference to the %s has failed; the frame does not exist or it's handle is null.",
|
L"reference to '%s' failed : handle is null.",
|
||||||
m_message_diag.c_str()
|
m_message_diag.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,24 +15,30 @@
|
||||||
|
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "Threading.h"
|
|
||||||
#include "wxBaseTools.h"
|
|
||||||
|
|
||||||
#include <wx/datetime.h>
|
#ifdef _WIN32
|
||||||
#include <wx/thread.h>
|
# include <wx/msw/wrapwin.h> // for thread renaming features
|
||||||
|
#endif
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
|
|
||||||
#ifdef __LINUX__
|
#ifdef __LINUX__
|
||||||
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
|
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "Threading.h"
|
||||||
|
#include "wxBaseTools.h"
|
||||||
|
|
||||||
|
#include <wx/datetime.h>
|
||||||
|
#include <wx/thread.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace Threading;
|
using namespace Threading;
|
||||||
|
|
||||||
namespace Threading
|
namespace Threading
|
||||||
{
|
{
|
||||||
static const wxTimeSpan ts_msec_250( 0, 0, 0, 250 );
|
static const wxTimeSpan ts_msec_250( 0, 0, 0, 250 );
|
||||||
|
|
||||||
static void _pt_callback_cleanup( void* handle )
|
void PersistentThread::_pt_callback_cleanup( void* handle )
|
||||||
{
|
{
|
||||||
((PersistentThread*)handle)->_ThreadCleanup();
|
((PersistentThread*)handle)->_ThreadCleanup();
|
||||||
}
|
}
|
||||||
|
@ -59,11 +65,11 @@ namespace Threading
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wxString logfix = L"Thread Destructor for " + m_name;
|
Console.WriteLn( L"Thread Log: Executing destructor for " + m_name );
|
||||||
|
|
||||||
if( m_running )
|
if( m_running )
|
||||||
{
|
{
|
||||||
Console.WriteLn( logfix + L": Waiting for running thread to end.");
|
Console.WriteLn( L"\tWaiting for running thread to end...");
|
||||||
#if wxUSE_GUI
|
#if wxUSE_GUI
|
||||||
m_sem_finished.WaitGui();
|
m_sem_finished.WaitGui();
|
||||||
#else
|
#else
|
||||||
|
@ -73,9 +79,7 @@ namespace Threading
|
||||||
// it gets destroyed, otherwise th mutex handle would become invalid.
|
// it gets destroyed, otherwise th mutex handle would become invalid.
|
||||||
ScopedLock locker( m_lock_start );
|
ScopedLock locker( m_lock_start );
|
||||||
}
|
}
|
||||||
else
|
Threading::Sleep( 1 );
|
||||||
Console.WriteLn( logfix + L": thread not running.");
|
|
||||||
Sleep( 1 );
|
|
||||||
Detach();
|
Detach();
|
||||||
}
|
}
|
||||||
DESTRUCTOR_CATCHALL
|
DESTRUCTOR_CATCHALL
|
||||||
|
@ -189,47 +193,22 @@ namespace Threading
|
||||||
m_except->Rethrow();
|
m_except->Rethrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoked internally when canceling or exiting the thread. Extending classes should implement
|
void PersistentThread::TestCancel()
|
||||||
// OnThreadCleanup() to extend clenup functionality.
|
|
||||||
void PersistentThread::_ThreadCleanup()
|
|
||||||
{
|
{
|
||||||
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
pxAssert( IsSelf() );
|
||||||
|
pthread_testcancel();
|
||||||
// Typically thread cleanup needs to lock against thread startup, since both
|
|
||||||
// will perform some measure of variable inits or resets, depending on how the
|
|
||||||
// derrived class is implemented.
|
|
||||||
ScopedLock startlock( m_lock_start );
|
|
||||||
|
|
||||||
OnThreadCleanup();
|
|
||||||
|
|
||||||
m_running = false;
|
|
||||||
m_sem_finished.Post();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString PersistentThread::GetName() const
|
// Executes the virtual member method
|
||||||
|
void PersistentThread::_try_virtual_invoke( void (PersistentThread::*method)() )
|
||||||
{
|
{
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PersistentThread::_internal_execute()
|
|
||||||
{
|
|
||||||
m_running = true;
|
|
||||||
DoSetThreadName( m_name );
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ExecuteTask();
|
(this->*method)();
|
||||||
}
|
|
||||||
catch( std::logic_error& ex )
|
|
||||||
{
|
|
||||||
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
|
|
||||||
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch( Exception::LogicError& ex )
|
|
||||||
{
|
|
||||||
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
|
||||||
ex.Rethrow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Neat repackaging for STL Runtime errors...
|
||||||
|
//
|
||||||
catch( std::runtime_error& ex )
|
catch( std::runtime_error& ex )
|
||||||
{
|
{
|
||||||
m_except = new Exception::RuntimeError(
|
m_except = new Exception::RuntimeError(
|
||||||
|
@ -244,16 +223,79 @@ namespace Threading
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
catch( Exception::RuntimeError& ex )
|
catch( Exception::RuntimeError& ex )
|
||||||
{
|
{
|
||||||
m_except = ex.Clone();
|
m_except = ex.Clone();
|
||||||
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Should we let logic errors propagate, or swallow them and let the thread manager
|
||||||
|
// handle them? Hmm..
|
||||||
|
/*catch( std::logic_error& ex )
|
||||||
|
{
|
||||||
|
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
|
||||||
|
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch( Exception::LogicError& ex )
|
||||||
|
{
|
||||||
|
m_except = ex.Clone();
|
||||||
|
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// BaseException / std::exception -- same deal. Allow propagation or no?
|
||||||
|
//
|
||||||
|
/*catch( std::exception& ex )
|
||||||
|
{
|
||||||
|
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s",
|
||||||
|
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch( Exception::BaseException& ex )
|
||||||
|
{
|
||||||
|
m_except = ex.Clone();
|
||||||
|
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked internally when canceling or exiting the thread. Extending classes should implement
|
||||||
|
// OnThreadCleanup() to extend cleanup functionality.
|
||||||
|
void PersistentThread::_ThreadCleanup()
|
||||||
|
{
|
||||||
|
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
||||||
|
|
||||||
|
// Typically thread cleanup needs to lock against thread startup, since both
|
||||||
|
// will perform some measure of variable inits or resets, depending on how the
|
||||||
|
// derived class is implemented.
|
||||||
|
ScopedLock startlock( m_lock_start );
|
||||||
|
|
||||||
|
_try_virtual_invoke( &PersistentThread::OnThreadCleanup );
|
||||||
|
|
||||||
|
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_virtual_invoke( &PersistentThread::ExecuteTask );
|
||||||
}
|
}
|
||||||
|
|
||||||
void PersistentThread::OnStart() {}
|
void PersistentThread::OnStart() {}
|
||||||
void PersistentThread::OnThreadCleanup() {}
|
void PersistentThread::OnThreadCleanup() {}
|
||||||
|
|
||||||
|
// passed into pthread_create, and is used to dispatch the thread's object oriented
|
||||||
|
// callback function
|
||||||
void* PersistentThread::_internal_callback( void* itsme )
|
void* PersistentThread::_internal_callback( void* itsme )
|
||||||
{
|
{
|
||||||
jASSUME( itsme != NULL );
|
jASSUME( itsme != NULL );
|
||||||
|
@ -265,12 +307,12 @@ namespace Threading
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PersistentThread::DoSetThreadName( const wxString& name )
|
void PersistentThread::_DoSetThreadName( const wxString& name )
|
||||||
{
|
{
|
||||||
DoSetThreadName( toUTF8(name) );
|
_DoSetThreadName( toUTF8(name) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void PersistentThread::DoSetThreadName( __unused const char* name )
|
void PersistentThread::_DoSetThreadName( __unused const char* name )
|
||||||
{
|
{
|
||||||
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "x86emitter/tools.h"
|
#include "x86emitter/tools.h"
|
||||||
#include "Threading.h"
|
#include "Threading.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "implement.h" // win32 pthreads implementations.
|
#include "implement.h" // win32 pthreads implementations.
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
|
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "internal.h"
|
|
||||||
#include "tools.h"
|
|
||||||
#include "Utilities/RedtapeWindows.h"
|
#include "Utilities/RedtapeWindows.h"
|
||||||
#include "Utilities/Threading.h"
|
#include "Utilities/Threading.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace x86Emitter;
|
using namespace x86Emitter;
|
||||||
|
|
||||||
__aligned16 x86CPU_INFO x86caps;
|
__aligned16 x86CPU_INFO x86caps;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
Name="PcsxBaseProperties"
|
Name="PcsxBaseProperties"
|
||||||
OutputDirectory="$(SolutionDir)\bin\$(PcsxSubsection)"
|
OutputDirectory="$(SolutionDir)\bin\$(PcsxSubsection)"
|
||||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||||
DeleteExtensionsOnClean="*.bsc;*.idb;*.sbr;*.res;*.pch;*.pdb;*.obj;*.ilk;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;$(TargetPath)"
|
DeleteExtensionsOnClean="*.bsc;*.idb;*.sbr;*.res;*.pch;*.pdb;*.obj;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;$(TargetPath)"
|
||||||
>
|
>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
WarningLevel="3"
|
WarningLevel="3"
|
||||||
DebugInformationFormat="3"
|
DebugInformationFormat="3"
|
||||||
CompileAs="0"
|
CompileAs="0"
|
||||||
|
DisableSpecificWarnings="4063;4100"
|
||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
WarningLevel="3"
|
WarningLevel="3"
|
||||||
DebugInformationFormat="3"
|
DebugInformationFormat="3"
|
||||||
CompileAs="0"
|
CompileAs="0"
|
||||||
|
DisableSpecificWarnings="4063;4100"
|
||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#define ENABLE_TIMESTAMPS
|
#define ENABLE_TIMESTAMPS
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
# include <wx/msw/wrapwin.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
#include "IsoFileTools.h"
|
#include "IsoFileTools.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
# include <wx/msw/wrapwin.h>
|
||||||
|
|
||||||
|
|
||||||
void *_openfile(const char *filename, int flags)
|
void *_openfile(const char *filename, int flags)
|
||||||
{
|
{
|
||||||
|
|
|
@ -102,7 +102,6 @@ protected:
|
||||||
int m_CopyCommandTally;
|
int m_CopyCommandTally;
|
||||||
int m_CopyDataTally;
|
int m_CopyDataTally;
|
||||||
volatile bool m_RingBufferIsBusy;
|
volatile bool m_RingBufferIsBusy;
|
||||||
volatile bool m_LoadState;
|
|
||||||
|
|
||||||
// Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to
|
// Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to
|
||||||
// throttle the number of frames allowed to be rendered ahead of time for games that
|
// throttle the number of frames allowed to be rendered ahead of time for games that
|
||||||
|
@ -153,7 +152,8 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void OpenPlugin();
|
void OpenPlugin();
|
||||||
void OnSuspendInThread();
|
void OnSuspendInThread();
|
||||||
void OnResumeInThread();
|
void OnPauseInThread() {}
|
||||||
|
void OnResumeInThread( bool IsSuspended );
|
||||||
|
|
||||||
void OnResumeReady();
|
void OnResumeReady();
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,7 @@ struct GIFregisters
|
||||||
u32 padding[3];
|
u32 padding[3];
|
||||||
tGIF_MODE mode;
|
tGIF_MODE mode;
|
||||||
u32 padding1[3];
|
u32 padding1[3];
|
||||||
tGIF_STAT stat;
|
tGIF_STAT stat;
|
||||||
u32 padding2[7];
|
u32 padding2[7];
|
||||||
|
|
||||||
tGIF_TAG0 tag0;
|
tGIF_TAG0 tag0;
|
||||||
|
@ -218,7 +218,7 @@ struct GIFregisters
|
||||||
u32 tag3;
|
u32 tag3;
|
||||||
u32 padding6[3];
|
u32 padding6[3];
|
||||||
|
|
||||||
tGIF_CNT cnt;
|
tGIF_CNT cnt;
|
||||||
u32 padding7[3];
|
u32 padding7[3];
|
||||||
tGIF_P3CNT p3cnt;
|
tGIF_P3CNT p3cnt;
|
||||||
u32 padding8[3];
|
u32 padding8[3];
|
||||||
|
|
|
@ -54,7 +54,6 @@ public:
|
||||||
|
|
||||||
extern StartupParams g_Startup;
|
extern StartupParams g_Startup;
|
||||||
|
|
||||||
extern void States_Load( const wxString& file );
|
|
||||||
extern void States_Save( const wxString& file );
|
extern void States_Save( const wxString& file );
|
||||||
extern bool States_isSlotUsed(int num);
|
extern bool States_isSlotUsed(int num);
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,6 @@ mtgsThreadObject::mtgsThreadObject() :
|
||||||
, m_CopyCommandTally( 0 )
|
, m_CopyCommandTally( 0 )
|
||||||
, m_CopyDataTally( 0 )
|
, m_CopyDataTally( 0 )
|
||||||
, m_RingBufferIsBusy( false )
|
, m_RingBufferIsBusy( false )
|
||||||
, m_LoadState( false )
|
|
||||||
, m_QueuedFrames( 0 )
|
, m_QueuedFrames( 0 )
|
||||||
, m_lock_FrameQueueCounter()
|
, m_lock_FrameQueueCounter()
|
||||||
, m_packet_size( 0 )
|
, m_packet_size( 0 )
|
||||||
|
@ -127,7 +126,6 @@ void mtgsThreadObject::OnStart()
|
||||||
m_WritePos = 0;
|
m_WritePos = 0;
|
||||||
|
|
||||||
m_RingBufferIsBusy = false;
|
m_RingBufferIsBusy = false;
|
||||||
m_LoadState = false;
|
|
||||||
|
|
||||||
m_QueuedFrames = 0;
|
m_QueuedFrames = 0;
|
||||||
m_packet_size = 0;
|
m_packet_size = 0;
|
||||||
|
@ -435,9 +433,9 @@ void mtgsThreadObject::OnSuspendInThread()
|
||||||
_clean_close_gs( NULL );
|
_clean_close_gs( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::OnResumeInThread()
|
void mtgsThreadObject::OnResumeInThread( bool isSuspended )
|
||||||
{
|
{
|
||||||
if( !m_LoadState )
|
if( isSuspended )
|
||||||
OpenPlugin();
|
OpenPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,7 +810,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
||||||
{
|
{
|
||||||
AtomicExchange( m_RingPos, m_WritePos );
|
AtomicExchange( m_RingPos, m_WritePos );
|
||||||
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||||
m_LoadState = true;
|
|
||||||
SetEvent();
|
SetEvent();
|
||||||
Resume();
|
Resume();
|
||||||
}
|
}
|
||||||
|
@ -820,7 +817,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
||||||
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||||
|
|
||||||
mtgsWaitGS();
|
mtgsWaitGS();
|
||||||
m_LoadState = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits for the GS to empty out the entire ring buffer contents.
|
// Waits for the GS to empty out the entire ring buffer contents.
|
||||||
|
|
|
@ -90,7 +90,7 @@ struct LegacyApi_CommonMethod
|
||||||
// returns the method name as a wxString, converted from UTF8.
|
// returns the method name as a wxString, converted from UTF8.
|
||||||
wxString GetMethodName( PluginsEnum_t pid ) const
|
wxString GetMethodName( PluginsEnum_t pid ) const
|
||||||
{
|
{
|
||||||
return tbl_PluginInfo[pid].GetShortname() + wxString::FromUTF8( MethodName );
|
return tbl_PluginInfo[pid].GetShortname() + fromUTF8( MethodName );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ struct LegacyApi_ReqMethod
|
||||||
// returns the method name as a wxString, converted from UTF8.
|
// returns the method name as a wxString, converted from UTF8.
|
||||||
wxString GetMethodName( ) const
|
wxString GetMethodName( ) const
|
||||||
{
|
{
|
||||||
return wxString::FromUTF8( MethodName );
|
return fromUTF8( MethodName );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ struct LegacyApi_OptMethod
|
||||||
VoidMethod** Dest; // Target function where the binding is saved.
|
VoidMethod** Dest; // Target function where the binding is saved.
|
||||||
|
|
||||||
// returns the method name as a wxString, converted from UTF8.
|
// returns the method name as a wxString, converted from UTF8.
|
||||||
wxString GetMethodName() const { return wxString::FromUTF8( MethodName ); }
|
wxString GetMethodName() const { return fromUTF8( MethodName ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,21 @@ struct PluginInfo
|
||||||
|
|
||||||
wxString GetShortname() const
|
wxString GetShortname() const
|
||||||
{
|
{
|
||||||
return wxString::FromUTF8( shortname );
|
return fromUTF8( shortname );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
// Disabling C4673: throwing 'Exception::Blah' the following types will not be considered at the catch site
|
||||||
|
// The warning is bugged, and happens even though we're properly inheriting classes with
|
||||||
|
// 'virtual' qualifiers. But since the warning is potentially useful elsewhere, I disable
|
||||||
|
// it only for the scope of these exceptions.
|
||||||
|
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable:4673)
|
||||||
|
#endif
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// Plugin-related Exceptions
|
// Plugin-related Exceptions
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -139,9 +150,12 @@ namespace Exception
|
||||||
virtual wxString FormatDiagnosticMessage() const;
|
virtual wxString FormatDiagnosticMessage() const;
|
||||||
virtual wxString FormatDisplayMessage() const;
|
virtual wxString FormatDisplayMessage() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// LegacyPluginAPI_Common
|
// LegacyPluginAPI_Common
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace R5900Exception
|
||||||
|
|
||||||
void Init( const char*msg )
|
void Init( const char*msg )
|
||||||
{
|
{
|
||||||
m_message = wxString::FromUTF8( msg );
|
m_message = fromUTF8( msg );
|
||||||
cpuState = cpuRegs;
|
cpuState = cpuRegs;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,57 +13,98 @@
|
||||||
* If not, see <http://www.gnu.org/licenses/>.
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
#include "App.h"
|
#include "App.h"
|
||||||
#include "HostGui.h"
|
#include "HostGui.h"
|
||||||
|
|
||||||
#include "zlib/zlib.h"
|
#include "zlib/zlib.h"
|
||||||
|
|
||||||
|
|
||||||
static SafeArray<u8> state_buffer;
|
static SafeArray<u8> state_buffer;
|
||||||
|
|
||||||
// Simple lock boolean for the state buffer in use by a thread. This simple solution works because
|
// Simple lock boolean for the state buffer being in use by a thread.
|
||||||
// we are assured that state save/load actions will only be initiated from the main thread.
|
static NonblockingMutex state_buffer_lock;
|
||||||
static bool state_buffer_lock = false;
|
|
||||||
|
|
||||||
// This boolean is to keep the system from resuming emulation until the current state has completely
|
// 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
|
// uploaded or downloaded itself. It is only modified from the main thread, and should only be read
|
||||||
// form the main thread.
|
// form the main thread.
|
||||||
bool sys_resume_lock = false;
|
bool sys_resume_lock = false;
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
|
||||||
// StateThread_Freeze
|
|
||||||
// --------------------------------------------------------------------------------------
|
enum
|
||||||
class StateThread_Freeze : public PersistentThread
|
{
|
||||||
|
StateThreadAction_None = 0,
|
||||||
|
StateThreadAction_Create,
|
||||||
|
StateThreadAction_Restore,
|
||||||
|
StateThreadAction_ZipToDisk,
|
||||||
|
StateThreadAction_UnzipFromDisk,
|
||||||
|
};
|
||||||
|
|
||||||
|
class _BaseStateThread : public PersistentThread
|
||||||
{
|
{
|
||||||
typedef PersistentThread _parent;
|
typedef PersistentThread _parent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StateThread_Freeze( const wxString& file )
|
virtual ~_BaseStateThread() throw()
|
||||||
{
|
{
|
||||||
m_name = L"SaveState::Freeze";
|
state_buffer_lock.Release(); // just in case;
|
||||||
|
|
||||||
AllowFromMainThreadOnly();
|
|
||||||
if( state_buffer_lock )
|
|
||||||
throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
|
|
||||||
|
|
||||||
Start();
|
|
||||||
sys_resume_lock = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnStart() {}
|
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished )
|
||||||
|
{
|
||||||
|
Callback_FreezeFinished = onFinished;
|
||||||
|
m_name = L"StateThread::" + fromUTF8(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnStart()
|
||||||
|
{
|
||||||
|
if( !state_buffer_lock.TryLock() )
|
||||||
|
throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendFinishEvent( int type )
|
||||||
|
{
|
||||||
|
wxCommandEvent evt( pxEVT_FreezeThreadFinished );
|
||||||
|
evt.SetClientData( this );
|
||||||
|
evt.SetInt( type );
|
||||||
|
wxGetApp().AddPendingEvent( evt );
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// StateThread_Freeze
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
class StateThread_Freeze : public _BaseStateThread
|
||||||
|
{
|
||||||
|
typedef _BaseStateThread _parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StateThread_Freeze( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Freeze", onFinished )
|
||||||
|
{
|
||||||
|
if( !SysHasValidState() )
|
||||||
|
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OnStart()
|
||||||
|
{
|
||||||
|
_parent::OnStart();
|
||||||
|
sys_resume_lock = true;
|
||||||
|
sCoreThread.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
void ExecuteTask()
|
void ExecuteTask()
|
||||||
{
|
{
|
||||||
memSavingState( state_buffer ).FreezeAll();
|
memSavingState( state_buffer ).FreezeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnThreadCleanup()
|
void OnThreadCleanup()
|
||||||
{
|
{
|
||||||
wxCommandEvent evt( pxEVT_FreezeFinished );
|
SendFinishEvent( StateThreadAction_Create );
|
||||||
evt.SetClientData( this );
|
|
||||||
wxGetApp().AddPendingEvent( evt );
|
|
||||||
|
|
||||||
_parent::OnThreadCleanup();
|
_parent::OnThreadCleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -71,37 +112,36 @@ protected:
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// StateThread_Thaw
|
// StateThread_Thaw
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
class StateThread_Thaw : public PersistentThread
|
class StateThread_Thaw : public _BaseStateThread
|
||||||
{
|
{
|
||||||
typedef PersistentThread _parent;
|
typedef _BaseStateThread _parent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StateThread_Thaw( const wxString& file )
|
StateThread_Thaw( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Thaw", onFinished ) { }
|
||||||
{
|
|
||||||
m_name = L"SaveState::Thaw";
|
|
||||||
|
|
||||||
AllowFromMainThreadOnly();
|
|
||||||
if( state_buffer_lock )
|
|
||||||
throw Exception::RuntimeError( "Cannot sload state; a previous save or load action is already in progress." );
|
|
||||||
|
|
||||||
Start();
|
|
||||||
sys_resume_lock = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnStart() {}
|
void OnStart()
|
||||||
|
{
|
||||||
|
_parent::OnStart();
|
||||||
|
|
||||||
|
if( state_buffer.IsDisposed() )
|
||||||
|
{
|
||||||
|
state_buffer_lock.Release();
|
||||||
|
throw Exception::RuntimeError( "ThawState request made, but no valid state exists!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_resume_lock = true;
|
||||||
|
sCoreThread.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
void ExecuteTask()
|
void ExecuteTask()
|
||||||
{
|
{
|
||||||
memSavingState( state_buffer ).FreezeAll();
|
memLoadingState( state_buffer ).FreezeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnThreadCleanup()
|
void OnThreadCleanup()
|
||||||
{
|
{
|
||||||
wxCommandEvent evt( pxEVT_FreezeFinished );
|
SendFinishEvent( StateThreadAction_Restore );
|
||||||
evt.SetClientData( this );
|
|
||||||
wxGetApp().AddPendingEvent( evt );
|
|
||||||
|
|
||||||
_parent::OnThreadCleanup();
|
_parent::OnThreadCleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -109,57 +149,46 @@ protected:
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// StateThread_ZipToDisk
|
// StateThread_ZipToDisk
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
class StateThread_ZipToDisk : public PersistentThread
|
class StateThread_ZipToDisk : public _BaseStateThread
|
||||||
{
|
{
|
||||||
typedef PersistentThread _parent;
|
typedef _BaseStateThread _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gzFile m_gzfp;
|
const wxString m_filename;
|
||||||
|
gzFile m_gzfp;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StateThread_ZipToDisk( const wxString& file ) : m_gzfp( NULL )
|
StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) :
|
||||||
|
_BaseStateThread( "ZipToDisk", onFinished )
|
||||||
|
, m_filename( file )
|
||||||
|
, m_gzfp( NULL )
|
||||||
{
|
{
|
||||||
m_name = L"SaveState::ZipToDisk";
|
|
||||||
|
|
||||||
AllowFromMainThreadOnly();
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~StateThread_ZipToDisk() throw()
|
~StateThread_ZipToDisk() throw()
|
||||||
{
|
{
|
||||||
sys_resume_lock = false; // just in case;
|
|
||||||
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnStart() {}
|
void OnStart()
|
||||||
|
{
|
||||||
|
_parent::OnStart();
|
||||||
|
m_gzfp = gzopen( toUTF8(m_filename), "wb" );
|
||||||
|
if( m_gzfp == NULL )
|
||||||
|
throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." );
|
||||||
|
}
|
||||||
|
|
||||||
void ExecuteTask()
|
void ExecuteTask()
|
||||||
{
|
{
|
||||||
Sleep( 2 );
|
Yield( 2 );
|
||||||
if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
|
if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
|
||||||
throw Exception::BadStream();
|
throw Exception::BadStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnThreadCleanup()
|
void OnThreadCleanup()
|
||||||
{
|
{
|
||||||
wxCommandEvent evt( pxEVT_FreezeFinished );
|
SendFinishEvent( StateThreadAction_ZipToDisk );
|
||||||
evt.SetClientData( this ); // tells message to clean us up.
|
|
||||||
wxGetApp().AddPendingEvent( evt );
|
|
||||||
|
|
||||||
_parent::OnThreadCleanup();
|
_parent::OnThreadCleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -168,43 +197,41 @@ protected:
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// StateThread_UnzipFromDisk
|
// StateThread_UnzipFromDisk
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
class StateThread_UnzipFromDisk : public PersistentThread
|
class StateThread_UnzipFromDisk : public _BaseStateThread
|
||||||
{
|
{
|
||||||
typedef PersistentThread _parent;
|
typedef _BaseStateThread _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gzFile m_gzfp;
|
const wxString m_filename;
|
||||||
|
gzFile m_gzfp;
|
||||||
|
|
||||||
|
// set true only once the whole file has finished loading. IF the thread is canceled or
|
||||||
|
// an error occurs, this will remain false.
|
||||||
|
bool m_finished;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StateThread_UnzipFromDisk( const wxString& file ) : m_gzfp( NULL )
|
StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) :
|
||||||
|
_BaseStateThread( "UnzipFromDisk", onFinished )
|
||||||
|
, m_filename( file )
|
||||||
|
, m_gzfp( NULL )
|
||||||
|
, m_finished( false )
|
||||||
{
|
{
|
||||||
m_name = L"SaveState::UnzipFromDisk";
|
|
||||||
|
|
||||||
AllowFromMainThreadOnly();
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~StateThread_UnzipFromDisk() throw()
|
~StateThread_UnzipFromDisk() throw()
|
||||||
{
|
{
|
||||||
sys_resume_lock = false; // just in case;
|
|
||||||
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnStart() {}
|
void OnStart()
|
||||||
|
{
|
||||||
|
_parent::OnStart();
|
||||||
|
|
||||||
|
m_gzfp = gzopen( toUTF8(m_filename), "rb" );
|
||||||
|
if( m_gzfp == NULL )
|
||||||
|
throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." );
|
||||||
|
}
|
||||||
|
|
||||||
void ExecuteTask()
|
void ExecuteTask()
|
||||||
{
|
{
|
||||||
|
@ -217,70 +244,95 @@ protected:
|
||||||
state_buffer.ExactAlloc( curidx+BlockSize );
|
state_buffer.ExactAlloc( curidx+BlockSize );
|
||||||
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
|
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
|
||||||
curidx += BlockSize;
|
curidx += BlockSize;
|
||||||
|
TestCancel();
|
||||||
} while( !gzeof(m_gzfp) );
|
} while( !gzeof(m_gzfp) );
|
||||||
|
|
||||||
|
m_finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnThreadCleanup()
|
void OnThreadCleanup()
|
||||||
{
|
{
|
||||||
wxCommandEvent evt( pxEVT_ThawFinished );
|
SendFinishEvent( StateThreadAction_UnzipFromDisk );
|
||||||
evt.SetClientData( this ); // tells message to clean us up.
|
|
||||||
wxGetApp().AddPendingEvent( evt );
|
|
||||||
|
|
||||||
_parent::OnThreadCleanup();
|
_parent::OnThreadCleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Pcsx2App::OnFreezeFinished( wxCommandEvent& evt )
|
void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
|
||||||
{
|
{
|
||||||
state_buffer.Dispose();
|
// clear the OnFreezeFinsihed to NULL now, in case of error.
|
||||||
state_buffer_lock = false;
|
// (but only actually run it if no errors occur)
|
||||||
|
FnType_OnThreadComplete* fn_tmp = Callback_FreezeFinished;
|
||||||
|
Callback_FreezeFinished = NULL;
|
||||||
|
|
||||||
SysClearExecutionCache();
|
{
|
||||||
sCoreThread.Resume();
|
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
|
||||||
|
if( !pxAssertDev( thr != NULL, "NULL thread handle on freeze finished?" ) ) return;
|
||||||
|
state_buffer_lock.Release();
|
||||||
|
sys_resume_lock = false;
|
||||||
|
thr->RethrowException();
|
||||||
|
}
|
||||||
|
|
||||||
delete (PersistentThread*)evt.GetClientData();
|
if( fn_tmp != NULL ) fn_tmp( evt );
|
||||||
|
|
||||||
|
//m_evtsrc_FreezeThreadFinished.Dispatch( evt );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pcsx2App::OnThawFinished( wxCommandEvent& evt )
|
void OnFinished_Resume( const wxCommandEvent& evt )
|
||||||
{
|
{
|
||||||
PersistentThread* thr = (PersistentThread*)evt.GetClientData();
|
if( evt.GetInt() == StateThreadAction_Restore )
|
||||||
if( thr == NULL )
|
|
||||||
{
|
{
|
||||||
pxAssert( false );
|
// Successfully restored state, so remove the copy. Don't remove it sooner
|
||||||
return;
|
// because the thread may have failed with some exception/error.
|
||||||
|
|
||||||
|
state_buffer.Dispose();
|
||||||
|
SysClearExecutionCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*catch( Exception::BadSavedState& ex)
|
|
||||||
{
|
|
||||||
// At this point we can return control back to the user, no questions asked.
|
|
||||||
// StateLoadErrors are only thorwn if the load failed prior to any virtual
|
|
||||||
// machine memory contents being changed. (usually missing file errors)
|
|
||||||
|
|
||||||
Console.Notice( ex.FormatDiagnosticMessage() );
|
|
||||||
sCoreThread.Resume();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
state_buffer.Dispose();
|
|
||||||
state_buffer_lock = false;
|
|
||||||
|
|
||||||
SysClearExecutionCache();
|
|
||||||
sCoreThread.Resume();
|
sCoreThread.Resume();
|
||||||
|
|
||||||
delete (PersistentThread*)evt.GetClientData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static wxString zip_dest_filename;
|
||||||
|
|
||||||
|
void OnFinished_ZipToDisk( const wxCommandEvent& evt )
|
||||||
|
{
|
||||||
|
if( !pxAssertDev( evt.GetInt() == StateThreadAction_Create, "Unexpected StateThreadAction value, aborting save." ) ) return;
|
||||||
|
|
||||||
|
if( zip_dest_filename.IsEmpty() )
|
||||||
|
{
|
||||||
|
Console.Notice( "Cannot save state to disk: empty filename specified." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Record to disk!!
|
||||||
|
(new StateThread_ZipToDisk( OnFinished_Resume, zip_dest_filename ))->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFinished_Restore( const wxCommandEvent& evt )
|
||||||
|
{
|
||||||
|
if( !pxAssertDev( evt.GetInt() == StateThreadAction_UnzipFromDisk, "Unexpected StateThreadAction value, aborting restore." ) ) return;
|
||||||
|
|
||||||
|
// Phase 2: Restore over existing VM state!!
|
||||||
|
(new StateThread_Thaw( OnFinished_Resume ))->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =====================================================================================================
|
||||||
|
// StateCopy Public Interface
|
||||||
|
// =====================================================================================================
|
||||||
|
|
||||||
void StateCopy_SaveToFile( const wxString& file )
|
void StateCopy_SaveToFile( const wxString& file )
|
||||||
{
|
{
|
||||||
if( state_buffer_lock ) return;
|
if( state_buffer_lock.IsLocked() ) return;
|
||||||
new StateThread_ZipToDisk( file );
|
zip_dest_filename = file;
|
||||||
|
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
|
||||||
|
Console.Status( wxsFormat( L"Saving savestate to file: %s", zip_dest_filename.c_str() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateCopy_LoadFromFile( const wxString& file )
|
void StateCopy_LoadFromFile( const wxString& file )
|
||||||
{
|
{
|
||||||
if( state_buffer_lock ) return;
|
if( state_buffer_lock.IsLocked() ) return;
|
||||||
|
sCoreThread.Pause();
|
||||||
sCoreThread.ShortSuspend();
|
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
|
||||||
new StateThread_UnzipFromDisk( file );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves recovery state info to the given saveslot, or saves the active emulation state
|
// Saves recovery state info to the given saveslot, or saves the active emulation state
|
||||||
|
@ -289,8 +341,28 @@ void StateCopy_LoadFromFile( const wxString& file )
|
||||||
// the one in the memory save. :)
|
// the one in the memory save. :)
|
||||||
void StateCopy_SaveToSlot( uint num )
|
void StateCopy_SaveToSlot( uint num )
|
||||||
{
|
{
|
||||||
if( state_buffer_lock ) return;
|
zip_dest_filename = SaveStateBase::GetFilename( num );
|
||||||
StateCopy_SaveToFile( SaveStateBase::GetFilename( num ) );
|
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
|
||||||
|
Console.Status( "Saving savestate to slot %d...", num );
|
||||||
|
Console.Status( wxsFormat(L"\tfilename: %s", zip_dest_filename.c_str()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateCopy_LoadFromSlot( uint slot )
|
||||||
|
{
|
||||||
|
if( state_buffer_lock.IsLocked() ) return;
|
||||||
|
wxString file( SaveStateBase::GetFilename( slot ) );
|
||||||
|
|
||||||
|
if( !wxFileExists( file ) )
|
||||||
|
{
|
||||||
|
Console.Notice( "Savestate slot %d is empty.", slot );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Status( "Loading savestate from slot %d...", slot );
|
||||||
|
Console.Status( wxsFormat(L"\tfilename: %s", file.c_str()) );
|
||||||
|
|
||||||
|
sCoreThread.Pause();
|
||||||
|
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StateCopy_IsValid()
|
bool StateCopy_IsValid()
|
||||||
|
@ -310,46 +382,19 @@ bool StateCopy_HasPartialState()
|
||||||
|
|
||||||
void StateCopy_FreezeToMem()
|
void StateCopy_FreezeToMem()
|
||||||
{
|
{
|
||||||
if( state_buffer_lock ) return;
|
if( state_buffer_lock.IsLocked() ) return;
|
||||||
|
(new StateThread_Freeze( OnFinished_Restore ))->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateCopy_ThawFromMem()
|
void StateCopy_ThawFromMem()
|
||||||
{
|
{
|
||||||
if( state_buffer_lock ) return;
|
if( state_buffer_lock.IsLocked() ) return;
|
||||||
|
new StateThread_Thaw( OnFinished_Restore );
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateCopy_Clear()
|
void StateCopy_Clear()
|
||||||
{
|
{
|
||||||
if( state_buffer_lock ) return;
|
if( state_buffer_lock.IsLocked() ) return;
|
||||||
state_buffer.Dispose();
|
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;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,9 @@ static const int MainMemorySizeInBytes =
|
||||||
|
|
||||||
void SaveStateBase::FreezeMainMemory()
|
void SaveStateBase::FreezeMainMemory()
|
||||||
{
|
{
|
||||||
|
if( IsLoading() )
|
||||||
|
PreLoadPrep();
|
||||||
|
|
||||||
// First Block - Memory Dumps
|
// First Block - Memory Dumps
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
|
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
|
||||||
|
@ -149,8 +152,8 @@ void SaveStateBase::FreezeMainMemory()
|
||||||
|
|
||||||
void SaveStateBase::FreezeRegisters()
|
void SaveStateBase::FreezeRegisters()
|
||||||
{
|
{
|
||||||
//if( IsLoading() )
|
if( IsLoading() )
|
||||||
// PreLoadPrep();
|
PreLoadPrep();
|
||||||
|
|
||||||
// Second Block - Various CPU Registers and States
|
// Second Block - Various CPU Registers and States
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
|
|
|
@ -202,5 +202,6 @@ extern void StateCopy_ThawFromMem();
|
||||||
extern void StateCopy_SaveToFile( const wxString& file );
|
extern void StateCopy_SaveToFile( const wxString& file );
|
||||||
extern void StateCopy_LoadFromFile( const wxString& file );
|
extern void StateCopy_LoadFromFile( const wxString& file );
|
||||||
extern void StateCopy_SaveToSlot( uint num );
|
extern void StateCopy_SaveToSlot( uint num );
|
||||||
|
extern void StateCopy_LoadFromSlot( uint slot );
|
||||||
extern void StateCopy_Clear();
|
extern void StateCopy_Clear();
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ SysCoreAllocations::SysCoreAllocations()
|
||||||
throw Exception::OutOfMemory(
|
throw Exception::OutOfMemory(
|
||||||
wxsFormat( // Diagnostic (english)
|
wxsFormat( // Diagnostic (english)
|
||||||
L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
|
L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
|
||||||
L"Error Details: " + wxString::FromUTF8( ex.what() )
|
L"Error Details: " + fromUTF8( ex.what() )
|
||||||
),
|
),
|
||||||
|
|
||||||
GetMemoryErrorVM() // translated
|
GetMemoryErrorVM() // translated
|
||||||
|
|
|
@ -55,7 +55,6 @@ void SysSuspendableThread::OnStart()
|
||||||
_parent::OnStart();
|
_parent::OnStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
|
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
|
||||||
// thread; or does nothing if the core is already suspended. Calling this thread from the
|
// thread; or does nothing if the core is already suspended. Calling this thread from the
|
||||||
// Core thread will result in deadlock.
|
// Core thread will result in deadlock.
|
||||||
|
@ -66,6 +65,11 @@ void SysSuspendableThread::OnStart()
|
||||||
// is mostly useful for starting certain non-Emu related gui activities (improves gui
|
// is mostly useful for starting certain non-Emu related gui activities (improves gui
|
||||||
// responsiveness).
|
// responsiveness).
|
||||||
//
|
//
|
||||||
|
// Exceptions:
|
||||||
|
// CancelEvent - thrown if the thread is already in a Paused state. Because actions that
|
||||||
|
// pause emulation typically rely on plugins remaining loaded/active, Suspension must
|
||||||
|
// cansel itself forcefully or risk crashing whatever other action is in progress.
|
||||||
|
//
|
||||||
void SysSuspendableThread::Suspend( bool isBlocking )
|
void SysSuspendableThread::Suspend( bool isBlocking )
|
||||||
{
|
{
|
||||||
if( IsSelf() || !IsRunning() ) return;
|
if( IsSelf() || !IsRunning() ) return;
|
||||||
|
@ -76,11 +80,34 @@ void SysSuspendableThread::Suspend( bool isBlocking )
|
||||||
if( m_ExecMode == ExecMode_Suspended )
|
if( m_ExecMode == ExecMode_Suspended )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if( m_ExecMode == ExecMode_Pausing || m_ExecMode == ExecMode_Paused )
|
||||||
|
throw Exception::CancelEvent( "Another thread is pausing the VM state." );
|
||||||
|
|
||||||
if( m_ExecMode == ExecMode_Running )
|
if( m_ExecMode == ExecMode_Running )
|
||||||
m_ExecMode = ExecMode_Suspending;
|
m_ExecMode = ExecMode_Suspending;
|
||||||
|
|
||||||
pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspended..." );
|
pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspending..." );
|
||||||
}
|
}
|
||||||
|
m_SuspendEvent.Reset();
|
||||||
|
m_sem_event.Post();
|
||||||
|
if( isBlocking ) m_SuspendEvent.WaitGui();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysSuspendableThread::Pause()
|
||||||
|
{
|
||||||
|
if( IsSelf() || !IsRunning() ) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopedLock locker( m_lock_ExecMode );
|
||||||
|
|
||||||
|
if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused) ) return;
|
||||||
|
|
||||||
|
if( m_ExecMode == ExecMode_Running )
|
||||||
|
m_ExecMode = ExecMode_Pausing;
|
||||||
|
|
||||||
|
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
|
||||||
|
}
|
||||||
|
m_SuspendEvent.Reset();
|
||||||
m_sem_event.Post();
|
m_sem_event.Post();
|
||||||
m_SuspendEvent.WaitGui();
|
m_SuspendEvent.WaitGui();
|
||||||
}
|
}
|
||||||
|
@ -111,6 +138,7 @@ void SysSuspendableThread::Resume()
|
||||||
// fall through...
|
// fall through...
|
||||||
|
|
||||||
case ExecMode_Suspending:
|
case ExecMode_Suspending:
|
||||||
|
case ExecMode_Pausing:
|
||||||
// we need to make sure and wait for the emuThread to enter a fully suspended
|
// we need to make sure and wait for the emuThread to enter a fully suspended
|
||||||
// state before continuing...
|
// state before continuing...
|
||||||
|
|
||||||
|
@ -120,8 +148,8 @@ void SysSuspendableThread::Resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pxAssertDev( m_ExecMode == ExecMode_Suspended,
|
pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused),
|
||||||
"SysSuspendableThread is not in a suspended/idle state? wtf!" );
|
"SysSuspendableThread is not in a suspended/paused state? wtf!" );
|
||||||
|
|
||||||
m_ExecMode = ExecMode_Running;
|
m_ExecMode = ExecMode_Running;
|
||||||
m_ResumeProtection = true;
|
m_ResumeProtection = true;
|
||||||
|
@ -148,7 +176,7 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
|
||||||
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
||||||
if( m_ExecMode == ExecMode_Running )
|
if( m_ExecMode == ExecMode_Running )
|
||||||
{
|
{
|
||||||
if( isCancelable ) pthread_testcancel();
|
if( isCancelable ) TestCancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,23 +198,41 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
|
||||||
// Yup, need this a second time. Variable state could have changed while we
|
// Yup, need this a second time. Variable state could have changed while we
|
||||||
// were trying to acquire the lock above.
|
// were trying to acquire the lock above.
|
||||||
if( isCancelable )
|
if( isCancelable )
|
||||||
pthread_testcancel();
|
TestCancel();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
case ExecMode_Pausing:
|
||||||
|
{
|
||||||
|
OnPauseInThread();
|
||||||
|
m_ExecMode = ExecMode_Paused;
|
||||||
|
m_SuspendEvent.Post();
|
||||||
|
}
|
||||||
|
// fallthrough...
|
||||||
|
|
||||||
|
case ExecMode_Paused:
|
||||||
|
m_lock_ExecMode.Unlock();
|
||||||
|
while( m_ExecMode == ExecMode_Paused )
|
||||||
|
m_ResumeEvent.WaitGui();
|
||||||
|
|
||||||
|
OnResumeInThread( false );
|
||||||
|
break;
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
case ExecMode_Suspending:
|
case ExecMode_Suspending:
|
||||||
{
|
{
|
||||||
OnSuspendInThread();
|
OnSuspendInThread();
|
||||||
m_ExecMode = ExecMode_Suspended;
|
m_ExecMode = ExecMode_Suspended;
|
||||||
m_SuspendEvent.Post();
|
m_SuspendEvent.Post();
|
||||||
}
|
}
|
||||||
// fall through...
|
// fallthrough...
|
||||||
|
|
||||||
case ExecMode_Suspended:
|
case ExecMode_Suspended:
|
||||||
m_lock_ExecMode.Unlock();
|
m_lock_ExecMode.Unlock();
|
||||||
while( m_ExecMode == ExecMode_Suspended )
|
while( m_ExecMode == ExecMode_Suspended )
|
||||||
m_ResumeEvent.WaitGui();
|
m_ResumeEvent.WaitGui();
|
||||||
|
|
||||||
OnResumeInThread();
|
OnResumeInThread( true );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
jNO_DEFAULT;
|
jNO_DEFAULT;
|
||||||
|
@ -218,16 +264,6 @@ void SysCoreThread::Start()
|
||||||
_parent::Start();
|
_parent::Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suspends the system without closing plugins or updating GUI status.
|
|
||||||
// Should be used for savestates or other actions which happen very quickly.
|
|
||||||
void SysCoreThread::ShortSuspend()
|
|
||||||
{
|
|
||||||
m_shortSuspend = true;
|
|
||||||
Suspend();
|
|
||||||
m_shortSuspend = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Resumes the core execution state, or does nothing is the core is already running. If
|
// Resumes the core execution state, or does nothing is the core is already running. If
|
||||||
// settings were changed, resets will be performed as needed and emulation state resumed from
|
// settings were changed, resets will be performed as needed and emulation state resumed from
|
||||||
// memory savestates.
|
// memory savestates.
|
||||||
|
@ -348,11 +384,6 @@ void SysCoreThread::CpuExecute()
|
||||||
PCSX2_MEM_PROTECT_END();
|
PCSX2_MEM_PROTECT_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _cet_callback_cleanup( void* handle )
|
|
||||||
{
|
|
||||||
((SysCoreThread*)handle)->OnThreadCleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SysCoreThread::ExecuteTask()
|
void SysCoreThread::ExecuteTask()
|
||||||
{
|
{
|
||||||
tls_coreThread = this;
|
tls_coreThread = this;
|
||||||
|
@ -369,9 +400,10 @@ void SysCoreThread::OnSuspendInThread()
|
||||||
m_plugins.Close();
|
m_plugins.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysCoreThread::OnResumeInThread()
|
void SysCoreThread::OnResumeInThread( bool isSuspended )
|
||||||
{
|
{
|
||||||
m_plugins.Open();
|
if( isSuspended )
|
||||||
|
m_plugins.Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,9 @@ protected:
|
||||||
ExecMode_NoThreadYet,
|
ExecMode_NoThreadYet,
|
||||||
ExecMode_Running,
|
ExecMode_Running,
|
||||||
ExecMode_Suspending,
|
ExecMode_Suspending,
|
||||||
ExecMode_Suspended
|
ExecMode_Suspended,
|
||||||
|
ExecMode_Pausing,
|
||||||
|
ExecMode_Paused,
|
||||||
};
|
};
|
||||||
|
|
||||||
volatile ExecutionMode m_ExecMode;
|
volatile ExecutionMode m_ExecMode;
|
||||||
|
@ -59,6 +61,7 @@ public:
|
||||||
|
|
||||||
virtual void Suspend( bool isBlocking = true );
|
virtual void Suspend( bool isBlocking = true );
|
||||||
virtual void Resume();
|
virtual void Resume();
|
||||||
|
virtual void Pause();
|
||||||
|
|
||||||
virtual void StateCheck( bool isCancelable = true );
|
virtual void StateCheck( bool isCancelable = true );
|
||||||
virtual void OnThreadCleanup();
|
virtual void OnThreadCleanup();
|
||||||
|
@ -78,11 +81,21 @@ protected:
|
||||||
// thread, requesting this thread suspend itself temporarily). After this is called,
|
// thread, requesting this thread suspend itself temporarily). After this is called,
|
||||||
// the thread enters a waiting state on the m_ResumeEvent semaphore.
|
// the thread enters a waiting state on the m_ResumeEvent semaphore.
|
||||||
virtual void OnSuspendInThread()=0;
|
virtual void OnSuspendInThread()=0;
|
||||||
|
|
||||||
|
// Extending classes should implement this, but should not call it. The parent class
|
||||||
|
// handles invocation by the following guidelines: Called *in thread* from StateCheck()
|
||||||
|
// prior to pausing the thread (ie, when Pause() has been called on a separate thread,
|
||||||
|
// requesting this thread pause itself temporarily). After this is called, the thread
|
||||||
|
// enters a waiting state on the m_ResumeEvent semaphore.
|
||||||
|
virtual void OnPauseInThread()=0;
|
||||||
|
|
||||||
// Extending classes should implement this, but should not call it. The parent class
|
// Extending classes should implement this, but should not call it. The parent class
|
||||||
// handles invocation by the following guidelines: Called from StateCheck() after the
|
// handles invocation by the following guidelines: Called from StateCheck() after the
|
||||||
// thread has been suspended and then subsequently resumed.
|
// thread has been suspended and then subsequently resumed.
|
||||||
virtual void OnResumeInThread()=0;
|
// Parameter:
|
||||||
|
// isSuspended - set to TRUE if the thread is returning from a suspended state, or
|
||||||
|
// FALSE if it's returning from a paused state.
|
||||||
|
virtual void OnResumeInThread( bool isSuspended )=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -107,7 +120,6 @@ public:
|
||||||
|
|
||||||
virtual void ApplySettings( const Pcsx2Config& src );
|
virtual void ApplySettings( const Pcsx2Config& src );
|
||||||
virtual void OnThreadCleanup();
|
virtual void OnThreadCleanup();
|
||||||
virtual void ShortSuspend();
|
|
||||||
virtual void OnResumeReady();
|
virtual void OnResumeReady();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -116,6 +128,7 @@ protected:
|
||||||
|
|
||||||
virtual void Start();
|
virtual void Start();
|
||||||
virtual void OnSuspendInThread();
|
virtual void OnSuspendInThread();
|
||||||
virtual void OnResumeInThread();
|
virtual void OnPauseInThread() {}
|
||||||
|
virtual void OnResumeInThread( bool IsSuspended );
|
||||||
virtual void ExecuteTask();
|
virtual void ExecuteTask();
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,12 +27,6 @@
|
||||||
VUmicroCpu CpuVU0; // contains a working copy of the VU0 cpu functions/API
|
VUmicroCpu CpuVU0; // contains a working copy of the VU0 cpu functions/API
|
||||||
VUmicroCpu CpuVU1; // contains a working copy of the VU1 cpu functions/API
|
VUmicroCpu CpuVU1; // contains a working copy of the VU1 cpu functions/API
|
||||||
|
|
||||||
static void DummyExecuteVU1Block(void)
|
|
||||||
{
|
|
||||||
VU0.VI[ REG_VPU_STAT ].UL &= ~0x100;
|
|
||||||
VU1.vifRegs->stat.VEW = 0; // also reset the bit (grandia 3 works)
|
|
||||||
}
|
|
||||||
|
|
||||||
void vuMicroCpuReset()
|
void vuMicroCpuReset()
|
||||||
{
|
{
|
||||||
CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;
|
CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <wx/docview.h>
|
#include <wx/docview.h>
|
||||||
#include <wx/apptrait.h>
|
#include <wx/apptrait.h>
|
||||||
|
|
||||||
|
#include "Utilities/Listeners.h"
|
||||||
|
|
||||||
class IniInterface;
|
class IniInterface;
|
||||||
class MainEmuFrame;
|
class MainEmuFrame;
|
||||||
class GSFrame;
|
class GSFrame;
|
||||||
|
@ -35,6 +37,9 @@ class AppCoreThread;
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
#include "System/SysThreads.h"
|
#include "System/SysThreads.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
|
||||||
|
|
||||||
#define AllowFromMainThreadOnly() \
|
#define AllowFromMainThreadOnly() \
|
||||||
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
||||||
|
|
||||||
|
@ -42,10 +47,10 @@ BEGIN_DECLARE_EVENT_TYPES()
|
||||||
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
|
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
|
||||||
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_SysExecute, -1 )
|
||||||
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
|
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
|
||||||
DECLARE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated, -1 )
|
DECLARE_EVENT_TYPE( pxEVT_AppCoreThreadFinished, -1 )
|
||||||
DECLARE_EVENT_TYPE( pxEVT_FreezeFinished, -1 )
|
DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 )
|
||||||
DECLARE_EVENT_TYPE( pxEVT_ThawFinished, -1 )
|
|
||||||
END_DECLARE_EVENT_TYPES()
|
END_DECLARE_EVENT_TYPES()
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -335,8 +340,6 @@ public:
|
||||||
Pcsx2App();
|
Pcsx2App();
|
||||||
virtual ~Pcsx2App();
|
virtual ~Pcsx2App();
|
||||||
|
|
||||||
void ReloadPlugins();
|
|
||||||
|
|
||||||
void PostPadKey( wxKeyEvent& evt );
|
void PostPadKey( wxKeyEvent& evt );
|
||||||
void PostMenuAction( MenuIdentifiers menu_id ) const;
|
void PostMenuAction( MenuIdentifiers menu_id ) const;
|
||||||
int ThreadedModalDialog( DialogIdentifiers dialogId );
|
int ThreadedModalDialog( DialogIdentifiers dialogId );
|
||||||
|
@ -392,6 +395,18 @@ public:
|
||||||
void DisableDiskLogging() const;
|
void DisableDiskLogging() const;
|
||||||
void OnProgramLogClosed();
|
void OnProgramLogClosed();
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Event Sources!
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CmdEvt_Source m_evtsrc_CorePluginStatus;
|
||||||
|
CmdEvt_Source m_evtsrc_CoreThreadStatus;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CmdEvt_Source& Source_CoreThreadStatus() { return m_evtsrc_CoreThreadStatus; }
|
||||||
|
CmdEvt_Source& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void InitDefaultGlobalAccelerators();
|
void InitDefaultGlobalAccelerators();
|
||||||
void BuildCommandHash();
|
void BuildCommandHash();
|
||||||
|
@ -402,18 +417,18 @@ protected:
|
||||||
|
|
||||||
void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const;
|
void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const;
|
||||||
|
|
||||||
|
void OnSysExecute( wxCommandEvent& evt );
|
||||||
void OnReloadPlugins( wxCommandEvent& evt );
|
void OnReloadPlugins( wxCommandEvent& evt );
|
||||||
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 OnCoreThreadTerminated( wxCommandEvent& evt );
|
||||||
|
|
||||||
void OnFreezeFinished( wxCommandEvent& evt );
|
void OnFreezeThreadFinished( 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
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -496,7 +511,7 @@ DECLARE_APP(Pcsx2App)
|
||||||
extern bool sys_resume_lock;
|
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( FnType_OnThreadComplete* onComplete );
|
||||||
extern void LoadPluginsImmediate();
|
extern void LoadPluginsImmediate();
|
||||||
extern void UnloadPlugins();
|
extern void UnloadPlugins();
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ void AppCoreThread::OnResumeReady()
|
||||||
// the new (lack of) thread status, so this posts a message to the App to do so.
|
// the new (lack of) thread status, so this posts a message to the App to do so.
|
||||||
void AppCoreThread::OnThreadCleanup()
|
void AppCoreThread::OnThreadCleanup()
|
||||||
{
|
{
|
||||||
wxCommandEvent evt( pxEVT_AppCoreThread_Terminated );
|
wxCommandEvent evt( pxEVT_AppCoreThreadFinished );
|
||||||
wxGetApp().AddPendingEvent( evt );
|
wxGetApp().AddPendingEvent( evt );
|
||||||
_parent::OnThreadCleanup();
|
_parent::OnThreadCleanup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,12 +220,12 @@ bool Pcsx2App::OnInit()
|
||||||
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
|
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
|
||||||
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_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
|
||||||
|
|
||||||
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
|
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
|
||||||
|
|
||||||
Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
|
Connect( pxEVT_AppCoreThreadFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
|
||||||
|
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
|
||||||
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 ) );
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ bool Pcsx2App::OnInit()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadPluginsPassive();
|
LoadPluginsPassive( NULL );
|
||||||
}
|
}
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
catch( Exception::StartupAborted& ex )
|
catch( Exception::StartupAborted& ex )
|
||||||
|
|
|
@ -30,10 +30,10 @@ IMPLEMENT_APP(Pcsx2App)
|
||||||
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
|
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_SysExecute );
|
||||||
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
|
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
|
||||||
DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated );
|
DEFINE_EVENT_TYPE( pxEVT_AppCoreThreadFinished );
|
||||||
DEFINE_EVENT_TYPE( pxEVT_FreezeFinished );
|
DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished );
|
||||||
DEFINE_EVENT_TYPE( pxEVT_ThawFinished );
|
|
||||||
|
|
||||||
bool UseAdminMode = false;
|
bool UseAdminMode = false;
|
||||||
wxDirName SettingsFolder;
|
wxDirName SettingsFolder;
|
||||||
|
@ -211,6 +211,18 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
|
||||||
(handler->*func)(event);
|
(handler->*func)(event);
|
||||||
}
|
}
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
catch( Exception::CancelEvent& ex )
|
||||||
|
{
|
||||||
|
Console.Notice( ex.FormatDiagnosticMessage() );
|
||||||
|
}
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
catch( Exception::BadSavedState& ex)
|
||||||
|
{
|
||||||
|
// Saved state load failed.
|
||||||
|
Console.Notice( ex.FormatDiagnosticMessage() );
|
||||||
|
sCoreThread.Resume();
|
||||||
|
}
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
catch( Exception::PluginError& ex )
|
catch( Exception::PluginError& ex )
|
||||||
{
|
{
|
||||||
Console.Error( ex.FormatDiagnosticMessage() );
|
Console.Error( ex.FormatDiagnosticMessage() );
|
||||||
|
@ -376,32 +388,53 @@ void Pcsx2App::OnMainFrameClosed()
|
||||||
m_MainFrame = NULL;
|
m_MainFrame = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// Sys/Core API and Shortcuts (for wxGetApp())
|
// Sys/Core API and Shortcuts (for wxGetApp())
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int _sysexec_cdvdsrc_type = -1;
|
||||||
|
|
||||||
|
static void OnSysExecuteAfterPlugins( const wxCommandEvent& loadevt )
|
||||||
|
{
|
||||||
|
if( !wxGetApp().m_CorePlugins ) return;
|
||||||
|
|
||||||
|
wxCommandEvent execevt( pxEVT_SysExecute );
|
||||||
|
execevt.SetInt( _sysexec_cdvdsrc_type );
|
||||||
|
wxGetApp().AddPendingEvent( execevt );
|
||||||
|
}
|
||||||
|
|
||||||
// Executes the emulator using a saved/existing virtual machine state and currently
|
// Executes the emulator using a saved/existing virtual machine state and currently
|
||||||
// configured CDVD source device.
|
// configured CDVD source device.
|
||||||
void Pcsx2App::SysExecute()
|
void Pcsx2App::SysExecute()
|
||||||
{
|
{
|
||||||
if( sys_resume_lock )
|
if( !m_CorePlugins )
|
||||||
{
|
{
|
||||||
Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
|
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SysReset();
|
wxCommandEvent evt( pxEVT_SysExecute );
|
||||||
LoadPluginsImmediate();
|
evt.SetInt( -1 );
|
||||||
m_CoreThread = new AppCoreThread( *m_CorePlugins );
|
AddPendingEvent( evt );
|
||||||
m_CoreThread->Resume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes the specified cdvd source and optional elf file. This command performs a
|
// Executes the specified cdvd source and optional elf file. This command performs a
|
||||||
// full closure of any existing VM state and starts a fresh VM with the requested
|
// full closure of any existing VM state and starts a fresh VM with the requested
|
||||||
// sources.
|
// sources.
|
||||||
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
|
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
|
||||||
|
{
|
||||||
|
if( !m_CorePlugins )
|
||||||
|
{
|
||||||
|
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxCommandEvent evt( pxEVT_SysExecute );
|
||||||
|
evt.SetInt( (int)cdvdsrc );
|
||||||
|
AddPendingEvent( evt );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
|
||||||
{
|
{
|
||||||
if( sys_resume_lock )
|
if( sys_resume_lock )
|
||||||
{
|
{
|
||||||
|
@ -409,10 +442,13 @@ void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SysReset();
|
// if something unloaded plugins since this messages was queued then it's best to ignore
|
||||||
LoadPluginsImmediate();
|
// it, because apparently too much stuff is going on and the emulation states are wonky.
|
||||||
|
if( !m_CorePlugins ) return;
|
||||||
|
|
||||||
|
if( evt.GetInt() != -1 ) SysReset();
|
||||||
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
|
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
|
||||||
CDVDsys_ChangeSource( cdvdsrc );
|
if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
|
||||||
|
|
||||||
m_CoreThread = new AppCoreThread( *m_CorePlugins );
|
m_CoreThread = new AppCoreThread( *m_CorePlugins );
|
||||||
m_CoreThread->Resume();
|
m_CoreThread->Resume();
|
||||||
|
|
|
@ -109,7 +109,7 @@ void ConsoleTestThread::ExecuteTask()
|
||||||
// worst case scenario (without being entirely unrealistic).
|
// worst case scenario (without being entirely unrealistic).
|
||||||
Console.WriteLn( wxsFormat( L"This is a threaded logging test. Something bad could happen... %d", ++numtrack ) );
|
Console.WriteLn( wxsFormat( L"This is a threaded logging test. Something bad could happen... %d", ++numtrack ) );
|
||||||
Console.Status( wxsFormat( L"Testing high stress loads %s", L"(multi-color)" ) );
|
Console.Status( wxsFormat( L"Testing high stress loads %s", L"(multi-color)" ) );
|
||||||
Sleep( 0 );
|
Yield( 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
|
||||||
m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ),
|
m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ),
|
||||||
wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN )
|
wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN )
|
||||||
{
|
{
|
||||||
static const wxString LabelAuthors = wxString::FromUTF8(
|
static const wxString LabelAuthors = fromUTF8(
|
||||||
"Developers"
|
"Developers"
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"v0.9.6+: Arcum42, Refraction,"
|
"v0.9.6+: Arcum42, Refraction,"
|
||||||
|
@ -69,7 +69,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
|
||||||
"Webmasters: CKemu, Falcon4ever"
|
"Webmasters: CKemu, Falcon4ever"
|
||||||
);
|
);
|
||||||
|
|
||||||
static const wxString LabelGreets = wxString::FromUTF8(
|
static const wxString LabelGreets = fromUTF8(
|
||||||
"Contributors"
|
"Contributors"
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"
|
"Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"
|
||||||
|
|
|
@ -44,7 +44,7 @@ static const int IdealWidth = 500;
|
||||||
template< typename T >
|
template< typename T >
|
||||||
void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid )
|
void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid )
|
||||||
{
|
{
|
||||||
const wxString labelstr( wxString::FromUTF8( label ) );
|
const wxString labelstr( fromUTF8( label ) );
|
||||||
const int curidx = m_labels.Add( labelstr );
|
const int curidx = m_labels.Add( labelstr );
|
||||||
g_ApplyState.SetCurrentPage( curidx );
|
g_ApplyState.SetCurrentPage( curidx );
|
||||||
m_listbook.AddPage( new T( m_listbook, IdealWidth ), wxGetTranslation( labelstr ),
|
m_listbook.AddPage( new T( m_listbook, IdealWidth ), wxGetTranslation( labelstr ),
|
||||||
|
|
|
@ -263,7 +263,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
|
||||||
Console.Notice( wxsFormat(
|
Console.Notice( wxsFormat(
|
||||||
L"Kbd Accelerator '%s' is mapped multiple times.\n"
|
L"Kbd Accelerator '%s' is mapped multiple times.\n"
|
||||||
L"\t'Command %s' is being replaced by '%s'",
|
L"\t'Command %s' is being replaced by '%s'",
|
||||||
acode.ToString().c_str(), wxString::FromUTF8( result->Id ).c_str(), searchfor )
|
acode.ToString().c_str(), fromUTF8( result->Id ).c_str(), searchfor )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
|
||||||
if( result == NULL )
|
if( result == NULL )
|
||||||
{
|
{
|
||||||
Console.Notice( wxsFormat( L"Kbd Accelerator '%s' is mapped to unknown command '%s'",
|
Console.Notice( wxsFormat( L"Kbd Accelerator '%s' is mapped to unknown command '%s'",
|
||||||
acode.ToString().c_str(), wxString::FromUTF8( searchfor ).c_str() )
|
acode.ToString().c_str(), fromUTF8( searchfor ).c_str() )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -310,7 +310,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
|
||||||
{
|
{
|
||||||
// Odd versions: beta / development editions, which feature revision number and compile date.
|
// Odd versions: beta / development editions, which feature revision number and compile date.
|
||||||
wintitle.Printf( _("PCSX2 %d.%d.%d.%d%s (svn) %s"), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo,
|
wintitle.Printf( _("PCSX2 %d.%d.%d.%d%s (svn) %s"), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo,
|
||||||
SVN_REV, SVN_MODS ? L"m" : wxEmptyString, wxString::FromUTF8(__DATE__).c_str() );
|
SVN_REV, SVN_MODS ? L"m" : wxEmptyString, fromUTF8(__DATE__).c_str() );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -559,13 +559,13 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTask()
|
||||||
DevCon.Status( "Plugin Enumeration Thread started..." );
|
DevCon.Status( "Plugin Enumeration Thread started..." );
|
||||||
|
|
||||||
wxGetApp().Ping(); // gives the gui thread some time to refresh
|
wxGetApp().Ping(); // gives the gui thread some time to refresh
|
||||||
Sleep( 3 );
|
Yield( 3 );
|
||||||
|
|
||||||
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
|
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
|
||||||
{
|
{
|
||||||
DoNextPlugin( curidx );
|
DoNextPlugin( curidx );
|
||||||
if( (curidx & 3) == 3 ) wxGetApp().Ping(); // gives the gui thread some time to refresh
|
if( (curidx & 3) == 3 ) wxGetApp().Ping(); // gives the gui thread some time to refresh
|
||||||
pthread_testcancel();
|
TestCancel();
|
||||||
//Sleep(150); // uncomment this to slow down the selector, for debugging threading.
|
//Sleep(150); // uncomment this to slow down the selector, for debugging threading.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
using namespace Threading;
|
using namespace Threading;
|
||||||
|
|
||||||
|
static FnType_OnThreadComplete* Callback_PluginsLoadComplete = NULL;
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// LoadPluginsTask
|
// LoadPluginsTask
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -37,76 +39,52 @@ using namespace Threading;
|
||||||
//
|
//
|
||||||
class LoadPluginsTask : public PersistentThread
|
class LoadPluginsTask : public PersistentThread
|
||||||
{
|
{
|
||||||
|
typedef PersistentThread _parent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Exception::PluginError* Ex_PluginError;
|
|
||||||
Exception::RuntimeError* Ex_RuntimeError;
|
|
||||||
PluginManager* Result;
|
PluginManager* Result;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxString m_folders[PluginId_Count];
|
wxString m_folders[PluginId_Count];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) :
|
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : Result( NULL )
|
||||||
Ex_PluginError( NULL )
|
|
||||||
, Ex_RuntimeError( NULL )
|
|
||||||
, Result( NULL )
|
|
||||||
{
|
{
|
||||||
for(int i=0; i<PluginId_Count; ++i )
|
for(int i=0; i<PluginId_Count; ++i )
|
||||||
m_folders[i] = folders[i];
|
m_folders[i] = folders[i];
|
||||||
|
|
||||||
Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~LoadPluginsTask() throw();
|
virtual ~LoadPluginsTask() throw();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnStart() {}
|
void OnStart() {}
|
||||||
void OnThreadCleanup() {}
|
void OnThreadCleanup();
|
||||||
void ExecuteTask();
|
void ExecuteTask();
|
||||||
};
|
};
|
||||||
|
|
||||||
static ScopedPtr<LoadPluginsTask> _loadTask;
|
|
||||||
|
|
||||||
LoadPluginsTask::~LoadPluginsTask() throw()
|
LoadPluginsTask::~LoadPluginsTask() throw()
|
||||||
{
|
{
|
||||||
if( _loadTask )
|
|
||||||
_loadTask.DetachPtr(); // avoids recursive deletion
|
|
||||||
|
|
||||||
PersistentThread::Cancel();
|
PersistentThread::Cancel();
|
||||||
_loadTask = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadPluginsTask::ExecuteTask()
|
void LoadPluginsTask::ExecuteTask()
|
||||||
{
|
{
|
||||||
wxGetApp().Ping();
|
wxGetApp().Ping();
|
||||||
Sleep(3);
|
Yield(3);
|
||||||
|
|
||||||
|
// This is for testing of the error handler... uncomment for fun?
|
||||||
|
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
|
||||||
|
|
||||||
|
Result = PluginManager_Create( m_folders );
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadPluginsTask::OnThreadCleanup()
|
||||||
|
{
|
||||||
wxCommandEvent evt( pxEVT_LoadPluginsComplete );
|
wxCommandEvent evt( pxEVT_LoadPluginsComplete );
|
||||||
evt.SetClientData( this );
|
evt.SetClientData( this );
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// This is for testing of the error handler... uncomment for fun?
|
|
||||||
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
|
|
||||||
|
|
||||||
Result = PluginManager_Create( m_folders );
|
|
||||||
}
|
|
||||||
catch( Exception::PluginError& ex )
|
|
||||||
{
|
|
||||||
Ex_PluginError = new Exception::PluginError( ex );
|
|
||||||
}
|
|
||||||
catch( Exception::RuntimeError& innerEx )
|
|
||||||
{
|
|
||||||
// Runtime errors are typically recoverable, so handle them here
|
|
||||||
// and prep them for re-throw on the main thread.
|
|
||||||
Ex_RuntimeError = new Exception::RuntimeError(
|
|
||||||
L"A runtime error occurred on the LoadPlugins thread.\n" + innerEx.FormatDiagnosticMessage(),
|
|
||||||
innerEx.FormatDisplayMessage()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// anything else leave unhandled so that the debugger catches it!
|
|
||||||
|
|
||||||
wxGetApp().AddPendingEvent( evt );
|
wxGetApp().AddPendingEvent( evt );
|
||||||
|
|
||||||
|
_parent::OnThreadCleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -132,30 +110,23 @@ int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
|
||||||
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
|
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
|
||||||
|
{
|
||||||
|
const PluginInfo* pi = tbl_PluginInfo; do
|
||||||
|
{
|
||||||
|
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
|
||||||
|
|
||||||
|
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
|
||||||
|
passins[pi->id] = g_Conf->FullpathTo( pi->id );
|
||||||
|
} while( ++pi, pi->shortname != NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
// boolean lock modified from the main thread only...
|
||||||
|
static bool plugin_load_lock = false;
|
||||||
|
|
||||||
void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt )
|
void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt )
|
||||||
{
|
{
|
||||||
ReloadPlugins();
|
if( plugin_load_lock ) return;
|
||||||
}
|
|
||||||
|
|
||||||
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
|
|
||||||
{
|
|
||||||
// scoped ptr ensures the thread object is cleaned up even on exception:
|
|
||||||
ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
|
|
||||||
m_CorePlugins = killTask->Result;
|
|
||||||
|
|
||||||
if( !m_CorePlugins )
|
|
||||||
{
|
|
||||||
if( killTask->Ex_PluginError != NULL )
|
|
||||||
throw *killTask->Ex_PluginError;
|
|
||||||
if( killTask->Ex_RuntimeError != NULL )
|
|
||||||
throw *killTask->Ex_RuntimeError; // Re-Throws generic threaded errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pcsx2App::ReloadPlugins()
|
|
||||||
{
|
|
||||||
if( _loadTask ) return;
|
|
||||||
|
|
||||||
m_CoreThread = NULL;
|
m_CoreThread = NULL;
|
||||||
m_CorePlugins = NULL;
|
m_CorePlugins = NULL;
|
||||||
|
|
||||||
|
@ -169,42 +140,79 @@ 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.Delete() = new LoadPluginsTask( passins );
|
(new LoadPluginsTask( passins ))->Start();
|
||||||
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
|
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
|
||||||
|
|
||||||
|
plugin_load_lock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: If the ClientData paremeter of wxCommandEvt is NULL, this message simply dispatches
|
||||||
|
// the plugged in listeners.
|
||||||
|
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
|
||||||
|
{
|
||||||
|
plugin_load_lock = false;
|
||||||
|
|
||||||
|
FnType_OnThreadComplete* fn_tmp = Callback_PluginsLoadComplete;
|
||||||
|
|
||||||
|
if( evt.GetClientData() != NULL )
|
||||||
|
{
|
||||||
|
if( !pxAssertDev( !m_CorePlugins, "LoadPlugins thread just finished, but CorePlugins state != NULL (odd!)." ) )
|
||||||
|
m_CorePlugins = NULL;
|
||||||
|
|
||||||
|
// scoped ptr ensures the thread object is cleaned up even on exception:
|
||||||
|
ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
|
||||||
|
killTask->RethrowException();
|
||||||
|
m_CorePlugins = killTask->Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( fn_tmp != NULL ) fn_tmp( evt );
|
||||||
|
|
||||||
|
//m_evtsrc_PluginLoadFinished.Dispatch( evt );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
|
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
|
||||||
// which is started on a pending event, so don't expect them to be ready "right now."
|
// which is started on a pending event, so don't expect them to be ready "right now."
|
||||||
// If plugins are already loaded then no action is performed.
|
// If plugins are already loaded, onComplete is invoked, and the function returns with no
|
||||||
void LoadPluginsPassive()
|
// other actions performed.
|
||||||
|
void LoadPluginsPassive( FnType_OnThreadComplete* onComplete )
|
||||||
{
|
{
|
||||||
if( g_plugins ) return;
|
// Plugins already loaded?
|
||||||
|
if( wxGetApp().m_CorePlugins )
|
||||||
|
{
|
||||||
|
if( onComplete ) onComplete( wxCommandEvent( pxEVT_LoadPluginsComplete ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( onComplete )
|
||||||
|
Callback_PluginsLoadComplete = onComplete;
|
||||||
|
|
||||||
wxCommandEvent evt( pxEVT_ReloadPlugins );
|
wxCommandEvent evt( pxEVT_ReloadPlugins );
|
||||||
wxGetApp().AddPendingEvent( evt );
|
wxGetApp().AddPendingEvent( evt );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocks until plugins have been successfully loaded, or throws an exception if
|
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
|
||||||
// the user cancels the loading procedure after error. If plugins are already loaded
|
// automatically to prevent race conditions (it depends on plugins).
|
||||||
// then no action is performed.
|
//
|
||||||
|
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
|
||||||
|
// to handle them.
|
||||||
|
//
|
||||||
|
// Note that this is not recommended for most situations, but coding improper passive loads
|
||||||
|
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
|
||||||
|
//
|
||||||
void LoadPluginsImmediate()
|
void LoadPluginsImmediate()
|
||||||
{
|
{
|
||||||
AllowFromMainThreadOnly();
|
if( g_plugins != NULL ) return;
|
||||||
if( g_plugins ) return;
|
|
||||||
|
|
||||||
static int _reentrant = 0;
|
wxGetApp().m_CoreThread = NULL;
|
||||||
RecursionGuard guard( _reentrant );
|
|
||||||
|
wxString passins[PluginId_Count];
|
||||||
|
ConvertPluginFilenames( passins );
|
||||||
|
wxGetApp().m_CorePlugins = PluginManager_Create( passins );
|
||||||
|
|
||||||
pxAssertMsg( !guard.IsReentrant(), "Recrsive calls to this function are prohibited." );
|
|
||||||
wxGetApp().ReloadPlugins();
|
|
||||||
while( _loadTask )
|
|
||||||
{
|
|
||||||
Sleep( 10 );
|
|
||||||
wxGetApp().ProcessPendingEvents();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnloadPlugins()
|
void UnloadPlugins()
|
||||||
{
|
{
|
||||||
|
wxGetApp().m_CoreThread = NULL;
|
||||||
wxGetApp().m_CorePlugins = NULL;
|
wxGetApp().m_CorePlugins = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,59 +23,10 @@
|
||||||
|
|
||||||
StartupParams g_Startup;
|
StartupParams g_Startup;
|
||||||
|
|
||||||
// returns true if the new state was loaded, or false if nothing happened.
|
|
||||||
void States_Load( const wxString& file )
|
|
||||||
{
|
|
||||||
sCoreThread.ShortSuspend();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StateCopy_LoadFromFile( file );
|
|
||||||
//SysLoadState( file );
|
|
||||||
//SysStatus( wxsFormat( _("Loaded State %s"), wxFileName( file ).GetFullName().c_str() ) );
|
|
||||||
}
|
|
||||||
catch( Exception::BaseException& )
|
|
||||||
{
|
|
||||||
// VM state is probably ruined. We'll need to recover from the in-memory backup.
|
|
||||||
//StateRecovery::Recover();
|
|
||||||
}
|
|
||||||
|
|
||||||
sApp.SysExecute();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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( !SysHasValidState() )
|
StateCopy_SaveToFile( file );
|
||||||
{
|
|
||||||
Msgbox::Alert( _("You need to start emulation first before you can save it's state.") );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Console.Status( wxsFormat( L"Saving savestate to file: %s", file.c_str() ) );
|
|
||||||
StateCopy_SaveToFile( file );
|
|
||||||
SysStatus( wxsFormat( _("State saved to file: %s"), wxFileName( file ).GetFullName().c_str() ) );
|
|
||||||
}
|
|
||||||
catch( Exception::BaseException& ex )
|
|
||||||
{
|
|
||||||
// TODO: Implement a "pause the action and issue a popup" thing here.
|
|
||||||
// *OR* some kind of GS overlay... [for now we use the console]
|
|
||||||
|
|
||||||
// Translation Tip: "Your emulation state has not been saved!"
|
|
||||||
|
|
||||||
/*Msgbox::Alert(
|
|
||||||
wxsFormat( _("Error saving state to file: %s"), file.c_str() ) +
|
|
||||||
L"\n\n" + _("Error details:") + ex.DisplayMessage()
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Console.Error( wxsFormat(
|
|
||||||
L"An error occurred while trying to save to file %s\n", file.c_str() ) +
|
|
||||||
L"Your emulation state has not been saved!\n"
|
|
||||||
L"\nError: " + ex.FormatDiagnosticMessage()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -95,22 +46,12 @@ bool States_isSlotUsed(int num)
|
||||||
|
|
||||||
void States_FreezeCurrentSlot()
|
void States_FreezeCurrentSlot()
|
||||||
{
|
{
|
||||||
Console.Status( "Saving savestate to slot %d...", StatesC );
|
StateCopy_SaveToSlot( StatesC );
|
||||||
States_Save( SaveStateBase::GetFilename( StatesC ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void States_DefrostCurrentSlot()
|
void States_DefrostCurrentSlot()
|
||||||
{
|
{
|
||||||
wxString file( SaveStateBase::GetFilename( StatesC ) );
|
StateCopy_LoadFromSlot( StatesC );
|
||||||
|
|
||||||
if( !wxFileExists( file ) )
|
|
||||||
{
|
|
||||||
Console.Notice( "Savestate slot %d is empty.", StatesC );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.Status( "Loading savestate from slot %d...", StatesC );
|
|
||||||
States_Load( file );
|
|
||||||
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
|
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1804,6 +1804,10 @@
|
||||||
RelativePath="..\..\gui\IniInterface.cpp"
|
RelativePath="..\..\gui\IniInterface.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\..\common\include\Utilities\Listeners.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\gui\MainFrame.cpp"
|
RelativePath="..\..\gui\MainFrame.cpp"
|
||||||
>
|
>
|
||||||
|
|
|
@ -142,6 +142,9 @@ static __forceinline bool ReadPipe(HANDLE h_Pipe, ConsoleColors color )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// WinPipeThread
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
class WinPipeThread : public PersistentThread
|
class WinPipeThread : public PersistentThread
|
||||||
{
|
{
|
||||||
typedef PersistentThread _parent;
|
typedef PersistentThread _parent;
|
||||||
|
@ -173,8 +176,7 @@ protected:
|
||||||
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
|
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
|
||||||
while( true )
|
while( true )
|
||||||
{
|
{
|
||||||
Sleep( 100 );
|
Yield( 100 );
|
||||||
pthread_testcancel();
|
|
||||||
ReadPipe( m_outpipe, m_color );
|
ReadPipe( m_outpipe, m_color );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,6 +191,9 @@ protected:
|
||||||
void OnThreadCleanup() { }
|
void OnThreadCleanup() { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// WinPipeRedirection
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
class WinPipeRedirection : public PipeRedirectionBase
|
class WinPipeRedirection : public PipeRedirectionBase
|
||||||
{
|
{
|
||||||
DeclareNoncopyableObject( WinPipeRedirection );
|
DeclareNoncopyableObject( WinPipeRedirection );
|
||||||
|
@ -221,7 +226,8 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
|
||||||
DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
|
DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
|
||||||
|
|
||||||
CreatePipe( m_pipe, m_file );
|
CreatePipe( m_pipe, m_file );
|
||||||
SetStdHandle( stdhandle, m_file );
|
if( 0 == SetStdHandle( stdhandle, m_file ) )
|
||||||
|
throw Exception::Win32Error( "SetStdHandle failed." );
|
||||||
|
|
||||||
// In some cases GetStdHandle can fail, even when the one we just assigned above is valid.
|
// In some cases GetStdHandle can fail, even when the one we just assigned above is valid.
|
||||||
HANDLE newhandle = GetStdHandle(stdhandle);
|
HANDLE newhandle = GetStdHandle(stdhandle);
|
||||||
|
@ -231,11 +237,11 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
|
||||||
if( newhandle == NULL )
|
if( newhandle == NULL )
|
||||||
throw Exception::RuntimeError( "GetStdHandle returned NULL." ); // not a Win32error (no error code)
|
throw Exception::RuntimeError( "GetStdHandle returned NULL." ); // not a Win32error (no error code)
|
||||||
|
|
||||||
m_crtFile = _open_osfhandle( (intptr_t)newhandle, _O_TEXT );
|
m_crtFile = _open_osfhandle( (intptr_t)newhandle, _O_TEXT );
|
||||||
if( m_crtFile == -1 )
|
if( m_crtFile == -1 )
|
||||||
throw Exception::RuntimeError( "_open_osfhandle returned -1." );
|
throw Exception::RuntimeError( "_open_osfhandle returned -1." );
|
||||||
|
|
||||||
m_fp = _fdopen( m_crtFile, "w" );
|
m_fp = _fdopen( m_crtFile, "w" );
|
||||||
if( m_fp == NULL )
|
if( m_fp == NULL )
|
||||||
throw Exception::RuntimeError( "_fdopen returned NULL." );
|
throw Exception::RuntimeError( "_fdopen returned NULL." );
|
||||||
|
|
||||||
|
@ -269,10 +275,22 @@ void WinPipeRedirection::Cleanup() throw()
|
||||||
{
|
{
|
||||||
fclose( m_fp );
|
fclose( m_fp );
|
||||||
m_fp = NULL;
|
m_fp = NULL;
|
||||||
|
|
||||||
|
m_crtFile = -1; // crtFile is closed implicitly when closing m_fp
|
||||||
|
m_file = INVALID_HANDLE_VALUE; // m_file is closed implicitly when closing crtFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_crtFile != -1 )
|
||||||
|
{
|
||||||
|
_close( m_crtFile );
|
||||||
|
m_crtFile = -1; // m_file is closed implicitly when closing crtFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// crtFile is closed implicitly when closing m_fp
|
if( m_file != INVALID_HANDLE_VALUE )
|
||||||
// m_file is closed implicitly when closing crtFile
|
{
|
||||||
|
CloseHandle( m_pipe );
|
||||||
|
m_file = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
if( m_pipe != INVALID_HANDLE_VALUE )
|
if( m_pipe != INVALID_HANDLE_VALUE )
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,6 @@ int mVUdebugNow = 0;
|
||||||
|
|
||||||
#ifdef DEBUG_COMPARE
|
#ifdef DEBUG_COMPARE
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
static int runAmount = 0;
|
static int runAmount = 0;
|
||||||
|
|
||||||
void VUtestPause() {
|
void VUtestPause() {
|
||||||
|
@ -70,8 +69,9 @@ void VUtestPause() {
|
||||||
SysPrintf("VU Mem CRC = 0x%08x\n", j);
|
SysPrintf("VU Mem CRC = 0x%08x\n", j);
|
||||||
SysPrintf("EndPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
|
SysPrintf("EndPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
|
||||||
|
|
||||||
|
// ... wtf?? --air
|
||||||
for (int i = 0; i < 10000000; i++) {
|
for (int i = 0; i < 10000000; i++) {
|
||||||
Sleep(1000);
|
Threading::Sleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -84,9 +84,6 @@ extern u32 vudump;
|
||||||
|
|
||||||
#ifdef DEBUG_COMPARE2
|
#ifdef DEBUG_COMPARE2
|
||||||
|
|
||||||
#ifndef DEBUG_COMPARE
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__aligned16 u8 backVUregs[sizeof(VURegs)];
|
__aligned16 u8 backVUregs[sizeof(VURegs)];
|
||||||
__aligned16 u8 cmpVUregs [sizeof(VURegs)];
|
__aligned16 u8 cmpVUregs [sizeof(VURegs)];
|
||||||
|
|
|
@ -238,20 +238,6 @@ static __forceinline s32 __fastcall GetNextDataBuffered( V_Core& thiscore, uint
|
||||||
g_counter_cache_misses++;
|
g_counter_cache_misses++;
|
||||||
}
|
}
|
||||||
|
|
||||||
s16* sbuffer = cacheLine.Sampledata;
|
|
||||||
|
|
||||||
//if( vc.LoopFlags & XAFLAG_LOOP )
|
|
||||||
// vc.Prev1 = vc.Prev2 = 0;
|
|
||||||
|
|
||||||
// saturated decoder
|
|
||||||
//XA_decode_block( sbuffer, memptr, vc.Prev1, vc.Prev2 );
|
|
||||||
|
|
||||||
// [Air]: Testing use of a new unsaturated decoder. (benchmark needed)
|
|
||||||
// Chances are the saturation isn't needed, but for a very few exception games.
|
|
||||||
// This is definitely faster than the above version, but is it by enough to
|
|
||||||
// merit possible lower compatibility? Especially now that games that make
|
|
||||||
// heavy use of the SPU2 via music or sfx will mostly use the cache anyway.
|
|
||||||
|
|
||||||
XA_decode_block_unsaturated( vc.SBuffer, memptr, vc.Prev1, vc.Prev2 );
|
XA_decode_block_unsaturated( vc.SBuffer, memptr, vc.Prev1, vc.Prev2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,6 @@ StereoOut32 V_Core::ReadInput_HiFi()
|
||||||
AdmaInProgress = 0;
|
AdmaInProgress = 0;
|
||||||
if(InputDataLeft >= 0x200)
|
if(InputDataLeft >= 0x200)
|
||||||
{
|
{
|
||||||
u8 k = (InputDataLeft >= InputDataProgress);
|
|
||||||
|
|
||||||
#ifdef PCM24_S1_INTERLEAVE
|
#ifdef PCM24_S1_INTERLEAVE
|
||||||
AutoDMAReadBuffer(1);
|
AutoDMAReadBuffer(1);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -837,7 +837,7 @@ static void __fastcall RegWrite_Core( u16 value )
|
||||||
{
|
{
|
||||||
bool irqe = thiscore.IRQEnable;
|
bool irqe = thiscore.IRQEnable;
|
||||||
int bit0 = thiscore.AttrBit0;
|
int bit0 = thiscore.AttrBit0;
|
||||||
int bit4 = thiscore.AttrBit4;
|
//int bit4 = thiscore.AttrBit4;
|
||||||
|
|
||||||
if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset
|
if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue