#include "ncchinfo.h" #include "ncch.h" #include "aes.h" u32 GetNcchInfoVersion(NcchInfoHeader* info) { if (!info->n_entries) return 0; // cannot be empty if (info->ncch_info_version == NCCHINFO_V3_MAGIC) return 3; else if (info->ncch_info_version == NCCHINFO_V4_MAGIC) return 4; else return 0; } u32 FixNcchInfoEntry(NcchInfoEntry* entry, u32 version) { // convert ncchinfo if v3 if (version == 3) { // ncchinfo v3 u8* entry_data = (u8*) (entry); memmove(entry_data + 56, entry_data + 48, 112); memset(entry_data + 48, 0, 8); // zero out nonexistent title id } else if (version != 4) { // !ncchinfo v4.0/v4.1/v4.2 return 1; } // poor man's UTF-16 -> UTF-8 if (entry->filename[1] == 0x00) { for (u32 i = 1; i < (sizeof(entry->filename) / 2); i++) entry->filename[i] = entry->filename[i*2]; } // fix sdmc: prefix if (memcmp(entry->filename, "sdmc:", 5) == 0) memmove(entry->filename, entry->filename + 5, 112 - 5); // workaround (1) for older (v4.0) ncchinfo.bin // this combination means seed crypto rather than FixedKey if ((entry->ncchFlag7 == 0x01) && entry->ncchFlag3) entry->ncchFlag7 = 0x20; // workaround (2) for older (v4.1) ncchinfo.bin if (!entry->size_b) entry->size_b = entry->size_mb * 1024 * 1024; return 0; } u32 BuildNcchInfoXorpad(void* buffer, NcchInfoEntry* entry, u32 size, u32 offset) { // set NCCH key // build faux NCCH header from entry NcchHeader ncch = { 0 }; memcpy(ncch.signature, entry->keyY, 16); ncch.flags[3] = (u8) entry->ncchFlag3; ncch.flags[7] = (u8) (entry->ncchFlag7 & ~0x04); ncch.programId = ncch.partitionId = entry->titleId; if (SetNcchKey(&ncch, NCCH_GET_CRYPTO(&ncch), 1) != 0) return 1; // write xorpad memset(buffer, 0, size); ctr_decrypt_byte(buffer, buffer, size, offset, AES_CNT_CTRNAND_MODE, entry->ctr); return 0; }