From 2f739ab13078d3c54a87f331e8d47d687e0294a9 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Thu, 22 Feb 2018 01:47:29 +0100 Subject: [PATCH] Added ability to extract data from DIFF files --- arm9/source/filesys/filetype.c | 9 +++-- arm9/source/filesys/filetype.h | 27 ++++++++------- arm9/source/godmode.c | 29 ++++++++++++---- arm9/source/utils/gameutil.c | 60 ++++++++++++++++++++++++++++++++++ arm9/source/utils/gameutil.h | 1 + arm9/source/virtual/vtickdb.c | 2 +- 6 files changed, 106 insertions(+), 22 deletions(-) diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index 0174f2e41..e022a447e 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -3,6 +3,7 @@ #include "fatmbr.h" #include "nand.h" #include "game.h" +#include "disadiff.h" #include "keydb.h" #include "ctrtransfer.h" #include "scripting.h" @@ -11,6 +12,8 @@ u64 IdentifyFileType(const char* path) { const u8 romfs_magic[] = { ROMFS_MAGIC }; + const u8 diff_magic[] = { DIFF_MAGIC }; + // const u8 disa_magic[] = { DISA_MAGIC }; const u8 tickdb_magic[] = { TICKDB_MAGIC }; const u8 smdh_magic[] = { SMDH_MAGIC }; const u8 threedsx_magic[] = { THREEDSX_EXT_MAGIC }; @@ -88,8 +91,10 @@ u64 IdentifyFileType(const char* path) { return SYS_FIRM; // FIRM file } else if ((ValidateAgbSaveHeader((AgbSaveHeader*) data) == 0) && (fsize >= AGBSAVE_MAX_SIZE)) { return SYS_AGBSAVE; // AGBSAVE file - } else if (memcmp(header + 0x100, tickdb_magic, sizeof(tickdb_magic)) == 0) { - return SYS_TICKDB; // ticket.db + } else if (memcmp(header + 0x100, diff_magic, sizeof(diff_magic)) == 0) { // DIFF file + if (memcmp(header + 0x100, tickdb_magic, sizeof(tickdb_magic)) == 0) // ticket.db file + return SYS_DIFF | SYS_TICKDB; // ticket.db + return SYS_DIFF; } else if (memcmp(header, smdh_magic, sizeof(smdh_magic)) == 0) { return GAME_SMDH; // SMDH file } else if (ValidateTwlHeader((TwlHeader*) data) == 0) { diff --git a/arm9/source/filesys/filetype.h b/arm9/source/filesys/filetype.h index 1adc7120d..6d4075565 100644 --- a/arm9/source/filesys/filetype.h +++ b/arm9/source/filesys/filetype.h @@ -19,18 +19,20 @@ #define GAME_GBA (1ULL<<14) #define GAME_TAD (1ULL<<15) #define SYS_FIRM (1ULL<<16) -#define SYS_AGBSAVE (1ULL<<17) -#define SYS_TICKDB (1ULL<<18) -#define BIN_NCCHNFO (1ULL<<19) -#define BIN_TIKDB (1ULL<<20) -#define BIN_KEYDB (1ULL<<21) -#define BIN_LEGKEY (1ULL<<22) -#define TXT_SCRIPT (1ULL<<23) -#define TXT_GENERIC (1ULL<<24) -#define GFX_PCX (1ULL<<25) -#define FONT_PBM (1ULL<<26) -#define NOIMG_NAND (1ULL<<27) -#define HDR_NAND (1ULL<<28) +#define SYS_DIFF (1ULL<<17) +#define SYS_DISA (1ULL<<18) // not yet used +#define SYS_AGBSAVE (1ULL<<19) +#define SYS_TICKDB (1ULL<<20) +#define BIN_NCCHNFO (1ULL<<21) +#define BIN_TIKDB (1ULL<<22) +#define BIN_KEYDB (1ULL<<23) +#define BIN_LEGKEY (1ULL<<24) +#define TXT_SCRIPT (1ULL<<25) +#define TXT_GENERIC (1ULL<<26) +#define GFX_PCX (1ULL<<27) +#define FONT_PBM (1ULL<<28) +#define NOIMG_NAND (1ULL<<29) +#define HDR_NAND (1ULL<<30) #define TYPE_BASE 0xFFFFFFFFULL // 32 bit reserved for base types // #define FLAG_FIRM (1ULL<<58) // <--- for CXIs containing FIRMs @@ -54,6 +56,7 @@ #define FTYPE_TRANSFERABLE(tp) ((u64) (tp&(IMG_FAT|FLAG_CTR)) == (u64) (IMG_FAT|FLAG_CTR)) #define FTYPE_NCSDFIXABLE(tp) (tp&(HDR_NAND|NOIMG_NAND)) #define FTYPE_HASCODE(tp) ((u64) (tp&(GAME_NCCH|FLAG_CXI)) == (u64) (GAME_NCCH|FLAG_CXI)) +#define FTYPE_ISDISADIFF(tp) (tp&(SYS_DIFF|SYS_DISA)) #define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND)) #define FTYPE_EBACKUP(tp) (tp&(IMG_NAND)) // #define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO)) // deprecated diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index 5d93c0b07..e43fb26e4 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -9,6 +9,7 @@ #include "virtual.h" #include "vcart.h" #include "game.h" +#include "disadiff.h" #include "unittype.h" #include "entrypoints.h" #include "bootfirm.h" @@ -1015,6 +1016,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT)); bool hsinjectable = (FTYPE_HASCODE(filetype)); bool extrcodeable = (FTYPE_HASCODE(filetype)); + bool extrdiffable = (FTYPE_ISDISADIFF(filetype)); bool restorable = (FTYPE_RESTORABLE(filetype) && IS_A9LH && !(drvtype & DRV_SYSNAND)); bool ebackupable = (FTYPE_EBACKUP(filetype)); bool ncsdfixable = (FTYPE_NCSDFIXABLE(filetype)); @@ -1038,7 +1040,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan extrcodeable = (FTYPE_HASCODE(filetype_cxi)); } - 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 || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable; + 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 || extrdiffable || keyinitable || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable; char pathstr[32+1]; TruncateString(pathstr, file_path, 32, 8); @@ -1082,7 +1084,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan (filetype & GAME_3DSX) ? "Show 3DSX title info" : (filetype & SYS_FIRM ) ? "FIRM image options..." : (filetype & SYS_AGBSAVE)? (agbimportable) ? "AGBSAVE options..." : "Dump GBA VC save" : - (filetype & SYS_TICKDB) ? (tik_buildable) ? "Ticket.db options..." : "Mount as ticket.db" : + (filetype & SYS_TICKDB) ? "Ticket.db options..." : + (filetype & SYS_DIFF) ? "Extract DIFF data" : (filetype & BIN_TIKDB) ? "Titlekey options..." : (filetype & BIN_KEYDB) ? "AESkeydb options..." : (filetype & BIN_LEGKEY) ? "Build " KEYDB_NAME : @@ -1224,6 +1227,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan int ctrtransfer = (transferable) ? ++n_opt : -1; int hsinject = (hsinjectable) ? ++n_opt : -1; int extrcode = (extrcodeable) ? ++n_opt : -1; + int extrdiff = (extrdiffable) ? ++n_opt : -1; int rename = (renamable) ? ++n_opt : -1; int xorpad = (xorpadable) ? ++n_opt : -1; int xorpad_inplace = (xorpadable) ? ++n_opt : -1; @@ -1256,6 +1260,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan if (xorpad > 0) optionstr[xorpad-1] = "Build XORpads (SD output)"; if (xorpad_inplace > 0) optionstr[xorpad_inplace-1] = "Build XORpads (inplace)"; if (extrcode > 0) optionstr[extrcode-1] = "Extract " EXEFS_CODE_NAME; + if (extrdiff > 0) optionstr[extrdiff-1] = "Extract DIFF data"; if (keyinit > 0) optionstr[keyinit-1] = "Init " KEYDB_NAME; if (keyinstall > 0) optionstr[keyinstall-1] = "Install " KEYDB_NAME; if (install > 0) optionstr[install-1] = "Install FIRM"; @@ -1576,7 +1581,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan } return 0; } - else if (user_select == extrcode) { // -> Extract code + else if ((user_select == extrcode) || (user_select == extrdiff)) { // -> Extract .code or DIFF partition if ((n_marked > 1) && ShowPrompt(true, "Try to extract all %lu selected files?", n_marked)) { u32 n_success = 0; u32 n_other = 0; @@ -1591,19 +1596,29 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan continue; } DrawDirContents(current_dir, (*cursor = i), scroll); - if (filetype & GAME_TMD) { + if (filetype & SYS_DIFF) { + if (ExtractDataFromDisaDiff(path) == 0) n_success++; + else continue; + } else if (filetype & GAME_TMD) { char cxi_pathl[256] = { 0 }; if ((GetTmdContentPath(cxi_pathl, path) == 0) && PathExist(cxi_pathl) && (ExtractCodeFromCxiFile(cxi_pathl, NULL, NULL) == 0)) { n_success++; - } - } else if (ExtractCodeFromCxiFile(path, NULL, NULL) == 0) n_success++; - else continue; + } else continue; + } else { + if (ExtractCodeFromCxiFile(path, NULL, NULL) == 0) n_success++; + else continue; + } current_dir->entry[i].marked = false; } if (n_other) ShowPrompt(false, "%lu/%lu files extracted ok\n%lu/%lu not of same type", n_success, n_marked, n_other, n_marked); else ShowPrompt(false, "%lu/%lu files extracted ok", n_success, n_marked); + } else if (filetype & SYS_DIFF) { + ShowString("%s\nExtracting data, please wait...", pathstr); + if (ExtractDataFromDisaDiff(file_path) == 0) { + ShowPrompt(false, "%s\ndata extracted to " OUTPUT_PATH, pathstr); + } else ShowPrompt(false, "%s\ndata extract failed", pathstr); } else { char extstr[8] = { 0 }; ShowString("%s\nExtracting .code, please wait...", pathstr); diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 4dd04053b..04992dca4 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -1574,6 +1574,66 @@ u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr) return 0; } +u32 ExtractDataFromDisaDiff(const char* path) { + char dest[256]; + u32 ret = 0; + + // build output name + char* name = strrchr(path, '/'); + if (!name) return 1; + snprintf(dest, 256, "%s/%s", OUTPUT_PATH, ++name); + + // replace extension + char* dot = strrchr(dest, '.'); + if (!dot || (dot < strrchr(dest, '/'))) + dot = dest + strnlen(dest, 256); + snprintf(dot, 16, ".%s", "bin"); + + if (!CheckWritePermissions(dest)) return 1; + + // prepare DISA / DIFF read + DisaDiffReaderInfo info; + u8* lvl2_cache = NULL; + if ((GetDisaDiffReaderInfo(path, &info, false) != 0) || + !(lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) || + (BuildDisaDiffDpfsLvl2Cache(path, &info, lvl2_cache, info.size_dpfs_lvl2) != 0)) { + if (lvl2_cache) free(lvl2_cache); + return 1; + } + + // prepare buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + free(lvl2_cache); + return 1; + } + + // open output file + FIL file; + if (fvx_open(&file, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { + free(buffer); + free(lvl2_cache); + return 1; + } + + // actually extract the partition + u32 total_size = 0; + for (u32 i = 0; ret == 0; i += STD_BUFFER_SIZE) { + UINT btr; + u32 add_size = ReadDisaDiffIvfcLvl4(path, &info, i, STD_BUFFER_SIZE, buffer); + if (!add_size) break; + if ((fvx_write(&file, buffer, add_size, &btr) != FR_OK) || (btr != add_size)) ret = 1; + total_size += add_size; + } + + // wrap it up + if (!total_size) ret = 1; + free(buffer); + free(lvl2_cache); + fvx_close(&file); + return ret; +} + u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { u64 filetype = IdentifyFileType(path); diff --git a/arm9/source/utils/gameutil.h b/arm9/source/utils/gameutil.h index 1c477d3e4..25e0f37e0 100644 --- a/arm9/source/utils/gameutil.h +++ b/arm9/source/utils/gameutil.h @@ -8,6 +8,7 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt); u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 DumpCxiSrlFromTmdFile(const char* path); u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr); +u32 ExtractDataFromDisaDiff(const char* path); u32 ShowGameFileTitleInfo(const char* path); u32 GetTmdContentPath(char* path_content, const char* path_tmd); u32 BuildNcchInfoXorpads(const char* destdir, const char* path); diff --git a/arm9/source/virtual/vtickdb.c b/arm9/source/virtual/vtickdb.c index 3277cd766..514c0ff40 100644 --- a/arm9/source/virtual/vtickdb.c +++ b/arm9/source/virtual/vtickdb.c @@ -121,7 +121,7 @@ void DeinitVTickDbDrive(void) { } u64 InitVTickDbDrive(void) { // prerequisite: ticket.db mounted as image - if (!(GetMountState() & SYS_TICKDB)) return 0; + if (!(GetMountState() & SYS_TICKDB)) return 0; // set up drive buffer / internal db DeinitVTickDbDrive();