mirror of
https://github.com/rvtr/TDT.git
synced 2025-10-31 13:51:07 -04:00
commit
0ebb1eda5e
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Build NAND Title Manager
|
name: Build TAD Delivery Tool
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -22,7 +22,7 @@ jobs:
|
|||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
- name: Setup environment
|
- name: Setup environment
|
||||||
run: git config --global safe.directory '*'
|
run: git config --global safe.directory '*'
|
||||||
- name: Build NAND Title Manager
|
- name: Build TAD Delivery Tool
|
||||||
run: make
|
run: make
|
||||||
- name: Publish build to GH Actions
|
- name: Publish build to GH Actions
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
*.nds
|
*.nds
|
||||||
|
*.tad
|
||||||
*.dsi
|
*.dsi
|
||||||
*.elf
|
*.elf
|
||||||
*.map
|
*.map
|
||||||
|
|||||||
10
Makefile
10
Makefile
@ -13,11 +13,11 @@ export TOPDIR := $(CURDIR)
|
|||||||
NITRO_FILES :=
|
NITRO_FILES :=
|
||||||
|
|
||||||
# These set the information text in the nds file
|
# These set the information text in the nds file
|
||||||
GAME_TITLE := NAND Title Manager
|
GAME_TITLE := TAD Delivery Tool
|
||||||
GAME_SUBTITLE1 := JeffRuLz, Pk11
|
GAME_SUBTITLE1 := JeffRuLz, Pk11, rmc
|
||||||
|
|
||||||
GAME_CODE := HTMA
|
GAME_CODE := 4TDA
|
||||||
GAME_LABEL := NANDTM
|
GAME_LABEL := TAD_DELIVERY
|
||||||
|
|
||||||
include $(DEVKITARM)/ds_rules
|
include $(DEVKITARM)/ds_rules
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ checkarm9:
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
$(TARGET).dsi : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
|
$(TARGET).dsi : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
|
||||||
ndstool -c $(TARGET).dsi -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
|
ndstool -c $(TARGET).dsi -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
|
||||||
-u "00030004" \
|
-u "00030015" \
|
||||||
-g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \
|
-g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \
|
||||||
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1)" \
|
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1)" \
|
||||||
$(_ADDFILES)
|
$(_ADDFILES)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
# NAND Title Manager
|
# NAND Title Manager
|
||||||
A basic title manager for the Nintendo DSi supporting both hiyaCFW's SDNAND and SysNAND, modified from JeffRuLz's Title Manager for HiyaCFW.
|
A basic title manager for the Nintendo DSi supporting both hiyaCFW's SDNAND and SysNAND, modified from JeffRuLz's Title Manager for HiyaCFW.
|
||||||
|
This fork has been modified to allow installing TADs, the DSi's version of a WAD. These are typically signed and encrypted for development units. This makes them impossible to install on a retail unit. My NTM fork should let dev TADs install on retail, retail TADs install on dev, etc.
|
||||||
|
|
||||||
## WARNING
|
## WARNING
|
||||||
This can modify your internal system NAND! There is *always* a risk of **bricking**, albeit small, when you modify NAND. Please proceed with caution. Having Unlaunch installed is also strongly recommended as it will likely protect you if something manages to go wrong.
|
This can modify your internal system NAND! There is *always* a risk of **bricking**, albeit small, when you modify NAND. Please proceed with caution. Having Unlaunch installed is also strongly recommended as it will likely protect you if something manages to go wrong.
|
||||||
@ -30,4 +31,4 @@ This can modify your internal system NAND! There is *always* a risk of **brickin
|
|||||||
- [Martin Korth (nocash)](https://problemkaputt.de): [GBATEK](https://problemkaputt.de/gbatek.htm)
|
- [Martin Korth (nocash)](https://problemkaputt.de): [GBATEK](https://problemkaputt.de/gbatek.htm)
|
||||||
- [JeffRuLz](https://github.com/JeffRuLz): [TMFH](https://github.com/JeffRuLz/TMFH) (what this is a fork of)
|
- [JeffRuLz](https://github.com/JeffRuLz): [TMFH](https://github.com/JeffRuLz/TMFH) (what this is a fork of)
|
||||||
- [DesperateProgrammer](https://github.com/DesperateProgrammer): [DSi Language Patcher](https://github.com/DesperateProgrammer/DSiLanguagePacher) (working NAND writing code)
|
- [DesperateProgrammer](https://github.com/DesperateProgrammer): [DSi Language Patcher](https://github.com/DesperateProgrammer/DSiLanguagePacher) (working NAND writing code)
|
||||||
- [rvtr](https://github.com/rvtr): Adding support for installing dev titles
|
- [rvtr](https://github.com/rvtr): Adding support for installing TADs and dev titles
|
||||||
|
|||||||
@ -157,12 +157,7 @@ static void generateList(Menu* m)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 0 ||
|
if (strcasecmp(strrchr(ent->d_name, '.'), ".tad") == 0)
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".app") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0)
|
|
||||||
{
|
{
|
||||||
if (count < m->page * ITEMS_PER_PAGE)
|
if (count < m->page * ITEMS_PER_PAGE)
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "nand/ticket0.h"
|
#include "nand/ticket0.h"
|
||||||
#include "rom.h"
|
#include "rom.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "tad.h"
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -351,7 +352,7 @@ static void _createTicket(tDSiHeader *h, char* ticketPath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool install(char* fpath, bool systemTitle)
|
bool install(char* tadPath, bool systemTitle)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
@ -364,6 +365,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
|
|
||||||
//start installation
|
//start installation
|
||||||
clearScreen(&bottomScreen);
|
clearScreen(&bottomScreen);
|
||||||
|
char* fpath = openTad(tadPath);
|
||||||
|
|
||||||
tDSiHeader* h = getRomHeader(fpath);
|
tDSiHeader* h = getRomHeader(fpath);
|
||||||
|
|
||||||
@ -372,7 +374,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
iprintf("\x1B[31m"); //red
|
iprintf("\x1B[31m"); //red
|
||||||
iprintf("Error: ");
|
iprintf("Error: ");
|
||||||
iprintf("\x1B[33m"); //yellow
|
iprintf("\x1B[33m"); //yellow
|
||||||
iprintf("Could not open file.\n");
|
iprintf("Could not decrypt TAD.\n");
|
||||||
iprintf("\x1B[47m"); //white
|
iprintf("\x1B[47m"); //white
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -385,35 +387,25 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
|
|
||||||
//title id must be one of these
|
//title id must be one of these
|
||||||
if (h->tid_high == 0x00030004 || // DSiWare
|
if (h->tid_high == 0x00030004 || // DSiWare
|
||||||
h->tid_high == 0x00030005 || // "unimportant" system titles
|
h->tid_high == 0x00030005 || // "Unimportant" system titles
|
||||||
h->tid_high == 0x00030011 || // SRLs in the TWL SDK
|
h->tid_high == 0x0003000f || // Data titles
|
||||||
h->tid_high == 0x00030015 || // system titles
|
h->tid_high == 0x00030015 || // System titles
|
||||||
h->tid_high == 0x00030017) // Launcher
|
h->tid_high == 0x00030017) // Launcher
|
||||||
{}
|
{}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
iprintf("\x1B[31m"); //red
|
iprintf("\x1B[31m"); //red
|
||||||
iprintf("Error: ");
|
iprintf("TID Error: ");
|
||||||
iprintf("\x1B[33m"); //yellow
|
iprintf("\x1B[33m"); //yellow
|
||||||
iprintf("This is not a DSi rom.\n");
|
iprintf("Could not decrypt TAD.\n%s", fpath);
|
||||||
iprintf("\x1B[47m"); //white
|
iprintf("\x1B[47m"); //white
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
// I am going to remove patching because it results in bad TMDs that the launcher auto deletes.
|
||||||
//patch dev titles to system titles on SysNAND.
|
// Comment these back in if you really want, but know that 99% of dev apps will not install right with patching.
|
||||||
//
|
|
||||||
//software released through the TWL SDK usually comes as a TAD and an SRL
|
|
||||||
//things like NandFiler have a TAD with a TID of 0x00030015 and an SRL with 0x00030011
|
|
||||||
//the TAD is the installable version, so I'm assuming that 0x00030015 is what the console wants to see on NAND
|
|
||||||
//this changes the SRL TID accordingly
|
|
||||||
//not entirely sure why there's even any difference. I think the installed TAD and SRL the same as each other (minus the TID)
|
|
||||||
if(!sdnandMode && h->tid_high == 0x00030011)
|
|
||||||
{
|
|
||||||
h->tid_high = 0x00030015;
|
|
||||||
fixHeader = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//offer to patch system titles to normal DSiWare on SysNAND
|
//offer to patch system titles to normal DSiWare on SysNAND
|
||||||
|
/*
|
||||||
if(!sdnandMode && h->tid_high != 0x00030004 && h->tid_high != 0x00030017) //do not allow patching home menus to be normal DSiWare! This will trigger "ERROR! - 0x0000000000000008 HWINFO_SECURE" on prototype launchers. May also cause issues on the prod versions.
|
if(!sdnandMode && h->tid_high != 0x00030004 && h->tid_high != 0x00030017) //do not allow patching home menus to be normal DSiWare! This will trigger "ERROR! - 0x0000000000000008 HWINFO_SECURE" on prototype launchers. May also cause issues on the prod versions.
|
||||||
{
|
{
|
||||||
if(choiceBox("This is set as a system/dev\ntitle, would you like to patch\nit to be a normal DSiWare?\n\nThis is safer, but invalidates\nRSA checks and may not work.\n\nIf the title is homebrew this isstrongly recommended.") == YES)
|
if(choiceBox("This is set as a system/dev\ntitle, would you like to patch\nit to be a normal DSiWare?\n\nThis is safer, but invalidates\nRSA checks and may not work.\n\nIf the title is homebrew this isstrongly recommended.") == YES)
|
||||||
@ -422,8 +414,10 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
fixHeader = true;
|
fixHeader = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
//offer to patch home menus to be system titles on SysNAND
|
//offer to patch home menus to be system titles on SysNAND
|
||||||
|
/*
|
||||||
if(!sdnandMode && h->tid_high == 0x00030017)
|
if(!sdnandMode && h->tid_high == 0x00030017)
|
||||||
{
|
{
|
||||||
if(choiceBox("This title is a home menu.\nWould you like to patch it to bea system title?\n\nThis is safer and prevents your\nhome menu from being hidden.") == YES)
|
if(choiceBox("This title is a home menu.\nWould you like to patch it to bea system title?\n\nThis is safer and prevents your\nhome menu from being hidden.") == YES)
|
||||||
@ -432,6 +426,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
fixHeader = true;
|
fixHeader = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
//no system titles without Unlaunch
|
//no system titles without Unlaunch
|
||||||
if (!unlaunchFound && h->tid_high != 0x00030004)
|
if (!unlaunchFound && h->tid_high != 0x00030004)
|
||||||
@ -450,28 +445,17 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
u32 tidLow = (h->tid_low & 0xFFFFFF00);
|
u32 tidLow = (h->tid_low & 0xFFFFFF00);
|
||||||
if (!sdnandMode && (
|
if (!sdnandMode && (
|
||||||
(h->tid_high == 0x00030005 && (
|
(h->tid_high == 0x00030005 && (
|
||||||
tidLow == 0x484e4400 || // DS Download Play
|
|
||||||
tidLow == 0x484e4500 || // PictoChat
|
|
||||||
tidLow == 0x484e4900 || // Nintendo DSi Camera
|
tidLow == 0x484e4900 || // Nintendo DSi Camera
|
||||||
tidLow == 0x484e4a00 || // Nintendo Zone
|
tidLow == 0x484e4a00 || // Nintendo Zone
|
||||||
tidLow == 0x484e4b00 // Nintendo DSi Sound
|
tidLow == 0x484e4b00 // Nintendo DSi Sound
|
||||||
)) || (h->tid_high == 0x00030011 && (
|
|
||||||
tidLow == 0x30535500 || // Twl SystemUpdater
|
|
||||||
tidLow == 0x34544e00 || // TwlNmenu
|
|
||||||
tidLow == 0x54574c00 // TWL EVA
|
|
||||||
)) || (h->tid_high == 0x00030015 && (
|
)) || (h->tid_high == 0x00030015 && (
|
||||||
tidLow == 0x484e4200 || // System Settings
|
tidLow == 0x484e4200 || // System Settings
|
||||||
tidLow == 0x484e4600 || // Nintendo DSi Shop
|
tidLow == 0x484e4600 // Nintendo DSi Shop
|
||||||
tidLow == 0x34544e00 // TwlNmenu
|
|
||||||
)) || (h->tid_high == 0x00030017 && (
|
)) || (h->tid_high == 0x00030017 && (
|
||||||
tidLow == 0x484e4100 // Launcher
|
tidLow == 0x484e4100 // Launcher
|
||||||
))) && (
|
))) && (
|
||||||
(h->tid_low & 0xFF) == region || // Only blacklist console region, or the following programs that have all-region codes:
|
(h->tid_low & 0xFF) == region || // Only blacklist console region, or the following programs that have all-region codes:
|
||||||
h->tid_low == 0x484e4541 || // PictoChat
|
|
||||||
h->tid_low == 0x484e4441 || // Download Play
|
|
||||||
h->tid_low == 0x30535541 || // Twl SystemUpdater (iirc one version fits in NAND)
|
|
||||||
h->tid_low == 0x34544e41 || // TwlNmenu (blocking due to potential to uninstall system titles)
|
h->tid_low == 0x34544e41 || // TwlNmenu (blocking due to potential to uninstall system titles)
|
||||||
h->tid_low == 0x54574c41 || // TWL EVA
|
|
||||||
region == 0 //if the region check failed somehow, blacklist everything
|
region == 0 //if the region check failed somehow, blacklist everything
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
@ -495,6 +479,9 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
{
|
{
|
||||||
const char system[] = "\x1B[41mWARNING:\x1B[47m This is a system app,\ninstalling it is potentially\nmore risky than regular DSiWare.\n\x1B[33m";
|
const char system[] = "\x1B[41mWARNING:\x1B[47m This is a system app,\ninstalling it is potentially\nmore risky than regular DSiWare.\n\x1B[33m";
|
||||||
const char areYouSure[] = "Are you sure you want to install\n";
|
const char areYouSure[] = "Are you sure you want to install\n";
|
||||||
|
clearScreen(&topScreen); // Top screen breaks after this for some reason.
|
||||||
|
printTadInfo(tadPath);
|
||||||
|
clearScreen(&bottomScreen);
|
||||||
char* msg = (char*)malloc(strlen(system) + strlen(areYouSure) + strlen(fpath) + 2);
|
char* msg = (char*)malloc(strlen(system) + strlen(areYouSure) + strlen(fpath) + 2);
|
||||||
if (sdnandMode || h->tid_high == 0x00030004)
|
if (sdnandMode || h->tid_high == 0x00030004)
|
||||||
sprintf(msg, "%s%s?\n", areYouSure, fpath);
|
sprintf(msg, "%s%s?\n", areYouSure, fpath);
|
||||||
@ -556,6 +543,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
//system title patch
|
//system title patch
|
||||||
|
/*
|
||||||
if (systemTitle)
|
if (systemTitle)
|
||||||
{
|
{
|
||||||
iprintf("System Title Patch...");
|
iprintf("System Title Patch...");
|
||||||
@ -567,8 +555,10 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
|
|
||||||
fixHeader = true;
|
fixHeader = true;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
//check that there's space on nand
|
//check that there's space on nand
|
||||||
|
/*
|
||||||
if (!_checkDsiSpace(installSize, (h->tid_high != 0x00030004)))
|
if (!_checkDsiSpace(installSize, (h->tid_high != 0x00030004)))
|
||||||
{
|
{
|
||||||
if (sdnandMode && choicePrint("Install as system title?"))
|
if (sdnandMode && choicePrint("Install as system title?"))
|
||||||
@ -581,6 +571,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
//check for saves
|
//check for saves
|
||||||
char pubPath[PATH_MAX];
|
char pubPath[PATH_MAX];
|
||||||
@ -682,20 +673,29 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
|
|
||||||
//create 000000##.app
|
//create 000000##.app
|
||||||
{
|
{
|
||||||
iprintf("Creating 000000%02x.app...", appVersion);
|
// We must get the app name from the TMD (0x1E4-1E8).
|
||||||
|
// NTM/TMFH did it weirdly before by using a single byte at 0x1E7 called "appVersion"?
|
||||||
|
// Not sure how that even worked at all. The home menu deleted the incorrectly named apps
|
||||||
|
// and TwlNmenu showed them as being broken.
|
||||||
|
//
|
||||||
|
// This new code should always create valid titles.
|
||||||
|
FILE *tmd = fopen(tmdPath, "rb");
|
||||||
|
unsigned char appName[4];
|
||||||
|
fseek(tmd, 484, SEEK_SET);
|
||||||
|
fread(appName, 1, 4, tmd);
|
||||||
|
fclose(tmd);
|
||||||
|
|
||||||
|
iprintf("Creating %02x%02x%02x%02x.app...", appName[0], appName[1], appName[2], appName[3]);
|
||||||
swiWaitForVBlank();
|
swiWaitForVBlank();
|
||||||
|
|
||||||
char appPath[80];
|
char appPath[80];
|
||||||
sprintf(appPath, "%s/000000%02x.app", contentPath, appVersion);
|
sprintf(appPath, "%s/%02x%02x%02x%02x.app", contentPath, appName[0], appName[1], appName[2], appName[3]);
|
||||||
|
|
||||||
//copy nds file to app
|
//copy nds file to app
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
if (!romIsCia(fpath))
|
|
||||||
result = copyFile(fpath, appPath);
|
result = copyFile(fpath, appPath);
|
||||||
else
|
|
||||||
result = copyFilePart(fpath, 0x3900, fileSize, appPath);
|
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
@ -867,5 +867,11 @@ complete:
|
|||||||
if (!sdnandMode)
|
if (!sdnandMode)
|
||||||
nandio_lock_writing();
|
nandio_lock_writing();
|
||||||
|
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.tmd");
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.tik");
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl.enc");
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl");
|
||||||
|
rmdir("sd:/_nds/TADDeliveryTool/tmp");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -186,12 +186,7 @@ static void generateList(Menu* m)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 0 ||
|
if (strcasecmp(strrchr(ent->d_name, '.'), ".tad") == 0)
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".app") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 ||
|
|
||||||
strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0)
|
|
||||||
{
|
{
|
||||||
if (count < m->page * ITEMS_PER_PAGE)
|
if (count < m->page * ITEMS_PER_PAGE)
|
||||||
count += 1;
|
count += 1;
|
||||||
@ -237,7 +232,7 @@ static void printItem(Menu* m)
|
|||||||
if (m->items[m->cursor].directory)
|
if (m->items[m->cursor].directory)
|
||||||
clearScreen(&topScreen);
|
clearScreen(&topScreen);
|
||||||
else
|
else
|
||||||
printRomInfo(m->items[m->cursor].value);
|
printTadInfo(m->items[m->cursor].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int subMenu()
|
static int subMenu()
|
||||||
|
|||||||
@ -11,8 +11,6 @@ bool programEnd = false;
|
|||||||
bool sdnandMode = true;
|
bool sdnandMode = true;
|
||||||
bool unlaunchFound = false;
|
bool unlaunchFound = false;
|
||||||
bool unlaunchPatches = false;
|
bool unlaunchPatches = false;
|
||||||
bool devkpFound = false;
|
|
||||||
bool launcherDSiFound = false;
|
|
||||||
bool arm7Exiting = false;
|
bool arm7Exiting = false;
|
||||||
bool charging = false;
|
bool charging = false;
|
||||||
u8 batteryLevel = 0;
|
u8 batteryLevel = 0;
|
||||||
@ -25,11 +23,11 @@ enum {
|
|||||||
MAIN_MENU_MODE,
|
MAIN_MENU_MODE,
|
||||||
MAIN_MENU_INSTALL,
|
MAIN_MENU_INSTALL,
|
||||||
MAIN_MENU_TITLES,
|
MAIN_MENU_TITLES,
|
||||||
MAIN_MENU_BACKUP,
|
//MAIN_MENU_BACKUP,
|
||||||
MAIN_MENU_TEST,
|
MAIN_MENU_TEST,
|
||||||
MAIN_MENU_FIX,
|
MAIN_MENU_FIX,
|
||||||
MAIN_MENU_DATA_MANAGEMENT,
|
//MAIN_MENU_DATA_MANAGEMENT,
|
||||||
MAIN_MENU_LANGUAGE_PATCHER,
|
//MAIN_MENU_LANGUAGE_PATCHER,
|
||||||
MAIN_MENU_EXIT
|
MAIN_MENU_EXIT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,32 +55,30 @@ static int _mainMenu(int cursor)
|
|||||||
//top screen
|
//top screen
|
||||||
clearScreen(&topScreen);
|
clearScreen(&topScreen);
|
||||||
|
|
||||||
iprintf("\t\tNAND Title Manager\n");
|
iprintf("\t\tTAD Delivery Tool\n");
|
||||||
iprintf("\t\t\tmodified from\n");
|
iprintf("\t\t\tmodified from\n");
|
||||||
iprintf("\tTitle Manager for HiyaCFW\n");
|
iprintf("\tTitle Manager for HiyaCFW\n");
|
||||||
|
iprintf("\tand Nand Title Manager\n");
|
||||||
iprintf("\nversion %s\n", VERSION);
|
iprintf("\nversion %s\n", VERSION);
|
||||||
iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n");
|
iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n");
|
||||||
iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n");
|
iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n");
|
||||||
iprintf("\n\n \x1B[46mgithub.com/Epicpkmn11/NTM/wiki\x1B[47m\n");
|
iprintf("\n\x1B[46mgithub.com/rvtr/TDT\x1B[47m\n");
|
||||||
iprintf("\x1b[22;0HJeff - 2018-2019");
|
iprintf("\x1b[21;0HJeff - 2018-2019");
|
||||||
iprintf("\x1b[23;0HPk11 - 2022-2023");
|
iprintf("\x1b[22;0HPk11 - 2022-2023");
|
||||||
|
iprintf("\x1b[23;0Hrmc - 2024-2024");
|
||||||
|
|
||||||
//menu
|
//menu
|
||||||
Menu* m = newMenu();
|
Menu* m = newMenu();
|
||||||
setMenuHeader(m, "MAIN MENU");
|
setMenuHeader(m, "MAIN MENU");
|
||||||
|
|
||||||
char modeStr[32], datamanStr[32], launcherStr[32];
|
char modeStr[32];
|
||||||
sprintf(modeStr, "Mode: %s", sdnandMode ? "SDNAND" : "\x1B[41mSysNAND\x1B[47m");
|
sprintf(modeStr, "Mode: %s", sdnandMode ? "SDNAND" : "\x1B[41mSysNAND\x1B[47m");
|
||||||
sprintf(datamanStr, "\x1B[%02omEnable Data Management", devkpFound ? 037 : 047);
|
|
||||||
sprintf(launcherStr, "\x1B[%02omUninstall region mod", launcherDSiFound ? 047 : 037);
|
|
||||||
addMenuItem(m, modeStr, NULL, 0);
|
addMenuItem(m, modeStr, NULL, 0);
|
||||||
addMenuItem(m, "Install", NULL, 0);
|
addMenuItem(m, "Install", NULL, 0);
|
||||||
addMenuItem(m, "Titles", NULL, 0);
|
addMenuItem(m, "Titles", NULL, 0);
|
||||||
addMenuItem(m, "Restore", NULL, 0);
|
//addMenuItem(m, "Restore", NULL, 0);
|
||||||
addMenuItem(m, "Test", NULL, 0);
|
addMenuItem(m, "Test", NULL, 0);
|
||||||
addMenuItem(m, "Fix FAT copy mismatch", NULL, 0);
|
addMenuItem(m, "Fix FAT copy mismatch", NULL, 0);
|
||||||
addMenuItem(m, datamanStr, NULL, 0);
|
|
||||||
addMenuItem(m, launcherStr, NULL, 0);
|
|
||||||
addMenuItem(m, "\x1B[47mExit", NULL, 0);
|
addMenuItem(m, "\x1B[47mExit", NULL, 0);
|
||||||
|
|
||||||
m->cursor = cursor;
|
m->cursor = cursor;
|
||||||
@ -168,8 +164,10 @@ int main(int argc, char **argv)
|
|||||||
char path[64];
|
char path[64];
|
||||||
sprintf(path, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid);
|
sprintf(path, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid);
|
||||||
unsigned long long tmdSize = getFileSizePath(path);
|
unsigned long long tmdSize = getFileSizePath(path);
|
||||||
if (tmdSize > 520)
|
//if (tmdSize > 520)
|
||||||
|
// unlaunchFound = true;
|
||||||
unlaunchFound = true;
|
unlaunchFound = true;
|
||||||
|
// unlaunch is always true just for testing.
|
||||||
|
|
||||||
//check if launcher patches are enabled
|
//check if launcher patches are enabled
|
||||||
const static u32 tidValues[][2] = {
|
const static u32 tidValues[][2] = {
|
||||||
@ -205,19 +203,13 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
else if (!unlaunchPatches)
|
else if (!unlaunchPatches)
|
||||||
{
|
{
|
||||||
messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need to\nprovide TMD files or reinstall.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m");
|
messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need these\nto boot any TADs.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//check for dev.kp (Data Management visible)
|
|
||||||
devkpFound = (access("sd:/sys/dev.kp", F_OK) == 0);
|
|
||||||
|
|
||||||
//check for launcher.dsi (Language patcher)
|
|
||||||
launcherDSiFound = (access("nand:/launcher.dsi", F_OK) == 0);
|
|
||||||
|
|
||||||
messageBox("\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n\nIf you have not yet done so,\nyou should make a NAND backup.");
|
messageBox("\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n\nIf you have not yet done so,\nyou should make a NAND backup.");
|
||||||
|
|
||||||
messageBox("If you are following a video\nguide, please stop.\n\nVideo guides for console moddingare often outdated or straight\nup incorrect to begin with.\n\nThe recommended guide for\nmodding your DSi is:\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m\n\nFor more information on using\nNTM, see the official wiki:\n\n\x1B[46mhttps://github.com/Epicpkmn11/\n\t\t\t\t\t\t\t\tNTM/wiki\x1B[47m");
|
messageBox("If you are following a video\nguide, please stop.\n\nVideo guides for console moddingare often outdated or straight\nup incorrect to begin with.\n\nThe recommended guide for\nmodding your DSi is:\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m\n\nFor more information on using\nTDT, see the official repo:\n\n\x1B[46mhttps://github.com/rvtr/TDT\x1B[47m");
|
||||||
//main menu
|
//main menu
|
||||||
int cursor = 0;
|
int cursor = 0;
|
||||||
|
|
||||||
@ -229,7 +221,6 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
case MAIN_MENU_MODE:
|
case MAIN_MENU_MODE:
|
||||||
sdnandMode = !sdnandMode;
|
sdnandMode = !sdnandMode;
|
||||||
devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MAIN_MENU_INSTALL:
|
case MAIN_MENU_INSTALL:
|
||||||
@ -239,11 +230,11 @@ int main(int argc, char **argv)
|
|||||||
case MAIN_MENU_TITLES:
|
case MAIN_MENU_TITLES:
|
||||||
titleMenu();
|
titleMenu();
|
||||||
break;
|
break;
|
||||||
|
/*
|
||||||
case MAIN_MENU_BACKUP:
|
case MAIN_MENU_BACKUP:
|
||||||
backupMenu();
|
backupMenu();
|
||||||
break;
|
break;
|
||||||
|
*/
|
||||||
case MAIN_MENU_TEST:
|
case MAIN_MENU_TEST:
|
||||||
testMenu();
|
testMenu();
|
||||||
break;
|
break;
|
||||||
@ -257,35 +248,6 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MAIN_MENU_DATA_MANAGEMENT:
|
|
||||||
if (!devkpFound && (choiceBox("Make Data Management visible\nin System Settings?") == YES) && (sdnandMode || nandio_unlock_writing()))
|
|
||||||
{
|
|
||||||
//ensure sys folder exists
|
|
||||||
if(access(sdnandMode ? "sd:/sys" : "nand:/sys", F_OK) != 0)
|
|
||||||
mkdir(sdnandMode ? "sd:/sys" : "nand:/sys", 0777);
|
|
||||||
|
|
||||||
//create empty file
|
|
||||||
FILE *file = fopen(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", "wb");
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
if(!sdnandMode)
|
|
||||||
nandio_lock_writing();
|
|
||||||
devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0);
|
|
||||||
messageBox("Data Management is now visible\nin System Settings.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MAIN_MENU_LANGUAGE_PATCHER:
|
|
||||||
if (launcherDSiFound && (choiceBox("Uninstall the language patched\nDSi Menu? (launcher.dsi)") == YES) && nandio_unlock_writing())
|
|
||||||
{
|
|
||||||
//delete launcher.dsi
|
|
||||||
remove("nand:/launcher.dsi");
|
|
||||||
|
|
||||||
nandio_lock_writing();
|
|
||||||
launcherDSiFound = (access("nand:/launcher.dsi", F_OK) == 0);
|
|
||||||
messageBox("The language patched DSi Menu\nhas been removed.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MAIN_MENU_EXIT:
|
case MAIN_MENU_EXIT:
|
||||||
programEnd = true;
|
programEnd = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "rom.h"
|
#include "rom.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "tad.h"
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <nds.h>
|
#include <nds.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
@ -19,11 +20,7 @@ tDSiHeader* getRomHeader(char const* fpath)
|
|||||||
|
|
||||||
if (h)
|
if (h)
|
||||||
{
|
{
|
||||||
if (romIsCia(fpath))
|
|
||||||
fseek(f, 0x3900, SEEK_SET);
|
|
||||||
else
|
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
fread(h, sizeof(tDSiHeader), 1, f);
|
fread(h, sizeof(tDSiHeader), 1, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,11 +47,7 @@ tNDSBanner* getRomBanner(char const* fpath)
|
|||||||
|
|
||||||
if (b)
|
if (b)
|
||||||
{
|
{
|
||||||
if (romIsCia(fpath))
|
|
||||||
fseek(f, 0x3900, SEEK_SET);
|
|
||||||
else
|
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
fseek(f, h->ndshdr.bannerOffset, SEEK_CUR);
|
fseek(f, h->ndshdr.bannerOffset, SEEK_CUR);
|
||||||
fread(b, sizeof(tNDSBanner), 1, f);
|
fread(b, sizeof(tNDSBanner), 1, f);
|
||||||
}
|
}
|
||||||
@ -146,12 +139,6 @@ void printRomInfo(char const* fpath)
|
|||||||
tDSiHeader* h = getRomHeader(fpath);
|
tDSiHeader* h = getRomHeader(fpath);
|
||||||
tNDSBanner* b = getRomBanner(fpath);
|
tNDSBanner* b = getRomBanner(fpath);
|
||||||
|
|
||||||
if (!isDsiHeader(h))
|
|
||||||
{
|
|
||||||
iprintf("Could not read dsi header.\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!b)
|
if (!b)
|
||||||
{
|
{
|
||||||
iprintf("Could not read banner.\n");
|
iprintf("Could not read banner.\n");
|
||||||
@ -246,7 +233,6 @@ void printRomInfo(char const* fpath)
|
|||||||
if (access(temp, F_OK) == 0)
|
if (access(temp, F_OK) == 0)
|
||||||
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == 0x4000) ? 047 : 041, strrchr(temp, '/') + 1);
|
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == 0x4000) ? 047 : 041, strrchr(temp, '/') + 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
free(b);
|
free(b);
|
||||||
free(h);
|
free(h);
|
||||||
@ -260,37 +246,11 @@ unsigned long long getRomSize(char const* fpath)
|
|||||||
FILE* f = fopen(fpath, "rb");
|
FILE* f = fopen(fpath, "rb");
|
||||||
|
|
||||||
if (f)
|
if (f)
|
||||||
{
|
|
||||||
//cia
|
|
||||||
if (romIsCia(fpath))
|
|
||||||
{
|
|
||||||
unsigned char bytes[4] = { 0 };
|
|
||||||
fseek(f, 0x38D0, SEEK_SET);
|
|
||||||
fread(bytes, 4, 1, f);
|
|
||||||
size = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
size = ftell(f);
|
size = ftell(f);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool romIsCia(char const* fpath)
|
|
||||||
{
|
|
||||||
if (!fpath) return false;
|
|
||||||
return (strstr(fpath, ".cia") != NULL || strstr(fpath, ".CIA") != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDsiHeader(tDSiHeader* h)
|
|
||||||
{
|
|
||||||
if (!h) return false;
|
|
||||||
|
|
||||||
u16 crc16 = swiCRC16(0xFFFF, h, 0x15E);
|
|
||||||
|
|
||||||
return h->ndshdr.headerCRC16 == crc16;
|
|
||||||
}
|
|
||||||
@ -17,7 +17,6 @@ void printRomInfo(char const* fpath);
|
|||||||
|
|
||||||
unsigned long long getRomSize(char const* fpath);
|
unsigned long long getRomSize(char const* fpath);
|
||||||
|
|
||||||
bool romIsCia(char const* fpath);
|
bool romIsSrl(char const* fpath);
|
||||||
bool isDsiHeader(tDSiHeader* h);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -440,6 +440,7 @@ int getMenuSlotsFree()
|
|||||||
const char* dirs[] = {
|
const char* dirs[] = {
|
||||||
"00030004",
|
"00030004",
|
||||||
"00030005",
|
"00030005",
|
||||||
|
"0003000f",
|
||||||
"00030015",
|
"00030015",
|
||||||
"00030017"
|
"00030017"
|
||||||
};
|
};
|
||||||
|
|||||||
372
arm9/src/tad.c
Normal file
372
arm9/src/tad.c
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
/*
|
||||||
|
A lot of this is heavily "inspired" by remaketad.pl in TwlIPL (/tools/bin/remaketad.pl)
|
||||||
|
That script was made to decrypt + unpack a dev TAD and and rebuild for SystemUpdaters.
|
||||||
|
|
||||||
|
https://github.com/rvtr/TwlIPL/blob/trunk/tools/bin/remaketad.pl
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tad.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "rom.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "nand/twltool/dsi.h"
|
||||||
|
#include <nds/ndstypes.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
The common keys for decrypting TADs.
|
||||||
|
|
||||||
|
DEV: Used in most TADs. Anything created with the standard maketad will be dev.
|
||||||
|
PROD: Used in some TADs for factory tools like PRE_IMPORT and IMPORT. Really uncommon. I only know of 24 prod TADs
|
||||||
|
to have ever been found, and 19 of those haven't ever been released (pleeeeeaaaaase release IMPORT soon).
|
||||||
|
All retail signed and can't be created with any leaked maketads.
|
||||||
|
DEBUGGER: Used in TwlSystemUpdater TADs. Created with maketad_updater.
|
||||||
|
|
||||||
|
If for whatever reason you want to make TADs, see here:
|
||||||
|
https://randommeaninglesscharacters.com/dsidev/man/maketad.html
|
||||||
|
*/
|
||||||
|
const unsigned char devKey[] = {
|
||||||
|
0xA1, 0x60, 0x4A, 0x6A, 0x71, 0x23, 0xB5, 0x29,
|
||||||
|
0xAE, 0x8B, 0xEC, 0x32, 0xC8, 0x16, 0xFC, 0xAA
|
||||||
|
};
|
||||||
|
const unsigned char prodKey[] = {
|
||||||
|
0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A,
|
||||||
|
0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61
|
||||||
|
};
|
||||||
|
const unsigned char debuggerKey[] = {
|
||||||
|
0xA2, 0xFD, 0xDD, 0xF2 ,0xE4, 0x23, 0x57, 0x4A,
|
||||||
|
0xE7, 0xED, 0x86, 0x57, 0xB5, 0xAB, 0x19, 0xD3
|
||||||
|
};
|
||||||
|
// Content IV be fine as a hardcoded string. Content IV is based off of the content index. (index # with zerobyte padding)
|
||||||
|
// All TADs I've seen only ever had a single content. It might be a good idea to add something down the line in case a
|
||||||
|
// weird TAD pops up, but until then this should do.
|
||||||
|
unsigned char content_iv[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
typedef struct {
|
||||||
|
uint32_t hdrSize;
|
||||||
|
uint16_t tadType;
|
||||||
|
uint16_t tadVersion;
|
||||||
|
uint32_t certSize;
|
||||||
|
uint32_t crlSize;
|
||||||
|
uint32_t ticketSize;
|
||||||
|
uint32_t tmdSize;
|
||||||
|
uint32_t srlSize;
|
||||||
|
uint32_t metaSize;
|
||||||
|
} Header;
|
||||||
|
typedef struct {
|
||||||
|
uint32_t hdrOffset;
|
||||||
|
uint32_t certOffset;
|
||||||
|
uint32_t crlOffset;
|
||||||
|
uint32_t ticketOffset;
|
||||||
|
uint32_t tmdOffset;
|
||||||
|
uint32_t srlOffset;
|
||||||
|
uint32_t metaOffset;
|
||||||
|
} Tad;
|
||||||
|
unsigned char srlCompany[2];
|
||||||
|
unsigned char srlTidLow[4];
|
||||||
|
unsigned char srlTidHigh[4];
|
||||||
|
unsigned char srlVerLow[1];
|
||||||
|
unsigned char srlVerHigh[1];
|
||||||
|
|
||||||
|
uint32_t swap_endian_u32(uint32_t x) {
|
||||||
|
return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t swap_endian_u16(uint16_t x) {
|
||||||
|
return (x >> 8) | (x << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t round_up( const u32 v, const u32 align ) {
|
||||||
|
u32 r = ((v + align - 1) / align) * align;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrypt_cbc(const unsigned char* key, const unsigned char* iv, const unsigned char* encryptedData, size_t dataSize, size_t keySize, unsigned char* decryptedData) {
|
||||||
|
aes_context ctx;
|
||||||
|
aes_setkey_dec(&ctx, key, 128);
|
||||||
|
aes_crypt_cbc(&ctx, AES_DECRYPT, dataSize, iv, encryptedData, decryptedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int openTad(char const* src) {
|
||||||
|
if (!src) return 1;
|
||||||
|
|
||||||
|
FILE *file = fopen(src, "rb");
|
||||||
|
if (file == NULL) {
|
||||||
|
printf("ERROR: fopen()");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// idk how to create folders recursively
|
||||||
|
mkdir("sd:/_nds", 0777);
|
||||||
|
mkdir("sd:/_nds/TADDeliveryTool", 0777);
|
||||||
|
mkdir("sd:/_nds/TADDeliveryTool/tmp", 0777);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Please excuse my terrible copy paste coding. I do not know C and I'm translating from other languages
|
||||||
|
that I don't know (python, perl).
|
||||||
|
|
||||||
|
Anyways, the code below is determining the file offsets and sizes within the TAD.
|
||||||
|
This is done using the 32 byte header.
|
||||||
|
|
||||||
|
Example header from "KART_K04.tad"
|
||||||
|
|
||||||
|
00000020 49730000 00000E80 00000000
|
||||||
|
000002A4 00000208 000DFC00 00000000
|
||||||
|
|
||||||
|
Breaking it down...
|
||||||
|
|
||||||
|
Hex | Dec | Meaning
|
||||||
|
-----------+--------+------------
|
||||||
|
0x00000020 | 32 | Header size
|
||||||
|
0x4973 | Is | TAD type
|
||||||
|
0x0000 | 0 | TAD version
|
||||||
|
0x00000E80 | 3712 | Cert size
|
||||||
|
0x00000000 | 0 | Crl size
|
||||||
|
0x000002A4 | 676 | Ticket size
|
||||||
|
0x00000208 | 520 | TMD size
|
||||||
|
0x000DFC00 | 916480 | SRL size
|
||||||
|
0x00000000 | 0 | Meta size
|
||||||
|
|
||||||
|
Gee, looks awfully like a WAD header, doesn't it? Turns out TADs are just renamed WADs. Not even changed one bit.
|
||||||
|
There's literally a commit replacing every instance of WAD with TAD in TwlIPL...
|
||||||
|
https://github.com/rvtr/TwlIPL/commit/baca65d35d5d62d815c88e6374b895d5b0755277
|
||||||
|
*/
|
||||||
|
|
||||||
|
Header header;
|
||||||
|
fread(&header, sizeof(Header), 1, file);
|
||||||
|
iprintf("Parsing TAD header...\n");
|
||||||
|
Tad tad;
|
||||||
|
tad.hdrOffset = 0;
|
||||||
|
// All offsets in the TAD are aligned to 64 bytes.
|
||||||
|
tad.certOffset = round_up(swap_endian_u32(header.hdrSize), 64);
|
||||||
|
tad.crlOffset = round_up(tad.certOffset + swap_endian_u32(header.certSize), 64);
|
||||||
|
tad.ticketOffset = round_up(tad.crlOffset + swap_endian_u32(header.crlSize), 64);
|
||||||
|
tad.tmdOffset = round_up(tad.ticketOffset + swap_endian_u32(header.ticketSize), 64);
|
||||||
|
tad.srlOffset = round_up(tad.tmdOffset + swap_endian_u32(header.tmdSize), 64);
|
||||||
|
tad.metaOffset = round_up(tad.srlOffset + swap_endian_u32(header.srlSize), 64);
|
||||||
|
// TODO: Make sure offset calculation and alignment is correct by comparing that to total size
|
||||||
|
//iprintf(" hdrSize: %lu\n", swap_endian_u32(header.hdrSize));
|
||||||
|
//iprintf(" hdrOffset: %lu\n", tad.hdrOffset);
|
||||||
|
|
||||||
|
// Commenting this block out makes the TAD decrypt improperly. Truly a programming moment.
|
||||||
|
// 18803 = "Is". This is the standard TAD type.
|
||||||
|
if (swap_endian_u16(header.tadType) == 18803) {
|
||||||
|
//iprintf(" tadType: 'Is'\n");
|
||||||
|
} else {
|
||||||
|
iprintf(" tadType: UNKNOWN\nERROR: unexpected TAD type\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//iprintf(" tadVersion: %u\n", swap_endian_u16(header.tadVersion));
|
||||||
|
//iprintf(" certSize: %lu\n", swap_endian_u32(header.certSize));
|
||||||
|
//iprintf(" certOffset: %lu\n", tad.certOffset);
|
||||||
|
//iprintf(" crlSize: %lu\n", swap_endian_u32(header.crlSize));
|
||||||
|
//iprintf(" crlOffset: %lu\n", tad.crlOffset);
|
||||||
|
//iprintf(" ticketSize: %lu\n", swap_endian_u32(header.ticketSize));
|
||||||
|
//iprintf(" ticketOffset: %lu\n", tad.ticketOffset);
|
||||||
|
//iprintf(" tmdSize: %lu\n", swap_endian_u32(header.tmdSize));
|
||||||
|
//iprintf(" tmdOffset: %lu\n", tad.tmdOffset);
|
||||||
|
//iprintf(" srlSize: %lu\n", swap_endian_u32(header.srlSize));
|
||||||
|
//iprintf(" srlOffset: %lu\n", tad.srlOffset);
|
||||||
|
//iprintf(" metaSize: %lu\n", swap_endian_u32(header.metaSize));
|
||||||
|
//iprintf(" metaOffset: %lu\n", tad.metaOffset);
|
||||||
|
|
||||||
|
fseek(file, tad.tmdOffset+396, SEEK_SET);
|
||||||
|
fread(srlTidHigh, 1, 4, file);
|
||||||
|
fread(srlTidLow, 1, 4, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copy the contents of the TAD to the SD card.
|
||||||
|
|
||||||
|
For installing we only need the TMD, ticket, and SRL (obviously lol).
|
||||||
|
|
||||||
|
We can skip the cert since that already exists in NAND, and using the TAD's cert could introduce problems like
|
||||||
|
trying to sign files for dev on a prod console.
|
||||||
|
*/
|
||||||
|
|
||||||
|
iprintf("Copying output files...\n");
|
||||||
|
// Sorry for copy pasting, I'll make this a routine later
|
||||||
|
iprintf(" Copying TMD...\n");
|
||||||
|
copyFilePart(src, tad.tmdOffset, swap_endian_u32(header.tmdSize), "sd:/_nds/TADDeliveryTool/tmp/temp.tmd");
|
||||||
|
|
||||||
|
iprintf(" Copying ticket...\n");
|
||||||
|
copyFilePart(src, tad.ticketOffset, swap_endian_u32(header.ticketSize), "sd:/_nds/TADDeliveryTool/tmp/temp.tik");
|
||||||
|
|
||||||
|
iprintf(" Copying SRL...\n");
|
||||||
|
copyFilePart(src, tad.srlOffset, swap_endian_u32(header.srlSize), "sd:/_nds/TADDeliveryTool/tmp/temp.srl.enc");
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the title key + IV from the ticket.
|
||||||
|
*/
|
||||||
|
|
||||||
|
iprintf("Decrypting SRL...\n");
|
||||||
|
//iprintf(" Finding title key...\n");
|
||||||
|
FILE *ticket = fopen("sd:/_nds/TADDeliveryTool/tmp/temp.tik", "rb");
|
||||||
|
unsigned char title_key_enc[16];
|
||||||
|
fseek(ticket, 447, SEEK_SET);
|
||||||
|
fread(title_key_enc, 1, 16, ticket);
|
||||||
|
//iprintf(" Title key found!\n");
|
||||||
|
/* for (int i = 0; i < 16; i++) {iprintf("%02X", title_key_enc[i]);} */
|
||||||
|
iprintf("\n");
|
||||||
|
//iprintf(" Finding title key IV...\n");
|
||||||
|
unsigned char title_key_iv[16];
|
||||||
|
fseek(ticket, 476, SEEK_SET);
|
||||||
|
fread(title_key_iv, 1, 8, ticket);
|
||||||
|
memset(title_key_iv + 8, 0, 8);
|
||||||
|
//iprintf(" Title key IV found!\n");
|
||||||
|
/* for (int i = 0; i < 16; i++) {iprintf("%02X", title_key_iv[i]);} */
|
||||||
|
iprintf("\n");
|
||||||
|
fclose(ticket);
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is SRL decryption (AES-CBC).
|
||||||
|
|
||||||
|
Common key + title key IV to decrypt title key, title key + content IV to decrypt content
|
||||||
|
|
||||||
|
We have to try each possible common key until we find one that works. I don't know a better way to do this
|
||||||
|
(nothing in the TAD would specify the key needed) so we'll try keys in the order of which ones are more common:
|
||||||
|
|
||||||
|
DEV --> DEBUGGER --> PROD
|
||||||
|
|
||||||
|
We check for only zerobytes at 0x15-1B to see if the SRL is decrypted properly. (should always be zerobytes)
|
||||||
|
|
||||||
|
https://problemkaputt.de/gbatek.htm#dscartridgeheader
|
||||||
|
https://gist.github.com/rvtr/f1069530129b7a57967e3fc4b30866b4#file-decrypt_tad-py-L84
|
||||||
|
*/
|
||||||
|
bool keyFail;
|
||||||
|
iprintf("Trying dev common key...\n");
|
||||||
|
keyFail = decryptTad(devKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow);
|
||||||
|
|
||||||
|
if (keyFail == TRUE) {
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl");
|
||||||
|
iprintf("Key fail!\n\nTrying debugger common key...\n");
|
||||||
|
keyFail = decryptTad(debuggerKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow);
|
||||||
|
}
|
||||||
|
if (keyFail == TRUE) {
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl");
|
||||||
|
iprintf("Key fail!\n\nTrying prod common key...\n");
|
||||||
|
keyFail = decryptTad(prodKey, title_key_iv, title_key_enc, content_iv, swap_endian_u32(header.srlSize), srlTidLow);
|
||||||
|
}
|
||||||
|
if (keyFail == TRUE) {
|
||||||
|
remove("sd:/_nds/TADDeliveryTool/tmp/temp.srl");
|
||||||
|
iprintf("All keys failed!\n");
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
return "sd:/_nds/TADDeliveryTool/tmp/temp.srl";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decryptTad(unsigned char* commonKey,
|
||||||
|
unsigned char* title_key_iv,
|
||||||
|
unsigned char* title_key_enc,
|
||||||
|
unsigned char* content_iv,
|
||||||
|
int srlSize,
|
||||||
|
unsigned char* srlTidLow) {
|
||||||
|
unsigned char title_key_dec[16];
|
||||||
|
unsigned char title_key_iv_bak[16];
|
||||||
|
unsigned char content_iv_bak[16];
|
||||||
|
unsigned char srl_buffer_enc[16];
|
||||||
|
unsigned char srl_buffer_dec[16];
|
||||||
|
|
||||||
|
// Backup IVs because PolarSSL will overwrite it
|
||||||
|
memcpy( title_key_iv_bak, title_key_iv, 16 );
|
||||||
|
memcpy( content_iv_bak, content_iv, 16 );
|
||||||
|
|
||||||
|
FILE *srlFile_enc = fopen("sd:/_nds/TADDeliveryTool/tmp/temp.srl.enc", "rb");
|
||||||
|
fseek(srlFile_enc, 0, SEEK_SET);
|
||||||
|
FILE *srlFile_dec = fopen("sd:/_nds/TADDeliveryTool/tmp/temp.srl", "wb");
|
||||||
|
fseek(srlFile_dec, 0, SEEK_SET);
|
||||||
|
|
||||||
|
iprintf(" Decrypting SRL in chunks..\n");
|
||||||
|
decrypt_cbc(commonKey, title_key_iv, title_key_enc, 16, 16, title_key_dec);
|
||||||
|
int i=0;
|
||||||
|
bool keyFail = FALSE;
|
||||||
|
while (i < srlSize && keyFail == FALSE) {
|
||||||
|
fread(srl_buffer_enc, 1, 16, srlFile_enc);
|
||||||
|
decrypt_cbc(title_key_dec, content_iv, srl_buffer_enc, 16, 16, srl_buffer_dec);
|
||||||
|
fwrite(srl_buffer_dec, 1, 16, srlFile_dec);
|
||||||
|
printProgressBar( ((float)i / (float)srlSize) );
|
||||||
|
if (i == 560) {
|
||||||
|
if (srl_buffer_dec[3] != srlTidLow[0] ||
|
||||||
|
srl_buffer_dec[2] != srlTidLow[1] ||
|
||||||
|
srl_buffer_dec[1] != srlTidLow[2] ||
|
||||||
|
srl_buffer_dec[0] != srlTidLow[3] ) {
|
||||||
|
keyFail = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i=i+16;
|
||||||
|
}
|
||||||
|
fclose(srlFile_dec);
|
||||||
|
fclose(srlFile_enc);
|
||||||
|
// Restore IVs
|
||||||
|
memcpy( title_key_iv, title_key_iv_bak, 16 );
|
||||||
|
memcpy( content_iv, content_iv_bak, 16 );
|
||||||
|
return keyFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printTadInfo(char const* fpath)
|
||||||
|
{
|
||||||
|
clearScreen(&topScreen);
|
||||||
|
if (!fpath) return;
|
||||||
|
|
||||||
|
FILE *file = fopen(fpath, "rb");
|
||||||
|
Header header;
|
||||||
|
fread(&header, sizeof(Header), 1, file);
|
||||||
|
Tad tad;
|
||||||
|
tad.hdrOffset = 0;
|
||||||
|
// All offsets in the TAD are aligned to 64 bytes.
|
||||||
|
tad.certOffset = round_up(swap_endian_u32(header.hdrSize), 64);
|
||||||
|
tad.crlOffset = round_up(tad.certOffset + swap_endian_u32(header.certSize), 64);
|
||||||
|
tad.ticketOffset = round_up(tad.crlOffset + swap_endian_u32(header.crlSize), 64);
|
||||||
|
tad.tmdOffset = round_up(tad.ticketOffset + swap_endian_u32(header.ticketSize), 64);
|
||||||
|
tad.srlOffset = round_up(tad.tmdOffset + swap_endian_u32(header.tmdSize), 64);
|
||||||
|
tad.metaOffset = round_up(tad.srlOffset + swap_endian_u32(header.srlSize), 64);
|
||||||
|
// Get info from TMD.
|
||||||
|
fseek(file, tad.tmdOffset+396, SEEK_SET);
|
||||||
|
fread(srlTidHigh, 1, 4, file);
|
||||||
|
fread(srlTidLow, 1, 4, file);
|
||||||
|
fseek(file, tad.tmdOffset+408, SEEK_SET);
|
||||||
|
fread(srlCompany, 1, 2, file);
|
||||||
|
fseek(file, tad.tmdOffset+476, SEEK_SET);
|
||||||
|
fread(srlVerHigh, 1, 1, file);
|
||||||
|
fread(srlVerLow, 1, 1, file);
|
||||||
|
|
||||||
|
// I am so sorry for this mess.
|
||||||
|
iprintf("\nSize:\n ");
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
unsigned long long romSize = ftell(file);
|
||||||
|
iprintf("\x1B[42m"); //green
|
||||||
|
printBytes(romSize);
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
iprintf(" (\x1B[42m%ld blocks\x1B[47m)\n", ((swap_endian_u32(header.srlSize) / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK);
|
||||||
|
|
||||||
|
iprintf("Game Code:\n ");
|
||||||
|
iprintf("\x1B[42m"); //green
|
||||||
|
for (int i = 0; i < 4; i++) {printf("%c", srlTidLow[i]);}
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
|
||||||
|
iprintf("\nGame Version:\n \x1B[42m%d.%d\x1B[47m (NUS: \x1B[42mv%d\x1B[47m)\n", (int)srlVerHigh[0] * 256, (int)srlVerLow[0], ((int)srlVerHigh[0] * 256) + (int)srlVerLow[0]);
|
||||||
|
|
||||||
|
iprintf("Company Code:\n \x1B[42m%c%c\x1B[47m(\x1B[42m%02x%02x\x1B[47m)\n", srlCompany[0], srlCompany[1], srlCompany[0], srlCompany[1]);
|
||||||
|
|
||||||
|
// Print program type based on TID high?
|
||||||
|
iprintf("Title ID: \n ");
|
||||||
|
iprintf("\x1B[42m"); //green
|
||||||
|
for (int i = 0; i < 4; i++) {printf("%02x", srlTidHigh[i]);}
|
||||||
|
iprintf(" ");
|
||||||
|
for (int i = 0; i < 4; i++) {printf("%02x", srlTidLow[i]);}
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
|
||||||
|
//print full file path
|
||||||
|
iprintf("\n\n%s\n", fpath);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
15
arm9/src/tad.h
Normal file
15
arm9/src/tad.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef TAD_H
|
||||||
|
#define TAD_H
|
||||||
|
|
||||||
|
#include <nds/ndstypes.h>
|
||||||
|
#include <nds/memory.h>
|
||||||
|
|
||||||
|
int openTad(char const* src);
|
||||||
|
bool decryptTad(unsigned char* commonKey,
|
||||||
|
unsigned char* title_key_iv,
|
||||||
|
unsigned char* title_key_enc,
|
||||||
|
unsigned char* content_iv,
|
||||||
|
int srlSize,
|
||||||
|
unsigned char* srlTidLow);
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -92,6 +92,7 @@ static void generateList(Menu* m)
|
|||||||
const char* dirs[] = {
|
const char* dirs[] = {
|
||||||
"00030004",
|
"00030004",
|
||||||
"00030005",
|
"00030005",
|
||||||
|
//"0003000f",
|
||||||
"00030015",
|
"00030015",
|
||||||
"00030017"
|
"00030017"
|
||||||
};
|
};
|
||||||
@ -100,17 +101,12 @@ static void generateList(Menu* m)
|
|||||||
{ // 00030004
|
{ // 00030004
|
||||||
NULL //nothing blacklisted
|
NULL //nothing blacklisted
|
||||||
},
|
},
|
||||||
{ // 00030005
|
{ // 0003000f
|
||||||
"484e44", // DS Download Play
|
"484e43", // WiFi Firmware
|
||||||
"484e45", // PictoChat
|
|
||||||
"484e49", // Nintendo DSi Camera
|
|
||||||
"484e4a", // Nintendo Zone
|
|
||||||
"484e4b", // Nintendo DSi Sound
|
|
||||||
NULL
|
NULL
|
||||||
},
|
},
|
||||||
{ // 00030015
|
{ // 00030015
|
||||||
"484e42", // System Settings
|
"484e42", // System Settings
|
||||||
"484e46", // Nintendo DSi Shop
|
|
||||||
NULL
|
NULL
|
||||||
},
|
},
|
||||||
{ // 00030017
|
{ // 00030017
|
||||||
@ -159,8 +155,7 @@ static void generateList(Menu* m)
|
|||||||
blacklisted = true;
|
blacklisted = true;
|
||||||
|
|
||||||
// also blacklist specific all-region titles
|
// also blacklist specific all-region titles
|
||||||
if ((strcmp("484e4441", ent->d_name) == 0) || // Download Play
|
if ((strcmp("484e4341", ent->d_name) == 0) || // WiFi Firmware
|
||||||
(strcmp("484e4541", ent->d_name) == 0) || // PictoChat
|
|
||||||
(strcmp("34544e41", ent->d_name) == 0)) // TwlNmenu
|
(strcmp("34544e41", ent->d_name) == 0)) // TwlNmenu
|
||||||
blacklisted = true;
|
blacklisted = true;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
icon.bmp
BIN
icon.bmp
Binary file not shown.
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 2.1 KiB |
Loading…
Reference in New Issue
Block a user