From 53a4687fedaa35224e72b913b36f91371ac7b0ba Mon Sep 17 00:00:00 2001 From: d0k3 Date: Mon, 6 Feb 2017 23:56:12 +0100 Subject: [PATCH] Enable building CIAs from CDN / NUS content --- source/game/gameutil.c | 60 ++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/source/game/gameutil.c b/source/game/gameutil.c index 1fc1232..00b741a 100644 --- a/source/game/gameutil.c +++ b/source/game/gameutil.c @@ -940,11 +940,11 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { } u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, u32 size, - TmdContentChunk* chunk, const u8* titlekey, bool force_legit, bool cxi_fix) { - // crypto types - bool ncch_crypto = (!force_legit && (CheckEncryptedNcchFile(path_content, offset) == 0)); - bool cia_crypto = (force_legit && (getbe16(chunk->type) & 0x01)); - if (!cia_crypto) chunk->type[1] &= ~0x01; // remove crypto flag + TmdContentChunk* chunk, const u8* titlekey, bool force_legit, bool cxi_fix, bool cdn_decrypt) { + // crypto types / ctr + bool ncch_decrypt = !force_legit; + bool cia_encrypt = (force_legit && (getbe16(chunk->type) & 0x01)); + if (!cia_encrypt) chunk->type[1] &= ~0x01; // remove crypto flag // open file(s) FIL ofile; @@ -963,28 +963,34 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, } // check if NCCH crypto is available - if (ncch_crypto) { + if (ncch_decrypt) { NcchHeader ncch; + u8 ctr[16]; + GetTmdCtr(ctr, chunk); if ((fvx_read(&ofile, &ncch, sizeof(NcchHeader), &bytes_read) != FR_OK) || + (cdn_decrypt && (DecryptCiaContentSequential((u8*) &ncch, 0x200, ctr, titlekey) != 0)) || (ValidateNcchHeader(&ncch) != 0) || (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0)) - ncch_crypto = false; + ncch_decrypt = false; fvx_lseek(&ofile, offset); } // main loop starts here - u8 ctr[16]; + u8 ctr_in[16]; + u8 ctr_out[16]; u32 ret = 0; - GetTmdCtr(ctr, chunk); + GetTmdCtr(ctr_in, chunk); + GetTmdCtr(ctr_out, chunk); if (!ShowProgress(0, 0, path_content)) ret = 1; for (u32 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) { u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); if (fvx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; - if (ncch_crypto && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1; + if (cdn_decrypt && (DecryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr_in, titlekey) != 0)) ret = 1; + if (ncch_decrypt && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1; if ((i == 0) && cxi_fix && (SetNcchSdFlag(MAIN_BUFFER) != 0)) ret = 1; if (i == 0) sha_init(SHA256_MODE); sha_update(MAIN_BUFFER, read_bytes); - if (cia_crypto && (EncryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey) != 0)) ret = 1; + if (cia_encrypt && (EncryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr_out, titlekey) != 0)) ret = 1; if (fvx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1; if ((read_bytes != bytes_read) || (bytes_read != bytes_written)) ret = 1; if (!ShowProgress(offset + i + read_bytes, fsize, path_content)) ret = 1; @@ -1016,7 +1022,7 @@ u32 InsertCiaMeta(const char* path_cia, CiaMeta* meta) { return (res) ? 0 : 1; } -u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_legit) { +u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_legit, bool cdn) { const u8 dlc_tid_high[] = { DLC_TID_HIGH }; CiaStub* cia = (CiaStub*) TEMP_BUFFER; CiaMeta* meta = (CiaMeta*) (TEMP_BUFFER + sizeof(CiaStub)); @@ -1040,17 +1046,24 @@ u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_l TmdContentChunk* content_list = cia->content_list; u32 content_count = getbe16(tmd->content_count); u8* title_id = tmd->title_id; - bool dlc = (memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0); + bool dlc = !cdn && (memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0); if (!content_count) return 1; // get (legit) ticket Ticket* ticket = &(cia->ticket); bool src_emunand = ((*path_tmd == 'B') || (*path_tmd == '4')); if (force_legit) { - if (FindTicket(ticket, title_id, true, src_emunand) != 0) { + if ((cdn && (LoadCdnTicketFile(ticket, path_tmd) != 0)) || + (!cdn && (FindTicket(ticket, title_id, true, src_emunand) != 0))) { ShowPrompt(false, "ID %016llX\nLegit ticket not found.", getbe64(title_id)); return 1; } + } else if (cdn) { + if ((LoadCdnTicketFile(ticket, path_tmd) != 0) && + (FindTitleKey(ticket, title_id) != 0)) { + ShowPrompt(false, "ID %016llX\nTitlekey not found.", getbe64(title_id)); + return 1; + } } else { if ((FindTitleKey(ticket, title_id) != 0) && (FindTicket(ticket, title_id, false, src_emunand) == 0) && @@ -1076,15 +1089,22 @@ u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_l for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { TmdContentChunk* chunk = &(content_list[i]); snprintf(name_content, 256 - (name_content - path_content), - (dlc) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id)); - if (InsertCiaContent(path_cia, path_content, 0, (u32) getbe64(chunk->size), chunk, titlekey, force_legit, false) != 0) { + (cdn) ? "%08lx" : (dlc) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id)); + if (InsertCiaContent(path_cia, path_content, 0, (u32) getbe64(chunk->size), chunk, titlekey, force_legit, false, cdn) != 0) { ShowPrompt(false, "ID %016llX.%08lX\nInsert content failed", getbe64(title_id), getbe32(chunk->id)); return 1; } } // try to build & insert meta, but ignore result - if (content_count) { + if (content_count && cdn) { + if (!force_legit || !(getbe16(content_list->type) & 0x01)) { + CiaInfo info; + GetCiaInfo(&info, &(cia->header)); + if ((LoadNcchMeta(meta, path_cia, info.offset_content) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) + cia->header.size_meta = CIA_META_SIZE; + } + } else if (content_count) { snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id)); if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) cia->header.size_meta = CIA_META_SIZE; @@ -1132,7 +1152,7 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { // insert NCCH content TmdContentChunk* chunk = cia->content_list; memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do - if (InsertCiaContent(path_cia, path_ncch, 0, 0, chunk, NULL, false, true) != 0) + if (InsertCiaContent(path_cia, path_ncch, 0, 0, chunk, NULL, false, true, false) != 0) return 1; // optional stuff (proper titlekey / meta data) @@ -1196,7 +1216,7 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { if (!size) continue; memset(chunk, 0, sizeof(TmdContentChunk)); chunk->id[3] = chunk->index[1] = i; - if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0)) != 0) + if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0), false) != 0) return 1; } @@ -1232,7 +1252,7 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) { // build CIA from game file if (filetype & GAME_TMD) - ret = BuildCiaFromTmdFile(path, dest, force_legit); + ret = BuildCiaFromTmdFile(path, dest, force_legit, filetype & FLAG_NUSCDN); else if (filetype & GAME_NCCH) ret = BuildCiaFromNcchFile(path, dest); else if (filetype & GAME_NCSD)