Use actual offsets from NCSD in virtual drives

This commit is contained in:
d0k3 2017-05-26 00:50:31 +02:00
parent 90049a5fd3
commit 48281307a0
3 changed files with 223 additions and 60 deletions

View File

@ -5,6 +5,7 @@
#include "keydb.h" #include "keydb.h"
#include "aes.h" #include "aes.h"
#include "sha.h" #include "sha.h"
#include "fatmbr.h"
#include "sdmmc.h" #include "sdmmc.h"
#include "image.h" #include "image.h"
@ -13,6 +14,19 @@
#define KEY95_SHA256 ((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256) #define KEY95_SHA256 ((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256)
#define SECTOR_SHA256 ((IS_DEVKIT) ? sector0x96dev_sha256 : sector0x96_sha256) #define SECTOR_SHA256 ((IS_DEVKIT) ? sector0x96dev_sha256 : sector0x96_sha256)
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
static const u32 np_keyslots[9][4] = { // [NP_TYPE][NP_SUBTYPE]
{ 0xFF, 0xFF, 0xFF, 0xFF }, // none
{ 0xFF, 0x03, 0x04, 0x05 }, // standard
{ 0xFF, 0x03, 0x04, 0x05 }, // FAT (custom, not in NCSD)
{ 0xFF, 0xFF, 0x06, 0xFF }, // FIRM
{ 0xFF, 0xFF, 0x07, 0xFF }, // AGBSAVE
{ 0xFF, 0xFF, 0xFF, 0xFF }, // NCSD (custom)
{ 0xFF, 0xFF, 0xFF, 0xFF }, // D0K3 (custom)
{ 0xFF, 0xFF, 0xFF, 0x11 }, // SECRET (custom)
{ 0xFF, 0xFF, 0xFF, 0xFF } // BONUS (custom)
};
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file
static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte) static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86, 0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86,
@ -96,23 +110,24 @@ static u32 emunand_base_sector = 0x000000;
u32 LoadKeyYFromP9(u8* key, const u8* keyhash, u32 offset, u32 keyslot) u32 LoadKeyYFromP9(u8* key, const u8* keyhash, u32 offset, u32 keyslot)
{ {
static u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81 static const u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81
static const u32 sector_firm0 = 0x058980; // standard firm0 sector (this only has to work in A9LH anyways)
u8 ctr0x15[16] __attribute__((aligned(32))); u8 ctr0x15[16] __attribute__((aligned(32)));
u8 keyY0x15[16] __attribute__((aligned(32))); u8 keyY0x15[16] __attribute__((aligned(32)));
u8 keyY[16] __attribute__((aligned(32))); u8 keyY[16] __attribute__((aligned(32)));
u8 header[0x200]; u8 header[0x200];
// check arm9loaderhax // check arm9loaderhax
if (!IS_A9LH || (offset < (offsetA9l + 0x0800))) return 1; if (!IS_A9LH || IS_SIGHAX || (offset < (offsetA9l + 0x0800))) return 1;
// section 2 (arm9loader) header of FIRM // section 2 (arm9loader) header of FIRM
// this is @0x066A00 in FIRM90 & FIRM81 // this is @0x066A00 in FIRM90 & FIRM81
ReadNandBytes(header, (SECTOR_FIRM0 * 0x200) + offsetA9l, 0x200, 0x06, NAND_SYSNAND); ReadNandBytes(header, (sector_firm0 * 0x200) + offsetA9l, 0x200, 0x06, NAND_SYSNAND);
memcpy(keyY0x15, header + 0x10, 0x10); // 0x15 keyY memcpy(keyY0x15, header + 0x10, 0x10); // 0x15 keyY
memcpy(ctr0x15, header + 0x20, 0x10); // 0x15 counter memcpy(ctr0x15, header + 0x20, 0x10); // 0x15 counter
// read and decrypt the encrypted keyY // read and decrypt the encrypted keyY
ReadNandBytes(keyY, (SECTOR_FIRM0 * 0x200) + offset, 0x10, 0x06, NAND_SYSNAND); ReadNandBytes(keyY, (sector_firm0 * 0x200) + offset, 0x10, 0x06, NAND_SYSNAND);
setup_aeskeyY(0x15, keyY0x15); setup_aeskeyY(0x15, keyY0x15);
use_aeskey(0x15); use_aeskey(0x15);
ctr_decrypt_byte(keyY, keyY, 0x10, offset - (offsetA9l + 0x800), AES_CNT_CTRNAND_MODE, ctr0x15); ctr_decrypt_byte(keyY, keyY, 0x10, offset - (offsetA9l + 0x800), AES_CNT_CTRNAND_MODE, ctr0x15);
@ -141,6 +156,7 @@ bool InitNandCrypto(void)
// store the current SHA256 from register // store the current SHA256 from register
memcpy(OtpSha256, (void*) REG_SHAHASH, 32); memcpy(OtpSha256, (void*) REG_SHAHASH, 32);
} else { } else {
// load hash via keys?
const char* base[] = { INPUT_PATHS }; const char* base[] = { INPUT_PATHS };
char path[64]; char path[64];
u8 otp[0x100]; u8 otp[0x100];
@ -231,18 +247,18 @@ bool CheckSlot0x05Crypto(void)
bool CheckSector0x96Crypto(void) bool CheckSector0x96Crypto(void)
{ {
u8 buffer[0x200]; u8 buffer[0x200];
ReadNandSectors(buffer, 0x96, 1, 0x11, NAND_SYSNAND); ReadNandSectors(buffer, SECTOR_SECRET, 1, 0x11, NAND_SYSNAND);
return (sha_cmp(KEY95_SHA256, buffer, 16, SHA256_MODE) == 0); return (sha_cmp(KEY95_SHA256, buffer, 16, SHA256_MODE) == 0);
} }
void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot) void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot)
{ {
u32 mode = (sector >= SECTOR_TWL + SIZE_TWL) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE; u32 mode = (keyslot != 0x03) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE; // somewhat hacky
u8 ctr[16] __attribute__((aligned(32))); u8 ctr[16] __attribute__((aligned(32)));
u32 blocks = count * (0x200 / 0x10); u32 blocks = count * (0x200 / 0x10);
// copy NAND CTR and increment it // copy NAND CTR and increment it
memcpy(ctr, (sector >= SECTOR_TWL + SIZE_TWL) ? CtrNandCtr : TwlNandCtr, 16); memcpy(ctr, (keyslot != 0x03) ? CtrNandCtr : TwlNandCtr, 16); // hacky again
add_ctr(ctr, sector * (0x200 / 0x10)); add_ctr(ctr, sector * (0x200 / 0x10));
// decrypt the data // decrypt the data
@ -356,7 +372,7 @@ int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_src
} else { } else {
return -1; return -1;
} }
if ((keyslot == 0x11) && (sector == 0x96)) CryptSector0x96(buffer, false); if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(buffer, false);
else if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot); else if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
return 0; return 0;
@ -368,7 +384,7 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 n
for (u32 s = 0; s < count; s += (NAND_BUFFER_SIZE / 0x200)) { for (u32 s = 0; s < count; s += (NAND_BUFFER_SIZE / 0x200)) {
u32 pcount = min((NAND_BUFFER_SIZE/0x200), (count - s)); u32 pcount = min((NAND_BUFFER_SIZE/0x200), (count - s));
memcpy(NAND_BUFFER, buffer + (s*0x200), pcount * 0x200); memcpy(NAND_BUFFER, buffer + (s*0x200), pcount * 0x200);
if ((keyslot == 0x11) && (sector == 0x96)) CryptSector0x96(NAND_BUFFER, true); if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(NAND_BUFFER, true);
else if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot); else if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
if (nand_dst == NAND_EMUNAND) { if (nand_dst == NAND_EMUNAND) {
int errorcode = 0; int errorcode = 0;
@ -391,6 +407,94 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 n
return 0; return 0;
} }
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd) // in sectors
{
u32 nand_minsize = 1;
for (u32 prt_idx = 0; prt_idx < 8; prt_idx++) {
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
if (prt_end > nand_minsize) nand_minsize = prt_end;
}
return nand_minsize;
}
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd)
{
// safety / set keyslot
if ((type == NP_TYPE_FAT) || (type > NP_TYPE_BONUS) || (subtype > NP_SUBTYPE_CTR_N)) return 1;
info->keyslot = np_keyslots[type][subtype];
// full (minimum) NAND "partition"
if (type == NP_TYPE_NONE) {
info->sector = 0x00;
info->count = GetNandNcsdMinSizeSectors(ncsd);
return 0;
}
// special, custom partition types, not in NCSD
if (type >= NP_TYPE_NCSD) {
if (type == NP_TYPE_NCSD) {
info->sector = 0x00; // hardcoded
info->count = 0x01;
} else if (type == NP_TYPE_D0K3) {
info->sector = SECTOR_D0K3; // hardcoded
info->count = SECTOR_SECRET - info->sector;
} else if (type == NP_TYPE_SECRET) {
info->sector = SECTOR_SECRET;
info->count = 0x01;
} else if (type == NP_TYPE_BONUS) {
info->sector = GetNandNcsdMinSizeSectors(ncsd);
info->count = 0x00; // placeholder, actual size needs info from NAND chip
} else return 1;
return 0;
}
u32 prt_idx = 8;
for (prt_idx = 0; prt_idx < 8; prt_idx++) {
if ((ncsd->partitions_fs_type[prt_idx] != type) ||
(ncsd->partitions_crypto_type[prt_idx] != subtype)) continue;
if (index == 0) break;
index--;
}
if (prt_idx >= 8) return 1; // not found
info->sector = ncsd->partitions[prt_idx].offset;
info->count = ncsd->partitions[prt_idx].size;
return 0;
}
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, u32 nand_src)
{
// check NAND NCSD integrity(!!!)
// workaround for ZERONAND
if (nand_src == NAND_ZERONAND) nand_src = NAND_SYSNAND;
// find type & subtype in NCSD header
u8 header[0x200];
ReadNandSectors(header, 0x00, 1, 0xFF, nand_src);
NandNcsdHeader* ncsd = (NandNcsdHeader*) header;
if (((type == NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, NP_TYPE_STD, subtype, 0, ncsd) != 0)) ||
((type != NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, type, subtype, index, ncsd) != 0)))
return 1; // not found
// size of bonus partition
if (type == NP_TYPE_BONUS) {
info->count = GetNandSizeSectors(nand_src) - info->sector;
} else if (type == NP_TYPE_FAT) { // FAT type specific stuff
ReadNandSectors(header, info->sector, 1, info->keyslot, nand_src);
MbrHeader* mbr = (MbrHeader*) header;
if ((ValidateMbrHeader(mbr) != 0) || (index >= 4) ||
(mbr->partitions[index].sector == 0) || (mbr->partitions[index].count == 0) ||
(mbr->partitions[index].sector + mbr->partitions[index].count > info->count))
return 1;
info->sector += mbr->partitions[index].sector;
info->count = mbr->partitions[index].count;
}
return 0;
}
u32 CheckNandMbr(u8* mbr) u32 CheckNandMbr(u8* mbr)
{ {
if (memcmp(mbr + (0x200 - sizeof(twl_mbr)), twl_mbr, sizeof(twl_mbr)) == 0) if (memcmp(mbr + (0x200 - sizeof(twl_mbr)), twl_mbr, sizeof(twl_mbr)) == 0)
@ -470,7 +574,7 @@ u32 GetLegitSector0x96(u8* sector)
// search for valid secret sector in SysNAND / EmuNAND // search for valid secret sector in SysNAND / EmuNAND
const u32 nand_src[] = { NAND_SYSNAND, NAND_EMUNAND }; const u32 nand_src[] = { NAND_SYSNAND, NAND_EMUNAND };
for (u32 i = 0; i < sizeof(nand_src) / sizeof(u32); i++) { for (u32 i = 0; i < sizeof(nand_src) / sizeof(u32); i++) {
ReadNandSectors(sector, 0x96, 1, 0x11, nand_src[i]); ReadNandSectors(sector, SECTOR_SECRET, 1, 0x11, nand_src[i]);
if (sha_cmp(SECTOR_SHA256, sector, 0x200, SHA256_MODE) == 0) if (sha_cmp(SECTOR_SHA256, sector, 0x200, SHA256_MODE) == 0)
return 0; return 0;
} }

View File

@ -17,6 +17,7 @@
// start sectors of partitions // start sectors of partitions
#define SECTOR_TWL 0x000000 #define SECTOR_TWL 0x000000
#define SECTOR_D0K3 0x000001
#define SECTOR_SECRET 0x000096 #define SECTOR_SECRET 0x000096
#define SECTOR_TWLN 0x000097 #define SECTOR_TWLN 0x000097
#define SECTOR_TWLP 0x04808D #define SECTOR_TWLP 0x04808D
@ -39,7 +40,52 @@
#define SECTOR_NAME "sector0x96.bin" #define SECTOR_NAME "sector0x96.bin"
#define SECRET_NAME "secret_sector.bin" #define SECRET_NAME "secret_sector.bin"
#define OTP_NAME "otp.bin" #define OTP_NAME "otp.bin"
#define OTP_BIG_NAME "otp0x108.bin" #define OTP_BIG_NAME "otp0x108.bin"
// 0x110...0x118 in the NAND NCSD header
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
#define NP_TYPE_NONE 0
#define NP_TYPE_STD 1
#define NP_TYPE_FAT 2 // this is of our own making
#define NP_TYPE_FIRM 3
#define NP_TYPE_AGB 4
#define NP_TYPE_NCSD 5 // this is of our own making
#define NP_TYPE_D0K3 6 // my own partition ^_^
#define NP_TYPE_SECRET 7 // this is of our own making
#define NP_TYPE_BONUS 8 // this is of our own making
// 0x118...0x120 in the NAND NCSD header
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
#define NP_SUBTYPE_NONE 0
#define NP_SUBTYPE_TWL 1
#define NP_SUBTYPE_CTR 2
#define NP_SUBTYPE_CTR_N 3
typedef struct {
u32 sector;
u32 count;
u32 keyslot;
} __attribute__((packed)) NandPartitionInfo;
typedef struct {
u32 offset;
u32 size;
} __attribute__((packed)) NandNcsdPartition;
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
typedef struct {
u8 signature[0x100];
u8 magic[4];
u32 size;
u64 mediaId; // this is zero
u8 partitions_fs_type[8];
u8 partitions_crypto_type[8];
NandNcsdPartition partitions[8];
u8 unknown[0x5E];
u8 twl_mbr[0x42];
} __attribute__((packed)) NandNcsdHeader;
bool InitNandCrypto(void); bool InitNandCrypto(void);
bool CheckSlot0x05Crypto(void); bool CheckSlot0x05Crypto(void);
@ -52,7 +98,10 @@ int WriteNandBytes(const u8* buffer, u64 offset, u64 count, u32 keyslot, u32 nan
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 src); int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 src);
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest); int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest);
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd);
u64 GetNandSizeSectors(u32 src); u64 GetNandSizeSectors(u32 src);
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd);
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, u32 nand_src);
u64 GetNandUnusedSectors(u32 src); u64 GetNandUnusedSectors(u32 src);
u32 CheckNandMbr(u8* mbr); u32 CheckNandMbr(u8* mbr);
u32 CheckNandHeader(u8* header); u32 CheckNandHeader(u8* header);

View File

@ -4,40 +4,45 @@
#include "essentials.h" #include "essentials.h"
#include "unittype.h" #include "unittype.h"
#define VFLAG_ON_O3DS NAND_TYPE_O3DS #define VFLAG_MBR (1UL<<27)
#define VFLAG_ON_N3DS NAND_TYPE_N3DS
#define VFLAG_ON_NO3DS NAND_TYPE_NO3DS
#define VFLAG_ON_NAND (VFLAG_ON_O3DS | VFLAG_ON_N3DS | VFLAG_ON_NO3DS)
#define VFLAG_ESSENTIAL (1UL<<28) #define VFLAG_ESSENTIAL (1UL<<28)
#define VFLAG_GBA_VC (1UL<<29) #define VFLAG_GBA_VC (1UL<<29)
#define VFLAG_NEEDS_OTP (1UL<<30) #define VFLAG_NEEDS_OTP (1UL<<30)
#define VFLAG_NAND_SIZE (1UL<<31) #define VFLAG_NAND_SIZE (1UL<<31)
// see: http://3dbrew.org/wiki/Flash_Filesystem#NAND_structure typedef struct {
// too much hardcoding, but more readable this way char name[32];
static const VirtualFile vNandFileTemplates[] = { u32 type;
{ "twln.bin" , 0x00012E00, 0x08FB5200, 0x03, VFLAG_ON_NAND }, u32 subtype;
{ "twlp.bin" , 0x09011A00, 0x020B6600, 0x03, VFLAG_ON_NAND }, u32 index;
{ "agbsave.bin" , 0x0B100000, 0x00030000, 0x07, VFLAG_ON_NAND }, u32 flags;
{ "firm0.bin" , 0x0B130000, 0x00400000, 0x06, VFLAG_ON_NAND}, } __attribute__((packed)) VirtualNandTemplate;
{ "firm1.bin" , 0x0B530000, 0x00400000, 0x06, VFLAG_ON_NAND},
{ "ctrnand_fat.bin" , 0x0B95CA00, 0x2F3E3600, 0x04, VFLAG_ON_O3DS }, // see NP_TYPE_ and NP_SUBTYPE_ in nand.h
{ "ctrnand_fat.bin" , 0x0B95AE00, 0x41D2D200, 0x05, VFLAG_ON_N3DS }, static const VirtualNandTemplate vNandTemplates[] = {
{ "ctrnand_fat.bin" , 0x0B95AE00, 0x41D2D200, 0x04, VFLAG_ON_NO3DS }, { "nand_hdr.bin" , NP_TYPE_NCSD , NP_SUBTYPE_CTR , 0, 0 },
{ "ctrnand_full.bin" , 0x0B930000, 0x2F5D0000, 0x04, VFLAG_ON_O3DS }, { "twlmbr.bin" , NP_TYPE_STD , NP_SUBTYPE_TWL , 0, VFLAG_MBR },
{ "ctrnand_full.bin" , 0x0B930000, 0x41ED0000, 0x05, VFLAG_ON_N3DS }, { "essential.exefs" , NP_TYPE_D0K3 , NP_SUBTYPE_NONE , 0, VFLAG_ESSENTIAL },
{ "ctrnand_full.bin" , 0x0B930000, 0x41ED0000, 0x04, VFLAG_ON_NO3DS }, { "sector0x96.bin" , NP_TYPE_SECRET, NP_SUBTYPE_CTR_N, 0, VFLAG_NEEDS_OTP },
{ "sector0x96.bin" , 0x00012C00, 0x00000200, 0x11, VFLAG_ON_NAND | VFLAG_NEEDS_OTP }, { "twln.bin" , NP_TYPE_FAT , NP_SUBTYPE_TWL , 0, 0 },
{ "nand.bin" , 0x00000000, 0x00000000, 0xFF, VFLAG_ON_NAND | VFLAG_NAND_SIZE }, { "twlp.bin" , NP_TYPE_FAT , NP_SUBTYPE_TWL , 1, 0 },
{ "nand_minsize.bin" , 0x00000000, 0x3AF00000, 0xFF, VFLAG_ON_O3DS }, { "agbsave.bin" , NP_TYPE_AGB , NP_SUBTYPE_CTR , 0, 0 },
{ "nand_minsize.bin" , 0x00000000, 0x4D800000, 0xFF, VFLAG_ON_N3DS | VFLAG_ON_NO3DS }, { "gbavc.sav" , NP_TYPE_AGB , NP_SUBTYPE_CTR , 0, VFLAG_GBA_VC },
{ "nand_hdr.bin" , 0x00000000, 0x00000200, 0xFF, VFLAG_ON_NAND }, { "firm0.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 0, 0 },
{ "twlmbr.bin" , 0x000001BE, 0x00000042, 0x03, VFLAG_ON_NAND }, { "firm1.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 1, 0 },
{ "free0x01.bin" , 0x00000200, 0x00012A00, 0xFF, VFLAG_ON_NAND }, { "firm2.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 2, 0 },
{ "essential.exefs" , 0x00000200, 0x00000000, 0xFF, VFLAG_ON_NAND | VFLAG_ESSENTIAL }, { "firm3.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 3, 0 },
{ "bonus0x1D7800.bin", 0x3AF00000, 0x00000000, 0xFF, VFLAG_ON_O3DS | VFLAG_NAND_SIZE }, { "firm4.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 4, 0 },
{ "bonus0x26C000.bin", 0x4D800000, 0x00000000, 0xFF, VFLAG_ON_N3DS | VFLAG_ON_NO3DS | VFLAG_NAND_SIZE }, { "firm5.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 5, 0 },
{ "gbavc.sav" , 0x0B100200, 0x00000000, 0x07, VFLAG_ON_NAND | VFLAG_GBA_VC }, { "firm6.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 6, 0 },
{ "firm7.bin" , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 7, 0 },
{ "ctrnand_full.bin" , NP_TYPE_STD , NP_SUBTYPE_CTR , 0, 0 },
{ "ctrnand_full.bin" , NP_TYPE_STD , NP_SUBTYPE_CTR_N, 0, 0 },
{ "ctrnand_fat.bin" , NP_TYPE_FAT , NP_SUBTYPE_CTR , 0, 0 },
{ "ctrnand_fat.bin" , NP_TYPE_FAT , NP_SUBTYPE_CTR_N, 0, 0 },
{ "bonus.bin" , NP_TYPE_BONUS , NP_SUBTYPE_CTR , 0, 0 },
{ "nand.bin" , NP_TYPE_NONE , NP_SUBTYPE_NONE , 0, VFLAG_NAND_SIZE },
{ "nand_minsize.bin" , NP_TYPE_NONE , NP_SUBTYPE_NONE , 0, 0 }
}; };
bool CheckVNandDrive(u32 nand_src) { bool CheckVNandDrive(u32 nand_src) {
@ -45,35 +50,38 @@ bool CheckVNandDrive(u32 nand_src) {
} }
bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c
int n_templates = sizeof(vNandFileTemplates) / sizeof(VirtualFile); int n_templates = sizeof(vNandTemplates) / sizeof(VirtualNandTemplate);
const VirtualFile* templates = vNandFileTemplates; const VirtualNandTemplate* templates = vNandTemplates;
u32 nand_src = vdir->flags & VRT_SOURCE; u32 nand_src = vdir->flags & VRT_SOURCE;
while (++vdir->index < n_templates) { while (++vdir->index < n_templates) {
// get NAND type (O3DS/N3DS/NO3DS), workaround for empty EmuNAND const VirtualNandTemplate* template = templates + vdir->index;
u32 nand_type = CheckNandType(nand_src); NandPartitionInfo prt_info;
if (!nand_type) nand_type = (IS_O3DS) ? NAND_TYPE_O3DS : NAND_TYPE_N3DS;
// copy current template to vfile // set up virtual file
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile)); if (GetNandPartitionInfo(&prt_info, template->type, template->subtype, template->index, nand_src) != 0)
continue;
snprintf(vfile->name, 32, "%s%s", template->name, (nand_src == VRT_XORPAD) ? ".xorpad" : "");
vfile->offset = ((u64) prt_info.sector) * 0x200;
vfile->size = ((u64) prt_info.count) * 0x200;
vfile->keyslot = prt_info.keyslot;
vfile->flags = template->flags;
// XORpad drive handling // handle special cases
if (nand_src == VRT_XORPAD) { if (!vfile->size) continue;
snprintf(vfile->name, 32, "%s.xorpad", templates[vdir->index].name); if ((nand_src == VRT_XORPAD) && ((vfile->keyslot == 0x11) || (vfile->keyslot >= 0x40) || (vfile->flags & VFLAG_GBA_VC)))
if ((vfile->keyslot == 0x11) || (vfile->keyslot >= 0x40) || (vfile->flags & VFLAG_GBA_VC)) continue;
continue;
}
// process / check special flags
if (!(vfile->flags & nand_type))
continue; // virtual file has wrong NAND type
if ((vfile->keyslot == 0x05) && !CheckSlot0x05Crypto()) if ((vfile->keyslot == 0x05) && !CheckSlot0x05Crypto())
continue; // keyslot 0x05 not properly set up continue; // keyslot 0x05 not properly set up
if ((vfile->flags & VFLAG_NEEDS_OTP) && !CheckSector0x96Crypto()) if ((vfile->flags & VFLAG_NEEDS_OTP) && !CheckSector0x96Crypto())
continue; // sector 0x96 crypto not set up continue; // sector 0x96 crypto not set up
if (vfile->flags & VFLAG_MBR) {
vfile->offset += 0x200 - 0x42;
vfile->size = 0x42;
}
if (vfile->flags & VFLAG_NAND_SIZE) { if (vfile->flags & VFLAG_NAND_SIZE) {
if ((nand_src != VRT_SYSNAND) && (GetNandSizeSectors(NAND_SYSNAND) != GetNandSizeSectors(nand_src))) if ((nand_src != VRT_SYSNAND) && (GetNandSizeSectors(NAND_SYSNAND) > GetNandSizeSectors(nand_src)))
continue; // EmuNAND/ImgNAND is too small continue; // EmuNAND / ImgNAND is too small
u64 nand_size = GetNandSizeSectors(NAND_SYSNAND) * 0x200; u64 nand_size = GetNandSizeSectors(NAND_SYSNAND) * 0x200;
if (nand_size <= vfile->offset) continue; if (nand_size <= vfile->offset) continue;
vfile->size = nand_size - vfile->offset; vfile->size = nand_size - vfile->offset;
@ -87,9 +95,11 @@ bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir
} }
if (vfile->flags & VFLAG_GBA_VC) { if (vfile->flags & VFLAG_GBA_VC) {
if (CheckAgbSaveCmac(nand_src) != 0) continue; if (CheckAgbSaveCmac(nand_src) != 0) continue;
vfile->offset += 0x200;
vfile->size = GetAgbSaveSize(nand_src); vfile->size = GetAgbSaveSize(nand_src);
} }
// found if arriving here // found if arriving here
vfile->flags |= nand_src; vfile->flags |= nand_src;
return true; return true;