mirror of
https://github.com/SciresM/hactool
synced 2024-11-22 03:53:09 +00:00
Merge pull request #82 from SciresM/save_dev
Implement savefile reading, output, file listing, and verification
This commit is contained in:
commit
21b84a3be3
9 changed files with 1522 additions and 3 deletions
4
Makefile
4
Makefile
|
@ -13,7 +13,7 @@ all:
|
|||
.c.o:
|
||||
$(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o nca0_romfs.o romfs.o utils.o nax0.o nso.o lz4.o nca.o xci.o main.o filepath.o ConvertUTF.o cJSON.o
|
||||
hactool: save.o sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o nca0_romfs.o romfs.o utils.o nax0.o nso.o lz4.o nca.o xci.o main.o filepath.o ConvertUTF.o cJSON.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR)
|
||||
|
||||
aes.o: aes.h types.h
|
||||
|
@ -52,6 +52,8 @@ nca0_romfs.o: nca0_romfs.h ivfc.h types.h
|
|||
|
||||
rsa.o: rsa.h sha.h types.h
|
||||
|
||||
save.o: save.h ivfc.h aes.h sha.h filepath.h types.h
|
||||
|
||||
sha.o: sha.h types.h
|
||||
|
||||
utils.o: utils.h types.h
|
||||
|
|
|
@ -84,6 +84,9 @@ INI1 options:
|
|||
NAX0 options:
|
||||
--sdseed=seed Set console unique seed for SD card NAX0 encryption.
|
||||
--sdpath=path Set relative path for NAX0 key derivation (ex: /registered/000000FF/cafebabecafebabecafebabecafebabe.nca).
|
||||
Save data options:
|
||||
--outdir=dir Specify save directory path.
|
||||
--listfiles List files in save file.
|
||||
Key Derivation options:
|
||||
--sbk=key Set console unique Secure Boot Key for key derivation.
|
||||
--tseckey=key Set console unique TSEC Key for key derivation.```
|
||||
|
|
|
@ -251,6 +251,9 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
|
|||
} else if (strcmp(key, "package2_key_source") == 0) {
|
||||
parse_hex_key(keyset->package2_key_source, value, sizeof(keyset->package2_key_source));
|
||||
matched_key = 1;
|
||||
} else if (strcmp(key, "per_console_key_source") == 0) {
|
||||
parse_hex_key(keyset->per_console_key_source, value, sizeof(keyset->per_console_key_source));
|
||||
matched_key = 1;
|
||||
} else if (strcmp(key, "sd_card_kek_source") == 0) {
|
||||
parse_hex_key(keyset->sd_card_kek_source, value, sizeof(keyset->sd_card_kek_source));
|
||||
matched_key = 1;
|
||||
|
|
9
ivfc.h
9
ivfc.h
|
@ -37,6 +37,15 @@ typedef struct {
|
|||
uint8_t master_hash[0x20];
|
||||
} ivfc_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t id;
|
||||
uint32_t master_hash_size;
|
||||
uint32_t num_levels;
|
||||
ivfc_level_hdr_t level_headers[IVFC_MAX_LEVEL];
|
||||
uint8_t salt_source[0x20];
|
||||
} ivfc_save_hdr_t;
|
||||
|
||||
/* RomFS structs. */
|
||||
#define ROMFS_HEADER_SIZE 0x00000050
|
||||
|
||||
|
|
21
main.c
21
main.c
|
@ -13,6 +13,7 @@
|
|||
#include "extkeys.h"
|
||||
#include "packages.h"
|
||||
#include "nso.h"
|
||||
#include "save.h"
|
||||
|
||||
static const char *prog_name = "hactool";
|
||||
|
||||
|
@ -97,6 +98,9 @@ static void usage(void) {
|
|||
"NAX0 options:\n"
|
||||
" --sdseed=seed Set console unique seed for SD card NAX0 encryption.\n"
|
||||
" --sdpath=path Set relative path for NAX0 key derivation (ex: /registered/000000FF/cafebabecafebabecafebabecafebabe.nca).\n"
|
||||
"Save data options:\n"
|
||||
" --outdir=dir Specify save directory path.\n"
|
||||
" --listfiles List files in save file.\n"
|
||||
"Key Derivation options:\n"
|
||||
" --sbk=key Set console unique Secure Boot Key for key derivation.\n"
|
||||
" --tseckey=key Set console unique TSEC Key for key derivation.\n"
|
||||
|
@ -110,7 +114,7 @@ int main(int argc, char **argv) {
|
|||
nca_ctx_t nca_ctx;
|
||||
char input_name[0x200];
|
||||
filepath_t keypath;
|
||||
|
||||
|
||||
prog_name = (argc < 1) ? "hactool" : argv[0];
|
||||
|
||||
nca_init(&nca_ctx);
|
||||
|
@ -180,6 +184,7 @@ int main(int argc, char **argv) {
|
|||
{"saveini1json", 0, NULL, 38},
|
||||
{"uncompressed", 1, NULL, 39},
|
||||
{"disablekeywarns", 0, NULL, 40},
|
||||
{"listfiles", 0, NULL, 41},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
|
||||
|
@ -237,6 +242,8 @@ int main(int argc, char **argv) {
|
|||
nca_ctx.tool_ctx->file_type = FILETYPE_NAX0;
|
||||
} else if (!strcmp(optarg, "keygen") || !strcmp(optarg, "keys") || !strcmp(optarg, "boot0") || !strcmp(optarg, "boot")) {
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_BOOT0;
|
||||
} else if (!strcmp(optarg, "save")) {
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_SAVE;
|
||||
}
|
||||
break;
|
||||
case 0: filepath_set(&nca_ctx.tool_ctx->settings.section_paths[0], optarg); break;
|
||||
|
@ -390,6 +397,9 @@ int main(int argc, char **argv) {
|
|||
case 40:
|
||||
nca_ctx.tool_ctx->settings.skip_key_warnings = 1;
|
||||
break;
|
||||
case 41:
|
||||
nca_ctx.tool_ctx->action |= ACTION_LISTFILES;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return EXIT_FAILURE;
|
||||
|
@ -675,6 +685,15 @@ int main(int argc, char **argv) {
|
|||
pki_print_keys(&new_keyset);
|
||||
break;
|
||||
}
|
||||
case FILETYPE_SAVE: {
|
||||
save_ctx_t save_ctx;
|
||||
memset(&save_ctx, 0, sizeof(save_ctx));
|
||||
save_ctx.file = tool_ctx.file;
|
||||
save_ctx.tool_ctx = &tool_ctx;
|
||||
save_process(&save_ctx);
|
||||
save_free_contexts(&save_ctx);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "Unknown File Type!\n\n");
|
||||
usage();
|
||||
|
|
12
pki.c
12
pki.c
|
@ -229,6 +229,10 @@ void pki_derive_keys(nca_keyset_t *keyset) {
|
|||
}
|
||||
aes_ctx_t *mac_gen_ctx = new_aes_ctx(&keyset->keyblob_keys[i], 0x10, AES_MODE_ECB);
|
||||
aes_decrypt(mac_gen_ctx, &keyset->keyblob_mac_keys[i], keyset->keyblob_mac_key_source, 0x10);
|
||||
/* Derive Device key */
|
||||
if (i == 0 && memcmp(keyset->per_console_key_source, zeroes, 0x10) != 0) {
|
||||
aes_decrypt(mac_gen_ctx, keyset->device_key, keyset->per_console_key_source, 0x10);
|
||||
}
|
||||
free_aes_ctx(mac_gen_ctx);
|
||||
}
|
||||
for (unsigned int i = 0; i < 0x6; i++) {
|
||||
|
@ -368,6 +372,11 @@ void pki_derive_keys(nca_keyset_t *keyset) {
|
|||
|
||||
free_aes_ctx(sd_ctx);
|
||||
}
|
||||
|
||||
/* Derive Save MAC Key */
|
||||
if (i == 0 && memcmp(keyset->save_mac_kek_source, zeroes, 0x10) != 0 && memcmp(keyset->save_mac_key_source, zeroes, 0x10) != 0 && memcmp(keyset->device_key, zeroes, 0x10) != 0) {
|
||||
generate_kek(keyset->save_mac_key, keyset->save_mac_kek_source, keyset->device_key, keyset->aes_kek_generation_source, keyset->save_mac_key_source);
|
||||
}
|
||||
|
||||
free_aes_ctx(master_ctx);
|
||||
}
|
||||
|
@ -381,6 +390,7 @@ void pki_print_keys(nca_keyset_t *keyset) {
|
|||
|
||||
PRINT_KEY_WITH_NAME(keyset->secure_boot_key, secure_boot_key);
|
||||
PRINT_KEY_WITH_NAME(keyset->tsec_key, tsec_key);
|
||||
PRINT_KEY_WITH_NAME(keyset->device_key, device_key);
|
||||
PRINT_KEY_WITH_NAME(keyset->tsec_root_kek, tsec_root_kek);
|
||||
PRINT_KEY_WITH_NAME(keyset->package1_mac_kek, package1_mac_kek);
|
||||
PRINT_KEY_WITH_NAME(keyset->package1_kek, package1_kek);
|
||||
|
@ -442,6 +452,7 @@ void pki_print_keys(nca_keyset_t *keyset) {
|
|||
PRINT_KEY_WITH_NAME_IDX(keyset->package2_keys[i], package2_key, i);
|
||||
}
|
||||
printf("\n");
|
||||
PRINT_KEY_WITH_NAME(keyset->per_console_key_source, per_console_key_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->aes_kek_generation_source, aes_kek_generation_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->aes_key_generation_source, aes_key_generation_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->titlekek_source, titlekek_source);
|
||||
|
@ -458,6 +469,7 @@ void pki_print_keys(nca_keyset_t *keyset) {
|
|||
PRINT_KEY_WITH_NAME(keyset->sd_card_key_sources[1], sd_card_nca_key_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->save_mac_kek_source, save_mac_kek_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->save_mac_key_source, save_mac_key_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->save_mac_key, save_mac_key);
|
||||
printf("\n");
|
||||
PRINT_KEY_WITH_NAME(keyset->header_key_source, header_key_source);
|
||||
PRINT_KEY_WITH_NAME(keyset->header_key, header_key);
|
||||
|
|
448
save.h
Normal file
448
save.h
Normal file
|
@ -0,0 +1,448 @@
|
|||
#ifndef HACTOOL_SAVE_H
|
||||
#define HACTOOL_SAVE_H
|
||||
#include "types.h"
|
||||
#include "settings.h"
|
||||
#include "ivfc.h"
|
||||
|
||||
#define SAVE_HEADER_SIZE 0x4000
|
||||
#define SAVE_FAT_ENTRY_SIZE 8
|
||||
#define SAVE_FS_LIST_MAX_NAME_LENGTH 0x40
|
||||
#define SAVE_FS_LIST_ENTRY_SIZE 0x60
|
||||
|
||||
#define MAGIC_DISF 0x46534944
|
||||
#define MAGIC_DPFS 0x53465044
|
||||
#define MAGIC_JNGL 0x4C474E4A
|
||||
#define MAGIC_SAVE 0x45564153
|
||||
#define MAGIC_RMAP 0x50414D52
|
||||
|
||||
typedef struct save_ctx_t save_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* DISF */
|
||||
uint32_t version;
|
||||
uint8_t hash[0x20];
|
||||
uint64_t file_map_entry_offset;
|
||||
uint64_t file_map_entry_size;
|
||||
uint64_t meta_map_entry_offset;
|
||||
uint64_t meta_map_entry_size;
|
||||
uint64_t file_map_data_offset;
|
||||
uint64_t file_map_data_size;
|
||||
uint64_t duplex_l1_offset_a;
|
||||
uint64_t duplex_l1_offset_b;
|
||||
uint64_t duplex_l1_size;
|
||||
uint64_t duplex_data_offset_a;
|
||||
uint64_t duplex_data_offset_b;
|
||||
uint64_t duplex_data_size;
|
||||
uint64_t journal_data_offset;
|
||||
uint64_t journal_data_size_a;
|
||||
uint64_t journal_data_size_b;
|
||||
uint64_t journal_size;
|
||||
uint64_t duplex_master_offset_a;
|
||||
uint64_t duplex_master_offset_b;
|
||||
uint64_t duplex_master_size;
|
||||
uint64_t ivfc_master_hash_offset_a;
|
||||
uint64_t ivfc_master_hash_offset_b;
|
||||
uint64_t ivfc_master_hash_size;
|
||||
uint64_t journal_map_table_offset;
|
||||
uint64_t journal_map_table_size;
|
||||
uint64_t journal_physical_bitmap_offset;
|
||||
uint64_t journal_physical_bitmap_size;
|
||||
uint64_t journal_virtual_bitmap_offset;
|
||||
uint64_t journal_virtual_bitmap_size;
|
||||
uint64_t journal_free_bitmap_offset;
|
||||
uint64_t journal_free_bitmap_size;
|
||||
uint64_t ivfc_l1_offset;
|
||||
uint64_t ivfc_l1_size;
|
||||
uint64_t ivfc_l2_offset;
|
||||
uint64_t ivfc_l2_size;
|
||||
uint64_t ivfc_l3_offset;
|
||||
uint64_t ivfc_l3_size;
|
||||
uint64_t fat_offset;
|
||||
uint64_t fat_size;
|
||||
uint64_t duplex_index;
|
||||
uint64_t fat_ivfc_master_hash_a;
|
||||
uint64_t fat_ivfc_master_hash_b;
|
||||
uint64_t fat_ivfc_l1_offset;
|
||||
uint64_t fat_ivfc_l1_size;
|
||||
uint64_t fat_ivfc_l2_offset;
|
||||
uint64_t fat_ivfc_l2_size;
|
||||
uint8_t _0x190[0x70];
|
||||
} fs_layout_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
uint32_t block_size_power;
|
||||
} duplex_info_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* DPFS */
|
||||
uint32_t version;
|
||||
duplex_info_t layers[3];
|
||||
} duplex_header_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t main_data_block_count;
|
||||
uint32_t journal_block_count;
|
||||
uint32_t _0x0C;
|
||||
} journal_map_header_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* JNGL */
|
||||
uint32_t version;
|
||||
uint64_t total_size;
|
||||
uint64_t journal_size;
|
||||
uint64_t block_size;
|
||||
} journal_header_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* SAVE */
|
||||
uint32_t version;
|
||||
uint64_t block_count;
|
||||
uint64_t block_size;
|
||||
} save_fs_header_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t block_size;
|
||||
uint64_t allocation_table_offset;
|
||||
uint32_t allocation_table_block_count;
|
||||
uint32_t _0x14;
|
||||
uint64_t data_offset;
|
||||
uint32_t data_block_count;
|
||||
uint32_t _0x24;
|
||||
uint32_t directory_table_block;
|
||||
uint32_t file_table_block;
|
||||
} fat_header_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* RMAP */
|
||||
uint32_t version;
|
||||
uint32_t map_entry_count;
|
||||
uint32_t map_segment_count;
|
||||
uint32_t segment_bits;
|
||||
uint8_t _0x14[0x2C];
|
||||
} remap_header_t;
|
||||
|
||||
typedef struct remap_segment_ctx_t remap_segment_ctx_t;
|
||||
typedef struct remap_entry_ctx_t remap_entry_ctx_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct remap_entry_ctx_t {
|
||||
uint64_t virtual_offset;
|
||||
uint64_t physical_offset;
|
||||
uint64_t size;
|
||||
uint32_t alignment;
|
||||
uint32_t _0x1C;
|
||||
uint64_t virtual_offset_end;
|
||||
uint64_t physical_offset_end;
|
||||
remap_segment_ctx_t *segment;
|
||||
remap_entry_ctx_t *next;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct remap_segment_ctx_t{
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
remap_entry_ctx_t *entries;
|
||||
uint64_t entry_count;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
uint8_t *bitmap;
|
||||
} duplex_bitmap_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t block_size;
|
||||
uint8_t *bitmap_storage;
|
||||
uint8_t *data_a;
|
||||
uint8_t *data_b;
|
||||
duplex_bitmap_t bitmap;
|
||||
uint64_t _length;
|
||||
} duplex_storage_ctx_t;
|
||||
|
||||
enum base_storage_type {
|
||||
STORAGE_BYTES = 0,
|
||||
STORAGE_DUPLEX = 1,
|
||||
STORAGE_REMAP = 2,
|
||||
STORAGE_JOURNAL = 3
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
remap_header_t *header;
|
||||
remap_entry_ctx_t *map_entries;
|
||||
remap_segment_ctx_t *segments;
|
||||
enum base_storage_type type;
|
||||
uint64_t base_storage_offset;
|
||||
duplex_storage_ctx_t *duplex;
|
||||
FILE *file;
|
||||
} remap_storage_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t title_id;
|
||||
uint8_t user_id[0x10];
|
||||
uint64_t save_id;
|
||||
uint8_t save_data_type;
|
||||
uint8_t _0x21[0x1F];
|
||||
uint64_t save_owner_id;
|
||||
uint64_t timestamp;
|
||||
uint64_t _0x50;
|
||||
uint64_t data_size;
|
||||
uint64_t journal_size;
|
||||
uint64_t commit_id;
|
||||
} extra_data_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint8_t cmac[0x10];
|
||||
uint8_t _0x10[0xF0];
|
||||
fs_layout_t layout;
|
||||
duplex_header_t duplex_header;
|
||||
ivfc_save_hdr_t data_ivfc_header;
|
||||
uint32_t _0x404;
|
||||
journal_header_t journal_header;
|
||||
journal_map_header_t map_header;
|
||||
uint8_t _0x438[0x1D0];
|
||||
save_fs_header_t save_header;
|
||||
fat_header_t fat_header;
|
||||
remap_header_t main_remap_header, meta_remap_header;
|
||||
uint64_t _0x6D0;
|
||||
extra_data_t extra_data;
|
||||
uint8_t _0x748[0x390];
|
||||
ivfc_save_hdr_t fat_ivfc_header;
|
||||
uint8_t _0xB98[0x3468];
|
||||
} save_header_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct {
|
||||
duplex_storage_ctx_t layers[2];
|
||||
duplex_storage_ctx_t data_layer;
|
||||
uint64_t _length;
|
||||
} hierarchical_duplex_storage_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data_a;
|
||||
uint8_t *data_b;
|
||||
duplex_info_t info;
|
||||
} duplex_fs_layer_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *map_storage;
|
||||
uint8_t *physical_block_bitmap;
|
||||
uint8_t *virtual_block_bitmap;
|
||||
uint8_t *free_block_bitmap;
|
||||
} journal_map_params_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t physical_index;
|
||||
uint32_t virtual_index;
|
||||
} journal_map_entry_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
journal_map_header_t *header;
|
||||
journal_map_entry_t *entries;
|
||||
uint8_t *map_storage;
|
||||
} journal_map_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
journal_map_ctx_t map;
|
||||
journal_header_t *header;
|
||||
uint32_t block_size;
|
||||
uint64_t journal_data_offset;
|
||||
uint64_t _length;
|
||||
FILE *file;
|
||||
} journal_storage_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t data_offset;
|
||||
uint64_t data_size;
|
||||
uint64_t hash_offset;
|
||||
uint32_t hash_block_size;
|
||||
validity_t hash_validity;
|
||||
enum base_storage_type type;
|
||||
save_ctx_t *save_ctx;
|
||||
} ivfc_level_save_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
ivfc_level_save_ctx_t *data;
|
||||
uint32_t block_size;
|
||||
uint8_t salt[0x20];
|
||||
} integrity_verification_info_ctx_t;
|
||||
|
||||
|
||||
typedef struct integrity_verification_storage_ctx_t integrity_verification_storage_ctx_t;
|
||||
|
||||
struct integrity_verification_storage_ctx_t {
|
||||
ivfc_level_save_ctx_t *hash_storage;
|
||||
ivfc_level_save_ctx_t *base_storage;
|
||||
validity_t *block_validities;
|
||||
uint8_t salt[0x20];
|
||||
uint32_t sector_size;
|
||||
uint32_t sector_count;
|
||||
uint64_t _length;
|
||||
integrity_verification_storage_ctx_t *next_level;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ivfc_level_save_ctx_t levels[5];
|
||||
ivfc_level_save_ctx_t *data_level;
|
||||
validity_t **level_validities;
|
||||
uint64_t _length;
|
||||
integrity_verification_storage_ctx_t integrity_storages[4];
|
||||
} hierarchical_integrity_verification_storage_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t prev;
|
||||
uint32_t next;
|
||||
} allocation_table_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t free_list_entry_index;
|
||||
void *base_storage;
|
||||
fat_header_t *header;
|
||||
} allocation_table_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
hierarchical_integrity_verification_storage_ctx_t *base_storage;
|
||||
uint32_t block_size;
|
||||
uint32_t initial_block;
|
||||
allocation_table_ctx_t *fat;
|
||||
uint64_t _length;
|
||||
} allocation_table_storage_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
allocation_table_ctx_t *fat;
|
||||
uint32_t virtual_block;
|
||||
uint32_t physical_block;
|
||||
uint32_t current_segment_size;
|
||||
uint32_t next_block;
|
||||
uint32_t prev_block;
|
||||
} allocation_table_iterator_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
char name[SAVE_FS_LIST_MAX_NAME_LENGTH];
|
||||
uint32_t parent;
|
||||
} save_entry_key_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t start_block;
|
||||
uint64_t length;
|
||||
uint32_t _0xC[2];
|
||||
} save_file_info_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t next_directory;
|
||||
uint32_t next_file;
|
||||
uint32_t _0x8[3];
|
||||
} save_find_position_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t next_sibling;
|
||||
union { /* Save table entry type. Size = 0x14. */
|
||||
save_file_info_t save_file_info;
|
||||
save_find_position_t save_find_position;
|
||||
};
|
||||
} save_table_entry_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t parent;
|
||||
char name[SAVE_FS_LIST_MAX_NAME_LENGTH];
|
||||
save_table_entry_t value;
|
||||
uint32_t next;
|
||||
} save_fs_list_entry_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct {
|
||||
uint32_t free_list_head_index;
|
||||
uint32_t used_list_head_index;
|
||||
allocation_table_storage_ctx_t storage;
|
||||
} save_filesystem_list_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
save_filesystem_list_ctx_t file_table;
|
||||
save_filesystem_list_ctx_t directory_table;
|
||||
} hierarchical_save_file_table_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
hierarchical_integrity_verification_storage_ctx_t *base_storage;
|
||||
allocation_table_ctx_t allocation_table;
|
||||
save_fs_header_t *header;
|
||||
hierarchical_save_file_table_ctx_t file_table;
|
||||
} save_filesystem_ctx_t;
|
||||
|
||||
struct save_ctx_t {
|
||||
FILE *file;
|
||||
hactool_ctx_t *tool_ctx;
|
||||
save_header_t header;
|
||||
validity_t header_cmac_validity;
|
||||
validity_t header_hash_validity;
|
||||
uint8_t *data_ivfc_master;
|
||||
uint8_t *fat_ivfc_master;
|
||||
remap_storage_ctx_t data_remap_storage;
|
||||
remap_storage_ctx_t meta_remap_storage;
|
||||
duplex_fs_layer_info_t duplex_layers[3];
|
||||
hierarchical_duplex_storage_ctx_t duplex_storage;
|
||||
journal_storage_ctx_t journal_storage;
|
||||
journal_map_params_t journal_map_info;
|
||||
hierarchical_integrity_verification_storage_ctx_t core_data_ivfc_storage;
|
||||
hierarchical_integrity_verification_storage_ctx_t fat_ivfc_storage;
|
||||
uint8_t *fat_storage;
|
||||
save_filesystem_ctx_t save_filesystem_core;
|
||||
};
|
||||
|
||||
static inline uint32_t allocation_table_entry_index_to_block(uint32_t entry_index) {
|
||||
return entry_index - 1;
|
||||
}
|
||||
|
||||
static inline uint32_t allocation_table_block_to_entry_index(uint32_t block_index) {
|
||||
return block_index + 1;
|
||||
}
|
||||
|
||||
static inline int allocation_table_is_list_end(allocation_table_entry_t *entry) {
|
||||
return (entry->next & 0x7FFFFFFF) == 0;
|
||||
}
|
||||
|
||||
static inline int allocation_table_is_list_start(allocation_table_entry_t *entry) {
|
||||
return entry->prev == 0x80000000;
|
||||
}
|
||||
|
||||
|
||||
static inline int allocation_table_get_next(allocation_table_entry_t *entry) {
|
||||
return entry->next & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
static inline int allocation_table_get_prev(allocation_table_entry_t *entry) {
|
||||
return entry->prev & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
static inline allocation_table_entry_t *save_allocation_table_read_entry(allocation_table_ctx_t *ctx, uint32_t entry_index) {
|
||||
return (allocation_table_entry_t *)((uint8_t *)ctx->base_storage + entry_index * SAVE_FAT_ENTRY_SIZE);
|
||||
}
|
||||
|
||||
static inline uint32_t save_allocation_table_get_free_list_entry_index(allocation_table_ctx_t *ctx) {
|
||||
return allocation_table_get_next(save_allocation_table_read_entry(ctx, ctx->free_list_entry_index));
|
||||
}
|
||||
|
||||
static inline uint32_t save_allocation_table_get_free_list_block_index(allocation_table_ctx_t *ctx) {
|
||||
return allocation_table_entry_index_to_block(save_allocation_table_get_free_list_entry_index(ctx));
|
||||
}
|
||||
|
||||
void save_process(save_ctx_t *ctx);
|
||||
void save_process_header(save_ctx_t *ctx);
|
||||
void save_save(save_ctx_t *ctx);
|
||||
void save_print(save_ctx_t *ctx);
|
||||
|
||||
void save_free_contexts(save_ctx_t *ctx);
|
||||
|
||||
#endif
|
|
@ -18,6 +18,7 @@ typedef enum {
|
|||
typedef struct {
|
||||
unsigned char secure_boot_key[0x10]; /* Secure boot key for use in key derivation. NOTE: CONSOLE UNIQUE. */
|
||||
unsigned char tsec_key[0x10]; /* TSEC key for use in key derivation. NOTE: CONSOLE UNIQUE. */
|
||||
unsigned char device_key[0x10]; /* Device key used to derive some FS keys. NOTE: CONSOLE UNIQUE. */
|
||||
unsigned char keyblob_keys[0x20][0x10]; /* Actual keys used to decrypt keyblobs. NOTE: CONSOLE UNIQUE.*/
|
||||
unsigned char keyblob_mac_keys[0x20][0x10]; /* Keys used to validate keyblobs. NOTE: CONSOLE UNIQUE. */
|
||||
unsigned char encrypted_keyblobs[0x20][0xB0]; /* Actual encrypted keyblobs (EKS). NOTE: CONSOLE UNIQUE. */
|
||||
|
@ -37,6 +38,7 @@ typedef struct {
|
|||
unsigned char package1_keys[0x20][0x10]; /* Package1 keys. */
|
||||
unsigned char package2_keys[0x20][0x10]; /* Package2 keys. */
|
||||
unsigned char package2_key_source[0x10]; /* Seed for Package2 key. */
|
||||
unsigned char per_console_key_source[0x10]; /* Seed for Device key. */
|
||||
unsigned char aes_kek_generation_source[0x10]; /* Seed for GenerateAesKek, usecase + generation 0. */
|
||||
unsigned char aes_key_generation_source[0x10]; /* Seed for GenerateAesKey. */
|
||||
unsigned char key_area_key_application_source[0x10]; /* Seed for kaek 0. */
|
||||
|
@ -52,6 +54,7 @@ typedef struct {
|
|||
unsigned char header_key[0x20]; /* NCA header key. */
|
||||
unsigned char titlekeks[0x20][0x10]; /* Title key encryption keys. */
|
||||
unsigned char key_area_keys[0x20][3][0x10]; /* Key area encryption keys. */
|
||||
unsigned char save_mac_key[0x10]; /* Key used to sign savedata. */
|
||||
unsigned char sd_card_keys[2][0x20];
|
||||
unsigned char nca_hdr_fixed_key_modulus[0x100]; /* NCA header fixed key RSA pubk. */
|
||||
unsigned char acid_fixed_key_modulus[0x100]; /* ACID fixed key RSA pubk. */
|
||||
|
@ -127,7 +130,8 @@ enum hactool_file_type
|
|||
FILETYPE_KIP1,
|
||||
FILETYPE_NSO0,
|
||||
FILETYPE_NAX0,
|
||||
FILETYPE_BOOT0
|
||||
FILETYPE_BOOT0,
|
||||
FILETYPE_SAVE
|
||||
};
|
||||
|
||||
#define ACTION_INFO (1<<0)
|
||||
|
@ -139,6 +143,7 @@ enum hactool_file_type
|
|||
#define ACTION_EXTRACTINI1 (1<<6)
|
||||
#define ACTION_ONLYUPDATEDROMFS (1<<7)
|
||||
#define ACTION_SAVEINIJSON (1<<8)
|
||||
#define ACTION_LISTFILES (1<<9)
|
||||
|
||||
struct nca_ctx; /* This will get re-defined by nca.h. */
|
||||
|
||||
|
|
Loading…
Reference in a new issue