diff --git a/Makefile b/Makefile index 14231f6..a580191 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ ifeq ($(strip $(DEVKITARM)),) $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") endif -export TARGET := $(shell basename $(CURDIR)) +export TARGET := NTM-unlaunch export TOPDIR := $(CURDIR) # specify a directory which contains the nitro filesystem diff --git a/arm9/src/main.c b/arm9/src/main.c index 7858c2f..0db9727 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -4,11 +4,14 @@ #include "nand/nandio.h" #include "storage.h" #include "version.h" +#include #include #include bool programEnd = false; bool sdnandMode = true; +bool hasTitleTmdMatchingLauncher = true; +bool unlaunchInstallerFound = false; bool unlaunchFound = false; bool unlaunchPatches = false; bool devkpFound = false; @@ -24,6 +27,8 @@ PrintConsole bottomScreen; enum { MAIN_MENU_MODE, MAIN_MENU_INSTALL, + MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL, + MAIN_MENU_SAFE_UNLAUNCH_INSTALL, MAIN_MENU_TITLES, MAIN_MENU_BACKUP, MAIN_MENU_TEST, @@ -77,6 +82,8 @@ static int _mainMenu(int cursor) sprintf(launcherStr, "\x1B[%02omUninstall region mod", launcherDSiFound ? 047 : 037); 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); @@ -123,6 +130,52 @@ void fifoHandlerBattery(u32 value32, void* userdata) 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; +} + +bool safeCreateDir(const char* path) +{ + if (((mkdir(path, 0777) == 0) || errno == EEXIST)) + return true; + + char errorStr[512]; + sprintf(errorStr, "\x1B[31mError:\x1B[33m Failed to create directory (%s)\n", path); + + messageBox(errorStr); + return false; +} + int main(int argc, char **argv) { srand(time(0)); @@ -152,8 +205,11 @@ int main(int argc, char **argv) messageBox("nand init \x1B[31mfailed\n\x1B[47m"); return 0; } + + unlaunchInstallerFound = (access("sd:/unlaunch.dsi", F_OK) == 0); //check for unlaunch and region + char launcherTmdPath[64]; { FILE *file = fopen("nand:/sys/HWINFO_S.dat", "rb"); if (file) @@ -165,37 +221,26 @@ int main(int argc, char **argv) region = launcherTid & 0xFF; - char path[64]; - sprintf(path, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid); - unsigned long long tmdSize = getFileSizePath(path); - if (tmdSize > 520) - unlaunchFound = true; - - //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) - }; - - FILE *tmd = fopen(path, "rb"); - if (tmd) + sprintf(launcherTmdPath, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid); + unsigned long long tmdSize = getFileSizePath(launcherTmdPath); + if(tmdSize != 520) { - for (int i = 0; i < sizeof(tidValues) / sizeof(tidValues[0]); i++) - { - if (fseek(file, tidValues[i][0], SEEK_SET) == 0) - { - u32 tidVal; - fread(&tidVal, sizeof(u32), 1, file); - if (tidVal == tidValues[i][1]) - { - unlaunchPatches = true; - break; - } - } - } + hasTitleTmdMatchingLauncher = false; + } + else if (tmdSize > 520) + { + unlaunchFound = true; + unlaunchPatches = checkIfUnlaunchHasPatches(launcherTmdPath); + } + } + + if (!unlaunchFound) + { + unsigned long long tmdSize = getFileSizePath("nand:/title/00030017/484e4141/content/title.tmd"); + if (tmdSize > 520) + { + unlaunchFound = true; + unlaunchPatches = checkIfUnlaunchHasPatches("nand:/title/00030017/484e4141/content/title.tmd"); } } @@ -236,6 +281,146 @@ int main(int argc, char **argv) installMenu(); break; + case MAIN_MENU_SAFE_UNLAUNCH_INSTALL: + if (unlaunchInstallerFound && (choiceBox("Install unlaunch?") == YES) + && (hasTitleTmdMatchingLauncher || (choiceBox("There doesn't seem to be a launcher.tmd\nfile matcing the hwinfo file\nKeep installing?") == YES)) + && nandio_unlock_writing()) + { + + FILE* unlaunchInstaller = fopen("sd:/unlaunch.dsi", "rb"); + if (!unlaunchInstaller) + { + messageBox("\x1B[31mError:\x1B[33m Failed to open unlaunch installer\n"); + nandio_lock_writing(); + break; + } + //Create HNAA launcher folder + if (!safeCreateDir("nand:/title/00030017") + || !safeCreateDir("nand:/title/00030017/484e4141") + || !safeCreateDir("nand:/title/00030017/484e4141/content")) { + nandio_lock_writing(); + break; + } + + FILE* targetTmd = fopen("nand:/title/00030017/484e4141/content/title.tmd", "wb"); + if (!targetTmd) + { + fclose(unlaunchInstaller); + messageBox("\x1B[31mError:\x1B[33m Failed to open target unlaunch tmd\n"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + break; + } + + { + char buffer[512] = {0}; + //write the first 512 bytes as 0, as that's the size of a tmd, but it can be whatever + if (fwrite(buffer, sizeof(char), 512, targetTmd) != 512) + { + fclose(unlaunchInstaller); + fclose(targetTmd); + messageBox("\x1B[31mError:\x1B[33m Failed write to target unlaunch tmd\n"); + remove("nand:/title/00030017/484e4141/content/title.tmd"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + break; + } + + size_t n; + bool failed = false; + + while ((n = fread(buffer, sizeof(char), sizeof(buffer), unlaunchInstaller)) > 0) + { + if (fwrite(buffer, sizeof(char), n, targetTmd) != n) + { + fclose(unlaunchInstaller); + fclose(targetTmd); + messageBox("\x1B[31mError:\x1B[33m Failed write to target unlaunch tmd\n"); + remove("nand:/title/00030017/484e4141/content/title.tmd"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + failed = true; + break; + } + } + if (failed) + break; + if (!feof(unlaunchInstaller) || ferror(unlaunchInstaller)) + { + fclose(unlaunchInstaller); + fclose(targetTmd); + messageBox("\x1B[31mError:\x1B[33m Failed read unlaunch installer\n"); + remove("nand:/title/00030017/484e4141/content/title.tmd"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + break; + } + } + fclose(unlaunchInstaller); + fclose(targetTmd); + + //Mark the tmd as readonly + int fatAttributes = FAT_getAttr("nand:/title/00030017/484e4141/content/title.tmd"); + if(!FAT_setAttr("nand:/title/00030017/484e4141/content/title.tmd", fatAttributes | ATTR_READONLY) != 0) + { + messageBox("\x1B[31mError:\x1B[33m Failed to mark unlaunch's title.tmd as read only\n"); + remove("nand:/title/00030017/484e4141/content/title.tmd"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + break; + } + + //Finally patch the default launcher tmd to be invalid + + //If there isn't a title.tmd matching the language region in the hwinfo + // nothing else has to be done, could be a language patch, or a dev system, the user will know what they have done + if (hasTitleTmdMatchingLauncher) + { + FILE* launcherTmd = fopen(launcherTmdPath, "rb"); + if(!launcherTmd) + { + messageBox("\x1B[31mError:\x1B[33m Failed to open default launcher's title.tmd\n"); + remove("nand:/title/00030017/484e4141/content/title.tmd"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + break; + } + FILE * f = fopen("title.tmd", "r+b"); + // Patches the title.tmd's title id from HNXX to GNXX + fseek(launcherTmd, 0x190, SEEK_SET); + char c; + fread(&c, 1, 1, launcherTmd); + //if byte is not already + if(c == 0x48) + { + fseek(launcherTmd, -1, SEEK_CUR); + c = 0x47; + fwrite(&launcherTmd, 1, 1, f); + } + else if(c != 0x47) + { + messageBox("\x1B[31mError:\x1B[33m Default launcher's title.tmd was tamprered with, aborting\n"); + remove("nand:/title/00030017/484e4141/content/title.tmd"); + rmdir("nand:/title/00030017/484e4141/content"); + rmdir("nand:/title/00030017/484e4141"); + nandio_lock_writing(); + fclose(launcherTmd); + break; + } + fclose(launcherTmd); + } + nandio_lock_writing(); + unlaunchFound = true; + messageBox("Unlaunch has been installed.\n"); + } + break; + case MAIN_MENU_TITLES: titleMenu(); break;