From eda5d1d834768bb109663db01865c696afcd4b0e Mon Sep 17 00:00:00 2001
From: "Jake.Stine" <Jake.Stine@96395faa-99c1-11dd-bbfe-3dabce05a288>
Date: Wed, 7 Oct 2009 19:20:11 +0000
Subject: [PATCH] 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
---
 common/build/Utilities/utilities.vcproj     |   4 +
 common/include/PluginCallbacks.h            |   2 +-
 common/include/Utilities/Assertions.h       |  22 +-
 common/include/Utilities/Exceptions.h       |  51 ++-
 common/include/Utilities/Listeners.h        | 204 +++++++++++
 common/include/Utilities/RedtapeWindows.h   |   5 +
 common/include/Utilities/Threading.h        |  95 +++--
 common/include/Utilities/win_memzero.h      |   4 +
 common/src/Utilities/Console.cpp            |   4 +-
 common/src/Utilities/Exceptions.cpp         |  16 +-
 common/src/Utilities/ThreadTools.cpp        | 138 ++++---
 common/src/Utilities/Windows/WinThreads.cpp |   1 -
 common/src/x86emitter/cpudetect.cpp         |   6 +-
 common/vsprops/BaseProperties.vsprops       |   3 +-
 common/vsprops/CommonLibrary.vsprops        |   1 +
 pcsx2/CDVD/CDVDaccess.cpp                   |   2 +-
 pcsx2/CDVD/IsoFileTools.cpp                 |   3 +-
 pcsx2/GS.h                                  |   4 +-
 pcsx2/Gif.h                                 |   4 +-
 pcsx2/HostGui.h                             |   1 -
 pcsx2/MTGS.cpp                              |   8 +-
 pcsx2/PluginManager.cpp                     |   6 +-
 pcsx2/Plugins.h                             |  18 +-
 pcsx2/R5900Exceptions.h                     |   2 +-
 pcsx2/RecoverySystem.cpp                    | 375 +++++++++++---------
 pcsx2/SaveState.cpp                         |   7 +-
 pcsx2/SaveState.h                           |   1 +
 pcsx2/System.cpp                            |   2 +-
 pcsx2/System/SysThreads.cpp                 |  84 +++--
 pcsx2/System/SysThreads.h                   |  21 +-
 pcsx2/VUmicroMem.cpp                        |   6 -
 pcsx2/gui/App.h                             |  33 +-
 pcsx2/gui/AppCoreThread.cpp                 |   2 +-
 pcsx2/gui/AppInit.cpp                       |  10 +-
 pcsx2/gui/AppMain.cpp                       |  64 +++-
 pcsx2/gui/ConsoleLogger.cpp                 |   2 +-
 pcsx2/gui/Dialogs/AboutBoxDialog.cpp        |   4 +-
 pcsx2/gui/Dialogs/ConfigurationDialog.cpp   |   2 +-
 pcsx2/gui/GlobalCommands.cpp                |   4 +-
 pcsx2/gui/MainFrame.cpp                     |   2 +-
 pcsx2/gui/Panels/PluginSelectorPanel.cpp    |   4 +-
 pcsx2/gui/Plugins.cpp                       | 166 ++++-----
 pcsx2/gui/Saveslots.cpp                     |  65 +---
 pcsx2/windows/VCprojects/pcsx2_2008.vcproj  |   4 +
 pcsx2/windows/WinConsolePipe.cpp            |  32 +-
 pcsx2/x86/iVU1micro.cpp                     |   7 +-
 plugins/spu2-x/src/Mixer.cpp                |  14 -
 plugins/spu2-x/src/ReadInput.cpp            |   2 -
 plugins/spu2-x/src/spu2sys.cpp              |   2 +-
 49 files changed, 986 insertions(+), 533 deletions(-)
 create mode 100644 common/include/Utilities/Listeners.h

diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj
index 5aba44fe5..3fcfaea46 100644
--- a/common/build/Utilities/utilities.vcproj
+++ b/common/build/Utilities/utilities.vcproj
@@ -429,6 +429,10 @@
 				RelativePath="..\..\include\intrin_x86.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\include\Utilities\Listeners.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\include\Utilities\lnx_memzero.h"
 				>
diff --git a/common/include/PluginCallbacks.h b/common/include/PluginCallbacks.h
index 7f4a0d24e..f6e2e85af 100644
--- a/common/include/PluginCallbacks.h
+++ b/common/include/PluginCallbacks.h
@@ -191,7 +191,7 @@ typedef struct _PS2E_SessionInfo
 	// 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
 	// will be a zero length string).
-	const char Serial[16];
+	char Serial[16];
 
 } PS2E_SessionInfo;
 
diff --git a/common/include/Utilities/Assertions.h b/common/include/Utilities/Assertions.h
index 4abe4dd93..d09439651 100644
--- a/common/include/Utilities/Assertions.h
+++ b/common/include/Utilities/Assertions.h
@@ -45,7 +45,7 @@
 #if defined(PCSX2_DEBUG)
 
 #	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)
 
@@ -57,26 +57,28 @@
 	// Devel builds use __assume for standard assertions and call pxOnAssertDevel
 	// 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)) || \
-		(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)
 
 #else
 
-	// Release Builds just use __assume as an optimization, and always return 'true'
-	// indicating the assertion check succeeded (no actual check is performed).
+	// Release Builds just use __assume as an optimization, and return the conditional
+	// as a result (if .
 
-#	define pxAssertMsg(cond, msg)	(__assume(cond), true)
-#	define pxAssertDev(cond, msg)	(__assume(cond), true)
-#	define pxFail(msg)				(__assume(false), true)
-#	define pxFailDev(msg)			(__assume(false), true)
+#	define pxAssertMsg(cond, msg)	(__assume(cond), likely(cond))
+#	define pxAssertDev(cond, msg)	(__assume(cond), likely(cond))
+#	define pxFail(msg)				(__assume(false), false)
+#	define pxFailDev(msg)			(__assume(false), false)
 
 #endif
 
+__cold
+
 #define pxAssert(cond)				pxAssertMsg(cond, (wxChar*)NULL)
 
 extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg);
diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h
index 1762e1fe9..0790ea614 100644
--- a/common/include/Utilities/Exceptions.h
+++ b/common/include/Utilities/Exceptions.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include "Dependencies.h"
+#include "StringHelpers.h"
 
 // --------------------------------------------------------------------------------------
 //  DESTRUCTOR_CATCHALL - safe destructor helper
@@ -133,7 +134,7 @@ namespace Exception
 //
 #define DEFINE_EXCEPTION_COPYTORS( classname ) \
 	virtual ~classname() throw() {} \
-	virtual void Rethrow() const { throw classname( *this ); } \
+	virtual void Rethrow() const { throw *this; } \
 	virtual BaseException* Clone() const { return new classname( *this ); }
 
 #define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
@@ -149,11 +150,13 @@ namespace Exception
 	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
 	{
+	public:
+		bool	IsSilent;
 	public:
 		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.") )
 	};
 
-	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:
 		wxString ObjectName;
@@ -175,17 +209,16 @@ namespace Exception
 
 		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...
 		}
-		
 
 		virtual wxString FormatDisplayMessage() const;
 		virtual wxString FormatDiagnosticMessage() const;
 	};
 
 	// ---------------------------------------------------------------------------------------
-	// OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
+	//  OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
 	// ---------------------------------------------------------------------------------------
 
 	class OutOfMemory : public virtual RuntimeError
@@ -282,17 +315,17 @@ namespace Exception
 	explicit classname( const char* objname, const char* msg=defmsg ) \
 	{ \
 		BaseException::InitBaseEx( msg ); \
-		StreamName = wxString::FromUTF8( objname ); \
+		StreamName = fromUTF8( objname ); \
 	} \
 	explicit classname( const char* objname, const wxString& msg_eng, const wxString& msg_xlt ) \
 	{ \
 		BaseException::InitBaseEx( msg_eng, msg_xlt ); \
-		StreamName = wxString::FromUTF8( objname ); \
+		StreamName = fromUTF8( objname ); \
 	} \
 	explicit classname( const char* objname, const wxString& msg_eng ) \
 	{ \
 		BaseException::InitBaseEx( msg_eng, msg_eng ); \
-		StreamName = wxString::FromUTF8( objname ); \
+		StreamName = fromUTF8( objname ); \
 	} \
 	explicit classname( const wxString& objname, const wxString& msg_eng ) \
 	{ \
diff --git a/common/include/Utilities/Listeners.h b/common/include/Utilities/Listeners.h
new file mode 100644
index 000000000..e1ee2b4ed
--- /dev/null
+++ b/common/include/Utilities/Listeners.h
@@ -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 ) );
+}
+
diff --git a/common/include/Utilities/RedtapeWindows.h b/common/include/Utilities/RedtapeWindows.h
index 2551a1a1c..aa3cb1b86 100644
--- a/common/include/Utilities/RedtapeWindows.h
+++ b/common/include/Utilities/RedtapeWindows.h
@@ -15,6 +15,10 @@
 
 #pragma once
 
+#ifdef __WXMSW__
+#	include <wx/msw/wrapwin.h>
+#else
+
 //////////////////////////////////////////////////////////////////////////////////////////
 // Windows Redtape!  No windows.h should be included without it!
 //
@@ -40,3 +44,4 @@
 #undef max
 
 #endif
+#endif
diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h
index 08e3473cd..59c67314c 100644
--- a/common/include/Utilities/Threading.h
+++ b/common/include/Utilities/Threading.h
@@ -15,25 +15,14 @@
  
 #pragma once
 
+#include <semaphore.h>
 #include <errno.h> // EBUSY
 #include <pthread.h>
-#include <semaphore.h>
 
 #include "Pcsx2Defs.h"
 #include "ScopedPtr.h"
 
-namespace Exception
-{
-	//////////////////////////////////////////////////////////////////////////////////////////
-	// 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
-	{
-	};
-}
-
+#undef Yield		// release th burden of windows.h global namespace spam.
 class wxTimeSpan;
 
 namespace Threading
@@ -57,6 +46,38 @@ namespace Threading
 	};
 #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
 	{
 		sem_t sema;
@@ -166,7 +187,7 @@ namespace Threading
 		pthread_t	m_thread;
 		Semaphore	m_sem_event;		// general wait event that's needed by most threads.
 		Semaphore	m_sem_finished;		// used for canceling and closing threads in a deadlock-safe manner
-		MutexLock	m_lock_start;		// used to lock the Start() code from starting simutaneous threads accidentally.
+		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_running;		// set true by Start(), and set false by Cancel(), Block(), etc.
@@ -190,27 +211,49 @@ namespace Threading
 		bool IsSelf() const;
 		wxString GetName() const;
 
-		void _ThreadCleanup();
-
 	protected:
-
 		// Extending classes should always implement your own OnStart(), which is called by
 		// Start() once necessary locks have been obtained.  Do not override Start() directly
 		// unless you're really sure that's what you need to do. ;)
 		virtual void OnStart()=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!
 		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 );
 	};
 	
 	//////////////////////////////////////////////////////////////////////////////////////////
diff --git a/common/include/Utilities/win_memzero.h b/common/include/Utilities/win_memzero.h
index 21dcc274a..3c26316b7 100644
--- a/common/include/Utilities/win_memzero.h
+++ b/common/include/Utilities/win_memzero.h
@@ -15,6 +15,10 @@
 
 #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.
 // For dynamic length clears, use the C-compiler provided memset instead.
 
diff --git a/common/src/Utilities/Console.cpp b/common/src/Utilities/Console.cpp
index 7b8c6fd5a..0a6db799d 100644
--- a/common/src/Utilities/Console.cpp
+++ b/common/src/Utilities/Console.cpp
@@ -227,14 +227,14 @@ void IConsoleWriter::_Write( const char* fmt, va_list args ) const
 {
 	std::string m_format_buffer;
 	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
 {
 	std::string m_format_buffer;
 	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
diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp
index c686c3c70..359097f66 100644
--- a/common/src/Utilities/Exceptions.cpp
+++ b/common/src/Utilities/Exceptions.cpp
@@ -116,11 +116,23 @@ wxString Exception::BaseException::FormatDiagnosticMessage() const
 	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
 {
 	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_stacktrace;
 }
@@ -128,7 +140,7 @@ wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
 wxString Exception::ObjectIsNull::FormatDisplayMessage() const
 {
 	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()
 	);
 }
diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp
index b6acb7b29..d2a87f429 100644
--- a/common/src/Utilities/ThreadTools.cpp
+++ b/common/src/Utilities/ThreadTools.cpp
@@ -15,24 +15,30 @@
 
 
 #include "PrecompiledHeader.h"
-#include "Threading.h"
-#include "wxBaseTools.h"
 
-#include <wx/datetime.h>
-#include <wx/thread.h>
+#ifdef _WIN32
+#	include <wx/msw/wrapwin.h>	// for thread renaming features
+#endif
 #include <wx/app.h>
 
 #ifdef __LINUX__
 #	include <signal.h>		// for pthread_kill, which is in pthread.h on w32-pthreads
 #endif
 
+#include "Threading.h"
+#include "wxBaseTools.h"
+
+#include <wx/datetime.h>
+#include <wx/thread.h>
+
+
 using namespace Threading;
 
 namespace Threading
 {
 	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();
 	}
@@ -59,11 +65,11 @@ namespace Threading
 	{
 		try
 		{
-			wxString logfix = L"Thread Destructor for " + m_name;
+			Console.WriteLn( L"Thread Log: Executing destructor for " + m_name );
 
 			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
 				m_sem_finished.WaitGui();
 	#else
@@ -73,9 +79,7 @@ namespace Threading
 				// it gets destroyed, otherwise th mutex handle would become invalid.
 				ScopedLock locker( m_lock_start );
 			}
-			else
-				Console.WriteLn( logfix + L": thread not running.");
-			Sleep( 1 );
+			Threading::Sleep( 1 );
 			Detach();
 		}
 		DESTRUCTOR_CATCHALL
@@ -189,47 +193,22 @@ namespace Threading
 		m_except->Rethrow();
 	}
 
-	// invoked internally when canceling or exiting the thread.  Extending classes should implement
-	// OnThreadCleanup() to extend clenup functionality.
-	void PersistentThread::_ThreadCleanup()
+	void PersistentThread::TestCancel()
 	{
-		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
-		// derrived class is implemented.
-		ScopedLock startlock( m_lock_start );
-
-		OnThreadCleanup();
-
-		m_running = false;
-		m_sem_finished.Post();
+		pxAssert( IsSelf() );
+		pthread_testcancel();
 	}
 
-	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 {
-			ExecuteTask();
-		}
-		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();
+			(this->*method)();
 		}
+
+		// ----------------------------------------------------------------------------
+		// Neat repackaging for STL Runtime errors...
+		//
 		catch( std::runtime_error& ex )
 		{
 			m_except = new Exception::RuntimeError(
@@ -244,16 +223,79 @@ namespace Threading
 				)
 			);
 		}
+
+		// ----------------------------------------------------------------------------
 		catch( Exception::RuntimeError& ex )
 		{
 			m_except = ex.Clone();
 			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::OnThreadCleanup() {}
 
+	// passed into pthread_create, and is used to dispatch the thread's object oriented
+	// callback function
 	void* PersistentThread::_internal_callback( void* itsme )
 	{
 		jASSUME( itsme != NULL );
@@ -265,12 +307,12 @@ namespace Threading
 		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.
 
diff --git a/common/src/Utilities/Windows/WinThreads.cpp b/common/src/Utilities/Windows/WinThreads.cpp
index 7bccb2de4..91eb0bb84 100644
--- a/common/src/Utilities/Windows/WinThreads.cpp
+++ b/common/src/Utilities/Windows/WinThreads.cpp
@@ -18,7 +18,6 @@
 #include "x86emitter/tools.h"
 #include "Threading.h"
 
-
 #ifdef _WIN32
 #include "implement.h"		// win32 pthreads implementations.
 #endif
diff --git a/common/src/x86emitter/cpudetect.cpp b/common/src/x86emitter/cpudetect.cpp
index fc7c58c76..79463297a 100644
--- a/common/src/x86emitter/cpudetect.cpp
+++ b/common/src/x86emitter/cpudetect.cpp
@@ -15,11 +15,13 @@
 
 
 #include "PrecompiledHeader.h"
-#include "internal.h"
-#include "tools.h"
 #include "Utilities/RedtapeWindows.h"
 #include "Utilities/Threading.h"
 
+#include "internal.h"
+#include "tools.h"
+
+
 using namespace x86Emitter;
 
 __aligned16 x86CPU_INFO x86caps;
diff --git a/common/vsprops/BaseProperties.vsprops b/common/vsprops/BaseProperties.vsprops
index ef8f2c325..1286c1a2b 100644
--- a/common/vsprops/BaseProperties.vsprops
+++ b/common/vsprops/BaseProperties.vsprops
@@ -5,7 +5,7 @@
 	Name="PcsxBaseProperties"
 	OutputDirectory="$(SolutionDir)\bin\$(PcsxSubsection)"
 	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
 		Name="VCCLCompilerTool"
@@ -18,6 +18,7 @@
 		WarningLevel="3"
 		DebugInformationFormat="3"
 		CompileAs="0"
+		DisableSpecificWarnings="4063;4100"
 	/>
 	<Tool
 		Name="VCLinkerTool"
diff --git a/common/vsprops/CommonLibrary.vsprops b/common/vsprops/CommonLibrary.vsprops
index 2a3f5e06e..c28c628b2 100644
--- a/common/vsprops/CommonLibrary.vsprops
+++ b/common/vsprops/CommonLibrary.vsprops
@@ -16,6 +16,7 @@
 		WarningLevel="3"
 		DebugInformationFormat="3"
 		CompileAs="0"
+		DisableSpecificWarnings="4063;4100"
 	/>
 	<Tool
 		Name="VCLinkerTool"
diff --git a/pcsx2/CDVD/CDVDaccess.cpp b/pcsx2/CDVD/CDVDaccess.cpp
index 207ddc9d1..98396ebb3 100644
--- a/pcsx2/CDVD/CDVDaccess.cpp
+++ b/pcsx2/CDVD/CDVDaccess.cpp
@@ -19,7 +19,7 @@
 #define ENABLE_TIMESTAMPS
 
 #ifdef _WIN32
-#include <windows.h>
+#	include <wx/msw/wrapwin.h>
 #endif
 
 #include <ctype.h>
diff --git a/pcsx2/CDVD/IsoFileTools.cpp b/pcsx2/CDVD/IsoFileTools.cpp
index c58af8171..ea5df1d96 100644
--- a/pcsx2/CDVD/IsoFileTools.cpp
+++ b/pcsx2/CDVD/IsoFileTools.cpp
@@ -17,7 +17,8 @@
 #include "IsoFileTools.h"
 
 #ifdef _WIN32
-#include <windows.h>
+#	include <wx/msw/wrapwin.h>
+
 
 void *_openfile(const char *filename, int flags)
 {
diff --git a/pcsx2/GS.h b/pcsx2/GS.h
index 83b31f613..e9f4277a5 100644
--- a/pcsx2/GS.h
+++ b/pcsx2/GS.h
@@ -102,7 +102,6 @@ protected:
 	int m_CopyCommandTally;
 	int m_CopyDataTally;
 	volatile bool m_RingBufferIsBusy;
-	volatile bool m_LoadState;
 
 	// 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
@@ -153,7 +152,8 @@ public:
 protected:
 	void OpenPlugin();
 	void OnSuspendInThread();
-	void OnResumeInThread();
+	void OnPauseInThread() {}
+	void OnResumeInThread( bool IsSuspended );
 
 	void OnResumeReady();
 
diff --git a/pcsx2/Gif.h b/pcsx2/Gif.h
index 40d7c89c6..7b0ab724f 100644
--- a/pcsx2/Gif.h
+++ b/pcsx2/Gif.h
@@ -206,7 +206,7 @@ struct GIFregisters
 	u32 padding[3];
 	tGIF_MODE 	mode;
 	u32 padding1[3];
-	tGIF_STAT		stat;
+	tGIF_STAT	stat;
 	u32 padding2[7];
 	
 	tGIF_TAG0	tag0;
@@ -218,7 +218,7 @@ struct GIFregisters
 	u32			tag3;
 	u32 padding6[3];
 	
-	tGIF_CNT		cnt;
+	tGIF_CNT	cnt;
 	u32 padding7[3];
 	tGIF_P3CNT	p3cnt;
 	u32 padding8[3];
diff --git a/pcsx2/HostGui.h b/pcsx2/HostGui.h
index eb19e043f..e663e10c7 100644
--- a/pcsx2/HostGui.h
+++ b/pcsx2/HostGui.h
@@ -54,7 +54,6 @@ public:
 
 extern StartupParams g_Startup;
 
-extern void States_Load( const wxString& file );
 extern void States_Save( const wxString& file );
 extern bool States_isSlotUsed(int num);
 
diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp
index 7d941fc56..266690425 100644
--- a/pcsx2/MTGS.cpp
+++ b/pcsx2/MTGS.cpp
@@ -99,7 +99,6 @@ mtgsThreadObject::mtgsThreadObject() :
 ,	m_CopyCommandTally( 0 )
 ,	m_CopyDataTally( 0 )
 ,	m_RingBufferIsBusy( false )
-,	m_LoadState( false )
 ,	m_QueuedFrames( 0 )
 ,	m_lock_FrameQueueCounter()
 ,	m_packet_size( 0 )
@@ -127,7 +126,6 @@ void mtgsThreadObject::OnStart()
 	m_WritePos		= 0;
 
 	m_RingBufferIsBusy	= false;
-	m_LoadState			= false;
 
 	m_QueuedFrames		= 0;
 	m_packet_size		= 0;
@@ -435,9 +433,9 @@ void mtgsThreadObject::OnSuspendInThread()
 	_clean_close_gs( NULL );
 }
 
-void mtgsThreadObject::OnResumeInThread()
+void mtgsThreadObject::OnResumeInThread( bool isSuspended )
 {
-	if( !m_LoadState )
+	if( isSuspended )
 		OpenPlugin();
 }
 
@@ -812,7 +810,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
 	{
 		AtomicExchange( m_RingPos, m_WritePos );
 		SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
-		m_LoadState = true;
 		SetEvent();
 		Resume();
 	}
@@ -820,7 +817,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
 		SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
 
 	mtgsWaitGS();
-	m_LoadState = false;
 }
 
 // Waits for the GS to empty out the entire ring buffer contents.
diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp
index 495d26dfc..f5a355c1f 100644
--- a/pcsx2/PluginManager.cpp
+++ b/pcsx2/PluginManager.cpp
@@ -90,7 +90,7 @@ struct LegacyApi_CommonMethod
 	// returns the method name as a wxString, converted from UTF8.
 	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.
 	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.
 
 	// returns the method name as a wxString, converted from UTF8.
-	wxString GetMethodName() const { return wxString::FromUTF8( MethodName ); }
+	wxString GetMethodName() const { return fromUTF8( MethodName ); }
 };
 
 
diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h
index aa91c8af5..a2e372263 100644
--- a/pcsx2/Plugins.h
+++ b/pcsx2/Plugins.h
@@ -32,10 +32,21 @@ struct PluginInfo
 
 	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
 // --------------------------------------------------------------------------------------
@@ -139,9 +150,12 @@ namespace Exception
 		virtual wxString FormatDiagnosticMessage() const;
 		virtual wxString FormatDisplayMessage() const;
 	};
-
 };
 
+#ifdef _MSC_VER
+#	pragma warning(pop)
+#endif
+
 // --------------------------------------------------------------------------------------
 //  LegacyPluginAPI_Common
 // --------------------------------------------------------------------------------------
diff --git a/pcsx2/R5900Exceptions.h b/pcsx2/R5900Exceptions.h
index 24586fd21..1af6d48de 100644
--- a/pcsx2/R5900Exceptions.h
+++ b/pcsx2/R5900Exceptions.h
@@ -47,7 +47,7 @@ namespace R5900Exception
 
 		void Init( const char*msg )
 		{
-			m_message = wxString::FromUTF8( msg );
+			m_message = fromUTF8( msg );
 			cpuState = cpuRegs;
 		}
 	};
diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp
index 2b73c16ef..561df0427 100644
--- a/pcsx2/RecoverySystem.cpp
+++ b/pcsx2/RecoverySystem.cpp
@@ -13,57 +13,98 @@
  *  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "PrecompiledHeader.h"
 
 #include "App.h"
 #include "HostGui.h"
+
 #include "zlib/zlib.h"
 
+
 static SafeArray<u8> state_buffer;
 
-// Simple lock boolean for the state buffer in use by a thread.  This simple solution works because
-// we are assured that state save/load actions will only be initiated from the main thread.
-static bool state_buffer_lock = false;
+// Simple lock boolean for the state buffer being in use by a thread.
+static NonblockingMutex state_buffer_lock;
 
 // This boolean is to keep the system from resuming emulation until the current state has completely
 // uploaded or downloaded itself.  It is only modified from the main thread, and should only be read
 // form the main thread.
 bool sys_resume_lock = false;
 
-// --------------------------------------------------------------------------------------
-//  StateThread_Freeze
-// --------------------------------------------------------------------------------------
-class StateThread_Freeze : public PersistentThread
+static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
+
+enum
+{
+	StateThreadAction_None = 0,
+	StateThreadAction_Create,
+	StateThreadAction_Restore,
+	StateThreadAction_ZipToDisk,
+	StateThreadAction_UnzipFromDisk,
+};
+
+class _BaseStateThread : public PersistentThread
 {
 	typedef PersistentThread _parent;
 
 public:
-	StateThread_Freeze( const wxString& file )
+	virtual ~_BaseStateThread() throw()
 	{
-		m_name = L"SaveState::Freeze";
-
-		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;
+		state_buffer_lock.Release();		// just in case;
 	}
-	
+
 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()
 	{
 		memSavingState( state_buffer ).FreezeAll();
 	}
-	
+
 	void OnThreadCleanup()
 	{
-		wxCommandEvent evt( pxEVT_FreezeFinished );
-		evt.SetClientData( this );
-		wxGetApp().AddPendingEvent( evt );
-
+		SendFinishEvent( StateThreadAction_Create );
 		_parent::OnThreadCleanup();
 	}
 };
@@ -71,37 +112,36 @@ protected:
 // --------------------------------------------------------------------------------------
 //   StateThread_Thaw
 // --------------------------------------------------------------------------------------
-class StateThread_Thaw : public PersistentThread
+class StateThread_Thaw : public _BaseStateThread
 {
-	typedef PersistentThread _parent;
+	typedef _BaseStateThread _parent;
 
 public:
-	StateThread_Thaw( const wxString& file )
-	{
-		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;
-	}
+	StateThread_Thaw( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Thaw", onFinished ) { }
 	
 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()
 	{
-		memSavingState( state_buffer ).FreezeAll();
+		memLoadingState( state_buffer ).FreezeAll();
 	}
 	
 	void OnThreadCleanup()
 	{
-		wxCommandEvent evt( pxEVT_FreezeFinished );
-		evt.SetClientData( this );
-		wxGetApp().AddPendingEvent( evt );
-
+		SendFinishEvent( StateThreadAction_Restore );
 		_parent::OnThreadCleanup();
 	}
 };
@@ -109,57 +149,46 @@ protected:
 // --------------------------------------------------------------------------------------
 //   StateThread_ZipToDisk
 // --------------------------------------------------------------------------------------
-class StateThread_ZipToDisk : public PersistentThread
+class StateThread_ZipToDisk : public _BaseStateThread
 {
-	typedef PersistentThread _parent;
+	typedef _BaseStateThread _parent;
 
 protected:
-	gzFile m_gzfp;
+	const wxString	m_filename;
+	gzFile			m_gzfp;
 
 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()
 	{
-		sys_resume_lock = false;		// just in case;
 		if( m_gzfp != NULL ) gzclose( m_gzfp );
 	}
 
 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()
 	{
-		Sleep( 2 );
+		Yield( 2 );
 		if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
 			throw Exception::BadStream();
 	}
 	
 	void OnThreadCleanup()
 	{
-		wxCommandEvent evt( pxEVT_FreezeFinished );
-		evt.SetClientData( this );		// tells message to clean us up.
-		wxGetApp().AddPendingEvent( evt );
-
+		SendFinishEvent( StateThreadAction_ZipToDisk );
 		_parent::OnThreadCleanup();
 	}
 };
@@ -168,43 +197,41 @@ protected:
 // --------------------------------------------------------------------------------------
 //   StateThread_UnzipFromDisk
 // --------------------------------------------------------------------------------------
-class StateThread_UnzipFromDisk : public PersistentThread
+class StateThread_UnzipFromDisk : public _BaseStateThread
 {
-	typedef PersistentThread _parent;
+	typedef _BaseStateThread _parent;
 
 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:
-	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()
 	{
-		sys_resume_lock = false;		// just in case;
 		if( m_gzfp != NULL ) gzclose( m_gzfp );
 	}
 
 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()
 	{
@@ -217,70 +244,95 @@ protected:
 			state_buffer.ExactAlloc( curidx+BlockSize );
 			gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
 			curidx += BlockSize;
+			TestCancel();
 		} while( !gzeof(m_gzfp) );
+		
+		m_finished = true;
 	}
 
 	void OnThreadCleanup()
 	{
-		wxCommandEvent evt( pxEVT_ThawFinished );
-		evt.SetClientData( this );		// tells message to clean us up.
-		wxGetApp().AddPendingEvent( evt );
-
+		SendFinishEvent( StateThreadAction_UnzipFromDisk );
 		_parent::OnThreadCleanup();
 	}
 };
 
-void Pcsx2App::OnFreezeFinished( wxCommandEvent& evt )
+void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
 {
-	state_buffer.Dispose();
-	state_buffer_lock = false;
+	// clear the OnFreezeFinsihed to NULL now, in case of error.
+	// (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( thr == NULL )
+	if( evt.GetInt() == StateThreadAction_Restore )
 	{
-		pxAssert( false );
-		return;
+		// Successfully restored state, so remove the copy.  Don't remove it sooner
+		// 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();
-
-	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 )
 {
-	if( state_buffer_lock ) return;
-	new StateThread_ZipToDisk( file );
+	if( state_buffer_lock.IsLocked() ) return;
+	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 )
 {
-	if( state_buffer_lock ) return;
-
-	sCoreThread.ShortSuspend();
-	new StateThread_UnzipFromDisk( file );
+	if( state_buffer_lock.IsLocked() ) return;
+	sCoreThread.Pause();
+	(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
 }
 
 // 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. :)
 void StateCopy_SaveToSlot( uint num )
 {
-	if( state_buffer_lock ) return;
-	StateCopy_SaveToFile( SaveStateBase::GetFilename( num ) );
+	zip_dest_filename = 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()
@@ -310,46 +382,19 @@ bool StateCopy_HasPartialState()
 
 void StateCopy_FreezeToMem()
 {
-	if( state_buffer_lock ) return;
+	if( state_buffer_lock.IsLocked() ) return;
+	(new StateThread_Freeze( OnFinished_Restore ))->Start();
 }
 
 void StateCopy_ThawFromMem()
 {
-	if( state_buffer_lock ) return;
+	if( state_buffer_lock.IsLocked() ) return;
+	new StateThread_Thaw( OnFinished_Restore );
 }
 
 void StateCopy_Clear()
 {
-	if( state_buffer_lock ) return;
+	if( state_buffer_lock.IsLocked() ) return;
 	state_buffer.Dispose();
 }
 
-
-
-// Creates a full recovery of the entire emulation state (CPU and all plugins).
-// If a current recovery state is already present, then nothing is done (the
-// existing recovery state takes precedence since if it were out-of-date it'd be
-// deleted!).
-void MakeFull()
-{
-	//if( g_RecoveryState ) return;
-	//if( !SysHasValidState() ) return;
-
-	/*
-	try
-	{
-		g_RecoveryState = new SafeArray<u8>( L"Memory Savestate Recovery" );
-		memSavingState( *g_RecoveryState ).FreezeAll();
-	}
-	catch( Exception::RuntimeError& ex )
-	{
-		Msgbox::Alert( wxsFormat(	// fixme: needs proper translation
-			L"PCSX2 encountered an error while trying to backup/suspend the PS2 VirtualMachine state. "
-			L"You may resume emulation without losing any data, however the machine state will not be "
-			L"able to recover if you make changes to your PCSX2 configuration.\n\n"
-			L"Details: %s", ex.FormatDisplayMessage().c_str() )
-		);
-		g_RecoveryState = NULL;
-	}*/
-}
-
diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp
index 54a06ef30..011167a8b 100644
--- a/pcsx2/SaveState.cpp
+++ b/pcsx2/SaveState.cpp
@@ -136,6 +136,9 @@ static const int MainMemorySizeInBytes =
 
 void SaveStateBase::FreezeMainMemory()
 {
+	if( IsLoading() )
+		PreLoadPrep();
+
 	// First Block - Memory Dumps
 	// ---------------------------
 	FreezeMem(PS2MEM_BASE,		Ps2MemSize::Base);		// 32 MB main memory   
@@ -149,8 +152,8 @@ void SaveStateBase::FreezeMainMemory()
 
 void SaveStateBase::FreezeRegisters()
 {
-	//if( IsLoading() )
-	//	PreLoadPrep();
+	if( IsLoading() )
+		PreLoadPrep();
 
 	// Second Block - Various CPU Registers and States
 	// -----------------------------------------------
diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h
index e0b5c062e..0c5f31e54 100644
--- a/pcsx2/SaveState.h
+++ b/pcsx2/SaveState.h
@@ -202,5 +202,6 @@ extern void StateCopy_ThawFromMem();
 extern void StateCopy_SaveToFile( const wxString& file );
 extern void StateCopy_LoadFromFile( const wxString& file );
 extern void StateCopy_SaveToSlot( uint num );
+extern void StateCopy_LoadFromSlot( uint slot );
 extern void StateCopy_Clear();
 
diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp
index 5c2b73487..0aa4cad53 100644
--- a/pcsx2/System.cpp
+++ b/pcsx2/System.cpp
@@ -139,7 +139,7 @@ SysCoreAllocations::SysCoreAllocations()
 		throw Exception::OutOfMemory(
 			wxsFormat(			// Diagnostic (english)
 				L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
-				L"Error Details: " + wxString::FromUTF8( ex.what() )
+				L"Error Details: " + fromUTF8( ex.what() )
 			),
 
 			GetMemoryErrorVM()	// translated
diff --git a/pcsx2/System/SysThreads.cpp b/pcsx2/System/SysThreads.cpp
index e05ee901c..7c977a1cd 100644
--- a/pcsx2/System/SysThreads.cpp
+++ b/pcsx2/System/SysThreads.cpp
@@ -55,7 +55,6 @@ void SysSuspendableThread::OnStart()
 	_parent::OnStart();
 }
 
-
 // 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
 // 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
 //      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 )
 {
 	if( IsSelf() || !IsRunning() ) return;
@@ -76,11 +80,34 @@ void SysSuspendableThread::Suspend( bool isBlocking )
 		if( m_ExecMode == ExecMode_Suspended )
 			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 )
 			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_SuspendEvent.WaitGui();
 }
@@ -111,6 +138,7 @@ void SysSuspendableThread::Resume()
 			// fall through...
 
 			case ExecMode_Suspending:
+			case ExecMode_Pausing:
 				// we need to make sure and wait for the emuThread to enter a fully suspended
 				// state before continuing...
 
@@ -120,8 +148,8 @@ void SysSuspendableThread::Resume()
 		}
 	}
 
-	pxAssertDev( m_ExecMode == ExecMode_Suspended,
-		"SysSuspendableThread is not in a suspended/idle state?  wtf!" );
+	pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused),
+		"SysSuspendableThread is not in a suspended/paused state?  wtf!" );
 
 	m_ExecMode = ExecMode_Running;
 	m_ResumeProtection = true;
@@ -148,7 +176,7 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
 	// Shortcut for the common case, to avoid unnecessary Mutex locks:
 	if( m_ExecMode == ExecMode_Running )
 	{
-		if( isCancelable ) pthread_testcancel();
+		if( isCancelable ) TestCancel();
 		return;
 	}
 
@@ -170,23 +198,41 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
 			// Yup, need this a second time.  Variable state could have changed while we
 			// were trying to acquire the lock above.
 			if( isCancelable )
-				pthread_testcancel();
+				TestCancel();
 		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:
 		{
 			OnSuspendInThread();
 			m_ExecMode = ExecMode_Suspended;
 			m_SuspendEvent.Post();
 		} 
-		// fall through...
-
+		// fallthrough...
+		
 		case ExecMode_Suspended:
 			m_lock_ExecMode.Unlock();
 			while( m_ExecMode == ExecMode_Suspended )
 				m_ResumeEvent.WaitGui();
 
-			OnResumeInThread();
+			OnResumeInThread( true );
 		break;
 		
 		jNO_DEFAULT;
@@ -218,16 +264,6 @@ void SysCoreThread::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
 // settings were changed, resets will be performed as needed and emulation state resumed from
 // memory savestates.
@@ -348,11 +384,6 @@ void SysCoreThread::CpuExecute()
 	PCSX2_MEM_PROTECT_END();
 }
 
-static void _cet_callback_cleanup( void* handle )
-{
-	((SysCoreThread*)handle)->OnThreadCleanup();
-}
-
 void SysCoreThread::ExecuteTask()
 {
 	tls_coreThread = this;
@@ -369,9 +400,10 @@ void SysCoreThread::OnSuspendInThread()
 		m_plugins.Close();
 }
 
-void SysCoreThread::OnResumeInThread()
+void SysCoreThread::OnResumeInThread( bool isSuspended )
 {
-	m_plugins.Open();
+	if( isSuspended )
+		m_plugins.Open();
 }
 
 
diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h
index 51204cfc3..241495a7f 100644
--- a/pcsx2/System/SysThreads.h
+++ b/pcsx2/System/SysThreads.h
@@ -41,7 +41,9 @@ protected:
 		ExecMode_NoThreadYet,
 		ExecMode_Running,
 		ExecMode_Suspending,
-		ExecMode_Suspended
+		ExecMode_Suspended,
+		ExecMode_Pausing,
+		ExecMode_Paused,
 	};
 
 	volatile ExecutionMode	m_ExecMode;
@@ -59,6 +61,7 @@ public:
 
 	virtual void Suspend( bool isBlocking = true );
 	virtual void Resume();
+	virtual void Pause();
 
 	virtual void StateCheck( bool isCancelable = true );
 	virtual void OnThreadCleanup();
@@ -78,11 +81,21 @@ protected:
 	// thread, requesting this thread suspend itself temporarily).  After this is called,
 	// the thread enters a waiting state on the m_ResumeEvent semaphore.
 	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
 	// handles invocation by the following guidelines: Called from StateCheck() after the
 	// 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 OnThreadCleanup();
-	virtual void ShortSuspend();
 	virtual void OnResumeReady();
 
 protected:
@@ -116,6 +128,7 @@ protected:
 
 	virtual void Start();
 	virtual void OnSuspendInThread();
-	virtual void OnResumeInThread();
+	virtual void OnPauseInThread() {}
+	virtual void OnResumeInThread( bool IsSuspended );
 	virtual void ExecuteTask();
 };
diff --git a/pcsx2/VUmicroMem.cpp b/pcsx2/VUmicroMem.cpp
index a89dd44ca..558d82813 100644
--- a/pcsx2/VUmicroMem.cpp
+++ b/pcsx2/VUmicroMem.cpp
@@ -27,12 +27,6 @@
 VUmicroCpu CpuVU0;		// contains a working copy of the VU0 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()
 {
 	CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;
diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h
index 0ccfc9ace..5553e2cf0 100644
--- a/pcsx2/gui/App.h
+++ b/pcsx2/gui/App.h
@@ -21,6 +21,8 @@
 #include <wx/docview.h>
 #include <wx/apptrait.h>
 
+#include "Utilities/Listeners.h"
+
 class IniInterface;
 class MainEmuFrame;
 class GSFrame;
@@ -35,6 +37,9 @@ class AppCoreThread;
 #include "System.h"
 #include "System/SysThreads.h"
 
+
+typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
+
 #define AllowFromMainThreadOnly() \
 	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_OpenModalDialog, -1 )
 	DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
+	DECLARE_EVENT_TYPE( pxEVT_SysExecute, -1 )
 	DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
-	DECLARE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated, -1 )
-	DECLARE_EVENT_TYPE( pxEVT_FreezeFinished, -1 )
-	DECLARE_EVENT_TYPE( pxEVT_ThawFinished, -1 )
+	DECLARE_EVENT_TYPE( pxEVT_AppCoreThreadFinished, -1 )
+	DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 )
 END_DECLARE_EVENT_TYPES()
 
 // This is used when the GS plugin is handling its own window.  Messages from the PAD
@@ -335,8 +340,6 @@ public:
 	Pcsx2App();
 	virtual ~Pcsx2App();
 
-	void ReloadPlugins();
-
 	void PostPadKey( wxKeyEvent& evt );
 	void PostMenuAction( MenuIdentifiers menu_id ) const;
 	int  ThreadedModalDialog( DialogIdentifiers dialogId );
@@ -392,6 +395,18 @@ public:
 	void DisableDiskLogging() const;
 	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:
 	void InitDefaultGlobalAccelerators();
 	void BuildCommandHash();
@@ -402,18 +417,18 @@ protected:
 
 	void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const;
 
+	void OnSysExecute( wxCommandEvent& evt );
 	void OnReloadPlugins( wxCommandEvent& evt );
 	void OnLoadPluginsComplete( wxCommandEvent& evt );
 	void OnSemaphorePing( wxCommandEvent& evt );
 	void OnOpenModalDialog( wxCommandEvent& evt );
 	void OnCoreThreadTerminated( wxCommandEvent& evt );
 
-	void OnFreezeFinished( wxCommandEvent& evt );
-	void OnThawFinished( wxCommandEvent& evt );
+	void OnFreezeThreadFinished( wxCommandEvent& evt );
 
 	void OnMessageBox( pxMessageBoxEvent& evt );
 	void OnEmuKeyDown( wxKeyEvent& evt );
-	
+
 	// ----------------------------------------------------------------------------
 	//      Override wx default exception handling behavior
 	// ----------------------------------------------------------------------------
@@ -496,7 +511,7 @@ DECLARE_APP(Pcsx2App)
 extern bool sys_resume_lock;
 
 extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
-extern void LoadPluginsPassive();
+extern void LoadPluginsPassive( FnType_OnThreadComplete* onComplete );
 extern void LoadPluginsImmediate();
 extern void UnloadPlugins();
 
diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp
index e53b2fdd8..ecac3008d 100644
--- a/pcsx2/gui/AppCoreThread.cpp
+++ b/pcsx2/gui/AppCoreThread.cpp
@@ -72,7 +72,7 @@ void AppCoreThread::OnResumeReady()
 // the new (lack of) thread status, so this posts a message to the App to do so.
 void AppCoreThread::OnThreadCleanup()
 {
-	wxCommandEvent evt( pxEVT_AppCoreThread_Terminated );
+	wxCommandEvent evt( pxEVT_AppCoreThreadFinished );
 	wxGetApp().AddPendingEvent( evt );
 	_parent::OnThreadCleanup();
 }
diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp
index 4da439576..1e98a3c80 100644
--- a/pcsx2/gui/AppInit.cpp
+++ b/pcsx2/gui/AppInit.cpp
@@ -220,12 +220,12 @@ bool Pcsx2App::OnInit()
 	Connect( pxEVT_SemaphorePing,	wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
 	Connect( pxEVT_OpenModalDialog,	wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
 	Connect( pxEVT_ReloadPlugins,	wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
+	Connect( pxEVT_SysExecute,		wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
+
 	Connect( pxEVT_LoadPluginsComplete,	wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
 
-	Connect( pxEVT_FreezeFinished,	wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
-	
-	Connect( pxEVT_FreezeFinished,	wxCommandEventHandler( Pcsx2App::OnFreezeFinished ) );
-	Connect( pxEVT_ThawFinished,	wxCommandEventHandler( Pcsx2App::OnThawFinished ) );
+	Connect( pxEVT_AppCoreThreadFinished,	wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
+	Connect( pxEVT_FreezeThreadFinished,	wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
 
 	Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
 
@@ -310,7 +310,7 @@ bool Pcsx2App::OnInit()
 				return false;
 		}
 
-		LoadPluginsPassive();
+		LoadPluginsPassive( NULL );
 	}
 	// ----------------------------------------------------------------------------
 	catch( Exception::StartupAborted& ex )
diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp
index 025266bc7..cde9bdcb2 100644
--- a/pcsx2/gui/AppMain.cpp
+++ b/pcsx2/gui/AppMain.cpp
@@ -30,10 +30,10 @@ IMPLEMENT_APP(Pcsx2App)
 DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
 DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
 DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
+DEFINE_EVENT_TYPE( pxEVT_SysExecute );
 DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
-DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated );
-DEFINE_EVENT_TYPE( pxEVT_FreezeFinished );
-DEFINE_EVENT_TYPE( pxEVT_ThawFinished );
+DEFINE_EVENT_TYPE( pxEVT_AppCoreThreadFinished );
+DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished );
 
 bool					UseAdminMode = false;
 wxDirName				SettingsFolder;
@@ -211,6 +211,18 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
 		(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 )
 	{
 		Console.Error( ex.FormatDiagnosticMessage() );
@@ -376,32 +388,53 @@ void Pcsx2App::OnMainFrameClosed()
 	m_MainFrame = NULL;
 }
 
-
-
 // --------------------------------------------------------------------------------------
 //  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
 // configured CDVD source device.
 void Pcsx2App::SysExecute()
 {
-	if( sys_resume_lock )
+	if( !m_CorePlugins )
 	{
-		Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
+		LoadPluginsPassive( OnSysExecuteAfterPlugins );
 		return;
 	}
 
-	SysReset();
-	LoadPluginsImmediate();
-	m_CoreThread = new AppCoreThread( *m_CorePlugins );
-	m_CoreThread->Resume();
+	wxCommandEvent evt( pxEVT_SysExecute );
+	evt.SetInt( -1 );
+	AddPendingEvent( evt );
 }
 
 // Executes the specified cdvd source and optional elf file.  This command performs a
 // full closure of any existing VM state and starts a fresh VM with the requested
 // sources.
 void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
+{
+	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 )
 	{
@@ -409,10 +442,13 @@ void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
 		return;
 	}
 
-	SysReset();
-	LoadPluginsImmediate();
+	// if something unloaded plugins since this messages was queued then it's best to ignore
+	// 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_ChangeSource( cdvdsrc );
+	if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
 
 	m_CoreThread = new AppCoreThread( *m_CorePlugins );
 	m_CoreThread->Resume();
diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp
index 5836c39f5..387505685 100644
--- a/pcsx2/gui/ConsoleLogger.cpp
+++ b/pcsx2/gui/ConsoleLogger.cpp
@@ -109,7 +109,7 @@ void ConsoleTestThread::ExecuteTask()
 		// worst case scenario (without being entirely unrealistic).
 		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)" ) );
-		Sleep( 0 );
+		Yield( 0 );
 	}
 }
 
diff --git a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp
index ab0f695fc..99f98fde8 100644
--- a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp
+++ b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp
@@ -51,7 +51,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
 	m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ),
 		wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN )
 {
-	static const wxString LabelAuthors = wxString::FromUTF8(
+	static const wxString LabelAuthors = fromUTF8(
 		"Developers"
 		"\n\n"
 		"v0.9.6+: Arcum42, Refraction,"
@@ -69,7 +69,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
 		"Webmasters: CKemu, Falcon4ever"
 	);
 
-	static const wxString LabelGreets = wxString::FromUTF8(
+	static const wxString LabelGreets = fromUTF8(
 		"Contributors"
 		"\n\n"
 		"Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"
diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp
index 8f7dfd1a5..1254f81bd 100644
--- a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp
+++ b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp
@@ -44,7 +44,7 @@ static const int IdealWidth = 500;
 template< typename T >
 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 );
 	g_ApplyState.SetCurrentPage( curidx );
 	m_listbook.AddPage( new T( m_listbook, IdealWidth ),	wxGetTranslation( labelstr ),
diff --git a/pcsx2/gui/GlobalCommands.cpp b/pcsx2/gui/GlobalCommands.cpp
index ec872aaeb..cb53f1535 100644
--- a/pcsx2/gui/GlobalCommands.cpp
+++ b/pcsx2/gui/GlobalCommands.cpp
@@ -263,7 +263,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
 		Console.Notice( wxsFormat(
 			L"Kbd Accelerator '%s' is mapped multiple times.\n"
 			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 )
 	{
 		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
diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp
index dd36f1f5d..0eba2da51 100644
--- a/pcsx2/gui/MainFrame.cpp
+++ b/pcsx2/gui/MainFrame.cpp
@@ -310,7 +310,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
 	{
 		// 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,
-			SVN_REV, SVN_MODS ? L"m" : wxEmptyString, wxString::FromUTF8(__DATE__).c_str() );
+			SVN_REV, SVN_MODS ? L"m" : wxEmptyString, fromUTF8(__DATE__).c_str() );
 	}
 	else
 	{
diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp
index f601a3f94..8f4e3a243 100644
--- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp
+++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp
@@ -559,13 +559,13 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTask()
 	DevCon.Status( "Plugin Enumeration Thread started..." );
 
 	wxGetApp().Ping();		// gives the gui thread some time to refresh
-	Sleep( 3 );
+	Yield( 3 );
 
 	for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
 	{
 		DoNextPlugin( curidx );
 		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.
 	}
 
diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp
index aa71caaa4..7241424f1 100644
--- a/pcsx2/gui/Plugins.cpp
+++ b/pcsx2/gui/Plugins.cpp
@@ -27,6 +27,8 @@
 
 using namespace Threading;
 
+static FnType_OnThreadComplete* Callback_PluginsLoadComplete = NULL;
+
 // --------------------------------------------------------------------------------------
 //  LoadPluginsTask
 // --------------------------------------------------------------------------------------
@@ -37,76 +39,52 @@ using namespace Threading;
 //
 class LoadPluginsTask : public PersistentThread
 {
+	typedef PersistentThread _parent;
+	
 public:
-	Exception::PluginError* Ex_PluginError;
-	Exception::RuntimeError* Ex_RuntimeError;
 	PluginManager* Result;
 
 protected:
 	wxString m_folders[PluginId_Count];
 
 public:
-	LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) :
-		Ex_PluginError( NULL )
-	,	Ex_RuntimeError( NULL )
-	,	Result( NULL )
+	LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : Result( NULL )
 	{
 		for(int i=0; i<PluginId_Count; ++i )
 			m_folders[i] = folders[i];
-
-		Start();
 	}
 
 	virtual ~LoadPluginsTask() throw();
 
 protected:
 	void OnStart() {}
-	void OnThreadCleanup() {}
+	void OnThreadCleanup();
 	void ExecuteTask();
 };
 
-static ScopedPtr<LoadPluginsTask> _loadTask;
-
 LoadPluginsTask::~LoadPluginsTask() throw()
 {
-	if( _loadTask )
-		_loadTask.DetachPtr();		// avoids recursive deletion
-
 	PersistentThread::Cancel();
-	_loadTask = NULL;
 }
 
 void LoadPluginsTask::ExecuteTask()
 {
 	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 );
 	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 );
+
+	_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;
 }
 
+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 )
 {
-	ReloadPlugins();
-}
-
-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;
-
+	if( plugin_load_lock ) return;
 	m_CoreThread	= NULL;
 	m_CorePlugins	= NULL;
 
@@ -169,42 +140,79 @@ void Pcsx2App::ReloadPlugins()
 			passins[pi->id] = g_Conf->FullpathTo( pi->id );
 	} while( ++pi, pi->shortname != NULL );
 
-	_loadTask.Delete() = new LoadPluginsTask( passins );
+	(new LoadPluginsTask( passins ))->Start();
 	// ...  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
 // 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.
-void LoadPluginsPassive()
+// If plugins are already loaded, onComplete is invoked, and the function returns with no
+// 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 );
 	wxGetApp().AddPendingEvent( evt );
 }
 
-// Blocks until plugins have been successfully loaded, or throws an exception if
-// the user cancels the loading procedure after error.  If plugins are already loaded
-// then no action is performed.
+// Performs a blocking load of plugins.  If the emulation thread is active, it is shut down
+// automatically to prevent race conditions (it depends on plugins).
+//
+// 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()
 {
-	AllowFromMainThreadOnly();
-	if( g_plugins ) return;
+	if( g_plugins != NULL ) return;
 
-	static int _reentrant = 0;
-	RecursionGuard guard( _reentrant );
+	wxGetApp().m_CoreThread = NULL;
+
+	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()
 {
+	wxGetApp().m_CoreThread = NULL;
 	wxGetApp().m_CorePlugins = NULL;
 }
diff --git a/pcsx2/gui/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp
index 386e374f3..1a6e72990 100644
--- a/pcsx2/gui/Saveslots.cpp
+++ b/pcsx2/gui/Saveslots.cpp
@@ -23,59 +23,10 @@
 
 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.
 void States_Save( const wxString& file )
 {
-	if( !SysHasValidState() )
-	{
-		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()
-		);
-	}
+	StateCopy_SaveToFile( file );
 }
 
 // --------------------------------------------------------------------------------------
@@ -95,22 +46,12 @@ bool States_isSlotUsed(int num)
 
 void States_FreezeCurrentSlot()
 {
-	Console.Status( "Saving savestate to slot %d...", StatesC );
-	States_Save( SaveStateBase::GetFilename( StatesC ) );
+	StateCopy_SaveToSlot( StatesC );
 }
 
 void States_DefrostCurrentSlot()
 {
-	wxString file( SaveStateBase::GetFilename( StatesC ) );
-
-	if( !wxFileExists( file ) )
-	{
-		Console.Notice( "Savestate slot %d is empty.", StatesC );
-		return;
-	}
-
-	Console.Status( "Loading savestate from slot %d...", StatesC );
-	States_Load( file );
+	StateCopy_LoadFromSlot( StatesC );
 	//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
 }
 
diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
index 6f11e8097..13561b2b0 100644
--- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
+++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
@@ -1804,6 +1804,10 @@
 				RelativePath="..\..\gui\IniInterface.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\..\..\common\include\Utilities\Listeners.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\gui\MainFrame.cpp"
 				>
diff --git a/pcsx2/windows/WinConsolePipe.cpp b/pcsx2/windows/WinConsolePipe.cpp
index bcadd4fbf..892a4874a 100644
--- a/pcsx2/windows/WinConsolePipe.cpp
+++ b/pcsx2/windows/WinConsolePipe.cpp
@@ -142,6 +142,9 @@ static __forceinline bool ReadPipe(HANDLE h_Pipe, ConsoleColors color )
 	return true;
 }
 
+// --------------------------------------------------------------------------------------
+//  WinPipeThread
+// --------------------------------------------------------------------------------------
 class WinPipeThread : public PersistentThread
 {
 	typedef PersistentThread _parent;
@@ -173,8 +176,7 @@ protected:
 			SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
 			while( true )
 			{
-				Sleep( 100 );
-				pthread_testcancel();
+				Yield( 100 );
 				ReadPipe( m_outpipe, m_color );
 			}
 		}
@@ -189,6 +191,9 @@ protected:
 	void OnThreadCleanup() { }
 };
 
+// --------------------------------------------------------------------------------------
+//  WinPipeRedirection
+// --------------------------------------------------------------------------------------
 class WinPipeRedirection : public PipeRedirectionBase
 {
 	DeclareNoncopyableObject( WinPipeRedirection );
@@ -221,7 +226,8 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
 		DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
 
 		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.
 		HANDLE newhandle = GetStdHandle(stdhandle);
@@ -231,11 +237,11 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
 		if( newhandle == NULL )
 			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 ) 
 			throw Exception::RuntimeError( "_open_osfhandle returned -1." );
 
-		m_fp		= _fdopen( m_crtFile, "w" );
+		m_fp = _fdopen( m_crtFile, "w" );
 		if( m_fp == NULL )
 			throw Exception::RuntimeError( "_fdopen returned NULL." );
 
@@ -269,10 +275,22 @@ void WinPipeRedirection::Cleanup() throw()
 	{
 		fclose( m_fp );
 		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
-	// m_file is closed implicitly when closing crtFile
+	if( m_file != INVALID_HANDLE_VALUE )
+	{
+		CloseHandle( m_pipe );
+		m_file = INVALID_HANDLE_VALUE;
+	}
 
 	if( m_pipe != INVALID_HANDLE_VALUE )
 	{
diff --git a/pcsx2/x86/iVU1micro.cpp b/pcsx2/x86/iVU1micro.cpp
index 99055bf18..f76c2c04f 100644
--- a/pcsx2/x86/iVU1micro.cpp
+++ b/pcsx2/x86/iVU1micro.cpp
@@ -30,7 +30,6 @@ int mVUdebugNow = 0;
 
 #ifdef DEBUG_COMPARE
 
-#include <windows.h>
 static int runAmount = 0;
 
 void VUtestPause() {
@@ -70,8 +69,9 @@ void VUtestPause() {
 	SysPrintf("VU Mem CRC = 0x%08x\n", j);
 	SysPrintf("EndPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
 
+	// ... wtf?? --air
 	for (int i = 0; i < 10000000; i++) {
-		Sleep(1000);
+		Threading::Sleep(1000);
 	}
 }
 #else
@@ -84,9 +84,6 @@ extern u32 vudump;
 
 #ifdef DEBUG_COMPARE2
 
-#ifndef DEBUG_COMPARE
-#include <windows.h>
-#endif
 
 __aligned16 u8 backVUregs[sizeof(VURegs)];
 __aligned16 u8 cmpVUregs [sizeof(VURegs)];
diff --git a/plugins/spu2-x/src/Mixer.cpp b/plugins/spu2-x/src/Mixer.cpp
index 244fa2a47..f2fcd24fd 100644
--- a/plugins/spu2-x/src/Mixer.cpp
+++ b/plugins/spu2-x/src/Mixer.cpp
@@ -238,20 +238,6 @@ static __forceinline s32 __fastcall GetNextDataBuffered( V_Core& thiscore, uint
 					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 );
 		}
 
diff --git a/plugins/spu2-x/src/ReadInput.cpp b/plugins/spu2-x/src/ReadInput.cpp
index a69fd0ac9..09129a772 100644
--- a/plugins/spu2-x/src/ReadInput.cpp
+++ b/plugins/spu2-x/src/ReadInput.cpp
@@ -63,8 +63,6 @@ StereoOut32 V_Core::ReadInput_HiFi()
 		AdmaInProgress = 0;
 		if(InputDataLeft >= 0x200)
 		{
-			u8 k = (InputDataLeft >= InputDataProgress);
-
 #ifdef PCM24_S1_INTERLEAVE
 			AutoDMAReadBuffer(1);
 #else
diff --git a/plugins/spu2-x/src/spu2sys.cpp b/plugins/spu2-x/src/spu2sys.cpp
index 581deccda..c2f24c045 100644
--- a/plugins/spu2-x/src/spu2sys.cpp
+++ b/plugins/spu2-x/src/spu2sys.cpp
@@ -837,7 +837,7 @@ static void __fastcall RegWrite_Core( u16 value )
 		{
 			bool irqe = thiscore.IRQEnable;
 			int bit0 = thiscore.AttrBit0;
-			int bit4 = thiscore.AttrBit4;
+			//int bit4 = thiscore.AttrBit4;
 
 			if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset
 			{