From d9227c9d6bc79b4e9968424ac94197116672a898 Mon Sep 17 00:00:00 2001 From: RocketRobz Date: Thu, 14 Nov 2024 00:39:47 -0700 Subject: [PATCH] Add Slot-1 microSD access support (on DSi/3DS) for N-Card --- settings/arm9/source/twlFlashcard.cpp | 202 ++++++++++++++++++++++++-- settings/nitrofiles/dldi/nrio.lz77 | Bin 0 -> 12509 bytes title/arm9/source/twlFlashcard.cpp | 186 +++++++++++++++++++++++- 3 files changed, 374 insertions(+), 14 deletions(-) create mode 100644 settings/nitrofiles/dldi/nrio.lz77 diff --git a/settings/arm9/source/twlFlashcard.cpp b/settings/arm9/source/twlFlashcard.cpp index d1d79392c6..506503bcbd 100644 --- a/settings/arm9/source/twlFlashcard.cpp +++ b/settings/arm9/source/twlFlashcard.cpp @@ -7,11 +7,12 @@ #include "common/flashcard.h" #include "common/bootstrappaths.h" #include "common/inifile.h" +#include "common/lzss.h" #include "common/systemdetails.h" +#include "common/tonccpy.h" #include "read_card.h" /*TWL_CODE bool UpdateCardInfo(char* gameid, char* gamename) { - cardReadHeader((uint8*)&ndsCardHeader); memcpy(gameid, ndsCardHeader.gameCode, 4); gameid[4] = 0x00; memcpy(gamename, ndsCardHeader.gameTitle, 12); @@ -23,6 +24,183 @@ TWL_CODE void ShowGameInfo(const char gameid[], const char gamename[]) { iprintf("Game id: %s\nName: %s", gameid, gamename); }*/ +typedef signed int addr_t; +typedef unsigned char data_t; + +#define FIX_ALL 0x01 +#define FIX_GLUE 0x02 +#define FIX_GOT 0x04 +#define FIX_BSS 0x08 + +enum DldiOffsets { + DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm" + DO_magicToken = 0x00, // 0xBF8DA5ED + DO_magicShortString = 0x04, // " Chishm" + DO_version = 0x0C, + DO_driverSize = 0x0D, + DO_fixSections = 0x0E, + DO_allocatedSpace = 0x0F, + + DO_friendlyName = 0x10, + + DO_text_start = 0x40, // Data start + DO_data_end = 0x44, // Data end + DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing + DO_glue_end = 0x4C, // Interworking glue end + DO_got_start = 0x50, // GOT start -- Needs address fixing + DO_got_end = 0x54, // GOT end + DO_bss_start = 0x58, // bss start -- Needs setting to zero + DO_bss_end = 0x5C, // bss end + + // IO_INTERFACE data + DO_ioType = 0x60, + DO_features = 0x64, + DO_startup = 0x68, + DO_isInserted = 0x6C, + DO_readSectors = 0x70, + DO_writeSectors = 0x74, + DO_clearStatus = 0x78, + DO_shutdown = 0x7C, + DO_code = 0x80 +}; + +static addr_t readAddr (data_t *mem, addr_t offset) { + return ((addr_t*)mem)[offset/sizeof(addr_t)]; +} + +static void writeAddr (data_t *mem, addr_t offset, addr_t value) { + ((addr_t*)mem)[offset/sizeof(addr_t)] = value; +} + +TWL_CODE static void dldiRelocateBinary (data_t *binData, size_t dldiFileSize) +{ + addr_t memOffset; // Offset of DLDI after the file is loaded into memory + addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly + addr_t ddmemOffset; // Original offset used in the DLDI file + addr_t ddmemStart; // Start of range that offsets can be in the DLDI file + addr_t ddmemEnd; // End of range that offsets can be in the DLDI file + addr_t ddmemSize; // Size of range that offsets can be in the DLDI file + + addr_t addrIter; + + data_t *pDH = binData; + data_t *pAH = (data_t*)(io_dldi_data); + + // size_t dldiFileSize = 1 << pDH[DO_driverSize]; + + memOffset = readAddr (pAH, DO_text_start); + if (memOffset == 0) { + memOffset = readAddr (pAH, DO_startup) - DO_code; + } + ddmemOffset = readAddr (pDH, DO_text_start); + relocationOffset = memOffset - ddmemOffset; + + ddmemStart = readAddr (pDH, DO_text_start); + ddmemSize = (1 << pDH[DO_driverSize]); + ddmemEnd = ddmemStart + ddmemSize; + + // Remember how much space is actually reserved + pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace]; + // Copy the DLDI patch into the application + tonccpy (pAH, pDH, dldiFileSize); + + // Fix the section pointers in the header + writeAddr (pAH, DO_text_start, readAddr (pAH, DO_text_start) + relocationOffset); + writeAddr (pAH, DO_data_end, readAddr (pAH, DO_data_end) + relocationOffset); + writeAddr (pAH, DO_glue_start, readAddr (pAH, DO_glue_start) + relocationOffset); + writeAddr (pAH, DO_glue_end, readAddr (pAH, DO_glue_end) + relocationOffset); + writeAddr (pAH, DO_got_start, readAddr (pAH, DO_got_start) + relocationOffset); + writeAddr (pAH, DO_got_end, readAddr (pAH, DO_got_end) + relocationOffset); + writeAddr (pAH, DO_bss_start, readAddr (pAH, DO_bss_start) + relocationOffset); + writeAddr (pAH, DO_bss_end, readAddr (pAH, DO_bss_end) + relocationOffset); + // Fix the function pointers in the header + writeAddr (pAH, DO_startup, readAddr (pAH, DO_startup) + relocationOffset); + writeAddr (pAH, DO_isInserted, readAddr (pAH, DO_isInserted) + relocationOffset); + writeAddr (pAH, DO_readSectors, readAddr (pAH, DO_readSectors) + relocationOffset); + writeAddr (pAH, DO_writeSectors, readAddr (pAH, DO_writeSectors) + relocationOffset); + writeAddr (pAH, DO_clearStatus, readAddr (pAH, DO_clearStatus) + relocationOffset); + writeAddr (pAH, DO_shutdown, readAddr (pAH, DO_shutdown) + relocationOffset); + + if (pDH[DO_fixSections] & FIX_ALL) { + // Search through and fix pointers within the data section of the file + for (addrIter = (readAddr(pDH, DO_text_start) - ddmemStart); addrIter < (readAddr(pDH, DO_data_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_GLUE) { + // Search through and fix pointers within the glue section of the file + for (addrIter = (readAddr(pDH, DO_glue_start) - ddmemStart); addrIter < (readAddr(pDH, DO_glue_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_GOT) { + // Search through and fix pointers within the Global Offset Table section of the file + for (addrIter = (readAddr(pDH, DO_got_start) - ddmemStart); addrIter < (readAddr(pDH, DO_got_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_BSS) { + // Initialise the BSS to 0 + toncset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start)); + } +} + +TWL_CODE void myDldiLoadFromFile (const char* filename) { + extern bool extension(const std::string_view filename, const std::vector extensions); + + u32* dldiAddr = new u32[0x8000/sizeof(u32)]; + + FILE* file = fopen(filename, "rb"); + if (extension(filename, {".lz77"})) { + fread((void*)0x02FF8004, 1, 0x3FFC, file); + + *(u32*)0x02FF8000 = 0x53535A4C; + LZ77_Decompress((u8*)0x02FF8004, (u8*)dldiAddr); + } else { + fread(dldiAddr, 1, 0x8000, file); + } + fclose(file); + + // Check that it is a valid DLDI + if (!dldiIsValid ((DLDI_INTERFACE*)dldiAddr)) { + delete[] dldiAddr; + return; + } + + DLDI_INTERFACE* device = (DLDI_INTERFACE*)dldiAddr; + size_t dldiSize; + + // Calculate actual size of DLDI + // Although the file may only go to the dldiEnd, the BSS section can extend past that + if (device->dldiEnd > device->bssEnd) { + dldiSize = (char*)device->dldiEnd - (char*)device->dldiStart; + } else { + dldiSize = (char*)device->bssEnd - (char*)device->dldiStart; + } + dldiSize = (dldiSize + 0x03) & ~0x03; // Round up to nearest integer multiple + + dldiRelocateBinary ((data_t*)dldiAddr, dldiSize); + delete[] dldiAddr; +} + +TWL_CODE const DISC_INTERFACE *dldiGet(void) { + if(io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) + sysSetCartOwner(BUS_OWNER_ARM9); + if(io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS) + sysSetCardOwner(BUS_OWNER_ARM9); + + return &io_dldi_data->ioInterface; +} + TWL_CODE void twl_flashcardInit(void) { if (REG_SCFG_MC != 0x11 && !sys().arm7SCFGLocked()) { // Reset Slot-1 to allow reading title name and ID @@ -31,23 +209,19 @@ TWL_CODE void twl_flashcardInit(void) { CIniFile settingsini( DSIMENUPP_INI ); if (settingsini.GetInt("SRLOADER", "SECONDARY_ACCESS", 0) == false) { + disableSlot1(); return; } // Read title name and ID cardInit(); - //char gamename[13]; - //char gameid[5]; + /*char gamename[13]; + char gameid[5]; - /*fifoSendValue32(FIFO_USER_04, 1); - for (int i = 0; i < 10; i++) { - swiWaitForVBlank(); - } - memcpy(&nds, (void*)0x02000000, sizeof(nds));*/ - //UpdateCardInfo(&gameid[0], &gamename[0]); + UpdateCardInfo(&gameid[0], &gamename[0]); - /*consoleDemoInit(); + consoleDemoInit(); iprintf("REG_SCFG_MC: %x\n", REG_SCFG_MC); ShowGameInfo(gameid, gamename); @@ -72,8 +246,11 @@ TWL_CODE void twl_flashcardInit(void) { fatMountSimple("fat", &io_dldi_data->ioInterface); } else*/ if (!memcmp(ndsCardHeader.gameTitle, "QMATETRIAL", 9) || !memcmp(ndsCardHeader.gameTitle, "R4DSULTRA", 9) // R4iDSN/R4 Ultra || !memcmp(ndsCardHeader.gameCode, "ACEK", 4) || !memcmp(ndsCardHeader.gameCode, "YCEP", 4) || !memcmp(ndsCardHeader.gameCode, "AHZH", 4) || !memcmp(ndsCardHeader.gameCode, "CHPJ", 4) || !memcmp(ndsCardHeader.gameCode, "ADLP", 4)) { // Acekard 2(i) - io_dldi_data = dldiLoadFromFile("nitro:/dldi/ak2.dldi"); - fatMountSimple("fat", &io_dldi_data->ioInterface); + myDldiLoadFromFile("nitro:/dldi/ak2.dldi"); + fatMountSimple("fat", dldiGet()); + } else if (!memcmp(ndsCardHeader.gameCode, "DSGB", 4)) { + myDldiLoadFromFile("nitro:/dldi/nrio.lz77"); + fatMountSimple("fat", dldiGet()); } /*else if (!memcmp(ndsCardHeader.gameCode, "ALXX", 4)) { io_dldi_data = dldiLoadFromFile("nitro:/dldi/dstwo.dldi"); fatMountSimple("fat", &io_dldi_data->ioInterface); @@ -82,6 +259,7 @@ TWL_CODE void twl_flashcardInit(void) { fatMountSimple("fat", &io_dldi_data->ioInterface); } */ + flashcardFoundReset(); if (!flashcardFound()) { disableSlot1(); } diff --git a/settings/nitrofiles/dldi/nrio.lz77 b/settings/nitrofiles/dldi/nrio.lz77 new file mode 100644 index 0000000000000000000000000000000000000000..fc556a403e5725945de0b86e1e8a247bbc95ac99 GIT binary patch literal 12509 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 extensions); + + u32* dldiAddr = new u32[0x8000/sizeof(u32)]; + + FILE* file = fopen(filename, "rb"); + if (extension(filename, {".lz77"})) { + fread((void*)0x02FF8004, 1, 0x3FFC, file); + + *(u32*)0x02FF8000 = 0x53535A4C; + LZ77_Decompress((u8*)0x02FF8004, (u8*)dldiAddr); + } else { + fread(dldiAddr, 1, 0x8000, file); + } + fclose(file); + + // Check that it is a valid DLDI + if (!dldiIsValid ((DLDI_INTERFACE*)dldiAddr)) { + delete[] dldiAddr; + return; + } + + DLDI_INTERFACE* device = (DLDI_INTERFACE*)dldiAddr; + size_t dldiSize; + + // Calculate actual size of DLDI + // Although the file may only go to the dldiEnd, the BSS section can extend past that + if (device->dldiEnd > device->bssEnd) { + dldiSize = (char*)device->dldiEnd - (char*)device->dldiStart; + } else { + dldiSize = (char*)device->bssEnd - (char*)device->dldiStart; + } + dldiSize = (dldiSize + 0x03) & ~0x03; // Round up to nearest integer multiple + + dldiRelocateBinary ((data_t*)dldiAddr, dldiSize); + delete[] dldiAddr; +} + +TWL_CODE const DISC_INTERFACE *dldiGet(void) { + if(io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) + sysSetCartOwner(BUS_OWNER_ARM9); + if(io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS) + sysSetCardOwner(BUS_OWNER_ARM9); + + return &io_dldi_data->ioInterface; +} + TWL_CODE void twl_flashcardInit(void) { if (REG_SCFG_MC != 0x11 && !sys().arm7SCFGLocked()) { // Reset Slot-1 to allow reading title name and ID @@ -67,8 +246,11 @@ TWL_CODE void twl_flashcardInit(void) { fatMountSimple("fat", &io_dldi_data->ioInterface); } else*/ if (!memcmp(ndsCardHeader.gameTitle, "QMATETRIAL", 9) || !memcmp(ndsCardHeader.gameTitle, "R4DSULTRA", 9) // R4iDSN/R4 Ultra || !memcmp(ndsCardHeader.gameCode, "ACEK", 4) || !memcmp(ndsCardHeader.gameCode, "YCEP", 4) || !memcmp(ndsCardHeader.gameCode, "AHZH", 4) || !memcmp(ndsCardHeader.gameCode, "CHPJ", 4) || !memcmp(ndsCardHeader.gameCode, "ADLP", 4)) { // Acekard 2(i) - io_dldi_data = dldiLoadFromFile("nitro:/dldi/ak2.dldi"); - fatMountSimple("fat", &io_dldi_data->ioInterface); + myDldiLoadFromFile("nitro:/dldi/ak2.dldi"); + fatMountSimple("fat", dldiGet()); + } else if (!memcmp(ndsCardHeader.gameCode, "DSGB", 4)) { + myDldiLoadFromFile("nitro:/dldi/nrio.lz77"); + fatMountSimple("fat", dldiGet()); } /*else if (!memcmp(ndsCardHeader.gameCode, "ALXX", 4)) { io_dldi_data = dldiLoadFromFile("nitro:/dldi/dstwo.dldi"); fatMountSimple("fat", &io_dldi_data->ioInterface);