Skip to content

Commit

Permalink
Fix XTS/NCA2 support. Add NCA0 support.
Browse files Browse the repository at this point in the history
  • Loading branch information
SciresM committed Jul 20, 2018
1 parent 94d55a9 commit 2e87aa8
Show file tree
Hide file tree
Showing 14 changed files with 730 additions and 74 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ all:
.c.o:
$(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<

hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o romfs.o utils.o nax0.o nca.o xci.o main.o filepath.o ConvertUTF.o cJSON.o
hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o nca0_romfs.o romfs.o utils.o nax0.o nca.o xci.o main.o filepath.o ConvertUTF.o cJSON.o
$(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR)

aes.o: aes.h types.h
Expand Down Expand Up @@ -44,6 +44,8 @@ npdm.o: npdm.c cJSON.h types.h

romfs.o: ivfc.h types.h

nca0_romfs.o: nca0_romfs.h ivfc.h types.h

rsa.o: rsa.h sha.h types.h

sha.o: sha.h types.h
Expand Down
6 changes: 6 additions & 0 deletions extkeys.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "pki.h"
#include "extkeys.h"

/**
Expand Down Expand Up @@ -236,6 +237,11 @@ void extkeys_initialize_keyset(nca_keyset_t *keyset, FILE *f) {
} else if (strcmp(key, "tsec_key") == 0) {
parse_hex_key(keyset->tsec_key, value, sizeof(keyset->tsec_key));
matched_key = 1;
} else if (strcmp(key, "beta_nca0_exponent") == 0) {
unsigned char exponent[0x100] = {0};
parse_hex_key(exponent, value, sizeof(exponent));
pki_set_beta_nca0_exponent(exponent);
matched_key = 1;
} else {
char test_name[0x100];
memset(test_name, 0, sizeof(100));
Expand Down
1 change: 1 addition & 0 deletions ivfc.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef struct {
} romfs_hdr_t;

typedef struct {
uint32_t parent;
uint32_t sibling;
uint32_t child;
uint32_t file;
Expand Down
16 changes: 16 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ int main(int argc, char **argv) {
nca_ctx.tool_ctx->file_type = FILETYPE_PFS0;
} else if (!strcmp(optarg, "romfs")) {
nca_ctx.tool_ctx->file_type = FILETYPE_ROMFS;
} else if (!strcmp(optarg, "nca0_romfs") || !strcmp(optarg, "nca0romfs") || !strcmp(optarg, "betaromfs") || !strcmp(optarg, "beta_romfs")) {
nca_ctx.tool_ctx->file_type = FILETYPE_NCA0_ROMFS;
} else if (!strcmp(optarg, "hfs0")) {
nca_ctx.tool_ctx->file_type = FILETYPE_HFS0;
} else if (!strcmp(optarg, "xci") || !strcmp(optarg, "gamecard") || !strcmp(optarg, "gc")) {
Expand Down Expand Up @@ -524,6 +526,20 @@ int main(int argc, char **argv) {
}
break;
}
case FILETYPE_NCA0_ROMFS: {
nca0_romfs_ctx_t romfs_ctx;
memset(&romfs_ctx, 0, sizeof(romfs_ctx));
romfs_ctx.file = tool_ctx.file;
romfs_ctx.tool_ctx = &tool_ctx;
nca0_romfs_process(&romfs_ctx);
if (romfs_ctx.files) {
free(romfs_ctx.files);
}
if (romfs_ctx.directories) {
free(romfs_ctx.directories);
}
break;
}
case FILETYPE_NPDM: {
npdm_t raw_hdr;
memset(&raw_hdr, 0, sizeof(raw_hdr));
Expand Down
387 changes: 327 additions & 60 deletions nca.c

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion nca.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
#include "npdm.h"
#include "pfs0.h"
#include "ivfc.h"
#include "nca0_romfs.h"
#include "bktr.h"

#define MAGIC_NCA3 0x3341434E /* "NCA3" */
#define MAGIC_NCA2 0x3241434E /* "NCA2" */
#define MAGIC_NCA0 0x3041434E /* "NCA0" */

typedef struct {
uint32_t media_start_offset;
Expand Down Expand Up @@ -57,7 +59,8 @@ typedef enum {
CRYPT_NONE = 1,
CRYPT_XTS = 2,
CRYPT_CTR = 3,
CRYPT_BKTR = 4
CRYPT_BKTR = 4,
CRYPT_NCA0 = MAGIC_NCA0
} section_crypt_type_t;

/* NCA FS header. */
Expand All @@ -71,6 +74,7 @@ typedef struct {
union { /* FS-specific superblock. Size = 0x138. */
pfs0_superblock_t pfs0_superblock;
romfs_superblock_t romfs_superblock;
nca0_romfs_superblock_t nca0_romfs_superblock;
bktr_superblock_t bktr_superblock;
};
union {
Expand Down Expand Up @@ -118,9 +122,19 @@ enum nca_section_type {
PFS0,
ROMFS,
BKTR,
NCA0_ROMFS,
INVALID
};

enum nca_version {
NCAVERSION_UNKNOWN = 0,
NCAVERSION_NCA0_BETA,
NCAVERSION_NCA0,
/* NCAVERSION_NCA1, // Does this exist? */
NCAVERSION_NCA2,
NCAVERSION_NCA3
};

typedef struct {
int is_present;
enum nca_section_type type;
Expand All @@ -130,11 +144,14 @@ typedef struct {
uint32_t section_num;
nca_fs_header_t *header;
int is_decrypted;
uint64_t sector_size;
uint64_t sector_mask;
aes_ctx_t *aes; /* AES context for the section. */
hactool_ctx_t *tool_ctx;
union {
pfs0_ctx_t pfs0_ctx;
romfs_ctx_t romfs_ctx;
nca0_romfs_ctx_t nca0_romfs_ctx;
bktr_section_ctx_t bktr_ctx;
};
validity_t superblock_hash_validity;
Expand All @@ -143,6 +160,7 @@ typedef struct {
size_t sector_num;
uint32_t sector_ofs;
int physical_reads; /* Should reads be forced physical? */
section_crypt_type_t crypt_type;
} nca_section_ctx_t;

typedef struct nca_ctx {
Expand All @@ -151,6 +169,7 @@ typedef struct nca_ctx {
unsigned char crypto_type;
int has_rights_id;
int is_decrypted;
enum nca_version format_version;
validity_t fixed_sig_validity;
validity_t npdm_sig_validity;
hactool_ctx_t *tool_ctx;
Expand All @@ -177,15 +196,18 @@ void nca_save_section_file(nca_section_ctx_t *ctx, uint64_t ofs, uint64_t total_
/* These have to be in nca.c, sadly... */
void nca_process_pfs0_section(nca_section_ctx_t *ctx);
void nca_process_ivfc_section(nca_section_ctx_t *ctx);
void nca_process_nca0_romfs_section(nca_section_ctx_t *ctx);
void nca_process_bktr_section(nca_section_ctx_t *ctx);
void nca_print_pfs0_section(nca_section_ctx_t *ctx);
void nca_print_ivfc_section(nca_section_ctx_t *ctx);
void nca_print_nca0_romfs_section(nca_section_ctx_t *ctx);
void nca_print_bktr_section(nca_section_ctx_t *ctx);


void nca_save_section(nca_section_ctx_t *ctx);
void nca_save_pfs0_section(nca_section_ctx_t *ctx);
void nca_save_ivfc_section(nca_section_ctx_t *ctx);
void nca_save_nca0_romfs_section(nca_section_ctx_t *ctx);
void nca_save_bktr_section(nca_section_ctx_t *ctx);

#endif
143 changes: 143 additions & 0 deletions nca0_romfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#include <stdio.h>
#include "types.h"
#include "utils.h"
#include "nca0_romfs.h"

/* NCA0 RomFS functions... */
void nca0_romfs_visit_file(nca0_romfs_ctx_t *ctx, uint32_t file_offset, filepath_t *dir_path) {
romfs_fentry_t *entry = romfs_get_fentry(ctx->files, file_offset);
filepath_t *cur_path = calloc(1, sizeof(filepath_t));
if (cur_path == NULL) {
fprintf(stderr, "Failed to allocate filepath!\n");
exit(EXIT_FAILURE);
}

filepath_copy(cur_path, dir_path);
if (entry->name_size) {
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
}

/* If we're extracting... */
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
printf("Saving %s...\n", cur_path->char_path);
save_file_section(ctx->file, ctx->romfs_offset + ctx->header.data_offset + entry->offset, entry->size, cur_path);
} else {
printf("rom:%s\n", cur_path->char_path);
}

free(cur_path);

if (entry->sibling != ROMFS_ENTRY_EMPTY) {
nca0_romfs_visit_file(ctx, entry->sibling, dir_path);
}
}

void nca0_romfs_visit_dir(nca0_romfs_ctx_t *ctx, uint32_t dir_offset, filepath_t *parent_path) {
romfs_direntry_t *entry = romfs_get_direntry(ctx->directories, dir_offset);
filepath_t *cur_path = calloc(1, sizeof(filepath_t));
if (cur_path == NULL) {
fprintf(stderr, "Failed to allocate filepath!\n");
exit(EXIT_FAILURE);
}

filepath_copy(cur_path, parent_path);
if (entry->name_size) {
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
}

/* If we're actually extracting the romfs, make directory. */
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
os_makedir(cur_path->os_path);
}

if (entry->file != ROMFS_ENTRY_EMPTY) {
nca0_romfs_visit_file(ctx, entry->file, cur_path);
}
if (entry->child != ROMFS_ENTRY_EMPTY) {
nca0_romfs_visit_dir(ctx, entry->child, cur_path);
}
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
nca0_romfs_visit_dir(ctx, entry->sibling, parent_path);
}

free(cur_path);
}

void nca0_romfs_process(nca0_romfs_ctx_t *ctx) {
ctx->romfs_offset = 0;
fseeko64(ctx->file, ctx->romfs_offset, SEEK_SET);
if (fread(&ctx->header, 1, sizeof(nca0_romfs_hdr_t), ctx->file) != sizeof(nca0_romfs_hdr_t)) {
fprintf(stderr, "Failed to read NCA0 RomFS header!\n");
return;
}

if ((ctx->tool_ctx->action & (ACTION_EXTRACT | ACTION_LISTROMFS)) && ctx->header.header_size == NCA0_ROMFS_HEADER_SIZE) {
/* Pre-load the file/data entry caches. */
ctx->directories = calloc(1, ctx->header.dir_meta_table_size);
if (ctx->directories == NULL) {
fprintf(stderr, "Failed to allocate NCA0 RomFS directory cache!\n");
exit(EXIT_FAILURE);
}

fseeko64(ctx->file, ctx->romfs_offset + ctx->header.dir_meta_table_offset, SEEK_SET);
if (fread(ctx->directories, 1, ctx->header.dir_meta_table_size, ctx->file) != ctx->header.dir_meta_table_size) {
fprintf(stderr, "Failed to read NCA0 RomFS directory cache!\n");
exit(EXIT_FAILURE);
}

ctx->files = calloc(1, ctx->header.file_meta_table_size);
if (ctx->files == NULL) {
fprintf(stderr, "Failed to allocate NCA0 RomFS file cache!\n");
exit(EXIT_FAILURE);
}
fseeko64(ctx->file, ctx->romfs_offset + ctx->header.file_meta_table_offset, SEEK_SET);
if (fread(ctx->files, 1, ctx->header.file_meta_table_size, ctx->file) != ctx->header.file_meta_table_size) {
fprintf(stderr, "Failed to read NCA0 RomFS file cache!\n");
exit(EXIT_FAILURE);
}
} else {
fprintf(stderr, "NCA0 RomFS is corrupt?\n");
return;
}

/* If there's ever anything meaningful to print about RomFS, uncomment and implement.
*
* if (ctx->tool_ctx->action & ACTION_INFO) {
* nca0_romfs_print(ctx);
* }
*/

if (ctx->tool_ctx->action & ACTION_EXTRACT) {
nca0_romfs_save(ctx);
}

}

void nca0_romfs_save(nca0_romfs_ctx_t *ctx) {
if (ctx->tool_ctx->action & ACTION_LISTROMFS) {
filepath_t fakepath;
filepath_init(&fakepath);
filepath_set(&fakepath, "");

nca0_romfs_visit_dir(ctx, 0, &fakepath);
} else {
/* Extract to directory. */
filepath_t *dirpath = NULL;
if (ctx->tool_ctx->settings.romfs_dir_path.enabled) {
dirpath = &ctx->tool_ctx->settings.romfs_dir_path.path;
}
if ((dirpath == NULL || dirpath->valid != VALIDITY_VALID) && (ctx->tool_ctx->file_type == FILETYPE_NCA0_ROMFS && ctx->tool_ctx->settings.out_dir_path.enabled)) {
dirpath = &ctx->tool_ctx->settings.out_dir_path.path;
}
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
os_makedir(dirpath->os_path);
nca0_romfs_visit_dir(ctx, 0, dirpath);
}
}

}

void nca0_romfs_print(nca0_romfs_ctx_t *ctx) {
/* Is there anything meaningful to print here? */
fprintf(stderr, "Error: NCA0 RomFS printing not implemented.\n");
}
53 changes: 53 additions & 0 deletions nca0_romfs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#ifndef HACTOOL_NCA0_ROMFS_H
#define HACTOOL_NCA0_ROMFS_H

#include "types.h"
#include "utils.h"
#include "ivfc.h"
#include "settings.h"

/* RomFS structs. */
#define NCA0_ROMFS_HEADER_SIZE 0x00000028

typedef struct {
uint32_t header_size;
uint32_t dir_hash_table_offset;
uint32_t dir_hash_table_size;
uint32_t dir_meta_table_offset;
uint32_t dir_meta_table_size;
uint32_t file_hash_table_offset;
uint32_t file_hash_table_size;
uint32_t file_meta_table_offset;
uint32_t file_meta_table_size;
uint32_t data_offset;
} nca0_romfs_hdr_t;

typedef struct {
uint8_t master_hash[0x20]; /* SHA-256 hash of the hash table. */
uint32_t block_size; /* In bytes. */
uint32_t always_2;
uint64_t hash_table_offset; /* Normally zero. */
uint64_t hash_table_size;
uint64_t romfs_offset;
uint64_t romfs_size;
uint8_t _0x48[0xF0];
} nca0_romfs_superblock_t;

typedef struct {
nca0_romfs_superblock_t *superblock;
FILE *file;
hactool_ctx_t *tool_ctx;
validity_t superblock_hash_validity;
validity_t hash_table_validity;
uint64_t romfs_offset;
nca0_romfs_hdr_t header;
romfs_direntry_t *directories;
romfs_fentry_t *files;
} nca0_romfs_ctx_t;

void nca0_romfs_process(nca0_romfs_ctx_t *ctx);
void nca0_romfs_save(nca0_romfs_ctx_t *ctx);
void nca0_romfs_print(nca0_romfs_ctx_t *ctx);


#endif
Loading

0 comments on commit 2e87aa8

Please sign in to comment.