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_UNINSTALL(tp) (tp&(GAME_TIE))
|
||||||
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
|
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
|
||||||
#define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY))
|
#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_CIACHECK(tp) (tp&GAME_CIA)
|
||||||
#define FTYPE_RENAMABLE(tp) (tp&(GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_NDS|GAME_GBA))
|
#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))
|
#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;
|
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) {
|
u32 BuildTwlSaveHeader(void* sav, u32 size) {
|
||||||
const u16 sct_size = 0x200;
|
const u16 sct_size = 0x200;
|
||||||
if (size / (u32) sct_size > 0xFFFF)
|
if (size / (u32) sct_size > 0xFFFF)
|
||||||
|
@ -127,6 +127,7 @@ typedef struct {
|
|||||||
} PACKED_STRUCT TwlHeader;
|
} PACKED_STRUCT TwlHeader;
|
||||||
|
|
||||||
u32 ValidateTwlHeader(TwlHeader* twl);
|
u32 ValidateTwlHeader(TwlHeader* twl);
|
||||||
|
u32 VerifyTwlIconData(TwlIconData* icon, u32 size);
|
||||||
u32 BuildTwlSaveHeader(void* sav, u32 size);
|
u32 BuildTwlSaveHeader(void* sav, u32 size);
|
||||||
u32 LoadTwlMetaData(const char* path, TwlHeader* hdr, TwlIconData* icon);
|
u32 LoadTwlMetaData(const char* path, TwlHeader* hdr, TwlIconData* icon);
|
||||||
u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon);
|
u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon);
|
||||||
|
@ -133,6 +133,17 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) {
|
|||||||
return (found) ? 0 : 1;
|
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) {
|
u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit) {
|
||||||
if (!tik_entry) { // no titlekey entry -> reset database
|
if (!tik_entry) { // no titlekey entry -> reset database
|
||||||
memset(tik_info, 0, 16);
|
memset(tik_info, 0, 16);
|
||||||
|
@ -38,6 +38,7 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
|||||||
u32 SetTitleKey(const u8* titlekey, Ticket* ticket);
|
u32 SetTitleKey(const u8* titlekey, Ticket* ticket);
|
||||||
u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand);
|
u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand);
|
||||||
u32 FindTitleKey(Ticket* ticket, u8* title_id);
|
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 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 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt);
|
||||||
u32 CryptTitleKeyInfo(TitleKeysInfo* tik_info, bool encrypt);
|
u32 CryptTitleKeyInfo(TitleKeysInfo* tik_info, bool encrypt);
|
||||||
|
@ -19,6 +19,33 @@
|
|||||||
// partitionA path
|
// partitionA path
|
||||||
#define PART_PATH "D:/partitionA.bin"
|
#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 GetNcchHeaders(NcchHeader* ncch, NcchExtHeader* exthdr, ExeFsHeader* exefs, FIL* file, bool nocrypto) {
|
||||||
u32 offset_ncch = fvx_tell(file);
|
u32 offset_ncch = fvx_tell(file);
|
||||||
UINT btr;
|
UINT btr;
|
||||||
@ -282,6 +309,9 @@ u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
|||||||
if (!name_content) return 1; // will not happen
|
if (!name_content) return 1; // will not happen
|
||||||
name_content++;
|
name_content++;
|
||||||
|
|
||||||
|
// CDN content?
|
||||||
|
bool cdn = IdentifyFileType(path_tmd) & (GAME_CDNTMD|GAME_TWLTMD);
|
||||||
|
|
||||||
// load TMD file
|
// load TMD file
|
||||||
TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX);
|
TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX);
|
||||||
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
|
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
|
||||||
@ -290,7 +320,7 @@ u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
|||||||
free(tmd);
|
free(tmd);
|
||||||
return 1;
|
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));
|
(memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id));
|
||||||
|
|
||||||
free(tmd);
|
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);
|
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,
|
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) {
|
TmdContentChunk* chunk, const u8* title_id, const u8* titlekey, bool cxi_fix, bool cdn_decrypt) {
|
||||||
char dest[256];
|
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
|
// try to build & insert meta, but ignore result (from encrypted data?)
|
||||||
if (!install) {
|
if (!install && content_count) {
|
||||||
CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta));
|
CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta));
|
||||||
if (meta) {
|
if (meta) {
|
||||||
if (content_count && cdn) {
|
if (cdn) {
|
||||||
if (!force_legit || !(getbe16(content_list->type) & 0x01)) {
|
if ((LoadEncryptedIconFromCiaTmd(path_tmd, meta, true) == GAME_NCCH) &&
|
||||||
CiaInfo info;
|
(InsertCiaMeta(path_dest, meta) == 0))
|
||||||
GetCiaInfo(&info, &(cia->header));
|
cia->header.size_meta = CIA_META_SIZE;
|
||||||
if ((LoadNcchMeta(meta, path_dest, info.offset_content) == 0) && (InsertCiaMeta(path_dest, meta) == 0))
|
} else {
|
||||||
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));
|
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))
|
if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_dest, meta) == 0))
|
||||||
cia->header.size_meta = CIA_META_SIZE;
|
cia->header.size_meta = CIA_META_SIZE;
|
||||||
@ -3050,13 +3197,17 @@ u32 ShowGameFileTitleInfoF(const char* path, u16* screen, bool clear) {
|
|||||||
char tidstr[32] = { '\0' };
|
char tidstr[32] = { '\0' };
|
||||||
if (tid) snprintf(tidstr, 32, "<%016llX>\n", tid);
|
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 ret = 1;
|
||||||
|
u32 tp = 0;
|
||||||
if (LoadSmdhFromGameFile(path, smdh) == 0)
|
if (LoadSmdhFromGameFile(path, smdh) == 0)
|
||||||
ret = ShowSmdhTitleInfo(smdh, tidstr, screen);
|
ret = ShowSmdhTitleInfo(smdh, tidstr, screen);
|
||||||
else if ((LoadTwlMetaData(path, NULL, twl_icon) == 0) ||
|
else if ((LoadTwlMetaData(path, NULL, twl_icon) == 0) ||
|
||||||
((itype & GAME_TAD) && (fvx_qread(path, twl_icon, TAD_BANNER_OFFSET, sizeof(TwlIconData), NULL) == FR_OK)))
|
((itype & GAME_TAD) && (fvx_qread(path, twl_icon, TAD_BANNER_OFFSET, sizeof(TwlIconData), NULL) == FR_OK)))
|
||||||
ret = ShowTwlIconTitleInfo(twl_icon, tidstr, screen);
|
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);
|
else ret = ShowGbaFileTitleInfo(path, screen);
|
||||||
|
|
||||||
if (!ret && clear) {
|
if (!ret && clear) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user