mirror of
https://github.com/SciresM/hactool
synced 2024-11-10 06:34:14 +00:00
Implement --basenca support (closes #1).
This commit is contained in:
parent
fba6933668
commit
0c65ac1e70
4 changed files with 106 additions and 18 deletions
57
main.c
57
main.c
|
@ -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
54
nca.c
|
@ -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
2
nca.h
|
@ -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;
|
||||
|
|
11
settings.h
11
settings.h
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue