mirror of
https://github.com/SciresM/hactool
synced 2024-11-10 06:34:14 +00:00
Add two new options to nca extraction
This commit is contained in:
parent
e37d4d2da2
commit
864e7ee86b
6 changed files with 197 additions and 56 deletions
15
filepath.c
15
filepath.c
|
@ -113,6 +113,21 @@ void filepath_set(filepath_t *fpath, const char *path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void filepath_set_format(filepath_t *fpath, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (fpath->valid == VALIDITY_INVALID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(fpath->char_path, 0, MAX_PATH);
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(fpath->char_path, MAX_PATH, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
filepath_update(fpath);
|
||||||
|
}
|
||||||
|
|
||||||
oschar_t *filepath_get(filepath_t *fpath) {
|
oschar_t *filepath_get(filepath_t *fpath) {
|
||||||
if (fpath->valid == VALIDITY_INVALID)
|
if (fpath->valid == VALIDITY_INVALID)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -39,6 +39,7 @@ void filepath_copy(filepath_t *fpath, filepath_t *copy);
|
||||||
void filepath_append(filepath_t *fpath, const char *format, ...);
|
void filepath_append(filepath_t *fpath, const char *format, ...);
|
||||||
void filepath_append_n(filepath_t *fpath, uint32_t n, const char *format, ...);
|
void filepath_append_n(filepath_t *fpath, uint32_t n, const char *format, ...);
|
||||||
void filepath_set(filepath_t *fpath, const char *path);
|
void filepath_set(filepath_t *fpath, const char *path);
|
||||||
|
void filepath_set_format(filepath_t *fpath, const char *format, ...);
|
||||||
oschar_t *filepath_get(filepath_t *fpath);
|
oschar_t *filepath_get(filepath_t *fpath);
|
||||||
|
|
||||||
|
|
||||||
|
|
32
main.c
32
main.c
|
@ -3,6 +3,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -57,6 +58,8 @@ static void usage(void) {
|
||||||
" --basenca Set Base NCA to use with update partitions.\n"
|
" --basenca Set Base NCA to use with update partitions.\n"
|
||||||
" --basefake Use a fake Base RomFS with update partitions (all reads will return 0xCC).\n"
|
" --basefake Use a fake Base RomFS with update partitions (all reads will return 0xCC).\n"
|
||||||
" --onlyupdated Ignore non-updated files in update partitions.\n"
|
" --onlyupdated Ignore non-updated files in update partitions.\n"
|
||||||
|
" --xcontenttype= Only extract contents if the content type matches an expected one.\n"
|
||||||
|
" --appendsectypes Append a section type string to section paths.\n"
|
||||||
"NPDM options:\n"
|
"NPDM options:\n"
|
||||||
" --json=file Specify file path for saving JSON representation of program permissions to.\n"
|
" --json=file Specify file path for saving JSON representation of program permissions to.\n"
|
||||||
"KIP1 options:\n"
|
"KIP1 options:\n"
|
||||||
|
@ -185,6 +188,8 @@ int main(int argc, char **argv) {
|
||||||
{"uncompressed", 1, NULL, 39},
|
{"uncompressed", 1, NULL, 39},
|
||||||
{"disablekeywarns", 0, NULL, 40},
|
{"disablekeywarns", 0, NULL, 40},
|
||||||
{"listfiles", 0, NULL, 41},
|
{"listfiles", 0, NULL, 41},
|
||||||
|
{"xcontenttype", 1, NULL, 42},
|
||||||
|
{"appendsectypes", 0, NULL, 43},
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -400,6 +405,33 @@ int main(int argc, char **argv) {
|
||||||
case 41:
|
case 41:
|
||||||
nca_ctx.tool_ctx->action |= ACTION_LISTFILES;
|
nca_ctx.tool_ctx->action |= ACTION_LISTFILES;
|
||||||
break;
|
break;
|
||||||
|
case 42:
|
||||||
|
if (strlen(optarg) > 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.has_expected_content_type = 1;
|
||||||
|
if (strcasecmp(optarg, "program") == 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = NCACONTENTTYPE_PROGRAM;
|
||||||
|
} else if (strcasecmp(optarg, "meta") == 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = NCACONTENTTYPE_META;
|
||||||
|
} else if (strcasecmp(optarg, "control") == 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = NCACONTENTTYPE_CONTROL;
|
||||||
|
} else if (strcasecmp(optarg, "manual") == 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = NCACONTENTTYPE_MANUAL;
|
||||||
|
} else if (strcasecmp(optarg, "data") == 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = NCACONTENTTYPE_DATA;
|
||||||
|
} else if (strcasecmp(optarg, "publicdata") == 0) {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = NCACONTENTTPYE_PUBLICDATA;
|
||||||
|
} else if ('0' <= optarg[0] && optarg[1] <= '9') {
|
||||||
|
nca_ctx.tool_ctx->settings.expected_content_type = (optarg[0] - '0');
|
||||||
|
} else {
|
||||||
|
/* Failure to parse expected content type. */
|
||||||
|
printf("[WARN] Unknown expected content type (%s).\n", optarg);
|
||||||
|
nca_ctx.tool_ctx->settings.has_expected_content_type = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 43:
|
||||||
|
nca_ctx.tool_ctx->settings.append_section_types = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
193
nca.c
193
nca.c
|
@ -50,7 +50,7 @@ void nca_section_fseek(nca_section_ctx_t *ctx, uint64_t offset) {
|
||||||
ctx->cur_seek = ((ctx->offset + offset - 0x400ULL) & ~ctx->sector_mask) + 0x400ULL;
|
ctx->cur_seek = ((ctx->offset + offset - 0x400ULL) & ~ctx->sector_mask) + 0x400ULL;
|
||||||
ctx->sector_num = (ctx->offset + offset - 0x400ULL) / ctx->sector_size;
|
ctx->sector_num = (ctx->offset + offset - 0x400ULL) / ctx->sector_size;
|
||||||
ctx->sector_ofs = (ctx->offset + offset - 0x400ULL) & ctx->sector_mask;
|
ctx->sector_ofs = (ctx->offset + offset - 0x400ULL) & ctx->sector_mask;
|
||||||
} else if (ctx->type == BKTR && ctx->bktr_ctx.subsection_block != NULL) {
|
} else if (ctx->type == BKTR && ctx->bktr_ctx.subsection_block != NULL) {
|
||||||
/* No better way to do this than to make all BKTR seeking virtual. */
|
/* No better way to do this than to make all BKTR seeking virtual. */
|
||||||
ctx->bktr_ctx.virtual_seek = offset;
|
ctx->bktr_ctx.virtual_seek = offset;
|
||||||
if (ctx->tool_ctx->base_file == NULL && ctx->physical_reads == 0) { /* Without base romfs, reads will be physical. */
|
if (ctx->tool_ctx->base_file == NULL && ctx->physical_reads == 0) { /* Without base romfs, reads will be physical. */
|
||||||
|
@ -78,14 +78,14 @@ static size_t nca_bktr_section_physical_fread(nca_section_ctx_t *ctx, void *buff
|
||||||
size_t read = 0; /* XXX */
|
size_t read = 0; /* XXX */
|
||||||
size_t size = 1;
|
size_t size = 1;
|
||||||
char block_buf[0x10];
|
char block_buf[0x10];
|
||||||
|
|
||||||
if (ctx->is_decrypted) {
|
if (ctx->is_decrypted) {
|
||||||
fseeko64(ctx->file, (ctx->offset + ctx->bktr_ctx.bktr_seek), SEEK_SET);
|
fseeko64(ctx->file, (ctx->offset + ctx->bktr_ctx.bktr_seek), SEEK_SET);
|
||||||
read = fread(buffer, size, count, ctx->file);
|
read = fread(buffer, size, count, ctx->file);
|
||||||
nca_section_fseek(ctx, ctx->bktr_ctx.virtual_seek + read);
|
nca_section_fseek(ctx, ctx->bktr_ctx.virtual_seek + read);
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
bktr_subsection_entry_t *subsec = bktr_get_subsection(ctx->bktr_ctx.subsection_block, ctx->bktr_ctx.bktr_seek);
|
bktr_subsection_entry_t *subsec = bktr_get_subsection(ctx->bktr_ctx.subsection_block, ctx->bktr_ctx.bktr_seek);
|
||||||
nca_update_bktr_ctr(ctx->ctr, subsec->ctr_val, ctx->bktr_ctx.bktr_seek + ctx->offset);
|
nca_update_bktr_ctr(ctx->ctr, subsec->ctr_val, ctx->bktr_ctx.bktr_seek + ctx->offset);
|
||||||
fseeko64(ctx->file, (ctx->offset + ctx->bktr_ctx.bktr_seek) & ~0xF, SEEK_SET);
|
fseeko64(ctx->file, (ctx->offset + ctx->bktr_ctx.bktr_seek) & ~0xF, SEEK_SET);
|
||||||
|
@ -111,7 +111,7 @@ static size_t nca_bktr_section_physical_fread(nca_section_ctx_t *ctx, void *buff
|
||||||
}
|
}
|
||||||
if ((read = fread(buffer, 1, count, ctx->file)) != count) {
|
if ((read = fread(buffer, 1, count, ctx->file)) != count) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
aes_setiv(ctx->aes, ctx->ctr, 16);
|
aes_setiv(ctx->aes, ctx->ctr, 16);
|
||||||
aes_decrypt(ctx->aes, buffer, buffer, count);
|
aes_decrypt(ctx->aes, buffer, buffer, count);
|
||||||
nca_section_fseek(ctx, ctx->bktr_ctx.virtual_seek + count);
|
nca_section_fseek(ctx, ctx->bktr_ctx.virtual_seek + count);
|
||||||
|
@ -126,7 +126,7 @@ static size_t nca_bktr_section_physical_fread(nca_section_ctx_t *ctx, void *buff
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ size_t nca_section_fread(nca_section_ctx_t *ctx, void *buffer, size_t count) {
|
||||||
/* Perform decryption, if necessary. */
|
/* Perform decryption, if necessary. */
|
||||||
/* AES-CTR. */
|
/* AES-CTR. */
|
||||||
if (ctx->crypt_type == CRYPT_CTR || (ctx->crypt_type == CRYPT_BKTR && ctx->bktr_ctx.subsection_block == NULL))
|
if (ctx->crypt_type == CRYPT_CTR || (ctx->crypt_type == CRYPT_BKTR && ctx->bktr_ctx.subsection_block == NULL))
|
||||||
{
|
{
|
||||||
if (ctx->sector_ofs) {
|
if (ctx->sector_ofs) {
|
||||||
if ((read = fread(block_buf, 1, 0x10, ctx->file)) != 0x10) {
|
if ((read = fread(block_buf, 1, 0x10, ctx->file)) != 0x10) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -250,7 +250,7 @@ size_t nca_section_fread(nca_section_ctx_t *ctx, void *buffer, size_t count) {
|
||||||
fprintf(stderr, "Unknown Base File Type!\n");
|
fprintf(stderr, "Unknown Base File Type!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint64_t within_relocation = next_reloc->virt_offset - ctx->bktr_ctx.virtual_seek;
|
uint64_t within_relocation = next_reloc->virt_offset - ctx->bktr_ctx.virtual_seek;
|
||||||
if ((read = nca_section_fread(ctx, buffer, within_relocation)) != within_relocation) {
|
if ((read = nca_section_fread(ctx, buffer, within_relocation)) != within_relocation) {
|
||||||
|
@ -311,6 +311,19 @@ void nca_free_section_contexts(nca_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *nca_get_section_type_name(enum nca_section_type type) {
|
||||||
|
switch (type) {
|
||||||
|
case PFS0:
|
||||||
|
return "pfs0";
|
||||||
|
case ROMFS:
|
||||||
|
case BKTR:
|
||||||
|
case NCA0_ROMFS:
|
||||||
|
return "romfs";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void nca_save(nca_ctx_t *ctx) {
|
static void nca_save(nca_ctx_t *ctx) {
|
||||||
/* Save header. */
|
/* Save header. */
|
||||||
filepath_t *header_path = &ctx->tool_ctx->settings.header_path;
|
filepath_t *header_path = &ctx->tool_ctx->settings.header_path;
|
||||||
|
@ -327,7 +340,7 @@ static void nca_save(nca_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 4; i++) {
|
for (unsigned int i = 0; i < 4; i++) {
|
||||||
if (ctx->section_contexts[i].is_present) {
|
if (ctx->section_contexts[i].is_present) {
|
||||||
/* printf("Saving section %"PRId32"...\n", i); */
|
/* printf("Saving section %"PRId32"...\n", i); */
|
||||||
|
@ -335,7 +348,7 @@ static void nca_save(nca_ctx_t *ctx) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save Decrypted NCA. */
|
/* Save Decrypted NCA. */
|
||||||
filepath_t *dec_path = &ctx->tool_ctx->settings.plaintext_path;
|
filepath_t *dec_path = &ctx->tool_ctx->settings.plaintext_path;
|
||||||
|
|
||||||
|
@ -348,7 +361,7 @@ static void nca_save(nca_ctx_t *ctx) {
|
||||||
fprintf(stderr, "Failed to write header!\n");
|
fprintf(stderr, "Failed to write header!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *buf = malloc(0x400000);
|
unsigned char *buf = malloc(0x400000);
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
fprintf(stderr, "Failed to allocate file-save buffer!\n");
|
fprintf(stderr, "Failed to allocate file-save buffer!\n");
|
||||||
|
@ -358,13 +371,13 @@ static void nca_save(nca_ctx_t *ctx) {
|
||||||
if (ctx->section_contexts[i].is_present) {
|
if (ctx->section_contexts[i].is_present) {
|
||||||
fseeko64(f_dec, ctx->section_contexts[i].offset, SEEK_SET);
|
fseeko64(f_dec, ctx->section_contexts[i].offset, SEEK_SET);
|
||||||
ctx->section_contexts[i].physical_reads = 1;
|
ctx->section_contexts[i].physical_reads = 1;
|
||||||
|
|
||||||
uint64_t read_size = 0x400000; /* 4 MB buffer. */
|
uint64_t read_size = 0x400000; /* 4 MB buffer. */
|
||||||
memset(buf, 0xCC, read_size); /* Debug in case I fuck this up somehow... */
|
memset(buf, 0xCC, read_size); /* Debug in case I fuck this up somehow... */
|
||||||
uint64_t ofs = 0;
|
uint64_t ofs = 0;
|
||||||
uint64_t end_ofs = ofs + ctx->section_contexts[i].size;
|
uint64_t end_ofs = ofs + ctx->section_contexts[i].size;
|
||||||
nca_section_fseek(&ctx->section_contexts[i], ofs);
|
nca_section_fseek(&ctx->section_contexts[i], ofs);
|
||||||
while (ofs < end_ofs) {
|
while (ofs < end_ofs) {
|
||||||
if (ofs + read_size >= end_ofs) read_size = end_ofs - ofs;
|
if (ofs + read_size >= end_ofs) read_size = end_ofs - ofs;
|
||||||
if (nca_section_fread(&ctx->section_contexts[i], buf, read_size) != read_size) {
|
if (nca_section_fread(&ctx->section_contexts[i], buf, read_size) != read_size) {
|
||||||
fprintf(stderr, "Failed to read file!\n");
|
fprintf(stderr, "Failed to read file!\n");
|
||||||
|
@ -380,7 +393,7 @@ static void nca_save(nca_ctx_t *ctx) {
|
||||||
ctx->section_contexts[i].physical_reads = 0;
|
ctx->section_contexts[i].physical_reads = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f_dec);
|
fclose(f_dec);
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
|
@ -418,7 +431,7 @@ void nca_process(nca_ctx_t *ctx) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->is_cli_target && ctx->tool_ctx->base_nca_ctx != NULL) {
|
if (ctx->is_cli_target && ctx->tool_ctx->base_nca_ctx != NULL) {
|
||||||
uint64_t base_tid = ctx->tool_ctx->base_nca_ctx->header.title_id;
|
uint64_t base_tid = ctx->tool_ctx->base_nca_ctx->header.title_id;
|
||||||
uint64_t expectation = ctx->header.title_id & 0xFFFFFFFFFFFFF7FFULL;
|
uint64_t expectation = ctx->header.title_id & 0xFFFFFFFFFFFFF7FFULL;
|
||||||
|
@ -427,6 +440,13 @@ void nca_process(nca_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enforce content type for extraction if required. */
|
||||||
|
if (ctx->tool_ctx->settings.has_expected_content_type) {
|
||||||
|
if (ctx->tool_ctx->settings.expected_content_type != ctx->header.content_type) {
|
||||||
|
ctx->tool_ctx->action &= ~(ACTION_EXTRACT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Decrypt key area if required. */
|
/* Decrypt key area if required. */
|
||||||
if (!ctx->has_rights_id) {
|
if (!ctx->has_rights_id) {
|
||||||
nca_decrypt_key_area(ctx);
|
nca_decrypt_key_area(ctx);
|
||||||
|
@ -445,7 +465,7 @@ void nca_process(nca_ctx_t *ctx) {
|
||||||
/* Parse sections. */
|
/* Parse sections. */
|
||||||
for (unsigned int i = 0; i < 4; i++) {
|
for (unsigned int i = 0; i < 4; i++) {
|
||||||
if (ctx->header.section_entries[i].media_start_offset) { /* Section exists. */
|
if (ctx->header.section_entries[i].media_start_offset) { /* Section exists. */
|
||||||
ctx->section_contexts[i].is_present = 1;
|
ctx->section_contexts[i].is_present = 1;
|
||||||
ctx->section_contexts[i].is_decrypted = ctx->is_decrypted;
|
ctx->section_contexts[i].is_decrypted = ctx->is_decrypted;
|
||||||
ctx->section_contexts[i].tool_ctx = ctx->tool_ctx;
|
ctx->section_contexts[i].tool_ctx = ctx->tool_ctx;
|
||||||
ctx->section_contexts[i].file = ctx->file;
|
ctx->section_contexts[i].file = ctx->file;
|
||||||
|
@ -579,15 +599,15 @@ int nca_decrypt_header(nca_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->is_decrypted = 0;
|
ctx->is_decrypted = 0;
|
||||||
|
|
||||||
nca_header_t dec_header;
|
nca_header_t dec_header;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
aes_ctx_t *hdr_aes_ctx = new_aes_ctx(ctx->tool_ctx->settings.keyset.header_key, 32, AES_MODE_XTS);
|
aes_ctx_t *hdr_aes_ctx = new_aes_ctx(ctx->tool_ctx->settings.keyset.header_key, 32, AES_MODE_XTS);
|
||||||
aes_xts_decrypt(hdr_aes_ctx, &dec_header, &ctx->header, 0x400, 0, 0x200);
|
aes_xts_decrypt(hdr_aes_ctx, &dec_header, &ctx->header, 0x400, 0, 0x200);
|
||||||
|
|
||||||
|
|
||||||
if (dec_header.magic == MAGIC_NCA3) {
|
if (dec_header.magic == MAGIC_NCA3) {
|
||||||
ctx->format_version = NCAVERSION_NCA3;
|
ctx->format_version = NCAVERSION_NCA3;
|
||||||
aes_xts_decrypt(hdr_aes_ctx, &dec_header, &ctx->header, 0xC00, 0, 0x200);
|
aes_xts_decrypt(hdr_aes_ctx, &dec_header, &ctx->header, 0xC00, 0, 0x200);
|
||||||
|
@ -610,7 +630,7 @@ int nca_decrypt_header(nca_ctx_t *ctx) {
|
||||||
if (out_len >= 0x20) {
|
if (out_len >= 0x20) {
|
||||||
memcpy(ctx->decrypted_keys, out_keydata, 0x20);
|
memcpy(ctx->decrypted_keys, out_keydata, 0x20);
|
||||||
ctx->format_version = NCAVERSION_NCA0_BETA;
|
ctx->format_version = NCAVERSION_NCA0_BETA;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unsigned char calc_hash[0x20];
|
unsigned char calc_hash[0x20];
|
||||||
static const unsigned char expected_hash[0x20] = {0x9A, 0xBB, 0xD2, 0x11, 0x86, 0x00, 0x21, 0x9D, 0x7A, 0xDC, 0x5B, 0x43, 0x95, 0xF8, 0x4E, 0xFD, 0xFF, 0x6B, 0x25, 0xEF, 0x9F, 0x96, 0x85, 0x28, 0x18, 0x9E, 0x76, 0xB0, 0x92, 0xF0, 0x6A, 0xCB};
|
static const unsigned char expected_hash[0x20] = {0x9A, 0xBB, 0xD2, 0x11, 0x86, 0x00, 0x21, 0x9D, 0x7A, 0xDC, 0x5B, 0x43, 0x95, 0xF8, 0x4E, 0xFD, 0xFF, 0x6B, 0x25, 0xEF, 0x9F, 0x96, 0x85, 0x28, 0x18, 0x9E, 0x76, 0xB0, 0x92, 0xF0, 0x6A, 0xCB};
|
||||||
|
@ -643,7 +663,7 @@ int nca_decrypt_header(nca_ctx_t *ctx) {
|
||||||
ctx->header = dec_header;
|
ctx->header = dec_header;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free_aes_ctx(hdr_aes_ctx);
|
free_aes_ctx(hdr_aes_ctx);
|
||||||
return ctx->format_version != NCAVERSION_UNKNOWN;
|
return ctx->format_version != NCAVERSION_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -748,7 +768,7 @@ static const char *nca_get_section_type(nca_section_ctx_t *meta) {
|
||||||
static void nca_print_sections(nca_ctx_t *ctx) {
|
static void nca_print_sections(nca_ctx_t *ctx) {
|
||||||
printf("Sections:\n");
|
printf("Sections:\n");
|
||||||
for (unsigned int i = 0; i < 4; i++) {
|
for (unsigned int i = 0; i < 4; i++) {
|
||||||
if (ctx->section_contexts[i].is_present) { /* Section exists. */
|
if (ctx->section_contexts[i].is_present) { /* Section exists. */
|
||||||
printf(" Section %"PRId32":\n", i);
|
printf(" Section %"PRId32":\n", i);
|
||||||
printf(" Offset: 0x%012"PRIx64"\n", ctx->section_contexts[i].offset);
|
printf(" Offset: 0x%012"PRIx64"\n", ctx->section_contexts[i].offset);
|
||||||
printf(" Size: 0x%012"PRIx64"\n", ctx->section_contexts[i].size);
|
printf(" Size: 0x%012"PRIx64"\n", ctx->section_contexts[i].size);
|
||||||
|
@ -815,7 +835,7 @@ void nca_print(nca_ctx_t *ctx) {
|
||||||
printf("Master Key Revision: %"PRIx8" (%s)\n", ctx->crypto_type, get_key_revision_summary(ctx->crypto_type));
|
printf("Master Key Revision: %"PRIx8" (%s)\n", ctx->crypto_type, get_key_revision_summary(ctx->crypto_type));
|
||||||
printf("Encryption Type: %s\n", nca_get_encryption_type(ctx));
|
printf("Encryption Type: %s\n", nca_get_encryption_type(ctx));
|
||||||
|
|
||||||
if (ctx->has_rights_id) {
|
if (ctx->has_rights_id) {
|
||||||
memdump(stdout, "Rights ID: ", &ctx->header.rights_id, 0x10);
|
memdump(stdout, "Rights ID: ", &ctx->header.rights_id, 0x10);
|
||||||
if (ctx->is_cli_target && ctx->tool_ctx->settings.has_cli_titlekey) {
|
if (ctx->is_cli_target && ctx->tool_ctx->settings.has_cli_titlekey) {
|
||||||
memdump(stdout, "Titlekey (Encrypted) (From CLI) ", ctx->tool_ctx->settings.cli_titlekey, 0x10);
|
memdump(stdout, "Titlekey (Encrypted) (From CLI) ", ctx->tool_ctx->settings.cli_titlekey, 0x10);
|
||||||
|
@ -867,7 +887,7 @@ static validity_t nca_section_check_external_hash_table(nca_section_ctx_t *ctx,
|
||||||
if (nca_section_fread(ctx, block, read_size) != read_size) {
|
if (nca_section_fread(ctx, block, read_size) != read_size) {
|
||||||
fprintf(stderr, "Failed to read section!\n");
|
fprintf(stderr, "Failed to read section!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
sha256_hash_buffer(cur_hash, block, full_block ? block_size : read_size);
|
sha256_hash_buffer(cur_hash, block, full_block ? block_size : read_size);
|
||||||
if (memcmp(cur_hash, cur_hash_table_entry, 0x20) != 0) {
|
if (memcmp(cur_hash, cur_hash_table_entry, 0x20) != 0) {
|
||||||
result = VALIDITY_INVALID;
|
result = VALIDITY_INVALID;
|
||||||
|
@ -885,7 +905,7 @@ static validity_t nca_section_check_hash_table(nca_section_ctx_t *ctx, uint64_t
|
||||||
if (block_size == 0) {
|
if (block_size == 0) {
|
||||||
/* Block size of 0 is always invalid. */
|
/* Block size of 0 is always invalid. */
|
||||||
return VALIDITY_INVALID;
|
return VALIDITY_INVALID;
|
||||||
}
|
}
|
||||||
uint64_t hash_table_size = data_len / block_size;
|
uint64_t hash_table_size = data_len / block_size;
|
||||||
if (data_len % block_size) hash_table_size++;
|
if (data_len % block_size) hash_table_size++;
|
||||||
hash_table_size *= 0x20;
|
hash_table_size *= 0x20;
|
||||||
|
@ -936,7 +956,7 @@ static void nca_save_pfs0_file(nca_section_ctx_t *ctx, uint32_t i, filepath_t *d
|
||||||
|
|
||||||
void nca_process_pfs0_section(nca_section_ctx_t *ctx) {
|
void nca_process_pfs0_section(nca_section_ctx_t *ctx) {
|
||||||
pfs0_superblock_t *sb = ctx->pfs0_ctx.superblock;
|
pfs0_superblock_t *sb = ctx->pfs0_ctx.superblock;
|
||||||
ctx->superblock_hash_validity = nca_section_check_external_hash_table(ctx, sb->master_hash, sb->hash_table_offset, sb->hash_table_size, sb->hash_table_size, 0);
|
ctx->superblock_hash_validity = nca_section_check_external_hash_table(ctx, sb->master_hash, sb->hash_table_offset, sb->hash_table_size, sb->hash_table_size, 0);
|
||||||
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
||||||
/* Verify actual PFS0... */
|
/* Verify actual PFS0... */
|
||||||
ctx->pfs0_ctx.hash_table_validity = nca_section_check_hash_table(ctx, sb->hash_table_offset, sb->pfs0_offset, sb->pfs0_size, sb->block_size, 0);
|
ctx->pfs0_ctx.hash_table_validity = nca_section_check_hash_table(ctx, sb->hash_table_offset, sb->pfs0_offset, sb->pfs0_size, sb->block_size, 0);
|
||||||
|
@ -945,7 +965,7 @@ void nca_process_pfs0_section(nca_section_ctx_t *ctx) {
|
||||||
if (ctx->superblock_hash_validity != VALIDITY_VALID) return;
|
if (ctx->superblock_hash_validity != VALIDITY_VALID) return;
|
||||||
|
|
||||||
/* Read *just* safe amount. */
|
/* Read *just* safe amount. */
|
||||||
pfs0_header_t raw_header;
|
pfs0_header_t raw_header;
|
||||||
nca_section_fseek(ctx, sb->pfs0_offset);
|
nca_section_fseek(ctx, sb->pfs0_offset);
|
||||||
if (nca_section_fread(ctx, &raw_header, sizeof(raw_header)) != sizeof(raw_header)) {
|
if (nca_section_fread(ctx, &raw_header, sizeof(raw_header)) != sizeof(raw_header)) {
|
||||||
fprintf(stderr, "Failed to read PFS0 header!\n");
|
fprintf(stderr, "Failed to read PFS0 header!\n");
|
||||||
|
@ -1052,7 +1072,7 @@ void nca_process_ivfc_section(nca_section_ctx_t *ctx) {
|
||||||
|
|
||||||
void nca_process_nca0_romfs_section(nca_section_ctx_t *ctx) {
|
void nca_process_nca0_romfs_section(nca_section_ctx_t *ctx) {
|
||||||
nca0_romfs_superblock_t *sb = ctx->nca0_romfs_ctx.superblock;
|
nca0_romfs_superblock_t *sb = ctx->nca0_romfs_ctx.superblock;
|
||||||
ctx->superblock_hash_validity = nca_section_check_external_hash_table(ctx, sb->master_hash, sb->hash_table_offset, sb->hash_table_size, sb->hash_table_size, 0);
|
ctx->superblock_hash_validity = nca_section_check_external_hash_table(ctx, sb->master_hash, sb->hash_table_offset, sb->hash_table_size, sb->hash_table_size, 0);
|
||||||
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
||||||
/* Verify actual ROMFS... */
|
/* Verify actual ROMFS... */
|
||||||
ctx->nca0_romfs_ctx.hash_table_validity = nca_section_check_hash_table(ctx, sb->hash_table_offset, sb->romfs_offset, sb->romfs_size, sb->block_size, 0);
|
ctx->nca0_romfs_ctx.hash_table_validity = nca_section_check_hash_table(ctx, sb->hash_table_offset, sb->romfs_offset, sb->romfs_size, sb->block_size, 0);
|
||||||
|
@ -1124,11 +1144,11 @@ void nca_process_bktr_section(nca_section_ctx_t *ctx) {
|
||||||
fprintf(stderr, "Failed to read subsection header!\n");
|
fprintf(stderr, "Failed to read subsection header!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE: Setting these variables changes the way fseek/fread work! */
|
/* NOTE: Setting these variables changes the way fseek/fread work! */
|
||||||
ctx->bktr_ctx.relocation_block = relocs;
|
ctx->bktr_ctx.relocation_block = relocs;
|
||||||
ctx->bktr_ctx.subsection_block = subs;
|
ctx->bktr_ctx.subsection_block = subs;
|
||||||
|
|
||||||
if (ctx->bktr_ctx.subsection_block->total_size != sb->subsection_header.offset) {
|
if (ctx->bktr_ctx.subsection_block->total_size != sb->subsection_header.offset) {
|
||||||
free(relocs);
|
free(relocs);
|
||||||
free(subs);
|
free(subs);
|
||||||
|
@ -1137,7 +1157,7 @@ void nca_process_bktr_section(nca_section_ctx_t *ctx) {
|
||||||
ctx->superblock_hash_validity = VALIDITY_INVALID;
|
ctx->superblock_hash_validity = VALIDITY_INVALID;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This simplifies logic greatly... */
|
/* This simplifies logic greatly... */
|
||||||
for (unsigned int i = ctx->bktr_ctx.relocation_block->num_buckets - 1; i > 0; i--) {
|
for (unsigned int i = ctx->bktr_ctx.relocation_block->num_buckets - 1; i > 0; i--) {
|
||||||
memcpy(bktr_get_relocation_bucket(ctx->bktr_ctx.relocation_block, i), &ctx->bktr_ctx.relocation_block->buckets[i], sizeof(bktr_relocation_bucket_t));
|
memcpy(bktr_get_relocation_bucket(ctx->bktr_ctx.relocation_block, i), &ctx->bktr_ctx.relocation_block->buckets[i], sizeof(bktr_relocation_bucket_t));
|
||||||
|
@ -1162,7 +1182,7 @@ void nca_process_bktr_section(nca_section_ctx_t *ctx) {
|
||||||
last_subsec_bucket->entries[last_subsec_bucket->num_entries].ctr_val = ctx->header->section_ctr_low;
|
last_subsec_bucket->entries[last_subsec_bucket->num_entries].ctr_val = ctx->header->section_ctr_low;
|
||||||
last_subsec_bucket->entries[last_subsec_bucket->num_entries + 1].offset = ctx->size;
|
last_subsec_bucket->entries[last_subsec_bucket->num_entries + 1].offset = ctx->size;
|
||||||
last_subsec_bucket->entries[last_subsec_bucket->num_entries + 1].ctr_val = 0;
|
last_subsec_bucket->entries[last_subsec_bucket->num_entries + 1].ctr_val = 0;
|
||||||
|
|
||||||
/* Now parse out the romfs stuff. */
|
/* Now parse out the romfs stuff. */
|
||||||
for (unsigned int i = 0; i < IVFC_MAX_LEVEL; i++) {
|
for (unsigned int i = 0; i < IVFC_MAX_LEVEL; i++) {
|
||||||
/* Load in the current level's header data. */
|
/* Load in the current level's header data. */
|
||||||
|
@ -1249,7 +1269,7 @@ void nca_print_ivfc_section(nca_section_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
memdump(stdout, " Superblock Hash: ", &ctx->romfs_ctx.superblock->ivfc_header.master_hash, 0x20);
|
memdump(stdout, " Superblock Hash: ", &ctx->romfs_ctx.superblock->ivfc_header.master_hash, 0x20);
|
||||||
}
|
}
|
||||||
print_magic(" Magic: ", ctx->romfs_ctx.superblock->ivfc_header.magic);
|
print_magic(" Magic: ", ctx->romfs_ctx.superblock->ivfc_header.magic);
|
||||||
printf(" ID: %08"PRIx32"\n", ctx->romfs_ctx.superblock->ivfc_header.id);
|
printf(" ID: %08"PRIx32"\n", ctx->romfs_ctx.superblock->ivfc_header.id);
|
||||||
for (unsigned int i = 0; i < IVFC_MAX_LEVEL; i++) {
|
for (unsigned int i = 0; i < IVFC_MAX_LEVEL; i++) {
|
||||||
|
@ -1298,7 +1318,7 @@ void nca_print_bktr_section(nca_section_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
memdump(stdout, " Superblock Hash: ", &ctx->bktr_ctx.superblock->ivfc_header.master_hash, 0x20);
|
memdump(stdout, " Superblock Hash: ", &ctx->bktr_ctx.superblock->ivfc_header.master_hash, 0x20);
|
||||||
}
|
}
|
||||||
print_magic(" Magic: ", ctx->bktr_ctx.superblock->ivfc_header.magic);
|
print_magic(" Magic: ", ctx->bktr_ctx.superblock->ivfc_header.magic);
|
||||||
printf(" ID: %08"PRIx32"\n", ctx->bktr_ctx.superblock->ivfc_header.id);
|
printf(" ID: %08"PRIx32"\n", ctx->bktr_ctx.superblock->ivfc_header.id);
|
||||||
for (unsigned int i = 0; i < IVFC_MAX_LEVEL; i++) {
|
for (unsigned int i = 0; i < IVFC_MAX_LEVEL; i++) {
|
||||||
|
@ -1314,7 +1334,7 @@ void nca_print_bktr_section(nca_section_ctx_t *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nca_save_section_file(nca_section_ctx_t *ctx, uint64_t ofs, uint64_t total_size, filepath_t *filepath) {
|
void nca_save_section_file(nca_section_ctx_t *ctx, uint64_t ofs, uint64_t total_size, filepath_t *filepath) {
|
||||||
FILE *f_out = os_fopen(filepath->os_path, OS_MODE_WRITE);
|
FILE *f_out = os_fopen(filepath->os_path, OS_MODE_WRITE);
|
||||||
|
|
||||||
if (f_out == NULL) {
|
if (f_out == NULL) {
|
||||||
|
@ -1330,7 +1350,7 @@ void nca_save_section_file(nca_section_ctx_t *ctx, uint64_t ofs, uint64_t total_
|
||||||
}
|
}
|
||||||
memset(buf, 0xCC, read_size); /* Debug in case I fuck this up somehow... */
|
memset(buf, 0xCC, read_size); /* Debug in case I fuck this up somehow... */
|
||||||
uint64_t end_ofs = ofs + total_size;
|
uint64_t end_ofs = ofs + total_size;
|
||||||
while (ofs < end_ofs) {
|
while (ofs < end_ofs) {
|
||||||
nca_section_fseek(ctx, ofs);
|
nca_section_fseek(ctx, ofs);
|
||||||
if (ofs + read_size >= end_ofs) read_size = end_ofs - ofs;
|
if (ofs + read_size >= end_ofs) read_size = end_ofs - ofs;
|
||||||
if (nca_section_fread(ctx, buf, read_size) != read_size) {
|
if (nca_section_fread(ctx, buf, read_size) != read_size) {
|
||||||
|
@ -1379,7 +1399,7 @@ void nca_save_section(nca_section_ctx_t *ctx) {
|
||||||
} else if (ctx->type == BKTR && ctx->bktr_ctx.subsection_block != NULL && ctx->tool_ctx->base_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->total_size;
|
size = ctx->bktr_ctx.relocation_block->total_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract to file. */
|
/* Extract to file. */
|
||||||
filepath_t *secpath = &ctx->tool_ctx->settings.section_paths[ctx->section_num];
|
filepath_t *secpath = &ctx->tool_ctx->settings.section_paths[ctx->section_num];
|
||||||
|
|
||||||
|
@ -1389,7 +1409,20 @@ void nca_save_section(nca_section_ctx_t *ctx) {
|
||||||
} else if ((ctx->type == ROMFS || ctx->type == NCA0_ROMFS) && ctx->tool_ctx->settings.romfs_path.enabled && ctx->tool_ctx->settings.romfs_path.path.valid == VALIDITY_VALID) {
|
} else if ((ctx->type == ROMFS || ctx->type == NCA0_ROMFS) && ctx->tool_ctx->settings.romfs_path.enabled && ctx->tool_ctx->settings.romfs_path.path.valid == VALIDITY_VALID) {
|
||||||
secpath = &ctx->tool_ctx->settings.romfs_path.path;
|
secpath = &ctx->tool_ctx->settings.romfs_path.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (secpath != NULL && secpath->valid == VALIDITY_VALID) {
|
if (secpath != NULL && secpath->valid == VALIDITY_VALID) {
|
||||||
|
filepath_t appended_path;
|
||||||
|
filepath_init(&appended_path);
|
||||||
|
filepath_copy(&appended_path, secpath);
|
||||||
|
if (ctx->tool_ctx->settings.append_section_types) {
|
||||||
|
filepath_set_format(&appended_path, "%s.%s", secpath->char_path, nca_get_section_type_name(ctx->type));
|
||||||
|
if (appended_path.valid == VALIDITY_VALID) {
|
||||||
|
secpath = &appended_path;
|
||||||
|
} else {
|
||||||
|
printf("[WARN] Failed to append section type to path\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printf("Saving Section %"PRId32" to %s...\n", ctx->section_num, secpath->char_path);
|
printf("Saving Section %"PRId32" to %s...\n", ctx->section_num, secpath->char_path);
|
||||||
printf("Size: %012"PRIx64"\n", size);
|
printf("Size: %012"PRIx64"\n", size);
|
||||||
nca_save_section_file(ctx, offset, size, secpath);
|
nca_save_section_file(ctx, offset, size, secpath);
|
||||||
|
@ -1428,6 +1461,18 @@ void nca_save_pfs0_section(nca_section_ctx_t *ctx) {
|
||||||
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
||||||
}
|
}
|
||||||
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
||||||
|
filepath_t appended_path;
|
||||||
|
filepath_init(&appended_path);
|
||||||
|
filepath_copy(&appended_path, dirpath);
|
||||||
|
if (ctx->tool_ctx->settings.append_section_types) {
|
||||||
|
filepath_set_format(&appended_path, "%s_%s", dirpath->char_path, nca_get_section_type_name(ctx->type));
|
||||||
|
if (appended_path.valid == VALIDITY_VALID) {
|
||||||
|
dirpath = &appended_path;
|
||||||
|
} else {
|
||||||
|
printf("[WARN] Failed to append section type to path\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
os_makedir(dirpath->os_path);
|
os_makedir(dirpath->os_path);
|
||||||
for (uint32_t i = 0; i < ctx->pfs0_ctx.header->num_files; i++) {
|
for (uint32_t i = 0; i < ctx->pfs0_ctx.header->num_files; i++) {
|
||||||
nca_save_pfs0_file(ctx, i, dirpath);
|
nca_save_pfs0_file(ctx, i, dirpath);
|
||||||
|
@ -1445,19 +1490,19 @@ static int nca_is_romfs_file_updated(nca_section_ctx_t *ctx, uint64_t file_offse
|
||||||
if (ctx->type == ROMFS) {
|
if (ctx->type == ROMFS) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bktr_relocation_entry_t *first_reloc = bktr_get_relocation(ctx->bktr_ctx.relocation_block, file_offset);
|
bktr_relocation_entry_t *first_reloc = bktr_get_relocation(ctx->bktr_ctx.relocation_block, file_offset);
|
||||||
bktr_relocation_entry_t *last_reloc = first_reloc;
|
bktr_relocation_entry_t *last_reloc = first_reloc;
|
||||||
while (last_reloc->virt_offset < file_offset + file_size) {
|
while (last_reloc->virt_offset < file_offset + file_size) {
|
||||||
last_reloc++;
|
last_reloc++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (bktr_relocation_entry_t *cur_reloc = first_reloc; cur_reloc < last_reloc; cur_reloc++) {
|
for (bktr_relocation_entry_t *cur_reloc = first_reloc; cur_reloc < last_reloc; cur_reloc++) {
|
||||||
if (cur_reloc->is_patch) {
|
if (cur_reloc->is_patch) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1478,7 +1523,7 @@ static int nca_visit_romfs_file(nca_section_ctx_t *ctx, uint32_t file_offset, fi
|
||||||
if (entry->name_size) {
|
if (entry->name_size) {
|
||||||
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int found_file = 1;
|
int found_file = 1;
|
||||||
|
|
||||||
/* If we're extracting... */
|
/* If we're extracting... */
|
||||||
|
@ -1504,7 +1549,7 @@ static int nca_visit_romfs_file(nca_section_ctx_t *ctx, uint32_t file_offset, fi
|
||||||
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
||||||
return found_file | nca_visit_romfs_file(ctx, entry->sibling, dir_path);
|
return found_file | nca_visit_romfs_file(ctx, entry->sibling, dir_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found_file;
|
return found_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1520,12 +1565,12 @@ static int nca_visit_nca0_romfs_file(nca_section_ctx_t *ctx, uint32_t file_offse
|
||||||
if (entry->name_size) {
|
if (entry->name_size) {
|
||||||
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int found_file = 1;
|
int found_file = 1;
|
||||||
|
|
||||||
/* If we're extracting... */
|
/* If we're extracting... */
|
||||||
uint64_t phys_offset = ctx->nca0_romfs_ctx.romfs_offset + ctx->nca0_romfs_ctx.header.data_offset + entry->offset;
|
uint64_t phys_offset = ctx->nca0_romfs_ctx.romfs_offset + ctx->nca0_romfs_ctx.header.data_offset + entry->offset;
|
||||||
|
|
||||||
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
||||||
printf("Saving %s...\n", cur_path->char_path);
|
printf("Saving %s...\n", cur_path->char_path);
|
||||||
nca_save_section_file(ctx, phys_offset, entry->size, cur_path);
|
nca_save_section_file(ctx, phys_offset, entry->size, cur_path);
|
||||||
|
@ -1538,7 +1583,7 @@ static int nca_visit_nca0_romfs_file(nca_section_ctx_t *ctx, uint32_t file_offse
|
||||||
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
||||||
return found_file | nca_visit_nca0_romfs_file(ctx, entry->sibling, dir_path);
|
return found_file | nca_visit_nca0_romfs_file(ctx, entry->sibling, dir_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found_file;
|
return found_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1548,13 +1593,13 @@ static int nca_visit_romfs_dir(nca_section_ctx_t *ctx, uint32_t dir_offset, file
|
||||||
entry = romfs_get_direntry(ctx->romfs_ctx.directories, dir_offset);
|
entry = romfs_get_direntry(ctx->romfs_ctx.directories, dir_offset);
|
||||||
} else {
|
} else {
|
||||||
entry = romfs_get_direntry(ctx->bktr_ctx.directories, dir_offset);
|
entry = romfs_get_direntry(ctx->bktr_ctx.directories, dir_offset);
|
||||||
}
|
}
|
||||||
filepath_t *cur_path = calloc(1, sizeof(filepath_t));
|
filepath_t *cur_path = calloc(1, sizeof(filepath_t));
|
||||||
if (cur_path == NULL) {
|
if (cur_path == NULL) {
|
||||||
fprintf(stderr, "Failed to allocate filepath!\n");
|
fprintf(stderr, "Failed to allocate filepath!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
filepath_copy(cur_path, parent_path);
|
filepath_copy(cur_path, parent_path);
|
||||||
if (entry->name_size) {
|
if (entry->name_size) {
|
||||||
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
||||||
|
@ -1564,7 +1609,7 @@ static int nca_visit_romfs_dir(nca_section_ctx_t *ctx, uint32_t dir_offset, file
|
||||||
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
||||||
os_makedir(cur_path->os_path);
|
os_makedir(cur_path->os_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int any_files = 0;
|
int any_files = 0;
|
||||||
|
|
||||||
if (entry->file != ROMFS_ENTRY_EMPTY) {
|
if (entry->file != ROMFS_ENTRY_EMPTY) {
|
||||||
|
@ -1573,16 +1618,16 @@ static int nca_visit_romfs_dir(nca_section_ctx_t *ctx, uint32_t dir_offset, file
|
||||||
if (entry->child != ROMFS_ENTRY_EMPTY) {
|
if (entry->child != ROMFS_ENTRY_EMPTY) {
|
||||||
any_files |= nca_visit_romfs_dir(ctx, entry->child, cur_path);
|
any_files |= nca_visit_romfs_dir(ctx, entry->child, cur_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any_files == 0 && ctx->type == BKTR && (ctx->tool_ctx->action & ACTION_ONLYUPDATEDROMFS)) {
|
if (any_files == 0 && ctx->type == BKTR && (ctx->tool_ctx->action & ACTION_ONLYUPDATEDROMFS)) {
|
||||||
os_rmdir(cur_path->os_path);
|
os_rmdir(cur_path->os_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
||||||
nca_visit_romfs_dir(ctx, entry->sibling, parent_path);
|
nca_visit_romfs_dir(ctx, entry->sibling, parent_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(cur_path);
|
free(cur_path);
|
||||||
return any_files;
|
return any_files;
|
||||||
}
|
}
|
||||||
|
@ -1604,7 +1649,7 @@ static int nca_visit_nca0_romfs_dir(nca_section_ctx_t *ctx, uint32_t dir_offset,
|
||||||
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
||||||
os_makedir(cur_path->os_path);
|
os_makedir(cur_path->os_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int any_files = 0;
|
int any_files = 0;
|
||||||
|
|
||||||
if (entry->file != ROMFS_ENTRY_EMPTY) {
|
if (entry->file != ROMFS_ENTRY_EMPTY) {
|
||||||
|
@ -1613,11 +1658,11 @@ static int nca_visit_nca0_romfs_dir(nca_section_ctx_t *ctx, uint32_t dir_offset,
|
||||||
if (entry->child != ROMFS_ENTRY_EMPTY) {
|
if (entry->child != ROMFS_ENTRY_EMPTY) {
|
||||||
any_files |= nca_visit_nca0_romfs_file(ctx, entry->child, cur_path);
|
any_files |= nca_visit_nca0_romfs_file(ctx, entry->child, cur_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
if (entry->sibling != ROMFS_ENTRY_EMPTY) {
|
||||||
nca_visit_nca0_romfs_dir(ctx, entry->sibling, parent_path);
|
nca_visit_nca0_romfs_dir(ctx, entry->sibling, parent_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(cur_path);
|
free(cur_path);
|
||||||
return any_files;
|
return any_files;
|
||||||
}
|
}
|
||||||
|
@ -1640,6 +1685,18 @@ void nca_save_ivfc_section(nca_section_ctx_t *ctx) {
|
||||||
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
||||||
}
|
}
|
||||||
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
||||||
|
filepath_t appended_path;
|
||||||
|
filepath_init(&appended_path);
|
||||||
|
filepath_copy(&appended_path, dirpath);
|
||||||
|
if (ctx->tool_ctx->settings.append_section_types) {
|
||||||
|
filepath_set_format(&appended_path, "%s_%s", dirpath->char_path, nca_get_section_type_name(ctx->type));
|
||||||
|
if (appended_path.valid == VALIDITY_VALID) {
|
||||||
|
dirpath = &appended_path;
|
||||||
|
} else {
|
||||||
|
printf("[WARN] Failed to append section type to path\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
os_makedir(dirpath->os_path);
|
os_makedir(dirpath->os_path);
|
||||||
nca_visit_romfs_dir(ctx, 0, dirpath);
|
nca_visit_romfs_dir(ctx, 0, dirpath);
|
||||||
}
|
}
|
||||||
|
@ -1671,6 +1728,18 @@ void nca_save_nca0_romfs_section(nca_section_ctx_t *ctx) {
|
||||||
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
||||||
}
|
}
|
||||||
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
||||||
|
filepath_t appended_path;
|
||||||
|
filepath_init(&appended_path);
|
||||||
|
filepath_copy(&appended_path, dirpath);
|
||||||
|
if (ctx->tool_ctx->settings.append_section_types) {
|
||||||
|
filepath_set_format(&appended_path, "%s_%s", dirpath->char_path, nca_get_section_type_name(ctx->type));
|
||||||
|
if (appended_path.valid == VALIDITY_VALID) {
|
||||||
|
dirpath = &appended_path;
|
||||||
|
} else {
|
||||||
|
printf("[WARN] Failed to append section type to path\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
os_makedir(dirpath->os_path);
|
os_makedir(dirpath->os_path);
|
||||||
nca_visit_nca0_romfs_dir(ctx, 0, dirpath);
|
nca_visit_nca0_romfs_dir(ctx, 0, dirpath);
|
||||||
}
|
}
|
||||||
|
@ -1701,6 +1770,18 @@ void nca_save_bktr_section(nca_section_ctx_t *ctx) {
|
||||||
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
dirpath = &ctx->tool_ctx->settings.section_dir_paths[ctx->section_num];
|
||||||
}
|
}
|
||||||
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
||||||
|
filepath_t appended_path;
|
||||||
|
filepath_init(&appended_path);
|
||||||
|
filepath_copy(&appended_path, dirpath);
|
||||||
|
if (ctx->tool_ctx->settings.append_section_types) {
|
||||||
|
filepath_set_format(&appended_path, "%s_%s", dirpath->char_path, nca_get_section_type_name(ctx->type));
|
||||||
|
if (appended_path.valid == VALIDITY_VALID) {
|
||||||
|
dirpath = &appended_path;
|
||||||
|
} else {
|
||||||
|
printf("[WARN] Failed to append section type to path\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
os_makedir(dirpath->os_path);
|
os_makedir(dirpath->os_path);
|
||||||
nca_visit_romfs_dir(ctx, 0, dirpath);
|
nca_visit_romfs_dir(ctx, 0, dirpath);
|
||||||
}
|
}
|
||||||
|
|
9
nca.h
9
nca.h
|
@ -135,6 +135,15 @@ enum nca_version {
|
||||||
NCAVERSION_NCA3
|
NCAVERSION_NCA3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum nca_content_type {
|
||||||
|
NCACONTENTTYPE_PROGRAM = 0,
|
||||||
|
NCACONTENTTYPE_META = 1,
|
||||||
|
NCACONTENTTYPE_CONTROL = 2,
|
||||||
|
NCACONTENTTYPE_MANUAL = 3,
|
||||||
|
NCACONTENTTYPE_DATA = 4,
|
||||||
|
NCACONTENTTPYE_PUBLICDATA = 5,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int is_present;
|
int is_present;
|
||||||
enum nca_section_type type;
|
enum nca_section_type type;
|
||||||
|
|
|
@ -81,6 +81,9 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nca_keyset_t keyset;
|
nca_keyset_t keyset;
|
||||||
int skip_key_warnings;
|
int skip_key_warnings;
|
||||||
|
int has_expected_content_type;
|
||||||
|
unsigned int expected_content_type;
|
||||||
|
int append_section_types;
|
||||||
int has_cli_titlekey;
|
int has_cli_titlekey;
|
||||||
unsigned char cli_titlekey[0x10];
|
unsigned char cli_titlekey[0x10];
|
||||||
unsigned char dec_cli_titlekey[0x10];
|
unsigned char dec_cli_titlekey[0x10];
|
||||||
|
|
Loading…
Reference in a new issue