mirror of
https://github.com/SciresM/hactool
synced 2025-02-16 19:28:23 +00:00
save: Begin filesystem and allocation table init
This commit is contained in:
parent
cfbb0086df
commit
94a7319985
2 changed files with 158 additions and 13 deletions
107
save.c
107
save.c
|
@ -83,7 +83,7 @@ void save_bitmap_clear_bit(void *buffer, size_t bit_offset) {
|
|||
*((uint8_t *)buffer + (bit_offset >> 3)) &= ~(uint8_t)(1 << (bit_offset & 7));
|
||||
}
|
||||
|
||||
uint8_t save_bitmap_check_bit(void *buffer, size_t bit_offset) {
|
||||
uint8_t save_bitmap_check_bit(const void *buffer, size_t bit_offset) {
|
||||
return *((uint8_t *)buffer + (bit_offset >> 3)) & (1 << (bit_offset & 7));
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ size_t save_ivfc_level_fread(ivfc_level_save_ctx_t *ctx, void *buffer, uint64_t
|
|||
}
|
||||
}
|
||||
|
||||
void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buffer, uint64_t offset, size_t count, uint32_t verify) {
|
||||
void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buffer, uint64_t offset, size_t count, int32_t verify) {
|
||||
if (count > ctx->sector_size) {
|
||||
fprintf(stderr, "IVFC read exceeds sector size!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -275,12 +275,88 @@ void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf
|
|||
|
||||
if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) {
|
||||
fprintf(stderr, "Hash error!\n");
|
||||
memdump(stderr, "exp: ", hash_buffer, 0x20);
|
||||
memdump(stderr, "act: ", hash, 0x20);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t save_allocation_table_read_entry_with_length(allocation_table_ctx_t *ctx, allocation_table_entry_t *entry) {
|
||||
uint32_t length = 1;
|
||||
uint32_t entry_index = allocation_table_block_to_entry_index(entry->next);
|
||||
|
||||
allocation_table_entry_t *entries = (allocation_table_entry_t *)((uint8_t *)(ctx->base_storage) + entry_index * SAVE_FAT_ENTRY_SIZE);
|
||||
if ((entries[0].next & 0x80000000) == 0) {
|
||||
if (entries[0].prev & 0x80000000 && entries[0].prev != 0x80000000) {
|
||||
fprintf(stderr, "Invalid iterated range entry in allocation table!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
length = entries[1].next - entry_index + 1;
|
||||
}
|
||||
|
||||
if (allocation_table_is_list_end(&entries[0])) {
|
||||
entry->next = 0xFFFFFFFF;
|
||||
} else {
|
||||
entry->next = allocation_table_entry_index_to_block(allocation_table_get_next(&entries[0]));
|
||||
}
|
||||
|
||||
if (allocation_table_is_list_start(&entries[0])) {
|
||||
entry->prev = 0xFFFFFFFF;
|
||||
} else {
|
||||
entry->prev = allocation_table_entry_index_to_block(allocation_table_get_prev(&entries[0]));
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
uint32_t save_allocation_table_get_list_length(allocation_table_ctx_t *ctx, uint32_t block_index) {
|
||||
allocation_table_entry_t entry;
|
||||
entry.next = block_index;
|
||||
uint32_t total_length = 0;
|
||||
uint32_t table_size = ctx->header->allocation_table_block_count;
|
||||
uint32_t nodes_iterated = 0;
|
||||
|
||||
while (entry.next != 0xFFFFFFFF) {
|
||||
total_length += save_allocation_table_read_entry_with_length(ctx, &entry);
|
||||
nodes_iterated++;
|
||||
if (nodes_iterated > table_size) {
|
||||
fprintf(stderr, "Cycle detected in allocation table!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
return total_length;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
uint64_t save_allocation_table_get_free_space_size(save_filesystem_ctx_t *ctx) {
|
||||
uint32_t free_list_start = save_allocation_table_get_free_list_block_index(&ctx->allocation_table);
|
||||
|
||||
if (free_list_start == 0xFFFFFFFF) return 0;
|
||||
|
||||
return ctx->header->block_size * save_allocation_table_get_list_length(&ctx->allocation_table, free_list_start);
|
||||
}
|
||||
|
||||
void save_open_fat_storage(save_filesystem_ctx_t *ctx, allocation_table_storage_ctx_t *storage_ctx, uint32_t block_index) {
|
||||
storage_ctx->base_storage = ctx->base_storage;
|
||||
storage_ctx->fat = &ctx->allocation_table;
|
||||
storage_ctx->block_size = (uint32_t)ctx->header->block_size;
|
||||
storage_ctx->initial_block = block_index;
|
||||
storage_ctx->_length = block_index == 0xFFFFFFFF ? 0 : save_allocation_table_get_list_length(storage_ctx->fat, block_index) * storage_ctx->block_size;
|
||||
}
|
||||
|
||||
void save_filesystem_init(save_filesystem_ctx_t *ctx, void *fat, save_fs_header_t *save_fs_header, fat_header_t *fat_header) {
|
||||
ctx->allocation_table.base_storage = fat;
|
||||
ctx->allocation_table.header = fat_header;
|
||||
ctx->allocation_table.free_list_entry_index = 0;
|
||||
ctx->header = save_fs_header;
|
||||
|
||||
allocation_table_storage_ctx_t dir_table_storage, file_table_storage;
|
||||
save_open_fat_storage(ctx, &dir_table_storage, fat_header->directory_table_block);
|
||||
save_open_fat_storage(ctx, &file_table_storage, fat_header->file_table_block);
|
||||
}
|
||||
|
||||
validity_t save_ivfc_validate(hierarchical_integrity_verification_storage_ctx_t *ctx, ivfc_save_hdr_t *ivfc) {
|
||||
validity_t result = VALIDITY_VALID;
|
||||
for (unsigned int i = 0; i < ivfc->num_levels - 1 && result != VALIDITY_INVALID; i++) {
|
||||
|
@ -465,18 +541,26 @@ void save_process(save_ctx_t *ctx) {
|
|||
save_ivfc_storage_init(&ctx->core_data_ivfc_storage, ctx->header.layout.ivfc_master_hash_offset_a, &ctx->header.data_ivfc_header);
|
||||
|
||||
// lh: local fatStorage from MetaRemapStorage
|
||||
ctx->fat_storage = malloc(ctx->header.layout.fat_size);
|
||||
save_remap_read(&ctx->meta_remap_storage, ctx->fat_storage, ctx->header.layout.fat_offset, ctx->header.layout.fat_size);
|
||||
|
||||
// lh: InitFatIvfcStorage for FatIvfcStorage
|
||||
if (ctx->header.layout.version >= 0x50000) {
|
||||
if (ctx->header.layout.version < 0x50000) {
|
||||
ctx->fat_storage = malloc(ctx->header.layout.fat_size);
|
||||
save_remap_read(&ctx->meta_remap_storage, ctx->fat_storage, ctx->header.layout.fat_offset, ctx->header.layout.fat_size);
|
||||
} else {
|
||||
// lh: InitFatIvfcStorage for FatIvfcStorage
|
||||
for (unsigned int i = 0; i < 5; i++) {
|
||||
ctx->fat_ivfc_storage.levels[i].save_ctx = ctx;
|
||||
}
|
||||
save_ivfc_storage_init(&ctx->fat_ivfc_storage, ctx->header.layout.fat_ivfc_master_hash_a, &ctx->header.fat_ivfc_header);
|
||||
ctx->fat_storage = malloc(ctx->fat_ivfc_storage._length);
|
||||
save_remap_read(&ctx->meta_remap_storage, ctx->fat_storage, ctx->header.fat_ivfc_header.level_headers[ctx->header.fat_ivfc_header.num_levels - 2].logical_offset, ctx->fat_ivfc_storage._length);
|
||||
}
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
||||
save_filesystem_verify(ctx);
|
||||
}
|
||||
|
||||
// lh: SaveDataFileSystemCore ctor for SaveDataFileSystemCore from CoreDataIvfcStorage, fatStorage
|
||||
ctx->save_filesystem_core.base_storage = &ctx->core_data_ivfc_storage;
|
||||
save_filesystem_init(&ctx->save_filesystem_core, ctx->fat_storage, &ctx->header.save_header, &ctx->header.fat_header);
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
save_print(ctx);
|
||||
|
@ -614,8 +698,9 @@ void save_print(save_ctx_t *ctx) {
|
|||
char timestamp[70];
|
||||
if (strftime(timestamp, sizeof(timestamp), "%F %T UTC", gmtime((time_t *)&ctx->header.extra_data.timestamp)))
|
||||
printf("Timestamp: %s\n", timestamp);
|
||||
printf("Save Data Size: %016"PRIx64"\n", ctx->header.extra_data.data_size);
|
||||
printf("Journal Size: %016"PRIx64"\n", ctx->header.extra_data.journal_size);
|
||||
printf("Save Data Size: 0x%016"PRIx64"\n", ctx->header.extra_data.data_size);
|
||||
printf("Journal Size: 0x%016"PRIx64"\n", ctx->header.extra_data.journal_size);
|
||||
printf("Free Space: 0x%016"PRIx64"\n", save_allocation_table_get_free_space_size(&ctx->save_filesystem_core));
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
||||
if (ctx->header_hash_validity == VALIDITY_VALID) {
|
||||
|
|
64
save.h
64
save.h
|
@ -5,6 +5,7 @@
|
|||
#include "ivfc.h"
|
||||
|
||||
#define SAVE_HEADER_SIZE 0x4000
|
||||
#define SAVE_FAT_ENTRY_SIZE 8
|
||||
|
||||
#define MAGIC_DISF 0x46534944
|
||||
#define MAGIC_DPFS 0x53465044
|
||||
|
@ -105,8 +106,8 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
uint64_t block_size;
|
||||
uint64_t fat_offset;
|
||||
uint32_t fat_block_count;
|
||||
uint64_t allocation_table_offset;
|
||||
uint32_t allocation_table_block_count;
|
||||
uint32_t _0x14;
|
||||
uint64_t data_offset;
|
||||
uint32_t data_block_count;
|
||||
|
@ -290,6 +291,31 @@ typedef struct {
|
|||
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 {
|
||||
hierarchical_integrity_verification_storage_ctx_t *base_storage;
|
||||
allocation_table_ctx_t allocation_table;
|
||||
save_fs_header_t *header;
|
||||
} save_filesystem_ctx_t;
|
||||
|
||||
struct save_ctx_t {
|
||||
FILE *file;
|
||||
hactool_ctx_t *tool_ctx;
|
||||
|
@ -307,8 +333,42 @@ struct save_ctx_t {
|
|||
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));
|
||||
}
|
||||
|
||||
void save_process(save_ctx_t *ctx);
|
||||
void save_process_header(save_ctx_t *ctx);
|
||||
void save_save(save_ctx_t *ctx);
|
||||
|
|
Loading…
Add table
Reference in a new issue