mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Title info and CIA metadata handling for encrypted CIA/CDN content
This commit is contained in:
parent
2f61722aa4
commit
c31737c257
@ -58,7 +58,7 @@
|
||||
#define FTYPE_UNINSTALL(tp) (tp&(GAME_TIE))
|
||||
#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_TIE|GAME_TICKET|GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS|GAME_GBA|GAME_TAD|GAME_3DSX))
|
||||
#define FTYPE_TITLEINFO(tp) (tp&(GAME_TIE|GAME_TICKET|GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_CDNTMD|GAME_TWLTMD|GAME_NDS|GAME_GBA|GAME_TAD|GAME_3DSX))
|
||||
#define FTYPE_CIACHECK(tp) (tp&GAME_CIA)
|
||||
#define FTYPE_RENAMABLE(tp) (tp&(GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_NDS|GAME_GBA))
|
||||
#define FTYPE_TRIMABLE(tp) (tp&(IMG_NAND|GAME_NCCH|GAME_NCSD|GAME_NDS|SYS_FIRM))
|
||||
|
@ -27,6 +27,23 @@ u32 ValidateTwlHeader(TwlHeader* twl) {
|
||||
return (crc16_quick(twl->logo, sizeof(twl->logo)) == NDS_LOGO_CRC16) ? 0 : 1;
|
||||
}
|
||||
|
||||
u32 VerifyTwlIconData(TwlIconData* icon, u32 version) {
|
||||
u32 tsize = TWLICON_SIZE_DATA(version);
|
||||
u32 isize = TWLICON_SIZE_DATA(icon->version);
|
||||
u8* icn = (u8*) icon;
|
||||
|
||||
if (!isize) return 1;
|
||||
if (version && (!tsize || tsize > isize)) return 1;
|
||||
|
||||
u32 size = version ? tsize : isize;
|
||||
if ((size >= 0x0840) && (crc16_quick(icn + 0x0020, 0x0840 - 0x0020) != icon->crc_0x0020_0x0840)) return 1;
|
||||
if ((size >= 0x0940) && (crc16_quick(icn + 0x0020, 0x0940 - 0x0020) != icon->crc_0x0020_0x0940)) return 1;
|
||||
if ((size >= 0x1240) && (crc16_quick(icn + 0x0020, 0x0A40 - 0x0020) != icon->crc_0x0020_0x0A40)) return 1;
|
||||
if ((size >= 0x23C0) && (crc16_quick(icn + 0x1240, 0x23C0 - 0x1240) != icon->crc_0x1240_0x23C0)) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 BuildTwlSaveHeader(void* sav, u32 size) {
|
||||
const u16 sct_size = 0x200;
|
||||
if (size / (u32) sct_size > 0xFFFF)
|
||||
|
@ -127,6 +127,7 @@ typedef struct {
|
||||
} PACKED_STRUCT TwlHeader;
|
||||
|
||||
u32 ValidateTwlHeader(TwlHeader* twl);
|
||||
u32 VerifyTwlIconData(TwlIconData* icon, u32 size);
|
||||
u32 BuildTwlSaveHeader(void* sav, u32 size);
|
||||
u32 LoadTwlMetaData(const char* path, TwlHeader* hdr, TwlIconData* icon);
|
||||
u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon);
|
||||
|
@ -133,6 +133,17 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) {
|
||||
return (found) ? 0 : 1;
|
||||
}
|
||||
|
||||
u32 FindTitleKeyForId(u8* titlekey, u8* title_id) {
|
||||
TicketCommon tik;
|
||||
|
||||
if ((BuildFakeTicket((Ticket*) &tik, title_id) != 0) ||
|
||||
(FindTitleKey((Ticket*) &tik, title_id) != 0) ||
|
||||
(GetTitleKey(titlekey, (Ticket*) &tik) != 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit) {
|
||||
if (!tik_entry) { // no titlekey entry -> reset database
|
||||
memset(tik_info, 0, 16);
|
||||
|
@ -38,6 +38,7 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
||||
u32 SetTitleKey(const u8* titlekey, Ticket* ticket);
|
||||
u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand);
|
||||
u32 FindTitleKey(Ticket* ticket, u8* title_id);
|
||||
u32 FindTitleKeyForId(u8* titlekey, u8* title_id);
|
||||
u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit);
|
||||
u32 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt);
|
||||
u32 CryptTitleKeyInfo(TitleKeysInfo* tik_info, bool encrypt);
|
||||
|
@ -19,6 +19,33 @@
|
||||
// partitionA path
|
||||
#define PART_PATH "D:/partitionA.bin"
|
||||
|
||||
|
||||
u32 GetCbcBlocks(FIL* file, void* buffer, u64 offset, u32 count, u8* titlekey, u8* forced_iv) {
|
||||
u8 iv[16] __attribute__((aligned(4)));
|
||||
UINT btr;
|
||||
|
||||
// sanity, get IV
|
||||
if ((count % 0x10) || (offset % 0x10)) return 1; // not possible
|
||||
if (forced_iv) memcpy(iv, forced_iv, 0x10);
|
||||
else if ((offset < 0x10) || (fvx_lseek(file, offset - 0x10) != FR_OK) ||
|
||||
(fvx_read(file, iv, 0x10, &btr) != FR_OK) || (btr != 0x10))
|
||||
return 1;
|
||||
|
||||
// load data
|
||||
if ((fvx_lseek(file, offset) != FR_OK) ||
|
||||
(fvx_read(file, buffer, count, &btr) != FR_OK) || (btr != count))
|
||||
return 1;
|
||||
|
||||
// decrypt
|
||||
if (titlekey) {
|
||||
setup_aeskey(0x11, titlekey);
|
||||
use_aeskey(0x11);
|
||||
cbc_decrypt(buffer, buffer, count / 0x10, AES_CNT_TITLEKEY_DECRYPT_MODE, iv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetNcchHeaders(NcchHeader* ncch, NcchExtHeader* exthdr, ExeFsHeader* exefs, FIL* file, bool nocrypto) {
|
||||
u32 offset_ncch = fvx_tell(file);
|
||||
UINT btr;
|
||||
@ -282,6 +309,9 @@ u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
||||
if (!name_content) return 1; // will not happen
|
||||
name_content++;
|
||||
|
||||
// CDN content?
|
||||
bool cdn = IdentifyFileType(path_tmd) & (GAME_CDNTMD|GAME_TWLTMD);
|
||||
|
||||
// load TMD file
|
||||
TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX);
|
||||
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
|
||||
@ -290,7 +320,7 @@ u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
||||
free(tmd);
|
||||
return 1;
|
||||
}
|
||||
snprintf(name_content, 256 - (name_content - path_content),
|
||||
snprintf(name_content, 256 - (name_content - path_content), cdn ? "%08lx" :
|
||||
(memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id));
|
||||
|
||||
free(tmd);
|
||||
@ -1617,6 +1647,126 @@ u32 UninstallGameDataTie(const char* path, bool remove_tie, bool remove_ticket,
|
||||
return UninstallGameData(tid64, remove_tie, remove_ticket, remove_save, from_emunand);
|
||||
}
|
||||
|
||||
u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, bool cia_meta) {
|
||||
u64 filetype = IdentifyFileType(path);
|
||||
u8 tik_data[16] __attribute__((aligned(32)));
|
||||
u8 iv[16] __attribute__((aligned(4)));
|
||||
u8* titlekey = NULL;
|
||||
u64 offset_cnt = 0;
|
||||
char path_cnt[256];
|
||||
u8 title_id[8];
|
||||
|
||||
strcpy(path_cnt, path);
|
||||
char* name_cnt = strrchr(path_cnt, '/');
|
||||
if (!name_cnt) return 0; // will not happen
|
||||
|
||||
void* data = (void*) malloc(max(sizeof(CiaStub), 0x1000));
|
||||
if (!data) return 0;
|
||||
|
||||
// get content path/offset and TMD data
|
||||
TitleMetaData* tmd = (TitleMetaData*) data;
|
||||
Ticket* ticket = NULL;
|
||||
if ((filetype & GAME_CIA) && (LoadCiaStub(data, path) == 0)) { // CIA file
|
||||
CiaStub* cia = (CiaStub*) data;
|
||||
CiaInfo info;
|
||||
GetCiaInfo(&info, data);
|
||||
tmd = &(cia->tmd);
|
||||
ticket =(Ticket*) &(cia->ticket);
|
||||
offset_cnt = info.offset_content;
|
||||
// strcpy(path_cnt, path); // unchanged
|
||||
} else if ((filetype & (GAME_TMD|GAME_CDNTMD|GAME_TWLTMD)) && (LoadTmdFile(tmd, path) == 0)) {
|
||||
const bool cdn = (filetype & (GAME_CDNTMD|GAME_TWLTMD));
|
||||
snprintf(++name_cnt, 16, (cdn ? "%08lx" : "%08lx.app"),
|
||||
getbe32(((TmdContentChunk*) (tmd+1))->id));
|
||||
// offset_cnt = 0; // unchanged
|
||||
} else {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// title_id, titlekey & iv
|
||||
TmdContentChunk* chunk = (TmdContentChunk*) (tmd+1);
|
||||
GetTmdCtr(iv, chunk);
|
||||
memcpy(title_id, tmd->title_id, 8);
|
||||
if (getbe16(chunk->type) & 0x1) {
|
||||
titlekey = tik_data;
|
||||
if ((ticket && (GetTitleKey(titlekey, ticket) != 0)) ||
|
||||
(FindTitleKeyForId(titlekey, title_id) != 0)) {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// load first block of data
|
||||
FIL file;
|
||||
if (fvx_open(&file, path_cnt, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
if (GetCbcBlocks(&file, data, offset_cnt, 0x1000, titlekey, iv) != 0) {
|
||||
fvx_close(&file);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find out what it is and proceed
|
||||
u32 ret = 0;
|
||||
if (!cia_meta && ValidateTwlHeader((TwlHeader*) data) == 0) {
|
||||
// TWL data
|
||||
TwlHeader* twl = (TwlHeader*) data;
|
||||
if (twl->icon_offset && (GetCbcBlocks(&file, (u8*) output,
|
||||
offset_cnt + twl->icon_offset, TWLICON_SIZE_DATA(0x0001), titlekey, NULL) == 0) &&
|
||||
(VerifyTwlIconData((TwlIconData*) output, 0x0001) == 0))
|
||||
ret = GAME_NDS;
|
||||
} else if (ValidateNcchHeader((NcchHeader*) data) == 0) {
|
||||
// NCCH data
|
||||
static const u8 smdh_magic[] = { SMDH_MAGIC };
|
||||
NcchHeader* ncch = (NcchHeader*) data;
|
||||
u8* icon = NULL;
|
||||
if (cia_meta) {
|
||||
CiaMeta* meta = (CiaMeta*) output;
|
||||
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (((u8*)data) + NCCH_EXTHDR_OFFSET);
|
||||
if (!ncch->size_exthdr ||
|
||||
(DecryptNcch(exthdr, NCCH_EXTHDR_OFFSET, NCCH_EXTHDR_SIZE, ncch, NULL) != 0) ||
|
||||
(BuildCiaMeta(meta, exthdr, NULL) != 0)) {
|
||||
fvx_close(&file);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
icon = (u8*) &(meta->smdh);
|
||||
} else icon = (u8*) output;
|
||||
memset(icon, 0x00, sizeof(smdh_magic)); // magic number
|
||||
|
||||
if (ncch->size_exefs) {
|
||||
ExeFsHeader exefs;
|
||||
u64 offset_exefs = ncch->offset_exefs * NCCH_MEDIA_UNIT;
|
||||
if ((GetCbcBlocks(&file, &exefs, offset_cnt + offset_exefs, sizeof(ExeFsHeader), titlekey, NULL) == 0) &&
|
||||
(DecryptNcch(&exefs, offset_exefs, sizeof(ExeFsHeader), ncch, NULL) == 0) &&
|
||||
(ValidateExeFsHeader(&exefs, ncch->size_exefs * NCCH_MEDIA_UNIT) == 0)) {
|
||||
ExeFsFileHeader* exefile = NULL;
|
||||
for (u32 i = 0; i < 10; i++) {
|
||||
if ((exefs.files[i].size == sizeof(Smdh)) &&
|
||||
(strncmp("icon", exefs.files[i].name, 8) == 0)) {
|
||||
exefile = exefs.files + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exefile) {
|
||||
u32 offset_exef = offset_exefs + sizeof(ExeFsHeader) + exefile->offset;
|
||||
if ((GetCbcBlocks(&file, icon, offset_cnt + offset_exef, sizeof(Smdh), titlekey, NULL) == 0) &&
|
||||
(DecryptNcch(icon, offset_exef, sizeof(Smdh), ncch, &exefs) == 0) &&
|
||||
(memcmp(icon, smdh_magic, sizeof(smdh_magic)) == 0))
|
||||
ret = GAME_NCCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fvx_close(&file);
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 InstallCiaContent(const char* drv, const char* path_content, u32 offset, u32 size,
|
||||
TmdContentChunk* chunk, const u8* title_id, const u8* titlekey, bool cxi_fix, bool cdn_decrypt) {
|
||||
char dest[256];
|
||||
@ -2236,18 +2386,15 @@ u32 BuildInstallFromTmdFileBuffered(const char* path_tmd, const char* path_dest,
|
||||
}
|
||||
}
|
||||
|
||||
// try to build & insert meta, but ignore result
|
||||
if (!install) {
|
||||
// try to build & insert meta, but ignore result (from encrypted data?)
|
||||
if (!install && content_count) {
|
||||
CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta));
|
||||
if (meta) {
|
||||
if (content_count && cdn) {
|
||||
if (!force_legit || !(getbe16(content_list->type) & 0x01)) {
|
||||
CiaInfo info;
|
||||
GetCiaInfo(&info, &(cia->header));
|
||||
if ((LoadNcchMeta(meta, path_dest, info.offset_content) == 0) && (InsertCiaMeta(path_dest, meta) == 0))
|
||||
if (cdn) {
|
||||
if ((LoadEncryptedIconFromCiaTmd(path_tmd, meta, true) == GAME_NCCH) &&
|
||||
(InsertCiaMeta(path_dest, meta) == 0))
|
||||
cia->header.size_meta = CIA_META_SIZE;
|
||||
}
|
||||
} else if (content_count) {
|
||||
} else {
|
||||
snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id));
|
||||
if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_dest, meta) == 0))
|
||||
cia->header.size_meta = CIA_META_SIZE;
|
||||
@ -3050,13 +3197,17 @@ u32 ShowGameFileTitleInfoF(const char* path, u16* screen, bool clear) {
|
||||
char tidstr[32] = { '\0' };
|
||||
if (tid) snprintf(tidstr, 32, "<%016llX>\n", tid);
|
||||
|
||||
// try loading SMDH, then try NDS / GBA
|
||||
// try loading SMDH, then try NDS / encrypted / GBA
|
||||
u32 ret = 1;
|
||||
u32 tp = 0;
|
||||
if (LoadSmdhFromGameFile(path, smdh) == 0)
|
||||
ret = ShowSmdhTitleInfo(smdh, tidstr, screen);
|
||||
else if ((LoadTwlMetaData(path, NULL, twl_icon) == 0) ||
|
||||
((itype & GAME_TAD) && (fvx_qread(path, twl_icon, TAD_BANNER_OFFSET, sizeof(TwlIconData), NULL) == FR_OK)))
|
||||
ret = ShowTwlIconTitleInfo(twl_icon, tidstr, screen);
|
||||
else if ((tp = LoadEncryptedIconFromCiaTmd(path, buffer, false)) != 0)
|
||||
ret = (tp == GAME_NCCH) ? ShowSmdhTitleInfo(smdh, tidstr, screen) :
|
||||
(tp == GAME_NDS ) ? ShowTwlIconTitleInfo(twl_icon, tidstr, screen) : 1;
|
||||
else ret = ShowGbaFileTitleInfo(path, screen);
|
||||
|
||||
if (!ret && clear) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user