2009-02-09 21:15:56 +00:00
|
|
|
/* Pcsx2 - Pc Ps2 Emulator
|
2009-02-15 23:23:46 +00:00
|
|
|
* Copyright (C) 2002-2009 Pcsx2 Team
|
2009-02-09 21:15:56 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
|
2009-03-13 06:14:08 +00:00
|
|
|
#include "IopCommon.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
#include "SaveState.h"
|
|
|
|
|
2009-05-16 07:24:31 +00:00
|
|
|
#include "CDVD/CDVDisodrv.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
#include "VUmicro.h"
|
|
|
|
#include "VU.h"
|
|
|
|
#include "iCore.h"
|
2009-06-11 09:02:10 +00:00
|
|
|
#include "sVU_zerorec.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
#include "GS.h"
|
|
|
|
#include "COP0.h"
|
|
|
|
#include "Cache.h"
|
|
|
|
|
|
|
|
using namespace R5900;
|
|
|
|
|
|
|
|
extern void recResetEE();
|
|
|
|
extern void recResetIOP();
|
|
|
|
|
|
|
|
static void PreLoadPrep()
|
|
|
|
{
|
2009-03-04 11:33:45 +00:00
|
|
|
SysClearExecutionCache();
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void PostLoadPrep()
|
|
|
|
{
|
|
|
|
memzero_obj(pCache);
|
|
|
|
// WriteCP0Status(cpuRegs.CP0.n.Status.val);
|
|
|
|
for(int i=0; i<48; i++) MapTLB(i);
|
|
|
|
}
|
|
|
|
|
2009-04-02 11:30:23 +00:00
|
|
|
wxString SaveState::GetFilename( int slot )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-04-27 02:04:31 +00:00
|
|
|
return (g_Conf.Folders.Savestates +
|
2009-05-01 02:15:18 +00:00
|
|
|
wxsFormat( L"%8.8X.%3.3d", ElfCRC, slot )).GetFullPath();
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2009-04-02 11:30:23 +00:00
|
|
|
SaveState::SaveState( const char* msg, const wxString& destination ) :
|
2009-03-19 04:16:24 +00:00
|
|
|
m_version( g_SaveVersion )
|
|
|
|
, m_tagspace( 128 )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-04-27 02:04:31 +00:00
|
|
|
Console::WriteLn( "%s %s", params msg, destination.ToAscii().data() );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 CALLBACK gsSafeFreeze( int mode, freezeData *data )
|
|
|
|
{
|
|
|
|
if( mtgsThread != NULL )
|
|
|
|
{
|
|
|
|
if( mode == 2 )
|
|
|
|
return GSfreeze( 2, data );
|
|
|
|
|
|
|
|
// have to call in thread, otherwise weird stuff will start happening
|
|
|
|
mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, data );
|
|
|
|
mtgsWaitGS();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Single threaded...
|
|
|
|
return GSfreeze( mode, data );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
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 )
|
|
|
|
{
|
|
|
|
assert( 0 );
|
2009-04-27 02:04:31 +00:00
|
|
|
throw Exception::BadSavedState(
|
|
|
|
// Untranslated diagnostic msg (use default msg for translation)
|
2009-05-01 02:15:18 +00:00
|
|
|
L"Savestate data corruption detected while reading tag: " + wxString::FromAscii(src)
|
2009-04-27 02:04:31 +00:00
|
|
|
);
|
2009-03-19 04:16:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
void SaveState::FreezeAll()
|
|
|
|
{
|
|
|
|
if( IsLoading() )
|
|
|
|
PreLoadPrep();
|
2009-03-19 04:16:24 +00:00
|
|
|
|
|
|
|
// 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).
|
|
|
|
|
2009-04-27 02:04:31 +00:00
|
|
|
char descin[128];
|
|
|
|
wxString descout;
|
2009-07-14 06:18:52 +00:00
|
|
|
IsBIOS( g_Conf.FullpathToBios(), descout );
|
2009-04-27 02:04:31 +00:00
|
|
|
memcpy_fast( descin, descout.ToAscii().data(), 128 );
|
2009-03-19 04:16:24 +00:00
|
|
|
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",
|
2009-04-27 02:04:31 +00:00
|
|
|
params descout.ToAscii().data(), descin
|
2009-03-19 04:16:24 +00:00
|
|
|
);
|
|
|
|
}
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
// First Block - Memory Dumps
|
|
|
|
// ---------------------------
|
|
|
|
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
|
2009-02-09 21:15:56 +00:00
|
|
|
FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad
|
2009-03-19 04:16:24 +00:00
|
|
|
FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
FreezeMem(psxM, Ps2MemSize::IopRam); // 2 MB main memory
|
|
|
|
FreezeMem(psxH, Ps2MemSize::IopHardware); // hardware memory
|
|
|
|
FreezeMem(psxS, 0x000100); // iop's sif memory
|
|
|
|
|
|
|
|
// Second Block - Various CPU Registers and States
|
|
|
|
// -----------------------------------------------
|
|
|
|
FreezeTag( "cpuRegs" );
|
2009-02-09 21:15:56 +00:00
|
|
|
Freeze(cpuRegs); // cpu regs + COP0
|
|
|
|
Freeze(psxRegs); // iop regs
|
2009-03-19 04:16:24 +00:00
|
|
|
Freeze(fpuRegs);
|
2009-02-09 21:15:56 +00:00
|
|
|
Freeze(tlb); // tlbs
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
// Third Block - Cycle Timers and Events
|
|
|
|
// -------------------------------------
|
|
|
|
FreezeTag( "Cycles" );
|
2009-02-09 21:15:56 +00:00
|
|
|
Freeze(EEsCycle);
|
|
|
|
Freeze(EEoCycle);
|
|
|
|
Freeze(g_nextBranchCycle);
|
|
|
|
Freeze(g_psxNextBranchCycle);
|
|
|
|
Freeze(s_iLastCOP0Cycle);
|
2009-02-19 22:48:05 +00:00
|
|
|
Freeze(s_iLastPERFCycle);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
// Fourth Block - EE-related systems
|
|
|
|
// ---------------------------------
|
2009-02-09 21:15:56 +00:00
|
|
|
rcntFreeze();
|
|
|
|
gsFreeze();
|
|
|
|
vuMicroFreeze();
|
|
|
|
vif0Freeze();
|
|
|
|
vif1Freeze();
|
|
|
|
sifFreeze();
|
|
|
|
ipuFreeze();
|
2009-02-20 00:39:58 +00:00
|
|
|
gifFreeze();
|
2009-03-04 13:05:06 +00:00
|
|
|
sprFreeze();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
// Fifth Block - iop-related systems
|
|
|
|
// ---------------------------------
|
|
|
|
psxRcntFreeze();
|
2009-02-09 21:15:56 +00:00
|
|
|
sioFreeze();
|
2009-03-19 04:16:24 +00:00
|
|
|
sio2Freeze();
|
2009-02-09 21:15:56 +00:00
|
|
|
cdrFreeze();
|
|
|
|
cdvdFreeze();
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
// Sixth Block - Plugins Galore!
|
|
|
|
// -----------------------------
|
2009-02-09 21:15:56 +00:00
|
|
|
FreezePlugin( "GS", gsSafeFreeze );
|
|
|
|
FreezePlugin( "SPU2", SPU2freeze );
|
|
|
|
FreezePlugin( "DEV9", DEV9freeze );
|
|
|
|
FreezePlugin( "USB", USBfreeze );
|
2009-03-15 09:30:31 +00:00
|
|
|
FreezePlugin( "PAD1", PAD1freeze );
|
|
|
|
FreezePlugin( "PAD2", PAD2freeze );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
if( IsLoading() )
|
|
|
|
PostLoadPrep();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// gzipped to/from disk state saves implementation
|
|
|
|
|
2009-04-02 11:30:23 +00:00
|
|
|
gzBaseStateInfo::gzBaseStateInfo( const char* msg, const wxString& filename ) :
|
2009-02-09 21:15:56 +00:00
|
|
|
SaveState( msg, filename )
|
|
|
|
, m_filename( filename )
|
|
|
|
, m_file( NULL )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
gzBaseStateInfo::~gzBaseStateInfo()
|
|
|
|
{
|
|
|
|
if( m_file != NULL )
|
|
|
|
{
|
|
|
|
gzclose( m_file );
|
|
|
|
m_file = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-02 11:30:23 +00:00
|
|
|
gzSavingState::gzSavingState( const wxString& filename ) :
|
2009-03-10 11:35:24 +00:00
|
|
|
gzBaseStateInfo( "Saving state to: ", filename )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-04-27 02:04:31 +00:00
|
|
|
m_file = gzopen(filename.ToAscii().data(), "wb");
|
2009-02-09 21:15:56 +00:00
|
|
|
if( m_file == NULL )
|
|
|
|
throw Exception::FileNotFound();
|
|
|
|
|
2009-03-30 18:19:05 +00:00
|
|
|
gzsetparams( m_file, Z_BEST_SPEED, Z_DEFAULT_STRATEGY );
|
2009-02-09 21:15:56 +00:00
|
|
|
Freeze( m_version );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-02 11:30:23 +00:00
|
|
|
gzLoadingState::gzLoadingState( const wxString& filename ) :
|
2009-03-10 11:35:24 +00:00
|
|
|
gzBaseStateInfo( "Loading state from: ", filename )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-04-27 02:04:31 +00:00
|
|
|
m_file = gzopen(filename.ToAscii().data(), "rb");
|
2009-02-09 21:15:56 +00:00
|
|
|
if( m_file == NULL )
|
|
|
|
throw Exception::FileNotFound();
|
|
|
|
|
|
|
|
gzread( m_file, &m_version, 4 );
|
|
|
|
|
2009-03-06 20:57:14 +00:00
|
|
|
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 )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-03-06 19:01:30 +00:00
|
|
|
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 );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gzLoadingState::~gzLoadingState() { }
|
|
|
|
|
|
|
|
|
|
|
|
void gzSavingState::FreezeMem( void* data, int size )
|
|
|
|
{
|
|
|
|
gzwrite( m_file, data, size );
|
|
|
|
}
|
|
|
|
|
|
|
|
void gzLoadingState::FreezeMem( void* data, int size )
|
|
|
|
{
|
2009-03-19 04:16:24 +00:00
|
|
|
if( gzread( m_file, data, size ) != size )
|
2009-02-09 21:15:56 +00:00
|
|
|
throw Exception::BadSavedState( m_filename );
|
|
|
|
}
|
|
|
|
|
|
|
|
void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
|
|
|
|
{
|
|
|
|
freezeData fP = { 0, NULL };
|
2009-03-19 04:16:24 +00:00
|
|
|
Console::WriteLn( "\tSaving %s", params name );
|
|
|
|
|
|
|
|
FreezeTag( name );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
if (freezer(FREEZE_SIZE, &fP) == -1)
|
|
|
|
throw Exception::FreezePluginFailure( name, "saving" );
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
Freeze( fP.size );
|
2009-02-09 21:15:56 +00:00
|
|
|
if( fP.size == 0 ) return;
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
SafeArray<s8> buffer( fP.size );
|
|
|
|
fP.data = buffer.GetPtr();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
if(freezer(FREEZE_SAVE, &fP) == -1)
|
|
|
|
throw Exception::FreezePluginFailure( name, "saving" );
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
FreezeMem( fP.data, fP.size );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
|
|
|
|
{
|
|
|
|
freezeData fP = { 0, NULL };
|
|
|
|
Console::WriteLn( "\tLoading %s", params name );
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
FreezeTag( name );
|
|
|
|
Freeze( fP.size );
|
2009-02-09 21:15:56 +00:00
|
|
|
if( fP.size == 0 ) return;
|
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
SafeArray<s8> buffer( fP.size );
|
|
|
|
fP.data = buffer.GetPtr();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-03-19 04:16:24 +00:00
|
|
|
FreezeMem( fP.data, fP.size );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
if(freezer(FREEZE_LOAD, &fP) == -1)
|
|
|
|
throw Exception::FreezePluginFailure( name, "loading" );
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// uncompressed to/from memory state saves implementation
|
|
|
|
|
2009-02-10 11:34:35 +00:00
|
|
|
memBaseStateInfo::memBaseStateInfo( SafeArray<u8>& memblock, const char* msg ) :
|
2009-05-01 02:15:18 +00:00
|
|
|
SaveState( msg, L"Memory")
|
2009-04-27 02:04:31 +00:00
|
|
|
, m_memory( memblock )
|
|
|
|
, m_idx( 0 )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
// Always clear the MTGS thread state.
|
|
|
|
mtgsWaitGS();
|
|
|
|
}
|
|
|
|
|
2009-03-10 11:35:24 +00:00
|
|
|
memSavingState::memSavingState( SafeArray<u8>& save_to ) : memBaseStateInfo( save_to, "Saving state to: " )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
save_to.ChunkSize = ReallocThreshold;
|
|
|
|
save_to.MakeRoomFor( MemoryBaseAllocSize );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saving of state data to a memory buffer
|
|
|
|
void memSavingState::FreezeMem( void* data, int size )
|
|
|
|
{
|
|
|
|
const int end = m_idx+size;
|
|
|
|
m_memory.MakeRoomFor( end );
|
|
|
|
|
|
|
|
u8* dest = (u8*)m_memory.GetPtr();
|
|
|
|
const u8* src = (u8*)data;
|
|
|
|
|
|
|
|
for( ; m_idx<end; ++m_idx, ++src )
|
|
|
|
dest[m_idx] = *src;
|
|
|
|
}
|
|
|
|
|
2009-02-10 11:34:35 +00:00
|
|
|
memLoadingState::memLoadingState(SafeArray<u8>& load_from ) :
|
2009-03-10 11:35:24 +00:00
|
|
|
memBaseStateInfo( load_from, "Loading state from: " )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
memLoadingState::~memLoadingState() { }
|
|
|
|
|
|
|
|
// Loading of state data from a memory buffer...
|
|
|
|
void memLoadingState::FreezeMem( void* data, int size )
|
|
|
|
{
|
|
|
|
const int end = m_idx+size;
|
|
|
|
const u8* src = (u8*)m_memory.GetPtr();
|
|
|
|
u8* dest = (u8*)data;
|
|
|
|
|
|
|
|
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", params 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", params name );
|
|
|
|
|
|
|
|
Freeze( fP.size );
|
|
|
|
if( fP.size == 0 ) return;
|
|
|
|
|
|
|
|
if( ( fP.size + m_idx ) > m_memory.GetSizeInBytes() )
|
|
|
|
{
|
|
|
|
assert(0);
|
2009-05-01 02:15:18 +00:00
|
|
|
throw Exception::BadSavedState( L"memory");
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fP.data = ((s8*)m_memory.GetPtr()) + m_idx;
|
|
|
|
if(freezer(FREEZE_LOAD, &fP) == -1)
|
|
|
|
throw Exception::FreezePluginFailure( name, "loading" );
|
|
|
|
|
|
|
|
m_idx += fP.size;
|
|
|
|
}
|