mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
On the fly crypto handling for mounted game files
This commit is contained in:
parent
f23d2fd30a
commit
d8442ac0cb
@ -38,7 +38,7 @@
|
||||
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
||||
|
||||
// GodMode9 version
|
||||
#define VERSION "0.8.2"
|
||||
#define VERSION "0.8.3"
|
||||
|
||||
// input / output paths
|
||||
#define INPUT_PATHS "0:", "0:/files9", "0:/Decrypt9"
|
||||
|
@ -1,4 +1,23 @@
|
||||
#include "ncch.h"
|
||||
#include "keydb.h"
|
||||
#include "aes.h"
|
||||
#include "sha.h"
|
||||
#include "ff.h"
|
||||
|
||||
#define SEEDDB_NAME "seeddb.bin"
|
||||
#define EXEFS_KEYID(name) (((strncmp(name, "banner", 8) == 0) || (strncmp(name, "icon", 8) == 0)) ? 0 : 1)
|
||||
|
||||
typedef struct {
|
||||
u64 titleId;
|
||||
u8 seed[16];
|
||||
u8 reserved[8];
|
||||
} __attribute__((packed)) SeedInfoEntry;
|
||||
|
||||
typedef struct {
|
||||
u32 n_entries;
|
||||
u8 padding[12];
|
||||
SeedInfoEntry entries[256]; // this number is only a placeholder
|
||||
} __attribute__((packed)) SeedInfo;
|
||||
|
||||
u32 ValidateNcchHeader(NcchHeader* header) {
|
||||
if (memcmp(header->magic, "NCCH", 4) != 0) // check magic number
|
||||
@ -22,3 +41,230 @@ u32 ValidateNcchHeader(NcchHeader* header) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetNcchCtr(u8* ctr, NcchHeader* ncch, u8 section) {
|
||||
memset(ctr, 0x00, 16);
|
||||
if (ncch->version == 1) {
|
||||
memcpy(ctr, &(ncch->programId), 8);
|
||||
if (section == 1) { // ExtHeader ctr
|
||||
add_ctr(ctr, NCCH_EXTHDR_OFFSET);
|
||||
} else if (section == 2) { // ExeFS ctr
|
||||
add_ctr(ctr, ncch->offset_exefs * NCCH_MEDIA_UNIT);
|
||||
} else if (section == 3) { // RomFS ctr
|
||||
add_ctr(ctr, ncch->offset_romfs * NCCH_MEDIA_UNIT);
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
ctr[i] = ((u8*) &(ncch->programId))[7-i];
|
||||
ctr[8] = section;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
|
||||
static u8 lseed[16+8] = { 0 }; // seed plus title ID for easy validation
|
||||
u64 titleId = ncch->programId;
|
||||
u32 hash_seed = ncch->hash_seed;
|
||||
|
||||
UINT btr = 0;
|
||||
FIL file;
|
||||
char path[128];
|
||||
u32 sha256sum[8];
|
||||
|
||||
memcpy(lseed+16, &(ncch->programId), 8);
|
||||
sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE);
|
||||
if (hash_seed == sha256sum[0]) {
|
||||
memcpy(seed, lseed, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// try to grab the seed from NAND database
|
||||
const char* nand_drv[] = {"1:", "4:"}; // SysNAND and EmuNAND
|
||||
for (u32 i = 0; i < (sizeof(nand_drv)/sizeof(char*)); i++) {
|
||||
// grab the key Y from movable.sed
|
||||
u8 movable_keyy[16];
|
||||
snprintf(path, 128, "%s/private/movable.sed", nand_drv[i]);
|
||||
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||
continue;
|
||||
f_lseek(&file, 0x110);
|
||||
f_read(&file, movable_keyy, 0x10, &btr);
|
||||
f_close(&file);
|
||||
|
||||
// build the seed save path
|
||||
sha_quick(sha256sum, movable_keyy, 0x10, SHA256_MODE);
|
||||
snprintf(path, 128, "%s/data/%08lX%08lX%08lX%08lX/sysdata/0001000F/00000000",
|
||||
nand_drv[i], sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]);
|
||||
|
||||
// check seedsave for seed
|
||||
u8* seedsave = (u8*) GAME_BUFFER;
|
||||
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||
continue;
|
||||
f_read(&file, seedsave, 0x200, &btr);
|
||||
u32 p_active = (getle32(seedsave + 0x168)) ? 1 : 0;
|
||||
static const u32 seed_offset[2] = {0x7000, 0x5C000};
|
||||
for (u32 p = 0; p < 2; p++) {
|
||||
f_lseek(&file, seed_offset[(p + p_active) % 2]);
|
||||
f_read(&file, seedsave, 2000*(8+16), &btr);
|
||||
for (u32 s = 0; s < 2000; s++) {
|
||||
if (titleId != getle64(seedsave + (s*8)))
|
||||
continue;
|
||||
memcpy(lseed, seedsave + (2000*8) + (s*16), 16);
|
||||
sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE);
|
||||
if (hash_seed == sha256sum[0]) {
|
||||
memcpy(seed, lseed, 16);
|
||||
f_close(&file);
|
||||
return 0; // found!
|
||||
}
|
||||
}
|
||||
}
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
// not found -> try seeddb.bin
|
||||
const char* base[] = { INPUT_PATHS };
|
||||
for (u32 i = 0; i < (sizeof(base)/sizeof(char*)); i++) {
|
||||
SeedInfo* seeddb = (SeedInfo*) GAME_BUFFER;
|
||||
snprintf(path, 128, "%s/%s", base[i], SEEDDB_NAME);
|
||||
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||
continue;
|
||||
f_read(&file, seeddb, GAME_BUFFER_SIZE, &btr);
|
||||
f_close(&file);
|
||||
if (seeddb->n_entries > (btr - 16) / 32)
|
||||
continue; // filesize / seeddb size mismatch
|
||||
for (u32 s = 0; s < seeddb->n_entries; s++) {
|
||||
if (titleId != seeddb->entries[s].titleId)
|
||||
continue;
|
||||
memcpy(lseed, seeddb->entries[s].seed, 16);
|
||||
sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE);
|
||||
if (hash_seed == sha256sum[0]) {
|
||||
memcpy(seed, lseed, 16);
|
||||
return 0; // found!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// out of options -> failed!
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 SetNcchKey(NcchHeader* ncch, u32 keyid) {
|
||||
u32 keyslot = (!keyid || !ncch->flags[3]) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto
|
||||
(ncch->flags[3] == 0x0A) ? 0x18 : (ncch->flags[3] == 0x0B) ? 0x1B : 0x25;
|
||||
|
||||
if (!NCCH_ENCRYPTED(ncch))
|
||||
return 1;
|
||||
|
||||
if (ncch->flags[7] & 0x01) { // fixed key crypto
|
||||
// from https://github.com/profi200/Project_CTR/blob/master/makerom/pki/dev.h
|
||||
u8 zeroKey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // zero key
|
||||
u8 sysKey[16] = { 0x52, 0x7C, 0xE6, 0x30, 0xA9, 0xCA, 0x30, 0x5F,
|
||||
0x36, 0x96, 0xF3, 0xCD, 0xE9, 0x54, 0x19, 0x4B }; // fixed sys key
|
||||
setup_aeskey(0x11, (ncch->programId & ((u64) 0x10 << 32)) ? sysKey : zeroKey);
|
||||
use_aeskey(0x11);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// load key X from file if required
|
||||
if ((keyslot != 0x2C) && (LoadKeyFromFile(NULL, keyslot, 'X', NULL) != 0))
|
||||
return 1;
|
||||
|
||||
// key Y for seed and non seed
|
||||
if (keyid && (ncch->flags[7] & 0x20)) { // seed crypto
|
||||
u8 keydata[16+16];
|
||||
u8 seedkeyY[16+16];
|
||||
memcpy(keydata, ncch->signature, 16);
|
||||
if (GetNcchSeed(keydata + 16, ncch) != 0)
|
||||
return 1;
|
||||
sha_quick(seedkeyY, keydata, 32, SHA256_MODE);
|
||||
setup_aeskeyY(keyslot, seedkeyY);
|
||||
} else { // no seed crypto
|
||||
setup_aeskeyY(keyslot, ncch->signature);
|
||||
}
|
||||
use_aeskey(keyslot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 CheckNcchCrypto(NcchHeader* ncch) {
|
||||
return (!NCCH_ENCRYPTED(ncch)) ? 1 :
|
||||
((SetNcchKey(ncch, 0) == 0) && (SetNcchKey(ncch, 1) == 0)) ? 0 : 1;
|
||||
}
|
||||
|
||||
u32 DecryptNcchSection(u8* data, u32 offset_data, u32 size_data,
|
||||
u32 offset_section, u32 size_section, u32 offset_ctr, NcchHeader* ncch, u32 snum, u32 keyid) {
|
||||
const u32 mode = AES_CNT_CTRNAND_MODE;
|
||||
|
||||
// check if section in data
|
||||
if ((offset_section >= offset_data + size_data) ||
|
||||
(offset_data >= offset_section + size_section) ||
|
||||
!size_section) {
|
||||
return 0; // section not in data
|
||||
}
|
||||
|
||||
// determine data / offset / size
|
||||
u8* data_i = data;
|
||||
u32 offset_i = 0;
|
||||
u32 size_i = size_section;
|
||||
if (offset_section < offset_data)
|
||||
offset_i = offset_data - offset_section;
|
||||
else data_i = data + (offset_section - offset_data);
|
||||
size_i = size_section - offset_i;
|
||||
if (size_i > size_data - (data_i - data))
|
||||
size_i = size_data - (data_i - data);
|
||||
|
||||
// actual decryption stuff
|
||||
u8 ctr[16];
|
||||
GetNcchCtr(ctr, ncch, snum);
|
||||
if (SetNcchKey(ncch, keyid) != 0) return 1;
|
||||
ctr_decrypt_boffset(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// on the fly decryptor for NCCH
|
||||
u32 DecryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs) {
|
||||
const u32 offset_flag3 = 0x188 + 3;
|
||||
const u32 offset_flag7 = 0x188 + 7;
|
||||
|
||||
// check for encryption
|
||||
if (!NCCH_ENCRYPTED(ncch))
|
||||
return 0;
|
||||
|
||||
// ncch flags handling
|
||||
if ((offset <= offset_flag3) && (offset + size > offset_flag3))
|
||||
data[offset_flag3 - offset] = 0;
|
||||
if ((offset <= offset_flag7) && (offset + size > offset_flag7)) {
|
||||
data[offset_flag7 - offset] &= ~(0x01|0x20);
|
||||
data[offset_flag7 - offset] |= 0x04;
|
||||
}
|
||||
|
||||
// exthdr handling
|
||||
if (DecryptNcchSection(data, offset, size,
|
||||
NCCH_EXTHDR_OFFSET,
|
||||
NCCH_EXTHDR_SIZE,
|
||||
0, ncch, 1, 0) != 0) return 1;
|
||||
|
||||
// exefs header handling
|
||||
if (DecryptNcchSection(data, offset, size,
|
||||
ncch->offset_exefs * NCCH_MEDIA_UNIT,
|
||||
0x200, 0, ncch, 2, 0) != 0) return 1;
|
||||
|
||||
// exefs file handling
|
||||
if (exefs) for (u32 i = 0; i < 10; i++) {
|
||||
ExeFsFileHeader* file = exefs->files + i;
|
||||
if (DecryptNcchSection(data, offset, size,
|
||||
(ncch->offset_exefs * NCCH_MEDIA_UNIT) + 0x200 + file->offset,
|
||||
file->size, 0x200 + file->offset,
|
||||
ncch, 2, EXEFS_KEYID(file->name)) != 0) return 1;
|
||||
}
|
||||
|
||||
// romfs handling
|
||||
if (DecryptNcchSection(data, offset, size,
|
||||
ncch->offset_romfs * NCCH_MEDIA_UNIT,
|
||||
ncch->size_romfs * NCCH_MEDIA_UNIT,
|
||||
0, ncch, 3, 1) != 0) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "exefs.h"
|
||||
|
||||
#define NCCH_MEDIA_UNIT 0x200
|
||||
|
||||
@ -33,7 +34,7 @@ typedef struct {
|
||||
u64 partitionId;
|
||||
u16 makercode;
|
||||
u16 version;
|
||||
u8 hash_seed[0x4];
|
||||
u32 hash_seed;
|
||||
u64 programId;
|
||||
u8 reserved0[0x10];
|
||||
u8 hash_logo[0x20];
|
||||
@ -59,3 +60,5 @@ typedef struct {
|
||||
} __attribute__((packed)) NcchHeader;
|
||||
|
||||
u32 ValidateNcchHeader(NcchHeader* header);
|
||||
u32 CheckNcchCrypto(NcchHeader* ncch);
|
||||
u32 DecryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "aes.h"
|
||||
#include "ff.h"
|
||||
|
||||
#define VFLAG_EXEFS_FILE (1<<25)
|
||||
#define VFLAG_EXTHDR (1<<26)
|
||||
#define VFLAG_CIA (1<<27)
|
||||
#define VFLAG_NCSD (1<<28)
|
||||
@ -20,18 +21,15 @@
|
||||
#define NAME_CIA_TMD "tmd.bin"
|
||||
#define NAME_CIA_TMDCHUNK "tmdchunks.bin"
|
||||
#define NAME_CIA_META "meta.bin"
|
||||
#define NAME_CIA_CONTENT "%04X.%08lX.app" // index.id.app
|
||||
#define NAME_CIA_DIR "%04X.%08lX" // index.id
|
||||
#define NAME_CIA_CONTENT "%04X.%08lX%s" // index.id(.ext)
|
||||
|
||||
#define NAME_NCSD_HEADER "ncsd.bin"
|
||||
#define NAME_NCSD_CARDINFO "cardinfo.bin"
|
||||
#define NAME_NCSD_DEVINFO "devinfo.bin"
|
||||
#define NAME_NCSD_CONTENT "cnt0.game.cxi", "cnt1.manual.cfa", "cnt2.dlp.cfa", \
|
||||
"cnt3.unk", "cnt4.unk", "cnt5.unk", \
|
||||
"cnt6.update_n3ds.cfa", "cnt7.update_o3ds.cfa"
|
||||
#define NAME_NCSD_DIR "cnt0.game", "cnt1.manual", "cnt2.dlp", \
|
||||
"cnt3", "cnt4", "cnt5", \
|
||||
"cnt6.update_n3ds", "cnt7.update_o3ds"
|
||||
#define NAME_NCSD_TYPES "game", "manual", "dlp", \
|
||||
"unk", "unk", "unk", \
|
||||
"update_n3ds", "update_o3ds"
|
||||
#define NAME_NCSD_CONTENT "content%lu.%s%s" // content?.type(.ext)
|
||||
|
||||
#define NAME_NCCH_HEADER "ncch.bin"
|
||||
#define NAME_NCCH_EXTHEADER "extheader.bin"
|
||||
@ -80,8 +78,8 @@ bool BuildVGameExeFsDir(void) {
|
||||
snprintf(templates[n].name, 32, "%.8s", file->name);
|
||||
templates[n].offset = offset_exefs + sizeof(ExeFsHeader) + file->offset;
|
||||
templates[n].size = file->size;
|
||||
templates[n].keyslot = 0xFF; // needs to be handled
|
||||
templates[n].flags = 0;
|
||||
templates[n].keyslot = NCCH_ENCRYPTED(ncch) ? 0x2C : 0xFF; // actual keyslot may be different
|
||||
templates[n].flags = VFLAG_EXEFS_FILE;
|
||||
n++;
|
||||
}
|
||||
|
||||
@ -93,6 +91,9 @@ bool BuildVGameNcchDir(void) {
|
||||
VirtualFile* templates = templates_ncch;
|
||||
u32 n = 0;
|
||||
|
||||
// NCCH crypto
|
||||
bool ncch_crypto = (CheckNcchCrypto(ncch) == 0);
|
||||
|
||||
// header
|
||||
strncpy(templates[n].name, NAME_NCCH_HEADER, 32);
|
||||
templates[n].offset = offset_ncch + 0;
|
||||
@ -106,7 +107,7 @@ bool BuildVGameNcchDir(void) {
|
||||
strncpy(templates[n].name, NAME_NCCH_EXTHEADER, 32);
|
||||
templates[n].offset = offset_ncch + NCCH_EXTHDR_OFFSET;
|
||||
templates[n].size = NCCH_EXTHDR_SIZE;
|
||||
templates[n].keyslot = 0xFF; // crypto ?
|
||||
templates[n].keyslot = ncch_crypto ? 0x2C : 0xFF;
|
||||
templates[n].flags = VFLAG_EXTHDR;
|
||||
n++;
|
||||
}
|
||||
@ -136,10 +137,10 @@ bool BuildVGameNcchDir(void) {
|
||||
strncpy(templates[n].name, NAME_NCCH_EXEFS, 32);
|
||||
templates[n].offset = offset_ncch + (ncch->offset_exefs * NCCH_MEDIA_UNIT);
|
||||
templates[n].size = ncch->size_exefs * NCCH_MEDIA_UNIT;
|
||||
templates[n].keyslot = 0xFF; // crypto ?
|
||||
templates[n].keyslot = ncch_crypto ? 0x2C : 0xFF; // real slot may be something else
|
||||
templates[n].flags = VFLAG_EXEFS;
|
||||
n++;
|
||||
if (!NCCH_ENCRYPTED(ncch)) {
|
||||
if (!NCCH_ENCRYPTED(ncch) || ncch_crypto) {
|
||||
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
|
||||
strncpy(templates[n].name, NAME_NCCH_EXEFSDIR, 32);
|
||||
templates[n].flags |= VFLAG_DIR;
|
||||
@ -152,10 +153,10 @@ bool BuildVGameNcchDir(void) {
|
||||
strncpy(templates[n].name, NAME_NCCH_ROMFS, 32);
|
||||
templates[n].offset = offset_ncch + (ncch->offset_romfs * NCCH_MEDIA_UNIT);
|
||||
templates[n].size = ncch->size_romfs * NCCH_MEDIA_UNIT;
|
||||
templates[n].keyslot = 0xFF; // crypto ?
|
||||
templates[n].keyslot = ncch_crypto ? 0x2C : 0xFF; // real slot may be something else
|
||||
templates[n].flags = VFLAG_ROMFS;
|
||||
n++;
|
||||
if (!NCCH_ENCRYPTED(ncch)) {
|
||||
if (!NCCH_ENCRYPTED(ncch) || ncch_crypto) {
|
||||
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
|
||||
strncpy(templates[n].name, NAME_NCCH_ROMFSDIR, 32);
|
||||
templates[n].flags |= VFLAG_DIR;
|
||||
@ -168,8 +169,7 @@ bool BuildVGameNcchDir(void) {
|
||||
}
|
||||
|
||||
bool BuildVGameNcsdDir(void) {
|
||||
const char* name_content[] = { NAME_NCSD_CONTENT };
|
||||
const char* name_dir[] = { NAME_NCSD_DIR };
|
||||
const char* name_type[] = { NAME_NCSD_TYPES };
|
||||
VirtualFile* templates = templates_ncsd;
|
||||
u32 n = 0;
|
||||
|
||||
@ -206,14 +206,14 @@ bool BuildVGameNcsdDir(void) {
|
||||
NcchPartition* partition = ncsd->partitions + i;
|
||||
if ((partition->offset == 0) && (partition->size == 0))
|
||||
continue;
|
||||
strncpy(templates[n].name, name_content[i], 32);
|
||||
snprintf(templates[n].name, 32, NAME_NCSD_CONTENT, i, name_type[i], ".app");
|
||||
templates[n].offset = partition->offset * NCSD_MEDIA_UNIT;
|
||||
templates[n].size = partition->size * NCSD_MEDIA_UNIT;
|
||||
templates[n].keyslot = 0xFF; // not encrypted
|
||||
templates[n].flags = VFLAG_NCCH;
|
||||
n++;
|
||||
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
|
||||
strncpy(templates[n].name, name_dir[i], 32);
|
||||
snprintf(templates[n].name, 32, NAME_NCSD_CONTENT, i, name_type[i], "");
|
||||
templates[n].flags |= VFLAG_DIR;
|
||||
n++;
|
||||
}
|
||||
@ -302,7 +302,7 @@ bool BuildVGameCiaDir(void) {
|
||||
is_ncch = (ValidateNcchHeader(&ncch) == 0);
|
||||
}
|
||||
snprintf(templates[n].name, 32, NAME_CIA_CONTENT,
|
||||
getbe16(content_list[i].index), getbe32(content_list[i].id));
|
||||
getbe16(content_list[i].index), getbe32(content_list[i].id), ".app");
|
||||
templates[n].offset = next_offset;
|
||||
templates[n].size = size;
|
||||
templates[n].keyslot = 0xFF; // even for encrypted stuff
|
||||
@ -310,8 +310,8 @@ bool BuildVGameCiaDir(void) {
|
||||
n++;
|
||||
if (is_ncch) {
|
||||
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
|
||||
snprintf(templates[n].name, 32, NAME_CIA_DIR,
|
||||
getbe16(content_list[i].index), getbe32(content_list[i].id));
|
||||
snprintf(templates[n].name, 32, NAME_CIA_CONTENT,
|
||||
getbe16(content_list[i].index), getbe32(content_list[i].id), "");
|
||||
templates[n].flags |= VFLAG_DIR;
|
||||
n++;
|
||||
}
|
||||
@ -347,6 +347,14 @@ u32 CheckVGameDrive(void) {
|
||||
return vgame_type;
|
||||
}
|
||||
|
||||
int ReadNcchImageBytes(u8* buffer, u64 offset, u64 count) {
|
||||
int ret = ReadImageBytes(buffer, offset, count);
|
||||
if ((offset_ncch == (u64) -1) || (ret != 0)) return ret;
|
||||
if (NCCH_ENCRYPTED(ncch) && (DecryptNcch(buffer, offset - offset_ncch, count, ncch,
|
||||
(offset_exefs == (u64) -1) ? NULL : exefs) != 0)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||
// build vdir object
|
||||
vdir->index = -1;
|
||||
@ -385,8 +393,14 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||
return false;
|
||||
offset_ncch = vdir->offset;
|
||||
if (!BuildVGameNcchDir()) return false;
|
||||
u32 ncch_offset_exefs = offset_ncch + (ncch->offset_exefs * NCCH_MEDIA_UNIT);
|
||||
if ((ReadNcchImageBytes((u8*) exefs, ncch_offset_exefs, sizeof(ExeFsHeader)) != 0) ||
|
||||
(ValidateExeFsHeader(exefs, ncch->size_exefs * NCCH_MEDIA_UNIT) != 0))
|
||||
return false;
|
||||
offset_exefs = ncch_offset_exefs;
|
||||
if (!BuildVGameExeFsDir()) return false;
|
||||
} else if ((vdir->flags & VFLAG_EXEFS) && (offset_exefs != vdir->offset)) {
|
||||
if ((ReadImageBytes((u8*) exefs, vdir->offset, sizeof(ExeFsHeader)) != 0) ||
|
||||
if ((ReadNcchImageBytes((u8*) exefs, vdir->offset, sizeof(ExeFsHeader)) != 0) ||
|
||||
(ValidateExeFsHeader(exefs, ncch->size_exefs * NCCH_MEDIA_UNIT) != 0))
|
||||
return false;
|
||||
offset_exefs = vdir->offset;
|
||||
@ -395,20 +409,20 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||
// validate romFS magic
|
||||
u8 magic[] = { ROMFS_MAGIC };
|
||||
u8 header[sizeof(magic)];
|
||||
if ((ReadImageBytes(header, vdir->offset, sizeof(magic)) != 0) ||
|
||||
if ((ReadNcchImageBytes(header, vdir->offset, sizeof(magic)) != 0) ||
|
||||
(memcmp(magic, header, sizeof(magic)) != 0))
|
||||
return false;
|
||||
// validate lv3 header
|
||||
RomFsLv3Header* lv3 = (RomFsLv3Header*) romfslv3;
|
||||
for (u32 i = 1; i < 8; i++) {
|
||||
offset_lv3 = vdir->offset + (i*OFFSET_LV3);
|
||||
if (ReadImageBytes(romfslv3, offset_lv3, sizeof(RomFsLv3Header)) != 0)
|
||||
if (ReadNcchImageBytes(romfslv3, offset_lv3, sizeof(RomFsLv3Header)) != 0)
|
||||
return false;
|
||||
if (ValidateLv3Header(lv3, VGAME_BUFFER_SIZE - 0x20000) == 0)
|
||||
break;
|
||||
offset_lv3 = (u64) -1;
|
||||
}
|
||||
if ((offset_lv3 == (u64) -1) || (ReadImageBytes(romfslv3, offset_lv3, lv3->offset_filedata) != 0))
|
||||
if ((offset_lv3 == (u64) -1) || (ReadNcchImageBytes(romfslv3, offset_lv3, lv3->offset_filedata) != 0))
|
||||
return false;
|
||||
offset_lv3fd = offset_lv3 + lv3->offset_filedata;
|
||||
offset_romfs = vdir->offset;
|
||||
@ -430,6 +444,7 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||
bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
|
||||
BuildLv3Index(&lv3idx, romfslv3);
|
||||
vfile->flags = VFLAG_LV3;
|
||||
vfile->keyslot = NCCH_ENCRYPTED(ncch) ? 0x2C : 0xFF; // actual keyslot may be different
|
||||
|
||||
// start from parent dir object
|
||||
if (vdir->index == -1) vdir->index = 0;
|
||||
@ -534,32 +549,16 @@ int ReadVGameFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) {
|
||||
lv3file = LV3_GET_FILE(vfile->offset, &lv3idx);
|
||||
vfoffset = offset_lv3fd + lv3file->offset_data;
|
||||
}
|
||||
int ret = ReadImageBytes(buffer, vfoffset + offset, count);
|
||||
if (ret != 0) return ret;
|
||||
/*if ((ret != 0) && (vfile->keyslot <= 0x40)) { // crypto
|
||||
// relies on first template being the header and everything aligned to AES_BLOCK_SIZE
|
||||
u32 offset_base = 0; // vfoffset - (*templates).offset;
|
||||
u8 ctr[16] = { 0 };
|
||||
ctr[0] = (vfile->index & 0xFF);
|
||||
ctr[1] = (vfile->index >> 8);
|
||||
setup_aeskeyY(0x11, titlekey);
|
||||
use_aeskey(0x11);
|
||||
ctr_decrypt_boffset(buffer, buffer, bytes_read, offset - offset_base,
|
||||
AES_CNT_TITLEKEY_DECRYPT_MODE, ctr);
|
||||
}*/
|
||||
return 0;
|
||||
if (NCCH_ENCRYPTED(ncch) && (vfile->keyslot < 0x40) &&
|
||||
(vfile->flags & (VFLAG_EXEFS_FILE|VFLAG_EXTHDR|VFLAG_EXEFS|VFLAG_ROMFS|VFLAG_LV3|VFLAG_NCCH)))
|
||||
return ReadNcchImageBytes(buffer, vfoffset + offset, count);
|
||||
else return ReadImageBytes(buffer, vfoffset + offset, count);
|
||||
}
|
||||
|
||||
bool FindVirtualFileInLv3Dir(VirtualFile* vfile, const VirtualDir* vdir, const char* name) {
|
||||
vfile->name[0] = '\0';
|
||||
vfile->flags = vdir->flags & ~VFLAG_DIR;
|
||||
|
||||
RomFsLv3FileMeta* lv3file = GetLv3FileMeta(name, vdir->offset, &lv3idx);
|
||||
if (lv3file) {
|
||||
vfile->offset = ((u8*) lv3file) - ((u8*) lv3idx.filemeta);
|
||||
vfile->size = lv3file->size_data;
|
||||
return true;
|
||||
}
|
||||
vfile->keyslot = NCCH_ENCRYPTED(ncch) ? 0x2C : 0xFF; // actual keyslot may be different
|
||||
|
||||
RomFsLv3DirMeta* lv3dir = GetLv3DirMeta(name, vdir->offset, &lv3idx);
|
||||
if (lv3dir) {
|
||||
@ -569,6 +568,13 @@ bool FindVirtualFileInLv3Dir(VirtualFile* vfile, const VirtualDir* vdir, const c
|
||||
return true;
|
||||
}
|
||||
|
||||
RomFsLv3FileMeta* lv3file = GetLv3FileMeta(name, vdir->offset, &lv3idx);
|
||||
if (lv3file) {
|
||||
vfile->offset = ((u8*) lv3file) - ((u8*) lv3idx.filemeta);
|
||||
vfile->size = lv3file->size_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -12,5 +12,6 @@ bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir);
|
||||
int ReadVGameFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
||||
// int WriteVGameFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count); // writing is not enabled
|
||||
|
||||
bool FindVirtualFileInLv3Dir(VirtualFile* vfile, const VirtualDir* vdir, const char* name);
|
||||
bool GetVGameLv3Filename(char* name, const VirtualFile* vfile, u32 n_chars);
|
||||
bool MatchVGameLv3Filename(const char* name, const VirtualFile* vfile, u32 n_chars);
|
||||
|
Loading…
x
Reference in New Issue
Block a user