mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-25 23:29:55 -06:00
Custom path support (#1333)
also including: * getting rid of shitty strings * all new, cleaner ROM handling code * base for DSi savestates * GBA slot addons (for now, memory cart)
This commit is contained in:
384
src/GBACart.cpp
384
src/GBACart.cpp
@ -18,6 +18,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "NDS.h"
|
||||
#include "GBACart.h"
|
||||
#include "CRC32.h"
|
||||
#include "Platform.h"
|
||||
@ -42,7 +43,6 @@ const char SOLAR_SENSOR_GAMECODES[10][5] =
|
||||
bool CartInserted;
|
||||
u8* CartROM;
|
||||
u32 CartROMSize;
|
||||
u32 CartCRC;
|
||||
u32 CartID;
|
||||
|
||||
CartCommon* Cart;
|
||||
@ -58,16 +58,20 @@ CartCommon::~CartCommon()
|
||||
{
|
||||
}
|
||||
|
||||
void CartCommon::Reset()
|
||||
{
|
||||
}
|
||||
|
||||
void CartCommon::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("GBCS");
|
||||
}
|
||||
|
||||
void CartCommon::LoadSave(const char* path, u32 type)
|
||||
void CartCommon::SetupSave(u32 type)
|
||||
{
|
||||
}
|
||||
|
||||
void CartCommon::RelocateSave(const char* path, bool write)
|
||||
void CartCommon::LoadSave(const u8* savedata, u32 savelen)
|
||||
{
|
||||
}
|
||||
|
||||
@ -99,22 +103,32 @@ CartGame::CartGame(u8* rom, u32 len) : CartCommon()
|
||||
{
|
||||
ROM = rom;
|
||||
ROMLength = len;
|
||||
|
||||
memset(&GPIO, 0, sizeof(GPIO));
|
||||
|
||||
SRAM = nullptr;
|
||||
SRAMFile = nullptr;
|
||||
SRAMLength = 0;
|
||||
SRAMType = S_NULL;
|
||||
SRAMFlashState = {};
|
||||
}
|
||||
|
||||
CartGame::~CartGame()
|
||||
{
|
||||
if (SRAMFile) fclose(SRAMFile);
|
||||
if (SRAM) delete[] SRAM;
|
||||
}
|
||||
|
||||
u32 CartGame::Checksum()
|
||||
{
|
||||
u32 crc = CRC32(ROM, 0xC0, 0);
|
||||
|
||||
// TODO: hash more contents?
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void CartGame::Reset()
|
||||
{
|
||||
memset(&GPIO, 0, sizeof(GPIO));
|
||||
|
||||
SRAM = nullptr;
|
||||
SRAMLength = 0;
|
||||
SRAMType = S_NULL;
|
||||
SRAMFlashState = {};
|
||||
}
|
||||
|
||||
void CartGame::DoSavestate(Savestate* file)
|
||||
{
|
||||
CartCommon::DoSavestate(file);
|
||||
@ -123,8 +137,6 @@ void CartGame::DoSavestate(Savestate* file)
|
||||
file->Var16(&GPIO.data);
|
||||
file->Var16(&GPIO.direction);
|
||||
|
||||
// logic mostly copied from NDSCart_SRAM
|
||||
|
||||
u32 oldlen = SRAMLength;
|
||||
|
||||
file->Var32(&SRAMLength);
|
||||
@ -133,6 +145,7 @@ void CartGame::DoSavestate(Savestate* file)
|
||||
{
|
||||
// reallocate save memory
|
||||
if (oldlen) delete[] SRAM;
|
||||
SRAM = nullptr;
|
||||
if (SRAMLength) SRAM = new u8[SRAMLength];
|
||||
}
|
||||
if (SRAMLength)
|
||||
@ -144,9 +157,7 @@ void CartGame::DoSavestate(Savestate* file)
|
||||
{
|
||||
// no save data, clear the current state
|
||||
SRAMType = SaveType::S_NULL;
|
||||
if (SRAMFile) fclose(SRAMFile);
|
||||
SRAM = nullptr;
|
||||
SRAMFile = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -158,27 +169,24 @@ void CartGame::DoSavestate(Savestate* file)
|
||||
file->Var8(&SRAMFlashState.state);
|
||||
|
||||
file->Var8((u8*)&SRAMType);
|
||||
|
||||
if ((!file->Saving) && SRAM)
|
||||
Platform::WriteGBASave(SRAM, SRAMLength, 0, SRAMLength);
|
||||
}
|
||||
|
||||
void CartGame::LoadSave(const char* path, u32 type)
|
||||
void CartGame::SetupSave(u32 type)
|
||||
{
|
||||
if (SRAM) delete[] SRAM;
|
||||
SRAM = nullptr;
|
||||
|
||||
strncpy(SRAMPath, path, 1023);
|
||||
SRAMPath[1023] = '\0';
|
||||
SRAMLength = 0;
|
||||
// TODO: have type be determined from some list, like in NDSCart
|
||||
// and not this gross hack!!
|
||||
SRAMLength = type;
|
||||
|
||||
FILE* f = Platform::OpenFile(SRAMPath, "r+b");
|
||||
if (f)
|
||||
if (SRAMLength)
|
||||
{
|
||||
fseek(f, 0, SEEK_END);
|
||||
SRAMLength = (u32)ftell(f);
|
||||
SRAM = new u8[SRAMLength];
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(SRAM, SRAMLength, 1, f);
|
||||
|
||||
SRAMFile = f;
|
||||
memset(SRAM, 0xFF, SRAMLength);
|
||||
}
|
||||
|
||||
switch (SRAMLength)
|
||||
@ -219,26 +227,13 @@ void CartGame::LoadSave(const char* path, u32 type)
|
||||
}
|
||||
}
|
||||
|
||||
void CartGame::RelocateSave(const char* path, bool write)
|
||||
void CartGame::LoadSave(const u8* savedata, u32 savelen)
|
||||
{
|
||||
if (!write)
|
||||
{
|
||||
LoadSave(path, 0); // lazy
|
||||
return;
|
||||
}
|
||||
if (!SRAM) return;
|
||||
|
||||
strncpy(SRAMPath, path, 1023);
|
||||
SRAMPath[1023] = '\0';
|
||||
|
||||
FILE *f = Platform::OpenFile(path, "r+b");
|
||||
if (!f)
|
||||
{
|
||||
printf("GBACart_SRAM::RelocateSave: failed to create new file. fuck\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SRAMFile = f;
|
||||
fwrite(SRAM, SRAMLength, 1, SRAMFile);
|
||||
u32 len = std::min(savelen, SRAMLength);
|
||||
memcpy(SRAM, savedata, len);
|
||||
Platform::WriteGBASave(savedata, len, 0, len);
|
||||
}
|
||||
|
||||
u16 CartGame::ROMRead(u32 addr)
|
||||
@ -469,11 +464,7 @@ void CartGame::SRAMWrite_FLASH(u32 addr, u8 val)
|
||||
u32 start_addr = addr + 0x10000 * SRAMFlashState.bank;
|
||||
memset((u8*)&SRAM[start_addr], 0xFF, 0x1000);
|
||||
|
||||
if (SRAMFile)
|
||||
{
|
||||
fseek(SRAMFile, start_addr, SEEK_SET);
|
||||
fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile);
|
||||
}
|
||||
Platform::WriteGBASave(SRAM, SRAMLength, start_addr, 0x1000);
|
||||
}
|
||||
SRAMFlashState.state = 0;
|
||||
SRAMFlashState.cmd = 0;
|
||||
@ -531,11 +522,8 @@ void CartGame::SRAMWrite_SRAM(u32 addr, u8 val)
|
||||
{
|
||||
*(u8*)&SRAM[addr] = val;
|
||||
|
||||
if (SRAMFile)
|
||||
{
|
||||
fseek(SRAMFile, addr, SEEK_SET);
|
||||
fwrite((u8*)&SRAM[addr], 1, 1, SRAMFile);
|
||||
}
|
||||
// TODO: optimize this!!
|
||||
Platform::WriteGBASave(SRAM, SRAMLength, addr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,16 +532,20 @@ const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 1
|
||||
|
||||
CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len)
|
||||
{
|
||||
LightEdge = false;
|
||||
LightCounter = 0;
|
||||
LightSample = 0xFF;
|
||||
LightLevel = 0;
|
||||
}
|
||||
|
||||
CartGameSolarSensor::~CartGameSolarSensor()
|
||||
{
|
||||
}
|
||||
|
||||
void CartGameSolarSensor::Reset()
|
||||
{
|
||||
LightEdge = false;
|
||||
LightCounter = 0;
|
||||
LightSample = 0xFF;
|
||||
LightLevel = 0;
|
||||
}
|
||||
|
||||
void CartGameSolarSensor::DoSavestate(Savestate* file)
|
||||
{
|
||||
CartGame::DoSavestate(file);
|
||||
@ -608,6 +600,86 @@ void CartGameSolarSensor::ProcessGPIO()
|
||||
}
|
||||
|
||||
|
||||
CartRAMExpansion::CartRAMExpansion() : CartCommon()
|
||||
{
|
||||
}
|
||||
|
||||
CartRAMExpansion::~CartRAMExpansion()
|
||||
{
|
||||
}
|
||||
|
||||
void CartRAMExpansion::Reset()
|
||||
{
|
||||
memset(RAM, 0xFF, sizeof(RAM));
|
||||
RAMEnable = 1;
|
||||
}
|
||||
|
||||
void CartRAMExpansion::DoSavestate(Savestate* file)
|
||||
{
|
||||
CartCommon::DoSavestate(file);
|
||||
|
||||
file->VarArray(RAM, sizeof(RAM));
|
||||
file->Var16(&RAMEnable);
|
||||
}
|
||||
|
||||
u16 CartRAMExpansion::ROMRead(u32 addr)
|
||||
{
|
||||
addr &= 0x01FFFFFF;
|
||||
|
||||
if (addr < 0x01000000)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xB0: return 0xFFFF;
|
||||
case 0xB2: return 0x0000;
|
||||
case 0xB4: return 0x2400;
|
||||
case 0xB6: return 0x2424;
|
||||
case 0xB8: return 0xFFFF;
|
||||
case 0xBA: return 0xFFFF;
|
||||
case 0xBC: return 0xFFFF;
|
||||
case 0xBE: return 0x7FFF;
|
||||
|
||||
case 0x1FFFC: return 0xFFFF;
|
||||
case 0x1FFFE: return 0x7FFF;
|
||||
|
||||
case 0x240000: return RAMEnable;
|
||||
case 0x240002: return 0x0000;
|
||||
}
|
||||
|
||||
return 0xFFFF;
|
||||
}
|
||||
else if (addr < 0x01800000)
|
||||
{
|
||||
if (!RAMEnable) return 0xFFFF;
|
||||
|
||||
return *(u16*)&RAM[addr & 0x7FFFFF];
|
||||
}
|
||||
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
void CartRAMExpansion::ROMWrite(u32 addr, u16 val)
|
||||
{
|
||||
addr &= 0x01FFFFFF;
|
||||
|
||||
if (addr < 0x01000000)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x240000:
|
||||
RAMEnable = val & 0x0001;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (addr < 0x01800000)
|
||||
{
|
||||
if (!RAMEnable) return;
|
||||
|
||||
*(u16*)&RAM[addr & 0x7FFFFF] = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Init()
|
||||
{
|
||||
CartROM = nullptr;
|
||||
@ -626,86 +698,65 @@ void DeInit()
|
||||
|
||||
void Reset()
|
||||
{
|
||||
// Do not reset cartridge ROM.
|
||||
// Prefer keeping the inserted cartridge on reset.
|
||||
// This allows resetting a DS game without losing GBA state,
|
||||
// and resetting to firmware without the slot being emptied.
|
||||
// The Stop function will clear the cartridge state via Eject().
|
||||
|
||||
// OpenBusDecay doesn't need to be reset, either, as it will be set
|
||||
// through NDS::SetGBASlotTimings().
|
||||
}
|
||||
|
||||
void Eject()
|
||||
{
|
||||
if (CartROM) delete[] CartROM;
|
||||
|
||||
CartInserted = false;
|
||||
CartROM = NULL;
|
||||
CartROMSize = 0;
|
||||
CartCRC = 0;
|
||||
CartID = 0;
|
||||
|
||||
if (Cart) delete Cart;
|
||||
Cart = nullptr;
|
||||
|
||||
Reset();
|
||||
if (Cart) Cart->Reset();
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("GBAC"); // Game Boy Advance Cartridge
|
||||
|
||||
// logic mostly copied from NDSCart
|
||||
// little state here
|
||||
// no need to save OpenBusDecay, it will be set later
|
||||
|
||||
// first we need to reload the cart itself,
|
||||
// since unlike with DS, it's not loaded in advance
|
||||
|
||||
file->Var32(&CartROMSize);
|
||||
if (!CartROMSize) // no GBA cartridge state? nothing to do here
|
||||
u32 carttype = 0;
|
||||
u32 cartchk = 0;
|
||||
if (Cart)
|
||||
{
|
||||
// do eject the cartridge if something is inserted
|
||||
Eject();
|
||||
return;
|
||||
carttype = Cart->Type();
|
||||
cartchk = Cart->Checksum();
|
||||
}
|
||||
|
||||
u32 oldCRC = CartCRC;
|
||||
file->Var32(&CartCRC);
|
||||
|
||||
if (CartCRC != oldCRC)
|
||||
if (file->Saving)
|
||||
{
|
||||
// delete and reallocate ROM so that it is zero-padded to its full length
|
||||
if (CartROM) delete[] CartROM;
|
||||
CartROM = new u8[CartROMSize];
|
||||
file->Var32(&carttype);
|
||||
file->Var32(&cartchk);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 savetype;
|
||||
file->Var32(&savetype);
|
||||
if (savetype != carttype) return;
|
||||
|
||||
// only save/load the cartridge header
|
||||
//
|
||||
// GBA connectivity on DS mainly involves identifying the title currently
|
||||
// inserted, reading save data, and issuing commands intercepted here
|
||||
// (e.g. solar sensor signals). we don't know of any case where GBA ROM is
|
||||
// read directly from DS software. therefore, it is more practical, both
|
||||
// from the development and user experience perspectives, to avoid dealing
|
||||
// with file dependencies, and store a small portion of ROM data that should
|
||||
// satisfy the needs of all known software that reads from the GBA slot.
|
||||
//
|
||||
// note: in case of a state load, only the cartridge header is restored, but
|
||||
// the rest of the ROM data is only cleared (zero-initialized) if the CRC
|
||||
// differs. Therefore, loading the GBA cartridge associated with the save state
|
||||
// in advance will maintain access to the full ROM contents.
|
||||
file->VarArray(CartROM, 192);
|
||||
|
||||
CartInserted = true; // known, because CartROMSize > 0
|
||||
file->Var32(&CartCRC);
|
||||
file->Var32(&CartID);
|
||||
|
||||
// now do the rest
|
||||
u32 savechk;
|
||||
file->Var32(&savechk);
|
||||
if (savechk != cartchk) return;
|
||||
}
|
||||
|
||||
if (Cart) Cart->DoSavestate(file);
|
||||
}
|
||||
|
||||
void LoadROMCommon(const char *sram)
|
||||
bool LoadROM(const u8* romdata, u32 romlen)
|
||||
{
|
||||
if (CartInserted)
|
||||
EjectCart();
|
||||
|
||||
CartROMSize = 0x200;
|
||||
while (CartROMSize < romlen)
|
||||
CartROMSize <<= 1;
|
||||
|
||||
try
|
||||
{
|
||||
CartROM = new u8[CartROMSize];
|
||||
}
|
||||
catch (const std::bad_alloc& e)
|
||||
{
|
||||
printf("GBACart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(CartROM, 0, CartROMSize);
|
||||
memcpy(CartROM, romdata, romlen);
|
||||
|
||||
char gamecode[5] = { '\0' };
|
||||
memcpy(&gamecode, CartROM + 0xAC, 4);
|
||||
printf("GBA game code: %s\n", gamecode);
|
||||
@ -722,9 +773,6 @@ void LoadROMCommon(const char *sram)
|
||||
printf("GBA solar sensor support detected!\n");
|
||||
}
|
||||
|
||||
CartCRC = CRC32(CartROM, CartROMSize);
|
||||
printf("GBA ROM CRC32: %08X\n", CartCRC);
|
||||
|
||||
CartInserted = true;
|
||||
|
||||
if (solarsensor)
|
||||
@ -732,61 +780,61 @@ void LoadROMCommon(const char *sram)
|
||||
else
|
||||
Cart = new CartGame(CartROM, CartROMSize);
|
||||
|
||||
if (Cart)
|
||||
Cart->Reset();
|
||||
|
||||
// save
|
||||
printf("GBA save file: %s\n", sram);
|
||||
//printf("GBA save file: %s\n", sram);
|
||||
|
||||
// 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);
|
||||
|
||||
bool LoadROM(const char* path, const char* sram)
|
||||
{
|
||||
FILE* f = Platform::OpenFile(path, "rb");
|
||||
if (!f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CartInserted)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
u32 len = (u32)ftell(f);
|
||||
|
||||
CartROMSize = 0x200;
|
||||
while (CartROMSize < len)
|
||||
CartROMSize <<= 1;
|
||||
|
||||
CartROM = new u8[CartROMSize];
|
||||
memset(CartROM, 0, CartROMSize);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(CartROM, 1, len, f);
|
||||
fclose(f);
|
||||
|
||||
LoadROMCommon(sram);
|
||||
// TODO: setup cart save here! from a list or something
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadROM(const u8* romdata, u32 filelength, const char *sram)
|
||||
void LoadSave(const u8* savedata, u32 savelen)
|
||||
{
|
||||
CartROMSize = 0x200;
|
||||
while (CartROMSize < filelength)
|
||||
CartROMSize <<= 1;
|
||||
if (Cart)
|
||||
{
|
||||
// gross hack
|
||||
Cart->SetupSave(savelen);
|
||||
|
||||
CartROM = new u8[CartROMSize];
|
||||
memcpy(CartROM, romdata, filelength);
|
||||
|
||||
LoadROMCommon(sram);
|
||||
|
||||
return true;
|
||||
Cart->LoadSave(savedata, savelen);
|
||||
}
|
||||
}
|
||||
|
||||
void RelocateSave(const char* path, bool write)
|
||||
void LoadAddon(int type)
|
||||
{
|
||||
if (Cart) Cart->RelocateSave(path, write);
|
||||
CartROMSize = 0;
|
||||
CartROM = nullptr;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case NDS::GBAAddon_RAMExpansion:
|
||||
Cart = new CartRAMExpansion();
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("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;
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user