From ec861a7bf7c162c605aea353c0b9cebe7fa80e71 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Fri, 17 Nov 2017 02:29:52 +0100 Subject: [PATCH] Added title info / mount support for DSiWare Exports --- arm9/source/filesys/filetype.c | 9 ++-- arm9/source/filesys/filetype.h | 6 +-- arm9/source/filesys/fsdrive.c | 5 +- arm9/source/godmode.c | 4 ++ arm9/source/utils/gameutil.c | 39 ++++++++-------- arm9/source/virtual/vgame.c | 84 ++++++++++++++++++++++++++++++++-- arm9/source/virtual/virtual.h | 4 +- 7 files changed, 119 insertions(+), 32 deletions(-) diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index 77a7a19..45a4058 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -127,10 +127,13 @@ u64 IdentifyFileType(const char* path) { type |= TXT_SCRIPT; // should be a script (which is also generic text) if (fsize < TEMP_BUFFER_SIZE) type |= TXT_GENERIC; return type; + } else if ((strncmp(path + 2, "/Nintendo DSiWare/", 18) == 0) && + (sscanf(fname, "%08lx.bin", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0)) { + DsiWareExpHeader hdr; + if ((FileGetData(path, &hdr, DSIWEXP_HEADER_LEN, DSIWEXP_HEADER_OFFSET) == DSIWEXP_HEADER_LEN) && + (strncmp(hdr.magic, DSIWEXP_HEADER_MAGIC, strlen(DSIWEXP_HEADER_MAGIC)) == 0)) + return GAME_TAD; } else if ((strnlen(fname, 16) == 8) && (sscanf(fname, "%08lx", &id) == 1)) { - if (strncmp(path + 2, "/Nintendo DSiWare/", 18) == 0) - return GAME_DSIWE; - char path_cdn[256]; char* name_cdn = path_cdn + (fname - path); strncpy(path_cdn, path, 256); diff --git a/arm9/source/filesys/filetype.h b/arm9/source/filesys/filetype.h index 32f162b..66db166 100644 --- a/arm9/source/filesys/filetype.h +++ b/arm9/source/filesys/filetype.h @@ -17,7 +17,7 @@ #define GAME_3DSX (1ULL<<12) #define GAME_NDS (1ULL<<13) #define GAME_GBA (1ULL<<14) -#define GAME_DSIWE (1ULL<<15) +#define GAME_TAD (1ULL<<15) #define SYS_FIRM (1ULL<<16) #define SYS_AGBSAVE (1ULL<<17) #define SYS_TICKDB (1ULL<<18) @@ -38,7 +38,7 @@ #define FLAG_NUSCDN (1ULL<<62) #define FLAG_CXI (1ULL<<63) -#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|GAME_NDS|SYS_FIRM|SYS_TICKDB|BIN_KEYDB)) +#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|GAME_NDS|GAME_TAD|SYS_FIRM|SYS_TICKDB|BIN_KEYDB)) #define FYTPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_BOSS|SYS_FIRM)) #define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM|BIN_KEYDB)) #define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|BIN_KEYDB)) @@ -47,7 +47,7 @@ #define FTYPE_CXIDUMP(tp) (tp&(GAME_TMD)) #define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB)) #define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY)) -#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS|GAME_GBA|GAME_3DSX)) +#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS|GAME_GBA|GAME_TAD|GAME_3DSX)) #define FTYPE_RENAMABLE(tp) (tp&(GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_NDS|GAME_GBA)) #define FTYPE_TRANSFERABLE(tp) ((u64) (tp&(IMG_FAT|FLAG_CTR)) == (u64) (IMG_FAT|FLAG_CTR)) #define FTYPE_NCSDFIXABLE(tp) (tp&(HDR_NAND|NOIMG_NAND)) diff --git a/arm9/source/filesys/fsdrive.c b/arm9/source/filesys/fsdrive.c index 02cad0e..c6c9a42 100644 --- a/arm9/source/filesys/fsdrive.c +++ b/arm9/source/filesys/fsdrive.c @@ -94,8 +94,9 @@ bool GetRootDirContentsWorker(DirStruct* contents) { (GetMountState() & GAME_NCCH ) ? "NCCH" : (GetMountState() & GAME_EXEFS) ? "EXEFS" : (GetMountState() & GAME_ROMFS) ? "ROMFS" : - (GetMountState() & GAME_NDS) ? "NDS" : - (GetMountState() & SYS_FIRM) ? "FIRM" : "UNK", drvname[i]); + (GetMountState() & GAME_NDS ) ? "NDS" : + (GetMountState() & SYS_FIRM ) ? "FIRM" : + (GetMountState() & GAME_TAD ) ? "DSIWARE" : "UNK", drvname[i]); else snprintf(entry->path + 4, 32, "[%s] %s", drvnum[i], drvname[i]); entry->name = entry->path + 4; entry->size = GetTotalSpace(entry->path); diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index a5bde6e..372e4d3 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -898,6 +898,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan bool in_output_path = (strncmp(current_path, OUTPUT_PATH, 256) == 0); + // don't handle TMDs inside the game drive, won't work properly anyways + if ((filetype & GAME_TMD) && (drvtype & DRV_GAME)) filetype &= ~GAME_TMD; + // special stuff, only available for known filetypes (see int special below) bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & DRV_IMAGE) && !((drvtype & (DRV_SYSNAND|DRV_EMUNAND)) && (drvtype & DRV_VIRTUAL) && (filetype & IMG_FAT))); @@ -975,6 +978,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan (filetype & GAME_NDS) ? "NDS image options..." : (filetype & GAME_GBA) ? "GBA image options..." : (filetype & GAME_TICKET)? "Ticket options..." : + (filetype & GAME_TAD) ? "TAD image options..." : (filetype & GAME_3DSX) ? "Show 3DSX title info" : (filetype & SYS_FIRM ) ? "FIRM image options..." : (filetype & SYS_AGBSAVE)? (agbimportable) ? "AGBSAVE options..." : "Dump GBA VC save" : diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 4f60117..dd9376f 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -1508,6 +1508,20 @@ u32 ShowSmdhTitleInfo(Smdh* smdh) { return 0; } +u32 ShowTwlIconTitleInfo(TwlIconData* twl_icon) { + const u32 lwrap = 24; + u8* icon = (u8*) (TEMP_BUFFER + sizeof(TwlIconData)); + char* desc = (char*) icon + TWLICON_SIZE_ICON; + if ((GetTwlIcon(icon, twl_icon) != 0) || + (GetTwlTitle(desc, twl_icon) != 0)) + return 1; + WordWrapString(desc, lwrap); + ShowIconString(icon, TWLICON_DIM_ICON, TWLICON_DIM_ICON, "%s", desc); + InputWait(0); + ClearScreenF(true, false, COLOR_STD_BG); + return 0; +} + u32 ShowGbaFileTitleInfo(const char* path) { AgbHeader agb; if ((fvx_qread(path, &agb, 0, sizeof(AgbHeader), NULL) != FR_OK) || @@ -1519,28 +1533,13 @@ u32 ShowGbaFileTitleInfo(const char* path) { } -u32 ShowNdsFileTitleInfo(const char* path) { - const u32 lwrap = 24; - TwlIconData* twl_icon = (TwlIconData*) TEMP_BUFFER; - u8* icon = (u8*) (TEMP_BUFFER + sizeof(TwlIconData)); - char* desc = (char*) icon + TWLICON_SIZE_ICON; - if ((LoadTwlMetaData(path, NULL, twl_icon) != 0) || - (GetTwlIcon(icon, twl_icon) != 0) || - (GetTwlTitle(desc, twl_icon) != 0)) - return 1; - WordWrapString(desc, lwrap); - ShowIconString(icon, TWLICON_DIM_ICON, TWLICON_DIM_ICON, "%s", desc); - InputWait(0); - ClearScreenF(true, false, COLOR_STD_BG); - return 0; - -} - u32 ShowGameFileTitleInfo(const char* path) { Smdh* smdh = (Smdh*) (void*) TEMP_BUFFER; + TwlIconData* twl_icon = (TwlIconData*) (void*) TEMP_BUFFER; char path_content[256]; - if (IdentifyFileType(path) & GAME_TMD) { + u64 itype = IdentifyFileType(path); // initial type + if (itype & GAME_TMD) { if (GetTmdContentPath(path_content, path) != 0) return 1; path = path_content; } @@ -1548,7 +1547,9 @@ u32 ShowGameFileTitleInfo(const char* path) { // try loading SMDH, then try NDS / GBA if (LoadSmdhFromGameFile(path, smdh) == 0) return ShowSmdhTitleInfo(smdh); - else if (ShowNdsFileTitleInfo(path) == 0) return 0; + else if ((LoadTwlMetaData(path, NULL, twl_icon) == 0) || + ((itype & GAME_TAD) && (fvx_qread(path, twl_icon, DSIWEXP_BANNER_OFFSET, sizeof(TwlIconData), NULL) == FR_OK))) + return ShowTwlIconTitleInfo(twl_icon); else return ShowGbaFileTitleInfo(path); } diff --git a/arm9/source/virtual/vgame.c b/arm9/source/virtual/vgame.c index 35051a6..d0a2779 100644 --- a/arm9/source/virtual/vgame.c +++ b/arm9/source/virtual/vgame.c @@ -3,7 +3,8 @@ #include "game.h" #include "aes.h" -#define VFLAG_NO_CRYPTO (1UL<<19) +#define VFLAG_NO_CRYPTO (1UL<<18) +#define VFLAG_DSIWARE (1UL<<19) #define VFLAG_CIA_CONTENT (1UL<<20) #define VFLAG_NDS (1UL<<21) #define VFLAG_NITRO_DIR (1UL<<22) @@ -16,7 +17,7 @@ #define VFLAG_NCCH (1UL<<29) #define VFLAG_EXEFS (1UL<<30) #define VFLAG_ROMFS (1UL<<31) -#define VFLAG_GAMEDIR (VFLAG_FIRM|VFLAG_CIA|VFLAG_NCSD|VFLAG_NCCH|VFLAG_EXEFS|VFLAG_ROMFS|VFLAG_LV3|VFLAG_NDS|VFLAG_NITRO_DIR|VFLAG_NITRO) +#define VFLAG_GAMEDIR (VFLAG_FIRM|VFLAG_CIA|VFLAG_NCSD|VFLAG_NCCH|VFLAG_EXEFS|VFLAG_ROMFS|VFLAG_LV3|VFLAG_NDS|VFLAG_NITRO_DIR|VFLAG_NITRO|VFLAG_DSIWARE) #define VFLAG_NCCH_CRYPTO (VFLAG_EXEFS_FILE|VFLAG_EXTHDR|VFLAG_EXEFS|VFLAG_ROMFS|VFLAG_LV3|VFLAG_NCCH) #define NAME_FIRM_HEADER "header.bin" @@ -60,17 +61,28 @@ #define NAME_NDS_BANNER "banner.bin" #define NAME_NDS_DATADIR "data" +#define NAME_DSIWE_BANNER "banner.bin" +#define NAME_DSIWE_HEADER "header.bin" +#define NAME_DSIWE_FOOTER "footer.bin" +#define NAME_DSIWE_TYPES "tmd", "srl", "02.unk", \ + "03.unk", "04.unk", "05.unk", \ + "06.unk", "07.unk", "08.unk", \ + "public.sav", "banner.sav", "11.unk" +#define NAME_DSIWE_CONTENT "%016llX.%s" // titleid.type + static u64 vgame_type = 0; static u32 base_vdir = 0; static VirtualFile* templates_cia = (VirtualFile*) VGAME_BUFFER; // first 56kb reserved (enough for 1024 entries) +static VirtualFile* templates_dsiwe = (VirtualFile*) (VGAME_BUFFER + 0xDC00); // 1kb reserved (enough for 18 entries) static VirtualFile* templates_firm = (VirtualFile*) (VGAME_BUFFER + 0xE000); // 2kb reserved (enough for 36 entries) static VirtualFile* templates_ncsd = (VirtualFile*) (VGAME_BUFFER + 0xE800); // 2kb reserved (enough for 36 entries) static VirtualFile* templates_ncch = (VirtualFile*) (VGAME_BUFFER + 0xF000); // 1kb reserved (enough for 18 entries) static VirtualFile* templates_nds = (VirtualFile*) (VGAME_BUFFER + 0xF400); // 1kb reserved (enough for 18 entries) static VirtualFile* templates_exefs = (VirtualFile*) (VGAME_BUFFER + 0xF800); // 2kb reserved (enough for 36 entries) static int n_templates_cia = -1; +static int n_templates_dsiwe = -1; static int n_templates_firm = -1; static int n_templates_ncsd = -1; static int n_templates_ncch = -1; @@ -89,6 +101,7 @@ static u64 offset_lv3fd = (u64) -1; static u64 offset_nds = (u64) -1; static u64 offset_nitro = (u64) -1; static u64 offset_ccnt = (u64) -1; +static u64 offset_dsiwe = (u64) -1; static u32 index_ccnt = (u32) -1; static CiaStub* cia = (CiaStub*) (void*) (VGAME_BUFFER + 0x10000); // 61kB reserved - should be enough by far @@ -648,6 +661,64 @@ bool BuildVGameFirmDir(void) { return true; } +bool BuildVGameDsiWareDir(void) { + const char* name_type[] = { NAME_DSIWE_TYPES }; + VirtualFile* templates = templates_dsiwe; + u32 content_offset = 0; + u32 n = 0; + + // read header, setup table + DsiWareExpContentTable tbl; + DsiWareExpHeader hdr; + ReadGameImageBytes(&hdr, DSIWEXP_HEADER_OFFSET, DSIWEXP_HEADER_LEN); + if (BuildDsiWareExportContentTable(&tbl, &hdr) != 0) { + n_templates_dsiwe = 0; + return false; + } + + // banner + strncpy(templates[n].name, NAME_DSIWE_BANNER, 32); + templates[n].offset = content_offset; + templates[n].size = tbl.banner_end - content_offset - sizeof(DsiWareExpBlockMetaData); + templates[n].keyslot = 0xFF; + templates[n].flags = 0; + content_offset = tbl.banner_end; + n++; + + // header + strncpy(templates[n].name, NAME_DSIWE_HEADER, 32); + templates[n].offset = content_offset; + templates[n].size = tbl.header_end - content_offset - sizeof(DsiWareExpBlockMetaData); + templates[n].keyslot = 0xFF; + templates[n].flags = 0; + content_offset = tbl.header_end; + n++; + + // footer + strncpy(templates[n].name, NAME_DSIWE_FOOTER, 32); + templates[n].offset = content_offset; + templates[n].size = tbl.footer_end - content_offset - sizeof(DsiWareExpBlockMetaData); + templates[n].keyslot = 0xFF; + templates[n].flags = 0; + content_offset = tbl.footer_end; + n++; + + // contents + for (u32 i = 0; i < DSIWEXP_NUM_CONTENT; content_offset = tbl.content_end[i++]) { + if (!hdr.content_size[i]) continue; // nothing in section + // use proper names, fix TMD handling + snprintf(templates[n].name, 32, NAME_DSIWE_CONTENT, hdr.title_id, name_type[i]); + templates[n].offset = content_offset; + templates[n].size = hdr.content_size[i]; + templates[n].keyslot = 0xFF; + templates[n].flags = 0; + n++; + } + + n_templates_dsiwe = n; + return true; +} + u64 InitVGameDrive(void) { // prerequisite: game file mounted as image u64 type = GetMountState(); @@ -671,7 +742,8 @@ u64 InitVGameDrive(void) { // prerequisite: game file mounted as image (type & GAME_NCCH ) ? VFLAG_NCCH : (type & GAME_EXEFS) ? VFLAG_EXEFS : (type & GAME_ROMFS) ? VFLAG_ROMFS : - (type & GAME_NDS ) ? VFLAG_NDS : 0; + (type & GAME_NDS ) ? VFLAG_NDS : + (type & GAME_TAD ) ? VFLAG_DSIWARE : 0; if (!base_vdir) return 0; vgame_type = type; @@ -718,6 +790,9 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) { ((SetupArm9BinaryCrypto(a9l)) == 0)) offset_a9bin = arm9s->offset + ARM9BIN_OFFSET; if (!BuildVGameFirmDir()) return false; + } else if ((vdir->flags & VFLAG_DSIWARE) && (offset_dsiwe != vdir->offset)) { + offset_dsiwe = vdir->offset; // always zero(!) + if (!BuildVGameDsiWareDir()) return false; } else if ((vdir->flags & VFLAG_CIA) && (offset_cia != vdir->offset)) { CiaInfo info; if ((ReadImageBytes((u8*) cia, 0, 0x20) != 0) || @@ -943,6 +1018,9 @@ bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir) { } else if (vdir->flags & VFLAG_NDS) { templates = templates_nds; n = n_templates_nds; + } else if (vdir->flags & VFLAG_DSIWARE) { + templates = templates_dsiwe; + n = n_templates_dsiwe; } else if (vdir->flags & VFLAG_LV3) { return ReadVGameDirLv3(vfile, vdir); } else if (vdir->flags & VFLAG_NITRO) { diff --git a/arm9/source/virtual/virtual.h b/arm9/source/virtual/virtual.h index 5bc826e..f0dae7d 100644 --- a/arm9/source/virtual/virtual.h +++ b/arm9/source/virtual/virtual.h @@ -29,8 +29,8 @@ // virtual file flag (subject to change): // bits 0...3 : reserved for NAND virtual sources and info // bits 4...9 : reserved for other virtual sources -// bits 10...17: reserved for external flags -// bits 18...31: reserved for internal flags (different per source, see vgame.c) +// bits 10...15: reserved for external flags +// bits 16...31: reserved for internal flags (different per source, see vgame.c) typedef struct { char name[32]; u64 offset; // must be a multiple of 0x200 (for NAND access)