Big changes....

* Stage2 launcher UI added. This is a heavily modified HBMenu with
ability to launch carts! This menu can be used to boot a bootloader NDS
file for flascharts that the direct cart launcher fails to boot.
* New audio files added for new menu.
* HBMenu UI loaded by default if booted with no cart inserted.
* INI file folder now located in NTR_Launcher folder instead. A default
ini file from NitroFS (if mount of nitrofs succeeds) will be copied to
SD if one is not present.
* Stage2 launchers are expected to be in NTR Launcehr folder though you
can navigate to any folder you want in the UI so stage2 launchers aren't
the only thing this can be used for. You can also use this as a means of
booting homebrew off SD in NTR mode.
This commit is contained in:
ApacheThunder 2024-04-10 22:59:52 -05:00
parent 30f6948612
commit a4e5a00409
97 changed files with 6168 additions and 706 deletions

View File

@ -24,7 +24,7 @@ SPECS := specs
#---------------------------------------------------------------------------------
ARCH := -mthumb-interwork -march=armv4t -mtune=arm7tdmi
CFLAGS := -g -Wall -O2 \
CFLAGS := -g -Wall -Oz \
-fomit-frame-pointer \
-ffast-math \
-Wall -Wextra -Werror \

View File

@ -33,9 +33,19 @@ typedef struct sLauncherSettings {
u8 twlCLK;
u8 twlVRAM;
u8 debugMode;
u8 fastBoot;
u8 unused2;
} tLauncherSettings;
extern volatile tLauncherSettings* tmpData;
extern volatile tLauncherSettings* launchData;
extern volatile int language;
extern volatile bool scfgUnlock;
extern volatile bool twlMode;
extern volatile bool twlCLK;
extern volatile bool debugMode;
#define CartHeaderCopy 0x02000000
#define CartChipIDCopy 0x02000180
enum ERROR_CODES {
ERR_NONE = 0x00,

View File

@ -53,15 +53,9 @@ External functions
extern void arm7_clearmem (void* loc, size_t len);
extern void arm7_reset (void);
static int language = -1;
static bool scfgUnlock = false;
static bool twlMode = false;
static bool twlCLK = false;
static bool debugMode = false;
static bool useTwlCfg = false;
static int twlCfgLang = 0;
// static bool useShortInit = false;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Important things
@ -375,7 +369,7 @@ static void arm7_readFirmware(tNDSHeader* ndsHeader) {
tonccpy(PersonalData, currentSettings, sizeof(PERSONAL_DATA));
if (useTwlCfg && (language == 0xFF || language == -1)) { language = twlCfgLang; }
if (useTwlCfg && (language == 0xFF)) { language = twlCfgLang; }
if (language >= 0 && language <= 7) {
// Change language
@ -420,8 +414,13 @@ static void arm7_resetMemory (void) {
arm7_clearmem ((void*)0x037F8000, 96*1024);
// clear most of EXRAM - except after 0x022FD800, which has the ARM9 code
// Skip 0x0200000 region if fastBoot enabled. (cart header copy stored here)
/*if (useShortInit) {
arm7_clearmem ((void*)0x02000200, 0x002FD600);
} else {
arm7_clearmem ((void*)0x02000000, 0x002FD800);
}*/
arm7_clearmem ((void*)0x02000000, 0x002FD800);
// clear last part of EXRAM, skipping the ARM9's section
arm7_clearmem ((void*)0x023FE000, 0x2000);
@ -487,7 +486,15 @@ static u32 arm7_loadBinary (void) {
tDSiHeader* twlHeaderTemp = (tDSiHeader*)TMP_HEADER; // Use same region cheat engine goes. Cheat engine will replace this later when it's not needed.
// Init card
/*if (useShortInit) {
errorCode = cardInitShort((sNDSHeaderExt*)twlHeaderTemp, &chipID);
arm7_clearmem ((void*)0x02000200, 0x200); // clear temp header data
} else {
errorCode = cardInit((sNDSHeaderExt*)twlHeaderTemp, &chipID);
}*/
errorCode = cardInit((sNDSHeaderExt*)twlHeaderTemp, &chipID);
if (errorCode)return errorCode;
ndsHeader = loadHeader(twlHeaderTemp); // copy twlHeaderTemp to ndsHeader location
@ -518,19 +525,21 @@ static u32 arm7_loadBinary (void) {
void arm7_main (void) {
u32 errorCode;
if (language != 0xFF)language = (int)tmpData->language;
if (tmpData->scfgUnlock > 0x00)scfgUnlock = true;
if (tmpData->twlMode > 0x00)twlMode = true;
if (tmpData->twlCLK > 0x00)twlCLK = true;
if (tmpData->debugMode > 0x00)debugMode = true;
// Synchronise start
while (ipcRecvState() != ARM9_START);
ipcSendState(ARM7_START);
// Wait until ARM9 is ready
while (ipcRecvState() != ARM9_READY);
if (language != 0xFF)language = (int)launchData->language;
if (launchData->scfgUnlock > 0x00)scfgUnlock = true;
if (launchData->twlMode > 0x00)twlMode = true;
if (launchData->twlCLK > 0x00)twlCLK = true;
if (launchData->debugMode > 0x00)debugMode = true;
// if (launchData->fastBoot > 0x00)useShortInit = true;
if (twlMode) {
REG_MBK9=0x0300000F;
@ -582,7 +591,7 @@ void arm7_main (void) {
REG_SCFG_EXT = 0x92A00000;
}
if (twlCLK) { REG_SCFG_CLK = 0x0185; } else { REG_SCFG_CLK = 0x0101; }
if (twlCLK) { REG_SCFG_CLK = 0x0187; } else { REG_SCFG_CLK = 0x0101; }
if (scfgUnlock) { REG_SCFG_EXT |= BIT(18); } else { REG_SCFG_EXT &= ~(1UL << 31); }
setMemoryAddress(ndsHeader);

View File

@ -40,7 +40,7 @@
#include "common.h"
#include "miniconsole.h"
#define TMP_DATA 0x027FC000
#define LAUNCH_DATA 0x020007F0
volatile int arm9_stateFlag = ARM9_BOOT;
volatile u32 arm9_errorCode = 0xFFFFFFFF;
@ -48,14 +48,16 @@ volatile bool arm9_errorClearBG = false;
volatile bool consoleDebugMode = false;
volatile u32 arm9_BLANK_RAM = 0;
volatile u32 defaultFontPalSlot = 0;
volatile tLauncherSettings* tmpData = (tLauncherSettings*)TMP_DATA;
static bool scfgUnlock = false;
static bool TWLMODE = false;
static bool TWLVRAM = false;
static bool TWLCLK = false;
static bool debugMode = false;
static bool consoleInit = false;
volatile tLauncherSettings* launchData = (tLauncherSettings*)LAUNCH_DATA;
volatile int language = -1;
volatile bool scfgUnlock = false;
volatile bool twlMode = false;
volatile bool twlCLK = false;
volatile bool TWLVRAM = false;
volatile bool debugMode = false;
volatile bool consoleInit = false;
static char TXT_STATUS[] = "STATUS: ";
static char TXT_ERROR[] = "ERROR: ";
@ -159,14 +161,16 @@ Written by Darkain, modified by Chishm
void arm9_main (void) {
register int i;
if (tmpData->scfgUnlock == 0x01)scfgUnlock = true;
if (tmpData->twlMode == 0x01)TWLMODE = true;
if (tmpData->twlVRAM == 0x01)TWLVRAM = true;
if (tmpData->twlCLK == 0x01)TWLCLK = true;
if (tmpData->debugMode == 0x01)debugMode = true;
if (TWLMODE) {
if (launchData->language != 0xFF)language = (u8)launchData->language;
if (launchData->scfgUnlock == 0x01)scfgUnlock = true;
if (launchData->twlMode == 0x01)twlMode = true;
if (launchData->twlVRAM == 0x01)TWLVRAM = true;
if (launchData->twlCLK == 0x01)twlCLK = true;
if (launchData->debugMode == 0x01)debugMode = true;
if (twlMode) {
*((vu32*)REG_MBK1)=0x8D898581;
*((vu32*)REG_MBK2)=0x8C888480;
*((vu32*)REG_MBK3)=0x9C989490;
@ -302,8 +306,8 @@ void arm9_main (void) {
videoSetModeSub(0);
REG_POWERCNT = 0x820F;
if (!TWLCLK)REG_SCFG_CLK = 0x80;
if (TWLMODE) {
if (!twlCLK)REG_SCFG_CLK = 0x80;
if (twlMode) {
REG_SCFG_EXT = 0x82073100;
REG_SCFG_RST = 1;
}

View File

@ -24,21 +24,24 @@
#include <nds/dma.h>
#include <nds/card.h>
#include <string.h>
#include <stdlib.h>
#include "encryption.h"
#include "common.h"
#include "tonccpy.h"
typedef union { char title[4]; u32 key; } GameCode;
static u32 portFlags = 0;
static u32 secureAreaData[CARD_SECURE_AREA_SIZE/sizeof(u32)];
// static bool shortInit = false;
static const u8 cardSeedBytes[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5};
static u32 getRandomNumber(void) {
return 4; // chosen by fair dice roll.
// guaranteed to be random.
}
// chosen by fair dice roll.
// guaranteed to be random.
// static u32 getRandomNumber(void) { return 4; }
static u32 getRandomNumber(void) { return rand(); } // make this actually random lol. :P
static void decryptSecureArea (u32 gameCode, u32* secureArea) {
int i;
@ -120,7 +123,7 @@ static void cardDelay (u16 readTimeout) {
}
int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
u32 portFlagsKey1, portFlagsSecRead;
bool normalChip; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000
u32* secureArea;
@ -128,6 +131,7 @@ int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
int i;
u8 cmdData[8] __attribute__ ((aligned));
GameCode* gameCode;
// shortInit = false;
// Dummy command sent after card reset
cardParamCommand (CARD_CMD_DUMMY, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), NULL, 0);
@ -136,19 +140,10 @@ int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
static_assert(sizeof(tNDSHeader) == 0x160, "tNDSHeader not packed properly");
// Read the header
cardParamCommand (CARD_CMD_HEADER_READ, 0,
CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F),
(uint32*)ndsHeader, sizeof(tNDSHeader));
cardParamCommand (CARD_CMD_HEADER_READ, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), (u32*)ndsHeader, sizeof(tNDSHeader));
// Check header CRC
if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E)) {
return ERR_HEAD_CRC;
}
// Check logo CRC
/*if (ndsHeader->logoCRC16 != 0xCF56) {
return ERR_LOGO_CRC;
}*/
if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E))return ERR_HEAD_CRC;
// Initialise blowfish encryption for KEY1 commands and decrypting the secure area
gameCode = (GameCode*)ndsHeader->gameCode;
@ -157,13 +152,10 @@ int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
// Port 40001A4h setting for normal reads (command B7)
portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7);
// Port 40001A4h setting for KEY1 commands (usually 001808F8h)
portFlagsKey1 = CARD_ACTIVATE | CARD_nRESET | (ndsHeader->cardControl13 & (CARD_WR|CARD_CLK_SLOW)) |
((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16));
portFlagsKey1 = CARD_ACTIVATE | CARD_nRESET | (ndsHeader->cardControl13 & (CARD_WR|CARD_CLK_SLOW)) | ((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16));
// 1st Get ROM Chip ID
cardParamCommand (CARD_CMD_HEADER_CHIPID, 0,
(ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE | CARD_BLK_SIZE(7),
chipID, sizeof(u32));
cardParamCommand (CARD_CMD_HEADER_CHIPID, 0, (ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE | CARD_BLK_SIZE(7), chipID, sizeof(u32));
// Adjust card transfer method depending on the most significant bit of the chip ID
normalChip = ((*chipID) & 0x80000000) != 0; // ROM chip ID MSB
@ -187,9 +179,9 @@ int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
// Set the KEY2 encryption registers
REG_ROMCTRL = 0;
REG_CARD_1B0 = cardSeedBytes[ndsHeader->deviceType & 0x07] | (key1data.nnn << 15) | (key1data.mmm << 27) | 0x6000;
REG_CARD_1B4 = 0x879b9b05;
REG_CARD_1B4 = 0x879B9B05;
REG_CARD_1B8 = key1data.mmm >> 5;
REG_CARD_1BA = 0x5c;
REG_CARD_1BA = 0x5C;
REG_ROMCTRL = CARD_nRESET | CARD_SEC_SEED | CARD_SEC_EN | CARD_SEC_DAT;
// Update the DS card flags to suit KEY2 encryption
@ -208,9 +200,8 @@ int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
// 2bbbbiii jjjkkkkk - Get Secure Area Block
secureArea = secureAreaData;
portFlagsSecRead = (ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF)|CARD_DELAY2(0x3F)))
| CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT;
portFlagsSecRead = (ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF)|CARD_DELAY2(0x3F))) | CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT;
for (secureBlockNumber = 4; secureBlockNumber < 8; secureBlockNumber++) {
createEncryptedCommand (CARD_CMD_SECURE_READ, cmdData, secureBlockNumber);
if (normalChip) {
@ -256,11 +247,10 @@ int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) {
return ERR_NONE;
}
void cardRead (u32 src, u32* dest, size_t size)
{
void cardRead (u32 src, u32* dest, size_t size) {
size_t readSize;
if (src < CARD_SECURE_AREA_OFFSET) {
if ((src < CARD_SECURE_AREA_OFFSET) /*&& !shortInit*/) {
return;
} else if (src < CARD_DATA_OFFSET) {
// Read data from secure area
@ -273,12 +263,65 @@ void cardRead (u32 src, u32* dest, size_t size)
while (size > 0) {
readSize = size < CARD_DATA_BLOCK_SIZE ? size : CARD_DATA_BLOCK_SIZE;
cardParamCommand (CARD_CMD_DATA_READ, src,
(portFlags &~CARD_BLK_SIZE(7)) | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1),
dest, readSize);
cardParamCommand (CARD_CMD_DATA_READ, src, (portFlags &~CARD_BLK_SIZE(7)) | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), dest, readSize);
src += readSize;
dest += readSize/sizeof(*dest);
size -= readSize;
}
}
// If booted from DSi System Menu short cart init with no card reads or pokes to rom ctrl registers can be done.
// System Menu is nice enough to do this for you. :P
// (also is the case for booting from DS Download Play. ;) )
/*u32 cardInitShort (sNDSHeaderExt* ndsHeader, u32* chipID) {
bool normalChip; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000
u32* secureArea;
GameCode* gameCode;
shortInit = true;
// Verify that the ndsHeader is packed correctly, now that it's no longer __packed__
static_assert(sizeof(tNDSHeader) == 0x160, "tNDSHeader not packed properly");
// Read the header
tonccpy(ndsHeader, (u32*)CartHeaderCopy, 0x180);
// Check header CRC
if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E))return ERR_HEAD_CRC;
// Initialise blowfish encryption for KEY1 commands and decrypting the secure area
gameCode = (GameCode*)ndsHeader->gameCode;
init_keycode (gameCode->key, 2, 8);
// Port 40001A4h setting for normal reads (command B7)
portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7);
// 1st Get ROM Chip ID
// chipID = *(u32*)0x02FFFC00; // This location contains cart's chipID when booting DSiWare that has Slot-1 access.
// chipID = *(u32*)0x027FF800; // This location contains cart's chipID for DS Download Play users
tonccpy(chipID, (u32*)CartChipIDCopy, 0x08); // This location contains cart's chipID when booting DSiWare that has Slot-1 access.
// Adjust card transfer method depending on the most significant bit of the chip ID
normalChip = ((*chipID) & 0x80000000) != 0; // ROM chip ID MSB
if (normalChip)cardDelay(ndsHeader->readTimeout);
// 2bbbbiii jjjkkkkk - Get Secure Area Block
secureArea = secureAreaData;
shortInit = false;
u32 secureBlockSize = 0x800;
if (!normalChip)secureBlockSize = 0x1000;
cardRead (ndsHeader->arm9romOffset, secureArea, secureBlockSize);
// Now deal with secure area decryption and verification
decryptSecureArea (gameCode->key, secureAreaData);
secureArea = secureAreaData;
if (secureArea[0] == 0x72636e65 && secureArea[1] == 0x6a624f79) {
// Secure area exists, so just clear the tag
secureArea[0] = 0xe7ffdeff;
secureArea[1] = 0xe7ffdeff;
}
return ERR_NONE;
}*/

View File

@ -121,9 +121,9 @@ typedef struct {
u8 dsi3[0x174];
} sNDSHeaderExt;
int cardInit (sNDSHeaderExt* ndsHeader, u32* chipID);
u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID);
void cardRead (u32 src, u32* dest, size_t size);
// u32 cardInitShort (sNDSHeaderExt* ndsHeader, u32* chipID);
#endif // READ_CARD_H

View File

@ -13,15 +13,19 @@ export TARGET := NTR_Launcher
export TOPDIR := $(CURDIR)
export VERSION_MAJOR := 2
export VERSION_MINOR := 7
export VERSION_MINOR := 9
export VERSTRING := $(VERSION_MAJOR).$(VERSION_MINOR)
.PHONY: bootloader clean arm7/$(TARGET).elf arm9/$(TARGET).elf
# specify a directory which contains the nitro filesystem
# this is relative to the Makefile
NITRO_FILES := CartFiles
.PHONY: bootloader ndsbootloader clean arm7/$(TARGET).elf arm9/$(TARGET).elf
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all: bootloader $(TARGET).nds
all: bootloader ndsbootloader $(TARGET).nds
dist: all
@mkdir -p debug
@ -31,7 +35,8 @@ dist: all
$(TARGET).nds: $(TARGET).arm7 $(TARGET).arm9
ndstool -c $(TARGET).nds -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf \
-b $(CURDIR)/icon.bmp "NTR Launcher;Slot-1 Launcher;Apache Thunder & RocketRobz" \
-g KKGP 01 "NTR Launcher" -z 80040000 -u 00030004 -a 00000138 -p 0001
-g KKGP 01 "NTR Launcher" -z 80040000 -u 00030004 -a 00000138 -p 0001 \
-d $(NITRO_FILES)
@cp $(TARGET).nds 00000000.app
$(TARGET).arm7 : arm7/$(TARGET).elf
@ -58,7 +63,9 @@ clean:
@rm -fr $(TARGET).arm7.elf
@rm -fr $(TARGET).arm9.elf
@rm -fr 00000000.app
@rm -fr $(TARGET).cia
@$(MAKE) -C bootloader clean
@$(MAKE) -C ndsbootloader clean
@$(MAKE) -C arm9 clean
@$(MAKE) -C arm7 clean
@ -67,4 +74,7 @@ data:
bootloader: data
@$(MAKE) -C bootloader
ndsbootloader: data
@$(MAKE) -C ndsbootloader

Binary file not shown.

View File

@ -22,7 +22,164 @@
#include <maxmod7.h>
bool SCFGCLKCheck = false;
#define REG_GPIO_WIFI *(vu16*)0x04004C04
static bool touchScreenCheck = false;
static void NDSTouchscreenMode() {
u8 volLevel = 0xA7;
// Touchscreen
cdcReadReg (0x63, 0x00);
cdcWriteReg(CDC_CONTROL, 0x3A, 0x00);
cdcReadReg (CDC_CONTROL, 0x51);
cdcReadReg (CDC_TOUCHCNT, 0x02);
cdcReadReg (CDC_CONTROL, 0x3F);
cdcReadReg (CDC_SOUND, 0x28);
cdcReadReg (CDC_SOUND, 0x2A);
cdcReadReg (CDC_SOUND, 0x2E);
cdcWriteReg(CDC_CONTROL, 0x52, 0x80);
cdcWriteReg(CDC_CONTROL, 0x40, 0x0C);
cdcWriteReg(CDC_SOUND, 0x24, 0xFF);
cdcWriteReg(CDC_SOUND, 0x25, 0xFF);
cdcWriteReg(CDC_SOUND, 0x26, 0x7F);
cdcWriteReg(CDC_SOUND, 0x27, 0x7F);
cdcWriteReg(CDC_SOUND, 0x28, 0x4A);
cdcWriteReg(CDC_SOUND, 0x29, 0x4A);
cdcWriteReg(CDC_SOUND, 0x2A, 0x10);
cdcWriteReg(CDC_SOUND, 0x2B, 0x10);
cdcWriteReg(CDC_CONTROL, 0x51, 0x00);
cdcReadReg (CDC_TOUCHCNT, 0x02);
cdcWriteReg(CDC_TOUCHCNT, 0x02, 0x98);
cdcWriteReg(CDC_SOUND, 0x23, 0x00);
cdcWriteReg(CDC_SOUND, 0x1F, 0x14);
cdcWriteReg(CDC_SOUND, 0x20, 0x14);
cdcWriteReg(CDC_CONTROL, 0x3F, 0x00);
cdcReadReg (CDC_CONTROL, 0x0B);
cdcWriteReg(CDC_CONTROL, 0x05, 0x00);
cdcWriteReg(CDC_CONTROL, 0x0B, 0x01);
cdcWriteReg(CDC_CONTROL, 0x0C, 0x02);
cdcWriteReg(CDC_CONTROL, 0x12, 0x01);
cdcWriteReg(CDC_CONTROL, 0x13, 0x02);
cdcWriteReg(CDC_SOUND, 0x2E, 0x00);
cdcWriteReg(CDC_CONTROL, 0x3A, 0x60);
cdcWriteReg(CDC_CONTROL, 0x01, 0x01);
cdcWriteReg(CDC_CONTROL, 0x39, 0x66);
cdcReadReg (CDC_SOUND, 0x20);
cdcWriteReg(CDC_SOUND, 0x20, 0x10);
cdcWriteReg(CDC_CONTROL, 0x04, 0x00);
cdcWriteReg(CDC_CONTROL, 0x12, 0x81);
cdcWriteReg(CDC_CONTROL, 0x13, 0x82);
cdcWriteReg(CDC_CONTROL, 0x51, 0x82);
cdcWriteReg(CDC_CONTROL, 0x51, 0x00);
cdcWriteReg(CDC_CONTROL, 0x04, 0x03);
cdcWriteReg(CDC_CONTROL, 0x05, 0xA1);
cdcWriteReg(CDC_CONTROL, 0x06, 0x15);
cdcWriteReg(CDC_CONTROL, 0x0B, 0x87);
cdcWriteReg(CDC_CONTROL, 0x0C, 0x83);
cdcWriteReg(CDC_CONTROL, 0x12, 0x87);
cdcWriteReg(CDC_CONTROL, 0x13, 0x83);
cdcReadReg (CDC_TOUCHCNT, 0x10);
cdcWriteReg(CDC_TOUCHCNT, 0x10, 0x08);
cdcWriteReg(0x04, 0x08, 0x7F);
cdcWriteReg(0x04, 0x09, 0xE1);
cdcWriteReg(0x04, 0x0A, 0x80);
cdcWriteReg(0x04, 0x0B, 0x1F);
cdcWriteReg(0x04, 0x0C, 0x7F);
cdcWriteReg(0x04, 0x0D, 0xC1);
cdcWriteReg(CDC_CONTROL, 0x41, 0x08);
cdcWriteReg(CDC_CONTROL, 0x42, 0x08);
cdcWriteReg(CDC_CONTROL, 0x3A, 0x00);
cdcWriteReg(0x04, 0x08, 0x7F);
cdcWriteReg(0x04, 0x09, 0xE1);
cdcWriteReg(0x04, 0x0A, 0x80);
cdcWriteReg(0x04, 0x0B, 0x1F);
cdcWriteReg(0x04, 0x0C, 0x7F);
cdcWriteReg(0x04, 0x0D, 0xC1);
cdcWriteReg(CDC_SOUND, 0x2F, 0x2B);
cdcWriteReg(CDC_SOUND, 0x30, 0x40);
cdcWriteReg(CDC_SOUND, 0x31, 0x40);
cdcWriteReg(CDC_SOUND, 0x32, 0x60);
cdcReadReg (CDC_CONTROL, 0x74);
cdcWriteReg(CDC_CONTROL, 0x74, 0x02);
cdcReadReg (CDC_CONTROL, 0x74);
cdcWriteReg(CDC_CONTROL, 0x74, 0x10);
cdcReadReg (CDC_CONTROL, 0x74);
cdcWriteReg(CDC_CONTROL, 0x74, 0x40);
cdcWriteReg(CDC_SOUND, 0x21, 0x20);
cdcWriteReg(CDC_SOUND, 0x22, 0xF0);
cdcReadReg (CDC_CONTROL, 0x51);
cdcReadReg (CDC_CONTROL, 0x3F);
cdcWriteReg(CDC_CONTROL, 0x3F, 0xD4);
cdcWriteReg(CDC_SOUND, 0x23, 0x44);
cdcWriteReg(CDC_SOUND, 0x1F, 0xD4);
cdcWriteReg(CDC_SOUND, 0x28, 0x4E);
cdcWriteReg(CDC_SOUND, 0x29, 0x4E);
cdcWriteReg(CDC_SOUND, 0x24, 0x9E);
cdcWriteReg(CDC_SOUND, 0x25, 0x9E);
cdcWriteReg(CDC_SOUND, 0x20, 0xD4);
cdcWriteReg(CDC_SOUND, 0x2A, 0x14);
cdcWriteReg(CDC_SOUND, 0x2B, 0x14);
cdcWriteReg(CDC_SOUND, 0x26, 0xA7);
cdcWriteReg(CDC_SOUND, 0x27, 0xA7);
cdcWriteReg(CDC_CONTROL, 0x40, 0x00);
cdcWriteReg(CDC_CONTROL, 0x3A, 0x60);
cdcWriteReg(CDC_SOUND, 0x26, volLevel);
cdcWriteReg(CDC_SOUND, 0x27, volLevel);
cdcWriteReg(CDC_SOUND, 0x2E, 0x03);
cdcWriteReg(CDC_TOUCHCNT, 0x03, 0x00);
cdcWriteReg(CDC_SOUND, 0x21, 0x20);
cdcWriteReg(CDC_SOUND, 0x22, 0xF0);
cdcReadReg (CDC_SOUND, 0x22);
cdcWriteReg(CDC_SOUND, 0x22, 0x00);
cdcWriteReg(CDC_CONTROL, 0x52, 0x80);
cdcWriteReg(CDC_CONTROL, 0x51, 0x00);
// Set remaining values
cdcWriteReg(CDC_CONTROL, 0x03, 0x44);
cdcWriteReg(CDC_CONTROL, 0x0D, 0x00);
cdcWriteReg(CDC_CONTROL, 0x0E, 0x80);
cdcWriteReg(CDC_CONTROL, 0x0F, 0x80);
cdcWriteReg(CDC_CONTROL, 0x10, 0x08);
cdcWriteReg(CDC_CONTROL, 0x14, 0x80);
cdcWriteReg(CDC_CONTROL, 0x15, 0x80);
cdcWriteReg(CDC_CONTROL, 0x16, 0x04);
cdcWriteReg(CDC_CONTROL, 0x1A, 0x01);
cdcWriteReg(CDC_CONTROL, 0x1E, 0x01);
cdcWriteReg(CDC_CONTROL, 0x24, 0x80);
cdcWriteReg(CDC_CONTROL, 0x33, 0x34);
cdcWriteReg(CDC_CONTROL, 0x34, 0x32);
cdcWriteReg(CDC_CONTROL, 0x35, 0x12);
cdcWriteReg(CDC_CONTROL, 0x36, 0x03);
cdcWriteReg(CDC_CONTROL, 0x37, 0x02);
cdcWriteReg(CDC_CONTROL, 0x38, 0x03);
cdcWriteReg(CDC_CONTROL, 0x3C, 0x19);
cdcWriteReg(CDC_CONTROL, 0x3D, 0x05);
cdcWriteReg(CDC_CONTROL, 0x44, 0x0F);
cdcWriteReg(CDC_CONTROL, 0x45, 0x38);
cdcWriteReg(CDC_CONTROL, 0x49, 0x00);
cdcWriteReg(CDC_CONTROL, 0x4A, 0x00);
cdcWriteReg(CDC_CONTROL, 0x4B, 0xEE);
cdcWriteReg(CDC_CONTROL, 0x4C, 0x10);
cdcWriteReg(CDC_CONTROL, 0x4D, 0xD8);
cdcWriteReg(CDC_CONTROL, 0x4E, 0x7E);
cdcWriteReg(CDC_CONTROL, 0x4F, 0xE3);
cdcWriteReg(CDC_CONTROL, 0x58, 0x7F);
cdcWriteReg(CDC_CONTROL, 0x74, 0xD2);
cdcWriteReg(CDC_CONTROL, 0x75, 0x2C);
cdcWriteReg(CDC_SOUND, 0x22, 0x70);
cdcWriteReg(CDC_SOUND, 0x2C, 0x20);
// Finish up!
cdcReadReg (CDC_TOUCHCNT, 0x02);
cdcWriteReg(CDC_TOUCHCNT, 0x02, 0x98);
cdcWriteReg(0xFF, 0x05, 0x00); //writeTSC(0x00, 0xFF);
// Power management
writePowerManagement(PM_READ_REGISTER, 0x00); //*(unsigned char*)0x40001C2 = 0x80, 0x00; // read PWR[0] ;<-- also part of TSC !
writePowerManagement(PM_CONTROL_REG, 0x0D); //*(unsigned char*)0x40001C2 = 0x00, 0x0D; // PWR[0]=0Dh ;<-- also part of TSC !
}
void VcountHandler() { inputGetAndSend(); }
@ -52,6 +209,21 @@ int main(void) {
i2cWriteRegister(0x4A, 0x12, 0x00); // Press power-button for auto-reset
i2cWriteRegister(0x4A, 0x70, 0x01); // Bootflag = Warmboot/SkipHealthSafety
while (1) { swiWaitForVBlank(); }
while (1) {
if (!touchScreenCheck) {
if(fifoCheckValue32(FIFO_USER_01)) {
if (cdcReadReg(CDC_SOUND, 0x22) == 0xF0) {
// Switch touch mode to NTR
*(u16*)0x4004700 = 0x800F;
NDSTouchscreenMode();
*(u16*)0x4000500 = 0x807F;
}
REG_GPIO_WIFI |= BIT(8); // Old NDS-Wifi mode
fifoSendValue32(FIFO_USER_02, 1);
touchScreenCheck = true;
}
}
swiWaitForVBlank();
}
}

View File

@ -16,8 +16,8 @@ include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
BUILD := build
IMAGES := graphics
SOURCES := source $(IMG_DATA)
INCLUDES := include
SOURCES := source common hbmenu $(IMG_DATA)
INCLUDES := include common hbmenu
DATA := ../data
MUSIC := music
@ -26,7 +26,7 @@ MUSIC := music
#---------------------------------------------------------------------------------
ARCH := -march=armv5te -mtune=arm946e-s -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O2\
CFLAGS := -g -Wall -O2 \
-fomit-frame-pointer\
-ffast-math \
$(ARCH)
@ -803,22 +803,6 @@ BotDSi10.s : ../$(IMAGES)/BotDSi10.bmp
BotDSi11.s : ../$(IMAGES)/BotDSi11.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
# generic fade from white to black
fade00.s : ../$(IMAGES)/fade00.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
fade01.s : ../$(IMAGES)/fade01.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
fade02.s : ../$(IMAGES)/fade02.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
fade03.s : ../$(IMAGES)/fade03.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
fade04.s : ../$(IMAGES)/fade04.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
# generic bottom screen error message
suberror00.s : ../$(IMAGES)/suberror00.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
@ -863,6 +847,25 @@ toperror2_05.s : ../$(IMAGES)/toperror2_05.bmp
toperror2_06.s : ../$(IMAGES)/toperror2_06.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
loading.s : ../$(IMAGES)/loading.bmp
grit $< -gB8 -gzl -fts -gTff00ff -o $@ -q
font.s : ../$(IMAGES)/font.bmp
grit $< -gt -pw16 -mR! -m! -gB4 -o $@ -q
font6x8.s : ../$(IMAGES)/font6x8.bmp
grit $< -gB8 -gTFFFFFF -gzl -o $@ -q
hbmenu_banner.s : ../$(IMAGES)/hbmenu_banner.bmp
grit $< -W3 -gT! -gzl -gB16 -o $@ -q
hbmenu_banner_cartSelected.s : ../$(IMAGES)/hbmenu_banner_cartSelected.bmp
grit $< -W3 -gT! -gzl -gB16 -o $@ -q
hbmenu_consolebg.s : ../$(IMAGES)/hbmenu_consolebg.bmp
grit $< -gB8 -gb -o $@ -q
#---------------------------------------------------------------------------------
# rule to build soundbank from music files
#---------------------------------------------------------------------------------

0
arm9/source/crc.c → arm9/common/crc.c Executable file → Normal file
View File

0
arm9/source/crc.h → arm9/common/crc.h Executable file → Normal file
View File

View File

@ -0,0 +1,55 @@
/*
NitroHax -- Cheat tool for the Nintendo DS
Copyright (C) 2008 Michael "Chishm" Chisholm
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LAUNCHERDATA_H
#define LAUNCHERDATA_H
#ifdef __cplusplus
extern "C" {
#endif
#define LAUNCH_DATA 0x020007F0
#define CartHeaderCopy 0x02000000
#define CartChipIDCopy 0x02000180
#define CartBannerBuffer 0x02500000
// #define InitialCartBannerOffset 0x02FFC068
#define InitialCartHeaderTWL 0x02FFC000 // System Menu keeps cart's header here (if cart is present) on initial boot of any DSiWare!
#define InitialCartHeader 0x02FFFA80 // System Menu keeps cart's header here (if cart is present) on initial boot of any DSiWare!
#define InitialCartChipID 0x02FFFC00 // System Menu keeps cart's chip id here (if cart is present) on initial boot of any DSiWare!
#define InitialCartBannerOffset 0x02FFFAE8
typedef struct sLauncherSettings {
u8 language;
u8 scfgUnlock;
u8 twlMode;
u8 twlCLK;
u8 twlVRAM;
u8 debugMode;
u8 fastBoot;
u8 unused2;
} tLauncherSettings;
#ifdef __cplusplus
}
#endif
#endif // LAUNCHERDATA_H

52
arm9/common/nds_card.c Normal file
View File

@ -0,0 +1,52 @@
/*
NitroHax -- Cheat tool for the Nintendo DS
Copyright (C) 2008 Michael "Chishm" Chisholm
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <nds.h>
#include "nds_card.h"
void CardReset (bool properReset) {
int i;
sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart
if (isDSiMode()) {
// Reset card slot
disableSlot1();
for (i = 0; i < 15; i++) { swiWaitForVBlank(); }
enableSlot1();
for (i = 0; i < 10; i++) { swiWaitForVBlank(); }
if (!properReset) {
// Dummy command sent after card reset
cardParamCommand (CARD_CMD_DUMMY, 0,
CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F),
NULL, 0);
}
}
if (!isDSiMode() || properReset) {
REG_ROMCTRL=0;
REG_AUXSPICNT=0;
for (i = 0; i < 10; i++) { swiWaitForVBlank(); }
REG_AUXSPICNT=CARD_CR1_ENABLE|CARD_CR1_IRQ;
REG_ROMCTRL=CARD_nRESET|CARD_SEC_SEED;
while (REG_ROMCTRL&CARD_BUSY);
cardReset();
while (REG_ROMCTRL&CARD_BUSY);
}
}

View File

@ -16,23 +16,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BIOS_DECOMPRESS_CALLBACK_H
#define BIOS_DECOMPRESS_CALLBACK_H
#ifndef NDS_CARD_H
#define NDS_CARD_H
#include <nds/ndstypes.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <nds/bios.h>
#include <nds/ndstypes.h>
extern TDecompressionStream decompressBiosCallback;
void CardReset (bool properReset);
#ifdef __cplusplus
}
#endif
#endif // BIOS_DECOMPRESS_CALLBACK_H
#endif // NDS_CARD_H

464
arm9/common/nitrofs.c Normal file
View File

@ -0,0 +1,464 @@
/*
nitrofs.c - eris's wai ossum nitro filesystem device driver
Based on information found at http://frangoassado.org/ds/rom_spec.txt and from the #dsdev ppls
Kallisti (K) 2008-01-26 All rights reversed.
2008-05-19 v0.2 - New And Improved!! :DDD
* fix'd the fseek SEEK_CUR issue (my fseek funct should not have returned a value :/)
* also thx to wintermute's input realized:
* if you dont give ndstool the -o wifilogo.bmp option it will run on emulators in gba mode
* you then dont need the gba's LOADEROFFSET, so it was set to 0x000
2008-05-21 v0.3 - newer and more improved
* fixed some issues with ftell() (again was fseek's fault u_u;;)
* fixed possible error in detecting sc.gba files when using dldi
* readded support for .gba files in addition to .nds emu
* added stat() support for completedness :)
2008-05-22 v0.3.1 - slight update
* again fixed fseek(), this time SEEK_END oddly i kinda forgot about it >_> sry
* also went ahead and inlined the functions, makes slight proformance improvement
2008-05-26 v0.4 - added chdir
* added proper chdir functionality
2008-05-30 v0.5.Turbo - major speed improvement
* This version uses a single filehandle to access the .nds file when not in GBA mode
improving the speed it takes to open a .nds file by around 106ms. This is great for
situations requiring reading alot of seperate small files. However it does take a little
bit longer when reading from multiple files simultainously
(around 122ms over 10,327 0x100 byte reads between 2 files).
2008-06-09
* Fixed bug with SEEK_END where it wouldnt utilize the submitted position..
(now can fseek(f,-128,SEEK_END) to read from end of file :D)
2008-06-18 v0.6.Turbo - . and .. :D
* Today i have added full "." and ".." support.
dirnext() will return . and .. first, and all relevent operations will
support . and .. in pathnames.
2009-05-10 v0.7.Turbo - small changes @_@?!
2009-08-08 v0.8.Turbo - fix fix fix
* fixed problem with some cards where the header would be loaded to GBA ram even if running
in NDS mode causing nitroFSInit() to think it was a valid GBA cart header and attempt to
read from GBA SLOT instead of SLOT 1. Fixed this by making it check that filename is not NULL
and then to try FAT/SLOT1 first. The NULL option allows forcing nitroFS to use gba.
2018-09-05 v0.9 - modernize devoptab (by RonnChyran)
* Updated for libsysbase change in devkitARM r46 and above.
*/
#include <string.h>
#include <errno.h>
#include <nds.h>
#include "nitrofs.h"
#include "tonccpy.h"
//This seems to be a typo! memory.h has REG_EXEMEMCNT
#ifndef REG_EXMEMCNT
#define REG_EXMEMCNT (*(vuint16 *)0x04000204)
#endif
#define __itcm __attribute__((section(".itcm")))
//Globals!
u32 fntOffset; //offset to start of filename table
u32 fatOffset; //offset to start of file alloc table
bool hasLoader; //single global nds filehandle (is null if not in dldi/fat mode)
u16 chdirpathid; //default dir path id...
FILE *ndsFile = NULL;
off_t ndsFileLastpos; //Used to determine need to fseek or not
devoptab_t nitroFSdevoptab = {
"nitro", // const char *name;
sizeof(struct nitroFSStruct), // int structSize;
&nitroFSOpen, // int (*open_r)(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
&nitroFSClose, // int (*close_r)(struct _reent *r,void* fd);
NULL, // int (*write_r)(struct _reent *r,void* fd,const char *ptr,int len);
&nitroFSRead, // int (*read_r)(struct _reent *r,void* fd,char *ptr,int len);
&nitroFSSeek, // int (*seek_r)(struct _reent *r,void* fd,int pos,int dir);
&nitroFSFstat, // int (*fstat_r)(struct _reent *r,void* fd,struct stat *st);
&nitroFSstat, // int (*stat_r)(struct _reent *r,const char *file,struct stat *st);
NULL, // int (*link_r)(struct _reent *r,const char *existing, const char *newLink);
NULL, // int (*unlink_r)(struct _reent *r,const char *name);
&nitroFSChdir, // int (*chdir_r)(struct _reent *r,const char *name);
NULL, // int (*rename_r) (struct _reent *r, const char *oldName, const char *newName);
NULL, // int (*mkdir_r) (struct _reent *r, const char *path, int mode);
sizeof(struct nitroDIRStruct), // int dirStateSize;
&nitroFSDirOpen, // DIR_ITER* (*diropen_r)(struct _reent *r, DIR_ITER *dirState, const char *path);
&nitroDirReset, // int (*dirreset_r)(struct _reent *r, DIR_ITER *dirState);
&nitroFSDirNext, // int (*dirnext_r)(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
&nitroFSDirClose // int (*dirclose_r)(struct _reent *r, DIR_ITER *dirState);
};
//K, i decided to inline these, improves speed slightly..
//these 2 'sub' functions deal with actually reading from either gba rom or .nds file :)
//what i rly rly rly wanna know is how an actual nds cart reads from itself, but it seems no one can tell me ~_~
//so, instead we have this weird weird haxy try gbaslot then try dldi method. If i (or you!!) ever do figure out
//how to read the proper way can replace these 4 functions and everything should work normally :)
//reads from rom image either gba rom or dldi
static inline ssize_t nitroSubRead(off_t *npos, void *ptr, size_t len)
{
if (ndsFile != NULL)
{ //read from ndsfile
if (ndsFileLastpos != *npos)
fseek(ndsFile, *npos, SEEK_SET); //if we need to, move! (might want to verify this succeed)
len = fread(ptr, 1, len, ndsFile);
}
else
{ //reading from gbarom
tonccpy(ptr, *npos + (void *)GBAROM, len); //len isnt checked here because other checks exist in the callers (hopefully)
}
if (len > 0)
*npos += len;
ndsFileLastpos = *npos; //save the current file nds pos
return (len);
}
//seek around
static inline void nitroSubSeek(off_t *npos, int pos, int dir)
{
if ((dir == SEEK_SET) || (dir == SEEK_END)) //otherwise just set the pos :)
*npos = pos;
else if (dir == SEEK_CUR)
*npos += pos; //see ez!
}
//Figure out if its gba or ds, setup stuff
int __itcm
nitroFSInit(const char *ndsfile)
{
off_t pos = 0;
char romstr[0x10];
chdirpathid = NITROROOT;
ndsFileLastpos = 0;
if(ndsFile != NULL) {
fclose(ndsFile);
ndsFile = NULL;
}
if (ndsfile != NULL)
{
if ((ndsFile = fopen(ndsfile, "rb")))
{
nitroSubRead(&pos, romstr, strlen(LOADERSTR));
if (strncmp(romstr, LOADERSTR, strlen(LOADERSTR)) == 0)
{
nitroSubSeek(&pos, LOADEROFFSET + FNTOFFSET, SEEK_SET);
nitroSubRead(&pos, &fntOffset, sizeof(fntOffset));
nitroSubSeek(&pos, LOADEROFFSET + FATOFFSET, SEEK_SET);
nitroSubRead(&pos, &fatOffset, sizeof(fatOffset));
fatOffset += LOADEROFFSET;
fntOffset += LOADEROFFSET;
hasLoader = true;
}
else
{
nitroSubSeek(&pos, FNTOFFSET, SEEK_SET);
nitroSubRead(&pos, &fntOffset, sizeof(fntOffset));
nitroSubSeek(&pos, FATOFFSET, SEEK_SET);
nitroSubRead(&pos, &fatOffset, sizeof(fatOffset));
hasLoader = false;
}
setvbuf(ndsFile, NULL, _IONBF, 0); //we dont need double buffs u_u
if (fntOffset==0 || fatOffset==0)
{
return (0);
}
else
{
AddDevice(&nitroFSdevoptab);
return (1);
}
}
}
return (0);
}
//Directory functs
DIR_ITER *nitroFSDirOpen(struct _reent *r, DIR_ITER *dirState, const char *path)
{
struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; //this makes it lots easier!
struct stat st;
char dirname[NITRONAMELENMAX];
char *cptr;
char mydirpath[NITROMAXPATHLEN]; //to hold copy of path string
char *dirpath = mydirpath;
bool pathfound;
if ((cptr = strchr(path, ':')))
path = cptr + 1; //move path past any device names (if it was nixy style wouldnt need this step >_>)
strncpy(dirpath, path, sizeof(mydirpath) - 1); //copy the string (as im gonna mutalate it)
dirStruct->pos = 0;
if (*dirpath == '/') //if first character is '/' use absolute root path plz
dirStruct->cur_dir_id = NITROROOT; //first root dir
else
dirStruct->cur_dir_id = chdirpathid; //else use chdirpath
nitroDirReset(r, dirState); //set dir to current path
do
{
while ((cptr = strchr(dirpath, '/')) == dirpath)
{
dirpath++; //move past any leading / or // together
}
if (cptr)
*cptr = 0; //erase /
if (*dirpath == 0)
{ //are we at the end of the path string?? if so there is nothing to search for we're already here !
pathfound = true; //mostly this handles searches for root or / or no path specified cases
break;
}
pathfound = false;
while (nitroFSDirNext(r, dirState, dirname, &st) == 0)
{
if ((st.st_mode == S_IFDIR) && !(strcmp(dirname, dirpath)))
{ //if its a directory and name matches dirpath
dirStruct->cur_dir_id = dirStruct->dir_id; //move us to the next dir in tree
nitroDirReset(r, dirState); //set dir to current path we just found...
pathfound = true;
break;
}
};
if (!pathfound)
break;
dirpath = cptr + 1; //move to right after last / we found
} while (cptr); // go till after the last /
if (pathfound)
{
return (dirState);
}
else
{
r->_errno = ENOENT;
return (NULL);
}
}
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState)
{
return (0);
}
/*Consts containing relative system path strings*/
const char *syspaths[2] = {
".",
".."};
//reset dir to start of entry selected by dirStruct->cur_dir_id which should be set in dirOpen okai?!
int nitroDirReset(struct _reent *r, DIR_ITER *dirState)
{
struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; //this makes it lots easier!
struct ROM_FNTDir dirsubtable;
off_t *pos = &dirStruct->pos;
nitroSubSeek(pos, fntOffset + ((dirStruct->cur_dir_id & NITRODIRMASK) * sizeof(struct ROM_FNTDir)), SEEK_SET);
nitroSubRead(pos, &dirsubtable, sizeof(dirsubtable));
dirStruct->namepos = dirsubtable.entry_start; //set namepos to first entry in this dir's table
dirStruct->entry_id = dirsubtable.entry_file_id; //get number of first file ID in this branch
dirStruct->parent_id = dirsubtable.parent_id; //save parent ID in case we wanna add ../ functionality
dirStruct->spc = 0; //system path counter, first two dirnext's deliver . and ..
return (0);
}
int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st)
{
unsigned char next;
struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; //this makes it lots easier!
off_t *pos = &dirStruct->pos;
if (dirStruct->spc <= 1)
{
if (st)
st->st_mode = S_IFDIR;
if ((dirStruct->spc == 0) || (dirStruct->cur_dir_id == NITROROOT))
{ // "." or its already root (no parent)
dirStruct->dir_id = dirStruct->cur_dir_id;
}
else
{ // ".."
dirStruct->dir_id = dirStruct->parent_id;
}
strcpy(filename, syspaths[dirStruct->spc++]);
return (0);
}
nitroSubSeek(pos, fntOffset + dirStruct->namepos, SEEK_SET);
nitroSubRead(pos, &next, sizeof(next));
// next: high bit 0x80 = entry isdir.. other 7 bits r size, the 16 bits following name are dir's entryid (starts with f000)
// 00 = endoftable //
if (next)
{
if (next & NITROISDIR)
{
if (st)
st->st_mode = S_IFDIR;
next &= NITROISDIR ^ 0xff; //invert bits and mask off 0x80
nitroSubRead(pos, filename, next);
nitroSubRead(&dirStruct->pos, &dirStruct->dir_id, sizeof(dirStruct->dir_id)); //read the dir_id
//grr cant get the struct member size?, just wanna test it so moving on...
// nitroSubRead(pos,&dirStruct->dir_id,sizeof(u16)); //read the dir_id
dirStruct->namepos += next + sizeof(u16) + 1; //now we points to next one plus dir_id size:D
}
else
{
if (st)
st->st_mode = 0;
nitroSubRead(pos, filename, next);
dirStruct->namepos += next + 1; //now we points to next one :D
//read file info to get filesize (and for fileopen)
nitroSubSeek(pos, fatOffset + (dirStruct->entry_id * sizeof(struct ROM_FAT)), SEEK_SET);
nitroSubRead(pos, &dirStruct->romfat, sizeof(dirStruct->romfat)); //retrieve romfat entry (contains filestart and end positions)
dirStruct->entry_id++; //advance ROM_FNTStrFile ptr
if (st)
st->st_size = dirStruct->romfat.bottom - dirStruct->romfat.top; //calculate filesize
}
filename[(int)next] = 0; //zero last char
return (0);
}
else
{
r->_errno = EIO;
return (-1);
}
}
//fs functs
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
{
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fileStruct;
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
dirState.dirStruct = &dirStruct; //create a temp dirstruct
struct _reent dre;
struct stat st; //all these are just used for reading the dir ~_~
char dirfilename[NITROMAXPATHLEN]; // to hold a full path (i tried to avoid using so much stack but blah :/)
char *filename; // to hold filename
char *cptr; //used to string searching and manipulation
cptr = (char *)path + strlen(path); //find the end...
filename = NULL;
do
{
if ((*cptr == '/') || (*cptr == ':'))
{ // split at either / or : (whichever comes first form the end!)
cptr++;
strncpy(dirfilename, path, cptr - path); //copy string up till and including/ or : zero rest
dirfilename[cptr - path] = 0; //it seems strncpy doesnt always zero?!
filename = cptr; //filename = now remainder of string
break;
}
} while (cptr-- != path); //search till start
if (!filename)
{ //we didnt find a / or : ? shouldnt realyl happen but if it does...
filename = (char *)path; //filename = complete path
dirfilename[0] = 0; //make directory path ""
}
if (nitroFSDirOpen(&dre, &dirState, dirfilename))
{
fatStruct->start = 0;
while (nitroFSDirNext(&dre, &dirState, dirfilename, &st) == 0)
{
if (!(st.st_mode & S_IFDIR) && (strcmp(dirfilename, filename) == 0))
{ //Found the *file* youre looking for!!
fatStruct->start = dirStruct.romfat.top;
fatStruct->end = dirStruct.romfat.bottom;
if (hasLoader)
{
fatStruct->start += LOADEROFFSET;
fatStruct->end += LOADEROFFSET;
}
break;
}
}
if (fatStruct->start)
{
nitroSubSeek(&fatStruct->pos, fatStruct->start, SEEK_SET); //seek to start of file
return (0); //woot!
}
nitroFSDirClose(&dre, &dirState);
}
if (r->_errno == 0)
{
r->_errno = ENOENT;
}
return (-1); //teh fail
}
int nitroFSClose(struct _reent *r, void* fd)
{
return (0);
}
ssize_t nitroFSRead(struct _reent *r, void* fd, char *ptr, size_t len)
{
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd;
off_t *npos = &fatStruct->pos;
if (*npos + len > fatStruct->end)
len = fatStruct->end - *npos; //dont let us read past the end plz!
if (*npos > fatStruct->end)
return (0); //hit eof
return (nitroSubRead(npos, ptr, len));
}
off_t nitroFSSeek(struct _reent *r, void* fd, off_t pos, int dir)
{
//need check for eof here...
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd;
off_t *npos = &fatStruct->pos;
if (dir == SEEK_SET)
pos += fatStruct->start; //add start from .nds file offset
else if (dir == SEEK_END)
pos += fatStruct->end; //set start to end of file (useless?)
if (pos > fatStruct->end)
return (-1); //dont let us read past the end plz!
nitroSubSeek(npos, pos, dir);
return (*npos - fatStruct->start);
}
int nitroFSFstat(struct _reent *r, void* fd, struct stat *st)
{
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd;
st->st_size = fatStruct->end - fatStruct->start;
return (0);
}
int nitroFSstat(struct _reent *r, const char *file, struct stat *st)
{
struct nitroFSStruct fatStruct;
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
if (nitroFSOpen(NULL, &fatStruct, file, 0, 0) >= 0)
{
st->st_mode = S_IFREG;
st->st_size = fatStruct.end - fatStruct.start;
return (0);
}
dirState.dirStruct = &dirStruct;
if ((nitroFSDirOpen(r, &dirState, file) != NULL))
{
st->st_mode = S_IFDIR;
nitroFSDirClose(r, &dirState);
return (0);
}
r->_errno = ENOENT;
return (-1);
}
int nitroFSChdir(struct _reent *r, const char *name)
{
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
dirState.dirStruct = &dirStruct;
if ((name != NULL) && (nitroFSDirOpen(r, &dirState, name) != NULL))
{
chdirpathid = dirStruct.cur_dir_id;
nitroFSDirClose(r, &dirState);
return (0);
}
else
{
r->_errno = ENOENT;
return (-1);
}
}

113
arm9/common/nitrofs.h Normal file
View File

@ -0,0 +1,113 @@
/*
nitrofs.h - eris's wai ossum nitro filesystem device driver header
Based on information found at http://frangoassado.org/ds/rom_spec.txt and from the #dsdev ppls
Kallisti (K) 2008-01-26 All rights reversed.
2008-05-19 v0.2 - New And Improved!! :DDD
* fix'd the fseek SEEK_CUR issue (my fseek funct should not have returned a value :/)
* also thx to wintermute's input realized:
* if you dont give ndstool the -o wifilogo.bmp option it will run on emulators in gba mode
* you then dont need the gba's LOADEROFFSET, so it was set to 0x000
2008-05-21 v0.3 - newer and more improved
* fixed some issues with ftell() (again was fseek's fault u_u;;)
* fixed possible error in detecting sc.gba files when using dldi
* readded support for .gba files in addition to .nds emu
* added stat() support for completedness :)
2008-05-30 v0.5.Turbo - major speed improvement
* This version uses a single filehandle to access the .nds file when not in GBA mode
improving the speed it takes to open a .nds file by around 106ms. This is great for
situations requiring reading alot of seperate small files. However it does take a little
bit longer when reading from multiple files simultainously
(around 122ms over 10,327 0x100 byte reads between 2 files).
2008-06-09
* Fixed bug with SEEK_END where it wouldnt utilize the submitted position..
(now can fseek(f,-128,SEEK_END) to read from end of file :D)
2008-06-18 v0.6.Turbo - . and .. :D
* Today i have added full "." and ".." support.
dirnext() will return . and .. first, and all relevent operations will
support . and .. in pathnames.
2018-09-05 v0.9 - modernize devoptab (by RonnChyran)
* Updated for libsysbase change in devkitARM r46 and above.
*/
#ifndef NITROFS_H
#define NITROFS_H
#include <sys/dir.h>
#include <sys/iosupport.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
int nitroFSInit(const char *ndsfile);
DIR_ITER *nitroFSDirOpen(struct _reent *r, DIR_ITER *dirState, const char *path);
int nitroDirReset(struct _reent *r, DIR_ITER *dirState);
int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st);
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState);
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
int nitroFSClose(struct _reent *r, void *fd);
ssize_t nitroFSRead(struct _reent *r, void *fd, char *ptr, size_t len);
off_t nitroFSSeek(struct _reent *r, void *fd, off_t pos, int dir);
int nitroFSFstat(struct _reent *r, void *fd, struct stat *st);
int nitroFSstat(struct _reent *r, const char *file, struct stat *st);
int nitroFSChdir(struct _reent *r, const char *name);
#define LOADERSTR "PASS" //look for this
#define LOADERSTROFFSET 0xac
#define LOADEROFFSET 0x0200
#define FNTOFFSET 0x40
#define FATOFFSET 0x48
#define NITRONAMELENMAX 0x80 //max file name is 127 +1 for zero byte :D
#define NITROMAXPATHLEN 0x100 //256 bytes enuff?
#define NITROROOT 0xf000 //root entry_file_id
#define NITRODIRMASK 0x0fff //remove leading 0xf
#define NITROISDIR 0x80 //mask to indicate this name entry is a dir, other 7 bits = name length
//Directory filename subtable entry structure
struct ROM_FNTDir
{
u32 entry_start;
u16 entry_file_id;
u16 parent_id;
};
//Yo, dis table is fat (describes the structures
struct ROM_FAT
{
u32 top; //start of file in rom image
u32 bottom; //end of file in rom image
};
struct nitroFSStruct
{
off_t pos; //where in the file am i?
off_t start; //where in the rom this file starts
off_t end; //where in the rom this file ends
};
struct nitroDIRStruct
{
off_t pos; //where in the file am i?
off_t namepos; //ptr to next name to lookup in list
struct ROM_FAT romfat;
u16 entry_id; //which entry this is (for files only) incremented with each new file in dir?
u16 dir_id; //which directory entry this is.. used ofc for dirs only
u16 cur_dir_id; //which directory entry we are using
u16 parent_id; //who is the parent of the current directory (this can be used to easily ../ )
u8 spc; //system path count.. used by dirnext, when 0=./ 1=../ >=2 actual dirs
};
#ifdef __cplusplus
}
#endif
#endif

View File

@ -26,6 +26,7 @@
#include "encryption.h"
#include "tonccpy.h"
#include "launcherData.h"
enum {
ERR_NONE = 0x00,
@ -33,13 +34,13 @@ enum {
ERR_STS_LOAD_BIN = 0x02,
ERR_STS_HOOK_BIN = 0x03,
ERR_STS_START = 0x04,
// initCard error codes:
ERR_LOAD_NORM = 0x11,
ERR_LOAD_OTHR = 0x12,
ERR_SEC_NORM = 0x13,
ERR_SEC_OTHR = 0x14,
ERR_LOGO_CRC = 0x15,
ERR_HEAD_CRC = 0x16,
// initCard error codes:
ERR_LOAD_NORM = 0x11,
ERR_LOAD_OTHR = 0x12,
ERR_SEC_NORM = 0x13,
ERR_SEC_OTHR = 0x14,
ERR_LOGO_CRC = 0x15,
ERR_HEAD_CRC = 0x16,
} ERROR_CODES;
// NAND Card commands
@ -54,30 +55,24 @@ enum {
#define CARD_CMD_NAND_UNKNOWN 0xBB
#define CARD_CMD_NAND_READ_ID 0x94
typedef union
{
char title[4];
u32 key;
} GameCode;
typedef union { char title[4]; u32 key; } GameCode;
static bool twlBlowfish = false;
static bool normalChip = false; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000
static u32 portFlags = 0;
static u32 headerData[0x1000/sizeof(u32)] = {0};
static u32 secureArea[CARD_SECURE_AREA_SIZE/sizeof(u32)] = {0};
static u32 iCardId;
static bool noSlotCrypto = true;
static bool nandChip = false;
static int nandSection = -1; // -1 = ROM, above that is the current 128 KiB section in RW
u32 cardNandRomEnd = 0;
u32 cardNandRwStart = 0;
static const u8 cardSeedBytes[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5};
static const u8 cardSeedBytes[] = { 0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5 };
static u32 getRandomNumber(void) {
return rand();
}
static u32 getRandomNumber(void) { return rand(); }
//---------------------------------------------------------------------------------
// https://github.com/devkitPro/libnds/blob/105d4943dbac8f2bd99a47b22cd3ed48f96af083/source/common/card.c#L47-L62
@ -106,16 +101,11 @@ static void cardPolledTransferWrite(u32 flags, u32 *buffer, u32 length, const u8
} while (REG_ROMCTRL & CARD_BUSY);
}
static void decryptSecureArea (u32 gameCode, u32* secureArea, int iCardDevice)
{
static void decryptSecureArea (u32 gameCode, u32* secureArea, int iCardDevice) {
init_keycode (gameCode, 2, 8, iCardDevice);
crypt_64bit_down (secureArea);
init_keycode (gameCode, 3, 8, iCardDevice);
for (int i = 0; i < 0x200; i+= 2) {
crypt_64bit_down (secureArea + i);
}
for (int i = 0; i < 0x200; i+= 2)crypt_64bit_down (secureArea + i);
}
static struct {
@ -151,13 +141,10 @@ static void initKey1Encryption (u8* cmdData, int iCardDevice) {
}
// Note: cmdData must be aligned on a word boundary
static void createEncryptedCommand (u8 command, u8* cmdData, u32 block)
{
static void createEncryptedCommand (u8 command, u8* cmdData, u32 block) {
unsigned long iii, jjj;
if (command != CARD_CMD_SECURE_READ) {
block = key1data.llll;
}
if (command != CARD_CMD_SECURE_READ)block = key1data.llll;
if (command == CARD_CMD_ACTIVATE_SEC) {
iii = key1data.mmm;
@ -200,7 +187,6 @@ static void switchToTwlBlowfish(sNDSHeaderExt* ndsHeader) {
if (twlBlowfish || ndsHeader->unitCode == 0) return;
// Used for dumping the DSi arm9i/7i binaries
u32 portFlagsKey1, portFlagsSecRead;
int secureBlockNumber;
int i;
@ -317,8 +303,7 @@ static void switchToTwlBlowfish(sNDSHeaderExt* ndsHeader) {
}
int cardInit (sNDSHeaderExt* ndsHeader)
{
int cardInit (sNDSHeaderExt* ndsHeader) {
u32 portFlagsKey1, portFlagsSecRead;
normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000
nandChip = false;
@ -327,7 +312,7 @@ int cardInit (sNDSHeaderExt* ndsHeader)
int i;
u8 cmdData[8] __attribute__ ((aligned));
GameCode* gameCode;
noSlotCrypto = false;
twlBlowfish = false;
sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart
@ -368,8 +353,7 @@ int cardInit (sNDSHeaderExt* ndsHeader)
tonccpy(ndsHeader, headerData, 0x200);
if ((ndsHeader->unitCode != 0) || (ndsHeader->dsi_flags != 0))
{
if ((ndsHeader->unitCode != 0) || (ndsHeader->dsi_flags != 0)) {
// Extended header found
if(normalChip) {
for(int i = 0; i < 8; i++) {
@ -481,10 +465,7 @@ int cardInit (sNDSHeaderExt* ndsHeader)
cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData);
//CycloDS doesn't like the dsi secure area being decrypted
if((ndsHeader->arm9romOffset != 0x4000) || secureArea[0] || secureArea[1])
{
decryptSecureArea (gameCode->key, secureArea, 0);
}
if((ndsHeader->arm9romOffset != 0x4000) || secureArea[0] || secureArea[1])decryptSecureArea (gameCode->key, secureArea, 0);
if (secureArea[0] == 0x72636e65 /*'encr'*/ && secureArea[1] == 0x6a624f79 /*'yObj'*/) {
// Secure area exists, so just clear the tag
@ -510,22 +491,78 @@ int cardInit (sNDSHeaderExt* ndsHeader)
return ERR_NONE;
}
u32 cardGetId() {
return iCardId;
// If booted from DSi System Menu short cart init with no card reads or pokes to rom ctrl registers can be done.
// System Menu is nice enough to do this for you. :P
// (also is the case for booting from DS Download Play. ;) )
int cardInitShort(sNDSHeaderExt* ndsHeader) {
normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000
nandChip = false;
nandSection = -1;
noSlotCrypto = true;
GameCode* gameCode;
twlBlowfish = false;
sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart
toncset(headerData, 0, 0x1000);
// tonccpy(headerData, (u32*)0x027FE940, 0x200); // Header of currently inserted cart is preserved here during DS Download Play.
tonccpy(headerData, (u32*)CartHeaderCopy, 0x180); // Header of currently inserted cart is preserved here from DSi System Menu on DSi consoles.
iCardId = *(u32*)CartChipIDCopy; // This location contains cart's chipID when booting DSiWare that has Slot-1 access.
// iCardId = *(u32*)0x027FF800; // This location contains cart's chipID for DS Download Play users
normalChip = (iCardId & BIT(31)) != 0; // ROM chip ID MSB
nandChip = (iCardId & BIT(27)) != 0; // Card has a NAND chip
tonccpy(ndsHeader, headerData, 0x200);
// Check header CRC
if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E))return ERR_HEAD_CRC;
// Initialise blowfish encryption for KEY1 commands and decrypting the secure area
gameCode = (GameCode*)ndsHeader->gameCode;
init_keycode (gameCode->key, 2, 8, 0);
// Port 40001A4h setting for normal reads (command B7)
portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7);
if (secureArea[0] == 0x72636e65 /*'encr'*/ && secureArea[1] == 0x6a624f79 /*'yObj'*/) {
// Secure area exists, so just clear the tag
secureArea[0] = 0xe7ffdeff;
secureArea[1] = 0xe7ffdeff;
}
// Set NAND card section location variables
if (nandChip) {
if(ndsHeader->nandRomEnd != 0) {
// TWL cards (Face Training) multiply by 0x80000 instead of 0x20000
cardNandRomEnd = ndsHeader->nandRomEnd * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000);
cardNandRwStart = ndsHeader->nandRwStart * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000);
} else {
// Jam with the Band (J) (大合奏!バンドブラザーズ) doesn't have the RW section in the header
cardNandRomEnd = 0x7200000;
cardNandRwStart = 0x7200000;
}
}
return ERR_NONE;
}
void cardRead (u32 src, void* dest, bool nandSave)
{
u32 cardGetId() { return iCardId; }
void cardRead (u32 src, void* dest, bool nandSave) {
sNDSHeaderExt* ndsHeader = (sNDSHeaderExt*)headerData;
if (src >= 0 && src < 0x1000) {
// Read header
tonccpy (dest, (u8*)headerData + src, 0x200);
return;
} else if (src < CARD_SECURE_AREA_OFFSET) {
} else if ((src < CARD_SECURE_AREA_OFFSET) && !noSlotCrypto) {
toncset (dest, 0, 0x200);
return;
} else if (src < CARD_DATA_OFFSET) {
} else if ((src < CARD_DATA_OFFSET) && !noSlotCrypto) {
// Read data from secure area
tonccpy (dest, (u8*)secureArea + src - CARD_SECURE_AREA_OFFSET, 0x200);
return;
@ -547,20 +584,46 @@ void cardRead (u32 src, void* dest, bool nandSave)
}
}
cardParamCommand (CARD_CMD_DATA_READ, src,
portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1),
dest, 0x200/sizeof(u32));
cardParamCommand (CARD_CMD_DATA_READ, src, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), dest, 0x200/sizeof(u32));
if (src > ndsHeader->romSize && !(nandSave && src >= cardNandRwStart)) {
switchToTwlBlowfish(ndsHeader);
if (src > ndsHeader->romSize && !(nandSave && src >= cardNandRwStart) && !noSlotCrypto)switchToTwlBlowfish(ndsHeader);
}
void cardReadAlt (u32 src, void* dest, size_t size) {
sNDSHeaderExt* ndsHeader = (sNDSHeaderExt*)headerData;
size_t readSize;
if (src >= 0 && src < 0x1000) {
// Read header
tonccpy (dest, (u8*)headerData + src, 0x200);
return;
} else if ((src < CARD_SECURE_AREA_OFFSET) && !noSlotCrypto) {
toncset (dest, 0, 0x200);
return;
} else if ((src < CARD_DATA_OFFSET) && !noSlotCrypto) {
// Read data from secure area
tonccpy (dest, (u8*)secureArea + src - CARD_SECURE_AREA_OFFSET, 0x200);
return;
} else if ((ndsHeader->unitCode != 0) && (src >= ndsHeader->arm9iromOffset) && (src < ndsHeader->arm9iromOffset+CARD_SECURE_AREA_SIZE)) {
// Read data from secure area
tonccpy (dest, (u8*)secureArea + src - ndsHeader->arm9iromOffset, 0x200);
return;
}
if ((src > ndsHeader->romSize) && !noSlotCrypto)switchToTwlBlowfish(ndsHeader);
while (size > 0) {
readSize = size < CARD_DATA_BLOCK_SIZE ? size : CARD_DATA_BLOCK_SIZE;
cardParamCommand (CARD_CMD_DATA_READ, src, (portFlags &~CARD_BLK_SIZE(7)) | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), dest, readSize);
src += readSize;
dest += readSize/sizeof(*dest);
size -= readSize;
}
}
// src must be a 0x800 byte array
void cardWriteNand (void* src, u32 dest)
{
if (dest < cardNandRwStart || !nandChip)
return;
void cardWriteNand (void* src, u32 dest) {
if (dest < cardNandRwStart || !nandChip)return;
if (nandSection != (dest - cardNandRwStart) / (128 << 10)) {
if(nandSection != -1) // Need to switch back to ROM mode before switching to another RW section

View File

@ -40,8 +40,10 @@ extern u32 cardNandRomEnd;
extern u32 cardNandRwStart;
int cardInit (sNDSHeaderExt* ndsHeader);
int cardInitShort(sNDSHeaderExt* ndsHeader);
void cardRead (u32 src, void* dest, bool nandSave);
void cardReadAlt (u32 src, void* dest, size_t size);
u32 cardGetId (void);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

BIN
arm9/graphics/font.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
arm9/graphics/font6x8.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

296
arm9/hbmenu/args.cpp Normal file
View File

@ -0,0 +1,296 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2017
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
Michael "mtheall" Theall
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <unistd.h>
#include <utility>
#include <vector>
#include "args.h"
using namespace std;
static const string NDS_EXT = ".nds";
static const string ARG_EXT = ".argv";
static const string EXT_EXT = ".ext";
static const char EXT_DIR[] = "/nds";
static const char SEPARATORS[] = "\n\r\t ";
/* Checks if s1 ends with s2, ignoring case.
Returns true if it does, false otherwise.
*/
static bool strCaseEnd(const string& s1, const string& s2) {
return (s1.size() >= s2.size() &&
strcasecmp(s1.c_str() + s1.size() - s2.size(), s2.c_str()) == 0);
}
/* Parses the contents of the file given by filename into argarray. Arguments
are tokenized based on whitespace.
*/
static bool parseArgFileAll(const string& filename, vector<string>& argarray) {
FILE *argfile = fopen(filename.c_str(), "rb");
if (!argfile) {
return false;
}
char *line = NULL;
size_t lineSize = 0;
while (__getline(&line, &lineSize, argfile) >= 0) {
// Find comment and end string there
char *pstr = strchr(line, '#');
if (pstr) {
*pstr = '\0';
}
// Tokenize arguments
char *saveptr;
pstr = strtok_r(line, SEPARATORS, &saveptr);
while (pstr) {
argarray.emplace_back(pstr);
pstr = strtok_r(NULL, SEPARATORS, &saveptr);
}
}
if (line) {
free(line);
}
fclose(argfile);
return argarray.size() > 0;
}
/* Parses the argument file given by filename and returns the NDS file that it
* points to.
*/
static bool parseArgFileNds(const std::string& filename, std::string& ndsPath) {
bool success = false;
FILE *argfile = fopen(filename.c_str(), "rb");
if (!argfile) {
return false;
}
char *line = NULL;
size_t lineSize = 0;
while (__getline(&line, &lineSize, argfile) >= 0) {
char *pstr = NULL;
// Find comment and end string there
pstr = strchr(line, '#');
if (pstr) {
*pstr = '\0';
}
// Tokenize arguments
char *saveptr;
pstr = strtok_r(line, SEPARATORS, &saveptr);
if (pstr) {
// Only want the first token, which should be the NDS file name
ndsPath = pstr;
success = true;
break;
}
}
if (line) {
free(line);
}
fclose(argfile);
return success;
}
/* Converts a plain filename into an absolute path. If it's already an absolute
* path, it is returned as-is. If basePath is NULL, the current working directory
* is used.
* Returns true on success, false on failure.
*/
static bool toAbsPath(const string& filename, const char* basePath, string& filePath) {
// Copy existing absolute or empty paths
if (filename.size() == 0 || filename[0] == '/') {
filePath = filename;
return true;
}
if (basePath == NULL) {
// Get current working directory (uses C-strings)
vector<char> cwd(PATH_MAX);
if (getcwd (cwd.data(), cwd.size()) == NULL) {
// Path was too long, abort
return false;
}
// Copy CWD into path
filePath = cwd.data();
} else {
// Just copy the base path
filePath = basePath;
}
// Ensure there's a path separator
if (filePath.back() != '/') {
filePath += '/';
}
// Now append the filename
filePath += filename;
return true;
}
/* Convert a dataFilePath to the path of the ext file that specifies the
* handler.
* Returns true on success, false on failure
*/
static bool toExtPath(const string& dataFilePath, string& extFilePath) {
// Figure out what the file extension is
size_t extPos = dataFilePath.rfind('.');
if (extPos == string::npos) {
return false;
}
extPos += 1;
if (extPos >= dataFilePath.size()) {
return false;
}
// Construct handler path from extension. Handlers are in the EXT_DIR and
// end with EXT_EXT.
const string ext = dataFilePath.substr(extPos);
if (!toAbsPath(ext, EXT_DIR, extFilePath)) {
return false;
}
extFilePath += EXT_EXT;
return true;
}
bool argsNdsPath(const std::string& filePath, std::string& ndsPath) {
if (strCaseEnd(filePath, NDS_EXT)) {
ndsPath = filePath;
return true;
} else if (strCaseEnd(filePath, ARG_EXT)) {
return parseArgFileNds(filePath, ndsPath);
} else {
// This is a data file associated with a handler NDS by an ext file
string extPath;
if (!toExtPath(filePath, extPath)) {
return false;
}
string ndsRelPath;
if (!parseArgFileNds(extPath, ndsRelPath)) {
return false;
}
// Handler is in EXT_DIR
return toAbsPath(ndsRelPath, EXT_DIR, ndsPath);
}
return false;
}
bool argsFillArray(const string& filePath, vector<string>& argarray) {
// Ensure argarray is empty
argarray.clear();
if (strCaseEnd(filePath, NDS_EXT)) {
string absPath;
if (!toAbsPath(filePath, NULL, absPath)) {
return false;
}
argarray.push_back(move(absPath));
} else if (strCaseEnd(filePath, ARG_EXT)) {
if (!parseArgFileAll(filePath, argarray)) {
return false;
}
// Ensure argv[0] is absolute path
string absPath;
if (!toAbsPath(argarray[0], NULL, absPath)) {
return false;
}
argarray[0] = absPath;
} else {
// This is a data file associated with a handler NDS by an ext file
string extPath;
if (!toExtPath(filePath, extPath)) {
return false;
}
// Read the arg file for the extension handler
if (!parseArgFileAll(extPath, argarray)) {
return false;
}
// Extension handler relative path is relative to EXT_DIR, not CWD
string absPath;
if (!toAbsPath(argarray[0], EXT_DIR, absPath)) {
return false;
}
argarray[0] = absPath;
// Add the data filename to the end. Its path is relative to CWD.
if (!toAbsPath(filePath, NULL, absPath)) {
return false;
}
argarray.push_back(move(absPath));
}
return argarray.size() > 0 && strCaseEnd(argarray[0], NDS_EXT);
}
vector<string> argsGetExtensionList() {
vector<string> extensionList;
// Always supported files: NDS binaries and predefined argument lists
extensionList.push_back(NDS_EXT);
extensionList.push_back(ARG_EXT);
// Get a list of extension files: argument lists associated with a file type
DIR *dir = opendir (EXT_DIR);
if (dir) {
for (struct dirent* dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) {
// Add the name component of all files ending with EXT_EXT to the list
if (dirent->d_type != DT_REG) {
continue;
}
if (dirent->d_name[0] != '.' && strCaseEnd(dirent->d_name, EXT_EXT)) {
size_t extPos = strlen(dirent->d_name) - EXT_EXT.size();
dirent->d_name[extPos] = '\0';
extensionList.push_back(dirent->d_name);
}
}
closedir(dir);
}
return extensionList;
}

47
arm9/hbmenu/args.h Normal file
View File

@ -0,0 +1,47 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2017
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
Michael "mtheall" Theall
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#ifndef ARGS_H
#define ARGS_H
#include <string>
#include <vector>
/* Convert a file path of any type (e.g. .nds or .argv) into a path to the NDS
* file to be opened. The returned path may be absolute or relative to the
* current working directory.
* Returns true on success, false on failure.
*/
bool argsNdsPath(const std::string& filePath, std::string& ndsPath);
/* Convert a file path of any type into an argument array by filling the array
* that is passed in. The first argument will be the full path to an NDS file.
* Returns true on success, false on failure.
*/
bool argsFillArray(const std::string& filePath, std::vector<std::string>& argarray);
/* Return a list of all file extensions that can be browsed and opened.
*/
std::vector<std::string> argsGetExtensionList();
#endif // ARGS_H

361
arm9/hbmenu/file_browse.cpp Normal file
View File

@ -0,0 +1,361 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2017
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include "file_browse.h"
#include <vector>
#include <algorithm>
#include <unistd.h>
#include <string>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <nds.h>
#include "hbmenu.h"
#include "iconTitle.h"
#include "read_card.h"
#include <maxmod9.h>
#include "soundbank.h"
#include "soundbank_bin.h"
#define SCREEN_COLS 30
#define ENTRIES_PER_SCREEN 20
#define ENTRIES_START_ROW 2
#define ENTRY_PAGE_LENGTH 10
using namespace std;
struct DirEntry {
string name;
bool isDirectory;
};
static mm_sound_effect sfxBack;
static mm_sound_effect sfxLaunch;
static mm_sound_effect sfxSelect;
static mm_sound_effect sfxWrong;
static bool audioReady = false;
void InitAudio() {
mmInitDefaultMem((mm_addr)soundbank_bin);
mmLoadEffect( SFX_BACK );
mmLoadEffect( SFX_LAUNCH );
mmLoadEffect( SFX_SELECT );
mmLoadEffect( SFX_WRONG );
sfxBack = {
{ SFX_BACK } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
sfxLaunch = {
{ SFX_LAUNCH } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
sfxSelect = {
{ SFX_SELECT } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
sfxWrong = {
{ SFX_WRONG } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
audioReady = true;
}
static ALIGN(4) sNDSHeaderExt ntrHeader;
static bool cardInserted = false;
static bool cardLoaded = false;
static bool initialLoad = true;
extern bool cartSelected;
static void cartCheck() {
switch (REG_SCFG_MC) {
case 0x10: {
if (!cardInserted)cardInserted = true;
initialLoad = false;
}break;
case 0x11: {
cardInserted = false;
initialLoad = false;
if (cartSelected)cartSelected = false;
}break;
case 0x18: {
cardInserted = true;
}break;
}
if (!cardInserted) {
if (cardLoaded) {
clearCartIcon(true);
cardLoaded = false;
if (cartSelected)cartSelected = false;
ToggleBackground();
}
if (cartSelected)cartSelected = false;
return;
}
if (cardInserted && initialLoad) {
cardInitShort(&ntrHeader);
cartIconUpdate(0, initialLoad);
initialLoad = false;
cardLoaded = true;
} else if (cardInserted && !cardLoaded){
cardInit(&ntrHeader);
for (int i = 0; i < 25; i++)swiWaitForVBlank();
cartIconUpdate(ntrHeader.bannerOffset, false);
cardLoaded = true;
}
}
bool nameEndsWith (const string& name, const vector<string> extensionList) {
if (name.size() == 0) return false;
if (name.front() == '.') return false;
if (extensionList.size() == 0) return true;
for (int i = 0; i < (int)extensionList.size(); i++) {
const string ext = extensionList.at(i);
if ( strcasecmp (name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true;
}
return false;
}
bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) {
if (!lhs.isDirectory && rhs.isDirectory)return false;
if (lhs.isDirectory && !rhs.isDirectory)return true;
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
}
void getDirectoryContents (vector<DirEntry>& dirContents, const vector<string> extensionList) {
struct stat st;
dirContents.clear();
DIR *pdir = opendir (".");
if (pdir == NULL) {
iprintf ("Unable to open the directory.\n");
} else {
while(true) {
DirEntry dirEntry;
struct dirent* pent = readdir(pdir);
if(pent == NULL) break;
stat(pent->d_name, &st);
dirEntry.name = pent->d_name;
dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false;
if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) {
dirContents.push_back (dirEntry);
}
}
closedir(pdir);
}
sort(dirContents.begin(), dirContents.end(), dirEntryPredicate);
}
void getDirectoryContents (vector<DirEntry>& dirContents) {
vector<string> extensionList;
getDirectoryContents (dirContents, extensionList);
}
void showDirectoryContents (const vector<DirEntry>& dirContents, int startRow) {
char path[PATH_MAX];
getcwd(path, PATH_MAX);
// Clear the screen
consoleClear();
// Print the path
if (strlen(path) < SCREEN_COLS) {
iprintf ("%s", path);
} else {
iprintf ("%s", path + strlen(path) - SCREEN_COLS);
}
// Move to 2nd row
iprintf ("\x1b[1;0H");
// Print line of dashes
iprintf ("------------------------------");
// Print directory listing
for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) {
const DirEntry* entry = &dirContents.at(i + startRow);
char entryName[SCREEN_COLS + 1];
// Set row
iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW);
if (entry->isDirectory) {
strncpy (entryName, entry->name.c_str(), SCREEN_COLS);
entryName[SCREEN_COLS - 3] = '\0';
iprintf (" [%s]", entryName);
} else {
strncpy (entryName, entry->name.c_str(), SCREEN_COLS);
entryName[SCREEN_COLS - 1] = '\0';
iprintf (" %s", entryName);
}
}
}
string browseForFile (const vector<string>& extensionList) {
if (!audioReady)InitAudio();
int pressed = 0;
int screenOffset = 0;
int fileOffset = 0;
vector<DirEntry> dirContents;
getDirectoryContents (dirContents, extensionList);
showDirectoryContents (dirContents, screenOffset);
while (true) {
// Clear old cursors
for (int i = ENTRIES_START_ROW; i < ENTRIES_PER_SCREEN + ENTRIES_START_ROW; i++) {
iprintf ("\x1b[%d;0H ", i);
}
// Show cursor
iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW);
iconTitleUpdate (dirContents.at(fileOffset).isDirectory, dirContents.at(fileOffset).name);
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do {
cartCheck();
scanKeys();
pressed = keysDownRepeat();
swiWaitForVBlank();
} while (!pressed);
if (!cartSelected) {
if (pressed & KEY_UP) { fileOffset -= 1; mmEffectEx(&sfxSelect); }
if (pressed & KEY_DOWN) { fileOffset += 1; mmEffectEx(&sfxSelect); }
if (pressed & KEY_LEFT) { fileOffset -= ENTRY_PAGE_LENGTH; mmEffectEx(&sfxSelect); }
if (pressed & KEY_RIGHT) { fileOffset += ENTRY_PAGE_LENGTH; mmEffectEx(&sfxSelect); }
if (fileOffset < 0)fileOffset = dirContents.size() - 1; // Wrap around to bottom of list
if (fileOffset > ((int)dirContents.size() - 1)) fileOffset = 0; // Wrap around to top of list
// Scroll screen if needed
if (fileOffset < screenOffset) {
screenOffset = fileOffset;
showDirectoryContents (dirContents, screenOffset);
}
if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) {
screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1;
showDirectoryContents (dirContents, screenOffset);
}
}
if ((pressed & KEY_X)) {
if (cardLoaded) {
mmEffectEx(&sfxSelect);
if (cartSelected) {
cartSelected = false;
ToggleBackground();
} else if (cardLoaded) {
cartSelected = true;
ToggleBackground();
}
} else {
mmEffectEx(&sfxWrong);
}
}
if (cartSelected) {
if (pressed & KEY_UP)mmEffectEx(&sfxWrong);
if (pressed & KEY_DOWN)mmEffectEx(&sfxWrong);
if (pressed & KEY_LEFT)mmEffectEx(&sfxWrong);
if (pressed & KEY_RIGHT)mmEffectEx(&sfxWrong);
if ((pressed & KEY_A)) {
if (cardLoaded) {
mmEffectEx(&sfxLaunch);
// Clear the screen
consoleClear();
return "CARTBOOT";
} else {
mmEffectEx(&sfxWrong);
}
}
} else {
if (pressed & KEY_A) {
DirEntry* entry = &dirContents.at(fileOffset);
if (entry->isDirectory) {
mmEffectEx(&sfxSelect);
iprintf("Entering directory\n");
// Enter selected directory
chdir (entry->name.c_str());
getDirectoryContents (dirContents, extensionList);
screenOffset = 0;
fileOffset = 0;
showDirectoryContents (dirContents, screenOffset);
} else {
mmEffectEx(&sfxLaunch);
// Clear the screen
consoleClear();
// Return the chosen file
return entry->name;
}
}
if (pressed & KEY_B) {
mmEffectEx(&sfxBack);
// Go up a directory
chdir ("..");
getDirectoryContents (dirContents, extensionList);
screenOffset = 0;
fileOffset = 0;
showDirectoryContents (dirContents, screenOffset);
}
}
}
}

32
arm9/hbmenu/file_browse.h Normal file
View File

@ -0,0 +1,32 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2017
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#ifndef FILE_BROWSE_H
#define FILE_BROWSE_H
#include <string>
#include <vector>
std::string browseForFile (const std::vector<std::string>& extensionList);
#endif //FILE_BROWSE_H

106
arm9/hbmenu/hbmenu.cpp Normal file
View File

@ -0,0 +1,106 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2013
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include <nds.h>
#include <stdio.h>
#include <fat.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include "args.h"
#include "file_browse.h"
#include "font.h"
#include "hbmenu_consolebg.h"
#include "iconTitle.h"
#include "nds_loader_arm9.h"
#include "launcherData.h"
#include "../source/launch_engine.h"
using namespace std;
static void InitGUI(void) {
iconTitleInit();
videoSetModeSub(MODE_4_2D);
vramSetBankC(VRAM_C_SUB_BG);
int bgSub = bgInitSub(3, BgType_Bmp8, BgSize_B8_256x256, 1, 0);
PrintConsole *console = consoleInit(0, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 6, false, false);
dmaCopy(hbmenu_consolebgBitmap, bgGetGfxPtr(bgSub), 256*256);
ConsoleFont font;
font.gfx = (u16*)fontTiles;
font.pal = (u16*)fontPal;
font.numChars = 95;
font.numColors = (fontPalLen / 2);
font.bpp = 4;
font.asciiOffset = 32;
font.convertSingleColor = true;
consoleSetFont(console, &font);
dmaCopy(hbmenu_consolebgPal, BG_PALETTE_SUB, 256*2);
BG_PALETTE_SUB[255] = RGB15(31,31,31);
keysSetRepeat(25,5);
consoleSetWindow(console, 1, 1, 30, 22);
}
static int stop(void) {
while (1) {
scanKeys();
while (!keysDown())swiWaitForVBlank();
}
return 0;
}
static int FileBrowser(tLauncherSettings launchdata) {
consoleClear();
vector<string> extensionList = argsGetExtensionList();
chdir("sd:/NTR_Launcher");
while(1) {
string filename = browseForFile(extensionList);
if (cartSelected)break;
// Construct a command line
vector<string> argarray;
if (!argsFillArray(filename, argarray)) {
printf("Invalid NDS or arg file selected\n");
} else {
iprintf("Running %s with %d parameters\n", argarray[0].c_str(), argarray.size());
// Make a copy of argarray using C strings, for the sake of runNdsFile
vector<const char*> c_args;
for (const auto& arg: argarray) { c_args.push_back(arg.c_str()); }
// Try to run the NDS file with the given arguments
int err = runNdsFile(c_args[0], c_args.size(), &c_args[0], launchdata);
iprintf("Start failed. Error %i\n", err);
break;
}
argarray.clear();
}
if (cartSelected) {
runLaunchEngine(launchdata);
}
return stop();
}
void StartFileBrowser(tLauncherSettings launchdata) {
InitGUI();
FileBrowser(launchdata);
}

32
arm9/hbmenu/hbmenu.h Normal file
View File

@ -0,0 +1,32 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2017
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
Michael "mtheall" Theall
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#ifndef HBMENU_H
#define HBMENU_H
#include "launcherData.h"
void StartFileBrowser(tLauncherSettings launchdata);
#endif // HBMENU_H

321
arm9/hbmenu/iconTitle.cpp Normal file
View File

@ -0,0 +1,321 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2013
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
Michael "mtheall" Theall
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include <nds.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include "args.h"
#include "hbmenu_banner.h"
#include "hbmenu_banner_cartSelected.h"
#include "font6x8.h"
#include "read_card.h"
#include "launcherData.h"
#define TITLE_POS_X (13*8)
#define TITLE_POS_Y (10*4)
#define CARTTITLE_OFFSET_Y 12
#define ICON_POS_X 26
#define ICON_POS_Y 40
#define ICON2_POS_X 26
#define ICON2_POS_Y 132
#define TEXT_WIDTH ((22-4)*8/6)
static int bg2, bg3;
static u16 *sprite;
static u16 *sprite2;
static tNDSBanner banner;
static tNDSBanner* cartBanner;
bool cartSelected = false;
static inline void writecharRS (int row, int col, u16 car) {
// get map pointer
u16 *gfx = bgGetMapPtr(bg2);
// get old pair of values from VRAM
u16 oldval = gfx[row*(512/8/2)+(col/2)];
// clear the half we will update
oldval &= (col%2) ? 0x00FF : 0xFF00;
// apply the updated half
oldval |= (col%2) ? (car<<8) : car;
// write back to VRAM
gfx[row*(512/8/2)+col/2] = oldval;
}
static inline void writeRow (int rownum, const char* text, bool isCartBannerText) {
int row = rownum;
if (isCartBannerText)row = (rownum + CARTTITLE_OFFSET_Y);
int i,len,p=0;
len=strlen(text);
if (len>TEXT_WIDTH)len=TEXT_WIDTH;
// clear left part
for (i=0;i<(TEXT_WIDTH-len)/2;i++)writecharRS (row, i, 0);
// write centered text
for (i=(TEXT_WIDTH-len)/2;i<((TEXT_WIDTH-len)/2+len);i++)writecharRS (row, i, text[p++]-' ');
// clear right part
for (i=((TEXT_WIDTH-len)/2+len);i<TEXT_WIDTH;i++)writecharRS (row, i, 0);
}
static inline void clearIcon (void) { dmaFillHalfWords(0, sprite, sizeof(banner.icon)); }
void clearCartIcon(bool clearBannerText) {
if (clearBannerText) {
writeRow (0, "", true);
writeRow (1, "(no cart inserted)", true);
writeRow (2, "", true);
writeRow (3, "", true);
}
dmaFillHalfWords(0, sprite2, sizeof(banner.icon));
}
void ToggleBackground () {
DC_FlushAll();
if (cartSelected) {
// load compressed bitmap into bg3
decompress(hbmenu_banner_cartSelectedBitmap, bgGetGfxPtr(bg3), LZ77Vram);
} else {
decompress(hbmenu_bannerBitmap, bgGetGfxPtr(bg3), LZ77Vram);
}
// apply the bg changes
// bgUpdate();
}
void iconTitleInit (void) {
// initialize video mode
videoSetMode(MODE_4_2D);
// initialize VRAM banks
vramSetBankA(VRAM_A_MAIN_BG);
vramSetBankB(VRAM_B_MAIN_SPRITE);
// initialize bg2 as a rotation background and bg3 as a bmp background
// http://mtheall.com/vram.html#T2=3&RNT2=96&MB2=3&TB2=0&S2=2&T3=6&MB3=1&S3=1
bg2 = bgInit(2, BgType_Rotation, BgSize_R_512x512, 3, 0);
bg3 = bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 1, 0);
// initialize rotate, scale, and scroll
bgSetRotateScale(bg3, 0, 1<<8, 1<<8);
bgSetScroll(bg3, 0, 0);
bgSetRotateScale(bg2, 0, 8*(1<<8)/6, 1<<8);
bgSetScroll(bg2, -TITLE_POS_X, -TITLE_POS_Y);
// clear bg2's map: 512x512 pixels is 64x64 tiles is 4KB
dmaFillHalfWords(0, bgGetMapPtr(bg2), 4096);
// load compressed font into bg2's tile data
decompress(font6x8Tiles, bgGetGfxPtr(bg2), LZ77Vram);
// load compressed bitmap into bg3
decompress(hbmenu_bannerBitmap, bgGetGfxPtr(bg3), LZ77Vram);
// load font palette
dmaCopy(font6x8Pal, BG_PALETTE, font6x8PalLen);
// apply the bg changes
bgUpdate();
// initialize OAM
oamInit(&oamMain, SpriteMapping_1D_128, false);
sprite = oamAllocateGfx(&oamMain, SpriteSize_32x32, SpriteColorFormat_16Color);
sprite2 = oamAllocateGfx(&oamMain, SpriteSize_32x32, SpriteColorFormat_16Color);
dmaFillHalfWords(0, sprite, sizeof(banner.icon));
dmaFillHalfWords(0, sprite2, sizeof(banner.icon));
oamSet(&oamMain, 0, ICON_POS_X, ICON_POS_Y, 0, 0, SpriteSize_32x32, SpriteColorFormat_16Color, sprite, -1, 0, 0, 0, 0, 0);
oamSet(&oamMain, 1, ICON2_POS_X, ICON2_POS_Y, 0, 0, SpriteSize_32x32, SpriteColorFormat_16Color, sprite2, -1, 0, 0, 0, 0, 0);
oamSetPalette(&oamMain, 1, 1);
// oam can only be updated during vblank
swiWaitForVBlank();
oamUpdate(&oamMain);
cartBanner = (tNDSBanner*)CartBannerBuffer;
// everything's ready :)
writeRow (1,"=====================", false);
writeRow (1,"==>> Loading ... <<==", false);
writeRow (1,"=====================", false);
}
void iconTitleUpdate (int isdir, const std::string& name) {
writeRow (0, name.c_str(), false);
writeRow (1, "", false);
writeRow (2, "", false);
writeRow (3, "", false);
if (isdir) {
// text
writeRow (2, "[directory]", false);
// icon
clearIcon();
} else {
std::string ndsPath;
if (!argsNdsPath(name, ndsPath)) {
writeRow(2, "(invalid argv or NDS file!)", false);
clearIcon();
return;
}
unsigned int Icon_title_offset;
// open file for reading info
FILE *fp = fopen (ndsPath.c_str(), "rb");
if (!fp) {
// text
writeRow (2,"(can't open file!)", false);
// icon
clearIcon();
fclose (fp);
return;
}
if (fseek (fp, offsetof(tNDSHeader, bannerOffset), SEEK_SET) != 0 ||
fread (&Icon_title_offset, sizeof(int), 1, fp) != 1) {
// text
writeRow (2, "(can't read file!)", false);
// icon
clearIcon();
fclose (fp);
return;
}
if (Icon_title_offset == 0) {
// text
writeRow (2, "(no title/icon)", false);
// icon
clearIcon();
fclose (fp);
return;
}
if (fseek (fp, Icon_title_offset, SEEK_SET) != 0 || fread (&banner, sizeof(banner), 1, fp) != 1) {
// text
writeRow (2,"(can't read icon/title!)", false);
// icon
clearIcon();
fclose (fp);
return;
}
// close file!
fclose (fp);
// turn unicode into ascii (kind of)
// and convert 0x0A into 0x00
char *p = (char*)banner.titles[0];
int rowOffset = 1;
int lineReturns = 0;
for (size_t i = 0; i < sizeof(banner.titles[0]); i = i+2) {
if ((p[i] == 0x0A) || (p[i] == 0xFF)) {
p[i/2] = 0;
lineReturns++;
} else {
p[i/2] = p[i];
}
}
if (lineReturns < 2)rowOffset = 2; // Recenter if bennar has less 2 or less rows of text maintaining empty row gap between nds file name and nds banner.
// text
for (size_t i = 0; i < 3; ++i) {
writeRow(i+rowOffset, p, false);
p += strlen(p) + 1;
}
// icon
DC_FlushAll();
dmaCopy(banner.icon, sprite, sizeof(banner.icon));
dmaCopy(banner.palette, SPRITE_PALETTE, sizeof(banner.palette));
}
}
void cartIconUpdate (u32 BannerOffset, bool readExistingBanner) {
if(readExistingBanner) {
cardReadAlt(*(u32*)InitialCartBannerOffset, (u32*)CartBannerBuffer, 0x1000);
} else {
cardReadAlt(BannerOffset, (u32*)CartBannerBuffer, 0x1000);
}
switch (cartBanner->crc) {
case 0x0000: {
clearCartIcon(false);
writeRow (1,"(invalid icon/title!)", true);
return;
}break;
case 0xFFFF: {
clearCartIcon(false);
writeRow (1,"(invalid icon/title!)", true);
return;
}break;
default: {
writeRow (0, "", true);
writeRow (1, "", true);
writeRow (2, "", true);
// turn unicode into ascii (kind of)
// and convert 0x0A into 0x00
char *p = (char*)cartBanner->titles[0];
int rowOffset = 0;
int lineReturns = 0;
for (size_t i = 0; i < sizeof(cartBanner->titles[0]); i = i+2) {
if ((p[i] == 0x0A) || (p[i] == 0xFF)) {
p[i/2] = 0;
lineReturns++;
} else {
p[i/2] = p[i];
}
}
// Recenter text to center row if less then 2 rows of text.
// Default offset 0 instead of 1 since no NDS file name for this to account for.
if (lineReturns < 2 && lineReturns != 1) {
rowOffset = 1;
} else if (lineReturns == 1) {
rowOffset = 0;
}
// text
for (size_t i = 0; i < 3; ++i) {
if ((i > 0) && (lineReturns == 1))rowOffset = 1;
writeRow(i+rowOffset, p, true);
p += strlen(p) + 1;
}
}break;
}
DC_FlushAll();
dmaCopy(&cartBanner->icon, sprite2, 512);
dmaCopy(&cartBanner->palette, (u16*)((u32)SPRITE_PALETTE + 0x20), 0x20);
}

30
arm9/hbmenu/iconTitle.h Normal file
View File

@ -0,0 +1,30 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2013
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include <string>
extern bool cartSelected;
void clearCartIcon(bool clearBannerText);
void ToggleBackground(void);
void iconTitleInit (void);
void iconTitleUpdate (int isdir, const std::string& name);
void cartIconUpdate (u32 BannerOffset, bool readExistingBanner);

View File

@ -0,0 +1,372 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2010
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include <string.h>
#include <nds.h>
#include <nds/arm9/dldi.h>
#include <sys/stat.h>
#include <limits.h>
#include <unistd.h>
#include <fat.h>
#include "load2_bin.h"
#include "nds_loader_arm9.h"
#include "launcherData.h"
#define LCDC_BANK_D (u16*)0x06860000
#define STORED_FILE_CLUSTER (*(((u32*)LCDC_BANK_D) + 1))
#define INIT_DISC (*(((u32*)LCDC_BANK_D) + 2))
#define WANT_TO_PATCH_DLDI (*(((u32*)LCDC_BANK_D) + 3))
#define STORED_FILE_CLUSTER_OFFSET 4
#define INIT_DISC_OFFSET 8
#define WANT_TO_PATCH_DLDI_OFFSET 12
#define ARG_START_OFFSET 16
#define ARG_SIZE_OFFSET 20
#define HAVE_DSISD_OFFSET 28
#define DSIMODE_OFFSET 32
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; }
static void vramcpy (void* dst, const void* src, int len) {
u16* dst16 = (u16*)dst;
u16* src16 = (u16*)src;
for ( ; len > 0; len -= 2) { *dst16++ = *src16++; }
}
/* static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) {
const int* dataChunk = (const int*) data;
int searchChunk = ((const int*)search)[0];
addr_t i;
addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int));
for ( i = 0; i < dataChunkEnd; i++) {
if (dataChunk[i] == searchChunk) {
if ((i*sizeof(int) + searchLen) > dataLen)return -1;
if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0)return i*sizeof(int);
}
}
return -1;
}*/
// Normal DLDI uses "\xED\xA5\x8D\xBF Chishm"
// Bootloader string is different to avoid being patched
// static const data_t dldiMagicLoaderString[] = "\xEE\xA5\x8D\xBF Chishm"; // Different to a normal DLDI file
/*#define DEVICE_TYPE_DLDI 0x49444C44
static bool dldiPatchLoader (data_t *binData, u32 binSize, bool clearBSS) {
addr_t memOffset; // Offset of DLDI after the file is loaded into memory
addr_t patchOffset; // Position of patch destination in the file
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;
data_t *pAH;
size_t dldiFileSize = 0;
// Find the DLDI reserved space in the file
patchOffset = quickFind (binData, dldiMagicLoaderString, binSize, sizeof(dldiMagicLoaderString));
if (patchOffset < 0)return false; // does not have a DLDI section
pDH = (data_t*)(io_dldi_data);
pAH = &(binData[patchOffset]);
if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI)return false; // No DLDI patch
if (pDH[DO_driverSize] > pAH[DO_allocatedSpace])return false; // Not enough space for patch
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
vramcpy (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 (clearBSS && (pDH[DO_fixSections] & FIX_BSS)) {
// Initialise the BSS to 0, only if the disc is being re-inited
memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start));
}
return true;
}*/
ITCM_CODE static void SetSCFG(u8 scfgUnlock, u8 twlMode, u8 twlCLK, u8 twlVRAM) {
if (twlMode > 0) {
*((vu32*)REG_MBK1)=0x8D898581;
*((vu32*)REG_MBK2)=0x8C888480;
*((vu32*)REG_MBK3)=0x9C989490;
*((vu32*)REG_MBK4)=0x8C888480;
*((vu32*)REG_MBK5)=0x9C989490;
REG_MBK6=0x00000000;
REG_MBK7=0x07C03740;
REG_MBK8=0x07403700;
} else {
// MBK settings for NTR mode games
*((vu32*)REG_MBK1)=0x8D898581;
*((vu32*)REG_MBK2)=0x91898581;
*((vu32*)REG_MBK3)=0x91999591;
*((vu32*)REG_MBK4)=0x91898581;
*((vu32*)REG_MBK5)=0x91999591;
REG_MBK6 = 0x00003000;
REG_MBK7 = 0x00003000;
REG_MBK8 = 0x00003000;
}
if (twlCLK == 0)REG_SCFG_CLK = 0x80;
if (twlMode > 0) {
REG_SCFG_EXT = 0x82073100;
REG_SCFG_RST = 1;
} else {
REG_SCFG_EXT=0x83002000;
}
if (twlVRAM == 0)REG_SCFG_EXT &= ~(1UL << 13);
if (scfgUnlock == 0x00)REG_SCFG_EXT &= ~(1UL << 31);
for (int i = 0; i < 10; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); }
}
eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool dldiPatchNds, int argc, const char** argv, tLauncherSettings launchdata) {
char* argStart;
u16* argData;
u16 argTempVal = 0;
int argSize;
const char* argChar;
bool scfgUnlock = false;
bool twlMode = false;
bool twlCLK = false;
bool twlVRAM = false;
if (launchdata.scfgUnlock > 0x00)scfgUnlock = true;
if (launchdata.twlMode > 0x00)twlMode = true;
if (launchdata.twlCLK > 0x00)twlCLK = true;
if (launchdata.twlVRAM > 0x00)twlVRAM = true;
if (!twlMode) {
fifoSendValue32(FIFO_USER_01, 1);
fifoWaitValue32(FIFO_USER_02);
}
irqDisable(IRQ_ALL);
SetSCFG(scfgUnlock, twlMode, twlCLK, twlVRAM);
// Direct CPU access to VRAM bank C
VRAM_D_CR = VRAM_ENABLE | VRAM_D_LCD;
// Load the loader/patcher into the correct address
vramcpy (LCDC_BANK_D, loader, loaderSize);
// Set the parameters for the loader
// STORED_FILE_CLUSTER = cluster;
writeAddr ((data_t*) LCDC_BANK_D, STORED_FILE_CLUSTER_OFFSET, cluster);
// INIT_DISC = initDisc;
writeAddr ((data_t*) LCDC_BANK_D, INIT_DISC_OFFSET, initDisc);
writeAddr ((data_t*) LCDC_BANK_D, DSIMODE_OFFSET, isDSiMode());
// if(argv[0][0]=='s' && argv[0][1]=='d') {
dldiPatchNds = false;
writeAddr ((data_t*) LCDC_BANK_D, HAVE_DSISD_OFFSET, 1);
// }
// WANT_TO_PATCH_DLDI = dldiPatchNds;
// writeAddr ((data_t*) LCDC_BANK_D, WANT_TO_PATCH_DLDI_OFFSET, dldiPatchNds);
writeAddr ((data_t*) LCDC_BANK_D, WANT_TO_PATCH_DLDI_OFFSET, false);
// Give arguments to loader
argStart = (char*)LCDC_BANK_D + readAddr((data_t*)LCDC_BANK_D, ARG_START_OFFSET);
argStart = (char*)(((int)argStart + 3) & ~3); // Align to word
argData = (u16*)argStart;
argSize = 0;
for (; argc > 0 && *argv; ++argv, --argc) {
for (argChar = *argv; *argChar != 0; ++argChar, ++argSize) {
if (argSize & 1) {
argTempVal |= (*argChar) << 8;
*argData = argTempVal;
++argData;
} else {
argTempVal = *argChar;
}
}
if (argSize & 1) { *argData = argTempVal; ++argData; }
argTempVal = 0;
++argSize;
}
*argData = argTempVal;
writeAddr ((data_t*) LCDC_BANK_D, ARG_START_OFFSET, (addr_t)argStart - (addr_t)LCDC_BANK_D);
writeAddr ((data_t*) LCDC_BANK_D, ARG_SIZE_OFFSET, argSize);
/*if(dldiPatchNds) {
// Patch the loader with a DLDI for the card
if (!dldiPatchLoader ((data_t*)LCDC_BANK_D, loaderSize, initDisc))return RUN_NDS_PATCH_DLDI_FAILED;
}*/
irqDisable(IRQ_ALL);
*(tLauncherSettings*)LAUNCH_DATA = launchdata;
// Give the VRAM to the ARM7
VRAM_D_CR = VRAM_ENABLE | VRAM_D_ARM7_0x06020000;
// Reset into a passme loop
REG_EXMEMCNT |= ARM7_OWNS_ROM | ARM7_OWNS_CARD;
*((vu32*)0x02FFFFFC) = 0;
*((vu32*)0x02FFFE04) = (u32)0xE59FF018;
*((vu32*)0x02FFFE24) = (u32)0x02FFFE04;
resetARM7(0x06020000);
swiSoftReset();
return RUN_NDS_OK;
}
eRunNdsRetCode runNdsFile (const char* filename, int argc, const char** argv, tLauncherSettings launchData) {
struct stat st;
char filePath[PATH_MAX];
int pathLen;
const char* args[1];
if (stat (filename, &st) < 0)return RUN_NDS_STAT_FAILED;
if (argc <= 0 || !argv) {
// Construct a command line if we weren't supplied with one
if (!getcwd (filePath, PATH_MAX))return RUN_NDS_GETCWD_FAILED;
pathLen = strlen (filePath);
strcpy (filePath + pathLen, filename);
args[0] = filePath;
argv = args;
}
// bool havedsiSD = false;
// if(argv[0][0]=='s' && argv[0][1]=='d') havedsiSD = true;
return runNds (load2_bin, load2_bin_size, st.st_ino, true, true, argc, argv, launchData);
}

View File

@ -0,0 +1,51 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 - 2010
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#ifndef NDS_LOADER_ARM9_H
#define NDS_LOADER_ARM9_H
#include "launcherData.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
RUN_NDS_OK = 0,
RUN_NDS_STAT_FAILED,
RUN_NDS_GETCWD_FAILED,
RUN_NDS_PATCH_DLDI_FAILED,
} eRunNdsRetCode;
#define LOAD_DEFAULT_NDS 0
eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool dldiPatchNds, int argc, const char** argv, tLauncherSettings launchdata);
eRunNdsRetCode runNdsFile (const char* filename, int argc, const char** argv, tLauncherSettings launchData);
// bool installBootStub(bool havedsiSD);
#ifdef __cplusplus
}
#endif
#endif // NDS_LOADER_ARM7_H

BIN
arm9/music/back.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
arm9/music/launch.wav Normal file

Binary file not shown.

BIN
arm9/music/select.wav Normal file

Binary file not shown.

BIN
arm9/music/wrong.wav Normal file

Binary file not shown.

View File

@ -1,40 +0,0 @@
/*
NitroHax -- Cheat tool for the Nintendo DS
Copyright (C) 2008 Michael "Chishm" Chisholm
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bios_decompress_callback.h"
static int getSizeBiosCallback (uint8 * source, uint16 * dest, uint32 r2)
{
(void)dest;
(void)r2;
return *((int*)source);
}
static uint8 readByteBiosCallback (uint8 * source)
{
return *source;
}
TDecompressionStream decompressBiosCallback =
{
getSizeBiosCallback,
(void*)0,
readByteBiosCallback
} ;

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@ void CartridgePromptNoHS (void);
void BootSplashDS (bool HealthandSafety_MSG, int language);
void FadeFX (void);
void ErrorNoCard (void);
void SimpleSplashInit(void);
void BootSplashInit (bool UseNTRSplash, bool HealthandSafety_MSG, int language, bool DisplayErrorSplash);
#ifdef __cplusplus

View File

@ -22,19 +22,10 @@
#include "load_bin.h"
#include "launch_engine.h"
#include "debugConsole.h"
#include "launcherData.h"
#define LCDC_BANK_D (u16*)0x06860000
#define TMP_DATA 0x027FC000
typedef struct sLauncherSettings {
u8 language;
u8 scfgUnlock;
u8 twlMode;
u8 twlCLK;
u8 twlVRAM;
u8 debugMode;
} tLauncherSettings;
void vramcpy (void* dst, const void* src, int len) {
u16* dst16 = (u16*)dst;
@ -47,17 +38,23 @@ extern void InitConsole();
extern bool ConsoleInit;
void runLaunchEngine (int language, bool scfgUnlock, bool TWLMODE, bool TWLCLK, bool TWLVRAM, bool debugMode) {
ITCM_CODE static void SETSCFG() {
REG_SCFG_EXT=0x83002000;
for(int i = 0; i < 8; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); }
}
void runLaunchEngine (tLauncherSettings launchdata) {
// Always init console so bootloader's new console can display error codes if needed.
if (!debugMode || !ConsoleInit) { InitConsole(); } else { consoleClear(); }
if (!launchdata.debugMode || !ConsoleInit) { InitConsole(); } else { consoleClear(); }
irqDisable(IRQ_ALL);
// Direct CPU access to VRAM bank D
VRAM_D_CR = VRAM_ENABLE | VRAM_D_LCD;
// Clear VRAM
memset (LCDC_BANK_D, 0x00, 128 * 1024);
// memset (LCDC_BANK_D, 0x00, 128 * 1024);
// Load the loader/patcher into the correct address
vramcpy (LCDC_BANK_D, load_bin, load_bin_size);
@ -69,21 +66,11 @@ void runLaunchEngine (int language, bool scfgUnlock, bool TWLMODE, bool TWLCLK,
// Reset into a passme loop
nocashMessage("Reset into a passme loop");
REG_EXMEMCNT |= ARM7_OWNS_ROM | ARM7_OWNS_CARD;
REG_SCFG_EXT=0x83002000;
// for (int i = 0; i < 10; i++) { swiWaitForVBlank(); }
*(tLauncherSettings*)LAUNCH_DATA = launchdata;
tLauncherSettings* tmpData = (tLauncherSettings*)TMP_DATA;
SETSCFG();
tmpData->language = 0xFF;
if (language != -1)tmpData->language = language;
if (scfgUnlock)tmpData->scfgUnlock = 0x01;
if (TWLMODE)tmpData->twlMode = 0x01;
if (TWLCLK)tmpData->twlCLK = 0x01;
if (TWLVRAM)tmpData->twlVRAM = 0x01;
if (debugMode)tmpData->debugMode = 0x01;
// Return to passme loop
*(vu32*)0x027FFFFC = 0;
*(vu32*)0x027FFE04 = (u32)0xE59FF018; // ldr pc, 0x02FFFE24
@ -91,7 +78,6 @@ void runLaunchEngine (int language, bool scfgUnlock, bool TWLMODE, bool TWLCLK,
// Reset ARM7
// nocashMessage("resetARM7");
resetARM7(0x06020000);
// swi soft reset
// nocashMessage("swiSoftReset");
swiSoftReset();

View File

@ -20,12 +20,13 @@
#define LAUNCH_ENGINE_H
#include <nds/ndstypes.h>
#include "launcherData.h"
#ifdef __cplusplus
extern "C" {
#endif
void runLaunchEngine (int language, bool scfgUnlock, bool TWLMODE, bool TWLCLK, bool TWLVRAM, bool debugMode);
void runLaunchEngine (tLauncherSettings launchdata);
#ifdef __cplusplus
}

View File

@ -30,14 +30,27 @@
#include "launch_engine.h"
#include "crc.h"
#include "tonccpy.h"
#include "read_card.h"
#include "nitrofs.h"
#include "nds_card.h"
#include "debugConsole.h"
#include "hbmenu.h"
#include "launcherData.h"
#include "read_card.h"
sNDSHeaderExt ndsHeader;
char gameTitle[13] = {0};
char gameCode[7] = {0};
#define FILECOPYBUFFER 0x02000000
const char* PROGVERSION = "2.7";
ALIGN(4) struct {
sNDSHeader header;
char padding[0x200 - sizeof(sNDSHeader)];
} ndsHeader;
static char gameTitle[13] = {0};
// static char gameCode[7] = {0};
static char gameCode[5] = {0};
static bool nitroFSMounted = false;
static ALIGN(4) sNDSHeaderExt ntrHeader;
const char* PROGVERSION = "3.0";
off_t getFileSize(const char *fileName) {
FILE* fp = fopen(fileName, "rb");
@ -52,6 +65,7 @@ off_t getFileSize(const char *fileName) {
}
extern void InitConsole();
extern void StartFileBrowser(tLauncherSettings launchdata);
extern bool ConsoleInit;
@ -60,130 +74,230 @@ void DisplayText(const char* text, bool clear = false, bool noText = false){
if (clear)consoleClear();
printf("--------------------------------\n");
printf("----[NTR Launcher Debug Mode]---\n");
printf("----------[Version: 2.7]--------\n");
printf("----------[Version: 3.0]--------\n");
printf("--------------------------------\n\n");
if (!noText)printf(text);
}
void DoWait(int waitTime = 30){
for (int i = 0; i < waitTime; i++) swiWaitForVBlank();
for (int i = 0; i < waitTime; i++)swiWaitForVBlank();
};
void DoCardInit(bool DebugMode) {
bool MountNitroFS() {
if (nitroFSMounted)return nitroFSMounted;
nitroFSMounted = nitroFSInit("sd:/title/00030004/4b4b4750/content/00000000.app");
if (nitroFSMounted)return nitroFSMounted;
nitroFSMounted = nitroFSInit("sd:/NTR_Launcher.nds");
if (nitroFSMounted)return nitroFSMounted;
nitroFSMounted = nitroFSInit("sd:/nds/NTR_Launcher.nds");
if (nitroFSMounted)return nitroFSMounted;
nitroFSMounted = nitroFSInit("sd:/homebrew/NTR_Launcher.nds");
return nitroFSMounted;
}
void StartStage2Mode(tLauncherSettings Launchdata) {
sysSetCardOwner (BUS_OWNER_ARM9);
StartFileBrowser(Launchdata);
}
bool DoCardInit(bool DebugMode, bool fastBoot) {
bool FastBoot = fastBoot;
if (DebugMode){
DisplayText("CLR", true, true);
DisplayText("Loading Cart details.\nPlease Wait...\n", true);
}
switch (REG_SCFG_MC) {
case 0x10: { enableSlot1(); DoWait(10); FastBoot = false; }break;
case 0x11: { enableSlot1(); DoWait(10); FastBoot = false; }break;
}
// Do cart init stuff to wake cart up. DLDI init may fail otherwise!
cardInit(&ndsHeader);
tonccpy(gameTitle, ndsHeader.gameTitle, 12);
tonccpy(gameCode, ndsHeader.gameCode, 6);
DoWait(60);
if (FastBoot) {
cardInitShort(&ntrHeader);
tonccpy(gameTitle, ntrHeader.gameTitle, 12);
tonccpy(gameCode, ntrHeader.gameCode, 4);
} else {
CardReset(true);
cardReadHeader((u8*)&ndsHeader);
CardReset(false);
tonccpy(gameTitle, ndsHeader.header.gameTitle, 12);
tonccpy(gameCode, ndsHeader.header.gameCode, 4);
}
if (DebugMode) {
DisplayText("CLR", true, true);
iprintf("Detected Cart Name: %12s \n", gameTitle);
iprintf("Detected Cart Game ID: %6s \n\n", gameCode);
printf("Press any button to continue...");
do { swiWaitForVBlank(); scanKeys(); } while (!keysDown());
while (keysDown()) { swiWaitForVBlank(); scanKeys(); }
while (!keysDown()) { swiWaitForVBlank(); scanKeys(); }
} else {
DoWait(25);
}
return FastBoot;
}
/*void ResetSlot1() {
if (REG_SCFG_MC == 0x11)return;
disableSlot1();
DoWait();
enableSlot1();
}*/
void DoSlotCheck(bool DebugMode) {
if (REG_SCFG_MC == 0x11) {
if(DebugMode) {
DisplayText("Please insert a cartridge...\n", true);
} else {
if (!ConsoleInit)InitConsole();
consoleClear();
printf("\n\n\n\n\n\n\n\n\n\n\n Please insert a cartridge... ");
}
REDO:
swiWaitForVBlank();
do { swiWaitForVBlank(); } while (REG_SCFG_MC != 0x10);
enableSlot1();
DoWait(60);
if (REG_SCFG_MC != 0x18) goto REDO;
} else if (REG_SCFG_MC == 0x10) {
enableSlot1();
bool DoSlotCheck(bool DebugMode) {
bool slotReset = false;
switch (REG_SCFG_MC) {
case 0x11: {
if(DebugMode) {
DisplayText("Please insert a cartridge...\n", true);
} else {
if (!ConsoleInit)InitConsole();
consoleClear();
printf("\n\n\n\n\n\n\n\n\n\n\n Please insert a cartridge... ");
}
REDO:
swiWaitForVBlank();
while (REG_SCFG_MC != 0x10)swiWaitForVBlank();
enableSlot1();
DoWait(60);
if (REG_SCFG_MC != 0x18) goto REDO;
slotReset = true;
}break;
case 0x10: {
slotReset = true;
enableSlot1();
}break;
}
return slotReset;
}
int main() {
defaultExceptionHandler();
bool scfgUnlock = false;
bool TWLMODE = false;
bool TWLCLK = false; // false == NTR, true == TWL
bool TWLVRAM = false;
bool UseAnimatedSplash = false;
bool UseNTRSplash = true;
bool HealthAndSafety_MSG = true;
sysSetCardOwner(BUS_OWNER_ARM9);
tLauncherSettings LaunchData = { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF };
// Create backup of Cart header (if present) saved by DSi System Menu.
// It will not survive memory reallocation after dropping to NTR ram spec during bootloader.
tonccpy ((u32*)CartHeaderCopy, (u32*)InitialCartHeader, 0x200);
int language = -1;
bool scfgunlock = false;
bool twlmode = false;
bool twlclk = false;
bool twlvram = false;
bool debugmode = false;
bool fastBoot = false;
bool stage2Menu = false;
bool useAnimatedSplash = false;
bool useNTRSplash = true;
bool healthAndSafety_MSG = true;
if (fatMountSimple("sd", get_io_dsisd())) {
if(access("sd:/NTR_Launcher", F_OK) != 0)mkdir("sd:/NTR_Launcher", 0777);
if ((access("sd:/NTR_Launcher/NTR_Launcher.ini", F_OK) != 0) && MountNitroFS()) {
FILE *src = fopen("nitro:/NTR_Launcher.ini", "rb");
if (src) {
FILE *dest = fopen("sd:/NTR_Launcher/NTR_Launcher.ini", "wb");
if (dest) {
fseek(src, 0, SEEK_END);
u32 fSize = ftell(src);
fseek(src, 0, SEEK_SET);
fread((void*)FILECOPYBUFFER, 1, fSize, src);
fwrite((void*)FILECOPYBUFFER, fSize, 1, dest);
fclose(src);
fclose(dest);
}
}
}
if (access("sd:/NTR_Launcher/NTR_Launcher.ini", F_OK) == 0) {
CIniFile ntrlauncher_config( "sd:/NTR_Launcher/NTR_Launcher.ini" );
twlclk = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLCLOCK",0);
twlvram = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLVRAM",0);
twlmode = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLMODE",0);
scfgunlock = ntrlauncher_config.GetInt("NTRLAUNCHER","SCFGUNLOCK",0);
useAnimatedSplash = ntrlauncher_config.GetInt("NTRLAUNCHER","ANIMATEDSPLASH",0);
useNTRSplash = ntrlauncher_config.GetInt("NTRLAUNCHER","NTRSPLASH",0);
healthAndSafety_MSG = ntrlauncher_config.GetInt("NTRLAUNCHER","HEALTHSAFETYSPLASH",0);
debugmode = ntrlauncher_config.GetInt("NTRLAUNCHER","DEBUGMODE",0);
language = ntrlauncher_config.GetInt("NTRLAUNCHER", "LANGUAGE", -1);
}
}
scanKeys();
swiWaitForVBlank();
bool DebugMode = false;
if (scfgunlock)LaunchData.scfgUnlock = 0x01;
if (twlmode)LaunchData.twlMode = 0x01;
if (twlclk)LaunchData.twlCLK = 0x01;
if (twlvram)LaunchData.twlVRAM = 0x01;
if (fastBoot)LaunchData.fastBoot = 0x01;
if (fatInitDefault()) {
CIniFile ntrlauncher_config( "/NDS/NTR_Launcher.ini" );
TWLCLK = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLCLOCK",0);
TWLVRAM = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLVRAM",0);
TWLMODE = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLMODE",0);
scfgUnlock = ntrlauncher_config.GetInt("NTRLAUNCHER","SCFGUNLOCK",0);
UseAnimatedSplash = ntrlauncher_config.GetInt("NTRLAUNCHER","ANIMATEDSPLASH",0);
UseNTRSplash = ntrlauncher_config.GetInt("NTRLAUNCHER","NTRSPLASH",0);
HealthAndSafety_MSG = ntrlauncher_config.GetInt("NTRLAUNCHER","HEALTHSAFETYSPLASH",0);
DebugMode = ntrlauncher_config.GetInt("NTRLAUNCHER","DEBUGMODE",0);
language = ntrlauncher_config.GetInt("NTRLAUNCHER", "LANGUAGE", -1);
if (((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) || (REG_SCFG_MC == 0x11))stage2Menu = true;
if ((REG_SCFG_MC == 0x10) && !stage2Menu) { sysSetCardOwner (BUS_OWNER_ARM9); DoCardInit(false, false); }
if (keysDown() & KEY_B) {
debugmode = true;
} else if (keysDown() & KEY_L) {
twlclk = false; twlvram = true; scfgunlock = false; twlmode = false;
} else if (keysDown() & KEY_R) {
twlclk = true; twlvram = true; scfgunlock = true; twlmode = true;
}
ConsoleInit = DebugMode;
if (DebugMode)UseAnimatedSplash = false;
if (!UseAnimatedSplash) {
if (DebugMode)InitConsole();
DoSlotCheck(DebugMode);
ConsoleInit = debugmode;
if (stage2Menu) { ConsoleInit = false; debugmode = false; }
if (debugmode)useAnimatedSplash = false;
if (!useAnimatedSplash) {
if (debugmode)InitConsole();
// fastBoot = !DoSlotCheck(debugmode);
if (!stage2Menu)DoSlotCheck(debugmode);
} else {
char *p = (char*)PersonalData->name;
for (int i = 0; i < 10; i++) {
if (p[i*2] == 0x00) { p[i*2/2] = 0; } else { p[i*2/2] = p[i*2]; }
}
if (language == -1) language = (PersonalData->language);
BootSplashInit(UseNTRSplash, HealthAndSafety_MSG, language, false);
if (language == -1)language = PersonalData->language;
BootSplashInit(useNTRSplash, healthAndSafety_MSG, (int)language, false);
}
sysSetCardOwner (BUS_OWNER_ARM9);
if (language != -1)LaunchData.language = (u8)language;
if (scfgunlock)LaunchData.scfgUnlock = 0x01;
if (twlmode)LaunchData.twlMode = 0x01;
if (twlclk)LaunchData.twlCLK = 0x01;
if (twlvram)LaunchData.twlVRAM = 0x01;
if (debugmode)LaunchData.debugMode = 0x01;
/*if (fastBoot) {
LaunchData.fastBoot = 0x01;
} else {
LaunchData.fastBoot = 0x00;
}*/
DoCardInit(DebugMode);
// Force specific settings needed for proper support for retail TWL carts
if (!memcmp(gameTitle, "D!S!XTREME", 9)) {
// DS-Xtreme does not like running in TWL clock speeds. (write function likely goes too fast and semi-bricks hidden sector region randomly)
TWLCLK = false;
}
while(1) {
// If SCFG_MC is returning as zero/null, this means SCFG_EXT registers are locked on arm9 or user attempted to run this while in NTR mode.
if((REG_SCFG_MC == 0x00) || (REG_SCFG_MC == 0x11) || (REG_SCFG_MC == 0x10)) {
if (UseAnimatedSplash) {
BootSplashInit(false, false, 0, true);
if (stage2Menu || (REG_SCFG_MC == 0x11)) {
StartStage2Mode(LaunchData);
} else {
// DoCardInit(debugmode, fastBoot);
DoCardInit(debugmode, false);
// DS-Xtreme does not like running in TWL clock speeds.
// (write function likely goes too fast and semi-bricks hidden sector region randomly when using official launcher)
if (!memcmp(gameTitle, "D!S!XTREME", 9)) {
scfgunlock = false;
twlclk = false;
twlvram = false;
twlmode = false;
}
while(1) {
// If SCFG_MC is returning as zero/null, this means SCFG_EXT registers are locked on arm9 or user attempted to run this while in NTR mode.
if((REG_SCFG_MC == 0x00) || (REG_SCFG_MC == 0x11) || (REG_SCFG_MC == 0x10)) {
if (useAnimatedSplash) {
BootSplashInit(false, false, 0, true);
} else {
DisplayText("Error has occured.!\nEither card ejected late,\nor NTR mode detected!\nPress any button to exit...", true);
do { swiWaitForVBlank(); scanKeys(); } while (!keysDown());
}
break;
} else {
DisplayText("Error has occured.!\nEither card ejected late,\nor NTR mode detected!\nPress any button to exit...", true);
do { swiWaitForVBlank(); scanKeys(); } while (!keysDown());
runLaunchEngine(LaunchData);
break;
}
break;
} else {
runLaunchEngine(language, scfgUnlock, TWLMODE, TWLCLK, TWLVRAM, DebugMode);
swiWaitForVBlank();
}
}
return 0;

BIN
icon.bmp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 630 B

3
ndsbootloader/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*build
load.bin
load.elf

124
ndsbootloader/Makefile Normal file
View File

@ -0,0 +1,124 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
endif
-include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := load2
BUILD ?= build
SOURCES := source source/patches
INCLUDES := build ../include
DATA := ../data
SPECS := specs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -Os \
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) $(EXTRA_CFLAGS) -DARM7
ASFLAGS := -g $(ARCH) $(EXTRA_CFLAGS) $(INCLUDE)
LDFLAGS = -nostartfiles -T $(TOPDIR)/load2.ld -Wl,-g $(ARCH) -Wl,-Map,$(TARGET).map
LIBS :=
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export TOPDIR := $(CURDIR)
export LOADBIN := $(CURDIR)/../data/$(TARGET).bin
export LOADELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
export CC := $(PREFIX)gcc
export CXX := $(PREFIX)g++
export AR := $(PREFIX)ar
export OBJCOPY := $(PREFIX)objcopy
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#---------------------------------------------------------------------------------
# use CC for linking standard C
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf *.bin
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(LOADBIN) : $(LOADELF)
@$(OBJCOPY) -O binary $< $@
@echo built ... $(notdir $@)
$(LOADELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
arm9mpu_reset.o: mpu_reset.bin
mpu_reset.bin: mpu_reset.elf
$(OBJCOPY) -O binary $< $@
mpu_reset.elf: $(TOPDIR)/arm9code/mpu_reset.s
$(CC) $(ASFLAGS) -Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $< -o $@
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,110 @@
/*
Copyright 2006 - 2015 Dave Murphy (WinterMute)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <nds/arm9/cache_asm.h>
.text
.align 4
.arm
.arch armv5te
.cpu arm946e-s
@---------------------------------------------------------------------------------
.global _start
.type _start STT_FUNC
@---------------------------------------------------------------------------------
_start:
@---------------------------------------------------------------------------------
@ Switch off MPU
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #PROTECT_ENABLE
mcr p15, 0, r0, c1, c0, 0
adr r12, mpu_initial_data
ldmia r12, {r0-r10}
mcr p15, 0, r0, c2, c0, 0
mcr p15, 0, r0, c2, c0, 1
mcr p15, 0, r1, c3, c0, 0
mcr p15, 0, r2, c5, c0, 2
mcr p15, 0, r3, c5, c0, 3
mcr p15, 0, r4, c6, c0, 0
mcr p15, 0, r5, c6, c1, 0
mcr p15, 0, r6, c6, c3, 0
mcr p15, 0, r7, c6, c4, 0
mcr p15, 0, r8, c6, c6, 0
mcr p15, 0, r9, c6, c7, 0
mcr p15, 0, r10, c9, c1, 0
mov r0, #0
mcr p15, 0, r0, c6, c2, 0 @ PU Protection Unit Data/Unified Region 2
mcr p15, 0, r0, c6, c5, 0 @ PU Protection Unit Data/Unified Region 5
mrc p15, 0, r0, c9, c1, 0 @ DTCM
mov r0, r0, lsr #12 @ base
mov r0, r0, lsl #12 @ size
add r0, r0, #0x4000 @ dtcm top
sub r0, r0, #4 @ irq vector
mov r1, #0
str r1, [r0]
sub r0, r0, #4 @ IRQ1 Check Bits
str r1, [r0]
sub r0, r0, #128
bic r0, r0, #7
msr cpsr_c, #0xd3 @ svc mode
mov sp, r0
sub r0, r0, #128
msr cpsr_c, #0xd2 @ irq mode
mov sp, r0
sub r0, r0, #128
msr cpsr_c, #0xdf @ system mode
mov sp, r0
@ enable cache & tcm
mrc p15, 0, r0, c1, c0, 0
ldr r1,= ITCM_ENABLE | DTCM_ENABLE | ICACHE_ENABLE | DCACHE_ENABLE
orr r0,r0,r1
mcr p15, 0, r0, c1, c0, 0
ldr r10, =0x2FFFE04
ldr r0, =0xE59FF018
str r0, [r10]
add r1, r10, #0x20
str r10, [r1]
bx r10
.pool
mpu_initial_data:
.word 0x00000042 @ p15,0,c2,c0,0..1,r0 ;PU Cachability Bits for Data/Unified+Instruction Protection Region
.word 0x00000002 @ p15,0,c3,c0,0,r1 ;PU Write-Bufferability Bits for Data Protection Regions
.word 0x15111011 @ p15,0,c5,c0,2,r2 ;PU Extended Access Permission Data/Unified Protection Region
.word 0x05100011 @ p15,0,c5,c0,3,r3 ;PU Extended Access Permission Instruction Protection Region
.word 0x04000033 @ p15,0,c6,c0,0,r4 ;PU Protection Unit Data/Unified Region 0
.word 0x0200002b @ p15,0,c6,c1,0,r5 ;PU Protection Unit Data/Unified Region 1 4MB
.word 0x08000035 @ p15,0,c6,c3,0,r6 ;PU Protection Unit Data/Unified Region 3
.word 0x0300001b @ p15,0,c6,c4,0,r7 ;PU Protection Unit Data/Unified Region 4
.word 0xffff001d @ p15,0,c6,c6,0,r8 ;PU Protection Unit Data/Unified Region 6
.word 0x02fff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB
.word 0x0300000a @ p15,0,c9,c1,0,r10 ;TCM Data TCM Base and Virtual Size

198
ndsbootloader/load2.ld Normal file
View File

@ -0,0 +1,198 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
MEMORY {
vram : ORIGIN = 0x06020000, LENGTH = 64K
}
__vram_start = ORIGIN(vram);
__vram_top = ORIGIN(vram)+ LENGTH(vram);
__sp_irq = __vram_top - 0x60;
__sp_svc = __sp_irq - 0x100;
__sp_usr = __sp_svc - 0x100;
__irq_flags = __vram_top - 8;
__irq_vector = __vram_top - 4;
SECTIONS
{
.init :
{
__text_start = . ;
KEEP (*(.init))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.plt :
{
*(.plt)
} >vram = 0xff
.text : /* ALIGN (4): */
{
*(.text*)
*(.stub)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t*)
*(.glue_7)
*(.glue_7t)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.fini :
{
KEEP (*(.fini))
} >vram =0xff
__text_end = . ;
.rodata :
{
*(.rodata)
*all.rodata*(*)
*(.roda)
*(.rodata.*)
*(.gnu.linkonce.r*)
SORT(CONSTRUCTORS)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >vram
__exidx_start = .;
.ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >vram
__exidx_end = .;
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(32 / 8);
PROVIDE (__preinit_array_start = .);
.preinit_array : { KEEP (*(.preinit_array)) } >vram = 0xff
PROVIDE (__preinit_array_end = .);
PROVIDE (__init_array_start = .);
.init_array : { KEEP (*(.init_array)) } >vram = 0xff
PROVIDE (__init_array_end = .);
PROVIDE (__fini_array_start = .);
.fini_array : { KEEP (*(.fini_array)) } >vram = 0xff
PROVIDE (__fini_array_end = .);
.ctors :
{
/* gcc uses crtbegin.o to find the start of the constructors, so
we make sure it is first. Because this is a wildcard, it
doesn't matter if the user does not actually link against
crtbegin.o; the linker won't look for a file to match a
wildcard. The wildcard also means that it doesn't matter which
directory crtbegin.o is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.eh_frame :
{
KEEP (*(.eh_frame))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.gcc_except_table :
{
*(.gcc_except_table)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram = 0xff
.jcr : { KEEP (*(.jcr)) } >vram = 0
.got : { *(.got.plt) *(.got) } >vram = 0
.vram ALIGN(4) :
{
__vram_start = ABSOLUTE(.) ;
*(.vram)
*vram.*(.text)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
__vram_end = ABSOLUTE(.) ;
} >vram = 0xff
.data ALIGN(4) : {
__data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
CONSTRUCTORS
. = ALIGN(4);
__data_end = ABSOLUTE(.) ;
} >vram = 0xff
.bss ALIGN(4) :
{
__bss_start = ABSOLUTE(.);
__bss_start__ = ABSOLUTE(.);
*(.dynbss)
*(.gnu.linkonce.b*)
*(.bss*)
*(COMMON)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >vram
__bss_end = . ;
__bss_end__ = . ;
_end = . ;
__end__ = . ;
PROVIDE (end = _end);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.stack 0x80000 : { _stack = .; *(.stack) }
/* These must appear regardless of . */
}

View File

@ -0,0 +1,70 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
.arm
.global arm7clearRAM
.type arm7clearRAM STT_FUNC
arm7clearRAM:
push {r0-r9}
// clear exclusive IWRAM
// 0380:0000 to 0380:FFFF, total 64KiB
mov r0, #0
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0x03800000
sub r8, #0x00008000
mov r9, #0x03800000
orr r9, r9, #0x10000
clear_IWRAM_loop:
stmia r8!, {r0, r1, r2, r3, r4, r5, r6, r7}
cmp r8, r9
blt clear_IWRAM_loop
// clear most of EWRAM - except after RAM end - 0xc000, which has the bootstub
mov r8, #0x02000000
ldr r9,=0x4004008
ldr r9,[r9]
ands r9,r9,#0x8000
bne dsi_mode
mov r9, #0x02400000
b ds_mode
dsi_mode:
mov r9, #0x03000000
ds_mode:
sub r9, #0x0000c000
clear_EWRAM_loop:
stmia r8!, {r0, r1, r2, r3, r4, r5, r6, r7}
cmp r8, r9
blt clear_EWRAM_loop
pop {r0-r9}
bx lr

View File

@ -0,0 +1,68 @@
#define ARM9
#undef ARM7
#include <nds/ndstypes.h>
#include <nds/dma.h>
#include <nds/system.h>
#include <nds/interrupts.h>
#include <nds/timers.h>
#include <nds/memory.h>
#include <nds/arm9/video.h>
#include <nds/arm9/input.h>
#include "boot.h"
/*-------------------------------------------------------------------------
resetMemory2_ARM9
Clears the ARM9's DMA channels and resets video memory
Written by Darkain.
Modified by Chishm:
* Changed MultiNDS specific stuff
--------------------------------------------------------------------------*/
void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9 (void) {
register int i;
//clear out ARM9 DMA channels
for (i=0; i<4; i++) {
DMA_CR(i) = 0;
DMA_SRC(i) = 0;
DMA_DEST(i) = 0;
TIMER_CR(i) = 0;
TIMER_DATA(i) = 0;
}
//set shared ram to ARM7
WRAM_CR = 0x03;
// Return to passme loop
*((vu32*)0x02FFFE04) = (u32)0xE59FF018; // ldr pc, 0x02FFFE24
*((vu32*)0x02FFFE24) = (u32)0x02FFFE04; // Set ARM9 Loop address
asm volatile(
"\tbx %0\n"
: : "r" (0x02FFFE04)
);
while(1);
}
/*-------------------------------------------------------------------------
startBinary_ARM9
Jumps to the ARM9 NDS binary in sync with the display and ARM7
Written by Darkain.
Modified by Chishm:
* Removed MultiNDS specific stuff
--------------------------------------------------------------------------*/
void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (void) {
REG_IME=0;
REG_EXMEMCNT = 0xE880;
// set ARM9 load address to 0 and wait for it to change again
ARM9_START_FLAG = 0;
while(REG_VCOUNT!=191);
while(REG_VCOUNT==191);
while ( ARM9_START_FLAG != 1 );
VoidFn arm9code = *(VoidFn*)(0x2FFFE24);
arm9code();
while(1);
}

View File

@ -0,0 +1,6 @@
.arm
.global mpu_reset, mpu_reset_end
mpu_reset:
.incbin "mpu_reset.bin"
mpu_reset_end:

View File

@ -0,0 +1,13 @@
.text
.align 4
.thumb
@---------------------------------------------------------------------------------
.global swiDelay
.thumb_func
@---------------------------------------------------------------------------------
swiDelay:
@---------------------------------------------------------------------------------
swi 0x03
bx lr

413
ndsbootloader/source/boot.c Normal file
View File

@ -0,0 +1,413 @@
/*-----------------------------------------------------------------
boot.c
BootLoader
Loads a file into memory and runs it
All resetMemory and startBinary functions are based
on the MultiNDS loader by Darkain.
Original source available at:
http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp
License:
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
Helpful information:
This code runs from VRAM bank C on ARM7
------------------------------------------------------------------*/
#include <nds/ndstypes.h>
#include <nds/dma.h>
#include <nds/system.h>
#include <nds/interrupts.h>
#include <nds/timers.h>
#include <nds/memory.h>
#include <nds/arm7/audio.h>
#include "fat.h"
#include "dldi_patcher.h"
#include "card.h"
#include "boot.h"
#include "sdmmc.h"
void arm7clearRAM();
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Important things
#define TEMP_MEM 0x02FFD000
#define TWL_HEAD 0x02FFE000
#define NDS_HEAD 0x02FFFE00
#define TEMP_ARM9_START_ADDRESS (*(vu32*)0x02FFFFF4)
const char* bootName = "BOOT.NDS";
extern unsigned long _start;
extern unsigned long storedFileCluster;
extern unsigned long initDisc;
extern unsigned long wantToPatchDLDI;
extern unsigned long argStart;
extern unsigned long argSize;
extern unsigned long dsiSD;
extern unsigned long dsiMode;
static bool scfgunlock = false;
static bool twlmode = false;
static bool twlclk = false;
static bool twlvram = false;
#define LAUNCH_DATA 0x020007F0
typedef struct sLauncherSettings {
u8 language;
u8 scfgUnlock;
u8 twlMode;
u8 twlCLK;
u8 twlVRAM;
u8 debugMode;
u8 fastBoot;
u8 unused2;
} tLauncherSettings;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Firmware stuff
#define FW_READ 0x03
void boot_readFirmware (uint32 address, uint8 * buffer, uint32 size) {
uint32 index;
// Read command
while (REG_SPICNT & SPI_BUSY);
REG_SPICNT = SPI_ENABLE | SPI_CONTINUOUS | SPI_DEVICE_NVRAM;
REG_SPIDATA = FW_READ;
while (REG_SPICNT & SPI_BUSY);
// Set the address
REG_SPIDATA = (address>>16) & 0xFF;
while (REG_SPICNT & SPI_BUSY);
REG_SPIDATA = (address>>8) & 0xFF;
while (REG_SPICNT & SPI_BUSY);
REG_SPIDATA = (address) & 0xFF;
while (REG_SPICNT & SPI_BUSY);
for (index = 0; index < size; index++) {
REG_SPIDATA = 0;
while (REG_SPICNT & SPI_BUSY);
buffer[index] = REG_SPIDATA & 0xFF;
}
REG_SPICNT = 0;
}
static inline void copyLoop (u32* dest, const u32* src, u32 size) {
size = (size +3) & ~3;
do {
*dest++ = *src++;
} while (size -= 4);
}
//#define resetCpu() __asm volatile("\tswi 0x000000\n");
static char boot_nds[] = "fat:/boot.nds";
static unsigned long argbuf[4];
/*-------------------------------------------------------------------------
passArgs_ARM7
Copies the command line arguments to the end of the ARM9 binary,
then sets a flag in memory for the loaded NDS to use
--------------------------------------------------------------------------*/
void passArgs_ARM7 (void) {
u32 ARM9_DST = *((u32*)(NDS_HEAD + 0x028));
u32 ARM9_LEN = *((u32*)(NDS_HEAD + 0x02C));
u32* argSrc;
u32* argDst;
if (!argStart || !argSize) {
char *arg = boot_nds;
argSize = __builtin_strlen(boot_nds);
if (dsiSD) {
arg++;
arg[0] = 's';
arg[1] = 'd';
}
__builtin_memcpy(argbuf,arg,argSize+1);
argSrc = argbuf;
} else {
argSrc = (u32*)(argStart + (int)&_start);
}
if ( ARM9_DST == 0 && ARM9_LEN == 0) {
ARM9_DST = *((u32*)(NDS_HEAD + 0x038));
ARM9_LEN = *((u32*)(NDS_HEAD + 0x03C));
}
argDst = (u32*)((ARM9_DST + ARM9_LEN + 3) & ~3); // Word aligned
if (dsiMode && (*(u8*)(NDS_HEAD + 0x012) & BIT(1)))
{
u32 ARM9i_DST = *((u32*)(TWL_HEAD + 0x1C8));
u32 ARM9i_LEN = *((u32*)(TWL_HEAD + 0x1CC));
if (ARM9i_LEN)
{
u32* argDst2 = (u32*)((ARM9i_DST + ARM9i_LEN + 3) & ~3); // Word aligned
if (argDst2 > argDst)
argDst = argDst2;
}
}
copyLoop(argDst, argSrc, argSize);
__system_argv->argvMagic = ARGV_MAGIC;
__system_argv->commandLine = (char*)argDst;
__system_argv->length = argSize;
}
/*-------------------------------------------------------------------------
resetMemory_ARM7
Clears all of the NDS's RAM that is visible to the ARM7
Written by Darkain.
Modified by Chishm:
* Added STMIA clear mem loop
--------------------------------------------------------------------------*/
void resetMemory_ARM7 (void) {
int i;
u8 settings1, settings2;
u32 settingsOffset = 0;
REG_IME = 0;
for (i=0; i<16; i++) {
SCHANNEL_CR(i) = 0;
SCHANNEL_TIMER(i) = 0;
SCHANNEL_SOURCE(i) = 0;
SCHANNEL_LENGTH(i) = 0;
}
REG_SOUNDCNT = 0;
//clear out ARM7 DMA channels and timers
for (i=0; i<4; i++) {
DMA_CR(i) = 0;
DMA_SRC(i) = 0;
DMA_DEST(i) = 0;
TIMER_CR(i) = 0;
TIMER_DATA(i) = 0;
}
arm7clearRAM();
REG_IE = 0;
REG_IF = ~0;
(*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version
(*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version
REG_POWERCNT = 1; //turn off power to stuff
// Get settings location
boot_readFirmware((u32)0x00020, (u8*)&settingsOffset, 0x2);
settingsOffset *= 8;
// Reload DS Firmware settings
boot_readFirmware(settingsOffset + 0x070, &settings1, 0x1);
boot_readFirmware(settingsOffset + 0x170, &settings2, 0x1);
if ((settings1 & 0x7F) == ((settings2+1) & 0x7F)) {
boot_readFirmware(settingsOffset + 0x000, (u8*)0x02FFFC80, 0x70);
} else {
boot_readFirmware(settingsOffset + 0x100, (u8*)0x02FFFC80, 0x70);
}
((vu32*)0x040044f0)[2] = 0x202DDD1D;
((vu32*)0x040044f0)[3] = 0xE1A00005;
while((*(vu32*)0x04004400) & 0x2000000);
}
void loadBinary_ARM7 (u32 fileCluster) {
u32 ndsHeader[0x170>>2];
// read NDS header
fileRead ((char*)ndsHeader, fileCluster, 0, 0x170);
// read ARM9 info from NDS header
u32 ARM9_SRC = ndsHeader[0x020>>2];
char* ARM9_DST = (char*)ndsHeader[0x028>>2];
u32 ARM9_LEN = ndsHeader[0x02C>>2];
// read ARM7 info from NDS header
u32 ARM7_SRC = ndsHeader[0x030>>2];
char* ARM7_DST = (char*)ndsHeader[0x038>>2];
u32 ARM7_LEN = ndsHeader[0x03C>>2];
// Load binaries into memory
fileRead(ARM9_DST, fileCluster, ARM9_SRC, ARM9_LEN);
fileRead(ARM7_DST, fileCluster, ARM7_SRC, ARM7_LEN);
// first copy the header to its proper location, excluding
// the ARM9 start address, so as not to start it
TEMP_ARM9_START_ADDRESS = ndsHeader[0x024>>2]; // Store for later
ndsHeader[0x024>>2] = 0;
dmaCopyWords(3, (void*)ndsHeader, (void*)NDS_HEAD, 0x170);
if (dsiMode && (ndsHeader[0x10>>2]&BIT(16+1))) {
// Read full TWL header
fileRead((char*)TWL_HEAD, fileCluster, 0, 0x1000);
u32 ARM9i_SRC = *(u32*)(TWL_HEAD+0x1C0);
char* ARM9i_DST = (char*)*(u32*)(TWL_HEAD+0x1C8);
u32 ARM9i_LEN = *(u32*)(TWL_HEAD+0x1CC);
u32 ARM7i_SRC = *(u32*)(TWL_HEAD+0x1D0);
char* ARM7i_DST = (char*)*(u32*)(TWL_HEAD+0x1D8);
u32 ARM7i_LEN = *(u32*)(TWL_HEAD+0x1DC);
if (ARM9i_LEN)fileRead(ARM9i_DST, fileCluster, ARM9i_SRC, ARM9i_LEN);
if (ARM7i_LEN)fileRead(ARM7i_DST, fileCluster, ARM7i_SRC, ARM7i_LEN);
}
}
/*-------------------------------------------------------------------------
startBinary_ARM7
Jumps to the ARM7 NDS binary in sync with the display and ARM9
Written by Darkain.
Modified by Chishm:
* Removed MultiNDS specific stuff
--------------------------------------------------------------------------*/
void startBinary_ARM7 (void) {
REG_IME=0;
while(REG_VCOUNT!=191);
while(REG_VCOUNT==191);
// copy NDS ARM9 start address into the header, starting ARM9
*((vu32*)0x02FFFE24) = TEMP_ARM9_START_ADDRESS;
ARM9_START_FLAG = 1;
// Start ARM7
VoidFn arm7code = *(VoidFn*)(0x2FFFE34);
arm7code();
}
#ifndef NO_SDMMC
int sdmmc_sd_readsectors(u32 sector_no, u32 numsectors, void *out);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Main function
bool sdmmc_inserted() { return true; }
bool sdmmc_startup() {
sdmmc_controller_init(true);
return sdmmc_sdcard_init() == 0;
}
bool sdmmc_readsectors(u32 sector_no, u32 numsectors, void *out) { return sdmmc_sdcard_readsectors(sector_no, numsectors, out) == 0; }
#endif
void mpu_reset();
void mpu_reset_end();
int main (void) {
tLauncherSettings* tmpData = (tLauncherSettings*)LAUNCH_DATA;
if (tmpData->scfgUnlock > 0x00)scfgunlock = true;
if (tmpData->twlMode > 0x00)twlmode = true;
if (tmpData->twlCLK > 0x00)twlclk = true;
if (tmpData->twlVRAM > 0x00)twlvram = true;
if (twlmode) {
REG_MBK9=0x0300000F;
REG_MBK6=0x080037C0;
REG_MBK7=0x07C03740;
REG_MBK8=0x07403700;
} else {
REG_MBK9=0xFCFFFF0F;
REG_MBK6=0x09403900;
REG_MBK7=0x09803940;
REG_MBK8=0x09C03980;
}
#ifdef NO_DLDI
dsiSD = true;
dsiMode = true;
#endif
#ifndef NO_SDMMC
if (dsiSD && dsiMode) {
_io_dldi.fn_readSectors = sdmmc_readsectors;
_io_dldi.fn_isInserted = sdmmc_inserted;
_io_dldi.fn_startup = sdmmc_startup;
}
#endif
u32 fileCluster = storedFileCluster;
// Init card
if(!FAT_InitFiles(initDisc))return -1;
/* Invalid file cluster specified */
if ((fileCluster < CLUSTER_FIRST) || (fileCluster >= CLUSTER_EOF))fileCluster = getBootFileCluster(bootName);
if (fileCluster == CLUSTER_FREE)return -1;
// ARM9 clears its memory part 2
// copy ARM9 function to RAM, and make the ARM9 jump to it
copyLoop((void*)TEMP_MEM, (void*)resetMemory2_ARM9, resetMemory2_ARM9_size);
(*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function
// Wait until the ARM9 has completed its task
while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM);
// ARM9 sets up mpu
// copy ARM9 function to RAM, and make the ARM9 jump to it
copyLoop((void*)TEMP_MEM, (void*)mpu_reset, mpu_reset_end - mpu_reset);
(*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function
// Wait until the ARM9 has completed its task
while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM);
// Get ARM7 to clear RAM
resetMemory_ARM7();
// ARM9 enters a wait loop
// copy ARM9 function to RAM, and make the ARM9 jump to it
copyLoop((void*)TEMP_MEM, (void*)startBinary_ARM9, startBinary_ARM9_size);
(*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function
// Load the NDS file
loadBinary_ARM7(fileCluster);
#ifndef NO_DLDI
// Patch with DLDI if desired
if (wantToPatchDLDI)dldiPatchBinary ((u8*)((u32*)NDS_HEAD)[0x0A], ((u32*)NDS_HEAD)[0x0B]);
#endif
#ifndef NO_SDMMC
if (dsiSD && dsiMode) {
sdmmc_controller_init(true);
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0;
}
#endif
// Pass command line arguments to loaded program
passArgs_ARM7();
if (twlclk) { REG_SCFG_CLK = 0x0187; } else { REG_SCFG_CLK = 0x0101; }
if (twlmode) {
REG_SCFG_EXT = 0x92FBFB06;
} else {
REG_SCFG_EXT = 0x92A00000;
REG_SCFG_ROM = 0x703;
}
if (scfgunlock) { REG_SCFG_EXT |= BIT(18); } else { REG_SCFG_EXT &= ~(1UL << 31); }
startBinary_ARM7();
return 0;
}

View File

@ -0,0 +1,11 @@
#ifndef _BOOT_H_
#define _BOOT_H_
#define resetMemory2_ARM9_size 0x400
void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9();
#define startBinary_ARM9_size 0x100
void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9();
#define ARM9_START_FLAG (*(vu8*)0x02FFFDFB)
#endif // _BOOT_H_

View File

@ -0,0 +1,45 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#ifndef CARD_H
#define CARD_H
#include "disc_io.h"
#include "io_dldi.h"
static inline bool CARD_StartUp (void) {
return _io_dldi.fn_startup();
}
static inline bool CARD_IsInserted (void) {
return _io_dldi.fn_isInserted();
}
static inline bool CARD_ReadSector (u32 sector, void *buffer) {
return _io_dldi.fn_readSectors(sector, 1, buffer);
}
static inline bool CARD_ReadSectors (u32 sector, int count, void *buffer) {
return _io_dldi.fn_readSectors(sector, count, buffer);
}
#endif // CARD_H

View File

@ -0,0 +1,82 @@
/*
disc_io.h
Interface template for low level disc functions.
Copyright (c) 2006 Michael "Chishm" Chisholm
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2006-07-11 - Chishm
* Original release
2006-07-16 - Chishm
* Renamed _CF_USE_DMA to _IO_USE_DMA
* Renamed _CF_ALLOW_UNALIGNED to _IO_ALLOW_UNALIGNED
*/
#ifndef _DISC_IO_H
#define _DISC_IO_H
#include <nds/ndstypes.h>
#define BYTES_PER_SECTOR 512
//----------------------------------------------------------------------
// Customisable features
// Use DMA to read the card, remove this line to use normal reads/writes
// #define _IO_USE_DMA
// Allow buffers not alligned to 16 bits when reading files.
// Note that this will slow down access speed, so only use if you have to.
// It is also incompatible with DMA
#define _IO_ALLOW_UNALIGNED
#if defined _IO_USE_DMA && defined _IO_ALLOW_UNALIGNED
#error "You can't use both DMA and unaligned memory"
#endif
#define FEATURE_MEDIUM_CANREAD 0x00000001
#define FEATURE_MEDIUM_CANWRITE 0x00000002
#define FEATURE_SLOT_GBA 0x00000010
#define FEATURE_SLOT_NDS 0x00000020
typedef bool (* FN_MEDIUM_STARTUP)(void) ;
typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u32 numSectors, void* buffer) ;
typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u32 numSectors, const void* buffer) ;
typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
struct IO_INTERFACE_STRUCT {
unsigned long ioType ;
unsigned long features ;
FN_MEDIUM_STARTUP fn_startup ;
FN_MEDIUM_ISINSERTED fn_isInserted ;
FN_MEDIUM_READSECTORS fn_readSectors ;
FN_MEDIUM_WRITESECTORS fn_writeSectors ;
FN_MEDIUM_CLEARSTATUS fn_clearStatus ;
FN_MEDIUM_SHUTDOWN fn_shutdown ;
} ;
typedef struct IO_INTERFACE_STRUCT IO_INTERFACE ;
#endif // define _DISC_IO_H

View File

@ -0,0 +1,223 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#ifndef NO_DLDI
#include <string.h>
#include <nds.h>
#include "dldi_patcher.h"
#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;
}
static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) {
const int* dataChunk = (const int*) data;
int searchChunk = ((const int*)search)[0];
addr_t i;
addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int));
for ( i = 0; i < dataChunkEnd; i++) {
if (dataChunk[i] == searchChunk) {
if ((i*sizeof(int) + searchLen) > dataLen) {
return -1;
}
if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0) {
return i*sizeof(int);
}
}
}
return -1;
}
// Strings are stored with bit 0x20 flipped (case inverted) to prevent accidental DLDI patching of them
#define DLDI_MAGIC_LEN 12
#define DLDI_MAGIC_MANGLE_VALUE 0x20
static const data_t dldiMagicStringMangled[DLDI_MAGIC_LEN] = "\xCD\x85\xAD\x9F\0cHISHM"; // Normal DLDI file
// Demangle the magic string by XORing every byte with 0x20, except the NULL terminator
static void demangleMagicString(data_t *dest, const data_t *src) {
int i;
memcpy(dest, src, DLDI_MAGIC_LEN);
for (i = 0; i < DLDI_MAGIC_LEN - 1; ++i) {
dest[i] ^= DLDI_MAGIC_MANGLE_VALUE;
}
}
#define DEVICE_TYPE_DLDI 0x49444C44
extern data_t _dldi_start[];
bool dldiPatchBinary (data_t *binData, u32 binSize) {
addr_t memOffset; // Offset of DLDI after the file is loaded into memory
addr_t patchOffset; // Position of patch destination in the file
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;
data_t *pAH;
data_t dldiMagicString[DLDI_MAGIC_LEN];
size_t dldiFileSize = 0;
// Find the DLDI reserved space in the file
demangleMagicString(dldiMagicString, dldiMagicStringMangled);
patchOffset = quickFind (binData, dldiMagicString, binSize, sizeof(dldiMagicString));
if (patchOffset < 0) {
// does not have a DLDI section
return false;
}
pDH = _dldi_start;
pAH = &(binData[patchOffset]);
if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI) {
// No DLDI patch
return false;
}
if (pDH[DO_driverSize] > pAH[DO_allocatedSpace]) {
// Not enough space for patch
return false;
}
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
memcpy (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);
// Put the correct DLDI magic string back into the DLDI header
memcpy (pAH, dldiMagicString, sizeof (dldiMagicString));
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
memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start));
}
return true;
}
#endif

View File

@ -0,0 +1,32 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#ifndef DLDI_PATCHER_H
#define DLDI_PATCHER_H
#include <nds/ndstypes.h>
typedef signed int addr_t;
typedef unsigned char data_t;
bool dldiPatchBinary (data_t *binData, u32 binSize);
#endif // DLDI_PATCHER_H

592
ndsbootloader/source/fat.c Normal file
View File

@ -0,0 +1,592 @@
/*-----------------------------------------------------------------
fat.c
NDS MP
GBAMP NDS Firmware Hack Version 2.12
An NDS aware firmware patch for the GBA Movie Player.
By Michael Chisholm (Chishm)
Filesystem code based on GBAMP_CF.c by Chishm (me).
License:
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#include "fat.h"
#include "card.h"
//---------------------------------------------------------------
// FAT constants
#define FILE_LAST 0x00
#define FILE_FREE 0xE5
#define ATTRIB_ARCH 0x20
#define ATTRIB_DIR 0x10
#define ATTRIB_LFN 0x0F
#define ATTRIB_VOL 0x08
#define ATTRIB_HID 0x02
#define ATTRIB_SYS 0x04
#define ATTRIB_RO 0x01
#define FAT16_ROOT_DIR_CLUSTER 0x00
// File Constants
#ifndef EOF
#define EOF -1
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
//-----------------------------------------------------------------
// FAT constants
#define CLUSTER_EOF_16 0xFFFF
#define ATTRIB_ARCH 0x20
#define ATTRIB_DIR 0x10
#define ATTRIB_LFN 0x0F
#define ATTRIB_VOL 0x08
#define ATTRIB_HID 0x02
#define ATTRIB_SYS 0x04
#define ATTRIB_RO 0x01
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Data Structures
#define __PACKED __attribute__ ((__packed__))
// BIOS Parameter Block
typedef struct {
u16 bytesPerSector;
u8 sectorsPerCluster;
u16 reservedSectors;
u8 numFATs;
u16 rootEntries;
u16 numSectorsSmall;
u8 mediaDesc;
u16 sectorsPerFAT;
u16 sectorsPerTrk;
u16 numHeads;
u32 numHiddenSectors;
u32 numSectors;
} __PACKED BIOS_BPB;
// Boot Sector - must be packed
typedef struct
{
u8 jmpBoot[3];
u8 OEMName[8];
BIOS_BPB bpb;
union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
{
struct
{
// Ext BIOS Parameter Block for FAT16
u8 driveNumber;
u8 reserved1;
u8 extBootSig;
u32 volumeID;
u8 volumeLabel[11];
u8 fileSysType[8];
// Bootcode
u8 bootCode[448];
} __PACKED fat16;
struct
{
// FAT32 extended block
u32 sectorsPerFAT32;
u16 extFlags;
u16 fsVer;
u32 rootClus;
u16 fsInfo;
u16 bkBootSec;
u8 reserved[12];
// Ext BIOS Parameter Block for FAT16
u8 driveNumber;
u8 reserved1;
u8 extBootSig;
u32 volumeID;
u8 volumeLabel[11];
u8 fileSysType[8];
// Bootcode
u8 bootCode[420];
} __PACKED fat32;
} __PACKED extBlock;
__PACKED u16 bootSig;
} __PACKED BOOT_SEC;
// _Static_assert(sizeof(BOOT_SEC) == 512);
// Directory entry - must be packed
typedef struct
{
u8 name[8];
u8 ext[3];
u8 attrib;
u8 reserved;
u8 cTime_ms;
u16 cTime;
u16 cDate;
u16 aDate;
u16 startClusterHigh;
u16 mTime;
u16 mDate;
u16 startCluster;
u32 fileSize;
} __PACKED DIR_ENT;
// File information - no need to pack
typedef struct
{
u32 firstCluster;
u32 length;
u32 curPos;
u32 curClus; // Current cluster to read from
int curSect; // Current sector within cluster
int curByte; // Current byte within sector
char readBuffer[512]; // Buffer used for unaligned reads
u32 appClus; // Cluster to append to
int appSect; // Sector within cluster for appending
int appByte; // Byte within sector for appending
bool read; // Can read from file
bool write; // Can write to file
bool append;// Can append to file
bool inUse; // This file is open
u32 dirEntSector; // The sector where the directory entry is stored
int dirEntOffset; // The offset within the directory sector
} FAT_FILE;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Global Variables
// _VARS_IN_RAM variables are stored in the largest section of WRAM
// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
// Locations on card
int discRootDir;
int discRootDirClus;
int discFAT;
int discSecPerFAT;
int discNumSec;
int discData;
int discBytePerSec;
int discSecPerClus;
int discBytePerClus;
enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} discFileSystem;
// Global sector buffer to save on stack space
unsigned char globalBuffer[BYTES_PER_SECTOR];
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//FAT routines
u32 FAT_ClustToSect (u32 cluster) {
return (((cluster-2) * discSecPerClus) + discData);
}
/*-----------------------------------------------------------------
FAT_NextCluster
Internal function - gets the cluster linked from input cluster
-----------------------------------------------------------------*/
u32 FAT_NextCluster(u32 cluster)
{
u32 nextCluster = CLUSTER_FREE;
u32 sector;
int offset;
switch (discFileSystem)
{
case FS_UNKNOWN:
nextCluster = CLUSTER_FREE;
break;
case FS_FAT12:
sector = discFAT + (((cluster * 3) / 2) / BYTES_PER_SECTOR);
offset = ((cluster * 3) / 2) % BYTES_PER_SECTOR;
CARD_ReadSector(sector, globalBuffer);
nextCluster = ((u8*) globalBuffer)[offset];
offset++;
if (offset >= BYTES_PER_SECTOR) {
offset = 0;
sector++;
}
CARD_ReadSector(sector, globalBuffer);
nextCluster |= (((u8*) globalBuffer)[offset]) << 8;
if (cluster & 0x01) {
nextCluster = nextCluster >> 4;
} else {
nextCluster &= 0x0FFF;
}
break;
case FS_FAT16:
sector = discFAT + ((cluster << 1) / BYTES_PER_SECTOR);
offset = cluster % (BYTES_PER_SECTOR >> 1);
CARD_ReadSector(sector, globalBuffer);
// read the nextCluster value
nextCluster = ((u16*)globalBuffer)[offset];
if (nextCluster >= 0xFFF7)
{
nextCluster = CLUSTER_EOF;
}
break;
case FS_FAT32:
sector = discFAT + ((cluster << 2) / BYTES_PER_SECTOR);
offset = cluster % (BYTES_PER_SECTOR >> 2);
CARD_ReadSector(sector, globalBuffer);
// read the nextCluster value
nextCluster = (((u32*)globalBuffer)[offset]) & 0x0FFFFFFF;
if (nextCluster >= 0x0FFFFFF7)
{
nextCluster = CLUSTER_EOF;
}
break;
default:
nextCluster = CLUSTER_FREE;
break;
}
return nextCluster;
}
/*-----------------------------------------------------------------
ucase
Returns the uppercase version of the given char
char IN: a character
char return OUT: uppercase version of character
-----------------------------------------------------------------*/
char ucase (char character)
{
if ((character > 0x60) && (character < 0x7B))
character = character - 0x20;
return (character);
}
/*-----------------------------------------------------------------
FAT_InitFiles
Reads the FAT information from the CF card.
You need to call this before reading any files.
bool return OUT: true if successful.
-----------------------------------------------------------------*/
bool FAT_InitFiles (bool initCard)
{
int i;
int bootSector;
BOOT_SEC* bootSec;
if (initCard && !CARD_StartUp())
{
return (false);
}
// Read first sector of card
if (!CARD_ReadSector (0, globalBuffer))
{
return false;
}
// Check if there is a FAT string, which indicates this is a boot sector
if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
{
bootSector = 0;
}
// Check for FAT32
else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
{
bootSector = 0;
}
else // This is an MBR
{
// Find first valid partition from MBR
// First check for an active partition
for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
// If it didn't find an active partition, search for any valid partition
if (i == 0x1FE)
for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
// Go to first valid partition
if ( i != 0x1FE) // Make sure it found a partition
{
bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
} else {
bootSector = 0; // No partition found, assume this is a MBR free disk
}
}
// Read in boot sector
bootSec = (BOOT_SEC*) globalBuffer;
CARD_ReadSector (bootSector, bootSec);
// Store required information about the file system
if (bootSec->bpb.sectorsPerFAT != 0)
{
discSecPerFAT = bootSec->bpb.sectorsPerFAT;
}
else
{
discSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
}
if (bootSec->bpb.numSectorsSmall != 0)
{
discNumSec = bootSec->bpb.numSectorsSmall;
}
else
{
discNumSec = bootSec->bpb.numSectors;
}
discBytePerSec = BYTES_PER_SECTOR; // Sector size is redefined to be 512 bytes
discSecPerClus = bootSec->bpb.sectorsPerCluster * bootSec->bpb.bytesPerSector / BYTES_PER_SECTOR;
discBytePerClus = discBytePerSec * discSecPerClus;
discFAT = bootSector + bootSec->bpb.reservedSectors;
discRootDir = discFAT + (bootSec->bpb.numFATs * discSecPerFAT);
discData = discRootDir + ((bootSec->bpb.rootEntries * sizeof(DIR_ENT)) / BYTES_PER_SECTOR);
if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 4085)
{
discFileSystem = FS_FAT12;
}
else if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 65525)
{
discFileSystem = FS_FAT16;
}
else
{
discFileSystem = FS_FAT32;
}
if (discFileSystem != FS_FAT32)
{
discRootDirClus = FAT16_ROOT_DIR_CLUSTER;
}
else // Set up for the FAT32 way
{
discRootDirClus = bootSec->extBlock.fat32.rootClus;
// Check if FAT mirroring is enabled
if (!(bootSec->extBlock.fat32.extFlags & 0x80))
{
// Use the active FAT
discFAT = discFAT + ( discSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
}
}
return (true);
}
/*-----------------------------------------------------------------
getBootFileCluster
-----------------------------------------------------------------*/
u32 getBootFileCluster (const char* bootName)
{
DIR_ENT dir;
int firstSector = 0;
bool notFound = false;
bool found = false;
// int maxSectors;
u32 wrkDirCluster = discRootDirClus;
u32 wrkDirSector = 0;
int wrkDirOffset = 0;
int nameOffset;
dir.startCluster = CLUSTER_FREE; // default to no file found
dir.startClusterHigh = CLUSTER_FREE;
// Check if fat has been initialised
if (discBytePerSec == 0)
{
return (CLUSTER_FREE);
}
char *ptr = (char*)bootName;
while (*ptr != '.') ptr++;
int namelen = ptr - bootName;
// maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (discData - discRootDir) : discSecPerClus);
// Scan Dir for correct entry
firstSector = discRootDir;
CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
found = false;
notFound = false;
wrkDirOffset = -1; // Start at entry zero, Compensating for increment
while (!found && !notFound) {
wrkDirOffset++;
if (wrkDirOffset == BYTES_PER_SECTOR / sizeof (DIR_ENT))
{
wrkDirOffset = 0;
wrkDirSector++;
if ((wrkDirSector == discSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
{
wrkDirSector = 0;
wrkDirCluster = FAT_NextCluster(wrkDirCluster);
if (wrkDirCluster == CLUSTER_EOF)
{
notFound = true;
}
firstSector = FAT_ClustToSect(wrkDirCluster);
}
else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (discData - discRootDir)))
{
notFound = true; // Got to end of root dir
}
CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
}
dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
found = true;
if ((dir.attrib & ATTRIB_DIR) || (dir.attrib & ATTRIB_VOL))
{
found = false;
}
if(namelen<8 && dir.name[namelen]!=0x20) found = false;
for (nameOffset = 0; nameOffset < namelen && found; nameOffset++)
{
if (ucase(dir.name[nameOffset]) != bootName[nameOffset])
found = false;
}
for (nameOffset = 0; nameOffset < 3 && found; nameOffset++)
{
if (ucase(dir.ext[nameOffset]) != bootName[nameOffset+namelen+1])
found = false;
}
if (dir.name[0] == FILE_LAST)
{
notFound = true;
}
}
// If no file is found, return CLUSTER_FREE
if (notFound)
{
return CLUSTER_FREE;
}
return (dir.startCluster | (dir.startClusterHigh << 16));
}
/*-----------------------------------------------------------------
fileRead(buffer, cluster, startOffset, length)
-----------------------------------------------------------------*/
u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length)
{
int curByte;
int curSect;
int dataPos = 0;
int chunks;
int beginBytes;
if (cluster == CLUSTER_FREE || cluster == CLUSTER_EOF)
{
return 0;
}
// Follow cluster list until desired one is found
for (chunks = startOffset / discBytePerClus; chunks > 0; chunks--)
{
cluster = FAT_NextCluster (cluster);
}
// Calculate the sector and byte of the current position,
// and store them
curSect = (startOffset % discBytePerClus) / BYTES_PER_SECTOR;
curByte = startOffset % BYTES_PER_SECTOR;
// Load sector buffer for new position in file
CARD_ReadSector( curSect + FAT_ClustToSect(cluster), globalBuffer);
curSect++;
// Number of bytes needed to read to align with a sector
beginBytes = (BYTES_PER_SECTOR < length + curByte ? (BYTES_PER_SECTOR - curByte) : length);
// Read first part from buffer, to align with sector boundary
for (dataPos = 0 ; dataPos < beginBytes; dataPos++)
{
buffer[dataPos] = globalBuffer[curByte++];
}
// Read in all the 512 byte chunks of the file directly, saving time
for ( chunks = ((int)length - beginBytes) / BYTES_PER_SECTOR; chunks > 0;)
{
int sectorsToRead;
// Move to the next cluster if necessary
if (curSect >= discSecPerClus)
{
curSect = 0;
cluster = FAT_NextCluster (cluster);
}
// Calculate how many sectors to read (read a maximum of discSecPerClus at a time)
sectorsToRead = discSecPerClus - curSect;
if(chunks < sectorsToRead)
sectorsToRead = chunks;
// Read the sectors
CARD_ReadSectors(curSect + FAT_ClustToSect(cluster), sectorsToRead, buffer + dataPos);
chunks -= sectorsToRead;
curSect += sectorsToRead;
dataPos += BYTES_PER_SECTOR * sectorsToRead;
}
// Take care of any bytes left over before end of read
if (dataPos < length)
{
// Update the read buffer
curByte = 0;
if (curSect >= discSecPerClus)
{
curSect = 0;
cluster = FAT_NextCluster (cluster);
}
CARD_ReadSector( curSect + FAT_ClustToSect( cluster), globalBuffer);
// Read in last partial chunk
for (; dataPos < length; dataPos++)
{
buffer[dataPos] = globalBuffer[curByte];
curByte++;
}
}
return dataPos;
}

View File

@ -0,0 +1,46 @@
/*-----------------------------------------------------------------
fat.h
NDS MP
GBAMP NDS Firmware Hack Version 2.12
An NDS aware firmware patch for the GBA Movie Player.
By Michael Chisholm (Chishm)
Filesystem code based on GBAMP_CF.c by Chishm (me).
License:
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#ifndef FAT_H
#define FAT_H
#include <nds/ndstypes.h>
#define CLUSTER_FREE 0x00000000
#define CLUSTER_EOF 0x0FFFFFFF
#define CLUSTER_FIRST 0x00000002
bool FAT_InitFiles (bool initCard);
u32 getBootFileCluster (const char* bootName);
u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length);
u32 FAT_ClustToSect (u32 cluster);
#endif // FAT_H

View File

@ -0,0 +1,44 @@
/*
io_dldi.h
Reserved space for post-compilation adding of an extra driver
Copyright (c) 2006 Michael "Chishm" Chisholm
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2006-12-22 - Chishm
* Original release
*/
#ifndef IO_DLDI_H
#define IO_DLDI_H
// 'DLDD'
#define DEVICE_TYPE_DLDD 0x49444C44
#include "disc_io.h"
// export interface
extern IO_INTERFACE _io_dldi ;
#endif // define IO_DLDI_H

View File

@ -0,0 +1,124 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
@---------------------------------------------------------------------------------
.align 4
.arm
.global _dldi_start
.global _io_dldi
@---------------------------------------------------------------------------------
.equ FEATURE_MEDIUM_CANREAD, 0x00000001
.equ FEATURE_MEDIUM_CANWRITE, 0x00000002
.equ FEATURE_SLOT_GBA, 0x00000010
.equ FEATURE_SLOT_NDS, 0x00000020
_dldi_start:
#ifndef NO_DLDI
@---------------------------------------------------------------------------------
@ Driver patch file standard header -- 32 bytes
#ifdef STANDARD_DLDI
.word 0xBF8DA5ED @ Magic number to identify this region
#else
.word 0xBF8DA5EE @ Magic number to identify this region
#endif
.asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator)
.byte 0x01 @ Version number
.byte 0x1a @ 32KiB @ Log [base-2] of the size of this driver in bytes.
.byte 0x00 @ Sections to fix
.byte 0x1a @ 32KiB @ Log [base-2] of the allocated space in bytes.
@---------------------------------------------------------------------------------
@ Text identifier - can be anything up to 47 chars + terminating null -- 32 bytes
.align 4
.asciz "Loader (No interface)"
@---------------------------------------------------------------------------------
@ Offsets to important sections within the data -- 32 bytes
.align 6
.word _dldi_start @ data start
.word _dldi_end @ data end
.word 0x00000000 @ Interworking glue start -- Needs address fixing
.word 0x00000000 @ Interworking glue end
.word 0x00000000 @ GOT start -- Needs address fixing
.word 0x00000000 @ GOT end
.word 0x00000000 @ bss start -- Needs setting to zero
.word 0x00000000 @ bss end
@---------------------------------------------------------------------------------
@ IO_INTERFACE data -- 32 bytes
_io_dldi:
.ascii "DLDI" @ ioType
.word 0x00000000 @ Features
.word _DLDI_startup @
.word _DLDI_isInserted @
.word _DLDI_readSectors @ Function pointers to standard device driver functions
.word _DLDI_writeSectors @
.word _DLDI_clearStatus @
.word _DLDI_shutdown @
@---------------------------------------------------------------------------------
_DLDI_startup:
_DLDI_isInserted:
_DLDI_readSectors:
_DLDI_writeSectors:
_DLDI_clearStatus:
_DLDI_shutdown:
mov r0, #0x00 @ Return false for every function
bx lr
@---------------------------------------------------------------------------------
.align
.pool
.space (_dldi_start + 32768) - . @ Fill to 32KiB
_dldi_end:
.end
@---------------------------------------------------------------------------------
#else
@---------------------------------------------------------------------------------
@ IO_INTERFACE data -- 32 bytes
_io_dldi:
.ascii "DLDI" @ ioType
.word 0x00000000 @ Features
.word _DLDI_startup @
.word _DLDI_isInserted @
.word _DLDI_readSectors @ Function pointers to standard device driver functions
.word _DLDI_writeSectors @
.word _DLDI_clearStatus @
.word _DLDI_shutdown @
_DLDI_startup:
_DLDI_isInserted:
_DLDI_readSectors:
_DLDI_writeSectors:
_DLDI_clearStatus:
_DLDI_shutdown:
mov r0, #0x00 @ Return false for every function
bx lr
#endif

View File

@ -0,0 +1,147 @@
/*-----------------------------------------------------------------
Copyright (C) 2005 Michael "Chishm" Chisholm
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
@---------------------------------------------------------------------------------
.section ".init"
.global _start
.global storedFileCluster
.global initDisc
.global wantToPatchDLDI
.global argStart
.global argSize
.global dsiSD
.global dsiMode
@---------------------------------------------------------------------------------
.align 4
.arm
@---------------------------------------------------------------------------------
_start:
@---------------------------------------------------------------------------------
b startUp
storedFileCluster:
.word 0x0FFFFFFF @ default BOOT.NDS
initDisc:
.word 0x00000001 @ init the disc by default
wantToPatchDLDI:
.word 0x00000001 @ by default patch the DLDI section of the loaded NDS
@ Used for passing arguments to the loaded app
argStart:
.word _end - _start
argSize:
.word 0x00000000
dldiOffset:
.word _dldi_start - _start
dsiSD:
.word 0
dsiMode:
.word 0
startUp:
mov r0, #0x04000000
mov r1, #0
str r1, [r0,#0x208] @ REG_IME
str r1, [r0,#0x210] @ REG_IE
str r1, [r0,#0x218] @ REG_AUXIE
mov r0, #0x12 @ Switch to IRQ Mode
msr cpsr, r0
ldr sp, =__sp_irq @ Set IRQ stack
mov r0, #0x13 @ Switch to SVC Mode
msr cpsr, r0
ldr sp, =__sp_svc @ Set SVC stack
mov r0, #0x1F @ Switch to System Mode
msr cpsr, r0
ldr sp, =__sp_usr @ Set user stack
ldr r0, =__bss_start @ Clear BSS section to 0x00
ldr r1, =__bss_end
sub r1, r1, r0
bl ClearMem
mov r0, #0 @ int argc
mov r1, #0 @ char *argv[]
ldr r3, =main
bl _blx_r3_stub @ jump to user code
@ If the user ever returns, restart
b _start
@---------------------------------------------------------------------------------
_blx_r3_stub:
@---------------------------------------------------------------------------------
bx r3
@---------------------------------------------------------------------------------
@ Clear memory to 0x00 if length != 0
@ r0 = Start Address
@ r1 = Length
@---------------------------------------------------------------------------------
ClearMem:
@---------------------------------------------------------------------------------
mov r2, #3 @ Round down to nearest word boundary
add r1, r1, r2 @ Shouldn't be needed
bics r1, r1, r2 @ Clear 2 LSB (and set Z)
bxeq lr @ Quit if copy size is 0
mov r2, #0
ClrLoop:
stmia r0!, {r2}
subs r1, r1, #4
bne ClrLoop
bx lr
@---------------------------------------------------------------------------------
@ Copy memory if length != 0
@ r1 = Source Address
@ r2 = Dest Address
@ r4 = Dest Address + Length
@---------------------------------------------------------------------------------
CopyMemCheck:
@---------------------------------------------------------------------------------
sub r3, r4, r2 @ Is there any data to copy?
@---------------------------------------------------------------------------------
@ Copy memory
@ r1 = Source Address
@ r2 = Dest Address
@ r3 = Length
@---------------------------------------------------------------------------------
CopyMem:
@---------------------------------------------------------------------------------
mov r0, #3 @ These commands are used in cases where
add r3, r3, r0 @ the length is not a multiple of 4,
bics r3, r3, r0 @ even though it should be.
bxeq lr @ Length is zero, so exit
CIDLoop:
ldmia r1!, {r0}
stmia r2!, {r0}
subs r3, r3, #4
bne CIDLoop
bx lr
@---------------------------------------------------------------------------------
.align
.pool
.end
@---------------------------------------------------------------------------------

View File

@ -0,0 +1,242 @@
#ifndef NO_SDMMC
#include <nds/bios.h>
#include <stddef.h>
#include "sdmmc.h"
static struct mmcdevice deviceSD;
//---------------------------------------------------------------------------------
int geterror(struct mmcdevice *ctx) {
//---------------------------------------------------------------------------------
//if(ctx->error == 0x4) return -1;
//else return 0;
return (ctx->error << 29) >> 31;
}
//---------------------------------------------------------------------------------
void setTarget(struct mmcdevice *ctx) {
//---------------------------------------------------------------------------------
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
setckl(ctx->clk);
if (ctx->SDOPT == 0) {
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
} else {
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
}
}
//---------------------------------------------------------------------------------
void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args) {
//---------------------------------------------------------------------------------
int i;
bool getSDRESP = (cmd << 15) >> 31;
uint16_t flags = (cmd << 15) >> 31;
const bool readdata = cmd & 0x20000;
const bool writedata = cmd & 0x40000;
if(readdata || writedata)
{
flags |= TMIO_STAT0_DATAEND;
}
ctx->error = 0;
while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
sdmmc_write16(REG_SDIRMASK0,0);
sdmmc_write16(REG_SDIRMASK1,0);
sdmmc_write16(REG_SDSTATUS0,0);
sdmmc_write16(REG_SDSTATUS1,0);
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
sdmmc_write16(REG_SDCMDARG1,args >> 16);
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
uint32_t size = ctx->size;
uint16_t *dataPtr = (uint16_t*)ctx->data;
uint32_t *dataPtr32 = (uint32_t*)ctx->data;
bool useBuf = ( NULL != dataPtr );
bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr))));
uint16_t status0 = 0;
while(1) {
volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1);
volatile uint16_t ctl32 = sdmmc_read16(REG_SDDATACTL32);
if((ctl32 & 0x100))
{
if(readdata) {
if(useBuf) {
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
if(size > 0x1FF) {
if(useBuf32) {
for(i = 0; i<0x200; i+=4) {
*dataPtr32++ = sdmmc_read32(REG_SDFIFO32);
}
} else {
for(i = 0; i<0x200; i+=2) {
*dataPtr++ = sdmmc_read16(REG_SDFIFO);
}
}
size -= 0x200;
}
}
sdmmc_mask16(REG_SDDATACTL32, 0x800, 0);
}
}
if(status1 & TMIO_MASK_GW) {
ctx->error |= 4;
break;
}
if(!(status1 & TMIO_STAT1_CMD_BUSY)) {
status0 = sdmmc_read16(REG_SDSTATUS0);
if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) {
ctx->error |= 0x1;
}
if(status0 & TMIO_STAT0_DATAEND) {
ctx->error |= 0x2;
}
if((status0 & flags) == flags)
break;
}
}
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
sdmmc_write16(REG_SDSTATUS0,0);
sdmmc_write16(REG_SDSTATUS1,0);
if(getSDRESP != 0) {
ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16);
ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16);
ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16);
ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16);
}
}
//---------------------------------------------------------------------------------
int sdmmc_cardinserted() {
//---------------------------------------------------------------------------------
return 1; //sdmmc_cardready;
}
//---------------------------------------------------------------------------------
void sdmmc_controller_init(bool force) {
//---------------------------------------------------------------------------------
deviceSD.isSDHC = 0;
deviceSD.SDOPT = 0;
deviceSD.res = 0;
deviceSD.initarg = 0;
deviceSD.clk = 0x80;
deviceSD.devicenumber = 0;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512;
*(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1;
*(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu;
*(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u;
*(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL;
*(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16;
*(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7
*(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
*(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20;
*(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE;
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512;
*(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0;
}
//---------------------------------------------------------------------------------
int sdmmc_sdcard_init() {
//---------------------------------------------------------------------------------
setTarget(&deviceSD);
swiDelay(0xF000);
sdmmc_send_command(&deviceSD,0,0);
sdmmc_send_command(&deviceSD,0x10408,0x1AA);
u32 temp = (deviceSD.error & 0x1) << 0x1E;
u32 temp2 = 0;
do {
do {
sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp);
temp2 = 1;
} while ( !(deviceSD.error & 1) );
} while((deviceSD.ret[0] & 0x80000000) == 0);
if(!((deviceSD.ret[0] >> 30) & 1) || !temp)
temp2 = 0;
deviceSD.isSDHC = temp2;
sdmmc_send_command(&deviceSD,0x10602,0);
if (deviceSD.error & 0x4) return -1;
sdmmc_send_command(&deviceSD,0x10403,0);
if (deviceSD.error & 0x4) return -1;
deviceSD.initarg = deviceSD.ret[0] >> 0x10;
sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
deviceSD.clk = 1;
setckl(1);
sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
sdmmc_send_command(&deviceSD,0x1076A,0x0);
if (deviceSD.error & 0x4) return -1;
sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
deviceSD.SDOPT = 1;
sdmmc_send_command(&deviceSD,0x10446,0x2);
if (deviceSD.error & 0x4) return -1;
sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
sdmmc_send_command(&deviceSD,0x10410,0x200);
if (deviceSD.error & 0x4) return -1;
deviceSD.clk |= 0x200;
return 0;
}
//---------------------------------------------------------------------------------
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out) {
//---------------------------------------------------------------------------------
if (deviceSD.isSDHC == 0)
sector_no <<= 9;
setTarget(&deviceSD);
sdmmc_write16(REG_SDSTOP,0x100);
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
sdmmc_write16(REG_SDBLKLEN32,0x200);
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
deviceSD.data = out;
deviceSD.size = numsectors << 9;
sdmmc_send_command(&deviceSD,0x33C12,sector_no);
return geterror(&deviceSD);
}
#endif

View File

@ -0,0 +1,194 @@
#ifndef __SDMMC_H__
#define __SDMMC_H__
#include <nds/ndstypes.h>
#define DATA32_SUPPORT
#define SDMMC_BASE 0x04004800
#define REG_SDCMD 0x00
#define REG_SDPORTSEL 0x02
#define REG_SDCMDARG 0x04
#define REG_SDCMDARG0 0x04
#define REG_SDCMDARG1 0x06
#define REG_SDSTOP 0x08
#define REG_SDRESP 0x0c
#define REG_SDBLKCOUNT 0x0a
#define REG_SDRESP0 0x0c
#define REG_SDRESP1 0x0e
#define REG_SDRESP2 0x10
#define REG_SDRESP3 0x12
#define REG_SDRESP4 0x14
#define REG_SDRESP5 0x16
#define REG_SDRESP6 0x18
#define REG_SDRESP7 0x1a
#define REG_SDSTATUS0 0x1c
#define REG_SDSTATUS1 0x1e
#define REG_SDIRMASK0 0x20
#define REG_SDIRMASK1 0x22
#define REG_SDCLKCTL 0x24
#define REG_SDBLKLEN 0x26
#define REG_SDOPT 0x28
#define REG_SDFIFO 0x30
#define REG_SDDATACTL 0xd8
#define REG_SDRESET 0xe0
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
#define REG_SDDATACTL32 0x100
#define REG_SDBLKLEN32 0x104
#define REG_SDBLKCOUNT32 0x108
#define REG_SDFIFO32 0x10C
#define REG_CLK_AND_WAIT_CTL 0x138
#define REG_RESET_SDIO 0x1e0
//The below defines are from linux kernel drivers/mmc tmio_mmc.h.
/* Definitions for values the CTRL_STATUS register can take. */
#define TMIO_STAT0_CMDRESPEND 0x0001
#define TMIO_STAT0_DATAEND 0x0004
#define TMIO_STAT0_CARD_REMOVE 0x0008
#define TMIO_STAT0_CARD_INSERT 0x0010
#define TMIO_STAT0_SIGSTATE 0x0020
#define TMIO_STAT0_WRPROTECT 0x0080
#define TMIO_STAT0_CARD_REMOVE_A 0x0100
#define TMIO_STAT0_CARD_INSERT_A 0x0200
#define TMIO_STAT0_SIGSTATE_A 0x0400
#define TMIO_STAT1_CMD_IDX_ERR 0x0001
#define TMIO_STAT1_CRCFAIL 0x0002
#define TMIO_STAT1_STOPBIT_ERR 0x0004
#define TMIO_STAT1_DATATIMEOUT 0x0008
#define TMIO_STAT1_RXOVERFLOW 0x0010
#define TMIO_STAT1_TXUNDERRUN 0x0020
#define TMIO_STAT1_CMDTIMEOUT 0x0040
#define TMIO_STAT1_RXRDY 0x0100
#define TMIO_STAT1_TXRQ 0x0200
#define TMIO_STAT1_ILL_FUNC 0x2000
#define TMIO_STAT1_CMD_BUSY 0x4000
#define TMIO_STAT1_ILL_ACCESS 0x8000
#define SDMC_NORMAL 0x00000000
#define SDMC_ERR_COMMAND 0x00000001
#define SDMC_ERR_CRC 0x00000002
#define SDMC_ERR_END 0x00000004
#define SDMC_ERR_TIMEOUT 0x00000008
#define SDMC_ERR_FIFO_OVF 0x00000010
#define SDMC_ERR_FIFO_UDF 0x00000020
#define SDMC_ERR_WP 0x00000040
#define SDMC_ERR_ABORT 0x00000080
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
#define SDMC_ERR_PARAM 0x00000200
#define SDMC_ERR_R1_STATUS 0x00000800
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
#define SDMC_ERR_RESET 0x00002000
#define SDMC_ERR_ILA 0x00004000
#define SDMC_ERR_INFO_DETECT 0x00008000
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
#define SDMC_STAT_ERR_CC 0x00100000
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
#define SDMC_STAT_ERR_CRC 0x00800000
#define SDMC_STAT_ERR_OTHER 0xf9c70008
#define TMIO_MASK_ALL 0x837f031d
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
typedef struct mmcdevice {
u8* data;
u32 size;
u32 error;
u16 stat0;
u16 stat1;
u32 ret[4];
u32 initarg;
u32 isSDHC;
u32 clk;
u32 SDOPT;
u32 devicenumber;
u32 total_size; //size in sectors of the device
u32 res;
} mmcdevice;
enum {
MMC_DEVICE_SDCARD,
MMC_DEVICE_NAND,
};
void sdmmc_controller_init(bool force_init);
void sdmmc_initirq();
int sdmmc_cardinserted();
int sdmmc_sdcard_init();
int sdmmc_nand_init();
void sdmmc_get_cid(int devicenumber, u32 *cid);
static inline void sdmmc_nand_cid( u32 *cid) {
sdmmc_get_cid(MMC_DEVICE_NAND,cid);
}
static inline void sdmmc_sdcard_cid( u32 *cid) {
sdmmc_get_cid(MMC_DEVICE_SDCARD,cid);
}
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out);
int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in);
int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out);
int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in);
extern u32 sdmmc_cid[];
extern int sdmmc_curdevice;
//---------------------------------------------------------------------------------
static inline u16 sdmmc_read16(u16 reg) {
//---------------------------------------------------------------------------------
return *(vu16*)(SDMMC_BASE + reg);
}
//---------------------------------------------------------------------------------
static inline void sdmmc_write16(u16 reg, u16 val) {
//---------------------------------------------------------------------------------
*(vu16*)(SDMMC_BASE + reg) = val;
}
//---------------------------------------------------------------------------------
static inline u32 sdmmc_read32(u16 reg) {
//---------------------------------------------------------------------------------
return *(vu32*)(SDMMC_BASE + reg);
}
//---------------------------------------------------------------------------------
static inline void sdmmc_write32(u16 reg, u32 val) {
//---------------------------------------------------------------------------------
*(vu32*)(SDMMC_BASE + reg) = val;
}
//---------------------------------------------------------------------------------
static inline void sdmmc_mask16(u16 reg, u16 clear, u16 set) {
//---------------------------------------------------------------------------------
u16 val = sdmmc_read16(reg);
val &= ~clear;
val |= set;
sdmmc_write16(reg, val);
}
//---------------------------------------------------------------------------------
static inline void setckl(u32 data) {
//---------------------------------------------------------------------------------
sdmmc_mask16(REG_SDCLKCTL, 0x100, 0);
sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF);
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
}
#endif