From e34c8fd52cd56d5167d892a41a72c5614a6db126 Mon Sep 17 00:00:00 2001 From: SuuperW Date: Mon, 30 Sep 2019 11:15:42 -0500 Subject: [PATCH] Add ability to load a ROM from a byte array instead of a file. --- desmume/src/NDSSystem.cpp | 266 +++++++++++--------- desmume/src/NDSSystem.h | 3 + desmume/src/frontend/windows/DesHawkAPI.cpp | 5 + 3 files changed, 157 insertions(+), 117 deletions(-) diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index a7920ab3e..af04a922a 100755 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -401,21 +401,8 @@ void GameInfo::populate() }*/ } -bool GameInfo::loadROM(std::string fname, u32 type) +bool GameInfo::loadROM(u32 type) { - //printf("ROM %s\n", CommonSettings.loadToMemory?"loaded to RAM":"stream from disk"); - - closeROM(); - - char *noext = strdup(fname.c_str()); - reader = ROMReaderInit(&noext); free(noext); - fROM = reader->Init(fname.c_str()); - if (!fROM) return false; - - headerOffset = (type == ROM_DSGBA)?DSGBA_LOADER_SIZE:0; - romsize = reader->Size(fROM) - headerOffset; - reader->Seek(fROM, headerOffset, SEEK_SET); - bool res = (reader->Read(fROM, &header, sizeof(header)) == sizeof(header)); if (res) @@ -426,10 +413,10 @@ bool GameInfo::loadROM(std::string fname, u32 type) const size_t offset; const size_t bytes; }; - + static const FieldSwap fieldSwaps[] = { { offsetof(NDS_header,makerCode), 2}, - + { offsetof(NDS_header,ARM9src), 4}, { offsetof(NDS_header,ARM9exe), 4}, { offsetof(NDS_header,ARM9cpy), 4}, @@ -449,35 +436,35 @@ bool GameInfo::loadROM(std::string fname, u32 type) { offsetof(NDS_header,normalCmd), 4}, { offsetof(NDS_header,Key1Cmd), 4}, { offsetof(NDS_header,IconOff), 4}, - + { offsetof(NDS_header,CRC16), 2}, { offsetof(NDS_header,ROMtimeout), 2}, - + { offsetof(NDS_header,ARM9autoload), 4}, { offsetof(NDS_header,ARM7autoload), 4}, { offsetof(NDS_header,endROMoffset), 4}, { offsetof(NDS_header,HeaderSize), 4}, - + { offsetof(NDS_header, ARM9module), 4}, { offsetof(NDS_header, ARM7module), 4}, - + { offsetof(NDS_header,logoCRC16), 2}, { offsetof(NDS_header,headerCRC16), 2}, }; - - for(size_t i = 0; i < ARRAY_SIZE(fieldSwaps); i++) + + for (size_t i = 0; i < ARRAY_SIZE(fieldSwaps); i++) { const u8 *fieldAddr = (u8 *)&header + fieldSwaps[i].offset; - - switch(fieldSwaps[i].bytes) + + switch (fieldSwaps[i].bytes) { - case 2: - *(u16 *)fieldAddr = LE_TO_LOCAL_16(*(u16 *)fieldAddr); - break; - - case 4: - *(u32 *)fieldAddr = LE_TO_LOCAL_32(*(u32 *)fieldAddr); - break; + case 2: + *(u16 *)fieldAddr = LE_TO_LOCAL_16(*(u16 *)fieldAddr); + break; + + case 4: + *(u32 *)fieldAddr = LE_TO_LOCAL_32(*(u32 *)fieldAddr); + break; } } #endif @@ -486,10 +473,10 @@ bool GameInfo::loadROM(std::string fname, u32 type) if (cardSize < romsize) { msgbox->warn("The ROM header is invalid.\nThe device size has been increased to allow for the provided file size.\n"); - + for (u32 i = header.cardSize; i < 0xF; i++) { - if (((128 * 1024) << i) >= romsize) + if (((128 * 1024) << i) >= romsize) { header.cardSize = i; cardSize = (128 * 1024) << i; @@ -497,13 +484,13 @@ bool GameInfo::loadROM(std::string fname, u32 type) } } } - + mask = (cardSize - 1); - mask |= (mask >>1); - mask |= (mask >>2); - mask |= (mask >>4); - mask |= (mask >>8); - mask |= (mask >>16); + mask |= (mask >> 1); + mask |= (mask >> 2); + mask |= (mask >> 4); + mask |= (mask >> 8); + mask |= (mask >> 16); if (type == ROM_NDS) { @@ -511,40 +498,15 @@ bool GameInfo::loadROM(std::string fname, u32 type) reader->Read(fROM, &secureArea[0], 0x4000); } - //for now, we have to do this, because the DLDI patching requires it - bool loadToMemory = CommonSettings.loadToMemory; - if(isHomebrew()) - loadToMemory = true; - - //convert to an in-memory reader around a pre-read buffer if that's what's requested - if (loadToMemory) - { - reader->Seek(fROM, headerOffset, SEEK_SET); - - romdataForReader = new u8[romsize]; - if (reader->Read(fROM, romdataForReader, romsize) != romsize) - { - delete [] romdataForReader; romdataForReader = NULL; - romsize = 0; - - return false; - } - - reader->DeInit(fROM); - fROM = NULL; - reader = MemROMReaderRead_TrueInit(romdataForReader, romsize); - fROM = reader->Init(NULL); - } - - if(hasRomBanner()) + if (hasRomBanner()) { reader->Seek(fROM, header.IconOff, SEEK_SET); reader->Read(fROM, &banner, sizeof(RomBanner)); - + banner.version = LE_TO_LOCAL_16(banner.version); banner.crc16 = LE_TO_LOCAL_16(banner.crc16); - - for(size_t i = 0; i < ARRAY_SIZE(banner.palette); i++) + + for (size_t i = 0; i < ARRAY_SIZE(banner.palette); i++) { banner.palette[i] = LE_TO_LOCAL_16(banner.palette[i]); } @@ -555,11 +517,11 @@ bool GameInfo::loadROM(std::string fname, u32 type) { reader->Seek(fROM, header.IconOff + headerOffset, SEEK_SET); reader->Read(fROM, &banner, sizeof(RomBanner)); - + banner.version = LE_TO_LOCAL_16(banner.version); banner.crc16 = LE_TO_LOCAL_16(banner.crc16); - - for(size_t i = 0; i < ARRAY_SIZE(banner.palette); i++) + + for (size_t i = 0; i < ARRAY_SIZE(banner.palette); i++) { banner.palette[i] = LE_TO_LOCAL_16(banner.palette[i]); } @@ -571,6 +533,63 @@ bool GameInfo::loadROM(std::string fname, u32 type) romsize = 0; reader->DeInit(fROM); fROM = NULL; return false; + +} +bool GameInfo::loadROM(std::string fname, u32 type) +{ + //printf("ROM %s\n", CommonSettings.loadToMemory?"loaded to RAM":"stream from disk"); + + closeROM(); + + char *noext = strdup(fname.c_str()); + reader = ROMReaderInit(&noext); free(noext); + fROM = reader->Init(fname.c_str()); + if (!fROM) return false; + + headerOffset = (type == ROM_DSGBA) ? DSGBA_LOADER_SIZE : 0; + romsize = reader->Size(fROM) - headerOffset; + reader->Seek(fROM, headerOffset, SEEK_SET); + + //for now, we have to do this, because the DLDI patching requires it + bool loadToMemory = CommonSettings.loadToMemory; + if (isHomebrew()) + loadToMemory = true; + + //convert to an in-memory reader around a pre-read buffer if that's what's requested + if (loadToMemory) + { + reader->Seek(fROM, headerOffset, SEEK_SET); + + romdataForReader = new u8[romsize]; + if (reader->Read(fROM, romdataForReader, romsize) != romsize) + { + delete[] romdataForReader; romdataForReader = NULL; + romsize = 0; + + return false; + } + + reader->DeInit(fROM); + fROM = NULL; + reader = MemROMReaderRead_TrueInit(romdataForReader, romsize); + fROM = reader->Init(NULL); + } + + return loadROM(type); +} +bool GameInfo::loadROM(u8* file, s32 fileSize) +{ + closeROM(); + + // create memory stream + reader = MemROMReaderRead_TrueInit(file, fileSize); + fROM = reader->Init(NULL); + + headerOffset = 0; + romsize = reader->Size(fROM) - headerOffset; + reader->Seek(fROM, headerOffset, SEEK_SET); + + return loadROM(ROM_NDS); } void GameInfo::closeROM() @@ -667,49 +686,21 @@ struct LastRom { std::string filename, physicalName, logicalFilename; } lastRom; -int NDS_LoadROM(const char *filename, const char *physicalName, const char *logicalFilename) +void LoadGameInfo() { - lastRom.filename = filename; - lastRom.physicalName = physicalName ? physicalName : ""; - lastRom.logicalFilename = logicalFilename ? logicalFilename : ""; - - int ret; - char buf[MAX_PATH]; - - if (filename == NULL) - return -1; - - ret = rom_init_path(filename, physicalName, logicalFilename); - if (ret < 1) - return ret; - - if (cheatSearch) - cheatSearch->close(); - FCEUI_StopMovie(); - - if (!gameInfo.ValidateHeader()) - { - ret = -1; - return ret; - } - gameInfo.populate(); - + //run crc over the whole buffer (chunk at a time, to avoid coding a streaming crc gameInfo.reader->Seek(gameInfo.fROM, 0, SEEK_SET); gameInfo.crc = 0; - bool first = true; - for(;;) { + for (;;) { u8 buf[4096]; - int read = gameInfo.reader->Read(gameInfo.fROM,buf,4096); - if(read == 0) break; - if(first && read >= 512) - gameInfo.crcForCheatsDb = ~crc32(0, buf, 512); - first = false; + int read = gameInfo.reader->Read(gameInfo.fROM, buf, 4096); + if (read == 0) break; gameInfo.crc = crc32(gameInfo.crc, buf, read); } - gameInfo.chipID = 0xC2; // The Manufacturer ID is defined by JEDEC (C2h = Macronix) + gameInfo.chipID = 0xC2; // The Manufacturer ID is defined by JEDEC (C2h = Macronix) if (!gameInfo.isHomebrew()) { gameInfo.chipID |= ((((128 << gameInfo.header.cardSize) / 1024) - 1) << 8); // Chip size in megabytes minus 1 @@ -740,8 +731,45 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi if (gameInfo.isDSiEnhanced()) INFO("ROM DSi Enhanced\n"); } - const char *makerName = Database::MakerNameForMakerCode(gameInfo.header.makerCode,true); - INFO("ROM developer: %s\n", ((gameInfo.header.makerCode == 0) && gameInfo.isHomebrew())?"Homebrew":makerName); + const char *makerName = Database::MakerNameForMakerCode(gameInfo.header.makerCode, true); + INFO("ROM developer: %s\n", ((gameInfo.header.makerCode == 0) && gameInfo.isHomebrew()) ? "Homebrew" : makerName); + + //for homebrew, try auto-patching DLDI. should be benign if there is no DLDI or if it fails + if (gameInfo.isHomebrew()) + { + //note: gameInfo.romdataForReader is safe here because we made sure to load the rom into memory for isHomebrew + if (slot1_GetCurrentType() == NDS_SLOT1_R4) + DLDI::tryPatch((void*)gameInfo.romdataForReader, gameInfo.romsize, 1); + else if (slot2_GetCurrentType() == NDS_SLOT2_CFLASH) + DLDI::tryPatch((void*)gameInfo.romdataForReader, gameInfo.romsize, 0); + } +} +int NDS_LoadROM(const char *filename, const char *physicalName, const char *logicalFilename) +{ + lastRom.filename = filename; + lastRom.physicalName = physicalName ? physicalName : ""; + lastRom.logicalFilename = logicalFilename ? logicalFilename : ""; + + int ret; + char buf[MAX_PATH]; + + if (filename == NULL) + return -1; + + ret = rom_init_path(filename, physicalName, logicalFilename); + if (ret < 1) + return ret; + + if (cheatSearch) + cheatSearch->close(); + FCEUI_StopMovie(); + + if (!gameInfo.ValidateHeader()) + { + ret = -1; + return ret; + } + LoadGameInfo(); buf[0] = gameInfo.header.gameCode[0]; buf[1] = gameInfo.header.gameCode[1]; @@ -769,16 +797,6 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi } printf("\n"); - //for homebrew, try auto-patching DLDI. should be benign if there is no DLDI or if it fails - if(gameInfo.isHomebrew()) - { - //note: gameInfo.romdataForReader is safe here because we made sure to load the rom into memory for isHomebrew - if (slot1_GetCurrentType() == NDS_SLOT1_R4) - DLDI::tryPatch((void*)gameInfo.romdataForReader, gameInfo.romsize, 1); - else if (slot2_GetCurrentType() == NDS_SLOT2_CFLASH) - DLDI::tryPatch((void*)gameInfo.romdataForReader, gameInfo.romsize, 0); - } - if (cheats != NULL) { memset(buf, 0, MAX_PATH); @@ -792,6 +810,20 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi return ret; } +int NDS_LoadROM(u8* file, int fileSize) +{ + gameInfo.loadROM(file, fileSize); + gameInfo.romType = ROM_NDS; + + if (!gameInfo.ValidateHeader()) + return -1; + // I'm not sure all this is necessary/wanted here. + LoadGameInfo(); + + NDS_Reset(); + + return 1; +} void NDS_FreeROM(void) { diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index ca6029907..f1ae9d2db 100755 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -373,6 +373,8 @@ struct GameInfo bool IsCode(const char* code) const; bool loadROM(std::string fname, u32 type = ROM_NDS); + bool loadROM(u8* file, s32 fileSize); + bool loadROM(u32 type); void closeROM(); u32 readROM(u32 pos); bool ValidateHeader(); @@ -465,6 +467,7 @@ void NDS_suspendProcessingInput(bool suspend); int NDS_LoadROM(const char *filename, const char* physicalFilename=0, const char* logicalFilename=0); +int NDS_LoadROM(u8* file, int fileSize); void NDS_FreeROM(void); void NDS_Reset(); diff --git a/desmume/src/frontend/windows/DesHawkAPI.cpp b/desmume/src/frontend/windows/DesHawkAPI.cpp index 69261ad36..e790e2e3d 100644 --- a/desmume/src/frontend/windows/DesHawkAPI.cpp +++ b/desmume/src/frontend/windows/DesHawkAPI.cpp @@ -65,3 +65,8 @@ DLL int GetFrameCount() { return currFrameCounter; } + +DLL bool LoadROM(u8* file, int fileSize) +{ + return (NDS_LoadROM(file, fileSize) > 0); +}