fceugx/source/ngc/fceustate.cpp

426 lines
8.8 KiB
C++
Raw Normal View History

2008-09-02 03:57:21 +02:00
/****************************************************************************
* FCE Ultra 0.98.12
* Nintendo Wii/Gamecube Port
*
2009-03-28 18:23:08 +01:00
* Tantric 2008-2009
2008-09-02 03:57:21 +02:00
*
2009-03-28 18:23:08 +01:00
* fceustate.cpp
2008-09-02 03:57:21 +02:00
*
* 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>
2009-04-06 09:27:40 +02:00
#include "pngu/pngu.h"
2008-09-02 03:57:21 +02:00
#include "types.h"
#include "state.h"
#include "x6502.h"
2008-09-02 03:57:21 +02:00
#include "fceugx.h"
2009-03-28 18:23:08 +01:00
#include "menu.h"
#include "filebrowser.h"
2008-09-02 03:57:21 +02:00
#include "memcardop.h"
#include "fileop.h"
2009-04-06 09:27:40 +02:00
#include "gcvideo.h"
2008-09-02 03:57:21 +02:00
extern "C" {
2008-09-02 03:57:21 +02:00
/*** External functions ***/
extern void FCEUPPU_SaveState(void);
extern void FCEUSND_SaveState(void);
extern void FCEUPPU_LoadState(int version);
extern void FCEUSND_LoadState(int version);
2008-09-02 03:57:21 +02:00
extern void FlipByteOrder(uint8 *src, uint32 count);
extern void (*GameStateRestore)(int version);
2008-09-02 03:57:21 +02:00
/*** 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;
}
2008-09-02 03:57:21 +02:00
#define RLSB 0x80000000
/****************************************************************************
* Memory based file functions
****************************************************************************/
static int sboffset; // Used as a basic fileptr
2008-09-02 03:57:21 +02:00
/*** Open a file ***/
static void memopen()
{
2009-03-28 18:23:08 +01:00
sboffset = 0;
2008-09-02 03:57:21 +02:00
}
/*** Write to the file ***/
static void memfwrite(void *buffer, int len)
{
2009-03-28 18:23:08 +01:00
if ((sboffset + len) > SAVEBUFFERSIZE)
ErrorPrompt("Buffer Exceeded");
if (len > 0)
{
memcpy(&savebuffer[sboffset], buffer, len);
sboffset += len;
}
2008-09-02 03:57:21 +02:00
}
/*** Read from a file ***/
static void memfread(void *buffer, int len)
{
2009-03-28 18:23:08 +01:00
if ((sboffset + len) > SAVEBUFFERSIZE)
ErrorPrompt("Buffer exceeded");
if (len > 0)
{
memcpy(buffer, &savebuffer[sboffset], len);
sboffset += len;
}
2008-09-02 03:57:21 +02:00
}
/****************************************************************************
* GCReadChunk
*
* Read the array of SFORMAT structures to memory
****************************************************************************/
static int GCReadChunk(int chunkid, SFORMAT *sf)
{
uint32 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
{
2009-03-28 18:23:08 +01:00
ErrorPrompt("Bad chunk link");
return 0;
}
}
else
{
sprintf(info, "No Sync %s %s", chunk, sf->desc);
2009-03-28 18:23:08 +01:00
ErrorPrompt(info);
return 0;
}
sf++;
}
}
else
return 0;
}
else
return 0;
return 1;
2008-09-02 03:57:21 +02:00
}
/****************************************************************************
* GCFCEUSS_Load
*
* Reads the SFORMAT arrays
****************************************************************************/
static int GCFCEUSS_Load(int method)
{
memopen(); // reset file pointer
sboffset += 16; // skip FCEU header
// Now read the chunks back
if (GCReadChunk(1, SFCPU))
{
if (GCReadChunk(2, SFCPUC))
{
X.mooPI = X.P; // Quick and dirty hack.
if (GCReadChunk(3, FCEUPPU_STATEINFO))
{
if (GCReadChunk(4, FCEUCTRL_STATEINFO))
{
if (GCReadChunk(5, FCEUSND_STATEINFO))
{
if (GCReadChunk(0x10, SFMDATA))
{
if (GameStateRestore)
GameStateRestore(FCEU_VERSION_NUMERIC);
FCEUPPU_LoadState(FCEU_VERSION_NUMERIC);
FCEUSND_LoadState(FCEU_VERSION_NUMERIC);
return 1;
}
}
}
}
}
}
2008-09-02 03:57:21 +02:00
return 0;
2008-09-02 03:57:21 +02:00
}
/****************************************************************************
* GCSaveChunk
*
* Write the array of SFORMAT structures to the file
****************************************************************************/
static int GCSaveChunk(int chunkid, SFORMAT *sf)
{
2009-03-28 18:23:08 +01:00
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;
2008-09-02 03:57:21 +02:00
}
/****************************************************************************
* 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.
****************************************************************************/
2008-10-09 06:25:35 +02:00
extern void (*SPreSave)(void);
extern void (*SPostSave)(void);
static int GCFCEUSS_Save(int method)
2008-09-02 03:57:21 +02:00
{
int totalsize = 0;
static unsigned char header[16] = "FCS\xff";
char chunk[] = "CHKE";
int zero = 0;
char comment[2][32];
memset(comment, 0, 64);
2008-09-02 03:57:21 +02:00
memopen(); // Reset Memory File
2008-09-02 03:57:21 +02:00
// Add version ID
int mcversion = 0x981211;
memcpy(&header[8], &mcversion, 4);
2008-09-02 03:57:21 +02:00
// Do internal Saving
FCEUPPU_SaveState();
FCEUSND_SaveState();
2008-10-09 06:25:35 +02:00
// 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);
2008-10-09 06:25:35 +02:00
if(nesGameType == 4) // FDS
SPreSave();
2008-09-02 03:57:21 +02:00
totalsize += GCSaveChunk(0x10, SFMDATA);
2008-10-09 06:25:35 +02:00
if(nesGameType == 4) // FDS
SPostSave();
2008-09-02 03:57:21 +02:00
// Add terminating CHNK
memfwrite(&chunk,4);
memfwrite(&zero,4);
totalsize += 8;
2008-09-02 03:57:21 +02:00
return totalsize;
2008-09-02 03:57:21 +02:00
}
2009-03-28 18:23:08 +01:00
bool SaveState (char * filepath, int method, bool silent)
2008-09-02 03:57:21 +02:00
{
bool retval = false;
int datasize;
int offset = 0;
2009-04-06 09:27:40 +02:00
int imgSize = 0; // image screenshot bytes written
2008-09-02 03:57:21 +02:00
2008-11-12 09:40:09 +01:00
if(method == METHOD_AUTO)
method = autoSaveMethod(silent);
2008-11-12 09:40:09 +01:00
2009-03-28 18:23:08 +01:00
if(method == METHOD_AUTO)
2008-11-12 09:40:09 +01:00
return false;
AllocSaveBuffer ();
2008-11-12 09:40:09 +01:00
datasize = GCFCEUSS_Save(method);
2008-09-02 03:57:21 +02:00
2008-11-12 09:40:09 +01:00
if (datasize)
2008-09-02 03:57:21 +02:00
{
2009-03-28 18:23:08 +01:00
if(method == METHOD_MC_SLOTA || method == METHOD_MC_SLOTB)
{
// Set the comments
char comments[2][32];
memset(comments, 0, 64);
sprintf (comments[0], "%s State", APPNAME);
snprintf (comments[1], 32, romFilename);
SetMCSaveComments(comments);
}
2008-11-12 09:40:09 +01:00
offset = SaveFile(filepath, datasize, method, silent);
2009-04-06 09:27:40 +02:00
}
FreeSaveBuffer ();
// save screenshot - I would prefer to do this from gameScreenTex
if(gameScreenTex2 != NULL && method != METHOD_MC_SLOTA && method != METHOD_MC_SLOTB)
{
AllocSaveBuffer ();
IMGCTX pngContext = PNGU_SelectImageFromBuffer(savebuffer);
if (pngContext != NULL)
{
imgSize = PNGU_EncodeFromGXTexture(pngContext, 640, 480, gameScreenTex2, 0);
PNGU_ReleaseImageContext(pngContext);
}
2008-09-02 03:57:21 +02:00
2009-04-06 09:27:40 +02:00
if(imgSize > 0)
2008-09-02 03:57:21 +02:00
{
2009-04-06 09:27:40 +02:00
char screenpath[1024];
filepath[strlen(filepath)-4] = 0;
sprintf(screenpath, "%s.png", filepath);
SaveFile(screenpath, imgSize, method, silent);
2008-09-02 03:57:21 +02:00
}
2009-04-06 09:27:40 +02:00
FreeSaveBuffer ();
}
if (offset > 0)
{
if (!silent)
InfoPrompt("Save successful");
retval = true;
2008-09-02 03:57:21 +02:00
}
return retval;
}
2009-03-28 18:23:08 +01:00
bool
SaveStateAuto (int method, bool silent)
2008-09-02 03:57:21 +02:00
{
2009-03-28 18:23:08 +01:00
if(method == METHOD_AUTO)
method = autoSaveMethod(silent);
if(method == METHOD_AUTO)
return false;
2008-11-12 09:40:09 +01:00
char filepath[1024];
2009-03-28 18:23:08 +01:00
if(!MakeFilePath(filepath, FILE_STATE, method, romFilename, 0))
return false;
return SaveState(filepath, method, silent);
}
bool LoadState (char * filepath, int method, bool silent)
{
2008-11-12 09:40:09 +01:00
int offset = 0;
bool retval = false;
2008-09-02 03:57:21 +02:00
if(method == METHOD_AUTO)
method = autoSaveMethod(silent); // we use 'Save' because we need R/W
2008-09-02 03:57:21 +02:00
2009-03-28 18:23:08 +01:00
if(method == METHOD_AUTO)
2008-11-12 09:40:09 +01:00
return false;
2008-09-09 20:03:41 +02:00
AllocSaveBuffer ();
2008-09-02 03:57:21 +02:00
2008-11-12 09:40:09 +01:00
offset = LoadFile(filepath, method, silent);
2008-09-02 03:57:21 +02:00
if (offset > 0)
{
GCFCEUSS_Load(method);
retval = true;
2008-09-02 03:57:21 +02:00
}
else
{
// if we reached here, nothing was done!
if(!silent)
2009-03-28 18:23:08 +01:00
ErrorPrompt ("State file not found");
}
FreeSaveBuffer ();
return retval;
2008-09-02 03:57:21 +02:00
}
2009-03-28 18:23:08 +01:00
bool
LoadStateAuto (int method, bool silent)
{
if(method == METHOD_AUTO)
method = autoSaveMethod(silent);
if(method == METHOD_AUTO)
return false;
char filepath[1024];
if(!MakeFilePath(filepath, FILE_STATE, method, romFilename, 0))
return false;
return LoadState(filepath, method, silent);
}