mirror of
https://github.com/SciresM/hactool
synced 2024-09-20 14:31:55 +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
96
kip.c
96
kip.c
|
@ -129,6 +129,81 @@ const char *kip1_get_json(kip1_ctx_t *ctx) {
|
|||
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) {
|
||||
/* Read *just* safe amount. */
|
||||
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);
|
||||
ctx->header = malloc(size);
|
||||
if (ctx->header == NULL) {
|
||||
fprintf(stderr, "Failed to allocate KIP1 header!\n");
|
||||
fprintf(stderr, "Failed to allocate KIP1!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -188,9 +263,10 @@ void kip1_print(kip1_ctx_t *ctx, int suppress) {
|
|||
}
|
||||
|
||||
void kip1_save(kip1_ctx_t *ctx) {
|
||||
/* Do nothing. */
|
||||
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;
|
||||
if (ctx->tool_ctx->file_type == FILETYPE_KIP1) {
|
||||
if (json_path->valid == VALIDITY_VALID) {
|
||||
FILE *f_json = os_fopen(json_path->os_path, OS_MODE_WRITE);
|
||||
if (f_json == NULL) {
|
||||
fprintf(stderr, "Failed to open %s!\n", json_path->char_path);
|
||||
|
@ -202,5 +278,19 @@ void kip1_save(kip1_ctx_t *ctx) {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
13
main.c
13
main.c
|
@ -54,8 +54,13 @@ static void usage(void) {
|
|||
" --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"
|
||||
" --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"
|
||||
"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"
|
||||
" --pfs0dir=dir Specify PFS0 directory path.\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},
|
||||
{"json", 1, NULL, 37},
|
||||
{"saveini1json", 0, NULL, 38},
|
||||
{"uncompressed", 1, NULL, 39},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
|
||||
|
@ -222,6 +228,8 @@ int main(int argc, char **argv) {
|
|||
nca_ctx.tool_ctx->file_type = FILETYPE_INI1;
|
||||
} else if (!strcmp(optarg, "kip1") || !strcmp(optarg, "kip")) {
|
||||
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")) {
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_NAX0;
|
||||
} 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:
|
||||
tool_ctx.action |= ACTION_SAVEINIJSON;
|
||||
break;
|
||||
case 39:
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.uncompressed_path, optarg);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return EXIT_FAILURE;
|
||||
|
|
|
@ -89,6 +89,7 @@ typedef struct {
|
|||
filepath_t pk21_dir_path;
|
||||
filepath_t ini1_dir_path;
|
||||
filepath_t plaintext_path;
|
||||
filepath_t uncompressed_path;
|
||||
filepath_t rootpt_dir_path;
|
||||
filepath_t update_dir_path;
|
||||
filepath_t normal_dir_path;
|
||||
|
@ -113,6 +114,7 @@ enum hactool_file_type
|
|||
FILETYPE_PACKAGE2,
|
||||
FILETYPE_INI1,
|
||||
FILETYPE_KIP1,
|
||||
FILETYPE_NSO0,
|
||||
FILETYPE_NAX0,
|
||||
FILETYPE_BOOT0
|
||||
};
|
||||
|
@ -125,7 +127,7 @@ enum hactool_file_type
|
|||
#define ACTION_DEV (1<<5)
|
||||
#define ACTION_EXTRACTINI1 (1<<6)
|
||||
#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. */
|
||||
|
||||
|
|
Loading…
Reference in a new issue