forked from Mirror/GodMode9
Enabled virtual dir handling
This commit is contained in:
parent
58d7573ef5
commit
ddac828dcb
16
source/game/exefs.c
Normal file
16
source/game/exefs.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "exefs.h"
|
||||||
|
|
||||||
|
u32 ValidateExeFsHeader(ExeFsHeader* exefs, u32 size) {
|
||||||
|
u32 data_size = 0;
|
||||||
|
for (u32 i = 0; i < 10; i++) {
|
||||||
|
ExeFsFileHeader* file = exefs->files + i;
|
||||||
|
if ((file->offset == 0) && (file->size == 0))
|
||||||
|
continue;
|
||||||
|
if (file->offset < data_size)
|
||||||
|
return 1; // overlapping data, failed
|
||||||
|
data_size = file->offset + file->size;
|
||||||
|
}
|
||||||
|
if (size && (data_size > (size - sizeof(ExeFsHeader)))) // exefs header not included in table
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
18
source/game/exefs.h
Normal file
18
source/game/exefs.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char name[8];
|
||||||
|
u32 offset;
|
||||||
|
u32 size;
|
||||||
|
} __attribute__((packed)) ExeFsFileHeader;
|
||||||
|
|
||||||
|
// see: https://www.3dbrew.org/wiki/ExeFS
|
||||||
|
typedef struct {
|
||||||
|
ExeFsFileHeader files[10];
|
||||||
|
u8 reserved[0x20];
|
||||||
|
u8 hashes[10][0x20];
|
||||||
|
} __attribute__((packed)) ExeFsHeader;
|
||||||
|
|
||||||
|
u32 ValidateExeFsHeader(ExeFsHeader* exefs, u32 size);
|
@ -4,3 +4,4 @@
|
|||||||
#include "cia.h"
|
#include "cia.h"
|
||||||
#include "ncsd.h"
|
#include "ncsd.h"
|
||||||
#include "ncch.h"
|
#include "ncch.h"
|
||||||
|
#include "exefs.h"
|
||||||
|
@ -4,10 +4,20 @@
|
|||||||
#include "aes.h"
|
#include "aes.h"
|
||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
|
|
||||||
#define VFLAG_EXTHDR (1<<29)
|
#define VFLAG_EXTHDR (1<<26)
|
||||||
|
#define VFLAG_CIA (1<<27) // unused, see below
|
||||||
|
#define VFLAG_NCSD (1<<28) // unused, see below
|
||||||
|
#define VFLAG_NCCH (1<<29)
|
||||||
#define VFLAG_EXEFS (1<<30)
|
#define VFLAG_EXEFS (1<<30)
|
||||||
#define VFLAG_ROMFS (1<<31)
|
#define VFLAG_ROMFS (1<<31)
|
||||||
|
|
||||||
|
#define VDIR_CIA VFLAG_CIA
|
||||||
|
#define VDIR_NCSD VFLAG_NCSD
|
||||||
|
#define VDIR_NCCH VFLAG_NCCH
|
||||||
|
#define VDIR_EXEFS VFLAG_EXEFS
|
||||||
|
#define VDIR_ROMFS VFLAG_ROMFS
|
||||||
|
#define VDIR_GAME (VDIR_CIA|VDIR_NCSD|VDIR_NCCH|VDIR_EXEFS|VDIR_ROMFS)
|
||||||
|
|
||||||
#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"
|
||||||
@ -17,6 +27,7 @@
|
|||||||
#define NAME_CIA_TMDCHUNK "tmdchunks.bin"
|
#define NAME_CIA_TMDCHUNK "tmdchunks.bin"
|
||||||
#define NAME_CIA_META "meta.bin"
|
#define NAME_CIA_META "meta.bin"
|
||||||
#define NAME_CIA_CONTENT "%04X.%08lX.app" // index.id.app
|
#define NAME_CIA_CONTENT "%04X.%08lX.app" // index.id.app
|
||||||
|
#define NAME_CIA_DIR "%04X.%08lX" // index.id
|
||||||
|
|
||||||
#define NAME_NCSD_HEADER "ncsd.bin"
|
#define NAME_NCSD_HEADER "ncsd.bin"
|
||||||
#define NAME_NCSD_CARDINFO "cardinfo.bin"
|
#define NAME_NCSD_CARDINFO "cardinfo.bin"
|
||||||
@ -24,6 +35,9 @@
|
|||||||
#define NAME_NCSD_CONTENT "cnt0.game.cxi", "cnt1.manual.cfa", "cnt2.dlp.cfa", \
|
#define NAME_NCSD_CONTENT "cnt0.game.cxi", "cnt1.manual.cfa", "cnt2.dlp.cfa", \
|
||||||
"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_NCSD_DIR "cnt0.game", "cnt1.manual", "cnt2.dlp", \
|
||||||
|
"cnt3", "cnt4", "cnt5", \
|
||||||
|
"cnt6.update_n3ds", "cnt7.update_o3ds"
|
||||||
|
|
||||||
#define NAME_NCCH_HEADER "ncch.bin"
|
#define NAME_NCCH_HEADER "ncch.bin"
|
||||||
#define NAME_NCCH_EXTHEADER "extheader.bin"
|
#define NAME_NCCH_EXTHEADER "extheader.bin"
|
||||||
@ -33,21 +47,254 @@
|
|||||||
#define NAME_NCCH_ROMFS "romfs.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 u32 base_vdir = 0;
|
||||||
static int n_templates = -1;
|
|
||||||
|
|
||||||
static CiaStub* cia = (CiaStub*) (VGAME_BUFFER + 0xF4000); // 48kB reserved - should be enough by far
|
static VirtualFile* templates_cia = (VirtualFile*) VGAME_BUFFER; // first 116kb reserved (enough for ~2000 entries)
|
||||||
static NcsdHeader* ncsd = (NcsdHeader*) (VGAME_BUFFER + 0xF3000); // needs only 512 byte
|
static VirtualFile* templates_ncsd = (VirtualFile*) VGAME_BUFFER + 0x1D000; // 4kb reserved (enough for ~80 entries)
|
||||||
static NcchHeader* ncch = (NcchHeader*) (VGAME_BUFFER + 0xF3200); // needs only 512 byte
|
static VirtualFile* templates_ncch = (VirtualFile*) VGAME_BUFFER + 0x1E000; // 4kb reserved (enough for ~80 entries)
|
||||||
static ExeFsHeader* exefs = (ExeFsHeader*) (VGAME_BUFFER + 0xF3400); // needs only 512 byte
|
static VirtualFile* templates_exefs = (VirtualFile*) VGAME_BUFFER + 0x1F000; // 4kb reserved (enough for ~80 entries)
|
||||||
|
static int n_templates_cia = -1;
|
||||||
|
static int n_templates_ncsd = -1;
|
||||||
|
static int n_templates_ncch = -1;
|
||||||
|
static int n_templates_exefs = -1;
|
||||||
|
|
||||||
static u32 offset_ncch = 0;
|
static u32 offset_ncch = 0;
|
||||||
static u32 offset_exefs = 0;
|
static u32 offset_exefs = 0;
|
||||||
static u32 offset_romfs = 0;
|
static u32 offset_romfs = 0;
|
||||||
|
|
||||||
|
static CiaStub* cia = (CiaStub*) (VGAME_BUFFER + 0xF4000); // 48kB reserved - should be enough by far
|
||||||
|
static NcsdHeader* ncsd = (NcsdHeader*) (VGAME_BUFFER + 0xF3000); // 512 byte reserved
|
||||||
|
static NcchHeader* ncch = (NcchHeader*) (VGAME_BUFFER + 0xF3200); // 512 byte reserved
|
||||||
|
static ExeFsHeader* exefs = (ExeFsHeader*) (VGAME_BUFFER + 0xF3400); // 512 byte reserved
|
||||||
|
|
||||||
|
bool BuildVGameNcchDir(void) {
|
||||||
|
VirtualFile* templates = templates_ncch;
|
||||||
|
u32 n = 0;
|
||||||
|
|
||||||
|
// header
|
||||||
|
strncpy(templates[n].name, NAME_NCCH_HEADER, 32);
|
||||||
|
templates[n].offset = offset_ncch + 0;
|
||||||
|
templates[n].size = 0x200;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
// extended header
|
||||||
|
if (ncch->size_exthdr) {
|
||||||
|
strncpy(templates[n].name, NAME_NCCH_EXTHEADER, 32);
|
||||||
|
templates[n].offset = offset_ncch + NCCH_EXTHDR_OFFSET;
|
||||||
|
templates[n].size = NCCH_EXTHDR_SIZE;
|
||||||
|
templates[n].keyslot = 0xFF; // crypto ?
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// plain region
|
||||||
|
if (ncch->size_plain) {
|
||||||
|
strncpy(templates[n].name, NAME_NCCH_PLAIN, 32);
|
||||||
|
templates[n].offset = offset_ncch + (ncch->offset_plain * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n].size = ncch->size_plain * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// logo region
|
||||||
|
if (ncch->size_logo) {
|
||||||
|
strncpy(templates[n].name, NAME_NCCH_LOGO, 32);
|
||||||
|
templates[n].offset =offset_ncch + (ncch->offset_logo * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n].size = ncch->size_logo * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exefs
|
||||||
|
if (ncch->size_exefs) {
|
||||||
|
strncpy(templates[n].name, NAME_NCCH_EXEFS, 32);
|
||||||
|
templates[n].offset = offset_ncch + (ncch->offset_exefs * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n].size = ncch->size_exefs * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// romfs
|
||||||
|
if (ncch->size_romfs) {
|
||||||
|
strncpy(templates[n].name, NAME_NCCH_ROMFS, 32);
|
||||||
|
templates[n].offset = offset_ncch + (ncch->offset_romfs * NCCH_MEDIA_UNIT);
|
||||||
|
templates[n].size = ncch->size_romfs * NCCH_MEDIA_UNIT;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_templates_ncch = n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildVGameNcsdDir(void) {
|
||||||
|
const char* name_content[] = { NAME_NCSD_CONTENT };
|
||||||
|
const char* name_dir[] = { NAME_NCSD_DIR };
|
||||||
|
VirtualFile* templates = templates_ncsd;
|
||||||
|
u32 n = 0;
|
||||||
|
|
||||||
|
// header
|
||||||
|
strncpy(templates[n].name, NAME_NCSD_HEADER, 32);
|
||||||
|
templates[n].offset = 0;
|
||||||
|
templates[n].size = 0x200;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
// card info header
|
||||||
|
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_CINFO_OFFSET + NCSD_CINFO_SIZE) {
|
||||||
|
strncpy(templates[n].name, NAME_NCSD_CARDINFO, 32);
|
||||||
|
templates[n].offset = NCSD_CINFO_OFFSET;
|
||||||
|
templates[n].size = NCSD_CINFO_SIZE;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dev info header
|
||||||
|
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_DINFO_OFFSET + NCSD_DINFO_SIZE) {
|
||||||
|
strncpy(templates[n].name, NAME_NCSD_DEVINFO, 32);
|
||||||
|
templates[n].offset = NCSD_DINFO_OFFSET;
|
||||||
|
templates[n].size = NCSD_DINFO_SIZE;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contents
|
||||||
|
for (u32 i = 0; i < 8; i++) {
|
||||||
|
NcchPartition* partition = ncsd->partitions + i;
|
||||||
|
if ((partition->offset == 0) && (partition->size == 0))
|
||||||
|
continue;
|
||||||
|
strncpy(templates[n].name, name_content[i], 32);
|
||||||
|
templates[n].offset = partition->offset * NCSD_MEDIA_UNIT;
|
||||||
|
templates[n].size = partition->size * NCSD_MEDIA_UNIT;
|
||||||
|
templates[n].keyslot = 0xFF; // not encrypted
|
||||||
|
templates[n].flags = VFLAG_NCCH;
|
||||||
|
n++;
|
||||||
|
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
|
||||||
|
strncpy(templates[n].name, name_dir[i], 32);
|
||||||
|
templates[n].flags |= VFLAG_DIR;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_templates_ncsd = n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#include "ui.h"
|
||||||
|
bool BuildVGameCiaDir(void) {
|
||||||
|
CiaInfo info;
|
||||||
|
VirtualFile* templates = templates_cia;
|
||||||
|
u32 n = 0;
|
||||||
|
|
||||||
|
if (GetCiaInfo(&info, &(cia->header)) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// header
|
||||||
|
strncpy(templates[n].name, NAME_CIA_HEADER, 32);
|
||||||
|
templates[n].offset = 0;
|
||||||
|
templates[n].size = info.size_header;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
// certificates
|
||||||
|
if (info.size_cert) {
|
||||||
|
strncpy(templates[n].name, NAME_CIA_CERT, 32);
|
||||||
|
templates[n].offset = info.offset_cert;
|
||||||
|
templates[n].size = info.size_cert;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ticket
|
||||||
|
if (info.size_ticket) {
|
||||||
|
strncpy(templates[n].name, NAME_CIA_TICKET, 32);
|
||||||
|
templates[n].offset = info.offset_ticket;
|
||||||
|
templates[n].size = info.size_ticket;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMD (the full thing)
|
||||||
|
if (info.size_tmd) {
|
||||||
|
strncpy(templates[n].name, NAME_CIA_TMD, 32);
|
||||||
|
templates[n].offset = info.offset_tmd;
|
||||||
|
templates[n].size = info.size_tmd;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMD content chunks
|
||||||
|
if (info.size_content_list) {
|
||||||
|
strncpy(templates[n].name, NAME_CIA_TMDCHUNK, 32);
|
||||||
|
templates[n].offset = info.offset_content_list;
|
||||||
|
templates[n].size = info.size_content_list;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// meta
|
||||||
|
if (info.size_meta) {
|
||||||
|
strncpy(templates[n].name, NAME_CIA_META, 32);
|
||||||
|
templates[n].offset = info.offset_meta;
|
||||||
|
templates[n].size = info.size_meta;
|
||||||
|
templates[n].keyslot = 0xFF;
|
||||||
|
templates[n].flags = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contents
|
||||||
|
if (info.size_content) {
|
||||||
|
TmdContentChunk* content_list = cia->content_list;
|
||||||
|
u32 content_count = getbe16(cia->tmd.content_count);
|
||||||
|
u64 next_offset = info.offset_content;
|
||||||
|
for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) {
|
||||||
|
u64 size = getbe64(content_list[i].size);
|
||||||
|
bool is_ncch = false; // is unencrypted NCCH?
|
||||||
|
if (!(getbe16(content_list[i].type) & 0x1)) {
|
||||||
|
NcchHeader ncch;
|
||||||
|
ReadImageBytes((u8*) &ncch, next_offset, sizeof(NcchHeader));
|
||||||
|
is_ncch = (ValidateNcchHeader(&ncch) == 0);
|
||||||
|
}
|
||||||
|
snprintf(templates[n].name, 32, NAME_CIA_CONTENT,
|
||||||
|
getbe16(content_list[i].index), getbe32(content_list[i].id));
|
||||||
|
templates[n].offset = next_offset;
|
||||||
|
templates[n].size = size;
|
||||||
|
templates[n].keyslot = 0xFF; // even for encrypted stuff
|
||||||
|
templates[n].flags = is_ncch ? VFLAG_NCCH : 0;
|
||||||
|
n++;
|
||||||
|
if (is_ncch) {
|
||||||
|
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
|
||||||
|
snprintf(templates[n].name, 32, NAME_CIA_DIR,
|
||||||
|
getbe16(content_list[i].index), getbe32(content_list[i].id));
|
||||||
|
templates[n].flags |= VFLAG_DIR;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
next_offset += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n_templates_cia = n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
||||||
u32 type = GetMountState();
|
u32 type = GetMountState();
|
||||||
vgame_type = 0;
|
vgame_type = 0;
|
||||||
|
offset_ncch = 0;
|
||||||
|
offset_exefs = 0;
|
||||||
|
offset_romfs = 0;
|
||||||
if (type == GAME_CIA) { // for CIAs: load the CIA stub and keep it in memory
|
if (type == GAME_CIA) { // for CIAs: load the CIA stub and keep it in memory
|
||||||
CiaInfo info;
|
CiaInfo info;
|
||||||
if ((ReadImageBytes((u8*) cia, 0, 0x20) != 0) ||
|
if ((ReadImageBytes((u8*) cia, 0, 0x20) != 0) ||
|
||||||
@ -55,15 +302,23 @@ u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
|||||||
(GetCiaInfo(&info, &(cia->header)) != 0) ||
|
(GetCiaInfo(&info, &(cia->header)) != 0) ||
|
||||||
(ReadImageBytes((u8*) cia, 0, info.offset_content) != 0))
|
(ReadImageBytes((u8*) cia, 0, info.offset_content) != 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (!BuildVGameCiaDir()) return 0;
|
||||||
|
base_vdir = VDIR_CIA;
|
||||||
} else if (type == GAME_NCSD) {
|
} else if (type == GAME_NCSD) {
|
||||||
if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) ||
|
if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) ||
|
||||||
(ValidateNcsdHeader(ncsd) != 0))
|
(ValidateNcsdHeader(ncsd) != 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (!BuildVGameNcsdDir()) return 0;
|
||||||
|
base_vdir = VDIR_NCSD;
|
||||||
} else if (type == GAME_NCCH) {
|
} else if (type == GAME_NCCH) {
|
||||||
offset_ncch = 0;
|
|
||||||
if ((ReadImageBytes((u8*) ncch, 0, sizeof(NcchHeader)) != 0) ||
|
if ((ReadImageBytes((u8*) ncch, 0, sizeof(NcchHeader)) != 0) ||
|
||||||
(ValidateNcchHeader(ncch) != 0))
|
(ValidateNcchHeader(ncch) != 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (!BuildVGameNcchDir()) return 0;
|
||||||
|
base_vdir = VDIR_NCCH;
|
||||||
|
offset_ncch = 0;
|
||||||
|
offset_exefs = ncch->offset_exefs * NCCH_MEDIA_UNIT;
|
||||||
|
offset_romfs = ncch->offset_romfs * NCCH_MEDIA_UNIT;
|
||||||
} else return 0; // not a mounted game file
|
} else return 0; // not a mounted game file
|
||||||
|
|
||||||
vgame_type = type;
|
vgame_type = type;
|
||||||
@ -75,228 +330,61 @@ u32 CheckVGameDrive(void) {
|
|||||||
return vgame_type;
|
return vgame_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildVGameNcchVDir(void) {
|
bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||||
if (CheckVGameDrive() != GAME_NCCH)
|
vdir->index = -1;
|
||||||
return false; // safety check
|
vdir->virtual_src = VRT_GAME;
|
||||||
|
if (!ventry) { // root dir
|
||||||
// header
|
vdir->offset = 0;
|
||||||
strncpy(templates[n_templates].name, NAME_NCCH_HEADER, 32);
|
vdir->size = GetMountSize();
|
||||||
templates[n_templates].offset = offset_ncch + 0;
|
vdir->flags = VFLAG_DIR|base_vdir;
|
||||||
templates[n_templates].size = 0x200;
|
// base dir is already in memory -> done
|
||||||
templates[n_templates].keyslot = 0xFF;
|
} else { // non root dir
|
||||||
templates[n_templates].flags = 0;
|
if (!(ventry->flags & VFLAG_DIR) || !(ventry->flags & VDIR_GAME))
|
||||||
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) {
|
|
||||||
const char* name_content[] = { NAME_NCSD_CONTENT };
|
|
||||||
|
|
||||||
if (CheckVGameDrive() != GAME_NCSD)
|
|
||||||
return false; // safety check
|
|
||||||
|
|
||||||
// header
|
|
||||||
strncpy(templates[n_templates].name, NAME_NCSD_HEADER, 32);
|
|
||||||
templates[n_templates].offset = 0;
|
|
||||||
templates[n_templates].size = 0x200;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
|
|
||||||
// card info header
|
|
||||||
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_CINFO_OFFSET + NCSD_CINFO_SIZE) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_NCSD_CARDINFO, 32);
|
|
||||||
templates[n_templates].offset = NCSD_CINFO_OFFSET;
|
|
||||||
templates[n_templates].size = NCSD_CINFO_SIZE;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// dev info header
|
|
||||||
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_DINFO_OFFSET + NCSD_DINFO_SIZE) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_NCSD_DEVINFO, 32);
|
|
||||||
templates[n_templates].offset = NCSD_DINFO_OFFSET;
|
|
||||||
templates[n_templates].size = NCSD_DINFO_SIZE;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// contents
|
|
||||||
for (u32 i = 0; i < 8; i++) {
|
|
||||||
NcchPartition* partition = ncsd->partitions + i;
|
|
||||||
if ((partition->offset == 0) && (partition->size == 0))
|
|
||||||
continue;
|
|
||||||
strncpy(templates[n_templates].name, name_content[i], 32);
|
|
||||||
templates[n_templates].offset = partition->offset * NCSD_MEDIA_UNIT;
|
|
||||||
templates[n_templates].size = partition->size * NCSD_MEDIA_UNIT;
|
|
||||||
templates[n_templates].keyslot = 0xFF; // even for encrypted stuff
|
|
||||||
templates[n_templates].flags = 0; // this handles encryption
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuildVGameCiaVDir(void) {
|
|
||||||
CiaInfo info;
|
|
||||||
|
|
||||||
if ((CheckVGameDrive() != GAME_CIA) || (GetCiaInfo(&info, &(cia->header)) != 0))
|
|
||||||
return false; // safety check
|
|
||||||
|
|
||||||
// header
|
|
||||||
strncpy(templates[n_templates].name, NAME_CIA_HEADER, 32);
|
|
||||||
templates[n_templates].offset = 0;
|
|
||||||
templates[n_templates].size = info.size_header;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
|
|
||||||
// certificates
|
|
||||||
if (info.size_cert) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_CIA_CERT, 32);
|
|
||||||
templates[n_templates].offset = info.offset_cert;
|
|
||||||
templates[n_templates].size = info.size_cert;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ticket
|
|
||||||
if (info.size_ticket) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_CIA_TICKET, 32);
|
|
||||||
templates[n_templates].offset = info.offset_ticket;
|
|
||||||
templates[n_templates].size = info.size_ticket;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TMD (the full thing)
|
|
||||||
if (info.size_tmd) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_CIA_TMD, 32);
|
|
||||||
templates[n_templates].offset = info.offset_tmd;
|
|
||||||
templates[n_templates].size = info.size_tmd;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TMD content chunks
|
|
||||||
if (info.size_content_list) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_CIA_TMDCHUNK, 32);
|
|
||||||
templates[n_templates].offset = info.offset_content_list;
|
|
||||||
templates[n_templates].size = info.size_content_list;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// meta
|
|
||||||
if (info.size_meta) {
|
|
||||||
strncpy(templates[n_templates].name, NAME_CIA_META, 32);
|
|
||||||
templates[n_templates].offset = info.offset_meta;
|
|
||||||
templates[n_templates].size = info.size_meta;
|
|
||||||
templates[n_templates].keyslot = 0xFF;
|
|
||||||
templates[n_templates].flags = 0;
|
|
||||||
n_templates++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// contents
|
|
||||||
if (info.size_content) {
|
|
||||||
TmdContentChunk* content_list = cia->content_list;
|
|
||||||
u32 content_count = getbe16(cia->tmd.content_count);
|
|
||||||
u64 next_offset = info.offset_content;
|
|
||||||
for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) {
|
|
||||||
u64 size = getbe64(content_list[i].size);
|
|
||||||
// bool encrypted = getbe16(content_list[i].type) & 0x1;
|
|
||||||
snprintf(templates[n_templates].name, 32, NAME_CIA_CONTENT,
|
|
||||||
getbe16(content_list[i].index), getbe32(content_list[i].id));
|
|
||||||
templates[n_templates].offset = (u32) next_offset;
|
|
||||||
templates[n_templates].size = (u32) size;
|
|
||||||
templates[n_templates].keyslot = 0xFF; // even for encrypted stuff
|
|
||||||
templates[n_templates].flags = 0; // this handles encryption
|
|
||||||
n_templates++;
|
|
||||||
next_offset += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
|
||||||
|
|
||||||
(void) path; // not in use yet
|
|
||||||
static int num = -1;
|
|
||||||
|
|
||||||
if (!vfile) { // NULL pointer
|
|
||||||
num = -1; // reset dir reader / internal number
|
|
||||||
memset(templates, 0, sizeof(VirtualFile) * MAX_N_TEMPLATES);
|
|
||||||
n_templates = 0;
|
|
||||||
if ((vgame_type == GAME_CIA) && BuildVGameCiaVDir()) // for CIA
|
|
||||||
return true;
|
|
||||||
else if ((vgame_type == GAME_NCSD) && BuildVGameNcsdVDir()) // for NCSD
|
|
||||||
return true;
|
|
||||||
else if ((vgame_type == GAME_NCCH) && BuildVGameNcchVDir()) // for NCSD
|
|
||||||
return true;
|
|
||||||
return false;
|
return false;
|
||||||
|
vdir->offset = ventry->offset;
|
||||||
|
vdir->size = ventry->size;
|
||||||
|
vdir->flags = ventry->flags;
|
||||||
|
// build directories where required
|
||||||
|
u32 curr_vdir = vdir->flags & VDIR_GAME;
|
||||||
|
if ((curr_vdir == VDIR_NCCH) && (offset_ncch != vdir->offset)) {
|
||||||
|
if ((ReadImageBytes((u8*) ncch, vdir->offset, sizeof(NcchHeader)) != 0) ||
|
||||||
|
(ValidateNcchHeader(ncch) != 0))
|
||||||
|
return false;
|
||||||
|
offset_ncch = vdir->offset;
|
||||||
|
if (!BuildVGameNcchDir()) return false;
|
||||||
|
} else if ((curr_vdir == VDIR_EXEFS) && (offset_exefs != vdir->offset)) {
|
||||||
|
if ((ReadImageBytes((u8*) exefs, vdir->offset, sizeof(ExeFsHeader)) != 0) ||
|
||||||
|
(ValidateExeFsHeader(exefs, ncch->size_exefs * NCCH_MEDIA_UNIT) != 0))
|
||||||
|
return false;
|
||||||
|
offset_exefs = vdir->offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++num < n_templates) {
|
return true; // error (should not happen)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||||
|
u32 curr_vdir = vdir->flags & VDIR_GAME;
|
||||||
|
VirtualFile* templates;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if (curr_vdir == VDIR_CIA) {
|
||||||
|
templates = templates_cia;
|
||||||
|
n = n_templates_cia;
|
||||||
|
} else if (curr_vdir == VDIR_NCSD) {
|
||||||
|
templates = templates_ncsd;
|
||||||
|
n = n_templates_ncsd;
|
||||||
|
} else if (curr_vdir == VDIR_NCCH) {
|
||||||
|
templates = templates_ncch;
|
||||||
|
n = n_templates_ncch;
|
||||||
|
} else if (curr_vdir == VDIR_EXEFS) {
|
||||||
|
templates = templates_exefs;
|
||||||
|
n = n_templates_exefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++vdir->index < n) {
|
||||||
// copy current template to vfile
|
// copy current template to vfile
|
||||||
memcpy(vfile, templates + num, sizeof(VirtualFile));
|
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
u32 InitVGameDrive(void);
|
u32 InitVGameDrive(void);
|
||||||
u32 CheckVGameDrive(void);
|
u32 CheckVGameDrive(void);
|
||||||
|
|
||||||
bool ReadVGameDir(VirtualFile* vfile, const char* path);
|
bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry);
|
||||||
|
bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir);
|
||||||
int ReadVGameFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
int ReadVGameFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
||||||
// int WriteVGameFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count); // writing is not enabled
|
// int WriteVGameFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count); // writing is not enabled
|
||||||
|
@ -30,52 +30,91 @@ bool CheckVirtualDrive(const char* path) {
|
|||||||
return virtual_src; // this is safe for SysNAND & memory
|
return virtual_src; // this is safe for SysNAND & memory
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadVirtualDir(VirtualFile* vfile, u32 virtual_src) {
|
bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||||
|
u32 virtual_src = vdir->virtual_src;
|
||||||
|
bool ret = false;
|
||||||
if (virtual_src & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND)) {
|
if (virtual_src & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND)) {
|
||||||
return ReadVNandDir(vfile, virtual_src);
|
ret = ReadVNandDir(vfile, vdir);
|
||||||
} else if (virtual_src & VRT_MEMORY) {
|
} else if (virtual_src & VRT_MEMORY) {
|
||||||
return ReadVMemDir(vfile);
|
ret = ReadVMemDir(vfile, vdir);
|
||||||
} else if (virtual_src & VRT_MEMORY) {
|
|
||||||
return ReadVMemDir(vfile);
|
|
||||||
} else if (virtual_src & VRT_GAME) {
|
} else if (virtual_src & VRT_GAME) {
|
||||||
return ReadVGameDir(vfile, NULL);
|
ret = ReadVGameDir(vfile, vdir);
|
||||||
}
|
}
|
||||||
return false;
|
vfile->flags |= virtual_src; // add source flag
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenVirtualRoot(VirtualDir* vdir, u32 virtual_src) {
|
||||||
|
if (virtual_src & VRT_GAME) {
|
||||||
|
if (!OpenVGameDir(vdir, NULL)) return false;
|
||||||
|
} else { // generic vdir object
|
||||||
|
vdir->offset = 0;
|
||||||
|
vdir->size = 0;
|
||||||
|
vdir->flags = 0;
|
||||||
|
}
|
||||||
|
vdir->index = -1;
|
||||||
|
vdir->flags |= VFLAG_DIR|virtual_src;
|
||||||
|
vdir->virtual_src = virtual_src;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenVirtualDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||||
|
u32 virtual_src = ventry->flags & VRT_SOURCE;
|
||||||
|
if (ventry->flags & VFLAG_ROOT)
|
||||||
|
return OpenVirtualRoot(vdir, virtual_src);
|
||||||
|
if (!(virtual_src & VRT_GAME)) return false; // no subdirs in other virtual sources
|
||||||
|
if (!OpenVGameDir(vdir, ventry)) return false;
|
||||||
|
vdir->flags |= virtual_src;
|
||||||
|
vdir->virtual_src = virtual_src;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetVirtualFile(VirtualFile* vfile, const char* path) {
|
bool GetVirtualFile(VirtualFile* vfile, const char* path) {
|
||||||
// get / fix the name
|
char lpath[256];
|
||||||
char* fname = strchr(path, '/');
|
strncpy(lpath, path, 256);
|
||||||
if (!fname) return false;
|
|
||||||
fname++;
|
|
||||||
|
|
||||||
// check path validity / get virtual source
|
// get virtual source / root dir object
|
||||||
u32 virtual_src = 0;
|
u32 virtual_src = 0;
|
||||||
virtual_src = GetVirtualSource(path);
|
virtual_src = GetVirtualSource(path);
|
||||||
if (!virtual_src || (fname - path != 3))
|
if (!virtual_src) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// read virtual dir, match the path / size
|
// set vfile as root object
|
||||||
ReadVirtualDir(NULL, virtual_src); // reset dir reader
|
memset(vfile, 0, sizeof(VirtualDir));
|
||||||
while (ReadVirtualDir(vfile, virtual_src)) {
|
vfile->flags = VFLAG_ROOT|virtual_src;
|
||||||
vfile->flags |= virtual_src; // add source flag
|
if (strnlen(lpath, 256) <= 3) return true;
|
||||||
if (strncasecmp(fname, vfile->name, 32) == 0)
|
|
||||||
return true; // file found
|
// tokenize / parse path
|
||||||
|
char* name;
|
||||||
|
VirtualDir vdir;
|
||||||
|
if (!OpenVirtualRoot(&vdir, virtual_src)) return false;
|
||||||
|
for (name = strtok(lpath + 3, "/"); name && vdir.virtual_src; name = strtok(NULL, "/")) {
|
||||||
|
while (true) {
|
||||||
|
if (!ReadVirtualDir(vfile, &vdir)) return false;
|
||||||
|
if (strncasecmp(name, vfile->name, 32) == 0)
|
||||||
|
break; // entry found
|
||||||
|
}
|
||||||
|
if (!OpenVirtualDir(&vdir, vfile))
|
||||||
|
vdir.virtual_src = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// failed if arriving here
|
return (name == NULL); // if name is NULL, this succeeded
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetVirtualDir(VirtualDir* vdir, const char* path) {
|
||||||
|
VirtualFile vfile;
|
||||||
|
return GetVirtualFile(&vfile, path) && OpenVirtualDir(vdir, &vfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hacky solution, actually ignores path
|
||||||
bool FindVirtualFileBySize(VirtualFile* vfile, const char* path, u32 size) {
|
bool FindVirtualFileBySize(VirtualFile* vfile, const char* path, u32 size) {
|
||||||
// get virtual source
|
// get virtual source
|
||||||
u32 virtual_src = 0;
|
u32 virtual_src = 0;
|
||||||
virtual_src = GetVirtualSource(path);
|
virtual_src = GetVirtualSource(path);
|
||||||
if (!virtual_src) return false;
|
if (!virtual_src) return false;
|
||||||
|
|
||||||
// read virtual dir, match the path / size
|
VirtualDir vdir; // read virtual root dir, match size
|
||||||
ReadVirtualDir(NULL, virtual_src); // reset dir reader
|
OpenVirtualRoot(&vdir, virtual_src); // get dir reader object
|
||||||
while (ReadVirtualDir(vfile, virtual_src)) {
|
while (ReadVirtualDir(vfile, &vdir)) {
|
||||||
vfile->flags |= virtual_src; // add source flag
|
vfile->flags |= virtual_src; // add source flag
|
||||||
if (vfile->size == size) // search by size should be a last resort solution
|
if (vfile->size == size) // search by size should be a last resort solution
|
||||||
return true; // file found
|
return true; // file found
|
||||||
@ -88,17 +127,18 @@ bool FindVirtualFileBySize(VirtualFile* vfile, const char* path, u32 size) {
|
|||||||
bool GetVirtualDirContents(DirStruct* contents, const char* path, const char* pattern) {
|
bool GetVirtualDirContents(DirStruct* contents, const char* path, const char* pattern) {
|
||||||
u32 virtual_src = GetVirtualSource(path);
|
u32 virtual_src = GetVirtualSource(path);
|
||||||
if (!virtual_src) return false; // not a virtual path
|
if (!virtual_src) return false; // not a virtual path
|
||||||
if (strchr(path, '/')) return false; // only top level paths
|
|
||||||
|
|
||||||
|
VirtualDir vdir;
|
||||||
VirtualFile vfile;
|
VirtualFile vfile;
|
||||||
ReadVirtualDir(NULL, virtual_src); // reset dir reader
|
if (!GetVirtualDir(&vdir, path))
|
||||||
while ((contents->n_entries < MAX_DIR_ENTRIES) && (ReadVirtualDir(&vfile, virtual_src))) {
|
return false; // get dir reader object
|
||||||
|
while ((contents->n_entries < MAX_DIR_ENTRIES) && (ReadVirtualDir(&vfile, &vdir))) {
|
||||||
DirEntry* entry = &(contents->entry[contents->n_entries]);
|
DirEntry* entry = &(contents->entry[contents->n_entries]);
|
||||||
if (pattern && !MatchName(pattern, vfile.name)) continue;
|
if (pattern && !MatchName(pattern, vfile.name)) continue;
|
||||||
snprintf(entry->path, 256, "%s/%s", path, vfile.name);
|
snprintf(entry->path, 256, "%s/%s", path, vfile.name);
|
||||||
entry->name = entry->path + strnlen(path, 256) + 1;
|
entry->name = entry->path + strnlen(path, 256) + 1;
|
||||||
entry->size = vfile.size;
|
entry->size = vfile.size;
|
||||||
entry->type = T_FILE;
|
entry->type = (vfile.flags & VFLAG_DIR) ? T_DIR : T_FILE;
|
||||||
entry->marked = 0;
|
entry->marked = 0;
|
||||||
contents->n_entries++;
|
contents->n_entries++;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,11 @@
|
|||||||
#define VRT_MEMORY (1<<10)
|
#define VRT_MEMORY (1<<10)
|
||||||
#define VRT_GAME (1<<11)
|
#define VRT_GAME (1<<11)
|
||||||
|
|
||||||
|
#define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_MEMORY|VRT_GAME)
|
||||||
|
|
||||||
#define VFLAG_A9LH_AREA (1<<20)
|
#define VFLAG_A9LH_AREA (1<<20)
|
||||||
|
#define VFLAG_DIR (1<<21)
|
||||||
|
#define VFLAG_ROOT (1<<22)
|
||||||
|
|
||||||
// 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
|
||||||
@ -19,12 +23,21 @@
|
|||||||
// bits 20...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)
|
u64 offset; // must be a multiple of 0x200 (for NAND access)
|
||||||
u32 size;
|
u64 size;
|
||||||
u32 keyslot;
|
u32 keyslot;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
} __attribute__((packed)) VirtualFile;
|
} __attribute__((packed)) VirtualFile;
|
||||||
|
|
||||||
|
// virtual dirs are only relevant for virtual game drives
|
||||||
|
typedef struct {
|
||||||
|
int index;
|
||||||
|
u64 offset;
|
||||||
|
u64 size;
|
||||||
|
u32 flags;
|
||||||
|
u32 virtual_src;
|
||||||
|
} __attribute__((packed)) VirtualDir;
|
||||||
|
|
||||||
u32 GetVirtualSource(const char* path);
|
u32 GetVirtualSource(const char* path);
|
||||||
bool CheckVirtualDrive(const char* path);
|
bool CheckVirtualDrive(const char* path);
|
||||||
bool GetVirtualFile(VirtualFile* vfile, const char* path);
|
bool GetVirtualFile(VirtualFile* vfile, const char* path);
|
||||||
|
@ -19,19 +19,13 @@ static const VirtualFile vMemFileTemplates[] = {
|
|||||||
{ "bootrom_unp.mem" , 0xFFFF0000, 0x00008000, 0xFF, 0 }
|
{ "bootrom_unp.mem" , 0xFFFF0000, 0x00008000, 0xFF, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ReadVMemDir(VirtualFile* vfile) {
|
bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c
|
||||||
static int num = -1;
|
|
||||||
int n_templates = sizeof(vMemFileTemplates) / sizeof(VirtualFile);
|
int n_templates = sizeof(vMemFileTemplates) / sizeof(VirtualFile);
|
||||||
const VirtualFile* templates = vMemFileTemplates;
|
const VirtualFile* templates = vMemFileTemplates;
|
||||||
|
|
||||||
if (!vfile) { // NULL pointer -> reset dir reader / internal number
|
while (++vdir->index < n_templates) {
|
||||||
num = -1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (++num < n_templates) {
|
|
||||||
// copy current template to vfile
|
// copy current template to vfile
|
||||||
memcpy(vfile, templates + num, sizeof(VirtualFile));
|
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile));
|
||||||
|
|
||||||
// process special flag
|
// process special flag
|
||||||
if ((vfile->flags & VFLAG_N3DS_ONLY) && (GetUnitPlatform() != PLATFORM_N3DS))
|
if ((vfile->flags & VFLAG_N3DS_ONLY) && (GetUnitPlatform() != PLATFORM_N3DS))
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "virtual.h"
|
#include "virtual.h"
|
||||||
|
|
||||||
bool ReadVMemDir(VirtualFile* vfile);
|
bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir);
|
||||||
int ReadVMemFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
int ReadVMemFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
||||||
int WriteVMemFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count);
|
int WriteVMemFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count);
|
||||||
|
@ -34,23 +34,18 @@ bool CheckVNandDrive(u32 nand_src) {
|
|||||||
return GetNandSizeSectors(nand_src);
|
return GetNandSizeSectors(nand_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadVNandDir(VirtualFile* vfile, u32 nand_src) {
|
bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c
|
||||||
static int num = -1;
|
|
||||||
int n_templates = sizeof(vNandFileTemplates) / sizeof(VirtualFile);
|
int n_templates = sizeof(vNandFileTemplates) / sizeof(VirtualFile);
|
||||||
const VirtualFile* templates = vNandFileTemplates;
|
const VirtualFile* templates = vNandFileTemplates;
|
||||||
|
u32 nand_src = vdir->virtual_src;
|
||||||
|
|
||||||
if (!vfile) { // NULL pointer -> reset dir reader / internal number
|
while (++vdir->index < n_templates) {
|
||||||
num = -1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (++num < n_templates) {
|
|
||||||
// get NAND type (O3DS/N3DS/NO3DS), workaround for empty EmuNAND
|
// get NAND type (O3DS/N3DS/NO3DS), workaround for empty EmuNAND
|
||||||
u32 nand_type = CheckNandType(nand_src);
|
u32 nand_type = CheckNandType(nand_src);
|
||||||
if (!nand_type) nand_type = (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_N3DS;
|
if (!nand_type) nand_type = (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_N3DS;
|
||||||
|
|
||||||
// copy current template to vfile
|
// copy current template to vfile
|
||||||
memcpy(vfile, templates + num, sizeof(VirtualFile));
|
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile));
|
||||||
|
|
||||||
// process / check special flags
|
// process / check special flags
|
||||||
if (!(vfile->flags & nand_type))
|
if (!(vfile->flags & nand_type))
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
#include "virtual.h"
|
#include "virtual.h"
|
||||||
|
|
||||||
bool CheckVNandDrive(u32 nand_src);
|
bool CheckVNandDrive(u32 nand_src);
|
||||||
bool ReadVNandDir(VirtualFile* vfile, u32 nand_src);
|
bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir);
|
||||||
int ReadVNandFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
int ReadVNandFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
||||||
int WriteVNandFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count);
|
int WriteVNandFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user