void BackupDevice::ensure(u32 addr, u8 val, EMUFILE_FILE *fpOut) { if (!fpOut && (addr < fsize)) return; EMUFILE_FILE *fp = fpOut?fpOut:fpMC; #ifndef _DONT_SAVE_BACKUP fp->fseek(fsize, SEEK_SET); #endif u32 padSize = pad_up_size(addr); u32 size = padSize - fsize; info.padSize = info.size = fsize = padSize; int type = searchFileSaveType(fsize); if (type != 0xFF) info.type = (type + 1); #ifndef _DONT_SAVE_BACKUP if (size > 0) { 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(DESMUME_BACKUP_FOOTER_TXT); //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(); //this is a HORRIBLE IDEA. //leave the FP positioned to write the final byte //this is a HACK to make the basic read/write byte operation work when it calls ensure(). //IDEALLY, no assumptions about the file pointer can be made. //but someone (actually, not really) so very carefully profiled the save IO code and discovered that not fseeking for every byte read/write was a great optimization. //so, now all this code is depending/assuming on the FP being kept in a precise position, and I dont think its smart to change the main user of this assumption to paper over this bug by making it fseek before read/write, while leaving other unknown assuming clients intact fpMC->fseek(addr-1, SEEK_SET); #endif }
void BackupDevice::ensure(u32 addr, u8 val, EMUFILE_FILE *fpOut) { if (!fpOut && (addr < fsize)) return; EMUFILE_FILE *fp = fpOut?fpOut:fpMC; #ifndef _DONT_SAVE_BACKUP fp->fseek(fsize, SEEK_SET); #endif u32 padSize = pad_up_size(addr); u32 size = padSize - fsize; info.padSize = info.size = fsize = padSize; int type = searchFileSaveType(fsize); if (type != 0xFF) info.type = (type + 1); #ifndef _DONT_SAVE_BACKUP if (size > 0) { 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(DESMUME_BACKUP_FOOTER_TXT); //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 }
FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext, int index, const char** extensions) { FILE *ipsfile=0; FCEUFILE *fceufp=0; bool read = (std::string)mode == "rb"; bool write = (std::string)mode == "wb"; if((read&&write) || (!read&&!write)) { FCEU_PrintError("invalid file open mode specified (only wb and rb are supported)"); return 0; } std::string archive,fname,fileToOpen; FCEU_SplitArchiveFilename(path,archive,fname,fileToOpen); //try to setup the ips file if(ipsfn && read) ipsfile=FCEUD_UTF8fopen(ipsfn,"rb"); if(read) { ArchiveScanRecord asr = FCEUD_ScanArchive(fileToOpen); asr.files.FilterByExtension(extensions); if(!asr.isArchive()) { //if the archive contained no files, try to open it the old fashioned way EMUFILE_FILE* fp = FCEUD_UTF8_fstream(fileToOpen,mode); if(!fp || (fp->get_fp() == NULL)) { return 0; } //try to read a zip file { fceufp = TryUnzip(fileToOpen); if(fceufp) { delete fp; fceufp->filename = fileToOpen; fceufp->logicalPath = fileToOpen; fceufp->fullFilename = fileToOpen; fceufp->archiveIndex = -1; goto applyips; } } //try to read a gzipped file { uint32 magic; magic = fp->fgetc(); magic|=fp->fgetc()<<8; magic|=fp->fgetc()<<16; fp->fseek(0,SEEK_SET); if(magic==0x088b1f) { // maybe gzip... void* gzfile = gzopen(fileToOpen.c_str(),"rb"); if(gzfile) { delete fp; int size; for(size=0; gzgetc(gzfile) != EOF; size++) {} EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(size); gzseek(gzfile,0,SEEK_SET); gzread(gzfile,ms->buf(),size); gzclose(gzfile); fceufp = new FCEUFILE(); fceufp->filename = fileToOpen; fceufp->logicalPath = fileToOpen; fceufp->fullFilename = fileToOpen; fceufp->archiveIndex = -1; fceufp->stream = ms; fceufp->size = size; goto applyips; } } } //open a plain old file fceufp = new FCEUFILE(); fceufp->filename = fileToOpen; fceufp->logicalPath = fileToOpen; fceufp->fullFilename = fileToOpen; fceufp->archiveIndex = -1; fceufp->stream = fp; FCEU_fseek(fceufp,0,SEEK_END); fceufp->size = FCEU_ftell(fceufp); FCEU_fseek(fceufp,0,SEEK_SET); goto applyips; } else { //open an archive file if(archive == "") if(index != -1) fceufp = FCEUD_OpenArchiveIndex(asr, fileToOpen, index); else fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0); else fceufp = FCEUD_OpenArchive(asr, archive, &fname); if(!fceufp) return 0; FileBaseInfo fbi = DetermineFileBase(fileToOpen); fceufp->logicalPath = fbi.filebasedirectory + fceufp->filename; goto applyips; } applyips: //try to open the ips file if(!ipsfile && !ipsfn) ipsfile=FCEUD_UTF8fopen(FCEU_MakeIpsFilename(DetermineFileBase(fceufp->logicalPath.c_str())),"rb"); ApplyIPS(ipsfile,fceufp); return fceufp; } return 0; }
BackupDevice::BackupDevice() { size_t elements_read, elements_written; fpMC = NULL; fsize = 0; addr_size = 0; isMovieMode = false; //default for most games; will be altered where appropriate //usually 0xFF, but occasionally others. If these exceptions could be related to a particular backup memory type, that would be helpful. //at first we assumed it would be 0x00, but baby pals proved 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 // if it is 0xFF then the game starts with its sound and music at max, as presumably it is supposed to. // so in r3303 we finally changed it (no$ appears definitely to initialize to 0xFF) uninitializedValue = 0xFF; 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"; 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]; elements_read = fread(data, 1, sz, in->get_fp()); elements_written = fwrite(data, 1, sz, out->get_fp()); if (elements_read != sz || elements_written != sz) printf("Possibly incomplete data read/write.\n"); 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("Converted from no$gba save.\n"); else sz = trim(buf, sz); if (fwrite(buf, 1, sz, fpOut->get_fp()) == sz) { u8 res = searchFileSaveType(sz); if (res != 0xFF) { info.type = (res + 1); addr_size = info.addr_size = save_types[info.type].addr_size; info.size = fsize = 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 -= (strlen(kDesmumeSaveCookie) + strlen(DESMUME_BACKUP_FOOTER_TXT) + 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; info.padSize = fsize; //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; } if (ss > 0) printf("Backup size: %u %cbit\n", ss, _Mbit?'M':'K'); } state = (fsize > 0)?RUNNING:DETECTING; reset(); }
FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, const char * mode, const char *ext, int index, const char** extensions) { FILE *ipsfile=0; FCEUFILE *fceufp=0; bool read = (std::string)mode == "rb"; bool write = (std::string)mode == "wb"; if((read&&write) || (!read&&!write)) { FCEU_PrintError("invalid file open mode specified (only wb and rb are supported)"); return 0; } std::string archive,fname,fileToOpen; FCEU_SplitArchiveFilename(path,archive,fname,fileToOpen); //try to setup the ips file if(ipsfn && read) ipsfile=fopen(ipsfn,"rb"); if(read) { ArchiveScanRecord asr = FCEUD_ScanArchive(fileToOpen); asr.files.FilterByExtension(extensions); if(!asr.isArchive()) { //if the archive contained no files, try to open it the old fashioned way EMUFILE_FILE* fp = new EMUFILE_FILE(fileToOpen,mode); if(!fp || (fp->get_fp() == NULL)) { return 0; } //open a plain old file fceufp = new FCEUFILE(); fceufp->filename = fileToOpen; fceufp->logicalPath = fileToOpen; fceufp->fullFilename = fileToOpen; fceufp->archiveIndex = -1; fceufp->stream = fp; FCEU_fseek(fceufp,0,SEEK_END); fceufp->size = fceufp->stream->ftell(); FCEU_fseek(fceufp,0,SEEK_SET); goto applyips; } else { //open an archive file if(archive == "") if(index != -1) fceufp = FCEUD_OpenArchiveIndex(asr, fileToOpen, index); else fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0); else fceufp = FCEUD_OpenArchive(asr, archive, &fname); if(!fceufp) return 0; FileBaseInfo fbi = DetermineFileBase(fileToOpen.c_str()); fceufp->logicalPath = fbi.filebasedirectory + fceufp->filename; goto applyips; } applyips: return fceufp; } return 0; }
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 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("Converted from no$gba save.\n"); else sz = trim(buf, sz); if (fwrite(buf, 1, sz, fpOut->get_fp()) == sz) { u8 res = searchFileSaveType(sz); if (res != 0xFF) { info.type = (res + 1); addr_size = info.addr_size = save_types[info.type].addr_size; info.size = fsize = 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 -= (strlen(kDesmumeSaveCookie) + strlen(DESMUME_BACKUP_FOOTER_TXT) + 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; info.padSize = fsize; //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; } if (ss > 0) printf("Backup size: %u %cbit\n", ss, _Mbit?'M':'K'); } state = (fsize > 0)?RUNNING:DETECTING; reset(); }