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
2012-08-04 22:33:16 +00:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2008-06-17 06:55:07 +00:00
*/
// TODO: Add (better) file io error checking
2006-07-29 05:46:15 +00:00
2009-10-08 13:48:15 +00:00
# include "version.h"
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-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"
2009-10-25 03:02:00 +00:00
# ifdef _S9XLUA_H
# include "fceulua.h"
# endif
2006-07-29 05:46:15 +00:00
2009-10-08 08:06:45 +00:00
//TODO - we really need some kind of global platform-specific options api
2021-02-06 17:15:28 +00:00
# ifdef __WIN_DRIVER__
2009-10-08 08:06:45 +00:00
# include "drivers/win/main.h"
2019-05-08 05:55:53 +00:00
# include "drivers/win/cheat.h"
2009-10-08 08:06:45 +00:00
# include "drivers/win/ram_search.h"
# include "drivers/win/ramwatch.h"
# endif
2013-04-13 02:52:13 +00:00
# include <string>
# include <cstdio>
# include <cstdlib>
# include <cstring>
//#include <unistd.h> //mbg merge 7/17/06 removed
# include <vector>
# include <fstream>
2008-12-19 22:11:01 +00:00
using namespace std ;
2020-12-30 20:20:34 +00:00
static void ( * SPreSave ) ( void ) = NULL ;
static void ( * SPostSave ) ( void ) = NULL ;
2006-07-29 05:46:15 +00:00
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
2020-12-30 20:20:34 +00:00
bool FCEU_state_loading_old_format = false ;
2008-08-10 04:03:50 +00:00
2023-01-21 03:37:17 +00:00
std : : string lastSavestateMade ; //Stores the filename of the last savestate made (needed for UndoSavestate)
2008-12-24 00:38:47 +00:00
bool undoSS = false ; //This will be true if there is lastSavestateMade, it was made since ROM was loaded, a backup state for lastSavestateMade exists
2008-12-23 22:44:04 +00:00
bool redoSS = false ; //This will be true if UndoSaveState is run, will turn false when a new savestate is made
2008-12-23 20:29:27 +00:00
2023-01-21 03:37:17 +00:00
std : : string lastLoadstateMade ; //Stores the filename of the last state loaded (needed for Undo/Redo loadstate)
2008-12-24 00:38:47 +00:00
bool undoLS = false ; //This will be true if a backupstate was made and it was made since ROM was loaded
bool redoLS = false ; //This will be true if a backupstate was loaded, meaning redoLoadState can be run
2009-10-25 03:02:00 +00:00
bool internalSaveLoad = false ;
2009-11-13 04:21:24 +00:00
bool backupSavestates = true ;
2009-11-13 05:35:32 +00:00
bool compressSavestates = true ; //By default FCEUX compresses savestates when a movie is inactive.
2009-11-13 04:21:24 +00:00
2012-02-25 23:55:35 +00:00
// a temp memory stream. We'll be dumping some data here and then compress
EMUFILE_MEMORY memory_savestate ;
// temporary buffer for compressed data of a savestate
std : : vector < uint8 > compressed_buf ;
2022-12-05 10:56:10 +00:00
# define SFMDATA_SIZE (128)
2006-07-29 05:46:15 +00:00
static SFORMAT SFMDATA [ SFMDATA_SIZE ] ;
static int SFEXINDEX ;
# define RLSB FCEUSTATE_RLSB //0x80000000
extern SFORMAT FCEUPPU_STATEINFO [ ] ;
2009-08-06 00:51:29 +00:00
extern SFORMAT FCEU_NEWPPU_STATEINFO [ ] ;
2006-07-29 05:46:15 +00:00
extern SFORMAT FCEUSND_STATEINFO [ ] ;
extern SFORMAT FCEUCTRL_STATEINFO [ ] ;
extern SFORMAT FCEUMOV_STATEINFO [ ] ;
2013-09-18 23:03:59 +00:00
//why two separate CPU structs?? who knows
2006-07-29 05:46:15 +00:00
SFORMAT SFCPU [ ] = {
2008-06-17 06:55:07 +00:00
{ & X . PC , 2 | RLSB , " PC \0 " } ,
{ & X . A , 1 , " A \0 \0 " } ,
{ & X . X , 1 , " X \0 \0 " } ,
{ & X . Y , 1 , " Y \0 \0 " } ,
{ & X . S , 1 , " S \0 \0 " } ,
2013-09-18 23:03:59 +00:00
{ & X . P , 1 , " P \0 \0 " } ,
2022-08-03 01:02:50 +00:00
{ & X . DB , 1 , " DB \0 " } ,
2008-06-17 06:55:07 +00:00
{ & 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-11-08 22:28:18 +00:00
void foo ( uint8 * test ) { ( void ) test ; }
2010-05-17 21:02:38 +00:00
static int SubWrite ( EMUFILE * os , SFORMAT * sf )
2008-06-02 14:02:02 +00:00
{
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?
{
2010-05-17 21:02:38 +00:00
os - > fwrite ( sf - > desc , 4 ) ;
2008-06-17 06:55:07 +00:00
write32le ( sf - > s & ( ~ FCEUSTATE_FLAGS ) , os ) ;
# ifndef LSB_FIRST
if ( sf - > s & RLSB )
2008-11-08 22:28:18 +00:00
FlipByteOrder ( ( uint8 * ) sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
# endif
if ( sf - > s & FCEUSTATE_INDIRECT )
2010-05-17 21:02:38 +00:00
os - > fwrite ( * ( char * * ) sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
else
2010-05-17 21:02:38 +00:00
os - > fwrite ( ( char * ) sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
//Now restore the original byte order.
# ifndef LSB_FIRST
2008-06-17 23:46:55 +00:00
if ( sf - > s & RLSB )
2008-11-08 22:28:18 +00:00
FlipByteOrder ( ( uint8 * ) sf - > v , sf - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
# 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
}
2010-05-17 21:02:38 +00:00
static int WriteStateChunk ( EMUFILE * os , int type , SFORMAT * sf )
2008-06-02 14:02:02 +00:00
{
2010-05-17 21:02:38 +00:00
os - > fputc ( type ) ;
int bsize = SubWrite ( ( EMUFILE * ) 0 , sf ) ;
2008-06-02 14:02:02 +00:00
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
}
2010-05-17 21:02:38 +00:00
static bool ReadStateChunk ( EMUFILE * is , SFORMAT * sf , int size )
2006-07-29 05:46:15 +00:00
{
2008-06-17 06:55:07 +00:00
SFORMAT * tmp ;
2010-05-17 21:02:38 +00:00
int temp = is - > ftell ( ) ;
2008-06-17 06:55:07 +00:00
2010-05-17 21:02:38 +00:00
while ( is - > ftell ( ) < temp + size )
2008-06-17 06:55:07 +00:00
{
uint32 tsize ;
char toa [ 4 ] ;
2010-05-17 21:02:38 +00:00
if ( is - > fread ( toa , 4 ) < 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 )
2010-05-17 21:02:38 +00:00
is - > fread ( * ( char * * ) tmp - > v , tmp - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
else
2010-05-17 21:02:38 +00:00
is - > fread ( ( char * ) tmp - > v , tmp - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
# ifndef LSB_FIRST
if ( tmp - > s & RLSB )
2008-11-08 22:28:18 +00:00
FlipByteOrder ( ( uint8 * ) tmp - > v , tmp - > s & ( ~ FCEUSTATE_FLAGS ) ) ;
2008-06-17 06:55:07 +00:00
# endif
}
else
2010-05-17 21:02:38 +00:00
is - > fseek ( tsize , SEEK_CUR ) ;
2008-06-17 06:55:07 +00:00
} // 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
2010-05-17 21:02:38 +00:00
static bool ReadStateChunks ( EMUFILE * 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 )
{
2010-05-17 21:02:38 +00:00
t = is - > fgetc ( ) ;
2008-06-17 06:55:07 +00:00
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 ;
2009-08-06 00:51:29 +00:00
case 31 : if ( ! ReadStateChunk ( is , FCEU_NEWPPU_STATEINFO , size ) ) ret = false ; break ;
2008-06-17 06:55:07 +00:00
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 ;
2013-09-18 23:03:59 +00:00
case 0x10 :
if ( ! ReadStateChunk ( is , SFMDATA , size ) )
ret = false ;
break ;
2008-06-17 06:55:07 +00:00
// now it gets hackier:
case 5 :
if ( ! ReadStateChunk ( is , FCEUSND_STATEINFO , size ) )
ret = false ;
else
read_snd = 1 ;
break ;
case 6 :
2010-05-12 15:31:24 +00:00
if ( FCEUMOV_Mode ( MOVIEMODE_PLAY | MOVIEMODE_RECORD | MOVIEMODE_FINISHED ) )
2008-06-17 06:55:07 +00:00
{
if ( ! ReadStateChunk ( is , FCEUMOV_STATEINFO , size ) ) ret = false ;
}
2006-07-29 05:46:15 +00:00
else
{
2010-05-17 21:02:38 +00:00
is - > fseek ( size , SEEK_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 ;
2022-08-23 02:52:40 +00:00
//ignore 8 garbage bytes, whose idea was it to write these or even have them there in the first place
if ( size = = 256 * 256 + 8 )
{
if ( is - > fread ( ( char * ) XBackBuf , 256 * 256 ) ! = 256 * 256 )
ret = false ;
is - > fseek ( 8 , SEEK_CUR ) ;
}
else
{
if ( is - > fread ( ( char * ) XBackBuf , size ) ! = size )
ret = false ;
}
2008-06-17 06:55:07 +00:00
//MBG TODO - can this be moved to a better place?
//does it even make sense, displaying XBuf when its XBackBuf we just loaded?
2021-02-06 17:15:28 +00:00
# ifdef __WIN_DRIVER__
2022-08-23 02:52:40 +00:00
if ( ret )
2008-06-17 06:55:07 +00:00
{
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;
2010-05-17 21:02:38 +00:00
is - > fseek ( size , SEEK_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
}
2009-04-11 13:20:27 +00:00
int CurrentState = 0 ;
2006-07-29 05:46:15 +00:00
extern int geniestage ;
2008-06-03 05:01:07 +00:00
2010-05-17 21:02:38 +00:00
bool FCEUSS_SaveMS ( EMUFILE * outstream , int compressionLevel )
2008-06-03 05:01:07 +00:00
{
2012-03-09 15:20:51 +00:00
// reinit memory_savestate
// memory_savestate is global variable which already has its vector of bytes, so no need to allocate memory every time we use save/loadstate
memory_savestate . set_len ( 0 ) ; // this also seeks to the beginning
memory_savestate . unfail ( ) ;
2008-06-03 05:01:07 +00:00
2012-02-25 23:55:35 +00:00
EMUFILE * os = & memory_savestate ;
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 ) ;
2009-08-06 00:51:29 +00:00
totalsize + = WriteStateChunk ( os , 31 , FCEU_NEWPPU_STATEINFO ) ;
2008-06-02 14:02:02 +00:00
totalsize + = WriteStateChunk ( os , 4 , FCEUCTRL_STATEINFO ) ;
totalsize + = WriteStateChunk ( os , 5 , FCEUSND_STATEINFO ) ;
2010-05-12 15:31:24 +00:00
if ( FCEUMOV_Mode ( MOVIEMODE_PLAY | MOVIEMODE_RECORD | MOVIEMODE_FINISHED ) )
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
2012-01-12 19:56:17 +00:00
//MBG TAS Editor HACK HACK HACK!
//do not save the movie state if we are in Taseditor! That would be a huge waste of time and space!
if ( ! FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) )
2008-06-03 05:50:58 +00:00
{
2010-05-17 21:02:38 +00:00
os - > fseek ( 5 , SEEK_CUR ) ;
2008-06-17 07:55:27 +00:00
int size = FCEUMOV_WriteState ( os ) ;
2010-05-17 21:02:38 +00:00
os - > fseek ( - ( size + 5 ) , SEEK_CUR ) ;
os - > fputc ( 7 ) ;
2008-06-03 05:50:58 +00:00
write32le ( size , os ) ;
2010-05-17 21:02:38 +00:00
os - > fseek ( size , SEEK_CUR ) ;
2008-06-17 07:55:27 +00:00
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 ;
2022-08-23 02:52:40 +00:00
uint32 size = 256 * 256 ;
2010-05-17 21:02:38 +00:00
os - > fputc ( 8 ) ;
2008-06-02 14:02:02 +00:00
write32le ( size , os ) ;
2010-05-17 21:02:38 +00:00
os - > fwrite ( ( 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 ) ;
2020-12-30 20:20:34 +00:00
if ( SPostSave ) SPostSave ( ) ;
2006-07-29 05:46:15 +00:00
2008-05-24 21:25:20 +00:00
//save the length of the file
2012-02-25 23:55:35 +00:00
int len = memory_savestate . 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 ;
2012-02-25 23:55:35 +00:00
uint8 * cbuf = ( uint8 * ) memory_savestate . buf ( ) ;
2008-06-03 03:31:05 +00:00
uLongf comprlen = - 1 ;
2012-04-07 17:10:29 +00:00
if ( compressionLevel ! = Z_NO_COMPRESSION & & ( compressSavestates | | FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) ) )
2008-06-03 03:31:05 +00:00
{
2012-02-25 23:55:35 +00:00
// worst case compression: zlib says "0.1% larger than sourceLen plus 12 bytes"
2008-06-03 03:31:05 +00:00
comprlen = ( len > > 9 ) + 12 + len ;
2012-02-25 23:55:35 +00:00
if ( compressed_buf . size ( ) < comprlen ) compressed_buf . resize ( comprlen ) ;
cbuf = & compressed_buf [ 0 ] ;
// do compression
error = compress2 ( cbuf , & comprlen , ( uint8 * ) memory_savestate . buf ( ) , len , compressionLevel ) ;
2008-06-03 03:31:05 +00:00
}
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
2010-05-17 21:02:38 +00:00
outstream - > fwrite ( ( char * ) header , 16 ) ;
outstream - > fwrite ( ( char * ) cbuf , comprlen = = - 1 ? totalsize : comprlen ) ;
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
2013-04-18 19:40:57 +00:00
void FCEUSS_Save ( const char * fname , bool display_message )
2006-07-29 05:46:15 +00:00
{
2010-05-17 21:02:38 +00:00
EMUFILE * st = 0 ;
2009-10-25 03:02:00 +00:00
char fn [ 2048 ] ;
2006-07-29 05:46:15 +00:00
2013-04-19 12:58:14 +00:00
if ( geniestage = = 1 )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
if ( display_message )
FCEU_DispMessage ( " Cannot save FCS in GG screen. " , 0 ) ;
2008-06-13 19:15:24 +00:00
return ;
}
2006-07-29 05:46:15 +00:00
2008-12-23 20:29:27 +00:00
if ( fname ) //If filename is given use it.
{
2010-05-17 21:02:38 +00:00
st = FCEUD_UTF8_fstream ( fname , " wb " ) ;
2009-10-25 03:02:00 +00:00
strcpy ( fn , fname ) ;
2008-12-23 20:29:27 +00:00
}
else //Else, generate one
2006-07-29 05:46:15 +00:00
{
2008-06-13 19:15:24 +00:00
//FCEU_PrintError("daCurrentState=%d",CurrentState);
2009-10-25 03:02:00 +00:00
strcpy ( fn , FCEU_MakeFName ( FCEUMKF_STATE , CurrentState , 0 ) . c_str ( ) ) ;
2008-12-23 03:54:31 +00:00
//backup existing savestate first
2009-11-13 04:21:24 +00:00
if ( CheckFileExists ( fn ) & & backupSavestates ) //adelikat: If the files exists and we are allowed to make backup savestates
2008-12-23 20:29:27 +00:00
{
CreateBackupSaveState ( fn ) ; //Make a backup of previous savestate before overwriting it
2023-01-21 03:37:17 +00:00
lastSavestateMade . assign ( fn ) ; //Remember what the last savestate filename was (for undoing later)
2008-12-24 04:46:56 +00:00
undoSS = true ; //Backup was created so undo is possible
2008-12-23 20:29:27 +00:00
}
else
2008-12-23 22:44:04 +00:00
undoSS = false ; //so backup made so lastSavestateMade does have a backup file, so no undo
2012-10-21 16:40:04 +00:00
2008-12-23 20:29:27 +00:00
st = FCEUD_UTF8_fstream ( fn , " wb " ) ;
2006-07-29 05:46:15 +00:00
}
2013-04-19 12:58:14 +00:00
if ( st = = NULL | | st - > get_fp ( ) = = NULL )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
if ( display_message )
FCEU_DispMessage ( " State %d save error. " , 0 , CurrentState ) ;
2008-06-13 19:15:24 +00:00
return ;
2006-07-29 05:46:15 +00:00
}
2009-10-25 03:02:00 +00:00
# ifdef _S9XLUA_H
if ( ! internalSaveLoad )
{
LuaSaveData saveData ;
CallRegisteredLuaSaveFunctions ( CurrentState , saveData ) ;
char luaSaveFilename [ 512 ] ;
strncpy ( luaSaveFilename , fn , 512 ) ;
luaSaveFilename [ 512 - ( 1 + 7 /*strlen(".luasav")*/ ) ] = ' \0 ' ;
strcat ( luaSaveFilename , " .luasav " ) ;
if ( saveData . recordList )
{
FILE * luaSaveFile = fopen ( luaSaveFilename , " wb " ) ;
if ( luaSaveFile )
{
saveData . ExportRecords ( luaSaveFile ) ;
fclose ( luaSaveFile ) ;
}
}
else
{
unlink ( luaSaveFilename ) ;
}
}
# endif
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
2013-04-19 12:58:14 +00:00
if ( ! fname )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
SaveStateStatus [ CurrentState ] = 1 ;
if ( display_message )
FCEU_DispMessage ( " State %d saved. " , 0 , CurrentState ) ;
2006-07-29 05:46:15 +00:00
}
2012-12-15 17:42:53 +00:00
redoSS = false ; //we have a new savestate so redo is not possible
2006-07-29 05:46:15 +00:00
}
2010-05-17 21:02:38 +00:00
int FCEUSS_LoadFP_old ( EMUFILE * 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;
2012-10-21 16:40:04 +00:00
//
2008-08-10 03:10:23 +00:00
// 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 ( ) ;
}
2010-09-17 18:56:17 +00:00
is - > fread ( ( char * ) & header , 16 ) ;
2008-08-10 03:10:23 +00:00
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 ) ;
2012-10-21 16:40:04 +00:00
FCEUSND_LoadState ( stateversion ) ;
2008-08-10 03:10:23 +00:00
x = FCEUMOV_PostLoad ( ) ;
}
if ( fn )
{
//if(!x || params == SSLOADPARAM_DUMMY) //is make_backup==2 possible?? oh well.
//{
// * Oops! Load the temporary savestate */
// FILE *fp;
2012-10-21 16:40:04 +00:00
//
2008-08-10 03:10:23 +00:00
// 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 ) ;
}
2010-05-17 21:02:38 +00:00
bool FCEUSS_LoadFP ( EMUFILE * is , ENUM_SSLOADPARAMS params )
2008-08-10 03:10:23 +00:00
{
2011-09-25 18:17:26 +00:00
if ( ! is ) return false ;
2008-08-14 07:50:41 +00:00
//maybe make a backup savestate
bool backup = ( params = = SSLOADPARAM_BACKUP ) ;
2011-09-25 18:17:26 +00:00
EMUFILE_MEMORY msBackupSavestate ;
2008-08-14 07:50:41 +00:00
if ( backup )
2011-09-25 18:17:26 +00:00
{
2008-08-14 07:50:41 +00:00
FCEUSS_SaveMS ( & msBackupSavestate , Z_NO_COMPRESSION ) ;
2011-09-25 18:17:26 +00:00
}
2008-08-14 07:50:41 +00:00
2008-08-10 03:10:23 +00:00
uint8 header [ 16 ] ;
2008-06-03 03:31:05 +00:00
//read and analyze the header
2010-05-17 21:02:38 +00:00
is - > fread ( ( 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
2010-05-17 21:02:38 +00:00
is - > fseek ( 0 , SEEK_SET ) ;
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 ;
2008-08-14 07:50:41 +00:00
if ( ! ret & & backup ) FCEUSS_LoadFP ( & msBackupSavestate , SSLOADPARAM_NOBACKUP ) ;
2008-08-10 04:03:50 +00:00
return ret ;
2008-08-10 03:10:23 +00:00
}
2012-10-21 16:40:04 +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 ) ;
2012-03-09 15:20:51 +00:00
// reinit memory_savestate
// memory_savestate is global variable which already has its vector of bytes, so no need to allocate memory every time we use save/loadstate
2012-02-25 23:55:35 +00:00
if ( ( int ) ( memory_savestate . get_vec ( ) ) - > size ( ) < totalsize )
( memory_savestate . get_vec ( ) ) - > resize ( totalsize ) ;
memory_savestate . set_len ( totalsize ) ;
2012-03-09 15:20:51 +00:00
memory_savestate . unfail ( ) ;
2012-02-25 23:55:35 +00:00
memory_savestate . fseek ( 0 , SEEK_SET ) ;
2008-06-03 03:31:05 +00:00
if ( comprlen ! = - 1 )
{
2012-02-25 23:55:35 +00:00
// the savestate is compressed: read from is to compressed_buf, then decompress from compressed_buf to memory_savestate.vec
if ( ( int ) compressed_buf . size ( ) < comprlen ) compressed_buf . resize ( comprlen ) ;
is - > fread ( & compressed_buf [ 0 ] , comprlen ) ;
2008-06-03 03:31:05 +00:00
uLongf uncomprlen = totalsize ;
2012-02-25 23:55:35 +00:00
int error = uncompress ( memory_savestate . buf ( ) , & uncomprlen , & compressed_buf [ 0 ] , comprlen ) ;
2008-06-03 03:31:05 +00:00
if ( error ! = Z_OK | | uncomprlen ! = totalsize )
2012-02-25 23:55:35 +00:00
return false ; // we dont need to restore the backup here because we havent messed with the emulator state yet
} else
2008-06-03 03:31:05 +00:00
{
2012-02-25 23:55:35 +00:00
// the savestate is not compressed: just read from is to memory_savestate.vec
is - > fread ( memory_savestate . buf ( ) , totalsize ) ;
2008-06-03 03:31:05 +00:00
}
2008-08-10 03:10:23 +00:00
FCEUMOV_PreLoad ( ) ;
2012-02-25 23:55:35 +00:00
bool x = ( ReadStateChunks ( & memory_savestate , 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 ) ;
}
2012-02-25 23:55:35 +00:00
if ( x )
2008-06-03 03:31:05 +00:00
{
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 ( ) ;
2012-02-25 23:55:35 +00:00
} else if ( backup )
{
2010-05-17 21:02:38 +00:00
msBackupSavestate . fseek ( 0 , SEEK_SET ) ;
2008-08-18 20:34:59 +00:00
FCEUSS_LoadFP ( & msBackupSavestate , SSLOADPARAM_NOBACKUP ) ;
}
2008-08-14 07:50:41 +00:00
2008-06-03 03:31:05 +00:00
return x ;
}
2008-05-24 21:25:20 +00:00
2013-04-18 19:40:57 +00:00
bool FCEUSS_Load ( const char * fname , bool display_message )
2006-07-29 05:46:15 +00:00
{
2010-05-17 21:02:38 +00:00
EMUFILE * st ;
2009-10-25 03:02:00 +00:00
char fn [ 2048 ] ;
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
2013-04-19 12:58:14 +00:00
if ( geniestage = = 1 )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
if ( display_message )
FCEU_DispMessage ( " Cannot load FCS in GG screen. " , 0 ) ;
2008-06-17 06:55:07 +00:00
return false ;
2006-07-29 05:46:15 +00:00
}
2013-04-19 12:58:14 +00:00
if ( fname )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
st = FCEUD_UTF8_fstream ( fname , " rb " ) ;
2009-10-25 03:02:00 +00:00
strcpy ( fn , fname ) ;
2013-04-19 12:58:14 +00:00
} else
2006-07-29 05:46:15 +00:00
{
2009-10-25 03:02:00 +00:00
strcpy ( fn , FCEU_MakeFName ( FCEUMKF_STATE , CurrentState , fname ) . c_str ( ) ) ;
2008-12-24 00:38:47 +00:00
st = FCEUD_UTF8_fstream ( fn , " rb " ) ;
2023-01-21 03:37:17 +00:00
lastLoadstateMade . assign ( fn ) ;
2006-07-29 05:46:15 +00:00
}
2013-04-19 12:58:14 +00:00
if ( st = = NULL | | ( st - > get_fp ( ) = = NULL ) )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
if ( display_message )
{
FCEU_DispMessage ( " State %d load error. " , 0 , CurrentState ) ;
//FCEU_DispMessage("State %d load error. Filename: %s", 0, CurrentState, fn);
}
SaveStateStatus [ CurrentState ] = 0 ;
2008-06-17 06:55:07 +00:00
return false ;
2006-07-29 05:46:15 +00:00
}
2012-10-21 16:40:04 +00:00
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.
2013-04-19 12:58:14 +00:00
if ( FCEUSS_LoadFP ( st , backupSavestates ? SSLOADPARAM_BACKUP : SSLOADPARAM_NOBACKUP ) )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
if ( fname )
2008-06-06 06:34:39 +00:00
{
char szFilename [ 260 ] = { 0 } ;
splitpath ( fname , 0 , 0 , szFilename , 0 ) ;
2013-04-19 12:58:14 +00:00
if ( display_message )
{
FCEU_DispMessage ( " State %s loaded. " , 0 , szFilename ) ;
//FCEU_DispMessage("State %s loaded. Filename: %s", 0, szFilename, fn);
2013-04-18 19:40:57 +00:00
}
2013-04-19 12:58:14 +00:00
} else
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
if ( display_message )
{
FCEU_DispMessage ( " State %d loaded. " , 0 , CurrentState ) ;
//FCEU_DispMessage("State %d loaded. Filename: %s", 0, CurrentState, fn);
2013-04-18 19:40:57 +00:00
}
2013-04-19 12:58:14 +00:00
SaveStateStatus [ CurrentState ] = 1 ;
2006-07-29 05:46:15 +00:00
}
2008-06-17 06:55:07 +00:00
delete st ;
2009-10-25 03:02:00 +00:00
# ifdef _S9XLUA_H
if ( ! internalSaveLoad )
{
LuaSaveData saveData ;
char luaSaveFilename [ 512 ] ;
strncpy ( luaSaveFilename , fn , 512 ) ;
luaSaveFilename [ 512 - ( 1 + 7 /*strlen(".luasav")*/ ) ] = ' \0 ' ;
strcat ( luaSaveFilename , " .luasav " ) ;
FILE * luaSaveFile = fopen ( luaSaveFilename , " rb " ) ;
if ( luaSaveFile )
{
saveData . ImportRecords ( luaSaveFile ) ;
fclose ( luaSaveFile ) ;
}
CallRegisteredLuaLoadFunctions ( CurrentState , saveData ) ;
}
# endif
2021-02-06 17:15:28 +00:00
# ifdef __WIN_DRIVER__
2009-10-08 08:06:45 +00:00
Update_RAM_Search ( ) ; // Update_RAM_Watch() is also called.
# endif
2012-10-21 16:40:04 +00:00
2010-05-12 03:07:03 +00:00
//Update input display if movie is loaded
extern uint32 cur_input_display ;
2010-05-12 13:06:23 +00:00
extern uint8 FCEU_GetJoyJoy ( void ) ;
2012-10-21 16:40:04 +00:00
2010-05-15 21:22:16 +00:00
cur_input_display = FCEU_GetJoyJoy ( ) ; //Input display should show the last buttons pressed (stored in the savestate)
2012-10-21 16:40:04 +00:00
2008-06-17 06:55:07 +00:00
return true ;
2013-04-19 12:58:14 +00:00
} else
2006-07-29 05:46:15 +00:00
{
if ( ! fname )
2013-04-19 12:58:14 +00:00
SaveStateStatus [ CurrentState ] = 1 ;
if ( display_message )
2006-07-29 05:46:15 +00:00
{
2013-04-19 12:58:14 +00:00
FCEU_DispMessage ( " Error(s) reading state %d! " , 0 , CurrentState ) ;
//FCEU_DispMessage("Error(s) reading state %d! Filename: %s", 0, CurrentState, fn);
2006-07-29 05:46:15 +00:00
}
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 )
2022-08-23 02:52:40 +00:00
FCEU_free ( ( void * ) SFMDATA [ x ] . desc ) ;
2008-06-17 06:55:07 +00:00
}
2009-03-14 15:37:13 +00:00
// adelikat, 3/14/09: had to add this to clear out the size parameter. NROM(mapper 0) games were having savestate crashes if loaded after a non NROM game because the size variable was carrying over and causing savestates to save too much data
2012-10-21 16:40:04 +00:00
SFMDATA [ 0 ] . s = 0 ;
2008-06-17 06:55:07 +00:00
SPreSave = PreSave ;
SPostSave = PostSave ;
SFEXINDEX = 0 ;
2006-07-29 05:46:15 +00:00
}
2020-06-20 03:58:12 +00:00
void AddExState ( void * v , uint32 s , int type , const char * desc )
2006-07-29 05:46:15 +00:00
{
2020-12-31 13:48:54 +00:00
//do not accept extra state information if a null pointer was provided for v, so list won't terminate early
if ( v = = 0 ) return ;
2012-06-19 20:44:05 +00:00
if ( s = = ~ 0 )
{
SFORMAT * sf = ( SFORMAT * ) v ;
std : : map < std : : string , bool > names ;
while ( sf - > v )
{
char tmp [ 5 ] = { 0 } ;
memcpy ( tmp , sf - > desc , 4 ) ;
std : : string desc = tmp ;
if ( names . find ( desc ) ! = names . end ( ) )
{
2021-02-06 17:15:28 +00:00
# ifdef __WIN_DRIVER__
2012-07-05 21:40:12 +00:00
MessageBox ( NULL , " OH NO!!! YOU HAVE AN INVALID SFORMAT! POST A BUG TICKET ALONG WITH INFO ON THE ROM YOURE USING \n " , " OOPS " , MB_OK ) ;
# else
2012-06-19 20:44:05 +00:00
printf ( " OH NO!!! YOU HAVE AN INVALID SFORMAT! POST A BUG TICKET ALONG WITH INFO ON THE ROM YOURE USING \n " ) ;
2012-07-05 21:40:12 +00:00
# endif
2012-06-19 20:44:05 +00:00
exit ( 0 ) ;
}
names [ desc ] = true ;
sf + + ;
}
}
2008-06-17 06:55:07 +00:00
if ( desc )
{
2020-06-20 03:58:12 +00:00
SFMDATA [ SFEXINDEX ] . desc = ( const char * ) FCEU_malloc ( strlen ( desc ) + 1 ) ;
strcpy ( ( char * ) SFMDATA [ SFEXINDEX ] . desc , desc ) ;
2008-06-17 06:55:07 +00:00
}
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-06-17 06:55:07 +00:00
int oldstate = CurrentState ;
2021-04-07 02:44:49 +00:00
FCEUSS_CheckStates ( ) ;
2008-06-17 06:55:07 +00:00
if ( w = = - 1 ) { StateShow = 0 ; return 0 ; } //mbg merge 7/17/06 had to make return a value
CurrentState = w ;
if ( show )
{
StateShow = 180 ;
2010-05-16 04:18:49 +00:00
FCEU_DispMessage ( " -select state- " , 0 ) ;
2008-06-17 06:55:07 +00:00
}
return oldstate ;
2008-06-17 23:46:55 +00:00
}
2006-07-29 05:46:15 +00:00
2013-04-18 19:40:57 +00:00
void FCEUI_SaveState ( const char * fname , bool display_message )
2006-07-29 05:46:15 +00:00
{
2008-06-05 06:49:11 +00:00
if ( ! FCEU_IsValidUI ( FCEUI_SAVESTATE ) ) return ;
2008-06-17 06:55:07 +00:00
2013-04-19 12:58:14 +00:00
StateShow = 0 ;
2012-10-21 16:40:04 +00:00
2013-04-18 19:40:57 +00:00
FCEUSS_Save ( fname , display_message ) ;
2006-07-29 05:46:15 +00:00
}
int loadStateFailed = 0 ; // hack, this function should return a value instead
2010-07-20 01:58:13 +00:00
bool file_exists ( const char * filename )
{
if ( FILE * file = fopen ( filename , " r " ) ) //I'm sure, you meant for READING =)
{
fclose ( file ) ;
return true ;
}
return false ;
}
2013-04-18 19:40:57 +00:00
void FCEUI_LoadState ( const char * fname , bool display_message )
2006-07-29 05:46:15 +00:00
{
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 ; ) ) .
*/
2013-04-19 12:58:14 +00:00
if ( backupSavestates )
BackupLoadState ( ) ; // If allowed, backup the current state before loading a new one
2012-10-21 16:40:04 +00:00
2008-12-23 01:07:55 +00:00
if ( ! movie_readonly & & autoMovieBackup & & freshMovie ) //If auto-backup is on, movie has not been altered this session and the movie is in read+write mode
{
2012-10-21 16:40:04 +00:00
FCEUI_MakeBackupMovie ( false ) ; //Backup the movie before the contents get altered, but do not display messages
2008-12-23 01:07:55 +00:00
}
2010-08-12 06:27:06 +00:00
if ( fname ! = NULL & & ! file_exists ( fname ) )
2010-07-20 01:58:13 +00:00
{
loadStateFailed = 1 ;
return ; // state doesn't exist; exit cleanly
}
2017-04-27 22:59:35 +00:00
2013-04-19 12:58:14 +00:00
if ( FCEUSS_Load ( fname , display_message ) )
2007-02-06 18:59:15 +00:00
{
2017-04-27 22:59:35 +00:00
//in case we're loading a savestate made with old ppu, we need to make sure ppur's regs used for dividing are ready to go
newppu_hacky_emergency_reset ( ) ;
2008-06-20 22:15:30 +00:00
//mbg todo netplay
2013-04-10 20:58:01 +00:00
#if 0
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 ) ;
2013-04-10 20:58:01 +00:00
}
# endif
2008-12-23 01:07:55 +00:00
freshMovie = false ; //The movie has been altered so it is no longer fresh
2013-04-19 12:58:14 +00:00
} else
2007-02-06 18:59:15 +00:00
{
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
}
2008-12-23 20:29:27 +00:00
//*************************************************************************
//Savestate backup functions
//(Used when making savestates)
//*************************************************************************
string GenerateBackupSaveStateFn ( const char * fname )
2008-12-19 22:11:01 +00:00
{
2008-12-23 20:29:27 +00:00
//This backup is for the backup "slot" for any savestate made. Example: smb.fc0 becomes smb-bak.fc0
2008-12-19 22:11:01 +00:00
string filename ;
2008-12-23 20:29:27 +00:00
filename = fname ; //Convert fname to a string object
int x = filename . find_last_of ( " . " ) ; //Find file extension
2012-10-21 16:40:04 +00:00
filename . insert ( x , " -bak " ) ; //add "-bak" before the dot.
2008-12-20 02:47:33 +00:00
return filename ;
2008-12-19 22:11:01 +00:00
}
2008-12-23 20:29:27 +00:00
2008-12-23 03:54:31 +00:00
void CreateBackupSaveState ( const char * fname )
{
2008-12-23 22:44:04 +00:00
string newFilename = GenerateBackupSaveStateFn ( fname ) ; //Get backup savestate filename
if ( CheckFileExists ( newFilename . c_str ( ) ) ) //See if backup already exists
remove ( newFilename . c_str ( ) ) ; //If so, delete it
rename ( fname , newFilename . c_str ( ) ) ; //Rename savestate to backup filename
undoSS = true ; //There is a backup savestate file to mast last loaded, so undo is possible
2008-12-23 03:54:31 +00:00
}
2008-12-23 20:29:27 +00:00
void SwapSaveState ( )
{
2008-12-23 21:28:06 +00:00
//--------------------------------------------------------------------------------------------
//Both files must exist
//--------------------------------------------------------------------------------------------
2012-10-21 16:40:04 +00:00
2023-01-21 03:37:17 +00:00
if ( lastSavestateMade . empty ( ) )
2008-12-23 21:28:06 +00:00
{
2010-05-16 04:18:49 +00:00
FCEUI_DispMessage ( " Can't Undo " , 0 ) ;
2008-12-23 21:28:06 +00:00
FCEUI_printf ( " Undo savestate was attempted but unsuccessful because there was not a recently used savestate. \n " ) ;
return ; //If there is no last savestate, can't undo
}
2023-01-21 03:37:17 +00:00
string backup = GenerateBackupSaveStateFn ( lastSavestateMade . c_str ( ) ) ; //Get filename of backup state
2012-10-21 16:40:04 +00:00
if ( ! CheckFileExists ( backup . c_str ( ) ) )
2008-12-23 20:29:27 +00:00
{
2010-05-16 04:18:49 +00:00
FCEUI_DispMessage ( " Can't Undo " , 0 ) ;
2008-12-23 21:28:06 +00:00
FCEUI_printf ( " Undo savestate was attempted but unsuccessful because there was not a backup of the last used savestate. \n " ) ;
return ; //If no backup, can't undo
2008-12-23 20:29:27 +00:00
}
//--------------------------------------------------------------------------------------------
//So both exists, now swap the last savestate and its backup
//--------------------------------------------------------------------------------------------
2008-12-23 21:28:06 +00:00
string temp = backup ; //Put backup filename in temp
2008-12-23 20:29:27 +00:00
temp . append ( " x " ) ; //Add x
2012-10-21 16:40:04 +00:00
2023-01-21 03:37:17 +00:00
rename ( backup . c_str ( ) , temp . c_str ( ) ) ; //rename backup file to temp file
rename ( lastSavestateMade . c_str ( ) , backup . c_str ( ) ) ; //rename current as backup
rename ( temp . c_str ( ) , lastSavestateMade . c_str ( ) ) ; //rename backup as current
2012-10-21 16:40:04 +00:00
2008-12-23 22:44:04 +00:00
undoSS = true ; //Just in case, if this was run, then there is definately a last savestate and backup
if ( redoSS ) //This was a redo function, so if run again it will be an undo again
redoSS = false ;
else //This was an undo function so next will be redo, so flag it
redoSS = true ;
2010-05-16 04:18:49 +00:00
FCEUI_DispMessage ( " %s restored " , 0 , backup . c_str ( ) ) ;
2011-11-22 20:43:18 +00:00
FCEUI_printf ( " %s restored \n " , backup . c_str ( ) ) ;
2012-10-21 16:40:04 +00:00
}
2008-12-23 20:29:27 +00:00
//------------------------------------------------------------------------------------------------------------------------------------------------------
//*************************************************************************
//Loadstate backup functions
//(Used when Loading savestates)
//*************************************************************************
string GetBackupFileName ( )
{
//This backup savestate is a special one specifically made whenever a loadstate occurs so that the user's place in a movie/game is never lost
//particularly from unintentional loadstating
string filename ;
int x ;
2012-10-21 16:40:04 +00:00
2022-08-04 09:06:31 +00:00
filename = FCEU_MakeFName ( FCEUMKF_STATE , CurrentState , 0 ) ; //Generate normal savestate filename
2008-12-23 20:29:27 +00:00
x = filename . find_last_of ( " . " ) ; //Find last dot
filename = filename . substr ( 0 , x ) ; //Chop off file extension
filename . append ( " .bak.fc0 " ) ; //add .bak
return filename ;
}
2008-12-23 03:54:31 +00:00
2008-12-23 23:08:27 +00:00
bool CheckBackupSaveStateExist ( )
{
//This function simply checks to see if the backup loadstate exists, the backup loadstate is a special savestate
//That is made before loading any state, so that the user never loses his data
string filename = GetBackupFileName ( ) ; //Get backup savestate filename
2012-10-21 16:40:04 +00:00
2008-12-23 23:08:27 +00:00
//Check if this filename exists
fstream test ;
test . open ( filename . c_str ( ) , fstream : : in ) ;
2012-10-21 16:40:04 +00:00
2008-12-23 23:08:27 +00:00
if ( test . fail ( ) )
{
test . close ( ) ;
return false ;
}
else
{
test . close ( ) ;
return true ;
}
}
2008-12-23 03:54:31 +00:00
void BackupLoadState ( )
2008-12-20 02:47:33 +00:00
{
string filename = GetBackupFileName ( ) ;
2009-10-25 03:02:00 +00:00
internalSaveLoad = true ;
2008-12-20 02:47:33 +00:00
FCEUSS_Save ( filename . c_str ( ) ) ;
2009-10-25 03:02:00 +00:00
internalSaveLoad = false ;
2008-12-24 00:38:47 +00:00
undoLS = true ;
2008-12-20 02:47:33 +00:00
}
void LoadBackup ( )
{
2008-12-24 00:38:47 +00:00
if ( ! undoLS ) return ;
string filename = GetBackupFileName ( ) ; //Get backup filename
if ( CheckBackupSaveStateExist ( ) )
{
2009-10-25 03:07:06 +00:00
//internalSaveLoad = true;
2008-12-24 00:38:47 +00:00
FCEUSS_Load ( filename . c_str ( ) ) ; //Load it
2009-10-25 03:07:06 +00:00
//internalSaveLoad = false;
2008-12-24 00:38:47 +00:00
redoLS = true ; //Flag redoLoadState
undoLS = false ; //Flag that LoadBackup cannot be run again
}
else
2010-05-16 04:18:49 +00:00
FCEUI_DispMessage ( " Error: Could not load %s " , 0 , filename . c_str ( ) ) ;
2008-12-24 00:38:47 +00:00
}
void RedoLoadState ( )
{
if ( ! redoLS ) return ;
2023-01-21 03:37:17 +00:00
if ( ! lastLoadstateMade . empty ( ) & & redoLS )
2008-12-24 00:38:47 +00:00
{
2023-01-21 03:37:17 +00:00
FCEUSS_Load ( lastLoadstateMade . c_str ( ) ) ;
FCEUI_printf ( " Redoing %s \n " , lastLoadstateMade . c_str ( ) ) ;
2008-12-24 00:38:47 +00:00
}
redoLS = false ; //Flag that RedoLoadState can not be run again
undoLS = true ; //Flag that LoadBackup can be run again
2009-10-19 03:37:38 +00:00
}