new save file system. the manual save type selection no longer does anything. If you think you still need it, we need to study the case and find out why.
This commit is contained in:
parent
89df112dd8
commit
ca540cb6b6
|
@ -0,0 +1,37 @@
|
|||
01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
The desmume save file format is merely a raw save file with a FOOTER.
|
||||
|
||||
This was chosen in order to maximize compatibility with other emulators,
|
||||
which tend load the savefile as-is and let the game read out of whatever range
|
||||
it is expecting. To assist with this, before writing the save file, desmume
|
||||
will pad the raw save file out to the next highest known length. Note that this
|
||||
may sometimes be incorrect if the savefile hasnt been written through to the end
|
||||
during initialization. This could cause other emulators to fail to recognize the
|
||||
save file.
|
||||
|
||||
The footer format can be identified by locating the 16Byte ascii string
|
||||
"|-DESMUME SAVE-|"
|
||||
at the end of the file. This corresponds with the following save structure:
|
||||
|
||||
struct Footer {
|
||||
u32 actually_written_size;
|
||||
u32 padded_size;
|
||||
u32 save_type; //(not currently used)
|
||||
u32 address_size; //address bus size
|
||||
u32 save_size; //size parameter of the save type (not currently used)
|
||||
u32 version_number; //should be 0
|
||||
char cookie[16];
|
||||
};
|
||||
|
||||
note that padded_size should be where you see the end of the raw save data
|
||||
and the beginning of desmume-specific information, including some junk before
|
||||
the actual footer.
|
||||
|
||||
actually_written_size is the highest address (plus one) written to by the game.
|
||||
|
||||
The new desmume savefile system tries to make as few decisions as possible,
|
||||
which is the reason for the behavior of actually_written_size and the disuse
|
||||
of save_type and save_size. If few decisions are made, then few mistakes can
|
||||
be made. That is the idea, anyway. We'll make decisions later if we need to.
|
||||
save_type and save_size are reserved in case we do.
|
||||
|
|
@ -361,6 +361,9 @@ static FORCEINLINE u32 MMU_LCDmap(u32 addr, bool& unmapped)
|
|||
//shared wram mapping for arm7
|
||||
if(PROCNUM==ARMCPU_ARM7)
|
||||
{
|
||||
//necessary? not sure
|
||||
//addr &= 0x3FFFF;
|
||||
//addr += 0x06000000;
|
||||
u32 ofs = addr & 0x1FFFF;
|
||||
u32 bank = (addr >> 17)&1;
|
||||
if(vram_arm7_map[bank] == VRAM_PAGE_UNMAPPED)
|
||||
|
@ -918,9 +921,9 @@ void MMU_Init(void) {
|
|||
MMU.fw.fp = NULL;
|
||||
|
||||
// Init Backup Memory device, this should really be done when the rom is loaded
|
||||
mc_init(&MMU.bupmem, MC_TYPE_AUTODETECT);
|
||||
mc_alloc(&MMU.bupmem, 1);
|
||||
MMU.bupmem.fp = NULL;
|
||||
//mc_init(&MMU.bupmem, MC_TYPE_AUTODETECT);
|
||||
//mc_alloc(&MMU.bupmem, 1);
|
||||
//MMU.bupmem.fp = NULL;
|
||||
rtcInit();
|
||||
addonsInit();
|
||||
if(Mic_Init() == FALSE)
|
||||
|
@ -934,9 +937,9 @@ void MMU_DeInit(void) {
|
|||
if (MMU.fw.fp)
|
||||
fclose(MMU.fw.fp);
|
||||
mc_free(&MMU.fw);
|
||||
if (MMU.bupmem.fp)
|
||||
fclose(MMU.bupmem.fp);
|
||||
mc_free(&MMU.bupmem);
|
||||
//if (MMU.bupmem.fp)
|
||||
// fclose(MMU.bupmem.fp);
|
||||
//mc_free(&MMU.bupmem);
|
||||
addonsClose();
|
||||
Mic_DeInit();
|
||||
}
|
||||
|
@ -1781,23 +1784,26 @@ void FASTCALL _MMU_ARM9_write16(u32 adr, u16 val)
|
|||
u16 oldval = T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM7][0x40], 0x204);
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x204, val);
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][0x40], 0x204, (val & 0xFF80) | (oldval & 0x7F));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
case REG_AUXSPICNT:
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM9][(REG_AUXSPICNT >> 20) & 0xff], REG_AUXSPICNT & 0xfff, val);
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][(REG_AUXSPICNT >> 20) & 0xff], REG_AUXSPICNT & 0xfff, val);
|
||||
AUX_SPI_CNT = val;
|
||||
|
||||
if (val == 0)
|
||||
mc_reset_com(&MMU.bupmem); /* reset backup memory device communication */
|
||||
//mc_reset_com(&MMU.bupmem); // reset backup memory device communication
|
||||
MMU.backupDevice.reset_command();
|
||||
return;
|
||||
|
||||
|
||||
case REG_AUXSPIDATA:
|
||||
if(val!=0)
|
||||
AUX_SPI_CMD = val & 0xFF;
|
||||
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM9][(REG_AUXSPIDATA >> 20) & 0xff], REG_AUXSPIDATA & 0xfff, bm_transfer(&MMU.bupmem, val));
|
||||
//T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][(REG_AUXSPIDATA >> 20) & 0xff], REG_AUXSPIDATA & 0xfff, bm_transfer(&MMU.bupmem, val));
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][(REG_AUXSPIDATA >> 20) & 0xff], REG_AUXSPIDATA & 0xfff, MMU.backupDevice.data_command(val));
|
||||
return;
|
||||
|
||||
case REG_DISPA_BG0CNT :
|
||||
//GPULOG("MAIN BG0 SETPROP 16B %08X\r\n", val);
|
||||
GPU_setBGProp(MainScreen.gpu, 0, val);
|
||||
|
@ -3079,14 +3085,16 @@ void FASTCALL _MMU_ARM7_write16(u32 adr, u16 val)
|
|||
AUX_SPI_CNT = val;
|
||||
|
||||
if (val == 0)
|
||||
mc_reset_com(&MMU.bupmem); // reset backup memory device communication
|
||||
//mc_reset_com(&MMU.bupmem); // reset backup memory device communication
|
||||
MMU.backupDevice.reset_command();
|
||||
return;
|
||||
|
||||
case REG_AUXSPIDATA:
|
||||
if(val!=0)
|
||||
AUX_SPI_CMD = val & 0xFF;
|
||||
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][(REG_AUXSPIDATA >> 20) & 0xff], REG_AUXSPIDATA & 0xfff, bm_transfer(&MMU.bupmem, val));
|
||||
//T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][(REG_AUXSPIDATA >> 20) & 0xff], REG_AUXSPIDATA & 0xfff, bm_transfer(&MMU.bupmem, val));
|
||||
T1WriteWord(MMU.MMU_MEM[ARMCPU_ARM7][(REG_AUXSPIDATA >> 20) & 0xff], REG_AUXSPIDATA & 0xfff, MMU.backupDevice.data_command(val));
|
||||
return;
|
||||
|
||||
case REG_SPICNT :
|
||||
|
@ -4019,10 +4027,10 @@ void FASTCALL MMU_DumpMemBlock(u8 proc, u32 address, u32 size, u8 *buffer)
|
|||
}
|
||||
|
||||
void mmu_select_savetype(int type, int *bmemtype, u32 *bmemsize) {
|
||||
if (type<0 || type > 6) return;
|
||||
*bmemtype=save_types[type][0];
|
||||
*bmemsize=save_types[type][1];
|
||||
mc_realloc(&MMU.bupmem, *bmemtype, *bmemsize);
|
||||
//if (type<0 || type > 6) return;
|
||||
//*bmemtype=save_types[type][0];
|
||||
//*bmemsize=save_types[type][1];
|
||||
//mc_realloc(&MMU.bupmem, *bmemtype, *bmemsize);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -118,6 +118,7 @@ struct MMU_struct {
|
|||
|
||||
memory_chip_t fw;
|
||||
memory_chip_t bupmem;
|
||||
BackupDevice backupDevice;
|
||||
|
||||
nds_dscard dscard[2];
|
||||
u32 CheckTimers;
|
||||
|
|
|
@ -832,8 +832,9 @@ int NDS_LoadROM( const char *filename, int bmtype, u32 bmsize,
|
|||
strcpy(buf, pathFilenameToROMwithoutExt);
|
||||
strcat(buf, ".sav"); // DeSmuME memory card :)
|
||||
|
||||
mc_realloc(&MMU.bupmem, bmtype, bmsize);
|
||||
mc_load_file(&MMU.bupmem, buf);
|
||||
//mc_realloc(&MMU.bupmem, bmtype, bmsize);
|
||||
//mc_load_file(&MMU.bupmem, buf);
|
||||
MMU.backupDevice.load_rom(buf);
|
||||
|
||||
memset(buf, 0, MAX_PATH);
|
||||
strcpy(buf, pathFilenameToROMwithoutExt);
|
||||
|
@ -903,6 +904,7 @@ void NDS_Reset(BOOL resetBySavestate)
|
|||
currFrameCounter=0;
|
||||
|
||||
MMU_clearMem();
|
||||
MMU.backupDevice.reset();
|
||||
|
||||
//ARM7 BIOS IRQ HANDLER
|
||||
if(CommonSettings.UseExtBIOS == true)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "types.h"
|
||||
#include "mc.h"
|
||||
#include "movie.h"
|
||||
#include "readwrite.h"
|
||||
|
||||
#define FW_CMD_READ 0x3
|
||||
#define FW_CMD_WRITEDISABLE 0x4
|
||||
|
@ -50,6 +51,16 @@
|
|||
#define CARDFLASH_DEEP_POWDOWN 0xB9 /* Not used*/
|
||||
#define CARDFLASH_WAKEUP 0xAB /* Not used*/
|
||||
|
||||
//this should probably be 0xFF but we're using 0x00 until we find out otherwise
|
||||
//(no$ appears definitely to initialized to 0xFF)
|
||||
static const u8 kUninitializedSaveDataValue = 0x00;
|
||||
|
||||
static const char* kDesmumeSaveCookie = "|-DESMUME SAVE-|";
|
||||
|
||||
static const u32 saveSizes[] = {512,8*1024,64*1024,256*1024,512*1024,32*1024};
|
||||
static const u32 saveSizes_count = ARRAY_SIZE(saveSizes);
|
||||
|
||||
|
||||
void mc_init(memory_chip_t *mc, int type)
|
||||
{
|
||||
mc->com = 0;
|
||||
|
@ -510,3 +521,342 @@ u8 bm_transfer(memory_chip_t *mc, u8 data)
|
|||
return data;
|
||||
}
|
||||
|
||||
|
||||
bool BackupDevice::save_state(std::ostream* os)
|
||||
{
|
||||
int version = 0;
|
||||
write32le(version,os);
|
||||
// write32le(0,os); //reserved for type if i need it later
|
||||
// write32le(0,os); //reserved for size if i need it later
|
||||
write32le(write_enable,os);
|
||||
write32le(com,os);
|
||||
write32le(addr_size,os);
|
||||
write32le(addr_counter,os);
|
||||
write32le((u32)state,os);
|
||||
writebuffer(data,os);
|
||||
writebuffer(data_autodetect,os);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BackupDevice::load_state(std::istream* is)
|
||||
{
|
||||
int version;
|
||||
if(read32le(&version,is)!=1) return false;
|
||||
if(version==0) {
|
||||
u32 size, type;
|
||||
// read32le(&size,is);
|
||||
// read32le(&type,is);
|
||||
read32le(&write_enable,is);
|
||||
read32le(&com,is);
|
||||
read32le(&addr_size,is);
|
||||
read32le(&addr_counter,is);
|
||||
read32le((u32*)&state,is);
|
||||
readbuffer(data,is);
|
||||
readbuffer(data_autodetect,is);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//due to unfortunate shortcomings in the emulator architecture,
|
||||
//at reset-time, we won't have a filename to the .sav file.
|
||||
//so the only difference between load_rom (init) and reset is that
|
||||
//one of them saves the filename
|
||||
void BackupDevice::load_rom(const char* filename)
|
||||
{
|
||||
this->filename = filename;
|
||||
reset();
|
||||
}
|
||||
|
||||
void BackupDevice::reset()
|
||||
{
|
||||
state = DETECTING;
|
||||
data.resize(0);
|
||||
data_autodetect.resize(0);
|
||||
loadfile();
|
||||
flushPending = false;
|
||||
}
|
||||
|
||||
void BackupDevice::close_rom() {}
|
||||
|
||||
void BackupDevice::reset_command()
|
||||
{
|
||||
//for a performance hack, save files are only flushed after each reset command
|
||||
//(hopefully, after each page)
|
||||
if(flushPending)
|
||||
{
|
||||
flush();
|
||||
flushPending = false;
|
||||
}
|
||||
|
||||
if(state == DETECTING && data_autodetect.size()>0)
|
||||
{
|
||||
//we can now safely detect the save address size
|
||||
u32 autodetect_size = data_autodetect.size();
|
||||
addr_size = autodetect_size - 1;
|
||||
if(autodetect_size==6) addr_size = 2; //castlevania dawn of sorrow 64kbit eeprom (EEPROM2 in the old system)
|
||||
if(addr_size>4)
|
||||
{
|
||||
LOG("Unexpected backup memory address size: %d\n",addr_size);
|
||||
}
|
||||
state = RUNNING;
|
||||
data_autodetect.resize(0);
|
||||
}
|
||||
|
||||
com = 0;
|
||||
}
|
||||
u8 BackupDevice::data_command(u8 val)
|
||||
{
|
||||
if(com == BM_CMD_READLOW || com == BM_CMD_WRITELOW)
|
||||
{
|
||||
//handle data or address
|
||||
if(state == DETECTING)
|
||||
{
|
||||
if(com == BM_CMD_WRITELOW)
|
||||
{
|
||||
LOG("Unexpected backup device initialization sequence using writes!\n");
|
||||
}
|
||||
|
||||
//just buffer the data until we're no longer detecting
|
||||
data_autodetect.push_back(val);
|
||||
val = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(addr_counter<addr_size)
|
||||
{
|
||||
//continue building address
|
||||
addr <<= 8;
|
||||
addr |= val;
|
||||
addr_counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//address is complete
|
||||
ensure(addr);
|
||||
if(com == BM_CMD_READLOW)
|
||||
{
|
||||
val = data[addr];
|
||||
//printf("read: %08X\n",addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
data[addr] = val;
|
||||
flushPending = true;
|
||||
//printf("writ: %08X\n",addr);
|
||||
}
|
||||
addr++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(com == BM_CMD_READSTATUS)
|
||||
{
|
||||
//handle request to read status
|
||||
//LOG("Backup Memory Read Status: %02X\n", mc->write_enable << 1);
|
||||
return (write_enable << 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//there is no current command. receive one
|
||||
switch(val)
|
||||
{
|
||||
case 0: break; //??
|
||||
|
||||
case BM_CMD_WRITEDISABLE:
|
||||
write_enable = FALSE;
|
||||
break;
|
||||
|
||||
case BM_CMD_READSTATUS:
|
||||
com = BM_CMD_READSTATUS;
|
||||
break;
|
||||
|
||||
case BM_CMD_WRITEENABLE:
|
||||
write_enable = TRUE;
|
||||
break;
|
||||
|
||||
case BM_CMD_WRITELOW:
|
||||
case BM_CMD_READLOW:
|
||||
com = val;
|
||||
addr_counter = 0;
|
||||
addr = 0;
|
||||
break;
|
||||
|
||||
case BM_CMD_WRITEHIGH:
|
||||
case BM_CMD_READHIGH:
|
||||
if(val == BM_CMD_WRITEHIGH) val = BM_CMD_WRITELOW;
|
||||
if(val == BM_CMD_READHIGH) val = BM_CMD_READLOW;
|
||||
com = val;
|
||||
addr_counter = 0;
|
||||
addr = 0;
|
||||
if(addr_size==1) {
|
||||
//"write command that's only available on ST M95040-W that I know of"
|
||||
//this makes sense, since this device would only have a 256 bytes address space with writelow
|
||||
//and writehigh would allow access to the upper 256 bytes
|
||||
//but it was detected in pokemon diamond also during the main save process
|
||||
addr = 0x1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG("Unhandled Backup Memory command: %02X\n", val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
//guarantees that the data buffer has room enough for a byte at the specified address
|
||||
void BackupDevice::ensure(u32 addr)
|
||||
{
|
||||
u32 size = data.size();
|
||||
if(size<addr+1)
|
||||
{
|
||||
data.resize(addr+1);
|
||||
}
|
||||
for(u32 i=size;i<=addr;i++)
|
||||
data[i] = kUninitializedSaveDataValue;
|
||||
}
|
||||
|
||||
|
||||
void BackupDevice::loadfile()
|
||||
{
|
||||
FILE* inf = fopen(filename.c_str(),"rb");
|
||||
if(inf)
|
||||
{
|
||||
//scan for desmume save footer
|
||||
const u32 cookieLen = strlen(kDesmumeSaveCookie);
|
||||
char *sigbuf = new char[cookieLen];
|
||||
fseek(inf, -cookieLen, SEEK_END);
|
||||
fread(sigbuf,1,cookieLen,inf);
|
||||
int cmp = memcmp(sigbuf,kDesmumeSaveCookie,cookieLen);
|
||||
delete[] sigbuf;
|
||||
if(cmp)
|
||||
{
|
||||
//raw save file
|
||||
fseek(inf, 0, SEEK_END);
|
||||
int size = ftell(inf);
|
||||
fseek(inf, 0, SEEK_SET);
|
||||
data.resize(size);
|
||||
fread(&data[0],1,size,inf);
|
||||
fclose(inf);
|
||||
return;
|
||||
}
|
||||
//desmume format
|
||||
fseek(inf, -cookieLen, SEEK_END);
|
||||
fseek(inf, -4, SEEK_CUR);
|
||||
u32 version = 0xFFFFFFFF;
|
||||
read32le(&version,inf);
|
||||
if(version!=0) {
|
||||
LOG("Unknown save file format\n");
|
||||
return;
|
||||
}
|
||||
fseek(inf, -24, SEEK_CUR);
|
||||
struct {
|
||||
u32 size,padSize,type,addr_size,mem_size;
|
||||
} info;
|
||||
read32le(&info.size,inf);
|
||||
read32le(&info.padSize,inf);
|
||||
read32le(&info.type,inf);
|
||||
read32le(&info.addr_size,inf);
|
||||
read32le(&info.mem_size,inf);
|
||||
|
||||
//establish the save data
|
||||
data.resize(info.size);
|
||||
fseek(inf, 0, SEEK_SET);
|
||||
fread(&data[0],1,info.size,inf); //read all the raw data we have
|
||||
state = RUNNING;
|
||||
addr_size = info.addr_size;
|
||||
//none of the other fields are used right now
|
||||
|
||||
fclose(inf);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Missing save file %s\n",filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
u32 BackupDevice::addr_size_for_old_save_size(int bupmem_size)
|
||||
{
|
||||
switch(bupmem_size) {
|
||||
case MC_SIZE_4KBITS: return 2; //1? hi command?
|
||||
case MC_SIZE_64KBITS:
|
||||
case MC_SIZE_256KBITS:
|
||||
case MC_SIZE_512KBITS:
|
||||
return 2;
|
||||
case MC_SIZE_1MBITS:
|
||||
case MC_SIZE_2MBITS:
|
||||
case MC_SIZE_4MBITS:
|
||||
case MC_SIZE_8MBITS:
|
||||
case MC_SIZE_16MBITS:
|
||||
case MC_SIZE_64MBITS:
|
||||
return 3;
|
||||
default:
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
u32 BackupDevice::addr_size_for_old_save_type(int bupmem_type)
|
||||
{
|
||||
switch(bupmem_type)
|
||||
{
|
||||
case MC_TYPE_EEPROM1:
|
||||
return 1;
|
||||
case MC_TYPE_EEPROM2:
|
||||
case MC_TYPE_FRAM:
|
||||
return 2;
|
||||
case MC_TYPE_FLASH:
|
||||
return 3;
|
||||
default:
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BackupDevice::load_old_state(u32 addr_size, u8* data, u32 datasize)
|
||||
{
|
||||
state = RUNNING;
|
||||
this->addr_size = addr_size;
|
||||
this->data.resize(datasize);
|
||||
memcpy(&this->data[0],data,datasize);
|
||||
}
|
||||
|
||||
void BackupDevice::flush()
|
||||
{
|
||||
FILE* outf = fopen(filename.c_str(),"wb");
|
||||
if(outf)
|
||||
{
|
||||
fwrite(&data[0],1,data.size(),outf);
|
||||
|
||||
//write the footer. we use a footer so that we can maximize the chance of the
|
||||
//save file being recognized as a raw save file by other emulators etc.
|
||||
|
||||
//first, pad up to the next largest known save size.
|
||||
u32 size = data.size();
|
||||
int ctr=0;
|
||||
while(ctr<saveSizes_count && size > saveSizes[ctr]) ctr++;
|
||||
u32 padSize = saveSizes[ctr];
|
||||
|
||||
for(u32 i=size;i<padSize;i++)
|
||||
fputc(kUninitializedSaveDataValue,outf);
|
||||
|
||||
//this is just for humans to read
|
||||
fprintf(outf,"|<--Snip above here to create a raw sav by excluding this DeSmuME savedata footer:");
|
||||
|
||||
//and now the actual footer
|
||||
write32le(size,outf); //the size of data that has actually been written
|
||||
write32le(padSize,outf); //the size we padded it to
|
||||
write32le(0,outf); //save memory type
|
||||
write32le(addr_size,outf);
|
||||
write32le(0,outf); //save memory size
|
||||
write32le(0,outf); //version number
|
||||
fprintf(outf,kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save
|
||||
|
||||
|
||||
fclose(outf);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Unable to open savefile %s\n",filename.c_str());
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@
|
|||
#define __FW_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "types.h"
|
||||
|
||||
#define MC_TYPE_AUTODETECT 0x0
|
||||
|
@ -60,6 +62,52 @@ typedef struct
|
|||
int autodetectsize;
|
||||
} memory_chip_t;
|
||||
|
||||
//the new backup system by zeromus
|
||||
class BackupDevice
|
||||
{
|
||||
public:
|
||||
void load_rom(const char* filename);
|
||||
void reset();
|
||||
void close_rom();
|
||||
|
||||
bool save_state(std::ostream* os);
|
||||
bool load_state(std::istream* is);
|
||||
|
||||
//commands from mmu
|
||||
void reset_command();
|
||||
u8 data_command(u8);
|
||||
|
||||
//this info was saved before the last reset (used for savestate compatibility)
|
||||
struct SavedInfo
|
||||
{
|
||||
u32 addr_size;
|
||||
} savedInfo;
|
||||
|
||||
void load_old_state(u32 addr_size, u8* data, u32 datasize);
|
||||
|
||||
static u32 addr_size_for_old_save_size(int bupmem_size);
|
||||
static u32 addr_size_for_old_save_type(int bupmem_type);
|
||||
|
||||
private:
|
||||
BOOL write_enable; //is write enabled?
|
||||
u32 com; //persistent command actually handled
|
||||
u32 addr_size, addr_counter;
|
||||
u32 addr;
|
||||
|
||||
std::string filename;
|
||||
std::vector<u8> data;
|
||||
std::vector<u8> data_autodetect;
|
||||
enum : u32 {
|
||||
DETECTING = 0, RUNNING = 1
|
||||
} state;
|
||||
|
||||
void loadfile();
|
||||
void ensure(u32 addr);
|
||||
void flush();
|
||||
|
||||
bool flushPending;
|
||||
};
|
||||
|
||||
#define NDS_FW_SIZE_V1 (256 * 1024) /* size of fw memory on nds v1 */
|
||||
#define NDS_FW_SIZE_V2 (512 * 1024) /* size of fw memory on nds v2 */
|
||||
|
||||
|
|
|
@ -155,3 +155,19 @@ int read32le(u32 *Bufo, std::istream *is)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int readbuffer(std::vector<u8> &vec, std::istream* is)
|
||||
{
|
||||
u32 size;
|
||||
if(read32le(&size,is) != 1) return 0;
|
||||
vec.resize(size);
|
||||
if(size>0) is->read((char*)&vec[0],size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int writebuffer(std::vector<u8>& vec, std::ostream* os)
|
||||
{
|
||||
u32 size = vec.size();
|
||||
write32le(size,os);
|
||||
if(size>0) os->write((char*)&vec[0],size);
|
||||
return 1;
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#include "types.h"
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
//well. just for the sake of consistency
|
||||
int write8le(u8 b, FILE *fp);
|
||||
|
@ -21,4 +22,7 @@ int read16le(u16 *Bufo, std::istream *is);
|
|||
inline int read16le(s16 *Bufo, std::istream* is) { return read16le((u16*)Bufo,is); }
|
||||
int read8le(u8 *Bufo, std::istream *is);
|
||||
|
||||
int readbuffer(std::vector<u8> &vec, std::istream* is);
|
||||
int writebuffer(std::vector<u8>& vec, std::ostream* os);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -255,11 +255,10 @@ SFORMAT SF_MOVIE[]={
|
|||
static void mmu_savestate(std::ostream* os)
|
||||
{
|
||||
//version
|
||||
write32le(1,os);
|
||||
|
||||
write32le(MMU.bupmem.type,os);
|
||||
write32le(MMU.bupmem.size,os);
|
||||
os->write((char*)MMU.bupmem.data,MMU.bupmem.size);
|
||||
write32le(2,os);
|
||||
|
||||
//newer savefile system:
|
||||
MMU.backupDevice.save_state(os);
|
||||
}
|
||||
|
||||
static bool mmu_loadstate(std::istream* is, int size)
|
||||
|
@ -268,29 +267,45 @@ static bool mmu_loadstate(std::istream* is, int size)
|
|||
int version;
|
||||
if(read32le(&version,is) != 1) return false;
|
||||
|
||||
|
||||
u32 bupmem_size;
|
||||
|
||||
if(version == 0)
|
||||
if(version == 0 || version == 1)
|
||||
{
|
||||
//version 0 was buggy and didnt save the type.
|
||||
//it would silently fail if there was a size mismatch
|
||||
SAV_silent_fail_flag = true;
|
||||
if(read32le(&bupmem_size,is) != 1) return false;
|
||||
//if(bupmem_size != MMU.bupmem.size) return false; //mismatch between current initialized and saved size
|
||||
mc_realloc(&MMU.bupmem,MC_TYPE_AUTODETECT,bupmem_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
//version 1 reinitializes the save system with the type that was saved
|
||||
int bupmem_type;
|
||||
if(read32le(&bupmem_type,is) != 1) return false;
|
||||
if(read32le(&bupmem_size,is) != 1) return false;
|
||||
mc_realloc(&MMU.bupmem,bupmem_type,bupmem_size);
|
||||
}
|
||||
u32 bupmem_size;
|
||||
u32 addr_size;
|
||||
|
||||
is->read((char*)MMU.bupmem.data,bupmem_size);
|
||||
if(is->fail()) return false;
|
||||
if(version == 0)
|
||||
{
|
||||
//version 0 was buggy and didnt save the type.
|
||||
//it would silently fail if there was a size mismatch
|
||||
SAV_silent_fail_flag = true;
|
||||
if(read32le(&bupmem_size,is) != 1) return false;
|
||||
//if(bupmem_size != MMU.bupmem.size) return false; //mismatch between current initialized and saved size
|
||||
addr_size = BackupDevice::addr_size_for_old_save_size(bupmem_size);
|
||||
}
|
||||
else if(version == 1)
|
||||
{
|
||||
//version 1 reinitializes the save system with the type that was saved
|
||||
int bupmem_type;
|
||||
if(read32le(&bupmem_type,is) != 1) return false;
|
||||
if(read32le(&bupmem_size,is) != 1) return false;
|
||||
addr_size = BackupDevice::addr_size_for_old_save_type(bupmem_type);
|
||||
if(addr_size == 0xFFFFFFFF)
|
||||
addr_size = BackupDevice::addr_size_for_old_save_size(bupmem_size);
|
||||
}
|
||||
|
||||
if(addr_size == 0xFFFFFFFF)
|
||||
return false;
|
||||
|
||||
u8* temp = new u8[bupmem_size];
|
||||
is->read((char*)temp,bupmem_size);
|
||||
MMU.backupDevice.load_old_state(addr_size,temp,bupmem_size);
|
||||
delete[] temp;
|
||||
if(is->fail()) return false;
|
||||
}
|
||||
else if(version == 2)
|
||||
{
|
||||
//newer savefile system:
|
||||
MMU.backupDevice.load_state(is);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue