Support for mounting NAND images

This commit is contained in:
d0k3 2016-04-04 22:45:49 +02:00
parent 59126078c7
commit 00fe5b9b36
11 changed files with 236 additions and 81 deletions

View File

@ -8,12 +8,15 @@
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
#include "diskio.h" /* FatFs lower layer API */ #include "diskio.h" /* FatFs lower layer API */
#include "image.h"
#include "nand.h" #include "nand.h"
#include "sdmmc.h" #include "sdmmc.h"
#define TYPE_SDCARD 0 #define TYPE_SDCARD 0x00
#define TYPE_SYSNAND 1 #define TYPE_SYSNAND NAND_SYSNAND
#define TYPE_EMUNAND 2 #define TYPE_EMUNAND NAND_EMUNAND
#define TYPE_IMGNAND NAND_IMGNAND
#define TYPE_IMAGE 0xFF
#define SUBTYPE_CTRN 0 #define SUBTYPE_CTRN 0
#define SUBTYPE_CTRN_N 1 #define SUBTYPE_CTRN_N 1
@ -33,7 +36,7 @@ typedef struct {
BYTE keyslot; BYTE keyslot;
} SubtypeDesc; } SubtypeDesc;
FATpartition DriveInfo[7] = { FATpartition DriveInfo[11] = {
{ TYPE_SDCARD, SUBTYPE_NONE }, // 0 - SDCARD { TYPE_SDCARD, SUBTYPE_NONE }, // 0 - SDCARD
{ TYPE_SYSNAND, SUBTYPE_CTRN }, // 1 - SYSNAND CTRNAND { TYPE_SYSNAND, SUBTYPE_CTRN }, // 1 - SYSNAND CTRNAND
{ TYPE_SYSNAND, SUBTYPE_TWLN }, // 2 - SYSNAND TWLN { TYPE_SYSNAND, SUBTYPE_TWLN }, // 2 - SYSNAND TWLN
@ -41,6 +44,10 @@ FATpartition DriveInfo[7] = {
{ TYPE_EMUNAND, SUBTYPE_CTRN }, // 4 - EMUNAND CTRNAND { TYPE_EMUNAND, SUBTYPE_CTRN }, // 4 - EMUNAND CTRNAND
{ TYPE_EMUNAND, SUBTYPE_TWLN }, // 5 - EMUNAND TWLN { TYPE_EMUNAND, SUBTYPE_TWLN }, // 5 - EMUNAND TWLN
{ TYPE_EMUNAND, SUBTYPE_TWLP }, // 6 - EMUNAND TWLP { TYPE_EMUNAND, SUBTYPE_TWLP }, // 6 - EMUNAND TWLP
{ TYPE_IMGNAND, SUBTYPE_CTRN }, // 7 - EMUNAND CTRNAND
{ TYPE_IMGNAND, SUBTYPE_TWLN }, // 8 - EMUNAND TWLN
{ TYPE_IMGNAND, SUBTYPE_TWLP }, // 9 - EMUNAND TWLP
{ TYPE_IMAGE, SUBTYPE_NONE } // X - IMAGE
}; };
SubtypeDesc SubTypes[5] = { SubtypeDesc SubTypes[5] = {
@ -51,8 +58,9 @@ SubtypeDesc SubTypes[5] = {
{ 0x04808D, 0x0105B3, 0x3 } // TWLP { 0x04808D, 0x0105B3, 0x3 } // TWLP
}; };
static BYTE nand_type_sys = NAND_TYPE_UNK; static BYTE nand_type_sys = 0;
static BYTE nand_type_emu = NAND_TYPE_UNK; static BYTE nand_type_emu = 0;
static BYTE nand_type_img = 0;
@ -68,10 +76,10 @@ SubtypeDesc* get_subtype_desc(
BYTE type = DriveInfo[pdrv].type; BYTE type = DriveInfo[pdrv].type;
BYTE subtype = DriveInfo[pdrv].subtype; BYTE subtype = DriveInfo[pdrv].subtype;
if (type == TYPE_SDCARD) { if (subtype == SUBTYPE_NONE) {
return NULL; return NULL;
} else if (subtype == SUBTYPE_CTRN) { } else if (subtype == SUBTYPE_CTRN) {
BYTE nand_type = (type == TYPE_SYSNAND) ? nand_type_sys : nand_type_emu; BYTE nand_type = (type == TYPE_SYSNAND) ? nand_type_sys : (type == TYPE_EMUNAND) ? nand_type_emu : nand_type_img;
if (nand_type != NAND_TYPE_O3DS) if (nand_type != NAND_TYPE_O3DS)
subtype = (nand_type == NAND_TYPE_N3DS) ? SUBTYPE_CTRN_N : SUBTYPE_CTRN_NO; subtype = (nand_type == NAND_TYPE_N3DS) ? SUBTYPE_CTRN_N : SUBTYPE_CTRN_NO;
} }
@ -108,9 +116,11 @@ DSTATUS disk_initialize (
if (!sdmmc_sdcard_init()) if (!sdmmc_sdcard_init())
return RES_PARERR; return RES_PARERR;
} else if (pdrv < 4) { } else if (pdrv < 4) {
nand_type_sys = CheckNandType(false); nand_type_sys = CheckNandType(NAND_SYSNAND);
} else if (pdrv < 7) { } else if (pdrv < 7) {
nand_type_emu = CheckNandType(true); nand_type_emu = CheckNandType(NAND_EMUNAND);
} else if (pdrv < 10) {
nand_type_img = CheckNandType(NAND_IMGNAND);
} }
return RES_OK; return RES_OK;
} }
@ -135,12 +145,14 @@ DRESULT disk_read (
if (sdmmc_sdcard_readsectors(sector, count, buff)) { if (sdmmc_sdcard_readsectors(sector, count, buff)) {
return RES_PARERR; return RES_PARERR;
} }
} else if (type == TYPE_IMAGE) {
return RES_PARERR;
} else { } else {
SubtypeDesc* subtype = get_subtype_desc(pdrv); SubtypeDesc* subtype = get_subtype_desc(pdrv);
BYTE keyslot = subtype->keyslot; BYTE keyslot = subtype->keyslot;
DWORD isector = subtype->offset + sector; DWORD isector = subtype->offset + sector;
if (ReadNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND)) if (ReadNandSectors(buff, isector, count, keyslot, type))
return RES_PARERR; return RES_PARERR;
} }
@ -168,12 +180,14 @@ DRESULT disk_write (
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
return RES_PARERR; return RES_PARERR;
} }
} else if (type == TYPE_IMAGE) {
return RES_PARERR;
} else { } else {
SubtypeDesc* subtype = get_subtype_desc(pdrv); SubtypeDesc* subtype = get_subtype_desc(pdrv);
BYTE keyslot = subtype->keyslot; BYTE keyslot = subtype->keyslot;
DWORD isector = subtype->offset + sector; DWORD isector = subtype->offset + sector;
if (WriteNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND)) if (WriteNandSectors(buff, isector, count, keyslot, type))
return RES_PARERR; // unstubbed! return RES_PARERR; // unstubbed!
} }
@ -197,13 +211,17 @@ DRESULT disk_ioctl (
void *buff /* Buffer to send/receive control data */ void *buff /* Buffer to send/receive control data */
) )
{ {
BYTE type = DriveInfo[pdrv].type;
switch (cmd) { switch (cmd) {
case GET_SECTOR_SIZE: case GET_SECTOR_SIZE:
*((DWORD*) buff) = 0x200; *((DWORD*) buff) = 0x200;
return RES_OK; return RES_OK;
case GET_SECTOR_COUNT: case GET_SECTOR_COUNT:
if (DriveInfo[pdrv].type == TYPE_SDCARD) { if (type == TYPE_SDCARD) {
*((DWORD*) buff) = getMMCDevice(1)->total_size; *((DWORD*) buff) = getMMCDevice(1)->total_size;
} else if (type == TYPE_IMAGE) {
*((DWORD*) buff) = GetMountSize();
} else { } else {
*((DWORD*) buff) = get_subtype_desc(pdrv)->size; *((DWORD*) buff) = get_subtype_desc(pdrv)->size;
} }
@ -212,7 +230,9 @@ DRESULT disk_ioctl (
*((DWORD*) buff) = 0x2000; *((DWORD*) buff) = 0x2000;
return RES_OK; return RES_OK;
case CTRL_SYNC: case CTRL_SYNC:
// nothing to do here - the disk_write function handles that if ((type == TYPE_IMAGE) || (type == TYPE_IMGNAND))
SyncImage();
// nothing else to do here - sdmmc.c handles that
return RES_OK; return RES_OK;
} }
return RES_PARERR; return RES_PARERR;

View File

@ -456,7 +456,7 @@ WCHAR ff_wtoupper(WCHAR chr)
/ or start-up routine being used is out of ANSI-C standard. / or start-up routine being used is out of ANSI-C standard.
*/ */
#if _VOLUMES < 1 || _VOLUMES > 9 #if _VOLUMES < 1 || _VOLUMES > 10 // <--- increased the limit to 10 (this won't break anything, no?)
#error Wrong _VOLUMES setting #error Wrong _VOLUMES setting
#endif #endif
static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */

View File

@ -141,11 +141,11 @@
/ Drive/Volume Configurations / Drive/Volume Configurations
/---------------------------------------------------------------------------*/ /---------------------------------------------------------------------------*/
#define _VOLUMES 7 #define _VOLUMES 10
/* Number of volumes (logical drives) to be used. */ /* Number of volumes (logical drives) to be used. */
#define _STR_VOLUME_ID 1 #define _STR_VOLUME_ID 0
#define _VOLUME_STRS "sdcard","sysnand","systwln","systwlp","emunand","emutwln","emutwlp" #define _VOLUME_STRS "sdcard","sysnand","systwln","systwlp","emunand","emutwln","emutwlp"
/* _STR_VOLUME_ID option switches string volume ID feature. /* _STR_VOLUME_ID option switches string volume ID feature.
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive

80
source/fatfs/image.c Normal file
View File

@ -0,0 +1,80 @@
#include "image.h"
#include "fatfs/ff.h"
FIL mount_file;
u32 mount_state = IMG_NONE;
int ReadImageSectors(u8* buffer, u32 sector, u32 count) {
UINT bytes_read;
UINT ret;
if (!mount_state) return FR_INVALID_OBJECT;
if (f_tell(&mount_file) != sector * 0x200)
f_lseek(&mount_file, sector * 0x200);
ret = f_read(&mount_file, buffer, count * 0x200, &bytes_read);
return (ret != 0) ? ret : (bytes_read != count * 0x200) ? -1 : 0;
}
int WriteImageSectors(const u8* buffer, u32 sector, u32 count) {
UINT bytes_written;
UINT ret;
if (!mount_state) return FR_INVALID_OBJECT;
if (f_tell(&mount_file) != sector * 0x200)
f_lseek(&mount_file, sector * 0x200);
ret = f_write(&mount_file, buffer, count * 0x200, &bytes_written);
return (ret != 0) ? ret : (bytes_written != count * 0x200) ? -1 : 0;
}
int SyncImage(void) {
return (mount_state) ? f_sync(&mount_file) : FR_INVALID_OBJECT;
}
u64 GetMountSize(void) {
return mount_state ? f_size(&mount_file) : 0;
}
u32 GetMountState(void) {
return mount_state;
}
u32 IdentifyImage(const char* path) {
u8 header[0x200];
FIL file;
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return IMG_NONE;
f_lseek(&file, 0);
f_sync(&file);
UINT fsize = f_size(&file);
UINT bytes_read;
if ((f_read(&file, header, 0x200, &bytes_read) != FR_OK) || (bytes_read != 0x200)) {
f_close(&file);
return IMG_NONE;
}
f_close(&file);
if ((getbe32(header + 0x100) == 0x4E435344) && (getbe64(header + 0x110) == (u64) 0x0104030301000000) && (getbe64(header + 0x108) == (u64) 0)) {
return IMG_NAND;
} else if (getbe16(header + 0x1FE) == 0x55AA) { // migt be FAT or MBR
if ((strncmp((char*) header + 0x36, "FAT12 ", 8) == 0) || (strncmp((char*) header + 0x36, "FAT16 ", 8) == 0) ||
(strncmp((char*) header + 0x36, "FAT ", 8) == 0) || (strncmp((char*) header + 0x52, "FAT32 ", 8) == 0)) {
return IMG_FAT; // this is an actual FAT header
} else if (((getle32(header + 0x1BE + 0x8) + getle32(header + 0x1BE + 0xC)) < (fsize / 0x200)) && // check file size
(getle32(header + 0x1BE + 0x8) > 0) && (getle32(header + 0x1BE + 0xC) >= 0x800) && // check first partition sanity
((header[0x1BE + 0x4] == 0x1) || (header[0x1BE + 0x4] == 0x4) || (header[0x1BE + 0x4] == 0x6) || // check filesystem type
(header[0x1BE + 0x4] == 0xB) || (header[0x1BE + 0x4] == 0xC) || (header[0x1BE + 0x4] == 0xE))) {
return IMG_FAT; // this might be an MBR -> give it the benefit of doubt
}
}
return IMG_NONE;
}
u32 MountImage(const char* path) {
if (mount_state) {
f_close(&mount_file);
mount_state = IMG_NONE;
}
if (!path || !IdentifyImage(path)) return IMG_NONE;
if (f_open(&mount_file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
return IMG_NONE;
f_lseek(&mount_file, 0);
f_sync(&mount_file);
return (mount_state = IdentifyImage(path));
}

16
source/fatfs/image.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "common.h"
#define IMG_NONE 0
#define IMG_FAT 1
#define IMG_NAND 2
int ReadImageSectors(u8* buffer, u32 sector, u32 count);
int WriteImageSectors(const u8* buffer, u32 sector, u32 count);
int SyncImage(void);
u64 GetMountSize(void);
u32 GetMountState(void);
u32 IdentifyImage(const char* path);
u32 MountImage(const char* path);

View File

@ -6,7 +6,8 @@
#define MAIN_BUFFER ((u8*)0x21200000) #define MAIN_BUFFER ((u8*)0x21200000)
#define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200 #define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200
#define MAX_FS 7 #define NORM_FS 10
#define VIRT_FS 3
// don't use this area for anything else! // don't use this area for anything else!
static FATFS* fs = (FATFS*)0x20316000; static FATFS* fs = (FATFS*)0x20316000;
@ -15,7 +16,7 @@ static FATFS* fs = (FATFS*)0x20316000;
static u32 write_permission_level = 1; static u32 write_permission_level = 1;
// number of currently open file systems // number of currently open file systems
static bool fs_mounted[MAX_FS] = { false }; static bool fs_mounted[NORM_FS] = { false };
bool InitSDCardFS() { bool InitSDCardFS() {
#ifndef EXEC_GATEWAY #ifndef EXEC_GATEWAY
@ -30,7 +31,7 @@ bool InitSDCardFS() {
bool InitNandFS() { bool InitNandFS() {
if (!fs_mounted[0]) if (!fs_mounted[0])
return false; return false;
for (u32 i = 1; i < MAX_FS; i++) { for (u32 i = 1; i < NORM_FS; i++) {
char fsname[8]; char fsname[8];
snprintf(fsname, 7, "%lu:", i); snprintf(fsname, 7, "%lu:", i);
if (f_mount(fs + i, fsname, 1) != FR_OK) return false; if (f_mount(fs + i, fsname, 1) != FR_OK) return false;
@ -40,7 +41,7 @@ bool InitNandFS() {
} }
void DeinitNandFS() { void DeinitNandFS() {
for (u32 i = MAX_FS; i > 0; i--) { for (u32 i = NORM_FS; i > 0; i--) {
if (fs_mounted[i]) { if (fs_mounted[i]) {
char fsname[8]; char fsname[8];
snprintf(fsname, 7, "%lu:", i); snprintf(fsname, 7, "%lu:", i);
@ -59,7 +60,7 @@ void DeinitSDCardFS() {
int PathToNumFS(const char* path) { int PathToNumFS(const char* path) {
int fsnum = *path - (int) '0'; int fsnum = *path - (int) '0';
if ((fsnum < 0) || (fsnum >= MAX_FS) || (path[1] != ':')) { if ((fsnum < 0) || (fsnum >= NORM_FS) || (path[1] != ':')) {
if (!IsVirtualPath(path)) ShowPrompt(false, "Invalid path (%s)", path); if (!IsVirtualPath(path)) ShowPrompt(false, "Invalid path (%s)", path);
return -1; return -1;
} }
@ -82,6 +83,10 @@ bool CheckWritePermissions(const char* path) {
if (ShowPrompt(true, "Writing to the EmuNAND is locked!\nUnlock it now?")) if (ShowPrompt(true, "Writing to the EmuNAND is locked!\nUnlock it now?"))
return SetWritePermissions(2); return SetWritePermissions(2);
return false; return false;
} else if ((pdrv >= 7) && (pdrv <= 9) && (write_permission_level < 2)) {
if (ShowPrompt(true, "Writing to the images is locked!\nUnlock it now?"))
return SetWritePermissions(2);
return false;
} else if ((pdrv == 0) && (write_permission_level < 1)) { } else if ((pdrv == 0) && (write_permission_level < 1)) {
if (ShowPrompt(true, "Writing to the SD card is locked!\nUnlock it now?")) if (ShowPrompt(true, "Writing to the SD card is locked!\nUnlock it now?"))
return SetWritePermissions(1); return SetWritePermissions(1);
@ -570,18 +575,19 @@ bool GetRootDirContentsWorker(DirStruct* contents) {
"SDCARD", "SDCARD",
"SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP", "SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP",
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP", "EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP",
"SYSNAND VIRTUAL", "EMUNAND VIRTUAL" "IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP",
"SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL",
}; };
static const char* drvnum[] = { static const char* drvnum[] = {
"0:", "1:", "2:", "3:", "4:", "5:", "6:", "S:", "E:" "0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:", "S:", "E:", "I:"
}; };
u32 n_entries = 0; u32 n_entries = 0;
// virtual root objects hacked in // virtual root objects hacked in
for (u32 pdrv = 0; (pdrv < MAX_FS+2) && (n_entries < MAX_ENTRIES); pdrv++) { for (u32 pdrv = 0; (pdrv < NORM_FS+VIRT_FS) && (n_entries < MAX_ENTRIES); pdrv++) {
DirEntry* entry = &(contents->entry[n_entries]); DirEntry* entry = &(contents->entry[n_entries]);
if ((pdrv < MAX_FS) && !fs_mounted[pdrv]) continue; if ((pdrv < NORM_FS) && !fs_mounted[pdrv]) continue;
else if ((pdrv >= MAX_FS) && (!CheckVirtualPath(drvnum[pdrv]))) continue; else if ((pdrv >= NORM_FS) && (!CheckVirtualPath(drvnum[pdrv]))) continue;
memset(entry->path, 0x00, 64); memset(entry->path, 0x00, 64);
snprintf(entry->path + 0, 4, drvnum[pdrv]); snprintf(entry->path + 0, 4, drvnum[pdrv]);
snprintf(entry->path + 4, 32, "[%s] %s", drvnum[pdrv], drvname[pdrv]); snprintf(entry->path + 4, 32, "[%s] %s", drvnum[pdrv], drvname[pdrv]);

View File

@ -5,8 +5,9 @@
#include "platform.h" #include "platform.h"
#include "nand.h" #include "nand.h"
#include "virtual.h" #include "virtual.h"
#include "image.h"
#define VERSION "0.2.1" #define VERSION "0.2.3"
#define COLOR_TOP_BAR ((GetWritePermissions() == 0) ? COLOR_WHITE : (GetWritePermissions() == 1) ? COLOR_BRIGHTGREEN : (GetWritePermissions() == 2) ? COLOR_BRIGHTYELLOW : COLOR_RED) #define COLOR_TOP_BAR ((GetWritePermissions() == 0) ? COLOR_WHITE : (GetWritePermissions() == 1) ? COLOR_BRIGHTGREEN : (GetWritePermissions() == 2) ? COLOR_BRIGHTYELLOW : COLOR_RED)
#define COLOR_SIDE_BAR COLOR_DARKGREY #define COLOR_SIDE_BAR COLOR_DARKGREY
@ -184,6 +185,20 @@ u32 GodMode() {
cursor = 1; cursor = 1;
scroll = 0; scroll = 0;
} else cursor = 0; } else cursor = 0;
} else if ((pad_state & BUTTON_A) && (current_dir->entry[cursor].type == T_FILE) &&
(PathToNumFS(current_dir->entry[cursor].path) == 0)) { // try to mount image
u32 file_type = IdentifyImage(current_dir->entry[cursor].path);
if (file_type && ShowPrompt(true, "This looks like a %s image\nTry to mount it?", (file_type == IMG_NAND) ? "NAND" : "FAT")) {
if (!MountImage(current_dir->entry[cursor].path)) {
ShowPrompt(false, "Mounting image: failed");
} else {
DeinitNandFS();
InitNandFS();
*current_path = '\0';
GetDirContents(current_dir, current_path);
cursor = 0;
}
}
} else if ((pad_state & BUTTON_B) && *current_path) { // one level down } else if ((pad_state & BUTTON_B) && *current_path) { // one level down
char old_path[256]; char old_path[256];
char* last_slash = strrchr(current_path, '/'); char* last_slash = strrchr(current_path, '/');

View File

@ -4,9 +4,11 @@
#include "sha.h" #include "sha.h"
#include "sdmmc.h" #include "sdmmc.h"
#include "nand.h" #include "nand.h"
#include "image.h"
#define NAND_BUFFER ((u8*)0x21100000) #define NAND_BUFFER ((u8*)0x21100000)
#define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200 #define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200
#define NAND_MIN_SIZE ((GetUnitPlatform() == PLATFORM_N3DS) ? 0x26C000 : 0x1D7800)
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from file static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from file
static u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY file static u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY file
@ -102,11 +104,9 @@ bool CheckSlot0x05Crypto(void)
const u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}; const u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
const u32 sector = 0x05CAD7; const u32 sector = 0x05CAD7;
u8 buffer[0x200]; u8 buffer[0x200];
for (u32 nand = 0; nand < 2; nand++) { ReadNandSectors(buffer, sector, 1, 0x05, NAND_SYSNAND);
ReadNandSectors(buffer, sector, 1, 0x05, nand); if (memcmp(buffer, magic, 8) == 0)
if (memcmp(buffer, magic, 8) == 0) return true;
return true;
}
// failed if we arrive here // failed if we arrive here
return false; return false;
@ -132,9 +132,9 @@ void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot)
} }
} }
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand) int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_src)
{ {
if (read_emunand) { if (nand_src == NAND_EMUNAND) { // EmuNAND
int errorcode = 0; int errorcode = 0;
if ((sector == 0) && (emunand_base_sector % 0x200000 == 0)) { // GW EmuNAND header handling if ((sector == 0) && (emunand_base_sector % 0x200000 == 0)) { // GW EmuNAND header handling
errorcode = sdmmc_sdcard_readsectors(emunand_base_sector + getMMCDevice(0)->total_size, 1, buffer); errorcode = sdmmc_sdcard_readsectors(emunand_base_sector + getMMCDevice(0)->total_size, 1, buffer);
@ -144,70 +144,83 @@ int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_em
} }
errorcode = (!errorcode && count) ? sdmmc_sdcard_readsectors(emunand_base_sector + sector, count, buffer) : errorcode; errorcode = (!errorcode && count) ? sdmmc_sdcard_readsectors(emunand_base_sector + sector, count, buffer) : errorcode;
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else { } else if (nand_src == NAND_IMGNAND) { // ImgNAND
int errorcode = ReadImageSectors(buffer, sector, count);
if (errorcode) return errorcode;
} else if (nand_src == NAND_SYSNAND) { // SysNAND
int errorcode = sdmmc_nand_readsectors(sector, count, buffer); int errorcode = sdmmc_nand_readsectors(sector, count, buffer);
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else {
return -1;
} }
if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot); if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
return 0; return 0;
} }
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool write_emunand) int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_dst)
{ {
// buffer must not be changed, so this is a little complicated // buffer must not be changed, so this is a little complicated
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 < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot); if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
if (write_emunand) { if (nand_dst == NAND_EMUNAND) {
int errorcode = 0; int errorcode = 0;
if ((sector + s == 0) && (emunand_base_sector % 0x200000 == 0)) { // GW EmuNAND header handling if ((sector + s == 0) && (emunand_base_sector % 0x200000 == 0)) { // GW EmuNAND header handling
errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + getMMCDevice(0)->total_size, 1, NAND_BUFFER); errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + getMMCDevice(0)->total_size, 1, NAND_BUFFER);
errorcode = (!errorcode && (pcount > 1)) ? sdmmc_sdcard_writesectors(emunand_base_sector + 1, pcount - 1, NAND_BUFFER + 0x200) : errorcode; errorcode = (!errorcode && (pcount > 1)) ? sdmmc_sdcard_writesectors(emunand_base_sector + 1, pcount - 1, NAND_BUFFER + 0x200) : errorcode;
} else errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + sector + s, pcount, NAND_BUFFER); } else errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + sector + s, pcount, NAND_BUFFER);
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else { } else if (nand_dst == NAND_IMGNAND) {
int errorcode = WriteImageSectors(NAND_BUFFER, sector + s, pcount);
if (errorcode) return errorcode;
} else if (nand_dst == NAND_SYSNAND) {
int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER); int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER);
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else {
return -1;
} }
} }
return 0; return 0;
} }
u8 CheckNandType(bool check_emunand) u8 CheckNandType(u32 nand_src)
{ {
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, check_emunand) != 0) if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, nand_src) != 0)
return NAND_TYPE_UNK; return NAND_UNKNOWN;
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) { if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) {
return NAND_TYPE_N3DS; return NAND_TYPE_N3DS;
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) { } else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) {
return (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_NO3DS; return (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_NO3DS;
} }
return NAND_TYPE_UNK; return NAND_UNKNOWN;
} }
u64 GetNandSizeSectors(bool size_emunand) u64 GetNandSizeSectors(u32 nand_src)
{ {
if (size_emunand) { // for EmuNAND u32 sysnand_sectors = getMMCDevice(0)->total_size;
if (nand_src == NAND_EMUNAND) { // for EmuNAND
u32 emunand_max_sectors = GetPartitionOffsetSector("0:") - (emunand_base_sector + 1); // +1 for safety u32 emunand_max_sectors = GetPartitionOffsetSector("0:") - (emunand_base_sector + 1); // +1 for safety
u32 emunand_min_sectors = (emunand_base_sector % 0x200000 == 0) ? getMMCDevice(0)->total_size : u32 emunand_min_sectors = (emunand_base_sector % 0x200000 == 0) ? sysnand_sectors : NAND_MIN_SIZE;
(GetUnitPlatform() == PLATFORM_N3DS) ? 0x26C000 : 0x1D7800; if (emunand_max_sectors >= sysnand_sectors) return sysnand_sectors;
if (emunand_max_sectors >= getMMCDevice(0)->total_size) return getMMCDevice(0)->total_size;
else return (emunand_min_sectors > emunand_max_sectors) ? 0 : emunand_min_sectors; else return (emunand_min_sectors > emunand_max_sectors) ? 0 : emunand_min_sectors;
} else return getMMCDevice(0)->total_size; // for SysNAND } else if (nand_src == NAND_IMGNAND) {
u32 img_size = (GetMountState() == IMG_NAND) ? GetMountSize() : 0;
return (img_size > sysnand_sectors) ? sysnand_sectors : (img_size > NAND_MIN_SIZE) ? NAND_MIN_SIZE : 0;
} else return sysnand_sectors; // for SysNAND
} }
bool InitEmuNandBase(void) bool InitEmuNandBase(void)
{ {
emunand_base_sector = 0x000000; // GW type EmuNAND emunand_base_sector = 0x000000; // GW type EmuNAND
if (CheckNandType(true) != NAND_TYPE_UNK) if (CheckNandType(NAND_EMUNAND) != NAND_UNKNOWN)
return true; return true;
emunand_base_sector = 0x000001; // RedNAND type EmuNAND emunand_base_sector = 0x000001; // RedNAND type EmuNAND
if (CheckNandType(true) != NAND_TYPE_UNK) if (CheckNandType(NAND_EMUNAND) != NAND_UNKNOWN)
return true; return true;
if (GetPartitionOffsetSector("0:") > getMMCDevice(0)->total_size) if (GetPartitionOffsetSector("0:") > getMMCDevice(0)->total_size)

View File

@ -2,19 +2,22 @@
#include "common.h" #include "common.h"
#define NAND_TYPE_UNK 0 #define NAND_UNKNOWN 0
#define NAND_TYPE_O3DS (1<<0) #define NAND_SYSNAND (1<<0)
#define NAND_TYPE_N3DS (1<<1) #define NAND_EMUNAND (1<<1)
#define NAND_TYPE_NO3DS (1<<2) #define NAND_IMGNAND (1<<2)
#define NAND_TYPE_O3DS (1<<3)
#define NAND_TYPE_N3DS (1<<4)
#define NAND_TYPE_NO3DS (1<<5)
bool InitNandCrypto(void); bool InitNandCrypto(void);
bool CheckSlot0x05Crypto(void); bool CheckSlot0x05Crypto(void);
void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot); void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot);
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand); int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 src);
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool write_emunand); int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest);
u64 GetNandSizeSectors(bool size_emunand); u64 GetNandSizeSectors(u32 src);
u8 CheckNandType(bool check_emunand); u8 CheckNandType(u32 src);
bool InitEmuNandBase(void); bool InitEmuNandBase(void);

View File

@ -4,8 +4,7 @@
#define VFLAG_ON_N3DS NAND_TYPE_N3DS #define VFLAG_ON_N3DS NAND_TYPE_N3DS
#define VFLAG_ON_NO3DS NAND_TYPE_NO3DS #define VFLAG_ON_NO3DS NAND_TYPE_NO3DS
#define VFLAG_ON_ALL (VFLAG_ON_O3DS | VFLAG_ON_N3DS | VFLAG_ON_NO3DS) #define VFLAG_ON_ALL (VFLAG_ON_O3DS | VFLAG_ON_N3DS | VFLAG_ON_NO3DS)
#define VFLAG_NAND_SIZE (1<<30) #define VFLAG_NAND_SIZE (1<<31)
#define VFLAG_ON_EMUNAND (1<<31)
VirtualFile virtualFileTemplates[] = { VirtualFile virtualFileTemplates[] = {
{ "twln.bin" , 0x00012E00, 0x08FB5200, 0x03, VFLAG_ON_ALL }, { "twln.bin" , 0x00012E00, 0x08FB5200, 0x03, VFLAG_ON_ALL },
@ -32,23 +31,23 @@ u32 IsVirtualPath(const char* path) {
return VRT_SYSNAND; return VRT_SYSNAND;
else if (strncmp(path, "E:/", (plen >= 3) ? 3 : 2) == 0) else if (strncmp(path, "E:/", (plen >= 3) ? 3 : 2) == 0)
return VRT_EMUNAND; return VRT_EMUNAND;
else if (strncmp(path, "I:/", (plen >= 3) ? 3 : 2) == 0)
return VRT_IMGNAND;
return 0; return 0;
} }
bool CheckVirtualPath(const char* path) { bool CheckVirtualPath(const char* path) {
u32 vp_nand = IsVirtualPath(path); u32 vp_nand = IsVirtualPath(path);
if (vp_nand == VRT_SYSNAND) { if ((vp_nand == VRT_EMUNAND) || (vp_nand == VRT_IMGNAND)) {
return true; // this is safe because we re-check for slot0x05 crypto return GetNandSizeSectors(vp_nand);
} else if (vp_nand == VRT_EMUNAND) {
return GetNandSizeSectors(true);
} }
return false; return vp_nand; // this is safe for SysNAND because we re-check for slot0x05 crypto
} }
bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size) bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size)
{ {
char* fname = strchr(path, '/'); char* fname = strchr(path, '/');
bool on_emunand = false; u8 nand_src = 0;
u8 nand_type = 0; u8 nand_type = 0;
// fix the name // fix the name
@ -56,12 +55,12 @@ bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size)
fname++; fname++;
// check path vailidity // check path vailidity
if (!IsVirtualPath(path) || (fname - path != 3)) nand_src = IsVirtualPath(path);
if (!nand_src || (fname - path != 3))
return false; return false;
// check NAND type // check NAND type
on_emunand = (IsVirtualPath(path) == VRT_EMUNAND); nand_type = CheckNandType(nand_src);
nand_type = CheckNandType(on_emunand);
// parse the template list, get the correct one // parse the template list, get the correct one
u32 n_templates = sizeof(virtualFileTemplates) / sizeof(VirtualFile); u32 n_templates = sizeof(virtualFileTemplates) / sizeof(VirtualFile);
@ -83,23 +82,25 @@ bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size)
if ((vfile->keyslot == 0x05) && !CheckSlot0x05Crypto()) if ((vfile->keyslot == 0x05) && !CheckSlot0x05Crypto())
return false; // keyslot 0x05 not properly set up return false; // keyslot 0x05 not properly set up
if (vfile->flags & VFLAG_NAND_SIZE) { if (vfile->flags & VFLAG_NAND_SIZE) {
if (on_emunand && (GetNandSizeSectors(false) != GetNandSizeSectors(true))) if ((nand_src != NAND_SYSNAND) && (GetNandSizeSectors(NAND_SYSNAND) != GetNandSizeSectors(nand_src)))
return false; // EmuNAND is too small return false; // EmuNAND/IMGNAND is too small
vfile->size = GetNandSizeSectors(false) * 0x200; vfile->size = GetNandSizeSectors(NAND_SYSNAND) * 0x200;
} }
if (on_emunand) vfile->flags |= VFLAG_ON_EMUNAND; vfile->flags |= nand_src;
return true; return true;
} }
int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count)
{ {
// simple wrapper function for ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand) // simple wrapper function for ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 src)
return ReadNandSectors(buffer, (vfile->offset + offset) / 0x200, (count+0x1FF) / 0x200, vfile->keyslot, vfile->flags & VFLAG_ON_EMUNAND); return ReadNandSectors(buffer, (vfile->offset + offset) / 0x200, (count+0x1FF) / 0x200, vfile->keyslot,
vfile->flags & (VRT_SYSNAND | VRT_EMUNAND | VRT_IMGNAND));
} }
int WriteVirtualFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count) int WriteVirtualFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count)
{ {
// simple wrapper function for WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool write_emunand) // simple wrapper function for WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest)
return WriteNandSectors(buffer, (vfile->offset + offset) / 0x200, (count+0x1FF) / 0x200, vfile->keyslot, vfile->flags & VFLAG_ON_EMUNAND); return WriteNandSectors(buffer, (vfile->offset + offset) / 0x200, (count+0x1FF) / 0x200, vfile->keyslot,
vfile->flags & (VRT_SYSNAND | VRT_EMUNAND | VRT_IMGNAND));
} }

View File

@ -4,8 +4,9 @@
#include "nand.h" #include "nand.h"
#define VRT_NONE 0 #define VRT_NONE 0
#define VRT_SYSNAND 1 #define VRT_SYSNAND NAND_SYSNAND
#define VRT_EMUNAND 2 #define VRT_EMUNAND NAND_EMUNAND
#define VRT_IMGNAND NAND_IMGNAND
static const char* virtualFileList[] = { // must have a match in virtualFileTemplates[] static const char* virtualFileList[] = { // must have a match in virtualFileTemplates[]
"twln.bin", "twlp.bin", "agbsave.bin", "firm0.bin", "firm1.bin", "ctrnand_fat.bin", "ctrnand_full.bin", "twln.bin", "twlp.bin", "agbsave.bin", "firm0.bin", "firm1.bin", "ctrnand_fat.bin", "ctrnand_full.bin",