mirror of
https://github.com/SciresM/hactool
synced 2024-11-10 14:44:14 +00:00
Add support for KIP1 decompression.
This commit is contained in:
parent
773dfc3589
commit
9b13c5cd7c
3 changed files with 118 additions and 15 deletions
116
kip.c
116
kip.c
|
@ -129,6 +129,81 @@ const char *kip1_get_json(kip1_ctx_t *ctx) {
|
||||||
return output_str;
|
return output_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kip1_blz_uncompress(void *hdr_end) {
|
||||||
|
uint32_t addl_size = ((uint32_t *)hdr_end)[-1];
|
||||||
|
uint32_t header_size = ((uint32_t *)hdr_end)[-2];
|
||||||
|
uint32_t cmp_and_hdr_size = ((uint32_t *)hdr_end)[-3];
|
||||||
|
|
||||||
|
unsigned char *cmp_start = (unsigned char *)(((uintptr_t)hdr_end) - cmp_and_hdr_size);
|
||||||
|
uint32_t cmp_ofs = cmp_and_hdr_size - header_size;
|
||||||
|
uint32_t out_ofs = cmp_and_hdr_size + addl_size;
|
||||||
|
|
||||||
|
while (out_ofs) {
|
||||||
|
unsigned char control = cmp_start[--cmp_ofs];
|
||||||
|
for (unsigned int i = 0; i < 8; i++) {
|
||||||
|
if (control & 0x80) {
|
||||||
|
if (cmp_ofs < 2) {
|
||||||
|
fprintf(stderr, "KIP1 decompression out of bounds!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
cmp_ofs -= 2;
|
||||||
|
uint16_t seg_val = ((unsigned int)cmp_start[cmp_ofs+1] << 8) | cmp_start[cmp_ofs];
|
||||||
|
uint32_t seg_size = ((seg_val >> 12) & 0xF) + 3;
|
||||||
|
uint32_t seg_ofs = (seg_val & 0x0FFF) + 3;
|
||||||
|
if (out_ofs < seg_size) {
|
||||||
|
/* Kernel restricts segment copy to stay in bounds. */
|
||||||
|
seg_size = out_ofs;
|
||||||
|
}
|
||||||
|
out_ofs -= seg_size;
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < seg_size; j++) {
|
||||||
|
cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Copy directly. */
|
||||||
|
if (cmp_ofs < 1) {
|
||||||
|
fprintf(stderr, "KIP1 decompression out of bounds!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
cmp_start[--out_ofs] = cmp_start[--cmp_ofs];
|
||||||
|
}
|
||||||
|
control <<= 1;
|
||||||
|
if (out_ofs == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *kip1_uncompress(kip1_ctx_t *ctx, uint64_t *size) {
|
||||||
|
/* Make new header with correct sizes, fixed flags. */
|
||||||
|
kip1_header_t new_header = *ctx->header;
|
||||||
|
for (unsigned int i = 0; i < 3; i++) {
|
||||||
|
new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size;
|
||||||
|
}
|
||||||
|
new_header.flags &= 0xF8;
|
||||||
|
|
||||||
|
*size = kip1_get_size_from_header(&new_header);
|
||||||
|
unsigned char *new_kip = calloc(1, *size);
|
||||||
|
if (new_kip == NULL) {
|
||||||
|
fprintf(stderr, "Failed to allocate uncompressed KIP1!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
*((kip1_header_t *)new_kip) = new_header;
|
||||||
|
|
||||||
|
uint64_t new_offset = 0x100;
|
||||||
|
uint64_t old_offset = 0x100;
|
||||||
|
for (unsigned int i = 0; i < 3; i++) {
|
||||||
|
// Copy in section data */
|
||||||
|
memcpy(new_kip + new_offset, (unsigned char *)ctx->header + old_offset, ctx->header->section_headers[i].compressed_size);
|
||||||
|
kip1_blz_uncompress(new_kip + new_offset + ctx->header->section_headers[i].compressed_size);
|
||||||
|
new_offset += ctx->header->section_headers[i].out_size;
|
||||||
|
old_offset += ctx->header->section_headers[i].compressed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_kip;
|
||||||
|
}
|
||||||
|
|
||||||
void kip1_process(kip1_ctx_t *ctx) {
|
void kip1_process(kip1_ctx_t *ctx) {
|
||||||
/* Read *just* safe amount. */
|
/* Read *just* safe amount. */
|
||||||
kip1_header_t raw_header;
|
kip1_header_t raw_header;
|
||||||
|
@ -146,7 +221,7 @@ void kip1_process(kip1_ctx_t *ctx) {
|
||||||
uint64_t size = kip1_get_size_from_header(&raw_header);
|
uint64_t size = kip1_get_size_from_header(&raw_header);
|
||||||
ctx->header = malloc(size);
|
ctx->header = malloc(size);
|
||||||
if (ctx->header == NULL) {
|
if (ctx->header == NULL) {
|
||||||
fprintf(stderr, "Failed to allocate KIP1 header!\n");
|
fprintf(stderr, "Failed to allocate KIP1!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,19 +263,34 @@ void kip1_print(kip1_ctx_t *ctx, int suppress) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void kip1_save(kip1_ctx_t *ctx) {
|
void kip1_save(kip1_ctx_t *ctx) {
|
||||||
/* Do nothing. */
|
|
||||||
filepath_t *json_path = &ctx->tool_ctx->settings.npdm_json_path;
|
filepath_t *json_path = &ctx->tool_ctx->settings.npdm_json_path;
|
||||||
if (ctx->tool_ctx->file_type == FILETYPE_KIP1 && json_path->valid == VALIDITY_VALID) {
|
filepath_t *uncmp_path = &ctx->tool_ctx->settings.uncompressed_path;
|
||||||
FILE *f_json = os_fopen(json_path->os_path, OS_MODE_WRITE);
|
if (ctx->tool_ctx->file_type == FILETYPE_KIP1) {
|
||||||
if (f_json == NULL) {
|
if (json_path->valid == VALIDITY_VALID) {
|
||||||
fprintf(stderr, "Failed to open %s!\n", json_path->char_path);
|
FILE *f_json = os_fopen(json_path->os_path, OS_MODE_WRITE);
|
||||||
return;
|
if (f_json == NULL) {
|
||||||
|
fprintf(stderr, "Failed to open %s!\n", json_path->char_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *json = kip1_get_json(ctx);
|
||||||
|
if (fwrite(json, 1, strlen(json), f_json) != strlen(json)) {
|
||||||
|
fprintf(stderr, "Failed to write JSON file!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
fclose(f_json);
|
||||||
|
} else if (uncmp_path->valid == VALIDITY_VALID) {
|
||||||
|
FILE *f_uncmp = os_fopen(uncmp_path->os_path, OS_MODE_WRITE);
|
||||||
|
if (f_uncmp == NULL) {
|
||||||
|
fprintf(stderr, "Failed to open %s!\n", uncmp_path->char_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t sz = 0;
|
||||||
|
void *uncmp = kip1_uncompress(ctx, &sz);
|
||||||
|
if (fwrite(uncmp, 1, sz, f_uncmp) != sz) {
|
||||||
|
fprintf(stderr, "Failed to write uncompressed kip!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
fclose(f_uncmp);
|
||||||
}
|
}
|
||||||
const char *json = kip1_get_json(ctx);
|
|
||||||
if (fwrite(json, 1, strlen(json), f_json) != strlen(json)) {
|
|
||||||
fprintf(stderr, "Failed to write JSON file!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
fclose(f_json);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
13
main.c
13
main.c
|
@ -54,8 +54,13 @@ 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"
|
||||||
"NPDM/KIP1 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"
|
||||||
|
" --json=file Specify file path for saving JSON representation of program permissions to.\n"
|
||||||
|
" --uncompressed=f Specify file path for saving uncompressed KIP1.\n"
|
||||||
|
"NSO0 options:\n"
|
||||||
|
" --uncompressed=f Specify file path for saving uncompressed NSO0.\n"
|
||||||
"PFS0 options:\n"
|
"PFS0 options:\n"
|
||||||
" --pfs0dir=dir Specify PFS0 directory path.\n"
|
" --pfs0dir=dir Specify PFS0 directory path.\n"
|
||||||
" --outdir=dir Specify PFS0 directory path. Overrides previous path, if present.\n"
|
" --outdir=dir Specify PFS0 directory path. Overrides previous path, if present.\n"
|
||||||
|
@ -171,6 +176,7 @@ int main(int argc, char **argv) {
|
||||||
{"tseckey", 1, NULL, 36},
|
{"tseckey", 1, NULL, 36},
|
||||||
{"json", 1, NULL, 37},
|
{"json", 1, NULL, 37},
|
||||||
{"saveini1json", 0, NULL, 38},
|
{"saveini1json", 0, NULL, 38},
|
||||||
|
{"uncompressed", 1, NULL, 39},
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,6 +228,8 @@ int main(int argc, char **argv) {
|
||||||
nca_ctx.tool_ctx->file_type = FILETYPE_INI1;
|
nca_ctx.tool_ctx->file_type = FILETYPE_INI1;
|
||||||
} else if (!strcmp(optarg, "kip1") || !strcmp(optarg, "kip")) {
|
} else if (!strcmp(optarg, "kip1") || !strcmp(optarg, "kip")) {
|
||||||
nca_ctx.tool_ctx->file_type = FILETYPE_KIP1;
|
nca_ctx.tool_ctx->file_type = FILETYPE_KIP1;
|
||||||
|
} else if (!strcmp(optarg, "nso0") || !strcmp(optarg, "nso")) {
|
||||||
|
nca_ctx.tool_ctx->file_type = FILETYPE_NSO0;
|
||||||
} else if (!strcmp(optarg, "nax0") || !strcmp(optarg, "nax")) {
|
} else if (!strcmp(optarg, "nax0") || !strcmp(optarg, "nax")) {
|
||||||
nca_ctx.tool_ctx->file_type = FILETYPE_NAX0;
|
nca_ctx.tool_ctx->file_type = FILETYPE_NAX0;
|
||||||
} else if (!strcmp(optarg, "keygen") || !strcmp(optarg, "keys") || !strcmp(optarg, "boot0") || !strcmp(optarg, "boot")) {
|
} else if (!strcmp(optarg, "keygen") || !strcmp(optarg, "keys") || !strcmp(optarg, "boot0") || !strcmp(optarg, "boot")) {
|
||||||
|
@ -373,6 +381,9 @@ int main(int argc, char **argv) {
|
||||||
case 38:
|
case 38:
|
||||||
tool_ctx.action |= ACTION_SAVEINIJSON;
|
tool_ctx.action |= ACTION_SAVEINIJSON;
|
||||||
break;
|
break;
|
||||||
|
case 39:
|
||||||
|
filepath_set(&nca_ctx.tool_ctx->settings.uncompressed_path, optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
|
@ -89,6 +89,7 @@ typedef struct {
|
||||||
filepath_t pk21_dir_path;
|
filepath_t pk21_dir_path;
|
||||||
filepath_t ini1_dir_path;
|
filepath_t ini1_dir_path;
|
||||||
filepath_t plaintext_path;
|
filepath_t plaintext_path;
|
||||||
|
filepath_t uncompressed_path;
|
||||||
filepath_t rootpt_dir_path;
|
filepath_t rootpt_dir_path;
|
||||||
filepath_t update_dir_path;
|
filepath_t update_dir_path;
|
||||||
filepath_t normal_dir_path;
|
filepath_t normal_dir_path;
|
||||||
|
@ -113,6 +114,7 @@ enum hactool_file_type
|
||||||
FILETYPE_PACKAGE2,
|
FILETYPE_PACKAGE2,
|
||||||
FILETYPE_INI1,
|
FILETYPE_INI1,
|
||||||
FILETYPE_KIP1,
|
FILETYPE_KIP1,
|
||||||
|
FILETYPE_NSO0,
|
||||||
FILETYPE_NAX0,
|
FILETYPE_NAX0,
|
||||||
FILETYPE_BOOT0
|
FILETYPE_BOOT0
|
||||||
};
|
};
|
||||||
|
@ -125,7 +127,7 @@ enum hactool_file_type
|
||||||
#define ACTION_DEV (1<<5)
|
#define ACTION_DEV (1<<5)
|
||||||
#define ACTION_EXTRACTINI1 (1<<6)
|
#define ACTION_EXTRACTINI1 (1<<6)
|
||||||
#define ACTION_ONLYUPDATEDROMFS (1<<7)
|
#define ACTION_ONLYUPDATEDROMFS (1<<7)
|
||||||
#define ACTION_SAVEINIJSON (1<<0)
|
#define ACTION_SAVEINIJSON (1<<8)
|
||||||
|
|
||||||
struct nca_ctx; /* This will get re-defined by nca.h. */
|
struct nca_ctx; /* This will get re-defined by nca.h. */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue