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))
|
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
||||||
|
|
||||||
// GodMode9 version
|
// GodMode9 version
|
||||||
#define VERSION "0.7.6"
|
#define VERSION "0.7.7"
|
||||||
|
|
||||||
// buffer area defines (in use by godmode.c)
|
// buffer area defines (in use by godmode.c)
|
||||||
#define DIR_BUFFER (0x21000000)
|
#define DIR_BUFFER (0x21000000)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "filetype.h"
|
#include "filetype.h"
|
||||||
#include "cia.h"
|
#include "game.h"
|
||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
|
|
||||||
u32 IdentifyFileType(const char* path) {
|
u32 IdentifyFileType(const char* path) {
|
||||||
@ -36,7 +36,10 @@ u32 IdentifyFileType(const char* path) {
|
|||||||
GetCiaInfo(&info, (CiaHeader*) header);
|
GetCiaInfo(&info, (CiaHeader*) header);
|
||||||
if (fsize >= info.size_cia)
|
if (fsize >= info.size_cia)
|
||||||
return GAME_CIA; // CIA file
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cia.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] =
|
if (mountable) optionstr[mountable-1] =
|
||||||
(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" : ""; // !!! 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";
|
if (searchdrv) optionstr[searchdrv-1] = "Open containing folder";
|
||||||
|
|
||||||
u32 user_select = ShowSelectPrompt(n_opt, optionstr, pathstr);
|
u32 user_select = ShowSelectPrompt(n_opt, optionstr, pathstr);
|
||||||
|
@ -14,10 +14,18 @@
|
|||||||
#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_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 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
|
||||||
|
|
||||||
u32 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
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) ||
|
(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;
|
||||||
} 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
|
} else return 0; // not a mounted game file
|
||||||
|
|
||||||
vgame_type = type;
|
vgame_type = type;
|
||||||
@ -42,6 +54,56 @@ u32 CheckVGameDrive(void) {
|
|||||||
return vgame_type;
|
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) {
|
bool BuildVGameCiaVDir(void) {
|
||||||
CiaInfo info;
|
CiaInfo info;
|
||||||
|
|
||||||
@ -129,6 +191,7 @@ bool BuildVGameCiaVDir(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
||||||
|
|
||||||
(void) path; // not in use yet
|
(void) path; // not in use yet
|
||||||
static int num = -1;
|
static int num = -1;
|
||||||
|
|
||||||
@ -136,9 +199,11 @@ bool ReadVGameDir(VirtualFile* vfile, const char* path) {
|
|||||||
num = -1; // reset dir reader / internal number
|
num = -1; // reset dir reader / internal number
|
||||||
memset(templates, 0, sizeof(VirtualFile) * MAX_N_TEMPLATES);
|
memset(templates, 0, sizeof(VirtualFile) * MAX_N_TEMPLATES);
|
||||||
n_templates = 0;
|
n_templates = 0;
|
||||||
if (!BuildVGameCiaVDir()) // NCCH / NCSD !!!
|
if ((vgame_type == GAME_CIA) && BuildVGameCiaVDir()) // for CIA
|
||||||
return false;
|
return true;
|
||||||
return true;
|
else if ((vgame_type == GAME_NCSD) && BuildVGameNcsdVDir()) // for NCSD
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++num < n_templates) {
|
if (++num < n_templates) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user