forked from Mirror/GodMode9
Install, build CIA, verify handling for TWL CDN content
This commit is contained in:
parent
f9408a9c10
commit
24195c319a
@ -86,9 +86,12 @@ u64 IdentifyFileType(const char* path) {
|
||||
return GAME_ROMFS; // RomFS file (check could be better)
|
||||
} else if (ValidateTmd((TitleMetaData*) data) == 0) {
|
||||
if (fsize == TMD_SIZE_N(getbe16(header + 0x1DE)) + TMD_CDNCERT_SIZE)
|
||||
return GAME_TMD | FLAG_NUSCDN; // TMD file from NUS/CDN
|
||||
return GAME_CDNTMD; // TMD file from NUS/CDN
|
||||
else if (fsize >= TMD_SIZE_N(getbe16(header + 0x1DE)))
|
||||
return GAME_TMD; // TMD file
|
||||
} else if (ValidateTwlTmd((TitleMetaData*) data) == 0) {
|
||||
if (fsize == TMD_SIZE_TWL + TMD_CDNCERT_SIZE)
|
||||
return GAME_TWLTMD; // TMD file from NUS/CDN (TWL)
|
||||
} else if (ValidateTicket((Ticket*) data) == 0) {
|
||||
return GAME_TICKET; // Ticket file
|
||||
} else if (ValidateFirmHeader((FirmHeader*) data, fsize) == 0) {
|
||||
@ -143,14 +146,6 @@ u64 IdentifyFileType(const char* path) {
|
||||
} else if ((strncasecmp(ext, "png", 4) == 0) &&
|
||||
(fsize > sizeof(png_magic)) && (memcmp(data, png_magic, sizeof(png_magic)) == 0)) {
|
||||
return GFX_PNG;
|
||||
} else if ((strncasecmp(ext, "cdn", 4) == 0) || (strncasecmp(ext, "nus", 4) == 0)) {
|
||||
char path_cetk[256];
|
||||
char* ext_cetk = path_cetk + (ext - path);
|
||||
strncpy(path_cetk, path, 256);
|
||||
path_cetk[255] = '\0';
|
||||
strncpy(ext_cetk, "cetk", 5);
|
||||
if (FileGetSize(path_cetk) > 0)
|
||||
return GAME_NUSCDN; // NUS/CDN type 2
|
||||
} else if (strncasecmp(fname, TIKDB_NAME_ENC, sizeof(TIKDB_NAME_ENC)+1) == 0) {
|
||||
return BIN_TIKDB | FLAG_ENC; // titlekey database / encrypted
|
||||
} else if (strncasecmp(fname, TIKDB_NAME_DEC, sizeof(TIKDB_NAME_DEC)+1) == 0) {
|
||||
@ -176,12 +171,12 @@ u64 IdentifyFileType(const char* path) {
|
||||
char* name_cdn = path_cdn + (fname - path);
|
||||
strncpy(path_cdn, path, 256);
|
||||
path_cdn[255] = '\0';
|
||||
strncpy(name_cdn, "tmd", 4);
|
||||
strncpy(name_cdn, "tmd", 4); // this will not catch tmd with version
|
||||
if (FileGetSize(path_cdn) > 0)
|
||||
return GAME_NUSCDN; // NUS/CDN type 1
|
||||
return GAME_NUSCDN; // NUS/CDN, recognized by TMD
|
||||
strncpy(name_cdn, "cetk", 5);
|
||||
if (FileGetSize(path_cdn) > 0)
|
||||
return GAME_NUSCDN; // NUS/CDN type 1
|
||||
return GAME_NUSCDN; // NUS/CDN, recognized by CETK
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -8,50 +8,51 @@
|
||||
#define GAME_NCSD (1ULL<<3)
|
||||
#define GAME_NCCH (1ULL<<4)
|
||||
#define GAME_TMD (1ULL<<5)
|
||||
#define GAME_CMD (1ULL<<6)
|
||||
#define GAME_EXEFS (1ULL<<7)
|
||||
#define GAME_ROMFS (1ULL<<8)
|
||||
#define GAME_BOSS (1ULL<<9)
|
||||
#define GAME_NUSCDN (1ULL<<10)
|
||||
#define GAME_TICKET (1ULL<<11)
|
||||
#define GAME_TIE (1ULL<<12)
|
||||
#define GAME_SMDH (1ULL<<13)
|
||||
#define GAME_3DSX (1ULL<<14)
|
||||
#define GAME_NDS (1ULL<<15)
|
||||
#define GAME_GBA (1ULL<<16)
|
||||
#define GAME_TAD (1ULL<<17)
|
||||
#define SYS_FIRM (1ULL<<18)
|
||||
#define SYS_DIFF (1ULL<<19)
|
||||
#define SYS_DISA (1ULL<<20)
|
||||
#define SYS_AGBSAVE (1ULL<<21)
|
||||
#define SYS_TICKDB (1ULL<<22)
|
||||
#define BIN_NCCHNFO (1ULL<<23)
|
||||
#define BIN_TIKDB (1ULL<<24)
|
||||
#define BIN_KEYDB (1ULL<<25)
|
||||
#define BIN_LEGKEY (1ULL<<26)
|
||||
#define TXT_SCRIPT (1ULL<<27)
|
||||
#define TXT_GENERIC (1ULL<<28)
|
||||
#define GFX_PNG (1ULL<<29)
|
||||
#define FONT_PBM (1ULL<<30)
|
||||
#define NOIMG_NAND (1ULL<<31)
|
||||
#define HDR_NAND (1ULL<<32)
|
||||
#define GAME_CDNTMD (1ULL<<6)
|
||||
#define GAME_TWLTMD (1ULL<<7)
|
||||
#define GAME_CMD (1ULL<<8)
|
||||
#define GAME_EXEFS (1ULL<<9)
|
||||
#define GAME_ROMFS (1ULL<<10)
|
||||
#define GAME_BOSS (1ULL<<11)
|
||||
#define GAME_NUSCDN (1ULL<<12)
|
||||
#define GAME_TICKET (1ULL<<13)
|
||||
#define GAME_TIE (1ULL<<14)
|
||||
#define GAME_SMDH (1ULL<<15)
|
||||
#define GAME_3DSX (1ULL<<16)
|
||||
#define GAME_NDS (1ULL<<17)
|
||||
#define GAME_GBA (1ULL<<18)
|
||||
#define GAME_TAD (1ULL<<19)
|
||||
#define SYS_FIRM (1ULL<<20)
|
||||
#define SYS_DIFF (1ULL<<21)
|
||||
#define SYS_DISA (1ULL<<22)
|
||||
#define SYS_AGBSAVE (1ULL<<23)
|
||||
#define SYS_TICKDB (1ULL<<24)
|
||||
#define BIN_NCCHNFO (1ULL<<25)
|
||||
#define BIN_TIKDB (1ULL<<26)
|
||||
#define BIN_KEYDB (1ULL<<27)
|
||||
#define BIN_LEGKEY (1ULL<<28)
|
||||
#define TXT_SCRIPT (1ULL<<29)
|
||||
#define TXT_GENERIC (1ULL<<30)
|
||||
#define GFX_PNG (1ULL<<31)
|
||||
#define FONT_PBM (1ULL<<32)
|
||||
#define NOIMG_NAND (1ULL<<33)
|
||||
#define HDR_NAND (1ULL<<34)
|
||||
#define TYPE_BASE 0xFFFFFFFFFFULL // 40 bit reserved for base types
|
||||
|
||||
// #define FLAG_FIRM (1ULL<<57) // <--- for CXIs containing FIRMs
|
||||
// #define FLAG_GBAVC (1ULL<<58) // <--- for GBAVC CXIs
|
||||
#define FLAG_DSIW (1ULL<<59)
|
||||
#define FLAG_ENC (1ULL<<60)
|
||||
#define FLAG_CTR (1ULL<<61)
|
||||
#define FLAG_NUSCDN (1ULL<<62)
|
||||
// #define FLAG_FIRM (1ULL<<58) // <--- for CXIs containing FIRMs
|
||||
// #define FLAG_GBAVC (1ULL<<59) // <--- for GBAVC CXIs
|
||||
#define FLAG_DSIW (1ULL<<60)
|
||||
#define FLAG_ENC (1ULL<<61)
|
||||
#define FLAG_CTR (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|GAME_TAD|SYS_FIRM|SYS_DIFF|SYS_DISA|SYS_TICKDB|BIN_KEYDB))
|
||||
#define FTYPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_TIE|GAME_TAD|GAME_TICKET|GAME_BOSS|SYS_FIRM))
|
||||
#define FTYPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_CDNTMD|GAME_TWLTMD|GAME_TIE|GAME_TAD|GAME_TICKET|GAME_BOSS|SYS_FIRM))
|
||||
#define FTYPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM|BIN_KEYDB))
|
||||
#define FTYPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|BIN_KEYDB))
|
||||
#define FTYPE_CIABUILD(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_TIE|GAME_TAD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))))
|
||||
#define FTYPE_CIABUILD_L(tp) (FTYPE_CIABUILD(tp) && (tp&(GAME_TMD|GAME_TIE|GAME_TAD)))
|
||||
#define FTYPE_CIAINSTALL(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_CIA)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))) || (tp&(GAME_TMD)&&(tp&(FLAG_NUSCDN))))
|
||||
#define FTYPE_CIABUILD(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_CDNTMD|GAME_TWLTMD|GAME_TIE|GAME_TAD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))))
|
||||
#define FTYPE_CIABUILD_L(tp) (tp&(GAME_TMD|GAME_CDNTMD|GAME_TIE|GAME_TAD))
|
||||
#define FTYPE_CIAINSTALL(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_CIA|GAME_CDNTMD|GAME_TWLTMD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))))
|
||||
#define FTYPE_TIKINSTALL(tp) (tp&(GAME_TICKET))
|
||||
#define FTYPE_CXIDUMP(tp) (tp&(GAME_TMD))
|
||||
#define FTYPE_UNINSTALL(tp) (tp&(GAME_TIE))
|
||||
|
@ -19,6 +19,14 @@ u32 ValidateTicket(Ticket* ticket) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ValidateTwlTicket(Ticket* ticket) {
|
||||
static const u8 magic[] = { TICKET_SIG_TYPE_TWL };
|
||||
if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) ||
|
||||
(strncmp((char*) ticket->issuer, TICKET_ISSUER_TWL, 0x40) != 0))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ValidateTicketSignature(Ticket* ticket) {
|
||||
static bool got_modexp = false;
|
||||
static u32 mod[0x100 / 0x4] = { 0 };
|
||||
|
@ -4,12 +4,16 @@
|
||||
|
||||
#define TICKET_COMMON_SIZE sizeof(TicketCommon)
|
||||
#define TICKET_MINIMUM_SIZE sizeof(TicketMinimum)
|
||||
#define TICKET_TWL_SIZE sizeof(Ticket)
|
||||
#define TICKET_CDNCERT_SIZE 0x700
|
||||
|
||||
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
||||
#define TICKET_ISSUER_DEV "Root-CA00000004-XS00000009"
|
||||
#define TICKET_SIG_TYPE 0x00, 0x01, 0x00, 0x04 // RSA_2048 SHA256
|
||||
|
||||
#define TICKET_ISSUER_TWL "Root-CA00000001-XS00000006"
|
||||
#define TICKET_SIG_TYPE_TWL 0x00, 0x01, 0x00, 0x01 // RSA_2048 SHA1
|
||||
|
||||
#define TICKET_DEVKIT(tick) (strncmp((char*)tick->issuer, TICKET_ISSUER_DEV, 0x40) == 0)
|
||||
|
||||
// from: https://github.com/profi200/Project_CTR/blob/02159e17ee225de3f7c46ca195ff0f9ba3b3d3e4/ctrtool/tik.h#L15-L39
|
||||
@ -91,6 +95,7 @@ typedef struct {
|
||||
} TicketRightsCheck;
|
||||
|
||||
u32 ValidateTicket(Ticket* ticket);
|
||||
u32 ValidateTwlTicket(Ticket* ticket);
|
||||
u32 ValidateTicketSignature(Ticket* ticket);
|
||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
||||
u32 GetTicketContentIndexSize(const Ticket* ticket);
|
||||
|
@ -26,15 +26,21 @@ u32 CryptTitleKey(TitleKeyEntry* tik, bool encrypt, bool devkit) {
|
||||
{0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F} , // 4
|
||||
{0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63} , // 5
|
||||
};
|
||||
// From unknown source
|
||||
static const u8 common_key_twl[16] __attribute__((aligned(16))) =
|
||||
{0xAF, 0x1B, 0xF5, 0x16, 0xA8, 0x07, 0xD2, 0x1A, 0xEA, 0x45, 0x98, 0x4F, 0x04, 0x74, 0x28, 0x61}; // TWL
|
||||
|
||||
u32 mode = (encrypt) ? AES_CNT_TITLEKEY_ENCRYPT_MODE : AES_CNT_TITLEKEY_DECRYPT_MODE;
|
||||
u8 ctr[16] = { 0 };
|
||||
|
||||
// setup key 0x3D // ctr
|
||||
if (tik->commonkey_idx >= 6) return 1;
|
||||
if (!devkit) setup_aeskeyY(0x3D, (void*) common_keyy[tik->commonkey_idx]);
|
||||
else setup_aeskey(0x3D, (void*) common_key_dev[tik->commonkey_idx]);
|
||||
use_aeskey(0x3D);
|
||||
if (getbe16(tik->title_id) == 0x3) { // setup TWL key
|
||||
setup_aeskey(0x11, (void*) common_key_twl);
|
||||
use_aeskey(0x11);
|
||||
} else { // setup key 0x3D // ctr
|
||||
if (!devkit) setup_aeskeyY(0x3D, (void*) common_keyy[tik->commonkey_idx]);
|
||||
else setup_aeskey(0x3D, (void*) common_key_dev[tik->commonkey_idx]);
|
||||
use_aeskey(0x3D);
|
||||
}
|
||||
memcpy(ctr, tik->title_id, 8);
|
||||
set_ctr(ctr);
|
||||
|
||||
@ -54,6 +60,17 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 SetTitleKey(const u8* titlekey, Ticket* ticket) {
|
||||
TitleKeyEntry tik = { 0 };
|
||||
memcpy(tik.title_id, ticket->title_id, 8);
|
||||
memcpy(tik.titlekey, titlekey, 16);
|
||||
tik.commonkey_idx = ticket->commonkey_idx;
|
||||
|
||||
if (CryptTitleKey(&tik, true, TICKET_DEVKIT(ticket)) != 0) return 1;
|
||||
memcpy(ticket->titlekey, tik.titlekey, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand) {
|
||||
const char* path_db = TICKDB_PATH(emunand); // EmuNAND / SysNAND
|
||||
char path_store[256] = { 0 };
|
||||
@ -141,3 +158,9 @@ u32 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt) { //
|
||||
tik.commonkey_idx = ticket->commonkey_idx;
|
||||
return AddTitleKeyToInfo(tik_info, &tik, false, decrypt, TICKET_DEVKIT(ticket));
|
||||
}
|
||||
|
||||
u32 CryptTitleKeyInfo(TitleKeysInfo* tik_info, bool encrypt) {
|
||||
for (u32 t = 0; t < tik_info->n_entries; t++)
|
||||
CryptTitleKey(tik_info->entries + t, encrypt, false);
|
||||
return 0;
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ typedef struct {
|
||||
|
||||
|
||||
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 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);
|
||||
|
@ -14,6 +14,15 @@ u32 ValidateTmd(TitleMetaData* tmd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ValidateTwlTmd(TitleMetaData* tmd) {
|
||||
static const u8 magic[] = { TMD_SIG_TYPE_TWL };
|
||||
if ((memcmp(tmd->sig_type, magic, sizeof(magic)) != 0) ||
|
||||
(strncmp((char*) tmd->issuer, TMD_ISSUER_TWL, 0x40) != 0) ||
|
||||
(getbe16(tmd->content_count) != 1))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ValidateTmdSignature(TitleMetaData* tmd) {
|
||||
static bool got_modexp = false;
|
||||
static u32 mod[0x100 / 4] = { 0 };
|
||||
|
@ -7,12 +7,17 @@
|
||||
#define TMD_SIZE_MIN sizeof(TitleMetaData)
|
||||
#define TMD_SIZE_MAX (sizeof(TitleMetaData) + (TMD_MAX_CONTENTS*sizeof(TmdContentChunk)))
|
||||
#define TMD_SIZE_N(n) (sizeof(TitleMetaData) + (n*sizeof(TmdContentChunk)))
|
||||
#define TMD_SIZE_STUB (TMD_SIZE_MIN - (0x20 + (64 * sizeof(TmdContentInfo))))
|
||||
#define TMD_SIZE_TWL (TMD_SIZE_STUB + 0x24)
|
||||
#define TMD_CDNCERT_SIZE 0x700
|
||||
|
||||
#define TMD_ISSUER "Root-CA00000003-CP0000000b"
|
||||
#define TMD_ISSUER_DEV "Root-CA00000004-CP0000000a"
|
||||
#define TMD_SIG_TYPE 0x00, 0x01, 0x00, 0x04 // RSA_2048 SHA256
|
||||
|
||||
#define TMD_ISSUER_TWL "Root-CA00000001-CP00000007"
|
||||
#define TMD_SIG_TYPE_TWL 0x00, 0x01, 0x00, 0x01 // RSA_2048 SHA1
|
||||
|
||||
#define DLC_TID_HIGH 0x00, 0x04, 0x00, 0x8C // title id high for DLC
|
||||
|
||||
// from: https://github.com/profi200/Project_CTR/blob/02159e17ee225de3f7c46ca195ff0f9ba3b3d3e4/ctrtool/tmd.h#L18-L59;
|
||||
@ -58,6 +63,7 @@ typedef struct {
|
||||
} __attribute__((packed, aligned(4))) TitleMetaData;
|
||||
|
||||
u32 ValidateTmd(TitleMetaData* tmd);
|
||||
u32 ValidateTwlTmd(TitleMetaData* tmd);
|
||||
u32 ValidateTmdSignature(TitleMetaData* tmd);
|
||||
u32 VerifyTmd(TitleMetaData* tmd);
|
||||
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk);
|
||||
|
@ -1126,7 +1126,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
||||
bool agbimportable = (FTYPE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
|
||||
|
||||
char cxi_path[256] = { 0 }; // special options for TMD
|
||||
if ((filetype & GAME_TMD) && !(filetype & FLAG_NUSCDN) &&
|
||||
if ((filetype & GAME_TMD) &&
|
||||
(GetTmdContentPath(cxi_path, file_path) == 0) &&
|
||||
(PathExist(cxi_path))) {
|
||||
u64 filetype_cxi = IdentifyFileType(cxi_path);
|
||||
@ -1134,7 +1134,12 @@ 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 || trimable || 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 || trimable || transferable ||
|
||||
hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable ||
|
||||
keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable ||
|
||||
agbimportable || cia_installable || tik_installable;
|
||||
|
||||
char pathstr[32+1];
|
||||
TruncateString(pathstr, file_path, 32, 8);
|
||||
@ -1176,6 +1181,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
||||
(filetype & GAME_EXEFS) ? "Mount as EXEFS image" :
|
||||
(filetype & GAME_ROMFS) ? "Mount as ROMFS image" :
|
||||
(filetype & GAME_TMD ) ? "TMD file options..." :
|
||||
(filetype & GAME_CDNTMD)? "TMD/CDN options..." :
|
||||
(filetype & GAME_TWLTMD)? "TMD/TWL options..." :
|
||||
(filetype & GAME_TIE ) ? "Manage Title..." :
|
||||
(filetype & GAME_BOSS ) ? "BOSS file options..." :
|
||||
(filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" :
|
||||
|
@ -194,14 +194,27 @@ u32 LoadNcchMeta(CiaMeta* meta, const char* path, u64 offset) {
|
||||
}
|
||||
|
||||
u32 LoadTmdFile(TitleMetaData* tmd, const char* path) {
|
||||
// first part (TMD only) (we need to read the content count first)
|
||||
if ((fvx_qread(path, tmd, 0, TMD_SIZE_MIN, NULL) != FR_OK) ||
|
||||
(ValidateTmd(tmd) != 0))
|
||||
// first part (TMD stub only) (we need to read the content count first)
|
||||
if (fvx_qread(path, tmd, 0, TMD_SIZE_STUB, NULL) != FR_OK)
|
||||
return 1;
|
||||
|
||||
// second part (read full size)
|
||||
if (fvx_qread(path, tmd, 0, TMD_SIZE_N(getbe16(tmd->content_count)), NULL) != FR_OK)
|
||||
return 1;
|
||||
if (ValidateTmd(tmd) == 0) {
|
||||
if (fvx_qread(path, tmd, 0, TMD_SIZE_N(getbe16(tmd->content_count)), NULL) != FR_OK)
|
||||
return 1;
|
||||
} else if ((ValidateTwlTmd(tmd) == 0) && (getbe16(tmd->content_count) == 1)) {
|
||||
// for TWL: convert to new TMD format
|
||||
static const u8 magic[] = { TMD_SIG_TYPE };
|
||||
memcpy(tmd->sig_type, magic, sizeof(magic));
|
||||
strncpy((char*) tmd->issuer, TMD_ISSUER, 0x40);
|
||||
memset(((u8*) tmd) + TMD_SIZE_STUB, 0x00, TMD_SIZE_N(1) - TMD_SIZE_STUB);
|
||||
(tmd->contentinfo)->cmd_count[1] = 0x01;
|
||||
// convert and take over chunk (SHA-1 hash stays)
|
||||
TmdContentChunk* chunk = (TmdContentChunk*) (void*) (tmd + 1);
|
||||
if ((fvx_qread(path, chunk, TMD_SIZE_STUB, 0x24, NULL) != FR_OK) ||
|
||||
(FixTmdHashes(tmd) != 0))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -212,17 +225,30 @@ u32 LoadTicketFile(Ticket** ticket, const char* path_tik) {
|
||||
// load and check ticket
|
||||
TicketMinimum tmp;
|
||||
UINT br;
|
||||
if ((fvx_qread(path_tik, &tmp, 0, TICKET_MINIMUM_SIZE, &br) != FR_OK) || (br != TICKET_MINIMUM_SIZE) ||
|
||||
ValidateTicket((Ticket*)&tmp) != 0) return 1;
|
||||
if (fvx_qread(path_tik, &tmp, 0, TICKET_MINIMUM_SIZE, &br) != FR_OK)
|
||||
return 1;
|
||||
|
||||
u32 tik_size = GetTicketSize((Ticket*)&tmp);
|
||||
// check type of ticket, set size
|
||||
u32 tik_size = 0;
|
||||
if ((br == TICKET_MINIMUM_SIZE) && (ValidateTicket((Ticket*)&tmp) == 0)) {
|
||||
// standard 3DS ticket
|
||||
tik_size = GetTicketSize((Ticket*)&tmp);
|
||||
} else if ((br == TICKET_TWL_SIZE) && (ValidateTwlTicket((Ticket*)&tmp) == 0)) {
|
||||
// TWL ticket
|
||||
tik_size = TICKET_COMMON_SIZE;
|
||||
} else return 1;
|
||||
|
||||
Ticket* tik = (Ticket*)malloc(tik_size);
|
||||
if (!tik) return 1;
|
||||
|
||||
if ((fvx_qread(path_tik, tik, 0, tik_size, &br) != FR_OK) || (br != tik_size)) {
|
||||
free(tik);
|
||||
return 1;
|
||||
if (br == TICKET_MINIMUM_SIZE) { // standard 3DS ticket
|
||||
if ((fvx_qread(path_tik, tik, 0, tik_size, &br) != FR_OK) || (br != tik_size)) {
|
||||
free(tik);
|
||||
return 1;
|
||||
}
|
||||
} else { // TWL ticket (just take over title id and key)
|
||||
BuildFakeTicket(tik, tmp.title_id);
|
||||
memcpy(tik->titlekey, tmp.titlekey, 0x10);
|
||||
}
|
||||
|
||||
*ticket = tik;
|
||||
@ -236,10 +262,9 @@ u32 LoadCdnTicketFile(Ticket** ticket, const char* path_cnt) {
|
||||
path_cetk[255] = '\0';
|
||||
char* name_cetk = strrchr(path_cetk, '/');
|
||||
if (!name_cetk) return 1; // will not happen
|
||||
char* ext_cetk = strrchr(++name_cetk, '.');
|
||||
ext_cetk = (ext_cetk) ? ext_cetk + 1 : name_cetk;
|
||||
snprintf(ext_cetk, 256 - (ext_cetk - path_cetk), "cetk");
|
||||
// ticket is loaded and vaildated here
|
||||
name_cetk++;
|
||||
snprintf(name_cetk, 256 - (name_cetk - path_cetk), "cetk");
|
||||
// ticket is loaded and validated here
|
||||
return LoadTicketFile(ticket, path_cetk);
|
||||
}
|
||||
|
||||
@ -389,7 +414,7 @@ u32 WriteCiaStub(CiaStub* stub, const char* path) {
|
||||
}
|
||||
|
||||
u32 VerifyTmdContent(const char* path, u64 offset, TmdContentChunk* chunk, const u8* titlekey) {
|
||||
u8 hash[32];
|
||||
u8 hash[32] = { 0 };
|
||||
u8 ctr[16];
|
||||
FIL file;
|
||||
|
||||
@ -412,8 +437,11 @@ u32 VerifyTmdContent(const char* path, u64 offset, TmdContentChunk* chunk, const
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 mode = SHA1_MODE;
|
||||
for (u32 i = 20; i < 32; i++)
|
||||
if (expected[i]) mode = SHA256_MODE;
|
||||
GetTmdCtr(ctr, chunk);
|
||||
sha_init(SHA256_MODE);
|
||||
sha_init(mode);
|
||||
for (u32 i = 0; i < size; i += STD_BUFFER_SIZE) {
|
||||
u32 read_bytes = min(STD_BUFFER_SIZE, (size - i));
|
||||
UINT bytes_read;
|
||||
@ -944,8 +972,8 @@ u32 VerifyGameFile(const char* path) {
|
||||
return VerifyNcsdFile(path);
|
||||
else if (filetype & GAME_NCCH)
|
||||
return VerifyNcchFile(path, 0, 0);
|
||||
else if (filetype & GAME_TMD)
|
||||
return VerifyTmdFile(path, filetype & FLAG_NUSCDN);
|
||||
else if (filetype & (GAME_TMD|GAME_CDNTMD|GAME_TWLTMD))
|
||||
return VerifyTmdFile(path, filetype & (GAME_CDNTMD|GAME_TWLTMD));
|
||||
else if (filetype & GAME_TIE)
|
||||
return VerifyTieFile(path);
|
||||
else if (filetype & GAME_TAD)
|
||||
@ -1065,7 +1093,7 @@ u32 CheckEncryptedGameFile(const char* path) {
|
||||
else if (filetype & SYS_FIRM)
|
||||
return CheckEncryptedFirmFile(path);
|
||||
else if (filetype & GAME_NUSCDN)
|
||||
return 0; // these should always be encrypted
|
||||
return 0; // these *should* always be encrypted
|
||||
else return 1;
|
||||
}
|
||||
|
||||
@ -1270,7 +1298,6 @@ u32 DecryptFirmFile(const char* orig, const char* dest) {
|
||||
}
|
||||
|
||||
u32 CryptCdnFileBuffered(const char* orig, const char* dest, u16 crypto, void* buffer) {
|
||||
bool inplace = (strncmp(orig, dest, 256) == 0);
|
||||
TitleMetaData* tmd = (TitleMetaData*) buffer;
|
||||
TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1);
|
||||
|
||||
@ -1335,15 +1362,7 @@ u32 CryptCdnFileBuffered(const char* orig, const char* dest, u16 crypto, void* b
|
||||
}
|
||||
|
||||
// actual crypto
|
||||
if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_NUSCDN, crypto, 0, 0, chunk, titlekey) != 0)
|
||||
return 1;
|
||||
|
||||
if (inplace && tmd) { // 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), NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CryptNcchNcsdBossFirmFile(orig, dest, GAME_NUSCDN, crypto, 0, 0, chunk, titlekey);
|
||||
}
|
||||
|
||||
u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) {
|
||||
@ -2123,6 +2142,9 @@ u32 BuildCiaFromTadFile(const char* path_tad, const char* path_dest, bool force_
|
||||
|
||||
u32 BuildInstallFromTmdFileBuffered(const char* path_tmd, const char* path_dest, bool force_legit, bool cdn, void* buffer, bool install) {
|
||||
const u8 dlc_tid_high[] = { DLC_TID_HIGH };
|
||||
static const u8 twl_tid_high[] = { 0x00, 0x03, 0x00, 0x04 };
|
||||
static const u8 ctr_tid_high[] = { 0x00, 0x04, 0x80, 0x04 };
|
||||
|
||||
CiaStub* cia = (CiaStub*) buffer;
|
||||
TitleMetaData* tmd = &(cia->tmd);
|
||||
TmdContentChunk* content_list = cia->content_list;
|
||||
@ -2151,6 +2173,15 @@ u32 BuildInstallFromTmdFileBuffered(const char* path_tmd, const char* path_dest,
|
||||
return 1;
|
||||
}
|
||||
|
||||
// fix title id/key for faked TWL TMDs & Tickets
|
||||
if (memcmp(title_id, twl_tid_high, 3) == 0) {
|
||||
u8 titlekey[16];
|
||||
memcpy(title_id, ctr_tid_high, 3);
|
||||
GetTitleKey(titlekey, (Ticket*) &(cia->ticket));
|
||||
memcpy((cia->ticket).title_id, title_id, 8);
|
||||
SetTitleKey(titlekey, (Ticket*) &(cia->ticket));
|
||||
}
|
||||
|
||||
// content path string
|
||||
char path_content[256];
|
||||
char* name_content;
|
||||
@ -2239,7 +2270,7 @@ u32 InstallFromTmdFile(const char* path_tmd, const char* path_dest) {
|
||||
void* buffer = (void*) malloc(sizeof(CiaStub));
|
||||
if (!buffer) return 1;
|
||||
|
||||
u32 ret = BuildInstallFromTmdFileBuffered(path_tmd, path_dest, true, true, buffer, true);
|
||||
u32 ret = BuildInstallFromTmdFileBuffered(path_tmd, path_dest, false, true, buffer, true);
|
||||
|
||||
free(buffer);
|
||||
return ret;
|
||||
@ -2500,7 +2531,7 @@ u64 GetGameFileTitleId(const char* path) {
|
||||
if (LoadCiaStub(cia, path) == 0)
|
||||
tid64 = getbe64(cia->tmd.title_id);
|
||||
free(cia);
|
||||
} else if (filetype & GAME_TMD) {
|
||||
} else if (filetype & (GAME_TMD|GAME_CDNTMD|GAME_TWLTMD)) {
|
||||
TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX);
|
||||
if (!tmd) return 0;
|
||||
if (LoadTmdFile(tmd, path) == 0)
|
||||
@ -2517,9 +2548,11 @@ u64 GetGameFileTitleId(const char* path) {
|
||||
} else if (filetype & GAME_NDS) {
|
||||
TwlHeader twl;
|
||||
if ((fvx_qread(path, &twl, 0, sizeof(TwlHeader), NULL) == FR_OK) && (twl.unit_code & 0x02))
|
||||
tid64 = 0x0004800000000000ull | (twl.title_id & 0xFFFFFFFFFFull);
|
||||
tid64 = twl.title_id;
|
||||
}
|
||||
|
||||
if ((tid64 & 0xFFFFFF0000000000ull) == 0x0003000000000000ull)
|
||||
tid64 = 0x0004800000000000ull | (tid64 & 0xFFFFFFFFFFull);
|
||||
return tid64;
|
||||
}
|
||||
|
||||
@ -2533,7 +2566,7 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
|
||||
char* dname = dest + strnlen(dest, 256);
|
||||
if (!((filetype & (GAME_TMD|GAME_TIE)) || (strncmp(path + 1, ":/title/", 8) == 0)) ||
|
||||
(GetGoodName(dname, path, false) != 0)) {
|
||||
u64 title_id = (filetype & GAME_TMD) ? GetGameFileTitleId(path) : 0;
|
||||
u64 title_id = (filetype & (GAME_TMD|GAME_CDNTMD|GAME_TWLTMD)) ? GetGameFileTitleId(path) : 0;
|
||||
if (!title_id) {
|
||||
char* name = strrchr(path, '/');
|
||||
if (!name) return 1;
|
||||
@ -2556,8 +2589,8 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
|
||||
// build CIA from game file
|
||||
if (filetype & GAME_TIE)
|
||||
ret = BuildCiaFromTieFile(path, dest, force_legit);
|
||||
else if (filetype & GAME_TMD)
|
||||
ret = BuildCiaFromTmdFile(path, dest, force_legit, filetype & FLAG_NUSCDN);
|
||||
else if (filetype & (GAME_TMD|GAME_CDNTMD|GAME_TWLTMD))
|
||||
ret = BuildCiaFromTmdFile(path, dest, force_legit, filetype & (GAME_CDNTMD|GAME_TWLTMD));
|
||||
else if (filetype & GAME_NCCH)
|
||||
ret = BuildInstallFromNcchFile(path, dest, false);
|
||||
else if (filetype & GAME_NCSD)
|
||||
@ -2625,7 +2658,7 @@ u32 InstallGameFile(const char* path, bool to_emunand) {
|
||||
// install game file
|
||||
if (filetype & GAME_CIA)
|
||||
ret = InstallFromCiaFile(path, drv);
|
||||
else if ((filetype & GAME_TMD) && (filetype & FLAG_NUSCDN))
|
||||
else if (filetype & (GAME_CDNTMD|GAME_TWLTMD))
|
||||
ret = InstallFromTmdFile(path, drv);
|
||||
else if (filetype & GAME_NCCH)
|
||||
ret = BuildInstallFromNcchFile(path, drv, true);
|
||||
@ -3069,7 +3102,7 @@ u32 ShowCiaCheckerInfo(const char* path) {
|
||||
state_ticket = (ValidateTicketSignature((Ticket*)&(cia->ticket)) == 0) ? 2 : 1;
|
||||
|
||||
// check tmd
|
||||
if (ValidateTmd(&(cia->tmd)) == 0)
|
||||
if (VerifyTmd(&(cia->tmd)) == 0)
|
||||
state_tmd = (ValidateTmdSignature(&(cia->tmd)) == 0) ? 2 : 1;
|
||||
|
||||
// check for available contents
|
||||
|
Loading…
x
Reference in New Issue
Block a user