mc.cpp: It is now possible for DeSmuME to import its own battery save file format using the new method, BackupDevice::import_dsv().
This commit is contained in:
parent
3b2a9ededc
commit
0907207222
|
@ -660,6 +660,7 @@
|
|||
[panel setAllowsMultipleSelection:NO];
|
||||
[panel setTitle:NSSTRING_TITLE_IMPORT_ROM_SAVE_PANEL];
|
||||
NSArray *fileTypes = [NSArray arrayWithObjects:
|
||||
@FILE_EXT_ROM_SAVE,
|
||||
@FILE_EXT_ROM_SAVE_RAW,
|
||||
@FILE_EXT_ACTION_REPLAY_SAVE,
|
||||
@FILE_EXT_ACTION_REPLAY_MAX_SAVE,
|
||||
|
|
|
@ -231,16 +231,16 @@ BackupDevice::BackupDevice()
|
|||
char buf[MAX_PATH] = {0};
|
||||
memset(buf, 0, MAX_PATH);
|
||||
path.getpathnoext(path.BATTERY, buf);
|
||||
filename = std::string(buf) + ".dsv";
|
||||
_fileName = std::string(buf) + ".dsv";
|
||||
|
||||
MCLOG("MC: %s\n", filename.c_str());
|
||||
MCLOG("MC: %s\n", _fileName.c_str());
|
||||
|
||||
bool fexists = (access(filename.c_str(), 0) == 0)?true:false;
|
||||
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 = EMUFILE_FILE(filename, "rb");
|
||||
EMUFILE_FILE in = EMUFILE_FILE(_fileName, "rb");
|
||||
if (!in.fail())
|
||||
{
|
||||
u32 sz = in.size();
|
||||
|
@ -278,7 +278,7 @@ BackupDevice::BackupDevice()
|
|||
|
||||
if (sz > 0)
|
||||
{
|
||||
EMUFILE_FILE fpOut = EMUFILE_FILE(filename, "wb");
|
||||
EMUFILE_FILE fpOut = EMUFILE_FILE(_fileName, "wb");
|
||||
if (!fpOut.fail())
|
||||
{
|
||||
u8 *buf = new u8[sz + 1];
|
||||
|
@ -301,15 +301,15 @@ BackupDevice::BackupDevice()
|
|||
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;
|
||||
_info.type = (res + 1);
|
||||
addr_size = _info.addr_size = save_types[_info.type].addr_size;
|
||||
_info.size = fsize = sz;
|
||||
fpMC = &fpOut; //so ensure() works
|
||||
ensure(sz, &fpOut);
|
||||
fsize = 0;
|
||||
}
|
||||
else
|
||||
info.type = 0;
|
||||
_info.type = 0;
|
||||
fexists = true;
|
||||
}
|
||||
else
|
||||
|
@ -323,7 +323,7 @@ BackupDevice::BackupDevice()
|
|||
}
|
||||
}
|
||||
|
||||
fpMC = new EMUFILE_FILE(filename, fexists?"rb+":"wb+");
|
||||
fpMC = new EMUFILE_FILE(_fileName, fexists?"rb+":"wb+");
|
||||
const bool fileCanReadWrite = (fpMC->get_fp() != NULL);
|
||||
if (!fileCanReadWrite)
|
||||
{
|
||||
|
@ -339,10 +339,10 @@ BackupDevice::BackupDevice()
|
|||
fpMC->truncate(0);
|
||||
|
||||
if (readFooter() == 0)
|
||||
fsize -= (strlen(kDesmumeSaveCookie) + strlen(DESMUME_BACKUP_FOOTER_TXT) + 24);
|
||||
fsize -= BackupDevice::GetDSVFooterSize();
|
||||
else
|
||||
{
|
||||
memset(&info, 0, sizeof(info));
|
||||
memset(&_info, 0, sizeof(_info));
|
||||
fsize = 0;
|
||||
}
|
||||
|
||||
|
@ -353,22 +353,22 @@ BackupDevice::BackupDevice()
|
|||
{
|
||||
if (advsc.isLoaded())
|
||||
{
|
||||
info.type = advsc.getSaveType();
|
||||
if (info.type != 0xFF && info.type != 0xFE)
|
||||
_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.type++;
|
||||
u32 adv_size = save_types[_info.type].size;
|
||||
if (_info.size > adv_size)
|
||||
{
|
||||
info.size = adv_size;
|
||||
_info.size = adv_size;
|
||||
fpMC->truncate(adv_size);
|
||||
ensure(adv_size, fpMC);
|
||||
}
|
||||
else
|
||||
if (info.size < adv_size)
|
||||
if (_info.size < adv_size)
|
||||
{
|
||||
left = adv_size - info.size;
|
||||
info.size = adv_size;
|
||||
left = adv_size - _info.size;
|
||||
_info.size = adv_size;
|
||||
ensure(adv_size);
|
||||
}
|
||||
|
||||
|
@ -377,17 +377,17 @@ BackupDevice::BackupDevice()
|
|||
}
|
||||
}
|
||||
|
||||
addr_size = info.addr_size;
|
||||
info.padSize = fsize;
|
||||
addr_size = _info.addr_size;
|
||||
_info.padSize = fsize;
|
||||
//none of the other fields are used right now
|
||||
|
||||
if (CommonSettings.autodetectBackupMethod != 1 && info.type == 0)
|
||||
if (CommonSettings.autodetectBackupMethod != 1 && _info.type == 0)
|
||||
{
|
||||
info.type = searchFileSaveType(info.size);
|
||||
if (info.type == 0xFF) info.type = 0;
|
||||
_info.type = searchFileSaveType(_info.size);
|
||||
if (_info.type == 0xFF) _info.type = 0;
|
||||
}
|
||||
|
||||
u32 ss = (info.padSize * 8) / 1024;
|
||||
u32 ss = (_info.padSize * 8) / 1024;
|
||||
bool _Mbit = false;
|
||||
|
||||
if (ss >= 1024)
|
||||
|
@ -413,7 +413,7 @@ BackupDevice::~BackupDevice()
|
|||
int BackupDevice::readFooter()
|
||||
{
|
||||
// Check if the footer data exists.
|
||||
if (fpMC->size() < (strlen(kDesmumeSaveCookie) + strlen(DESMUME_BACKUP_FOOTER_TXT) + 24))
|
||||
if (fpMC->size() < BackupDevice::GetDSVFooterSize())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -440,18 +440,18 @@ int BackupDevice::readFooter()
|
|||
return -2;
|
||||
|
||||
fpMC->fseek(-24, SEEK_CUR);
|
||||
fpMC->read_32LE(info.size);
|
||||
fpMC->read_32LE(info.padSize);
|
||||
fpMC->read_32LE(info.type);
|
||||
fpMC->read_32LE(info.addr_size);
|
||||
fpMC->read_32LE(info.mem_size);
|
||||
fpMC->read_32LE(this->_info.size);
|
||||
fpMC->read_32LE(this->_info.padSize);
|
||||
fpMC->read_32LE(this->_info.type);
|
||||
fpMC->read_32LE(this->_info.addr_size);
|
||||
fpMC->read_32LE(this->_info.mem_size);
|
||||
|
||||
MCLOG("DeSmuME backup footer:\n");
|
||||
MCLOG("\t* size:\t\t%u\n", info.size);
|
||||
MCLOG("\t* padSize:\t%u\n", info.padSize);
|
||||
MCLOG("\t* type (%u):\t%s\n", info.type, save_types[info.type].descr);
|
||||
MCLOG("\t* addr_size:\t%u\n", info.addr_size);
|
||||
MCLOG("\t* mem_size:\t%u\n", info.mem_size);
|
||||
MCLOG("\t* size:\t\t%u\n", this->_info.size);
|
||||
MCLOG("\t* padSize:\t%u\n", this->_info.padSize);
|
||||
MCLOG("\t* type (%u):\t%s\n", this->_info.type, save_types[this->_info.type].descr);
|
||||
MCLOG("\t* addr_size:\t%u\n", this->_info.addr_size);
|
||||
MCLOG("\t* mem_size:\t%u\n", this->_info.mem_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -952,9 +952,9 @@ void BackupDevice::ensure(u32 addr, u8 val, EMUFILE *fpOut)
|
|||
|
||||
u32 padSize = pad_up_size(addr);
|
||||
u32 size = padSize - fsize;
|
||||
info.padSize = info.size = fsize = padSize;
|
||||
this->_info.padSize = this->_info.size = fsize = padSize;
|
||||
int type = searchFileSaveType(fsize);
|
||||
if (type != 0xFF) info.type = (type + 1);
|
||||
if (type != 0xFF) this->_info.type = (type + 1);
|
||||
|
||||
#ifndef _DONT_SAVE_BACKUP
|
||||
if (size > 0)
|
||||
|
@ -971,9 +971,9 @@ void BackupDevice::ensure(u32 addr, u8 val, EMUFILE *fpOut)
|
|||
//and now the actual footer
|
||||
fp->write_32LE(addr); //the size of data that has actually been written
|
||||
fp->write_32LE(padSize); //the size we padded it to
|
||||
fp->write_32LE(info.type); //save memory type
|
||||
fp->write_32LE(this->_info.type); //save memory type
|
||||
fp->write_32LE(addr_size);
|
||||
fp->write_32LE(info.size); //save memory size
|
||||
fp->write_32LE(this->_info.size); //save memory size
|
||||
fp->write_32LE((u32)0); //version number
|
||||
fp->fprintf("%s", kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save
|
||||
|
||||
|
@ -1066,22 +1066,43 @@ u32 BackupDevice::importDataSize(const char *filename)
|
|||
bool BackupDevice::importData(const char *filename, u32 force_size)
|
||||
{
|
||||
bool res = false;
|
||||
if (strlen(filename) < 4) return res;
|
||||
|
||||
if (strlen(filename) < 4)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string ext = strright(filename,4);
|
||||
|
||||
bool isNative = strncasecmp(ext.c_str(), ".dsv", 4) == 0;
|
||||
bool isDuc = strncasecmp(ext.c_str(), ".duc", 4) == 0;
|
||||
bool isDss = strncasecmp(ext.c_str(), ".dss", 4) == 0;
|
||||
if(isDuc || isDss)
|
||||
|
||||
if (isNative)
|
||||
{
|
||||
res = import_dsv(filename);
|
||||
}
|
||||
else if (isDuc || isDss)
|
||||
{
|
||||
res = import_duc(filename, force_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (import_no_gba(filename, force_size))
|
||||
{
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = import_raw(filename, force_size);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
NDS_Reset();
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1573,6 +1594,75 @@ bool BackupDevice::import_duc(const char* filename, u32 force_size)
|
|||
|
||||
}
|
||||
|
||||
bool BackupDevice::import_dsv(const char *filename)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
FILE *theFile = fopen(filename, "rb");
|
||||
if (theFile == NULL)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate the DeSmuME footer.
|
||||
BackupDeviceFileSaveFooter importFileFooter;
|
||||
size_t importFileSize = 0;
|
||||
|
||||
const bool isFileValid = BackupDevice::GetDSVFileInfo(theFile, &importFileFooter, &importFileSize);
|
||||
if (!isFileValid)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( (this->addr_size != 0) && (this->addr_size != 0xFFFFFFFF) && (this->addr_size != importFileFooter.info.addr_size))
|
||||
{
|
||||
printf("BackupDevice: WARNING! Importing an address bus size that differs from what this game is currently using. (Importing \'%u\'; Expected \'%u\'.\n", (unsigned int)importFileFooter.info.addr_size, (unsigned int)addr_size);
|
||||
}
|
||||
|
||||
if ( (this->_info.padSize > 0) && (this->_info.padSize != importFileFooter.info.padSize) )
|
||||
{
|
||||
printf("BackupDevice: NOTE - Importing a backup data size that differs from what this game is currently using. (Importing \'%u\'; Expected \'%u\'.\n", (unsigned int)importFileFooter.info.padSize, (unsigned int)this->_info.padSize);
|
||||
}
|
||||
|
||||
// Read the backup data into memory.
|
||||
u8 *backupData = (u8 *)malloc(importFileFooter.info.padSize);
|
||||
|
||||
fseek(theFile, 0, SEEK_SET);
|
||||
const size_t backupDataReadByteCount = fread(backupData, 1, importFileFooter.info.padSize, theFile);
|
||||
fclose(theFile); // At this point, both backup data and footer should have been read, so we can close the import file now.
|
||||
|
||||
if (backupDataReadByteCount != importFileFooter.info.padSize)
|
||||
{
|
||||
free(backupData);
|
||||
printf("BackupDevice: DSV import failed! Could not read the backup data.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Write out the entirety of the file data to the EMUFILE.
|
||||
// Note: If EMUFILE is an EMUFILE_FILE, know that we are doing a straight overwrite here.
|
||||
// TODO: For better safety, we should create a new file and then swap.
|
||||
this->fpMC->fseek(0, SEEK_SET);
|
||||
if (importFileFooter.info.padSize > 0)
|
||||
{
|
||||
this->fpMC->fwrite(backupData, importFileFooter.info.padSize);
|
||||
}
|
||||
|
||||
this->addr_size = importFileFooter.info.addr_size;
|
||||
this->fsize = importFileFooter.info.padSize;
|
||||
|
||||
this->ensure(importFileFooter.info.padSize, this->fpMC);
|
||||
free(backupData);
|
||||
|
||||
// Truncate the file if necessary.
|
||||
// * Also see TODO note above, since that applies to this step as well.
|
||||
const size_t newFileSize = this->_info.padSize + BackupDevice::GetDSVFooterSize();
|
||||
this->fpMC->truncate(newFileSize);
|
||||
|
||||
result = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BackupDevice::load_movie(EMUFILE &is)
|
||||
{
|
||||
const s32 cookieLen = (s32)strlen(kDesmumeSaveCookie);
|
||||
|
@ -1588,10 +1678,7 @@ bool BackupDevice::load_movie(EMUFILE &is)
|
|||
}
|
||||
is.fseek(-24, SEEK_CUR);
|
||||
|
||||
struct
|
||||
{
|
||||
u32 size,padSize,type,addr_size,mem_size;
|
||||
} info;
|
||||
BackupDeviceFileInfo info;
|
||||
|
||||
is.read_32LE(info.size);
|
||||
is.read_32LE(info.padSize);
|
||||
|
@ -1614,3 +1701,85 @@ void BackupDevice::forceManualBackupType()
|
|||
addr_size = addr_size_for_old_save_size(save_types[CommonSettings.manualBackupType].size);
|
||||
state = RUNNING;
|
||||
}
|
||||
|
||||
size_t BackupDevice::GetDSVFooterSize()
|
||||
{
|
||||
return (strlen(DESMUME_BACKUP_FOOTER_TXT) + sizeof(BackupDeviceFileSaveFooter));
|
||||
}
|
||||
|
||||
bool BackupDevice::GetDSVFileInfo(FILE *inFileDSV, BackupDeviceFileSaveFooter *outFooter, size_t *outFileSize)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (inFileDSV == NULL)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the total file size.
|
||||
fseek(inFileDSV, 0, SEEK_END);
|
||||
const size_t inFileSize = (size_t)ftell(inFileDSV);
|
||||
fseek(inFileDSV, 0, SEEK_SET);
|
||||
|
||||
// Check for the DeSmuME footer.
|
||||
if (inFileSize < BackupDevice::GetDSVFooterSize())
|
||||
{
|
||||
printf("BackupDevice: File validation failed! The file appears to be corrupted.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate the DeSmuME footer.
|
||||
BackupDeviceFileSaveFooter inFileFooter;
|
||||
|
||||
fseek(inFileDSV, -(s32)sizeof(inFileFooter), SEEK_END);
|
||||
const size_t footerReadByteCount = fread(&inFileFooter, 1, sizeof(inFileFooter), inFileDSV);
|
||||
|
||||
if (footerReadByteCount != sizeof(inFileFooter))
|
||||
{
|
||||
printf("BackupDevice: File validation failed! Could not read the file footer.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (strncmp(inFileFooter.cookie, kDesmumeSaveCookie, sizeof(inFileFooter.cookie)) != 0)
|
||||
{
|
||||
char inCookieTerminatedString[sizeof(inFileFooter.cookie) + 1];
|
||||
strncpy(inCookieTerminatedString, inFileFooter.cookie, sizeof(inFileFooter.cookie));
|
||||
inCookieTerminatedString[sizeof(inFileFooter.cookie)] = '\0';
|
||||
|
||||
printf("BackupDevice: File validation failed! Incorrect cookie found. (Read \'%s\'; Expected \'%s\'.\n", inCookieTerminatedString, kDesmumeSaveCookie);
|
||||
return result;
|
||||
}
|
||||
|
||||
inFileFooter.version = LE_TO_LOCAL_32(inFileFooter.version);
|
||||
if (inFileFooter.version != 0)
|
||||
{
|
||||
printf("BackupDevice: File validation failed! Incorrect version. (Read \'%u\'; Expected \'%u\'.\n", (unsigned int)inFileFooter.version, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
inFileFooter.info.padSize = LE_TO_LOCAL_32(inFileFooter.info.padSize);
|
||||
const size_t backupDataSize = (inFileSize - BackupDevice::GetDSVFooterSize());
|
||||
if (inFileFooter.info.padSize != backupDataSize)
|
||||
{
|
||||
printf("BackupDevice: File validation failed! Incorrect backup data size. (Read \'%u\'; Expected \'%u\'.\n", (unsigned int)inFileFooter.info.padSize, (unsigned int)backupDataSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (outFooter != NULL)
|
||||
{
|
||||
inFileFooter.info.size = LE_TO_LOCAL_32(inFileFooter.info.size);
|
||||
inFileFooter.info.type = LE_TO_LOCAL_32(inFileFooter.info.type);
|
||||
inFileFooter.info.addr_size = LE_TO_LOCAL_32(inFileFooter.info.addr_size);
|
||||
inFileFooter.info.mem_size = LE_TO_LOCAL_32(inFileFooter.info.mem_size);
|
||||
|
||||
*outFooter = inFileFooter;
|
||||
}
|
||||
|
||||
if (outFileSize != NULL)
|
||||
{
|
||||
*outFileSize = inFileSize;
|
||||
}
|
||||
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,24 @@
|
|||
|
||||
class EMUFILE;
|
||||
|
||||
struct BackupDeviceFileInfo
|
||||
{
|
||||
u32 size;
|
||||
u32 padSize;
|
||||
u32 type;
|
||||
u32 addr_size;
|
||||
u32 mem_size;
|
||||
};
|
||||
typedef struct BackupDeviceFileInfo BackupDeviceFileInfo;
|
||||
|
||||
struct BackupDeviceFileSaveFooter
|
||||
{
|
||||
BackupDeviceFileInfo info;
|
||||
u32 version;
|
||||
char cookie[16];
|
||||
};
|
||||
typedef struct BackupDeviceFileSaveFooter BackupDeviceFileSaveFooter;
|
||||
|
||||
//This "backup device" represents a typical retail NDS save memory accessible via AUXSPI.
|
||||
//It is managed as a core emulator service for historical reasons which are bad,
|
||||
//and possible infrastructural simplification reasons which are good.
|
||||
|
@ -66,7 +84,7 @@ public:
|
|||
void close_rom();
|
||||
void forceManualBackupType();
|
||||
void reset_hardware();
|
||||
std::string getFilename() { return filename; }
|
||||
std::string getFilename() { return this->_fileName; }
|
||||
|
||||
u8 readByte(u32 addr, const u8 init);
|
||||
u16 readWord(u32 addr, const u16 init);
|
||||
|
@ -124,28 +142,30 @@ public:
|
|||
bool import_duc(const char* filename, u32 force_size = 0);
|
||||
bool import_no_gba(const char *fname, u32 force_size = 0);
|
||||
bool import_raw(const char* filename, u32 force_size = 0);
|
||||
bool import_dsv(const char *filename);
|
||||
bool export_no_gba(const char* fname);
|
||||
bool export_raw(const char* filename);
|
||||
bool no_gba_unpack(u8 *&buf, u32 &size);
|
||||
|
||||
bool load_movie(EMUFILE &is);
|
||||
|
||||
struct {
|
||||
u32 size,padSize,type,addr_size,mem_size;
|
||||
} info;
|
||||
BackupDeviceFileInfo _info;
|
||||
|
||||
bool isMovieMode;
|
||||
|
||||
u32 importDataSize(const char *filename);
|
||||
bool importData(const char *filename, u32 force_size = 0);
|
||||
bool exportData(const char *filename);
|
||||
|
||||
static size_t GetDSVFooterSize();
|
||||
static bool GetDSVFileInfo(FILE *inFileDSV, BackupDeviceFileSaveFooter *outFooter, size_t *outFileSize);
|
||||
|
||||
//the value contained in memory when shipped from factory (before user program ever writes to it). more details commented elsewhere.
|
||||
u8 uninitializedValue;
|
||||
|
||||
private:
|
||||
EMUFILE *fpMC;
|
||||
std::string filename;
|
||||
std::string _fileName;
|
||||
u32 fsize;
|
||||
int readFooter();
|
||||
bool write(u8 val);
|
||||
|
|
Loading…
Reference in New Issue