nrioTool/arm9/source/main.cpp
ApacheThunder 6fa7430f62 1.8 Release Commit ...
* Final D2 init command now uses the value from the cart's header (found
at offset 0x8) instead of the hardcoded value found with most 16g and 8g
carts.
2024-04-11 23:02:40 -05:00

1040 lines
31 KiB
C++

#include <nds.h>
#include <fat.h>
#include <nds/arm9/dldi.h>
#include <nds/fifocommon.h>
#include <nds/fifomessages.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include "ui.h"
#include "nrio_card.h"
#include "my_sd.h"
#include "nitrofs.h"
#include "tonccpy.h"
#include "read_card.h"
#include "nds_card.h"
#include "launch_engine.h"
#define NDS_HEADER 0x027FFE00
#define DSI_HEADER 0x027FE000
#define FILECOPYBUFFER 0x02100000
#define MAXFILESIZE (u32)0x00200000
#define INITBUFFER 0x02000000
#define STAGE2_HEADER 0x02000200
#define UDISK_HEADER 0x02000400
#define CONSOLE_SCREEN_WIDTH 32
#define CONSOLE_SCREEN_HEIGHT 24
#define StatRefreshRate 41
// #define NUM_SECTORS 4023552
// #define NUM_SECTORS 3991808
// #define NUM_SECTORS 32769
// #define NUM_SECTORS 16896
// #define NUM_SECTORS 10000
// #define NUM_SECTORS 5056
#define NUM_SECTORS 3300
#define SECTOR_SIZE 512
#define STAGE1OFFSET (u32)0x400
#define STAGE1SECTORCOUNT 22
#define STAGE2OFFSET (u32)0x00020000
#define UDISKROMOFFSET (u32)0x00080000
#define FALLBACKSIZE 20000
#define UPDATEBUFFER 0x02700000
// #define MAXUPDATERESULTSIZE 1545
#define MAXUPDATERESULTSIZE 4096
// #define UPDATE_SIZE 1170
tNDSHeader* ndsHeader = (tNDSHeader*)NDS_HEADER;
tDSiHeader* cartHeader = (tDSiHeader*)DSI_HEADER;
tNDSHeader* stage2Header = (tNDSHeader*)STAGE2_HEADER;
tNDSHeader* uDiskHeader = (tNDSHeader*)UDISK_HEADER;
DTCM_DATA ALIGN(4) u8 ReadBuffer[SECTOR_SIZE];
DTCM_DATA ALIGN(4) u8 FATBuffer[2048];
// ALIGN(4) u32 CartReadBuffer[512];
static bool autoBootCart = false;
static bool SCFGUnlocked = false;
static bool ErrorState = false;
static bool fatMounted = false;
static bool nitroFSMounted = false;
static bool nrioDLDIMounted = false;
static bool sdMounted = false;
static int MenuID = 0;
static int cartSize = 0;
static bool wasDSi = false;
static bool ntrMode = false;
static bool dldiWarned = false;
static bool uDiskFileFound = false;
static bool WarningPosted = false;
bool dldiDebugMode = false;
char gameTitle[13] = {0};
const char* textBufferTop = "X------------------------------X\nX------------------------------X";
const char* textProgressTopBuffer = "X------------------------------X\nX------------------------------X";
const char* textBuffer = "X------------------------------X\nX------------------------------X";
const char* textProgressBuffer = "X------------------------------X\nX------------------------------X";
int ProgressTracker = 0;
bool UpdateProgressText = false;
bool UpdateDebugText = false;
extern bool TopSelected;
u64 getBytesFree(const char* drivePath) {
struct statvfs st;
statvfs(drivePath, &st);
return (u64)st.f_bsize * (u64)st.f_bavail;
}
static tNDSHeader* loadHeader(tDSiHeader* twlHeaderTemp) {
tNDSHeader* ntrHeader = (tNDSHeader*)NDS_HEADER;
*ntrHeader = twlHeaderTemp->ndshdr;
return ntrHeader;
}
void DoWait(int waitTime = 30) {
if (waitTime > 0)for (int i = 0; i < waitTime; i++) { swiWaitForVBlank(); }
}
void DoFATerror(bool isFatel, bool isSDError) {
consoleClear();
if (isSDError) { printf("SD Init Failed!\n"); } else { printf("FAT Init Failed!\n"); }
printf("\nPress [A] to exit...\n");
ErrorState = isFatel;
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
}
}
void DoError(const char* Message, bool isFatal = true) {
consoleClear();
printf(Message);
if (isFatal) { printf("\n\nPress [A] to exit...\n"); } else { printf("\n\nPress [A] to abort...\n"); }
ErrorState = isFatal;
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
}
}
void CardInit(bool Silent = true, bool SkipSlotReset = false) {
if (!Silent || !wasDSi)consoleClear();
DoWait(30);
if (!wasDSi) {
ALIGN(4) u32 ntrHeader[0x80];
printf("Cart reset required!\nPlease eject Cart...\n");
do { swiWaitForVBlank(); getHeader (ntrHeader); } while (ntrHeader[0] != 0xffffffff);
consoleClear();
printf("Reinsert Cart...");
do { swiWaitForVBlank(); getHeader (ntrHeader); } while (ntrHeader[0] == 0xffffffff);
// Delay half a second for the DS card to stabilise
DoWait();
}
// Do cart init stuff to wake cart up. DLDI init may fail otherwise!
cardInit((sNDSHeaderExt*)cartHeader, SkipSlotReset);
char gameCode[7] = {0};
tonccpy(gameTitle, cartHeader->ndshdr.gameTitle, 12);
tonccpy(gameCode, cartHeader->ndshdr.gameCode, 6);
ndsHeader = loadHeader(cartHeader); // copy twlHeaderTemp to ndsHeader location
u32 CardType = *(u32*)(DSI_HEADER + 0x08);
InitCartNandReadMode(CardType);
DoWait();
if (!Silent) {
if (wasDSi && SCFGUnlocked) { iprintf("SCFG_MC Status: %2x \n\n", REG_SCFG_MC); }
iprintf("Detected Cart Name: %12s \n\n", gameTitle);
iprintf("Detected Cart Game Id: %6s \n\n", gameCode);
iprintf("Detected Cart Type: %8x \n\n", (unsigned int)CardType);
printf("Press any button to continue...");
do { swiWaitForVBlank(); scanKeys(); } while (!keysDown());
}
}
void MountFATDevices(bool mountSD = true) {
if (wasDSi) {
if (mountSD && !sdMounted) {
// Important to set this else SD init will hang/fail!
fifoSetValue32Handler(FIFO_USER_04, sdStatusHandler, nullptr);
DoWait();
sdMounted = fatMountSimple("sd", __my_io_dsisd());
}
if (sdMounted && !nitroFSMounted) {
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("sd:/title/00030004/44534e52/content/00000000.app");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("sd:/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("sd:/nds/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("sd:/_nds/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("sd:/homebrew/nrioTool.nds");
}
} else {
// if (!fatMounted)fatMounted = fatMountSimple("nrio", &io_nrio_);
if (!fatMounted)fatMounted = fatInitDefault();
if (fatMounted && !nitroFSMounted) {
nitroFSMounted = nitroFSInit("fat:/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("fat:/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("fat:/nds/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("fat:/_nds/nrioTool.nds");
if (!nitroFSMounted)nitroFSMounted = nitroFSInit("fat:/homebrew/nrioTool.nds");
}
}
}
void DoUpdateConvert() {
consoleClear();
DoWait(60);
printf("About to convert update file...\n\n");
printf("Press A to continue.\n");
printf("Press B to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) {
MountFATDevices(wasDSi);
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) { DoFATerror(true, wasDSi); return; }
}
FILE *src;
if (sdMounted) { src = fopen("sd:/nrioFiles/update.bin", "rb"); } else { src = fopen("fat:/nrioFiles/update.bin", "rb"); }
if (!src) { DoError("ERROR: Failed to find/open\n update file!", false); return; }
fseek(src, 0, SEEK_END);
int fileSize = (int)(ftell(src) / 0x200);
fseek(src, 0, SEEK_SET);
int UpdateResultSize = (((fileSize * 512) + (fileSize * 3)) / 512) + 1;
if (UpdateResultSize > MAXUPDATERESULTSIZE) { DoError("ERROR: Update result will exceed\nmemory limits!", false); return; }
// if (fileSize != UPDATE_SIZE) { fclose(src); DoError("ERROR: Unexpected file size for\n target file!", false); return; }
consoleClear();
ProgressTracker = (fileSize + 1);
textBuffer = "Reading and converting\nupdate.bin...\n\n\n";
textProgressBuffer = "Blocks Remaining: ";
u32 MarkerOffset = 0;
for (int i = 0; i < (fileSize + 1); i++) {
fseek(src, (u32)(i * 512), SEEK_SET);
u32 ReadOffset = ((u32)UPDATEBUFFER + ((u32)(i * 512) + MarkerOffset));
fread((void*)ReadOffset, 1, 0x200, src);
/* *(vu8*)((u32)(ReadOffset + 0x200)) = 0x3C;
*(vu8*)((u32)(ReadOffset + 0x201)) = 0xC0;
*(vu8*)((u32)(ReadOffset + 0x202)) = 0x3C;*/
*(vu8*)((u32)(ReadOffset + 0x200)) = 0x4D;
*(vu8*)((u32)(ReadOffset + 0x201)) = 0x4B;
*(vu8*)((u32)(ReadOffset + 0x202)) = 0x52;
MarkerOffset += 0x03;
ProgressTracker--;
UpdateProgressText = true;
}
fclose(src);
while(UpdateProgressText)swiWaitForVBlank();
FILE *dest;
if (sdMounted) { dest = fopen("sd:/nrioFiles/update_converted.bin", "wb"); } else { dest = fopen("fat:/nrioFiles/update_converted.bin", "wb"); }
if (!dest) { DoError("ERROR: Failed to create\nupdate_converted.bin!", false); return; }
consoleClear();
printf("Writing final result to\nupdate_converted.bin...\n");
fwrite((void*)UPDATEBUFFER, (u32)(UpdateResultSize * 512), 1, dest);
fclose(dest);
while (UpdateProgressText)swiWaitForVBlank();
consoleClear();
iprintf("Conversion finished!\n\n");
iprintf("Press A to return to menu\n");
iprintf("Press B to exit!\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
}
void DoCartBoot() {
consoleClear();
if(access("sd:/nrioFiles/xBootStrap.nds", F_OK) != 0 && nitroFSMounted) {
FILE *src = fopen("nitro:/xBootStrap.nds", "rb");
if (src) {
FILE *dest = fopen("sd:/nrioFiles/xBootStrap.nds", "wb");
if (dest) {
fseek(src, 0, SEEK_END);
u32 fSize = ftell(src);
fseek(src, 0, SEEK_SET);
if (fSize < MAXFILESIZE && fSize > 0) {
printf("WARNING: xBootStrap not found!\n");
printf("Trying to use NitroFS copy.\n\n");
printf("Please wait...\n");
fread((void*)FILECOPYBUFFER, 1, fSize, src);
fwrite((void*)FILECOPYBUFFER, fSize, 1, dest);
fclose(src);
fclose(dest);
}
}
}
}
if (access("sd:/nrioFiles/xBootStrap.nds", F_OK) == 0) {
struct stat st;
char filePath[PATH_MAX];
int pathLen;
const char* filename = "sd:/nrioFiles/xBootStrap.nds";
if (stat (filename, &st) < 0) { ErrorState = true; return; }
if (!getcwd (filePath, PATH_MAX)) { ErrorState = true; return; }
pathLen = strlen(filename);
strcpy (filePath + pathLen, filename);
runLaunchEngine(-1, st.st_ino);
ErrorState = true;
return;
} else {
printf("ERROR: xBootStrap.nds not found!");
printf("\n\n\nPress [A] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)return;
}
}
return;
}
void DoImageDump() {
consoleClear();
DoWait(60);
printf("About to dump FAT image.\n");
printf("This may take while!.\n\n");
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if (wasDSi && !sdMounted) {
MountFATDevices(wasDSi);
if (!sdMounted) { DoFATerror(true, true); return; }
}
u64 CartCapacity = 0;
struct statvfs st;
if (statvfs("fat:/", &st) == 0)CartCapacity = (st.f_bsize * st.f_blocks);
if (CartCapacity > getBytesFree("sd:") || CartCapacity == 0 || getBytesFree("sd:") == 0) {
consoleClear();
printf("ERROR: Insufficient free space!\n\n");
printf("Press [A] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)return;
}
}
FILE *dest = fopen("sd:/nrioFiles/nrio_fatimage_dump.bin", "wb");
if (!dest) { DoError("ERROR: Failed to create file!"); return; }
ProgressTracker = (int)(CartCapacity / 0x800) + 4;
int ReadSize = ProgressTracker;
textBuffer = "Dumping sectors to\nnrio_fatimage_dump.bin\n\nPress [B] to abort...\n\n\n";
textProgressBuffer = "Blocks Remaining: ";
bool readEndedEarly = false;
UpdateDebugText = dldiDebugMode;
for (int i = 0; i < ReadSize; i++) {
if (io_dldi_data->ioInterface.readSectors((i * 4), 4, FATBuffer)) {
fwrite(FATBuffer, 0x800, 1, dest); // Used Region
} else {
readEndedEarly = true;
break;
}
ProgressTracker--;
if (ProgressTracker < 4)ProgressTracker = 0;
UpdateProgressText = true;
scanKeys();
if(keysDown() & KEY_B)break;
}
fclose(dest);
while (UpdateProgressText)swiWaitForVBlank();
if(UpdateDebugText)UpdateDebugText = false;
consoleClear();
if (!readEndedEarly) {
printf("Image dump finished!\n\n");
} else {
printf("Warning: Image dump finished\n early!\n\n");
}
printf("Press [A] to return to DLDI menu\n\n");
printf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) {
// CardInit();
// InitCartNandReadMode();
return;
} else if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
}
void DoImageRestore() {
consoleClear();
DoWait(60);
printf("About to write default fat image\n\n");
iprintf("Target Cart Capacity: %d\n\n", cartSize);
const char* FilePath = "sd:/NOTSUPPORTED";
if (cartSize == 2011584)FilePath = "sd:/nrioFiles/nrio_basicimage_2011584_16g.img";
if (cartSize == 1995712)FilePath = "sd:/nrioFiles/nrio_basicimage_1995712_16g.img";
if (access("sd:/nrioFiles/nrio_customimage.img", F_OK) == 0)FilePath = "nrio_customimage.img"; // if present, will write this file over any other.
if (access(FilePath, F_OK) != 0 && nitroFSMounted) {
if (cartSize == 2011584)FilePath = "nitro:/nrio_basicimage_2011584_16g.img";
if (cartSize == 1995712)FilePath = "nitro:/nrio_basicimage_1995712_16g.img";
}
if (!memcmp(FilePath, "sd:/NOTSUPPORTED", 16)) {
printf("\nERROR: Cart capacity not\nsupported!\n");
printf("\nPress [A] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)return;
}
}
printf("\nPress [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
FILE *src = fopen(FilePath, "rb");
if (!src) { DoError("ERROR: Failed to find or open\nnrio_basicimage!\n"); return; }
fseek(src, 0, SEEK_END);
int fileSize = (int)(ftell(src) / 0x800) + 4;
u32 fSize = ftell(src);
fseek(src, 0, SEEK_SET);
ProgressTracker = (int)fileSize;
textBuffer = "Writing FAT image to cart.\nPlease Wait...\n\n\n";
textProgressBuffer = "Blocks Remaining: ";
for (int i = 0; i < fileSize; i++){
fseek(src, (i * 0x800), SEEK_SET);
fread(FATBuffer, 1, 0x800, src);
io_dldi_data->ioInterface.writeSectors((i * 4), 4, FATBuffer);
if (ProgressTracker < 4)ProgressTracker = 0;
ProgressTracker--;
UpdateProgressText = true;
if (fSize < (u32)(i * 0x800))break;
}
while(UpdateProgressText)swiWaitForVBlank();
fclose(src);
consoleClear();
printf("FAT image write finished!\n\n");
printf("Press [A] to return to DLDI menu\n\n");
printf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
// if ((u64)fsize > driveSizeFree("fat:/")) { }
}
void DoSector0Dump() {
consoleClear();
DoWait(60);
printf("About to dump FAT sector 0.\n\n");
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
FILE *dest = fopen("sd:/nrioFiles/nrio_fatsector0.bin", "wb");
if (!dest) { DoError("ERROR: Failed to create file!"); return; }
io_dldi_data->ioInterface.readSectors(0, 1, ReadBuffer);
fwrite(ReadBuffer, 0x200, 1, dest);
fclose(dest);
consoleClear();
printf("Sector 0 dump finished!\n\n");
printf("Press [A] to return to DLDI menu\n");
printf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
// fseek(dest, 0x200, SEEK_SET);
// if ((u64)fsize > driveSizeFree("fat:/")) { }
}
void DoSector0Restore() {
consoleClear();
DoWait(60);
printf("About to write FAT sector 0.\n\n");
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if (access("sd:/nrioFiles/nrio_fatsector0.bin", F_OK) != 0) {
DoError("ERROR: Failed to find\nnrio_fatsector0.bin!");
return;
}
FILE *src = fopen("sd:/nrioFiles/nrio_fatsector0.bin", "rb");
if (!src) { DoError("ERROR: Failed to open\nnrio_fatsector0.bin!"); return; }
fseek(src, 0, SEEK_END);
u32 fSectorSize = (ftell(src) / 0x200);
fseek(src, 0, SEEK_SET);
if (fSectorSize != 1) { DoError("ERROR: nrio_fatsector0.bin has incorrect size!"); return; }
fread(ReadBuffer, 1, 0x200, src);
io_dldi_data->ioInterface.readSectors(0, 1, ReadBuffer);
fclose(src);
consoleClear();
printf("Sector 0 write finished!\n");
printf("Press [A] to return to DLDI menu\n\n");
printf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
// fseek(dest, 0x200, SEEK_SET);
// if ((u64)fsize > driveSizeFree("fat:/")) { }
}
void DoTestDump() {
consoleClear();
DoWait(60);
iprintf("About to dump %d sectors.\n\n", NUM_SECTORS);
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) {
MountFATDevices(wasDSi);
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) { DoFATerror(true, wasDSi); return; }
}
FILE *dest;
if (sdMounted) { dest = fopen("sd:/nrioFiles/nrio_data.bin", "wb"); } else { dest = fopen("fat:/nrioFiles/nrio_data.bin", "wb"); }
ProgressTracker = NUM_SECTORS;
textBuffer = "Dumping sectors to nrio_data.bin\n\nPress [B] to abort...\n\n\n";
textProgressBuffer = "Sectors Remaining: ";
// int Sector0Location = 44800;
// for (int i = Sector0Location; i < NUM_SECTORS + Sector0Location; i++) {
for (int i = 0; i < NUM_SECTORS; i++) {
nrio_readSector(ReadBuffer, (u32)(i * 0x200));
fwrite(ReadBuffer, 0x200, 1, dest); // Used Region
ProgressTracker--;
UpdateProgressText = true;
scanKeys();
if(keysDown() & KEY_B)break;
}
fclose(dest);
while(UpdateProgressText)swiWaitForVBlank();
consoleClear();
printf("Sector dump finished!\n\n");
printf("Press [A] to return to main menu\n\n");
printf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
}
void DoStage1Dump() {
consoleClear();
DoWait(60);
printf("About to dump stage1 data...\n\n");
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) {
MountFATDevices(wasDSi);
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) { DoFATerror(true, wasDSi); return; }
}
FILE *dest;
if (sdMounted) { dest = fopen("sd:/nrioFiles/stage1.bin", "wb"); } else { dest = fopen("fat:/nrioFiles/stage1.bin", "wb"); }
ProgressTracker = STAGE1SECTORCOUNT;
textBuffer = "Dumping stage1 rom to file.\n\nPlease Wait...\n\n\n";
textProgressBuffer = "Sectors Remaining: ";
UpdateProgressText = true;
for (int i = 0; i < STAGE1SECTORCOUNT; i++){
nrio_readSector(ReadBuffer, (u32)(i * 0x200) + STAGE1OFFSET);
fwrite(ReadBuffer, 0x200, 1, dest); // Used Region
ProgressTracker--;
UpdateProgressText = true;
}
fclose(dest);
while(UpdateProgressText)swiWaitForVBlank();
consoleClear();
printf("Stage1 dump finished!\n\n");
printf("Press [A] to return to main menu\n\n");
printf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
}
void DoStage2Dump() {
consoleClear();
DoWait(60);
printf("About to dump stage2 SRL...\n\n");
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) {
MountFATDevices(wasDSi);
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) { DoFATerror(true, wasDSi); return; }
}
int Sectors;
nrio_readSector((void*)STAGE2_HEADER, STAGE2OFFSET);
DoWait(2);
if ((stage2Header->romSize > 0) && (stage2Header->romSize < 0x00FFFFF)) {
Sectors = (int)(stage2Header->romSize / 0x200) + 1; // Add one sector to avoid underdump if size is not a multiple of 128 words
} else {
Sectors = 20000;
}
FILE *dest;
if (sdMounted) { dest = fopen("sd:/nrioFiles/stage2.nds", "wb"); } else { dest = fopen("fat:/nrioFiles/stage2.nds", "wb"); }
if (!dest) { DoError("ERROR: Failed to create file!"); return; }
ProgressTracker = Sectors;
textBuffer = "Dumping to stage2.nds...\nPlease Wait...\n\n\n";
textProgressBuffer = "Sectors Remaining: ";
for (int i = 0; i < Sectors; i++){
nrio_readSector(ReadBuffer, (u32)(i * 0x200) + STAGE2OFFSET);
fwrite(ReadBuffer, 0x200, 1, dest);
ProgressTracker--;
UpdateProgressText = true;
}
fclose(dest);
consoleClear();
iprintf("Dump finished!\n\n");
iprintf("Press [A] to return to main menu\n\n");
iprintf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
}
void DoUdiskDump() {
consoleClear();
DoWait(60);
printf("About to dump uDisk...\n\n");
printf("Press [A] to continue.\n\n");
printf("Press [B] to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) {
MountFATDevices(wasDSi);
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) { DoFATerror(true, wasDSi); return; }
}
int Sectors;
nrio_readSector((void*)UDISK_HEADER, UDISKROMOFFSET);
DoWait(2);
if ((uDiskHeader->romSize > 0) && (uDiskHeader->romSize < 0x00FFFFF)) {
Sectors = (int)(uDiskHeader->romSize / 0x200) + 1; // Add one sector to avoid underdump if size is not a multiple of 128 words
} else {
Sectors = 20000;
}
FILE *dest;
if (sdMounted) { dest = fopen("sd:/nrioFiles/udisk.nds", "wb"); } else { dest = fopen("fat:/nrioFiles/udisk.nds", "wb"); }
if (!dest) { DoError("ERROR: Failed to create file!"); return; }
ProgressTracker = Sectors;
textBuffer = "Dumping to udisk.nds...\nPlease Wait...\n\n\n";
textProgressBuffer = "Sectors Remaining: ";
for (int i = 0; i < Sectors; i++){
nrio_readSector(ReadBuffer, (u32)(i * 0x200) + UDISKROMOFFSET);
fwrite(ReadBuffer, 0x200, 1, dest);
ProgressTracker--;
UpdateProgressText = true;
}
fclose(dest);
consoleClear();
iprintf("Dump finished!\n\n");
iprintf("Press [A] to return to main menu\n\n");
iprintf("Press [B] to exit\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) {
ErrorState = true;
return;
}
}
}
// Not working yet
/*void DoBannerWrite() {
consoleClear();
DoWait(60);
printf("About to write banner!\n\n");
printf("Press A to continue.\n");
printf("Press B to abort.\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B)return;
}
toncset((void*)0x02000000, 0, 0x800);
consoleClear();
printf("Reading banner please wait...\n\n");
// CardInit();
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) {
MountFATDevices(wasDSi);
if ((wasDSi && !sdMounted) || (!wasDSi && !fatMounted)) { DoFATerror(true, wasDSi); return; }
}
// nrio_readSector((void*)STAGE2_HEADER, 0);
DoWait(2);
consoleClear();
FILE *bannerFile;
if (sdMounted) { bannerFile = fopen("sd:/nrioFiles/banner.bin", "rb"); } else { bannerFile = fopen("fat:/nrioFiles/banner.bin", "rb"); }
if (!bannerFile) { DoError("ERROR: Failed to read banner\nfile!\n\n"); return; }
fread((void*)UPDATEBUFFER, 1, 0xA00, bannerFile);
printf("Writing banner.bin to cart...\nPlease Wait...\n\n\n");
// int Sectors = 5;
// ProgressTracker = Sectors;
// textBuffer = "Writing banner.bin to cart...\nPlease Wait...\n\n\n";
// textProgressBuffer = "Sectors Remaining: ";
// UpdateProgressText = true;
// for (int i = 0; i < Sectors; i++){
// nrio_writeSector((stage2Header->bannerOffset + (u32)(i * 0x200)), (void*)((u32)UPDATEBUFFER + (u32)(i * 0x200)));
nrio_writeSectors(0x2000, UPDATEBUFFER, 0x800);
nrio_writeSectors(0x2800, UPDATEBUFFER, 0x800);
// ProgressTracker--;
// UpdateProgressText = true;
// }
// UpdateProgressText = true;
fclose(bannerFile);
CardInit();
consoleClear();
printf("Banner write finished!\n\n");
printf("Press A to return to main menu!\n");
printf("Press B to exit!\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A) return;
if(keysDown() & KEY_B) { ErrorState = true; return; }
}
}*/
int UtilityMenu() {
if(!WarningPosted)LoadTopScreenUtilitySplash();
int value = -1;
consoleClear();
printf("Press [A] to build update file\n");
// printf("Press [X] write banner to cart\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n\n\n\n\n\n\n\nPress [B] to go to main menu...\n");
while(value == -1) {
swiWaitForVBlank();
scanKeys();
switch (keysDown()){
case KEY_A: { value = 0; } break;
// case KEY_X: { value = 1; } break;
case KEY_B: { value = 2; } break;
}
}
return value;
}
int DLDIMenu() {
int value = -1;
consoleClear();
if (!dldiWarned) {
printf("WARNING! Leaving DLDI menu will\nrequire restart!\n");
printf("Do you wish to continue?\n\n");
printf("\nPress [A] to continue\n");
printf("\nPress [B] to return to main menu\n");
while(1) {
swiWaitForVBlank();
scanKeys();
if(keysDown() & KEY_A)break;
if(keysDown() & KEY_B) { value = 6; break; }
}
consoleClear();
if (value == 6) { return value; } else { dldiWarned = true; }
}
if (WarningPosted)consoleClearTop(false);
LoadTopScreenDLDISplash();
swiWaitForVBlank();
if (!ntrMode) {
ntrMode = true;
REG_SCFG_EXT &= ~(1UL << 14);
REG_SCFG_EXT &= ~(1UL << 15);
for (int i = 0; i < 30; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); }
}
if (!nrioDLDIMounted) {
nrioDLDIMounted = fatInitDefault();
if (!nrioDLDIMounted) { DoError("DLDI Init Failed!\n"); return 4; }
if (!uDiskFileFound)uDiskFileFound = (access("sd:/nrioFiles/xBootStrap.nds", F_OK) == 0);
}
consoleClear();
struct statvfs st;
if ((cartSize == 0) && statvfs("fat:/", &st) == 0)cartSize = (st.f_bsize * st.f_blocks) / 1024;
printf("Press [A] to boot cart menu\n");
printf("\nPress [X] to dump sector 0\n");
printf("\nPress [Y] to restore sector 0\n");
printf("\nPress [DPAD UP] to dump FAT\nimage\n");
printf("\nPress [START] to write default\nFAT image\n");
printf("\n\n\n\n\n\n\n\nPress [B] to exit...\n");
while(value == -1) {
swiWaitForVBlank();
scanKeys();
switch (keysDown()){
case KEY_A: { value = 0; } break;
case KEY_X: { value = 1; } break;
case KEY_Y: { value = 2; } break;
case KEY_UP: { value = 3; } break;
case KEY_START: { value = 4; } break;
case KEY_B: { ErrorState = true; value = 5; } break;
}
}
return value;
}
int MainMenu() {
int value = -1;
consoleClear();
printf("Press [A] to dump Stage2 SRL\n");
printf("\nPress [Y] to dump UDISK SRL\n");
if (wasDSi)printf("\nPress [X] go to DLDI menu\n");
printf("\nPress [DPAD LEFT] dump main SRL\n");
printf("\nPress [DPAD RIGHT] to do test\ndump\n");
printf("\nPress [L] go to utility menu\n");
if (!wasDSi)printf("\n");
printf("\n\n\n\nPress [B] to exit\n");
// printf("START to write new banner\n");
// printf("SELECT to write new Arm binaries\n\n\n");
while(value == -1) {
swiWaitForVBlank();
scanKeys();
switch (keysDown()) {
case KEY_A: { value = 0; } break;
case KEY_Y: { value = 1; } break;
case KEY_L: { value = 2; } break;
case KEY_X: { if (wasDSi)value = 3; } break;
case KEY_LEFT: { value = 4; } break;
case KEY_RIGHT: { value = 5; } break;
case KEY_B: { value = 6; } break;
case KEY_START: { value = 7; } break;
}
}
return value;
}
void vblankHandler (void) {
if (UpdateProgressText) {
if (TopSelected) {
SelectBotConsole();
TopSelected = false;
}
consoleClear();
printf(textBuffer);
printf(textProgressBuffer);
iprintf("%d \n", ProgressTracker);
UpdateProgressText = false;
}
if (UpdateDebugText) {
PrintToTop("%02x ", *(u8*)0x40001A1, false);
PrintToTop("%08x", *(u32*)0x40001A8, false);
PrintToTop("%08x ", *(u32*)0x40001AC, false);
PrintToTop("%08x\n", *(u32*)0x40001A4, false);
// UpdateDebugText = false;
}
}
ITCM_CODE void SetSCFG() {
if (REG_SCFG_EXT & BIT(31)) {
REG_SCFG_EXT &= ~(1UL << 14);
REG_SCFG_EXT &= ~(1UL << 15);
for (int i = 0; i < 10; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); }
}
}
int main() {
scanKeys();
if(keysDown() & KEY_B)autoBootCart = true;
wasDSi = isDSiMode();
if (wasDSi && (REG_SCFG_EXT & BIT(31)))SCFGUnlocked = true;
defaultExceptionHandler();
BootSplashInit();
if (autoBootCart) {
printf("\n\n\n\n\n\n Launching XuluMenu.\n Please Wait...");
} else {
printf("\n\n\n\n\n\n Checking cart. Please Wait...");
}
sysSetCardOwner(BUS_OWNER_ARM9);
if (!wasDSi)sysSetCartOwner(BUS_OWNER_ARM7);
MountFATDevices();
toncset((void*)0x02000000, 0, 0x800);
CardInit();
if ((!sdMounted && wasDSi) || (!fatMounted && !wasDSi)) {
DoFATerror(true, wasDSi);
consoleClear();
fifoSendValue32(FIFO_USER_03, 1);
return 0;
}
if (memcmp(cartHeader->ndshdr.gameCode, "DSGB", 4)) {
if(!WarningPosted)LoadTopScreenDebugSplash();
WarningPosted = true;
consoleClear();
PrintToTop("WARNING! The cart in slot 1\ndoesn't appear to be an\nN-Card or one of it's clones!\n\n", -1, false);
}
if (*(u32*)(INITBUFFER) != 0x2991AE1D) {
if(!WarningPosted)LoadTopScreenDebugSplash();
consoleClear();
WarningPosted = true;
FILE *initFile = fopen("sd:/nrioFiles/nrio_InitLog.bin", "wb");
if (initFile) {
fwrite((u32*)INITBUFFER, 0x200, 1, initFile); // Used Region
fclose(initFile);
}
toncset((void*)0x02000000, 0, 0x800);
PrintToTop("WARNING! Cart returned\nunexpected response from init\ncommand!\n\n", -1, false);
}
if (autoBootCart) { SetSCFG(); DoCartBoot(); }
// Enable vblank handler
irqSet(IRQ_VBLANK, vblankHandler);
if (wasDSi) { if(access("sd:/nrioFiles", F_OK) != 0)mkdir("sd:/nrioFiles", 0777); } else { if(access("fat:/nrioFiles", F_OK) != 0)mkdir("fat:/nrioFiles", 0777); }
while(1) {
if (ErrorState) {
consoleClear();
fifoSendValue32(FIFO_USER_03, 1);
return 0;
}
switch (MenuID) {
case 0: {
switch (MainMenu()) {
case 0: { DoStage2Dump(); } break;
case 1: { DoUdiskDump(); } break;
case 2: { MenuID = 1; } break;
case 3: { MenuID = 2; } break;
case 4: { DoStage1Dump(); } break;
case 5: { DoTestDump(); } break;
case 6: { ErrorState = true; } break;
case 7: {
SetSCFG();
DoCartBoot();
} break;
}
} break;
case 1: {
switch (UtilityMenu()) {
case 0: { DoUpdateConvert(); } break;
// case 1: { DoBannerWrite(); } break;
case 2: {
MenuID = 0;
if (!WarningPosted)LoadTopScreenSplash();
} break;
}
} break;
case 2: {
switch (DLDIMenu()) {
case 0: { DoCartBoot(); } break;
case 1: { DoSector0Dump(); } break;
case 2: { DoSector0Restore(); } break;
case 3: { DoImageDump(); } break;
case 4: { DoImageRestore(); } break;
case 5: { ErrorState = true; } break;
case 6: { MenuID = 0; } break;
}
} break;
}
swiWaitForVBlank();
}
return 0;
}