2006-07-29 05:46:15 +00:00
/* FCE Ultra - NES/Famicom Emulator
2008-06-17 06:55:07 +00:00
*
* Copyright notice for this file :
* Copyright ( C ) 2002 Xodnizel
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
// TODO: Add (better) file io error checking
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
# include <string>
2006-07-29 05:46:15 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
//#include <unistd.h> //mbg merge 7/17/06 removed
2008-05-24 21:25:20 +00:00
# include <vector>
2008-06-17 06:55:07 +00:00
# include <fstream>
2008-05-24 21:25:20 +00:00
2006-07-29 05:46:15 +00:00
# include "types.h"
# include "x6502.h"
# include "fceu.h"
# include "sound.h"
2006-08-01 05:50:19 +00:00
# include "utils/endian.h"
# include "utils/memory.h"
2008-06-03 04:04:04 +00:00
# include "utils/memorystream.h"
2008-06-06 06:34:39 +00:00
# include "utils/xstring.h"
2006-08-01 05:50:19 +00:00
# include "file.h"
2006-07-29 05:46:15 +00:00
# include "fds.h"
# include "state.h"
# include "movie.h"
# include "ppu.h"
# include "netplay.h"
# include "video.h"
# include "input.h"
2008-05-24 21:25:20 +00:00
# include "zlib.h"
2008-06-17 06:55:07 +00:00
# include "driver.h"
2006-07-29 05:46:15 +00:00
static void ( * SPreSave ) ( void ) ;
static void ( * SPostSave ) ( void ) ;
static int SaveStateStatus [ 10 ] ;
static int StateShow ;
2008-08-10 04:03:50 +00:00
//tells the save system innards that we're loading the old format
bool FCEU_state_loading_old_format ;
2006-07-29 05:46:15 +00:00
# define SFMDATA_SIZE (64)
static SFORMAT SFMDATA [ SFMDATA_SIZE ] ;
static int SFEXINDEX ;
# define RLSB FCEUSTATE_RLSB //0x80000000
extern SFORMAT FCEUPPU_STATEINFO [ ] ;
extern SFORMAT FCEUSND_STATEINFO [ ] ;
extern SFORMAT FCEUCTRL_STATEINFO [ ] ;
extern SFORMAT FCEUMOV_STATEINFO [ ] ;
SFORMAT SFCPU [ ] = {
2008-06-17 06:55:07 +00:00
{ & X . PC , 2 | RLSB , " PC \0 " } ,
{ & X . A , 1 , " A \0 \0 " } ,
{ & X . P , 1 , " P \0 \0 " } ,
{ & X . X , 1 , " X \0 \0 " } ,
{ & X . Y , 1 , " Y \0 \0 " } ,
{ & X . S , 1 , " S \0 \0 " } ,
{ & RAM , 0x800 | FCEUSTATE_INDIRECT , " RAM " , } ,
{ 0 }
2006-07-29 05:46:15 +00:00
} ;
SFORMAT SFCPUC [ ] = {
2008-06-17 06:55:07 +00:00
{ & X . jammed , 1 , " JAMM " } ,
{ & X . IRQlow , 4 | RLSB , " IQLB " } ,
{ & X . tcount , 4 | RLSB , " ICoa " } ,
{ & X . count , 4 | RLSB , " ICou " } ,
{ & timestampbase , sizeof ( timestampbase ) | RLSB , " TSBS " } ,
{ & X . mooPI , 1 , " MooP " } , // alternative to the "quick and dirty hack"
{ 0 }
2006-07-29 05:46:15 +00:00
} ;
2008-06-02 14:02:02 +00:00
static int SubWrite ( std : : ostream * os , SFORMAT * sf )
{
2008-06-17 06:55:07 +00:00
uint32 acc = 0 ;
while ( sf - > v )
{
if ( sf - > s = = ~ 0 ) //Link to another struct
{
uint32 tmp ;
if ( ! ( tmp = SubWrite ( os , ( SFORMAT * ) sf - > v ) ) )
return ( 0 ) ;
acc + = tmp ;
sf + + ;
continue ;
}
acc + = 8 ; //Description + size
acc + = sf - > s & ( ~ FCEUSTATE_FLAGS ) ;
if ( os ) //Are we writing or calculating the size of this block?
{
os - > write ( sf - > desc , 4 ) ;
write32le ( sf - > s & ( ~ FCEUSTATE_FLAGS ) , os ) ;
# ifndef LSB_FIRST
if ( sf - > s & RLSB )
FlipByteOrder ( sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
# endif
if ( sf - > s & FCEUSTATE_INDIRECT )
os - > write ( * ( char * * ) sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
else
os - > write ( ( char * ) sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
//Now restore the original byte order.
# ifndef LSB_FIRST
2008-06-17 23:46:55 +00:00
if ( sf - > s & RLSB )
2008-06-17 06:55:07 +00:00
FlipByteOrder ( sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
# endif
}
2008-06-17 23:46:55 +00:00
sf + + ;
2008-06-17 06:55:07 +00:00
}
return ( acc ) ;
2008-06-02 14:02:02 +00:00
}
static int WriteStateChunk ( std : : ostream * os , int type , SFORMAT * sf )
{
os - > put ( type ) ;
int bsize = SubWrite ( ( std : : ostream * ) 0 , sf ) ;
write32le ( bsize , os ) ;
if ( ! SubWrite ( os , sf ) )
{
return 5 ;
}
return ( bsize + 5 ) ;
}
2006-07-29 05:46:15 +00:00
static SFORMAT * CheckS ( SFORMAT * sf , uint32 tsize , char * desc )
{
2008-06-17 06:55:07 +00:00
while ( sf - > v )
{
if ( sf - > s = = ~ 0 ) // Link to another SFORMAT structure.
{
SFORMAT * tmp ;
if ( ( tmp = CheckS ( ( SFORMAT * ) sf - > v , tsize , desc ) ) )
return ( tmp ) ;
sf + + ;
continue ;
}
if ( ! memcmp ( desc , sf - > desc , 4 ) )
{
if ( tsize ! = ( sf - > s & ( ~ FCEUSTATE_FLAGS ) ) )
return ( 0 ) ;
return ( sf ) ;
}
sf + + ;
}
return ( 0 ) ;
2006-07-29 05:46:15 +00:00
}
2008-06-17 06:55:07 +00:00
static bool ReadStateChunk ( std : : istream * is , SFORMAT * sf , int size )
2006-07-29 05:46:15 +00:00
{
2008-06-17 06:55:07 +00:00
SFORMAT * tmp ;
int temp = is - > tellg ( ) ;
while ( is - > tellg ( ) < temp + size )
{
uint32 tsize ;
char toa [ 4 ] ;
2008-06-17 08:32:24 +00:00
if ( is - > read ( toa , 4 ) . gcount ( ) < 4 )
2008-06-17 06:55:07 +00:00
return false ;
read32le ( & tsize , is ) ;
if ( ( tmp = CheckS ( sf , tsize , toa ) ) )
{
if ( tmp - > s & FCEUSTATE_INDIRECT )
2008-06-17 08:32:24 +00:00
is - > read ( * ( char * * ) tmp - > v , tmp - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
else
2008-06-17 08:32:24 +00:00
is - > read ( ( char * ) tmp - > v , tmp - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
# ifndef LSB_FIRST
if ( tmp - > s & RLSB )
FlipByteOrder ( tmp - > v , tmp - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
# endif
}
else
is - > seekg ( tsize , std : : ios : : cur ) ;
} // while(...)
return true ;
2006-07-29 05:46:15 +00:00
}
static int read_sfcpuc = 0 , read_snd = 0 ;
void FCEUD_BlitScreen ( uint8 * XBuf ) ; //mbg merge 7/17/06 YUCKY had to add
void UpdateFCEUWindow ( void ) ; //mbg merge 7/17/06 YUCKY had to add
2008-06-17 06:55:07 +00:00
static bool ReadStateChunks ( std : : istream * is , int32 totalsize )
2006-07-29 05:46:15 +00:00
{
2008-06-17 06:55:07 +00:00
int t ;
uint32 size ;
bool ret = true ;
bool warned = false ;
read_sfcpuc = 0 ;
read_snd = 0 ;
//mbg 6/16/08 - wtf
//// int moo=X.mooPI;
// if(!scan_chunks)
// X.mooPI=/*X.P*/0xFF;
while ( totalsize > 0 )
{
t = is - > get ( ) ;
if ( t = = EOF ) break ;
if ( ! read32le ( & size , is ) ) break ;
totalsize - = size + 5 ;
switch ( t )
2006-07-29 05:46:15 +00:00
{
2008-06-17 06:55:07 +00:00
case 1 : if ( ! ReadStateChunk ( is , SFCPU , size ) ) ret = false ; break ;
case 3 : if ( ! ReadStateChunk ( is , FCEUPPU_STATEINFO , size ) ) ret = false ; break ;
case 4 : if ( ! ReadStateChunk ( is , FCEUCTRL_STATEINFO , size ) ) ret = false ; break ;
2008-08-10 04:03:50 +00:00
case 7 :
if ( ! FCEUMOV_ReadState ( is , size ) ) {
//allow this to fail in old-format savestates.
if ( ! FCEU_state_loading_old_format )
ret = false ;
}
break ;
2008-06-17 06:55:07 +00:00
case 0x10 : if ( ! ReadStateChunk ( is , SFMDATA , size ) ) ret = false ; break ;
// now it gets hackier:
case 5 :
if ( ! ReadStateChunk ( is , FCEUSND_STATEINFO , size ) )
ret = false ;
else
read_snd = 1 ;
break ;
case 6 :
if ( FCEUMOV_Mode ( MOVIEMODE_PLAY | MOVIEMODE_RECORD ) )
{
if ( ! ReadStateChunk ( is , FCEUMOV_STATEINFO , size ) ) ret = false ;
}
2006-07-29 05:46:15 +00:00
else
{
2008-06-17 06:55:07 +00:00
is - > seekg ( size , std : : ios : : cur ) ;
2006-07-29 05:46:15 +00:00
}
2008-06-17 06:55:07 +00:00
break ;
case 8 :
// load back buffer
{
extern uint8 * XBackBuf ;
2008-06-17 08:32:24 +00:00
if ( is - > read ( ( char * ) XBackBuf , size ) . gcount ( ) ! = size )
2008-06-17 06:55:07 +00:00
ret = false ;
//MBG TODO - can this be moved to a better place?
//does it even make sense, displaying XBuf when its XBackBuf we just loaded?
# ifdef WIN32
else
{
FCEUD_BlitScreen ( XBuf ) ;
UpdateFCEUWindow ( ) ;
}
2006-07-29 05:46:15 +00:00
# endif
2008-06-17 06:55:07 +00:00
}
break ;
case 2 :
{
if ( ! ReadStateChunk ( is , SFCPUC , size ) )
ret = false ;
else
read_sfcpuc = 1 ;
} break ;
default :
// for somebody's sanity's sake, at least warn about it:
if ( ! warned )
{
char str [ 256 ] ;
sprintf ( str , " Warning: Found unknown save chunk of type %d. \n This could indicate the save state is corrupted \n or made with a different (incompatible) emulator version. " , t ) ;
FCEUD_PrintError ( str ) ;
warned = true ;
}
//if(fseek(st,size,SEEK_CUR)<0) goto endo;break;
is - > seekg ( size , std : : ios : : cur ) ;
2006-07-29 05:46:15 +00:00
}
2008-06-17 06:55:07 +00:00
}
//endo:
//mbg 6/16/08 - wtf
// if(X.mooPI==0xFF && !scan_chunks)
// {
//// FCEU_PrintError("prevmoo=%d, p=%d",moo,X.P);
// X.mooPI=X.P; // "Quick and dirty hack." //begone
// }
extern int resetDMCacc ;
if ( read_snd )
resetDMCacc = 0 ;
else
resetDMCacc = 1 ;
return ret ;
2006-07-29 05:46:15 +00:00
}
int CurrentState = 1 ;
extern int geniestage ;
2008-06-03 05:01:07 +00:00
bool FCEUSS_SaveMS ( std : : ostream * outstream , int compressionLevel )
{
//a temp memory stream. we'll dump some data here and then compress
//TODO - support dumping directly without compressing to save a buffer copy
2008-06-02 14:02:02 +00:00
memorystream ms ;
std : : ostream * os = ( std : : ostream * ) & ms ;
2008-05-24 21:25:20 +00:00
uint32 totalsize = 0 ;
FCEUPPU_SaveState ( ) ;
FCEUSND_SaveState ( ) ;
2008-06-02 14:02:02 +00:00
totalsize = WriteStateChunk ( os , 1 , SFCPU ) ;
totalsize + = WriteStateChunk ( os , 2 , SFCPUC ) ;
totalsize + = WriteStateChunk ( os , 3 , FCEUPPU_STATEINFO ) ;
totalsize + = WriteStateChunk ( os , 4 , FCEUCTRL_STATEINFO ) ;
totalsize + = WriteStateChunk ( os , 5 , FCEUSND_STATEINFO ) ;
2008-06-05 06:49:11 +00:00
if ( FCEUMOV_Mode ( MOVIEMODE_PLAY | MOVIEMODE_RECORD ) )
2008-05-24 21:25:20 +00:00
{
2008-06-02 14:02:02 +00:00
totalsize + = WriteStateChunk ( os , 6 , FCEUMOV_STATEINFO ) ;
2008-06-03 05:50:58 +00:00
//MBG tasedit HACK HACK HACK!
//do not save the movie state if we are in tasedit! that is a huge waste of time and space!
2008-06-05 06:49:11 +00:00
if ( ! FCEUMOV_Mode ( MOVIEMODE_TASEDIT ) )
2008-06-03 05:50:58 +00:00
{
2008-06-17 07:55:27 +00:00
os - > seekp ( 5 , std : : ios : : cur ) ;
int size = FCEUMOV_WriteState ( os ) ;
os - > seekp ( - ( size + 5 ) , std : : ios : : cur ) ;
2008-06-03 05:50:58 +00:00
os - > put ( 7 ) ;
write32le ( size , os ) ;
2008-06-17 07:55:27 +00:00
os - > seekp ( size , std : : ios : : cur ) ;
2008-06-03 05:50:58 +00:00
totalsize + = 5 + size ;
}
2008-05-24 21:25:20 +00:00
}
// save back buffer
{
extern uint8 * XBackBuf ;
uint32 size = 256 * 256 + 8 ;
2008-06-02 14:02:02 +00:00
os - > put ( 8 ) ;
write32le ( size , os ) ;
os - > write ( ( char * ) XBackBuf , size ) ;
2008-05-24 21:25:20 +00:00
totalsize + = 5 + size ;
}
2006-07-29 05:46:15 +00:00
if ( SPreSave ) SPreSave ( ) ;
2008-06-02 14:02:02 +00:00
totalsize + = WriteStateChunk ( os , 0x10 , SFMDATA ) ;
2006-07-29 05:46:15 +00:00
if ( SPreSave ) SPostSave ( ) ;
2008-05-24 21:25:20 +00:00
//save the length of the file
2008-06-02 14:02:02 +00:00
int len = ms . size ( ) ;
2008-05-24 21:25:20 +00:00
2008-06-03 04:04:04 +00:00
//sanity check: len and totalsize should be the same
if ( len ! = totalsize )
{
FCEUD_PrintError ( " sanity violation: len != totalsize " ) ;
return false ;
}
2008-06-03 03:31:05 +00:00
int error = Z_OK ;
uint8 * cbuf = ( uint8 * ) ms . buf ( ) ;
uLongf comprlen = - 1 ;
2008-06-03 05:01:07 +00:00
if ( compressionLevel ! = Z_NO_COMPRESSION )
2008-06-03 03:31:05 +00:00
{
//worst case compression.
//zlib says "0.1% larger than sourceLen plus 12 bytes"
comprlen = ( len > > 9 ) + 12 + len ;
2008-06-17 23:46:55 +00:00
cbuf = new uint8 [ comprlen ] ;
2008-06-03 03:31:05 +00:00
error = compress2 ( cbuf , & comprlen , ( uint8 * ) ms . buf ( ) , len , compressionLevel ) ;
}
2008-05-24 21:25:20 +00:00
//dump the header
uint8 header [ 16 ] = " FCSX " ;
FCEU_en32lsb ( header + 4 , totalsize ) ;
FCEU_en32lsb ( header + 8 , FCEU_VERSION_NUMERIC ) ;
FCEU_en32lsb ( header + 12 , comprlen ) ;
//dump it to the destination file
2008-06-03 05:01:07 +00:00
outstream - > write ( ( char * ) header , 16 ) ;
2008-06-03 05:50:58 +00:00
outstream - > write ( ( char * ) cbuf , comprlen = = - 1 ? totalsize : comprlen ) ;
2008-05-24 21:25:20 +00:00
2008-06-03 03:31:05 +00:00
if ( cbuf ! = ( uint8 * ) ms . buf ( ) ) delete [ ] cbuf ;
2008-05-24 21:25:20 +00:00
return error = = Z_OK ;
2006-07-29 05:46:15 +00:00
}
2008-05-24 21:25:20 +00:00
2006-07-29 05:46:15 +00:00
void FCEUSS_Save ( char * fname )
{
2008-06-13 19:15:24 +00:00
std : : fstream * st = 0 ;
2006-07-29 05:46:15 +00:00
char * fn ;
if ( geniestage = = 1 )
{
2008-06-13 19:15:24 +00:00
FCEU_DispMessage ( " Cannot save FCS in GG screen. " ) ;
return ;
}
2006-07-29 05:46:15 +00:00
if ( fname )
2008-06-13 19:15:24 +00:00
st = FCEUD_UTF8_fstream ( fname , " wb " ) ;
2006-07-29 05:46:15 +00:00
else
{
2008-06-13 19:15:24 +00:00
//FCEU_PrintError("daCurrentState=%d",CurrentState);
2008-06-17 06:55:07 +00:00
fn = strdup ( FCEU_MakeFName ( FCEUMKF_STATE , CurrentState , 0 ) . c_str ( ) ) ;
st = FCEUD_UTF8_fstream ( fn , " wb " ) ;
2008-06-13 19:15:24 +00:00
free ( fn ) ;
2006-07-29 05:46:15 +00:00
}
if ( st = = NULL )
{
2008-06-13 19:15:24 +00:00
FCEU_DispMessage ( " State %d save error. " , CurrentState ) ;
return ;
2006-07-29 05:46:15 +00:00
}
2008-06-13 19:15:24 +00:00
if ( FCEUMOV_Mode ( MOVIEMODE_INACTIVE ) )
FCEUSS_SaveMS ( st , - 1 ) ;
else
FCEUSS_SaveMS ( st , 0 ) ;
delete st ;
2006-07-29 05:46:15 +00:00
if ( ! fname )
{
SaveStateStatus [ CurrentState ] = 1 ;
FCEU_DispMessage ( " State %d saved. " , CurrentState ) ;
}
}
2008-08-10 03:10:23 +00:00
int FCEUSS_LoadFP_old ( std : : istream * is , ENUM_SSLOADPARAMS params )
2006-07-29 05:46:15 +00:00
{
2008-08-10 03:10:23 +00:00
//if(params==SSLOADPARAM_DUMMY && suppress_scan_chunks)
// return 1;
int x ;
2008-06-03 03:31:05 +00:00
uint8 header [ 16 ] ;
2008-08-10 03:10:23 +00:00
int stateversion ;
char * fn = 0 ;
////Make temporary savestate in case something screws up during the load
//if(params == SSLOADPARAM_BACKUP)
//{
// fn=FCEU_MakeFName(FCEUMKF_NPTEMP,0,0);
// FILE *fp;
//
// if((fp=fopen(fn,"wb")))
// {
// if(FCEUSS_SaveFP(fp))
// {
// fclose(fp);
// }
// else
// {
// fclose(fp);
// unlink(fn);
// free(fn);
// fn=0;
// }
// }
//}
//if(params!=SSLOADPARAM_DUMMY)
{
FCEUMOV_PreLoad ( ) ;
}
is - > read ( ( char * ) & header , 16 ) ;
if ( memcmp ( header , " FCS " , 3 ) )
{
return ( 0 ) ;
}
if ( header [ 3 ] = = 0xFF )
{
stateversion = FCEU_de32lsb ( header + 8 ) ;
}
else
{
stateversion = header [ 3 ] * 100 ;
}
//if(params == SSLOADPARAM_DUMMY)
//{
// scan_chunks=1;
//}
x = ReadStateChunks ( is , * ( uint32 * ) ( header + 4 ) ) ;
//if(params == SSLOADPARAM_DUMMY)
//{
// scan_chunks=0;
// return 1;
//}
if ( read_sfcpuc & & stateversion < 9500 )
{
X . IRQlow = 0 ;
}
if ( GameStateRestore )
{
GameStateRestore ( stateversion ) ;
}
if ( x )
{
FCEUPPU_LoadState ( stateversion ) ;
FCEUSND_LoadState ( stateversion ) ;
x = FCEUMOV_PostLoad ( ) ;
}
if ( fn )
{
//if(!x || params == SSLOADPARAM_DUMMY) //is make_backup==2 possible?? oh well.
//{
// * Oops! Load the temporary savestate */
// FILE *fp;
//
// if((fp=fopen(fn,"rb")))
// {
// FCEUSS_LoadFP(fp,SSLOADPARAM_NOBACKUP);
// fclose(fp);
// }
// unlink(fn);
//}
free ( fn ) ;
}
2008-06-03 03:31:05 +00:00
2008-08-10 03:10:23 +00:00
return ( x ) ;
}
bool FCEUSS_LoadFP ( std : : istream * is , ENUM_SSLOADPARAMS params )
{
uint8 header [ 16 ] ;
2008-06-03 03:31:05 +00:00
//read and analyze the header
is - > read ( ( char * ) & header , 16 ) ;
2008-08-10 03:10:23 +00:00
if ( memcmp ( header , " FCSX " , 4 ) ) {
//its not an fceux save file.. perhaps it is an fceu savefile
is - > seekg ( 0 ) ;
2008-08-10 04:03:50 +00:00
FCEU_state_loading_old_format = true ;
bool ret = FCEUSS_LoadFP_old ( is , params ) ! = 0 ;
FCEU_state_loading_old_format = false ;
return ret ;
2008-08-10 03:10:23 +00:00
}
2008-06-03 03:31:05 +00:00
int totalsize = FCEU_de32lsb ( header + 4 ) ;
int stateversion = FCEU_de32lsb ( header + 8 ) ;
int comprlen = FCEU_de32lsb ( header + 12 ) ;
2008-06-17 06:55:07 +00:00
std : : vector < char > buf ( totalsize ) ;
2008-06-03 03:31:05 +00:00
//not compressed:
if ( comprlen ! = - 1 )
{
//load the compressed chunk and decompress
2008-06-17 06:55:07 +00:00
std : : vector < char > cbuf ( comprlen ) ;
2008-06-03 03:31:05 +00:00
is - > read ( ( char * ) & cbuf [ 0 ] , comprlen ) ;
uLongf uncomprlen = totalsize ;
2008-06-17 06:55:07 +00:00
int error = uncompress ( ( uint8 * ) & buf [ 0 ] , & uncomprlen , ( uint8 * ) & cbuf [ 0 ] , comprlen ) ;
2008-06-03 03:31:05 +00:00
if ( error ! = Z_OK | | uncomprlen ! = totalsize )
return false ;
}
else
{
is - > read ( ( char * ) & buf [ 0 ] , totalsize ) ;
}
2008-08-10 03:10:23 +00:00
FCEUMOV_PreLoad ( ) ;
2008-06-17 06:55:07 +00:00
memorystream mstemp ( & buf ) ;
bool x = ReadStateChunks ( & mstemp , totalsize ) ! = 0 ;
2008-06-03 03:31:05 +00:00
//mbg 5/24/08 - we don't support old states, so this shouldnt matter.
//if(read_sfcpuc && stateversion<9500)
// X.IRQlow=0;
if ( GameStateRestore )
{
GameStateRestore ( stateversion ) ;
}
if ( x )
{
FCEUPPU_LoadState ( stateversion ) ;
2008-06-17 23:46:55 +00:00
FCEUSND_LoadState ( stateversion ) ;
2008-06-17 06:55:07 +00:00
x = FCEUMOV_PostLoad ( ) ;
2008-06-03 03:31:05 +00:00
}
2008-05-24 21:25:20 +00:00
2008-06-03 03:31:05 +00:00
return x ;
}
2008-05-24 21:25:20 +00:00
2008-06-17 06:55:07 +00:00
bool FCEUSS_Load ( char * fname )
2006-07-29 05:46:15 +00:00
{
2008-06-17 06:55:07 +00:00
std : : fstream * st ;
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
//mbg movie - this needs to be overhauled
////this fixes read-only toggle problems
//if(FCEUMOV_IsRecording()) {
// FCEUMOV_AddCommand(0);
// MovieFlushHeader();
//}
2006-07-29 05:46:15 +00:00
if ( geniestage = = 1 )
{
FCEU_DispMessage ( " Cannot load FCS in GG screen. " ) ;
2008-06-17 06:55:07 +00:00
return false ;
2006-07-29 05:46:15 +00:00
}
if ( fname )
{
2008-06-17 06:55:07 +00:00
st = FCEUD_UTF8_fstream ( fname , " rb " ) ;
2006-07-29 05:46:15 +00:00
}
else
{
2008-06-17 06:55:07 +00:00
st = FCEUD_UTF8_fstream ( FCEU_MakeFName ( FCEUMKF_STATE , CurrentState , fname ) , " rb " ) ;
2006-07-29 05:46:15 +00:00
}
if ( st = = NULL )
{
FCEU_DispMessage ( " State %d load error. " , CurrentState ) ;
SaveStateStatus [ CurrentState ] = 0 ;
2008-06-17 06:55:07 +00:00
return false ;
2006-07-29 05:46:15 +00:00
}
//If in bot mode, don't do a backup when loading.
//Otherwise you eat at the hard disk, since so many
//states are being loaded.
2008-07-31 16:21:42 +00:00
if ( FCEUSS_LoadFP ( st , SSLOADPARAM_BACKUP ) )
2006-07-29 05:46:15 +00:00
{
2008-06-06 06:34:39 +00:00
if ( fname )
{
char szFilename [ 260 ] = { 0 } ;
splitpath ( fname , 0 , 0 , szFilename , 0 ) ;
FCEU_DispMessage ( " State %s loaded. " , szFilename ) ;
}
else
2006-07-29 05:46:15 +00:00
{
//This looks redudant to me... but why bother deleting it:)
SaveStateStatus [ CurrentState ] = 1 ;
2008-06-27 04:22:33 +00:00
FCEU_DispMessage ( " State %d loaded. " , CurrentState ) ;
2006-07-29 05:46:15 +00:00
SaveStateStatus [ CurrentState ] = 1 ;
}
2008-06-17 06:55:07 +00:00
delete st ;
return true ;
2008-06-17 23:46:55 +00:00
}
2006-07-29 05:46:15 +00:00
else
{
if ( ! fname )
{
SaveStateStatus [ CurrentState ] = 1 ;
}
FCEU_DispMessage ( " Error(s) reading state %d! " , CurrentState ) ;
2008-06-17 06:55:07 +00:00
delete st ;
return 0 ;
2006-07-29 05:46:15 +00:00
}
}
void FCEUSS_CheckStates ( void )
{
2008-06-17 06:55:07 +00:00
FILE * st = NULL ;
int ssel ;
for ( ssel = 0 ; ssel < 10 ; ssel + + )
{
st = FCEUD_UTF8fopen ( FCEU_MakeFName ( FCEUMKF_STATE , ssel , 0 ) , " rb " ) ;
if ( st )
{
SaveStateStatus [ ssel ] = 1 ;
fclose ( st ) ;
}
else
SaveStateStatus [ ssel ] = 0 ;
}
2006-07-29 05:46:15 +00:00
CurrentState = 1 ;
StateShow = 0 ;
}
void ResetExState ( void ( * PreSave ) ( void ) , void ( * PostSave ) ( void ) )
{
2008-06-17 06:55:07 +00:00
int x ;
for ( x = 0 ; x < SFEXINDEX ; x + + )
{
if ( SFMDATA [ x ] . desc )
free ( SFMDATA [ x ] . desc ) ;
}
SPreSave = PreSave ;
SPostSave = PostSave ;
SFEXINDEX = 0 ;
2006-07-29 05:46:15 +00:00
}
void AddExState ( void * v , uint32 s , int type , char * desc )
{
2008-06-17 06:55:07 +00:00
if ( desc )
{
SFMDATA [ SFEXINDEX ] . desc = ( char * ) FCEU_malloc ( 5 ) ;
strcpy ( SFMDATA [ SFEXINDEX ] . desc , desc ) ;
}
else
SFMDATA [ SFEXINDEX ] . desc = 0 ;
SFMDATA [ SFEXINDEX ] . v = v ;
SFMDATA [ SFEXINDEX ] . s = s ;
if ( type ) SFMDATA [ SFEXINDEX ] . s | = RLSB ;
if ( SFEXINDEX < SFMDATA_SIZE - 1 )
SFEXINDEX + + ;
else
{
static int once = 1 ;
if ( once )
{
once = 0 ;
FCEU_PrintError ( " Error in AddExState: SFEXINDEX overflow. \n Somebody made SFMDATA_SIZE too small. " ) ;
}
}
SFMDATA [ SFEXINDEX ] . v = 0 ; // End marker.
2006-07-29 05:46:15 +00:00
}
void FCEUI_SelectStateNext ( int n )
{
if ( n > 0 )
CurrentState = ( CurrentState + 1 ) % 10 ;
else
CurrentState = ( CurrentState + 9 ) % 10 ;
FCEUI_SelectState ( CurrentState , 1 ) ;
}
int FCEUI_SelectState ( int w , int show )
{
2008-08-07 04:10:41 +00:00
FCEUSS_CheckStates ( ) ;
2008-06-17 06:55:07 +00:00
int oldstate = CurrentState ;
if ( w = = - 1 ) { StateShow = 0 ; return 0 ; } //mbg merge 7/17/06 had to make return a value
CurrentState = w ;
if ( show )
{
StateShow = 180 ;
FCEU_DispMessage ( " -select state- " ) ;
}
return oldstate ;
2008-06-17 23:46:55 +00:00
}
2006-07-29 05:46:15 +00:00
void FCEUI_SaveState ( char * fname )
{
2008-06-05 06:49:11 +00:00
if ( ! FCEU_IsValidUI ( FCEUI_SAVESTATE ) ) return ;
2008-06-17 06:55:07 +00:00
2008-06-05 06:49:11 +00:00
StateShow = 0 ;
FCEUSS_Save ( fname ) ;
2006-07-29 05:46:15 +00:00
}
int loadStateFailed = 0 ; // hack, this function should return a value instead
void FCEUI_LoadState ( char * fname )
{
2008-06-05 06:49:11 +00:00
if ( ! FCEU_IsValidUI ( FCEUI_LOADSTATE ) ) return ;
2007-02-06 18:59:15 +00:00
StateShow = 0 ;
loadStateFailed = 0 ;
2006-07-29 05:46:15 +00:00
2007-02-06 18:59:15 +00:00
/* For network play, be load the state locally, and then save the state to a temporary file,
and send that . This insures that if an older state is loaded that is missing some
information expected in newer save states , desynchronization won ' t occur ( at least not
from this ; ) ) .
*/
2006-07-29 05:46:15 +00:00
2007-02-06 18:59:15 +00:00
if ( FCEUSS_Load ( fname ) )
{
2008-06-20 22:15:30 +00:00
//mbg todo netplay
/*if(FCEUnetplay)
2007-02-06 18:59:15 +00:00
{
2008-06-17 06:55:07 +00:00
char * fn = strdup ( FCEU_MakeFName ( FCEUMKF_NPTEMP , 0 , 0 ) . c_str ( ) ) ;
2007-02-06 18:59:15 +00:00
FILE * fp ;
2006-07-29 05:46:15 +00:00
2007-02-06 18:59:15 +00:00
if ( ( fp = fopen ( fn , " wb " ) ) )
{
2008-06-13 19:15:24 +00:00
if ( FCEUSS_SaveFP ( fp , 0 ) )
2007-02-06 18:59:15 +00:00
{
fclose ( fp ) ;
2008-06-17 23:46:55 +00:00
FCEUNET_SendFile ( FCEUNPCMD_LOADSTATE , fn ) ;
2007-02-06 18:59:15 +00:00
}
else
{
fclose ( fp ) ;
}
unlink ( fn ) ;
}
free ( fn ) ;
2008-06-20 22:15:30 +00:00
} */
2007-02-06 18:59:15 +00:00
}
else
{
loadStateFailed = 1 ;
}
2006-07-29 05:46:15 +00:00
}
void FCEU_DrawSaveStates ( uint8 * XBuf )
{
2008-06-17 06:55:07 +00:00
if ( ! StateShow ) return ;
2006-07-29 05:46:15 +00:00
2008-06-17 06:55:07 +00:00
FCEU_DrawNumberRow ( XBuf , SaveStateStatus , CurrentState ) ;
StateShow - - ;
2006-07-29 05:46:15 +00:00
}