mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Enable mounting of NCSD files
This commit is contained in:
parent
2c5a46522d
commit
622947f63a
@ -38,7 +38,7 @@
|
||||
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
||||
|
||||
// GodMode9 version
|
||||
#define VERSION "0.7.6"
|
||||
#define VERSION "0.7.7"
|
||||
|
||||
// buffer area defines (in use by godmode.c)
|
||||
#define DIR_BUFFER (0x21000000)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "filetype.h"
|
||||
#include "cia.h"
|
||||
#include "game.h"
|
||||
#include "ff.h"
|
||||
|
||||
u32 IdentifyFileType(const char* path) {
|
||||
@ -36,7 +36,10 @@ u32 IdentifyFileType(const char* path) {
|
||||
GetCiaInfo(&info, (CiaHeader*) header);
|
||||
if (fsize >= info.size_cia)
|
||||
return GAME_CIA; // CIA file
|
||||
} // more to come
|
||||
} else if (ValidateNcsdHeader((NcsdHeader*) header) == 0) {
|
||||
if (fsize >= (((NcsdHeader*) header)->size * NCSD_MEDIA_UNIT))
|
||||
return GAME_NCSD; // NCSD (".3DS") file
|
||||
} // NCCH still missing
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,3 +2,4 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "cia.h"
|
||||
#include "ncsd.h"
|
||||
|
22
source/game/ncsd.c
Normal file
22
source/game/ncsd.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include "ncsd.h"
|
||||
|
||||
u32 ValidateNcsdHeader(NcsdHeader* header) {
|
||||
u8 zeroes[16] = { 0 };
|
||||
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
|
||||
(memcmp(header->partitions_fs_type, zeroes, 8) != 0)) // prevent detection of NAND images
|
||||
return 1;
|
||||
|
||||
u32 data_units = 0;
|
||||
for (u32 i = 0; i < 8; i++) {
|
||||
NcchPartition* partition = header->partitions + i;
|
||||
if ((partition->offset == 0) && (partition->size == 0))
|
||||
continue;
|
||||
if (partition->offset < data_units)
|
||||
return 1; // overlapping partitions, failed
|
||||
data_units = partition->offset + partition->size;
|
||||
}
|
||||
if (data_units > header->size)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
30
source/game/ncsd.h
Normal file
30
source/game/ncsd.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define NCSD_MEDIA_UNIT 0x200
|
||||
|
||||
// see: https://www.3dbrew.org/wiki/NCSD
|
||||
typedef struct {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
} __attribute__((packed)) NcchPartition;
|
||||
|
||||
// see: https://www.3dbrew.org/wiki/NCSD
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u8 magic[4];
|
||||
u32 size;
|
||||
u64 mediaId;
|
||||
u8 partitions_fs_type[8];
|
||||
u8 partitions_crypto_type[8];
|
||||
NcchPartition partitions[8];
|
||||
u8 hash_exthdr[0x20];
|
||||
u8 size_addhdr[0x4];
|
||||
u8 sector_zero_offset[0x4];
|
||||
u8 partition_flags[8];
|
||||
u8 partitionId_table[8][8];
|
||||
u8 reserved[0x40];
|
||||
} __attribute__((packed, aligned(16))) NcsdHeader;
|
||||
|
||||
u32 ValidateNcsdHeader(NcsdHeader* header);
|
@ -639,7 +639,8 @@ u32 GodMode() {
|
||||
if (mountable) optionstr[mountable-1] =
|
||||
(file_type == IMG_NAND) ? "Mount as NAND image" :
|
||||
(file_type == IMG_FAT) ? "Mount as FAT image" :
|
||||
(file_type == GAME_CIA) ? "Mount as CIA image" : ""; // !!! NCCH / NCSD
|
||||
(file_type == GAME_CIA) ? "Mount as CIA image" :
|
||||
(file_type == GAME_NCSD) ? "Mount as NCSD image" : ""; // !!! NCCH
|
||||
if (searchdrv) optionstr[searchdrv-1] = "Open containing folder";
|
||||
|
||||
u32 user_select = ShowSelectPrompt(n_opt, optionstr, pathstr);
|
||||
|
@ -14,10 +14,18 @@
|
||||
#define NAME_CIA_META "meta.bin"
|
||||
#define NAME_CIA_CONTENT "%04X.%08lX.app" // index.id.app
|
||||
|
||||
#define NAME_NCSD_HEADER "ncsd.bin"
|
||||
#define NAME_NCSD_CARDINFO "cardinfo.bin"
|
||||
#define NAME_NCSD_DEVINFO "devinfo.bin"
|
||||
#define NAME_NCSD_CONTENT "cnt0.game.cxi", "cnt1.manual.cfa", "cnt2.dlp.cfa", \
|
||||
"cnt3.unk", "cnt4.unk", "cnt5.unk", \
|
||||
"cnt6.update_n3ds.cfa", "cnt7.update_o3ds.cfa"
|
||||
|
||||
static u32 vgame_type = 0;
|
||||
static VirtualFile* templates = (VirtualFile*) VGAME_BUFFER; // first 128kb reserved
|
||||
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
|
||||
|
||||
u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
||||
@ -30,7 +38,11 @@ u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
||||
(GetCiaInfo(&info, &(cia->header)) != 0) ||
|
||||
(ReadImageBytes((u8*) cia, 0, info.offset_content) != 0))
|
||||
return 0;
|
||||
} else if ((type == GAME_NCCH) || (type == GAME_NCSD)) {
|
||||
} else if (type == GAME_NCSD) {
|
||||
if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) ||
|
||||
(ValidateNcsdHeader(ncsd) != 0))
|
||||
return 0;
|
||||
} else if (type == GAME_NCCH) {
|
||||
} else return 0; // not a mounted game file
|
||||
|
||||
vgame_type = type;
|
||||
@ -42,6 +54,56 @@ u32 CheckVGameDrive(void) {
|
||||
return vgame_type;
|
||||
}
|
||||
|
||||
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 >= 0x1200) {
|
||||
strncpy(templates[n_templates].name, NAME_NCSD_CARDINFO, 32);
|
||||
templates[n_templates].offset = 0x200;
|
||||
templates[n_templates].size = 0x1000;
|
||||
templates[n_templates].keyslot = 0xFF;
|
||||
templates[n_templates].flags = 0;
|
||||
n_templates++;
|
||||
}
|
||||
|
||||
// dev info header
|
||||
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= 0x1500) {
|
||||
strncpy(templates[n_templates].name, NAME_NCSD_DEVINFO, 32);
|
||||
templates[n_templates].offset = 0x1200;
|
||||
templates[n_templates].size = 0x300;
|
||||
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;
|
||||
|
||||
@ -129,6 +191,7 @@ bool BuildVGameCiaVDir(void) {
|
||||
}
|
||||
|
||||
bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
||||
|
||||
(void) path; // not in use yet
|
||||
static int num = -1;
|
||||
|
||||
@ -136,9 +199,11 @@ bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
||||
num = -1; // reset dir reader / internal number
|
||||
memset(templates, 0, sizeof(VirtualFile) * MAX_N_TEMPLATES);
|
||||
n_templates = 0;
|
||||
if (!BuildVGameCiaVDir()) // NCCH / NCSD !!!
|
||||
return false;
|
||||
return true;
|
||||
if ((vgame_type == GAME_CIA) && BuildVGameCiaVDir()) // for CIA
|
||||
return true;
|
||||
else if ((vgame_type == GAME_NCSD) && BuildVGameNcsdVDir()) // for NCSD
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++num < n_templates) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user