Major overhaul of the savestate system!

Since backwards compat of savestates got totally broken a few revisions ago anyway, I decided to take this opportunity to revamp the savestate system with some significant cleanups (yes it loses all backward compat once again).  Improvements include:

 * Reduced state size by removing some unneeded data (faster saves now too!)
 * Added string tags to varios "sections" of the states to assist in troubleshooting and retaining savestate compat in future versions.
 * Better error handling and fewer memory leaks.
 * Removed some unused/obsolete data from psxRegs, Counters, and psxCounters structures.
 * Removed all old savestate versioning code, since none of the old versions are supported anymore anyways.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@815 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-03-19 04:16:24 +00:00
parent a51b407cdb
commit 1f36e97927
21 changed files with 230 additions and 247 deletions

View File

@ -786,6 +786,7 @@ struct Freeze_v10Compat
void SaveState::cdvdFreeze() void SaveState::cdvdFreeze()
{ {
FreezeTag( "cdvd" );
Freeze( cdvd ); Freeze( cdvd );
if( IsLoading() ) if( IsLoading() )

View File

@ -944,15 +944,9 @@ void cdrReset() {
cdReadTime = (PSXCLK / 1757) * BIAS; cdReadTime = (PSXCLK / 1757) * BIAS;
} }
void SaveState::cdrFreeze() { void SaveState::cdrFreeze()
{
FreezeTag( "cdrom" );
Freeze(cdr); Freeze(cdr);
// Alrighty! This code used to, for some reason, recalculate the pTransfer value
// even though it's being saved as part of the cdr struct. Probably a backwards
// compat fix with an earlier save version.
int tmp; // = (int)(cdr.pTransfer - cdr.Transfer);
Freeze(tmp);
//if (Mode == 0) cdr.pTransfer = cdr.Transfer + tmp;
} }

View File

@ -43,7 +43,9 @@ int gates = 0;
// Counter 4 takes care of scanlines - hSync/hBlanks // Counter 4 takes care of scanlines - hSync/hBlanks
// Counter 5 takes care of vSync/vBlanks // Counter 5 takes care of vSync/vBlanks
Counter counters[6]; Counter counters[4];
SyncCounter hsyncCounter;
SyncCounter vsyncCounter;
u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate() u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate() s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
@ -101,7 +103,7 @@ static __forceinline void cpuRcntSet()
int i; int i;
nextsCounter = cpuRegs.cycle; nextsCounter = cpuRegs.cycle;
nextCounter = (counters[5].sCycle + counters[5].CycleT) - cpuRegs.cycle; nextCounter = (vsyncCounter.sCycle + vsyncCounter.CycleT) - cpuRegs.cycle;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
_rcntSet( i ); _rcntSet( i );
@ -124,10 +126,10 @@ void rcntInit() {
counters[2].interrupt = 11; counters[2].interrupt = 11;
counters[3].interrupt = 12; counters[3].interrupt = 12;
counters[4].modeval = MODE_HRENDER; hsyncCounter.Mode = MODE_HRENDER;
counters[4].sCycle = cpuRegs.cycle; hsyncCounter.sCycle = cpuRegs.cycle;
counters[5].modeval = MODE_VRENDER; vsyncCounter.Mode = MODE_VRENDER;
counters[5].sCycle = cpuRegs.cycle; vsyncCounter.sCycle = cpuRegs.cycle;
UpdateVSyncRate(); UpdateVSyncRate();
@ -136,7 +138,7 @@ void rcntInit() {
} }
// debug code, used for stats // debug code, used for stats
int g_nCounters[4]; int g_nhsyncCounter;
static uint iFrame = 0; static uint iFrame = 0;
#ifndef _WIN32 #ifndef _WIN32
@ -235,8 +237,8 @@ u32 UpdateVSyncRate()
vSyncInfoCalc( &vSyncInfo, FRAMERATE_NTSC, SCANLINES_TOTAL_NTSC ); vSyncInfoCalc( &vSyncInfo, FRAMERATE_NTSC, SCANLINES_TOTAL_NTSC );
} }
counters[4].CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated hsyncCounter.CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated
counters[5].CycleT = vSyncInfo.Render; // Amount of cycles before the counter will be updated vsyncCounter.CycleT = vSyncInfo.Render; // Amount of cycles before the counter will be updated
if (Config.CustomFps > 0) if (Config.CustomFps > 0)
{ {
@ -393,28 +395,28 @@ static int vblankinc = 0;
__forceinline void rcntUpdate_hScanline() __forceinline void rcntUpdate_hScanline()
{ {
if( !cpuTestCycle( counters[4].sCycle, counters[4].CycleT ) ) return; if( !cpuTestCycle( hsyncCounter.sCycle, hsyncCounter.CycleT ) ) return;
//iopBranchAction = 1; //iopBranchAction = 1;
if (counters[4].modeval & MODE_HBLANK) { //HBLANK Start if (hsyncCounter.Mode & MODE_HBLANK) { //HBLANK Start
rcntStartGate(false, counters[4].sCycle); rcntStartGate(false, hsyncCounter.sCycle);
psxCheckStartGate16(0); psxCheckStartGate16(0);
// Setup the hRender's start and end cycle information: // Setup the hRender's start and end cycle information:
counters[4].sCycle += vSyncInfo.hBlank; // start (absolute cycle value) hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
counters[4].CycleT = vSyncInfo.hRender; // endpoint (delta from start value) hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
counters[4].modeval = MODE_HRENDER; hsyncCounter.Mode = MODE_HRENDER;
} }
else { //HBLANK END / HRENDER Begin else { //HBLANK END / HRENDER Begin
if (CSRw & 0x4) GSCSRr |= 4; // signal if (CSRw & 0x4) GSCSRr |= 4; // signal
if (!(GSIMR&0x400)) gsIrq(); if (!(GSIMR&0x400)) gsIrq();
if (gates) rcntEndGate(false, counters[4].sCycle); if (gates) rcntEndGate(false, hsyncCounter.sCycle);
if (psxhblankgate) psxCheckEndGate16(0); if (psxhblankgate) psxCheckEndGate16(0);
// set up the hblank's start and end cycle information: // set up the hblank's start and end cycle information:
counters[4].sCycle += vSyncInfo.hRender; // start (absolute cycle value) hsyncCounter.sCycle += vSyncInfo.hRender; // start (absolute cycle value)
counters[4].CycleT = vSyncInfo.hBlank; // endpoint (delta from start value) hsyncCounter.CycleT = vSyncInfo.hBlank; // endpoint (delta from start value)
counters[4].modeval = MODE_HBLANK; hsyncCounter.Mode = MODE_HBLANK;
# ifdef VSYNC_DEBUG # ifdef VSYNC_DEBUG
hsc++; hsc++;
@ -424,30 +426,30 @@ __forceinline void rcntUpdate_hScanline()
__forceinline bool rcntUpdate_vSync() __forceinline bool rcntUpdate_vSync()
{ {
s32 diff = (cpuRegs.cycle - counters[5].sCycle); s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle);
if( diff < counters[5].CycleT ) return false; if( diff < vsyncCounter.CycleT ) return false;
//iopBranchAction = 1; //iopBranchAction = 1;
if (counters[5].modeval == MODE_VSYNC) if (vsyncCounter.Mode == MODE_VSYNC)
{ {
VSyncEnd(counters[5].sCycle); VSyncEnd(vsyncCounter.sCycle);
counters[5].sCycle += vSyncInfo.Blank; vsyncCounter.sCycle += vSyncInfo.Blank;
counters[5].CycleT = vSyncInfo.Render; vsyncCounter.CycleT = vSyncInfo.Render;
counters[5].modeval = MODE_VRENDER; vsyncCounter.Mode = MODE_VRENDER;
return true; return true;
} }
else // VSYNC end / VRENDER begin else // VSYNC end / VRENDER begin
{ {
VSyncStart(counters[5].sCycle); VSyncStart(vsyncCounter.sCycle);
counters[5].sCycle += vSyncInfo.Render; vsyncCounter.sCycle += vSyncInfo.Render;
counters[5].CycleT = vSyncInfo.Blank; vsyncCounter.CycleT = vSyncInfo.Blank;
counters[5].modeval = MODE_VSYNC; vsyncCounter.Mode = MODE_VSYNC;
// Accumulate hsync rounding errors: // Accumulate hsync rounding errors:
counters[4].sCycle += vSyncInfo.hSyncError; hsyncCounter.sCycle += vSyncInfo.hSyncError;
# ifdef VSYNC_DEBUG # ifdef VSYNC_DEBUG
vblankinc++; vblankinc++;
@ -766,15 +768,12 @@ u32 __fastcall rcntCycle(int index)
void SaveState::rcntFreeze() void SaveState::rcntFreeze()
{ {
Freeze(counters); Freeze( counters );
Freeze(nextCounter); Freeze( hsyncCounter );
Freeze(nextsCounter); Freeze( vsyncCounter );
Freeze( nextCounter );
// New in version 1 -- save the PAL/NTSC info! Freeze( nextsCounter );
if( GetVersion() >= 0x1 ) Freeze( Config.PsxType );
{
Freeze( Config.PsxType );
}
if( IsLoading() ) if( IsLoading() )
{ {

View File

@ -66,7 +66,8 @@ struct EECNT_MODE
// fixme: Cycle and sCycleT members are unused. // fixme: Cycle and sCycleT members are unused.
// But they can't be removed without making a new savestate version. // But they can't be removed without making a new savestate version.
struct Counter { struct Counter
{
u32 count; u32 count;
union union
{ {
@ -75,10 +76,14 @@ struct Counter {
}; };
u32 target, hold; u32 target, hold;
u32 rate, interrupt; u32 rate, interrupt;
u32 Cycle; u32 sCycleT; // delta values should be signed.
};
struct SyncCounter
{
u32 Mode;
u32 sCycle; // start cycle of timer u32 sCycle; // start cycle of timer
s32 CycleT; s32 CycleT;
u32 sCycleT; // delta values should be signed.
}; };
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -124,7 +129,10 @@ struct Counter {
#define MODE_HBLANK 0x1 //Set for the remaining ~1/6 of 1 Scanline #define MODE_HBLANK 0x1 //Set for the remaining ~1/6 of 1 Scanline
extern Counter counters[6]; extern Counter counters[4];
extern SyncCounter hsyncCounter;
extern SyncCounter vsyncCounter;
extern s32 nextCounter; // delta until the next counter event (must be signed) extern s32 nextCounter; // delta until the next counter event (must be signed)
extern u32 nextsCounter; extern u32 nextsCounter;

View File

@ -615,13 +615,12 @@ void gifMFIFOInterrupt()
void SaveState::gifFreeze() void SaveState::gifFreeze()
{ {
if( GetVersion() >= 0x04 ) FreezeTag( "GIFdma" );
{
Freeze( gifstate );
Freeze( gifqwc );
Freeze( gspath3done );
Freeze( gscycles );
// Note: mfifocycles is not a persistent var, so no need to save it here. Freeze( gifstate );
} Freeze( gifqwc );
Freeze( gspath3done );
Freeze( gscycles );
// Note: mfifocycles is not a persistent var, so no need to save it here.
} }

View File

@ -163,14 +163,14 @@ void ipuShutdown()
// fixme - ipuFreeze looks fairly broken. Should probably take a closer look at some point. // fixme - ipuFreeze looks fairly broken. Should probably take a closer look at some point.
void SaveState::ipuFreeze() { void SaveState::ipuFreeze()
{
IPUProcessInterrupt(); IPUProcessInterrupt();
if( GetVersion() < 0x04 ) FreezeTag( "IPU" );
{
// old versions saved the IPU regs, but they're already saved as part of HW! // old versions saved the IPU regs, but they're already saved as part of HW!
FreezeMem(ipuRegs, sizeof(IPUregisters)); //FreezeMem(ipuRegs, sizeof(IPUregisters));
}
Freeze(g_nDMATransfer); Freeze(g_nDMATransfer);
Freeze(FIreadpos); Freeze(FIreadpos);

View File

@ -751,6 +751,8 @@ void psxRcntSetGates()
void SaveState::psxRcntFreeze() void SaveState::psxRcntFreeze()
{ {
FreezeTag( "iopCounters" );
Freeze(psxCounters); Freeze(psxCounters);
Freeze(psxNextCounter); Freeze(psxNextCounter);
Freeze(psxNextsCounter); Freeze(psxNextsCounter);

View File

@ -25,8 +25,7 @@
struct psxCounter { struct psxCounter {
u64 count, target; u64 count, target;
u32 mode; u32 mode;
u32 rate, interrupt, otarget; u32 rate, interrupt;
u32 sCycle, Cycle;
u32 sCycleT; u32 sCycleT;
s32 CycleT; s32 CycleT;
}; };

View File

@ -203,6 +203,7 @@ u8 sio2_fifoOut(){
void SaveState::sio2Freeze() void SaveState::sio2Freeze()
{ {
FreezeTag( "sio2" );
Freeze(sio2); Freeze(sio2);
} }

View File

@ -116,6 +116,8 @@ static void _mtgsFreezeGIF( SaveState& state, GIFPath (&paths)[3] )
void SaveState::mtgsFreeze() void SaveState::mtgsFreeze()
{ {
FreezeTag( "mtgs" );
if( mtgsThread != NULL ) if( mtgsThread != NULL )
{ {
mtgsThread->Freeze( *this ); mtgsThread->Freeze( *this );

View File

@ -176,9 +176,9 @@ u32 GetBiosVersion() {
} }
//2002-09-22 (Florin) //2002-09-22 (Florin)
int IsBIOS(char *filename, char *description) int IsBIOS(const char *filename, char *description)
{ {
char ROMVER[14+1], zone[12+1]; char ROMVER[14+1];
FILE *fp; FILE *fp;
unsigned int fileOffset=0, found=FALSE; unsigned int fileOffset=0, found=FALSE;
struct romdir rd; struct romdir rd;
@ -200,31 +200,38 @@ int IsBIOS(char *filename, char *description)
return FALSE; //Unable to locate ROMDIR structure in file or a ioprpXXX.img return FALSE; //Unable to locate ROMDIR structure in file or a ioprpXXX.img
} }
while(strlen(rd.fileName) > 0){ while(strlen(rd.fileName) > 0)
if (strcmp(rd.fileName, "ROMVER") == 0){ // found romver {
unsigned int filepos=ftell(fp); if (strcmp(rd.fileName, "ROMVER") == 0) // found romver
{
uint filepos = ftell(fp);
fseek(fp, fileOffset, SEEK_SET); fseek(fp, fileOffset, SEEK_SET);
if (fread(&ROMVER, 14, 1, fp) == 0) break; if (fread(&ROMVER, 14, 1, fp) == 0) break;
fseek(fp, filepos, SEEK_SET);//go back fseek(fp, filepos, SEEK_SET);//go back
switch(ROMVER[4]){ const char zonefail[2] = { ROMVER[4], '\0' }; // the default "zone" (unknown code)
case 'T':sprintf(zone, "T10K "); break; const char* zone = zonefail;
case 'X':sprintf(zone, "Test ");break;
case 'J':sprintf(zone, "Japan "); break; switch(ROMVER[4])
case 'A':sprintf(zone, "USA "); break; {
case 'E':sprintf(zone, "Europe"); break; case 'T': zone = "T10K "; break;
case 'H':sprintf(zone, "HK "); break; case 'X': zone = "Test "; break;
case 'P':sprintf(zone, "Free "); break; case 'J': zone = "Japan "; break;
case 'C':sprintf(zone, "China "); break; case 'A': zone = "USA "; break;
default: sprintf(zone, "%c ",ROMVER[4]); break;//shoudn't show case 'E': zone = "Europe"; break;
case 'H': zone = "HK "; break;
case 'P': zone = "Free "; break;
case 'C': zone = "China "; break;
} }
sprintf(description, "%s vXX.XX(XX/XX/XXXX) %s", zone,
ROMVER[5]=='C'?"Console":ROMVER[5]=='D'?"Devel":""); sprintf(description, "%s v%c%c.%c%c(%c%c/%c%c/%c%c%c%c) %s", zone,
strncpy(description+ 8, ROMVER+ 0, 2);//ver major ROMVER[0], ROMVER[1], // ver major
strncpy(description+11, ROMVER+ 2, 2);//ver minor ROMVER[2], ROMVER[3], // ver minor
strncpy(description+14, ROMVER+12, 2);//day ROMVER[12], ROMVER[13], // day
strncpy(description+17, ROMVER+10, 2);//month ROMVER[10], ROMVER[11], // month
strncpy(description+20, ROMVER+ 6, 4);//year ROMVER[6], ROMVER[7], ROMVER[8], ROMVER[9], // year!
(ROMVER[5]=='C') ? "Console" : (ROMVER[5]=='D') ? "Devel" : ""
);
found = TRUE; found = TRUE;
} }

View File

@ -55,8 +55,9 @@ extern u64 GetCPUTicks();
extern u64 GetTickFrequency(); extern u64 GetTickFrequency();
// Used in Misc,and Windows/Linux files. // Used in Misc,and Windows/Linux files.
void ProcessFKeys(int fkey, int shift); // processes fkey related commands value 1-12 extern void ProcessFKeys(int fkey, int shift); // processes fkey related commands value 1-12
int IsBIOS(char *filename, char *description); extern int IsBIOS(const char *filename, char *description);
char *ParseLang(char *id); char *ParseLang(char *id);
extern const char *LabelAuthors; extern const char *LabelAuthors;
extern const char *LabelGreets; extern const char *LabelGreets;

View File

@ -112,10 +112,10 @@ struct psxRegisters {
u32 code; /* The instruction */ u32 code; /* The instruction */
u32 cycle; u32 cycle;
u32 interrupt; u32 interrupt;
u32 sCycle[64]; // start cycle for signaled ints u32 sCycle[32]; // start cycle for signaled ints
s32 eCycle[64]; // cycle delta for signaled ints (sCycle + eCycle == branch cycle) s32 eCycle[32]; // cycle delta for signaled ints (sCycle + eCycle == branch cycle)
u32 _msflag[32]; //u32 _msflag[32];
u32 _smflag[32]; //u32 _smflag[32];
}; };
PCSX2_ALIGNED16_EXTERN(psxRegisters psxRegs); PCSX2_ALIGNED16_EXTERN(psxRegisters psxRegs);

View File

@ -522,7 +522,7 @@ __forceinline bool _cpuBranchTest_Shared()
cpuSetNextBranchDelta( ((g_psxNextBranchCycle-psxRegs.cycle)*8) - EEsCycle ); cpuSetNextBranchDelta( ((g_psxNextBranchCycle-psxRegs.cycle)*8) - EEsCycle );
// Apply the hsync counter's nextCycle // Apply the hsync counter's nextCycle
cpuSetNextBranch( counters[4].sCycle, counters[4].CycleT ); cpuSetNextBranch( hsyncCounter.sCycle, hsyncCounter.CycleT );
// Apply vsync and other counter nextCycles // Apply vsync and other counter nextCycles
cpuSetNextBranch( nextsCounter, nextCounter ); cpuSetNextBranch( nextsCounter, nextCounter );

View File

@ -454,11 +454,9 @@ void SPRTOinterrupt()
void SaveState::sprFreeze() void SaveState::sprFreeze()
{ {
// Gotta save the weird ref-style DMA timing vars! FreezeTag( "SPRdma" );
if( GetVersion() >= 0x05 )
{ Freeze(spr0finished);
Freeze(spr0finished); Freeze(spr1finished);
Freeze(spr1finished); Freeze(mfifotransferred);
Freeze(mfifotransferred);
}
} }

View File

@ -53,7 +53,9 @@ string SaveState::GetFilename( int slot )
return Path::Combine( SSTATES_DIR, fmt_string( "%8.8X.%3.3d", ElfCRC, slot ) ); return Path::Combine( SSTATES_DIR, fmt_string( "%8.8X.%3.3d", ElfCRC, slot ) );
} }
SaveState::SaveState( const char* msg, const string& destination ) : m_version( g_SaveVersion ) SaveState::SaveState( const char* msg, const string& destination ) :
m_version( g_SaveVersion )
, m_tagspace( 128 )
{ {
Console::WriteLn( "%s %hs", params msg, &destination ); Console::WriteLn( "%s %hs", params msg, &destination );
} }
@ -77,44 +79,76 @@ s32 CALLBACK gsSafeFreeze( int mode, freezeData *data )
} }
} }
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 );
throw Exception::BadSavedState( string( "Tag: " )+src );
}
}
void SaveState::FreezeAll() void SaveState::FreezeAll()
{ {
if( IsLoading() ) if( IsLoading() )
PreLoadPrep(); 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).
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory char descout[128], descin[128];
FreezeMem(PS2MEM_ROM, Ps2MemSize::Rom); // 4 mb rom memory memzero_obj( descout );
FreezeMem(PS2MEM_ROM1, Ps2MemSize::Rom1); // 256kb rom1 memory IsBIOS( Config.Bios, descout );
memcpy_fast( descin, descout, 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",
params descout, descin
);
}
// First Block - Memory Dumps
// ---------------------------
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad
FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory 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
// Second Block - Various CPU Registers and States
// -----------------------------------------------
FreezeTag( "cpuRegs" );
Freeze(cpuRegs); // cpu regs + COP0 Freeze(cpuRegs); // cpu regs + COP0
Freeze(psxRegs); // iop regs Freeze(psxRegs); // iop regs
if (GetVersion() >= 0x6) Freeze(fpuRegs);
Freeze(fpuRegs);
else
{
// Old versiosn didn't save the ACCflags...
FreezeLegacy(fpuRegs, sizeof(u32)); // fpu regs
fpuRegs.ACCflag = 0;
}
Freeze(tlb); // tlbs Freeze(tlb); // tlbs
// Third Block - Cycle Timers and Events
// -------------------------------------
FreezeTag( "Cycles" );
Freeze(EEsCycle); Freeze(EEsCycle);
Freeze(EEoCycle); Freeze(EEoCycle);
Freeze(psxRegs.cycle); // used to be IOPoCycle. This retains compatibility.
Freeze(g_nextBranchCycle); Freeze(g_nextBranchCycle);
Freeze(g_psxNextBranchCycle); Freeze(g_psxNextBranchCycle);
Freeze(s_iLastCOP0Cycle); Freeze(s_iLastCOP0Cycle);
Freeze(s_iLastPERFCycle); Freeze(s_iLastPERFCycle);
u32 dummy = 1; // Fourth Block - EE-related systems
Freeze( dummy ); // was g_psxWriteOk // ---------------------------------
//hope didn't forgot any cpu....
rcntFreeze(); rcntFreeze();
gsFreeze(); gsFreeze();
vuMicroFreeze(); vuMicroFreeze();
@ -125,17 +159,16 @@ void SaveState::FreezeAll()
gifFreeze(); gifFreeze();
sprFreeze(); sprFreeze();
// iop now // Fifth Block - iop-related systems
FreezeMem(psxM, Ps2MemSize::IopRam); // 2 MB main memory // ---------------------------------
FreezeMem(psxH, Ps2MemSize::IopHardware); // hardware memory psxRcntFreeze();
//FreezeMem(psxS, 0x00010000); // sif memory
sioFreeze(); sioFreeze();
sio2Freeze();
cdrFreeze(); cdrFreeze();
cdvdFreeze(); cdvdFreeze();
psxRcntFreeze();
sio2Freeze();
// Sixth Block - Plugins Galore!
// -----------------------------
FreezePlugin( "GS", gsSafeFreeze ); FreezePlugin( "GS", gsSafeFreeze );
FreezePlugin( "SPU2", SPU2freeze ); FreezePlugin( "SPU2", SPU2freeze );
FreezePlugin( "DEV9", DEV9freeze ); FreezePlugin( "DEV9", DEV9freeze );
@ -147,18 +180,6 @@ void SaveState::FreezeAll()
PostLoadPrep(); PostLoadPrep();
} }
// this function is yet incomplete. Version numbers hare still < 0x12 so it won't be run.
// (which is good because it won't work :P)
void SaveState::_testCdvdCrc()
{
/*if( GetVersion() < 0x0012 ) return;
u32 thiscrc = ElfCRC;
Freeze( thiscrc );
if( thiscrc != ElfCRC )
throw Exception::StateCrcMismatch( thiscrc, ElfCRC );*/
}
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// gzipped to/from disk state saves implementation // gzipped to/from disk state saves implementation
@ -215,8 +236,6 @@ gzLoadingState::gzLoadingState( const string& filename ) :
"\tThe savestate was created with a newer version of Pcsx2. I don't know how to load it!" ); "\tThe savestate was created with a newer version of Pcsx2. I don't know how to load it!" );
throw Exception::UnsupportedStateVersion( m_version ); throw Exception::UnsupportedStateVersion( m_version );
} }
_testCdvdCrc();
} }
gzLoadingState::~gzLoadingState() { } gzLoadingState::~gzLoadingState() { }
@ -229,34 +248,30 @@ void gzSavingState::FreezeMem( void* data, int size )
void gzLoadingState::FreezeMem( void* data, int size ) void gzLoadingState::FreezeMem( void* data, int size )
{ {
gzread( m_file, data, size ); if( gzread( m_file, data, size ) != size )
if( gzeof( m_file ) )
throw Exception::BadSavedState( m_filename ); throw Exception::BadSavedState( m_filename );
} }
void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) ) void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
{ {
Console::WriteLn( "\tSaving %s", params name );
freezeData fP = { 0, NULL }; freezeData fP = { 0, NULL };
Console::WriteLn( "\tSaving %s", params name );
FreezeTag( name );
if (freezer(FREEZE_SIZE, &fP) == -1) if (freezer(FREEZE_SIZE, &fP) == -1)
throw Exception::FreezePluginFailure( name, "saving" ); throw Exception::FreezePluginFailure( name, "saving" );
gzwrite(m_file, &fP.size, sizeof(fP.size)); Freeze( fP.size );
if( fP.size == 0 ) return; if( fP.size == 0 ) return;
fP.data = (s8*)malloc(fP.size); SafeArray<s8> buffer( fP.size );
if (fP.data == NULL) fP.data = buffer.GetPtr();
throw Exception::OutOfMemory();
if(freezer(FREEZE_SAVE, &fP) == -1) if(freezer(FREEZE_SAVE, &fP) == -1)
throw Exception::FreezePluginFailure( name, "saving" ); throw Exception::FreezePluginFailure( name, "saving" );
if (fP.size) FreezeMem( fP.data, fP.size );
{
gzwrite(m_file, fP.data, fP.size);
free(fP.data);
}
} }
void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) ) void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )
@ -264,21 +279,17 @@ void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int
freezeData fP = { 0, NULL }; freezeData fP = { 0, NULL };
Console::WriteLn( "\tLoading %s", params name ); Console::WriteLn( "\tLoading %s", params name );
gzread(m_file, &fP.size, sizeof(fP.size)); FreezeTag( name );
Freeze( fP.size );
if( fP.size == 0 ) return; if( fP.size == 0 ) return;
fP.data = (s8*)malloc(fP.size); SafeArray<s8> buffer( fP.size );
if (fP.data == NULL) fP.data = buffer.GetPtr();
throw Exception::OutOfMemory();
int read = gzread(m_file, fP.data, fP.size);
if( read != fP.size ) FreezeMem( fP.data, fP.size );
throw Exception::BadSavedState( m_filename );
if(freezer(FREEZE_LOAD, &fP) == -1) if(freezer(FREEZE_LOAD, &fP) == -1)
throw Exception::FreezePluginFailure( name, "loading" ); throw Exception::FreezePluginFailure( name, "loading" );
if (fP.size) free(fP.data);
} }
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////

View File

@ -24,10 +24,14 @@
#include "PS2Edefs.h" #include "PS2Edefs.h"
#endif #endif
#include "System.h" #include "System.h"
// Savestate Versioning! // Savestate Versioning!
// If you make changes to the savestate version, please increment the value below. // If you make changes to the savestate version, please increment the value below.
// If the change is minor and compatibility with old states is retained, increment
// 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 = 0x8b400006; static const u32 g_SaveVersion = 0x8b410000;
// this function is meant to be used in the place of GSfreeze, and provides a safe layer // 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. :) // between the GS saving function and the MTGS's needs. :)
@ -41,6 +45,7 @@ class SaveState
{ {
protected: protected:
u32 m_version; // version of the savestate being loaded. u32 m_version; // version of the savestate being loaded.
SafeArray<char> m_tagspace;
public: public:
SaveState( const char* msg, const string& destination ); SaveState( const char* msg, const string& destination );
@ -76,6 +81,12 @@ public:
FreezeMem( &data, sizeof( T ) - sizeOfNewStuff ); FreezeMem( &data, sizeof( T ) - sizeOfNewStuff );
} }
// 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. // 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; virtual void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )=0;
@ -95,11 +106,6 @@ public:
protected: protected:
// Used internally by constructors to check the cdvd's crc against the CRC of the savestate.
// This allows for proper exception handling of changed CDs on-the-fly.
void _testCdvdCrc();
// Load/Save functions for the various components of our glorious emulator! // Load/Save functions for the various components of our glorious emulator!
void rcntFreeze(); void rcntFreeze();

View File

@ -586,22 +586,13 @@ __forceinline void dmaSIF2() {
} }
void SaveState::sifFreeze() { void SaveState::sifFreeze()
{
FreezeTag( "SIFdma" );
Freeze(sif0); Freeze(sif0);
Freeze(sif1); Freeze(sif1);
if( GetVersion() >= 0x0002 ) Freeze(eesifbusy);
{ Freeze(iopsifbusy);
Freeze(eesifbusy);
Freeze(iopsifbusy);
}
else if( IsLoading() )
{
// Old savestate, inferior data so...
// Take an educated guess on what they should be. Or well, set to 1 because
// it more or less forces them to "kick"
iopsifbusy[0] = eesifbusy[0] = 1;
iopsifbusy[1] = eesifbusy[1] = 1;
}
} }

View File

@ -566,22 +566,15 @@ void SaveState::sioFreeze()
// CRCs for memory cards. // CRCs for memory cards.
u64 m_mcdCRCs[2]; u64 m_mcdCRCs[2];
FreezeTag( "sio" );
Freeze( sio ); Freeze( sio );
// versions prior to 3 didn't have CRCs. if( IsSaving() )
if( GetVersion() >= 0x03 )
{ {
if( IsSaving() ) for( int i=0; i<2; ++i )
{ m_mcdCRCs[i] = MemoryCard::GetCRC( i );
for( int i=0; i<2; ++i )
m_mcdCRCs[i] = MemoryCard::GetCRC( i );
}
Freeze( m_mcdCRCs );
}
else
{
m_mcdCRCs[0] = m_mcdCRCs[1] = 0;
} }
Freeze( m_mcdCRCs );
if( IsLoading() && Config.McdEnableEject ) if( IsLoading() && Config.McdEnableEject )
{ {

View File

@ -160,6 +160,8 @@ void vuMicroMemReset()
void SaveState::vuMicroFreeze() void SaveState::vuMicroFreeze()
{ {
FreezeTag( "vuMicro" );
jASSUME( VU0.Mem != NULL ); jASSUME( VU0.Mem != NULL );
jASSUME( VU1.Mem != NULL ); jASSUME( VU1.Mem != NULL );
@ -169,15 +171,7 @@ void SaveState::vuMicroFreeze()
FreezeMem(VU0.Micro, 4*1024); FreezeMem(VU0.Micro, 4*1024);
Freeze(VU0.VF); Freeze(VU0.VF);
if( GetVersion() >= 0x02 ) Freeze(VU0.VI);
Freeze(VU0.VI);
else
{
// Old versions stored the VIregs as 32 bit values...
memzero_obj( VU0.VI );
for(int i=0; i<32; i++ )
Freeze( VU0.VI[i].UL );
}
Freeze(VU1.ACC); Freeze(VU1.ACC);
Freeze(VU1.code); Freeze(VU1.code);
@ -185,14 +179,5 @@ void SaveState::vuMicroFreeze()
FreezeMem(VU1.Micro, 16*1024); FreezeMem(VU1.Micro, 16*1024);
Freeze(VU1.VF); Freeze(VU1.VF);
if( GetVersion() >= 0x02 ) Freeze(VU1.VI);
Freeze(VU1.VI);
else
{
// Old versions stored the VIregs as 32 bit values...
memzero_obj( VU1.VI );
for(int i=0; i<32; i++ )
Freeze( VU1.VI[i].UL );
}
} }

View File

@ -1344,23 +1344,17 @@ void vif0Reset() {
void SaveState::vif0Freeze() void SaveState::vif0Freeze()
{ {
FreezeTag( "VIFdma" );
// Dunno if this one is needed, but whatever, it's small. :) // Dunno if this one is needed, but whatever, it's small. :)
if( GetVersion() >= 0x04 ) Freeze( g_vifCycles );
Freeze( g_vifCycles );
Freeze( vif0 ); Freeze( vif0 );
if( GetVersion() >= 0x04 )
{ Freeze( g_vif0HasMask3 );
Freeze( g_vif0HasMask3 ); Freeze( g_vif0Masks );
Freeze( g_vif0Masks ); Freeze( g_vifRow0 );
Freeze( g_vifRow0 ); Freeze( g_vifCol0 );
Freeze( g_vifCol0 );
}
else if( IsLoading() )
{
// Hack to "help" old savestates recover...
SetNewMask(g_vif0Masks, g_vif0HasMask3, vif0Regs->mask, ~vif0Regs->mask);
}
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -2357,16 +2351,8 @@ void SaveState::vif1Freeze()
{ {
Freeze(vif1); Freeze(vif1);
if( GetVersion() >= 0x04 ) Freeze( g_vif1HasMask3 );
{ Freeze( g_vif1Masks );
Freeze( g_vif1HasMask3 ); Freeze( g_vifRow1 );
Freeze( g_vif1Masks ); Freeze( g_vifCol1 );
Freeze( g_vifRow1 );
Freeze( g_vifCol1 );
}
else if( IsLoading() )
{
SetNewMask(g_vif1Masks, g_vif1HasMask3, vif1Regs->mask, ~vif1Regs->mask);
//if(vif1ch->chcr & 0x100) vif1.done = 0;
}
} }