mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Enable mounting of NCCH files
This commit is contained in:
parent
622947f63a
commit
58d7573ef5
@ -3,7 +3,7 @@
|
|||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
|
|
||||||
u32 IdentifyFileType(const char* path) {
|
u32 IdentifyFileType(const char* path) {
|
||||||
u8 header[0x200]; // minimum required size
|
u8 __attribute__((aligned(16))) header[0x200]; // minimum required size
|
||||||
FIL file;
|
FIL file;
|
||||||
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||||
return 0;
|
return 0;
|
||||||
@ -37,9 +37,14 @@ u32 IdentifyFileType(const char* path) {
|
|||||||
if (fsize >= info.size_cia)
|
if (fsize >= info.size_cia)
|
||||||
return GAME_CIA; // CIA file
|
return GAME_CIA; // CIA file
|
||||||
} else if (ValidateNcsdHeader((NcsdHeader*) header) == 0) {
|
} else if (ValidateNcsdHeader((NcsdHeader*) header) == 0) {
|
||||||
if (fsize >= (((NcsdHeader*) header)->size * NCSD_MEDIA_UNIT))
|
NcsdHeader* ncsd = (NcsdHeader*) header;
|
||||||
|
if (fsize >= (ncsd->size * NCSD_MEDIA_UNIT))
|
||||||
return GAME_NCSD; // NCSD (".3DS") file
|
return GAME_NCSD; // NCSD (".3DS") file
|
||||||
} // NCCH still missing
|
} else if (ValidateNcchHeader((NcchHeader*) header) == 0) {
|
||||||
|
NcchHeader* ncch = (NcchHeader*) header;
|
||||||
|
if (fsize >= (ncch->size * NCCH_MEDIA_UNIT))
|
||||||
|
return GAME_NCCH; // NCSD (".3DS") file
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cia.h"
|
#include "cia.h"
|
||||||
#include "ncsd.h"
|
#include "ncsd.h"
|
||||||
|
#include "ncch.h"
|
||||||
|
24
source/game/ncch.c
Normal file
24
source/game/ncch.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "ncch.h"
|
||||||
|
|
||||||
|
u32 ValidateNcchHeader(NcchHeader* header) {
|
||||||
|
if (memcmp(header->magic, "NCCH", 4) != 0) // check magic number
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
u32 ncch_units = (NCCH_EXTHDR_OFFSET + header->size_exthdr) / NCCH_MEDIA_UNIT; // exthdr
|
||||||
|
if (header->size_plain) { // plain region
|
||||||
|
if (header->offset_plain < ncch_units) return 1; // overlapping plain region
|
||||||
|
ncch_units = (header->offset_plain + header->size_plain);
|
||||||
|
}
|
||||||
|
if (header->size_exefs) { // ExeFS
|
||||||
|
if (header->offset_exefs < ncch_units) return 1; // overlapping exefs region
|
||||||
|
ncch_units = (header->offset_exefs + header->size_exefs);
|
||||||
|
}
|
||||||
|
if (header->size_romfs) { // RomFS
|
||||||
|
if (header->offset_romfs < ncch_units) return 1; // overlapping romfs region
|
||||||
|
ncch_units = (header->offset_romfs + header->size_romfs);
|
||||||
|
}
|
||||||
|
// size check
|
||||||
|
if (ncch_units > header->size) return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
59
source/game/ncch.h
Normal file
59
source/game/ncch.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define NCCH_MEDIA_UNIT 0x200
|
||||||
|
|
||||||
|
#define NCCH_EXTHDR_SIZE 0x800 // NCCH header says 0x400, which is not the full thing
|
||||||
|
#define NCCH_EXTHDR_OFFSET 0x200
|
||||||
|
|
||||||
|
// see: https://www.3dbrew.org/wiki/NCCH/Extended_Header
|
||||||
|
// very limited, contains only required stuff
|
||||||
|
typedef struct {
|
||||||
|
char name[8];
|
||||||
|
u8 reserved[0x5];
|
||||||
|
u8 flag; // bit 1 for SD
|
||||||
|
u32 remaster_version;
|
||||||
|
u8 sci_data[0x30];
|
||||||
|
u8 dependencies[0x180];
|
||||||
|
u8 sys_info[0x40];
|
||||||
|
u8 aci_data[0x200];
|
||||||
|
u8 signature[0x100];
|
||||||
|
u8 public_key[0x100];
|
||||||
|
u8 aci_limit_data[0x200];
|
||||||
|
} __attribute__((packed)) NcchExtHeader;
|
||||||
|
|
||||||
|
// see: https://www.3dbrew.org/wiki/NCCH#NCCH_Header
|
||||||
|
typedef struct {
|
||||||
|
u8 signature[0x100];
|
||||||
|
u8 magic[0x4];
|
||||||
|
u32 size;
|
||||||
|
u64 partitionId;
|
||||||
|
u16 makercode;
|
||||||
|
u16 version;
|
||||||
|
u8 hash_seed[0x4];
|
||||||
|
u64 programId;
|
||||||
|
u8 reserved0[0x10];
|
||||||
|
u8 hash_logo[0x20];
|
||||||
|
char productcode[0x10];
|
||||||
|
u8 hash_exthdr[0x20];
|
||||||
|
u32 size_exthdr;
|
||||||
|
u8 reserved1[0x4];
|
||||||
|
u8 flags[0x8];
|
||||||
|
u32 offset_plain;
|
||||||
|
u32 size_plain;
|
||||||
|
u32 offset_logo;
|
||||||
|
u32 size_logo;
|
||||||
|
u32 offset_exefs;
|
||||||
|
u32 size_exefs;
|
||||||
|
u32 size_exefs_hash;
|
||||||
|
u8 reserved2[0x4];
|
||||||
|
u32 offset_romfs;
|
||||||
|
u32 size_romfs;
|
||||||
|
u32 size_romfs_hash;
|
||||||
|
u8 reserved3[0x4];
|
||||||
|
u8 hash_exefs[0x20];
|
||||||
|
u8 hash_romfs[0x20];
|
||||||
|
} __attribute__((packed)) NcchHeader;
|
||||||
|
|
||||||
|
u32 ValidateNcchHeader(NcchHeader* header);
|
@ -2,15 +2,19 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define NCSD_MEDIA_UNIT 0x200
|
#define NCSD_MEDIA_UNIT 0x200
|
||||||
|
|
||||||
|
#define NCSD_CINFO_OFFSET 0x200
|
||||||
|
#define NCSD_CINFO_SIZE 0x1000
|
||||||
|
#define NCSD_DINFO_OFFSET 0x1200
|
||||||
|
#define NCSD_DINFO_SIZE 0x300
|
||||||
|
|
||||||
// see: https://www.3dbrew.org/wiki/NCSD
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 size;
|
u32 size;
|
||||||
} __attribute__((packed)) NcchPartition;
|
} __attribute__((packed)) NcchPartition;
|
||||||
|
|
||||||
// see: https://www.3dbrew.org/wiki/NCSD
|
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 signature[0x100];
|
u8 signature[0x100];
|
||||||
u8 magic[4];
|
u8 magic[4];
|
||||||
@ -25,6 +29,6 @@ typedef struct {
|
|||||||
u8 partition_flags[8];
|
u8 partition_flags[8];
|
||||||
u8 partitionId_table[8][8];
|
u8 partitionId_table[8][8];
|
||||||
u8 reserved[0x40];
|
u8 reserved[0x40];
|
||||||
} __attribute__((packed, aligned(16))) NcsdHeader;
|
} __attribute__((packed)) NcsdHeader;
|
||||||
|
|
||||||
u32 ValidateNcsdHeader(NcsdHeader* header);
|
u32 ValidateNcsdHeader(NcsdHeader* header);
|
||||||
|
@ -640,7 +640,8 @@ u32 GodMode() {
|
|||||||
(file_type == IMG_NAND) ? "Mount as NAND image" :
|
(file_type == IMG_NAND) ? "Mount as NAND image" :
|
||||||
(file_type == IMG_FAT) ? "Mount as FAT image" :
|
(file_type == IMG_FAT) ? "Mount as FAT image" :
|
||||||
(file_type == GAME_CIA) ? "Mount as CIA image" :
|
(file_type == GAME_CIA) ? "Mount as CIA image" :
|
||||||
(file_type == GAME_NCSD) ? "Mount as NCSD image" : ""; // !!! NCCH
|
(file_type == GAME_NCSD) ? "Mount as NCSD image" :
|
||||||
|
(file_type == GAME_NCCH) ? "Mount as NCCH image" : "???";
|
||||||
if (searchdrv) optionstr[searchdrv-1] = "Open containing folder";
|
if (searchdrv) optionstr[searchdrv-1] = "Open containing folder";
|
||||||
|
|
||||||
u32 user_select = ShowSelectPrompt(n_opt, optionstr, pathstr);
|
u32 user_select = ShowSelectPrompt(n_opt, optionstr, pathstr);
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
#include "aes.h"
|
#include "aes.h"
|
||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
|
|
||||||
|
#define VFLAG_EXTHDR (1<<29)
|
||||||
|
#define VFLAG_EXEFS (1<<30)
|
||||||
|
#define VFLAG_ROMFS (1<<31)
|
||||||
|
|
||||||
#define MAX_N_TEMPLATES 2048 // this leaves us with enough room (128kB reserved)
|
#define MAX_N_TEMPLATES 2048 // this leaves us with enough room (128kB reserved)
|
||||||
|
|
||||||
#define NAME_CIA_HEADER "header.bin"
|
#define NAME_CIA_HEADER "header.bin"
|
||||||
@ -21,12 +25,25 @@
|
|||||||
"cnt3.unk", "cnt4.unk", "cnt5.unk", \
|
"cnt3.unk", "cnt4.unk", "cnt5.unk", \
|
||||||
"cnt6.update_n3ds.cfa", "cnt7.update_o3ds.cfa"
|
"cnt6.update_n3ds.cfa", "cnt7.update_o3ds.cfa"
|
||||||
|
|
||||||
|
#define NAME_NCCH_HEADER "ncch.bin"
|
||||||
|
#define NAME_NCCH_EXTHEADER "extheader.bin"
|
||||||
|
#define NAME_NCCH_PLAIN "plain.bin"
|
||||||
|
#define NAME_NCCH_LOGO "logo.bin"
|
||||||
|
#define NAME_NCCH_EXEFS "exefs.bin"
|
||||||
|
#define NAME_NCCH_ROMFS "romfs.bin"
|
||||||
|
|
||||||
static u32 vgame_type = 0;
|
static u32 vgame_type = 0;
|
||||||
static VirtualFile* templates = (VirtualFile*) VGAME_BUFFER; // first 128kb reserved
|
static VirtualFile* templates = (VirtualFile*) VGAME_BUFFER; // first 128kb reserved
|
||||||
static int n_templates = -1;
|
static int n_templates = -1;
|
||||||
|
|
||||||
static NcsdHeader* ncsd = (NcsdHeader*) (VGAME_BUFFER + 0xF3000); // needs only 512 byte
|
|
||||||
static CiaStub* cia = (CiaStub*) (VGAME_BUFFER + 0xF4000); // 48kB reserved - should be enough by far
|
static CiaStub* cia = (CiaStub*) (VGAME_BUFFER + 0xF4000); // 48kB reserved - should be enough by far
|
||||||
|
static NcsdHeader* ncsd = (NcsdHeader*) (VGAME_BUFFER + 0xF3000); // needs only 512 byte
|
||||||
|
static NcchHeader* ncch = (NcchHeader*) (VGAME_BUFFER + 0xF3200); // needs only 512 byte
|
||||||
|
static ExeFsHeader* exefs = (ExeFsHeader*) (VGAME_BUFFER + 0xF3400); // needs only 512 byte
|
||||||
|
|
||||||
|
static u32 offset_ncch = 0;
|
||||||
|
static u32 offset_exefs = 0;
|
||||||
|
static u32 offset_romfs = 0;
|
||||||
|
|
||||||
u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
||||||
u32 type = GetMountState();
|
u32 type = GetMountState();
|
||||||
@ -43,6 +60,10 @@ u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
|||||||
(ValidateNcsdHeader(ncsd) != 0))
|
(ValidateNcsdHeader(ncsd) != 0))
|
||||||
return 0;
|
return 0;
|
||||||
} else if (type == GAME_NCCH) {
|
} else if (type == GAME_NCCH) {
|
||||||
|
offset_ncch = 0;
|
||||||
|
if ((ReadImageBytes((u8*) ncch, 0, sizeof(NcchHeader)) != 0) ||
|
||||||
|
(ValidateNcchHeader(ncch) != 0))
|
||||||
|
return 0;
|
||||||
} else return 0; // not a mounted game file
|
} else return 0; // not a mounted game file
|
||||||
|
|
||||||
vgame_type = type;
|
vgame_type = type;
|
||||||
@ -54,6 +75,71 @@ u32 CheckVGameDrive(void) {
|
|||||||
return vgame_type;
|
return vgame_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BuildVGameNcchVDir(void) {
|
||||||
|
if (CheckVGameDrive() != GAME_NCCH)
|
||||||
|
return false; // safety check
|
||||||
|
|
||||||
|
// header
|
||||||
|
strncpy(templates[n_templates].name, NAME_NCCH_HEADER, 32);
|
||||||
|
templates[n_templates].offset = offset_ncch + 0;
|
||||||
|
templates[n_templates].size = 0x200;
|
||||||
|
templates[n_templates].keyslot = 0xFF;
|
||||||
|
templates[n_templates].flags = 0;
|
||||||
|
n_templates++;
|
||||||
|
|
||||||
|
// extended header
|
||||||
|
if (ncch->size_exthdr) {
|
||||||
|
strncpy(templates[n_templates].name, NAME_NCCH_EXTHEADER, 32);
|
||||||
|
templates[n_templates].offset = offset_ncch + NCCH_EXTHDR_OFFSET;
|
||||||
|
templates[n_templates].size = NCCH_EXTHDR_SIZE;
|
||||||
|
templates[n_templates].keyslot = 0xFF; // crypto ?
|
||||||
|
templates[n_templates].flags = 0;
|
||||||
|
n_templates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// plain region
|
||||||
|
if (ncch->size_plain) {
|
||||||
|
strncpy(templates[n_templates].name, NAME_NCCH_PLAIN, 32);
|
||||||
|
templates[n_templates].offset = offset_ncch + (ncch->offset_plain * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n_templates].size = ncch->size_plain * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n_templates].keyslot = 0xFF;
|
||||||
|
templates[n_templates].flags = 0;
|
||||||
|
n_templates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// logo region
|
||||||
|
if (ncch->size_logo) {
|
||||||
|
strncpy(templates[n_templates].name, NAME_NCCH_LOGO, 32);
|
||||||
|
templates[n_templates].offset =offset_ncch + (ncch->offset_logo * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n_templates].size = ncch->size_logo * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n_templates].keyslot = 0xFF;
|
||||||
|
templates[n_templates].flags = 0;
|
||||||
|
n_templates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exefs
|
||||||
|
if (ncch->size_exefs) {
|
||||||
|
strncpy(templates[n_templates].name, NAME_NCCH_EXEFS, 32);
|
||||||
|
templates[n_templates].offset = offset_ncch + (ncch->offset_exefs * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n_templates].size = ncch->size_exefs * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n_templates].keyslot = 0xFF;
|
||||||
|
templates[n_templates].flags = 0;
|
||||||
|
n_templates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// romfs
|
||||||
|
if (ncch->size_romfs) {
|
||||||
|
strncpy(templates[n_templates].name, NAME_NCCH_ROMFS, 32);
|
||||||
|
templates[n_templates].offset = offset_ncch + (ncch->offset_romfs * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n_templates].size = ncch->size_romfs * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n_templates].keyslot = 0xFF;
|
||||||
|
templates[n_templates].flags = 0;
|
||||||
|
n_templates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool BuildVGameNcsdVDir(void) {
|
bool BuildVGameNcsdVDir(void) {
|
||||||
const char* name_content[] = { NAME_NCSD_CONTENT };
|
const char* name_content[] = { NAME_NCSD_CONTENT };
|
||||||
|
|
||||||
@ -69,20 +155,20 @@ bool BuildVGameNcsdVDir(void) {
|
|||||||
n_templates++;
|
n_templates++;
|
||||||
|
|
||||||
// card info header
|
// card info header
|
||||||
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= 0x1200) {
|
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_CINFO_OFFSET + NCSD_CINFO_SIZE) {
|
||||||
strncpy(templates[n_templates].name, NAME_NCSD_CARDINFO, 32);
|
strncpy(templates[n_templates].name, NAME_NCSD_CARDINFO, 32);
|
||||||
templates[n_templates].offset = 0x200;
|
templates[n_templates].offset = NCSD_CINFO_OFFSET;
|
||||||
templates[n_templates].size = 0x1000;
|
templates[n_templates].size = NCSD_CINFO_SIZE;
|
||||||
templates[n_templates].keyslot = 0xFF;
|
templates[n_templates].keyslot = 0xFF;
|
||||||
templates[n_templates].flags = 0;
|
templates[n_templates].flags = 0;
|
||||||
n_templates++;
|
n_templates++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dev info header
|
// dev info header
|
||||||
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= 0x1500) {
|
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_DINFO_OFFSET + NCSD_DINFO_SIZE) {
|
||||||
strncpy(templates[n_templates].name, NAME_NCSD_DEVINFO, 32);
|
strncpy(templates[n_templates].name, NAME_NCSD_DEVINFO, 32);
|
||||||
templates[n_templates].offset = 0x1200;
|
templates[n_templates].offset = NCSD_DINFO_OFFSET;
|
||||||
templates[n_templates].size = 0x300;
|
templates[n_templates].size = NCSD_DINFO_SIZE;
|
||||||
templates[n_templates].keyslot = 0xFF;
|
templates[n_templates].keyslot = 0xFF;
|
||||||
templates[n_templates].flags = 0;
|
templates[n_templates].flags = 0;
|
||||||
n_templates++;
|
n_templates++;
|
||||||
@ -203,6 +289,8 @@ bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
|||||||
return true;
|
return true;
|
||||||
else if ((vgame_type == GAME_NCSD) && BuildVGameNcsdVDir()) // for NCSD
|
else if ((vgame_type == GAME_NCSD) && BuildVGameNcsdVDir()) // for NCSD
|
||||||
return true;
|
return true;
|
||||||
|
else if ((vgame_type == GAME_NCCH) && BuildVGameNcchVDir()) // for NCSD
|
||||||
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
// virtual file flag (subject to change):
|
// virtual file flag (subject to change):
|
||||||
// bits 0...9 : reserved for NAND virtual sources and info
|
// bits 0...9 : reserved for NAND virtual sources and info
|
||||||
// bits 10...15: reserved for other virtual sources
|
// bits 10...15: reserved for other virtual sources
|
||||||
// bits 16...23: reserved for external flags
|
// bits 16...19: reserved for external flags
|
||||||
// bits 24...31: reserved for internal flags (different per source)
|
// bits 20...31: reserved for internal flags (different per source)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[32];
|
char name[32];
|
||||||
u32 offset; // must be a multiple of 0x200 (for NAND access)
|
u32 offset; // must be a multiple of 0x200 (for NAND access)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user