Misc stuff and added docker to readme

This commit is contained in:
Lillian Skinner 2024-11-18 18:03:09 -05:00
parent cf2444a1ed
commit de4da857db
No known key found for this signature in database
10 changed files with 301 additions and 35 deletions

View File

@ -1,22 +1,44 @@
# ![app icon](icon.bmp) TwlNandTool # ![app icon](icon.bmp) TwlNandTool
This is the best DSi NAND repair tool out there, offering features such as: TwlNandTool is **the** best DSi NAND repair tool out there, offering features such as:
- Fixing MBR - Fixing MBR
- Formatting FAT partitions - Formatting FAT partitions
- Fixing Stage2 (NandFirm) - Recovering deleted HWInfo Secure
- Recovering deleted HWInfo secure - Installing Stage2 (NandFirm)
- Installing new system files
- Installing minimal firmware titles and unlaunch - Installing minimal firmware titles and unlaunch
- Testing firmware and DSiWare for corruption
- Lots of useful NAND diagnostic info - Lots of useful NAND diagnostic info
- And more documented [here!](USAGE.md)
Why do these matter? You can create a new working NAND from scratch! No backups needed (but still recommended)! Why do these matter? No matter how badly messed up your DSi is- even if your NAND is zerofilled- TwlNandTool can put it back in a working state. You don't even need a NAND backup (though it is still **HIGHLY RECOMMENDED**)! Best of all, it's compatible with retail and development DSis both!
![FileSystem menu](.github/filesystem.png)![NAND CID info](.github/cidinfo.png) ![FileSystem menu](.github/filesystem.png)![NAND CID info](.github/cidinfo.png)
## Building
devkitPro decided to release libnds v2.0 and break... everything. Features I need like lower level NAND editing do not seem to work in the new calico, so I'm evily forcing you to use an offensive pre-v2.0 docker image that'll destroy the community or something. I think that's what devkitPro would say. Anyways:
Download the outdated docker image!
```sh
docker image pull devkitpro/devkitarm:20241104
```
Run the outdated docker image!
```sh
docker run --rm -v /home/rvtr/Desktop/TwlNandTool:/work \
-it --entrypoint bash devkitpro/devkitarm:20241104
```
This will open a shell. Simply do this:
```sh
cd work
make
```
I have made [a backup](https://twlsdk.randommeaninglesscharacters.com/dkp_docker_20241104.tar) in case the docker image ever gets pulled.
## Contributing ## Contributing
I'm basing a lot of this off of my private DSi [NAND archive](https://nands.randommeaninglesscharacters.com/directory.html) and a lot of DSi [factory tools](https://randommeaninglesscharacters.com/dsidev/twl_eva.html). If you have any of the above, *especially a __bricked__ DSi*, please [contact me](https://randommeaninglesscharacters.com/contact.html). Having these as a reference is extremely helpful for covering more edge cases. I'm basing a lot of this off of my private DSi [NAND archive](https://nands.randommeaninglesscharacters.com/directory.html) and a lot of DSi [factory tools](https://randommeaninglesscharacters.com/dsidev/twl_eva.html). If you have any of the above, *especially a __bricked__ DSi*, please [contact me](https://randommeaninglesscharacters.com/contact.html). Having these as a reference is extremely helpful for covering more edge cases.
## Notes ## Notes
- I am including my own hostile and outdated fork of libfat. This is to block `nand_Startup()` during `fatMount()`. Without this having a NAND re-mount would run `nand_Startup()` more than once and break every NAND R/W function until reboot... - I am including my own hostile and outdated fork of libfat (devkitPro's enemy speedrun any %). This is to block `nand_Startup()` during `fatMount()`. Without this having a NAND re-mount would run `nand_Startup()` more than once and break every NAND R/W function until reboot...
- I do not use the release NandFirm/stage2/bootloader (v2435-8325). Instead I use newer NandFirms as listed below. These NandFirms are able to run unlaunch, however they will stop the installer from working ("unknown bootcode version"). Unlaunch installs carry a brick risk by sometimes erasing the Launcher TMD, so this will somewhat forcefully encourage users to move to a [safer installer](https://github.com/edo9300/unlaunch-installer). Normally I'm against intentionally breaking things but this will prevent future bricks. - I do not use the release NandFirm/stage2/bootloader (v2435-8325). Instead I use newer NandFirms as listed below. These NandFirms are able to run unlaunch, however they will stop the installer from working ("unknown bootcode version"). Unlaunch installs carry a brick risk by sometimes erasing the Launcher TMD, so this will somewhat forcefully encourage users to move to a [safer installer](https://github.com/edo9300/unlaunch-installer). Normally I'm against intentionally breaking things but this will prevent future bricks.
- v2265-9336 (prod) - v2265-9336 (prod)
- v2725-9336 (dev) - v2725-9336 (dev)

View File

@ -20,7 +20,7 @@ I will add a friendly built in "tutorial" at some point, but until then please d
- [Import NandFirm (SDMC)](#--import-nandfirm-(sdmc)) - [Import NandFirm (SDMC)](#--import-nandfirm-(sdmc))
- [Sys File Menu](#sys-file-menu) - [Sys File Menu](#sys-file-menu)
- [Find HWINFO\_S.dat](#--find-hwinfo_sdat) - [Find HWINFO\_S.dat](#--find-hwinfo_sdat)
- [Find HWINFO\_S.dat (deep)](#--find-hwinfo_sdat-(deep)) - [Find HWINFO\_S.dat (deep)](#--find-hwinfo_sdat-deep)
- [Init HWINFO\_N.dat](#--init-hwinfo_ndat) - [Init HWINFO\_N.dat](#--init-hwinfo_ndat)
- [Init HWINFO\_S.dat](#--init-hwinfo_sdat) - [Init HWINFO\_S.dat](#--init-hwinfo_sdat)
- [Init cert.sys](#--init-certsys) - [Init cert.sys](#--init-certsys)
@ -55,9 +55,9 @@ Install the standard NandFirm. The versions I include are slightly updated for r
- `v2265-9336` (prod) - `v2265-9336` (prod)
- `v2725-9336` (dev) - `v2725-9336` (dev)
### - Import NandFirm (SDMC) ### - Import NandFirm (SDMC)
Install the [SDMC Launcher](https://randommeaninglesscharacters.com/dsidev/sdmc_launcher.html) NandFirm. It is completely different from the standard NandFirm, and it will leave the home menu inaccessible. This is only for extreme edge cases and should be avoided. Install the [SDMC Launcher](https://randommeaninglesscharacters.com/dsidev/sdmc_launcher.html) NandFirm. It is completely different from the standard NandFirm, and it will leave the firmware inaccessible. This is only for extreme edge cases where the filesystem cannot be repaired, and so SDMC Launcher should be avoided.
**DO NOT INSTALL THIS UNLESS OTHERWISE TOLD.** **DO NOT INSTALL SDMC LAUNCHER UNLESS OTHERWISE TOLD.**
## Sys File Menu ## Sys File Menu
These functions deal with system files that are required for the DSi to boot. Much of this is focused around HWInfo Secure, the file that sets your region and serial number. It is impossible to recreate due to signing, and without it you will be permanently forced to use unlaunch. These functions deal with system files that are required for the DSi to boot. Much of this is focused around HWInfo Secure, the file that sets your region and serial number. It is impossible to recreate due to signing, and without it you will be permanently forced to use unlaunch.

View File

@ -21,14 +21,27 @@
TODO: TODO:
- LESS NAND WRITES!!!!! - LESS NAND WRITES!!!!!
- Write good NAND read routine - Write good NAND read routine
- Detect debugger vs dev - Detect debugger vs dev (less important currently)
- Recover HWInfo - Recover HWInfo byte by byte
- Back up/restore screen memory - HWInfo verify
- NandFirm hash checking <-- very very very important! - HWInfo sign (can wait for a very long time)
- System transfer (way later on)
- Why doesn't unmounting NAND get reflected in the file test? - Why doesn't unmounting NAND get reflected in the file test?
- NandFirm hash checking <-- very very very important!
- Wipe TWLCFG, launcher saves, etc
- product.log reading and writing
- TAD stuff
- Title verification (exception for HNAG)
- PUB/PRV formatting and testing (cringe)
- Getting title lists
- Cut over 39 titles
- Unlaunch stuff
- Repair bricked TMD (depends on title verification)
- Install unlaunch through the safe method
COSMETIC OR OPTIONAL:
- Back up/restore screen memory
- System transfer (way later on)
*/ */
extern bool nand_Startup(); extern bool nand_Startup();
extern bool sdio_Startup(); extern bool sdio_Startup();
@ -181,6 +194,15 @@ int main(int argc, char **argv)
mountNitroFS(); mountNitroFS();
agingMode = false; agingMode = false;
//
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_START || keysDown() & KEY_SELECT) {
} else {
debug1();
}
clearScreen(cSUB); clearScreen(cSUB);
clearScreen(cMAIN); clearScreen(cMAIN);
@ -296,6 +318,9 @@ int debug3(void) {
iprintf("\n>> NAND AGING tester "); iprintf("\n>> NAND AGING tester ");
iprintf("\n--------------------------------"); iprintf("\n--------------------------------");
if (success == true && !cpuPrintInfo()) {
success = false;
}
if (success == true && !nandPrintInfo()) { if (success == true && !nandPrintInfo()) {
success = false; success = false;
} }
@ -326,6 +351,15 @@ int debug3(void) {
if (success == true && !filetestMain()) { if (success == true && !filetestMain()) {
success = false; success = false;
} }
if (success == true && !makeSystemFolders()) {
success = false;
}
if (success == true && !makeCertChain()) {
success = false;
}
if (success == true && !makeFontTable()) {
success = false;
}
if (success == true && !unmountMain()) { if (success == true && !unmountMain()) {
success = false; success = false;
} }

View File

@ -344,12 +344,15 @@ void wait(int ticks){
while(ticks--)swiWaitForVBlank(); while(ticks--)swiWaitForVBlank();
} }
int loadingCounter = 0;
char downloadPlayLoading(int number) { char downloadPlayLoading(int number) {
char pictoload[] = {(char)142, (char)143, (char)144, (char)145, (char)146, (char)147, (char)148, (char)149}; char pictoload[] = {(char)143, (char)144, (char)145, (char)146, (char)147, (char)148, (char)149, (char)150};
static int counter = 0; if (loadingCounter >= 7) {
loadingCounter = 0;
counter = (counter % 7) + 1; } else {
return pictoload[counter]; loadingCounter++;
}
return pictoload[loadingCounter];
} }
void exitFunction() { void exitFunction() {

View File

@ -39,6 +39,6 @@ void printMenu(Menu* m, int level);
bool moveCursor(Menu* m); bool moveCursor(Menu* m);
char downloadPlayLoading(int number); char downloadPlayLoading(int percent);
void exitFunction(); void exitFunction();
#endif #endif

View File

@ -491,6 +491,59 @@ bool good_nandio_write_file(int inputAddress, int inputLength, FILE *fp, bool cr
return true; return true;
} }
bool good_nandio_read(int inputAddress, int inputLength, u8 *buffer, bool crypt) {
nandWritten = true;
if (inputLength > BUFFER_SIZE) {
return false;
}
// Sorry lol I just don't want to deal with sector calculation
int byteOffset = inputAddress % SECTOR_SIZE;
int sectorNum = inputAddress / SECTOR_SIZE;
int byteEndOffset = (inputAddress+inputLength) % SECTOR_SIZE;
int sectorEndNum = (inputAddress+inputLength) / SECTOR_SIZE;
int i;
if (inputLength <= SECTOR_SIZE) {
// Handle a single sector write differently since it is unpredictable
nand_ReadSectors(sectorNum, 1, sector_buf);
if (crypt == true) {
dsi_nand_crypt(sector_buf, sector_buf, sectorNum * SECTOR_SIZE / AES_BLOCK_SIZE, SECTOR_SIZE / AES_BLOCK_SIZE);
}
memcpy(buffer, sector_buf + byteOffset, inputLength);
} else {
iprintf("\n ");
for (i = sectorNum; i < sectorEndNum + 1;) {
char currentPicto = downloadPlayLoading(i);
if (i % (sectorEndNum / 15) == 0) {
printf("\b%c", currentPicto);
}
// Back up sector
nand_ReadSectors(i, 1, sector_buf);
if (crypt == true) {
// offset * SECTOR_SIZE / AES_BLOCK_SIZE
dsi_crypt_init((const u8*)consoleIDfixed, (const u8*)0x2FFD7BC, is3DS);
dsi_nand_crypt(sector_buf, sector_buf, i * SECTOR_SIZE / AES_BLOCK_SIZE, SECTOR_SIZE / AES_BLOCK_SIZE);
// Okay so the below one encrypted every other sector, failing the 1st, 3rd, 5th, etc.
//dsi_nand_crypt(sector_buf, sector_buf, inputAddress * SECTOR_SIZE / AES_BLOCK_SIZE, SECTOR_SIZE / AES_BLOCK_SIZE);
}
if (i == sectorNum) {
// Handle the first sector differently since we'll only be writing a partial amount of data
memcpy(buffer, sector_buf + byteOffset, SECTOR_SIZE - byteOffset);
} else if (i == sectorEndNum) {
// Handle the last sector differently since we'll only be writing a partial amount of data
memcpy(buffer + (((i - sectorNum) * SECTOR_SIZE) - byteOffset), sector_buf, byteEndOffset);
} else {
// Handle the middle sectors the same because they'll always be the full sector
memcpy(buffer + (((i - sectorNum) * SECTOR_SIZE) - byteOffset), sector_buf, SECTOR_SIZE);
}
i++;
// Do some check to make sure it is not outside of NAND range.
}
iprintf("\b\x1B[1A");
}
return true;
}
bool nandio_clear_status() bool nandio_clear_status()
{ {
return true; return true;

View File

@ -19,7 +19,7 @@ extern "C" {
/************************ Constants / Defines *********************************/ /************************ Constants / Defines *********************************/
#define SECTOR_SIZE 0x200 #define SECTOR_SIZE 0x200
#define BUFFER_SIZE 0x1000 #define BUFFER_SIZE 0x5000
#define MBR_PARTITIONS 4 #define MBR_PARTITIONS 4
#define MBR_BOOTSTRAP_SIZE (SECTOR_SIZE - (2 + MBR_PARTITIONS * 16)) #define MBR_BOOTSTRAP_SIZE (SECTOR_SIZE - (2 + MBR_PARTITIONS * 16))

View File

@ -14,6 +14,7 @@
#include "../message.h" #include "../message.h"
#include "../main.h" #include "../main.h"
#include "../video.h" #include "../video.h"
#include "../storage.h"
static size_t i; static size_t i;
@ -21,6 +22,7 @@ enum {
SYSFILEMENU_RECOVER, SYSFILEMENU_RECOVER,
SYSFILEMENU_RECOVER2, SYSFILEMENU_RECOVER2,
SYSFILEMENU_NULL, SYSFILEMENU_NULL,
SYSFILEMENU_INIT_FOLDER,
SYSFILEMENU_INIT_N, SYSFILEMENU_INIT_N,
SYSFILEMENU_INIT_S, SYSFILEMENU_INIT_S,
SYSFILEMENU_INIT_CERT, SYSFILEMENU_INIT_CERT,
@ -101,11 +103,12 @@ static int _sysfileMenu(int cursor)
addMenuItem(m, "Find HWINFO_S.dat", NULL, 0, "Search for HWInfo in TWL_MAIN\n then in common offsets.\n\n In most cases this will be\n enough to recover HWInfo."); addMenuItem(m, "Find HWINFO_S.dat", NULL, 0, "Search for HWInfo in TWL_MAIN\n then in common offsets.\n\n In most cases this will be\n enough to recover HWInfo.");
addMenuItem(m, "Find HWINFO_S.dat (deep)", NULL, 0, "Do a byte by byte search for\n HWInfo in the NAND.\n\n This is rarely required and\n should be thought of as a last\n resort only."); addMenuItem(m, "Find HWINFO_S.dat (deep)", NULL, 0, "Do a byte by byte search for\n HWInfo in the NAND.\n\n This is rarely required and\n should be thought of as a last\n resort only.");
addMenuItem(m, "------------------------", NULL, 0, "Leave the NandFirm menu."); addMenuItem(m, "------------------------", NULL, 0, "");
addMenuItem(m, "Init HWINFO_N.dat", NULL, 0, "Create HWINFO_N.dat.\n\n This file is required to boot."); addMenuItem(m, "Write NAND folders", NULL, 0, "Create folders that store\n system files.");
addMenuItem(m, "Init HWINFO_S.dat", NULL, 0, "Recover and make HWINFO_S.dat.\n\n If HWINFO_S.dat cannot be\n recovered, it cannot be\n recreated and unlaunch will be\n required to boot the launcher."); addMenuItem(m, "Write HWINFO_N.dat", NULL, 0, "Create HWINFO_N.dat.\n\n This file is required to boot.");
addMenuItem(m, "Init cert.sys", NULL, 0, "Create the certificate chain."); addMenuItem(m, "Write HWINFO_S.dat", NULL, 0, "Recover and make HWINFO_S.dat.\n\n If HWINFO_S.dat cannot be\n recovered, it cannot be\n recreated and unlaunch will be\n required to boot the launcher.");
addMenuItem(m, "Init TWLFontTable", NULL, 0, "Create the font data."); addMenuItem(m, "Write cert.sys", NULL, 0, "Create the certificate chain.");
addMenuItem(m, "Write TWLFontTable", NULL, 0, "Create the font data.");
m->cursor = cursor; m->cursor = cursor;
@ -155,6 +158,10 @@ int sysfileMain(void)
case SYSFILEMENU_NULL: case SYSFILEMENU_NULL:
break; break;
case SYSFILEMENU_INIT_FOLDER:
makeSystemFolders();
break;
case SYSFILEMENU_INIT_S: case SYSFILEMENU_INIT_S:
break; break;
@ -162,9 +169,11 @@ int sysfileMain(void)
break; break;
case SYSFILEMENU_INIT_CERT: case SYSFILEMENU_INIT_CERT:
makeCertChain();
break; break;
case SYSFILEMENU_INIT_FONT: case SYSFILEMENU_INIT_FONT:
makeFontTable();
break; break;
} }
} }
@ -174,3 +183,137 @@ int sysfileMain(void)
return 0; return 0;
} }
bool makeSystemFolders(void) {
success = true;
clearScreen(cSUB);
iprintf("\n>> Make NAND folders ");
iprintf("\n--------------------------------");
if (nandMounted == true) {
printf("\nMaking /sys/...");
mkdir("nand:/sys", 0777);
printf("\nMaking /sys/log/...");
mkdir("nand:/sys/log", 0777);
printf("\nMaking /shared1/...");
mkdir("nand:/shared1", 0777);
printf("\nMaking /shared2/...");
mkdir("nand:/shared2", 0777);
printf("\nMaking /ticket/...");
mkdir("nand:/ticket", 0777);
printf("\nMaking /title/...");
mkdir("nand:/title", 0777);
printf("\nMaking /tmp/...");
mkdir("nand:/tmp", 0777);
printf("\nDone!");
} else {
success = false;
iprintf("\nTWL_MAIN is not mounted!");
}
exitFunction();
return success;
}
bool makeCertChain(void) {
success = true;
clearScreen(cSUB);
iprintf("\n>> Make cert.sys ");
iprintf("\n--------------------------------");
if (nandMounted == true) {
printf("\nRemoving old cert.sys...");
char nitro_path[100];
snprintf(nitro_path, 100, "nitro:/import/%s/cert.sys", consoleSignName);
remove("nand:/sys/cert.sys");
printf("\nWriting cert.sys...");
if (copyFile(nitro_path, "nand:/sys/cert.sys") != 0) {
success = false;
} else {
printf("\nDone!");
}
} else {
success = false;
iprintf("\nTWL_MAIN is not mounted!");
}
exitFunction();
return success;
}
bool makeFontTable(void) {
success = true;
clearScreen(cSUB);
iprintf("\n>> Make TWLFontTable ");
iprintf("\n--------------------------------");
if (nandMounted == true) {
// I need to do region checking (world/korea/china)
printf("\nRemoving old TWLFontTable...");
char nitro_path[100];
snprintf(nitro_path, 100, "nitro:/import/common/TWLFontTable.dat");
remove("nand:/sys/TWLFontTable.dat");
printf("\nWriting TWLFontTable (world)...");
if (copyFile(nitro_path, "nand:/sys/TWLFontTable.dat") != 0) {
success = false;
} else {
printf("\nDone!");
}
} else {
success = false;
iprintf("\nTWL_MAIN is not mounted!");
}
exitFunction();
return success;
}
/*
bool recoverHWInfo(void) {
success = true;
bool hwinfofound = false;
clearScreen(cSUB);
iprintf("\n>> Recover HWInfo Secure ");
iprintf("\n--------------------------------");
if (nandMounted == true) {
printf("\nChecking mounted TWL_MAIN...");
if (fileExists("nand:/sys/HWINFO_S.dat")) {
printf("\nFound HWInfo.");
if (copyFile("nand:/sys/HWINFO_S.dat", "sd:/HWINFO_S_BACKUP.dat") != 0) {
success = false;
printf("\nCouldn't copy HWInfo!");
} else {
printf("\nCopied okay.");
// Some verification here
hwinfofound = true;
}
} else {
printf("\nCouldn't find HWInfo!");
}
} else {
iprintf("\nChecking common offsets...");
nandioread(offset + tidOffset, 16)
if (first 3 bytes == ANH)
nandioread(offset + tidOffset + 0x3CB, 16)
if (nandioread == nandioread)
// Verify
fopen fwrite fclose
}
exitFunction();
return success;
}
*/

View File

@ -15,7 +15,10 @@
void wait(int ticks); void wait(int ticks);
int sysfileMain(void); int sysfileMain();
bool recoverHWInfoShallow(void); bool recoverHWInfo();
bool recoverHWInfoDeep(void); bool recoverHWInfoDeep();
bool makeSystemFolders();
bool makeCertChain();
bool makeFontTable();

View File

@ -117,7 +117,7 @@ int copyFilePart(char const* src, u32 offset, u32 size, char const* dst)
{ {
fseek(fin, offset, SEEK_SET); fseek(fin, offset, SEEK_SET);
consoleSet(cMAIN); iprintf("\n ");
int bytesRead; int bytesRead;
unsigned long long totalBytesRead = 0; unsigned long long totalBytesRead = 0;
@ -125,8 +125,11 @@ int copyFilePart(char const* src, u32 offset, u32 size, char const* dst)
#define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds. #define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds.
char* buffer = (char*)malloc(BUFF_SIZE); char* buffer = (char*)malloc(BUFF_SIZE);
int i = 0;
while (!programEnd) while (!programEnd)
{ {
unsigned int toRead = BUFF_SIZE; unsigned int toRead = BUFF_SIZE;
if (size - totalBytesRead < BUFF_SIZE) if (size - totalBytesRead < BUFF_SIZE)
toRead = size - totalBytesRead; toRead = size - totalBytesRead;
@ -135,15 +138,20 @@ int copyFilePart(char const* src, u32 offset, u32 size, char const* dst)
fwrite(buffer, bytesRead, 1, fout); fwrite(buffer, bytesRead, 1, fout);
totalBytesRead += bytesRead; totalBytesRead += bytesRead;
printProgressBar( ((float)totalBytesRead / (float)size) ); //printProgressBar( ((float)totalBytesRead / (float)size) );
if (i >= 50) {
char currentPicto = downloadPlayLoading(69);
printf("\b%c", currentPicto);
i = 0;
} else {
i++;
}
if (bytesRead != BUFF_SIZE) if (bytesRead != BUFF_SIZE)
break; break;
} }
clearProgressBar(); iprintf("\b\x1B[1A");
consoleSet(cSUB); //clearProgressBar();
free(buffer); free(buffer);
} }