Implement --basenca support (closes #1).

This commit is contained in:
Michael Scire 2018-01-27 03:22:09 -08:00
parent fba6933668
commit 0c65ac1e70
4 changed files with 106 additions and 18 deletions

57
main.c
View file

@ -42,7 +42,8 @@ static void usage(void) {
" --romfs=file Specify RomFS file path. Overrides appropriate section file path.\n"
" --romfsdir=dir Specify RomFS directory path. Overrides appropriate section directory path.\n"
" --listromfs List files in RomFS.\n"
" --baseromfs Set Base RomFS to use with update partitions.\n"
" --baseromfs Set Base RomFS to use with update partitions.\n"
" --basenca Set Base NCA to use with update partitions.\n"
"\n", __TIME__, __DATE__, prog_name);
exit(EXIT_FAILURE);
}
@ -87,6 +88,7 @@ void parse_hex_key(unsigned char *key, const char *hex) {
int main(int argc, char **argv) {
ncatool_ctx_t tool_ctx;
ncatool_ctx_t base_ctx; /* Context for base NCA, if used. */
nca_ctx_t ctx;
char input_name[0x200];
@ -94,6 +96,7 @@ int main(int argc, char **argv) {
nca_init(&ctx);
memset(&tool_ctx, 0, sizeof(tool_ctx));
memset(&base_ctx, 0, sizeof(base_ctx));
memset(input_name, 0, sizeof(input_name));
ctx.tool_ctx = &tool_ctx;
@ -126,6 +129,7 @@ int main(int argc, char **argv) {
{"contentkey", 1, NULL, 13},
{"listromfs", 0, NULL, 14},
{"baseromfs", 1, NULL, 15},
{"basenca", 1, NULL, 16},
{NULL, 0, NULL, 0},
};
@ -187,10 +191,34 @@ int main(int argc, char **argv) {
ctx.tool_ctx->action |= ACTION_LISTROMFS;
break;
case 15:
if ((ctx.tool_ctx->base_romfs_file = fopen(optarg, "rb")) == NULL) {
if (ctx.tool_ctx->base_file != NULL) {
usage();
return EXIT_FAILURE;
}
if ((ctx.tool_ctx->base_file = fopen(optarg, "rb")) == NULL) {
fprintf(stderr, "unable to open %s: %s\n", optarg, strerror(errno));
return EXIT_FAILURE;
}
ctx.tool_ctx->base_file_type = BASEFILE_ROMFS;
break;
case 16:
if (ctx.tool_ctx->base_file != NULL) {
usage();
return EXIT_FAILURE;
}
if ((ctx.tool_ctx->base_file = fopen(optarg, "rb")) == NULL) {
fprintf(stderr, "unable to open %s: %s\n", optarg, strerror(errno));
return EXIT_FAILURE;
}
ctx.tool_ctx->base_file_type = BASEFILE_NCA;
ctx.tool_ctx->base_nca_ctx = malloc(sizeof(*ctx.tool_ctx->base_nca_ctx));
if (ctx.tool_ctx->base_nca_ctx == NULL) {
fprintf(stderr, "Failed to allocate base NCA context!\n");
return EXIT_FAILURE;
}
nca_init(ctx.tool_ctx->base_nca_ctx);
base_ctx.file = ctx.tool_ctx->base_file;
ctx.tool_ctx->base_nca_ctx->file = base_ctx.file;
break;
default:
usage();
@ -209,13 +237,34 @@ int main(int argc, char **argv) {
fprintf(stderr, "unable to open %s: %s\n", input_name, strerror(errno));
return EXIT_FAILURE;
}
if (ctx.tool_ctx->base_nca_ctx != NULL) {
memcpy(&base_ctx.settings.keyset, &tool_ctx.settings.keyset, sizeof(nca_keyset_t));
ctx.tool_ctx->base_nca_ctx->tool_ctx = &base_ctx;
nca_process(ctx.tool_ctx->base_nca_ctx);
int found_romfs = 0;
for (unsigned int i = 0; i < 4; i++) {
if (ctx.tool_ctx->base_nca_ctx->section_contexts[i].is_present && ctx.tool_ctx->base_nca_ctx->section_contexts[i].type == ROMFS) {
found_romfs = 1;
break;
}
}
if (found_romfs == 0) {
fprintf(stderr, "Unable to locate RomFS in base NCA!\n");
return EXIT_FAILURE;
}
}
ctx.file = tool_ctx.file;
nca_process(&ctx);
nca_free_section_contexts(&ctx);
if (ctx.tool_ctx->base_romfs_file != NULL) {
fclose(ctx.tool_ctx->base_romfs_file);
if (ctx.tool_ctx->base_file != NULL) {
fclose(ctx.tool_ctx->base_file);
if (ctx.tool_ctx->base_file_type == BASEFILE_NCA) {
nca_free_section_contexts(ctx.tool_ctx->base_nca_ctx);
free(ctx.tool_ctx->base_nca_ctx);
}
}
if (ctx.file != NULL) {
fclose(ctx.file);

54
nca.c
View file

@ -46,7 +46,7 @@ void nca_section_fseek(nca_section_ctx_t *ctx, uint64_t offset) {
} else if (ctx->type == BKTR && ctx->bktr_ctx.subsection_block != NULL) {
/* No better way to do this than to make all BKTR seeking virtual. */
ctx->bktr_ctx.virtual_seek = offset;
if (ctx->tool_ctx->base_romfs_file == NULL) { /* Without base romfs, reads will be physical. */
if (ctx->tool_ctx->base_file == NULL) { /* Without base romfs, reads will be physical. */
ctx->bktr_ctx.bktr_seek = offset;
} else { /* Let's do the complicated thing. */
bktr_relocation_entry_t *reloc = bktr_get_relocation(ctx->bktr_ctx.relocation_block, offset);
@ -200,7 +200,7 @@ size_t nca_section_fread(nca_section_ctx_t *ctx, void *buffer, size_t count) {
nca_section_fseek(ctx, ctx->cur_seek - ctx->offset + count);
} else if (ctx->header->crypt_type == CRYPT_BKTR) { /* Spooky BKTR AES-CTR. */
/* Are we doing virtual reads, or physical reads? */
if (ctx->tool_ctx->base_romfs_file != NULL) {
if (ctx->tool_ctx->base_file != NULL) {
bktr_relocation_entry_t *reloc = bktr_get_relocation(ctx->bktr_ctx.relocation_block, ctx->bktr_ctx.virtual_seek);
bktr_relocation_entry_t *next_reloc = reloc + 1;
uint64_t virt_seek = ctx->bktr_ctx.virtual_seek;
@ -210,9 +210,24 @@ size_t nca_section_fread(nca_section_ctx_t *ctx, void *buffer, size_t count) {
read = nca_bktr_section_physical_fread(ctx, buffer, count);
} else {
/* Nice and easy read from the base rom. */
fseeko64(ctx->tool_ctx->base_romfs_file, ctx->bktr_ctx.base_seek, SEEK_SET);
if ((read = fread(buffer, 1, count, ctx->tool_ctx->base_romfs_file)) != count) {
return 0;
if (ctx->tool_ctx->base_file_type == BASEFILE_ROMFS) {
fseeko64(ctx->tool_ctx->base_file, ctx->bktr_ctx.base_seek, SEEK_SET);
if ((read = fread(buffer, 1, count, ctx->tool_ctx->base_file)) != count) {
return 0;
}
} else {
nca_ctx_t *base_ctx = ctx->tool_ctx->base_nca_ctx;
unsigned int romfs_section_num;
for (romfs_section_num = 0; romfs_section_num < 4; romfs_section_num++) {
if (base_ctx->section_contexts[romfs_section_num].type == ROMFS) {
break;
}
}
nca_section_fseek(&base_ctx->section_contexts[romfs_section_num], ctx->bktr_ctx.base_seek);
if ((read = nca_section_fread(&base_ctx->section_contexts[romfs_section_num], buffer, count)) != count) {
fprintf(stderr, "Failed to read from Base NCA RomFS!\n");
exit(EXIT_FAILURE);
}
}
}
} else {
@ -250,6 +265,19 @@ void nca_free_section_contexts(nca_ctx_t *ctx) {
if (ctx->section_contexts[i].romfs_ctx.files) {
free(ctx->section_contexts[i].romfs_ctx.files);
}
} else if (ctx->section_contexts[i].type == BKTR) {
if (ctx->section_contexts[i].bktr_ctx.subsection_block) {
free(ctx->section_contexts[i].bktr_ctx.subsection_block);
}
if (ctx->section_contexts[i].bktr_ctx.relocation_block) {
free(ctx->section_contexts[i].bktr_ctx.relocation_block);
}
if (ctx->section_contexts[i].bktr_ctx.directories) {
free(ctx->section_contexts[i].bktr_ctx.directories);
}
if (ctx->section_contexts[i].bktr_ctx.files) {
free(ctx->section_contexts[i].bktr_ctx.files);
}
}
}
}
@ -382,10 +410,12 @@ void nca_process(nca_ctx_t *ctx) {
}
}
nca_print(ctx);
if (ctx->tool_ctx->action & ACTION_INFO) {
nca_print(ctx);
}
for (unsigned int i = 0; i < 4; i++) {
if (ctx->section_contexts[i].is_present) {
if (ctx->section_contexts[i].is_present && ctx->tool_ctx->action & ACTION_EXTRACT) {
/* printf("Saving section %"PRId32"...\n", i); */
nca_save_section(&ctx->section_contexts[i]);
printf("\n");
@ -850,7 +880,7 @@ void nca_process_bktr_section(nca_section_ctx_t *ctx) {
if (i != 0) {
/* Hash table is previous level's data. */
cur_level->hash_offset = ctx->bktr_ctx.ivfc_levels[i-1].data_offset;
} else if (ctx->tool_ctx->base_romfs_file != NULL) {
} else if (ctx->tool_ctx->base_file != NULL) {
/* Hash table is the superblock hash. Always check the superblock hash. */
ctx->superblock_hash_validity = nca_section_check_external_hash_table(ctx, sb->ivfc_header.master_hash, cur_level->data_offset, cur_level->data_size, cur_level->hash_block_size, 1);
cur_level->hash_validity = ctx->superblock_hash_validity;
@ -863,7 +893,7 @@ void nca_process_bktr_section(nca_section_ctx_t *ctx) {
}
ctx->bktr_ctx.romfs_offset = ctx->bktr_ctx.ivfc_levels[IVFC_MAX_LEVEL - 1].data_offset;
if (ctx->tool_ctx->base_romfs_file != NULL) {
if (ctx->tool_ctx->base_file != NULL) {
nca_section_fseek(ctx, ctx->bktr_ctx.romfs_offset);
if (nca_section_fread(ctx, &ctx->bktr_ctx.header, sizeof(romfs_hdr_t)) != sizeof(romfs_hdr_t)) {
fprintf(stderr, "Failed to read BKTR Virtual RomFS header!\n");
@ -947,7 +977,7 @@ void nca_print_bktr_section(nca_section_ctx_t *ctx) {
printf(" BKTR section seems to be corrupted.\n");
return;
}
int did_verify = (ctx->tool_ctx->action & ACTION_VERIFY) && (ctx->tool_ctx->base_romfs_file != NULL);
int did_verify = (ctx->tool_ctx->action & ACTION_VERIFY) && (ctx->tool_ctx->base_file != NULL);
if (did_verify ) {
if (ctx->superblock_hash_validity == VALIDITY_VALID) {
memdump(stdout, " Superblock Hash (GOOD): ", &ctx->bktr_ctx.superblock->ivfc_header.master_hash, 0x20);
@ -1025,7 +1055,7 @@ void nca_save_section(nca_section_ctx_t *ctx) {
case INVALID:
break;
}
} else if (ctx->type == BKTR && ctx->bktr_ctx.subsection_block != NULL && ctx->tool_ctx->base_romfs_file != NULL) {
} else if (ctx->type == BKTR && ctx->bktr_ctx.subsection_block != NULL && ctx->tool_ctx->base_file != NULL) {
size = ctx->bktr_ctx.relocation_block->patch_romfs_size;
}
@ -1051,7 +1081,7 @@ void nca_save_section(nca_section_ctx_t *ctx) {
nca_save_ivfc_section(ctx);
break;
case BKTR:
if (ctx->tool_ctx->base_romfs_file == NULL) {
if (ctx->tool_ctx->base_file == NULL) {
fprintf(stderr, "Note: cannot save BKTR section without base romfs.\n");
break;
}

2
nca.h
View file

@ -177,7 +177,7 @@ typedef struct {
uint32_t sector_ofs;
} nca_section_ctx_t;
typedef struct {
typedef struct nca_ctx {
FILE *file; /* File for this NCA. */
size_t file_size;
unsigned char crypto_type;

View file

@ -9,6 +9,11 @@ typedef enum {
KEYSET_RETAIL
} keyset_variant_t;
typedef enum {
BASEFILE_ROMFS,
BASEFILE_NCA
} ncatool_basefile_t;
typedef struct {
unsigned char header_key[0x20];
unsigned char titlekeks[0x20][0x10];
@ -48,10 +53,14 @@ enum ncatool_file_type
#define ACTION_RAW (1<<3)
#define ACTION_LISTROMFS (1<<4)
struct nca_ctx; /* This will get re-defined by nca.h. */
typedef struct {
enum ncatool_file_type file_type;
FILE *file;
FILE *base_romfs_file;
FILE *base_file;
ncatool_basefile_t base_file_type;
struct nca_ctx *base_nca_ctx;
ncatool_settings_t settings;
uint32_t action;
} ncatool_ctx_t;