diff --git a/source/common/common.h b/source/common/common.h index 3dd9801..3095109 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -38,7 +38,7 @@ (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) // GodMode9 version -#define VERSION "0.9.7" +#define VERSION "0.9.8" // input / output paths #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" diff --git a/source/fs/filetype.c b/source/fs/filetype.c index 0b5e62c..4fca8c2 100644 --- a/source/fs/filetype.c +++ b/source/fs/filetype.c @@ -11,6 +11,7 @@ u32 IdentifyFileType(const char* path) { size_t fsize = FileGetSize(path); char* fname = strrchr(path, '/'); char* ext = (fname) ? strrchr(++fname, '.') : NULL; + u32 id = 0; if (ext) ext++; if (FileGetData(path, header, 0x200, 0) < ((fsize > 0x200) ? 0x200 : fsize)) return 0; @@ -49,6 +50,8 @@ u32 IdentifyFileType(const char* path) { return GAME_TMD | FLAG_NUSCDN; // TMD file from NUS/CDN else if (fsize >= TMD_SIZE_N(getbe16(header + 0x1DE))) return GAME_TMD; // TMD file + } else if (ValidateTicket((Ticket*) data) == 0) { + return GAME_TICKET; // Ticket file (not used for anything right now) } else if (ValidateFirmHeader((FirmHeader*) data, fsize) == 0) { return SYS_FIRM; // FIRM file } @@ -60,6 +63,16 @@ u32 IdentifyFileType(const char* path) { (GetNcchInfoVersion((NcchInfoHeader*) data)) && fname && (strncasecmp(fname, NCCHINFO_NAME, 32) == 0)) { return BIN_NCCHNFO; // ncchinfo.bin file + } else if ((strnlen(fname, 16) == 8) && (sscanf(fname, "%08lx", &id) == 1)) { + char path_cdn[256]; + char* name_cdn = path_cdn + (fname - path); + strncpy(path_cdn, path, 256); + strncpy(name_cdn, "tmd", 4); + if (FileGetSize(path_cdn) > 0) + return GAME_NUSCDN; + strncpy(name_cdn, "cetk", 5); + if (FileGetSize(path_cdn) > 0) + return GAME_NUSCDN; #if PAYLOAD_MAX_SIZE <= TEMP_BUFFER_SIZE } else if ((fsize <= PAYLOAD_MAX_SIZE) && ext && (strncasecmp(ext, "bin", 4) == 0)) { return BIN_LAUNCH; // assume it's an ARM9 payload diff --git a/source/fs/filetype.h b/source/fs/filetype.h index 0b30245..aabb198 100644 --- a/source/fs/filetype.h +++ b/source/fs/filetype.h @@ -11,9 +11,11 @@ #define GAME_EXEFS (1<<6) #define GAME_ROMFS (1<<7) #define GAME_BOSS (1<<8) -#define SYS_FIRM (1<<9) -#define BIN_NCCHNFO (1<<10) -#define BIN_LAUNCH (1<<11) +#define GAME_NUSCDN (1<<9) +#define GAME_TICKET (1<<10) +#define SYS_FIRM (1<<11) +#define BIN_NCCHNFO (1<<12) +#define BIN_LAUNCH (1<<13) #define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types #define FLAG_NUSCDN (1<<30) @@ -21,7 +23,7 @@ #define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM)) #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|SYS_FIRM)) +#define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM)) #define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS)) #define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD)) #define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) diff --git a/source/game/gameutil.c b/source/game/gameutil.c index 7f7a4da..2d9720f 100644 --- a/source/game/gameutil.c +++ b/source/game/gameutil.c @@ -729,6 +729,8 @@ u32 CheckEncryptedGameFile(const char* path) { return CheckEncryptedBossFile(path); else if (filetype & SYS_FIRM) return CheckEncryptedFirmFile(path); + else if (filetype & GAME_NUSCDN) + return 0; // these should always be encrypted else return 1; } @@ -767,7 +769,8 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 } fsize = fvx_size(ofp); // for progress bar - if (!size) size = fsize; + if (fsize < offset) return 1; + if (!size) size = fsize - offset; u32 ret = 0; if (!ShowProgress(offset, fsize, dest)) ret = 1; @@ -786,7 +789,7 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 if ((read_bytes != bytes_read) || (bytes_read != bytes_written)) ret = 1; if (!ShowProgress(offset + i + read_bytes, fsize, dest)) ret = 1; } - } else if (mode & GAME_CIA) { // for NCCHs inside CIAs + } else if (mode & (GAME_CIA|GAME_NUSCDN)) { // for NCCHs inside CIAs bool cia_crypto = getbe16(chunk->type) & 0x1; bool ncch_crypto; // find out by decrypting the NCCH header UINT bytes_read, bytes_written; @@ -920,6 +923,70 @@ u32 DecryptFirmFile(const char* orig, const char* dest) { return 0; } +u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) { + bool inplace = (strncmp(orig, dest, 256) == 0); + TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; + TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1); + Ticket* ticket = (Ticket*) (TEMP_BUFFER + TMD_SIZE_MAX); + u8 titlekey[0x10] = { 0xFF }; + u32 cnt_id; + + // try to load TMD file + char path_tmd[256]; + char* name_tmd; + strncpy(path_tmd, orig, 256); + name_tmd = strrchr(path_tmd, '/'); + if (!name_tmd) return 1; // will not happen + name_tmd++; + snprintf(name_tmd, 256 - (name_tmd - path_tmd), "tmd"); + if (LoadTmdFile(tmd, path_tmd) != 0) tmd = NULL; + + // load or build ticket + if (LoadCdnTicketFile(ticket, orig) != 0) { + if (!tmd || (BuildFakeTicket(ticket, tmd->title_id) != 0)) return 1; + if (FindTitleKey(ticket, tmd->title_id) != 0) return 1; + } + + // get titlekey + if (GetTitleKey(titlekey, ticket) != 0) + return 1; + + // get content id + char* fname; + fname = strrchr(orig, '/'); + if (!fname) return 1; // will not happen + if (sscanf(++fname, "%08lx", &cnt_id) != 1) + return 1; // this won't either + + // find (build fake) content chunk + TmdContentChunk* chunk = NULL; + if (!tmd) { + chunk = content_list; + memset(chunk, 0, sizeof(TmdContentChunk)); + chunk->type[1] = 0x01; // encrypted + } else { + u32 content_count = getbe16(tmd->content_count); + for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { + chunk = &(content_list[i]); + if (getbe32(chunk->id) == cnt_id) break; + chunk = NULL; + } + if (!chunk || !(getbe16(chunk->type) & 0x01)) return 1; + } + + // actual crypto + if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_NUSCDN, crypto, 0, 0, chunk, titlekey) != 0) + return 1; + + if (inplace && tmd) { + UINT bw; // in that case, write the change to the TMD file, too + u32 offset = ((u8*) chunk) - ((u8*) tmd); + fvx_qwrite(path_tmd, chunk, offset, sizeof(TmdContentChunk), &bw); + } + + return 0; +} + u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { u32 filetype = IdentifyFileType(path); u16 crypto = encrypt ? CRYPTO_ENCRYPT : CRYPTO_DECRYPT; @@ -943,6 +1010,8 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { if (filetype & GAME_CIA) ret = CryptCiaFile(path, destptr, crypto); + else if (filetype & GAME_NUSCDN) + ret = CryptCdnFile(path, destptr, crypto); else if (filetype & SYS_FIRM) ret = DecryptFirmFile(path, destptr); else if (filetype & (GAME_NCCH|GAME_NCSD|GAME_BOSS)) diff --git a/source/godmode.c b/source/godmode.c index d7e7a83..081c82b 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -627,6 +627,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur (filetype & GAME_ROMFS) ? "Mount as ROMFS image" : (filetype & GAME_TMD) ? "TMD file options..." : (filetype & GAME_BOSS) ? "BOSS file options..." : + (filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" : (filetype & SYS_FIRM) ? "FIRM image options..." : (filetype & BIN_NCCHNFO)? "NCCHinfo options..." : (filetype & BIN_LAUNCH) ? "Launch as arm9 payload" : "???";