mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Support for mounting NAND images
This commit is contained in:
parent
59126078c7
commit
00fe5b9b36
@ -8,12 +8,15 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "image.h"
|
||||
#include "nand.h"
|
||||
#include "sdmmc.h"
|
||||
|
||||
#define TYPE_SDCARD 0
|
||||
#define TYPE_SYSNAND 1
|
||||
#define TYPE_EMUNAND 2
|
||||
#define TYPE_SDCARD 0x00
|
||||
#define TYPE_SYSNAND NAND_SYSNAND
|
||||
#define TYPE_EMUNAND NAND_EMUNAND
|
||||
#define TYPE_IMGNAND NAND_IMGNAND
|
||||
#define TYPE_IMAGE 0xFF
|
||||
|
||||
#define SUBTYPE_CTRN 0
|
||||
#define SUBTYPE_CTRN_N 1
|
||||
@ -33,7 +36,7 @@ typedef struct {
|
||||
BYTE keyslot;
|
||||
} SubtypeDesc;
|
||||
|
||||
FATpartition DriveInfo[7] = {
|
||||
FATpartition DriveInfo[11] = {
|
||||
{ TYPE_SDCARD, SUBTYPE_NONE }, // 0 - SDCARD
|
||||
{ TYPE_SYSNAND, SUBTYPE_CTRN }, // 1 - SYSNAND CTRNAND
|
||||
{ TYPE_SYSNAND, SUBTYPE_TWLN }, // 2 - SYSNAND TWLN
|
||||
@ -41,6 +44,10 @@ FATpartition DriveInfo[7] = {
|
||||
{ TYPE_EMUNAND, SUBTYPE_CTRN }, // 4 - EMUNAND CTRNAND
|
||||
{ TYPE_EMUNAND, SUBTYPE_TWLN }, // 5 - EMUNAND TWLN
|
||||
{ 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] = {
|
||||
@ -51,8 +58,9 @@ SubtypeDesc SubTypes[5] = {
|
||||
{ 0x04808D, 0x0105B3, 0x3 } // TWLP
|
||||
};
|
||||
|
||||
static BYTE nand_type_sys = NAND_TYPE_UNK;
|
||||
static BYTE nand_type_emu = NAND_TYPE_UNK;
|
||||
static BYTE nand_type_sys = 0;
|
||||
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 subtype = DriveInfo[pdrv].subtype;
|
||||
|
||||
if (type == TYPE_SDCARD) {
|
||||
if (subtype == SUBTYPE_NONE) {
|
||||
return NULL;
|
||||
} 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)
|
||||
subtype = (nand_type == NAND_TYPE_N3DS) ? SUBTYPE_CTRN_N : SUBTYPE_CTRN_NO;
|
||||
}
|
||||
@ -108,9 +116,11 @@ DSTATUS disk_initialize (
|
||||
if (!sdmmc_sdcard_init())
|
||||
return RES_PARERR;
|
||||
} else if (pdrv < 4) {
|
||||
nand_type_sys = CheckNandType(false);
|
||||
nand_type_sys = CheckNandType(NAND_SYSNAND);
|
||||
} 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;
|
||||
}
|
||||
@ -135,12 +145,14 @@ DRESULT disk_read (
|
||||
if (sdmmc_sdcard_readsectors(sector, count, buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
} else if (type == TYPE_IMAGE) {
|
||||
return RES_PARERR;
|
||||
} else {
|
||||
SubtypeDesc* subtype = get_subtype_desc(pdrv);
|
||||
BYTE keyslot = subtype->keyslot;
|
||||
DWORD isector = subtype->offset + sector;
|
||||
|
||||
if (ReadNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND))
|
||||
if (ReadNandSectors(buff, isector, count, keyslot, type))
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
@ -168,12 +180,14 @@ DRESULT disk_write (
|
||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
} else if (type == TYPE_IMAGE) {
|
||||
return RES_PARERR;
|
||||
} else {
|
||||
SubtypeDesc* subtype = get_subtype_desc(pdrv);
|
||||
BYTE keyslot = subtype->keyslot;
|
||||
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!
|
||||
}
|
||||
|
||||
@ -197,13 +211,17 @@ DRESULT disk_ioctl (
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
BYTE type = DriveInfo[pdrv].type;
|
||||
|
||||
switch (cmd) {
|
||||
case GET_SECTOR_SIZE:
|
||||
*((DWORD*) buff) = 0x200;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
if (DriveInfo[pdrv].type == TYPE_SDCARD) {
|
||||
if (type == TYPE_SDCARD) {
|
||||
*((DWORD*) buff) = getMMCDevice(1)->total_size;
|
||||
} else if (type == TYPE_IMAGE) {
|
||||
*((DWORD*) buff) = GetMountSize();
|
||||
} else {
|
||||
*((DWORD*) buff) = get_subtype_desc(pdrv)->size;
|
||||
}
|
||||
@ -212,7 +230,9 @@ DRESULT disk_ioctl (
|
||||
*((DWORD*) buff) = 0x2000;
|
||||
return RES_OK;
|
||||
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_PARERR;
|
||||
|
@ -456,7 +456,7 @@ WCHAR ff_wtoupper(WCHAR chr)
|
||||
/ 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
|
||||
#endif
|
||||
static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */
|
||||
|
@ -141,11 +141,11 @@
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 7
|
||||
#define _VOLUMES 10
|
||||
/* 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"
|
||||
/* _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
|
||||
|
80
source/fatfs/image.c
Normal file
80
source/fatfs/image.c
Normal 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
16
source/fatfs/image.h
Normal 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);
|
26
source/fs.c
26
source/fs.c
@ -6,7 +6,8 @@
|
||||
#define MAIN_BUFFER ((u8*)0x21200000)
|
||||
#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!
|
||||
static FATFS* fs = (FATFS*)0x20316000;
|
||||
@ -15,7 +16,7 @@ static FATFS* fs = (FATFS*)0x20316000;
|
||||
static u32 write_permission_level = 1;
|
||||
|
||||
// number of currently open file systems
|
||||
static bool fs_mounted[MAX_FS] = { false };
|
||||
static bool fs_mounted[NORM_FS] = { false };
|
||||
|
||||
bool InitSDCardFS() {
|
||||
#ifndef EXEC_GATEWAY
|
||||
@ -30,7 +31,7 @@ bool InitSDCardFS() {
|
||||
bool InitNandFS() {
|
||||
if (!fs_mounted[0])
|
||||
return false;
|
||||
for (u32 i = 1; i < MAX_FS; i++) {
|
||||
for (u32 i = 1; i < NORM_FS; i++) {
|
||||
char fsname[8];
|
||||
snprintf(fsname, 7, "%lu:", i);
|
||||
if (f_mount(fs + i, fsname, 1) != FR_OK) return false;
|
||||
@ -40,7 +41,7 @@ bool InitNandFS() {
|
||||
}
|
||||
|
||||
void DeinitNandFS() {
|
||||
for (u32 i = MAX_FS; i > 0; i--) {
|
||||
for (u32 i = NORM_FS; i > 0; i--) {
|
||||
if (fs_mounted[i]) {
|
||||
char fsname[8];
|
||||
snprintf(fsname, 7, "%lu:", i);
|
||||
@ -59,7 +60,7 @@ void DeinitSDCardFS() {
|
||||
|
||||
int PathToNumFS(const char* path) {
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
@ -82,6 +83,10 @@ bool CheckWritePermissions(const char* path) {
|
||||
if (ShowPrompt(true, "Writing to the EmuNAND is locked!\nUnlock it now?"))
|
||||
return SetWritePermissions(2);
|
||||
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)) {
|
||||
if (ShowPrompt(true, "Writing to the SD card is locked!\nUnlock it now?"))
|
||||
return SetWritePermissions(1);
|
||||
@ -570,18 +575,19 @@ bool GetRootDirContentsWorker(DirStruct* contents) {
|
||||
"SDCARD",
|
||||
"SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND 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[] = {
|
||||
"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;
|
||||
|
||||
// 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]);
|
||||
if ((pdrv < MAX_FS) && !fs_mounted[pdrv]) continue;
|
||||
else if ((pdrv >= MAX_FS) && (!CheckVirtualPath(drvnum[pdrv]))) continue;
|
||||
if ((pdrv < NORM_FS) && !fs_mounted[pdrv]) continue;
|
||||
else if ((pdrv >= NORM_FS) && (!CheckVirtualPath(drvnum[pdrv]))) continue;
|
||||
memset(entry->path, 0x00, 64);
|
||||
snprintf(entry->path + 0, 4, drvnum[pdrv]);
|
||||
snprintf(entry->path + 4, 32, "[%s] %s", drvnum[pdrv], drvname[pdrv]);
|
||||
|
@ -5,8 +5,9 @@
|
||||
#include "platform.h"
|
||||
#include "nand.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_SIDE_BAR COLOR_DARKGREY
|
||||
@ -184,6 +185,20 @@ u32 GodMode() {
|
||||
cursor = 1;
|
||||
scroll = 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
|
||||
char old_path[256];
|
||||
char* last_slash = strrchr(current_path, '/');
|
||||
|
@ -4,9 +4,11 @@
|
||||
#include "sha.h"
|
||||
#include "sdmmc.h"
|
||||
#include "nand.h"
|
||||
#include "image.h"
|
||||
|
||||
#define NAND_BUFFER ((u8*)0x21100000)
|
||||
#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_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 u32 sector = 0x05CAD7;
|
||||
u8 buffer[0x200];
|
||||
for (u32 nand = 0; nand < 2; nand++) {
|
||||
ReadNandSectors(buffer, sector, 1, 0x05, nand);
|
||||
if (memcmp(buffer, magic, 8) == 0)
|
||||
return true;
|
||||
}
|
||||
ReadNandSectors(buffer, sector, 1, 0x05, NAND_SYSNAND);
|
||||
if (memcmp(buffer, magic, 8) == 0)
|
||||
return true;
|
||||
|
||||
// failed if we arrive here
|
||||
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;
|
||||
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);
|
||||
@ -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;
|
||||
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);
|
||||
if (errorcode) return errorcode;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
|
||||
|
||||
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
|
||||
for (u32 s = 0; s < count; s += (NAND_BUFFER_SIZE / 0x200)) {
|
||||
u32 pcount = min((NAND_BUFFER_SIZE/0x200), (count - s));
|
||||
memcpy(NAND_BUFFER, buffer + (s*0x200), pcount * 0x200);
|
||||
if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
|
||||
if (write_emunand) {
|
||||
if (nand_dst == NAND_EMUNAND) {
|
||||
int errorcode = 0;
|
||||
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 = (!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);
|
||||
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);
|
||||
if (errorcode) return errorcode;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 CheckNandType(bool check_emunand)
|
||||
u8 CheckNandType(u32 nand_src)
|
||||
{
|
||||
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, check_emunand) != 0)
|
||||
return NAND_TYPE_UNK;
|
||||
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, nand_src) != 0)
|
||||
return NAND_UNKNOWN;
|
||||
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) {
|
||||
return NAND_TYPE_N3DS;
|
||||
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) {
|
||||
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_min_sectors = (emunand_base_sector % 0x200000 == 0) ? getMMCDevice(0)->total_size :
|
||||
(GetUnitPlatform() == PLATFORM_N3DS) ? 0x26C000 : 0x1D7800;
|
||||
if (emunand_max_sectors >= getMMCDevice(0)->total_size) return getMMCDevice(0)->total_size;
|
||||
u32 emunand_min_sectors = (emunand_base_sector % 0x200000 == 0) ? sysnand_sectors : NAND_MIN_SIZE;
|
||||
if (emunand_max_sectors >= sysnand_sectors) return sysnand_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)
|
||||
{
|
||||
emunand_base_sector = 0x000000; // GW type EmuNAND
|
||||
if (CheckNandType(true) != NAND_TYPE_UNK)
|
||||
if (CheckNandType(NAND_EMUNAND) != NAND_UNKNOWN)
|
||||
return true;
|
||||
|
||||
emunand_base_sector = 0x000001; // RedNAND type EmuNAND
|
||||
if (CheckNandType(true) != NAND_TYPE_UNK)
|
||||
if (CheckNandType(NAND_EMUNAND) != NAND_UNKNOWN)
|
||||
return true;
|
||||
|
||||
if (GetPartitionOffsetSector("0:") > getMMCDevice(0)->total_size)
|
||||
|
@ -2,19 +2,22 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define NAND_TYPE_UNK 0
|
||||
#define NAND_TYPE_O3DS (1<<0)
|
||||
#define NAND_TYPE_N3DS (1<<1)
|
||||
#define NAND_TYPE_NO3DS (1<<2)
|
||||
#define NAND_UNKNOWN 0
|
||||
#define NAND_SYSNAND (1<<0)
|
||||
#define NAND_EMUNAND (1<<1)
|
||||
#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 CheckSlot0x05Crypto(void);
|
||||
|
||||
void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot);
|
||||
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand);
|
||||
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool write_emunand);
|
||||
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);
|
||||
|
||||
u64 GetNandSizeSectors(bool size_emunand);
|
||||
u8 CheckNandType(bool check_emunand);
|
||||
u64 GetNandSizeSectors(u32 src);
|
||||
u8 CheckNandType(u32 src);
|
||||
|
||||
bool InitEmuNandBase(void);
|
||||
|
@ -4,8 +4,7 @@
|
||||
#define VFLAG_ON_N3DS NAND_TYPE_N3DS
|
||||
#define VFLAG_ON_NO3DS NAND_TYPE_NO3DS
|
||||
#define VFLAG_ON_ALL (VFLAG_ON_O3DS | VFLAG_ON_N3DS | VFLAG_ON_NO3DS)
|
||||
#define VFLAG_NAND_SIZE (1<<30)
|
||||
#define VFLAG_ON_EMUNAND (1<<31)
|
||||
#define VFLAG_NAND_SIZE (1<<31)
|
||||
|
||||
VirtualFile virtualFileTemplates[] = {
|
||||
{ "twln.bin" , 0x00012E00, 0x08FB5200, 0x03, VFLAG_ON_ALL },
|
||||
@ -32,23 +31,23 @@ u32 IsVirtualPath(const char* path) {
|
||||
return VRT_SYSNAND;
|
||||
else if (strncmp(path, "E:/", (plen >= 3) ? 3 : 2) == 0)
|
||||
return VRT_EMUNAND;
|
||||
else if (strncmp(path, "I:/", (plen >= 3) ? 3 : 2) == 0)
|
||||
return VRT_IMGNAND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CheckVirtualPath(const char* path) {
|
||||
u32 vp_nand = IsVirtualPath(path);
|
||||
if (vp_nand == VRT_SYSNAND) {
|
||||
return true; // this is safe because we re-check for slot0x05 crypto
|
||||
} else if (vp_nand == VRT_EMUNAND) {
|
||||
return GetNandSizeSectors(true);
|
||||
if ((vp_nand == VRT_EMUNAND) || (vp_nand == VRT_IMGNAND)) {
|
||||
return GetNandSizeSectors(vp_nand);
|
||||
}
|
||||
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)
|
||||
{
|
||||
char* fname = strchr(path, '/');
|
||||
bool on_emunand = false;
|
||||
u8 nand_src = 0;
|
||||
u8 nand_type = 0;
|
||||
|
||||
// fix the name
|
||||
@ -56,12 +55,12 @@ bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size)
|
||||
fname++;
|
||||
|
||||
// check path vailidity
|
||||
if (!IsVirtualPath(path) || (fname - path != 3))
|
||||
nand_src = IsVirtualPath(path);
|
||||
if (!nand_src || (fname - path != 3))
|
||||
return false;
|
||||
|
||||
// check NAND type
|
||||
on_emunand = (IsVirtualPath(path) == VRT_EMUNAND);
|
||||
nand_type = CheckNandType(on_emunand);
|
||||
nand_type = CheckNandType(nand_src);
|
||||
|
||||
// parse the template list, get the correct one
|
||||
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())
|
||||
return false; // keyslot 0x05 not properly set up
|
||||
if (vfile->flags & VFLAG_NAND_SIZE) {
|
||||
if (on_emunand && (GetNandSizeSectors(false) != GetNandSizeSectors(true)))
|
||||
return false; // EmuNAND is too small
|
||||
vfile->size = GetNandSizeSectors(false) * 0x200;
|
||||
if ((nand_src != NAND_SYSNAND) && (GetNandSizeSectors(NAND_SYSNAND) != GetNandSizeSectors(nand_src)))
|
||||
return false; // EmuNAND/IMGNAND is too small
|
||||
vfile->size = GetNandSizeSectors(NAND_SYSNAND) * 0x200;
|
||||
}
|
||||
if (on_emunand) vfile->flags |= VFLAG_ON_EMUNAND;
|
||||
vfile->flags |= nand_src;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
return ReadNandSectors(buffer, (vfile->offset + offset) / 0x200, (count+0x1FF) / 0x200, vfile->keyslot, vfile->flags & VFLAG_ON_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 & (VRT_SYSNAND | VRT_EMUNAND | VRT_IMGNAND));
|
||||
}
|
||||
|
||||
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)
|
||||
return WriteNandSectors(buffer, (vfile->offset + offset) / 0x200, (count+0x1FF) / 0x200, vfile->keyslot, vfile->flags & VFLAG_ON_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 & (VRT_SYSNAND | VRT_EMUNAND | VRT_IMGNAND));
|
||||
}
|
||||
|
@ -4,8 +4,9 @@
|
||||
#include "nand.h"
|
||||
|
||||
#define VRT_NONE 0
|
||||
#define VRT_SYSNAND 1
|
||||
#define VRT_EMUNAND 2
|
||||
#define VRT_SYSNAND NAND_SYSNAND
|
||||
#define VRT_EMUNAND NAND_EMUNAND
|
||||
#define VRT_IMGNAND NAND_IMGNAND
|
||||
|
||||
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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user