diff --git a/source/common/common.h b/source/common/common.h index 387a312..57af230 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -41,7 +41,7 @@ #define ENTRY_GATEWAY (2) // GodMode9 version -#define VERSION "1.0.1" +#define VERSION "1.0.2" // input / output paths #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" diff --git a/source/game/gameutil.c b/source/game/gameutil.c index 020d526..2d60ab0 100644 --- a/source/game/gameutil.c +++ b/source/game/gameutil.c @@ -1400,15 +1400,16 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { return ret; } +u32 CheckHealthAndSafetyInject(const char* hsdrv) { + char path_mrk[32] = { 0 }; + snprintf(path_mrk, 32, "%s/%s", hsdrv, HSINJECT_MARKFILE); + return (f_stat(path_mrk, NULL) == FR_OK) ? 0 : 1; +} + u32 InjectHealthAndSafety(const char* path, const char* destdrv) { const u32 tidlow_hs_o3ds[] = { 0x00020300, 0x00021300, 0x00022300, 0, 0x00026300, 0x00027300, 0x00028300 }; const u32 tidlow_hs_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x00027300, 0 }; NcchHeader ncch; - - // check input file / crypto - if ((LoadNcchHeaders(&ncch, NULL, NULL, path, 0) != 0) || - !(NCCH_IS_CXI(&ncch)) || (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0)) - return 1; // write permissions if (!CheckWritePermissions(destdrv)) @@ -1434,17 +1435,35 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) { if (!tidlow_hs) return 1; // build paths - char path_tmd[64]; - char path_cxi[64]; - char path_bak[64]; - snprintf(path_tmd, 64, "%s/title/00040010/%08lx/content/00000000.tmd", destdrv, tidlow_hs); + char path_cxi[64] = { 0 }; + char path_bak[64] = { 0 }; + char path_mrk[32] = { 0 }; + snprintf(path_mrk, 32, "%s/%s", destdrv, HSINJECT_MARKFILE); + for (u32 i = 0; i < 8; i++) { // 8 is an arbitrary number + TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; + TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); + char path_tmd[64]; + snprintf(path_tmd, 64, "%s/title/00040010/%08lx/content/%08lx.tmd", destdrv, tidlow_hs, i); + if (LoadTmdFile(tmd, path_tmd) != 0) continue; + if (!getbe16(tmd->content_count)) return 1; + snprintf(path_cxi, 64, "%s/title/00040010/%08lx/content/%08lx.app", destdrv, tidlow_hs, getbe32(chunk->id)); + snprintf(path_bak, 64, "%s/title/00040010/%08lx/content/%08lx.bak", destdrv, tidlow_hs, getbe32(chunk->id)); + break; + } + if (!(*path_cxi)) return 1; - TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; - TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); - if (LoadTmdFile(tmd, path_tmd) != 0) return 1; - if (!getbe16(tmd->content_count)) return 1; - snprintf(path_cxi, 64, "%s/title/00040010/%08lx/content/%08lX.app", destdrv, tidlow_hs, getbe32(chunk->id)); - snprintf(path_bak, 64, "%s/title/00040010/%08lx/content/%08lX.bak", destdrv, tidlow_hs, getbe32(chunk->id)); + if (!path) { // if path == NULL -> restore H&S from backup + if (f_stat(path_bak, NULL) != FR_OK) return 1; + f_unlink(path_mrk); + f_unlink(path_cxi); + f_rename(path_bak, path_cxi); + return 0; + } + + // check input file / crypto + if ((LoadNcchHeaders(&ncch, NULL, NULL, path, 0) != 0) || + !(NCCH_IS_CXI(&ncch)) || (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0)) + return 1; // check crypto, get sig u64 tid_hs = ((u64) 0x00040010 << 32) | tidlow_hs; @@ -1485,6 +1504,9 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) { if (ret != 0) { // in case of failure: try recover f_unlink(path_cxi); f_rename(path_bak, path_cxi); + } else { // in case of success: mark this ctrnand as H&S injected + UINT bw; + fvx_qwrite(path_mrk, path_bak, 0, strnlen(path_bak, 63) + 1, &bw); } return ret; diff --git a/source/game/gameutil.h b/source/game/gameutil.h index cc6e8f1..31e1d21 100644 --- a/source/game/gameutil.h +++ b/source/game/gameutil.h @@ -2,9 +2,12 @@ #include "common.h" +#define HSINJECT_MARKFILE "__gm9_hsbak.pth" + u32 VerifyGameFile(const char* path); u32 CheckEncryptedGameFile(const char* path); u32 CryptGameFile(const char* path, bool inplace, bool encrypt); u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 BuildNcchInfoXorpads(const char* destdir, const char* path); +u32 CheckHealthAndSafetyInject(const char* hsdrv); u32 InjectHealthAndSafety(const char* path, const char* destdrv); diff --git a/source/godmode.c b/source/godmode.c index 5b19272..0de65dd 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -608,7 +608,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool in_output_path = (strncmp(current_path, OUTPUT_PATH, 256) == 0); // special stuff, only available for known filetypes (see int special below) - bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & (DRV_IMAGE|DRV_RAMDRIVE))); + bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & (DRV_IMAGE|DRV_BONUS|DRV_RAMDRIVE))); bool verificable = (FYTPE_VERIFICABLE(filetype)); bool decryptable = (FYTPE_DECRYPTABLE(filetype)); bool encryptable = (FYTPE_ENCRYPTABLE(filetype)); @@ -623,7 +623,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool special_opt = mountable || verificable || decryptable || encryptable || buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable || ebackupable; - char pathstr[48]; + char pathstr[32]; TruncateString(pathstr, curr_entry->path, 32, 8); u32 n_marked = 0; @@ -731,8 +731,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur current_dir->entry[i].marked = false; if (PathCopy(OUTPUT_PATH, path, &flags)) n_success++; else { // on failure: set cursor on failed title, break; - char currstr[20+1]; - TruncateString(currstr, clipboard->entry[0].name, 20, 12); + char currstr[32+1]; + TruncateString(currstr, path, 32, 12); ShowPrompt(false, "%s\nFailed copying file", currstr); *cursor = i; break; @@ -1034,6 +1034,87 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur return FileHandlerMenu(current_path, cursor, scroll, current_dir, clipboard); } +u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboard) { + const char* optionstr[8]; + const char* promptstr = "HOME more.. menu.\nSelect action:"; + u32 n_opt = 0; + int sdformat = ++n_opt; + int bonus = (GetNandUnusedSectors(NAND_SYSNAND) > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize + int multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1; + int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1; + int nandbak = ++n_opt; + + if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu"; + if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup"; + if (multi > 0) optionstr[multi - 1] = "Switch EmuNAND"; + if (hsrestore > 0) optionstr[hsrestore - 1] = "Restore H&S"; + if (nandbak > 0) optionstr[nandbak - 1] = "Backup NAND"; + + int user_select = ShowSelectPrompt(n_opt, optionstr, promptstr); + if (user_select == sdformat) { // format SD card + bool sd_state = CheckSDMountState(); + if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & (DRV_SDCARD|DRV_ALIAS|DRV_EMUNAND|DRV_IMAGE))) + clipboard->n_entries = 0; // remove SD clipboard entries + DeinitExtFS(); + DeinitSDCardFS(); + if ((SdFormatMenu() == 0) || sd_state) {; + while (!InitSDCardFS() && + ShowPrompt(true, "Reinitialising SD card failed! Retry?")); + } + ClearScreenF(true, true, COLOR_STD_BG); + InitEmuNandBase(true); + InitExtFS(); + GetDirContents(current_dir, current_path); + return 0; + } else if (user_select == bonus) { // setup bonus drive + if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & (DRV_BONUS|DRV_IMAGE))) + clipboard->n_entries = 0; // remove bonus drive clipboard entries + if (!SetupBonusDrive()) ShowPrompt(false, "Setup failed!"); + ClearScreenF(true, true, COLOR_STD_BG); + GetDirContents(current_dir, current_path); + return 0; + } else if (user_select == multi) { // switch EmuNAND offset + while (ShowPrompt(true, "Current EmuNAND offset is %06X.\nSwitch to next offset?", GetEmuNandBase())) { + if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & DRV_EMUNAND)) + clipboard->n_entries = 0; // remove EmuNAND clipboard entries + DismountDriveType(DRV_EMUNAND); + InitEmuNandBase(false); + InitExtFS(); + } + GetDirContents(current_dir, current_path); + return 0; + } else if (user_select == hsrestore) { // restore Health & Safety + n_opt = 0; + int sys = (CheckHealthAndSafetyInject("1:") == 0) ? (int) ++n_opt : -1; + int emu = (CheckHealthAndSafetyInject("4:") == 0) ? (int) ++n_opt : -1; + if (sys > 0) optionstr[sys - 1] = "Restore H&S (SysNAND)"; + if (emu > 0) optionstr[emu - 1] = "Restore H&S (EmuNAND)"; + user_select = (n_opt > 1) ? ShowSelectPrompt(n_opt, optionstr, promptstr) : n_opt; + if (user_select > 0) { + InjectHealthAndSafety(NULL, (user_select == sys) ? "1:" : "4:"); + GetDirContents(current_dir, current_path); + return 0; + } + } else if (user_select == nandbak) { // dump NAND backup + n_opt = 0; + int sys = (DriveType("1:")) ? (int) ++n_opt : -1; + int emu = (DriveType("4:")) ? (int) ++n_opt : -1; + if (sys > 0) optionstr[sys - 1] = "Backup SysNAND"; + if (emu > 0) optionstr[emu - 1] = "BAckup EmuNAND"; + user_select = (n_opt > 1) ? ShowSelectPrompt(n_opt, optionstr, promptstr) : n_opt; + if (user_select > 0) { + u32 flags = CALC_SHA; + ShowPrompt(false, "NAND backup: %s", + (PathCopy(OUTPUT_PATH, (user_select == sys) ? "1:/nand_min.bin" : "4:/nand_min.bin", &flags)) ? + "success" : "failed"); + GetDirContents(current_dir, current_path); + return 0; + } + } else return 1; + + return HomeMoreMenu(current_path, current_dir, clipboard); +} + u32 GodMode() { static const u32 quick_stp = 20; u32 exit_mode = GODMODE_EXIT_REBOOT; @@ -1371,46 +1452,18 @@ u32 GodMode() { exit_mode = GODMODE_EXIT_POWEROFF; break; } else if (pad_state & BUTTON_HOME) { // Home menu - const char* optionstr[] = { "Poweroff system", "Reboot system", "SD format menu", "Bonus drive setup", "Switch EmuNAND" }; + const char* optionstr[] = { "Poweroff system", "Reboot system", "More..." }; + const char* promptstr = "HOME button pressed.\nSelect action:"; u32 n_opt = 3; - int bonus = (GetNandUnusedSectors(NAND_SYSNAND) > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize - int multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1; - if (bonus < 0) optionstr[3] = "Switch EmuNAND"; - u32 user_select = ShowSelectPrompt(n_opt, optionstr, "HOME button pressed.\nSelect action:" ); + u32 user_select = 0; + while (((user_select = ShowSelectPrompt(n_opt, optionstr, promptstr)) == 3) && + (HomeMoreMenu(current_path, current_dir, clipboard) == 1)); // more... menu if (user_select == 1) { exit_mode = GODMODE_EXIT_POWEROFF; break; } else if (user_select == 2) { exit_mode = GODMODE_EXIT_REBOOT; break; - } else if (user_select == 3) { // format SD card - bool sd_state = CheckSDMountState(); - if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & (DRV_SDCARD|DRV_ALIAS|DRV_EMUNAND|DRV_IMAGE))) - clipboard->n_entries = 0; // remove SD clipboard entries - DeinitExtFS(); - DeinitSDCardFS(); - if ((SdFormatMenu() == 0) || sd_state) {; - while (!InitSDCardFS() && - ShowPrompt(true, "Reinitialising SD card failed! Retry?")); - } - ClearScreenF(true, true, COLOR_STD_BG); - InitEmuNandBase(true); - InitExtFS(); - GetDirContents(current_dir, current_path); - } else if (user_select == (u32) bonus) { // setup bonus drive - if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & (DRV_BONUS|DRV_IMAGE))) - clipboard->n_entries = 0; // remove bonus drive clipboard entries - if (!SetupBonusDrive()) ShowPrompt(false, "Setup failed!"); - ClearScreenF(true, true, COLOR_STD_BG); - GetDirContents(current_dir, current_path); - } else if (user_select == (u32) multi) { // switch EmuNAND offset - while (ShowPrompt(true, "Current EmuNAND offset is %06X.\nSwitch to next offset?", GetEmuNandBase())) { - if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & DRV_EMUNAND)) - clipboard->n_entries = 0; // remove SD clipboard entries - DismountDriveType(DRV_EMUNAND); - InitEmuNandBase(false); - InitExtFS(); - } } } else if (pad_state & (CART_INSERT|CART_EJECT)) { if (!InitVCartDrive() && (pad_state & CART_INSERT)) // reinit virtual cart drive