aboutsummaryrefslogtreecommitdiff
path: root/src/NDSCart.cpp
diff options
context:
space:
mode:
authorAdrian Siekierka <kontakt@asie.pl>2023-12-15 08:19:53 +0100
committerGitHub <noreply@github.com>2023-12-15 08:19:53 +0100
commit6f47c9ed4c0e5b1035089805f272c6965343f113 (patch)
treefadad3548cea8de7fe5ae380d4ff857909c9cb27 /src/NDSCart.cpp
parent9bfc9c08ffe88de4b54734d6fd03182c0a51e181 (diff)
Support emulating R4 Revolution/M3DS Simply cartridges. (#1854)
* Support emulating R4 Revolution/M3DS Simply cartridges. * NDSCartR4: Write state information to savestate file. * NDSCart: Use strncmp instead of strcmp for R4 detection. * NDSCartR4: stylistic improvements * NDSCartR4: rudimentary Ace3DS support * NDSCartR4: fix boot when firmware enabled * NDSCartR4: Fix for namespace changes --------- Co-authored-by: RSDuck <RSDuck@users.noreply.github.com>
Diffstat (limited to 'src/NDSCart.cpp')
-rw-r--r--src/NDSCart.cpp230
1 files changed, 120 insertions, 110 deletions
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index c0e1c5f..4474f97 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -1145,12 +1145,12 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
return 0;
}
-CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
- CartHomebrew(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard))
-{
-}
-CartHomebrew::CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
+CartSD::CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
+ CartSD(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard))
+{}
+
+CartSD::CartSD(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartCommon(std::move(rom), len, chipid, false, romparams, CartType::Homebrew),
SD(std::move(sdcard))
{
@@ -1158,112 +1158,11 @@ CartHomebrew::CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROM
// std::move on optionals usually results in an optional with a moved-from object
}
-CartHomebrew::~CartHomebrew() = default;
+CartSD::~CartSD() = default;
// The SD card is destroyed by the optional's destructor
-void CartHomebrew::Reset()
-{
- CartCommon::Reset();
-
- if (SD)
- {
- ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly());
- }
-}
-
-void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
-{
- CartCommon::SetupDirectBoot(romname, nds);
-
- if (SD)
- {
- // add the ROM to the SD volume
-
- if (!SD->InjectFile(romname, ROM.get(), ROMLength))
- return;
-
- // setup argv command line
-
- char argv[512] = {0};
- int argvlen;
-
- strncpy(argv, "fat:/", 511);
- strncat(argv, romname.c_str(), 511);
- argvlen = strlen(argv);
-
- const NDSHeader& header = GetHeader();
-
- u32 argvbase = header.ARM9RAMAddress + header.ARM9Size;
- argvbase = (argvbase + 0xF) & ~0xF;
-
- for (u32 i = 0; i <= argvlen; i+=4)
- nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]);
-
- nds.ARM9Write32(0x02FFFE70, 0x5F617267);
- nds.ARM9Write32(0x02FFFE74, argvbase);
- nds.ARM9Write32(0x02FFFE78, argvlen+1);
- // The DSi version of ARM9Write32 will be called if nds is really a DSi
- }
-}
-
-int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
-{
- if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
-
- switch (cmd[0])
- {
- case 0xB7:
- {
- u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- memset(data, 0, len);
-
- if (((addr + len - 1) >> 12) != (addr >> 12))
- {
- u32 len1 = 0x1000 - (addr & 0xFFF);
- ReadROM_B7(addr, len1, data, 0);
- ReadROM_B7(addr+len1, len-len1, data, len1);
- }
- else
- ReadROM_B7(addr, len, data, 0);
- }
- return 0;
-
- case 0xC0: // SD read
- {
- u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- if (SD) SD->ReadSectors(sector, len>>9, data);
- }
- return 0;
-
- case 0xC1: // SD write
- return 1;
-
- default:
- return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
- }
-}
-
-void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
-{
- if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
-
- // TODO: delayed SD writing? like we have for SRAM
-
- switch (cmd[0])
- {
- case 0xC1:
- {
- u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data);
- }
- break;
-
- default:
- return CartCommon::ROMCommandFinish(cmd, data, len);
- }
-}
-void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const
+void CartSD::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const
{
if (patch[0x0D] > binary[dldioffset+0x0F])
{
@@ -1364,7 +1263,7 @@ void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch,
Log(LogLevel::Debug, "applied DLDI patch at %08X\n", dldioffset);
}
-void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
+void CartSD::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
{
if (*(u32*)&patch[0] != 0xBF8DA5ED ||
*(u32*)&patch[4] != 0x69684320 ||
@@ -1394,7 +1293,7 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
}
}
-void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const
+void CartSD::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const
{
// TODO: how strict should this be for homebrew?
@@ -1403,7 +1302,115 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const
memcpy(data+offset, ROM.get()+addr, len);
}
+CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
+ CartSD(rom, len, chipid, romparams, std::move(sdcard))
+{}
+
+CartHomebrew::CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
+ CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard))
+{}
+
+CartHomebrew::~CartHomebrew() = default;
+
+void CartHomebrew::Reset()
+{
+ CartSD::Reset();
+
+ if (SD)
+ ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly());
+}
+
+void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
+{
+ CartCommon::SetupDirectBoot(romname, nds);
+
+ if (SD)
+ {
+ // add the ROM to the SD volume
+
+ if (!SD->InjectFile(romname, ROM.get(), ROMLength))
+ return;
+
+ // setup argv command line
+
+ char argv[512] = {0};
+ int argvlen;
+
+ strncpy(argv, "fat:/", 511);
+ strncat(argv, romname.c_str(), 511);
+ argvlen = strlen(argv);
+
+ const NDSHeader& header = GetHeader();
+
+ u32 argvbase = header.ARM9RAMAddress + header.ARM9Size;
+ argvbase = (argvbase + 0xF) & ~0xF;
+
+ for (u32 i = 0; i <= argvlen; i+=4)
+ nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]);
+
+ nds.ARM9Write32(0x02FFFE70, 0x5F617267);
+ nds.ARM9Write32(0x02FFFE74, argvbase);
+ nds.ARM9Write32(0x02FFFE78, argvlen+1);
+ // The DSi version of ARM9Write32 will be called if nds is really a DSi
+ }
+}
+
+int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len)
+{
+ if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
+ switch (cmd[0])
+ {
+ case 0xB7:
+ {
+ u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
+ memset(data, 0, len);
+
+ if (((addr + len - 1) >> 12) != (addr >> 12))
+ {
+ u32 len1 = 0x1000 - (addr & 0xFFF);
+ ReadROM_B7(addr, len1, data, 0);
+ ReadROM_B7(addr+len1, len-len1, data, len1);
+ }
+ else
+ ReadROM_B7(addr, len, data, 0);
+ }
+ return 0;
+
+ case 0xC0: // SD read
+ {
+ u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
+ if (SD) SD->ReadSectors(sector, len>>9, data);
+ }
+ return 0;
+
+ case 0xC1: // SD write
+ return 1;
+
+ default:
+ return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
+ }
+}
+
+void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
+{
+ if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len);
+
+ // TODO: delayed SD writing? like we have for SRAM
+
+ switch (cmd[0])
+ {
+ case 0xC1:
+ {
+ u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
+ if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data);
+ }
+ break;
+
+ default:
+ return CartCommon::ROMCommandFinish(cmd, data, len);
+ }
+}
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) noexcept : NDS(nds)
{
@@ -1588,6 +1595,7 @@ std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen
dsi = false;
}
+ const char *gametitle = header.GameTitle;
u32 gamecode = header.GameCodeAsU32();
u32 arm9base = header.ARM9ROMOffset;
@@ -1645,6 +1653,8 @@ std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen
auto [sram, sramlen] = args ? std::move(*args->SRAM) : std::make_pair(nullptr, 0);
if (homebrew)
cart = std::make_unique<CartHomebrew>(std::move(cartrom), cartromsize, cartid, romparams, args ? std::move(args->SDCard) : std::nullopt);
+ else if (gametitle[0] == 0 && !strncmp("SD/TF-NDS", gametitle + 1, 9) && gamecode == 0x414D5341)
+ cart = std::make_unique<CartR4>(std::move(cartrom), cartromsize, cartid, romparams, CartR4TypeR4, CartR4LanguageEnglish, args ? std::move(args->SDCard) : std::nullopt);
else if (cartid & 0x08000000)
cart = std::make_unique<CartRetailNAND>(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen);
else if (irversion != 0)