more work on the sav system. see forum for details

This commit is contained in:
zeromus 2009-06-01 08:40:46 +00:00
parent 1ce2cb86f5
commit 40d5c4a485
11 changed files with 386 additions and 545 deletions

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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];

View File

@ -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 <windows.h>
#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(size<addr+1)
if(size<addr)
{
data.resize(addr+1);
data.resize(addr);
}
for(u32 i=size;i<=addr;i++)
for(u32 i=size;i<addr;i++)
data[i] = kUninitializedSaveDataValue;
}
void BackupDevice::loadfile()
{
FILE* inf = fopen(filename.c_str(),"rb");
if(inf)
{
//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)
{
//raw save file
fseek(inf, 0, SEEK_END);
int size = ftell(inf);
fseek(inf, 0, SEEK_SET);
data.resize(size);
fread(&data[0],1,size,inf);
fclose(inf);
return;
}
//desmume format
fseek(inf, -cookieLen, SEEK_END);
fseek(inf, -4, SEEK_CUR);
u32 version = 0xFFFFFFFF;
read32le(&version,inf);
if(version!=0) {
LOG("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);
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);
}
else
{
LOG("Missing save file %s\n",filename.c_str());
}
}
u32 BackupDevice::addr_size_for_old_save_size(int bupmem_size)
{
switch(bupmem_size) {
case MC_SIZE_4KBITS: return 2; //1? hi command?
case MC_SIZE_4KBITS:
return 1;
case MC_SIZE_64KBITS:
case MC_SIZE_256KBITS:
case MC_SIZE_512KBITS:
@ -837,23 +528,142 @@ void BackupDevice::load_old_state(u32 addr_size, u8* data, u32 datasize)
this->addr_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<padSize;i++)
fputc(kUninitializedSaveDataValue,outf);
fclose(outf);
return true;
}
u32 BackupDevice::pad_up_size(u32 startSize)
{
u32 size = startSize;
int ctr=0;
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;
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_count && size > saveSizes[ctr]) ctr++;
u32 padSize = saveSizes[ctr];
u32 padSize = pad_up_size(size);
for(u32 i=size;i<padSize;i++)
fputc(kUninitializedSaveDataValue,outf);
@ -875,40 +685,80 @@ void BackupDevice::flush()
}
else
{
LOG("Unable to open savefile %s\n",filename.c_str());
printf("Unable to open savefile %s\n",filename.c_str());
}
}
void BackupDevice::raw_applyUserSettings(u32& size)
{
//respect the user's choice of backup memory type
if(CommonSettings.manualBackupType == MC_TYPE_AUTODETECT)
addr_size = addr_size_for_old_save_size(size);
else
{
int savetype = save_types[CommonSettings.manualBackupType][0];
int savesize = save_types[CommonSettings.manualBackupType][1];
addr_size = addr_size_for_old_save_type(savetype);
if(savesize<size) size = savesize;
}
state = RUNNING;
}
bool BackupDevice::load_raw(const char* filename)
{
FILE* inf = fopen(filename,"rb");
fseek(inf, 0, SEEK_END);
u32 size = (u32)ftell(inf);
fseek(inf, 0, SEEK_SET);
raw_applyUserSettings(size);
data.resize(size);
fread(&data[0],1,size,inf);
fclose(inf);
//dump back out as a dsv, just to keep things sane
flush();
return true;
}
bool BackupDevice::load_duc(const char* filename)
{
long size;
u32 size;
char id[16];
FILE* file = fopen(filename, "rb");
size_t elems_read = 0;
if(file == NULL)
return false;
fseek(file, 0, SEEK_END);
size = ftell(file) - 500;
size = (u32)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);
fread((void *)id, sizeof(char), 16, file);
if (memcmp(id, "ARDS000000000001", 16) != 0)
{
LOG("Not recognized as a valid DUC file\n");
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((u32)size);
fread(&data[0],1,size,file);
fclose(file);
//choose
flush();
return true;

View File

@ -66,7 +66,13 @@ typedef struct
class BackupDevice
{
public:
BackupDevice();
//signals the save system that we are in our regular mode, loading up a rom. initializes for that case.
void load_rom(const char* filename);
//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();
@ -83,18 +89,30 @@ public:
u32 addr_size;
} savedInfo;
//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);
static u32 addr_size_for_old_save_type(int bupmem_type);
static u32 pad_up_size(u32 startSize);
void raw_applyUserSettings(u32& size);
bool load_duc(const char* filename);
bool load_raw(const char* filename);
bool save_raw(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();
private:
BOOL write_enable; //is write enabled?
u32 com; //persistent command actually handled
u32 addr_size, addr_counter;
u32 addr;
bool isMovieMode;
std::string filename;
std::vector<u8> 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__*/

View File

@ -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);*/
}

View File

@ -1120,6 +1120,8 @@ DWORD WINAPI run()
{
Lock lock;
NDS_exec<false>();
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();

View File

@ -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

View File

@ -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