Split the cart parsing and loading steps (#1707)

* Split ROMList into a .cpp file

- Its definition in ROMList.h was causing multiple-definition linker errors
- Introduce ROMListSize, since you can't take a sizeof() of an extern declaration
- Mark ROMList and ROMListSize as const

* Update ReadROMParams to accommodate ROMList changes

* Split parsing and loading of NDS ROMs

- Introduce an NDSCartData class for parsing a ROM file
- Introduce InsertROM for loading a NDS cart
- Refactor LoadROM to use NDSCartData and InsertROM under the hood

* Reset cart state and initialize save memory in the NDSCartData constructor

- Otherwise there's no way to know about SRAM-specific attributes before inserting the game

* Add a comment to NDSCartData

* First crack at splitting parsing and loading for GBACart

* Add some logging calls for encrypting the secure area

* Log the XXH64 hash of the inserted NDS ROM

* Log the XXH64 hash of the secure area after decryption

* Add some logging to Key1_LoadKeyBuf

* Re-encrypt the secure area when inserting the cart, not when parsing it

- This way, constructing a NDSCart doesn't imply a read from the filesystem (as is done in Key1_KeyBuf)

* Load Key1_KeyBuf from memory, not from the file system

- Now that the cart's secure area isn't re-encrypted until insertion, we can expect that the BIOS will be ready at this point

* Add some helper query methods to NDSHeader

* Query the DSi region directly from the header instead of checking the ROM again

* Introduce a CartType enum

- So CartCommon::Type doesn't have to return magic numbers

* Reset the cart in NDSCart::InsertROM instead of the NDSCartData constructor

- That way the constructor doesn't rely on the config or on file I/O when loading homebrew
- This keeps the use of global state closer to one place

* Add non-const getters for the carts

* Add InsertROM overloads that accept unique_ptrs

* Fix a comment

* Rename member functions on NDSCartData and GBACartData to adhere to the convention

* Rename members on NDSCartData and GBACartData to adhere to the convention

* Fix build errors on some platforms

* Add NDSHeader::IsDSiWare

* Add a ROMListEntry parameter to the cart constructors

- To allow for looking up details of SRAM or expected ROM size

* Add some new getters to CartCommon

* Use the Header/Banner members instead of globals

* Make CartCommon abstract

- It's not supposed to be instantiated anyway

* Change the signature of CartCommon::Checksum

- It's neither overridden nor mutating

* Add some clarifying comments to NDSHeader

* Delete CartCommon::ROM in its destructor

- ParseROM copies its input and gives that copy to the cart object, so it's okay

* Add some getters to CartCommon

* Refactor NDSCart

- Get rid of NDSCartData
- Get rid of cart-specific global state within NDSCart (so registers are untouched)
- Refactor uses of removed global variables to use the Cart pointer instead
- Refactor ROMInfoDialog's icon functions to accept const arguments

* Return the cart pointer

- So *that's* why it was crashing. Whoops
- Why is this even allowed?

* Refactor GBACart

- Delete CartGame::ROM in the destructor
- Get rid of GBACartData
- Remove some global state

* Mark NDSCart::CartCommon::Type as const

* Slightly refactor GBACart::CartCommon

- Mark Type as const
- Use enum constants
- Make CartCommon itself abstract

* Mark CRC32's data parameter as const

* Mark GBACart::CartCommon::Checksum as const

* Use assert.h instead of cassert

- As demanded by the style guide

* Fix some includes to adhere to the style guide

* Get the ARM9 entry address directly from the header object

* Use more Header fields directly

* Rename some parameters to match the style guide

* Remove some unused includes

* Slightly change NDS_Header::IsHomebrew for clarity
This commit is contained in:
Jesse Talavera-Greenberg 2023-06-30 07:28:52 -04:00 committed by GitHub
parent 7b948e6ec9
commit b659bce3c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 7321 additions and 7127 deletions

View File

@ -38,6 +38,7 @@ add_library(core STATIC
NDSCart.cpp NDSCart.cpp
Platform.h Platform.h
ROMList.h ROMList.h
ROMList.cpp
FreeBIOS.h FreeBIOS.h
RTC.cpp RTC.cpp
Savestate.cpp Savestate.cpp

View File

@ -52,7 +52,7 @@ void _inittable()
} }
} }
u32 CRC32(u8 *data, int len, u32 start) u32 CRC32(const u8 *data, int len, u32 start)
{ {
if (!tableinited) if (!tableinited)
{ {

View File

@ -21,6 +21,6 @@
#include "types.h" #include "types.h"
u32 CRC32(u8* data, int len, u32 start=0); u32 CRC32(const u8* data, int len, u32 start=0);
#endif // CRC32_H #endif // CRC32_H

View File

@ -164,7 +164,7 @@ void Reset()
SCFG_Clock7 = 0x0187; SCFG_Clock7 = 0x0187;
SCFG_EXT[0] = 0x8307F100; SCFG_EXT[0] = 0x8307F100;
SCFG_EXT[1] = 0x93FFFB06; SCFG_EXT[1] = 0x93FFFB06;
SCFG_MC = 0x0010 | (~((u32)NDSCart::CartInserted)&1);//0x0011; SCFG_MC = 0x0010 | (~((u32)(NDSCart::Cart != nullptr))&1);//0x0011;
SCFG_RST = 0; SCFG_RST = 0;
DSi_DSP::SetRstLine(false); DSi_DSP::SetRstLine(false);
@ -275,11 +275,14 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv)
if ((offset == 0) || (size == 0)) if ((offset == 0) || (size == 0))
return; return;
if ((NDSCart::Header.DSiCryptoFlags & (1<<4)) || const NDSHeader& header = NDSCart::Cart->GetHeader();
(NDSCart::Header.AppFlags & (1<<7)))
if ((header.DSiCryptoFlags & (1<<4)) ||
(header.AppFlags & (1<<7)))
{ {
// dev key // dev key
memcpy(key, &NDSCart::CartROM[0], 16); const u8* cartrom = NDSCart::Cart->GetROM();
memcpy(key, &cartrom[0], 16);
} }
else else
{ {
@ -287,16 +290,16 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv)
*(u32*)&keyX[0] = 0x746E694E; *(u32*)&keyX[0] = 0x746E694E;
*(u32*)&keyX[4] = 0x6F646E65; *(u32*)&keyX[4] = 0x6F646E65;
keyX[8] = NDSCart::Header.GameCode[0]; keyX[8] = header.GameCode[0];
keyX[9] = NDSCart::Header.GameCode[1]; keyX[9] = header.GameCode[1];
keyX[10] = NDSCart::Header.GameCode[2]; keyX[10] = header.GameCode[2];
keyX[11] = NDSCart::Header.GameCode[3]; keyX[11] = header.GameCode[3];
keyX[12] = NDSCart::Header.GameCode[3]; keyX[12] = header.GameCode[3];
keyX[13] = NDSCart::Header.GameCode[2]; keyX[13] = header.GameCode[2];
keyX[14] = NDSCart::Header.GameCode[1]; keyX[14] = header.GameCode[1];
keyX[15] = NDSCart::Header.GameCode[0]; keyX[15] = header.GameCode[0];
memcpy(keyY, NDSCart::Header.DSiARM9iHash, 16); memcpy(keyY, header.DSiARM9iHash, 16);
DSi_AES::DeriveNormalKey(keyX, keyY, tmp); DSi_AES::DeriveNormalKey(keyX, keyY, tmp);
} }
@ -314,28 +317,28 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv)
// than the binary area being considered // than the binary area being considered
// but I have seen modcrypt areas smaller than the ARM9i binary // but I have seen modcrypt areas smaller than the ARM9i binary
#define BINARY_GOOD(name) \ #define BINARY_GOOD(name) \
((offset >= NDSCart::Header.name##ROMOffset) && \ ((offset >= header.name##ROMOffset) && \
(offset+roundedsize) <= (NDSCart::Header.name##ROMOffset + ((NDSCart::Header.name##Size + 0xF) & ~0xF))) (offset+roundedsize) <= (header.name##ROMOffset + ((header.name##Size + 0xF) & ~0xF)))
if (BINARY_GOOD(ARM9)) if (BINARY_GOOD(ARM9))
{ {
binaryaddr = NDSCart::Header.ARM9RAMAddress; binaryaddr = header.ARM9RAMAddress;
binarysize = NDSCart::Header.ARM9Size; binarysize = header.ARM9Size;
} }
else if (BINARY_GOOD(ARM7)) else if (BINARY_GOOD(ARM7))
{ {
binaryaddr = NDSCart::Header.ARM7RAMAddress; binaryaddr = header.ARM7RAMAddress;
binarysize = NDSCart::Header.ARM7Size; binarysize = header.ARM7Size;
} }
else if (BINARY_GOOD(DSiARM9i)) else if (BINARY_GOOD(DSiARM9i))
{ {
binaryaddr = NDSCart::Header.DSiARM9iRAMAddress; binaryaddr = header.DSiARM9iRAMAddress;
binarysize = NDSCart::Header.DSiARM9iSize; binarysize = header.DSiARM9iSize;
} }
else if (BINARY_GOOD(DSiARM7i)) else if (BINARY_GOOD(DSiARM7i))
{ {
binaryaddr = NDSCart::Header.DSiARM7iRAMAddress; binaryaddr = header.DSiARM7iRAMAddress;
binarysize = NDSCart::Header.DSiARM7iSize; binarysize = header.DSiARM7iSize;
} }
else else
return; return;
@ -365,9 +368,12 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv)
void SetupDirectBoot() void SetupDirectBoot()
{ {
bool dsmode = false; bool dsmode = false;
NDSHeader& header = NDSCart::Cart->GetHeader();
const u8* cartrom = NDSCart::Cart->GetROM();
u32 cartid = NDSCart::Cart->ID();
// TODO: add controls for forcing DS or DSi mode? // TODO: add controls for forcing DS or DSi mode?
if (!(NDSCart::Header.UnitCode & 0x02)) if (!(header.UnitCode & 0x02))
dsmode = true; dsmode = true;
if (dsmode) if (dsmode)
@ -404,7 +410,7 @@ void SetupDirectBoot()
MBK[1][8] = 0; MBK[1][8] = 0;
u32 mbk[12]; u32 mbk[12];
memcpy(mbk, &NDSCart::CartROM[0x180], 12*4); memcpy(mbk, &cartrom[0x180], 12*4);
MapNWRAM_A(0, mbk[0] & 0xFF); MapNWRAM_A(0, mbk[0] & 0xFF);
MapNWRAM_A(1, (mbk[0] >> 8) & 0xFF); MapNWRAM_A(1, (mbk[0] >> 8) & 0xFF);
@ -442,7 +448,7 @@ void SetupDirectBoot()
NDS::MapSharedWRAM(mbk[11] >> 24); NDS::MapSharedWRAM(mbk[11] >> 24);
if (!(NDSCart::Header.AppFlags & (1<<0))) if (!(header.AppFlags & (1<<0)))
DSi_SPI_TSC::SetMode(0x00); DSi_SPI_TSC::SetMode(0x00);
} }
@ -453,21 +459,21 @@ void SetupDirectBoot()
{ {
for (u32 i = 0; i < 0x170; i+=4) for (u32 i = 0; i < 0x170; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[i]; u32 tmp = *(u32*)&cartrom[i];
ARM9Write32(0x027FFE00+i, tmp); ARM9Write32(0x027FFE00+i, tmp);
} }
ARM9Write32(0x027FF800, NDSCart::CartID); ARM9Write32(0x027FF800, cartid);
ARM9Write32(0x027FF804, NDSCart::CartID); ARM9Write32(0x027FF804, cartid);
ARM9Write16(0x027FF808, NDSCart::Header.HeaderCRC16); ARM9Write16(0x027FF808, header.HeaderCRC16);
ARM9Write16(0x027FF80A, NDSCart::Header.SecureAreaCRC16); ARM9Write16(0x027FF80A, header.SecureAreaCRC16);
ARM9Write16(0x027FF850, 0x5835); ARM9Write16(0x027FF850, 0x5835);
ARM9Write32(0x027FFC00, NDSCart::CartID); ARM9Write32(0x027FFC00, cartid);
ARM9Write32(0x027FFC04, NDSCart::CartID); ARM9Write32(0x027FFC04, cartid);
ARM9Write16(0x027FFC08, NDSCart::Header.HeaderCRC16); ARM9Write16(0x027FFC08, header.HeaderCRC16);
ARM9Write16(0x027FFC0A, NDSCart::Header.SecureAreaCRC16); ARM9Write16(0x027FFC0A, header.SecureAreaCRC16);
ARM9Write16(0x027FFC10, 0x5835); ARM9Write16(0x027FFC10, 0x5835);
ARM9Write16(0x027FFC30, 0xFFFF); ARM9Write16(0x027FFC30, 0xFFFF);
@ -480,14 +486,14 @@ void SetupDirectBoot()
for (u32 i = 0; i < 0x160; i+=4) for (u32 i = 0; i < 0x160; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[i]; u32 tmp = *(u32*)&cartrom[i];
ARM9Write32(0x02FFFA80+i, tmp); ARM9Write32(0x02FFFA80+i, tmp);
ARM9Write32(0x02FFFE00+i, tmp); ARM9Write32(0x02FFFE00+i, tmp);
} }
for (u32 i = 0; i < 0x1000; i+=4) for (u32 i = 0; i < 0x1000; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[i]; u32 tmp = *(u32*)&cartrom[i];
ARM9Write32(0x02FFC000+i, tmp); ARM9Write32(0x02FFC000+i, tmp);
ARM9Write32(0x02FFE000+i, tmp); ARM9Write32(0x02FFE000+i, tmp);
} }
@ -535,7 +541,7 @@ void SetupDirectBoot()
// TODO: the shit at 02FFD7B0..02FFDC00 // TODO: the shit at 02FFD7B0..02FFDC00
// and some other extra shit? // and some other extra shit?
ARM9Write32(0x02FFFC00, NDSCart::CartID); ARM9Write32(0x02FFFC00, cartid);
ARM9Write16(0x02FFFC40, 0x0001); // boot indicator ARM9Write16(0x02FFFC40, 0x0001); // boot indicator
ARM9Write8(0x02FFFDFA, DSi_BPTWL::GetBootFlag() | 0x80); ARM9Write8(0x02FFFDFA, DSi_BPTWL::GetBootFlag() | 0x80);
@ -547,56 +553,56 @@ void SetupDirectBoot()
u32 arm9start = 0; u32 arm9start = 0;
// load the ARM9 secure area // load the ARM9 secure area
if (NDSCart::Header.ARM9ROMOffset >= 0x4000 && NDSCart::Header.ARM9ROMOffset < 0x8000) if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000)
{ {
u8 securearea[0x800]; u8 securearea[0x800];
NDSCart::DecryptSecureArea(securearea); NDSCart::DecryptSecureArea(securearea);
for (u32 i = 0; i < 0x800; i+=4) for (u32 i = 0; i < 0x800; i+=4)
{ {
ARM9Write32(NDSCart::Header.ARM9RAMAddress+i, *(u32*)&securearea[i]); ARM9Write32(header.ARM9RAMAddress+i, *(u32*)&securearea[i]);
arm9start += 4; arm9start += 4;
} }
} }
for (u32 i = arm9start; i < NDSCart::Header.ARM9Size; i+=4) for (u32 i = arm9start; i < header.ARM9Size; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM9ROMOffset+i]; u32 tmp = *(u32*)&cartrom[header.ARM9ROMOffset+i];
ARM9Write32(NDSCart::Header.ARM9RAMAddress+i, tmp); ARM9Write32(header.ARM9RAMAddress+i, tmp);
} }
for (u32 i = 0; i < NDSCart::Header.ARM7Size; i+=4) for (u32 i = 0; i < header.ARM7Size; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM7ROMOffset+i]; u32 tmp = *(u32*)&cartrom[header.ARM7ROMOffset+i];
ARM7Write32(NDSCart::Header.ARM7RAMAddress+i, tmp); ARM7Write32(header.ARM7RAMAddress+i, tmp);
} }
if ((!dsmode) && (NDSCart::Header.DSiCryptoFlags & (1<<0))) if ((!dsmode) && (header.DSiCryptoFlags & (1<<0)))
{ {
// load DSi-specific regions // load DSi-specific regions
for (u32 i = 0; i < NDSCart::Header.DSiARM9iSize; i+=4) for (u32 i = 0; i < header.DSiARM9iSize; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.DSiARM9iROMOffset+i]; u32 tmp = *(u32*)&cartrom[header.DSiARM9iROMOffset+i];
ARM9Write32(NDSCart::Header.DSiARM9iRAMAddress+i, tmp); ARM9Write32(header.DSiARM9iRAMAddress+i, tmp);
} }
for (u32 i = 0; i < NDSCart::Header.DSiARM7iSize; i+=4) for (u32 i = 0; i < header.DSiARM7iSize; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.DSiARM7iROMOffset+i]; u32 tmp = *(u32*)&cartrom[header.DSiARM7iROMOffset+i];
ARM7Write32(NDSCart::Header.DSiARM7iRAMAddress+i, tmp); ARM7Write32(header.DSiARM7iRAMAddress+i, tmp);
} }
// decrypt any modcrypt areas // decrypt any modcrypt areas
if (NDSCart::Header.DSiCryptoFlags & (1<<1)) if (header.DSiCryptoFlags & (1<<1))
{ {
DecryptModcryptArea(NDSCart::Header.DSiModcrypt1Offset, DecryptModcryptArea(header.DSiModcrypt1Offset,
NDSCart::Header.DSiModcrypt1Size, header.DSiModcrypt1Size,
NDSCart::Header.DSiARM9Hash); header.DSiARM9Hash);
DecryptModcryptArea(NDSCart::Header.DSiModcrypt2Offset, DecryptModcryptArea(header.DSiModcrypt2Offset,
NDSCart::Header.DSiModcrypt2Size, header.DSiModcrypt2Size,
NDSCart::Header.DSiARM7Hash); header.DSiARM7Hash);
} }
} }

View File

@ -16,6 +16,7 @@
with melonDS. If not, see http://www.gnu.org/licenses/. with melonDS. If not, see http://www.gnu.org/licenses/.
*/ */
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "NDS.h" #include "NDS.h"
@ -41,13 +42,7 @@ const char SOLAR_SENSOR_GAMECODES[10][5] =
"A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample)
}; };
std::unique_ptr<CartCommon> Cart;
bool CartInserted;
u8* CartROM;
u32 CartROMSize;
u32 CartID;
CartCommon* Cart;
u16 OpenBusDecay; u16 OpenBusDecay;
@ -124,9 +119,10 @@ CartGame::CartGame(u8* rom, u32 len) : CartCommon()
CartGame::~CartGame() CartGame::~CartGame()
{ {
if (SRAM) delete[] SRAM; if (SRAM) delete[] SRAM;
delete[] ROM;
} }
u32 CartGame::Checksum() u32 CartGame::Checksum() const
{ {
u32 crc = CRC32(ROM, 0xC0, 0); u32 crc = CRC32(ROM, 0xC0, 0);
@ -703,8 +699,6 @@ void CartRAMExpansion::ROMWrite(u32 addr, u16 val)
bool Init() bool Init()
{ {
CartROM = nullptr;
Cart = nullptr; Cart = nullptr;
return true; return true;
@ -712,9 +706,7 @@ bool Init()
void DeInit() void DeInit()
{ {
if (CartROM) delete[] CartROM; Cart = nullptr;
if (Cart) delete Cart;
} }
void Reset() void Reset()
@ -756,36 +748,47 @@ void DoSavestate(Savestate* file)
if (Cart) Cart->DoSavestate(file); if (Cart) Cart->DoSavestate(file);
} }
bool LoadROM(const u8* romdata, u32 romlen)
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
{ {
if (CartInserted) if (romdata == nullptr)
EjectCart(); {
Log(LogLevel::Error, "GBACart: romdata is null\n");
return nullptr;
}
CartROMSize = 0x200; if (romlen == 0)
while (CartROMSize < romlen) {
CartROMSize <<= 1; Log(LogLevel::Error, "GBACart: romlen is zero\n");
return nullptr;
}
u32 cartromsize = 0x200;
while (cartromsize < romlen)
cartromsize <<= 1;
u8* cartrom = nullptr;
try try
{ {
CartROM = new u8[CartROMSize]; cartrom = new u8[cartromsize];
} }
catch (const std::bad_alloc& e) catch (const std::bad_alloc& e)
{ {
Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize); Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", cartromsize);
return false;
return nullptr;
} }
memset(CartROM, 0, CartROMSize); memset(cartrom, 0, cartromsize);
memcpy(CartROM, romdata, romlen); memcpy(cartrom, romdata, romlen);
char gamecode[5] = { '\0' }; char gamecode[5] = { '\0' };
memcpy(&gamecode, CartROM + 0xAC, 4); memcpy(&gamecode, cartrom + 0xAC, 4);
Log(LogLevel::Info, "GBA game code: %s\n", gamecode);
bool solarsensor = false; bool solarsensor = false;
for (size_t i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++) for (const char* i : SOLAR_SENSOR_GAMECODES)
{ {
if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) if (strcmp(gamecode, i) == 0)
solarsensor = true; solarsensor = true;
} }
@ -794,15 +797,15 @@ bool LoadROM(const u8* romdata, u32 romlen)
Log(LogLevel::Info, "GBA solar sensor support detected!\n"); Log(LogLevel::Info, "GBA solar sensor support detected!\n");
} }
CartInserted = true; std::unique_ptr<CartCommon> cart;
if (solarsensor) if (solarsensor)
Cart = new CartGameSolarSensor(CartROM, CartROMSize); cart = std::make_unique<CartGameSolarSensor>(cartrom, cartromsize);
else else
Cart = new CartGame(CartROM, CartROMSize); cart = std::make_unique<CartGame>(cartrom, cartromsize);
if (Cart) cart->Reset();
Cart->Reset();
// TODO: setup cart save here! from a list or something
// save // save
//printf("GBA save file: %s\n", sram); //printf("GBA save file: %s\n", sram);
@ -810,11 +813,44 @@ bool LoadROM(const u8* romdata, u32 romlen)
// TODO: have a list of sorts like in NDSCart? to determine the savemem type // TODO: have a list of sorts like in NDSCart? to determine the savemem type
//if (Cart) Cart->LoadSave(sram, 0); //if (Cart) Cart->LoadSave(sram, 0);
// TODO: setup cart save here! from a list or something return cart;
}
bool InsertROM(std::unique_ptr<CartCommon>&& cart)
{
if (!cart) {
Log(LogLevel::Error, "Failed to insert invalid GBA cart; existing cart (if any) was not ejected.\n");
return false;
}
if (Cart != nullptr)
EjectCart();
Cart = std::move(cart);
const u8* cartrom = Cart->GetROM();
if (cartrom)
{
char gamecode[5] = { '\0' };
memcpy(&gamecode, Cart->GetROM() + 0xAC, 4);
Log(LogLevel::Info, "Inserted GBA cart with game code: %s\n", gamecode);
}
else
{
Log(LogLevel::Info, "Inserted GBA cart with no game code (it's probably an accessory)\n");
}
return true; return true;
} }
bool LoadROM(const u8* romdata, u32 romlen)
{
std::unique_ptr<CartCommon> data = ParseROM(romdata, romlen);
return InsertROM(std::move(data));
}
void LoadSave(const u8* savedata, u32 savelen) void LoadSave(const u8* savedata, u32 savelen)
{ {
if (Cart) if (Cart)
@ -828,34 +864,21 @@ void LoadSave(const u8* savedata, u32 savelen)
void LoadAddon(int type) void LoadAddon(int type)
{ {
CartROMSize = 0;
CartROM = nullptr;
switch (type) switch (type)
{ {
case NDS::GBAAddon_RAMExpansion: case NDS::GBAAddon_RAMExpansion:
Cart = new CartRAMExpansion(); Cart = std::make_unique<CartRAMExpansion>();
break; break;
default: default:
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
return; return;
} }
CartInserted = true;
} }
void EjectCart() void EjectCart()
{ {
if (Cart) delete Cart;
Cart = nullptr; Cart = nullptr;
if (CartROM) delete[] CartROM;
CartInserted = false;
CartROM = nullptr;
CartROMSize = 0;
CartID = 0;
} }

View File

@ -19,12 +19,21 @@
#ifndef GBACART_H #ifndef GBACART_H
#define GBACART_H #define GBACART_H
#include <memory>
#include "types.h" #include "types.h"
#include "Savestate.h" #include "Savestate.h"
namespace GBACart namespace GBACart
{ {
enum CartType
{
Default = 0x001,
Game = 0x101,
GameSolarSensor = 0x102,
RAMExpansion = 0x201,
};
// CartCommon -- base code shared by all cart types // CartCommon -- base code shared by all cart types
class CartCommon class CartCommon
{ {
@ -32,8 +41,8 @@ public:
CartCommon(); CartCommon();
virtual ~CartCommon(); virtual ~CartCommon();
virtual u32 Type() { return 0x001; } virtual u32 Type() const = 0;
virtual u32 Checksum() { return 0; } virtual u32 Checksum() const { return 0; }
virtual void Reset(); virtual void Reset();
@ -50,6 +59,9 @@ public:
virtual u8 SRAMRead(u32 addr); virtual u8 SRAMRead(u32 addr);
virtual void SRAMWrite(u32 addr, u8 val); virtual void SRAMWrite(u32 addr, u8 val);
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
virtual u8* GetSaveMemory() const; virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const; virtual u32 GetSaveMemoryLength() const;
}; };
@ -61,8 +73,8 @@ public:
CartGame(u8* rom, u32 len); CartGame(u8* rom, u32 len);
virtual ~CartGame() override; virtual ~CartGame() override;
virtual u32 Type() override { return 0x101; } virtual u32 Type() const override { return CartType::Game; }
virtual u32 Checksum() override; virtual u32 Checksum() const override;
virtual void Reset() override; virtual void Reset() override;
@ -77,6 +89,9 @@ public:
virtual u8 SRAMRead(u32 addr) override; virtual u8 SRAMRead(u32 addr) override;
virtual void SRAMWrite(u32 addr, u8 val) override; virtual void SRAMWrite(u32 addr, u8 val) override;
[[nodiscard]] const u8* GetROM() const override { return ROM; }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
virtual u8* GetSaveMemory() const override; virtual u8* GetSaveMemory() const override;
virtual u32 GetSaveMemoryLength() const override; virtual u32 GetSaveMemoryLength() const override;
protected: protected:
@ -133,7 +148,7 @@ public:
CartGameSolarSensor(u8* rom, u32 len); CartGameSolarSensor(u8* rom, u32 len);
virtual ~CartGameSolarSensor() override; virtual ~CartGameSolarSensor() override;
virtual u32 Type() override { return 0x102; } virtual u32 Type() const override { return CartType::GameSolarSensor; }
virtual void Reset() override; virtual void Reset() override;
@ -159,7 +174,7 @@ public:
CartRAMExpansion(); CartRAMExpansion();
~CartRAMExpansion() override; ~CartRAMExpansion() override;
virtual u32 Type() override { return 0x201; } virtual u32 Type() const override { return CartType::RAMExpansion; }
void Reset() override; void Reset() override;
@ -180,9 +195,7 @@ enum
Input_SolarSensorUp, Input_SolarSensorUp,
}; };
extern bool CartInserted; extern std::unique_ptr<CartCommon> Cart;
extern u8* CartROM;
extern u32 CartROMSize;
bool Init(); bool Init();
void DeInit(); void DeInit();
@ -190,6 +203,20 @@ void Reset();
void DoSavestate(Savestate* file); void DoSavestate(Savestate* file);
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
/// @param romdata The ROM data to parse.
/// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes.
/// @returns A \c GBACart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
/// Applies the GBACartData to the emulator state and unloads an existing ROM if any.
/// Upon successful insertion, \c cart will be nullptr and the global GBACart state
/// (\c CartROM, CartInserted, etc.) will be updated.
bool InsertROM(std::unique_ptr<CartCommon>&& cart);
bool LoadROM(const u8* romdata, u32 romlen); bool LoadROM(const u8* romdata, u32 romlen);
void LoadSave(const u8* savedata, u32 savelen); void LoadSave(const u8* savedata, u32 savelen);

View File

@ -379,33 +379,37 @@ bool NeedsDirectBoot()
void SetupDirectBoot(const std::string& romname) void SetupDirectBoot(const std::string& romname)
{ {
const NDSHeader& header = NDSCart::Cart->GetHeader();
if (ConsoleType == 1) if (ConsoleType == 1)
{ {
DSi::SetupDirectBoot(); DSi::SetupDirectBoot();
} }
else else
{ {
u32 cartid = NDSCart::Cart->ID();
const u8* cartrom = NDSCart::Cart->GetROM();
MapSharedWRAM(3); MapSharedWRAM(3);
// setup main RAM data // setup main RAM data
for (u32 i = 0; i < 0x170; i+=4) for (u32 i = 0; i < 0x170; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[i]; u32 tmp = *(u32*)&cartrom[i];
ARM9Write32(0x027FFE00+i, tmp); ARM9Write32(0x027FFE00+i, tmp);
} }
ARM9Write32(0x027FF800, NDSCart::CartID); ARM9Write32(0x027FF800, cartid);
ARM9Write32(0x027FF804, NDSCart::CartID); ARM9Write32(0x027FF804, cartid);
ARM9Write16(0x027FF808, NDSCart::Header.HeaderCRC16); ARM9Write16(0x027FF808, header.HeaderCRC16);
ARM9Write16(0x027FF80A, NDSCart::Header.SecureAreaCRC16); ARM9Write16(0x027FF80A, header.SecureAreaCRC16);
ARM9Write16(0x027FF850, 0x5835); ARM9Write16(0x027FF850, 0x5835);
ARM9Write32(0x027FFC00, NDSCart::CartID); ARM9Write32(0x027FFC00, cartid);
ARM9Write32(0x027FFC04, NDSCart::CartID); ARM9Write32(0x027FFC04, cartid);
ARM9Write16(0x027FFC08, NDSCart::Header.HeaderCRC16); ARM9Write16(0x027FFC08, header.HeaderCRC16);
ARM9Write16(0x027FFC0A, NDSCart::Header.SecureAreaCRC16); ARM9Write16(0x027FFC0A, header.SecureAreaCRC16);
ARM9Write16(0x027FFC10, 0x5835); ARM9Write16(0x027FFC10, 0x5835);
ARM9Write16(0x027FFC30, 0xFFFF); ARM9Write16(0x027FFC30, 0xFFFF);
@ -414,30 +418,30 @@ void SetupDirectBoot(const std::string& romname)
u32 arm9start = 0; u32 arm9start = 0;
// load the ARM9 secure area // load the ARM9 secure area
if (NDSCart::Header.ARM9ROMOffset >= 0x4000 && NDSCart::Header.ARM9ROMOffset < 0x8000) if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000)
{ {
u8 securearea[0x800]; u8 securearea[0x800];
NDSCart::DecryptSecureArea(securearea); NDSCart::DecryptSecureArea(securearea);
for (u32 i = 0; i < 0x800; i+=4) for (u32 i = 0; i < 0x800; i+=4)
{ {
ARM9Write32(NDSCart::Header.ARM9RAMAddress+i, *(u32*)&securearea[i]); ARM9Write32(header.ARM9RAMAddress+i, *(u32*)&securearea[i]);
arm9start += 4; arm9start += 4;
} }
} }
// CHECKME: firmware seems to load this in 0x200 byte chunks // CHECKME: firmware seems to load this in 0x200 byte chunks
for (u32 i = arm9start; i < NDSCart::Header.ARM9Size; i+=4) for (u32 i = arm9start; i < header.ARM9Size; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM9ROMOffset+i]; u32 tmp = *(u32*)&cartrom[header.ARM9ROMOffset+i];
ARM9Write32(NDSCart::Header.ARM9RAMAddress+i, tmp); ARM9Write32(header.ARM9RAMAddress+i, tmp);
} }
for (u32 i = 0; i < NDSCart::Header.ARM7Size; i+=4) for (u32 i = 0; i < header.ARM7Size; i+=4)
{ {
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM7ROMOffset+i]; u32 tmp = *(u32*)&cartrom[header.ARM7ROMOffset+i];
ARM7Write32(NDSCart::Header.ARM7RAMAddress+i, tmp); ARM7Write32(header.ARM7RAMAddress+i, tmp);
} }
ARM7BIOSProt = 0x1204; ARM7BIOSProt = 0x1204;
@ -472,20 +476,20 @@ void SetupDirectBoot(const std::string& romname)
NDSCart::SetupDirectBoot(romname); NDSCart::SetupDirectBoot(romname);
ARM9->R[12] = NDSCart::Header.ARM9EntryAddress; ARM9->R[12] = header.ARM9EntryAddress;
ARM9->R[13] = 0x03002F7C; ARM9->R[13] = 0x03002F7C;
ARM9->R[14] = NDSCart::Header.ARM9EntryAddress; ARM9->R[14] = header.ARM9EntryAddress;
ARM9->R_IRQ[0] = 0x03003F80; ARM9->R_IRQ[0] = 0x03003F80;
ARM9->R_SVC[0] = 0x03003FC0; ARM9->R_SVC[0] = 0x03003FC0;
ARM7->R[12] = NDSCart::Header.ARM7EntryAddress; ARM7->R[12] = header.ARM7EntryAddress;
ARM7->R[13] = 0x0380FD80; ARM7->R[13] = 0x0380FD80;
ARM7->R[14] = NDSCart::Header.ARM7EntryAddress; ARM7->R[14] = header.ARM7EntryAddress;
ARM7->R_IRQ[0] = 0x0380FF80; ARM7->R_IRQ[0] = 0x0380FF80;
ARM7->R_SVC[0] = 0x0380FFC0; ARM7->R_SVC[0] = 0x0380FFC0;
ARM9->JumpTo(NDSCart::Header.ARM9EntryAddress); ARM9->JumpTo(header.ARM9EntryAddress);
ARM7->JumpTo(NDSCart::Header.ARM7EntryAddress); ARM7->JumpTo(header.ARM7EntryAddress);
PostFlag9 = 0x01; PostFlag9 = 0x01;
PostFlag7 = 0x01; PostFlag7 = 0x01;
@ -978,7 +982,7 @@ void EjectCart()
bool CartInserted() bool CartInserted()
{ {
return NDSCart::CartInserted; return NDSCart::Cart != nullptr;
} }
bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
@ -1666,9 +1670,10 @@ void MonitorARM9Jump(u32 addr)
// checkme: can the entrypoint addr be THUMB? // checkme: can the entrypoint addr be THUMB?
// also TODO: make it work in DSi mode // also TODO: make it work in DSi mode
if ((!RunningGame) && NDSCart::CartROM) if ((!RunningGame) && NDSCart::Cart)
{ {
if (addr == *(u32*)&NDSCart::CartROM[0x24]) const NDSHeader& header = NDSCart::Cart->GetHeader();
if (addr == header.ARM9EntryAddress)
{ {
Log(LogLevel::Info, "Game is now booting\n"); Log(LogLevel::Info, "Game is now booting\n");
RunningGame = true; RunningGame = true;

View File

@ -16,7 +16,6 @@
with melonDS. If not, see http://www.gnu.org/licenses/. with melonDS. If not, see http://www.gnu.org/licenses/.
*/ */
#include <stdio.h>
#include <string.h> #include <string.h>
#include "NDS.h" #include "NDS.h"
#include "DSi.h" #include "DSi.h"
@ -27,6 +26,7 @@
#include "Platform.h" #include "Platform.h"
#include "ROMList.h" #include "ROMList.h"
#include "melonDLDI.h" #include "melonDLDI.h"
#include "xxhash/xxhash.h"
using Platform::Log; using Platform::Log;
using Platform::LogLevel; using Platform::LogLevel;
@ -52,15 +52,7 @@ u32 TransferLen;
u32 TransferDir; u32 TransferDir;
u8 TransferCmd[8]; u8 TransferCmd[8];
bool CartInserted; std::unique_ptr<CartCommon> Cart;
u8* CartROM;
u32 CartROMSize;
u32 CartID;
NDSHeader Header;
NDSBanner Banner;
CartCommon* Cart;
u32 Key1_KeyBuf[0x412]; u32 Key1_KeyBuf[0x412];
@ -134,32 +126,36 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod)
} }
} }
void Key1_LoadKeyBuf(bool dsi) void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength)
{ {
// it is possible that this gets called before the BIOSes are loaded if (externalBios)
// so we will read from the BIOS files directly
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{ {
std::string path = Platform::GetConfigString(dsi ? Platform::DSi_BIOS7Path : Platform::BIOS7Path); u32 expected_bios_length = dsi ? 0x10000 : 0x4000;
FILE* f = Platform::OpenLocalFile(path, "rb"); if (biosLength != expected_bios_length)
if (f)
{ {
fseek(f, dsi ? 0xC6D0 : 0x0030, SEEK_SET); Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got %u bytes\n", expected_bios_length, biosLength);
fread(Key1_KeyBuf, 0x1048, 1, f); }
fclose(f); else if (bios == nullptr)
{
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got nullptr\n", expected_bios_length);
}
else
{
memcpy(Key1_KeyBuf, bios + (dsi ? 0xC6D0 : 0x0030), 0x1048);
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n");
} }
} }
else else
{ {
// well // well
memset(Key1_KeyBuf, 0, 0x1048); memset(Key1_KeyBuf, 0, 0x1048);
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
} }
} }
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength)
{ {
Key1_LoadKeyBuf(dsi); Key1_LoadKeyBuf(dsi, Platform::GetConfigBool(Platform::ExternalBIOSEnable), bios, biosLength);
u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod); if (level >= 1) Key1_ApplyKeycode(keycode, mod);
@ -194,32 +190,35 @@ void Key2_Encrypt(u8* data, u32 len)
} }
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump) CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams)
{ {
ROM = rom; ROM = rom;
ROMLength = len; ROMLength = len;
ChipID = chipid; ChipID = chipid;
ROMParams = romparams;
u8 unitcode = ROM[0x12]; memcpy(&Header, rom, sizeof(Header));
IsDSi = (unitcode & 0x02) != 0 && !badDSiDump; IsDSi = Header.IsDSi() && !badDSiDump;
DSiBase = *(u16*)&ROM[0x92] << 19; DSiBase = Header.DSiRegionStart << 19;
} }
CartCommon::~CartCommon() CartCommon::~CartCommon()
{ {
delete[] ROM;
} }
u32 CartCommon::Checksum() u32 CartCommon::Checksum() const
{ {
const NDSHeader& header = GetHeader();
u32 crc = CRC32(ROM, 0x40); u32 crc = CRC32(ROM, 0x40);
crc = CRC32(&ROM[Header.ARM9ROMOffset], Header.ARM9Size, crc); crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc);
crc = CRC32(&ROM[Header.ARM7ROMOffset], Header.ARM7Size, crc); crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc);
if (IsDSi) if (IsDSi)
{ {
crc = CRC32(&ROM[Header.DSiARM9iROMOffset], Header.DSiARM9iSize, crc); crc = CRC32(&ROM[header.DSiARM9iROMOffset], header.DSiARM9iSize, crc);
crc = CRC32(&ROM[Header.DSiARM7iROMOffset], Header.DSiARM7iSize, crc); crc = CRC32(&ROM[header.DSiARM7iROMOffset], header.DSiARM7iSize, crc);
} }
return crc; return crc;
@ -285,7 +284,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
case 0x3C: case 0x3C:
CmdEncMode = 1; CmdEncMode = 1;
Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2); Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
DSiMode = false; DSiMode = false;
return 0; return 0;
@ -293,7 +292,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
if (IsDSi) if (IsDSi)
{ {
CmdEncMode = 1; CmdEncMode = 1;
Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2); Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS));
DSiMode = true; DSiMode = true;
} }
return 0; return 0;
@ -402,8 +401,19 @@ void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset)
memcpy(data+offset, ROM+addr, len); memcpy(data+offset, ROM+addr, len);
} }
const NDSBanner* CartCommon::Banner() const
{
const NDSHeader& header = GetHeader();
size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40;
if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize))
{
return reinterpret_cast<const NDSBanner*>(ROM + header.BannerOffset);
}
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump) : CartCommon(rom, len, chipid, badDSiDump) return nullptr;
}
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) : CartCommon(rom, len, chipid, badDSiDump, romparams)
{ {
SRAM = nullptr; SRAM = nullptr;
} }
@ -865,7 +875,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
} }
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid, false) CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
{ {
} }
@ -1092,7 +1102,7 @@ void CartRetailNAND::BuildSRAMID()
} }
CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump) : CartRetail(rom, len, chipid, badDSiDump) CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams) : CartRetail(rom, len, chipid, badDSiDump, romparams)
{ {
IRVersion = irversion; IRVersion = irversion;
} }
@ -1138,7 +1148,7 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
} }
CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid, false) CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
{ {
Log(LogLevel::Info,"POKETYPE CART\n"); Log(LogLevel::Info,"POKETYPE CART\n");
} }
@ -1172,7 +1182,7 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
} }
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid, false) CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams)
{ {
SD = nullptr; SD = nullptr;
} }
@ -1225,7 +1235,7 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname)
{ {
// add the ROM to the SD volume // add the ROM to the SD volume
if (!SD->InjectFile(romname, CartROM, CartROMSize)) if (!SD->InjectFile(romname, ROM, ROMLength))
return; return;
// setup argv command line // setup argv command line
@ -1237,9 +1247,10 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname)
strncat(argv, romname.c_str(), 511); strncat(argv, romname.c_str(), 511);
argvlen = strlen(argv); argvlen = strlen(argv);
const NDSHeader& header = GetHeader();
void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32; void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32;
u32 argvbase = Header.ARM9RAMAddress + Header.ARM9Size; u32 argvbase = header.ARM9RAMAddress + header.ARM9Size;
argvbase = (argvbase + 0xF) & ~0xF; argvbase = (argvbase + 0xF) & ~0xF;
for (u32 i = 0; i <= argvlen; i+=4) for (u32 i = 0; i <= argvlen; i+=4)
@ -1457,8 +1468,6 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
bool Init() bool Init()
{ {
CartInserted = false;
CartROM = nullptr;
Cart = nullptr; Cart = nullptr;
return true; return true;
@ -1466,8 +1475,7 @@ bool Init()
void DeInit() void DeInit()
{ {
if (CartROM) delete[] CartROM; Cart = nullptr;
if (Cart) delete Cart;
} }
void Reset() void Reset()
@ -1530,14 +1538,12 @@ void DoSavestate(Savestate* file)
bool ReadROMParams(u32 gamecode, ROMListEntry* params) bool ReadROMParams(u32 gamecode, ROMListEntry* params)
{ {
u32 len = sizeof(ROMList) / sizeof(ROMListEntry);
u32 offset = 0; u32 offset = 0;
u32 chk_size = len >> 1; u32 chk_size = ROMListSize >> 1;
for (;;) for (;;)
{ {
u32 key = 0; u32 key = 0;
ROMListEntry* curentry = &ROMList[offset + chk_size]; const ROMListEntry* curentry = &ROMList[offset + chk_size];
key = curentry->GameCode; key = curentry->GameCode;
if (key == gamecode) if (key == gamecode)
@ -1562,7 +1568,7 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params)
chk_size >>= 1; chk_size >>= 1;
} }
if (offset >= len) if (offset >= ROMListSize)
{ {
return false; return false;
} }
@ -1572,21 +1578,24 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params)
void DecryptSecureArea(u8* out) void DecryptSecureArea(u8* out)
{ {
u32 gamecode = (u32)Header.GameCode[3] << 24 | const NDSHeader& header = Cart->GetHeader();
(u32)Header.GameCode[2] << 16 | const u8* cartrom = Cart->GetROM();
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
u32 arm9base = Header.ARM9ROMOffset;
memcpy(out, &CartROM[arm9base], 0x800); u32 gamecode = header.GameCodeAsU32();
u32 arm9base = header.ARM9ROMOffset;
Key1_InitKeycode(false, gamecode, 2, 2); memcpy(out, &cartrom[arm9base], 0x800);
Key1_InitKeycode(false, gamecode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
Key1_Decrypt((u32*)&out[0]); Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(false, gamecode, 3, 2); Key1_InitKeycode(false, gamecode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]); Key1_Decrypt((u32*)&out[i]);
XXH64_hash_t hash = XXH64(out, 0x800, 0);
Log(LogLevel::Debug, "Secure area post-decryption xxh64 hash: %zx\n", hash);
if (!strncmp((const char*)out, "encryObj", 8)) if (!strncmp((const char*)out, "encryObj", 8))
{ {
Log(LogLevel::Info, "Secure area decryption OK\n"); Log(LogLevel::Info, "Secure area decryption OK\n");
@ -1601,38 +1610,47 @@ void DecryptSecureArea(u8* out)
} }
} }
bool LoadROM(const u8* romdata, u32 romlen) std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
{ {
if (CartInserted) if (romdata == nullptr)
EjectCart(); {
Log(LogLevel::Error, "NDSCart: romdata is null\n");
return nullptr;
}
memset(&Header, 0, sizeof(Header)); if (romlen == 0)
memset(&Banner, 0, sizeof(Banner)); {
Log(LogLevel::Error, "NDSCart: romlen is zero\n");
return nullptr;
}
CartROMSize = 0x200; u32 cartromsize = 0x200;
while (CartROMSize < romlen) while (cartromsize < romlen)
CartROMSize <<= 1; cartromsize <<= 1; // ROM size must be a power of 2
u8* cartrom = nullptr;
try try
{ {
CartROM = new u8[CartROMSize]; cartrom = new u8[cartromsize];
} }
catch (const std::bad_alloc& e) catch (const std::bad_alloc& e)
{ {
Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize); Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", cartromsize);
return false;
return nullptr;
} }
memset(CartROM, 0, CartROMSize); // copy romdata into cartrom then zero out the remaining space
memcpy(CartROM, romdata, romlen); memcpy(cartrom, romdata, romlen);
memset(cartrom + romlen, 0, cartromsize - romlen);
memcpy(&Header, CartROM, sizeof(Header)); NDSHeader header {};
memcpy(&header, cartrom, sizeof(header));
u8 unitcode = Header.UnitCode; bool dsi = header.IsDSi();
bool dsi = (unitcode & 0x02) != 0;
bool badDSiDump = false; bool badDSiDump = false;
u32 dsiRegion = *(u32*)&CartROM[0x1B0]; u32 dsiRegion = header.DSiRegionMask;
if (dsi && dsiRegion == 0) if (dsi && dsiRegion == 0)
{ {
Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n"); Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n");
@ -1640,85 +1658,49 @@ bool LoadROM(const u8* romdata, u32 romlen)
dsi = false; dsi = false;
} }
size_t bannersize = dsi ? 0x23C0 : 0xA40; u32 gamecode = header.GameCodeAsU32();
if (Header.BannerOffset >= 0x200 && Header.BannerOffset < (CartROMSize - bannersize))
{
memcpy(&Banner, CartROM + Header.BannerOffset, bannersize);
}
Log(LogLevel::Info, "Game code: %.4s\n", Header.GameCode); u32 arm9base = header.ARM9ROMOffset;
bool homebrew = header.IsHomebrew();
u32 gamecode = (u32)Header.GameCode[3] << 24 | ROMListEntry romparams {};
(u32)Header.GameCode[2] << 16 |
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
u32 arm9base = Header.ARM9ROMOffset;
bool homebrew = (arm9base < 0x4000) || (gamecode == 0x23232323);
ROMListEntry romparams;
if (!ReadROMParams(gamecode, &romparams)) if (!ReadROMParams(gamecode, &romparams))
{ {
// set defaults // set defaults
Log(LogLevel::Warn, "ROM entry not found\n"); Log(LogLevel::Warn, "ROM entry not found for gamecode %d\n", gamecode);
romparams.GameCode = gamecode; romparams.GameCode = gamecode;
romparams.ROMSize = CartROMSize; romparams.ROMSize = cartromsize;
if (homebrew) if (homebrew)
romparams.SaveMemType = 0; // no saveRAM for homebrew romparams.SaveMemType = 0; // no saveRAM for homebrew
else else
romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME) romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME)
} }
else
Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
if (romparams.ROMSize != romlen) if (romparams.ROMSize != romlen)
Log(LogLevel::Warn, "!! bad ROM size %d (expected %d) rounded to %d\n", romlen, romparams.ROMSize, CartROMSize); Log(LogLevel::Warn, "!! bad ROM size %d (expected %d) rounded to %d\n", romlen, romparams.ROMSize, cartromsize);
// generate a ROM ID // generate a ROM ID
// note: most games don't check the actual value // note: most games don't check the actual value
// it just has to stay the same throughout gameplay // it just has to stay the same throughout gameplay
CartID = 0x000000C2; u32 cartid = 0x000000C2;
if (CartROMSize >= 1024*1024 && CartROMSize <= 128*1024*1024) if (cartromsize >= 1024 * 1024 && cartromsize <= 128 * 1024 * 1024)
CartID |= ((CartROMSize >> 20) - 1) << 8; cartid |= ((cartromsize >> 20) - 1) << 8;
else else
CartID |= (0x100 - (CartROMSize >> 28)) << 8; cartid |= (0x100 - (cartromsize >> 28)) << 8;
if (romparams.SaveMemType >= 8 && romparams.SaveMemType <= 10) if (romparams.SaveMemType >= 8 && romparams.SaveMemType <= 10)
CartID |= 0x08000000; // NAND flag cartid |= 0x08000000; // NAND flag
if (dsi) if (dsi)
CartID |= 0x40000000; cartid |= 0x40000000;
// cart ID for Jam with the Band // cart ID for Jam with the Band
// TODO: this kind of ID triggers different KEY1 phase // TODO: this kind of ID triggers different KEY1 phase
// (repeats commands a bunch of times) // (repeats commands a bunch of times)
//CartID = 0x88017FEC; //cartid = 0x88017FEC;
//CartID = 0x80007FC2; // pokémon typing adventure //cartid = 0x80007FC2; // pokémon typing adventure
Log(LogLevel::Info, "Cart ID: %08X\n", CartID);
if (arm9base >= 0x4000 && arm9base < 0x8000)
{
// reencrypt secure area if needed
if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF)
{
Log(LogLevel::Debug, "Re-encrypting cart secure area\n");
strncpy((char*)&CartROM[arm9base], "encryObj", 8);
Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&CartROM[arm9base + i]);
Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Encrypt((u32*)&CartROM[arm9base]);
}
}
CartInserted = true;
DSi::SetCartInserted(true);
u32 irversion = 0; u32 irversion = 0;
if ((gamecode & 0xFF) == 'I') if ((gamecode & 0xFF) == 'I')
@ -1729,26 +1711,83 @@ bool LoadROM(const u8* romdata, u32 romlen)
irversion = 2; // Pokémon HG/SS, B/W, B2/W2 irversion = 2; // Pokémon HG/SS, B/W, B2/W2
} }
std::unique_ptr<CartCommon> cart;
if (homebrew) if (homebrew)
Cart = new CartHomebrew(CartROM, CartROMSize, CartID); cart = std::make_unique<CartHomebrew>(cartrom, cartromsize, cartid, romparams);
else if (CartID & 0x08000000) else if (cartid & 0x08000000)
Cart = new CartRetailNAND(CartROM, CartROMSize, CartID); cart = std::make_unique<CartRetailNAND>(cartrom, cartromsize, cartid, romparams);
else if (irversion != 0) else if (irversion != 0)
Cart = new CartRetailIR(CartROM, CartROMSize, CartID, irversion, badDSiDump); cart = std::make_unique<CartRetailIR>(cartrom, cartromsize, cartid, irversion, badDSiDump, romparams);
else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx
Cart = new CartRetailBT(CartROM, CartROMSize, CartID); cart = std::make_unique<CartRetailBT>(cartrom, cartromsize, cartid, romparams);
else else
Cart = new CartRetail(CartROM, CartROMSize, CartID, badDSiDump); cart = std::make_unique<CartRetail>(cartrom, cartromsize, cartid, badDSiDump, romparams);
if (romparams.SaveMemType > 0)
cart->SetupSave(romparams.SaveMemType);
return cart;
}
// Why a move function? Because the Cart object is polymorphic,
// and cloning polymorphic objects without knowing the underlying type is annoying.
bool InsertROM(std::unique_ptr<CartCommon>&& cart)
{
if (!cart) {
Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n");
return false;
}
if (Cart) if (Cart)
Cart->Reset(); EjectCart();
if (Cart && romparams.SaveMemType > 0) Cart = std::move(cart);
Cart->SetupSave(romparams.SaveMemType);
Cart->Reset();
const NDSHeader& header = Cart->GetHeader();
const ROMListEntry romparams = Cart->GetROMParams();
const u8* cartrom = Cart->GetROM();
if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000)
{
// reencrypt secure area if needed
if (*(u32*)&cartrom[header.ARM9ROMOffset] == 0xE7FFDEFF && *(u32*)&cartrom[header.ARM9ROMOffset + 0x10] != 0xE7FFDEFF)
{
Log(LogLevel::Debug, "Re-encrypting cart secure area\n");
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS));
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
}
else
{
Log(LogLevel::Debug, "No need to re-encrypt cart secure area\n");
}
}
Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode);
Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID());
Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
DSi::SetCartInserted(true);
return true; return true;
} }
bool LoadROM(const u8* romdata, u32 romlen)
{
std::unique_ptr<CartCommon> cart = ParseROM(romdata, romlen);
return InsertROM(std::move(cart));
}
void LoadSave(const u8* savedata, u32 savelen) void LoadSave(const u8* savedata, u32 savelen)
{ {
if (Cart) if (Cart)
@ -1773,21 +1812,14 @@ u32 GetSaveMemoryLength()
void EjectCart() void EjectCart()
{ {
if (!CartInserted) return; if (!Cart) return;
// ejecting the cart triggers the gamecard IRQ // ejecting the cart triggers the gamecard IRQ
NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); NDS::SetIRQ(0, NDS::IRQ_CartIREQMC);
NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); NDS::SetIRQ(1, NDS::IRQ_CartIREQMC);
if (Cart) delete Cart;
Cart = nullptr; Cart = nullptr;
CartInserted = false;
if (CartROM) delete[] CartROM;
CartROM = nullptr;
CartROMSize = 0;
CartID = 0;
DSi::SetCartInserted(false); DSi::SetCartInserted(false);
// CHECKME: does an eject imply anything for the ROM/SPI transfer registers? // CHECKME: does an eject imply anything for the ROM/SPI transfer registers?

View File

@ -20,24 +20,36 @@
#define NDSCART_H #define NDSCART_H
#include <string> #include <string>
#include <memory>
#include "types.h" #include "types.h"
#include "Savestate.h" #include "Savestate.h"
#include "NDS_Header.h" #include "NDS_Header.h"
#include "FATStorage.h" #include "FATStorage.h"
#include "ROMList.h"
namespace NDSCart namespace NDSCart
{ {
enum CartType
{
Default = 0x001,
Retail = 0x101,
RetailNAND = 0x102,
RetailIR = 0x103,
RetailBT = 0x104,
Homebrew = 0x201,
};
// CartCommon -- base code shared by all cart types // CartCommon -- base code shared by all cart types
class CartCommon class CartCommon
{ {
public: public:
CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump); CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams);
virtual ~CartCommon(); virtual ~CartCommon();
virtual u32 Type() { return 0x001; } virtual u32 Type() const = 0;
virtual u32 Checksum(); [[nodiscard]] u32 Checksum() const;
virtual void Reset(); virtual void Reset();
virtual void SetupDirectBoot(const std::string& romname); virtual void SetupDirectBoot(const std::string& romname);
@ -55,6 +67,15 @@ public:
virtual u8* GetSaveMemory() const; virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const; virtual u32 GetSaveMemoryLength() const;
[[nodiscard]] const NDSHeader& GetHeader() const { return Header; }
[[nodiscard]] NDSHeader& GetHeader() { return Header; }
/// @return The cartridge's banner if available, or \c nullptr if not.
[[nodiscard]] const NDSBanner* Banner() const;
[[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; };
[[nodiscard]] u32 ID() const { return ChipID; }
[[nodiscard]] const u8* GetROM() const { return ROM; }
[[nodiscard]] u32 GetROMLength() const { return ROMLength; }
protected: protected:
void ReadROM(u32 addr, u32 len, u8* data, u32 offset); void ReadROM(u32 addr, u32 len, u8* data, u32 offset);
@ -69,16 +90,20 @@ protected:
u32 CmdEncMode; u32 CmdEncMode;
u32 DataEncMode; u32 DataEncMode;
// Kept separate from the ROM data so we can decrypt the modcrypt area
// without touching the overall ROM data
NDSHeader Header;
ROMListEntry ROMParams;
}; };
// CartRetail -- regular retail cart (ROM, SPI SRAM) // CartRetail -- regular retail cart (ROM, SPI SRAM)
class CartRetail : public CartCommon class CartRetail : public CartCommon
{ {
public: public:
CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump); CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams);
virtual ~CartRetail() override; virtual ~CartRetail() override;
virtual u32 Type() override { return 0x101; } virtual u32 Type() const override { return CartType::Retail; }
virtual void Reset() override; virtual void Reset() override;
@ -115,10 +140,10 @@ protected:
class CartRetailNAND : public CartRetail class CartRetailNAND : public CartRetail
{ {
public: public:
CartRetailNAND(u8* rom, u32 len, u32 chipid); CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams);
~CartRetailNAND() override; ~CartRetailNAND() override;
virtual u32 Type() override { return 0x102; } virtual u32 Type() const override { return CartType::RetailNAND; }
void Reset() override; void Reset() override;
@ -145,10 +170,10 @@ private:
class CartRetailIR : public CartRetail class CartRetailIR : public CartRetail
{ {
public: public:
CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump); CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams);
~CartRetailIR() override; ~CartRetailIR() override;
virtual u32 Type() override { return 0x103; } virtual u32 Type() const override { return CartType::RetailIR; }
void Reset() override; void Reset() override;
@ -165,10 +190,10 @@ private:
class CartRetailBT : public CartRetail class CartRetailBT : public CartRetail
{ {
public: public:
CartRetailBT(u8* rom, u32 len, u32 chipid); CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams);
~CartRetailBT() override; ~CartRetailBT() override;
virtual u32 Type() override { return 0x104; } virtual u32 Type() const override { return CartType::RetailBT; }
void Reset() override; void Reset() override;
@ -181,10 +206,10 @@ public:
class CartHomebrew : public CartCommon class CartHomebrew : public CartCommon
{ {
public: public:
CartHomebrew(u8* rom, u32 len, u32 chipid); CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams);
~CartHomebrew() override; ~CartHomebrew() override;
virtual u32 Type() override { return 0x201; } virtual u32 Type() const override { return CartType::Homebrew; }
void Reset() override; void Reset() override;
void SetupDirectBoot(const std::string& romname) override; void SetupDirectBoot(const std::string& romname) override;
@ -208,14 +233,8 @@ extern u32 ROMCnt;
extern u8 ROMCommand[8]; extern u8 ROMCommand[8];
extern bool CartInserted; /// The currently loaded NDS cart.
extern u8* CartROM; extern std::unique_ptr<CartCommon> Cart;
extern u32 CartROMSize;
extern u32 CartID;
extern NDSHeader Header;
extern NDSBanner Banner;
bool Init(); bool Init();
void DeInit(); void DeInit();
@ -225,6 +244,41 @@ void DoSavestate(Savestate* file);
void DecryptSecureArea(u8* out); void DecryptSecureArea(u8* out);
/// Parses the given ROM data and constructs a \c NDSCart::CartCommon subclass
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
/// @param romdata The ROM data to parse.
/// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes.
/// @returns A \c NDSCart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
/// Loads a Nintendo DS cart object into the emulator.
/// The emulator takes ownership of the cart object and its underlying resources
/// and re-encrypts the ROM's secure area if necessary.
/// If a cartridge is already inserted, it is first ejected
/// and its state is discarded.
/// If the provided cart is not valid,
/// then the currently-loaded ROM will not be ejected.
///
/// @param cart Movable reference to the cart.
/// @returns \c true if the cart was successfully loaded,
/// \c false otherwise.
/// @post If the cart was successfully loaded,
/// then \c cart will be \c nullptr
/// and \c Cart will contain the object that \c cart previously pointed to.
/// Otherwise, \c cart and \c Cart will be both be unchanged.
bool InsertROM(std::unique_ptr<CartCommon>&& cart);
/// Parses a ROM image and loads it into the emulator.
/// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence.
/// @param romdata Pointer to the ROM image.
/// The cart emulator maintains its own copy of this data,
/// so the caller is free to discard this data after calling this function.
/// @param romlen The length of the ROM image, in bytes.
/// @returns \c true if the ROM image was successfully loaded,
/// \c false if not.
bool LoadROM(const u8* romdata, u32 romlen); bool LoadROM(const u8* romdata, u32 romlen);
void LoadSave(const u8* savedata, u32 savelen); void LoadSave(const u8* savedata, u32 savelen);
void SetupDirectBoot(const std::string& romname); void SetupDirectBoot(const std::string& romname);

View File

@ -179,6 +179,23 @@ struct NDSHeader
u8 Reserved10[384]; u8 Reserved10[384];
u8 HeaderSignature[128]; // RSA-SHA1 across 0x000..0xDFF u8 HeaderSignature[128]; // RSA-SHA1 across 0x000..0xDFF
/// @return \c true if this header represents a DSi title
/// (either a physical cartridge or a DSiWare title).
[[nodiscard]] bool IsDSi() const { return (UnitCode & 0x02) != 0; }
[[nodiscard]] u32 GameCodeAsU32() const {
return (u32)GameCode[3] << 24 |
(u32)GameCode[2] << 16 |
(u32)GameCode[1] << 8 |
(u32)GameCode[0];
}
[[nodiscard]] bool IsHomebrew() const
{
return (ARM9ROMOffset < 0x4000) || (strncmp(GameCode, "####", 4) == 0);
}
/// @return \c true if this header represents a DSiWare title.
[[nodiscard]] bool IsDSiWare() const { return IsDSi() && DSiRegionStart == 0; }
}; };
static_assert(sizeof(NDSHeader) == 4096, "NDSHeader is not 4096 bytes!"); static_assert(sizeof(NDSHeader) == 4096, "NDSHeader is not 4096 bytes!");

6803
src/ROMList.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -43,16 +43,17 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
const NDSBanner* banner = NDSCart::Cart->Banner();
const NDSHeader& header = NDSCart::Cart->GetHeader();
u32 iconData[32 * 32]; u32 iconData[32 * 32];
ROMManager::ROMIcon(NDSCart::Banner.Icon, NDSCart::Banner.Palette, iconData); ROMManager::ROMIcon(banner->Icon, banner->Palette, iconData);
iconImage = QImage(reinterpret_cast<unsigned char*>(iconData), 32, 32, QImage::Format_ARGB32).copy(); iconImage = QImage(reinterpret_cast<unsigned char*>(iconData), 32, 32, QImage::Format_ARGB32).copy();
ui->iconImage->setPixmap(QPixmap::fromImage(iconImage)); ui->iconImage->setPixmap(QPixmap::fromImage(iconImage));
if (NDSCart::Banner.Version == 0x103) if (banner->Version == 0x103)
{ {
u32 animatedIconData[32 * 32 * 64] = {0}; u32 animatedIconData[32 * 32 * 64] = {0};
ROMManager::AnimatedROMIcon(NDSCart::Banner.DSiIcon, NDSCart::Banner.DSiPalette, NDSCart::Banner.DSiSequence, animatedIconData, animatedSequence); ROMManager::AnimatedROMIcon(banner->DSiIcon, banner->DSiPalette, banner->DSiSequence, animatedIconData, animatedSequence);
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; i++)
{ {
@ -73,44 +74,44 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI
ui->dsiIconImage->setPixmap(QPixmap::fromImage(iconImage)); ui->dsiIconImage->setPixmap(QPixmap::fromImage(iconImage));
} }
ui->iconTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle)); ui->iconTitle->setText(QString::fromUtf16(banner->EnglishTitle));
ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle)); ui->japaneseTitle->setText(QString::fromUtf16(banner->JapaneseTitle));
ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle)); ui->englishTitle->setText(QString::fromUtf16(banner->EnglishTitle));
ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle)); ui->frenchTitle->setText(QString::fromUtf16(banner->FrenchTitle));
ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle)); ui->germanTitle->setText(QString::fromUtf16(banner->GermanTitle));
ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle)); ui->italianTitle->setText(QString::fromUtf16(banner->ItalianTitle));
ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle)); ui->spanishTitle->setText(QString::fromUtf16(banner->SpanishTitle));
if (NDSCart::Banner.Version > 1) if (banner->Version > 1)
ui->chineseTitle->setText(QString::fromUtf16(NDSCart::Banner.ChineseTitle)); ui->chineseTitle->setText(QString::fromUtf16(banner->ChineseTitle));
else else
ui->chineseTitle->setText("None"); ui->chineseTitle->setText("None");
if (NDSCart::Banner.Version > 2) if (banner->Version > 2)
ui->koreanTitle->setText(QString::fromUtf16(NDSCart::Banner.KoreanTitle)); ui->koreanTitle->setText(QString::fromUtf16(banner->KoreanTitle));
else else
ui->koreanTitle->setText("None"); ui->koreanTitle->setText("None");
ui->gameTitle->setText(QString::fromLatin1(NDSCart::Header.GameTitle, 12)); ui->gameTitle->setText(QString::fromLatin1(header.GameTitle, 12));
ui->gameCode->setText(QString::fromLatin1(NDSCart::Header.GameCode, 4)); ui->gameCode->setText(QString::fromLatin1(header.GameCode, 4));
ui->makerCode->setText(QString::fromLatin1(NDSCart::Header.MakerCode, 2)); ui->makerCode->setText(QString::fromLatin1(header.MakerCode, 2));
ui->cardSize->setText(QString::number(128 << NDSCart::Header.CardSize) + " KB"); ui->cardSize->setText(QString::number(128 << header.CardSize) + " KB");
ui->arm9RomOffset->setText(IntToHex(NDSCart::Header.ARM9ROMOffset)); ui->arm9RomOffset->setText(IntToHex(header.ARM9ROMOffset));
ui->arm9EntryAddress->setText(IntToHex(NDSCart::Header.ARM9EntryAddress)); ui->arm9EntryAddress->setText(IntToHex(header.ARM9EntryAddress));
ui->arm9RamAddress->setText(IntToHex(NDSCart::Header.ARM9RAMAddress)); ui->arm9RamAddress->setText(IntToHex(header.ARM9RAMAddress));
ui->arm9Size->setText(QStringBytes(NDSCart::Header.ARM9Size)); ui->arm9Size->setText(QStringBytes(header.ARM9Size));
ui->arm7RomOffset->setText(IntToHex(NDSCart::Header.ARM7ROMOffset)); ui->arm7RomOffset->setText(IntToHex(header.ARM7ROMOffset));
ui->arm7EntryAddress->setText(IntToHex(NDSCart::Header.ARM7EntryAddress)); ui->arm7EntryAddress->setText(IntToHex(header.ARM7EntryAddress));
ui->arm7RamAddress->setText(IntToHex(NDSCart::Header.ARM7RAMAddress)); ui->arm7RamAddress->setText(IntToHex(header.ARM7RAMAddress));
ui->arm7Size->setText(QStringBytes(NDSCart::Header.ARM7Size)); ui->arm7Size->setText(QStringBytes(header.ARM7Size));
ui->fntOffset->setText(IntToHex(NDSCart::Header.FNTOffset)); ui->fntOffset->setText(IntToHex(header.FNTOffset));
ui->fntSize->setText(QStringBytes(NDSCart::Header.FNTSize)); ui->fntSize->setText(QStringBytes(header.FNTSize));
ui->fatOffset->setText(IntToHex(NDSCart::Header.FATOffset)); ui->fatOffset->setText(IntToHex(header.FATOffset));
ui->fatSize->setText(QStringBytes(NDSCart::Header.FATSize)); ui->fatSize->setText(QStringBytes(header.FATSize));
} }

View File

@ -944,7 +944,7 @@ QString GBACartLabel()
} }
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef) void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef)
{ {
int index = 0; int index = 0;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
@ -975,7 +975,7 @@ void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8) #define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0) #define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef) void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], const u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
{ {
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; i++)
{ {

View File

@ -57,9 +57,9 @@ void UndoStateLoad();
void EnableCheats(bool enable); void EnableCheats(bool enable);
ARCodeFile* GetCheatFile(); ARCodeFile* GetCheatFile();
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef); void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef);
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16],
u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], const u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64],
std::vector<int> &animatedSequenceRef); std::vector<int> &animatedSequenceRef);
} }