2008-09-02 03:57:21 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* FCE Ultra 0.98.12
|
|
|
|
* Nintendo Wii/Gamecube Port
|
|
|
|
*
|
|
|
|
* Tantric September 2008
|
|
|
|
*
|
|
|
|
* fceustate.c
|
|
|
|
*
|
|
|
|
* Memory Based Load/Save State Manager
|
|
|
|
*
|
|
|
|
* These are simply the state routines, brought together as GCxxxxx
|
|
|
|
* The original file I/O is replaced with Memory Read/Writes to the
|
|
|
|
* statebuffer below
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <gccore.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <fat.h>
|
|
|
|
#include "types.h"
|
|
|
|
#include "state.h"
|
|
|
|
|
|
|
|
#include "images/saveicon.h"
|
|
|
|
#include "fceuconfig.h"
|
|
|
|
#include "intl.h"
|
|
|
|
#include "menudraw.h"
|
|
|
|
#include "filesel.h"
|
|
|
|
#include "memcardop.h"
|
|
|
|
#include "fileop.h"
|
|
|
|
#include "smbop.h"
|
|
|
|
|
|
|
|
/*** External functions ***/
|
|
|
|
extern void FCEUPPU_SaveState(void);
|
|
|
|
extern void FCEUSND_SaveState(void);
|
|
|
|
extern void FlipByteOrder(uint8 *src, uint32 count);
|
|
|
|
extern void FCEUD_SetPalette(unsigned char index, unsigned char r, unsigned char g, unsigned char b);
|
|
|
|
extern void FCEU_ResetPalette(void);
|
|
|
|
extern void FCEUI_DisableSpriteLimitation( int a );
|
|
|
|
|
|
|
|
/*** External save structures ***/
|
|
|
|
extern SFORMAT SFCPU[];
|
|
|
|
extern SFORMAT SFCPUC[];
|
|
|
|
extern SFORMAT FCEUPPU_STATEINFO[];
|
|
|
|
extern SFORMAT FCEUCTRL_STATEINFO[];
|
|
|
|
extern SFORMAT FCEUSND_STATEINFO[];
|
|
|
|
extern SFORMAT SFMDATA[64];
|
|
|
|
extern u32 iNESGameCRC32;
|
|
|
|
|
|
|
|
#define RLSB 0x80000000
|
|
|
|
#define FILESIZEOFFSET 2116
|
|
|
|
|
|
|
|
int sboffset; /*** Used as a basic fileptr ***/
|
|
|
|
int mcversion = 0x981211;
|
|
|
|
|
|
|
|
extern unsigned char savebuffer[SAVEBUFFERSIZE];
|
|
|
|
extern char romFilename[];
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Memory based file functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*** Open a file ***/
|
|
|
|
void memopen() {
|
|
|
|
sboffset = 0;
|
|
|
|
memset(savebuffer, 0, sizeof(savebuffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** Close a file ***/
|
|
|
|
void memclose() {
|
|
|
|
sboffset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** Write to the file ***/
|
|
|
|
void memfwrite( void *buffer, int len ) {
|
|
|
|
if ( (sboffset + len ) > sizeof(savebuffer))
|
|
|
|
WaitPrompt("Buffer Exceeded");
|
|
|
|
|
|
|
|
if ( len > 0 ) {
|
|
|
|
memcpy(&savebuffer[sboffset], buffer, len );
|
|
|
|
sboffset += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** Read from a file ***/
|
|
|
|
void memfread( void *buffer, int len ) {
|
|
|
|
|
|
|
|
if ( ( sboffset + len ) > sizeof(savebuffer))
|
|
|
|
WaitPrompt("Buffer exceeded");
|
|
|
|
|
|
|
|
if ( len > 0 ) {
|
|
|
|
memcpy(buffer, &savebuffer[sboffset], len);
|
|
|
|
sboffset += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* GCReadChunk
|
|
|
|
*
|
|
|
|
* Read the array of SFORMAT structures to memory
|
|
|
|
****************************************************************************/
|
|
|
|
int GCReadChunk( int chunkid, SFORMAT *sf ) {
|
|
|
|
int csize;
|
|
|
|
static char chunk[6];
|
|
|
|
int chunklength;
|
|
|
|
int thischunk;
|
|
|
|
char info[128];
|
|
|
|
|
|
|
|
memfread(&chunk, 4);
|
|
|
|
memfread(&thischunk, 4);
|
|
|
|
memfread(&chunklength, 4);
|
|
|
|
|
|
|
|
if (strcmp(chunk, "CHNK") == 0) {
|
|
|
|
if (chunkid == thischunk) {
|
|
|
|
/*** Now decode the array of chunks to this one ***/
|
|
|
|
while (sf->v) {
|
|
|
|
memfread(&chunk, 4);
|
|
|
|
if ( memcmp(&chunk, "CHKE", 4) == 0 )
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (memcmp(&chunk, sf->desc, 4) == 0) {
|
|
|
|
memfread(&csize, 4);
|
|
|
|
if (csize == (sf->s & (~RLSB ))) {
|
|
|
|
memfread( sf->v, csize );
|
|
|
|
sprintf(info,"%s %d", chunk, csize);
|
|
|
|
} else {
|
|
|
|
WaitPrompt("Bad chunk link");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sprintf(info, "No Sync %s %s", chunk, sf->desc);
|
|
|
|
WaitPrompt(info);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sf++;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* GCFCEUSS_Load
|
|
|
|
*
|
|
|
|
* Reads the SFORMAT arrays
|
|
|
|
****************************************************************************/
|
|
|
|
int GCFCEUSS_Load() {
|
|
|
|
int totalsize = 0;
|
|
|
|
|
|
|
|
sboffset = 16 + sizeof(saveicon) + 64; /*** Reset memory file pointer ***/
|
|
|
|
|
|
|
|
memcpy(&totalsize, &savebuffer[FILESIZEOFFSET], 4);
|
|
|
|
|
|
|
|
/*** Now read the chunks back ***/
|
|
|
|
if (GCReadChunk(1, SFCPU)) {
|
|
|
|
if (GCReadChunk(2, SFCPUC)) {
|
|
|
|
if (GCReadChunk(3, FCEUPPU_STATEINFO)) {
|
|
|
|
if (GCReadChunk(4, FCEUCTRL_STATEINFO)) {
|
|
|
|
if (GCReadChunk(5, FCEUSND_STATEINFO)) {
|
|
|
|
if (GCReadChunk(0x10, SFMDATA))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* GCSaveChunk
|
|
|
|
*
|
|
|
|
* Write the array of SFORMAT structures to the file
|
|
|
|
****************************************************************************/
|
|
|
|
int GCSaveChunk(int chunkid, SFORMAT *sf) {
|
|
|
|
int chnkstart;
|
|
|
|
int csize = 0;
|
|
|
|
int chsize = 0;
|
|
|
|
char chunk[] = "CHNK";
|
|
|
|
|
|
|
|
/*** Add chunk marker ***/
|
|
|
|
memfwrite(&chunk, 4);
|
|
|
|
memfwrite(&chunkid, 4);
|
|
|
|
chnkstart = sboffset; /*** Save ptr ***/
|
|
|
|
sboffset += 4; /*** Space for length ***/
|
|
|
|
csize += 12;
|
|
|
|
|
|
|
|
/*** Now run through this structure ***/
|
|
|
|
while (sf->v) {
|
|
|
|
/*** Check that there is a decription ***/
|
|
|
|
if ( sf->desc == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*** Write out the description ***/
|
|
|
|
memfwrite( sf->desc, 4);
|
|
|
|
|
|
|
|
/*** Write the length of this chunk ***/
|
|
|
|
chsize = ( sf->s & (~RLSB) );
|
|
|
|
memfwrite( &chsize, 4);
|
|
|
|
|
|
|
|
if ( chsize > 0 )
|
|
|
|
/*** Write the actual data ***/
|
|
|
|
memfwrite( sf->v, chsize );
|
|
|
|
|
|
|
|
csize += 8;
|
|
|
|
csize += chsize;
|
|
|
|
|
|
|
|
sf++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** Update CHNK length ***/
|
|
|
|
memcpy(&savebuffer[chnkstart], &csize, 4);
|
|
|
|
|
|
|
|
return csize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* GCFCEUSS_Save
|
|
|
|
*
|
|
|
|
* This is a modified version of FCEUSS_Save
|
|
|
|
* It uses memory for it's I/O and has an added CHNK block.
|
|
|
|
* The file is terminated with CHNK length of 0.
|
|
|
|
****************************************************************************/
|
|
|
|
int GCFCEUSS_Save()
|
|
|
|
{
|
|
|
|
int totalsize = 0;
|
|
|
|
static unsigned char header[16] = "FCS\xff";
|
|
|
|
char chunk[] = "CHKE";
|
|
|
|
int zero = 0;
|
|
|
|
char Comment[2][100] = { { MENU_CREDITS_TITLE }, { "GAME" } };
|
|
|
|
|
|
|
|
memopen(); /*** Reset Memory File ***/
|
|
|
|
|
|
|
|
/*** Add version ID ***/
|
|
|
|
memcpy(&header[8], &mcversion, 4);
|
|
|
|
|
|
|
|
/*** Do internal Saving ***/
|
|
|
|
FCEUPPU_SaveState();
|
|
|
|
FCEUSND_SaveState();
|
|
|
|
|
|
|
|
/*** Write Icon ***/
|
|
|
|
memfwrite(&saveicon, sizeof(saveicon));
|
|
|
|
totalsize += sizeof(saveicon);
|
|
|
|
|
|
|
|
/*** And Comments ***/
|
|
|
|
strncpy (Comment[1],romFilename,31); // we only have 32 chars to work with!
|
|
|
|
memfwrite(&Comment[0], 64);
|
|
|
|
totalsize += 64;
|
|
|
|
|
|
|
|
/*** Write header ***/
|
|
|
|
memfwrite(&header, 16);
|
|
|
|
totalsize += 16;
|
|
|
|
totalsize += GCSaveChunk(1, SFCPU);
|
|
|
|
totalsize += GCSaveChunk(2, SFCPUC);
|
|
|
|
totalsize += GCSaveChunk(3, FCEUPPU_STATEINFO);
|
|
|
|
totalsize += GCSaveChunk(4, FCEUCTRL_STATEINFO);
|
|
|
|
totalsize += GCSaveChunk(5, FCEUSND_STATEINFO);
|
|
|
|
totalsize += GCSaveChunk(0x10, SFMDATA);
|
|
|
|
|
|
|
|
/*** Add terminating CHNK ***/
|
|
|
|
memfwrite(&chunk,4);
|
|
|
|
memfwrite(&zero,4);
|
|
|
|
totalsize += 8;
|
|
|
|
|
|
|
|
/*** Update size element ***/
|
|
|
|
memcpy(&savebuffer[FILESIZEOFFSET], &totalsize, 4);
|
|
|
|
|
|
|
|
return totalsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SaveState (int method, bool silent)
|
|
|
|
{
|
|
|
|
ShowAction ((char*) "Saving...");
|
|
|
|
|
|
|
|
if(method == METHOD_AUTO)
|
|
|
|
method = autoSaveMethod();
|
|
|
|
|
|
|
|
bool retval = false;
|
|
|
|
char filepath[1024];
|
|
|
|
int datasize;
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
datasize = GCFCEUSS_Save();
|
|
|
|
|
|
|
|
if ( datasize )
|
|
|
|
{
|
|
|
|
if(method == METHOD_SD || method == METHOD_USB)
|
|
|
|
{
|
|
|
|
if(ChangeFATInterface(method, NOTSILENT))
|
|
|
|
{
|
|
|
|
sprintf (filepath, "%s/%s/%s.fcs", ROOTFATDIR, GCSettings.SaveFolder, romFilename);
|
|
|
|
offset = SaveBufferToFAT (filepath, datasize, silent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(method == METHOD_SMB)
|
|
|
|
{
|
|
|
|
sprintf (filepath, "%s/%s.fcs", GCSettings.SaveFolder, romFilename);
|
|
|
|
offset = SaveBufferToSMB (filepath, datasize, silent);
|
|
|
|
}
|
|
|
|
else if(method == METHOD_MC_SLOTA || method == METHOD_MC_SLOTB)
|
|
|
|
{
|
|
|
|
sprintf (filepath, "%08x.fcs", iNESGameCRC32);
|
|
|
|
|
|
|
|
if(method == METHOD_MC_SLOTA)
|
|
|
|
offset = SaveBufferToMC (savebuffer, CARD_SLOTA, filepath, datasize, silent);
|
|
|
|
else
|
|
|
|
offset = SaveBufferToMC (savebuffer, CARD_SLOTB, filepath, datasize, silent);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset > 0)
|
|
|
|
{
|
|
|
|
if ( !silent )
|
|
|
|
WaitPrompt((char *)"Save successful");
|
|
|
|
retval = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadState (int method, bool silent)
|
|
|
|
{
|
|
|
|
ShowAction ((char*) "Loading...");
|
|
|
|
|
|
|
|
if(method == METHOD_AUTO)
|
2008-09-06 05:30:42 +02:00
|
|
|
method = autoSaveMethod(); // we use 'Save' because we need R/W
|
2008-09-02 03:57:21 +02:00
|
|
|
|
|
|
|
char filepath[1024];
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
if(method == METHOD_SD || method == METHOD_USB)
|
|
|
|
{
|
|
|
|
ChangeFATInterface(method, NOTSILENT);
|
|
|
|
sprintf (filepath, "%s/%s/%s.fcs", ROOTFATDIR, GCSettings.SaveFolder, romFilename);
|
|
|
|
offset = LoadBufferFromFAT (filepath, silent);
|
2008-09-09 20:03:41 +02:00
|
|
|
|
|
|
|
if(offset == 0) // file not found
|
|
|
|
{
|
|
|
|
// look for CRC save
|
|
|
|
sprintf (filepath, "%08x.fcs", iNESGameCRC32);
|
|
|
|
offset = LoadBufferFromFAT (filepath, silent);
|
|
|
|
}
|
2008-09-02 03:57:21 +02:00
|
|
|
}
|
|
|
|
else if(method == METHOD_SMB)
|
|
|
|
{
|
|
|
|
sprintf (filepath, "%s/%s.fcs", GCSettings.SaveFolder, romFilename);
|
|
|
|
offset = LoadSaveBufferFromSMB (filepath, silent);
|
|
|
|
}
|
|
|
|
else if(method == METHOD_MC_SLOTA || method == METHOD_MC_SLOTB)
|
|
|
|
{
|
|
|
|
sprintf (filepath, "%08x.fcs", iNESGameCRC32);
|
|
|
|
|
|
|
|
if(method == METHOD_MC_SLOTA)
|
|
|
|
offset = LoadBufferFromMC (savebuffer, CARD_SLOTA, filepath, silent);
|
|
|
|
else
|
|
|
|
offset = LoadBufferFromMC (savebuffer, CARD_SLOTB, filepath, silent);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset > 0)
|
|
|
|
{
|
|
|
|
GCFCEUSS_Load();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we reached here, nothing was done!
|
|
|
|
if(!silent)
|
|
|
|
WaitPrompt ((char*) "State file not found");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|