diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/arm9/src/main.c b/arm9/src/main.c index b3786d6..0134de1 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -1,555 +1,555 @@ -#include "bgMenu.h" -#include "main.h" -#include "menu.h" -#include "message.h" -#include "nand/nandio.h" -#include "storage.h" -#include "version.h" -#include "unlaunch.h" -#include "nitrofs.h" -#include "deviceList.h" -#include "nocashFooter.h" - -volatile bool programEnd = false; -static volatile bool arm7Exiting = false; -static bool unlaunchFound = false; -static bool hnaaUnlaunchFound = false; -static bool retailLauncherTmdPresentAndToBePatched = true; -static bool retailConsole = true; -static UNLAUNCH_VERSION foundUnlaunchInstallerVersion = INVALID; -static bool disableAllPatches = false; -static bool enableSoundAndSplash = false; -static const char* splashSoundBinaryPatchPath = NULL; -static const char* customBgPath = NULL; -volatile bool charging = false; -volatile u8 batteryLevel = 0; -static bool advancedOptionsUnlocked = false; -static bool needsNocashFooterToBeWritten = false; -static bool isLauncherVersionSupported = true; -static NocashFooter computedNocashFooter; - -PrintConsole topScreen; -PrintConsole bottomScreen; - -enum { - MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL, - MAIN_MENU_CUSTOM_BG, - MAIN_MENU_SOUND_SPLASH_PATCHES, - MAIN_MENU_SAFE_UNLAUNCH_INSTALL, - MAIN_MENU_EXIT, - MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL_NO_BACKUP, - MAIN_MENU_WRITE_NOCASH_FOOTER_ONLY, - MAIN_MENU_TID_PATCHES, -}; - -static void setupScreens() -{ - REG_DISPCNT = MODE_FB0; - VRAM_A_CR = VRAM_ENABLE; - - videoSetMode(MODE_0_2D); - videoSetModeSub(MODE_0_2D); - - vramSetBankA(VRAM_A_MAIN_BG); - vramSetBankC(VRAM_C_SUB_BG); - - consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true); - consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true); - - clearScreen(&bottomScreen); - - VRAM_A[100] = 0xFFFF; -} - -static int mainMenu(int cursor) -{ - //top screen - clearScreen(&topScreen); - - iprintf("\t\"Safe\" unlaunch installer\n"); - iprintf("\nversion %s\n", VERSION); - iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to" - "\nyour internal NAND!" - "\n\nThis always has a risk, albeit" - "\nlow, of \x1B[41mbricking\x1B[47m your system" - "\nand should be done with caution!\n"); - iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n"); - iprintf("\x1b[23;0Hedo9300 - 2024"); - - //menu - Menu* m = newMenu(); - setMenuHeader(m, "MAIN MENU"); - - char soundPatchesStr[64], tidPatchesStr[32], installUnlaunchStr[32]; - sprintf(tidPatchesStr, "Disable all patches: %s", - disableAllPatches ? "On" : "Off"); - sprintf(soundPatchesStr, "Enable sound and splash: %s", - enableSoundAndSplash ? "On" : "Off"); - if(foundUnlaunchInstallerVersion != INVALID) - { - sprintf(installUnlaunchStr, "Install unlaunch (%s)", getUnlaunchVersionString(foundUnlaunchInstallerVersion)); - } - else - { - strcpy(installUnlaunchStr, "Install unlaunch"); - } - addMenuItem(m, "Uninstall unlaunch", NULL, unlaunchFound && isLauncherVersionSupported, false); - addMenuItem(m, "Custom background", NULL, foundUnlaunchInstallerVersion != INVALID && isLauncherVersionSupported, true); - addMenuItem(m, soundPatchesStr, NULL, foundUnlaunchInstallerVersion == v2_0 && !disableAllPatches && splashSoundBinaryPatchPath != NULL && isLauncherVersionSupported, false); - addMenuItem(m, installUnlaunchStr, NULL, foundUnlaunchInstallerVersion != INVALID && !unlaunchFound && isLauncherVersionSupported, false); - addMenuItem(m, "Exit", NULL, true, false); - if(!isLauncherVersionSupported) - { - addMenuItem(m, "Uninstall unlaunch no backup", NULL, unlaunchFound, false); - } - else if(advancedOptionsUnlocked) - { - addMenuItem(m, "Uninstall unlaunch no backup", NULL, unlaunchFound, false); - addMenuItem(m, "Write nocash footer", NULL, needsNocashFooterToBeWritten, false); - addMenuItem(m, tidPatchesStr, NULL, (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) && isLauncherVersionSupported, false); - } - - m->cursor = cursor; - - //bottom screen - printMenu(m); - - int konamiCode = 0; - bool konamiCodeCooldown = false; - - while (!programEnd) - { - swiWaitForVBlank(); - scanKeys(); - - if (moveCursor(m)) - printMenu(m); - - if (keysDown() & KEY_A) - break; - - if(advancedOptionsUnlocked) - continue; - - int held = keysHeld(); - - if ((held & (KEY_L | KEY_R | KEY_Y)) == (KEY_L | KEY_R | KEY_Y)) - { - if(held == (KEY_L | KEY_R | KEY_Y) && !konamiCodeCooldown) - { - konamiCodeCooldown = true; - ++konamiCode; - } - } - else - { - konamiCodeCooldown = false; - } - if (konamiCode == 5) - { - advancedOptionsUnlocked = true; - // Enabled by default when unsupported - if(isLauncherVersionSupported) - { - addMenuItem(m, "Uninstall unlaunch no backup", NULL, unlaunchFound, false); - } - addMenuItem(m, "Write nocash footer", NULL, needsNocashFooterToBeWritten, false); - addMenuItem(m, tidPatchesStr, NULL, (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) && isLauncherVersionSupported, false); - } - } - - int result = m->cursor; - freeMenu(m); - - return result; -} - -static void fifoHandlerPower(u32 value32, void* userdata) -{ - if (value32 == 0x54495845) // 'EXIT' - { - programEnd = true; - arm7Exiting = true; - } -} - -static void fifoHandlerBattery(u32 value32, void* userdata) -{ - batteryLevel = value32 & 0xF; - charging = (value32 & BIT(7)) != 0; -} - -int main(int argc, char **argv) -{ - keysSetRepeat(25, 5); - setupScreens(); - - fifoSetValue32Handler(FIFO_USER_01, fifoHandlerPower, NULL); - fifoSetValue32Handler(FIFO_USER_03, fifoHandlerBattery, NULL); - - //DSi check - if (!isDSiMode()) - { - messageBox("\x1B[31mError:\x1B[33m This app is exclusively for DSi."); - return 0; - } - - //setup sd card access - if (!fatInitDefault()) - { - messageBox("fatInitDefault()...\x1B[31mFailed\n\x1B[47m"); - } - - u32 clusterSize = getClusterSizeForPartition("sd:/"); - if(clusterSize > 32768) - { - messageBox("Sd card cluster size is too large"); - return 0; - } - - //setup nand access - if (!fatMountSimple("nand", &io_dsi_nand)) - { - messageBox("nand init \x1B[31mfailed\n\x1B[47m"); - return 0; - } - - NocashFooter footer; - - nandio_read_nocash_footer(&footer); - nandio_construct_nocash_footer(&computedNocashFooter); - - if(!(needsNocashFooterToBeWritten = !isFooterValid(&footer))) - { - if(memcmp(&footer, &computedNocashFooter, sizeof(footer)) != 0) - { - messageBox("\x1B[31mError:\x1B[33m This console has a\n" - "nocash footer embedded in its\n" - "nand that doesn't match the one\n" - "generated.\n" - "The footer already present will\n" - "be overwritten."); - needsNocashFooterToBeWritten = true; - } - } - - while (batteryLevel < 7 && !charging) - { - if (choiceBox("\x1B[47mBattery is too low!\nPlease plug in the console.\n\nContinue?") == NO) - return 0; - } - - DeviceList* deviceList = getDeviceList(); - - const char* installerPath = (argc > 0) ? argv[0] : (deviceList ? deviceList->appname : "sd:/ntrboot.nds"); - - if (!nitroFSInit(installerPath)) - { - messageBox("nitroFSInit()...\x1B[31mFailed\n\x1B[47m"); - } - - if (fileExists("sd:/unlaunch.dsi")) - { - foundUnlaunchInstallerVersion = loadUnlaunchInstaller("sd:/unlaunch.dsi"); - if (foundUnlaunchInstallerVersion == INVALID) - { - messageBox("\x1B[41mWARNING:\x1B[47m Failed to load unlaunch.dsi\n" - "from the root of the sd card.\n" - "Attempting to use the bundled one."); - } - } - - if(foundUnlaunchInstallerVersion == INVALID) - { - foundUnlaunchInstallerVersion = loadUnlaunchInstaller("nitro:/unlaunch.dsi"); - if (foundUnlaunchInstallerVersion == INVALID) - { - messageBox("\x1B[41mWARNING:\x1B[47m Failed to load bundled unlaunch\n" - "installer.\n" - "Installing unlaunch won't be possible."); - } - } - if (fileExists("sd:/unlaunch-patch.bin")) { - splashSoundBinaryPatchPath = "sd:/unlaunch-patch.bin"; - } - else if(fileExists("nitro:/unlaunch-patch.bin")) - { - splashSoundBinaryPatchPath = "nitro:/unlaunch-patch.bin"; - } - - //check for unlaunch and region - u8 region = 0xff; - char retailLauncherTmdPath[64]; - char retailLauncherPath[64]; - const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd"; - { - FILE* file = fopen("nand:/sys/HWINFO_S.dat", "rb"); - bool mainTmdIsPatched = false; - if (file) - { - fseek(file, 0xA0, SEEK_SET); - u32 launcherTid; - fread(&launcherTid, sizeof(u32), 1, file); - fclose(file); - - region = launcherTid & 0xFF; - - sprintf(retailLauncherTmdPath, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid); - FILE* tmd = fopen(retailLauncherTmdPath, "rb"); - unsigned long long tmdSize = getFileSize(tmd); - if(!tmd || tmdSize < 520) - { - //if size isn't 520 then the tmd either is not present, or is already invalid, thus no need to patch - retailLauncherTmdPresentAndToBePatched = false; - } - else - { - if (tmdSize > 520) - { - unlaunchFound = true; - } - else - { - mainTmdIsPatched = isLauncherTmdPatched(retailLauncherTmdPath); - } - fseek(tmd, 0x1DC, SEEK_SET); - unsigned short launcherVersion; - fread(&launcherVersion, sizeof(launcherVersion), 1, tmd); - // Launcher v4, build v1024 (shipped with firmware 1.4.2 (not sure about J, and 1.4.3 for china) - // will fail to launch if another tmd withouth appropriate application, or an invalid - // tmd (in our case the one installed from unlaunch) is found in the HNAA launcher folder - // there's really no workaround to that, so that specific version is blacklisted and only uninstalling - // an "officially" installed unlaunch without leaving any backup behind will be allowed - if(launcherVersion == 4) - { - isLauncherVersionSupported = false; - messageBox("\x1B[41mWARNING:\x1B[47m This system version\n" - "doesn't support this install\n" - "method, only uninstalling\n" - "unaunch without backups will\n" - "be possible"); - } - else if (launcherVersion > 7) - { - char messageBoxError[128]; - sprintf(messageBoxError, "\x1B[41mWARNING:\x1B[47m This system version (%d)\n" - "is unknown\n" - "nothing will be done", (int)launcherVersion); - messageBox("\x1B[41mWARNING:\x1B[47m This system version\n" - "is unknown\n" - "nothing will be done"); - goto abort; - } - sprintf(retailLauncherPath, "nand:/title/00030017/%08lx/content/0000000%d.app", launcherTid, (int)launcherVersion); - } - if(tmd) - { - fclose(tmd); - } - // HWINFO_S may not always exist (PRE_IMPORT). Fill in defaults if that happens. - } - - // I own and know of many people with retail and dev prototypes - // These can normally be identified by having the region set to ALL (0x41) - retailConsole = (region != 0x41 && region != 0xFF); - - unsigned long long tmdSize = getFileSizePath(hnaaTmdPath); - if (tmdSize > 520) - { - unlaunchFound = unlaunchFound || (mainTmdIsPatched || !retailConsole); - hnaaUnlaunchFound = true; - } - } - - messageBox("\x1B[41mWARNING:\x1B[47m This tool can write to\n" - "your internal NAND!\n\n" - "This always has a risk, albeit\n" - "low, of \x1B[41mbricking\x1B[47m your system\n" - "and should be done with caution!\n\n" - "If you have not yet done so,\n" - "you should make a NAND backup."); - //main menu - int cursor = 0; - - while (!programEnd) - { - cursor = mainMenu(cursor); - - switch (cursor) - { - case MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL: - case MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL_NO_BACKUP: - bool unsafeUninstall = (advancedOptionsUnlocked || !isLauncherVersionSupported) && cursor == MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL_NO_BACKUP; - if(!unlaunchFound || !unsafeUninstall) - { - break; - } - if(!nandio_unlock_writing()) - { - break; - } - printf("Uninstalling"); - if(needsNocashFooterToBeWritten) - { - printf("Writing nocash footer\n"); - if(!nandio_write_nocash_footer(&computedNocashFooter)) - { - nandio_lock_writing(); - messageBox("Failed to write nocash footer"); - break; - } - needsNocashFooterToBeWritten = false; - } - if(uninstallUnlaunch(retailConsole, hnaaUnlaunchFound, retailLauncherTmdPath, retailLauncherPath, unsafeUninstall)) - { - messageBox("Uninstall successful!\n"); - unlaunchFound = false; - } - else - { - messageBox("\x1B[31mError:\x1B[33m Uninstall failed\n"); - } - nandio_lock_writing(); - printf("Synchronizing FAT tables...\n"); - nandio_synchronize_fats(); - break; - - case MAIN_MENU_CUSTOM_BG: - if(!isLauncherVersionSupported) - { - break; - } - if(foundUnlaunchInstallerVersion == INVALID) - { - break; - } - const char* customBg = backgroundMenu(); - if(!customBg) - { - break; - } - if(strcmp(customBg, "default") == 0) - { - customBgPath = NULL; - } - else - { - customBgPath = customBg; - } - break; - - case MAIN_MENU_TID_PATCHES: - if(!isLauncherVersionSupported) - { - break; - } - if(advancedOptionsUnlocked && (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0)) { - disableAllPatches = !disableAllPatches; - } - break; - - case MAIN_MENU_SOUND_SPLASH_PATCHES: - if(!isLauncherVersionSupported) - { - break; - } - if(foundUnlaunchInstallerVersion == v2_0 && !disableAllPatches && splashSoundBinaryPatchPath != NULL) { - enableSoundAndSplash = !enableSoundAndSplash; - } - break; - - case MAIN_MENU_SAFE_UNLAUNCH_INSTALL: - if(!isLauncherVersionSupported) - { - break; - } - if(unlaunchFound || foundUnlaunchInstallerVersion == INVALID) - { - break; - } - if(choiceBox("Install unlaunch?") == NO) - { - break; - } - if(!retailLauncherTmdPresentAndToBePatched - && (choiceBox("There doesn't seem to be a launcher.tmd\n" - "file matcing the hwinfo file\n" - "Keep installing?") == NO)) - { - break; - } - if(!nandio_unlock_writing()) - { - break; - } - printf("Installing\n"); - if(needsNocashFooterToBeWritten) - { - printf("Writing nocash footer\n"); - if(!nandio_write_nocash_footer(&computedNocashFooter)) - { - nandio_lock_writing(); - messageBox("Failed to write nocash footer"); - break; - } - needsNocashFooterToBeWritten = false; - } - if(installUnlaunch(retailConsole, - retailLauncherTmdPresentAndToBePatched ? retailLauncherTmdPath : NULL, - retailLauncherTmdPresentAndToBePatched ? retailLauncherPath : NULL, - disableAllPatches, - enableSoundAndSplash ? splashSoundBinaryPatchPath : NULL, - customBgPath)) - { - messageBox("Install successful!\n"); - unlaunchFound = true; - } - else - { - messageBox("\x1B[31mError:\x1B[33m Install failed\n"); - } - nandio_lock_writing(); - printf("Synchronizing FAT tables...\n"); - nandio_synchronize_fats(); - break; - - case MAIN_MENU_WRITE_NOCASH_FOOTER_ONLY: - if(!needsNocashFooterToBeWritten) - { - break; - } - if(!nandio_write_nocash_footer(&computedNocashFooter)) - { - messageBox("Failed to write nocash footer"); - break; - } - needsNocashFooterToBeWritten = false; - break; - - case MAIN_MENU_EXIT: - programEnd = true; - break; - } - } - - abort: - - clearScreen(&bottomScreen); - printf("Unmounting NAND...\n"); - fatUnmount("nand:"); - printf("Merging stages...\n"); - nandio_shutdown(); - - fifoSendValue32(FIFO_USER_02, 0x54495845); // 'EXIT' - - while (arm7Exiting) - swiWaitForVBlank(); - - return 0; -} - -void clearScreen(PrintConsole* screen) -{ - consoleSelect(screen); - consoleClear(); +#include "bgMenu.h" +#include "main.h" +#include "menu.h" +#include "message.h" +#include "nand/nandio.h" +#include "storage.h" +#include "version.h" +#include "unlaunch.h" +#include "nitrofs.h" +#include "deviceList.h" +#include "nocashFooter.h" + +volatile bool programEnd = false; +static volatile bool arm7Exiting = false; +static bool unlaunchFound = false; +static bool hnaaUnlaunchFound = false; +static bool retailLauncherTmdPresentAndToBePatched = true; +static bool retailConsole = true; +static UNLAUNCH_VERSION foundUnlaunchInstallerVersion = INVALID; +static bool disableAllPatches = false; +static bool enableSoundAndSplash = false; +static const char* splashSoundBinaryPatchPath = NULL; +static const char* customBgPath = NULL; +volatile bool charging = false; +volatile u8 batteryLevel = 0; +static bool advancedOptionsUnlocked = false; +static bool needsNocashFooterToBeWritten = false; +static bool isLauncherVersionSupported = true; +static NocashFooter computedNocashFooter; + +PrintConsole topScreen; +PrintConsole bottomScreen; + +enum { + MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL, + MAIN_MENU_CUSTOM_BG, + MAIN_MENU_SOUND_SPLASH_PATCHES, + MAIN_MENU_SAFE_UNLAUNCH_INSTALL, + MAIN_MENU_EXIT, + MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL_NO_BACKUP, + MAIN_MENU_WRITE_NOCASH_FOOTER_ONLY, + MAIN_MENU_TID_PATCHES, +}; + +static void setupScreens() +{ + REG_DISPCNT = MODE_FB0; + VRAM_A_CR = VRAM_ENABLE; + + videoSetMode(MODE_0_2D); + videoSetModeSub(MODE_0_2D); + + vramSetBankA(VRAM_A_MAIN_BG); + vramSetBankC(VRAM_C_SUB_BG); + + consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true); + consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true); + + clearScreen(&bottomScreen); + + VRAM_A[100] = 0xFFFF; +} + +static int mainMenu(int cursor) +{ + //top screen + clearScreen(&topScreen); + + iprintf("\t\"Safe\" unlaunch installer\n"); + iprintf("\nversion %s\n", VERSION); + iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to" + "\nyour internal NAND!" + "\n\nThis always has a risk, albeit" + "\nlow, of \x1B[41mbricking\x1B[47m your system" + "\nand should be done with caution!\n"); + iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n"); + iprintf("\x1b[23;0Hedo9300 - 2024"); + + //menu + Menu* m = newMenu(); + setMenuHeader(m, "MAIN MENU"); + + char soundPatchesStr[64], tidPatchesStr[32], installUnlaunchStr[32]; + sprintf(tidPatchesStr, "Disable all patches: %s", + disableAllPatches ? "On" : "Off"); + sprintf(soundPatchesStr, "Enable sound and splash: %s", + enableSoundAndSplash ? "On" : "Off"); + if(foundUnlaunchInstallerVersion != INVALID) + { + sprintf(installUnlaunchStr, "Install unlaunch (%s)", getUnlaunchVersionString(foundUnlaunchInstallerVersion)); + } + else + { + strcpy(installUnlaunchStr, "Install unlaunch"); + } + addMenuItem(m, "Uninstall unlaunch", NULL, unlaunchFound && isLauncherVersionSupported, false); + addMenuItem(m, "Custom background", NULL, foundUnlaunchInstallerVersion != INVALID && isLauncherVersionSupported, true); + addMenuItem(m, soundPatchesStr, NULL, foundUnlaunchInstallerVersion == v2_0 && !disableAllPatches && splashSoundBinaryPatchPath != NULL && isLauncherVersionSupported, false); + addMenuItem(m, installUnlaunchStr, NULL, foundUnlaunchInstallerVersion != INVALID && !unlaunchFound && isLauncherVersionSupported, false); + addMenuItem(m, "Exit", NULL, true, false); + if(!isLauncherVersionSupported) + { + addMenuItem(m, "Uninstall unlaunch no backup", NULL, unlaunchFound, false); + } + else if(advancedOptionsUnlocked) + { + addMenuItem(m, "Uninstall unlaunch no backup", NULL, unlaunchFound, false); + addMenuItem(m, "Write nocash footer", NULL, needsNocashFooterToBeWritten, false); + addMenuItem(m, tidPatchesStr, NULL, (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) && isLauncherVersionSupported, false); + } + + m->cursor = cursor; + + //bottom screen + printMenu(m); + + int konamiCode = 0; + bool konamiCodeCooldown = false; + + while (!programEnd) + { + swiWaitForVBlank(); + scanKeys(); + + if (moveCursor(m)) + printMenu(m); + + if (keysDown() & KEY_A) + break; + + if(advancedOptionsUnlocked) + continue; + + int held = keysHeld(); + + if ((held & (KEY_L | KEY_R | KEY_Y)) == (KEY_L | KEY_R | KEY_Y)) + { + if(held == (KEY_L | KEY_R | KEY_Y) && !konamiCodeCooldown) + { + konamiCodeCooldown = true; + ++konamiCode; + } + } + else + { + konamiCodeCooldown = false; + } + if (konamiCode == 5) + { + advancedOptionsUnlocked = true; + // Enabled by default when unsupported + if(isLauncherVersionSupported) + { + addMenuItem(m, "Uninstall unlaunch no backup", NULL, unlaunchFound, false); + } + addMenuItem(m, "Write nocash footer", NULL, needsNocashFooterToBeWritten, false); + addMenuItem(m, tidPatchesStr, NULL, (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) && isLauncherVersionSupported, false); + } + } + + int result = m->cursor; + freeMenu(m); + + return result; +} + +static void fifoHandlerPower(u32 value32, void* userdata) +{ + if (value32 == 0x54495845) // 'EXIT' + { + programEnd = true; + arm7Exiting = true; + } +} + +static void fifoHandlerBattery(u32 value32, void* userdata) +{ + batteryLevel = value32 & 0xF; + charging = (value32 & BIT(7)) != 0; +} + +int main(int argc, char **argv) +{ + keysSetRepeat(25, 5); + setupScreens(); + + fifoSetValue32Handler(FIFO_USER_01, fifoHandlerPower, NULL); + fifoSetValue32Handler(FIFO_USER_03, fifoHandlerBattery, NULL); + + //DSi check + if (!isDSiMode()) + { + messageBox("\x1B[31mError:\x1B[33m This app is exclusively for DSi."); + return 0; + } + + //setup sd card access + if (!fatInitDefault()) + { + messageBox("fatInitDefault()...\x1B[31mFailed\n\x1B[47m"); + } + + u32 clusterSize = getClusterSizeForPartition("sd:/"); + if(clusterSize > 32768) + { + messageBox("Sd card cluster size is too large"); + return 0; + } + + //setup nand access + if (!fatMountSimple("nand", &io_dsi_nand)) + { + messageBox("nand init \x1B[31mfailed\n\x1B[47m"); + return 0; + } + + NocashFooter footer; + + nandio_read_nocash_footer(&footer); + nandio_construct_nocash_footer(&computedNocashFooter); + + if(!(needsNocashFooterToBeWritten = !isFooterValid(&footer))) + { + if(memcmp(&footer, &computedNocashFooter, sizeof(footer)) != 0) + { + messageBox("\x1B[31mError:\x1B[33m This console has a\n" + "nocash footer embedded in its\n" + "nand that doesn't match the one\n" + "generated.\n" + "The footer already present will\n" + "be overwritten."); + needsNocashFooterToBeWritten = true; + } + } + + while (batteryLevel < 7 && !charging) + { + if (choiceBox("\x1B[47mBattery is too low!\nPlease plug in the console.\n\nContinue?") == NO) + return 0; + } + + DeviceList* deviceList = getDeviceList(); + + const char* installerPath = (argc > 0) ? argv[0] : (deviceList ? deviceList->appname : "sd:/ntrboot.nds"); + + if (!nitroFSInit(installerPath)) + { + messageBox("nitroFSInit()...\x1B[31mFailed\n\x1B[47m"); + } + + if (fileExists("sd:/unlaunch.dsi")) + { + foundUnlaunchInstallerVersion = loadUnlaunchInstaller("sd:/unlaunch.dsi"); + if (foundUnlaunchInstallerVersion == INVALID) + { + messageBox("\x1B[41mWARNING:\x1B[47m Failed to load unlaunch.dsi\n" + "from the root of the sd card.\n" + "Attempting to use the bundled one."); + } + } + + if(foundUnlaunchInstallerVersion == INVALID) + { + foundUnlaunchInstallerVersion = loadUnlaunchInstaller("nitro:/unlaunch.dsi"); + if (foundUnlaunchInstallerVersion == INVALID) + { + messageBox("\x1B[41mWARNING:\x1B[47m Failed to load bundled unlaunch\n" + "installer.\n" + "Installing unlaunch won't be possible."); + } + } + if (fileExists("sd:/unlaunch-patch.bin")) { + splashSoundBinaryPatchPath = "sd:/unlaunch-patch.bin"; + } + else if(fileExists("nitro:/unlaunch-patch.bin")) + { + splashSoundBinaryPatchPath = "nitro:/unlaunch-patch.bin"; + } + + //check for unlaunch and region + u8 region = 0xff; + char retailLauncherTmdPath[64]; + char retailLauncherPath[64]; + const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd"; + { + FILE* file = fopen("nand:/sys/HWINFO_S.dat", "rb"); + bool mainTmdIsPatched = false; + if (file) + { + fseek(file, 0xA0, SEEK_SET); + u32 launcherTid; + fread(&launcherTid, sizeof(u32), 1, file); + fclose(file); + + region = launcherTid & 0xFF; + + sprintf(retailLauncherTmdPath, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid); + FILE* tmd = fopen(retailLauncherTmdPath, "rb"); + unsigned long long tmdSize = getFileSize(tmd); + if(!tmd || tmdSize < 520) + { + //if size isn't 520 then the tmd either is not present, or is already invalid, thus no need to patch + retailLauncherTmdPresentAndToBePatched = false; + } + else + { + if (tmdSize > 520) + { + unlaunchFound = true; + } + else + { + mainTmdIsPatched = isLauncherTmdPatched(retailLauncherTmdPath); + } + fseek(tmd, 0x1DC, SEEK_SET); + unsigned short launcherVersion; + fread(&launcherVersion, sizeof(launcherVersion), 1, tmd); + // Launcher v4, build v1024 (shipped with firmware 1.4.2 (not sure about J, and 1.4.3 for china) + // will fail to launch if another tmd withouth appropriate application, or an invalid + // tmd (in our case the one installed from unlaunch) is found in the HNAA launcher folder + // there's really no workaround to that, so that specific version is blacklisted and only uninstalling + // an "officially" installed unlaunch without leaving any backup behind will be allowed + if(launcherVersion == 4) + { + isLauncherVersionSupported = false; + messageBox("\x1B[41mWARNING:\x1B[47m This system version\n" + "doesn't support this install\n" + "method, only uninstalling\n" + "unaunch without backups will\n" + "be possible"); + } + else if (launcherVersion > 7) + { + char messageBoxError[128]; + sprintf(messageBoxError, "\x1B[41mWARNING:\x1B[47m This system version (%d)\n" + "is unknown\n" + "nothing will be done", (int)launcherVersion); + messageBox("\x1B[41mWARNING:\x1B[47m This system version\n" + "is unknown\n" + "nothing will be done"); + goto abort; + } + sprintf(retailLauncherPath, "nand:/title/00030017/%08lx/content/0000000%d.app", launcherTid, (int)launcherVersion); + } + if(tmd) + { + fclose(tmd); + } + // HWINFO_S may not always exist (PRE_IMPORT). Fill in defaults if that happens. + } + + // I own and know of many people with retail and dev prototypes + // These can normally be identified by having the region set to ALL (0x41) + retailConsole = (region != 0x41 && region != 0xFF); + + unsigned long long tmdSize = getFileSizePath(hnaaTmdPath); + if (tmdSize > 520) + { + unlaunchFound = unlaunchFound || (mainTmdIsPatched || !retailConsole); + hnaaUnlaunchFound = true; + } + } + + messageBox("\x1B[41mWARNING:\x1B[47m This tool can write to\n" + "your internal NAND!\n\n" + "This always has a risk, albeit\n" + "low, of \x1B[41mbricking\x1B[47m your system\n" + "and should be done with caution!\n\n" + "If you have not yet done so,\n" + "you should make a NAND backup."); + //main menu + int cursor = 0; + + while (!programEnd) + { + cursor = mainMenu(cursor); + + switch (cursor) + { + case MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL: + case MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL_NO_BACKUP: + bool unsafeUninstall = (advancedOptionsUnlocked || !isLauncherVersionSupported) && cursor == MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL_NO_BACKUP; + if(!unlaunchFound || !unsafeUninstall) + { + break; + } + if(!nandio_unlock_writing()) + { + break; + } + printf("Uninstalling"); + if(needsNocashFooterToBeWritten) + { + printf("Writing nocash footer\n"); + if(!nandio_write_nocash_footer(&computedNocashFooter)) + { + nandio_lock_writing(); + messageBox("Failed to write nocash footer"); + break; + } + needsNocashFooterToBeWritten = false; + } + if(uninstallUnlaunch(retailConsole, hnaaUnlaunchFound, retailLauncherTmdPath, retailLauncherPath, unsafeUninstall)) + { + messageBox("Uninstall successful!\n"); + unlaunchFound = false; + } + else + { + messageBox("\x1B[31mError:\x1B[33m Uninstall failed\n"); + } + nandio_lock_writing(); + printf("Synchronizing FAT tables...\n"); + nandio_synchronize_fats(); + break; + + case MAIN_MENU_CUSTOM_BG: + if(!isLauncherVersionSupported) + { + break; + } + if(foundUnlaunchInstallerVersion == INVALID) + { + break; + } + const char* customBg = backgroundMenu(); + if(!customBg) + { + break; + } + if(strcmp(customBg, "default") == 0) + { + customBgPath = NULL; + } + else + { + customBgPath = customBg; + } + break; + + case MAIN_MENU_TID_PATCHES: + if(!isLauncherVersionSupported) + { + break; + } + if(advancedOptionsUnlocked && (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0)) { + disableAllPatches = !disableAllPatches; + } + break; + + case MAIN_MENU_SOUND_SPLASH_PATCHES: + if(!isLauncherVersionSupported) + { + break; + } + if(foundUnlaunchInstallerVersion == v2_0 && !disableAllPatches && splashSoundBinaryPatchPath != NULL) { + enableSoundAndSplash = !enableSoundAndSplash; + } + break; + + case MAIN_MENU_SAFE_UNLAUNCH_INSTALL: + if(!isLauncherVersionSupported) + { + break; + } + if(unlaunchFound || foundUnlaunchInstallerVersion == INVALID) + { + break; + } + if(choiceBox("Install unlaunch?") == NO) + { + break; + } + if(!retailLauncherTmdPresentAndToBePatched + && (choiceBox("There doesn't seem to be a launcher.tmd\n" + "file matcing the hwinfo file\n" + "Keep installing?") == NO)) + { + break; + } + if(!nandio_unlock_writing()) + { + break; + } + printf("Installing\n"); + if(needsNocashFooterToBeWritten) + { + printf("Writing nocash footer\n"); + if(!nandio_write_nocash_footer(&computedNocashFooter)) + { + nandio_lock_writing(); + messageBox("Failed to write nocash footer"); + break; + } + needsNocashFooterToBeWritten = false; + } + if(installUnlaunch(retailConsole, + retailLauncherTmdPresentAndToBePatched ? retailLauncherTmdPath : NULL, + retailLauncherTmdPresentAndToBePatched ? retailLauncherPath : NULL, + disableAllPatches, + enableSoundAndSplash ? splashSoundBinaryPatchPath : NULL, + customBgPath)) + { + messageBox("Install successful!\n"); + unlaunchFound = true; + } + else + { + messageBox("\x1B[31mError:\x1B[33m Install failed\n"); + } + nandio_lock_writing(); + printf("Synchronizing FAT tables...\n"); + nandio_synchronize_fats(); + break; + + case MAIN_MENU_WRITE_NOCASH_FOOTER_ONLY: + if(!needsNocashFooterToBeWritten) + { + break; + } + if(!nandio_write_nocash_footer(&computedNocashFooter)) + { + messageBox("Failed to write nocash footer"); + break; + } + needsNocashFooterToBeWritten = false; + break; + + case MAIN_MENU_EXIT: + programEnd = true; + break; + } + } + + abort: + + clearScreen(&bottomScreen); + printf("Unmounting NAND...\n"); + fatUnmount("nand:"); + printf("Merging stages...\n"); + nandio_shutdown(); + + fifoSendValue32(FIFO_USER_02, 0x54495845); // 'EXIT' + + while (arm7Exiting) + swiWaitForVBlank(); + + return 0; +} + +void clearScreen(PrintConsole* screen) +{ + consoleSelect(screen); + consoleClear(); } \ No newline at end of file diff --git a/arm9/src/main.h b/arm9/src/main.h index 2ff3ede..b86ee00 100644 --- a/arm9/src/main.h +++ b/arm9/src/main.h @@ -1,25 +1,25 @@ -#ifndef MAIN_H -#define MAIN_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern volatile bool programEnd; -extern volatile bool charging; -extern volatile u8 batteryLevel; - -extern PrintConsole topScreen; -extern PrintConsole bottomScreen; - -void clearScreen(PrintConsole* screen); - -#ifdef __cplusplus -} -#endif - +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern volatile bool programEnd; +extern volatile bool charging; +extern volatile u8 batteryLevel; + +extern PrintConsole topScreen; +extern PrintConsole bottomScreen; + +void clearScreen(PrintConsole* screen); + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/arm9/src/menu.c b/arm9/src/menu.c index df2cf4b..6c589b2 100644 --- a/arm9/src/menu.c +++ b/arm9/src/menu.c @@ -1,237 +1,237 @@ -#include "menu.h" -#include "main.h" - -#define sign(X) ( ((X) > 0) - ((X) < 0) ) -#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++) - -Menu* newMenu() -{ - Menu* m = (Menu*)malloc(sizeof(Menu)); - - m->cursor = 0; - m->page = 0; - m->itemCount = 0; - m->nextPage = false; - m->changePage = 0; - m->header[0] = '\0'; - - for (int i = 0; i < ITEMS_PER_PAGE; i++) - { - m->items[i].directory = false; - m->items[i].label = NULL; - m->items[i].value = NULL; - } - - return m; -} - -void freeMenu(Menu* m) -{ - if (!m) return; - - clearMenu(m); - - free(m); - m = NULL; -} - -void addMenuItem(Menu* m, char const* label, char const* value, bool enabled, bool directory) -{ - if (!m) return; - - int i = m->itemCount; - if (i >= ITEMS_PER_PAGE) return; - - m->items[i].directory = directory; - m->items[i].enabled = enabled; - - if (label) - { - m->items[i].label = (char*)malloc(32); - sprintf(m->items[i].label, "%.31s", label); - } - - if (value) - { - m->items[i].value = (char*)malloc(strlen(value)+1); - sprintf(m->items[i].value, "%s", value); - } - - m->itemCount += 1; -} - -static int alphabeticalCompare(const void* a, const void* b) -{ - const Item* itemA = (const Item*)a; - const Item* itemB = (const Item*)b; - - if (itemA->directory && !itemB->directory) - return -1; - else if (!itemA->directory && itemB->directory) - return 1; - else - return strcasecmp(itemA->label, itemB->label); -} - -void sortMenuItems(Menu* m) -{ - qsort(m->items, m->itemCount, sizeof(Item), alphabeticalCompare); -} - -void setMenuHeader(Menu* m, const char* str) -{ - if (!m) return; - - if (!str) - { - m->header[0] = '\0'; - return; - } - - const char* strPtr = str; - - if (strlen(strPtr) > 30) - strPtr = str + (strlen(strPtr) - 30); - - sprintf(m->header, "%.30s", strPtr); -} - -void resetMenu(Menu* m) -{ - m->cursor = 0; - m->page = 0; - m->changePage = 0; - m->nextPage = 0; -} - -void clearMenu(Menu* m) -{ - if (!m) return; - - for (int i = 0; i < ITEMS_PER_PAGE; i++) - { - if (m->items[i].label) - { - free(m->items[i].label); - m->items[i].label = NULL; - } - - if (m->items[i].value) - { - free(m->items[i].value); - m->items[i].value = NULL; - } - } - - m->itemCount = 0; -} - -void printMenu(Menu* m) -{ - clearScreen(&bottomScreen); - - if (!m) return; - - //header - iprintf("\x1B[42m"); //green - iprintf("%.30s\n\n", m->header); - iprintf("\x1B[47m"); //white - - if (m->itemCount <= 0) - { - iprintf("Back - [B]\n"); - return; - } - - //items - for (int i = 0; i < m->itemCount; i++) - { - if (m->items[i].label) - { - if(!m->items[i].enabled) - iprintf("\x1B[37m"); //gray - - if (m->items[i].directory) - iprintf(" [%.26s]\n", m->items[i].label); - else - iprintf(" %.28s\n", m->items[i].label); - - if(!m->items[i].enabled) - iprintf("\x1B[47m"); //white - } - else - iprintf(" \n"); - } - - //cursor - iprintf("\x1b[%d;0H>", 2 + m->cursor); - - //scroll arrows - if (m->page > 0) - iprintf("\x1b[2;31H^"); - - if (m->nextPage) - iprintf("\x1b[21;31Hv"); -} - -static void _moveCursor(Menu* m, int dir) -{ - if (m->changePage != 0) - return; - - m->cursor += sign(dir); - - if (m->cursor < 0) - { - if (m->page <= 0) - m->cursor = 0; - else - { - m->changePage = -1; - m->cursor = ITEMS_PER_PAGE - 1; - } - } - - else if (m->cursor > m->itemCount-1) - { - if (m->nextPage && m->cursor >= ITEMS_PER_PAGE) - { - m->changePage = 1; - m->cursor = 0; - } - else - { - m->cursor = m->itemCount-1; - } - } -} - -bool moveCursor(Menu* m) -{ - if (!m) return false; - - m->changePage = 0; - int lastCursor = m->cursor; - - u32 down = keysDownRepeat(); - - if (down & KEY_DOWN) - _moveCursor(m, 1); - - else if (down & KEY_UP) - _moveCursor(m, -1); - - if (down & KEY_RIGHT) - { - repeat(10) - _moveCursor(m, 1); - } - - else if (down & KEY_LEFT) - { - repeat(10) - _moveCursor(m, -1); - } - - return !(lastCursor == m->cursor); +#include "menu.h" +#include "main.h" + +#define sign(X) ( ((X) > 0) - ((X) < 0) ) +#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++) + +Menu* newMenu() +{ + Menu* m = (Menu*)malloc(sizeof(Menu)); + + m->cursor = 0; + m->page = 0; + m->itemCount = 0; + m->nextPage = false; + m->changePage = 0; + m->header[0] = '\0'; + + for (int i = 0; i < ITEMS_PER_PAGE; i++) + { + m->items[i].directory = false; + m->items[i].label = NULL; + m->items[i].value = NULL; + } + + return m; +} + +void freeMenu(Menu* m) +{ + if (!m) return; + + clearMenu(m); + + free(m); + m = NULL; +} + +void addMenuItem(Menu* m, char const* label, char const* value, bool enabled, bool directory) +{ + if (!m) return; + + int i = m->itemCount; + if (i >= ITEMS_PER_PAGE) return; + + m->items[i].directory = directory; + m->items[i].enabled = enabled; + + if (label) + { + m->items[i].label = (char*)malloc(32); + sprintf(m->items[i].label, "%.31s", label); + } + + if (value) + { + m->items[i].value = (char*)malloc(strlen(value)+1); + sprintf(m->items[i].value, "%s", value); + } + + m->itemCount += 1; +} + +static int alphabeticalCompare(const void* a, const void* b) +{ + const Item* itemA = (const Item*)a; + const Item* itemB = (const Item*)b; + + if (itemA->directory && !itemB->directory) + return -1; + else if (!itemA->directory && itemB->directory) + return 1; + else + return strcasecmp(itemA->label, itemB->label); +} + +void sortMenuItems(Menu* m) +{ + qsort(m->items, m->itemCount, sizeof(Item), alphabeticalCompare); +} + +void setMenuHeader(Menu* m, const char* str) +{ + if (!m) return; + + if (!str) + { + m->header[0] = '\0'; + return; + } + + const char* strPtr = str; + + if (strlen(strPtr) > 30) + strPtr = str + (strlen(strPtr) - 30); + + sprintf(m->header, "%.30s", strPtr); +} + +void resetMenu(Menu* m) +{ + m->cursor = 0; + m->page = 0; + m->changePage = 0; + m->nextPage = 0; +} + +void clearMenu(Menu* m) +{ + if (!m) return; + + for (int i = 0; i < ITEMS_PER_PAGE; i++) + { + if (m->items[i].label) + { + free(m->items[i].label); + m->items[i].label = NULL; + } + + if (m->items[i].value) + { + free(m->items[i].value); + m->items[i].value = NULL; + } + } + + m->itemCount = 0; +} + +void printMenu(Menu* m) +{ + clearScreen(&bottomScreen); + + if (!m) return; + + //header + iprintf("\x1B[42m"); //green + iprintf("%.30s\n\n", m->header); + iprintf("\x1B[47m"); //white + + if (m->itemCount <= 0) + { + iprintf("Back - [B]\n"); + return; + } + + //items + for (int i = 0; i < m->itemCount; i++) + { + if (m->items[i].label) + { + if(!m->items[i].enabled) + iprintf("\x1B[37m"); //gray + + if (m->items[i].directory) + iprintf(" [%.26s]\n", m->items[i].label); + else + iprintf(" %.28s\n", m->items[i].label); + + if(!m->items[i].enabled) + iprintf("\x1B[47m"); //white + } + else + iprintf(" \n"); + } + + //cursor + iprintf("\x1b[%d;0H>", 2 + m->cursor); + + //scroll arrows + if (m->page > 0) + iprintf("\x1b[2;31H^"); + + if (m->nextPage) + iprintf("\x1b[21;31Hv"); +} + +static void _moveCursor(Menu* m, int dir) +{ + if (m->changePage != 0) + return; + + m->cursor += sign(dir); + + if (m->cursor < 0) + { + if (m->page <= 0) + m->cursor = 0; + else + { + m->changePage = -1; + m->cursor = ITEMS_PER_PAGE - 1; + } + } + + else if (m->cursor > m->itemCount-1) + { + if (m->nextPage && m->cursor >= ITEMS_PER_PAGE) + { + m->changePage = 1; + m->cursor = 0; + } + else + { + m->cursor = m->itemCount-1; + } + } +} + +bool moveCursor(Menu* m) +{ + if (!m) return false; + + m->changePage = 0; + int lastCursor = m->cursor; + + u32 down = keysDownRepeat(); + + if (down & KEY_DOWN) + _moveCursor(m, 1); + + else if (down & KEY_UP) + _moveCursor(m, -1); + + if (down & KEY_RIGHT) + { + repeat(10) + _moveCursor(m, 1); + } + + else if (down & KEY_LEFT) + { + repeat(10) + _moveCursor(m, -1); + } + + return !(lastCursor == m->cursor); } \ No newline at end of file diff --git a/arm9/src/menu.h b/arm9/src/menu.h index f8c3e41..9f4f806 100644 --- a/arm9/src/menu.h +++ b/arm9/src/menu.h @@ -1,46 +1,46 @@ -#ifndef MENU_H -#define MENU_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define ITEMS_PER_PAGE 20 - -typedef struct { - bool directory; - bool enabled; - char* label; - char* value; -} Item; - -typedef struct { - int cursor; - int page; - int itemCount; - bool nextPage; - int changePage; - char header[32]; - Item items[ITEMS_PER_PAGE]; -} Menu; - -Menu* newMenu(); -void freeMenu(Menu* m); - -void addMenuItem(Menu* m, char const* label, char const* value, bool enabled, bool directory); -void sortMenuItems(Menu* m); -void setMenuHeader(Menu* m, const char* str); - -void resetMenu(Menu* m); -void clearMenu(Menu* m); -void printMenu(Menu* m); - -bool moveCursor(Menu* m); - -#ifdef __cplusplus -} -#endif - +#ifndef MENU_H +#define MENU_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ITEMS_PER_PAGE 20 + +typedef struct { + bool directory; + bool enabled; + char* label; + char* value; +} Item; + +typedef struct { + int cursor; + int page; + int itemCount; + bool nextPage; + int changePage; + char header[32]; + Item items[ITEMS_PER_PAGE]; +} Menu; + +Menu* newMenu(); +void freeMenu(Menu* m); + +void addMenuItem(Menu* m, char const* label, char const* value, bool enabled, bool directory); +void sortMenuItems(Menu* m); +void setMenuHeader(Menu* m, const char* str); + +void resetMenu(Menu* m); +void clearMenu(Menu* m); +void printMenu(Menu* m); + +bool moveCursor(Menu* m); + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/arm9/src/message.c b/arm9/src/message.c index 830a1c5..6a019de 100644 --- a/arm9/src/message.c +++ b/arm9/src/message.c @@ -1,161 +1,161 @@ -#include "message.h" -#include "main.h" - -void keyWait(u32 key) -{ - while (!programEnd) - { - swiWaitForVBlank(); - scanKeys(); - - if (keysDown() & key) - break; - } -} - -bool choiceBox(const char* message) -{ - const int choiceRow = 10; - int cursor = 0; - - clearScreen(&bottomScreen); - - iprintf("\x1B[33m"); //yellow - iprintf("%s\n", message); - iprintf("\x1B[47m"); //white - iprintf("\x1b[%d;0H\tYes\n\tNo\n", choiceRow); - - while (!programEnd) - { - swiWaitForVBlank(); - scanKeys(); - - //Clear cursor - iprintf("\x1b[%d;0H ", choiceRow + cursor); - - if (keysDown() & (KEY_UP | KEY_DOWN)) - cursor = !cursor; - - //Print cursor - iprintf("\x1b[%d;0H>", choiceRow + cursor); - - if (keysDown() & (KEY_A | KEY_START)) - break; - - if (keysDown() & KEY_B) - { - cursor = 1; - break; - } - } - - scanKeys(); - return (cursor == 0)? YES: NO; -} - -bool choicePrint(const char* message) -{ - bool choice = NO; - - iprintf("\x1B[33m"); //yellow - iprintf("\n%s\n", message); - iprintf("\x1B[47m"); //white - iprintf("Yes - [A]\nNo - [B]\n"); - - while (!programEnd) - { - swiWaitForVBlank(); - scanKeys(); - - if (keysDown() & KEY_A) - { - choice = YES; - break; - } - - else if (keysDown() & KEY_B) - { - choice = NO; - break; - } - } - - scanKeys(); - return choice; -} - -const static u16 keys[] = {KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_A, KEY_B, KEY_X, KEY_Y}; -const static char *keysLabels[] = {"\x18", "\x19", "\x1A", "\x1B", "", "", "", ""}; - -bool randomConfirmBox(const char* message) -{ - const int choiceRow = 10; - int sequencePosition = 0; - - u8 sequence[8]; - for (int i = 0; i < sizeof(sequence); i++) - { - sequence[i] = rand() % (sizeof(keys) / sizeof(keys[0])); - } - - clearScreen(&bottomScreen); - - iprintf("\x1B[43m"); //yellow - iprintf("%s\n", message); - iprintf("\x1B[47m"); //white - iprintf("\n cancel\n"); - - while (!programEnd && sequencePosition < sizeof(sequence)) - { - swiWaitForVBlank(); - scanKeys(); - - //Print sequence - iprintf("\x1b[%d;0H", choiceRow); - for (int i = 0; i < sizeof(sequence); i++) - { - iprintf("\x1B[%0om", i < sequencePosition ? 032 : 047); - iprintf("%s ", keysLabels[sequence[i]]); - } - - if (keysDown() & (KEY_UP | KEY_DOWN | KEY_RIGHT | KEY_LEFT | KEY_A | KEY_B | KEY_X | KEY_Y)) - { - if (keysDown() & keys[sequence[sequencePosition]]) - sequencePosition++; - else - sequencePosition = 0; - } - - if (keysDown() & KEY_START) - { - sequencePosition = 0; - break; - } - } - - scanKeys(); - return sequencePosition == sizeof(sequence); -} - -void messageBox(const char* message) -{ - clearScreen(&bottomScreen); - messagePrint(message); -} - -void messagePrint(const char* message) -{ - iprintf("%s\n", message); - iprintf("\nOkay - [A]\n"); - - while (!programEnd) - { - swiWaitForVBlank(); - scanKeys(); - - if (keysDown() & (KEY_A | KEY_B | KEY_START)) - break; - } - - scanKeys(); +#include "message.h" +#include "main.h" + +void keyWait(u32 key) +{ + while (!programEnd) + { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & key) + break; + } +} + +bool choiceBox(const char* message) +{ + const int choiceRow = 10; + int cursor = 0; + + clearScreen(&bottomScreen); + + iprintf("\x1B[33m"); //yellow + iprintf("%s\n", message); + iprintf("\x1B[47m"); //white + iprintf("\x1b[%d;0H\tYes\n\tNo\n", choiceRow); + + while (!programEnd) + { + swiWaitForVBlank(); + scanKeys(); + + //Clear cursor + iprintf("\x1b[%d;0H ", choiceRow + cursor); + + if (keysDown() & (KEY_UP | KEY_DOWN)) + cursor = !cursor; + + //Print cursor + iprintf("\x1b[%d;0H>", choiceRow + cursor); + + if (keysDown() & (KEY_A | KEY_START)) + break; + + if (keysDown() & KEY_B) + { + cursor = 1; + break; + } + } + + scanKeys(); + return (cursor == 0)? YES: NO; +} + +bool choicePrint(const char* message) +{ + bool choice = NO; + + iprintf("\x1B[33m"); //yellow + iprintf("\n%s\n", message); + iprintf("\x1B[47m"); //white + iprintf("Yes - [A]\nNo - [B]\n"); + + while (!programEnd) + { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & KEY_A) + { + choice = YES; + break; + } + + else if (keysDown() & KEY_B) + { + choice = NO; + break; + } + } + + scanKeys(); + return choice; +} + +const static u16 keys[] = {KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_A, KEY_B, KEY_X, KEY_Y}; +const static char *keysLabels[] = {"\x18", "\x19", "\x1A", "\x1B", "", "", "", ""}; + +bool randomConfirmBox(const char* message) +{ + const int choiceRow = 10; + int sequencePosition = 0; + + u8 sequence[8]; + for (int i = 0; i < sizeof(sequence); i++) + { + sequence[i] = rand() % (sizeof(keys) / sizeof(keys[0])); + } + + clearScreen(&bottomScreen); + + iprintf("\x1B[43m"); //yellow + iprintf("%s\n", message); + iprintf("\x1B[47m"); //white + iprintf("\n cancel\n"); + + while (!programEnd && sequencePosition < sizeof(sequence)) + { + swiWaitForVBlank(); + scanKeys(); + + //Print sequence + iprintf("\x1b[%d;0H", choiceRow); + for (int i = 0; i < sizeof(sequence); i++) + { + iprintf("\x1B[%0om", i < sequencePosition ? 032 : 047); + iprintf("%s ", keysLabels[sequence[i]]); + } + + if (keysDown() & (KEY_UP | KEY_DOWN | KEY_RIGHT | KEY_LEFT | KEY_A | KEY_B | KEY_X | KEY_Y)) + { + if (keysDown() & keys[sequence[sequencePosition]]) + sequencePosition++; + else + sequencePosition = 0; + } + + if (keysDown() & KEY_START) + { + sequencePosition = 0; + break; + } + } + + scanKeys(); + return sequencePosition == sizeof(sequence); +} + +void messageBox(const char* message) +{ + clearScreen(&bottomScreen); + messagePrint(message); +} + +void messagePrint(const char* message) +{ + iprintf("%s\n", message); + iprintf("\nOkay - [A]\n"); + + while (!programEnd) + { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & (KEY_A | KEY_B | KEY_START)) + break; + } + + scanKeys(); } \ No newline at end of file diff --git a/arm9/src/message.h b/arm9/src/message.h index a873a28..8e3ecba 100644 --- a/arm9/src/message.h +++ b/arm9/src/message.h @@ -1,26 +1,26 @@ -#ifndef MESSAGE_H -#define MESSAGE_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - YES = true, - NO = false -}; - -void keyWait(u32 key); -bool choiceBox(const char* message); -bool choicePrint(const char* message); -bool randomConfirmBox(const char* message); -void messageBox(const char* message); -void messagePrint(const char* message); - -#ifdef __cplusplus -} -#endif - +#ifndef MESSAGE_H +#define MESSAGE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + YES = true, + NO = false +}; + +void keyWait(u32 key); +bool choiceBox(const char* message); +bool choicePrint(const char* message); +bool randomConfirmBox(const char* message); +void messageBox(const char* message); +void messagePrint(const char* message); + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/arm9/src/storage.c b/arm9/src/storage.c index 6f38508..fc56487 100644 --- a/arm9/src/storage.c +++ b/arm9/src/storage.c @@ -1,257 +1,257 @@ -#include "storage.h" -#include "main.h" -#include "message.h" -#include -#include -#include - -#define TITLE_LIMIT 39 - -//progress bar -static int lastBars = 0; - -static void printProgressBar(float percent) -{ - if (percent < 0.f) percent = 0.f; - if (percent > 1.f) percent = 1.f; - - int bars = (int)(30.f * percent); - - //skip redundant prints - if (bars != lastBars) - { - consoleSelect(&topScreen); - - iprintf("\x1B[42m"); //green - - //Print frame - if (lastBars <= 0) - { - iprintf("\x1b[23;0H["); - iprintf("\x1b[23;31H]"); - } - - //Print bars - if (bars > 0) - { - for (int i = 0; i < bars; i++) - iprintf("\x1b[23;%dH|", 1 + i); - } - - lastBars = bars; - - iprintf("\x1B[47m"); //white - } -} - -static void clearProgressBar() -{ - lastBars = 0; - consoleSelect(&topScreen); - iprintf("\x1b[23;0H "); -} - -//files -bool fileExists(char const* path) -{ - return access(path, F_OK) == 0; -} - -int copyFile(char const* src, char const* dst) -{ - if (!src) return 1; - - unsigned long long size = getFileSizePath(src); - return copyFilePart(src, 0, size, dst); -} - -int copyFilePart(char const* src, u32 offset, u32 size, char const* dst) -{ - if (!src) return 1; - if (!dst) return 2; - - FILE* fin = fopen(src, "rb"); - - if (!fin) - { - fclose(fin); - return 3; - } - else - { - if (fileExists(dst)) - remove(dst); - - FILE* fout = fopen(dst, "wb"); - - if (!fout) - { - fclose(fin); - fclose(fout); - return 4; - } - else - { - fseek(fin, offset, SEEK_SET); - - consoleSelect(&topScreen); - - int bytesRead; - unsigned long long totalBytesRead = 0; - - #define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds. - char* buffer = (char*)malloc(BUFF_SIZE); - - while (!programEnd) - { - unsigned int toRead = BUFF_SIZE; - if (size - totalBytesRead < BUFF_SIZE) - toRead = size - totalBytesRead; - - bytesRead = fread(buffer, 1, toRead, fin); - fwrite(buffer, bytesRead, 1, fout); - - totalBytesRead += bytesRead; - printProgressBar( ((float)totalBytesRead / (float)size) ); - - if (bytesRead != BUFF_SIZE) - break; - } - - clearProgressBar(); - consoleSelect(&bottomScreen); - - free(buffer); - } - - fclose(fout); - } - - fclose(fin); - return 0; -} - -unsigned long long getFileSize(FILE* f) -{ - if (!f) return 0; - - fseek(f, 0, SEEK_END); - unsigned long long size = ftell(f); - fseek(f, 0, SEEK_SET); - - return size; -} - -unsigned long long getFileSizePath(char const* path) -{ - if (!path) return 0; - - FILE* f = fopen(path, "rb"); - unsigned long long size = getFileSize(f); - fclose(f); - - return size; -} - -bool toggleFileReadOnly(const char* path, bool readOnly) -{ - int fatAttributes = FAT_getAttr(path); - if (readOnly) - fatAttributes |= ATTR_READONLY; - else - fatAttributes &= ~ATTR_READONLY; - return FAT_setAttr(path, fatAttributes) == 0; -} - -bool writeToFile(FILE* fd, const char* buffer, size_t size) -{ - int toWrite = size; - size_t written; - //write the first 520 bytes as 0, as that's the size of a tmd, but it can be whatever content - while (toWrite > 0 && (written = fwrite(buffer, sizeof(char), toWrite, fd)) > 0) - { - toWrite -= written; - buffer += written; - } - return toWrite == 0; -} - -bool calculateFileSha1(FILE* f, void* digest) -{ - fseek(f, 0, SEEK_SET); - - swiSHA1context_t ctx; - ctx.sha_block = 0; //this is weird but it has to be done - swiSHA1Init(&ctx); - - char buffer[512]; - size_t n = 0; - while ((n = fread(buffer, sizeof(char), sizeof(buffer), f)) > 0) - { - swiSHA1Update(&ctx, buffer, n); - } - if (ferror(f) || !feof(f)) - { - return false; - } - swiSHA1Final(digest, &ctx); - return true; -} - -bool calculateFileSha1Path(const char* path, void* digest) -{ - FILE* targetFile = fopen(path, "rb"); - if (!targetFile) - { - return false; - } - bool res = calculateFileSha1(targetFile, digest); - fclose(targetFile); - return res; -} - -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; -} - -bool removeIfExists(const char* path) -{ - return remove(path) == 0 || errno == ENOENT; -} - -// Filesystem type -typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; -//trimmed down PARTITION struct from libfat internals -typedef struct { - const void* disc; - void* cache; - // Info about the partition - FS_TYPE filesysType; - uint64_t totalSize; - sec_t rootDirStart; - uint32_t rootDirCluster; - uint32_t numberOfSectors; - sec_t dataStart; - uint32_t bytesPerSector; - uint32_t sectorsPerCluster; - uint32_t bytesPerCluster; - uint32_t fsInfoSector; -} PARTITION; - -extern PARTITION* _FAT_partition_getPartitionFromPath(const char* path); - -u32 getClusterSizeForPartition(const char* path) -{ - PARTITION* p = _FAT_partition_getPartitionFromPath(path); - if(!p) - return 0; - return p->bytesPerCluster; -} +#include "storage.h" +#include "main.h" +#include "message.h" +#include +#include +#include + +#define TITLE_LIMIT 39 + +//progress bar +static int lastBars = 0; + +static void printProgressBar(float percent) +{ + if (percent < 0.f) percent = 0.f; + if (percent > 1.f) percent = 1.f; + + int bars = (int)(30.f * percent); + + //skip redundant prints + if (bars != lastBars) + { + consoleSelect(&topScreen); + + iprintf("\x1B[42m"); //green + + //Print frame + if (lastBars <= 0) + { + iprintf("\x1b[23;0H["); + iprintf("\x1b[23;31H]"); + } + + //Print bars + if (bars > 0) + { + for (int i = 0; i < bars; i++) + iprintf("\x1b[23;%dH|", 1 + i); + } + + lastBars = bars; + + iprintf("\x1B[47m"); //white + } +} + +static void clearProgressBar() +{ + lastBars = 0; + consoleSelect(&topScreen); + iprintf("\x1b[23;0H "); +} + +//files +bool fileExists(char const* path) +{ + return access(path, F_OK) == 0; +} + +int copyFile(char const* src, char const* dst) +{ + if (!src) return 1; + + unsigned long long size = getFileSizePath(src); + return copyFilePart(src, 0, size, dst); +} + +int copyFilePart(char const* src, u32 offset, u32 size, char const* dst) +{ + if (!src) return 1; + if (!dst) return 2; + + FILE* fin = fopen(src, "rb"); + + if (!fin) + { + fclose(fin); + return 3; + } + else + { + if (fileExists(dst)) + remove(dst); + + FILE* fout = fopen(dst, "wb"); + + if (!fout) + { + fclose(fin); + fclose(fout); + return 4; + } + else + { + fseek(fin, offset, SEEK_SET); + + consoleSelect(&topScreen); + + int bytesRead; + unsigned long long totalBytesRead = 0; + + #define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds. + char* buffer = (char*)malloc(BUFF_SIZE); + + while (!programEnd) + { + unsigned int toRead = BUFF_SIZE; + if (size - totalBytesRead < BUFF_SIZE) + toRead = size - totalBytesRead; + + bytesRead = fread(buffer, 1, toRead, fin); + fwrite(buffer, bytesRead, 1, fout); + + totalBytesRead += bytesRead; + printProgressBar( ((float)totalBytesRead / (float)size) ); + + if (bytesRead != BUFF_SIZE) + break; + } + + clearProgressBar(); + consoleSelect(&bottomScreen); + + free(buffer); + } + + fclose(fout); + } + + fclose(fin); + return 0; +} + +unsigned long long getFileSize(FILE* f) +{ + if (!f) return 0; + + fseek(f, 0, SEEK_END); + unsigned long long size = ftell(f); + fseek(f, 0, SEEK_SET); + + return size; +} + +unsigned long long getFileSizePath(char const* path) +{ + if (!path) return 0; + + FILE* f = fopen(path, "rb"); + unsigned long long size = getFileSize(f); + fclose(f); + + return size; +} + +bool toggleFileReadOnly(const char* path, bool readOnly) +{ + int fatAttributes = FAT_getAttr(path); + if (readOnly) + fatAttributes |= ATTR_READONLY; + else + fatAttributes &= ~ATTR_READONLY; + return FAT_setAttr(path, fatAttributes) == 0; +} + +bool writeToFile(FILE* fd, const char* buffer, size_t size) +{ + int toWrite = size; + size_t written; + //write the first 520 bytes as 0, as that's the size of a tmd, but it can be whatever content + while (toWrite > 0 && (written = fwrite(buffer, sizeof(char), toWrite, fd)) > 0) + { + toWrite -= written; + buffer += written; + } + return toWrite == 0; +} + +bool calculateFileSha1(FILE* f, void* digest) +{ + fseek(f, 0, SEEK_SET); + + swiSHA1context_t ctx; + ctx.sha_block = 0; //this is weird but it has to be done + swiSHA1Init(&ctx); + + char buffer[512]; + size_t n = 0; + while ((n = fread(buffer, sizeof(char), sizeof(buffer), f)) > 0) + { + swiSHA1Update(&ctx, buffer, n); + } + if (ferror(f) || !feof(f)) + { + return false; + } + swiSHA1Final(digest, &ctx); + return true; +} + +bool calculateFileSha1Path(const char* path, void* digest) +{ + FILE* targetFile = fopen(path, "rb"); + if (!targetFile) + { + return false; + } + bool res = calculateFileSha1(targetFile, digest); + fclose(targetFile); + return res; +} + +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; +} + +bool removeIfExists(const char* path) +{ + return remove(path) == 0 || errno == ENOENT; +} + +// Filesystem type +typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; +//trimmed down PARTITION struct from libfat internals +typedef struct { + const void* disc; + void* cache; + // Info about the partition + FS_TYPE filesysType; + uint64_t totalSize; + sec_t rootDirStart; + uint32_t rootDirCluster; + uint32_t numberOfSectors; + sec_t dataStart; + uint32_t bytesPerSector; + uint32_t sectorsPerCluster; + uint32_t bytesPerCluster; + uint32_t fsInfoSector; +} PARTITION; + +extern PARTITION* _FAT_partition_getPartitionFromPath(const char* path); + +u32 getClusterSizeForPartition(const char* path) +{ + PARTITION* p = _FAT_partition_getPartitionFromPath(path); + if(!p) + return 0; + return p->bytesPerCluster; +} diff --git a/arm9/src/storage.h b/arm9/src/storage.h index d166460..5766511 100644 --- a/arm9/src/storage.h +++ b/arm9/src/storage.h @@ -1,34 +1,34 @@ -#ifndef STORAGE_H -#define STORAGE_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -//Files -bool fileExists(char const* path); -int copyFile(char const* src, char const* dst); -int copyFilePart(char const* src, u32 offset, u32 size, char const* dst); -unsigned long long getFileSize(FILE* f); -unsigned long long getFileSizePath(char const* path); -bool toggleFileReadOnly(const char* path, bool readOnly); -bool writeToFile(FILE* fd, const char* buffer, size_t size); -bool calculateFileSha1(FILE* f, void* digest); -bool calculateFileSha1Path(const char* path, void* digest); - -//Directories -bool safeCreateDir(const char* path); - -//Files and directories -bool removeIfExists(const char* path); - -u32 getClusterSizeForPartition(const char* path); - -#ifdef __cplusplus -} -#endif - +#ifndef STORAGE_H +#define STORAGE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//Files +bool fileExists(char const* path); +int copyFile(char const* src, char const* dst); +int copyFilePart(char const* src, u32 offset, u32 size, char const* dst); +unsigned long long getFileSize(FILE* f); +unsigned long long getFileSizePath(char const* path); +bool toggleFileReadOnly(const char* path, bool readOnly); +bool writeToFile(FILE* fd, const char* buffer, size_t size); +bool calculateFileSha1(FILE* f, void* digest); +bool calculateFileSha1Path(const char* path, void* digest); + +//Directories +bool safeCreateDir(const char* path); + +//Files and directories +bool removeIfExists(const char* path); + +u32 getClusterSizeForPartition(const char* path); + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file