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.
@ -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 \
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}*/
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
18
Makefile
@ -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
|
||||
|
||||
|
BIN
NTR_Launcher.dsi
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
0
arm9/source/crc.h → arm9/common/crc.h
Executable file → Normal file
55
arm9/common/launcherData.h
Normal 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
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
14
arm9/source/bios_decompress_callback.h → arm9/common/nds_card.h
Executable file → Normal 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
@ -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
@ -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
|
@ -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
|
@ -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);
|
||||
|
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB |
BIN
arm9/graphics/font.bmp
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
arm9/graphics/font6x8.bmp
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
arm9/graphics/hbmenu_banner.bmp
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
arm9/graphics/hbmenu_banner_cartSelected.bmp
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
arm9/graphics/hbmenu_consolebg.bmp
Normal file
After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
296
arm9/hbmenu/args.cpp
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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);
|
372
arm9/hbmenu/nds_loader_arm9.c
Normal 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);
|
||||
}
|
||||
|
51
arm9/hbmenu/nds_loader_arm9.h
Normal 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
BIN
arm9/music/launch.wav
Normal file
BIN
arm9/music/select.wav
Normal file
BIN
arm9/music/wrong.wav
Normal 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
|
||||
} ;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 630 B |
3
ndsbootloader/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*build
|
||||
load.bin
|
||||
load.elf
|
124
ndsbootloader/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
110
ndsbootloader/arm9code/mpu_reset.s
Normal 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
@ -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 . */
|
||||
}
|
70
ndsbootloader/source/arm7clear.s
Normal 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
|
||||
|
68
ndsbootloader/source/arm9clear.arm.c
Normal 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);
|
||||
}
|
||||
|
6
ndsbootloader/source/arm9mpu_reset.s
Normal file
@ -0,0 +1,6 @@
|
||||
.arm
|
||||
.global mpu_reset, mpu_reset_end
|
||||
|
||||
mpu_reset:
|
||||
.incbin "mpu_reset.bin"
|
||||
mpu_reset_end:
|
13
ndsbootloader/source/bios.s
Normal 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
@ -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;
|
||||
}
|
||||
|
11
ndsbootloader/source/boot.h
Normal 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_
|
||||
|
45
ndsbootloader/source/card.h
Normal 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
|
82
ndsbootloader/source/disc_io.h
Normal 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
|
223
ndsbootloader/source/dldi_patcher.c
Normal 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
|
32
ndsbootloader/source/dldi_patcher.h
Normal 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
@ -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;
|
||||
}
|
46
ndsbootloader/source/fat.h
Normal 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
|
44
ndsbootloader/source/io_dldi.h
Normal 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
|
124
ndsbootloader/source/io_dldi.s
Normal 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
|
147
ndsbootloader/source/load_crt0.s
Normal 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
|
||||
@---------------------------------------------------------------------------------
|
242
ndsbootloader/source/sdmmc.c
Normal 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
|
194
ndsbootloader/source/sdmmc.h
Normal 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
|