/* Copyright (C) 2006 thoduv Copyright (C) 2006-2007 Theo Berkau Copyright (C) 2008-2012 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the this software. If not, see . */ #include #include //#include "debug.h" #include "types.h" #include "mc.h" //#include "movie.h" #include "readwrite.h" #include "NDSSystem.h" #define FW_CMD_READ 0x03 #define FW_CMD_WRITEDISABLE 0x04 #define FW_CMD_READSTATUS 0x05 #define FW_CMD_WRITEENABLE 0x06 #define FW_CMD_PAGEWRITE 0x0A #define FW_CMD_READ_ID 0x9F #define BM_CMD_AUTODETECT 0xFF #define BM_CMD_WRITESTATUS 0x01 #define BM_CMD_WRITELOW 0x02 #define BM_CMD_READLOW 0x03 #define BM_CMD_WRITEDISABLE 0x04 #define BM_CMD_READSTATUS 0x05 #define BM_CMD_WRITEENABLE 0x06 #define BM_CMD_WRITEHIGH 0x0A #define BM_CMD_READHIGH 0x0B /* FLASH*/ #define COMM_PAGE_WRITE 0x0A #define COMM_PAGE_ERASE 0xDB #define COMM_SECTOR_ERASE 0xD8 #define COMM_CHIP_ERASE 0xC7 #define CARDFLASH_READ_BYTES_FAST 0x0B /* Not used*/ #define CARDFLASH_DEEP_POWDOWN 0xB9 /* Not used*/ #define CARDFLASH_WAKEUP 0xAB /* Not used*/ //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 //if it is 0xFF then the game starts with its sound and music at max, as presumably it is supposed to. //so in r3303 I finally changed it (no$ appears definitely to initialized to 0xFF) static const uint8_t kUninitializedSaveDataValue = 0xFF; static const char* kDesmumeSaveCookie = "|-DESMUME SAVE-|"; static const uint32_t saveSizes[] = {512, // 4k 8*1024, // 64k 32*1024, // 512k 64*1024, // 1Mbit 256*1024, // 2Mbit 512*1024, // 4Mbit 1024*1024, // 8Mbit 2048*1024, // 16Mbit 4096*1024, // 32Mbit 8192*1024, // 64Mbit 16384*1024, // 128Mbit 32768*1024, // 256Mbit 65536*1024, // 512Mbit 0xFFFFFFFF}; static const uint32_t saveSizes_count = ARRAY_SIZE(saveSizes); //the lookup table from user save types to save parameters const int save_types[][2] = { {MC_TYPE_AUTODETECT,1}, {MC_TYPE_EEPROM1,MC_SIZE_4KBITS}, {MC_TYPE_EEPROM2,MC_SIZE_64KBITS}, {MC_TYPE_EEPROM2,MC_SIZE_512KBITS}, {MC_TYPE_FRAM,MC_SIZE_256KBITS}, {MC_TYPE_FLASH,MC_SIZE_2MBITS}, {MC_TYPE_FLASH,MC_SIZE_4MBITS}, {MC_TYPE_FLASH,MC_SIZE_8MBITS}, {MC_TYPE_FLASH,MC_SIZE_16MBITS}, {MC_TYPE_FLASH,MC_SIZE_32MBITS}, {MC_TYPE_FLASH,MC_SIZE_64MBITS}, {MC_TYPE_FLASH,MC_SIZE_128MBITS}, {MC_TYPE_FLASH,MC_SIZE_256MBITS}, {MC_TYPE_FLASH,MC_SIZE_512MBITS} }; /*const char *save_names[] = { "EEPROM 4kbit", "EEPROM 64kbit", "EEPROM 512kbit", "FRAM 256kbit", "FLASH 2Mbit", "FLASH 4Mbit", "FLASH 8Mbit", "FLASH 16Mbit", "FLASH 32Mbit", "FLASH 64Mbit", "FLASH 128Mbit", "FLASH 256Mbit", "FLASH 512Mbit" };*/ //forces the currently selected backup type to be current //(can possibly be used to repair poorly chosen save types discovered late in gameplay i.e. pokemon gamers) /*void backup_forceManualBackupType() { MMU_new.backupDevice.forceManualBackupType(); }*/ /*void backup_setManualBackupType(int type) { CommonSettings.manualBackupType = type; }*/ void mc_init(memory_chip_t *mc, int type) { mc->com = 0; mc->addr = 0; mc->addr_shift = 0; mc->data.clear(); mc->size = 0; mc->write_enable = false; mc->writeable_buffer = false; mc->type = type; mc->autodetectsize = 0; switch(mc->type) { case MC_TYPE_EEPROM1: mc->addr_size = 1; break; case MC_TYPE_EEPROM2: case MC_TYPE_FRAM: mc->addr_size = 2; break; case MC_TYPE_FLASH: mc->addr_size = 3; break; default: break; } } uint8_t *mc_alloc(memory_chip_t *mc, uint32_t size) { /*uint8_t *buffer; buffer = new uint8_t[size]; memset(buffer,0,size);*/ //if (mc->data) delete [] mc->data; mc->data.clear(); //mc->data = buffer; //if(!buffer) { return NULL; } mc->data.resize(size, 0); mc->size = size; mc->writeable_buffer = true; //return buffer; return NULL; } void mc_free(memory_chip_t *mc) { //if(mc->data) delete[] mc->data; mc->data.clear(); mc_init(mc, 0); } void fw_reset_com(memory_chip_t *mc) { if(mc->com == FW_CMD_PAGEWRITE) { if (mc->fp) { fseek(mc->fp, 0, SEEK_SET); fwrite(&mc->data[0], mc->size, 1, mc->fp); } if (mc->isFirmware&&CommonSettings.UseExtFirmware) { // copy User Settings 1 to User Settings 0 area memcpy(&mc->data[0x3FE00], &mc->data[0x3FF00], 0x100); printf("Firmware: save config"); FILE *fp = fopen(mc->userfile, "wb"); if (fp) { if (fwrite(&mc->data[0x3FF00], 1, 0x100, fp) == 0x100) // User Settings { if (fwrite(&mc->data[0x0002A], 1, 0x1D6, fp) == 0x1D6) // WiFi Settings { if (fwrite(&mc->data[0x3FA00], 1, 0x300, fp) == 0x300) // WiFi AP Settings printf(" - done\n"); else printf(" - failed\n"); } } fclose(fp); } else printf(" - failed\n"); } mc->write_enable = false; } mc->com = 0; } uint8_t fw_transfer(memory_chip_t *mc, uint8_t data) { if(mc->com == FW_CMD_READ || mc->com == FW_CMD_PAGEWRITE) /* check if we are in a command that needs 3 bytes address */ { if(mc->addr_shift > 0) /* if we got a complete address */ { mc->addr_shift--; mc->addr |= data << (mc->addr_shift * 8); /* argument is a byte of address */ } else /* if we have received 3 bytes of address, proceed command */ { switch(mc->com) { case FW_CMD_READ: if(mc->addr < mc->size) /* check if we can read */ { data = mc->data[mc->addr]; /* return byte */ mc->addr++; /* then increment address */ } break; case FW_CMD_PAGEWRITE: if(mc->addr < mc->size) { mc->data[mc->addr] = data; /* write byte */ mc->addr++; } break; } } } else if(mc->com == FW_CMD_READ_ID) { switch(mc->addr) { //here is an ID string measured from an old ds fat: 62 16 00 (0x62=sanyo) //but we chose to use an ST from martin's ds fat string so programs might have a clue as to the firmware size: //20 40 12 case 0: data = 0x20; mc->addr=1; break; case 1: data = 0x40; //according to gbatek this is the device ID for the flash on someone's ds fat mc->addr=2; break; case 2: data = 0x12; mc->addr = 0; break; } } else if(mc->com == FW_CMD_READSTATUS) { return mc->write_enable ? 0x02 : 0x00; } else //finally, check if it's a new command { switch(data) { case 0: break; //nothing case FW_CMD_READ_ID: mc->addr = 0; mc->com = FW_CMD_READ_ID; break; case FW_CMD_READ: //read command mc->addr = 0; mc->addr_shift = 3; mc->com = FW_CMD_READ; break; case FW_CMD_WRITEENABLE: //enable writing if(mc->writeable_buffer) { mc->write_enable = true; } break; case FW_CMD_WRITEDISABLE: //disable writing mc->write_enable = false; break; case FW_CMD_PAGEWRITE: //write command if(mc->write_enable) { mc->addr = 0; mc->addr_shift = 3; mc->com = FW_CMD_PAGEWRITE; } else { data = 0; } break; case FW_CMD_READSTATUS: //status register command mc->com = FW_CMD_READSTATUS; break; default: printf("Unhandled FW command: %02X\n", data); break; } } return data; } /*bool BackupDevice::save_state(EMUFILE* os) { uint32_t version = 2; //v0 write32le(version,os); write32le(write_enable,os); write32le(com,os); write32le(addr_size,os); write32le(addr_counter,os); write32le((uint32_t)state,os); writebuffer(data,os); writebuffer(data_autodetect,os); //v1 write32le(addr,os); //v2 write8le(motionInitState,os); write8le(motionFlag,os); return true; }*/ bool BackupDevice::load_state(EMUFILE* is) { uint32_t version; if(read32le(&version,is)!=1) return false; //if(version>=0) { readbool(&write_enable,is); read32le(&com,is); read32le(&addr_size,is); read32le(&addr_counter,is); uint32_t temp; read32le(&temp,is); state = (STATE)temp; readbuffer(data,is); readbuffer(data_autodetect,is); } if(version>=1) read32le(&addr,is); if(version>=2) { read8le(&motionInitState,is); read8le(&motionFlag,is); } return true; } BackupDevice::BackupDevice() { //isMovieMode = false; reset(); } //due to unfortunate shortcomings in the emulator architecture, //at reset-time, we won't have a filename to the .dsv 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* fn) { //isMovieMode = false; this->filename = fn; reset(); } /*void BackupDevice::movie_mode() { isMovieMode = true; reset(); }*/ void BackupDevice::reset_hardware() { write_enable = false; com = 0; addr = addr_counter = 0; motionInitState = MOTION_INIT_STATE_IDLE; motionFlag = MOTION_FLAG_NONE; state = DETECTING; flushPending = false; lazyFlushPending = false; } 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) { state = RUNNING; int savetype = save_types[CommonSettings.manualBackupType][0]; int savesize = save_types[CommonSettings.manualBackupType][1]; ensure((uint32_t)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(); }*/ /*void BackupDevice::reset_command() { //printf("MC RESET\n"); //for a performance hack, save files are only flushed after each reset command //(hopefully, after each page) if(flushPending) { //flush(); flushPending = false; lazyFlushPending = false; } if(state == DETECTING && data_autodetect.size()>0) { //we can now safely detect the save address size uint32_t autodetect_size = data_autodetect.size(); printf("Autodetecting with autodetect_size=%d\n",autodetect_size); const uint8_t sm64_sig[] = {0x01,0x80,0x00,0x00}; if(autodetect_size == 4 && !memcmp(&data_autodetect[0],sm64_sig,4)) { addr_size = 2; } else //detect based on rules switch(autodetect_size) { case 0: case 1: printf("Catastrophic error while autodetecting save type.\nIt will need to be specified manually\n"); #ifdef _WINDOWS MessageBoxA(0,"Catastrophic Error Code: Camel;\nyour save type has not been autodetected correctly;\nplease report to developers",0,0); #endif addr_size = 1; //choose 1 just to keep the busted savefile from growing too big break; case 2: //the modern typical case for small eeproms addr_size = 1; break; case 3: //another modern typical case.. //but unfortunately we select this case for spider-man 3, when what it meant to do was //present the archaic 1+2 case //it seems that over the hedge does this also. addr_size = 2; break; case 4: //a modern typical case addr_size = 3; break; default: //the archaic case: write the address and then some modulo-4 number of bytes //why modulo 4? who knows. addr_size = autodetect_size & 3; break; } state = RUNNING; data_autodetect.resize(0); //flush(); } com = 0; }*/ /*uint8_t BackupDevice::data_command(uint8_t val, int cpu) { //printf("MC CMD: %02X\n",val); //motion: some guessing here... hope it doesn't break anything if(com == BM_CMD_READLOW && motionInitState == MOTION_INIT_STATE_RECEIVED_4_B && val == 0) { motionInitState = MOTION_INIT_STATE_IDLE; motionFlag |= MOTION_FLAG_ENABLED; //return 0x04; //return 0x04 to enable motion!!!!! return 0; //but we return 0 to disable it! since we don't emulate it anyway } //if the game firmly believes we have motion support, then ignore all motion commands, since theyre not emulated. if(motionFlag & MOTION_FLAG_SENSORMODE) { return 0; } if(com == BM_CMD_READLOW || com == BM_CMD_WRITELOW) { //handle data or address if(state == DETECTING) { if(com == BM_CMD_WRITELOW) { printf("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_counteraddr_size = addrSize; resize(datasize); memcpy(&this->data[0],Data,datasize); //dump back out as a dsv, just to keep things sane //flush(); } //======================================================================= no$GBA //======================================================================= //======================================================================= static int no_gba_unpackSAV(void *in_buf, uint32_t fsize, void *out_buf, uint32_t &size) { const char no_GBA_HEADER_ID[] = "NocashGbaBackupMediaSavDataFile"; const char no_GBA_HEADER_SRAM_ID[] = "SRAM"; uint8_t *src = (uint8_t *)in_buf; uint8_t *dst = (uint8_t *)out_buf; uint32_t src_pos = 0; uint32_t dst_pos = 0; uint8_t cc = 0; uint32_t size_unpacked = 0; //uint32_t size_packed = 0; uint32_t compressMethod = 0; if (fsize < 0x50) return 1; for (int i = 0; i < 0x1F; i++) { if (src[i] != no_GBA_HEADER_ID[i]) return 2; } if (src[0x1F] != 0x1A) return 2; for (int i = 0; i < 0x4; i++) { if (src[i+0x40] != no_GBA_HEADER_SRAM_ID[i]) return 2; } compressMethod = *((uint32_t*)(src+0x44)); if (compressMethod == 0) // unpacked { size_unpacked = *((uint32_t*)(src+0x48)); src_pos = 0x4C; for (uint32_t i = 0; i < size_unpacked; i++) { dst[dst_pos++] = src[src_pos++]; } size = dst_pos; return 0; } if (compressMethod == 1) // packed (method 1) { //size_packed = *((uint32_t*)(src+0x48)); size_unpacked = *((uint32_t*)(src+0x4C)); src_pos = 0x50; while (true) { cc = src[src_pos++]; if (cc == 0) { size = dst_pos; return 0; } if (cc == 0x80) { uint16_t tsize = *((uint16_t*)(src+src_pos+1)); for (int t = 0; t < tsize; t++) dst[dst_pos++] = src[src_pos]; src_pos += 3; continue; } if (cc > 0x80) // repeat { cc -= 0x80; for (int t = 0; t < cc; t++) dst[dst_pos++] = src[src_pos]; src_pos++; continue; } // copy for (int t = 0; t < cc; t++) dst[dst_pos++] = src[src_pos++]; } size = dst_pos; return 0; } return 200; } static uint32_t no_gba_savTrim(void *buf, uint32_t size) { uint32_t rows = size / 16; uint32_t pos = (size - 16); uint8_t *src = (uint8_t*)buf; for (unsigned int i = 0; i < rows; i++, pos -= 16) { if (src[pos] == 0xFF) { for (int t = 0; t < 16; t++) { if (src[pos+t] != 0xFF) return pos+16; } } else { return pos+16; } } return size; } static uint32_t no_gba_fillLeft(uint32_t size) { for (uint32_t i = 1; i < ARRAY_SIZE(save_types); i++) { if (size <= (uint32_t)save_types[i][1]) return size + (save_types[i][1] - size); } return size; } bool BackupDevice::load_no_gba(const char *fname) { FILE *fsrc = fopen(fname, "rb"); uint8_t *in_buf = NULL; uint8_t *out_buf = NULL; if (fsrc) { uint32_t fsize = 0; fseek(fsrc, 0, SEEK_END); fsize = ftell(fsrc); fseek(fsrc, 0, SEEK_SET); //printf("Open %s file (size %i bytes)\n", fname, fsize); in_buf = new uint8_t [fsize]; if (fread(in_buf, 1, fsize, fsrc) == fsize) { out_buf = new uint8_t [8 * 1024 * 1024 / 8]; uint32_t size = 0; memset(out_buf, 0xFF, 8 * 1024 * 1024 / 8); if (no_gba_unpackSAV(in_buf, fsize, out_buf, size) == 0) { //printf("New size %i byte(s)\n", size); size = no_gba_savTrim(out_buf, size); //printf("--- new size after trim %i byte(s)\n", size); size = no_gba_fillLeft(size); //printf("--- new size after fill %i byte(s)\n", size); raw_applyUserSettings(size); data.resize(size); for (uint32_t 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"); if (in_buf) delete [] in_buf; if (out_buf) delete [] out_buf; fclose(fsrc); return true; } if (out_buf) delete [] out_buf; } if (in_buf) delete [] in_buf; fclose(fsrc); } return false; } /*bool BackupDevice::save_no_gba(const char* fname) { FILE* outf = fopen(fname,"wb"); if(!outf) return false; uint32_t size = data.size(); uint32_t padSize = pad_up_size(size); if(data.size()>0) fwrite(&data[0],1,size,outf); for(uint32_t i=size;ifail()) { 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]; strcpy(tmp,filename.c_str()); tmp[strlen(tmp)-3] = 0; 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 int32_t cookieLen = (int32_t)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); uint32_t 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); //uint32_t left = 0; /*if (CommonSettings.autodetectBackupMethod == 1) { if (advsc.isLoaded()) { info.type = advsc.getSaveType(); if (info.type != 0xFF || info.type != 0xFE) { u32 adv_size = save_types[info.type+1][1]; 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); //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; } uint32_t ss = info.size * 8 / 1024; if (ss >= 1024) { ss /= 1024; printf("Backup size: %i Mbit\n", ss); } else printf("Backup size: %i Kbit\n", ss);*/ delete inf; } } /*bool BackupDevice::save_raw(const char* fn) { FILE* outf = fopen(fn,"wb"); if(!outf) return false; uint32_t size = data.size(); uint32_t padSize = pad_up_size(size); if(data.size()>0) fwrite(&data[0],1,size,outf); for(uint32_t i=size;i saveSizes[ctr]) ctr++; uint32_t 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; 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. uint32_t size = data.size(); uint32_t padSize = pad_up_size(size); for(uint32_t 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(0,outf); //save memory type write32le(addr_size,outf); write32le(0,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()); } }*/ void BackupDevice::raw_applyUserSettings(uint32_t& size, bool manual) { //respect the user's choice of backup memory type if(CommonSettings.manualBackupType == MC_TYPE_AUTODETECT && !manual) { addr_size = addr_size_for_old_save_size(size); resize(size); } else { uint32_t type = CommonSettings.manualBackupType; /*if (manual) { uint32_t res = searchFileSaveType(size); if (res != 0xFF) type = (res + 1); // +1 - skip autodetect }*/ int savetype = save_types[type][0]; int savesize = save_types[type][1]; addr_size = addr_size_for_old_save_type(savetype); if((uint32_t)savesize 0) { if (size > force_size) size = force_size; else if (size < force_size) { left = force_size - size; size = force_size; } } fseek(inf, 0, SEEK_SET); raw_applyUserSettings(size, force_size > 0); fread(&data[0],1,size - left,inf); fclose(inf); //dump back out as a dsv, just to keep things sane //flush(); return true; } /*bool BackupDevice::load_duc(const char* fn) { uint32_t size; char id[16]; FILE* file = fopen(fn, "rb"); if(file == NULL) return false; fseek(file, 0, SEEK_END); size = (uint32_t)ftell(file) - 500; fseek(file, 0, SEEK_SET); // Make sure we really have the right file fread((void *)id, sizeof(char), 16, file); if (memcmp(id, "ARDS000000000001", 16) != 0) { printf("Not recognized as a valid DUC file\n"); fclose(file); return false; } // Skip the rest of the header since we don't need it fseek(file, 500, SEEK_SET); raw_applyUserSettings(size); ensure((uint32_t)size); fread(&data[0],1,size,file); fclose(file); //choose //flush(); return true; }*/ /*bool BackupDevice::load_movie(EMUFILE* is) { const int32_t cookieLen = (int32_t)strlen(kDesmumeSaveCookie); is->fseek(-cookieLen, SEEK_END); is->fseek(-4, SEEK_CUR); uint32_t version = 0xFFFFFFFF; is->fread((char*)&version,4); if(version!=0) { printf("Unknown save file format\n"); return false; } is->fseek(-24, SEEK_CUR); struct{ uint32_t size,padSize,type,addr_size,mem_size; }info; is->fread((char*)&info.size,4); is->fread((char*)&info.padSize,4); is->fread((char*)&info.type,4); is->fread((char*)&info.addr_size,4); is->fread((char*)&info.mem_size,4); //establish the save data data.resize(info.size); is->fseek(0, SEEK_SET); if(info.size>0) is->fread((char*)&data[0],info.size); state = RUNNING; addr_size = info.addr_size; //none of the other fields are used right now return true; }*/ /*void BackupDevice::forceManualBackupType() { addr_size = addr_size_for_old_save_size(save_types[CommonSettings.manualBackupType][1]); state = RUNNING; }*/