- rewrite IO backup memory. now reading/writing direct from/to file without temporary buffer in memory.

This commit is contained in:
mtabachenko 2013-11-15 15:08:15 +00:00
parent 2db8779e08
commit 6cdd10b84a
5 changed files with 456 additions and 308 deletions

View File

@ -1734,9 +1734,6 @@ void NDS_exec(s32 nb)
{
LagFrameFlag=1;
if((currFrameCounter&63) == 0)
MMU_new.backupDevice.lazy_flush();
sequencer.nds_vblankEnded = false;
nds.cpuloopIterationCount = 0;

View File

@ -521,6 +521,7 @@ extern struct TCommonSettings {
, StylusPressure(50)
, ConsoleType(NDS_CONSOLE_TYPE_FAT)
, StylusJitter(false)
, backupSave(false)
{
strcpy(ARM9BIOS, "biosnds9.bin");
strcpy(ARM7BIOS, "biosnds7.bin");
@ -611,6 +612,7 @@ extern struct TCommonSettings {
int autodetectBackupMethod;
//this is the user's choice of manual backup type, for cases when the autodetection can't be trusted
int manualBackupType;
bool backupSave;
bool spu_muteChannels[16];
bool spu_captureMuted;

View File

@ -130,6 +130,7 @@ public:
case 0x8B:
mode = cmd;
handle_save = 0;
MMU_new.backupDevice.flushBackup();
break;
case 0xB7:
@ -182,10 +183,9 @@ public:
{
if(handle_save)
{
MMU_new.backupDevice.ensure(save_adr+4,0);
val = MMU_new.backupDevice.data[save_adr+3]<<24 | MMU_new.backupDevice.data[save_adr+2]<<16 | MMU_new.backupDevice.data[save_adr+1]<<8| MMU_new.backupDevice.data[save_adr+0]<<0;
MMU_new.backupDevice.ensure(save_adr+4, (u8)0);
MMU_new.backupDevice.setLazyFlushPending();
val = MMU_new.backupDevice.readLong(save_adr, 0);
save_adr += 4;
}
@ -244,12 +244,10 @@ public:
switch(cmd)
{
case 0x81: //Nand Write
MMU_new.backupDevice.ensure(adr+4,0);
MMU_new.backupDevice.data[adr+0] = (val >> 0) & 0xFF;
MMU_new.backupDevice.data[adr+1] = (val >> 8) & 0xFF;
MMU_new.backupDevice.data[adr+2] = (val >> 16) & 0xFF;
MMU_new.backupDevice.data[adr+3] = (val >> 24) & 0xFF;
MMU_new.backupDevice.setFlushPending();
MMU_new.backupDevice.ensure(adr+4, (u8)0);
MMU_new.backupDevice.writeLong(adr, val);
save_adr += 4;
break;
}

View File

@ -29,8 +29,8 @@
#include "path.h"
#include "utils/advanscene.h"
//#define _DONT_LOAD_BACKUP
//#define _DONT_SAVE_BACKUP
//#define _MCLOG
// TODO: motion device was broken
//#define _ENABLE_MOTION
@ -58,6 +58,12 @@
#define CARDFLASH_DEEP_POWDOWN 0xB9 /* Not used*/
#define CARDFLASH_WAKEUP 0xAB /* Not used*/
#ifdef _MCLOG
#define MCLOG(...) printf(__VA_ARGS__)
#else
#define MCLOG(...)
#endif
//since r2203 this was 0x00.
//but baby pals proves finally that it should be 0xFF:
//the game reads its initial sound volumes from uninitialized data, and if it is 0, the game will be silent
@ -116,7 +122,11 @@ void backup_setManualBackupType(int type)
bool BackupDevice::save_state(EMUFILE* os)
{
u32 version = 4;
u32 savePos = fpMC->ftell();
std::vector<u8> data(fsize);
fread((char*)&data[0], 1, fsize, fpMC->get_fp());
u32 version = 5;
//v0
write32le(version,os);
write32le(write_enable,os);
@ -135,23 +145,39 @@ bool BackupDevice::save_state(EMUFILE* os)
writebool(reset_command_state,os);
//v4
write8le(write_protect,os);
//v5
write32le(savePos,os);
fpMC->fseek(savePos, SEEK_SET);
data.clear();
return true;
}
bool BackupDevice::load_state(EMUFILE* is)
{
u32 version;
u32 temp;
if(read32le(&version,is)!=1) return false;
if(version>=0)
{
std::vector<u8> data;
readbool(&write_enable,is);
read32le(&com,is);
read32le(&addr_size,is);
read32le(&addr_counter,is);
u32 temp;
read32le(&temp,is);
state = (STATE)temp;
readbuffer(data,is);
fsize = data.size();
#ifdef _DONT_SAVE_BACKUP
fpMC->fseek(0, SEEK_SET);
fwrite((char*)&data[0], 1, fsize, fpMC->get_fp());
ensure(data.size(), fpMC);
#endif
readbuffer(data_autodetect,is);
}
if(version>=1)
@ -173,20 +199,295 @@ bool BackupDevice::load_state(EMUFILE* is)
read8le(&write_protect,is);
}
if(version>=5)
{
read32le(&temp,is);
fpMC->fseek(temp, SEEK_SET);
}
else
fpMC->fseek(addr, SEEK_SET);
return true;
}
BackupDevice::BackupDevice()
{
fpMC = NULL;
fsize = 0;
addr_size = 0;
isMovieMode = false;
if (gameInfo.romsize == 0) return;
char buf[MAX_PATH] = {0};
memset(buf, 0, MAX_PATH);
path.getpathnoext(path.BATTERY, buf);
filename = std::string(buf) + ".dsv"; // DeSmuME memory card
isMovieMode = false;
MCLOG("MC: %s\n", filename.c_str());
bool fexists = (access(filename.c_str(), 0) == 0)?true:false;
if (fexists && CommonSettings.backupSave)
{
std::string tmp_fsav = std::string(buf) + ".dsv.bak";
EMUFILE_FILE *in = new EMUFILE_FILE(filename, "rb");
if (!in->fail())
{
u32 sz = in->size();
if (sz > 0)
{
EMUFILE_FILE *out = new EMUFILE_FILE(tmp_fsav, "wb");
if (!out->fail())
{
u8 *data = new u8[sz];
fread(data, 1, sz, in->get_fp());
fwrite(data, 1, sz, out->get_fp());
delete [] data;
}
delete out;
}
}
delete in;
}
if (!fexists)
{
printf("DeSmuME .dsv save file not found. Trying to load an old raw .sav file.\n");
std::string tmp_fsav = std::string(buf) + ".sav";
EMUFILE_FILE *fpTmp = new EMUFILE_FILE(tmp_fsav, "rb");
if (!fpTmp->fail())
{
u32 sz = fpTmp->size();
if (sz > 0)
{
EMUFILE_FILE *fpOut = new EMUFILE_FILE(filename, "wb");
if (!fpOut->fail())
{
u8 *buf = new u8[sz + 1];
if ((buf) && (fread(buf, 1, sz, fpTmp->get_fp()) == sz))
{
if (no_gba_unpack(buf, sz))
printf("Found no$gba save\n");
if (fwrite(buf, 1, sz, fpOut->get_fp()) == sz)
{
u8 res = searchFileSaveType(sz);
if (res != 0xFF)
{
info.type = (res + 1); // +1 - skip autodetect
addr_size = info.addr_size = addr_size_for_old_save_type(info.type);
fsize = sz;
info.size = sz;
ensure(sz, fpOut);
fsize = 0;
}
else
info.type = 0;
fexists = true;
}
}
delete [] buf;
}
delete fpOut;
}
}
delete fpTmp;
}
fpMC = new EMUFILE_FILE(filename, fexists?"rb+":"wb+");
if (!fpMC->fail())
{
fsize = fpMC->size();
if (fsize < saveSizes[0])
fpMC->truncate(0);
if (readFooter() == 0)
fsize = fsize - 24;
else
{
memset(&info, 0, sizeof(info));
fsize = 0;
}
fpMC->fseek(0, SEEK_SET);
u32 left = 0;
if (CommonSettings.autodetectBackupMethod == 1)
{
if (advsc.isLoaded())
{
info.type = advsc.getSaveType();
if (info.type != 0xFF && info.type != 0xFE)
{
info.type++;
u32 adv_size = save_types[info.type].size;
if (info.size > adv_size)
{
info.size = adv_size;
fpMC->truncate(adv_size);
ensure(adv_size, fpMC);
}
else
if (info.size < adv_size)
{
left = adv_size - info.size;
info.size = adv_size;
ensure(adv_size);
}
fsize = adv_size;
}
}
}
addr_size = info.addr_size;
//none of the other fields are used right now
if (CommonSettings.autodetectBackupMethod != 1 && info.type == 0)
{
info.type = searchFileSaveType(info.size);
if (info.type == 0xFF) info.type = 0;
}
u32 ss = (info.padSize * 8) / 1024;
bool _Mbit = false;
if (ss >= 1024)
{
ss /= 1024;
_Mbit = true;
}
printf("Backup size: %u %cbit\n", ss, _Mbit?'M':'K');
}
state = (fsize > 0)?RUNNING:DETECTING;
reset();
}
BackupDevice::~BackupDevice()
{
delete fpMC;
fpMC = NULL;
}
int BackupDevice::readFooter()
{
//scan for desmume save footer
const s32 cookieLen = (s32)strlen(kDesmumeSaveCookie);
char *sigbuf = new char[cookieLen];
fpMC->fseek(-cookieLen, SEEK_END);
fpMC->fread(sigbuf, cookieLen);
int cmp = memcmp(sigbuf,kDesmumeSaveCookie,cookieLen);
delete[] sigbuf;
if (cmp)
return -1;
//desmume format
fpMC->fseek(-cookieLen, SEEK_END);
fpMC->fseek(-4, SEEK_CUR);
u32 version = 0xFFFFFFFF;
fpMC->read32le(&version);
if (version != 0)
return -2;
fpMC->fseek(-24, SEEK_CUR);
fpMC->read32le(&info.size);
fpMC->read32le(&info.padSize);
fpMC->read32le(&info.type);
fpMC->read32le(&info.addr_size);
fpMC->read32le(&info.mem_size);
MCLOG("DeSmuME backup footer:\n");
MCLOG("\t* size:\t\t%u\n", info.size);
MCLOG("\t* padSize:\t%u\n", info.padSize);
MCLOG("\t* type (%u):\t%s\n", info.type, save_types[info.type].descr);
MCLOG("\t* addr_size:\t%u\n", info.addr_size);
MCLOG("\t* mem_size:\t%u\n", info.mem_size);
return 0;
}
u8 BackupDevice::read()
{
u8 val = 0xFF;
fpMC->read8le(&val);
return val;
}
u8 BackupDevice::readByte(u32 addr, const u8 init)
{
u8 val = init;
fpMC->fseek(addr, SEEK_SET);
fpMC->read8le(&val);
return val;
}
u16 BackupDevice::readWord(u32 addr, const u16 init)
{
u16 val = init;
fpMC->fseek(addr, SEEK_SET);
fpMC->read16le(&val);
return val;
}
u32 BackupDevice::readLong(u32 addr, const u32 init)
{
u32 val = init;
fpMC->fseek(addr, SEEK_SET);
fpMC->read32le(&val);
return val;
}
bool BackupDevice::write(u8 val)
{
#ifdef _DONT_SAVE_BACKUP
return true;
#endif
//never use save files if we are in movie mode
if (isMovieMode) return true;
return fwrite(&val, 1, 1, fpMC->get_fp())?true:false;
}
void BackupDevice::writeByte(u32 addr, u8 val)
{
if (isMovieMode) return;
fpMC->fseek(addr, SEEK_SET);
fpMC->write8le(val);
}
void BackupDevice::writeWord(u32 addr, u16 val)
{
if (isMovieMode) return;
fpMC->fseek(addr, SEEK_SET);
fpMC->write16le(val);
}
void BackupDevice::writeLong(u32 addr, u32 val)
{
if (isMovieMode) return;
fpMC->fseek(addr, SEEK_SET);
fpMC->write32le(val);
}
bool BackupDevice::saveBuffer(u8 *data, u32 size)
{
fsize = size;
fwrite(data, 1, size, fpMC->get_fp());
ensure(size, fpMC);
return true;
}
void BackupDevice::movie_mode()
{
isMovieMode = true;
@ -195,16 +496,14 @@ void BackupDevice::movie_mode()
void BackupDevice::reset_hardware()
{
com = 0;
reset_command_state = false;
write_enable = FALSE;
write_protect = 0;
com = 0;
addr = addr_counter = 0;
motionInitState = MOTION_INIT_STATE_IDLE;
motionFlag = MOTION_FLAG_NONE;
state = DETECTING;
reset_command_state = false;
flushPending = false;
lazyFlushPending = false;
kUninitializedSaveDataValue = 0xFF;
@ -213,12 +512,8 @@ void BackupDevice::reset_hardware()
void BackupDevice::reset()
{
memset(&info, 0, sizeof(info));
reset_hardware();
resize(0);
data_autodetect.resize(0);
addr_size = 0;
loadfile();
//if the user has requested a manual choice for backup type, and we havent imported a raw save file, then apply it now
if(state == DETECTING && CommonSettings.manualBackupType != MC_TYPE_AUTODETECT)
@ -227,15 +522,15 @@ void BackupDevice::reset()
int savetype = save_types[CommonSettings.manualBackupType].media_type;
int savesize = save_types[CommonSettings.manualBackupType].size;
ensure((u32)savesize); //expand properly if necessary
resize(savesize); //truncate if necessary
addr_size = addr_size_for_old_save_type(savetype);
flush();
}
}
void BackupDevice::close_rom()
{
flush();
fpMC->fflush();
delete fpMC;
fpMC = NULL;
}
u8 BackupDevice::searchFileSaveType(u32 size)
@ -302,7 +597,6 @@ void BackupDevice::detect()
state = RUNNING;
data_autodetect.resize(0);
flush();
}
}
@ -312,14 +606,8 @@ void BackupDevice::checkReset()
{
//printf("MC : reset command\n");
//for a performance hack, save files are only flushed after each reset command
//(hopefully, after each page)
if(flushPending && (com == BM_CMD_WRITELOW || com == BM_CMD_WRITEHIGH))
{
flush();
flushPending = false;
lazyFlushPending = false;
}
if(com == BM_CMD_WRITELOW || com == BM_CMD_WRITEHIGH)
fpMC->fflush();
com = 0;
reset_command_state = false;
@ -376,30 +664,30 @@ u8 BackupDevice::data_command(u8 val, u8 PROCNUM)
addr |= val;
addr_counter++;
val = 0xFF;
if (addr_counter >= addr_size)
{
//why does tomb raider underworld access 0x180 and go clear through to 0x280?
//should this wrap around at 0 or at 0x100?
if(addr_size == 1) addr &= 0x1FF;
fpMC->fseek(addr, SEEK_SET);
}
}
else
{
//why does tomb raider underworld access 0x180 and go clear through to 0x280?
//should this wrap around at 0 or at 0x100?
if(addr_size == 1) addr &= 0x1FF;
//address is complete
ensure(addr+1);
ensure(addr);
if(com == BM_CMD_READLOW)
{
val = data[addr];
//flushPending = true; //is this a good idea? it may slow stuff down, but it is helpful for debugging
lazyFlushPending = true; //lets do this instead
//printf("MC%c: read from %08X, value %02Xh\n", PROCNUM?'7':'9', addr, val);
val = read();
//MCLOG("BACKUP: Read at 0x%08X, value 0x%02X\n", addr, val);
}
else
if(write_enable)
{
data[addr] = val;
flushPending = true;
//printf("MC%c: write to %08X, value %02Xh\n", PROCNUM?'7':'9', addr, val);
write(val);
//MCLOG("BACKUP: Write to %08X, value %02Xh\n", addr, val);
}
addr++;
}
}
@ -530,31 +818,47 @@ u8 BackupDevice::data_command(u8 val, u8 PROCNUM)
}
//guarantees that the data buffer has room enough for the specified number of bytes
void BackupDevice::ensure(u32 addr)
void BackupDevice::ensure(u32 addr, EMUFILE_FILE *fpOut)
{
ensure(addr,kUninitializedSaveDataValue);
ensure(addr, kUninitializedSaveDataValue, fpOut);
}
void BackupDevice::ensure(u32 addr, u8 val)
void BackupDevice::ensure(u32 addr, u8 val, EMUFILE_FILE *fpOut)
{
u32 size = data.size();
if(size<addr)
{
resize(addr, val);
}
}
if (!fpOut && (addr < fsize)) return;
void BackupDevice::resize(u32 size)
{
resize(size,kUninitializedSaveDataValue);
}
EMUFILE_FILE *fp = fpOut?fpOut:fpMC;
void BackupDevice::resize(u32 size, u8 val)
{
size_t old_size = data.size();
data.resize(size);
for(u32 i=old_size;i<size;i++)
data[i] = val;
#ifdef _DONT_SAVE_BACKUP
fp->fseek(fsize, SEEK_SET);
#endif
u32 padSize = pad_up_size(addr);
u32 size = padSize - fsize;
fsize = padSize;
#ifdef _DONT_SAVE_BACKUP
u8 *tmp = new u8[size];
memset(tmp, val, size);
fwrite(tmp, 1, size, fp->get_fp());
delete [] tmp;
//this is just for humans to read
fp->fprintf("|<--Snip above here to create a raw sav by excluding this DeSmuME savedata footer:");
//and now the actual footer
fp->write32le(addr); //the size of data that has actually been written
fp->write32le(padSize); //the size we padded it to
fp->write32le(info.type); //save memory type
fp->write32le(addr_size);
fp->write32le(info.size); //save memory size
fp->write32le((u32)0); //version number
fp->fprintf("%s", kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save
fp->fflush();
fp->fseek(addr, SEEK_SET);
#endif
}
u32 BackupDevice::addr_size_for_old_save_size(int bupmem_size)
@ -594,16 +898,16 @@ u32 BackupDevice::addr_size_for_old_save_type(int bupmem_type)
}
}
// TODO: this is required yet?
void BackupDevice::load_old_state(u32 addr_size, u8* data, u32 datasize)
{
state = RUNNING;
this->addr_size = addr_size;
resize(datasize);
memcpy(&this->data[0],data,datasize);
//state = RUNNING;
//this->addr_size = addr_size;
//resize(datasize);
//memcpy(&this->data[0],data,datasize);
//dump back out as a dsv, just to keep things sane
flush();
//flush();
}
//======================================================================= Tools
@ -847,13 +1151,7 @@ bool BackupDevice::load_no_gba(const char *fname, u32 force_size)
size = no_gba_fillLeft(size);
//printf("--- new size after fill %i byte(s)\n", size);
raw_applyUserSettings(size, (force_size > 0));
resize(size);
for (u32 tt = 0; tt < size; tt++)
data[tt] = out_buf[tt];
//dump back out as a dsv, just to keep things sane
flush();
printf("---- Loaded no$GBA save\n");
saveBuffer(out_buf, size);
if (in_buf) delete [] in_buf;
if (out_buf) delete [] out_buf;
@ -865,12 +1163,39 @@ bool BackupDevice::load_no_gba(const char *fname, u32 force_size)
if (in_buf) delete [] in_buf;
fclose(fsrc);
}
return false;
}
bool BackupDevice::no_gba_unpack(u8 *&buf, u32 &size)
{
if (!buf) return false;
u32 out_size = saveSizes[saveSizes_count - 1];
u8 *out_buf = new u8 [out_size];
if (out_buf)
{
if (no_gba_unpackSAV(buf, size, out_buf, out_size) == 0)
{
out_size = no_gba_savTrim(out_buf, out_size);
out_size = no_gba_fillLeft(out_size);
delete [] buf;
buf = out_buf;
size = out_size;
return true;
}
}
delete out_buf;
return false;
}
bool BackupDevice::save_no_gba(const char* fname)
{
std::vector<u8> data(fsize);
u32 pos = fpMC->ftell();
fread((char*)&data[0], 1, fsize, fpMC->get_fp());
fpMC->fseek(pos, SEEK_SET);
FILE* outf = fopen(fname,"wb");
if(!outf) return false;
u32 size = data.size();
@ -886,135 +1211,19 @@ bool BackupDevice::save_no_gba(const char* fname)
fputc(0xFF,outf);
}
fclose(outf);
return true;
}
//======================================================================= end
//=======================================================================
//======================================================================= no$GBA
void BackupDevice::loadfile()
{
//never use save files if we are in movie mode
if(isMovieMode) return;
if(filename.length() ==0) return; //No sense crashing if no filename supplied
#ifdef _DONT_LOAD_BACKUP
return;
#endif
EMUFILE_FILE* inf = new EMUFILE_FILE(filename.c_str(),"rb");
if(inf->fail())
{
delete inf;
//no dsv found; we need to try auto-importing a file with .sav extension
printf("DeSmuME .dsv save file not found. Trying to load an old raw .sav file.\n");
//change extension to sav
char tmp[MAX_PATH] = {0};
path.getpathnoext(path.BATTERY, tmp);
strcat(tmp, ".sav");
inf = new EMUFILE_FILE(tmp,"rb");
if(inf->fail())
{
delete inf;
printf("Missing save file %s\n",filename.c_str());
return;
}
delete inf;
if (!load_no_gba(tmp))
load_raw(tmp);
}
else
{
//scan for desmume save footer
const s32 cookieLen = (s32)strlen(kDesmumeSaveCookie);
char *sigbuf = new char[cookieLen];
inf->fseek(-cookieLen, SEEK_END);
inf->fread(sigbuf,cookieLen);
int cmp = memcmp(sigbuf,kDesmumeSaveCookie,cookieLen);
delete[] sigbuf;
if(cmp)
{
//maybe it is a misnamed raw save file. try loading it that way
printf("Not a DeSmuME .dsv save file. Trying to load as raw.\n");
delete inf;
if (!load_no_gba(filename.c_str()))
load_raw(filename.c_str());
return;
}
//desmume format
inf->fseek(-cookieLen, SEEK_END);
inf->fseek(-4, SEEK_CUR);
u32 version = 0xFFFFFFFF;
read32le(&version,inf);
if(version!=0) {
printf("Unknown save file format\n");
return;
}
inf->fseek(-24, SEEK_CUR);
read32le(&info.size,inf);
read32le(&info.padSize,inf);
read32le(&info.type,inf);
read32le(&info.addr_size,inf);
read32le(&info.mem_size,inf);
u32 left = 0;
if (CommonSettings.autodetectBackupMethod == 1)
{
if (advsc.isLoaded())
{
info.type = advsc.getSaveType();
if (info.type != 0xFF && info.type != 0xFE)
{
info.type++;
u32 adv_size = save_types[info.type].size;
if (info.size > adv_size)
info.size = adv_size;
else
if (info.size < adv_size)
{
left = adv_size - info.size;
info.size = adv_size;
}
}
}
}
//establish the save data
resize(info.size);
inf->fseek(0, SEEK_SET);
if(info.size>0)
inf->fread(&data[0],info.size - left); //read all the raw data we have
state = RUNNING;
addr_size = info.addr_size;
//none of the other fields are used right now
if (CommonSettings.autodetectBackupMethod != 1 && info.type == 0)
{
info.type = searchFileSaveType(info.size);
if (info.type == 0xFF) info.type = 0;
}
u32 ss = (info.padSize * 8) / 1024;
bool _Mbit = false;
if (ss >= 1024)
{
ss /= 1024;
_Mbit = true;
}
printf("Backup size: %u %cbit\n", ss, _Mbit?'M':'K');
delete inf;
}
}
bool BackupDevice::save_raw(const char* filename)
{
std::vector<u8> data(fsize);
u32 pos = fpMC->ftell();
fread((char*)&data[0], 1, fsize, fpMC->get_fp());
fpMC->fseek(pos, SEEK_SET);
FILE* outf = fopen(filename,"wb");
if(!outf) return false;
u32 size = data.size();
@ -1024,6 +1233,7 @@ bool BackupDevice::save_raw(const char* filename)
for(u32 i=size;i<padSize;i++)
fputc(kUninitializedSaveDataValue,outf);
fclose(outf);
return true;
}
@ -1031,71 +1241,17 @@ u32 BackupDevice::pad_up_size(u32 startSize)
{
u32 size = startSize;
u32 ctr=0;
while(ctr<saveSizes_count && size > saveSizes[ctr]) ctr++;
while ((ctr < saveSizes_count) && (size > saveSizes[ctr])) ctr++;
u32 padSize = saveSizes[ctr];
if(padSize == 0xFFFFFFFF)
{
printf("PANIC! Couldn't pad up save size. Refusing to pad.\n");
padSize = startSize;
}
return padSize;
}
void BackupDevice::lazy_flush()
{
if(flushPending || lazyFlushPending)
{
lazyFlushPending = flushPending = false;
flush();
}
}
void BackupDevice::flush()
{
//never use save files if we are in movie mode
if(isMovieMode) return;
#ifdef _DONT_SAVE_BACKUP
return;
#endif
if (filename.length() == 0) return;
EMUFILE* outf = new EMUFILE_FILE(filename.c_str(),"wb");
if(!outf->fail())
{
if(data.size()>0)
outf->fwrite(&data[0],data.size());
//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();
u32 padSize = pad_up_size(size);
for(u32 i=size;i<padSize;i++)
outf->fputc(kUninitializedSaveDataValue);
//this is just for humans to read
outf->fprintf("|<--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(info.type,outf); //save memory type
write32le(addr_size,outf);
write32le(info.size,outf); //save memory size
write32le(0,outf); //version number
outf->fprintf("%s", kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save
delete outf;
}
else
{
delete outf;
printf("Unable to open savefile %s\n", filename.c_str());
}
return padSize;
}
void BackupDevice::raw_applyUserSettings(u32& size, bool manual)
@ -1104,7 +1260,7 @@ void BackupDevice::raw_applyUserSettings(u32& size, bool manual)
if(CommonSettings.manualBackupType == MC_TYPE_AUTODETECT && !manual)
{
addr_size = addr_size_for_old_save_size(size);
resize(size);
ensure(size);
}
else
{
@ -1118,9 +1274,7 @@ void BackupDevice::raw_applyUserSettings(u32& size, bool manual)
int savesize = save_types[type].size;
addr_size = addr_size_for_old_save_type(savetype);
if((u32)savesize<size) size = savesize;
resize(savesize);
}
state = RUNNING;
}
@ -1161,11 +1315,14 @@ bool BackupDevice::load_raw(const char* filename, u32 force_size)
raw_applyUserSettings(size, (force_size > 0));
u8 *data = new u8[size];
fread(&data[0],1,size-left,inf);
fclose(inf);
saveBuffer(data, size);
delete data;
//dump back out as a dsv, just to keep things sane
flush();
return true;
}
@ -1184,7 +1341,7 @@ u32 BackupDevice::get_save_duc_size(const char* fname)
bool BackupDevice::load_duc(const char* filename, u32 force_size)
{
u32 size;
u32 size;
char id[16];
FILE* file = fopen(filename, "rb");
@ -1221,14 +1378,13 @@ bool BackupDevice::load_duc(const char* filename, u32 force_size)
raw_applyUserSettings(size, (force_size > 0));
ensure((u32)size);
u8 *data = new u8[size];
fread(&data[0],1,size-left,file);
fread(data, 1, (size-left), file);
fclose(file);
//choose
flush();
saveBuffer(data, size);
delete data;
return true;
@ -1259,11 +1415,8 @@ bool BackupDevice::load_movie(EMUFILE* is) {
is->fread((char*)&info.addr_size,4);
is->fread((char*)&info.mem_size,4);
//establish the save data
resize(info.size);
is->fseek(0, SEEK_SET);
if(info.size>0)
is->fread((char*)&data[0],info.size);
fpMC = (EMUFILE_FILE*)&is;
state = RUNNING;
addr_size = info.addr_size;

View File

@ -50,7 +50,6 @@
#define MC_SIZE_256MBITS 0x2000000
#define MC_SIZE_512MBITS 0x4000000
//This "backup device" represents a typical retail NDS save memory accessible via AUXSPI.
//It is managed as a core emulator service for historical reasons which are bad,
//and possible infrastructural simplification reasons which are good.
@ -59,15 +58,26 @@ class BackupDevice
{
public:
BackupDevice();
~BackupDevice();
//signals the save system that we are in MOVIE mode. doesnt load up a rom, and never saves it. initializes for that case.
void movie_mode();
void reset();
void close_rom();
void forceManualBackupType();
void reset_hardware();
std::string getFilename() { return filename; }
u8 readByte(u32 addr, const u8 init = 0xFF);
u16 readWord(u32 addr, const u16 init = 0xFFFF);
u32 readLong(u32 addr, const u32 init = 0xFFFFFFFF);
void writeByte(u32 addr, u8 val);
void writeWord(u32 addr, u16 val);
void writeLong(u32 addr, u32 val);
void flushBackup() { fpMC->fflush(); }
u8 searchFileSaveType(u32 size);
bool save_state(EMUFILE* os);
@ -76,7 +86,6 @@ public:
//commands from mmu
void reset_command() { reset_command_state = true; };
u8 data_command(u8, u8);
std::vector<u8> data;
//this info was saved before the last reset (used for savestate compatibility)
struct SavedInfo
@ -84,6 +93,9 @@ public:
u32 addr_size;
} savedInfo;
void ensure(u32 addr, EMUFILE_FILE *fpOut = NULL);
void ensure(u32 addr, u8 val, EMUFILE_FILE *fpOut = NULL);
//and these are used by old savestates
void load_old_state(u32 addr_size, u8* data, u32 datasize);
static u32 addr_size_for_old_save_size(int bupmem_size);
@ -93,6 +105,7 @@ public:
void raw_applyUserSettings(u32& size, bool manual = false);
bool load_duc(const char* filename, u32 force_size = 0);
bool no_gba_unpack(u8 *&buf, u32 &size);
bool load_no_gba(const char *fname, u32 force_size = 0);
bool save_no_gba(const char* fname);
bool load_raw(const char* filename, u32 force_size = 0);
@ -102,19 +115,6 @@ public:
u32 get_save_nogba_size(const char* filename);
u32 get_save_raw_size(const char* filename);
//call me once a second or so to lazy flush the save data
//here's the reason for this system: we want to dump save files when theyre READ
//so that we have a better idea earlier on how large they are. but it slows things down
//way too much if we flush whenever we read.
void lazy_flush();
void flush();
void setFlushPending() { flushPending = true; }
void setLazyFlushPending() { lazyFlushPending = true; }
void ensure(u32 addr);
void ensure(u32 addr, u8 val);
struct {
u32 size,padSize,type,addr_size,mem_size;
} info;
@ -124,18 +124,23 @@ public:
u32 importDataSize(const char *filename);
u32 importData(const char *filename, u32 force_size = 0);
bool exportData(const char *filename);
private:
void detect();
private:
EMUFILE_FILE *fpMC;
std::string filename;
u32 fsize;
int readFooter();
bool write(u8 val);
u8 read();
bool saveBuffer(u8 *data, u32 size);
bool write_enable; //is write enabled?
u8 write_protect;
bool write_enable;
bool reset_command_state;
u32 com; //persistent command actually handled
u32 addr_size, addr_counter;
u32 addr;
u8 write_protect;
std::vector<u8> data_autodetect;
enum STATE {
DETECTING = 0, RUNNING = 1
@ -154,15 +159,8 @@ private:
};
u8 motionInitState, motionFlag;
void loadfile();
bool _loadfile(const char *fname);
bool flushPending, lazyFlushPending;
void resize(u32 size);
void resize(u32 size, u8 val);
void checkReset();
void detect();
};