Remove not needed NTM files

This commit is contained in:
Edoardo Lolletti 2024-04-25 17:11:14 +02:00
parent c00569d5c5
commit 664e7a2090
19 changed files with 29 additions and 3338 deletions

View File

@ -1,275 +0,0 @@
#include "install.h"
#include "main.h"
#include "menu.h"
#include "rom.h"
#include "storage.h"
#include "message.h"
#include "nand/nandio.h"
#include <dirent.h>
#include <sys/stat.h>
enum {
BACKUP_MENU_RESTORE,
BACKUP_MENU_DELETE,
BACKUP_MENU_BACK
};
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static bool delete(Menu* m);
void backupMenu()
{
clearScreen(&topScreen);
Menu* m = newMenu();
setMenuHeader(m, "BACKUP MENU");
generateList(m);
//no files found
if (m->itemCount <= 0)
{
messageBox("\x1B[33mNo backups found.\n\x1B[47m");
}
else
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
if (keysDown() & KEY_B || m->itemCount <= 0)
break;
else if (keysDown() & KEY_A)
{
switch (subMenu())
{
case BACKUP_MENU_RESTORE:
install(m->items[m->cursor].value, false);
break;
case BACKUP_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Restore", NULL, 0);
addMenuItem(m, "Delete", NULL, 0);
addMenuItem(m, "Back - [B]", NULL, 0);
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static void generateList(Menu* m)
{
if (!m) return;
//reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
struct dirent* ent;
DIR* dir = opendir(BACKUP_PATH);
if (dir)
{
int count = 0;
while ( (ent = readdir(dir)) && !done)
{
if (ent->d_name[0] == '.')
continue;
if (ent->d_type == DT_DIR)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", BACKUP_PATH, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 1);
}
}
}
else
{
if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 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)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", BACKUP_PATH, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 0);
free(fpath);
}
}
}
}
}
}
closedir(dir);
sortMenuItems(m);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
if (m->itemCount <= 0) return;
if (m->items[m->cursor].directory)
clearScreen(&topScreen);
else
printRomInfo(m->items[m->cursor].value);
}
static bool delete(Menu* m)
{
if (!m) return false;
char* label = m->items[m->cursor].label;
char* fpath = m->items[m->cursor].value;
bool result = false;
bool choice = NO;
{
const char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(label) + 2);
sprintf(msg, "%s%s?", str, label);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("\x1B[31mFailed to delete backup.\n\x1B[47m");
}
else
{
if (access(fpath, F_OK) != 0)
{
messageBox("\x1B[31mFailed to delete backup.\n\x1B[47m");
}
else
{
clearScreen(&bottomScreen);
//app
remove(fpath);
//tmd
strcpy(strrchr(fpath, '.'), ".tmd");
remove(fpath);
//public save
strcpy(strrchr(fpath, '.'), ".pub");
remove(fpath);
//private save
strcpy(strrchr(fpath, '.'), ".prv");
remove(fpath);
//banner save
strcpy(strrchr(fpath, '.'), ".bnr");
remove(fpath);
result = true;
messagePrint("\x1B[42m\nBackup deleted.\n\x1B[47m");
}
}
}
return result;
}

View File

@ -1,871 +0,0 @@
#include "install.h"
#include "sav.h"
#include "main.h"
#include "message.h"
#include "maketmd.h"
#include "nand/crypto.h"
#include "nand/nandio.h"
#include "nand/ticket0.h"
#include "rom.h"
#include "storage.h"
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
static bool _titleIsUsed(tDSiHeader* h)
{
if (!h) return false;
char path[64];
sprintf(path, "%s:/title/%08x/%08x/", sdnandMode ? "sd" : "nand", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
return dirExists(path);
}
//patch homebrew roms if gameCode is #### or null
static bool _patchGameCode(tDSiHeader* h)
{
if (!h) return false;
if ((strcmp(h->ndshdr.gameCode, "####") == 0 && h->tid_low == 0x23232323) || (!*h->ndshdr.gameCode && h->tid_low == 0))
{
iprintf("Fixing Game Code...");
swiWaitForVBlank();
//set as standard app
h->tid_high = 0x00030004;
do {
do {
//generate a random game code
for (int i = 0; i < 4; i++)
h->ndshdr.gameCode[i] = 'A' + (rand() % 26);
}
while (h->ndshdr.gameCode[0] == 'A'); //first letter shouldn't be A
//correct title id
h->tid_low = ( (h->ndshdr.gameCode[0] << 24) | (h->ndshdr.gameCode[1] << 16) | (h->ndshdr.gameCode[2] << 8) | h->ndshdr.gameCode[3] );
}
while (_titleIsUsed(h));
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
return true;
}
return false;
}
static bool _iqueHack(tDSiHeader* h)
{
if (!h) return false;
if (h->ndshdr.reserved1[8] == 0x80)
{
iprintf("iQue Hack...");
h->ndshdr.reserved1[8] = 0x00;
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
return true;
}
return false;
}
static unsigned long long _getSaveDataSize(tDSiHeader* h)
{
unsigned long long size = 0;
if (h)
{
size += h->public_sav_size;
size += h->private_sav_size;
//banner.sav
if (h->appflags & 0x4)
size += 0x4000;
}
return size;
}
static bool _checkSdSpace(unsigned long long size)
{
iprintf("Enough room on SD card?...");
swiWaitForVBlank();
if (getSDCardFree() < size)
{
iprintf("\x1B[31m"); //red
iprintf("No\n");
iprintf("\x1B[47m"); //white
return false;
}
iprintf("\x1B[42m"); //green
iprintf("Yes\n");
iprintf("\x1B[47m"); //white
return true;
}
static bool _checkDsiSpace(unsigned long long size, bool systemApp)
{
iprintf("Enough room on DSi?...");
swiWaitForVBlank();
//ensure there's at least 1 MiB free, to leave margin for error
if (((systemApp ? getDsiRealFree() : getDsiFree()) < size) || (((systemApp ? getDsiRealFree() : getDsiFree()) - size) < (1 << 20)))
{
iprintf("\x1B[31m"); //red
iprintf("No\n");
iprintf("\x1B[47m"); //white
return false;
}
iprintf("\x1B[42m"); //green
iprintf("Yes\n");
iprintf("\x1B[47m"); //white
return true;
}
static bool _openMenuSlot()
{
iprintf("Open DSi menu slot?...");
swiWaitForVBlank();
if (getMenuSlotsFree() <= 0)
{
iprintf("\x1B[31m"); //red
iprintf("No\n");
iprintf("\x1B[47m"); //white
return false;
}
iprintf("\x1B[42m"); //green
iprintf("Yes\n");
iprintf("\x1B[47m"); //white
return true;
}
static void _createPublicSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->public_sav_size > 0)
{
iprintf("Creating public.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
char* publicPath = (char*)malloc(strlen(dataPath) + strlen("/public.sav") + 1);
sprintf(publicPath, "%s/public.sav", dataPath);
FILE* f = fopen(publicPath, "wb");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, h->public_sav_size-1, SEEK_SET);
fputc(0, f);
initFatHeader(f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
free(publicPath);
}
}
}
static void _createPrivateSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->private_sav_size > 0)
{
iprintf("Creating private.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
char* privatePath = (char*)malloc(strlen(dataPath) + strlen("/private.sav") + 1);
sprintf(privatePath, "%s/private.sav", dataPath);
FILE* f = fopen(privatePath, "wb");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, h->private_sav_size-1, SEEK_SET);
fputc(0, f);
initFatHeader(f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
free(privatePath);
}
}
}
static void _createBannerSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->appflags & 0x4)
{
iprintf("Creating banner.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
char* bannerPath = (char*)malloc(strlen(dataPath) + strlen("/banner.sav") + 1);
sprintf(bannerPath, "%s/banner.sav", dataPath);
FILE* f = fopen(bannerPath, "wb");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, 0x4000 - 1, SEEK_SET);
fputc(0, f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
free(bannerPath);
}
}
}
static void _createTicket(tDSiHeader *h, char* ticketPath)
{
if (!h) return;
iprintf("Forging ticket...");
swiWaitForVBlank();
if (!ticketPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
const u32 encryptedSize = sizeof(ticket_v0_t) + 0x20;
u8 *buffer = (u8*)memalign(4, encryptedSize); //memalign might be needed for encryption, but not sure
memset(buffer, 0, encryptedSize);
ticket_v0_t *ticket = (ticket_v0_t*)buffer;
ticket->sig_type[0] = 0x00;
ticket->sig_type[1] = 0x01;
ticket->sig_type[2] = 0x00;
ticket->sig_type[3] = 0x01;
strcpy(ticket->issuer, "Root-CA00000001-XS00000006");
PUT_UINT32_BE(h->tid_high, ticket->title_id, 0);
PUT_UINT32_BE(h->tid_low, ticket->title_id, 4);
memset(ticket->content_access_permissions, 0xFF, 0x20);
// Encrypt
if (dsi_es_block_crypt(buffer, encryptedSize, ENCRYPT) != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
free(buffer);
return;
}
FILE *file = fopen(ticketPath, "wb");
if (!file)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
free(buffer);
return;
}
if (fwrite(buffer, 1, encryptedSize, file) != encryptedSize)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
free(buffer);
fclose(file);
}
}
bool install(char* fpath, bool systemTitle)
{
bool result = false;
//check battery level
while (batteryLevel < 7 && !charging)
{
if (choiceBox("\x1B[47mBattery is too low!\nPlease plug in the console.\n\nContinue?") == NO)
return false;
}
//start installation
clearScreen(&bottomScreen);
tDSiHeader* h = getRomHeader(fpath);
if (!h)
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("Could not open file.\n");
iprintf("\x1B[47m"); //white
goto error;
}
else
{
bool fixHeader = false;
if (_patchGameCode(h))
fixHeader = true;
//title id must be one of these
if (h->tid_high == 0x00030004 || // DSiWare
h->tid_high == 0x00030005 || // "unimportant" system titles
h->tid_high == 0x00030011 || // SRLs in the TWL SDK
h->tid_high == 0x00030015 || // system titles
h->tid_high == 0x00030017) // Launcher
{}
else
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("This is not a DSi rom.\n");
iprintf("\x1B[47m"); //white
goto error;
}
//patch dev titles to system titles on SysNAND.
//
//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
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)
{
h->tid_high = 0x00030004;
fixHeader = true;
}
}
//offer to patch home menus to be system titles on SysNAND
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)
{
h->tid_high = 0x00030015;
fixHeader = true;
}
}
//no system titles without Unlaunch
if (!unlaunchFound && h->tid_high != 0x00030004)
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("This title cannot be\ninstalled without Unlaunch.\n");
iprintf("\x1B[47m"); //white
goto error;
}
//blacklisted titles
{
//tid without region
u32 tidLow = (h->tid_low & 0xFFFFFF00);
if (!sdnandMode && (
(h->tid_high == 0x00030005 && (
tidLow == 0x484e4400 || // DS Download Play
tidLow == 0x484e4500 || // PictoChat
tidLow == 0x484e4900 || // Nintendo DSi Camera
tidLow == 0x484e4a00 || // Nintendo Zone
tidLow == 0x484e4b00 // Nintendo DSi Sound
)) || (h->tid_high == 0x00030011 && (
tidLow == 0x30535500 || // Twl SystemUpdater
tidLow == 0x34544e00 || // TwlNmenu
tidLow == 0x54574c00 // TWL EVA
)) || (h->tid_high == 0x00030015 && (
tidLow == 0x484e4200 || // System Settings
tidLow == 0x484e4600 || // Nintendo DSi Shop
tidLow == 0x34544e00 // TwlNmenu
)) || (h->tid_high == 0x00030017 && (
tidLow == 0x484e4100 // Launcher
))) && (
(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 == 0x54574c41 || // TWL EVA
region == 0 //if the region check failed somehow, blacklist everything
))
{
//check if title exists, if it does then show any error
//otherwise allow reinstalling it
char path[PATH_MAX];
sprintf(path, "nand:/title/%08lx/%08lx/content/title.tmd", h->tid_high, h->tid_low);
if (access(path, F_OK) == 0)
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("This title cannot be\ninstalled to SysNAND.\n");
iprintf("\x1B[47m"); //white
goto error;
}
}
}
//confirmation message
{
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";
char* msg = (char*)malloc(strlen(system) + strlen(areYouSure) + strlen(fpath) + 2);
if (sdnandMode || h->tid_high == 0x00030004)
sprintf(msg, "%s%s?\n", areYouSure, fpath);
else
sprintf(msg, "%s%s%s?\n", system, areYouSure, fpath);
bool choice = choiceBox(msg);
free(msg);
if (choice == NO)
return false;
}
if (!sdnandMode && !nandio_unlock_writing())
return false;
clearScreen(&bottomScreen);
iprintf("Installing %s\n\n", fpath); swiWaitForVBlank();
//check for legit TMD, if found we'll generate a ticket which increases the size
int extensionPos = strrchr(fpath, '.') - fpath;
char tmdPath[PATH_MAX];
strcpy(tmdPath, fpath);
strcpy(tmdPath + extensionPos, ".tmd");
//DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520
int tmdSize = getFileSizePath(tmdPath);
bool tmdFound = (tmdSize == 520) || (tmdSize == 2312);
if (access(tmdPath, F_OK) == 0 && !tmdFound)
{
if (choicePrint("Incorrect TMD.\nInstall anyway?") == YES)
tmdFound = false;
else
goto error;
}
else if(!sdnandMode && !unlaunchPatches && access(tmdPath, F_OK) != 0)
{
if (choicePrint("TMD not found, game cannot be\nplayed without Unlaunch's\nlauncher patches.\nSee wiki for how to get a TMD.\n\nInstall anyway?") == YES)
tmdFound = false;
else
goto error;
}
//get install size
iprintf("Install Size: ");
swiWaitForVBlank();
u32 clusterSize = getDsiClusterSize();
unsigned long long fileSize = getRomSize(fpath), fileSizeOnDisk = fileSize;
if ((fileSizeOnDisk % clusterSize) != 0)
fileSizeOnDisk += clusterSize - (fileSizeOnDisk % clusterSize);
//file + saves + TMD (rounded up to cluster size)
unsigned long long installSize = fileSizeOnDisk + _getSaveDataSize(h) + clusterSize;
if (tmdFound) installSize += clusterSize; //ticket, rounded up to cluster size
printBytes(installSize);
iprintf("\n");
if (sdnandMode && !_checkSdSpace(installSize))
goto error;
//system title patch
if (systemTitle)
{
iprintf("System Title Patch...");
swiWaitForVBlank();
h->tid_high = 0x00030015;
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
fixHeader = true;
}
//check that there's space on nand
if (!_checkDsiSpace(installSize, (h->tid_high != 0x00030004)))
{
if (sdnandMode && choicePrint("Install as system title?"))
{
h->tid_high = 0x00030015;
fixHeader = true;
}
else
{
goto error;
}
}
//check for saves
char pubPath[PATH_MAX];
strcpy(pubPath, fpath);
strcpy(pubPath + extensionPos, ".pub");
bool pubFound = getFileSizePath(pubPath) == h->public_sav_size;
if (access(pubPath, F_OK) == 0 && !pubFound)
{
if (choicePrint("Incorrect public save.\nInstall anyway?") == YES)
pubFound = false;
else
goto error;
}
char prvPath[PATH_MAX];
strcpy(prvPath, fpath);
strcpy(prvPath + extensionPos, ".prv");
bool prvFound = getFileSizePath(prvPath) == h->private_sav_size;
if (access(prvPath, F_OK) == 0 && !prvFound)
{
if (choicePrint("Incorrect private save.\nInstall anyway?") == YES)
prvFound = false;
else
goto error;
}
char bnrPath[PATH_MAX];
strcpy(bnrPath, fpath);
strcpy(bnrPath + extensionPos, ".bnr");
bool bnrFound = getFileSizePath(bnrPath) == 0x4000;
if (access(bnrPath, F_OK) == 0 && !bnrFound)
{
if (choicePrint("Incorrect banner save.\nInstall anyway?") == YES)
bnrFound = false;
else
goto error;
}
if (_iqueHack(h))
fixHeader = true;
if (fixHeader && tmdFound)
{
if (choicePrint("Legit TMD cannot be used.\nInstall anyway?") == YES)
tmdFound = false;
else
goto error;
}
//create title directory /title/XXXXXXXX/XXXXXXXX
char dirPath[32];
mkdir(sdnandMode ? "sd:/title" : "nand:/title", 0777);
sprintf(dirPath, "%s:/title/%08x", sdnandMode ? "sd" : "nand", (unsigned int)h->tid_high);
mkdir(dirPath, 0777);
sprintf(dirPath, "%s:/title/%08x/%08x", sdnandMode ? "sd" : "nand", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
//check if title is free
if (_titleIsUsed(h))
{
char msg[64];
sprintf(msg, "Title %08x is already used.\nInstall anyway?", (unsigned int)h->tid_low);
if (choicePrint(msg) == NO)
goto error;
else
{
iprintf("\nDeleting:\n");
deleteDir(dirPath);
iprintf("\n");
}
}
if (!_openMenuSlot())
goto error;
mkdir(dirPath, 0777);
//content folder /title/XXXXXXXX/XXXXXXXXX/content
{
char contentPath[64];
sprintf(contentPath, "%s/content", dirPath);
mkdir(contentPath, 0777);
u8 appVersion = 0;
if (tmdFound)
{
FILE *file = fopen(tmdPath, "rb");
if (file)
{
fseek(file, 0x1E7, SEEK_SET);
fread(&appVersion, sizeof(appVersion), 1, file);
fclose(file);
}
}
//create 000000##.app
{
iprintf("Creating 000000%02x.app...", appVersion);
swiWaitForVBlank();
char appPath[80];
sprintf(appPath, "%s/000000%02x.app", contentPath, appVersion);
//copy nds file to app
{
int result = 0;
if (!romIsCia(fpath))
result = copyFile(fpath, appPath);
else
result = copyFilePart(fpath, 0x3900, fileSize, appPath);
if (result != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", appPath);
iprintf("%s\n", strerror(errno));
iprintf("\x1B[47m"); //white
goto error;
}
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
//pad out banner if it is the last part of the file
{
if (h->ndshdr.bannerOffset > (fileSize - 0x23C0))
{
iprintf("Padding banner...");
swiWaitForVBlank();
if (padFile(appPath, h->ndshdr.bannerOffset + 0x23C0 - fileSize) == false)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
}
}
//update header
{
if (fixHeader)
{
iprintf("Fixing header...");
swiWaitForVBlank();
//fix header checksum
h->ndshdr.headerCRC16 = swiCRC16(0xFFFF, h, 0x15E);
//fix RSA signature
u8 buffer[20];
swiSHA1Calc(&buffer, h, 0xE00);
memcpy(&(h->rsa_signature[0x6C]), buffer, 20);
FILE* f = fopen(appPath, "r+");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, 0, SEEK_SET);
fwrite(h, sizeof(tDSiHeader), 1, f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
}
}
//make/copy TMD
char newTmdPath[80];
sprintf(newTmdPath, "%s/title.tmd", contentPath);
if (tmdFound)
{
if (copyFilePart(tmdPath, 0, 520, newTmdPath) != 0)
goto error;
}
else
{
if (maketmd(appPath, newTmdPath) != 0)
goto error;
}
}
}
//data folder
{
char dataPath[64];
sprintf(dataPath, "%s/data", dirPath);
mkdir(dataPath, 0777);
if (pubFound)
{
char newPubPath[80];
sprintf(newPubPath, "%s/public.sav", dataPath);
copyFile(pubPath, newPubPath);
}
else
{
_createPublicSav(h, dataPath);
}
if (prvFound)
{
char newPrvPath[80];
sprintf(newPrvPath, "%s/private.sav", dataPath);
copyFile(prvPath, newPrvPath);
}
else
{
_createPrivateSav(h, dataPath);
}
if (bnrFound)
{
char newBnrPath[80];
sprintf(newBnrPath, "%s/banner.sav", dataPath);
copyFile(bnrPath, newBnrPath);
}
else
{
_createBannerSav(h, dataPath);
}
}
//ticket folder /ticket/XXXXXXXX
if (tmdFound)
{
//ensure folders exist
char ticketPath[32];
siprintf(ticketPath, "%s:/ticket", sdnandMode ? "sd" : "nand");
mkdir(ticketPath, 0777);
siprintf(ticketPath, "%s/%08lx", ticketPath, h->tid_high);
mkdir(ticketPath, 0777);
//actual tik path
siprintf(ticketPath, "%s/%08lx.tik", ticketPath, h->tid_low);
if (access(ticketPath, F_OK) != 0 || (choicePrint("Ticket already exists.\nKeep it? (recommended)") == NO && choicePrint("Are you sure?") == YES))
_createTicket(h, ticketPath);
}
//end
result = true;
iprintf("\x1B[42m"); //green
iprintf("\nInstallation complete.\n");
iprintf("\x1B[47m"); //white
iprintf("Back - [B]\n");
keyWait(KEY_A | KEY_B);
goto complete;
}
error:
messagePrint("\x1B[31m\nInstallation failed.\n\x1B[47m");
complete:
free(h);
if (!sdnandMode)
nandio_lock_writing();
return result;
}

View File

@ -1,8 +0,0 @@
#ifndef INSTALL_H
#define INSTALL_H
#include <nds/ndstypes.h>
bool install(char* fpath, bool systemTitle);
#endif

View File

@ -1,320 +0,0 @@
#include "main.h"
#include "rom.h"
#include "install.h"
#include "menu.h"
#include "storage.h"
#include "message.h"
#include <dirent.h>
enum {
INSTALL_MENU_INSTALL,
INSTALL_MENU_SYSTEM_TITLE,
INSTALL_MENU_DELETE,
INSTALL_MENU_BACK
};
static char currentDir[512] = "";
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static bool delete(Menu* m);
static void _setHeader(Menu* m)
{
if (!m) return;
if (currentDir[0] == '\0')
setMenuHeader(m, "sd:/");
else
setMenuHeader(m, currentDir);
}
void installMenu()
{
Menu* m = newMenu();
_setHeader(m);
generateList(m);
//no files found
/* if (m->itemCount <= 0)
{
clearScreen(&bottomScreen);
iprintf("\x1B[31m"); //red
iprintf("No files found.\n");
iprintf("\x1B[47m"); //white
iprintf("\nBack - [B]\n");
keyWait(KEY_B | KEY_A | KEY_START);
}
else*/
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
//back
if (keysDown() & KEY_B)
{
char* ptr = strrchr(currentDir, '/');
if (ptr)
{
*ptr = '\0';
_setHeader(m);
resetMenu(m);
generateList(m);
printMenu(m);
}
else
{
break;
}
}
else if (keysDown() & KEY_X)
break;
//selection
else if (keysDown() & KEY_A)
{
if (m->itemCount > 0)
{
if (m->items[m->cursor].directory == false)
{
//nds file
switch (subMenu())
{
case INSTALL_MENU_INSTALL:
install(m->items[m->cursor].value, false);
break;
case INSTALL_MENU_SYSTEM_TITLE:
if (sdnandMode)
install(m->items[m->cursor].value, true);
break;
case INSTALL_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
case INSTALL_MENU_BACK:
break;
}
}
else
{
//directory
sprintf(currentDir, "%s", m->items[m->cursor].value);
_setHeader(m);
resetMenu(m);
generateList(m);
}
printMenu(m);
}
}
}
}
freeMenu(m);
}
static void generateList(Menu* m)
{
if (!m) return;
//reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
struct dirent* ent;
DIR* dir = NULL;
if (currentDir[0] == '\0')
dir = opendir("sd:/");
else
dir = opendir(currentDir);
if (dir)
{
int count = 0;
while ( (ent = readdir(dir)) && !done)
{
if (ent->d_name[0] == '.')
continue;
if (ent->d_type == DT_DIR)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(currentDir) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", currentDir, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 1);
}
}
}
else
{
if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 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)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(currentDir) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", currentDir, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 0);
free(fpath);
}
}
}
}
}
}
closedir(dir);
sortMenuItems(m);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
if (m->itemCount <= 0) return;
if (m->items[m->cursor].directory)
clearScreen(&topScreen);
else
printRomInfo(m->items[m->cursor].value);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Install", NULL, 0);
addMenuItem(m, sdnandMode ? "Install as System Title" : "\x1B[37m[Disabled]\x1B[47m", NULL, 0);
addMenuItem(m, "Delete", NULL, 0);
addMenuItem(m, "Back - [B]", NULL, 0);
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
{
result = -1;
break;
}
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor].value;
bool result = false;
bool choice = NO;
{
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(fpath) + 1);
sprintf(msg, "%s%s", str, fpath);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("\x1B[31mCould not delete file.\x1B[47m");
}
else
{
if (remove(fpath) == 0)
{
result = true;
messageBox("\x1B[42mFile deleted.\x1B[47m");
}
else
{
messageBox("\x1B[31mCould not delete file.\x1B[47m");
}
}
}
return result;
}

View File

@ -9,34 +9,21 @@
#include <dirent.h> #include <dirent.h>
#include <time.h> #include <time.h>
bool programEnd = false; volatile bool programEnd = false;
bool sdnandMode = true; static bool unlaunchFound = false;
bool retailLauncherTmdPresentAndToBePatched = true; static bool retailLauncherTmdPresentAndToBePatched = true;
bool retailConsole = true; static bool retailConsole = true;
bool unlaunchInstallerFound = false; static bool unlaunchInstallerFound = false;
bool unlaunchFound = 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;
u8 region = 0xFF;
PrintConsole topScreen; PrintConsole topScreen;
PrintConsole bottomScreen; PrintConsole bottomScreen;
enum { enum {
MAIN_MENU_MODE,
MAIN_MENU_INSTALL,
MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL, MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL,
MAIN_MENU_SAFE_UNLAUNCH_INSTALL, MAIN_MENU_SAFE_UNLAUNCH_INSTALL,
MAIN_MENU_TITLES,
MAIN_MENU_BACKUP,
MAIN_MENU_TEST,
MAIN_MENU_FIX,
MAIN_MENU_DATA_MANAGEMENT,
MAIN_MENU_LANGUAGE_PATCHER,
MAIN_MENU_EXIT MAIN_MENU_EXIT
}; };
@ -78,20 +65,10 @@ static int _mainMenu(int cursor)
Menu* m = newMenu(); Menu* m = newMenu();
setMenuHeader(m, "MAIN MENU"); setMenuHeader(m, "MAIN MENU");
char modeStr[32], datamanStr[32], launcherStr[32]; char uninstallStr[32];
sprintf(modeStr, "Mode: %s", sdnandMode ? "SDNAND" : "\x1B[41mSysNAND\x1B[47m"); sprintf(uninstallStr, "\x1B[%02omSafe unlaunch uninstall", unlaunchFound ? 047 : 037);
sprintf(datamanStr, "\x1B[%02omEnable Data Management", devkpFound ? 037 : 047); addMenuItem(m, uninstallStr, NULL, 0);
sprintf(launcherStr, "\x1B[%02omUninstall region mod", launcherDSiFound ? 047 : 037); addMenuItem(m, "\x1B[47mSafe unlaunch install", NULL, 0);
addMenuItem(m, modeStr, NULL, 0);
addMenuItem(m, "Install", NULL, 0);
addMenuItem(m, "Safe unlaunch uninstall", NULL, 0);
addMenuItem(m, "Safe unlaunch install", NULL, 0);
addMenuItem(m, "Titles", NULL, 0);
addMenuItem(m, "Restore", NULL, 0);
addMenuItem(m, "Test", 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;
@ -132,40 +109,6 @@ void fifoHandlerBattery(u32 value32, void* userdata)
charging = (value32 & BIT(7)) != 0; charging = (value32 & BIT(7)) != 0;
} }
bool checkIfUnlaunchHasPatches(const char* path)
{
//check if launcher patches are enabled
const static u32 tidValues[][2] = {
// {location, value}
{0xE439, 0x382E3176}, // 1.8
{0xB07C, 0x17484E41}, // 1.9
{0xB099, 0x17484E41}, // 2.0 (Normal)
{0xB079, 0x484E1841}, // 2.0 (Patched)
};
bool patched = false;
FILE *tmd = fopen(path, "rb");
if (tmd)
{
for (int i = 0; i < sizeof(tidValues) / sizeof(tidValues[0]); i++)
{
if (fseek(tmd, tidValues[i][0], SEEK_SET) == 0)
{
u32 tidVal;
fread(&tidVal, sizeof(u32), 1, tmd);
if (tidVal == tidValues[i][1])
{
patched = true;
break;
}
}
}
}
fclose(tmd);
return patched;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
srand(time(0)); srand(time(0));
@ -196,9 +139,10 @@ int main(int argc, char **argv)
return 0; return 0;
} }
unlaunchInstallerFound = (access("sd:/unlaunch.dsi", F_OK) == 0); unlaunchInstallerFound = fileExists("sd:/unlaunch.dsi");
//check for unlaunch and region //check for unlaunch and region
u8 region = 0xff;
char retailLauncherTmdPath[64]; char retailLauncherTmdPath[64];
const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd"; const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd";
{ {
@ -218,7 +162,6 @@ int main(int argc, char **argv)
if (tmdSize > 520) if (tmdSize > 520)
{ {
unlaunchFound = true; unlaunchFound = true;
unlaunchPatches = checkIfUnlaunchHasPatches(retailLauncherTmdPath);
} }
else if(tmdSize != 520) else if(tmdSize != 520)
{ {
@ -242,25 +185,9 @@ int main(int argc, char **argv)
if (tmdSize > 520) if (tmdSize > 520)
{ {
unlaunchFound = true; unlaunchFound = true;
unlaunchPatches = checkIfUnlaunchHasPatches(hnaaTmdPath);
} }
} }
if (!unlaunchFound)
{
messageBox("Unlaunch not found. TMD files\nwill be required and there\nis a greater risk something\ncould go wrong.\n\nSee \x1B[46mhttps://dsi.cfw.guide/\x1B[47m to\ninstall.");
} }
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");
}
}
//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.");
@ -274,15 +201,6 @@ int main(int argc, char **argv)
switch (cursor) switch (cursor)
{ {
case MAIN_MENU_MODE:
sdnandMode = !sdnandMode;
devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0);
break;
case MAIN_MENU_INSTALL:
installMenu();
break;
case MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL: case MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL:
if(!unlaunchFound) if(!unlaunchFound)
{ {
@ -291,6 +209,7 @@ int main(int argc, char **argv)
if(uninstallUnlaunch(retailConsole, retailLauncherTmdPath)) if(uninstallUnlaunch(retailConsole, retailLauncherTmdPath))
{ {
messageBox("Uninstall successful!\n"); messageBox("Uninstall successful!\n");
unlaunchFound = false;
} else { } else {
messageBox("\x1B[31mError:\x1B[33m Uninstall failed\n"); messageBox("\x1B[31mError:\x1B[33m Uninstall failed\n");
} }
@ -306,6 +225,7 @@ int main(int argc, char **argv)
if(installUnlaunch(retailConsole, retailLauncherTmdPresentAndToBePatched ? retailLauncherTmdPath : NULL)) if(installUnlaunch(retailConsole, retailLauncherTmdPresentAndToBePatched ? retailLauncherTmdPath : NULL))
{ {
messageBox("Install successful!\n"); messageBox("Install successful!\n");
unlaunchFound = true;
} else { } else {
messageBox("\x1B[31mError:\x1B[33m Install failed\n"); messageBox("\x1B[31mError:\x1B[33m Install failed\n");
} }
@ -313,56 +233,6 @@ int main(int argc, char **argv)
} }
break; break;
case MAIN_MENU_TITLES:
titleMenu();
break;
case MAIN_MENU_BACKUP:
backupMenu();
break;
case MAIN_MENU_TEST:
testMenu();
break;
case MAIN_MENU_FIX:
if (nandio_unlock_writing())
{
nandio_force_fat_fix();
nandio_lock_writing();
messageBox("Mismatch in FAT copies will be\nfixed on close.\n");
}
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;

View File

@ -5,26 +5,13 @@
#include <fat.h> #include <fat.h>
#include <stdio.h> #include <stdio.h>
extern bool programEnd; extern volatile bool programEnd;
extern bool sdnandMode;
extern bool unlaunchFound;
extern bool unlaunchPatches;
extern bool charging; extern bool charging;
extern u8 batteryLevel; extern u8 batteryLevel;
extern u8 region;
void installMenu();
void titleMenu();
void backupMenu();
void testMenu();
extern PrintConsole topScreen; extern PrintConsole topScreen;
extern PrintConsole bottomScreen; extern PrintConsole bottomScreen;
void clearScreen(PrintConsole* screen); void clearScreen(PrintConsole* screen);
#define abs(X) ( (X) < 0 ? -(X): (X) )
#define sign(X) ( ((X) > 0) - ((X) < 0) )
#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++)
#endif #endif

View File

@ -1,191 +0,0 @@
/*---------------------------------------------------------------------------------
maketmd.cpp -- TMD Creator for DSiWare Homebrew
Copyright (C) 2018
Przemyslaw Skryjomski (Tuxality)
Big thanks to:
Apache Thunder
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
/* September 2018 - Jeff - Translated from C++ to C and uses libnds instead of openssl
Original: github.com/Tuxality/maketmd
*/
#include "maketmd.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <nds/sha1.h>
#include <nds/ndstypes.h>
#include <machine/endian.h>
//#define TMD_CREATOR_VER "0.2"
#define TMD_SIZE 0x208
#define SHA_BUFFER_SIZE 0x200
#define SHA_DIGEST_LENGTH 0x14
void tmd_create(uint8_t* tmd, FILE* app)
{
// Phase 1 - offset 0x18C (Title ID, first part)
{
fseek(app, 0x234, SEEK_SET);
uint32_t value;
fread(&value, 4, 1, app);
value = __bswap32(value);
memcpy(tmd + 0x18c, &value, 4);
}
// Phase 2 - offset 0x190 (Title ID, second part)
{
// We can take this also from 0x230, but reversed
fseek(app, 0x0C, SEEK_SET);
fread((char*)&tmd[0x190], 4, 1, app);
}
// Phase 3 - offset 0x198 (Group ID = '01')
{
fseek(app, 0x10, SEEK_SET);
fread((char*)&tmd[0x198], 2, 1, app);
}
// Phase 4 - offset 0x1AA (fill-in 0x80 value, 0x10 times)
{
for (size_t i = 0; i<0x10; i++)
{
tmd[0x1AA + i] = 0x80;
}
}
// Phase 5 - offset 0x1DE (number of contents = 1)
{
tmd[0x1DE] = 0x00;
tmd[0x1DF] = 0x01;
}
// Phase 6 - offset 0x1EA (type of content = 1)
{
tmd[0x1EA] = 0x00;
tmd[0x1EB] = 0x01;
}
// Phase 7 - offset, 0x1EC (file size, 8B)
uint32_t filesize = 0;
uint32_t fileread = 0;
{
fseek(app, 0, SEEK_END);
filesize = ftell(app);
uint32_t size = __bswap32(filesize);
// We only use 4B for size as for now
memcpy((tmd + 0x1F0), &size, sizeof(u32));
}
// Phase 8 - offset, 0x1F4 (SHA1 sum, 20B)
{
// Makes use of libnds
fseek(app, 0, SEEK_SET);
uint8_t buffer[SHA_BUFFER_SIZE] = { 0 };
uint32_t buffer_read = 0;
swiSHA1context_t ctx;
swiSHA1Init(&ctx);
do {
buffer_read = fread((char*)&buffer[0], 1, SHA_BUFFER_SIZE, app);
fileread += buffer_read;
swiSHA1Update(&ctx, buffer, buffer_read);
printProgressBar((float)fileread / (float)filesize);
}
while (buffer_read == SHA_BUFFER_SIZE);
clearProgressBar();
consoleSelect(&bottomScreen);
swiSHA1Final(buffer, &ctx);
//Store SHA1 sum
memcpy((tmd + 0x1F4), buffer, SHA_DIGEST_LENGTH);
}
}
int maketmd(char* input, char* tmdPath)
{
iprintf("MakeTMD for DSiWare Homebrew\n");
iprintf("by Przemyslaw Skryjomski\n\t(Tuxality)\n");
if (input == NULL || tmdPath == NULL)
{
iprintf("\x1B[33m"); //yellow
iprintf("\nUsage: %s file.app <file.tmd>\n", "maketmd");
iprintf("\x1B[47m"); //white
return 1;
}
// APP file (input)
FILE* app = fopen(input, "rb");
if (!app)
{
iprintf("\x1B[31m"); //red
iprintf("Error at opening %s for reading.\n", input);
iprintf("\x1B[47m"); //white
return 1;
}
// TMD file (output)
FILE* tmd = fopen(tmdPath, "wb");
if (!tmd)
{
fclose(app);
iprintf("\x1B[31m"); //white
iprintf("Error at opening %s for writing.\n", tmdPath);
iprintf("\x1B[47m"); //white
return 1;
}
// Allocate memory for TMD
uint8_t* tmd_template = (uint8_t*)malloc(sizeof(uint8_t) * TMD_SIZE);
memset(tmd_template, 0, sizeof(uint8_t) * TMD_SIZE); // zeroed
// Prepare TMD template then write to file
tmd_create(tmd_template, app);
fwrite((const char*)(&tmd_template[0]), TMD_SIZE, 1, tmd);
// Free allocated memory for TMD
free(tmd_template);
// This is done in dtor, but we additionally flush tmd.
fclose(app);
fclose(tmd);
return 0;
}

View File

@ -1,9 +0,0 @@
#ifndef MAKETMD_H
#define MAKETMD_H
#include "main.h"
#include "storage.h"
int maketmd(char* input, char* tmdPath);
#endif

View File

@ -1,6 +1,9 @@
#include "menu.h" #include "menu.h"
#include "main.h" #include "main.h"
#define sign(X) ( ((X) > 0) - ((X) < 0) )
#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++)
Menu* newMenu() Menu* newMenu()
{ {
Menu* m = (Menu*)malloc(sizeof(Menu)); Menu* m = (Menu*)malloc(sizeof(Menu));

View File

@ -264,7 +264,6 @@ bool nandio_lock_writing()
bool nandio_unlock_writing() bool nandio_unlock_writing()
{ {
if (writingLocked && randomConfirmBox("Writing to NAND is locked!\nIf you're sure you understand\nthe risk, input the sequence\nbelow."))
writingLocked = false; writingLocked = false;
return !writingLocked; return !writingLocked;

View File

@ -1,296 +0,0 @@
#include "rom.h"
#include "main.h"
#include "storage.h"
#include <dirent.h>
#include <nds.h>
#include <malloc.h>
#include <stdio.h>
tDSiHeader* getRomHeader(char const* fpath)
{
if (!fpath) return NULL;
tDSiHeader* h = NULL;
FILE* f = fopen(fpath, "rb");
if (f)
{
h = (tDSiHeader*)malloc(sizeof(tDSiHeader));
if (h)
{
if (romIsCia(fpath))
fseek(f, 0x3900, SEEK_SET);
else
fseek(f, 0, SEEK_SET);
fread(h, sizeof(tDSiHeader), 1, f);
}
fclose(f);
}
return h;
}
tNDSBanner* getRomBanner(char const* fpath)
{
if (!fpath) return NULL;
tDSiHeader* h = getRomHeader(fpath);
tNDSBanner* b = NULL;
if (h)
{
FILE* f = fopen(fpath, "rb");
if (f)
{
b = (tNDSBanner*)malloc(sizeof(tNDSBanner));
if (b)
{
if (romIsCia(fpath))
fseek(f, 0x3900, SEEK_SET);
else
fseek(f, 0, SEEK_SET);
fseek(f, h->ndshdr.bannerOffset, SEEK_CUR);
fread(b, sizeof(tNDSBanner), 1, f);
}
}
free(h);
fclose(f);
}
return b;
}
bool getGameTitle(tNDSBanner* b, char* out, bool full)
{
if (!b) return false;
if (!out) return false;
//get system language
int lang = PersonalData->language;
//not japanese or chinese
if (lang == 0 || lang == 6)
lang = 1;
//read title
u16 c;
for (int i = 0; i < 128; i++)
{
c = b->titles[lang][i];
//remove accents
if (c == 0x00F3)
c = 'o';
if (c == 0x00E1)
c = 'a';
out[i] = (char)c;
if (!full && out[i] == '\n')
{
out[i] = '\0';
break;
}
}
out[128] = '\0';
return true;
}
bool getGameTitlePath(char const* fpath, char* out, bool full)
{
if (!fpath) return false;
if (!out) return false;
tNDSBanner* b = getRomBanner(fpath);
bool result = getGameTitle(b, out, full);
free(b);
return result;
}
bool getRomLabel(tDSiHeader* h, char* out)
{
if (!h) return false;
if (!out) return false;
sprintf(out, "%.12s", h->ndshdr.gameTitle);
return true;
}
bool getRomCode(tDSiHeader* h, char* out)
{
if (!h) return false;
if (!out) return false;
sprintf(out, "%.4s", h->ndshdr.gameCode);
return true;
}
void printRomInfo(char const* fpath)
{
clearScreen(&topScreen);
if (!fpath) return;
tDSiHeader* h = getRomHeader(fpath);
tNDSBanner* b = getRomBanner(fpath);
if (!isDsiHeader(h))
{
iprintf("Could not read dsi header.\n");
}
else
{
if (!b)
{
iprintf("Could not read banner.\n");
}
else
{
//proper title
{
char gameTitle[128+1];
getGameTitle(b, gameTitle, true);
iprintf("%s\n\n", gameTitle);
}
//file size
{
iprintf("Size: ");
unsigned long long romSize = getRomSize(fpath);
printBytes(romSize);
//size in blocks, rounded up
iprintf(" (%lld blocks)\n", ((romSize / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK);
}
iprintf("Label: %.12s\n", h->ndshdr.gameTitle);
iprintf("Game Code: %.4s\n", h->ndshdr.gameCode);
//system type
{
iprintf("Unit Code: ");
switch (h->ndshdr.unitCode)
{
case 0: iprintf("NDS"); break;
case 2: iprintf("NDS+DSi"); break;
case 3: iprintf("DSi"); break;
default: iprintf("unknown");
}
iprintf("\n");
}
//application type
{
iprintf("Program Type: ");
switch (h->ndshdr.reserved1[7])
{
case 0x3: iprintf("Normal"); break;
case 0xB: iprintf("Sys"); break;
case 0xF: iprintf("Debug/Sys"); break;
default: iprintf("unknown");
}
iprintf("\n");
}
//DSi title ids
{
if (h->tid_high == 0x00030004 ||
h->tid_high == 0x00030005 ||
h->tid_high == 0x00030011 || // TID for software in TWL SDK
h->tid_high == 0x00030015 ||
h->tid_high == 0x00030017 ||
h->tid_high == 0x00030000)
{
iprintf("Title ID: %08x %08x\n", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
}
}
//print full file path
iprintf("\n%s\n", fpath);
//print extra files
int extensionPos = strrchr(fpath, '.') - fpath;
char temp[PATH_MAX];
strcpy(temp, fpath);
strcpy(temp + extensionPos, ".tmd");
//DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520
int tmdSize = getFileSizePath(temp);
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (tmdSize == 520 || tmdSize == 2312) ? 047 : 041, strrchr(temp, '/') + 1);
strcpy(temp + extensionPos, ".pub");
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->public_sav_size) ? 047 : 041, strrchr(temp, '/') + 1);
strcpy(temp + extensionPos, ".prv");
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->private_sav_size) ? 047 : 041, strrchr(temp, '/') + 1);
strcpy(temp + extensionPos, ".bnr");
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == 0x4000) ? 047 : 041, strrchr(temp, '/') + 1);
}
}
free(b);
free(h);
}
unsigned long long getRomSize(char const* fpath)
{
if (!fpath) return 0;
unsigned long long size = 0;
FILE* f = fopen(fpath, "rb");
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);
size = ftell(f);
}
}
fclose(f);
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;
}

View File

@ -1,23 +0,0 @@
#ifndef ROM_H
#define ROM_H
#include <nds/ndstypes.h>
#include <nds/memory.h>
tDSiHeader* getRomHeader(char const* fpath);
tNDSBanner* getRomBanner(char const* fpath);
bool getGameTitle(tNDSBanner* b, char* out, bool full);
bool getGameTitlePath(char const* fpath, char* out, bool full);
bool getRomLabel(tDSiHeader* h, char* out);
bool getRomCode(tDSiHeader* h, char* out);
void printRomInfo(char const* fpath);
unsigned long long getRomSize(char const* fpath);
bool romIsCia(char const* fpath);
bool isDsiHeader(tDSiHeader* h);
#endif

View File

@ -1,93 +0,0 @@
#include "sav.h"
#include <string.h>
#include <malloc.h>
#define align(v, a) (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
bool initFatHeader(FILE* f)
{
if (!f)
return false;
//get size
fseek(f, 0, SEEK_END);
u32 size = ftell(f);
//based on GodMode9
//https://github.com/d0k3/GodMode9/blob/d8d43c14f3317423c677b1c7e0987bcb9bbd7299/arm9/source/game/nds.c#L47-L105
const u16 sectorSize = 0x200;
//fit maximum sectors for the size
const u16 maxSectors = size / sectorSize;
u16 sectorCount = 1;
u16 secPerTrk = 1;
u16 numHeads = 1;
u16 sectorCountNext = 0;
while (sectorCountNext <= maxSectors)
{
sectorCountNext = secPerTrk * (numHeads + 1) * (numHeads + 1);
if (sectorCountNext <= maxSectors)
{
numHeads++;
sectorCount = sectorCountNext;
secPerTrk++;
sectorCountNext = secPerTrk * numHeads * numHeads;
if (sectorCountNext <= maxSectors)
{
sectorCount = sectorCountNext;
}
}
}
sectorCountNext = (secPerTrk + 1) * numHeads * numHeads;
if (sectorCountNext <= maxSectors)
{
secPerTrk++;
sectorCount = sectorCountNext;
}
u8 secPerCluster = (sectorCount > (8 << 10)) ? 8 : (sectorCount > (1 << 10) ? 4 : 1);
u16 rootEntryCount = size < 0x8C000 ? 0x20 : 0x200;
u16 totalClusters = align(sectorCount, secPerCluster) / secPerCluster;
u32 fatBytes = (align(totalClusters, 2) / 2) * 3; // 2 sectors -> 3 byte
u16 fatSize = align(fatBytes, sectorSize) / sectorSize;
FATHeader* h = (FATHeader*)malloc(sizeof(FATHeader));
h->BS_JmpBoot[0] = 0xE9;
h->BS_JmpBoot[1] = 0;
h->BS_JmpBoot[2] = 0;
memcpy(h->BS_OEMName, "MSWIN4.1", 8);
h->BPB_BytesPerSec = sectorSize;
h->BPB_SecPerClus = secPerCluster;
h->BPB_RsvdSecCnt = 0x0001;
h->BPB_NumFATs = 0x02;
h->BPB_RootEntCnt = rootEntryCount;
h->BPB_TotSec16 = sectorCount;
h->BPB_Media = 0xF8; // "hard drive"
h->BPB_FATSz16 = fatSize;
h->BPB_SecPerTrk = secPerTrk;
h->BPB_NumHeads = numHeads;
h->BPB_HiddSec = 0x00000000;
h->BPB_TotSec32 = 0x00000000;
h->BS_DrvNum = 0x05;
h->BS_Reserved1 = 0x00;
h->BS_BootSig = 0x29;
h->BS_VolID = 0x12345678;
memcpy(h->BS_VolLab, "VOLUMELABEL", 11);
memcpy(h->BS_FilSysType,"FAT12 ", 8);
memset(h->BS_BootCode, 0, sizeof(h->BS_BootCode));
h->BS_BootSign = 0xAA55;
fseek(f, 0, SEEK_SET);
fwrite(h, sizeof(FATHeader), 1, f);
free(h);
return true;
}

View File

@ -1,38 +0,0 @@
#ifndef SAV_H
#define SAV_H
#include <nds/ndstypes.h>
#include <stdio.h>
//http://elm-chan.org/docs/fat_e.html
#pragma pack(push, 1)
typedef struct
{
u8 BS_JmpBoot[3]; //0x0000
u8 BS_OEMName[8]; //0x0003
u16 BPB_BytesPerSec; //0x000B
u8 BPB_SecPerClus; //0x000D
u16 BPB_RsvdSecCnt; //0x000E
u8 BPB_NumFATs;
u16 BPB_RootEntCnt;
u16 BPB_TotSec16;
u8 BPB_Media;
u16 BPB_FATSz16;
u16 BPB_SecPerTrk;
u16 BPB_NumHeads;
u32 BPB_HiddSec;
u32 BPB_TotSec32;
u8 BS_DrvNum;
u8 BS_Reserved1;
u8 BS_BootSig;
u32 BS_VolID;
u8 BS_VolLab[11];
u8 BS_FilSysType[8];
u8 BS_BootCode[448];
u16 BS_BootSign;
} FATHeader;
#pragma pack(push, 0)
bool initFatHeader(FILE* f);
#endif

View File

@ -6,26 +6,10 @@
#define TITLE_LIMIT 39 #define TITLE_LIMIT 39
//printing
void printBytes(unsigned long long bytes)
{
if (bytes < 1024)
iprintf("%dB", (unsigned int)bytes);
else if (bytes < 1024 * 1024)
printf("%.2fKB", (float)bytes / 1024.f);
else if (bytes < 1024 * 1024 * 1024)
printf("%.2fMB", (float)bytes / 1024.f / 1024.f);
else
printf("%.2fGB", (float)bytes / 1024.f / 1024.f / 1024.f);
}
//progress bar //progress bar
static int lastBars = 0; static int lastBars = 0;
void printProgressBar(float percent) static void printProgressBar(float percent)
{ {
if (percent < 0.f) percent = 0.f; if (percent < 0.f) percent = 0.f;
if (percent > 1.f) percent = 1.f; if (percent > 1.f) percent = 1.f;
@ -59,7 +43,7 @@ void printProgressBar(float percent)
} }
} }
void clearProgressBar() static void clearProgressBar()
{ {
lastBars = 0; lastBars = 0;
consoleSelect(&topScreen); consoleSelect(&topScreen);
@ -69,14 +53,7 @@ void clearProgressBar()
//files //files
bool fileExists(char const* path) bool fileExists(char const* path)
{ {
if (!path) return false; return access(path, F_OK) == 0;
FILE* f = fopen(path, "rb");
if (!f)
return false;
fclose(f);
return true;
} }
int copyFile(char const* src, char const* dst) int copyFile(char const* src, char const* dst)
@ -175,25 +152,6 @@ unsigned long long getFileSizePath(char const* path)
return size; return size;
} }
bool padFile(char const* path, int size)
{
if (!path) return false;
FILE* f = fopen(path, "ab");
if (!f)
{
return false;
}
else
{
for (int i = 0; i < size; i++)
fputc('\0', f);
}
fclose(f);
return true;
}
bool toggleFileReadOnly(const char* path, bool readOnly) bool toggleFileReadOnly(const char* path, bool readOnly)
{ {
int fatAttributes = FAT_getAttr(path); int fatAttributes = FAT_getAttr(path);
@ -217,236 +175,6 @@ bool writeToFile(FILE* fd, const char* buffer, size_t size)
return toWrite == 0; return toWrite == 0;
} }
//directories
bool dirExists(char const* path)
{
if (!path) return false;
DIR* dir = opendir(path);
if (!dir)
return false;
closedir(dir);
return true;
}
bool copyDir(char const* src, char const* dst)
{
if (!src || !dst) return false;
// iprintf("copyDir\n%s\n%s\n\n", src, dst);
bool result = true;
DIR* dir = opendir(src);
struct dirent* ent;
if (!dir)
{
return false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char* dsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 4);
sprintf(dsrc, "%s/%s", src, ent->d_name);
char* ddst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 4);
sprintf(ddst, "%s/%s", dst, ent->d_name);
mkdir(ddst, 0777);
if (!copyDir(dsrc, ddst))
result = false;
free(ddst);
free(dsrc);
}
else
{
char* fsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 4);
sprintf(fsrc, "%s/%s", src, ent->d_name);
char* fdst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 4);
sprintf(fdst, "%s/%s", dst, ent->d_name);
// iprintf("%s\n%s\n\n", fsrc, fdst);
iprintf("%s -> \n%s...", fsrc, fdst);
int ret = copyFile(fsrc, fdst);
if (ret != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Fail\n");
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", strerror(errno));
/*
switch (ret)
{
case 1:
iprintf("Empty input path.\n");
break;
case 2:
iprintf("Empty output path.\n");
break;
case 3:
iprintf("Error opening input file.\n");
break;
case 4:
iprintf("Error opening output file.\n");
break;
}
*/
iprintf("\x1B[47m"); //white
result = false;
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
free(fdst);
free(fsrc);
}
}
}
closedir(dir);
return result;
}
bool deleteDir(char const* path)
{
if (!path) return false;
if (strcmp("/", path) == 0)
{
//oh fuck no
return false;
}
bool result = true;
DIR* dir = opendir(path);
struct dirent* ent;
if (!dir)
{
result = false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//Delete directory
char subpath[512];
sprintf(subpath, "%s/%s", path, ent->d_name);
if (!deleteDir(subpath))
result = false;
}
else
{
//Delete file
char fpath[512];
sprintf(fpath, "%s/%s", path, ent->d_name);
iprintf("%s...", fpath);
if (remove(fpath) != 0)
{
iprintf("\x1B[31m");
iprintf("Fail\n");
iprintf("\x1B[47m");
result = false;
}
else
{
iprintf("\x1B[42m");
iprintf("Done\n");
iprintf("\x1B[47m");
}
}
}
}
closedir(dir);
iprintf("%s...", path);
if (remove(path) != 0)
{
iprintf("\x1B[31m");
iprintf("Fail\n");
iprintf("\x1B[47m");
result = false;
}
else
{
iprintf("\x1B[42m");
iprintf("Done\n");
iprintf("\x1B[47m");
}
return result;
}
unsigned long long getDirSize(const char* path, u32 blockSize)
{
if (!path) return 0;
unsigned long long size = 0;
DIR* dir = opendir(path);
struct dirent* ent;
if (dir)
{
while ((ent = readdir(dir)))
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char fullpath[512];
sprintf(fullpath, "%s/%s", path, ent->d_name);
size += getDirSize(fullpath, blockSize);
}
else
{
char fullpath[260];
sprintf(fullpath, "%s/%s", path, ent->d_name);
size += getFileSizePath(fullpath);
// If we've specified a block size, round up to it
if ((size % blockSize) != 0)
size += blockSize - (size % blockSize);
}
}
}
closedir(dir);
return size;
}
bool safeCreateDir(const char* path) bool safeCreateDir(const char* path)
{ {
if (((mkdir(path, 0777) == 0) || errno == EEXIST)) if (((mkdir(path, 0777) == 0) || errno == EEXIST))
@ -458,142 +186,3 @@ bool safeCreateDir(const char* path)
messageBox(errorStr); messageBox(errorStr);
return false; return false;
} }
//home menu
int getMenuSlots()
{
//Assume the home menu has a hard limit on slots
//Find a better way to do this
return TITLE_LIMIT;
}
int getMenuSlotsFree()
{
//Get number of open menu slots by subtracting the number of directories in the title folders
//Find a better way to do this
const int NUM_OF_DIRS = 4;
const char* dirs[] = {
"00030004",
"00030005",
"00030015",
"00030017"
};
int freeSlots = getMenuSlots();
DIR* dir;
struct dirent* ent;
for (int i = 0; i < NUM_OF_DIRS; i++)
{
char path[256];
sprintf(path, "%s:/title/%s", sdnandMode ? "sd" : "nand", dirs[i]);
dir = opendir(path);
if (dir)
{
while ( (ent = readdir(dir)) != NULL )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
freeSlots -= 1;
}
}
closedir(dir);
}
return freeSlots;
}
//SD card
bool sdIsInserted()
{
//Find a better way to do this.
return true;
}
unsigned long long getSDCardSize()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("sd:/", &st) == 0)
return st.f_bsize * st.f_blocks;
}
return 0;
}
unsigned long long getSDCardFree()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("sd:/", &st) == 0)
return st.f_bsize * st.f_bavail;
}
return 0;
}
//internal storage
unsigned long long getDsiSize()
{
//The DSi has 256MB of internal storage. Some is unavailable and used by other things.
//An empty DSi reads 1024 open blocks
return 1024 * BYTES_PER_BLOCK;
}
unsigned long long getDsiFree()
{
u32 blockSize = getDsiClusterSize();
//Get free space by subtracting file sizes in nand folders
unsigned long long size = getDsiSize();
unsigned long long appSize = getDirSize(sdnandMode ? "sd:/title/00030004" : "nand:/title/00030004", blockSize);
//subtract, but don't go under 0
if (appSize > size)
{
size = 0;
}
else
{
size -= appSize;
}
unsigned long long realFree = getDsiRealFree();
return (realFree < size) ? realFree : size;
}
unsigned long long getDsiRealSize()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize * st.f_blocks;
return 0;
}
unsigned long long getDsiRealFree()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize * st.f_bavail;
return 0;
}
u32 getDsiClusterSize()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize;
return 0;
}

View File

@ -4,50 +4,16 @@
#include <nds/ndstypes.h> #include <nds/ndstypes.h>
#include <stdio.h> #include <stdio.h>
#define BACKUP_PATH "sd:/_nds/ntm/backup"
#define BYTES_PER_BLOCK (1024*128)
//printing
void printBytes(unsigned long long bytes);
//progress bar
void printProgressBar(float percent);
void clearProgressBar();
//Files //Files
bool fileExists(char const* path); bool fileExists(char const* path);
int copyFile(char const* src, char const* dst); int copyFile(char const* src, char const* dst);
int copyFilePart(char const* src, u32 offset, u32 size, char const* dst); int copyFilePart(char const* src, u32 offset, u32 size, char const* dst);
unsigned long long getFileSize(FILE* f); unsigned long long getFileSize(FILE* f);
unsigned long long getFileSizePath(char const* path); unsigned long long getFileSizePath(char const* path);
bool padFile(char const* path, int size);
bool toggleFileReadOnly(const char* path, bool readOnly); bool toggleFileReadOnly(const char* path, bool readOnly);
bool writeToFile(FILE* fd, const char* buffer, size_t size); bool writeToFile(FILE* fd, const char* buffer, size_t size);
//Directories //Directories
bool dirExists(char const* path);
bool copyDir(char const* src, char const* dst);
bool deleteDir(char const* path);
unsigned long long getDirSize(char const* path, u32 blockSize);
bool safeCreateDir(const char* path); bool safeCreateDir(const char* path);
//home menu
int getMenuSlots();
int getMenuSlotsFree();
#define getMenuSlotsUsed() (getMenuSlots() - getMenuSlotsFree())
//SD card
bool sdIsInserted();
unsigned long long getSDCardSize();
unsigned long long getSDCardFree();
#define getSDCardUsedSpace() (getSDCardSize() - getSDCardFree())
//internal storage
unsigned long long getDsiSize();
unsigned long long getDsiFree();
unsigned long long getDsiRealSize();
unsigned long long getDsiRealFree();
u32 getDsiClusterSize();
#define getDsiUsed() (getDSIStorageSize() - getDSIStorageFree())
#endif #endif

View File

@ -1,75 +0,0 @@
#include "main.h"
#include "message.h"
#include "storage.h"
void testMenu()
{
//top screen
clearScreen(&topScreen);
iprintf("Storage Check Test\n\n");
//bottom screen
clearScreen(&bottomScreen);
unsigned int free = 0;
unsigned int size = 0;
//home menu slots
{
iprintf("Free Home Menu Slots:\n");
free = getMenuSlotsFree();
iprintf("\t%d / ", free);
size = getMenuSlots();
iprintf("%d\n", size);
}
//dsi menu
{
iprintf("\nFree DSi Menu Space:\n\t");
free = getDsiFree();
printBytes(free);
iprintf(" / ");
size = getDsiSize();
printBytes(size);
iprintf("\n");
iprintf("\t%d / %d blocks\n", free / BYTES_PER_BLOCK, size / BYTES_PER_BLOCK);
}
//nand
if (!sdnandMode)
{
iprintf("\nFree NAND Space:\n\t");
free = getDsiRealFree();
printBytes(free);
iprintf(" / ");
size = getDsiRealSize();
printBytes(size);
iprintf("\n");
}
//SD Card
{
iprintf("\nFree SD Space:\n\t");
unsigned long long sdfree = getSDCardFree();
printBytes(sdfree);
iprintf(" / ");
unsigned long long sdsize = getSDCardSize();
printBytes(sdsize);
iprintf("\n");
printf("\t%d / %d blocks\n", (unsigned int)(sdfree / BYTES_PER_BLOCK), (unsigned int)(sdsize / BYTES_PER_BLOCK));
}
//end
iprintf("\nBack - [B]\n");
keyWait(KEY_B);
}

View File

@ -1,523 +0,0 @@
#include "main.h"
#include "rom.h"
#include "menu.h"
#include "message.h"
#include "nand/nandio.h"
#include "storage.h"
#include <dirent.h>
enum {
TITLE_MENU_BACKUP,
TITLE_MENU_DELETE,
TITLE_MENU_READ_ONLY,
TITLE_MENU_BACK
};
static bool readOnly = false;
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static void backup(Menu* m);
static bool delete(Menu* m);
static void toggleReadOnly(Menu* m);
void titleMenu()
{
Menu* m = newMenu();
setMenuHeader(m, "INSTALLED TITLES");
generateList(m);
//no titles
if (m->itemCount <= 0)
{
messageBox("No titles found.");
}
else
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
if (keysDown() & KEY_B || m->itemCount <= 0)
break;
else if (keysDown() & KEY_A)
{
readOnly = FAT_getAttr(m->items[m->cursor].value) & ATTR_READONLY;
switch (subMenu())
{
case TITLE_MENU_BACKUP:
backup(m);
break;
case TITLE_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
case TITLE_MENU_READ_ONLY:
toggleReadOnly(m);
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static void generateList(Menu* m)
{
if (!m) return;
const int NUM_OF_DIRS = 4;
const char* dirs[] = {
"00030004",
"00030005",
"00030015",
"00030017"
};
const char* blacklist[4][6] = {
{ // 00030004
NULL //nothing blacklisted
},
{ // 00030005
"484e44", // DS Download Play
"484e45", // PictoChat
"484e49", // Nintendo DSi Camera
"484e4a", // Nintendo Zone
"484e4b", // Nintendo DSi Sound
NULL
},
{ // 00030015
"484e42", // System Settings
"484e46", // Nintendo DSi Shop
NULL
},
{ // 00030017
"484e41", // Launcher
NULL
}
};
//Reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
int count = 0; //used to skip to the right page
//search each category directory /title/XXXXXXXX
for (int i = 0; i < NUM_OF_DIRS && done == false; i++)
{
char* dirPath = (char*)malloc(strlen(dirs[i])+15);
sprintf(dirPath, "%s:/title/%s", sdnandMode ? "sd" : "nand", dirs[i]);
struct dirent* ent;
DIR* dir = opendir(dirPath);
if (dir)
{
while ( (ent = readdir(dir)) && done == false)
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
//blacklisted titles
if (!sdnandMode)
{
//if the region check somehow failed blacklist all-non DSiWare
if (region == 0 && i > 0) continue;
bool blacklisted = false;
for (int j = 0; blacklist[i][j] != NULL; j++)
{
char titleId[9];
sprintf(titleId, "%s%02x", blacklist[i][j], region);
if (strcmp(titleId, ent->d_name) == 0)
blacklisted = true;
// also blacklist specific all-region titles
if ((strcmp("484e4441", ent->d_name) == 0) || // Download Play
(strcmp("484e4541", ent->d_name) == 0) || // PictoChat
(strcmp("34544e41", ent->d_name) == 0)) // TwlNmenu
blacklisted = true;
}
if (blacklisted) continue;
}
if (ent->d_type == DT_DIR)
{
//scan content folder /title/XXXXXXXX/content
char* contentPath = (char*)malloc(strlen(dirPath) + strlen(ent->d_name) + 20);
sprintf(contentPath, "%s/%s/content", dirPath, ent->d_name);
struct dirent* subent;
DIR* subdir = opendir(contentPath);
if (subdir)
{
while ( (subent = readdir(subdir)) && done == false)
{
if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0)
continue;
if (subent->d_type != DT_DIR)
{
//found .app file
if (strstr(subent->d_name, ".app") != NULL)
{
//current item is not on page
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
//found requested title
char* path = (char*)malloc(strlen(contentPath) + strlen(subent->d_name) + 10);
sprintf(path, "%s/%s", contentPath, subent->d_name);
char title[128];
getGameTitlePath(path, title, false);
addMenuItem(m, title, path, 0);
free(path);
}
}
}
}
}
}
closedir(subdir);
free(contentPath);
}
}
}
closedir(dir);
free(dirPath);
}
sortMenuItems(m);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
printRomInfo(m->items[m->cursor].value);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Backup", NULL, 0);
addMenuItem(m, "Delete", NULL, 0);
addMenuItem(m, readOnly ? "Mark not read-only" : "Mark read-only", NULL, 0);
addMenuItem(m, "Back - [B]", NULL, 0);
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static void backup(Menu* m)
{
char* fpath = m->items[m->cursor].value;
char *backname = NULL;
tDSiHeader* h = getRomHeader(fpath);
{
//make backup folder name
char label[13];
getRomLabel(h, label);
char gamecode[5];
getRomCode(h, gamecode);
backname = (char*)malloc(strlen(label) + strlen(gamecode) + 16);
sprintf(backname, "%s-%s", label, gamecode);
//make sure dir is unused
char* dstpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(backname) + 32);
sprintf(dstpath, "%s/%s.nds", BACKUP_PATH, backname);
int try = 1;
while (access(dstpath, F_OK) == 0)
{
try += 1;
sprintf(backname, "%s-%s(%d)", label, gamecode, try);
sprintf(dstpath, "%s/%s.nds", BACKUP_PATH, backname);
}
free(dstpath);
}
bool choice = NO;
{
const char str[] = "Are you sure you want to backup\n";
char* msg = (char*)malloc(strlen(str) + strlen(backname) + 2);
sprintf(msg, "%s%s?", str, backname);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
char srcpath[30];
sprintf(srcpath, "%s:/title/%08lx/%08lx", sdnandMode ? "sd" : "nand", h->tid_high, h->tid_low);
if (getSDCardFree() < getDirSize(srcpath, 0))
{
messageBox("Not enough space on SD.");
}
else
{
//create dirs
{
//create subdirectories
char backupPath[sizeof(BACKUP_PATH)];
strcpy(backupPath, BACKUP_PATH);
for (char *slash = strchr(backupPath, '/'); slash; slash = strchr(slash + 1, '/'))
{
char temp = *slash;
*slash = '\0';
mkdir(backupPath, 0777);
*slash = temp;
}
mkdir(backupPath, 0777); // sd:/_nds/ntm/backup
}
clearScreen(&bottomScreen);
char path[256], dstpath[256];
//tmd
sprintf(path, "%s/content/title.tmd", srcpath);
sprintf(dstpath, "%s/%s.tmd", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
//get app version
FILE *tmd = fopen(path, "rb");
if (tmd)
{
u8 appVersion[4];
fseek(tmd, 0x1E4, SEEK_SET);
fread(&appVersion, 1, 4, tmd);
fclose(tmd);
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
//app
sprintf(path, "%s/content/%02x%02x%02x%02x.app", srcpath, appVersion[0], appVersion[1], appVersion[2], appVersion[3]);
sprintf(dstpath, "%s/%s.nds", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
}
}
//public save
sprintf(path, "%s/data/public.sav", srcpath);
sprintf(dstpath, "%s/%s.pub", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
//private save
sprintf(path, "%s/data/private.sav", srcpath);
sprintf(dstpath, "%s/%s.prv", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
//banner save
sprintf(path, "%s/data/banner.sav", srcpath);
sprintf(dstpath, "%s/%s.bnr", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
messagePrint("\x1B[42m\nBackup finished.\x1B[47m");
}
}
free(h);
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor].value;
bool result = false;
bool choice = NO;
{
//get app title
char title[128];
getGameTitlePath(m->items[m->cursor].value, title, false);
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(title) + 8);
sprintf(msg, "%s%s", str, title);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("Failed to delete title.\n");
}
else
{
char dirPath[64];
sprintf(dirPath, "%.*s", sdnandMode ? 27 : 29, fpath);
if (!dirExists(dirPath))
{
messageBox("Failed to delete title.\n");
}
else
{
if (!sdnandMode && !nandio_unlock_writing())
return false;
clearScreen(&bottomScreen);
if (deleteDir(dirPath))
{
result = true;
messagePrint("\nTitle deleted.\n");
}
else
{
messagePrint("\nTitle could not be deleted.\n");
}
if (!sdnandMode)
nandio_lock_writing();
}
}
}
return result;
}
static void toggleReadOnly(Menu* m)
{
if (!m) return;
tDSiHeader* h = getRomHeader(m->items[m->cursor].value);
char path[256];
char srcpath[30];
sprintf(srcpath, "%s:/title/%08lx/%08lx", sdnandMode ? "sd" : "nand", h->tid_high, h->tid_low);
if (!sdnandMode && !nandio_unlock_writing()) return;
//app
strcpy(path, m->items[m->cursor].value);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//tmd
sprintf(path, "%s/content/title.tmd", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//public save
sprintf(path, "%s/data/public.sav", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//private save
sprintf(path, "%s/data/private.sav", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//banner save
sprintf(path, "%s/data/banner.sav", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
if (!sdnandMode)
nandio_lock_writing();
free(h);
messageBox("Title's read-only status\nsuccesfully toggled.");
}

View File

@ -7,6 +7,7 @@
static char unlaunchInstallerBuffer[0x30000]; static char unlaunchInstallerBuffer[0x30000];
static const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd"; static const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd";
static const char* hnaaBackupTmdPath = "nand:/title/00030017/484e4141/content/title.tmd.bak";
bool isLauncherTmdPatched(const char* path) bool isLauncherTmdPatched(const char* path)
{ {
@ -110,16 +111,14 @@ static bool patchMainTmd(const char* path)
static bool restoreProtoTmd(const char* path) static bool restoreProtoTmd(const char* path)
{ {
bool hnaaBackupExists = false; if (!fileExists(hnaaBackupTmdPath))
hnaaBackupExists = (access("nand:/title/00030017/484e4141/content/title.tmd.bak", F_OK) == 0);
if (!hnaaBackupExists)
{ {
messageBox("\x1B[31mError:\x1B[33m No original tmd found!\nCan't uninstall unlaunch.\n"); messageBox("\x1B[31mError:\x1B[33m No original tmd found!\nCan't uninstall unlaunch.\n");
return false; return false;
} }
remove(path); remove(path);
copyFile("nand:/title/00030017/484e4141/content/title.tmd.bak", path); rename(hnaaBackupTmdPath, path);
remove("nand:/title/00030017/484e4141/content/title.tmd.bak"); toggleFileReadOnly(path, false);
return true; return true;
} }
@ -170,7 +169,7 @@ static bool installUnlaunchRetailConsole(const char* retailLauncherTmdPath)
} }
// We have to remove write protect otherwise reinstalling will fail. // We have to remove write protect otherwise reinstalling will fail.
if (access(hnaaTmdPath, F_OK) == 0 && !toggleFileReadOnly(hnaaTmdPath, false)) { if (fileExists(hnaaTmdPath) && !toggleFileReadOnly(hnaaTmdPath, false)) {
messageBox("\x1B[31mError:\x1B[33m Can't remove launcher tmd write protect\n"); messageBox("\x1B[31mError:\x1B[33m Can't remove launcher tmd write protect\n");
return false; return false;
} }
@ -240,15 +239,15 @@ static bool installUnlaunchProtoConsole(void)
// Likely factory rejects that never had production firmware flashed. // Likely factory rejects that never had production firmware flashed.
// We have to remove write protect otherwise reinstalling will fail. // We have to remove write protect otherwise reinstalling will fail.
if (access(hnaaTmdPath, F_OK) == 0 && !toggleFileReadOnly(hnaaTmdPath, false)) { if (fileExists(hnaaTmdPath) && !toggleFileReadOnly(hnaaTmdPath, false)) {
messageBox("\x1B[31mError:\x1B[33m Can't remove launcher tmd write protect\n"); messageBox("\x1B[31mError:\x1B[33m Can't remove launcher tmd write protect\n");
return false; return false;
} }
bool hnaaBackupExists = (access("nand:/title/00030017/484e4141/content/title.tmd.bak", F_OK) == 0); bool hnaaBackupExists = fileExists(hnaaBackupTmdPath);
// Back up the TMD since we'll be writing to it directly. // Back up the TMD since we'll be writing to it directly.
if (!hnaaBackupExists) if (!hnaaBackupExists)
{ {
rename(hnaaTmdPath, "nand:/title/00030017/484e4141/content/title.tmd.bak"); rename(hnaaTmdPath, hnaaBackupTmdPath);
// Mark backup tmd as readonly, just to be sure // Mark backup tmd as readonly, just to be sure
toggleFileReadOnly("nand:/title/00030017/484e4141/content/title.tmd.bak", true); toggleFileReadOnly("nand:/title/00030017/484e4141/content/title.tmd.bak", true);
} }