diff --git a/desmume/sav.txt b/desmume/dsv.txt similarity index 89% rename from desmume/sav.txt rename to desmume/dsv.txt index 76482a0f2..2f5b63b8f 100644 --- a/desmume/sav.txt +++ b/desmume/dsv.txt @@ -1,4 +1,3 @@ -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, @@ -9,6 +8,9 @@ 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. +Additionally, the footer makes it easier to analyze save files, because the +game's data will be at the correct addresses starting at offset 0x0000. + 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: diff --git a/desmume/src/MMU.cpp b/desmume/src/MMU.cpp index 784a57744..743571f9d 100644 --- a/desmume/src/MMU.cpp +++ b/desmume/src/MMU.cpp @@ -86,17 +86,6 @@ static u64 isqrt (u64 x) { return root; } - -static const int save_types[7][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} -}; - u16 partie = 1; u32 _MMU_MAIN_MEM_MASK = 0x3FFFFF; @@ -3136,7 +3125,7 @@ void FASTCALL _MMU_ARM7_write16(u32 adr, u16 val) if ( reset_firmware) { // reset fw device communication - mc_reset_com(&MMU.fw); + fw_reset_com(&MMU.fw); } SPI_CNT = val; @@ -4045,13 +4034,6 @@ 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); -} - //////////////////////////////////////////////////////////// //function pointer handlers for gdb stub stuff diff --git a/desmume/src/MMU.h b/desmume/src/MMU.h index a147ea934..d005898c3 100644 --- a/desmume/src/MMU.h +++ b/desmume/src/MMU.h @@ -159,7 +159,7 @@ struct armcpu_memory_iface { }; -void mmu_select_savetype(int type, int *bmemtype, u32 *bmemsize); +void mmu_select_savetype(int type); void MMU_Init(void); void MMU_DeInit(void); diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 91ff8ea23..ff9069a31 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -829,10 +829,8 @@ int NDS_LoadROM( const char *filename, int bmtype, u32 bmsize, memset(buf, 0, MAX_PATH); strcpy(buf, pathFilenameToROMwithoutExt); - strcat(buf, ".sav"); // DeSmuME memory card :) + strcat(buf, ".dsv"); // DeSmuME memory card :) - //mc_realloc(&MMU.bupmem, bmtype, bmsize); - //mc_load_file(&MMU.bupmem, buf); MMU_new.backupDevice.load_rom(buf); memset(buf, 0, MAX_PATH); @@ -849,32 +847,6 @@ int NDS_LoadROM( const char *filename, int bmtype, u32 bmsize, return ret; } -void MovieSRAM() -{ - int bmtype = MMU.bupmem.type; - u32 bmsize = MMU.bupmem.size; - - char buf[MAX_PATH]; - - memset(buf, 0, MAX_PATH); - strcpy(buf, pathFilenameToROMwithoutExt); - strcat(buf, ".sav"); // DeSmuME memory card :) - - if(movieMode != MOVIEMODE_INACTIVE) { - strcat(buf, "movie"); - } - - if (MMU.bupmem.fp) - { - fclose(MMU.bupmem.fp); - MMU.bupmem.fp = NULL; - } - - - mc_realloc(&MMU.bupmem, bmtype, bmsize); - mc_load_file(&MMU.bupmem, buf); -} - void NDS_FreeROM(void) { if (MMU.CART_ROM != MMU.UNUSED_RAM) @@ -1091,10 +1063,23 @@ int NDS_ImportSave(const char *filename) if (memcmp(filename+strlen(filename)-4, ".duc", 4) == 0) return MMU_new.backupDevice.load_duc(filename); + else + return MMU_new.backupDevice.load_raw(filename); return 0; } +bool NDS_ExportSave(const char *filename) +{ + if (strlen(filename) < 4) + return false; + + if (memcmp(filename+strlen(filename)-4, ".sav", 4) == 0) + return MMU_new.backupDevice.save_raw(filename); + + return false; +} + static int WritePNGChunk(FILE *fp, uint32 size, const char *type, const uint8 *data) { uint32 crc; diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index c087c47f4..d8cfe15f9 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -281,6 +281,7 @@ int NDS_LoadROM(const char *filename, int bmtype, u32 bmsize, void NDS_FreeROM(void); void NDS_Reset(BOOL resetBySaveState = FALSE); int NDS_ImportSave(const char *filename); +bool NDS_ExportSave(const char *filename); int NDS_WriteBMP(const char *filename); int NDS_LoadFirmware(const char *filename); @@ -357,6 +358,7 @@ extern struct TCommonSettings { , spuInterpolationMode(SPUInterpolation_Linear) , spuAdpcmCache(false) , gfx3d_flushMode(0) + , manualBackupType(0) { strcpy(ARM9BIOS, "biosnds9.bin"); strcpy(ARM7BIOS, "biosnds7.bin"); @@ -382,6 +384,10 @@ extern struct TCommonSettings { //this is a temporary hack until we straighten out the flushing logic and/or gxfifo int gfx3d_flushMode; + + //this is the user's choice of manual backup type, for cases when the autodetection can't be trusted + int manualBackupType; + } CommonSettings; extern char ROMserial[20]; diff --git a/desmume/src/mc.cpp b/desmume/src/mc.cpp index 0477db04a..83cd8702a 100644 --- a/desmume/src/mc.cpp +++ b/desmume/src/mc.cpp @@ -26,6 +26,12 @@ #include "mc.h" #include "movie.h" #include "readwrite.h" +#include "NDSSystem.h" + +//temporary hack until we have better error reporting facilities +#ifdef _MSC_VER +#include +#endif #define FW_CMD_READ 0x3 #define FW_CMD_WRITEDISABLE 0x4 @@ -58,9 +64,24 @@ 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[] = {512,8*1024,32*1024,64*1024,256*1024,512*1024,0xFFFFFFFF}; static const u32 saveSizes_count = ARRAY_SIZE(saveSizes); +//the lookup table from user save types to save parameters +static const int save_types[7][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} +}; + +void backup_setManualBackupType(int type) +{ + CommonSettings.manualBackupType = type; +} void mc_init(memory_chip_t *mc, int type) { @@ -110,215 +131,12 @@ void mc_free(memory_chip_t *mc) mc_init(mc, 0); } -void mc_reset_com(memory_chip_t *mc) +void fw_reset_com(memory_chip_t *mc) { - size_t elems_written = 0; - - if (mc->type == MC_TYPE_AUTODETECT && mc->com == BM_CMD_AUTODETECT) - { - u32 addr, size; - - LOG("autodetectsize = %d\n",mc->autodetectsize); - - if (mc->autodetectsize == (32768+2)) - { - // FRAM - addr = (mc->autodetectbuf[0] << 8) | mc->autodetectbuf[1]; - mc->type = MC_TYPE_FRAM; - mc->size = MC_SIZE_256KBITS; - } - else if (mc->autodetectsize == (512+3)) - { - // Flash 4Mbit - addr = (mc->autodetectbuf[0] << 16) | - (mc->autodetectbuf[1] << 8) | - mc->autodetectbuf[2]; - mc->type = MC_TYPE_FLASH; - mc->size = MC_SIZE_4MBITS; - } - else if (mc->autodetectsize == (256+3)) - { - // Flash 2Mbit - addr = (mc->autodetectbuf[0] << 16) | - (mc->autodetectbuf[1] << 8) | - mc->autodetectbuf[2]; - mc->type = MC_TYPE_FLASH; - mc->size = MC_SIZE_2MBITS; - } - else if ((mc->autodetectsize == (128+2)) || (mc->autodetectsize == (64+2)) || (mc->autodetectsize == (40+2))) - { - // 512 Kbit EEPROM - addr = (mc->autodetectbuf[0] << 8) | mc->autodetectbuf[1]; - mc->type = MC_TYPE_EEPROM2; - mc->size = MC_SIZE_512KBITS; - } - else if ((mc->autodetectsize == (32+2)) || (mc->autodetectsize == (16+2))) - { - // 64 Kbit EEPROM - addr = (mc->autodetectbuf[0] << 8) | mc->autodetectbuf[1]; - mc->type = MC_TYPE_EEPROM2; - mc->size = MC_SIZE_64KBITS; - } - else if (mc->autodetectsize == (16+1)) - { - // 4 Kbit EEPROM - addr = mc->autodetectbuf[0]; - mc->type = MC_TYPE_EEPROM1; - mc->size = MC_SIZE_4KBITS; - } - else - { - /* - // Assume it's a Flash non-page write - LOG("Flash detected(guessed). autodetectsize = %d\n", mc->autodetectsize); - addr = (mc->autodetectbuf[0] << 16) | - (mc->autodetectbuf[1] << 8) | - mc->autodetectbuf[2]; - mc->type = MC_TYPE_FLASH; - mc->size = MC_SIZE_2MBITS; - */ - // 64 Kbit EEPROM - addr = (mc->autodetectbuf[0] << 8) | mc->autodetectbuf[1]; - mc->type = MC_TYPE_EEPROM2; - mc->size = MC_SIZE_64KBITS; - } - - size = mc->autodetectsize; - mc_realloc(mc, mc->type, mc->size); - memcpy(mc->data+addr, mc->autodetectbuf+mc->addr_size, size-mc->addr_size); - mc->autodetectsize = 0; - mc->write_enable = FALSE; - - // Generate file - if ((mc->fp = fopen(mc->filename, "wb+")) != NULL) - elems_written += fwrite((void *)mc->data, 1, mc->size, mc->fp); - } - else if ((mc->com == BM_CMD_WRITELOW) || (mc->com == FW_CMD_PAGEWRITE)) - { - if(!mc->fp) - mc->fp = fopen(mc->filename, "wb+"); - - if (mc->fp) - { - fseek(mc->fp, 0, SEEK_SET); - elems_written += fwrite((void *)mc->data, 1, mc->size, mc->fp); // FIXME - } - // FIXME: desmume silently ignores not having opened save-file - mc->write_enable = FALSE; - } - + //not supporting writing back to the firmware right now, so nothing to be done here mc->com = 0; } -void mc_realloc(memory_chip_t *mc, int type, u32 size) -{ - if(mc->data) delete[] mc->data; - mc_init(mc, type); - mc_alloc(mc, size); -} - -void mc_load_file(memory_chip_t *mc, const char* filename) -{ - long size; - int type = -1; - FILE* file; - size_t elems_read; - - if(movieMode != MOVIEMODE_INACTIVE) { - mc->filename = strdup(filename); - return; - } - else - file = fopen(filename, "rb+"); - - if(file == NULL) - { - mc->filename = strdup(filename); - return; - } - - fseek(file, 0, SEEK_END); - size = ftell(file); - fseek(file, 0, SEEK_SET); - - if (mc->type == MC_TYPE_AUTODETECT) - { - if (size == MC_SIZE_4KBITS) - type = MC_TYPE_EEPROM1; - else if (size == MC_SIZE_64KBITS) - type = MC_TYPE_EEPROM2; - else if (size == MC_SIZE_256KBITS) - type = MC_TYPE_FRAM; - else if (size == MC_SIZE_512KBITS) - type = MC_TYPE_EEPROM2; - else if (size >= MC_SIZE_2MBITS) - type = MC_TYPE_FLASH; - else if (size >= MC_SIZE_4MBITS) - type = MC_TYPE_FLASH; - - if (type != -1) - mc_realloc(mc, type, size); - } - - if ((u32)size > mc->size) - size = mc->size; - elems_read = fread (mc->data, 1, size, file); - mc->fp = file; -} - -int mc_load_duc(memory_chip_t *mc, const char* filename) -{ - long size; - int type = -1; - char id[16]; - FILE* file = fopen(filename, "rb"); - size_t elems_read = 0; - if(file == NULL) - return 0; - - fseek(file, 0, SEEK_END); - size = ftell(file) - 500; - fseek(file, 0, SEEK_SET); - - // Make sure we really have the right file - elems_read += fread((void *)id, sizeof(char), 16, file); - - if (memcmp(id, "ARDS000000000001", 16) != 0) - { - fclose(file); - return 0; - } - - // Alright, it's time to load the file - if (mc->type == MC_TYPE_AUTODETECT) - { - if (size == MC_SIZE_4KBITS) - type = MC_TYPE_EEPROM1; - else if (size == MC_SIZE_64KBITS) - type = MC_TYPE_EEPROM2; - else if (size == MC_SIZE_256KBITS) - type = MC_TYPE_FRAM; - else if (size == MC_SIZE_512KBITS) - type = MC_TYPE_EEPROM2; - else if (size >= MC_SIZE_2MBITS) - type = MC_TYPE_FLASH; - else if (size >= MC_SIZE_4MBITS) - type = MC_TYPE_FLASH; - - if (type != -1) - mc_realloc(mc, type, size); - } - - if ((u32)size > mc->size) - size = mc->size; - // Skip the rest of the header since we don't need it - fseek(file, 500, SEEK_SET); - elems_read += fread (mc->data, 1, size, file); - fclose(file); - - return 1; -} - u8 fw_transfer(memory_chip_t *mc, u8 data) { if(mc->com == FW_CMD_READ || mc->com == FW_CMD_PAGEWRITE) /* check if we are in a command that needs 3 bytes address */ @@ -390,7 +208,7 @@ u8 fw_transfer(memory_chip_t *mc, u8 data) break; default: - LOG("Unhandled FW command: %02X\n", data); + printf("Unhandled FW command: %02X\n", data); break; } } @@ -398,137 +216,10 @@ u8 fw_transfer(memory_chip_t *mc, u8 data) return data; } -u8 bm_transfer(memory_chip_t *mc, u8 data) -{ - if(mc->com == BM_CMD_READLOW || mc->com == BM_CMD_WRITELOW) /* check if we are in a command that needs multiple byte 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 all bytes of address, proceed command */ - { - switch(mc->com) - { - case BM_CMD_READLOW: - if(mc->addr < mc->size) /* check if we can read */ - { - //LOG("Read Backup Memory addr %08X(%02X)\n", mc->addr, mc->data[mc->addr]); - data = mc->data[mc->addr]; /* return byte */ - mc->addr++; /* then increment address */ - } - break; - - case BM_CMD_WRITELOW: - if(mc->addr < mc->size) - { - //LOG("Write Backup Memory addr %08X with %02X\n", mc->addr, data); - mc->data[mc->addr] = data; /* write byte */ - mc->addr++; - } - break; - } - - } - } - else if(mc->com == BM_CMD_AUTODETECT) - { - // Store everything in a temporary - mc->autodetectbuf[mc->autodetectsize] = data; - mc->autodetectsize++; - return 0; - } - else if(mc->com == BM_CMD_READSTATUS) - { - //LOG("Backup Memory Read Status: %02X\n", mc->write_enable << 1); - return (mc->write_enable << 1); - } - else /* finally, check if it's a new command */ - { - switch(data) - { - case 0: break; /* nothing */ - - case BM_CMD_WRITELOW: /* write command */ - if(mc->write_enable) - { - if(mc->type == MC_TYPE_AUTODETECT) - { - mc->com = BM_CMD_AUTODETECT; - break; - } - - mc->addr = 0; - mc->addr_shift = mc->addr_size; - mc->com = BM_CMD_WRITELOW; - } - else { data = 0; } - break; - - case BM_CMD_READLOW: /* read command */ - mc->addr = 0; - mc->addr_shift = mc->addr_size; - mc->com = BM_CMD_READLOW; - break; - - case BM_CMD_WRITEDISABLE: /* disable writing */ - mc->write_enable = FALSE; - break; - - case BM_CMD_READSTATUS: /* status register command */ - mc->com = BM_CMD_READSTATUS; - break; - - case BM_CMD_WRITEENABLE: /* enable writing */ - if(mc->writeable_buffer) { mc->write_enable = TRUE; } - break; - - case BM_CMD_WRITEHIGH: /* write command that's only available on ST M95040-W that I know of */ - if(mc->write_enable) - { - if(mc->type == MC_TYPE_AUTODETECT) - { - mc->com = BM_CMD_AUTODETECT; - break; - } - - if (mc->type == MC_TYPE_EEPROM1) - mc->addr = 0x100; - else - mc->addr = 0; - mc->addr_shift = mc->addr_size; - mc->com = BM_CMD_WRITELOW; - } - else { data = 0; } - break; - - case BM_CMD_READHIGH: /* read command that's only available on ST M95040-W that I know of */ - if (mc->type == MC_TYPE_EEPROM1) - mc->addr = 0x100; - else - mc->addr = 0; - mc->addr_shift = mc->addr_size; - mc->com = BM_CMD_READLOW; - - break; - - default: - LOG("TRANSFER: Unhandled Backup Memory command: %02X\n", data); - break; - } - } - - 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); @@ -544,11 +235,6 @@ bool BackupDevice::load_state(std::istream* is) int version; if(read32le(&version,is)!=1) return false; if(version==0) { -#if 0 - u32 size, type; - read32le(&size,is); - read32le(&type,is); -#endif read32le(&write_enable,is); read32le(&com,is); read32le(&addr_size,is); @@ -562,26 +248,57 @@ bool BackupDevice::load_state(std::istream* is) return true; } +BackupDevice::BackupDevice() +{ +} + //due to unfortunate shortcomings in the emulator architecture, -//at reset-time, we won't have a filename to the .sav file. +//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* filename) { + isMovieMode = false; this->filename = filename; reset(); } +void BackupDevice::movie_mode() +{ + isMovieMode = true; + reset(); +} + void BackupDevice::reset() { - state = DETECTING; + com = 0; + addr = addr_counter = 0; + flushPending = false; + lazyFlushPending = false; data.resize(0); data_autodetect.resize(0); + + state = DETECTING; + addr_size = 0; loadfile(); - flushPending = false; + + //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((u32)savesize); //expand properly if necessary + data.resize(savesize); //truncate if necessary + addr_size = addr_size_for_old_save_type(savetype); + flush(); + } } -void BackupDevice::close_rom() {} +void BackupDevice::close_rom() +{ + flush(); +} void BackupDevice::reset_command() { @@ -591,33 +308,56 @@ void BackupDevice::reset_command() { flush(); flushPending = false; + lazyFlushPending = 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 - if(autodetect_size==7) addr_size = 3; //advance wars dual strike 2mbit flash - if(autodetect_size==31) addr_size = 3; //daigasso! band brothers 2mbit flash - if(autodetect_size==258) addr_size = 2; //warioware touched - if(autodetect_size==257) addr_size = 1; //yoshi touch & go - if(autodetect_size==9) addr_size = 1; //star wars III - if(autodetect_size==113) addr_size = 1; //space invaders revolution - if(autodetect_size==33) addr_size = 1; //bomberman - if(autodetect_size==65) addr_size = 1; //robots - if(autodetect_size==66) addr_size = 2; //pokemon dash - if(autodetect_size==22) addr_size = 2; //puyo pop fever - if(autodetect_size==18) addr_size = 2; //lunar dragon song - if(autodetect_size==17) addr_size = 1; //shrek super slam - if(autodetect_size==109) addr_size = 1; //scooby-doo! unmasked - if(addr_size>4) + + printf("Autodetecting with autodetect_size=%d\n",autodetect_size); + + const u8 sm64_sig[] = {0x01,0x80,0x00,0x00}; + if(autodetect_size == 4 && !memcmp(&data_autodetect[0],sm64_sig,4)) { - INFO("RESET: Unexpected backup memory address size: %d\n",addr_size); + 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 _MSC_VER + MessageBox(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 + 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; @@ -631,7 +371,7 @@ u8 BackupDevice::data_command(u8 val) { if(com == BM_CMD_WRITELOW) { - LOG("Unexpected backup device initialization sequence using writes!\n"); + printf("Unexpected backup device initialization sequence using writes!\n"); } //just buffer the data until we're no longer detecting @@ -646,14 +386,20 @@ u8 BackupDevice::data_command(u8 val) addr <<= 8; addr |= val; addr_counter++; + //if(addr_counter==addr_size) printf("ADR: %08X\n",addr); + //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; } else { //address is complete - ensure(addr); + ensure(addr+1); 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("read: %08X\n",addr); } else @@ -694,6 +440,7 @@ u8 BackupDevice::data_command(u8 val) case BM_CMD_WRITELOW: case BM_CMD_READLOW: + //printf("XLO: %08X\n",addr); com = val; addr_counter = 0; addr = 0; @@ -701,6 +448,7 @@ u8 BackupDevice::data_command(u8 val) case BM_CMD_WRITEHIGH: case BM_CMD_READHIGH: + //printf("XHI: %08X\n",addr); if(val == BM_CMD_WRITEHIGH) val = BM_CMD_WRITELOW; if(val == BM_CMD_READHIGH) val = BM_CMD_READLOW; com = val; @@ -716,88 +464,31 @@ u8 BackupDevice::data_command(u8 val) break; default: - LOG("COMMAND: Unhandled Backup Memory command: %02X\n", val); + printf("COMMAND: 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 +//guarantees that the data buffer has room enough for the specified number of bytes void BackupDevice::ensure(u32 addr) { u32 size = data.size(); - if(sizeaddr_size = addr_size; this->data.resize(datasize); memcpy(&this->data[0],data,datasize); + + //dump back out as a dsv, just to keep things sane + flush(); +} + + +void BackupDevice::loadfile() +{ + //never use save files if we are in movie mode + if(isMovieMode) return; + + FILE* inf = fopen(filename.c_str(),"rb"); + if(!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 = fopen(tmp,"rb"); + if(!inf) + { + printf("Missing save file %s\n",filename.c_str()); + return; + } + fclose(inf); + + load_raw(tmp); + } + else + { + //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) + { + //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"); + fclose(inf); + load_raw(filename.c_str()); + return; + } + //desmume format + fseek(inf, -cookieLen, SEEK_END); + fseek(inf, -4, SEEK_CUR); + u32 version = 0xFFFFFFFF; + read32le(&version,inf); + if(version!=0) { + printf("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); + if(info.size>0) + 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); + } +} + +bool BackupDevice::save_raw(const char* filename) +{ + FILE* outf = fopen(filename,"wb"); + if(!outf) return false; + u32 size = data.size(); + u32 padSize = pad_up_size(size); + if(data.size()>0) + fwrite(&data[0],1,size,outf); + for(u32 i=size;i 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; + FILE* outf = fopen(filename.c_str(),"wb"); if(outf) { - fwrite(&data[0],1,data.size(),outf); + if(data.size()>0) + 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[ctr]) ctr++; - u32 padSize = saveSizes[ctr]; + u32 padSize = pad_up_size(size); for(u32 i=size;i data; @@ -104,10 +122,11 @@ private: } state; void loadfile(); + bool _loadfile(const char *fname); void ensure(u32 addr); void flush(); - bool flushPending; + bool flushPending, lazyFlushPending; }; #define NDS_FW_SIZE_V1 (256 * 1024) /* size of fw memory on nds v1 */ @@ -117,11 +136,11 @@ void mc_init(memory_chip_t *mc, int type); /* reset and init values for memor u8 *mc_alloc(memory_chip_t *mc, u32 size); /* alloc mc memory */ void mc_realloc(memory_chip_t *mc, int type, u32 size); /* realloc mc memory */ void mc_load_file(memory_chip_t *mc, const char* filename); /* load save file and setup fp */ -int mc_load_duc(memory_chip_t *mc, const char* filename); /* load Action Replay DS save file */ void mc_free(memory_chip_t *mc); /* delete mc memory */ -void mc_reset_com(memory_chip_t *mc); /* reset communication with mc */ -u8 fw_transfer(memory_chip_t *mc, u8 data); /* transfer to, then receive data from firmware */ -u8 bm_transfer(memory_chip_t *mc, u8 data); /* transfer to, then receive data from backup memory */ +void fw_reset_com(memory_chip_t *mc); /* reset communication with mc */ +u8 fw_transfer(memory_chip_t *mc, u8 data); + +void backup_setManualBackupType(int type); #endif /*__FW_H__*/ diff --git a/desmume/src/movie.cpp b/desmume/src/movie.cpp index 2a7123c92..0c40b066b 100644 --- a/desmume/src/movie.cpp +++ b/desmume/src/movie.cpp @@ -436,7 +436,7 @@ void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _paus movieMode = MOVIEMODE_PLAY; currRerecordCount = currMovieData.rerecordCount; InitMovieTime(); - MovieSRAM(); + MMU_new.backupDevice.movie_mode(); freshMovie = true; ClearAutoHold(); @@ -496,7 +496,7 @@ static void openRecordingMovie(const char* fname) movie_readonly = false; currRerecordCount = 0; InitMovieTime(); - MovieSRAM(); + MMU_new.backupDevice.movie_mode(); driver->USR_InfoMessage("Movie recording started."); } @@ -593,8 +593,6 @@ static void openRecordingMovie(const char* fname) osd->addFixed(180, 176, "%s", "Recording"); } - currFrameCounter++; - /*extern uint8 joy[4]; memcpy(&cur_input_display,joy,4);*/ } diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index 2fd69e5bf..94e41e8d6 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -1120,6 +1120,8 @@ DWORD WINAPI run() { Lock lock; NDS_exec(); + if((currFrameCounter&63) == 0) + MMU_new.backupDevice.lazy_flush(); win_sound_samplecounter = 735; } DRV_AviVideoUpdate((u16*)GPU_screen); @@ -1261,6 +1263,8 @@ DWORD WINAPI run() if (ShowMicrophone) osd->addFixed(Hud.Microphone.x, Hud.Microphone.y, "%d",MicDisplay); // DisplayMessage(); CheckMessages(); + + currFrameCounter++; //this needs to be moved into NDS_exec somehow } paused = TRUE; @@ -2608,6 +2612,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM //Menu items dependent on a ROM loaded EnableMenuItem(mainMenu, IDM_GAME_INFO, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_IMPORTBACKUPMEMORY,MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(mainMenu, IDM_EXPORTBACKUPMEMORY,MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_STATE_SAVE, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_STATE_LOAD, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_PRINTSCREEN, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); @@ -2686,7 +2691,10 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM //Language selection //Save type - MainWindow->checkMenu(IDC_SAVETYPE1, MF_BYCOMMAND | MF_CHECKED); + const int savelist[] = {IDC_SAVETYPE1,IDC_SAVETYPE2,IDC_SAVETYPE3,IDC_SAVETYPE4,IDC_SAVETYPE5,IDC_SAVETYPE6,IDC_SAVETYPE7}; + for(int i=0;i<7;i++) MainWindow->checkMenu(savelist[i], MF_BYCOMMAND | MF_UNCHECKED); + MainWindow->checkMenu(savelist[CommonSettings.manualBackupType], MF_BYCOMMAND | MF_CHECKED); + return 0; } @@ -3058,7 +3066,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; - ofn.lpstrFilter = "All supported types\0*.duc;*.sav\0Action Replay DS Save (*.duc)\0*.duc\0DS-Xtreme Save (*.sav)\0*.sav\0\0"; + ofn.lpstrFilter = "All supported types\0*.duc;*.sav\0Action Replay DS Save (*.duc)\0*.duc\0Raw Save format (*.sav)\0*.sav\0\0"; ofn.nFilterIndex = 1; ofn.lpstrFile = ImportSavName; ofn.nMaxFile = MAX_PATH; @@ -3075,6 +3083,31 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM NDS_UnPause(); return 0; } + case IDM_EXPORTBACKUPMEMORY: + { + OPENFILENAME ofn; + NDS_Pause(); + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwnd; + ofn.lpstrFilter = "Raw Save format (*.sav)\0*.sav\0\0"; + ofn.nFilterIndex = 0; + ofn.lpstrFile = ImportSavName; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrDefExt = "sav"; + + if(!GetSaveFileName(&ofn)) + { + NDS_UnPause(); + return 0; + } + + if (!NDS_ExportSave(ImportSavName)) + MessageBox(hwnd,"Save was not successfully exported","Error",MB_OK); + NDS_UnPause(); + return 0; + } + case IDM_CONFIG: RunConfig(CONFIGSCREEN_INPUT); @@ -3342,53 +3375,13 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM SetForegroundWindow(RamWatchHWnd); return 0; -#define clearsaver() \ - MainWindow->checkMenu(IDC_SAVETYPE1, MF_BYCOMMAND | MF_UNCHECKED); \ - MainWindow->checkMenu(IDC_SAVETYPE2, MF_BYCOMMAND | MF_UNCHECKED); \ - MainWindow->checkMenu(IDC_SAVETYPE3, MF_BYCOMMAND | MF_UNCHECKED); \ - MainWindow->checkMenu(IDC_SAVETYPE4, MF_BYCOMMAND | MF_UNCHECKED); \ - MainWindow->checkMenu(IDC_SAVETYPE5, MF_BYCOMMAND | MF_UNCHECKED); \ - MainWindow->checkMenu(IDC_SAVETYPE6, MF_BYCOMMAND | MF_UNCHECKED); \ - MainWindow->checkMenu(IDC_SAVETYPE7, MF_BYCOMMAND | MF_UNCHECKED); - -#define saver(one) \ - MainWindow->checkMenu(one, MF_BYCOMMAND | MF_CHECKED); - - case IDC_SAVETYPE1: - clearsaver(); - saver(IDC_SAVETYPE1); - mmu_select_savetype(0,&backupmemorytype,&backupmemorysize); - return 0; - case IDC_SAVETYPE2: - clearsaver(); - saver(IDC_SAVETYPE2); - mmu_select_savetype(1,&backupmemorytype,&backupmemorysize); - return 0; - case IDC_SAVETYPE3: - clearsaver(); - saver(IDC_SAVETYPE3); - mmu_select_savetype(2,&backupmemorytype,&backupmemorysize); - return 0; - case IDC_SAVETYPE4: - clearsaver(); - saver(IDC_SAVETYPE4); - mmu_select_savetype(3,&backupmemorytype,&backupmemorysize); - return 0; - case IDC_SAVETYPE5: - clearsaver(); - saver(IDC_SAVETYPE5); - mmu_select_savetype(4,&backupmemorytype,&backupmemorysize); - return 0; - case IDC_SAVETYPE6: - clearsaver(); - saver(IDC_SAVETYPE6); - mmu_select_savetype(5,&backupmemorytype,&backupmemorysize); - return 0; - case IDC_SAVETYPE7: - clearsaver(); - saver(IDC_SAVETYPE7); - mmu_select_savetype(6,&backupmemorytype,&backupmemorysize); - return 0; + case IDC_SAVETYPE1: backup_setManualBackupType(0); return 0; + case IDC_SAVETYPE2: backup_setManualBackupType(1); return 0; + case IDC_SAVETYPE3: backup_setManualBackupType(2); return 0; + case IDC_SAVETYPE4: backup_setManualBackupType(3); return 0; + case IDC_SAVETYPE5: backup_setManualBackupType(4); return 0; + case IDC_SAVETYPE6: backup_setManualBackupType(5); return 0; + case IDC_SAVETYPE7: backup_setManualBackupType(6); return 0; case IDM_RESET: ResetGame(); diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index a3ee58ae7..cc8645375 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -45,6 +45,8 @@ #define IDM_SUBMITBUGREPORT 134 #define IDM_STATE_LOAD 135 #define IDM_STATE_SAVE 136 +#define IDM_STATE_LOAD_POPUP 137 +#define IDM_STATE_SAVE_POPUP 138 #define IDM_STATE_SAVE_F1 140 #define IDM_STATE_SAVE_F2 141 #define IDM_STATE_SAVE_F3 142 @@ -620,6 +622,7 @@ #define IDM_FILE_RECORDAVI 40015 #define IDM_SOUND_VIEW 40016 #define IDM_FILE_RECORDWAV 40017 +#define IDM_EXPORTBACKUPMEMORY 40018 #define IDM_STOPMOVIE 40019 #define ID_FILE_RECENTROM 40034 #define IDC_SAVETYPE7 40037 diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index c55ae9f17..c99d6ea7a 100644 --- a/desmume/src/windows/resources.rc +++ b/desmume/src/windows/resources.rc @@ -64,6 +64,7 @@ MENU_PRINCIPAL MENU } MENUITEM SEPARATOR MENUITEM "Importere Backup Hukommelse", IDM_IMPORTBACKUPMEMORY + MENUITEM "Export Backup Memory", IDM_EXPORTBACKUPMEMORY MENUITEM SEPARATOR MENUITEM "Gem Skærmbillede &Som...", IDM_PRINTSCREEN MENUITEM "&Hurtigt Skærmbillede", IDM_QUICK_PRINTSCREEN @@ -235,6 +236,7 @@ MENU_PRINCIPAL MENU } MENUITEM SEPARATOR MENUITEM "Import Backup Memory", IDM_IMPORTBACKUPMEMORY + MENUITEM "Export Backup Memory", IDM_EXPORTBACKUPMEMORY MENUITEM SEPARATOR MENUITEM "Save Screenshot &As...", IDM_PRINTSCREEN MENUITEM "&Quick Screenshot", IDM_QUICK_PRINTSCREEN @@ -418,6 +420,7 @@ MENU_PRINCIPAL MENU } MENUITEM SEPARATOR MENUITEM "Import Backup Memory", IDM_IMPORTBACKUPMEMORY + MENUITEM "Export Backup Memory", IDM_EXPORTBACKUPMEMORY MENUITEM SEPARATOR MENUITEM "Capture d'écr&an...", IDM_PRINTSCREEN MENUITEM "Capture d'écran rapide", IDM_QUICK_PRINTSCREEN