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
15 changed files with 7321 additions and 7127 deletions

View File

@ -16,6 +16,7 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <assert.h>
#include <stdio.h>
#include <string.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)
};
bool CartInserted;
u8* CartROM;
u32 CartROMSize;
u32 CartID;
CartCommon* Cart;
std::unique_ptr<CartCommon> Cart;
u16 OpenBusDecay;
@ -124,9 +119,10 @@ CartGame::CartGame(u8* rom, u32 len) : CartCommon()
CartGame::~CartGame()
{
if (SRAM) delete[] SRAM;
delete[] ROM;
}
u32 CartGame::Checksum()
u32 CartGame::Checksum() const
{
u32 crc = CRC32(ROM, 0xC0, 0);
@ -703,8 +699,6 @@ void CartRAMExpansion::ROMWrite(u32 addr, u16 val)
bool Init()
{
CartROM = nullptr;
Cart = nullptr;
return true;
@ -712,9 +706,7 @@ bool Init()
void DeInit()
{
if (CartROM) delete[] CartROM;
if (Cart) delete Cart;
Cart = nullptr;
}
void Reset()
@ -756,36 +748,47 @@ void DoSavestate(Savestate* 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)
EjectCart();
if (romdata == nullptr)
{
Log(LogLevel::Error, "GBACart: romdata is null\n");
return nullptr;
}
CartROMSize = 0x200;
while (CartROMSize < romlen)
CartROMSize <<= 1;
if (romlen == 0)
{
Log(LogLevel::Error, "GBACart: romlen is zero\n");
return nullptr;
}
u32 cartromsize = 0x200;
while (cartromsize < romlen)
cartromsize <<= 1;
u8* cartrom = nullptr;
try
{
CartROM = new u8[CartROMSize];
cartrom = new u8[cartromsize];
}
catch (const std::bad_alloc& e)
{
Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize);
return false;
Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", cartromsize);
return nullptr;
}
memset(CartROM, 0, CartROMSize);
memcpy(CartROM, romdata, romlen);
memset(cartrom, 0, cartromsize);
memcpy(cartrom, romdata, romlen);
char gamecode[5] = { '\0' };
memcpy(&gamecode, CartROM + 0xAC, 4);
Log(LogLevel::Info, "GBA game code: %s\n", gamecode);
memcpy(&gamecode, cartrom + 0xAC, 4);
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;
}
@ -794,15 +797,15 @@ bool LoadROM(const u8* romdata, u32 romlen)
Log(LogLevel::Info, "GBA solar sensor support detected!\n");
}
CartInserted = true;
std::unique_ptr<CartCommon> cart;
if (solarsensor)
Cart = new CartGameSolarSensor(CartROM, CartROMSize);
cart = std::make_unique<CartGameSolarSensor>(cartrom, cartromsize);
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
//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
//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;
}
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)
{
if (Cart)
@ -828,34 +864,21 @@ void LoadSave(const u8* savedata, u32 savelen)
void LoadAddon(int type)
{
CartROMSize = 0;
CartROM = nullptr;
switch (type)
{
case NDS::GBAAddon_RAMExpansion:
Cart = new CartRAMExpansion();
Cart = std::make_unique<CartRAMExpansion>();
break;
default:
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
return;
}
CartInserted = true;
}
void EjectCart()
{
if (Cart) delete Cart;
Cart = nullptr;
if (CartROM) delete[] CartROM;
CartInserted = false;
CartROM = nullptr;
CartROMSize = 0;
CartID = 0;
}