From 8edec83002249c99d24a7dd1569990d87875588f Mon Sep 17 00:00:00 2001 From: mtabachenko Date: Mon, 28 Oct 2013 13:18:37 +0000 Subject: [PATCH] core: - add streaming ROM data from disk. I was broke all ports except windows, on linux/mac ports need fix rom_init_path in NDSSystem.cpp but i can't test this; --- desmume/src/MMU.cpp | 12 -- desmume/src/MMU.h | 6 - desmume/src/NDSSystem.cpp | 200 +++++++++++------- desmume/src/NDSSystem.h | 113 +++------- .../src/addons/slot1_retail_mcrom_debug.cpp | 22 +- desmume/src/addons/slot1comp_rom.cpp | 6 +- desmume/src/commandline.cpp | 8 + desmume/src/commandline.h | 1 + desmume/src/utils/decrypt/decrypt.cpp | 22 +- desmume/src/utils/decrypt/decrypt.h | 6 +- desmume/src/utils/decrypt/header.cpp | 9 +- desmume/src/utils/decrypt/header.h | 2 +- desmume/src/windows/fsnitroView.cpp | 6 +- desmume/src/windows/ginfo.cpp | 39 ++-- desmume/src/windows/main.cpp | 48 +++-- desmume/src/windows/memView.cpp | 16 +- desmume/src/windows/resource.h | 5 +- desmume/src/windows/resources.rc | Bin 235958 -> 236362 bytes 18 files changed, 276 insertions(+), 245 deletions(-) diff --git a/desmume/src/MMU.cpp b/desmume/src/MMU.cpp index b37019ffd..3f1696b66 100644 --- a/desmume/src/MMU.cpp +++ b/desmume/src/MMU.cpp @@ -908,8 +908,6 @@ void MMU_Init(void) memset(&MMU, 0, sizeof(MMU_struct)); - MMU.CART_ROM = MMU.UNUSED_RAM; - //MMU.DTCMRegion = 0x027C0000; //even though apps may change dtcm immediately upon startup, this is the correct hardware starting value: MMU.DTCMRegion = 0x08000000; @@ -1056,16 +1054,6 @@ void SetupMMU(bool debugConsole, bool dsi) { _MMU_MAIN_MEM_MASK32 = _MMU_MAIN_MEM_MASK & ~3; } -void MMU_setRom(u8 * rom, u32 mask) -{ - MMU.CART_ROM = rom; -} - -void MMU_unsetRom() -{ - MMU.CART_ROM=MMU.UNUSED_RAM; -} - static void execsqrt() { u32 ret; u8 mode = MMU_new.sqrt.mode; diff --git a/desmume/src/MMU.h b/desmume/src/MMU.h index 121a2b7f2..39b92bbd3 100644 --- a/desmume/src/MMU.h +++ b/desmume/src/MMU.h @@ -406,9 +406,6 @@ struct MMU_struct //32KB of shared WRAM - can be switched between ARM7 & ARM9 in two blocks u8 SWIRAM[0x8000]; - //Card rom & ram - u8 * CART_ROM; - //Unused ram u8 UNUSED_RAM[4]; @@ -546,9 +543,6 @@ void MMU_DeInit(void); void MMU_Reset( void); -void MMU_setRom(u8 * rom, u32 mask); -void MMU_unsetRom( void); - void print_memory_profiling( void); // Memory reading/writing (old) diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index e1018c25e..86a9ff22f 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -153,10 +153,9 @@ int NDS_Init( void) return 0; } -void NDS_DeInit(void) { - if(MMU.CART_ROM != MMU.UNUSED_RAM) - NDS_FreeROM(); - +void NDS_DeInit(void) +{ + gameInfo.closeROM(); SPU_DeInit(); Screen_DeInit(); MMU_DeInit(); @@ -191,23 +190,11 @@ void NDS_DeInit(void) { #endif } -BOOL NDS_SetROM(u8 * rom, u32 mask) -{ - MMU_setRom(rom, mask); - - return TRUE; -} - NDS_header * NDS_getROMHeader(void) { - if(MMU.CART_ROM == MMU.UNUSED_RAM) return NULL; NDS_header * header = new NDS_header; - //copy all data blindly, but not entirely blindly.. in case the header is smaller than normal, we dont want to read junk data - //(we memset to 0xFF since thats likely to be what gets read from the invalid area) - memset(header,0xFF,sizeof(NDS_header)); - int todo = std::min(gameInfo.romsize,sizeof(NDS_header)); - memcpy(header,MMU.CART_ROM,todo); + memcpy(header, &gameInfo.header, sizeof(gameInfo.header)); //endian swap necessary fields. It would be better if we made accessors for these. I wonder if you could make a macro for a field accessor that would take the bitsize and do the swap on the fly struct FieldSwap { @@ -245,6 +232,9 @@ NDS_header * NDS_getROMHeader(void) { 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}, }; @@ -339,14 +329,7 @@ bool GameInfo::hasRomBanner() const RomBanner& GameInfo::getRomBanner() { - //we may not have a valid banner. return a default one - if(!hasRomBanner()) - { - static RomBanner defaultBanner(true); - return defaultBanner; - } - - return *(RomBanner*)(romdata+header.IconOff); + return banner; } void GameInfo::populate() @@ -440,31 +423,105 @@ void GameInfo::populate() } -bool GameInfo::isDSiEnhanced() -{ - return ((*(u32*)(romdata + 0x180) == 0x8D898581U) && (*(u32*)(romdata + 0x184) == 0x8C888480U)); -} - #ifdef _WINDOWS static std::vector buffer; static std::vector v; -static void loadrom(std::string fname) { - FILE* inf = fopen(fname.c_str(),"rb"); - if(!inf) return; - - fseek(inf,0,SEEK_END); - int size = ftell(inf); - fseek(inf,0,SEEK_SET); - - gameInfo.resize(size); - - gameInfo.loadRom(inf); - - gameInfo.fillGap(); +bool GameInfo::loadROM(std::string fname) +{ + printf("ROM %s\n", CommonSettings.loadToMemory?"loaded to RAM":"stream from disk"); - fclose(inf); + closeROM(); + + fROM = fopen(fname.c_str(), "rb"); + if (!fROM) return false; + + fseek(fROM, 0, SEEK_END); + romsize = ftell(fROM); + fseek(fROM, 0, SEEK_SET); + + bool res = (fread(&header, 1, sizeof(header), fROM) == sizeof(header)); + + if (res) + { + cardSize = (128 * 1024) << header.cardSize; + mask = (cardSize - 1); + mask |= (mask >>1); + mask |= (mask >>2); + mask |= (mask >>4); + mask |= (mask >>8); + mask |= (mask >>16); + + fseek(fROM, 0x4000, SEEK_SET); + fread(&secureArea[0], 1, 0x4000, fROM); + + if (CommonSettings.loadToMemory) + { + fseek(fROM, 0, SEEK_SET); + + romdata = new u8[romsize + 4]; + if (fread(romdata, 1, romsize, fROM) != romsize) + { + delete [] romdata; romdata = NULL; + romsize = 0; + + return false; + } + + if(hasRomBanner()) + memcpy(&banner, romdata + header.IconOff, sizeof(RomBanner)); + + _isDSiEnhanced = ((*(u32*)(romdata + 0x180) == 0x8D898581U) && (*(u32*)(romdata + 0x184) == 0x8C888480U)); + fclose(fROM); fROM = NULL; + return true; + } + _isDSiEnhanced = ((readROM(0x180) == 0x8D898581U) && (readROM(0x184) == 0x8C888480U)); + if (hasRomBanner()) + { + fseek(fROM, header.IconOff, SEEK_SET); + fread(&banner, 1, sizeof(RomBanner), fROM); + } + fseek(fROM, 0, SEEK_SET); + lastReadPos = 0; + return true; + } + + romsize = 0; + fclose(fROM); fROM = NULL; + return false; +} + +void GameInfo::closeROM() +{ + if (fROM) + fclose(fROM); + + if (romdata) + delete [] romdata; + + fROM = NULL; + romdata = NULL; + romsize = 0; + lastReadPos = 0xFFFFFFFF; +} + +u32 GameInfo::readROM(u32 pos) +{ + if ((pos < 0x8000) && (pos >= 0x4000)) + return *(u32*)(secureArea + (pos - 0x4000)); + + if (!romdata) + { + u32 data; + if (lastReadPos != pos) + fseek(fROM, pos, SEEK_SET); + u32 num = fread(&data, 1, 4, fROM); + lastReadPos = (pos + num); + return data; + } + else + return *(u32*)(romdata + pos); } static int rom_init_path(const char *filename, const char *physicalName, const char *logicalFilename) @@ -475,11 +532,11 @@ static int rom_init_path(const char *filename, const char *physicalName, const c if ( path.isdsgba(path.path)) { type = ROM_DSGBA; - loadrom(path.path); + gameInfo.loadROM(path.path); } else if ( !strcasecmp(path.extension().c_str(), "nds")) { type = ROM_NDS; - loadrom(physicalName ? physicalName : path.path); //n.b. this does nothing if the file can't be found (i.e. if it was an extracted tempfile)... + gameInfo.loadROM(physicalName ? physicalName : path.path); //n.b. this does nothing if the file can't be found (i.e. if it was an extracted tempfile)... //...but since the data was extracted to gameInfo then it is ok } //ds.gba in archives, it's already been loaded into memory at this point @@ -488,14 +545,17 @@ static int rom_init_path(const char *filename, const char *physicalName, const c } else { //well, try to load it as an nds rom anyway type = ROM_NDS; - loadrom(physicalName ? physicalName : path.path); + gameInfo.loadROM(physicalName ? physicalName : path.path); } + // TODO: !!!!! +#if 0 if(type == ROM_DSGBA) { std::vector v(gameInfo.romdata + DSGBA_LOADER_SIZE, gameInfo.romdata + gameInfo.romsize); gameInfo.loadData(&v[0],gameInfo.romsize - DSGBA_LOADER_SIZE); } +#endif //check that size is at least the size of the header if (gameInfo.romsize < 352) { @@ -555,6 +615,7 @@ static int rom_init_path(const char *filename, const char *physicalName, const c // Make sure old ROM is freed first(at least this way we won't be eating // up a ton of ram before the old ROM is freed) + if(MMU.CART_ROM != MMU.UNUSED_RAM) NDS_FreeROM(); @@ -585,17 +646,19 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi //check whether this rom is any kind of valid - if(!CheckValidRom((u8*)gameInfo.romdata,gameInfo.romsize)) + if(!CheckValidRom((u8*)&gameInfo.header, gameInfo.secureArea)) { printf("Specified file is not a valid rom\n"); return -1; } - MMU_unsetRom(); - NDS_SetROM((u8*)gameInfo.romdata, gameInfo.mask); - gameInfo.populate(); - gameInfo.crc = crc32(0,(u8*)gameInfo.romdata,gameInfo.romsize); + + + if (CommonSettings.loadToMemory) + gameInfo.crc = crc32(0, (u8*)gameInfo.romdata, gameInfo.romsize); + else + gameInfo.crc = 0; gameInfo.chipID = 0xC2; // The Manufacturer ID is defined by JEDEC (C2h = Macronix) gameInfo.chipID |= ((((128 << gameInfo.header.cardSize) / 1024) - 1) << 8); // Chip size in megabytes minus 1 @@ -623,9 +686,6 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi if (gameInfo.isDSiEnhanced()) INFO("ROM DSi Enhanced\n"); INFO("ROM developer: %s\n", getDeveloperNameByID(gameInfo.header.makerCode).c_str()); - //crazymax: how would it have got whacked? dont think we need this - //gameInfo.storeSecureArea(); - #if 1 u32 mask = gameInfo.header.endROMoffset - 1; mask |= (mask >> 1); @@ -636,7 +696,7 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi printf("======================================================================\n"); printf("card size %10u (%08Xh) mask %08Xh\n", gameInfo.cardSize, gameInfo.cardSize, gameInfo.mask); - printf("file size %10u (%08Xh) mask %08Xh\n", gameInfo.romsize, gameInfo.romsize, gameInfo.filemask); + printf("file size %10u (%08Xh)\n", gameInfo.romsize, gameInfo.romsize); printf("endROMoffset %10u (%08Xh) mask %08Xh\n", gameInfo.header.endROMoffset, gameInfo.header.endROMoffset, mask); printf("======================================================================\n"); #endif @@ -672,7 +732,7 @@ 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) + if(gameInfo.isHomebrew && CommonSettings.loadToMemory) DLDI::tryPatch((void*)gameInfo.romdata, gameInfo.romsize); if (cheats != NULL) @@ -691,11 +751,7 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi void NDS_FreeROM(void) { FCEUI_StopMovie(); - if ((u8*)MMU.CART_ROM == (u8*)gameInfo.romdata) - gameInfo.romdata = NULL; - if (MMU.CART_ROM != MMU.UNUSED_RAM) - delete [] MMU.CART_ROM; - MMU_unsetRom(); + gameInfo.closeROM(); } u32 NDS_ImportSaveSize(const char *filename) @@ -2504,8 +2560,8 @@ bool NDS_LegitBoot() firmware->loadSettings(); //since firmware only boots encrypted roms, we have to make sure it's encrypted first - //this has not been validated on big endian systems. it almost positively doesn't work. - EncryptSecureArea((u8*)gameInfo.romdata,gameInfo.romsize); + //this has not been validated on big endian systems. it almost positively doesn't work. + EncryptSecureArea((u8*)&gameInfo.header, (u8*)gameInfo.secureArea); //boot processors from their bios entrypoints armcpu_init(&NDS_ARM7, 0x00000000); @@ -2548,7 +2604,7 @@ bool NDS_FakeBoot() //since we're bypassing the code to decrypt the secure area, we need to make sure its decrypted first //this has not been validated on big endian systems. it almost positively doesn't work. - bool okRom = DecryptSecureArea((u8*)gameInfo.romdata,gameInfo.romsize); + bool okRom = DecryptSecureArea((u8*)&gameInfo.header, (u8*)gameInfo.secureArea); if(!okRom) { printf("Specified file is not a valid rom\n"); @@ -2578,7 +2634,8 @@ bool NDS_FakeBoot() u32 dst = header->ARM9cpy; for(u32 i = 0; i < (header->ARM9binSize>>2); ++i) { - _MMU_write32(dst, T1ReadLong(MMU.CART_ROM, src)); + _MMU_write32(dst, gameInfo.readROM(src)); + dst += 4; src += 4; } @@ -2588,7 +2645,8 @@ bool NDS_FakeBoot() dst = header->ARM7cpy; for(u32 i = 0; i < (header->ARM7binSize>>2); ++i) { - _MMU_write32(dst, T1ReadLong(MMU.CART_ROM, src)); + _MMU_write32(dst, gameInfo.readROM(src)); + dst += 4; src += 4; } @@ -2613,13 +2671,13 @@ bool NDS_FakeBoot() if(nds.Is_DSI()) { //dsi needs this copied later in memory. there are probably a number of things that get copied to a later location in memory.. thats where the NDS consoles tend to stash stuff. - for (int i = 0; i < ((0x170)/4); i++) - _MMU_write32(0x02FFFE00+i*4, LE_TO_LOCAL_32(((u32*)MMU.CART_ROM)[i])); + for (int i = 0; i < (0x170); i+=4) + _MMU_write32(0x027FFE00 + i, LE_TO_LOCAL_32(gameInfo.readROM(i))); } else { - for (int i = 0; i < ((0x170)/4); i++) - _MMU_write32(0x027FFE00+i*4, LE_TO_LOCAL_32(((u32*)MMU.CART_ROM)[i])); + for (int i = 0; i < (0x170); i+=4) + _MMU_write32(0x027FFE00 + i, LE_TO_LOCAL_32(gameInfo.readROM(i))); } //the firmware will be booting to these entrypoint addresses via BX (well, the arm9 at least; is unverified for the arm7) diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index 47f014959..fc822c62f 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -297,7 +297,7 @@ NDS_header * NDS_getROMHeader(void); struct RomBanner { - RomBanner(bool defaultInit); + RomBanner(bool defaultInit = true); u16 version; //Version (0001h) u16 crc16; //CRC16 across entries 020h..83Fh u8 reserved[0x1C]; //Reserved (zero-filled) @@ -322,102 +322,48 @@ struct RomBanner struct GameInfo { - GameInfo() : romdata(NULL), + FILE *fROM; + u8 *romdata; + u32 romsize; + u32 cardSize; + u32 mask; + u32 crc; + u32 chipID; + u32 lastReadPos; + char ROMserial[20]; + char ROMname[20]; + bool _isDSiEnhanced; + bool isHomebrew; + NDS_header header; + //a copy of the pristine secure area from the rom + u8 secureArea[0x4000]; + RomBanner banner; + const RomBanner& getRomBanner(); + + GameInfo() : fROM(NULL), + romdata(NULL), crc(0), chipID(0x00000FC2), romsize(0), cardSize(0), - allocatedSize(0), mask(0), - filemask(0) + lastReadPos(0xFFFFFFFF), + _isDSiEnhanced(false) { memset(&header, 0, sizeof(header)); memset(&ROMserial[0], 0, sizeof(ROMserial)); memset(&ROMname[0], 0, sizeof(ROMname)); - memset(&secureArea[0], 0, sizeof(secureArea)); } - void loadData(char* buf, int size) - { - resize(size); - memcpy(romdata,buf,size); - romsize = (u32)size; - fillGap(); - } + ~GameInfo() { closeROM(); } - void storeSecureArea() - { - if ((header.ARM9src >= 0x4000) && (header.ARM9src < 0x8000)) - memcpy(&secureArea[0], &romdata[header.ARM9src], 0x8000 - header.ARM9src); - } - - void restoreSecureArea() - { - if ((header.ARM9src >= 0x4000) && (header.ARM9src < 0x8000)) - memcpy(&romdata[header.ARM9src], &secureArea[0], 0x8000 - header.ARM9src); - } - - void fillGap() - { - memset(romdata+romsize,0xFF,allocatedSize-romsize); - } - - void resize(int size) { - if(romdata != NULL) delete[] romdata; - - //calculate the necessary mask for the requested size - filemask = (size - 1); - filemask |= (filemask >>1); - filemask |= (filemask >>2); - filemask |= (filemask >>4); - filemask |= (filemask >>8); - filemask |= (filemask >>16); - - //now, we actually need to over-allocate, because bytes from anywhere protected by that mask - //could be read from the rom - allocatedSize = (filemask + 4); - - romdata = new char[allocatedSize]; - romsize = size; - } - - bool loadRom(FILE *fp) - { - bool res = (fread(romdata, 1, romsize, fp) == romsize); - if (res) - { - cardSize = (128 * 1024) << romdata[0x14]; - mask = (cardSize - 1); - mask |= (mask >>1); - mask |= (mask >>2); - mask |= (mask >>4); - mask |= (mask >>8); - mask |= (mask >>16); - return true; - } - return false; - } - - bool isDSiEnhanced(); - u32 crc; - u32 chipID; - NDS_header header; - char ROMserial[20]; - char ROMname[20]; - //char ROMfullName[7][0x100]; + bool loadROM(std::string fname); + void closeROM(); + u32 readROM(u32 pos); void populate(); - char* romdata; - u32 romsize; - u32 cardSize; - u32 allocatedSize; - u32 mask; - u32 filemask; - const RomBanner& getRomBanner(); + bool isDSiEnhanced() { return _isDSiEnhanced; }; bool hasRomBanner(); - bool isHomebrew; - //a copy of the pristine secure area from the rom - u8 secureArea[0x4000]; }; typedef struct TSCalInfo @@ -551,6 +497,7 @@ extern struct TCommonSettings { , GFX3D_Zelda_Shadow_Depth_Hack(0) , GFX3D_Renderer_Multisample(false) , jit_max_block_size(100) + , loadToMemory(true) , UseExtBIOS(false) , SWIFromBIOS(false) , PatchSWI3(false) @@ -604,6 +551,8 @@ extern struct TCommonSettings { int GFX3D_Zelda_Shadow_Depth_Hack; bool GFX3D_Renderer_Multisample; + bool loadToMemory; + bool UseExtBIOS; char ARM9BIOS[256]; char ARM7BIOS[256]; diff --git a/desmume/src/addons/slot1_retail_mcrom_debug.cpp b/desmume/src/addons/slot1_retail_mcrom_debug.cpp index aa56abe81..742b3fadb 100644 --- a/desmume/src/addons/slot1_retail_mcrom_debug.cpp +++ b/desmume/src/addons/slot1_retail_mcrom_debug.cpp @@ -51,17 +51,23 @@ public: virtual void connect() { - if (!MMU.CART_ROM) return; protocol.reset(this); protocol.chipId = gameInfo.chipID; protocol.gameCode = T1ReadLong((u8*)gameInfo.header.gameCode,0); - pathData = path.getpath(path.SLOT1D) + path.GetRomNameWithoutExtension(); - printf("Path to Slot1 data: %s\n", pathData.c_str()); - curr_file_id = 0xFFFF; fpROM = NULL; - fs = new FS_NITRO(MMU.CART_ROM); + fs = NULL; + + if (!CommonSettings.loadToMemory) + { + printf("NitroFS: change load type to \"Load to RAM\"\n"); + return; + } + pathData = path.getpath(path.SLOT1D) + path.GetRomNameWithoutExtension(); + printf("Path to Slot1 data: %s\n", pathData.c_str()); + + fs = new FS_NITRO(gameInfo.romdata); fs->rebuildFAT(pathData); } @@ -113,13 +119,13 @@ public: u16 file_id = 0xFFFF; u32 offset = 0; bool bFromFile = false; - if (fs->isFAT(protocol.address)) + if (fs && fs->isFAT(protocol.address)) { fs->rebuildFAT(protocol.address, protocol.length, pathData); } else { - if (fs->getFileIdByAddr(protocol.address, file_id, offset)) + if (fs && fs->getFileIdByAddr(protocol.address, file_id, offset)) { if (file_id != curr_file_id) { @@ -178,7 +184,7 @@ private: u32 address = rom.getAddress(); - if (fs->isFAT(address)) + if (fs && fs->isFAT(address)) { u32 res = fs->getFATRecord(address); if (res != 0xFFFFFFFF) diff --git a/desmume/src/addons/slot1comp_rom.cpp b/desmume/src/addons/slot1comp_rom.cpp index 902019bd6..930c6db7d 100644 --- a/desmume/src/addons/slot1comp_rom.cpp +++ b/desmume/src/addons/slot1comp_rom.cpp @@ -32,7 +32,7 @@ u32 Slot1Comp_Rom::read() { case eSlot1Operation_00_ReadHeader_Unencrypted: { - u32 ret = T1ReadLong(MMU.CART_ROM, address); + u32 ret = gameInfo.readROM(address); address = (address + 4) & 0xFFF; return ret; } @@ -42,7 +42,7 @@ u32 Slot1Comp_Rom::read() { //see B7 for details address &= gameInfo.mask; //sanity check - u32 ret = T1ReadLong(MMU.CART_ROM, address); + u32 ret = gameInfo.readROM(address); address = (address&~0xFFF) + ((address+4)&0xFFF); return ret; } @@ -72,7 +72,7 @@ u32 Slot1Comp_Rom::read() } //actually read from the ROM provider - u32 ret = T1ReadLong(MMU.CART_ROM, address); + u32 ret = gameInfo.readROM(address); //"However, the datastream wraps to the begin of the current 4K block when address+length crosses a 4K boundary (1000h bytes)" address = (address&~0xFFF) + ((address+4)&0xFFF); diff --git a/desmume/src/commandline.cpp b/desmume/src/commandline.cpp index 2ca86c7e6..e6b5db792 100644 --- a/desmume/src/commandline.cpp +++ b/desmume/src/commandline.cpp @@ -33,6 +33,7 @@ int _commandline_linux_nojoy = 0; CommandLine::CommandLine() : is_cflash_configured(false) +, _load_to_memory(-1) , error(NULL) , ctx(g_option_context_new ("")) , _play_movie_file(0) @@ -80,6 +81,7 @@ void CommandLine::loadCommonOptions() //but also see the gtk port for an example of how to combine this with other options //(you may need to use ifdefs to cause options to be entered in the desired order) static const GOptionEntry options[] = { + { "load-type", 0, 0, G_OPTION_ARG_INT, &_load_to_memory, "Load ROM type, 0 - stream from disk, 1 - to RAM (default 1)", "LOAD_TYPE"}, { "load-slot", 0, 0, G_OPTION_ARG_INT, &load_slot, "Loads savegame from slot NUM", "NUM"}, { "play-movie", 0, 0, G_OPTION_ARG_FILENAME, &_play_movie_file, "Specifies a dsm format movie to play", "PATH_TO_PLAY_MOVIE"}, { "record-movie", 0, 0, G_OPTION_ARG_FILENAME, &_record_movie_file, "Specifies a path to a new dsm format movie", "PATH_TO_RECORD_MOVIE"}, @@ -143,6 +145,7 @@ bool CommandLine::parse(int argc,char **argv) if(_slot1_fat_dir) slot1_fat_dir = _slot1_fat_dir; if(_slot1) slot1 = _slot1; slot1 = strtoupper(slot1); if(_console_type) console_type = _console_type; + if(_load_to_memory != -1) CommonSettings.loadToMemory = (_load_to_memory == 1)?true:false; if(_play_movie_file) play_movie_file = _play_movie_file; if(_record_movie_file) record_movie_file = _record_movie_file; if(_cflash_image) cflash_image = _cflash_image; @@ -210,6 +213,11 @@ bool CommandLine::validate() } } + if (_load_to_memory < -1 || _load_to_memory > 1) { + g_printerr("Invalid parameter (0 - stream from disk, 1 - from RAM)\n"); + return false; + } + if (load_slot < -1 || load_slot > 10) { g_printerr("I only know how to load from slots 0-10; -1 means 'do not load savegame' and is default\n"); return false; diff --git a/desmume/src/commandline.h b/desmume/src/commandline.h index 1db7ca63f..270827cab 100644 --- a/desmume/src/commandline.h +++ b/desmume/src/commandline.h @@ -85,6 +85,7 @@ private: char* _cflash_path; char* _gbaslot_rom; char* _bios_arm9, *_bios_arm7; + int _load_to_memory; int _bios_swi; int _spu_advanced; int _num_cores; diff --git a/desmume/src/utils/decrypt/decrypt.cpp b/desmume/src/utils/decrypt/decrypt.cpp index 8fa2ad90b..35400e4be 100644 --- a/desmume/src/utils/decrypt/decrypt.cpp +++ b/desmume/src/utils/decrypt/decrypt.cpp @@ -487,12 +487,12 @@ static void encrypt_arm9(u32 cardheader_gamecode, unsigned char *data) //0x0200 - 0x3FFF : typically, nothing is stored here. on retail cards, you can't read from that area anyway, but im not sure if that's done in the game card or the GC bus controller on the system //0x4000 - 0x7FFF : secure area (details in gbatek) -bool DecryptSecureArea(u8 *romdata, long romlen) +bool DecryptSecureArea(u8 *romheader, u8 *secure) { //this looks like it will only work on little endian hosts - Header* header = (Header*)romdata; + Header* header = (Header*)romheader; - int romType = DetectRomType(*header,(char*)romdata); + int romType = DetectRomType(*header, (char*)secure); if(romType == ROMTYPE_INVALID) return false; @@ -512,7 +512,7 @@ bool DecryptSecureArea(u8 *romdata, long romlen) //// write secure 0x800 //memcpy(romdata+0x4000,data,0x800); - decrypt_arm9(*(u32 *)header->gamecode, romdata+0x4000); + decrypt_arm9(*(u32 *)header->gamecode, secure); printf("Decrypted.\n"); } @@ -524,12 +524,12 @@ bool DecryptSecureArea(u8 *romdata, long romlen) return true; } -bool EncryptSecureArea(u8 *romdata, long romlen) +bool EncryptSecureArea(u8 *romheader, u8 *secure) { //this looks like it will only work on little endian hosts - Header* header = (Header*)romdata; + Header* header = (Header*)romheader; - int romType = DetectRomType(*header,(char*)romdata); + int romType = DetectRomType(*header, (char*)secure); if(romType == ROMTYPE_INVALID) return false; @@ -544,7 +544,7 @@ bool EncryptSecureArea(u8 *romdata, long romlen) //// write secure 0x800 //memcpy(romdata+0x4000,data,0x800); - encrypt_arm9(*(u32 *)header->gamecode, romdata+0x4000); + encrypt_arm9(*(u32 *)header->gamecode, secure); printf("Encrypted.\n"); } @@ -552,11 +552,11 @@ bool EncryptSecureArea(u8 *romdata, long romlen) return true; } -bool CheckValidRom(u8 *romdata, long romlen) +bool CheckValidRom(u8 *header, u8 *secure) { - Header* header = (Header*)romdata; + Header* hdr = (Header*)header; - int romType = DetectRomType(*header,(char*)romdata); + int romType = DetectRomType(*hdr, (char*)secure); return (romType != ROMTYPE_INVALID); } \ No newline at end of file diff --git a/desmume/src/utils/decrypt/decrypt.h b/desmume/src/utils/decrypt/decrypt.h index 8180a6247..58242b445 100644 --- a/desmume/src/utils/decrypt/decrypt.h +++ b/desmume/src/utils/decrypt/decrypt.h @@ -24,12 +24,12 @@ extern const unsigned char arm7_key[]; //decrypts the secure area of a rom (or does nothing if it is already decrypted) -bool DecryptSecureArea(u8 *romdata, long romlen); +bool DecryptSecureArea(u8 *romheader, u8 *secure); //encrypts the secure area of a rom (or does nothing if it is already encrypted) -bool EncryptSecureArea(u8 *romdata, long romlen); +bool EncryptSecureArea(u8 *romheader, u8 *secure); //since we have rom-type detection heuristics here, this module is responsible for checking whether a rom is valid -bool CheckValidRom(u8 *romdata, long romlen); +bool CheckValidRom(u8 *header, u8 *secure); #endif diff --git a/desmume/src/utils/decrypt/header.cpp b/desmume/src/utils/decrypt/header.cpp index d3dd6e682..5edc948a5 100644 --- a/desmume/src/utils/decrypt/header.cpp +++ b/desmume/src/utils/decrypt/header.cpp @@ -66,9 +66,9 @@ /* * DetectRomType */ -int DetectRomType(const Header& header, char* romdata) +int DetectRomType(const Header& header, char* secure) { - unsigned int * data = (unsigned int*)(romdata + 0x4000); + unsigned int * data = (unsigned int*)(secure); //this is attempting to check for an utterly invalid nds header if(header.unitcode < 0 && header.unitcode > 3) return ROMTYPE_INVALID; @@ -76,8 +76,9 @@ int DetectRomType(const Header& header, char* romdata) if (header.arm9_rom_offset < 0x4000) return ROMTYPE_HOMEBREW; if (data[0] == 0x00000000 && data[1] == 0x00000000) return ROMTYPE_MULTIBOOT; if (data[0] == 0xE7FFDEFF && data[1] == 0xE7FFDEFF) return ROMTYPE_NDSDUMPED; - for (int i=0x200; i<0x4000; i++) - if (romdata[i]) return ROMTYPE_MASKROM; // found something odd ;) + //TODO + //for (int i=0x200; i<0x4000; i++) + // if (romdata[i]) return ROMTYPE_MASKROM; // found something odd ;) return ROMTYPE_ENCRSECURE; } diff --git a/desmume/src/utils/decrypt/header.h b/desmume/src/utils/decrypt/header.h index bca8ee524..5009673c9 100644 --- a/desmume/src/utils/decrypt/header.h +++ b/desmume/src/utils/decrypt/header.h @@ -121,7 +121,7 @@ unsigned short CalcLogoCRC(Header &header); void FixHeaderCRC(char *ndsfilename); void ShowInfo(char *ndsfilename); int HashAndCompareWithList(char *filename, unsigned char sha1[]); -int DetectRomType(const Header& header, char* romdata); +int DetectRomType(const Header& header, char* secure); unsigned short CalcSecureAreaCRC(bool encrypt); #define ROMTYPE_HOMEBREW 0 diff --git a/desmume/src/windows/fsnitroView.cpp b/desmume/src/windows/fsnitroView.cpp index 2e24fddcb..adf06b929 100644 --- a/desmume/src/windows/fsnitroView.cpp +++ b/desmume/src/windows/fsnitroView.cpp @@ -21,6 +21,7 @@ #include "fsnitroView.h" #include "CWindow.h" #include "../MMU.h" +#include "../NDSSystem.h" #include "../path.h" #include "../utils/fsnitro.h" #include "memView.h" @@ -87,7 +88,7 @@ void refreshQView(HWND hWnd, u16 id) u32 len = std::min(sizeof(buf), fs->getFileSizeById(id)); u32 start = fs->getStartAddrById(id); - memcpy(&buf[0], &MMU.CART_ROM[start], len); + memcpy(&buf[0], &gameInfo.romdata[start], len); for (u32 i = 0; i < len; i++) if (buf[i] < 0x20) buf[i] = 0x20; @@ -104,7 +105,8 @@ BOOL CALLBACK ViewFSNitroProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam { case WM_INITDIALOG: { - fs = new FS_NITRO(MMU.CART_ROM); + fs = new FS_NITRO(gameInfo.romdata); + if (!fs) { msgbox->error("Error reading FS from ROM"); diff --git a/desmume/src/windows/ginfo.cpp b/desmume/src/windows/ginfo.cpp index f90d89a70..d7ac04e6a 100644 --- a/desmume/src/windows/ginfo.cpp +++ b/desmume/src/windows/ginfo.cpp @@ -110,82 +110,81 @@ LRESULT GInfo_Paint(HWND hDlg, WPARAM wParam, LPARAM lParam) //TODO - pull this from the header, not straight out of the rom (yuck!) - memcpy(text, MMU.CART_ROM, 12); + memcpy(text, (u8*)&gameInfo.header, 12); text[12] = '\0'; SetWindowText(GetDlgItem(hDlg, IDC_GI_GAMETITLE), text); - memcpy(text, (MMU.CART_ROM+0xC), 4); + memcpy(text, ((u8*)&gameInfo.header+0xC), 4); text[4] = '\0'; SetWindowText(GetDlgItem(hDlg, IDC_GI_GAMECODE), text); - memcpy(text, (MMU.CART_ROM+0x10), 2); + memcpy(text, ((u8*)&gameInfo.header+0x10), 2); text[2] = '\0'; SetWindowText(GetDlgItem(hDlg, IDC_GI_MAKERCODE), text); - SetWindowText(GetDlgItem(hDlg, IDC_SDEVELOPER), getDeveloperNameByID(T1ReadWord(MMU.CART_ROM, 0x10)).c_str()); + SetWindowText(GetDlgItem(hDlg, IDC_SDEVELOPER), getDeveloperNameByID(T1ReadWord((u8*)&gameInfo.header, 0x10)).c_str()); - val = T1ReadByte(MMU.CART_ROM, 0x14); + val = T1ReadByte((u8*)&gameInfo.header, 0x14); sprintf(text, "%i kilobytes", (0x80 << val)); SetWindowText(GetDlgItem(hDlg, IDC_GI_CHIPSIZE), text); - val = T1ReadLong(MMU.CART_ROM, 0x20); + val = T1ReadLong((u8*)&gameInfo.header, 0x20); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM9ROM), text); - val = T1ReadLong(MMU.CART_ROM, 0x24); + val = T1ReadLong((u8*)&gameInfo.header, 0x24); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM9ENTRY), text); - val = T1ReadLong(MMU.CART_ROM, 0x28); + val = T1ReadLong((u8*)&gameInfo.header, 0x28); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM9START), text); - val = T1ReadLong(MMU.CART_ROM, 0x2C); + val = T1ReadLong((u8*)&gameInfo.header, 0x2C); sprintf(text, "%i bytes", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM9SIZE), text); - val = T1ReadLong(MMU.CART_ROM, 0x30); + val = T1ReadLong((u8*)&gameInfo.header, 0x30); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM7ROM), text); - val = T1ReadLong(MMU.CART_ROM, 0x34); + val = T1ReadLong((u8*)&gameInfo.header, 0x34); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM7ENTRY), text); - val = T1ReadLong(MMU.CART_ROM, 0x38); + val = T1ReadLong((u8*)&gameInfo.header, 0x38); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM7START), text); - val = T1ReadLong(MMU.CART_ROM, 0x3C); + val = T1ReadLong((u8*)&gameInfo.header, 0x3C); sprintf(text, "%i bytes", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_ARM7SIZE), text); - val = T1ReadLong(MMU.CART_ROM, 0x40); + val = T1ReadLong((u8*)&gameInfo.header, 0x40); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_FNTOFS), text); - val = T1ReadLong(MMU.CART_ROM, 0x44); + val = T1ReadLong((u8*)&gameInfo.header, 0x44); sprintf(text, "%i bytes", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_FNTSIZE), text); - val = T1ReadLong(MMU.CART_ROM, 0x48); + val = T1ReadLong((u8*)&gameInfo.header, 0x48); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_FATOFS), text); - val = T1ReadLong(MMU.CART_ROM, 0x4C); + val = T1ReadLong((u8*)&gameInfo.header, 0x4C); sprintf(text, "%i bytes", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_FATSIZE), text); - icontitleOffset = T1ReadLong(MMU.CART_ROM, 0x68); + icontitleOffset = T1ReadLong((u8*)&gameInfo.header, 0x68); sprintf(text, "0x%08X", icontitleOffset); SetWindowText(GetDlgItem(hDlg, IDC_GI_ICONTITLEOFS), text); - val = T1ReadLong(MMU.CART_ROM, 0x80); + val = T1ReadLong((u8*)&gameInfo.header, 0x80); sprintf(text, "0x%08X", val); SetWindowText(GetDlgItem(hDlg, IDC_GI_USEDROMSIZE), text); - EndPaint(hDlg, &ps); return 0; diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index e823cfdcc..b0b80c2aa 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -2935,10 +2935,11 @@ int _main() msgbox = &msgBoxWnd; - char text[80]; + char text[80] = {0}; path.ReadPathSettings(); + CommonSettings.loadToMemory = GetPrivateProfileBool("General", "loadType", true, IniName); CommonSettings.cheatsDisable = GetPrivateProfileBool("General", "cheatsDisable", false, IniName); CommonSettings.autodetectBackupMethod = GetPrivateProfileInt("General", "autoDetectMethod", 0, IniName); @@ -3975,11 +3976,10 @@ void CloseRom() { StopAllLuaScripts(); // cheatsSearchClose(); - NDS_FreeROM(); romloaded = false; execute = false; Hud.resetTransient(); - NDS_Reset(); + NDS_FreeROM(); // clear screen so the last frame we rendered doesn't stick around // (TODO: maybe NDS_Reset should do this?) @@ -4151,7 +4151,7 @@ void ScreenshotToClipboard(bool extraInfo) int exHeight = 0; if(extraInfo) { - exHeight = (14 * (twolinever ? 7:6)); + exHeight = (14 * (twolinever ? 8:7)); } HDC hScreenDC = GetDC(NULL); @@ -4196,21 +4196,19 @@ void ScreenshotToClipboard(bool extraInfo) else TextOut(hMemDC, 0, 384 + 14, nameandver, strlen(nameandver)); - char str[32]; - memcpy(&str[0], &MMU.CART_ROM[0], 12); str[12] = '\0'; - int titlelen = strlen(str); - str[titlelen] = ' '; - memcpy(&str[titlelen+1], &MMU.CART_ROM[12], 6); str[titlelen+1+6] = '\0'; - TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 3:2), str, strlen(str)); + char str[32] = {0}; + TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 3:2), gameInfo.ROMname, strlen(gameInfo.ROMname)); + TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 4:3), gameInfo.ROMserial, strlen(gameInfo.ROMserial)); + sprintf(str, "CPU: %s", CommonSettings.use_jit ? "JIT":"Interpreter"); - TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 4:3), str, strlen(str)); - - sprintf(str, "FPS: %i/%i (%02d%%/%02d%%) | %s", mainLoopData.fps, mainLoopData.fps3d, Hud.cpuload[0], Hud.cpuload[1], paused ? "Paused":"Running"); TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 5:4), str, strlen(str)); - sprintf(str, "3D Render: %s", core3DList[cur3DCore]->name); + sprintf(str, "FPS: %i/%i (%02d%%/%02d%%) | %s", mainLoopData.fps, mainLoopData.fps3d, Hud.cpuload[0], Hud.cpuload[1], paused ? "Paused":"Running"); TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 6:5), str, strlen(str)); + + sprintf(str, "3D Render: %s", core3DList[cur3DCore]->name); + TextOut(hMemDC, 8, 384 + 14 * (twolinever ? 7:6), str, strlen(str)); } OpenClipboard(NULL); @@ -4404,6 +4402,9 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM DesEnableMenuItem(mainMenu, IDC_BACKGROUNDINPUT, !lostFocusPause); + DesEnableMenuItem(mainMenu, ID_LOADTORAM, !romloaded); + DesEnableMenuItem(mainMenu, ID_STREAMFROMDISK, !romloaded); + //Update savestate slot items based on ROM loaded for (int x = 0; x < 10; x++) { @@ -4556,6 +4557,10 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM DesEnableMenuItem(mainMenu, IDM_SCREENSEP_COLORBLACK, false); } + // load type + MainWindow->checkMenu(ID_STREAMFROMDISK, ((CommonSettings.loadToMemory == false))); + MainWindow->checkMenu(ID_LOADTORAM, ((CommonSettings.loadToMemory == true))); + // Tools MainWindow->checkMenu(IDM_CONSOLE_ALWAYS_ON_TOP, gConsoleTopmost); @@ -5585,6 +5590,11 @@ DOKEYDOWN: return 0; case ID_TOOLS_VIEWFSNITRO: + if (!CommonSettings.loadToMemory) + { + msgbox->error("Change load type to \"Load to RAM\""); + return 0; + } ViewFSNitro->open(); return 0; //========================================================== Tools end @@ -5800,6 +5810,16 @@ DOKEYDOWN: ResetGame(); return 0; + case ID_STREAMFROMDISK: + CommonSettings.loadToMemory = false; + WritePrivateProfileBool("General", "loadType", CommonSettings.loadToMemory, IniName); + return 0; + + case ID_LOADTORAM: + CommonSettings.loadToMemory = true; + WritePrivateProfileBool("General", "loadType", CommonSettings.loadToMemory, IniName); + return 0; + case IDM_3DCONFIG: { bool tpaused = false; diff --git a/desmume/src/windows/memView.cpp b/desmume/src/windows/memView.cpp index 0f3231478..f32e1ff7e 100644 --- a/desmume/src/windows/memView.cpp +++ b/desmume/src/windows/memView.cpp @@ -82,7 +82,7 @@ u8 memRead8 (MemRegionType regionType, HWAddressType address) return value; case MEMVIEW_ROM: if (address < gameInfo.romsize) - value = MMU.CART_ROM[address]; + value = gameInfo.romdata[address]; return value; case MEMVIEW_FULL: MMU_DumpMemBlock(0, address, 1, &value); @@ -113,7 +113,7 @@ u16 memRead16 (MemRegionType regionType, HWAddressType address) return value; case MEMVIEW_ROM: if (address < (gameInfo.romsize - 2)) - value = T1ReadWord(MMU.CART_ROM, address); + value = T1ReadWord(gameInfo.romdata, address); return value; case MEMVIEW_FULL: MMU_DumpMemBlock(0, address, 2, (u8*)&value); @@ -144,7 +144,8 @@ u32 memRead32 (MemRegionType regionType, HWAddressType address) return value; case MEMVIEW_ROM: if (address < (gameInfo.romsize - 4)) - value = T1ReadLong(MMU.CART_ROM, address); + value = T1ReadLong(gameInfo.romdata, address); + return value; case MEMVIEW_FULL: MMU_DumpMemBlock(0, address, 4, (u8*)&value); @@ -197,7 +198,7 @@ void memWrite8(MemRegionType regionType, HWAddressType address, u8 value) MMU.fw.data[address] = value; break; case MEMVIEW_ROM: - MMU.CART_ROM[address] = value; + gameInfo.romdata[address] = value; break; case MEMVIEW_FULL: MMU_write8(ARMCPU_ARM9, address, value); @@ -220,7 +221,7 @@ void memWrite16(MemRegionType regionType, HWAddressType address, u16 value) *((u16*)&MMU.fw.data[address]) = value; break; case MEMVIEW_ROM: - *((u16*)&MMU.CART_ROM[address]) = value; + *((u16*)&gameInfo.romdata[address]) = value; break; case MEMVIEW_FULL: MMU_write16(ARMCPU_ARM9, address, value); @@ -243,7 +244,7 @@ void memWrite32(MemRegionType regionType, HWAddressType address, u32 value) *((u32*)&MMU.fw.data[address]) = value; break; case MEMVIEW_ROM: - *((u32*)&MMU.CART_ROM[address]) = value; + *((u32*)&gameInfo.romdata[address]) = value; break; case MEMVIEW_FULL: MMU_write32(ARMCPU_ARM9, address, value); @@ -271,8 +272,9 @@ CMemView::CMemView(MemRegionType memRegion, u32 start_address) s_memoryRegions.push_back(s_arm9Region); s_memoryRegions.push_back(s_arm7Region); s_memoryRegions.push_back(s_firmwareRegion); - s_memoryRegions.push_back(s_RomRegion); s_memoryRegions.push_back(s_fullRegion); + if (CommonSettings.loadToMemory) + s_memoryRegions.push_back(s_RomRegion); } PostInitialize(); diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index 190fdd21c..d584cb661 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -934,8 +934,11 @@ #define IDC_VIEW_PADTOINTEGER 40107 #define ID_TOOLS_VIEWFSNITRO 40108 #define ID_EXTRACTFILE 40109 +#define ID_CONFIG_LOADROMTYPE 40109 #define ID_EXTRACTALL 40110 +#define ID_LOADTORAM 40110 #define ID_CLOSE 40111 +#define ID_STREAMFROMDISK 40111 #define ID_FSNITRO_VIEW 40112 #define ID_LABEL_HK3b 44670 #define ID_LABEL_HK3c 44671 @@ -1043,7 +1046,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 128 -#define _APS_NEXT_COMMAND_VALUE 40109 +#define _APS_NEXT_COMMAND_VALUE 40111 #define _APS_NEXT_CONTROL_VALUE 1057 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index 6edc6d16137cb65b8f650a5d9cb39c51726cf3f6..ff6f7ab884ed9c549b8a1389821b29e344a142c5 100644 GIT binary patch delta 223 zcmdnCneWs#zJ?aY7N#xC9%9oCUNQ;r`!M7)Br>EhC@=&u_%rxU&y-_Uoc`cElht&a z^Gsa9B|vEf1~s6hBZDuHrNNK}R9?(b!jK9kb%0{IKs7}`KFA1=Doq9@29UTXg9}4E zgAdSHM<6Q%NQ334D_&wUR15|h0n(HR)UE(DvF(F_2RwhBN1