diff --git a/source/godmode.c b/source/godmode.c index 024149e..870e1f0 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -860,7 +860,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool bootable = (FTYPE_BOOTABLE(filetype)); bool installable = (FTYPE_INSTALLABLE(filetype)); bool agbexportable = (FTPYE_AGBSAVE(filetype)); - bool agbimportable = (FTPYE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); + bool agbimportable = (FTPYE_AGBSAVE(filetype)); bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || bootable || scriptable || installable || agbexportable || agbimportable; char pathstr[32+1]; @@ -1771,7 +1771,7 @@ u32 GodMode(bool is_b9s) { // bootloader handler if (bootloader) { const char* bootfirm_paths[] = { BOOTFIRM_PATHS }; - if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "0:/bootonce.firm"); + if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "sdmc:/bootonce.firm"); for (u32 i = 0; i < sizeof(bootfirm_paths) / sizeof(char*); i++) { BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1); } diff --git a/source/nand/nandcmac.c b/source/nand/nandcmac.c index ee9bdb5..dbaf244 100644 --- a/source/nand/nandcmac.c +++ b/source/nand/nandcmac.c @@ -19,12 +19,13 @@ #define CMAC_TITLEDB_SD 8 #define CMAC_MOVABLE 9 #define CMAC_AGBSAVE 10 +#define CMAC_AGBSAVE_SD 11 // see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header -#define CMAC_SAVETYPE NULL, "CTR-EXT0", "CTR-EXT0", "CTR-SYS0", "CTR-NOR0", "CTR-SAV0", "CTR-SIGN", "CTR-9DB0", "CTR-9DB0", NULL, NULL +#define CMAC_SAVETYPE NULL, "CTR-EXT0", "CTR-EXT0", "CTR-SYS0", "CTR-NOR0", "CTR-SAV0", "CTR-SIGN", "CTR-9DB0", "CTR-9DB0", NULL, NULL, NULL // see: http://3dbrew.org/wiki/AES_Registers#Keyslots -#define CMAC_KEYSLOT 0xFF, 0x30 /*0x3A?*/, 0x30, 0x30, 0x33 /*0x19*/, 0xFF, 0x30, 0x0B, 0x30, 0x0B, 0x24 +#define CMAC_KEYSLOT 0xFF, 0x30 /*0x3A?*/, 0x30, 0x30, 0x33 /*0x19*/, 0xFF, 0x30, 0x0B, 0x30, 0x0B, 0x24, 0x30 // see: https://www.3dbrew.org/wiki/Title_Database #define SYS_DB_NAMES "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db" @@ -47,6 +48,16 @@ // "%c:/agbsave.bin" virtual AGBSAVE file +u32 CheckAgbSaveHeader(const char* path) { + AgbSaveHeader agbsave; + UINT br; + + if ((fvx_qread(path, &agbsave, 0, 0x200, &br) != FR_OK) || (br != 0x200)) + return 1; + + return ValidateAgbSaveHeader(&agbsave); +} + u32 CheckCmacHeader(const char* path) { u8 cmac_hdr[0x100]; UINT br; @@ -70,7 +81,7 @@ u32 ReadFileCmac(const char* path, u8* cmac) { if (!cmac_type) return 1; else if (cmac_type == CMAC_MOVABLE) offset = 0x130; - else if (cmac_type == CMAC_AGBSAVE) offset = 0x010; + else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = 0x010; else offset = 0x000; return ((fvx_qread(path, cmac, offset, 0x10, &br) != FR_OK) || (br != 0x10)) ? 1 : 0; @@ -83,7 +94,7 @@ u32 WriteFileCmac(const char* path, u8* cmac) { if (!cmac_type) return 1; else if (cmac_type == CMAC_MOVABLE) offset = 0x130; - else if (cmac_type == CMAC_AGBSAVE) offset = 0x010; + else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = 0x010; else offset = 0x000; if (!CheckWritePermissions(path)) return 1; @@ -111,8 +122,8 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { sid = 1; cmac_type = CMAC_EXTDATA_SD; } else if ((sscanf(path, "%c:/title/%08lx/%08lx/data/%08lx.sav", &drv, &tid_high, &tid_low, &sid) == 4) && - ext && (strncmp(ext, "sav", 4) == 0) && (CheckCmacHeader(path) == 0)) { - cmac_type = CMAC_SAVEDATA_SD; + ext && (strncasecmp(ext, "sav", 4) == 0)) { + cmac_type = (CheckCmacHeader(path) == 0) ? CMAC_SAVEDATA_SD : (CheckAgbSaveHeader(path) == 0) ? CMAC_AGBSAVE_SD : 0; } } else if ((drv == '1') || (drv == '4') || (drv == '7')) { // data on CTRNAND u64 id0_high, id0_low; // ID0 @@ -162,14 +173,29 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { } // build hash data block, get size - if (cmac_type == CMAC_AGBSAVE) { // agbsaves - // see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header + if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) { // agbsaves AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data; if ((TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) || (fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) || (br < 0x200) || (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br)) return 1; hashsize = (0x200 - 0x30) + agbsave->save_size; hashdata = data + 0x30; + if (cmac_type == CMAC_AGBSAVE_SD) { + // see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header + // thanks to TuxSH, AuroraWright and Wolfvak for helping me + // reverse engineering P9 and figuring out AGBSAVE on SD CMACs + // this won't work on devkits(!!!) + const char* cmac_savetype[] = { CMAC_SAVETYPE }; + u8* hashdata0 = data + AGBSAVE_MAX_SIZE; + memcpy(hashdata0 + 0x00, cmac_savetype[CMAC_SAVEGAME], 8); + sha_quick(hashdata0 + 0x08, hashdata, hashsize, SHA256_MODE); + + hashdata = data; + memcpy(hashdata + 0x00, cmac_savetype[CMAC_SAVEDATA_SD], 8); + memcpy(hashdata + 0x08, &(agbsave->title_id), 8); + sha_quick(hashdata + 0x10, hashdata0, 0x28, SHA256_MODE); + hashsize = 0x30; + } } else if (cmac_type == CMAC_MOVABLE) { // movable.sed // see: https://3dbrew.org/wiki/Nand/private/movable.sed if ((fvx_qread(path, data, 0, 0x140, &br) != FR_OK) || (br != 0x140))