diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index a65a9146d..57e0ab10b 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -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; diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index dc5b80a35..ce0e05ddb 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -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; diff --git a/desmume/src/addons/slot1_retail_nand.cpp b/desmume/src/addons/slot1_retail_nand.cpp index 065ba2876..11310f22d 100644 --- a/desmume/src/addons/slot1_retail_nand.cpp +++ b/desmume/src/addons/slot1_retail_nand.cpp @@ -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; } diff --git a/desmume/src/mc.cpp b/desmume/src/mc.cpp index 7f2e865b6..e09dade37 100644 --- a/desmume/src/mc.cpp +++ b/desmume/src/mc.cpp @@ -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 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 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(sizefseek(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 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 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 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;ifputc(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 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; diff --git a/desmume/src/mc.h b/desmume/src/mc.h index 4c02cc3ab..63ccbfa7c 100644 --- a/desmume/src/mc.h +++ b/desmume/src/mc.h @@ -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 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 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(); };