Yay, craploads of fixups for the new gui:

* Lots of crashfixes and threading rules compliance (like using wxYield instead of ProcessPendingEvents)
 * Killed off some memory corruption
 * Better error handling and reporting
 * Much speedier suspend/resume during emulation
 * Revamped entire savestate system to use a RIFF-style file format (untested, will work on it soon)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1832 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-09-16 17:23:02 +00:00
parent 7593072214
commit c9c924fe35
75 changed files with 1736 additions and 1666 deletions

View File

@ -300,7 +300,7 @@ namespace Exception
class BadStream : public virtual Stream
{
public:
DEFINE_STREAM_EXCEPTION( BadStream, wxLt("File data is corrupted or incomplete, or the stream connection closed unexpectedly") )
DEFINE_STREAM_EXCEPTION( BadStream, wxLt("File data is corrupted or incomplete, or the stream connection closed unexpectedly.") )
};
// A generic exception for odd-ball stream creation errors.
@ -308,7 +308,7 @@ namespace Exception
class CreateStream : public virtual Stream
{
public:
DEFINE_STREAM_EXCEPTION( CreateStream, wxLt("File could not be created or opened") )
DEFINE_STREAM_EXCEPTION( CreateStream, wxLt("File could not be created or opened.") )
};
// Exception thrown when an attempt to open a non-existent file is made.
@ -317,13 +317,13 @@ namespace Exception
class FileNotFound : public virtual CreateStream
{
public:
DEFINE_STREAM_EXCEPTION( FileNotFound, wxLt("File not found") )
DEFINE_STREAM_EXCEPTION( FileNotFound, wxLt("File not found.") )
};
class AccessDenied : public virtual CreateStream
{
public:
DEFINE_STREAM_EXCEPTION( AccessDenied, wxLt("Permission denied to file") )
DEFINE_STREAM_EXCEPTION( AccessDenied, wxLt("Permission denied to file.") )
};
// EndOfStream can be used either as an error, or used just as a shortcut for manual
@ -332,7 +332,7 @@ namespace Exception
class EndOfStream : public virtual Stream
{
public:
DEFINE_STREAM_EXCEPTION( EndOfStream, wxLt("Unexpected end of file") );
DEFINE_STREAM_EXCEPTION( EndOfStream, wxLt("Unexpected end of file or stream.") );
};
// ---------------------------------------------------------------------------------------
@ -346,29 +346,7 @@ namespace Exception
class BadSavedState : public virtual BadStream
{
public:
DEFINE_STREAM_EXCEPTION( BadSavedState, wxLt("Savestate data is corrupted or incomplete") )
};
// Exception thrown by SaveState class when a plugin returns an error during state
// load or save.
//
class FreezePluginFailure : public virtual RuntimeError
{
public:
wxString PluginName; // name of the plugin
wxString FreezeAction;
public:
DEFINE_EXCEPTION_COPYTORS( FreezePluginFailure )
explicit FreezePluginFailure( const char* plugin, const char* action )
{
PluginName = wxString::FromUTF8( plugin );
FreezeAction = wxString::FromUTF8( action );
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
DEFINE_STREAM_EXCEPTION( BadSavedState, wxLt("Savestate data is corrupted or incomplete.") )
};
// thrown when the savestate being loaded isn't supported.

View File

@ -550,6 +550,9 @@ template< class Key, class T >
class HashMap : public google::dense_hash_map<Key, T, CommonHashClass>
{
public:
using dense_hash_map<Key, T, CommonHashClass>::operator[];
using dense_hash_map<Key, T, CommonHashClass>::const_iterator;
virtual ~HashMap() {}
/// <summary>
@ -574,13 +577,13 @@ public:
/// parameter. This is a more favorable alternative to the indexer operator since the
/// indexer implementation can and will create new entries for every request that
/// </remarks>
/*void TryGetValue( const Key& key, T& outval ) const
void TryGetValue( const Key& key, T& outval ) const
{
// See above class for notes on why this is commented out.
const_iterator iter = find( key );
if( iter != end() )
outval = iter->second;
}*/
}
const T& GetValue( Key key ) const
{

View File

@ -248,7 +248,7 @@ protected:
}
public:
virtual ~SafeList()
virtual ~SafeList() throw()
{
safe_free( m_ptr );
}
@ -287,6 +287,11 @@ public:
// The actual size of the allocation may differ.
int GetSizeInBytes() const { return m_length * sizeof(T); }
void MatchLengthToAllocatedSize()
{
m_length = m_allocsize;
}
// Ensures that the allocation is large enough to fit data of the
// amount requested. The memory allocation is not resized smaller.
void MakeRoomFor( int blockSize )
@ -398,7 +403,9 @@ protected:
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// --------------------------------------------------------------------------------------
// SafeAlignedArray class
// --------------------------------------------------------------------------------------
// Handy little class for allocating a resizable memory block, complete with
// exception-based error handling and automatic cleanup.
// This one supports aligned data allocations too!

View File

@ -29,6 +29,9 @@ extern const wxRect wxDefaultRect;
// This should prove useful....
#define wxsFormat wxString::Format
extern void SplitString( wxArrayString& dest, const wxString& src, const wxString& delims );
extern void JoinString( wxString& dest, const wxArrayString& src, const wxString& separator );
extern wxString ToString( const wxPoint& src, const wxString& separator=L"," );
extern wxString ToString( const wxSize& src, const wxString& separator=L"," );
extern wxString ToString( const wxRect& src, const wxString& separator=L"," );

View File

@ -130,7 +130,7 @@ namespace Threading
volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
public:
virtual ~PersistentThread();
virtual ~PersistentThread() throw();
PersistentThread();
virtual void Start();
@ -172,7 +172,7 @@ namespace Threading
bool m_IsLocked;
public:
virtual ~ScopedLock()
virtual ~ScopedLock() throw()
{
if( m_IsLocked )
m_lock.Unlock();
@ -239,77 +239,35 @@ namespace Threading
{
protected:
volatile bool m_Done;
volatile bool m_TaskComplete;
volatile bool m_TaskPending;
Semaphore m_post_TaskComplete;
MutexLock m_lock_TaskComplete;
public:
virtual ~BaseTaskThread() {}
virtual ~BaseTaskThread() throw() {}
BaseTaskThread() :
m_Done( false )
, m_TaskComplete( false )
, m_TaskPending( false )
, m_post_TaskComplete()
{
}
// Tells the thread to exit and then waits for thread termination.
sptr Block()
{
if( !m_running ) return m_returncode;
m_Done = true;
m_sem_event.Post();
return PersistentThread::Block();
}
// Initiates the new task. This should be called after your own StartTask has
// initialized internal variables / preparations for task execution.
void PostTask()
{
jASSUME( m_running );
m_TaskComplete = false;
m_post_TaskComplete.Reset();
m_sem_event.Post();
}
// Blocks current thread execution pending the completion of the parallel task.
void WaitForResult()
{
if( !m_running ) return;
if( !m_TaskComplete )
m_post_TaskComplete.Wait();
else
m_post_TaskComplete.Reset();
}
sptr Block();
void PostTask();
void WaitForResult();
protected:
// Abstract method run when a task has been posted. Implementing classes should do
// all your necessary processing work here.
virtual void Task()=0;
sptr ExecuteTask()
{
do
{
// Wait for a job!
m_sem_event.Wait();
if( m_Done ) break;
Task();
m_TaskComplete = true;
m_post_TaskComplete.Post();
} while( !m_Done );
return 0;
}
sptr ExecuteTask();
};
//////////////////////////////////////////////////////////////////////////////////////////
// Our fundamental interlocking functions. All other useful interlocks can be derived
// from these little beasties!
extern long pcsx2_InterlockedExchange(volatile long* Target, long srcval);
extern long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp );
extern long pcsx2_InterlockedExchangeAdd( volatile long* target, long addval );
extern void AtomicExchange( volatile u32& Target, u32 value );
extern void AtomicExchangeAdd( volatile u32& Target, u32 value );
extern void AtomicIncrement( volatile u32& Target );

View File

@ -52,8 +52,7 @@ namespace Console
{
std::string m_format_buffer;
vssprintf( m_format_buffer, fmt, args );
m_format_buffer += "\n";
Write( wxString::FromUTF8( m_format_buffer.c_str() ) );
WriteLn( wxString::FromUTF8( m_format_buffer.c_str() ) );
}
__forceinline void __fastcall _WriteLn( Colors color, const char* fmt, va_list args )

View File

@ -104,29 +104,15 @@ namespace Exception
wxString Stream::FormatDiagnosticMessage() const
{
return wxsFormat(
L"Stream exception: %s\n\tObject name: %s",
L"Stream exception: %s\n\tFile/Object: %s",
m_message_diag.c_str(), StreamName.c_str()
) + m_stacktrace;
}
wxString Stream::FormatDisplayMessage() const
{
return m_message_user + L"\n\n" + StreamName;
}
// ------------------------------------------------------------------------
wxString FreezePluginFailure::FormatDiagnosticMessage() const
{
return wxsFormat(
L"%s plugin returned an error while %s the state.\n\n",
PluginName.c_str(),
FreezeAction.c_str()
) + m_stacktrace;
}
wxString FreezePluginFailure::FormatDisplayMessage() const
{
return m_message_user;
return m_message_user + L"\n\n" +
wxsFormat( _("Name: %s"), StreamName.c_str() );
}
// ------------------------------------------------------------------------

View File

@ -17,7 +17,6 @@
const wxRect wxDefaultRect( wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, wxDefaultCoord );
//////////////////////////////////////////////////////////////////////////////////////////
// Splits a string into parts and adds the parts into the given SafeList.
// This list is not cleared, so concatenating many splits into a single large list is
// the 'default' behavior, unless you manually clear the SafeList prior to subsequent calls.
@ -31,7 +30,13 @@ void SplitString( SafeList<wxString>& dest, const wxString& src, const wxString&
dest.Add( parts.GetNextToken() );
}
//////////////////////////////////////////////////////////////////////////////////////////
void SplitString( wxArrayString& dest, const wxString& src, const wxString& delims )
{
wxStringTokenizer parts( src, delims );
while( parts.HasMoreTokens() )
dest.Add( parts.GetNextToken() );
}
// Joins a list of strings into one larger string, using the given string concatenation
// character as a separator. If you want to be able to split the string later then the
// concatenation string needs to be a single character.
@ -48,7 +53,16 @@ void JoinString( wxString& dest, const SafeList<wxString>& src, const wxString&
}
}
//////////////////////////////////////////////////////////////////////////////////////////
void JoinString( wxString& dest, const wxArrayString& src, const wxString& separator )
{
for( int i=0, len=src.GetCount(); i<len; ++i )
{
if( i != 0 )
dest += separator;
dest += src[i];
}
}
// Attempts to parse and return a value for the given template type, and throws a ParseError
// exception if the parse fails. The template type can be anything that is supported/
// implemented via one of the TryParse() method overloads.
@ -65,9 +79,9 @@ T Parse( const wxString& src, const wxString& separators=L",")
}
//////////////////////////////////////////////////////////////////////////////////////////
// ToString helpers for wxString!
//
// --------------------------------------------------------------------------------------
// ToString helpers for wxString!
// --------------------------------------------------------------------------------------
// Converts a wxPoint into a comma-delimited string!
wxString ToString( const wxPoint& src, const wxString& separator )
@ -87,9 +101,9 @@ wxString ToString( const wxRect& src, const wxString& separator )
return ToString( src.GetLeftTop(), separator ) << separator << ToString( src.GetSize(), separator );
}
//////////////////////////////////////////////////////////////////////////////////////////
// Parse helpers for wxString!
//
// --------------------------------------------------------------------------------------
// Parse helpers for wxString!
// --------------------------------------------------------------------------------------
bool TryParse( wxPoint& dest, wxStringTokenizer& parts )
{

View File

@ -51,7 +51,7 @@ namespace Threading
// Extending classes should always implement their own thread closure process.
// This class must not be deleted from its own thread. That would be like marrying
// your sister, and then cheating on her with your daughter.
PersistentThread::~PersistentThread()
PersistentThread::~PersistentThread() throw()
{
if( !m_running ) return;
@ -202,8 +202,67 @@ namespace Threading
return (void*)owner.m_returncode;
}
// pthread Cond is an evil api that is not suited for Pcsx2 needs.
// Let's not use it. (Air)
// --------------------------------------------------------------------------------------
// BaseTaskThread Implementations
// --------------------------------------------------------------------------------------
// Tells the thread to exit and then waits for thread termination.
sptr BaseTaskThread::Block()
{
if( !IsRunning() ) return m_returncode;
m_Done = true;
m_sem_event.Post();
return PersistentThread::Block();
}
// Initiates the new task. This should be called after your own StartTask has
// initialized internal variables / preparations for task execution.
void BaseTaskThread::PostTask()
{
wxASSERT( !m_detached );
ScopedLock locker( m_lock_TaskComplete );
m_TaskPending = true;
m_post_TaskComplete.Reset();
m_sem_event.Post();
}
// Blocks current thread execution pending the completion of the parallel task.
void BaseTaskThread::WaitForResult()
{
if( m_detached || !m_running ) return;
if( m_TaskPending )
#ifdef wxUSE_GUI
m_post_TaskComplete.WaitGui();
#else
m_post_TaskComplete.Wait();
#endif
m_post_TaskComplete.Reset();
}
sptr BaseTaskThread::ExecuteTask()
{
while( !m_Done )
{
// Wait for a job -- or get a pthread_cancel. I'm easy.
m_sem_event.Wait();
Task();
m_lock_TaskComplete.Lock();
m_TaskPending = false;
m_post_TaskComplete.Post();
m_lock_TaskComplete.Unlock();
};
return 0;
}
// --------------------------------------------------------------------------------------
// pthread Cond is an evil api that is not suited for Pcsx2 needs.
// Let's not use it. (Air)
// --------------------------------------------------------------------------------------
#if 0
WaitEvent::WaitEvent()
{
@ -234,6 +293,10 @@ namespace Threading
}
#endif
// --------------------------------------------------------------------------------------
// Semaphore Implementations
// --------------------------------------------------------------------------------------
Semaphore::Semaphore()
{
sem_init( &sema, false, 0 );
@ -257,12 +320,19 @@ namespace Threading
// Valid on Win32 builds only!! Attempts to use it on Linux will result in unresolved
// external linker errors.
#if defined(_MSC_VER)
void Semaphore::Post( int multiple )
{
#if defined(_MSC_VER)
sem_post_multiple( &sema, multiple );
}
#else
// Only w32pthreads has the post_multiple, but it's easy enough to fake:
while( multiple > 0 )
{
multiple--;
sem_post( &sema );
}
#endif
}
#if wxUSE_GUI
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
@ -278,7 +348,7 @@ namespace Threading
// to handle messages.
do {
wxTheApp->ProcessPendingEvents();
wxTheApp->Yield();
} while( !Wait( ts_msec_250 ) );
}
}
@ -297,7 +367,7 @@ namespace Threading
// to handle messages.
do {
wxTheApp->ProcessPendingEvents();
wxTheApp->Yield();
if( Wait( ts_msec_250 ) ) break;
countdown -= ts_msec_250;
} while( countdown.GetMilliseconds() > 0 );
@ -342,6 +412,10 @@ namespace Threading
return retval;
}
// --------------------------------------------------------------------------------------
// MutexLock Implementations
// --------------------------------------------------------------------------------------
MutexLock::MutexLock()
{
int err = 0;
@ -383,74 +457,58 @@ namespace Threading
pthread_mutex_unlock( &mutex );
}
//////////////////////////////////////////////////////////////////////
// define some overloads for InterlockedExchanges
// for commonly used types, like u32 and s32.
__forceinline long pcsx2_InterlockedExchange( volatile long* target, long srcval )
{
return _InterlockedExchange( target, srcval );
}
__forceinline long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp )
{
// Use the pthreads-win32 implementation...
return _InterlockedCompareExchange( target, srcval, comp );
}
__forceinline long pcsx2_InterlockedExchangeAdd( volatile long* target, long srcval )
{
return _InterlockedExchangeAdd( target, srcval );
}
// --------------------------------------------------------------------------------------
// InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions)
// --------------------------------------------------------------------------------------
// define some overloads for InterlockedExchanges for commonly used types, like u32 and s32.
__forceinline void AtomicExchange( volatile u32& Target, u32 value )
{
pcsx2_InterlockedExchange( (volatile long*)&Target, value );
_InterlockedExchange( (volatile long*)&Target, value );
}
__forceinline void AtomicExchangeAdd( volatile u32& Target, u32 value )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value );
_InterlockedExchangeAdd( (volatile long*)&Target, value );
}
__forceinline void AtomicIncrement( volatile u32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
}
__forceinline void AtomicDecrement( volatile u32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
}
__forceinline void AtomicExchange( volatile s32& Target, s32 value )
{
pcsx2_InterlockedExchange( (volatile long*)&Target, value );
_InterlockedExchange( (volatile long*)&Target, value );
}
__forceinline void AtomicExchangeAdd( s32& Target, u32 value )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value );
_InterlockedExchangeAdd( (volatile long*)&Target, value );
}
__forceinline void AtomicIncrement( volatile s32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
}
__forceinline void AtomicDecrement( volatile s32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
}
__forceinline void _AtomicExchangePointer( const void ** target, const void* value )
{
pcsx2_InterlockedExchange( (volatile long*)target, (long)value );
_InterlockedExchange( (volatile long*)target, (long)value );
}
__forceinline void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand )
{
pcsx2_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand );
_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand );
}
}

View File

@ -483,7 +483,7 @@ struct Freeze_v10Compat
bool Spinning;
};
void SaveState::cdvdFreeze()
void SaveStateBase::cdvdFreeze()
{
FreezeTag( "cdvd" );
Freeze( cdvd );

View File

@ -961,7 +961,7 @@ void cdrReset() {
cdReadTime = (PSXCLK / 1757) * BIAS;
}
void SaveState::cdrFreeze()
void SaveStateBase::cdrFreeze()
{
FreezeTag( "cdrom" );
Freeze(cdr);

View File

@ -40,6 +40,5 @@
// Some homeless externs. This is as good a spot as any for now...
extern bool EmulationInProgress();
extern void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR);
extern u32 g_sseVUMXCSR, g_sseMXCSR;

View File

@ -46,13 +46,15 @@ enum PluginsEnum_t
u32 bitset; \
struct {
#define BITFIELD_END }; };
//------------ DEFAULT sseMXCSR VALUES ---------------
#define DEFAULT_sseMXCSR 0xffc0 //FPU rounding > DaZ, FtZ, "chop"
#define DEFAULT_sseVUMXCSR 0xffc0 //VU rounding > DaZ, FtZ, "chop"
/////////////////////////////////////////////////////////////////////////////////////////
// Pcsx2Config
//
// --------------------------------------------------------------------------------------
// Pcsx2Config class
// --------------------------------------------------------------------------------------
// This is intended to be a public class library between the core emulator and GUI only.
// It is *not* meant to be shared data between core emulation and plugins, due to issues
// with version incompatibilities if the structure formats are changed.
@ -75,7 +77,7 @@ public:
RecBlocks_IOP:1, // Enables per-block profiling for the IOP recompiler [unimplemented]
RecBlocks_VU0:1, // Enables per-block profiling for the VU0 recompiler [unimplemented]
RecBlocks_VU1:1; // Enables per-block profiling for the VU1 recompiler [unimplemented]
}; };
BITFIELD_END
// Default is Disabled, with all recs enabled underneath.
ProfilerOptions() : bitset( 0xfffffffe ) {}
@ -105,7 +107,7 @@ public:
bool
UseMicroVU0:1,
UseMicroVU1:1;
}; };
BITFIELD_END
// All recs are enabled by default.
RecompilerOptions() : bitset( 0xffffffff ) { }
@ -142,7 +144,7 @@ public:
fpuOverflow:1,
fpuExtraOverflow:1,
fpuFullMode:1;
}; };
BITFIELD_END
CpuOptions();
void LoadSave( IniInterface& conf );
@ -213,7 +215,7 @@ public:
DMAExeHack:1, // Fix for Fatal Frame; breaks Gust and Tri-Ace games.
XgKickHack:1, // Fix for Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics, but breaks Tri-ace games and others.
MpegHack:1; // Fix for Mana Khemia 1, breaks Digital Devil Saga.
}; };
BITFIELD_END
// all gamefixes are disabled by default.
GamefixOptions() : bitset( 0 ) {}
@ -240,7 +242,7 @@ public:
BIFC0:1, // enables BIFC0 detection and fast-forwarding
vuFlagHack:1, // microVU specific flag hack; Can cause Infinite loops, SPS, etc...
vuMinMax:1; // microVU specific MinMax hack; Can cause SPS, Black Screens, etc...
}; };
BITFIELD_END
u8 EECycleRate; // EE cycle rate selector (1.0, 1.5, 2.0)
u8 VUCycleSteal; // VU Cycle Stealer factor (0, 1, 2, or 3)
@ -275,7 +277,7 @@ public:
MultitapPort0_Enabled:1,
MultitapPort1_Enabled:1;
}; };
BITFIELD_END
CpuOptions Cpu;
VideoOptions Video;

View File

@ -798,7 +798,7 @@ __forceinline u32 rcntCycle(int index)
return counters[index].count;
}
void SaveState::rcntFreeze()
void SaveStateBase::rcntFreeze()
{
Freeze( counters );
Freeze( hsyncCounter );

View File

@ -40,57 +40,6 @@ static s64 m_iSlowTicks=0;
static bool m_justSkipped = false;
static bool m_StrictSkipping = false;
#ifdef PCSX2_DEVBUILD
// GS Playback
int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save
int g_nLeftGSFrames = 0; // when saving, number of frames left
gzSavingState* g_fGSSave;
// fixme - need to take this concept and make it MTGS friendly.
#ifdef _STGS_GSSTATE_CODE
void GSGIFTRANSFER1(u32 *pMem, u32 addr) {
if( g_SaveGSStream == 2) {
u32 type = GSRUN_TRANS1;
u32 size = (0x4000-(addr))/16;
g_fGSSave->Freeze( type );
g_fGSSave->Freeze( size );
g_fGSSave->FreezeMem( ((u8*)pMem)+(addr), size*16 );
}
GSgifTransfer1(pMem, addr);
}
void GSGIFTRANSFER2(u32 *pMem, u32 size) {
if( g_SaveGSStream == 2) {
u32 type = GSRUN_TRANS2;
u32 _size = size;
g_fGSSave->Freeze( type );
g_fGSSave->Freeze( size );
g_fGSSave->FreezeMem( pMem, _size*16 );
}
GSgifTransfer2(pMem, size);
}
void GSGIFTRANSFER3(u32 *pMem, u32 size) {
if( g_SaveGSStream == 2 ) {
u32 type = GSRUN_TRANS3;
u32 _size = size;
g_fGSSave->Freeze( type );
g_fGSSave->Freeze( size );
g_fGSSave->FreezeMem( pMem, _size*16 );
}
GSgifTransfer3(pMem, size);
}
__forceinline void GSVSYNC(void) {
if( g_SaveGSStream == 2 ) {
u32 type = GSRUN_VSYNC;
g_fGSSave->Freeze( type );
}
}
#endif
#endif
void _gs_ChangeTimings( u32 framerate, u32 iTicks )
{
m_iSlowStart = GetCPUTicks();
@ -615,7 +564,7 @@ void gsDynamicSkipEnable()
frameLimitReset();
}
void SaveState::gsFreeze()
void SaveStateBase::gsFreeze()
{
FreezeMem(PS2MEM_GS, 0x2000);
Freeze(CSRw);

View File

@ -135,9 +135,16 @@ enum GS_RINGTYPE
, GS_RINGTYPE_QUIT
};
struct MTGS_FreezeData
{
freezeData* fdata;
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
};
class mtgsThreadObject : public Threading::PersistentThread
{
friend class SaveState;
friend class SaveStateBase;
protected:
// Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s.
@ -192,6 +199,10 @@ protected:
#endif
// the MTGS "dummy" GIFtag info!
// fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
// separate paths. But for that to work properly we need also interlocked path sources.
// That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
// this is not emulated!
GIFPath m_path[3];
// contains aligned memory allocations for gs and Ringbuffer.
@ -203,7 +214,7 @@ protected:
public:
mtgsThreadObject();
virtual ~mtgsThreadObject();
virtual ~mtgsThreadObject() throw();
void Start();
void Cancel();
@ -222,13 +233,11 @@ public:
void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 );
u8* GetDataPacketPtr() const;
void Freeze( SaveState& state );
void Freeze( SaveStateBase& state );
void SetEvent();
void PostVsyncEnd( bool updategs );
void _close_gs();
protected:
// Saves MMX/XMM regs, posts an event to the mtgsThread flag and releases a timeslice.
// For use in surrounding loops that wait on the mtgs.
@ -331,11 +340,10 @@ enum gsrun
extern int g_SaveGSStream;
extern int g_nLeftGSFrames;
extern gzSavingState* g_fGSSave;
#endif
extern void SaveGSState(const wxString& file);
extern void LoadGSState(const wxString& file);
extern void RunGSState(gzLoadingState& f);
extern void RunGSState( memLoadingState& f );

View File

@ -19,6 +19,55 @@
#include <list>
#ifdef PCSX2_DEVBUILD
// GS Playback
int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save
int g_nLeftGSFrames = 0; // when saving, number of frames left
static wxScopedPtr<memSavingState> g_fGSSave;
// fixme - need to take this concept and make it MTGS friendly.
#ifdef _STGS_GSSTATE_CODE
void GSGIFTRANSFER1(u32 *pMem, u32 addr) {
if( g_SaveGSStream == 2) {
u32 type = GSRUN_TRANS1;
u32 size = (0x4000-(addr))/16;
g_fGSSave->Freeze( type );
g_fGSSave->Freeze( size );
g_fGSSave->FreezeMem( ((u8*)pMem)+(addr), size*16 );
}
GSgifTransfer1(pMem, addr);
}
void GSGIFTRANSFER2(u32 *pMem, u32 size) {
if( g_SaveGSStream == 2) {
u32 type = GSRUN_TRANS2;
u32 _size = size;
g_fGSSave->Freeze( type );
g_fGSSave->Freeze( size );
g_fGSSave->FreezeMem( pMem, _size*16 );
}
GSgifTransfer2(pMem, size);
}
void GSGIFTRANSFER3(u32 *pMem, u32 size) {
if( g_SaveGSStream == 2 ) {
u32 type = GSRUN_TRANS3;
u32 _size = size;
g_fGSSave->Freeze( type );
g_fGSSave->Freeze( size );
g_fGSSave->FreezeMem( pMem, _size*16 );
}
GSgifTransfer3(pMem, size);
}
__forceinline void GSVSYNC(void) {
if( g_SaveGSStream == 2 ) {
u32 type = GSRUN_VSYNC;
g_fGSSave->Freeze( type );
}
}
#endif
void SaveGSState(const wxString& file)
{
if( g_SaveGSStream ) return;
@ -26,7 +75,8 @@ void SaveGSState(const wxString& file)
Console::WriteLn( "Saving GS State..." );
Console::WriteLn( wxsFormat( L"\t%s", file.c_str() ) );
g_fGSSave = new gzSavingState( file );
SafeArray<u8> buf;
g_fGSSave.reset( new memSavingState( buf ) );
g_SaveGSStream = 1;
g_nLeftGSFrames = 2;
@ -37,46 +87,35 @@ void SaveGSState(const wxString& file)
void LoadGSState(const wxString& file)
{
int ret;
gzLoadingState* f;
Console::Status( "Loading GS State..." );
try
{
f = new gzLoadingState( file );
}
catch( Exception::FileNotFound& )
{
// file not found? try prefixing with sstates folder:
if( !Path::IsRelative( file ) )
{
//f = new gzLoadingState( Path::Combine( g_Conf->Folders.Savestates, file ) );
wxString src( file );
// If this load attempt fails, then let the exception bubble up to
// the caller to deal with...
}
}
/*if( !wxFileName::FileExists( src ) )
src = Path::Combine( g_Conf->Folders.Savestates, src );*/
if( !wxFileName::FileExists( src ) )
return;
SafeArray<u8> buf;
memLoadingState f( buf );
// Always set gsIrq callback -- GS States are always exclusionary of MTGS mode
GSirqCallback( gsIrq );
ret = GSopen(&pDsp, "PCSX2", 0);
if (ret != 0)
{
delete f;
throw Exception::PluginOpenError( PluginId_GS );
}
ret = PADopen((void *)&pDsp);
f->Freeze(g_nLeftGSFrames);
f->gsFreeze();
f.Freeze(g_nLeftGSFrames);
f.gsFreeze();
f->FreezePlugin( "GS", gsSafeFreeze );
GetPluginManager().Freeze( PluginId_GS, f );
RunGSState( *f );
delete( f );
RunGSState( f );
g_plugins->Close( PluginId_GS );
g_plugins->Close( PluginId_PAD );
@ -90,12 +129,12 @@ struct GSStatePacket
// runs the GS
// (this should really be part of the AppGui)
void RunGSState( gzLoadingState& f )
void RunGSState( memLoadingState& f )
{
u32 newfield;
std::list< GSStatePacket > packets;
while( !f.Finished() )
while( !f.IsFinished() )
{
int type, size;
f.Freeze( type );
@ -152,3 +191,77 @@ void RunGSState( gzLoadingState& f )
}
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
//
void vSyncDebugStuff( uint frame )
{
#ifdef OLD_TESTBUILD_STUFF
if( g_TestRun.enabled && g_TestRun.frame > 0 ) {
if( frame > g_TestRun.frame ) {
// take a snapshot
if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) {
if( g_TestRun.snapdone ) {
g_TestRun.curimage++;
g_TestRun.snapdone = 0;
g_TestRun.frame += 20;
if( g_TestRun.curimage >= g_TestRun.numimages ) {
// exit
g_EmuThread->Cancel();
}
}
else {
// query for the image
GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture);
}
}
else {
// exit
g_EmuThread->Cancel();
}
}
}
GSVSYNC();
if( g_SaveGSStream == 1 ) {
freezeData fP;
g_SaveGSStream = 2;
g_fGSSave->gsFreeze();
if (GSfreeze(FREEZE_SIZE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
fP.data = (s8*)malloc(fP.size);
if (fP.data == NULL) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
if (GSfreeze(FREEZE_SAVE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
g_fGSSave->Freeze( fP.size );
if (fP.size) {
g_fGSSave->FreezeMem( fP.data, fP.size );
free(fP.data);
}
}
}
}
}
else if( g_SaveGSStream == 2 ) {
if( --g_nLeftGSFrames <= 0 ) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
Console::WriteLn("Done saving GS stream");
}
}
#endif
}

View File

@ -101,15 +101,6 @@ static u32 WRITERING_DMA(u32 *pMem, u32 qwc)
int size = mtgsThread->PrepDataPacket(GIF_PATH_3, pMem, qwc);
u8* pgsmem = mtgsThread->GetDataPacketPtr();
/* check if page of endmem is valid (dark cloud2) */
// fixme: this hack makes no sense, because the giftagDummy will
// process the full length of bytes regardess of how much we copy.
// So you'd think if we're truncating the copy to prevent DEPs, we
// should truncate the gif packet size too.. (air)
// fixed? PrepDataPacket now returns the actual size of the packet.
// VIF handles scratchpad wrapping also, so this code shouldn't be needed anymore.
memcpy_aligned(pgsmem, pMem, size<<4);
mtgsThread->SendDataPacket();
@ -572,7 +563,7 @@ void gifMFIFOInterrupt()
clearFIFOstuff(false);
}
void SaveState::gifFreeze()
void SaveStateBase::gifFreeze()
{
FreezeTag( "GIFdma" );

View File

@ -195,7 +195,7 @@ void ReportIPU()
}
// fixme - ipuFreeze looks fairly broken. Should probably take a closer look at some point.
void SaveState::ipuFreeze()
void SaveStateBase::ipuFreeze()
{
IPUProcessInterrupt();

View File

@ -748,7 +748,7 @@ void psxRcntSetGates()
psxvblankgate &= ~(1<<3);
}
void SaveState::psxRcntFreeze()
void SaveStateBase::psxRcntFreeze()
{
FreezeTag( "iopCounters" );

View File

@ -199,7 +199,7 @@ u8 sio2_fifoOut(){
return 0; // No Data
}
void SaveState::sio2Freeze()
void SaveStateBase::sio2Freeze()
{
FreezeTag( "sio2" );
Freeze(sio2);

View File

@ -78,7 +78,7 @@ __forceinline void GIFPath::SetTag(const void* mem)
curreg = 0;
}
static void _mtgsFreezeGIF( SaveState& state, GIFPath (&paths)[3] )
static void _mtgsFreezeGIF( SaveStateBase& state, GIFPath (&paths)[3] )
{
for(int i=0; i<3; i++ )
{
@ -92,7 +92,7 @@ static void _mtgsFreezeGIF( SaveState& state, GIFPath (&paths)[3] )
}
}
void SaveState::mtgsFreeze()
void SaveStateBase::mtgsFreeze()
{
FreezeTag( "mtgs" );
mtgsThread->Freeze( *this );
@ -388,17 +388,12 @@ struct PacketTagType
extern bool renderswitch;
void mtgsThreadObject::_close_gs()
static void _clean_close_gs( void* obj )
{
if( g_plugins != NULL )
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
}
static void _clean_close_gs( void* obj )
{
((mtgsThreadObject*)obj)->_close_gs();
}
void mtgsThreadObject::_RingbufferLoop()
{
pthread_cleanup_push( _clean_close_gs, this );
@ -510,9 +505,9 @@ void mtgsThreadObject::_RingbufferLoop()
case GS_RINGTYPE_FREEZE:
{
freezeData* data = (freezeData*)(*(uptr*)&tag.data[1]);
MTGS_FreezeData* data = (MTGS_FreezeData*)(*(uptr*)&tag.data[1]);
int mode = tag.data[0];
GetPluginManager().Freeze( PluginId_GS, mode, data );
data->retval = GetPluginManager().DoFreeze( PluginId_GS, mode, data->fdata );
break;
}
@ -576,11 +571,17 @@ void mtgsThreadObject::_RingbufferLoop()
pthread_cleanup_pop( true );
}
static void dummyIrqCallback()
{
// dummy, because MTGS doesn't need this mess!
// (and zerogs does >_<)
}
sptr mtgsThreadObject::ExecuteTask()
{
memcpy_aligned( m_gsMem, PS2MEM_GS, sizeof(PS2MEM_GS) );
GSsetBaseMem( m_gsMem );
GSirqCallback( NULL );
GSirqCallback( dummyIrqCallback );
Console::WriteLn( (wxString)L"\t\tForced software switch: " + (renderswitch ? L"Enabled" : L"Disabled") );
m_returncode = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
@ -781,14 +782,6 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
jASSUME( size < m_RingBufferSize );
jASSUME( writepos < m_RingBufferSize );
//fixme: Vif sometimes screws up and size is unaligned, try this then (rama)
//Is this still a problem? It should be fixed on the specific VIF command now. (air)
//It seems to be fixed in Fatal Frame, leaving the code here still in case we get that again (rama)
/*if( (size&15) != 0){
Console::Error( "MTGS problem, size unaligned");
size = (size+15)&(~15);
}*/
m_packet_size = gifTransferDummy(pathidx, srcdata, size);
size = m_packet_size + 1; // takes into account our command qword.
@ -1009,7 +1002,7 @@ void mtgsThreadObject::GIFSoftReset( int mask )
mtgsThread->SendSimplePacket( GS_RINGTYPE_SOFTRESET, mask, 0, 0 );
}
void mtgsThreadObject::Freeze( SaveState& state )
void mtgsThreadObject::Freeze( SaveStateBase& state )
{
_mtgsFreezeGIF( state, this->m_path );
}

View File

@ -593,6 +593,34 @@ wxString Exception::PluginError::FormatDisplayMessage() const
return wxsFormat( m_message_user, tbl_PluginInfo[PluginId].GetShortname().c_str() );
}
wxString Exception::FreezePluginFailure::FormatDiagnosticMessage() const
{
return wxsFormat(
L"%s plugin returned an error while saving the state.\n\n",
tbl_PluginInfo[PluginId].shortname
) + m_stacktrace;
}
wxString Exception::FreezePluginFailure::FormatDisplayMessage() const
{
// [TODO]
return m_message_user;
}
wxString Exception::ThawPluginFailure::FormatDiagnosticMessage() const
{
return wxsFormat(
L"%s plugin returned an error while loading the state.\n\n",
tbl_PluginInfo[PluginId].shortname
) + m_stacktrace;
}
wxString Exception::ThawPluginFailure::FormatDisplayMessage() const
{
// [TODO]
return m_message_user;
}
// --------------------------------------------------------------------------------------
// PCSX2 Callbacks passed to Plugins
// --------------------------------------------------------------------------------------
@ -702,6 +730,8 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
// fixme: use plugin's GetLastError (not implemented yet!)
throw Exception::PluginLoadError( PluginId_Mcd, wxEmptyString, "Internal Memorycard Plugin failed to load." );
}
g_plugins = this;
}
PluginManager::~PluginManager()
@ -713,6 +743,9 @@ PluginManager::~PluginManager()
}
DESTRUCTOR_CATCHALL
// All library unloading done automatically.
if( g_plugins == this )
g_plugins = NULL;
}
void PluginManager::BindCommon( PluginsEnum_t pid )
@ -890,7 +923,7 @@ void PluginManager::Open()
void PluginManager::Close( PluginsEnum_t pid )
{
if( !m_info[pid].IsOpened ) return;
DevCon::Status( "\tClosing %s", tbl_PluginInfo[pid].shortname );
Console::Status( "\tClosing %s", tbl_PluginInfo[pid].shortname );
if( pid == PluginId_GS )
{
@ -910,7 +943,7 @@ void PluginManager::Close( PluginsEnum_t pid )
void PluginManager::Close( bool closegs )
{
Console::Status( "Closing plugins..." );
DbgCon::Status( "Closing plugins..." );
// Close plugins in reverse order of the initialization procedure.
@ -920,7 +953,7 @@ void PluginManager::Close( bool closegs )
Close( tbl_PluginInfo[i].id );
}
Console::Status( "Plugins closed successfully." );
DbgCon::Status( "Plugins closed successfully." );
}
// Initializes all plugins. Plugin initialization should be done once for every new emulation
@ -974,8 +1007,8 @@ void PluginManager::Init()
void PluginManager::Shutdown()
{
Close();
DbgCon::Status( "Shutting down plugins..." );
Console::Status( "Shutting down plugins..." );
// Shutdown plugins in reverse order (probably doesn't matter...
// ... but what the heck, right?)
@ -990,52 +1023,82 @@ void PluginManager::Shutdown()
// More memorycard hacks!!
if( EmuPlugins.Mcd != NULL && m_mcdPlugin != NULL )
if( (EmuPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) )
{
m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)EmuPlugins.Mcd );
EmuPlugins.Mcd = NULL;
}
Console::Status( "Plugins shutdown successfully." );
DbgCon::Status( "Plugins shutdown successfully." );
}
void PluginManager::Freeze( PluginsEnum_t pid, int mode, freezeData* data )
// For internal use only, unless you're the MTGS. Then it's for you too!
// Returns false if the plugin returned an error.
bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
{
m_info[pid].CommonBindings.Freeze( mode, data );
}
// ----------------------------------------------------------------------------
// Thread Safety:
// This function should only be called by the Main GUI thread and the GS thread (for GS states only),
// as it has special handlers to ensure that GS freeze commands are executed appropriately on the
// GS thread.
//
void PluginManager::Freeze( PluginsEnum_t pid, SaveState& state )
{
if( pid == PluginId_GS && wxThread::IsMain() )
if( (pid == PluginId_GS) && wxThread::IsMain() )
{
// Need to send the GS freeze request on the GS thread.
MTGS_FreezeData woot = { data, 0 };
// GS needs some thread safety love...
mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &woot );
mtgsWaitGS();
return woot.retval != -1;
}
else
{
state.FreezePlugin( tbl_PluginInfo[pid].shortname, m_info[pid].CommonBindings.Freeze );
return m_info[pid].CommonBindings.Freeze( mode, data ) != -1;
}
}
// ----------------------------------------------------------------------------
// This overload of Freeze performs savestate freeze operation on *all* plugins,
// as according to the order in PluignsEnum_t.
//
// Thread Safety:
// This function should only be called by the Main GUI thread and the GS thread (for GS states only),
// as it has special handlers to ensure that GS freeze commands are executed appropriately on the
// GS thread.
//
void PluginManager::Freeze( SaveState& state )
void PluginManager::Freeze( PluginsEnum_t pid, SaveStateBase& state )
{
const PluginInfo* pi = tbl_PluginInfo-1;
while( ++pi, pi->shortname != NULL )
Freeze( pi->id, state );
Console::WriteLn( "\t%s %s", state.IsSaving() ? "Saving" : "Loading",
tbl_PluginInfo[pid].shortname );
freezeData fP = { 0, NULL };
if( !DoFreeze( pid, FREEZE_SIZE, &fP ) )
fP.size = 0;
int fsize = fP.size;
state.Freeze( fsize );
if( state.IsLoading() && (fsize == 0) )
{
// no state data to read, but the plugin expects some state data.
// Issue a warning to console...
if( fP.size != 0 )
Console::Notice( "\tWarning: No data for this plugin was found. Plugin status may be unpredictable." );
return;
// Note: Size mismatch check could also be done here on loading, but
// some plugins may have built-in version support for non-native formats or
// older versions of a different size... or could give different sizes depending
// on the status of the plugin when loading, so let's ignore it.
}
fP.size = fsize;
if( fP.size == 0 ) return;
state.PrepBlock( fP.size );
fP.data = (s8*)state.GetBlockPtr();
if( state.IsSaving() )
{
if( !DoFreeze(pid, FREEZE_SAVE, &fP) )
throw Exception::FreezePluginFailure( pid );
}
else
{
if( !DoFreeze(pid, FREEZE_LOAD, &fP) )
throw Exception::ThawPluginFailure( pid );
}
state.CommitBlock( fP.size );
}
bool PluginManager::KeyEvent( const keyEvent& evt )
@ -1062,9 +1125,7 @@ void PluginManager::Configure( PluginsEnum_t pid )
//
PluginManager* PluginManager_Create( const wxString (&folders)[PluginId_Count] )
{
PluginManager* retval = new PluginManager( folders );
retval->Init();
return retval;
return new PluginManager( folders );
}
PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] )

View File

@ -106,6 +106,39 @@ namespace Exception
PluginId = pid;
}
};
// This exception is thrown when a plugin returns an error while trying to save itself.
// Typically this should be a very rare occurance since a plugin typically shoudn't
// be doing memory allocations or file access during state saving.
//
class FreezePluginFailure : public virtual PluginError
{
public:
DEFINE_EXCEPTION_COPYTORS( FreezePluginFailure )
explicit FreezePluginFailure( PluginsEnum_t pid)
{
PluginId = pid;
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
class ThawPluginFailure : public virtual PluginError, public virtual BadSavedState
{
public:
DEFINE_EXCEPTION_COPYTORS( ThawPluginFailure )
explicit ThawPluginFailure( PluginsEnum_t pid )
{
PluginId = pid;
}
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
};
// --------------------------------------------------------------------------------------
@ -140,7 +173,7 @@ struct LegacyPluginAPI_Common
}
};
class SaveState;
class SaveStateBase;
class mtgsThreadObject;
// --------------------------------------------------------------------------------------
@ -201,9 +234,12 @@ public:
virtual void Close( PluginsEnum_t pid ) {}
virtual void Close( bool closegs=true ) {}
virtual void Freeze( PluginsEnum_t pid, int mode, freezeData* data ) { wxASSERT_MSG( false, L"Null PluginManager!" ); }
virtual void Freeze( PluginsEnum_t pid, SaveState& state ) { wxASSERT_MSG( false, L"Null PluginManager!" ); }
virtual void Freeze( SaveState& state ) { wxASSERT_MSG( false, L"Null PluginManager!" ); }
virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state ) { wxASSERT_MSG( false, L"Null PluginManager!" ); }
virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
{
wxASSERT_MSG( false, L"Null PluginManager!" );
return false;
}
virtual bool KeyEvent( const keyEvent& evt ) { return false; }
};
@ -238,9 +274,11 @@ protected:
bool m_initialized;
PluginStatus_t m_info[PluginId_Count];
const PS2E_LibraryAPI* m_mcdPlugin;
public: // hack until we unsuck plugins...
PluginStatus_t m_info[PluginId_Count];
public:
virtual ~PluginManager();
@ -251,9 +289,8 @@ public:
void Close( PluginsEnum_t pid );
void Close( bool closegs=true );
void Freeze( PluginsEnum_t pid, int mode, freezeData* data );
void Freeze( PluginsEnum_t pid, SaveState& state );
void Freeze( SaveState& state );
void Freeze( PluginsEnum_t pid, SaveStateBase& state );
bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data );
bool KeyEvent( const keyEvent& evt );
void Configure( PluginsEnum_t pid );

View File

@ -83,6 +83,7 @@ typedef int BOOL;
#include "i18n.h"
#include "Config.h"
#include "Utilities/wxBaseTools.h"
#include "Utilities/ScopedPtr.h"
#include "Utilities/Path.h"
#include "Utilities/Console.h"
#include "Utilities/Exceptions.h"

View File

@ -16,128 +16,33 @@
#include "PrecompiledHeader.h"
#include "Common.h"
#include "ps2/CoreEmuThread.h"
#include "App.h"
#include "HostGui.h"
//////////////////////////////////////////////////////////////////////////////////////////
// RecoverySystem.cpp -- houses code for recovering from on-the-fly changes to the emu
// configuration, and for saving/restoring the GS state (for more seamless exiting of
// fullscreen GS operation).
//
// The following handful of local classes are implemented att he bottom of this file.
static SafeArray<u8>* g_RecoveryState = NULL;
static SafeArray<u8>* g_gsRecoveryState = NULL;
// This class type creates a memory savestate using the existing Recovery information
// (if present) to generate the savestate material. If no recovery data is present,
// the current emulation state is used instead.
class RecoveryMemSavingState : public memSavingState, Sealed
{
public:
virtual ~RecoveryMemSavingState() { }
RecoveryMemSavingState();
void gsFreeze();
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) );
};
// This class type creates an on-disk (zipped) savestate using the existing Recovery
// information (if present) to generate the savestate material. If no recovery data is
// present, the current emulation state is used instead.
class RecoveryZipSavingState : public gzSavingState, Sealed
{
public:
virtual ~RecoveryZipSavingState() { }
RecoveryZipSavingState( const wxString& filename );
void gsFreeze();
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) );
};
// Special helper class used to save *just* the GS-relevant state information.
class JustGsSavingState : public memSavingState, Sealed
{
public:
virtual ~JustGsSavingState() { }
JustGsSavingState();
// This special override saves the gs info to m_idx+4, and then goes back and
// writes in the length of data saved.
void gsFreeze();
};
static wxScopedPtr<SafeArray<u8>> g_RecoveryState;
namespace StateRecovery {
bool HasState()
{
return g_RecoveryState != NULL || g_gsRecoveryState != NULL;
return g_RecoveryState;
}
void Recover()
{
wxASSERT( g_EmuThread != NULL );
wxASSERT( g_EmuThread->IsSelf() );
if( !g_RecoveryState ) return;
if( g_RecoveryState != NULL )
{
Console::Status( "Resuming execution from full memory state..." );
memLoadingState( *g_RecoveryState ).FreezeAll();
}
else if( g_gsRecoveryState != NULL )
{
s32 dummylen;
Console::Status( "Resuming execution from gsState..." );
memLoadingState eddie( *g_gsRecoveryState );
eddie.FreezePlugin( "GS", gsSafeFreeze );
eddie.Freeze( dummylen ); // reads the length value recorded earlier.
eddie.gsFreeze();
}
Console::Status( "Resuming execution from full memory state..." );
memLoadingState( *g_RecoveryState ).FreezeAll();
StateRecovery::Clear();
SysClearExecutionCache();
if( GSsetGameCRC != NULL )
GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
}
// Saves recovery state info to the given filename, or saves the active emulation state
// (if one exists) and no recovery data was found. This is needed because when a recovery
// state is made, the emulation state is usually reset so the only persisting state is
// the one in the memory save. :)
//
// Threading Notes:
// This function can be invoked by any thread. However, if it is run outside the context
// of a CoreEmuThred then it
void SaveToFile( const wxString& file )
{
SysSuspend();
if( g_RecoveryState == NULL )
{
RecoveryMemSavingState().FreezeAll();
}
SysResume();
if( g_RecoveryState != NULL )
{
gzFile fileptr = gzopen( file.ToAscii().data(), "wb" );
if( fileptr == NULL )
throw Exception::CreateStream( file, "General savestate file creation error." );
gzwrite( fileptr, &g_SaveVersion, sizeof( u32 ) );
gzwrite( fileptr, g_RecoveryState->GetPtr(), g_RecoveryState->GetSizeInBytes() );
gzclose( fileptr );
}
else
{
if( !EmulationInProgress() ) return;
RecoveryZipSavingState( file ).FreezeAll();
}
SafeArray<u8> buf;
memSavingState( buf ).FreezeAll();
}
// Saves recovery state info to the given saveslot, or saves the active emulation state
@ -146,40 +51,22 @@ namespace StateRecovery {
// the one in the memory save. :)
void SaveToSlot( uint num )
{
SaveToFile( SaveState::GetFilename( num ) );
SaveToFile( SaveStateBase::GetFilename( num ) );
}
// This method will override any existing recovery states, so call it with caution, if you
// think that there could be existing important state info in the recovery buffers (but
// really there shouldn't be, unless you're calling this function when it's not intended
// to be called).
void MakeGsOnly()
{
StateRecovery::Clear();
if( !EmulationInProgress() ) return;
g_gsRecoveryState = new SafeArray<u8>();
JustGsSavingState eddie;
eddie.FreezePlugin( "GS", gsSafeFreeze ) ;
eddie.gsFreeze();
}
// 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 != NULL ) return;
if( g_RecoveryState ) return;
if( !EmulationInProgress() ) return;
SysSuspend();
try
{
g_RecoveryState = new SafeArray<u8>( L"Memory Savestate Recovery" );
RecoveryMemSavingState().FreezeAll();
safe_delete( g_gsRecoveryState );
g_RecoveryState.reset( new SafeArray<u8>( L"Memory Savestate Recovery" ) );
memSavingState( *g_RecoveryState ).FreezeAll();
}
catch( Exception::RuntimeError& ex )
{
@ -189,101 +76,13 @@ namespace StateRecovery {
L"able to recover if you make changes to your PCSX2 configuration.\n\n"
L"Details: %s", ex.FormatDisplayMessage().c_str() )
);
safe_delete( g_RecoveryState );
g_RecoveryState.reset();
}
}
// Clears and deallocates any recovery states.
void Clear()
{
safe_delete( g_RecoveryState );
safe_delete( g_gsRecoveryState );
}
}
RecoveryMemSavingState::RecoveryMemSavingState() : memSavingState( *g_RecoveryState )
{
}
void RecoveryMemSavingState::gsFreeze()
{
if( g_gsRecoveryState != NULL )
{
// just copy the data from src to dst.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
const u32 pluginlen = *((u32*)g_gsRecoveryState->GetPtr());
const u32 gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(pluginlen+8), gslen );
m_idx += gslen;
}
else
memSavingState::gsFreeze();
}
void RecoveryMemSavingState::FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) )
{
// Gs data is already in memory, so just copy from src to dest:
// length of the GS data is stored as the first u32, so use that to run the copy:
const u32 len = *((u32*)g_gsRecoveryState->GetPtr());
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(), len+4 );
m_idx += len+4;
}
else
memSavingState::FreezePlugin( name, freezer );
}
RecoveryZipSavingState::RecoveryZipSavingState( const wxString& filename ) : gzSavingState( filename )
{
}
void RecoveryZipSavingState::gsFreeze()
{
if( g_gsRecoveryState != NULL )
{
// read data from the gsRecoveryState allocation instead of the GS, since the gs
// info was invalidated when the plugin was shut down.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
u32& pluginlen = *((u32*)g_gsRecoveryState->GetPtr(0));
u32& gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
gzwrite( m_file, g_gsRecoveryState->GetPtr(pluginlen+4), gslen );
}
else
gzSavingState::gsFreeze();
}
void RecoveryZipSavingState::FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) )
{
// Gs data is already in memory, so just copy from there into the gzip file.
// length of the GS data is stored as the first u32, so use that to run the copy:
u32& len = *((u32*)g_gsRecoveryState->GetPtr());
gzwrite( m_file, g_gsRecoveryState->GetPtr(), len+4 );
}
else
gzSavingState::FreezePlugin( name, freezer );
}
JustGsSavingState::JustGsSavingState() : memSavingState( *g_gsRecoveryState )
{
}
// This special override saves the gs info to m_idx+4, and then goes back and
// writes in the length of data saved.
void JustGsSavingState::gsFreeze()
{
int oldmidx = m_idx;
m_idx += 4;
memSavingState::gsFreeze();
if( IsSaving() )
{
s32& len = *((s32*)m_memory.GetPtr( oldmidx ));
len = (m_idx - oldmidx)-4;
g_RecoveryState.reset();
}
}

View File

@ -437,7 +437,7 @@ void SPRTOinterrupt()
hwDmacIrq(DMAC_TO_SPR);
}
void SaveState::sprFreeze()
void SaveStateBase::sprFreeze()
{
FreezeTag("SPRdma");

View File

@ -34,9 +34,6 @@
using namespace R5900;
extern void recResetEE();
extern void recResetIOP();
static void PreLoadPrep()
{
SysClearExecutionCache();
@ -49,38 +46,45 @@ static void PostLoadPrep()
for(int i=0; i<48; i++) MapTLB(i);
}
wxString SaveState::GetFilename( int slot )
wxString SaveStateBase::GetFilename( int slot )
{
return (g_Conf->Folders.Savestates +
wxsFormat( L"%8.8X.%3.3d", ElfCRC, slot )).GetFullPath();
}
SaveState::SaveState( const char* msg, const wxString& destination ) :
m_version( g_SaveVersion )
, m_tagspace( 128 )
SaveStateBase::SaveStateBase( SafeArray<u8>& memblock ) :
m_memory( memblock )
, m_version( g_SaveVersion )
, m_idx( 0 )
, m_sectid( FreezeId_Unknown )
, m_pid( PluginId_GS )
, m_DidBios( false )
{
Console::WriteLn( "%s %s", msg, destination.ToAscii().data() );
}
s32 CALLBACK gsSafeFreeze( int mode, freezeData *data )
void SaveStateBase::PrepBlock( int size )
{
// have to call in the GS thread, otherwise weird stuff will start happening
mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, data );
mtgsWaitGS();
return 0;
}
void SaveState::FreezeTag( const char* src )
{
const int length = strlen( src );
m_tagspace.MakeRoomFor( length+1 );
strcpy( m_tagspace.GetPtr(), src );
FreezeMem( m_tagspace.GetPtr(), length );
if( strcmp( m_tagspace.GetPtr(), src ) != 0 )
const int end = m_idx+size;
if( IsSaving() )
m_memory.MakeRoomFor( end );
else
{
assert( 0 );
if( m_memory.GetSizeInBytes() <= end )
throw Exception::BadSavedState();
}
}
void SaveStateBase::FreezeTag( const char* src )
{
wxASSERT( strlen(src) < (sizeof( m_tagspace )-1) );
memzero_obj( m_tagspace );
strcpy( m_tagspace, src );
Freeze( m_tagspace );
if( strcmp( m_tagspace, src ) != 0 )
{
wxASSERT_MSG( false, L"Savestate data corruption detected while reading tag" );
throw Exception::BadSavedState(
// Untranslated diagnostic msg (use default msg for translation)
L"Savestate data corruption detected while reading tag: " + wxString::FromAscii(src)
@ -88,48 +92,68 @@ void SaveState::FreezeTag( const char* src )
}
}
void SaveState::FreezeAll()
void SaveStateBase::FreezeBios()
{
if( IsLoading() )
PreLoadPrep();
// Check the BIOS, and issue a warning if the bios for this state
// doesn't match the bios currently being used (chances are it'll still
// work fine, but some games are very picky).
char descin[128];
wxString descout;
IsBIOS( g_Conf->FullpathToBios(), descout );
memcpy_fast( descin, descout.ToAscii().data(), 128 );
Freeze( descin );
if( memcmp( descin, descout, 128 ) != 0 )
{
Console::Error(
"\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n"
"\t\tCurrent BIOS: %s\n"
"\t\tSavestate BIOS: %s\n",
descout.ToAscii().data(), descin
);
}
// ... and only freeze bios info once per state, since the user msg could
// become really annoying on a corrupted state or something. (have to always
// load though, so that we advance past the duplicated info, if present)
if( IsLoading() || !m_DidBios )
Freeze( descin );
if( !m_DidBios )
{
if( memcmp( descin, descout, 128 ) != 0 )
{
Console::Error(
"\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n"
"\t\tCurrent Version: %s\n"
"\t\tSavestate Version: %s\n",
descout.ToAscii().data(), descin
);
}
}
m_DidBios = true;
}
static const int MainMemorySizeInBytes =
Ps2MemSize::Base + Ps2MemSize::Scratch + Ps2MemSize::Hardware +
Ps2MemSize::IopRam + Ps2MemSize::IopHardware + 0x0100;
void SaveStateBase::FreezeMainMemory()
{
// First Block - Memory Dumps
// ---------------------------
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad
FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad
FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory
FreezeMem(psxM, Ps2MemSize::IopRam); // 2 MB main memory
FreezeMem(psxH, Ps2MemSize::IopHardware); // hardware memory
FreezeMem(psxS, 0x000100); // iop's sif memory
}
void SaveStateBase::FreezeRegisters()
{
if( IsLoading() )
PreLoadPrep();
// Second Block - Various CPU Registers and States
// -----------------------------------------------
FreezeTag( "cpuRegs" );
Freeze(cpuRegs); // cpu regs + COP0
Freeze(psxRegs); // iop regs
Freeze(cpuRegs); // cpu regs + COP0
Freeze(psxRegs); // iop regs
Freeze(fpuRegs);
Freeze(tlb); // tlbs
Freeze(tlb); // tlbs
// Third Block - Cycle Timers and Events
// -------------------------------------
@ -143,6 +167,7 @@ void SaveState::FreezeAll()
// Fourth Block - EE-related systems
// ---------------------------------
FreezeTag( "EE-Subsystems" );
rcntFreeze();
gsFreeze();
vuMicroFreeze();
@ -155,154 +180,172 @@ void SaveState::FreezeAll()
// Fifth Block - iop-related systems
// ---------------------------------
FreezeTag( "IOP-Subsystems" );
psxRcntFreeze();
sioFreeze();
sio2Freeze();
cdrFreeze();
cdvdFreeze();
// Sixth Block - Plugins Galore!
// -----------------------------
FreezePlugin( "GS", gsSafeFreeze );
g_plugins->Freeze( *this );
if( IsLoading() )
PostLoadPrep();
}
/////////////////////////////////////////////////////////////////////////////
// gzipped to/from disk state saves implementation
gzBaseStateInfo::gzBaseStateInfo( const char* msg, const wxString& filename ) :
SaveState( msg, filename )
, m_filename( filename )
, m_file( NULL )
bool SaveStateBase::FreezeSection()
{
}
Freeze( m_sectid );
gzBaseStateInfo::~gzBaseStateInfo()
{
if( m_file != NULL )
switch( m_sectid )
{
gzclose( m_file );
m_file = NULL;
case FreezeId_End:
return false;
case FreezeId_Bios:
{
int sectlen = 128;
FreezeTag( "BiosVersion" );
Freeze( sectlen );
if( sectlen != MainMemorySizeInBytes )
{
throw Exception::BadSavedState( wxEmptyString,
L"Invalid size encountered on BiosVersion section.",
_("The savestate data is invalid or corrupted.")
);
}
FreezeBios();
m_sectid++;
}
break;
case FreezeId_Memory:
{
FreezeTag( "MainMemory" );
int sectlen = MainMemorySizeInBytes;
Freeze( sectlen );
if( sectlen != MainMemorySizeInBytes )
{
throw Exception::BadSavedState( wxEmptyString,
L"Invalid size encountered on MainMemory section.",
_("The savestate data is invalid or corrupted.")
);
}
FreezeMainMemory();
m_sectid++;
}
break;
case FreezeId_Registers:
{
FreezeTag( "HardwareRegisters" );
int seekpos = m_idx;
int sectsize;
Freeze( sectsize );
FreezeRegisters();
int realsectsize = m_idx - seekpos;
if( IsSaving() )
{
// write back the section length...
*((u32*)m_memory.GetPtr(seekpos)) = realsectsize - 4;
}
else // IsLoading!!
{
if( sectsize != realsectsize ) // if they don't match then we have a problem, jim.
{
throw Exception::BadSavedState( wxEmptyString,
L"Invalid size encountered on HardwareRegisters section.",
_("The savestate data is invalid or corrupted.")
);
}
}
}
break;
case FreezeId_Plugin:
{
FreezeTag( "Plugin" );
int seekpos = m_idx;
int sectsize;
Freeze( sectsize );
Freeze( m_pid );
g_plugins->Freeze( (PluginsEnum_t)m_pid, *this );
int realsectsize = m_idx - seekpos;
if( IsSaving() )
{
// write back the section length...
*((u32*)m_memory.GetPtr(seekpos)) = realsectsize - 4;
}
else
{
if( sectsize != realsectsize ) // if they don't match then we have a problem, jim.
{
throw Exception::BadSavedState( wxEmptyString,
L"Invalid size encountered on Plugin section.",
_("The savestate data is invalid or corrupted.")
);
}
}
// following increments only affect Saving mode, are ignored by Loading mode.
m_pid++;
if( m_pid > PluginId_Count )
m_sectid++;
}
break;
case FreezeId_Unknown:
default:
if( IsSaving() )
m_sectid = FreezeId_End;
else
{
// Skip unknown sections with a warning log.
// Maybe it'll work! (haha?)
int size;
Freeze( m_tagspace );
Freeze( size );
m_tagspace[sizeof(m_tagspace)-1] = 0;
Console::Notice(
"Warning: Unknown tag encountered while loading savestate; going to ignore it!\n"
"\tTagname: %s, Size: %d", m_tagspace, size
);
m_idx += size;
}
break;
}
return true;
}
gzSavingState::gzSavingState( const wxString& filename ) :
gzBaseStateInfo( "Saving state to: ", filename )
void SaveStateBase::FreezeAll()
{
m_file = gzopen(filename.ToAscii().data(), "wb");
if( m_file == NULL )
throw Exception::FileNotFound();
m_sectid = (int)FreezeId_End+1;
m_pid = PluginId_GS;
gzsetparams( m_file, Z_BEST_SPEED, Z_DEFAULT_STRATEGY );
Freeze( m_version );
}
gzLoadingState::gzLoadingState( const wxString& filename ) :
gzBaseStateInfo( "Loading state from: ", filename )
{
m_file = gzopen(filename.ToAscii().data(), "rb");
if( m_file == NULL )
throw Exception::FileNotFound();
gzread( m_file, &m_version, 4 );
if( (m_version >> 16) != (g_SaveVersion >> 16) )
{
Console::Error(
"Savestate load aborted:\n"
"\tUnknown or invalid savestate identifier, either from a (very!) old version of\n"
"\tPcsx2, or the file is corrupted"
);
throw Exception::UnsupportedStateVersion( m_version );
}
else if( m_version > g_SaveVersion )
{
Console::Error(
"Savestate load aborted:\n"
"\tThe savestate was created with a newer version of Pcsx2. I don't know how to load it!" );
throw Exception::UnsupportedStateVersion( m_version );
}
}
gzLoadingState::~gzLoadingState() { }
void gzSavingState::FreezeMem( void* data, int size )
{
gzwrite( m_file, data, size );
}
void gzLoadingState::FreezeMem( void* data, int size )
{
if( gzread( m_file, data, size ) != size )
throw Exception::BadSavedState( m_filename );
}
void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
{
freezeData fP = { 0, NULL };
Console::WriteLn( "\tSaving %s", name );
FreezeTag( name );
if (freezer(FREEZE_SIZE, &fP) == -1)
throw Exception::FreezePluginFailure( name, "saving" );
Freeze( fP.size );
if( fP.size == 0 ) return;
SafeArray<s8> buffer( fP.size );
fP.data = buffer.GetPtr();
if(freezer(FREEZE_SAVE, &fP) == -1)
throw Exception::FreezePluginFailure( name, "saving" );
FreezeMem( fP.data, fP.size );
}
void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
{
freezeData fP = { 0, NULL };
Console::WriteLn( "\tLoading %s", name );
FreezeTag( name );
Freeze( fP.size );
if( fP.size == 0 ) return;
SafeArray<s8> buffer( fP.size );
fP.data = buffer.GetPtr();
FreezeMem( fP.data, fP.size );
if(freezer(FREEZE_LOAD, &fP) == -1)
throw Exception::FreezePluginFailure( name, "loading" );
while( FreezeSection() ) ;
}
//////////////////////////////////////////////////////////////////////////////////
// uncompressed to/from memory state saves implementation
memBaseStateInfo::memBaseStateInfo( SafeArray<u8>& memblock, const char* msg ) :
SaveState( msg, L"Memory")
, m_memory( memblock )
, m_idx( 0 )
{
// Always clear the MTGS thread state.
mtgsWaitGS();
}
memSavingState::memSavingState( SafeArray<u8>& save_to ) : memBaseStateInfo( save_to, "Saving state to: " )
memSavingState::memSavingState( SafeArray<u8>& save_to ) :
SaveStateBase( save_to )
{
save_to.ChunkSize = ReallocThreshold;
save_to.MakeRoomFor( MemoryBaseAllocSize );
}
// Saving of state data to a memory buffer
// Saving of state data
void memSavingState::FreezeMem( void* data, int size )
{
const int end = m_idx+size;
@ -315,14 +358,14 @@ void memSavingState::FreezeMem( void* data, int size )
dest[m_idx] = *src;
}
memLoadingState::memLoadingState(SafeArray<u8>& load_from ) :
memBaseStateInfo( load_from, "Loading state from: " )
memLoadingState::memLoadingState( const SafeArray<u8>& load_from ) :
SaveStateBase( const_cast<SafeArray<u8>&>(load_from) )
{
}
memLoadingState::~memLoadingState() { }
// Loading of state data from a memory buffer...
// Loading of state data
void memLoadingState::FreezeMem( void* data, int size )
{
const int end = m_idx+size;
@ -332,45 +375,3 @@ void memLoadingState::FreezeMem( void* data, int size )
for( ; m_idx<end; ++m_idx, ++dest )
*dest = src[m_idx];
}
void memSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
{
freezeData fP = { 0, NULL };
Console::WriteLn( "\tSaving %s", name );
if( freezer(FREEZE_SIZE, &fP) == -1 )
throw Exception::FreezePluginFailure( name, "saving" );
Freeze( fP.size );
if( fP.size == 0 ) return;
const int end = m_idx+fP.size;
m_memory.MakeRoomFor( end );
fP.data = ((s8*)m_memory.GetPtr()) + m_idx;
if(freezer(FREEZE_SAVE, &fP) == -1)
throw Exception::FreezePluginFailure( name, "saving" );
m_idx += fP.size;
}
void memLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
{
freezeData fP;
Console::WriteLn( "\tLoading %s", name );
Freeze( fP.size );
if( fP.size == 0 ) return;
if( ( fP.size + m_idx ) > m_memory.GetSizeInBytes() )
{
assert(0);
throw Exception::BadSavedState( L"memory");
}
fP.data = ((s8*)m_memory.GetPtr()) + m_idx;
if(freezer(FREEZE_LOAD, &fP) == -1)
throw Exception::FreezePluginFailure( name, "loading" );
m_idx += fP.size;
}

View File

@ -13,8 +13,7 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SAVESTATE_H_
#define _SAVESTATE_H_
#pragma once
// This shouldn't break Win compiles, but it does.
#ifdef __LINUX__
@ -28,25 +27,53 @@
// the lower 16 bit value. IF the change is breaking of all compatibility with old
// states, increment the upper 16 bit value, and clear the lower 16 bits to 0.
static const u32 g_SaveVersion = 0x8b410001;
static const u32 g_SaveVersion = 0x8b410002;
// this function is meant to be used in the place of GSfreeze, and provides a safe layer
// between the GS saving function and the MTGS's needs. :)
extern s32 CALLBACK gsSafeFreeze( int mode, freezeData *data );
// This class provides the base API for both loading and saving savestates.
// Normally you'll want to use one of the four "functional" derived classes rather
// than this class directly: gzLoadingState, gzSavingState (gzipped disk-saved
enum FreezeSectionId
{
FreezeId_End,
FreezeId_Memory,
FreezeId_Registers,
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
// but can be skipped if the savestate has only plugins.
FreezeId_Bios,
FreezeId_Plugin,
// anything here and beyond we can skip, with a warning
FreezeId_Unknown,
};
// --------------------------------------------------------------------------------------
// SaveStateBase class
// --------------------------------------------------------------------------------------
// Provides the base API for both loading and saving savestates. Normally you'll want to
// use one of the four "functional" derived classes rather than this class directly: gzLoadingState, gzSavingState (gzipped disk-saved
// states), and memLoadingState, memSavingState (uncompressed memory states).
class SaveState
class SaveStateBase
{
protected:
SafeArray<u8>& m_memory;
char m_tagspace[32];
u32 m_version; // version of the savestate being loaded.
SafeArray<char> m_tagspace;
int m_idx; // current read/write index of the allocation
int m_sectid;
int m_pid;
bool m_DidBios;
public:
SaveState( const char* msg, const wxString& destination );
virtual ~SaveState() { }
SaveStateBase( SafeArray<u8>& memblock );
virtual ~SaveStateBase() { }
static wxString GetFilename( int slot );
@ -78,14 +105,28 @@ public:
FreezeMem( &data, sizeof( T ) - sizeOfNewStuff );
}
void PrepBlock( int size );
u8* GetBlockPtr()
{
return &m_memory[m_idx];
}
void CommitBlock( int size )
{
m_idx += size;
}
bool FreezeSection();
// Freezes an identifier value into the savestate for troubleshooting purposes.
// Identifiers can be used to determine where in a savestate that data has become
// skewed (if the value does not match then the error occurs somewhere prior to that
// position).
void FreezeTag( const char* src );
// Loads or saves a plugin. Plugin name is for console logging purposes.
virtual void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )=0;
// Returns true if this object is a StateLoading type object.
bool IsLoading() const { return !IsSaving(); }
// Loads or saves a memory block.
virtual void FreezeMem( void* data, int size )=0;
@ -93,18 +134,18 @@ public:
// Returns true if this object is a StateSaving type object.
virtual bool IsSaving() const=0;
// Returns true if this object is a StateLoading type object.
bool IsLoading() const { return !IsSaving(); }
// note: gsFreeze() needs to be public because of the GSState recorder.
public:
virtual void gsFreeze();
// note: gsFreeze() needs to be public because of the GSState recorder.
void gsFreeze();
protected:
// Load/Save functions for the various components of our glorious emulator!
void FreezeBios();
void FreezeMainMemory();
void FreezeRegisters();
void rcntFreeze();
void vuMicroFreeze();
void vif0Freeze();
@ -125,57 +166,11 @@ protected:
};
/////////////////////////////////////////////////////////////////////////////////
// Class Declarations for Savestates using zlib
// --------------------------------------------------------------------------------------
// Saving and Loading Specialized Implementations...
// --------------------------------------------------------------------------------------
class gzBaseStateInfo : public SaveState
{
protected:
const wxString m_filename;
gzFile m_file; // used for reading/writing disk saves
public:
gzBaseStateInfo( const char* msg, const wxString& filename );
virtual ~gzBaseStateInfo();
};
class gzSavingState : public gzBaseStateInfo
{
public:
virtual ~gzSavingState() {}
gzSavingState( const wxString& filename ) ;
void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) );
void FreezeMem( void* data, int size );
bool IsSaving() const { return true; }
};
class gzLoadingState : public gzBaseStateInfo
{
public:
virtual ~gzLoadingState();
gzLoadingState( const wxString& filename );
void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) );
void FreezeMem( void* data, int size );
bool IsSaving() const { return false; }
bool Finished() const { return !!gzeof( m_file ); }
};
//////////////////////////////////////////////////////////////////////////////////
class memBaseStateInfo : public SaveState
{
protected:
SafeArray<u8>& m_memory;
int m_idx; // current read/write index of the allocation
public:
virtual ~memBaseStateInfo() { }
memBaseStateInfo( SafeArray<u8>& memblock, const char* msg );
};
class memSavingState : public memBaseStateInfo
class memSavingState : public SaveStateBase
{
protected:
static const int ReallocThreshold = 0x200000; // 256k reallocation block size.
@ -185,22 +180,21 @@ public:
virtual ~memSavingState() { }
memSavingState( SafeArray<u8>& save_to );
void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) );
// Saving of state data to a memory buffer
void FreezeMem( void* data, int size );
bool IsSaving() const { return true; }
};
class memLoadingState : public memBaseStateInfo
class memLoadingState : public SaveStateBase
{
public:
virtual ~memLoadingState();
memLoadingState(SafeArray<u8>& load_from );
memLoadingState( const SafeArray<u8>& load_from );
void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) );
// Loading of state data from a memory buffer...
void FreezeMem( void* data, int size );
bool IsSaving() const { return false; }
bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); }
};
namespace StateRecovery
@ -209,10 +203,7 @@ namespace StateRecovery
extern void Recover();
extern void SaveToFile( const wxString& file );
extern void SaveToSlot( uint num );
extern void MakeGsOnly();
extern void MakeFull();
extern void Clear();
}
#endif

View File

@ -499,7 +499,7 @@ __forceinline void dmaSIF2()
}
void SaveState::sifFreeze()
void SaveStateBase::sifFreeze()
{
FreezeTag("SIFdma");

View File

@ -645,7 +645,7 @@ void SIO_FORCEINLINE sioInterrupt() {
psxHu32(0x1070)|=0x80;
}
void SaveState::sioFreeze()
void SaveStateBase::sioFreeze()
{
// CRCs for memory cards.
u64 m_mcdCRCs[2][8];

View File

@ -39,25 +39,16 @@ Pcsx2Config EmuConfig;
// disable all session overrides by default...
SessionOverrideFlags g_Session = {false};
CoreEmuThread* g_EmuThread;
bool sysInitialized = false;
// -----------------------------------------------------------------------
// This function should be called once during program execution.
//
void SysDetect()
{
using namespace Console;
#ifdef __LINUX__
// Haven't rigged up getting the svn version yet... --arcum42
Notice("PCSX2 %d.%d.%d - compiled on " __DATE__, PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo);
#else
Notice("PCSX2 %d.%d.%d.r%d %s - compiled on " __DATE__, PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo,
SVN_REV, SVN_MODS ? "(modded)" : ""
);
#endif
Notice("Savestate version: %x", g_SaveVersion);
cpudetectInit();
@ -82,49 +73,51 @@ void SysDetect()
x86caps.Flags, x86caps.Flags2,
x86caps.EFlags
) );
wxArrayString features[2]; // 2 lines, for readability!
if( x86caps.hasMultimediaExtensions ) features[0].Add( L"MMX" );
if( x86caps.hasStreamingSIMDExtensions ) features[0].Add( L"SSE" );
if( x86caps.hasStreamingSIMD2Extensions ) features[0].Add( L"SSE2" );
if( x86caps.hasStreamingSIMD3Extensions ) features[0].Add( L"SSE3" );
if( x86caps.hasSupplementalStreamingSIMD3Extensions ) features[0].Add( L"SSSE3" );
if( x86caps.hasStreamingSIMD4Extensions ) features[0].Add( L"SSE4.1" );
if( x86caps.hasStreamingSIMD4Extensions2 ) features[0].Add( L"SSE4.2" );
WriteLn( "Features:" );
WriteLn(
"\t%sDetected MMX\n"
"\t%sDetected SSE\n"
"\t%sDetected SSE2\n"
"\t%sDetected SSE3\n"
"\t%sDetected SSSE3\n"
"\t%sDetected SSE4.1\n"
"\t%sDetected SSE4.2\n",
x86caps.hasMultimediaExtensions ? "" : "Not ",
x86caps.hasStreamingSIMDExtensions ? "" : "Not ",
x86caps.hasStreamingSIMD2Extensions ? "" : "Not ",
x86caps.hasStreamingSIMD3Extensions ? "" : "Not ",
x86caps.hasSupplementalStreamingSIMD3Extensions ? "" : "Not ",
x86caps.hasStreamingSIMD4Extensions ? "" : "Not ",
x86caps.hasStreamingSIMD4Extensions2 ? "" : "Not "
);
if( x86caps.hasMultimediaExtensionsExt ) features[1].Add( L"MMX2 " );
if( x86caps.has3DNOWInstructionExtensions ) features[1].Add( L"3DNOW " );
if( x86caps.has3DNOWInstructionExtensionsExt ) features[1].Add( L"3DNOW2" );
if( x86caps.hasStreamingSIMD4ExtensionsA ) features[1].Add( L"SSE4a " );
if ( x86caps.VendorName[0] == 'A' ) //AMD cpu
{
WriteLn( " Extended AMD Features:" );
WriteLn(
"\t%sDetected MMX2\n"
"\t%sDetected 3DNOW\n"
"\t%sDetected 3DNOW2\n"
"\t%sDetected SSE4a\n",
x86caps.hasMultimediaExtensionsExt ? "" : "Not ",
x86caps.has3DNOWInstructionExtensions ? "" : "Not ",
x86caps.has3DNOWInstructionExtensionsExt ? "" : "Not ",
x86caps.hasStreamingSIMD4ExtensionsA ? "" : "Not "
);
}
wxString result[2];
JoinString( result[0], features[0], L".. " );
JoinString( result[1], features[1], L".. " );
WriteLn( L"Features Detected:\n\t" + result[0] + (result[1].IsEmpty() ? wxEmptyString : (L"\n\t" + result[1])) + L"\n" );
//if ( x86caps.VendorName[0] == 'A' ) //AMD cpu
Console::ClearColor();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Allocates memory for all PS2 systems.
bool SysAllocateMem()
// returns the translated error message for the Virtual Machine failing to allocate!
static wxString GetMemoryErrorVM()
{
// Allocate PS2 system ram space (required by interpreters and recompilers both)
return pxE( ".Popup Error:EmuCore::MemoryForVM",
L"PCSX2 is unable to allocate memory needed for the PS2 virtual machine. "
L"Close out some memory hogging background tasks and try again."
);
}
EmuCoreAllocations::EmuCoreAllocations()
{
Console::Status( "Initializing PS2 virtual machine..." );
RecSuccess_EE = false;
RecSuccess_IOP = false;
RecSuccess_VU0 = false;
RecSuccess_VU1 = false;
try
{
vtlb_Core_Alloc();
@ -132,147 +125,116 @@ bool SysAllocateMem()
psxMemAlloc();
vuMicroMemAlloc();
}
catch( Exception::OutOfMemory& )
// ----------------------------------------------------------------------------
catch( Exception::OutOfMemory& ex )
{
// TODO : Should this error be handled here or allowed to be handled by the main
// exception handler?
wxString newmsg( ex.UserMsg() + L"\n\n" + GetMemoryErrorVM() );
ex.UserMsg() = newmsg;
CleanupMess();
throw;
}
catch( std::bad_alloc& ex )
{
CleanupMess();
// Failures on the core initialization of memory is bad, since it means the emulator is
// completely non-functional.
// re-throw std::bad_alloc as something more friendly.
//Msgbox::Alert( "Failed to allocate memory needed to run pcsx2.\n\nError: %s", ex.cMessage() );
SysShutdownMem();
return false;
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() )
),
GetMemoryErrorVM() // translated
);
}
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////
// Allocates memory for all recompilers, and force-disables any recs that fail to initialize.
// This should be done asap, since the recompilers tend to demand a lot of system resources,
// and prefer to have those resources at specific address ranges. The sooner memory is
// allocated, the better.
//
// Returns FALSE on *critical* failure (GUI should issue a msg and exit).
void SysAllocateDynarecs()
{
// Attempt to initialize the recompilers.
// Most users want to use recs anyway, and if they are using interpreters I don't think the
// extra few megs of allocation is going to be an issue.
Console::Status( "Allocating memory for recompilers..." );
try
{
// R5900 and R3000a must be rec-enabled together for now so if either fails they both fail.
recCpu.Allocate();
psxRec.Allocate();
RecSuccess_EE = true;
}
catch( Exception::BaseException& )
catch( Exception::BaseException& ex )
{
// TODO : Fix this message. It should respond according to the user's
// currently configured recompiler.interpreter options, for example.
/*Msgbox::Alert(
"The EE/IOP recompiler failed to initialize with the following error:\n\n"
"%s"
"\n\nThe EE/IOP interpreter will be used instead (slow!).", params
ex.cMessage()
);*/
g_Session.ForceDisableEErec = true;
Console::Error( L"EE Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
recCpu.Shutdown();
}
try
{
psxRec.Allocate();
RecSuccess_IOP = true;
}
catch( Exception::BaseException& ex )
{
Console::Error( L"IOP Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
psxRec.Shutdown();
}
// hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :(
try
{
VU0micro::recAlloc();
RecSuccess_VU0 = true;
}
catch( Exception::BaseException& )
catch( Exception::BaseException& ex )
{
// TODO : Fix this message. It should respond according to the user's
// currently configured recompiler.interpreter options, for example.
/*
Msgbox::Alert(
"The VU0 recompiler failed to initialize with the following error:\n\n"
"%s"
"\n\nThe VU0 interpreter will be used for this session (may slow down some games).", params
ex.cMessage()
);
*/
g_Session.ForceDisableVU0rec = true;
Console::Error( L"VU0 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
VU0micro::recShutdown();
}
try
{
VU1micro::recAlloc();
RecSuccess_VU1 = true;
}
catch( Exception::BaseException& )
catch( Exception::BaseException& ex )
{
// TODO : Fix this message. It should respond according to the user's
// currently configured recompiler.interpreter options, for example.
/*
Msgbox::Alert(
"The VU1 recompiler failed to initialize with the following error:\n\n"
"%s"
"\n\nThe VU1 interpreter will be used for this session (will slow down most games).", params
ex.cMessage()
);
*/
g_Session.ForceDisableVU1rec = true;
Console::Error( L"VU1 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
VU1micro::recShutdown();
}
// If both VUrecs failed, then make sure the SuperVU is totally closed out:
if( !CHECK_VU0REC && !CHECK_VU1REC)
// If both VUrecs failed, then make sure the SuperVU is totally closed out, because it
// actually initializes everything once and then shares it between both VU recs.
if( !RecSuccess_VU0 && !RecSuccess_VU1 )
SuperVUDestroy( -1 );
}
void EmuCoreAllocations::CleanupMess() throw()
{
try
{
// Special SuperVU "complete" terminator.
SuperVUDestroy( -1 );
VU1micro::recShutdown();
VU0micro::recShutdown();
psxRec.Shutdown();
recCpu.Shutdown();
vuMicroMemShutdown();
psxMemShutdown();
memShutdown();
vtlb_Core_Shutdown();
}
DESTRUCTOR_CATCHALL
}
// This should be called last thing before PCSX2 exits.
//
void SysShutdownMem()
EmuCoreAllocations::~EmuCoreAllocations() throw()
{
if( sysInitialized )
SysShutdown();
vuMicroMemShutdown();
psxMemShutdown();
memShutdown();
vtlb_Core_Shutdown();
CleanupMess();
}
// This should generally be called right before calling SysShutdownMem(), although you can optionally
// use it in conjunction with SysAllocDynarecs to allocate/free the dynarec resources on the fly (as
// risky as it might be, since dynarecs could very well fail on the second attempt).
void SysShutdownDynarecs()
bool EmuCoreAllocations::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const
{
// Special SuperVU "complete" terminator.
SuperVUDestroy( -1 );
VU0micro::recShutdown();
VU1micro::recShutdown();
psxRec.Shutdown();
recCpu.Shutdown();
}
void SysShutdown()
{
sysInitialized = false;
Console::Status( "Shutting down PS2 virtual machine..." );
SysEndExecution();
safe_delete( g_plugins );
SysShutdownDynarecs();
SysShutdownMem();
return (recOpts.EnableEE && !RecSuccess_EE) ||
(recOpts.EnableIOP && !RecSuccess_IOP) ||
(recOpts.EnableVU0 && !RecSuccess_VU0) ||
(recOpts.EnableVU1 && !RecSuccess_VU1);
}
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
@ -291,63 +253,6 @@ void SysClearExecutionCache()
vuMicroCpuReset();
}
bool EmulationInProgress()
{
return (g_EmuThread != NULL) && g_EmuThread->IsRunning();
}
// Executes the specified cdvd source and optional elf file. This command performs a
// full closure of any existing VM state and starts a fresh VM with the requested
// sources.
void SysExecute( CoreEmuThread* newThread, CDVD_SourceType cdvdsrc )
{
wxASSERT( newThread != NULL );
safe_delete( g_EmuThread );
CDVDsys_ChangeSource( cdvdsrc );
g_EmuThread = newThread;
g_EmuThread->Resume();
}
// Executes the emulator using a saved/existing virtual machine state and currently
// configured CDVD source device.
// Debug assertions:
void SysExecute( CoreEmuThread* newThread )
{
wxASSERT( newThread != NULL );
safe_delete( g_EmuThread );
g_EmuThread = newThread;
g_EmuThread->Resume();
}
// Once execution has been ended no action can be taken on the Virtual Machine (such as
// saving states). No assertions or exceptions.
void SysEndExecution()
{
safe_delete( g_EmuThread );
GetPluginManager().Shutdown();
}
void SysSuspend()
{
if( g_EmuThread != NULL )
g_EmuThread->Suspend();
}
void SysResume()
{
if( g_EmuThread != NULL )
g_EmuThread->Resume();
}
void SysRestorableReset()
{
if( !EmulationInProgress() ) return;
StateRecovery::MakeFull();
}
// The calling function should trap and handle exceptions as needed.
// Exceptions:
// Exception::StateLoadError - thrown when a fully recoverable exception ocurred. The
@ -355,35 +260,19 @@ void SysRestorableReset()
//
// Any other exception means the Virtual Memory state is indeterminate and probably
// invalid.
void SysLoadState( const wxString& file )
void SysLoadState( const wxString& srcfile )
{
SafeArray<u8> buf;
memLoadingState joe( buf ); // this could throw n StateLoadError.
// we perform a full backup to memory first so that we can restore later if the
// load fails. fixme: should this be made optional? It could have significant
// speed impact on state loads on slower machines with low ram. >_<
StateRecovery::MakeFull();
gzLoadingState joe( file ); // this'll throw an StateLoadError.
GetPluginManager().Open();
cpuReset();
SysClearExecutionCache();
cpuReset();
joe.FreezeAll();
if( GSsetGameCRC != NULL )
GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
}
void SysReset()
{
Console::Status( "Resetting PS2 virtual machine..." );
SysEndExecution();
StateRecovery::Clear();
ElfCRC = 0;
// Note : No need to call cpuReset() here. It gets called automatically before the
// emulator resumes execution.
}
// Maps a block of memory for use as a recompiled code buffer, and ensures that the
@ -414,29 +303,3 @@ u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
}
return Mem;
}
// Ensures existence of necessary folders, and performs error handling if the
// folders fail to create.
static void InitFolderStructure()
{
}
// Returns FALSE if the core/recompiler memory allocations failed.
bool SysInit()
{
if( sysInitialized ) return true;
sysInitialized = true;
SysDetect();
PCSX2_MEM_PROTECT_BEGIN();
Console::Status( "Initializing PS2 virtual machine..." );
if( !SysAllocateMem() )
return false; // critical memory allocation failure;
SysAllocateDynarecs();
PCSX2_MEM_PROTECT_END();
return true;
}

View File

@ -23,36 +23,46 @@ static const int PCSX2_VersionHi = 0;
static const int PCSX2_VersionMid = 9;
static const int PCSX2_VersionLo = 7;
class CoreEmuThread;
extern bool SysInit();
// --------------------------------------------------------------------------------------
// EmuCoreAllocations class
// --------------------------------------------------------------------------------------
class EmuCoreAllocations
{
public:
// This set of booleans defaults to false and are only set TRUE if the corresponding
// recompilers succeeded to initialize/allocate. The host application should honor
// these booleans when selecting between recompiler or interpreter, since recompilers
// will fail to operate if these are "false."
bool RecSuccess_EE:1,
RecSuccess_IOP:1,
RecSuccess_VU0:1,
RecSuccess_VU1:1;
protected:
public:
EmuCoreAllocations();
virtual ~EmuCoreAllocations() throw();
bool HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const;
protected:
void CleanupMess() throw();
};
extern void SysDetect(); // Detects cpu type and fills cpuInfo structs.
extern void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers.
extern void SysExecute( CoreEmuThread* newThread );
extern void SysExecute( CoreEmuThread* newThread, CDVD_SourceType cdvdsrc );
extern void SysEndExecution();
extern void SysSuspend();
extern void SysResume();
extern bool SysAllocateMem(); // allocates memory for all PS2 systems; returns FALSe on critical error.
extern void SysAllocateDynarecs(); // allocates memory for all dynarecs, and force-disables any failures.
extern void SysShutdownDynarecs();
extern void SysShutdownMem();
extern void SysShutdown();
extern void SysLoadState( const wxString& file );
extern void SysRestorableReset(); // Saves the current emulation state prior to spu reset.
extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed");
extern void vSyncDebugStuff( uint frame );
extern CoreEmuThread* g_EmuThread;
//////////////////////////////////////////////////////////////////////////////////////////
//
#ifdef __LINUX__

View File

@ -138,7 +138,7 @@ void vuMicroMemReset()
VU1.vifRegs = vif1Regs;
}
void SaveState::vuMicroFreeze()
void SaveStateBase::vuMicroFreeze()
{
FreezeTag( "vuMicro" );

View File

@ -1658,7 +1658,7 @@ void vif0Reset()
vif0Regs->stat &= ~VIF0_STAT_FQC; // FQC=0
}
void SaveState::vif0Freeze()
void SaveStateBase::vif0Freeze()
{
FreezeTag("VIFdma");
@ -2803,7 +2803,7 @@ void vif1Reset()
vif1Regs->stat &= ~VIF1_STAT_FQC; // FQC=0
}
void SaveState::vif1Freeze()
void SaveStateBase::vif1Freeze()
{
Freeze(vif1);

View File

@ -30,6 +30,12 @@
class IniInterface;
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 )
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
END_DECLARE_EVENT_TYPES()
// ------------------------------------------------------------------------
// All Menu Options for the Main Window! :D
// ------------------------------------------------------------------------
@ -119,6 +125,14 @@ enum MenuIdentifiers
MenuId_Debug_Usermode,
};
enum DialogIdentifiers
{
DialogId_CoreSettings = 0x800,
DialogId_BiosSelector,
DialogId_LogOptions,
DialogId_About,
};
//////////////////////////////////////////////////////////////////////////////////////////
// ScopedWindowDisable
//
@ -197,53 +211,89 @@ struct AppImageIds
} Toolbars;
};
//////////////////////////////////////////////////////////////////////////////////////////
//
class pxAppTraits : public wxGUIAppTraits
struct MsgboxEventResult
{
#ifdef __WXDEBUG__
public:
virtual bool ShowAssertDialog(const wxString& msg);
protected:
virtual wxString GetAssertStackTrace();
#endif
Semaphore WaitForMe;
int result;
MsgboxEventResult() :
WaitForMe(), result( 0 )
{
}
};
//////////////////////////////////////////////////////////////////////////////////////////
//
// --------------------------------------------------------------------------------------
// Pcsx2App - main wxApp class
// --------------------------------------------------------------------------------------
class Pcsx2App : public wxApp
{
protected:
wxImageList m_ConfigImages;
wxScopedPtr<wxImageList> m_ToolbarImages;
wxScopedPtr<wxBitmap> m_Bitmap_Logo;
wxScopedPtr<EmuCoreAllocations> m_CoreAllocs;
public:
wxScopedPtr<PluginManager> m_CorePlugins;
wxScopedPtr<CoreEmuThread> m_CoreThread;
protected:
// Note: Pointers to frames should not be scoped because wxWidgets handles deletion
// of these objects internally.
MainEmuFrame* m_MainFrame;
ConsoleLogFrame* m_ProgramLogBox;
wxBitmap* m_Bitmap_Logo;
wxImageList m_ConfigImages;
bool m_ConfigImagesAreLoaded;
wxImageList* m_ToolbarImages; // dynamic (pointer) to allow for large/small redefinition.
AppImageIds m_ImageId;
bool m_ConfigImagesAreLoaded;
AppImageIds m_ImageId;
public:
Pcsx2App();
virtual ~Pcsx2App();
wxFrame* GetMainWindow() const;
void ReloadPlugins();
bool OnInit();
int OnExit();
void CleanUp();
void ApplySettings( const AppConfig* oldconf = NULL );
void LoadSettings();
void SaveSettings();
void PostMenuAction( MenuIdentifiers menu_id ) const;
int ThreadedModalDialog( DialogIdentifiers dialogId );
void Ping() const;
void OnInitCmdLine( wxCmdLineParser& parser );
bool OnCmdLineParsed( wxCmdLineParser& parser );
bool OnCmdLineError( wxCmdLineParser& parser );
bool PrepForExit();
// Executes the emulator using a saved/existing virtual machine state and currently
// configured CDVD source device.
// Debug assertions:
void SysExecute();
void SysExecute( CDVD_SourceType cdvdsrc );
#ifdef __WXDEBUG__
void OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg );
#endif
void SysResume()
{
if( !m_CoreThread ) return;
m_CoreThread->Resume();
}
void SysSuspend()
{
if( !m_CoreThread ) return;
m_CoreThread->Suspend();
}
void SysReset()
{
m_CoreThread.reset();
m_CorePlugins.reset();
}
bool EmuInProgress() const
{
return m_CoreThread && m_CoreThread->IsRunning();
}
const wxBitmap& GetLogoBitmap();
wxImageList& GetImgList_Config();
@ -253,16 +303,24 @@ public:
MainEmuFrame& GetMainFrame() const
{
wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
wxASSERT( m_MainFrame != NULL );
return *m_MainFrame;
}
void PostMenuAction( MenuIdentifiers menu_id ) const;
void Ping() const;
// --------------------------------------------------------------------------
// Overrides of wxApp virtuals:
// --------------------------------------------------------------------------
bool OnInit();
int OnExit();
void ApplySettings( const AppConfig& newconf );
void LoadSettings();
void SaveSettings();
void OnInitCmdLine( wxCmdLineParser& parser );
bool OnCmdLineParsed( wxCmdLineParser& parser );
bool OnCmdLineError( wxCmdLineParser& parser );
#ifdef __WXDEBUG__
void OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg );
#endif
// ----------------------------------------------------------------------------
// Console / Program Logging Helpers
@ -303,7 +361,9 @@ protected:
void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const;
void OnReloadPlugins( wxCommandEvent& evt );
void OnSemaphorePing( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt );
void OnMessageBox( pxMessageBoxEvent& evt );
void OnEmuKeyDown( wxKeyEvent& evt );
@ -322,15 +382,18 @@ protected:
void OnUnhandledException() { throw; }
};
//////////////////////////////////////////////////////////////////////////////////////////
//
// --------------------------------------------------------------------------------------
// AppEmuThread class
// --------------------------------------------------------------------------------------
class AppEmuThread : public CoreEmuThread
{
protected:
wxKeyEvent m_kevt;
public:
AppEmuThread();
AppEmuThread( PluginManager& plugins );
virtual ~AppEmuThread() { }
virtual void Resume();
@ -340,14 +403,29 @@ protected:
sptr ExecuteTask();
};
DECLARE_APP(Pcsx2App)
class EntryGuard
{
public:
int& Counter;
EntryGuard( int& counter ) : Counter( counter )
{ ++Counter; }
virtual ~EntryGuard() throw()
{ --Counter; }
bool IsReentrant() const { return Counter > 1; }
};
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
extern void LoadPlugins();
extern void InitPlugins();
extern void OpenPlugins();
extern void LoadPluginsPassive();
extern void LoadPluginsImmediate();
extern void UnloadPlugins();
extern wxRect wxGetDisplayArea();
extern bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos );
extern bool HandlePluginError( Exception::PluginError& ex );
extern bool EmulationInProgress();

View File

@ -68,10 +68,6 @@ static wxString pxGetStackTrace()
static __threadlocal bool _reentrant_lock = false;
#ifdef __WXDEBUG__
wxString pxAppTraits::GetAssertStackTrace()
{
return pxGetStackTrace();
}
// This override of wx's implementation provides thread safe assertion message reporting. If we aren't
// on the main gui thread then the assertion message box needs to be passed off to the main gui thread
@ -101,8 +97,8 @@ void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func
// make life easier for people using VC++ IDE by using this format, which allows double-click
// response times from the Output window...
dbgmsg.Printf( L"%s(%d) : assertion failed%s%s: %s", file, line,
(func==NULL) ? L"" : L" in ",
(func==NULL) ? L"" : func,
(func==NULL) ? wxEmptyString : L" in ",
(func==NULL) ? wxEmptyString : func,
message.c_str()
);

View File

@ -94,7 +94,10 @@ namespace PathDefs
// share with other programs: screenshots, memory cards, and savestates.
wxDirName GetDocuments()
{
return (wxDirName)g_Conf->GetDefaultDocumentsFolder();
if( UseAdminMode )
return (wxDirName)wxGetCwd();
else
return (wxDirName)Path::Combine( wxStandardPaths::Get().GetDocumentsDir(), wxGetApp().GetAppName() );
}
wxDirName GetSnapshots()
@ -155,20 +158,12 @@ namespace PathDefs
}
};
wxString AppConfig::GetDefaultDocumentsFolder()
{
if( UseAdminMode )
return wxGetCwd();
else
return Path::Combine( wxStandardPaths::Get().GetDocumentsDir(), wxGetApp().GetAppName() );
}
const wxDirName& AppConfig::FolderOptions::operator[]( FoldersEnum_t folderidx ) const
{
switch( folderidx )
{
case FolderId_Plugins: return Plugins;
case FolderId_Settings: return Settings;
case FolderId_Settings: return SettingsFolder;
case FolderId_Bios: return Bios;
case FolderId_Snapshots: return Snapshots;
case FolderId_Savestates: return Savestates;
@ -185,7 +180,7 @@ const bool AppConfig::FolderOptions::IsDefault( FoldersEnum_t folderidx ) const
switch( folderidx )
{
case FolderId_Plugins: return UseDefaultPlugins;
case FolderId_Settings: return UseDefaultSettings;
case FolderId_Settings: return UseDefaultSettingsFolder;
case FolderId_Bios: return UseDefaultBios;
case FolderId_Snapshots: return UseDefaultSnapshots;
case FolderId_Savestates: return UseDefaultSavestates;
@ -207,8 +202,8 @@ void AppConfig::FolderOptions::Set( FoldersEnum_t folderidx, const wxString& src
break;
case FolderId_Settings:
Settings = src;
UseDefaultSettings = useDefault;
SettingsFolder = src;
UseDefaultSettingsFolder = useDefault;
break;
case FolderId_Bios:
@ -240,8 +235,9 @@ void AppConfig::FolderOptions::Set( FoldersEnum_t folderidx, const wxString& src
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// --------------------------------------------------------------------------------------
// Default Filenames
// --------------------------------------------------------------------------------------
namespace FilenameDefs
{
wxFileName GetConfig()
@ -290,9 +286,14 @@ wxString AppConfig::FullpathTo( PluginsEnum_t pluginidx ) const
return Path::Combine( Folders.Plugins, BaseFilenames[pluginidx] );
}
wxString AppConfig::FullPathToConfig() const
wxDirName GetSettingsFolder()
{
return g_Conf->Folders.Settings.Combine( FilenameDefs::GetConfig() ).GetFullPath();
return UseDefaultSettingsFolder ? PathDefs::GetSettings() : SettingsFolder;
}
wxString GetSettingsFilename()
{
return GetSettingsFolder().Combine( FilenameDefs::GetConfig() ).GetFullPath();
}
@ -349,7 +350,8 @@ void AppConfig::LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash )
ini.GetConfig().Write( L"Timestamp", timestamp_now );*/
ini.Entry( L"UseAdminMode", UseAdminMode, false );
ini.Entry( L"SettingsPath", Folders.Settings, PathDefs::GetSettings() );
ini.Entry( L"UseDefaultSettingsFolder", UseDefaultSettingsFolder, true );
ini.Entry( L"SettingsFolder", SettingsFolder, PathDefs::GetSettings() );
ini.Flush();
}
@ -404,40 +406,6 @@ void AppConfig::LoadSave( IniInterface& ini )
ini.Flush();
}
// ------------------------------------------------------------------------
// Performs necessary operations to ensure that the current g_Conf settings (and other config-stored
// globals) are applied to the pcsx2 main window and primary emulation subsystems (if active).
//
void AppConfig::Apply()
{
Folders.ApplyDefaults();
// Ensure existence of necessary documents folders. Plugins and other parts
// of PCSX2 rely on them.
Folders.MemoryCards.Mkdir();
Folders.Savestates.Mkdir();
Folders.Snapshots.Mkdir();
EmuOptions.BiosFilename = FullpathToBios();
// Update the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression.
NTFS_CompressFile( Folders.MemoryCards.ToString(), McdEnableNTFS );
{
wxDoNotLogInThisScope please;
if( !i18n_SetLanguage( LanguageId ) )
{
if( !i18n_SetLanguage( wxLANGUAGE_DEFAULT ) )
{
i18n_SetLanguage( wxLANGUAGE_ENGLISH );
}
}
}
}
// ------------------------------------------------------------------------
AppConfig::ConsoleLogOptions::ConsoleLogOptions() :
Visible( false )
@ -463,7 +431,6 @@ void AppConfig::ConsoleLogOptions::LoadSave( IniInterface& ini, const wxChar* lo
void AppConfig::FolderOptions::ApplyDefaults()
{
if( UseDefaultPlugins ) Plugins = PathDefs::GetPlugins();
if( UseDefaultSettings ) Settings = PathDefs::GetSettings();
if( UseDefaultBios ) Bios = PathDefs::GetBios();
if( UseDefaultSnapshots ) Snapshots = PathDefs::GetSnapshots();
if( UseDefaultSavestates ) Savestates = PathDefs::GetSavestates();
@ -475,7 +442,6 @@ void AppConfig::FolderOptions::ApplyDefaults()
AppConfig::FolderOptions::FolderOptions() :
bitset( 0xffffffff )
, Plugins( PathDefs::GetPlugins() )
, Settings( PathDefs::GetSettings() )
, Bios( PathDefs::GetBios() )
, Snapshots( PathDefs::GetSnapshots() )
, Savestates( PathDefs::GetSavestates() )
@ -503,7 +469,6 @@ void AppConfig::FolderOptions::LoadSave( IniInterface& ini )
IniBitBool( UseDefaultLogs );
IniEntry( Plugins );
IniEntry( Settings );
IniEntry( Bios );
IniEntry( Snapshots );
IniEntry( Savestates );
@ -552,13 +517,13 @@ void AppConfig_ReloadGlobalSettings( bool overwrite )
PathDefs::GetSettings().Mkdir();
// Allow wx to use our config, and enforces auto-cleanup as well
delete wxConfigBase::Set( OpenFileConfig( g_Conf->FullPathToConfig() ) );
delete wxConfigBase::Set( OpenFileConfig( GetSettingsFilename() ) );
wxConfigBase::Get()->SetRecordDefaults();
if( !overwrite )
wxGetApp().LoadSettings();
wxGetApp().ApplySettings( *g_Conf );
wxGetApp().ApplySettings();
g_Conf->Folders.Logs.Mkdir();
wxString newlogname( Path::Combine( g_Conf->Folders.Logs.ToString(), L"emuLog.txt" ) );

View File

@ -21,7 +21,12 @@
class IniInterface;
class wxFileConfig;
extern bool UseAdminMode; // dictates if the program uses /home/user or /cwd for the program data
extern bool UseAdminMode; // dictates if the program uses /home/user or /cwd for the program data
extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE.
extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the UseAdminMode
wxDirName GetSettingsFolder();
wxString GetSettingsFilename();
//////////////////////////////////////////////////////////////////////////////////////////
// Pcsx2 Application Configuration.
@ -33,15 +38,15 @@ public:
// ------------------------------------------------------------------------
struct ConsoleLogOptions
{
bool Visible;
bool Visible;
// if true, DisplayPos is ignored and the console is automatically docked to the main window.
bool AutoDock;
bool AutoDock;
// Display position used if AutoDock is false (ignored otherwise)
wxPoint DisplayPosition;
wxSize DisplaySize;
wxPoint DisplayPosition;
wxSize DisplaySize;
// Size of the font in points.
int FontSize;
int FontSize;
ConsoleLogOptions();
void LoadSave( IniInterface& conf, const wxChar* title );
@ -59,11 +64,10 @@ public:
UseDefaultSavestates:1,
UseDefaultMemoryCards:1,
UseDefaultLogs:1;
}; };
BITFIELD_END
wxDirName
Plugins,
Settings,
Bios,
Snapshots,
Savestates,
@ -156,14 +160,10 @@ public:
wxString FullpathToBios() const;
wxString FullpathToMcd( uint port, uint slot ) const;
wxString FullpathTo( PluginsEnum_t pluginId ) const;
wxString FullPathToConfig() const;
void LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash );
wxString GetDefaultDocumentsFolder();
protected:
void Apply();
void LoadSave( IniInterface& ini );
void LoadSaveMemcards( IniInterface& ini );
@ -181,4 +181,4 @@ extern ConfigOverrides OverrideOptions;
extern wxFileConfig* OpenFileConfig( const wxString& filename );
extern void AppConfig_ReloadGlobalSettings( bool overwrite = false );
extern AppConfig* g_Conf;
extern wxScopedPtr<AppConfig> g_Conf;

View File

@ -19,6 +19,9 @@
#include "Plugins.h"
#include "Dialogs/ModalPopups.h"
#include "Dialogs/ConfigurationDialog.h"
#include "Dialogs/LogOptionsDialog.h"
#include "Utilities/ScopedPtr.h"
#include "Utilities/HashMap.h"
@ -28,15 +31,16 @@
IMPLEMENT_APP(Pcsx2App)
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
END_DECLARE_EVENT_TYPES()
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing )
bool UseAdminMode = false;
wxDirName SettingsFolder;
bool UseDefaultSettingsFolder = true;
bool UseAdminMode = false;
AppConfig* g_Conf = NULL;
ConfigOverrides OverrideOptions;
wxScopedPtr<AppConfig> g_Conf;
ConfigOverrides OverrideOptions;
namespace Exception
{
@ -57,8 +61,8 @@ namespace Exception
};
}
AppEmuThread::AppEmuThread() :
CoreEmuThread()
AppEmuThread::AppEmuThread( PluginManager& plugins ) :
CoreEmuThread( plugins )
, m_kevt()
{
}
@ -127,19 +131,50 @@ void AppEmuThread::StateCheck()
}
}
static bool HandlePluginError( Exception::PluginError& ex )
// Executes the emulator using a saved/existing virtual machine state and currently
// configured CDVD source device.
void Pcsx2App::SysExecute()
{
SysReset();
LoadPluginsImmediate();
m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) );
m_CoreThread->Resume();
}
// 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 )
{
SysReset();
LoadPluginsImmediate();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
CDVDsys_ChangeSource( cdvdsrc );
m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) );
m_CoreThread->Resume();
}
__forceinline bool EmulationInProgress()
{
return wxGetApp().EmuInProgress();
}
bool HandlePluginError( Exception::PluginError& ex )
{
if( pxDialogExists( DialogId_CoreSettings ) ) return true;
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.") );
_("\n\nPress Ok to go to the Plugin Configuration Panel.")
);
if( result )
{
g_Conf->SettingsTabName = L"Plugins";
wxGetApp().PostMenuAction( MenuId_Config_Settings );
wxGetApp().Ping();
// fixme: Send a message to the panel to select the failed plugin.
// fixme: handle case where user cancels the settings dialog. (should return FALSE).
if( Dialogs::ConfigurationDialog().ShowModal() == wxID_CANCEL )
return false;
}
return result;
}
@ -162,11 +197,14 @@ sptr AppEmuThread::ExecuteTask()
if( result )
{
wxGetApp().PostMenuAction( MenuId_Config_BIOS );
wxGetApp().Ping();
// fixme: handle case where user cancels the settings dialog. (should return FALSE).
// fixme: automatically re-try emu startup here...
if( wxGetApp().ThreadedModalDialog( DialogId_BiosSelector ) == wxID_CANCEL )
{
// fixme: handle case where user cancels the settings dialog. (should return FALSE).
}
else
{
// fixme: automatically re-try emu startup here...
}
}
}
}
@ -174,10 +212,13 @@ sptr AppEmuThread::ExecuteTask()
catch( Exception::PluginError& ex )
{
GetPluginManager().Close();
if( HandlePluginError( ex ) )
Console::Error( ex.FormatDiagnosticMessage() );
Msgbox::Alert( ex.FormatDisplayMessage(), _("Plugin Open Error") );
/*if( HandlePluginError( ex ) )
{
// fixme: automatically re-try emu startup here...
}
}*/
}
// ----------------------------------------------------------------------------
// [TODO] : Add exception handling here for debuggable PS2 exceptions that allows
@ -194,8 +235,6 @@ sptr AppEmuThread::ExecuteTask()
}
wxFrame* Pcsx2App::GetMainWindow() const { return m_MainFrame; }
void Pcsx2App::OpenWizardConsole()
{
if( !IsDebugBuild ) return;
@ -254,7 +293,7 @@ void Pcsx2App::ReadUserModeSettings()
IniLoader loader( *conf_usermode );
g_Conf->LoadSaveUserMode( loader, groupname );
if( !wxFile::Exists( g_Conf->FullPathToConfig() ) )
if( !wxFile::Exists( GetSettingsFilename() ) )
{
// user wiped their pcsx2.ini -- needs a reconfiguration via wizard!
// (we skip the first page since it's a usermode.ini thing)
@ -361,7 +400,7 @@ bool Pcsx2App::OnInit()
wxInitAllImageHandlers();
if( !wxApp::OnInit() ) return false;
g_Conf = new AppConfig();
g_Conf.reset( new AppConfig() );
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
@ -371,6 +410,8 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_CallStackBox, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxID_Window_GS, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
@ -406,9 +447,51 @@ bool Pcsx2App::OnInit()
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
m_MainFrame->Show();
SysInit();
ApplySettings( *g_Conf );
InitPlugins();
SysDetect();
ApplySettings();
m_CoreAllocs.reset( new EmuCoreAllocations() );
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{
wxString message( _("The following cpu recompilers failed to initialize and will not be available:\n\n") );
if( !m_CoreAllocs->RecSuccess_EE )
{
message += L"\t* R5900 (EE)\n";
g_Session.ForceDisableEErec = true;
}
if( !m_CoreAllocs->RecSuccess_IOP )
{
message += L"\t* R3000A (IOP)\n";
g_Session.ForceDisableIOPrec = true;
}
if( !m_CoreAllocs->RecSuccess_VU0 )
{
message += L"\t* VU0\n";
g_Session.ForceDisableVU0rec = true;
}
if( !m_CoreAllocs->RecSuccess_VU1 )
{
message += L"\t* VU1\n";
g_Session.ForceDisableVU1rec = true;
}
message += pxE( ".Popup Error:EmuCore:MemoryForRecs",
L"These errors are the result of memory allocation failures (see the program log for details). "
L"Closing out some memory hogging background tasks may resolve this error.\n\n"
L"These recompilers have been disabled and interpreters will be used in their place. "
L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2."
);
if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
return false;
}
LoadPluginsPassive();
}
// ----------------------------------------------------------------------------
catch( Exception::StartupAborted& ex )
@ -417,10 +500,16 @@ bool Pcsx2App::OnInit()
return false;
}
// ----------------------------------------------------------------------------
catch( Exception::PluginError& ex )
// Failures on the core initialization procedure (typically OutOfMemory errors) are bad,
// since it means the emulator is completely non-functional. Let's pop up an error and
// exit gracefully-ish.
//
catch( Exception::RuntimeError& ex )
{
if( !HandlePluginError( ex ) )
return false;
Console::Error( ex.FormatDiagnosticMessage() );
Msgbox::Alert( ex.FormatDisplayMessage() + L"\n\nPress OK to close PCSX2.",
_("PCSX2 Critical Error"), wxICON_ERROR );
return false;
}
return true;
}
@ -433,11 +522,27 @@ void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const
if( m_MainFrame == NULL ) return;
wxCommandEvent joe( wxEVT_COMMAND_MENU_SELECTED, menu_id );
m_MainFrame->GetEventHandler()->AddPendingEvent( joe );
if( wxThread::IsMain() )
m_MainFrame->GetEventHandler()->ProcessEvent( joe );
else
m_MainFrame->GetEventHandler()->AddPendingEvent( joe );
}
int Pcsx2App::ThreadedModalDialog( DialogIdentifiers dialogId )
{
wxASSERT( !wxThread::IsMain() ); // don't call me from MainThread!
MsgboxEventResult result;
wxCommandEvent joe( pxEVT_OpenModalDialog, dialogId );
joe.SetClientData( &result );
AddPendingEvent( joe );
result.WaitForMe.WaitNoCancel();
return result.result;
}
// Waits for the main GUI thread to respond. If run from the main GUI thread, returns
// immediately without error.
// immediately without error. Use this on non-GUI threads to have them sleep until
// the GUI has processed all its pending messages.
void Pcsx2App::Ping() const
{
if( wxThread::IsMain() ) return;
@ -458,6 +563,53 @@ void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt )
((Semaphore*)evt.GetClientData())->Post();
}
void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
{
using namespace Dialogs;
MsgboxEventResult& evtres( *((MsgboxEventResult*)evt.GetClientData()) );
switch( evt.GetId() )
{
case DialogId_CoreSettings:
{
static int _guard = 0;
EntryGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = ConfigurationDialog().ShowModal();
}
break;
case DialogId_BiosSelector:
{
static int _guard = 0;
EntryGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = BiosSelectorDialog().ShowModal();
}
break;
case DialogId_LogOptions:
{
static int _guard = 0;
EntryGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = LogOptionsDialog().ShowModal();
}
break;
case DialogId_About:
{
static int _guard = 0;
EntryGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = AboutBoxDialog().ShowModal();
}
break;
}
evtres.WaitForMe.Post();
}
void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt )
{
Msgbox::OnEvent( evt );
@ -467,16 +619,9 @@ void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt )
void Pcsx2App::CleanupMess()
{
safe_delete( m_Bitmap_Logo );
safe_delete( g_Conf );
}
// This cleanup procedure is issued by wxWidgets prior to destroying base windows and window
// classes.
void Pcsx2App::CleanUp()
{
SysShutdown();
wxApp::CleanUp();
m_CorePlugins.reset();
m_ProgramLogBox = NULL;
m_MainFrame = NULL;
}
void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const
@ -489,7 +634,11 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
catch( Exception::PluginError& ex )
{
Console::Error( ex.FormatDiagnosticMessage() );
Msgbox::Alert( ex.FormatDisplayMessage() );
if( !HandlePluginError( ex ) )
{
Console::Error( L"User-canceled plugin configuration after load failure. Plugins not loaded!" );
Msgbox::Alert( _("Warning! Plugins have not been loaded. PCSX2 will be inoperable.") );
}
}
// ----------------------------------------------------------------------------
catch( Exception::RuntimeError& ex )
@ -505,15 +654,15 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
// Common exit handler which can be called from any event (though really it should
// be called only from CloseWindow handlers since that's the more appropriate way
// to handle window closures)
// to handle cancelable window closures)
//
// returns true if the app can close, or false if the close event was canceled by
// the glorious user, whomever (s)he-it might be.
bool Pcsx2App::PrepForExit()
{
SysShutdown();
m_CoreThread.reset();
CleanupMess();
m_ProgramLogBox = NULL;
m_MainFrame = NULL;
return true;
}
@ -521,9 +670,12 @@ int Pcsx2App::OnExit()
{
PrepForExit();
if( g_Conf != NULL )
if( g_Conf )
SaveSettings();
while( wxGetLocale() != NULL )
delete wxGetLocale();
return wxApp::OnExit();
}
@ -540,47 +692,48 @@ Pcsx2App::Pcsx2App() :
Pcsx2App::~Pcsx2App()
{
// Typically OnExit cleans everything up before we get here, *unless* we cancel
// out of program startup in OnInit (return false) -- then remaning cleanup needs
// to happen here in the destructor.
CleanupMess();
while( wxGetLocale() != NULL )
delete wxGetLocale();
}
void Pcsx2App::ApplySettings( const AppConfig& newconf )
void Pcsx2App::ApplySettings( const AppConfig* oldconf )
{
DevAssert( wxThread::IsMain(), "ApplySettings valid from the GUI thread only." );
if( &newconf != g_Conf )
// Ensure existence of necessary documents folders. Plugins and other parts
// of PCSX2 rely on them.
g_Conf->Folders.MemoryCards.Mkdir();
g_Conf->Folders.Savestates.Mkdir();
g_Conf->Folders.Snapshots.Mkdir();
g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios();
// Update the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression.
NTFS_CompressFile( g_Conf->Folders.MemoryCards.ToString(), g_Conf->McdEnableNTFS );
if( (oldconf == NULL) || (oldconf->LanguageId != g_Conf->LanguageId) )
{
// Need to unload the current emulation state if the user changed plugins, because
// the whole plugin system needs to be re-loaded.
const PluginInfo* pi = tbl_PluginInfo-1;
while( ++pi, pi->shortname != NULL )
wxDoNotLogInThisScope please;
if( !i18n_SetLanguage( g_Conf->LanguageId ) )
{
if( newconf.FullpathTo( pi->id ) != g_Conf->FullpathTo( pi->id ) )
break;
}
if( pi->shortname != NULL )
{
// [TODO] : Post notice that this shuts down existing emulation.
SysEndExecution();
safe_delete( g_plugins );
// Think safe to do this earlier, but not positive.
// Have to update the config *before* loading the new plugins.
*g_Conf = newconf;
LoadPlugins();
}
else {
*g_Conf = newconf;
if( !i18n_SetLanguage( wxLANGUAGE_DEFAULT ) )
{
i18n_SetLanguage( wxLANGUAGE_ENGLISH );
}
}
}
g_Conf->Apply();
if( m_MainFrame != NULL )
m_MainFrame->ApplySettings();
if( g_EmuThread != NULL )
g_EmuThread->ApplySettings( g_Conf->EmuOptions );
if( m_CoreThread )
m_CoreThread->ApplySettings( g_Conf->EmuOptions );
}
void Pcsx2App::LoadSettings()

View File

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

View File

@ -686,17 +686,6 @@ static int pxCallstackDialog( const wxString& content, const wxString& caption,
return wxMessageDialog( NULL, content, caption, flags ).ShowModal();
}
struct MsgboxEventResult
{
Semaphore WaitForMe;
int result;
MsgboxEventResult() :
WaitForMe(), result( 0 )
{
}
};
class pxMessageBoxEvent : public wxEvent
{
protected:

View File

@ -58,7 +58,7 @@ public:
{
}
~ConsoleTestThread()
~ConsoleTestThread() throw()
{
m_done = true;
}

View File

@ -26,23 +26,27 @@
using namespace wxHelpers;
namespace Dialogs {
//////////////////////////////////////////////////////////////////////////////////////////
// Helper class for creating wxStaticText labels which are aligned to center.
// (automates the passing of wxDefaultSize and wxDefaultPosition)
//
class StaticTextCentered : public wxStaticText
namespace Dialogs
{
public:
StaticTextCentered( wxWindow* parent, const wxString& text, int id=wxID_ANY ) :
wxStaticText( parent, id, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER )
// Helper class for creating wxStaticText labels which are aligned to center.
// (automates the passing of wxDefaultSize and wxDefaultPosition)
//
class StaticTextCentered : public wxStaticText
{
}
};
public:
StaticTextCentered( wxWindow* parent, const wxString& text, int id=wxID_ANY ) :
wxStaticText( parent, id, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER )
{
}
};
}
AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
// --------------------------------------------------------------------------------------
// AboutBoxDialog Implementation
// --------------------------------------------------------------------------------------
Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
wxDialog( parent, id, _("About PCSX2"), parent->GetPosition()-wxSize( 32, 32 ) ),
m_bitmap_logo( this, wxID_ANY, wxGetApp().GetLogoBitmap(),
wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN ),
@ -120,5 +124,3 @@ AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
mainSizer.Add( new wxButton( this, wxID_OK, L"I've seen enough"), SizerFlags::StdCenter() );
SetSizerAndFit( &mainSizer );
}
} // end namespace Dialogs

View File

@ -144,8 +144,8 @@ void Dialogs::ConfigurationDialog::OnApply_Click( wxCommandEvent& evt )
// ----------------------------------------------------------------------------
Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent ) :
wxDialogWithHelpers( parent, wxID_ANY, _("BIOS Selector"), false )
Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent, int id ) :
wxDialogWithHelpers( parent, id, _("BIOS Selector"), false )
{
wxBoxSizer& bleh( *new wxBoxSizer( wxVERTICAL ) );

View File

@ -33,7 +33,7 @@ namespace Dialogs
public:
virtual ~ConfigurationDialog();
ConfigurationDialog(wxWindow* parent, int id=wxID_ANY);
ConfigurationDialog(wxWindow* parent=NULL, int id=DialogId_CoreSettings);
protected:
template< typename T >
@ -60,7 +60,7 @@ namespace Dialogs
public:
virtual ~BiosSelectorDialog() {}
BiosSelectorDialog(wxWindow* parent );
BiosSelectorDialog( wxWindow* parent=NULL, int id=DialogId_BiosSelector );
protected:
void OnOk_Click( wxCommandEvent& evt );

View File

@ -76,10 +76,7 @@ FirstTimeWizard::UsermodePage::UsermodePage( wxWizard* parent ) :
void FirstTimeWizard::UsermodePage::OnUsermodeChanged( wxCommandEvent& evt )
{
m_panel_UserSel.Apply( *g_Conf ); // this assigns the current user mode to g_ApplyState.UseAdminMode
if( g_ApplyState.UseAdminMode == UseAdminMode ) return;
UseAdminMode = g_ApplyState.UseAdminMode;
m_panel_UserSel.Apply();
g_Conf->Folders.ApplyDefaults();
m_dirpick_settings.Reset();
}
@ -161,7 +158,7 @@ void FirstTimeWizard::OnPageChanging( wxWizardEvent& evt )
if( page == 0 )
{
if( wxFile::Exists( g_Conf->FullPathToConfig() ) )
if( wxFile::Exists( GetSettingsFilename() ) )
{
// Asks the user if they want to import or overwrite the existing settings.

View File

@ -112,8 +112,8 @@ LogOptionsDialog::iopLogOptionsPanel::iopLogOptionsPanel( wxWindow* parent ) :
//////////////////////////////////////////////////////////////////////////////////////////
//
LogOptionsDialog::LogOptionsDialog(wxWindow* parent, int id, const wxPoint& pos, const wxSize& size):
wxDialogWithHelpers( parent, id, _("Logging"), true, pos, size )
LogOptionsDialog::LogOptionsDialog(wxWindow* parent, int id):
wxDialogWithHelpers( parent, id, _("Logging"), true )
{
eeLogOptionsPanel& eeBox = *new eeLogOptionsPanel( this );
iopLogOptionsPanel& iopSizer = *new iopLogOptionsPanel( this );

View File

@ -14,9 +14,8 @@
*/
#pragma once
#include <wx/wx.h>
#include <wx/image.h>
#include "App.h"
#include "wxHelpers.h"
#include "CheckedStaticBox.h"
@ -26,7 +25,7 @@ namespace Dialogs {
class LogOptionsDialog: public wxDialogWithHelpers
{
public:
LogOptionsDialog( wxWindow* parent, int id, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize );
LogOptionsDialog( wxWindow* parent=NULL, int id=DialogId_LogOptions );
protected:

View File

@ -15,6 +15,7 @@
#pragma once
#include "App.h"
#include "Panels/ConfigurationPanels.h"
#include <wx/image.h>
@ -68,7 +69,7 @@ namespace Dialogs
class AboutBoxDialog: public wxDialog
{
public:
AboutBoxDialog( wxWindow* parent, int id );
AboutBoxDialog( wxWindow* parent=NULL, int id=DialogId_About );
protected:
wxStaticBitmap m_bitmap_logo;

View File

@ -33,7 +33,7 @@ namespace HostGui
// Sets the status bar message without mirroring the output to the console.
void SetStatusMsg( const wxString& text )
{
wxGetApp().GetMainWindow()->SetStatusText( text );
wxGetApp().GetMainFrame().SetStatusText( text );
}
void Notice( const wxString& text )
@ -63,10 +63,7 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
switch( evt.GetKeyCode() )
{
case WXK_ESCAPE:
if( g_Conf->CloseGSonEsc )
StateRecovery::MakeGsOnly();
SysEndExecution();
m_CoreThread->Suspend();
break;
case WXK_F1:
@ -82,7 +79,7 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
Console::Notice( " > Selected savestate slot %d", StatesC);
if( GSchangeSaveState != NULL )
GSchangeSaveState(StatesC, SaveState::GetFilename(StatesC).mb_str());
GSchangeSaveState(StatesC, SaveStateBase::GetFilename(StatesC).mb_str());
break;
case WXK_F3:
@ -100,15 +97,10 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
break;
case WXK_F9: //gsdx "on the fly" renderer switching
SysSuspend();
StateRecovery::MakeGsOnly();
GetPluginManager().Close( PluginId_GS );
m_CoreThread->Suspend();
m_CorePlugins->Close( PluginId_GS );
renderswitch = !renderswitch;
StateRecovery::Recover();
SysResume();
m_CoreThread->Resume();
break;
#ifdef PCSX2_DEVBUILD

View File

@ -126,10 +126,12 @@ void MainEmuFrame::PopulatePadMenu()
//
void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
{
// Note Closure Veting would be handled here (user prompt confirmation
// Note Closure Vetoing would be handled here (user prompt confirmation
// of closing the app)
wxGetApp().PrepForExit();
if( !wxGetApp().PrepForExit() )
evt.Veto( true );
evt.Skip();
}

View File

@ -82,13 +82,13 @@ bool MainEmuFrame::_DoSelectIsoBrowser()
void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
{
SysSuspend();
wxGetApp().SysSuspend();
if( !wxFileExists( g_Conf->CurrentIso ) )
{
if( !_DoSelectIsoBrowser() )
{
SysResume();
wxGetApp().SysResume();
return;
}
}
@ -100,66 +100,37 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
if( !result )
{
SysResume();
wxGetApp().SysResume();
return;
}
}
SysEndExecution();
g_Conf->EmuOptions.SkipBiosSplash = GetMenuBar()->IsChecked( MenuId_SkipBiosToggle );
wxGetApp().SaveSettings();
InitPlugins();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
SysExecute( new AppEmuThread(), g_Conf->CdvdSource );
wxGetApp().SysExecute( g_Conf->CdvdSource );
}
void MainEmuFrame::Menu_IsoBrowse_Click( wxCommandEvent &event )
{
SysSuspend();
wxGetApp().SysSuspend();
_DoSelectIsoBrowser();
SysResume();
wxGetApp().SysResume();
}
void MainEmuFrame::Menu_RunIso_Click( wxCommandEvent &event )
{
SysSuspend();
wxGetApp().SysSuspend();
if( !_DoSelectIsoBrowser() )
{
SysResume();
wxGetApp().SysResume();
return;
}
SysEndExecution();
InitPlugins();
SysExecute( new AppEmuThread(), CDVDsrc_Iso );
wxGetApp().SysExecute( CDVDsrc_Iso );
}
/*void MainEmuFrame::Menu_RunWithoutDisc_Click(wxCommandEvent &event)
{
if( EmulationInProgress() )
{
SysSuspend();
// [TODO] : Add one of 'dems checkboxes that read like "[x] don't show this stupid shit again, kthx."
bool result = Msgbox::OkCancel( pxE( ".Popup:ConfirmEmuReset", L"This will reset the emulator and your current emulation session will be lost. Are you sure?") );
if( !result )
{
SysResume();
return;
}
}
SysEndExecution();
InitPlugins();
SysExecute( new AppEmuThread(), CDVDsrc_NoDisc );
}*/
void MainEmuFrame::Menu_IsoRecent_Click(wxCommandEvent &event)
{
//Console::Status( "%d", event.GetId() - g_RecentIsoList->GetBaseId() );
@ -194,34 +165,32 @@ void MainEmuFrame::Menu_SaveStateOther_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event)
{
SysReset();
Close();
}
void MainEmuFrame::Menu_EmuClose_Click(wxCommandEvent &event)
{
SysEndExecution();
//wxGetApp()->
GetMenuBar()->Check( MenuId_Emu_Pause, false );
}
void MainEmuFrame::Menu_EmuPause_Click(wxCommandEvent &event)
{
if( event.IsChecked() )
SysSuspend();
wxGetApp().SysSuspend();
else
SysResume();
wxGetApp().SysResume();
}
void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event)
{
bool wasRunning = EmulationInProgress();
SysReset();
wxGetApp().SysReset();
GetMenuBar()->Check( MenuId_Emu_Pause, false );
if( !wasRunning ) return;
InitPlugins();
SysExecute( new AppEmuThread() );
wxGetApp().SysExecute();
}
void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
@ -229,8 +198,11 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
typedef void (CALLBACK* PluginConfigureFnptr)();
const PluginsEnum_t pid = (PluginsEnum_t)( event.GetId() - MenuId_Config_GS );
LoadPlugins();
ScopedWindowDisable disabler( this );
LoadPluginsImmediate();
if( g_plugins == NULL ) return;
wxWindowDisabler disabler;
//ScopedWindowDisable disabler( this );
g_plugins->Configure( pid );
}

View File

@ -32,10 +32,6 @@ struct Component_FileMcd;
#include <wx/file.h>
#ifdef _WIN32
extern void NTFS_CompressFile( const wxString& file, bool compressMode );
#endif
static const int MCD_SIZE = 1024 * 8 * 16;
static const int MC2_SIZE = 1024 * 528 * 16;
@ -106,9 +102,7 @@ FileMemoryCard::FileMemoryCard()
// [TODO] : Add memcard size detection and report it to the console log.
// (8MB, 256Mb, whatever)
#ifdef _WIN32
NTFS_CompressFile( str, g_Conf->McdEnableNTFS );
#endif
if( !m_file[port][slot].Open( str.c_str(), wxFile::read_write ) )
{

View File

@ -70,7 +70,6 @@ Panels::BiosSelectorPanel::BiosSelectorPanel( wxWindow& parent, int idealWidth )
_("BIOS Search Path:"), // static box label
_("Select folder with PS2 BIOS roms") // dir picker popup label
) )
, m_BiosList( NULL )
{
m_ComboBox.SetFont( wxFont( m_ComboBox.GetFont().GetPointSize()+1, wxFONTFAMILY_MODERN, wxNORMAL, wxNORMAL, false, L"Lucida Console" ) );
m_ComboBox.SetMinSize( wxSize( wxDefaultCoord, std::max( m_ComboBox.GetMinSize().GetHeight(), 96 ) ) );
@ -88,7 +87,6 @@ Panels::BiosSelectorPanel::BiosSelectorPanel( wxWindow& parent, int idealWidth )
Panels::BiosSelectorPanel::~BiosSelectorPanel()
{
safe_delete( m_BiosList );
}
bool Panels::BiosSelectorPanel::ValidateEnumerationStatus()
@ -102,11 +100,10 @@ bool Panels::BiosSelectorPanel::ValidateEnumerationStatus()
if( m_FolderPicker.GetPath().Exists() )
wxDir::GetAllFiles( m_FolderPicker.GetPath().ToString(), bioslist.get(), L"*.bin", wxDIR_FILES );
if( (m_BiosList == NULL) || (*bioslist != *m_BiosList) )
if( !m_BiosList || (*bioslist != *m_BiosList) )
validated = false;
delete m_BiosList;
m_BiosList = bioslist.release();
m_BiosList.swap( bioslist );
return validated;
}
@ -116,7 +113,7 @@ void Panels::BiosSelectorPanel::ReloadSettings()
m_FolderPicker.Reset();
}
void Panels::BiosSelectorPanel::Apply( AppConfig& conf )
void Panels::BiosSelectorPanel::Apply()
{
int sel = m_ComboBox.GetSelection();
if( sel == wxNOT_FOUND )
@ -133,11 +130,13 @@ void Panels::BiosSelectorPanel::Apply( AppConfig& conf )
);
}
conf.BaseFilenames.Bios = (*m_BiosList)[(int)m_ComboBox.GetClientData(sel)];
g_Conf->BaseFilenames.Bios = (*m_BiosList)[(int)m_ComboBox.GetClientData(sel)];
}
void Panels::BiosSelectorPanel::DoRefresh()
{
if( !m_BiosList ) return;
wxFileName right( g_Conf->FullpathToBios() );
right.MakeAbsolute();

View File

@ -93,10 +93,6 @@ namespace Panels
// TODO : Rename me to CurOwnerBook, or rename the one above to ParentPage.
wxBookCtrlBase* ParentBook;
// Crappy hack to handle the UseAdminMode option, which can't be part of AppConfig
// because AppConfig depends on this value to initialize itself.
bool UseAdminMode;
StaticApplyState() :
PanelList()
, CurOwnerPage( wxID_NONE )
@ -168,7 +164,7 @@ namespace Panels
// configuration structure (which is typically a copy of g_Conf). If validation
// of form contents fails, the function should throw Exception::CannotApplySettings.
// If no exceptions are thrown, then the operation is assumed a success. :)
virtual void Apply( AppConfig& conf )=0;
virtual void Apply()=0;
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -183,7 +179,7 @@ namespace Panels
virtual ~UsermodeSelectionPanel() { }
UsermodeSelectionPanel( wxWindow& parent, int idealWidth=wxDefaultCoord, bool isFirstTime = true );
void Apply( AppConfig& conf );
void Apply();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -198,7 +194,7 @@ namespace Panels
virtual ~LanguageSelectionPanel() { }
LanguageSelectionPanel( wxWindow& parent, int idealWidth=wxDefaultCoord );
void Apply( AppConfig& conf );
void Apply();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -216,7 +212,7 @@ namespace Panels
public:
CpuPanel( wxWindow& parent, int idealWidth );
void Apply( AppConfig& conf );
void Apply();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -227,7 +223,7 @@ namespace Panels
public:
VideoPanel( wxWindow& parent, int idealWidth );
void Apply( AppConfig& conf );
void Apply();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -248,7 +244,7 @@ namespace Panels
public:
SpeedHacksPanel( wxWindow& parent, int idealWidth );
void Apply( AppConfig& conf );
void Apply();
protected:
const wxChar* GetEEcycleSliderMsg( int val );
@ -267,7 +263,7 @@ namespace Panels
public:
GameFixesPanel( wxWindow& parent, int idealWidth );
void Apply( AppConfig& conf );
void Apply();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -286,7 +282,7 @@ namespace Panels
DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid, const wxString& label, const wxString& dialogLabel );
virtual ~DirPickerPanel() { }
void Apply( AppConfig& conf );
void Apply();
void Reset();
wxDirName GetPath() const { return wxDirName( m_pickerCtrl->GetPath() ); }
@ -354,9 +350,9 @@ namespace Panels
class BiosSelectorPanel : public BaseSelectorPanel
{
protected:
wxScopedPtr<wxArrayString> m_BiosList;
wxListBox& m_ComboBox;
DirPickerPanel& m_FolderPicker;
wxArrayString* m_BiosList;
public:
BiosSelectorPanel( wxWindow& parent, int idealWidth );
@ -364,7 +360,7 @@ namespace Panels
void ReloadSettings();
protected:
virtual void Apply( AppConfig& conf );
virtual void Apply();
virtual void DoRefresh();
virtual bool ValidateEnumerationStatus();
};
@ -399,16 +395,18 @@ namespace Panels
class EnumThread : public Threading::PersistentThread
{
public:
EnumeratedPluginInfo* Results; // array of plugin results.
SafeList<EnumeratedPluginInfo> Results; // array of plugin results.
protected:
PluginSelectorPanel& m_master;
volatile bool m_cancel;
public:
virtual ~EnumThread();
virtual ~EnumThread() throw()
{
PersistentThread::Cancel();
}
EnumThread( PluginSelectorPanel& master );
void Cancel();
void DoNextPlugin( int evtidx );
protected:
@ -447,25 +445,27 @@ namespace Panels
};
// ------------------------------------------------------------------------
// PluginSelectorPanel Members
// PluginSelectorPanel Members
// ------------------------------------------------------------------------
protected:
wxArrayString* m_FileList; // list of potential plugin files
StatusPanel& m_StatusPanel;
ComboBoxPanel& m_ComponentBoxes;
EnumThread* m_EnumeratorThread;
wxScopedPtr<wxArrayString> m_FileList; // list of potential plugin files
wxScopedPtr<EnumThread> m_EnumeratorThread;
public:
virtual ~PluginSelectorPanel();
PluginSelectorPanel( wxWindow& parent, int idealWidth );
void CancelRefresh(); // used from destructor, stays non-virtual
void Apply( AppConfig& conf );
void Apply();
void ReloadSettings();
protected:
void OnConfigure_Clicked( wxCommandEvent& evt );
void OnShowStatusBar( wxCommandEvent& evt );
virtual void OnProgress( wxCommandEvent& evt );
virtual void OnEnumComplete( wxCommandEvent& evt );

View File

@ -78,9 +78,9 @@ Panels::CpuPanel::CpuPanel( wxWindow& parent, int idealWidth ) :
m_Option_sVU1->SetValue( recOps.EnableVU1 );
}
void Panels::CpuPanel::Apply( AppConfig& conf )
void Panels::CpuPanel::Apply()
{
Pcsx2Config::RecompilerOptions& recOps( conf.EmuOptions.Cpu.Recompiler );
Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler );
recOps.EnableEE = m_Option_RecEE->GetValue();
recOps.EnableIOP = m_Option_RecIOP->GetValue();
recOps.EnableVU0 = m_Option_mVU0->GetValue() || m_Option_sVU0->GetValue();

View File

@ -93,8 +93,7 @@ Panels::DirPickerPanel::DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid
m_checkCtrl = &AddCheckBox( s_lower, _("Use default setting") );
m_checkCtrl->SetToolTip( pxE( ".Tooltip:DirPicker:UseDefault",
L"When checked this folder will automatically reflect the default associated with PCSX2's current usermode setting. "
L"" )
L"When checked this folder will automatically reflect the default associated with PCSX2's current usermode setting. " )
);
#ifndef __WXGTK__
@ -134,7 +133,7 @@ void Panels::DirPickerPanel::Reset()
m_pickerCtrl->SetPath( GetNormalizedConfigFolder( m_FolderId ) );
}
void Panels::DirPickerPanel::Apply( AppConfig& conf )
void Panels::DirPickerPanel::Apply()
{
conf.Folders.Set( m_FolderId, m_pickerCtrl->GetPath(), m_checkCtrl->GetValue() );
g_Conf->Folders.Set( m_FolderId, m_pickerCtrl->GetPath(), m_checkCtrl->GetValue() );
}

View File

@ -85,9 +85,9 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow& parent, int idealWidth ) :
}
// I could still probably get rid of the for loop, but I think this is clearer.
void Panels::GameFixesPanel::Apply( AppConfig& conf )
void Panels::GameFixesPanel::Apply()
{
Pcsx2Config::GamefixOptions& opts( conf.EmuOptions.Gamefixes );
Pcsx2Config::GamefixOptions& opts( g_Conf->EmuOptions.Gamefixes );
for (int i = 0; i < NUM_OF_GAME_FIXES; i++)
{
if (m_checkbox[i]->GetValue())

View File

@ -60,31 +60,40 @@ void Panels::StaticApplyState::StartWizard()
bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess )
{
bool retval = true;
// Save these settings so we can restore them if the Apply fails.
bool oldAdminMode = UseAdminMode;
wxDirName oldSettingsFolder = SettingsFolder;
bool oldUseDefSet = UseDefaultSettingsFolder;
AppConfig confcopy( *g_Conf );
try
{
AppConfig confcopy( *g_Conf );
g_ApplyState.UseAdminMode = UseAdminMode;
PanelApplyList_t::iterator yay = PanelList.begin();
while( yay != PanelList.end() )
{
//DbgCon::Status( L"Writing settings for: " + (*yay)->GetLabel() );
if( (pageid < 0) || (*yay)->IsOnPage( pageid ) )
(*yay)->Apply( confcopy );
(*yay)->Apply();
yay++;
}
// If an exception is thrown above, this code below won't get run.
// (conveniently skipping any option application! :D)
UseAdminMode = g_ApplyState.UseAdminMode;
wxGetApp().ApplySettings( confcopy );
wxGetApp().ApplySettings( &confcopy );
if( saveOnSuccess )
wxGetApp().SaveSettings();
}
catch( Exception::CannotApplySettings& ex )
{
UseAdminMode = oldAdminMode;
SettingsFolder = oldSettingsFolder;
UseDefaultSettingsFolder = oldUseDefSet;
*g_Conf = confcopy;
wxMessageBox( ex.FormatDisplayMessage(), _("Cannot apply settings...") );
if( ex.GetPanel() != NULL )
@ -92,6 +101,15 @@ bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess )
retval = false;
}
catch( ... )
{
UseAdminMode = oldAdminMode;
SettingsFolder = oldSettingsFolder;
UseDefaultSettingsFolder = oldUseDefSet;
*g_Conf = confcopy;
throw;
}
return retval;
}
@ -133,12 +151,12 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow& parent, int id
SetSizer( &s_boxer );
}
void Panels::UsermodeSelectionPanel::Apply( AppConfig& conf )
void Panels::UsermodeSelectionPanel::Apply()
{
if( !m_radio_cwd->GetValue() && !m_radio_user->GetValue() )
throw Exception::CannotApplySettings( this, wxLt( "You must select one of the available user modes before proceeding." ) );
g_ApplyState.UseAdminMode = m_radio_cwd->GetValue();
UseAdminMode = m_radio_cwd->GetValue();
}
// -----------------------------------------------------------------------
@ -173,20 +191,20 @@ Panels::LanguageSelectionPanel::LanguageSelectionPanel( wxWindow& parent, int id
SetSizer( &s_lang );
}
void Panels::LanguageSelectionPanel::Apply( AppConfig& conf )
void Panels::LanguageSelectionPanel::Apply()
{
// The combo box's order is sorted and may not match our m_langs order, so
// we have to do a string comparison to find a match:
wxString sel( m_picker->GetString( m_picker->GetSelection() ) );
conf.LanguageId = wxLANGUAGE_DEFAULT; // use this if no matches found
g_Conf->LanguageId = wxLANGUAGE_DEFAULT; // use this if no matches found
int size = m_langs.size();
for( int i=0; i<size; ++i )
{
if( m_langs[i].englishName == sel )
{
conf.LanguageId = m_langs[i].wxLangId;
g_Conf->LanguageId = m_langs[i].wxLangId;
break;
}
}

View File

@ -34,12 +34,14 @@ using namespace wxHelpers;
using namespace Threading;
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(wxEVT_EnumeratedNext, -1)
DECLARE_EVENT_TYPE(wxEVT_EnumerationFinished, -1)
DECLARE_EVENT_TYPE(pxEVT_EnumeratedNext, -1)
DECLARE_EVENT_TYPE(pxEVT_EnumerationFinished, -1)
DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1)
END_DECLARE_EVENT_TYPES()
DEFINE_EVENT_TYPE(wxEVT_EnumeratedNext)
DEFINE_EVENT_TYPE(wxEVT_EnumerationFinished);
DEFINE_EVENT_TYPE(pxEVT_EnumeratedNext)
DEFINE_EVENT_TYPE(pxEVT_EnumerationFinished);
DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
typedef s32 (CALLBACK* PluginTestFnptr)();
typedef void (CALLBACK* PluginConfigureFnptr)();
@ -55,8 +57,10 @@ namespace Exception
};
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// --------------------------------------------------------------------------------------
// PluginEnumerator class
// --------------------------------------------------------------------------------------
class PluginEnumerator
{
protected:
@ -74,15 +78,15 @@ public:
// Constructor!
//
// Possible Exceptions:
// BadStream - thrown if the provided file is simply not a loadable DLL.
// NotPcsxPlugin - thrown if the DLL is not a PCSX2 plugin, or if it's of an unsupported version.
// BadStream - thrown if the provided file is simply not a loadable DLL.
// NotEnumerablePlugin - thrown if the DLL is not a PCSX2 plugin, or if it's of an unsupported version.
//
PluginEnumerator( const wxString& plugpath ) :
m_plugpath( plugpath )
, m_plugin()
{
if( !m_plugin.Load( m_plugpath ) )
throw Exception::BadStream( m_plugpath, "File is not a valid dynamic library" );
throw Exception::BadStream( m_plugpath, "File is not a valid dynamic library." );
wxDoNotLogInThisScope please;
m_GetLibType = (_PS2EgetLibType)m_plugin.GetSymbol( L"PS2EgetLibType" );
@ -90,9 +94,8 @@ public:
m_GetLibVersion2 = (_PS2EgetLibVersion2)m_plugin.GetSymbol( L"PS2EgetLibVersion2" );
if( m_GetLibType == NULL || m_GetLibName == NULL || m_GetLibVersion2 == NULL )
{
throw Exception::NotEnumerablePlugin( m_plugpath );
}
m_type = m_GetLibType();
}
@ -134,7 +137,10 @@ public:
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
// ------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
// PluginSelectorPanel implementations
// --------------------------------------------------------------------------------------
Panels::PluginSelectorPanel::StatusPanel::StatusPanel( wxWindow* parent ) :
wxPanelWithHelpers( parent )
, m_gauge( *new wxGauge( this, wxID_ANY, 10 ) )
@ -221,10 +227,8 @@ void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
// ------------------------------------------------------------------------
Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow& parent, int idealWidth ) :
BaseSelectorPanel( parent, idealWidth )
, m_FileList( NULL )
, m_StatusPanel( *new StatusPanel( this ) )
, m_ComponentBoxes( *new ComboBoxPanel( this ) )
, m_EnumeratorThread( NULL )
{
// note: the status panel is a floating window, so that it can be positioned in the
// center of the dialog after it's been fitted to the contents.
@ -242,8 +246,9 @@ Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow& parent, int idealWid
SetSizer( &s_main );
Connect( wxEVT_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) );
Connect( wxEVT_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) );
Connect( pxEVT_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) );
Connect( pxEVT_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) );
Connect( pxEVT_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) );
Connect( ButtonId_Configure, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnConfigure_Clicked ) );
}
@ -257,10 +262,19 @@ void Panels::PluginSelectorPanel::ReloadSettings()
m_ComponentBoxes.GetDirPicker().Reset();
}
void Panels::PluginSelectorPanel::Apply( AppConfig& conf )
static wxString GetApplyFailedMsg()
{
return pxE( ".Popup Error:PluginSelector:ApplyFailed",
L"All plugins must have valid selections for PCSX2 to run. If you are unable to make\n"
L"a valid selection due to missing plugins or an incomplete install of PCSX2, then\n"
L"press cancel to close the Configuration panel."
);
}
void Panels::PluginSelectorPanel::Apply()
{
// user never entered plugins panel? Skip application since combo boxes are invalid/uninitialized.
if( m_FileList == NULL ) return;
if( !m_FileList ) return;
for( int i=0; i<NumPluginTypes; ++i )
{
@ -271,32 +285,72 @@ void Panels::PluginSelectorPanel::Apply( AppConfig& conf )
throw Exception::CannotApplySettings( this,
// English Log
wxsFormat( L"User did not specify a valid selection for the %s plugin.", plugname.c_str() ),
wxsFormat( L"PluginSelectorPanel: Invalid or missing selection for the %s plugin.", plugname.c_str() ),
// Translated
wxsFormat( L"Please select a valid plugin for the %s.", plugname.c_str() ) + L"\n\n" +
pxE( ".Popup Error:Invalid Plugin Selection",
L"All plugins must have valid selections for PCSX2 to run. If you are unable to make\n"
L"a valid selection due to missing plugins or an incomplete install of PCSX2, then\n"
L"press cancel to close the Configuration panel."
)
wxsFormat( L"Please select a valid plugin for the %s.", plugname.c_str() ) + L"\n\n" + GetApplyFailedMsg()
);
}
conf.BaseFilenames.Plugins[tbl_PluginInfo[i].id] = GetFilename((int)m_ComponentBoxes.Get(i).GetClientData(sel));
g_Conf->BaseFilenames.Plugins[tbl_PluginInfo[i].id] = GetFilename((int)m_ComponentBoxes.Get(i).GetClientData(sel));
}
// ----------------------------------------------------------------------------
// Make sure folders are up to date, and try to load/reload plugins if needed...
g_Conf->Folders.ApplyDefaults();
// Need to unload the current emulation state if the user changed plugins, because
// the whole plugin system needs to be re-loaded.
const PluginInfo* pi = tbl_PluginInfo-1;
while( ++pi, pi->shortname != NULL )
{
if( g_Conf->FullpathTo( pi->id ) != g_Conf->FullpathTo( pi->id ) )
break;
}
if( pi->shortname != NULL )
{
if( wxGetApp().m_CoreThread )
{
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
}
wxGetApp().SysReset();
}
if( !wxGetApp().m_CorePlugins )
{
try
{
LoadPluginsImmediate();
}
catch( Exception::PluginError& ex )
{
// Rethrow PluginLoadErrors as a failure to Apply...
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
throw Exception::CannotApplySettings( this,
// English Log
ex.FormatDiagnosticMessage(),
// Translated
wxsFormat( L"The selected %s plugin failed to load.", plugname.c_str() ) + L"\n\n" + GetApplyFailedMsg()
);
}
}
}
void Panels::PluginSelectorPanel::CancelRefresh()
{
safe_delete( m_EnumeratorThread );
safe_delete( m_FileList );
}
void Panels::PluginSelectorPanel::DoRefresh()
{
m_ComponentBoxes.Reset();
if( m_FileList == NULL )
if( !m_FileList )
{
wxCommandEvent evt;
OnEnumComplete( evt );
@ -304,21 +358,17 @@ void Panels::PluginSelectorPanel::DoRefresh()
}
// Disable all controls until enumeration is complete.
// Show status bar for plugin enumeration.
// fixme: the status bar doesn't always fit itself to the window correctly because
// sometimes this gets called before the parent window has been fully created. I'm
// not quite sure how to fix (a delayed event/message might work tho implementing it
// may not be trivial) -- air
// Show status bar for plugin enumeration. Use a pending event so that
// the window's size can get initialized properly before trying to custom-
// fit the status panel to it.
m_ComponentBoxes.Hide();
m_StatusPanel.SetSize( m_ComponentBoxes.GetSize().GetWidth() - 8, wxDefaultCoord );
m_StatusPanel.CentreOnParent();
m_StatusPanel.Show();
wxCommandEvent evt( pxEVT_ShowStatusBar );
GetEventHandler()->AddPendingEvent( evt );
// Use a thread to load plugins.
safe_delete( m_EnumeratorThread );
m_EnumeratorThread = new EnumThread( *this );
m_EnumeratorThread.reset( NULL );
m_EnumeratorThread.reset( new EnumThread( *this ) );
if( DisableThreading )
m_EnumeratorThread->DoNextPlugin( 0 );
@ -328,6 +378,8 @@ void Panels::PluginSelectorPanel::DoRefresh()
bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
{
m_EnumeratorThread.reset(); // make sure the thread is STOPPED, just in case...
bool validated = true;
// re-enumerate plugins, and if anything changed then we need to wipe
@ -339,17 +391,16 @@ bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes.GetPluginsPath(), pluginlist.get() );
if( (m_FileList == NULL) || (*pluginlist != *m_FileList) )
if( !m_FileList || (*pluginlist != *m_FileList) )
validated = false;
if( pluggers == 0 )
{
safe_delete( m_FileList );
m_FileList.reset();
return validated;
}
delete m_FileList;
m_FileList = pluginlist.release();
m_FileList.swap( pluginlist );
m_StatusPanel.SetGaugeLength( pluggers );
@ -370,9 +421,16 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
}
}
void Panels::PluginSelectorPanel::OnShowStatusBar( wxCommandEvent& evt )
{
m_StatusPanel.SetSize( m_ComponentBoxes.GetSize().GetWidth() - 8, wxDefaultCoord );
m_StatusPanel.CentreOnParent();
m_StatusPanel.Show();
}
void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
{
safe_delete( m_EnumeratorThread );
m_EnumeratorThread.reset();
// fixme: Default plugins should be picked based on the timestamp of the DLL or something?
// (for now we just force it to selection zero if nothing's selected)
@ -395,7 +453,7 @@ void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt )
{
if( m_FileList == NULL ) return;
if( !m_FileList ) return;
const size_t evtidx = evt.GetExtraLong();
@ -404,7 +462,7 @@ void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt )
const int nextidx = evtidx+1;
if( nextidx == m_FileList->Count() )
{
wxCommandEvent done( wxEVT_EnumerationFinished );
wxCommandEvent done( pxEVT_EnumerationFinished );
GetEventHandler()->AddPendingEvent( done );
}
else
@ -447,28 +505,16 @@ void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt )
}
//////////////////////////////////////////////////////////////////////////////////////////
// EnumThread Method Implementations
// --------------------------------------------------------------------------------------
// EnumThread method implementations
// --------------------------------------------------------------------------------------
Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master ) :
PersistentThread()
, Results( new EnumeratedPluginInfo[master.FileCount()] )
, Results( master.FileCount(), L"PluginSelectorResults" )
, m_master( master )
, m_cancel( false )
{
}
Panels::PluginSelectorPanel::EnumThread::~EnumThread()
{
EnumThread::Cancel();
safe_delete_array( Results );
}
void Panels::PluginSelectorPanel::EnumThread::Cancel()
{
m_cancel = true;
Sleep( 2 );
PersistentThread::Cancel();
Results.MatchLengthToAllocatedSize();
}
void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
@ -499,7 +545,7 @@ void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
Console::Status( ex.FormatDiagnosticMessage() );
}
wxCommandEvent yay( wxEVT_EnumeratedNext );
wxCommandEvent yay( pxEVT_EnumeratedNext );
yay.SetExtraLong( curidx );
m_master.GetEventHandler()->AddPendingEvent( yay );
}
@ -509,15 +555,15 @@ sptr Panels::PluginSelectorPanel::EnumThread::ExecuteTask()
DevCon::Status( "Plugin Enumeration Thread started..." );
wxGetApp().Ping(); // gives the gui thread some time to refresh
Sleep( 3 );
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
{
if( m_cancel ) return 0;
DoNextPlugin( curidx );
pthread_testcancel();
}
wxCommandEvent done( wxEVT_EnumerationFinished );
wxCommandEvent done( pxEVT_EnumerationFinished );
m_master.GetEventHandler()->AddPendingEvent( done );
DevCon::Status( "Plugin Enumeration Thread complete!" );

View File

@ -219,9 +219,9 @@ Panels::SpeedHacksPanel::SpeedHacksPanel( wxWindow& parent, int idealWidth ) :
Connect( m_slider_vustealer->GetId(), wxEVT_SCROLL_CHANGED, wxScrollEventHandler( SpeedHacksPanel::VUCycleRate_Scroll ) );
}
void Panels::SpeedHacksPanel::Apply( AppConfig& conf )
void Panels::SpeedHacksPanel::Apply()
{
Pcsx2Config::SpeedhackOptions& opts( conf.EmuOptions.Speedhacks );
Pcsx2Config::SpeedhackOptions& opts( g_Conf->EmuOptions.Speedhacks );
opts.EECycleRate = m_slider_eecycle->GetValue()-1;
opts.VUCycleSteal = m_slider_vustealer->GetValue();

View File

@ -25,6 +25,6 @@ Panels::VideoPanel::VideoPanel( wxWindow& parent, int idealWidth ) :
// MTGS Forced Synchronization
}
void Panels::VideoPanel::Apply( AppConfig& conf )
void Panels::VideoPanel::Apply()
{
}

View File

@ -14,7 +14,7 @@
*/
#include "PrecompiledHeader.h"
#include "Utilities/ScopedPtr.h"
#include "App.h"
#include <wx/dir.h>
#include <wx/file.h>
@ -44,9 +44,73 @@ int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
}
void LoadPlugins()
using namespace Threading;
void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt )
{
if( g_plugins != NULL ) return;
ReloadPlugins();
}
void Pcsx2App::ReloadPlugins()
{
class LoadPluginsTask : public Threading::BaseTaskThread
{
public:
PluginManager* Result;
Exception::PluginError* Ex_PluginError;
Exception::RuntimeError* Ex_RuntimeError;
protected:
const wxString (&m_folders)[PluginId_Count];
public:
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) :
Result( NULL )
, Ex_PluginError( NULL )
, Ex_RuntimeError( NULL )
, m_folders( folders )
{
}
virtual ~LoadPluginsTask() throw()
{
BaseTaskThread::Cancel();
}
protected:
void Task()
{
wxGetApp().Ping();
Sleep(3);
try
{
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
Result = PluginManager_Create( m_folders );
}
catch( Exception::PluginError& ex )
{
Result = NULL;
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.
Result = NULL;
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!
}
};
m_CoreThread.reset();
m_CorePlugins.reset();
wxString passins[PluginId_Count];
const PluginInfo* pi = tbl_PluginInfo-1;
@ -57,29 +121,45 @@ void LoadPlugins()
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
passins[pi->id] = g_Conf->FullpathTo( pi->id );
}
LoadPluginsTask task( passins );
task.Start();
task.PostTask();
task.WaitForResult();
if( task.Result == NULL )
{
if( task.Ex_PluginError != NULL )
throw *task.Ex_PluginError;
if( task.Ex_RuntimeError != NULL )
throw *task.Ex_RuntimeError; // Re-Throws generic threaded errors
}
g_plugins = PluginManager_Create( passins );
m_CorePlugins.reset( task.Result );
}
void InitPlugins()
// 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."
void LoadPluginsPassive()
{
if( g_plugins == NULL )
LoadPlugins();
GetPluginManager().Init();
wxCommandEvent evt( pxEVT_ReloadPlugins );
wxGetApp().AddPendingEvent( evt );
}
// All plugins except the CDVD are opened here. The CDVD must be opened manually by
// the GUI, depending on the user's menu/config in use.
//
// Exceptions:
// PluginFailure - if any plugin fails to initialize or open.
// ThreadCreationError - If the MTGS thread fails to be created.
//
void OpenPlugins()
// Blocks until plugins have been successfully loaded, or throws an exception if
// the user cancels the loading procedure after error.
void LoadPluginsImmediate()
{
if( g_plugins == NULL )
InitPlugins();
wxASSERT( wxThread::IsMain() );
GetPluginManager().Open();
static int _reentrant = 0;
EntryGuard guard( _reentrant );
wxASSERT( !guard.IsReentrant() );
wxGetApp().ReloadPlugins();
}
void UnloadPlugins()
{
wxGetApp().m_CorePlugins.reset();
}

View File

@ -33,7 +33,7 @@ bool States_isSlotUsed(int num)
if (ElfCRC == 0)
return false;
else
return wxFileExists( SaveState::GetFilename( num ) );
return wxFileExists( SaveStateBase::GetFilename( num ) );
}
//////////////////////////////////////////////////////////////////////////////////////////
@ -62,12 +62,12 @@ void States_Load( const wxString& file )
StateRecovery::Recover();
}
SysExecute( new AppEmuThread() );
wxGetApp().SysExecute();
}
void States_Load(int num)
{
wxString file( SaveState::GetFilename( num ) );
wxString file( SaveStateBase::GetFilename( num ) );
if( !wxFileExists( file ) )
{
@ -120,79 +120,5 @@ void States_Save( const wxString& file )
void States_Save(int num)
{
Console::Status( "Saving savestate to slot %d...", num );
States_Save( SaveState::GetFilename( num ) );
}
//////////////////////////////////////////////////////////////////////////////////////////
//
void vSyncDebugStuff( uint frame )
{
#ifdef OLD_TESTBUILD_STUFF
if( g_TestRun.enabled && g_TestRun.frame > 0 ) {
if( frame > g_TestRun.frame ) {
// take a snapshot
if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) {
if( g_TestRun.snapdone ) {
g_TestRun.curimage++;
g_TestRun.snapdone = 0;
g_TestRun.frame += 20;
if( g_TestRun.curimage >= g_TestRun.numimages ) {
// exit
g_EmuThread->Cancel();
}
}
else {
// query for the image
GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture);
}
}
else {
// exit
g_EmuThread->Cancel();
}
}
}
GSVSYNC();
if( g_SaveGSStream == 1 ) {
freezeData fP;
g_SaveGSStream = 2;
g_fGSSave->gsFreeze();
if (GSfreeze(FREEZE_SIZE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
fP.data = (s8*)malloc(fP.size);
if (fP.data == NULL) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
if (GSfreeze(FREEZE_SAVE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
g_fGSSave->Freeze( fP.size );
if (fP.size) {
g_fGSSave->FreezeMem( fP.data, fP.size );
free(fP.data);
}
}
}
}
}
else if( g_SaveGSStream == 2 ) {
if( --g_nLeftGSFrames <= 0 ) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
Console::WriteLn("Done saving GS stream");
}
}
#endif
States_Save( SaveStateBase::GetFilename( num ) );
}

View File

@ -14,6 +14,7 @@
*/
#include "PrecompiledHeader.h"
#include "Utilities/HashMap.h"
#include "wxHelpers.h"
#include <wx/cshelp.h>
@ -276,12 +277,21 @@ void pxTextWrapperBase::Wrap( const wxWindow *win, const wxString& text, int wid
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
HashTools::HashMap< wxWindowID, int > m_DialogIdents( 0, wxID_ANY );
bool pxDialogExists( wxWindowID id )
{
int dest = 0;
m_DialogIdents.TryGetValue( id, dest );
return (dest > 0);
}
wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos, const wxSize& size ) :
wxDialog( parent, id, title, pos, size , wxDEFAULT_DIALOG_STYLE), //, (wxCAPTION | wxMAXIMIZE | wxCLOSE_BOX | wxRESIZE_BORDER) ), // flags for resizable dialogs, currently unused.
m_hasContextHelp( hasContextHelp )
{
++m_DialogIdents[GetId()];
if( hasContextHelp )
wxHelpProvider::Set( new wxSimpleHelpProvider() );
@ -291,6 +301,12 @@ wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxStr
// any good.
}
wxDialogWithHelpers::~wxDialogWithHelpers()
{
--m_DialogIdents[GetId()];
wxASSERT( m_DialogIdents[GetId()] >= 0 );
}
// ------------------------------------------------------------------------
// Creates a new checkbox and adds it to the specified sizer/parent combo, with optional tooltip.
// Uses the default spacer setting for adding checkboxes, and the tooltip (if specified) is applied

View File

@ -55,6 +55,7 @@ protected:
public:
wxDialogWithHelpers(wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize );
virtual ~wxDialogWithHelpers() throw();
wxCheckBox& AddCheckBox( wxSizer& sizer, const wxString& label, const wxString& subtext=wxEmptyString, const wxString& tooltip=wxEmptyString );
wxStaticText& AddStaticText(wxSizer& sizer, const wxString& label, int alignFlags=wxALIGN_CENTRE, int size=wxDefaultCoord );
@ -88,3 +89,6 @@ protected:
m_StartNewRadioGroup = true;
}
};
extern bool pxDialogExists( wxWindowID id );

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

View File

@ -235,7 +235,7 @@ bool IsBIOS(const wxString& filename, wxString& description)
romver[12], romver[13], // day
romver[10], romver[11], // month
romver[6], romver[7], romver[8], romver[9], // year!
(aROMVER[5]=='C') ? L"Console" : (aROMVER[5]=='D') ? L"Devel" : L""
(aROMVER[5]=='C') ? L"Console" : (aROMVER[5]=='D') ? L"Devel" : wxEmptyString
);
found = true;
}

View File

@ -35,10 +35,10 @@ CoreEmuThread& CoreEmuThread::Get()
void CoreEmuThread::CpuInitializeMess()
{
GetPluginManager().Open();
m_plugins.Open();
cpuReset();
SysClearExecutionCache();
GetPluginManager().Open();
m_plugins.Open();
if( StateRecovery::HasState() )
{
@ -133,53 +133,61 @@ sptr CoreEmuThread::ExecuteTask()
void CoreEmuThread::StateCheck()
{
ScopedLock locker( m_lock_ExecMode );
switch( m_ExecMode )
{
case ExecMode_NoThreadYet:
case ExecMode_Idle:
// threads should never have an idle execution state set while the
// thread is in any way active or alive.
DevAssert( false, "Invalid execution state detected." );
break;
// These are not the case statements you're looking for. Move along.
case ExecMode_Running:
pthread_testcancel();
break;
case ExecMode_Suspending:
{
ScopedLock locker( m_lock_ExecMode );
m_plugins.Close();
m_ExecMode = ExecMode_Suspended;
m_SuspendEvent.Post();
}
}
// fall through...
case ExecMode_Suspended:
m_lock_ExecMode.Unlock();
while( m_ExecMode == ExecMode_Suspended )
m_ResumeEvent.WaitGui();
m_plugins.Open();
break;
}
}
CoreEmuThread::CoreEmuThread() :
m_ExecMode( ExecMode_Idle )
, m_ResumeEvent()
, m_SuspendEvent()
CoreEmuThread::CoreEmuThread( PluginManager& plugins ) :
m_ExecMode( ExecMode_NoThreadYet )
, m_lock_ExecMode()
, m_resetRecompilers( false )
, m_resetProfilers( false )
, m_lock_ExecMode()
, m_plugins( plugins )
, m_ResumeEvent()
, m_SuspendEvent()
{
PersistentThread::Start();
}
// Invoked by the pthread_exit or pthread_cancel
void CoreEmuThread::DoThreadCleanup()
{
GetPluginManager().Close();
m_plugins.Shutdown();
PersistentThread::DoThreadCleanup();
}
CoreEmuThread::~CoreEmuThread()
CoreEmuThread::~CoreEmuThread() throw()
{
PersistentThread::Cancel();
}
@ -187,13 +195,26 @@ CoreEmuThread::~CoreEmuThread()
// 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.
//
// Exceptions (can occur on first call only):
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
// on the first time the thread is resumed from it's initial idle state)
// ThreadCreationError - Insufficient system resources to create thread.
//
void CoreEmuThread::Resume()
{
if( IsSelf() || !IsRunning() ) return;
if( IsSelf() ) return;
{
ScopedLock locker( m_lock_ExecMode );
if( m_ExecMode == ExecMode_NoThreadYet )
{
m_plugins.Init();
Start();
m_ExecMode = ExecMode_Idle;
}
if( m_ExecMode == ExecMode_Running )
return;

View File

@ -19,14 +19,15 @@
using namespace Threading;
/////////////////////////////////////////////////////////////////////////////////////////
// CoreEmuThread
//
// --------------------------------------------------------------------------------------
// CoreEmuThread class
// --------------------------------------------------------------------------------------
class CoreEmuThread : public PersistentThread
{
public:
protected:
enum ExecutionMode
{
ExecMode_NoThreadYet,
ExecMode_Idle,
ExecMode_Running,
ExecMode_Suspending,
@ -34,22 +35,22 @@ public:
};
protected:
volatile ExecutionMode m_ExecMode;
volatile ExecutionMode m_ExecMode;
MutexLock m_lock_ExecMode;
Semaphore m_ResumeEvent;
Semaphore m_SuspendEvent;
bool m_resetRecompilers;
bool m_resetProfilers;
bool m_resetRecompilers;
bool m_resetProfilers;
MutexLock m_lock_ExecMode;
PluginManager& m_plugins;
Semaphore m_ResumeEvent;
Semaphore m_SuspendEvent;
public:
static CoreEmuThread& Get();
public:
explicit CoreEmuThread();
virtual ~CoreEmuThread();
explicit CoreEmuThread( PluginManager& plugins );
virtual ~CoreEmuThread() throw();
bool IsSuspended() const { return (m_ExecMode == ExecMode_Suspended); }
virtual void Suspend( bool isBlocking = true );

View File

@ -20,7 +20,6 @@
<Configurations>
<Configuration
Name="Debug|Win32"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
InheritedPropertySheets=".\vsprops\common.vsprops;..\..\..\common\vsprops\BaseProperties.vsprops;..\..\..\common\vsprops\3rdpartyDeps.vsprops;..\..\..\common\vsprops\pthreads.vsprops;.\vsprops\devbuild.vsprops;..\..\..\common\vsprops\CodeGen_Debug.vsprops;..\..\..\common\vsprops\IncrementalLinking.vsprops"
UseOfMFC="0"

View File

@ -212,8 +212,8 @@ Global
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release|Win32.ActiveCfg = Release|Win32
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release|Win32.Build.0 = Release|Win32
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release|x64.ActiveCfg = Release|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Debug|Win32.ActiveCfg = Debug SSE2|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Debug|Win32.Build.0 = Debug SSE2|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Debug|Win32.ActiveCfg = Debug SSE4|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Debug|Win32.Build.0 = Debug SSE4|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Debug|x64.ActiveCfg = Debug|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Devel|Win32.ActiveCfg = Release SSE2|Win32
{18E42F6F-3A62-41EE-B42F-79366C4F1E95}.Devel|Win32.Build.0 = Release SSE2|Win32

View File

@ -1109,12 +1109,9 @@ __declspec(align(16)) struct GIFPath
{
if((++reg & 0xf) == nreg)
{
reg = 0;
reg = 0;
if(--nloop == 0)
{
return false;
}
}
return true;

View File

@ -56,6 +56,7 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\$(ProjectName)-$(SSEtype)-dbg.dll"
ModuleDefinitionFile=".\GSdx.def"
TargetMachine="1"
/>
@ -119,7 +120,6 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\$(ProjectName)-$(SSEtype).dll"
ModuleDefinitionFile=".\GSdx.def"
TargetMachine="1"
/>
@ -245,7 +245,7 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\$(ProjectName)-$(SSEtype).dll"
OutputFile="$(OutDir)\$(ProjectName)-$(SSEtype)-dbg.dll"
ModuleDefinitionFile=".\GSdx.def"
TargetMachine="1"
/>
@ -308,6 +308,7 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\$(ProjectName)-$(SSEtype)-dbg.dll"
ModuleDefinitionFile=".\GSdx.def"
TargetMachine="1"
/>
@ -371,7 +372,6 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\$(ProjectName)-$(SSEtype).dll"
ModuleDefinitionFile=".\GSdx.def"
TargetMachine="1"
/>