mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
Fix bugs + improve bktr interface.
* Check for sparse layers in nsp_dumper and xml_generator PoCs before attempting to initialize content-type-specific contexts. * system_title_dumper now builds. * gamecardCloseStorageArea() no longer returns prematurely on application exit. * Fix section size for sparse sections in ncaInitializeFsSectionContext(). * Fix ncaFsSectionValidateHashDataBoundaries(). * Implement bktrInitializeIndirectStorage() and bktrInitializeAesCtrExStorage().
This commit is contained in:
parent
4507af9e34
commit
8b0ed76011
7 changed files with 286 additions and 207 deletions
|
@ -312,85 +312,88 @@ static void dump_thread_func(void *arg)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(content_info->content_type)
|
if (!cur_nca_ctx->fs_ctx[0].has_sparse_layer)
|
||||||
{
|
{
|
||||||
case NcmContentType_Program:
|
switch(content_info->content_type)
|
||||||
{
|
{
|
||||||
// don't proceed if we didn't allocate programinfo ctx
|
case NcmContentType_Program:
|
||||||
if (!program_count || !program_info_ctx) break;
|
|
||||||
|
|
||||||
ProgramInfoContext *cur_program_info_ctx = &(program_info_ctx[program_idx]);
|
|
||||||
|
|
||||||
if (!programInfoInitializeContext(cur_program_info_ctx, cur_nca_ctx))
|
|
||||||
{
|
{
|
||||||
consolePrint("initialize program info ctx failed (%s)\n", cur_nca_ctx->content_id_str);
|
// don't proceed if we didn't allocate programinfo ctx or if we're dealing with a sparse layer
|
||||||
goto end;
|
if (!program_count || !program_info_ctx) break;
|
||||||
|
|
||||||
|
ProgramInfoContext *cur_program_info_ctx = &(program_info_ctx[program_idx]);
|
||||||
|
|
||||||
|
if (!programInfoInitializeContext(cur_program_info_ctx, cur_nca_ctx))
|
||||||
|
{
|
||||||
|
consolePrint("initialize program info ctx failed (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!programInfoGenerateAuthoringToolXml(cur_program_info_ctx))
|
||||||
|
{
|
||||||
|
consolePrint("program info xml failed (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
program_idx++;
|
||||||
|
|
||||||
|
consolePrint("initialize program info ctx succeeded (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case NcmContentType_Control:
|
||||||
if (append_authoringtool_data && !programInfoGenerateAuthoringToolXml(cur_program_info_ctx))
|
|
||||||
{
|
{
|
||||||
consolePrint("program info xml failed (%s)\n", cur_nca_ctx->content_id_str);
|
// don't proceed if we didn't allocate nacp ctx
|
||||||
goto end;
|
if (!control_count || !nacp_ctx) break;
|
||||||
|
|
||||||
|
NacpContext *cur_nacp_ctx = &(nacp_ctx[control_idx]);
|
||||||
|
|
||||||
|
if (!nacpInitializeContext(cur_nacp_ctx, cur_nca_ctx))
|
||||||
|
{
|
||||||
|
consolePrint("initialize nacp ctx failed (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nacpGenerateNcaPatch(cur_nacp_ctx, patch_sua, patch_screenshot, patch_video_capture, patch_hdcp))
|
||||||
|
{
|
||||||
|
consolePrint("nacp nca patch failed (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (append_authoringtool_data && !nacpGenerateAuthoringToolXml(cur_nacp_ctx, title_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
|
||||||
|
{
|
||||||
|
consolePrint("nacp xml failed (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
control_idx++;
|
||||||
|
|
||||||
|
consolePrint("initialize nacp ctx succeeded (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case NcmContentType_LegalInformation:
|
||||||
program_idx++;
|
{
|
||||||
|
// don't proceed if we didn't allocate legalinfo ctx
|
||||||
consolePrint("initialize program info ctx succeeded (%s)\n", cur_nca_ctx->content_id_str);
|
if (!legal_info_count || !legal_info_ctx) break;
|
||||||
|
|
||||||
break;
|
LegalInfoContext *cur_legal_info_ctx = &(legal_info_ctx[legal_info_idx]);
|
||||||
|
|
||||||
|
if (!legalInfoInitializeContext(cur_legal_info_ctx, cur_nca_ctx))
|
||||||
|
{
|
||||||
|
consolePrint("initialize legal info ctx failed (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
legal_info_idx++;
|
||||||
|
|
||||||
|
consolePrint("initialize legal info ctx succeeded (%s)\n", cur_nca_ctx->content_id_str);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case NcmContentType_Control:
|
|
||||||
{
|
|
||||||
// don't proceed if we didn't allocate nacp ctx
|
|
||||||
if (!control_count || !nacp_ctx) break;
|
|
||||||
|
|
||||||
NacpContext *cur_nacp_ctx = &(nacp_ctx[control_idx]);
|
|
||||||
|
|
||||||
if (!nacpInitializeContext(cur_nacp_ctx, cur_nca_ctx))
|
|
||||||
{
|
|
||||||
consolePrint("initialize nacp ctx failed (%s)\n", cur_nca_ctx->content_id_str);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nacpGenerateNcaPatch(cur_nacp_ctx, patch_sua, patch_screenshot, patch_video_capture, patch_hdcp))
|
|
||||||
{
|
|
||||||
consolePrint("nacp nca patch failed (%s)\n", cur_nca_ctx->content_id_str);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (append_authoringtool_data && !nacpGenerateAuthoringToolXml(cur_nacp_ctx, title_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
|
|
||||||
{
|
|
||||||
consolePrint("nacp xml failed (%s)\n", cur_nca_ctx->content_id_str);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
control_idx++;
|
|
||||||
|
|
||||||
consolePrint("initialize nacp ctx succeeded (%s)\n", cur_nca_ctx->content_id_str);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NcmContentType_LegalInformation:
|
|
||||||
{
|
|
||||||
// don't proceed if we didn't allocate legalinfo ctx
|
|
||||||
if (!legal_info_count || !legal_info_ctx) break;
|
|
||||||
|
|
||||||
LegalInfoContext *cur_legal_info_ctx = &(legal_info_ctx[legal_info_idx]);
|
|
||||||
|
|
||||||
if (!legalInfoInitializeContext(cur_legal_info_ctx, cur_nca_ctx))
|
|
||||||
{
|
|
||||||
consolePrint("initialize legal info ctx failed (%s)\n", cur_nca_ctx->content_id_str);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
legal_info_idx++;
|
|
||||||
|
|
||||||
consolePrint("initialize legal info ctx succeeded (%s)\n", cur_nca_ctx->content_id_str);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ncaEncryptHeader(cur_nca_ctx))
|
if (!ncaEncryptHeader(cur_nca_ctx))
|
||||||
|
|
|
@ -98,7 +98,7 @@ static void dumpPartitionFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(path, sizeof(path), OUTPATH "/%016lX - %s/%s (%s)/Section %u (%s)", info->meta_key.id, info->app_metadata->lang_entry.name, ((NcaContext*)nca_fs_ctx->nca_ctx)->content_id_str, \
|
snprintf(path, sizeof(path), OUTPATH "/%016lX - %s/%s (%s)/Section %u (%s)", info->meta_key.id, info->app_metadata->lang_entry.name, ((NcaContext*)nca_fs_ctx->nca_ctx)->content_id_str, \
|
||||||
titleGetNcmContentTypeName(((NcaContext*)nca_fs_ctx->nca_ctx)->content_type), nca_fs_ctx->section_num, ncaGetFsSectionTypeName(nca_fs_ctx));
|
titleGetNcmContentTypeName(((NcaContext*)nca_fs_ctx->nca_ctx)->content_type), nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||||
utilsCreateDirectoryTree(path, true);
|
utilsCreateDirectoryTree(path, true);
|
||||||
path_len = strlen(path);
|
path_len = strlen(path);
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(path, sizeof(path), OUTPATH "/%016lX - %s/%s (%s)/Section %u (%s)", info->meta_key.id, info->app_metadata->lang_entry.name, ((NcaContext*)nca_fs_ctx->nca_ctx)->content_id_str, \
|
snprintf(path, sizeof(path), OUTPATH "/%016lX - %s/%s (%s)/Section %u (%s)", info->meta_key.id, info->app_metadata->lang_entry.name, ((NcaContext*)nca_fs_ctx->nca_ctx)->content_id_str, \
|
||||||
titleGetNcmContentTypeName(((NcaContext*)nca_fs_ctx->nca_ctx)->content_type), nca_fs_ctx->section_num, ncaGetFsSectionTypeName(nca_fs_ctx));
|
titleGetNcmContentTypeName(((NcaContext*)nca_fs_ctx->nca_ctx)->content_type), nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||||
utilsCreateDirectoryTree(path, true);
|
utilsCreateDirectoryTree(path, true);
|
||||||
path_len = strlen(path);
|
path_len = strlen(path);
|
||||||
|
|
||||||
|
|
|
@ -289,6 +289,8 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
|
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
|
||||||
|
|
||||||
|
if (nca_ctx[j].fs_ctx[0].has_sparse_layer) continue;
|
||||||
|
|
||||||
switch(content_info->content_type)
|
switch(content_info->content_type)
|
||||||
{
|
{
|
||||||
case NcmContentType_Program:
|
case NcmContentType_Program:
|
||||||
|
@ -352,9 +354,6 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
consolePrint("cnmt xml succeeded\n");
|
consolePrint("cnmt xml succeeded\n");
|
||||||
|
|
||||||
//sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
|
||||||
//writeFile(cnmt_ctx.raw_data, cnmt_ctx.raw_data_size, path);
|
|
||||||
|
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
||||||
writeFile(cnmt_ctx.authoring_tool_xml, cnmt_ctx.authoring_tool_xml_size, path);
|
writeFile(cnmt_ctx.authoring_tool_xml, cnmt_ctx.authoring_tool_xml_size, path);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -94,14 +94,15 @@ typedef struct {
|
||||||
NXDT_ASSERT(BktrAesCtrExStorageBlock, 0x4000);
|
NXDT_ASSERT(BktrAesCtrExStorageBlock, 0x4000);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
RomFileSystemContext base_romfs_ctx; ///< Base NCA RomFS context.
|
RomFileSystemContext base_romfs_ctx; ///< Base NCA RomFS context.
|
||||||
RomFileSystemContext patch_romfs_ctx; ///< Update NCA RomFS context. Must be used with RomFS directory/file entry functions, because it holds the updated directory/file tables.
|
RomFileSystemContext patch_romfs_ctx; ///< Update NCA RomFS context. Must be used with RomFS directory/file entry functions, because it holds the updated directory/file tables.
|
||||||
u64 offset; ///< Patched RomFS image offset (relative to the start of the update NCA FS section).
|
u64 offset; ///< Patched RomFS image offset (relative to the start of the update NCA FS section).
|
||||||
u64 size; ///< Patched RomFS image size.
|
u64 size; ///< Patched RomFS image size.
|
||||||
u64 body_offset; ///< Patched RomFS image file data body offset (relative to the start of the RomFS).
|
u64 body_offset; ///< Patched RomFS image file data body offset (relative to the start of the RomFS).
|
||||||
BktrIndirectStorageBlock *indirect_block; ///< BKTR Indirect Storage Block.
|
BktrIndirectStorageBlock *indirect_block; ///< BKTR Indirect Storage Block.
|
||||||
BktrAesCtrExStorageBlock *aes_ctr_ex_block; ///< BKTR AesCtrEx Storage Block.
|
BktrAesCtrExStorageBlock *aes_ctr_ex_block; ///< BKTR AesCtrEx Storage Block.
|
||||||
bool missing_base_romfs; ///< If true, only Patch RomFS data is used. Needed for games with base Program NCAs without a RomFS section (e.g. Fortnite, World of Tanks Blitz, etc.).
|
bool missing_base_romfs; ///< If true, only Patch RomFS data is used. Needed for games with base Program NCAs without a RomFS section (e.g. Fortnite, World of Tanks Blitz, etc.).
|
||||||
|
BktrIndirectStorageBlock *base_indirect_block; ///< Base NCA Indirect Storage Block (sparse layer), if available.
|
||||||
} BktrContext;
|
} BktrContext;
|
||||||
|
|
||||||
/// Initializes a BKTR context.
|
/// Initializes a BKTR context.
|
||||||
|
@ -127,6 +128,7 @@ NX_INLINE void bktrFreeContext(BktrContext *ctx)
|
||||||
romfsFreeContext(&(ctx->patch_romfs_ctx));
|
romfsFreeContext(&(ctx->patch_romfs_ctx));
|
||||||
if (ctx->indirect_block) free(ctx->indirect_block);
|
if (ctx->indirect_block) free(ctx->indirect_block);
|
||||||
if (ctx->aes_ctr_ex_block) free(ctx->aes_ctr_ex_block);
|
if (ctx->aes_ctr_ex_block) free(ctx->aes_ctr_ex_block);
|
||||||
|
if (ctx->base_indirect_block) free(ctx->base_indirect_block);
|
||||||
memset(ctx, 0, sizeof(BktrContext));
|
memset(ctx, 0, sizeof(BktrContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,13 @@
|
||||||
|
|
||||||
#include "nxdt_utils.h"
|
#include "nxdt_utils.h"
|
||||||
#include "bktr.h"
|
#include "bktr.h"
|
||||||
|
#include "aes.h"
|
||||||
|
|
||||||
/* Function prototypes. */
|
/* Function prototypes. */
|
||||||
|
|
||||||
|
static bool bktrInitializeIndirectStorage(NcaFsSectionContext *nca_fs_ctx, bool patch_indirect_bucket, BktrIndirectStorageBlock **out_indirect_block);
|
||||||
|
static bool bktrInitializeAesCtrExStorage(NcaFsSectionContext *nca_fs_ctx, BktrAesCtrExStorageBlock **out_aes_ctr_ex_block);
|
||||||
|
|
||||||
static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, u64 offset);
|
static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, u64 offset);
|
||||||
static bool bktrAesCtrExStorageRead(BktrContext *ctx, void *out, u64 read_size, u64 virtual_offset, u64 section_offset);
|
static bool bktrAesCtrExStorageRead(BktrContext *ctx, void *out, u64 read_size, u64 virtual_offset, u64 section_offset);
|
||||||
|
|
||||||
|
@ -37,6 +41,7 @@ static BktrAesCtrExStorageEntry *bktrGetAesCtrExStorageEntry(BktrAesCtrExStorage
|
||||||
bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ctx, NcaFsSectionContext *update_nca_fs_ctx)
|
bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ctx, NcaFsSectionContext *update_nca_fs_ctx)
|
||||||
{
|
{
|
||||||
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
|
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
|
||||||
|
bool success = false, dump_patch_romfs_header = false;
|
||||||
|
|
||||||
if (!out || !base_nca_fs_ctx || !(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || \
|
if (!out || !base_nca_fs_ctx || !(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || \
|
||||||
!update_nca_fs_ctx || !update_nca_fs_ctx->enabled || !(update_nca_ctx = (NcaContext*)update_nca_fs_ctx->nca_ctx) || \
|
!update_nca_fs_ctx || !update_nca_fs_ctx->enabled || !(update_nca_ctx = (NcaContext*)update_nca_fs_ctx->nca_ctx) || \
|
||||||
|
@ -60,7 +65,6 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
||||||
|
|
||||||
/* Update missing base NCA RomFS status. */
|
/* Update missing base NCA RomFS status. */
|
||||||
out->missing_base_romfs = (!base_nca_fs_ctx->enabled || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs);
|
out->missing_base_romfs = (!base_nca_fs_ctx->enabled || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs);
|
||||||
|
|
||||||
if (!out->missing_base_romfs)
|
if (!out->missing_base_romfs)
|
||||||
{
|
{
|
||||||
if (!base_nca_fs_ctx->has_sparse_layer)
|
if (!base_nca_fs_ctx->has_sparse_layer)
|
||||||
|
@ -73,90 +77,21 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Initializing the base NCA RomFS on its own is impossible if we're dealing with a sparse layer. */
|
/* Initializing the base NCA RomFS on its own is impossible if we're dealing with a sparse layer. */
|
||||||
/* Let's just handle everything here. */
|
/* Let's just handle everything here, starting with initializing the sparse layer's indirect storage. */
|
||||||
LOG_MSG("We got here, that's gotta be something.");
|
if (!bktrInitializeIndirectStorage(base_nca_fs_ctx, false, &(out->base_indirect_block))) return false;
|
||||||
return false;
|
|
||||||
|
/* Update the NCA FS section context from the base RomFS context. */
|
||||||
|
out->base_romfs_ctx.nca_fs_ctx = base_nca_fs_ctx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill context. */
|
/* Initialize Patch RomFS indirect storage. */
|
||||||
bool success = false, dump_patch_romfs_header = false;
|
if (!bktrInitializeIndirectStorage(update_nca_fs_ctx, true, &(out->indirect_block))) goto end;
|
||||||
NcaPatchInfo *patch_info = &(update_nca_fs_ctx->header.patch_info);
|
|
||||||
|
|
||||||
/* Allocate space for an extra (fake) indirect storage entry, to simplify our logic. */
|
/* Initialize Patch RomFS AesCtrEx storage. */
|
||||||
out->indirect_block = calloc(1, patch_info->indirect_bucket.size + ((0x3FF0 / sizeof(u64)) * sizeof(BktrIndirectStorageEntry)));
|
if (!bktrInitializeAesCtrExStorage(update_nca_fs_ctx, &(out->aes_ctr_ex_block))) goto end;
|
||||||
if (!out->indirect_block)
|
|
||||||
{
|
|
||||||
LOG_MSG("Unable to allocate memory for the BKTR Indirect Storage Block!");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read indirect storage block data. */
|
/* Initialize Patch RomFS context. */
|
||||||
if (!ncaReadFsSection(update_nca_fs_ctx, out->indirect_block, patch_info->indirect_bucket.size, patch_info->indirect_bucket.offset))
|
|
||||||
{
|
|
||||||
LOG_MSG("Failed to read BKTR Indirect Storage Block data!");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate space for an extra (fake) AesCtrEx storage entry, to simplify our logic. */
|
|
||||||
out->aes_ctr_ex_block = calloc(1, patch_info->aes_ctr_ex_bucket.size + (((0x3FF0 / sizeof(u64)) + 1) * sizeof(BktrAesCtrExStorageEntry)));
|
|
||||||
if (!out->aes_ctr_ex_block)
|
|
||||||
{
|
|
||||||
LOG_MSG("Unable to allocate memory for the BKTR AesCtrEx Storage Block!");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read AesCtrEx storage block data. */
|
|
||||||
if (!ncaReadFsSection(update_nca_fs_ctx, out->aes_ctr_ex_block, patch_info->aes_ctr_ex_bucket.size, patch_info->aes_ctr_ex_bucket.offset))
|
|
||||||
{
|
|
||||||
LOG_MSG("Failed to read BKTR AesCtrEx Storage Block data!");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out->aes_ctr_ex_block->physical_size != patch_info->aes_ctr_ex_bucket.offset)
|
|
||||||
{
|
|
||||||
LOG_DATA(out->aes_ctr_ex_block, patch_info->aes_ctr_ex_bucket.size, "Invalid BKTR AesCtrEx Storage Block size! AesCtrEx Storage Block dump:");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This simplifies logic greatly... */
|
|
||||||
for(u32 i = (out->indirect_block->bucket_count - 1); i > 0; i--)
|
|
||||||
{
|
|
||||||
BktrIndirectStorageBucket tmp_bucket = {0};
|
|
||||||
memcpy(&tmp_bucket, &(out->indirect_block->indirect_storage_buckets[i]), sizeof(BktrIndirectStorageBucket));
|
|
||||||
memcpy(bktrGetIndirectStorageBucket(out->indirect_block, i), &tmp_bucket, sizeof(BktrIndirectStorageBucket));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(u32 i = 0; (i + 1) < out->indirect_block->bucket_count; i++)
|
|
||||||
{
|
|
||||||
BktrIndirectStorageBucket *cur_bucket = bktrGetIndirectStorageBucket(out->indirect_block, i);
|
|
||||||
cur_bucket->indirect_storage_entries[cur_bucket->entry_count].virtual_offset = out->indirect_block->virtual_offsets[i + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(u32 i = (out->aes_ctr_ex_block->bucket_count - 1); i > 0; i--)
|
|
||||||
{
|
|
||||||
BktrAesCtrExStorageBucket tmp_bucket = {0};
|
|
||||||
memcpy(&tmp_bucket, &(out->aes_ctr_ex_block->aes_ctr_ex_storage_buckets[i]), sizeof(BktrAesCtrExStorageBucket));
|
|
||||||
memcpy(bktrGetAesCtrExStorageBucket(out->aes_ctr_ex_block, i), &tmp_bucket, sizeof(BktrAesCtrExStorageBucket));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(u32 i = 0; (i + 1) < out->aes_ctr_ex_block->bucket_count; i++)
|
|
||||||
{
|
|
||||||
BktrAesCtrExStorageBucket *cur_bucket = bktrGetAesCtrExStorageBucket(out->aes_ctr_ex_block, i);
|
|
||||||
BktrAesCtrExStorageBucket *next_bucket = bktrGetAesCtrExStorageBucket(out->aes_ctr_ex_block, i + 1);
|
|
||||||
cur_bucket->aes_ctr_ex_storage_entries[cur_bucket->entry_count].offset = next_bucket->aes_ctr_ex_storage_entries[0].offset;
|
|
||||||
cur_bucket->aes_ctr_ex_storage_entries[cur_bucket->entry_count].generation = next_bucket->aes_ctr_ex_storage_entries[0].generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
BktrIndirectStorageBucket *last_indirect_bucket = bktrGetIndirectStorageBucket(out->indirect_block, out->indirect_block->bucket_count - 1);
|
|
||||||
BktrAesCtrExStorageBucket *last_aes_ctr_ex_bucket = bktrGetAesCtrExStorageBucket(out->aes_ctr_ex_block, out->aes_ctr_ex_block->bucket_count - 1);
|
|
||||||
last_indirect_bucket->indirect_storage_entries[last_indirect_bucket->entry_count].virtual_offset = out->indirect_block->virtual_size;
|
|
||||||
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count].offset = patch_info->indirect_bucket.offset;
|
|
||||||
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count].generation = update_nca_fs_ctx->header.aes_ctr_upper_iv.generation;
|
|
||||||
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count + 1].offset = update_nca_fs_ctx->section_size;
|
|
||||||
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count + 1].generation = 0;
|
|
||||||
|
|
||||||
/* Initialize update NCA RomFS context. */
|
|
||||||
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image. */
|
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image. */
|
||||||
out->patch_romfs_ctx.nca_fs_ctx = update_nca_fs_ctx;
|
out->patch_romfs_ctx.nca_fs_ctx = update_nca_fs_ctx;
|
||||||
|
|
||||||
|
@ -169,7 +104,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
||||||
out->patch_romfs_ctx.offset = out->offset;
|
out->patch_romfs_ctx.offset = out->offset;
|
||||||
out->patch_romfs_ctx.size = out->size;
|
out->patch_romfs_ctx.size = out->size;
|
||||||
|
|
||||||
/* Read update NCA RomFS header. */
|
/* Read Patch RomFS header. */
|
||||||
if (!bktrPhysicalSectionRead(out, &(out->patch_romfs_ctx.header), sizeof(RomFileSystemHeader), out->patch_romfs_ctx.offset))
|
if (!bktrPhysicalSectionRead(out, &(out->patch_romfs_ctx.header), sizeof(RomFileSystemHeader), out->patch_romfs_ctx.offset))
|
||||||
{
|
{
|
||||||
LOG_MSG("Failed to read update NCA RomFS header!");
|
LOG_MSG("Failed to read update NCA RomFS header!");
|
||||||
|
@ -183,7 +118,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read directory entries table. */
|
/* Read Patch RomFS directory entries table. */
|
||||||
u64 dir_table_offset = out->patch_romfs_ctx.header.cur_format.directory_entry_offset;
|
u64 dir_table_offset = out->patch_romfs_ctx.header.cur_format.directory_entry_offset;
|
||||||
out->patch_romfs_ctx.dir_table_size = out->patch_romfs_ctx.header.cur_format.directory_entry_size;
|
out->patch_romfs_ctx.dir_table_size = out->patch_romfs_ctx.header.cur_format.directory_entry_size;
|
||||||
|
|
||||||
|
@ -207,7 +142,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read file entries table. */
|
/* Read Patch RomFS file entries table. */
|
||||||
u64 file_table_offset = out->patch_romfs_ctx.header.cur_format.file_entry_offset;
|
u64 file_table_offset = out->patch_romfs_ctx.header.cur_format.file_entry_offset;
|
||||||
out->patch_romfs_ctx.file_table_size = out->patch_romfs_ctx.header.cur_format.file_entry_size;
|
out->patch_romfs_ctx.file_table_size = out->patch_romfs_ctx.header.cur_format.file_entry_size;
|
||||||
|
|
||||||
|
@ -231,7 +166,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get file data body offset. */
|
/* Get Patch RomFS file data body offset. */
|
||||||
out->patch_romfs_ctx.body_offset = out->body_offset = out->patch_romfs_ctx.header.cur_format.body_offset;
|
out->patch_romfs_ctx.body_offset = out->body_offset = out->patch_romfs_ctx.header.cur_format.body_offset;
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -240,7 +175,6 @@ end:
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
if (dump_patch_romfs_header) LOG_DATA(&(out->patch_romfs_ctx.header), sizeof(RomFileSystemHeader), "Update RomFS header dump:");
|
if (dump_patch_romfs_header) LOG_DATA(&(out->patch_romfs_ctx.header), sizeof(RomFileSystemHeader), "Update RomFS header dump:");
|
||||||
|
|
||||||
bktrFreeContext(out);
|
bktrFreeContext(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,9 +255,140 @@ bool bktrIsFileEntryUpdated(BktrContext *ctx, RomFileSystemFileEntry *file_entry
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool bktrInitializeIndirectStorage(NcaFsSectionContext *nca_fs_ctx, bool patch_indirect_bucket, BktrIndirectStorageBlock **out_indirect_block)
|
||||||
|
{
|
||||||
|
if (!nca_fs_ctx || (patch_indirect_bucket && nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs) || \
|
||||||
|
(!patch_indirect_bucket && (nca_fs_ctx->section_type != NcaFsSectionType_RomFs || !nca_fs_ctx->has_sparse_layer)) || !out_indirect_block)
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid parameters!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NcaBucketInfo *indirect_bucket = (patch_indirect_bucket ? &(nca_fs_ctx->header.patch_info.indirect_bucket) : &(nca_fs_ctx->header.sparse_info.bucket));
|
||||||
|
BktrIndirectStorageBlock *indirect_block = NULL;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
/* Allocate space for an extra (fake) indirect storage entry, to simplify our logic. */
|
||||||
|
indirect_block = calloc(1, indirect_bucket->size + ((0x3FF0 / sizeof(u64)) * sizeof(BktrIndirectStorageEntry)));
|
||||||
|
if (!indirect_block)
|
||||||
|
{
|
||||||
|
LOG_MSG("Unable to allocate memory for the BKTR Indirect Storage Block! (%s).", patch_indirect_bucket ? "patch" : "sparse");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read indirect storage block data. */
|
||||||
|
if ((patch_indirect_bucket && !ncaReadFsSection(nca_fs_ctx, indirect_block, indirect_bucket->size, indirect_bucket->offset)) || \
|
||||||
|
(!patch_indirect_bucket && !ncaReadContentFile((NcaContext*)nca_fs_ctx->nca_ctx, indirect_block, nca_fs_ctx->sparse_table_size, nca_fs_ctx->sparse_table_offset)))
|
||||||
|
{
|
||||||
|
LOG_MSG("Failed to read BKTR Indirect Storage Block data! (%s).", patch_indirect_bucket ? "patch" : "sparse");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrypt indirect block, if needed. */
|
||||||
|
if (!patch_indirect_bucket)
|
||||||
|
{
|
||||||
|
aes128CtrUpdatePartialCtr(nca_fs_ctx->sparse_ctr, nca_fs_ctx->sparse_table_offset);
|
||||||
|
aes128CtrContextResetCtr(&(nca_fs_ctx->sparse_ctr_ctx), nca_fs_ctx->sparse_ctr);
|
||||||
|
aes128CtrCrypt(&(nca_fs_ctx->sparse_ctr_ctx), indirect_block, indirect_block, nca_fs_ctx->sparse_table_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This simplifies logic greatly... */
|
||||||
|
for(u32 i = (indirect_block->bucket_count - 1); i > 0; i--)
|
||||||
|
{
|
||||||
|
BktrIndirectStorageBucket tmp_bucket = {0};
|
||||||
|
memcpy(&tmp_bucket, &(indirect_block->indirect_storage_buckets[i]), sizeof(BktrIndirectStorageBucket));
|
||||||
|
memcpy(bktrGetIndirectStorageBucket(indirect_block, i), &tmp_bucket, sizeof(BktrIndirectStorageBucket));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(u32 i = 0; (i + 1) < indirect_block->bucket_count; i++)
|
||||||
|
{
|
||||||
|
BktrIndirectStorageBucket *cur_bucket = bktrGetIndirectStorageBucket(indirect_block, i);
|
||||||
|
cur_bucket->indirect_storage_entries[cur_bucket->entry_count].virtual_offset = indirect_block->virtual_offsets[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
BktrIndirectStorageBucket *last_indirect_bucket = bktrGetIndirectStorageBucket(indirect_block, indirect_block->bucket_count - 1);
|
||||||
|
last_indirect_bucket->indirect_storage_entries[last_indirect_bucket->entry_count].virtual_offset = indirect_block->virtual_size;
|
||||||
|
|
||||||
|
/* Update output values. */
|
||||||
|
*out_indirect_block = indirect_block;
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (!success && indirect_block) free(indirect_block);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bktrInitializeAesCtrExStorage(NcaFsSectionContext *nca_fs_ctx, BktrAesCtrExStorageBlock **out_aes_ctr_ex_block)
|
||||||
|
{
|
||||||
|
if (!nca_fs_ctx || nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || !out_aes_ctr_ex_block)
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid parameters!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NcaBucketInfo *aes_ctr_ex_bucket = &(nca_fs_ctx->header.patch_info.aes_ctr_ex_bucket);
|
||||||
|
BktrAesCtrExStorageBlock *aes_ctr_ex_block = NULL;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
/* Allocate space for an extra (fake) AesCtrEx storage entry, to simplify our logic. */
|
||||||
|
aes_ctr_ex_block = calloc(1, aes_ctr_ex_bucket->size + (((0x3FF0 / sizeof(u64)) + 1) * sizeof(BktrAesCtrExStorageEntry)));
|
||||||
|
if (!aes_ctr_ex_block)
|
||||||
|
{
|
||||||
|
LOG_MSG("Unable to allocate memory for the BKTR AesCtrEx Storage Block!");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read AesCtrEx storage block data. */
|
||||||
|
if (!ncaReadFsSection(nca_fs_ctx, aes_ctr_ex_block, aes_ctr_ex_bucket->size, aes_ctr_ex_bucket->offset))
|
||||||
|
{
|
||||||
|
LOG_MSG("Failed to read BKTR AesCtrEx Storage Block data!");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aes_ctr_ex_block->physical_size != aes_ctr_ex_bucket->offset)
|
||||||
|
{
|
||||||
|
LOG_DATA(aes_ctr_ex_block, aes_ctr_ex_bucket->size, "Invalid BKTR AesCtrEx Storage Block size! AesCtrEx Storage Block dump:");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This simplifies logic greatly... */
|
||||||
|
for(u32 i = (aes_ctr_ex_block->bucket_count - 1); i > 0; i--)
|
||||||
|
{
|
||||||
|
BktrAesCtrExStorageBucket tmp_bucket = {0};
|
||||||
|
memcpy(&tmp_bucket, &(aes_ctr_ex_block->aes_ctr_ex_storage_buckets[i]), sizeof(BktrAesCtrExStorageBucket));
|
||||||
|
memcpy(bktrGetAesCtrExStorageBucket(aes_ctr_ex_block, i), &tmp_bucket, sizeof(BktrAesCtrExStorageBucket));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(u32 i = 0; (i + 1) < aes_ctr_ex_block->bucket_count; i++)
|
||||||
|
{
|
||||||
|
BktrAesCtrExStorageBucket *cur_bucket = bktrGetAesCtrExStorageBucket(aes_ctr_ex_block, i);
|
||||||
|
BktrAesCtrExStorageBucket *next_bucket = bktrGetAesCtrExStorageBucket(aes_ctr_ex_block, i + 1);
|
||||||
|
cur_bucket->aes_ctr_ex_storage_entries[cur_bucket->entry_count].offset = next_bucket->aes_ctr_ex_storage_entries[0].offset;
|
||||||
|
cur_bucket->aes_ctr_ex_storage_entries[cur_bucket->entry_count].generation = next_bucket->aes_ctr_ex_storage_entries[0].generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
BktrAesCtrExStorageBucket *last_aes_ctr_ex_bucket = bktrGetAesCtrExStorageBucket(aes_ctr_ex_block, aes_ctr_ex_block->bucket_count - 1);
|
||||||
|
|
||||||
|
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count].offset = nca_fs_ctx->header.patch_info.indirect_bucket.offset;
|
||||||
|
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count].generation = nca_fs_ctx->header.aes_ctr_upper_iv.generation;
|
||||||
|
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count + 1].offset = nca_fs_ctx->section_size;
|
||||||
|
last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count + 1].generation = 0;
|
||||||
|
|
||||||
|
/* Update output values. */
|
||||||
|
*out_aes_ctr_ex_block = aes_ctr_ex_block;
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (!success && aes_ctr_ex_block) free(aes_ctr_ex_block);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, u64 offset)
|
static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, u64 offset)
|
||||||
{
|
{
|
||||||
if (!ctx || (!ctx->missing_base_romfs && !ctx->base_romfs_ctx.nca_fs_ctx) || !ctx->indirect_block || !out || !read_size)
|
if (!ctx || (!ctx->missing_base_romfs && ((!ctx->base_romfs_ctx.nca_fs_ctx->has_sparse_layer && !ctx->base_romfs_ctx.nca_fs_ctx) || \
|
||||||
|
(ctx->base_romfs_ctx.nca_fs_ctx->has_sparse_layer && !ctx->base_indirect_block))) || !ctx->indirect_block || !out || !read_size)
|
||||||
{
|
{
|
||||||
LOG_MSG("Invalid parameters!");
|
LOG_MSG("Invalid parameters!");
|
||||||
return false;
|
return false;
|
||||||
|
@ -357,8 +422,22 @@ static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size,
|
||||||
} else
|
} else
|
||||||
if (!ctx->missing_base_romfs)
|
if (!ctx->missing_base_romfs)
|
||||||
{
|
{
|
||||||
success = ncaReadFsSection(ctx->base_romfs_ctx.nca_fs_ctx, out, read_size, section_offset);
|
if (!ctx->base_romfs_ctx.nca_fs_ctx->has_sparse_layer)
|
||||||
if (!success) LOG_MSG("Failed to read 0x%lX bytes block from base RomFS at offset 0x%lX!", read_size, section_offset);
|
{
|
||||||
|
success = ncaReadFsSection(ctx->base_romfs_ctx.nca_fs_ctx, out, read_size, section_offset);
|
||||||
|
if (!success) LOG_MSG("Failed to read 0x%lX bytes block from base RomFS at offset 0x%lX!", read_size, section_offset);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//ctx->base_indirect_block
|
||||||
|
|
||||||
|
|
||||||
|
LOG_MSG("Attempting to read 0x%lX bytes block from sparse layer at offset 0x%lX!", read_size, section_offset);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_MSG("Attempting to read 0x%lX bytes block from non-existent base RomFS at offset 0x%lX!", read_size, section_offset);
|
LOG_MSG("Attempting to read 0x%lX bytes block from non-existent base RomFS at offset 0x%lX!", read_size, section_offset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -838,6 +838,8 @@ end:
|
||||||
|
|
||||||
static void gamecardFreeInfo(bool clear_status)
|
static void gamecardFreeInfo(bool clear_status)
|
||||||
{
|
{
|
||||||
|
gamecardCloseStorageArea(true);
|
||||||
|
|
||||||
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
|
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
|
||||||
|
|
||||||
memset(&g_gameCardInfoArea, 0, sizeof(GameCardInfo));
|
memset(&g_gameCardInfoArea, 0, sizeof(GameCardInfo));
|
||||||
|
@ -864,8 +866,6 @@ static void gamecardFreeInfo(bool clear_status)
|
||||||
|
|
||||||
g_gameCardHfsCount = 0;
|
g_gameCardHfsCount = 0;
|
||||||
|
|
||||||
gamecardCloseStorageArea(true);
|
|
||||||
|
|
||||||
if (clear_status) g_gameCardStatus = GameCardStatus_NotInserted;
|
if (clear_status) g_gameCardStatus = GameCardStatus_NotInserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -708,19 +708,17 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
/* Clear FS section context. */
|
|
||||||
memset(fs_ctx, 0, sizeof(NcaFsSectionContext));
|
|
||||||
|
|
||||||
/* Fill section context. */
|
/* Fill section context. */
|
||||||
fs_ctx->nca_ctx = nca_ctx;
|
fs_ctx->nca_ctx = nca_ctx;
|
||||||
fs_ctx->section_idx = section_idx;
|
fs_ctx->section_idx = section_idx;
|
||||||
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
||||||
fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
|
fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
|
||||||
|
fs_ctx->enabled = false;
|
||||||
|
|
||||||
/* Don't proceed if this NCA FS section isn't populated. */
|
/* Don't proceed if this NCA FS section isn't populated. */
|
||||||
if (!ncaIsFsInfoEntryValid(fs_info))
|
if (!ncaIsFsInfoEntryValid(fs_info))
|
||||||
{
|
{
|
||||||
LOG_MSG("Invalid FsInfo entry for section #%u in \"%s\". Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
//LOG_MSG("Invalid FsInfo entry for section #%u in \"%s\". Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,8 +826,7 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update section size. */
|
/* Update section size. */
|
||||||
fs_ctx->section_size = (MIN(fs_ctx->section_offset, raw_storage_offset) + (MAX(fs_ctx->section_offset, raw_storage_offset) - \
|
fs_ctx->section_size = raw_storage_size;
|
||||||
MIN(fs_ctx->section_offset, raw_storage_offset)) + raw_storage_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we're within boundaries. */
|
/* Check if we're within boundaries. */
|
||||||
|
@ -839,24 +836,6 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate HashData boundaries. */
|
|
||||||
if (!ncaFsSectionValidateHashDataBoundaries(fs_ctx)) goto end;
|
|
||||||
|
|
||||||
/* Get hash layer region size (offset must always be 0). */
|
|
||||||
fs_ctx->hash_region.offset = 0;
|
|
||||||
if (!ncaGetFsSectionHashTargetProperties(fs_ctx, NULL, &(fs_ctx->hash_region.size)))
|
|
||||||
{
|
|
||||||
LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", fs_ctx->section_idx, nca_ctx->content_id_str, fs_ctx->hash_type);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we're within boundaries. */
|
|
||||||
if (fs_ctx->hash_region.size > fs_ctx->section_size || (fs_ctx->section_offset + fs_ctx->hash_region.size) > nca_ctx->content_size)
|
|
||||||
{
|
|
||||||
LOG_MSG("Hash layer region for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine FS section type. */
|
/* Determine FS section type. */
|
||||||
/* TODO: should NcaHashType_None be handled here as well? */
|
/* TODO: should NcaHashType_None be handled here as well? */
|
||||||
switch(fs_ctx->header.fs_type)
|
switch(fs_ctx->header.fs_type)
|
||||||
|
@ -906,6 +885,24 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Validate HashData boundaries. */
|
||||||
|
if (!ncaFsSectionValidateHashDataBoundaries(fs_ctx)) goto end;
|
||||||
|
|
||||||
|
/* Get hash layer region size (offset must always be 0). */
|
||||||
|
fs_ctx->hash_region.offset = 0;
|
||||||
|
if (!ncaGetFsSectionHashTargetProperties(fs_ctx, &(fs_ctx->hash_region.size), NULL))
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", fs_ctx->section_idx, nca_ctx->content_id_str, fs_ctx->hash_type);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're within boundaries. */
|
||||||
|
if (fs_ctx->hash_region.size > fs_ctx->section_size || (fs_ctx->section_offset + fs_ctx->hash_region.size) > nca_ctx->content_size)
|
||||||
|
{
|
||||||
|
LOG_MSG("Hash layer region for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if we should skip hash layer decryption while reading this FS section. */
|
/* Check if we should skip hash layer decryption while reading this FS section. */
|
||||||
fs_ctx->skip_hash_layer_crypto = (fs_ctx->encryption_type == NcaEncryptionType_AesCtrSkipLayerHash || fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash);
|
fs_ctx->skip_hash_layer_crypto = (fs_ctx->encryption_type == NcaEncryptionType_AesCtrSkipLayerHash || fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash);
|
||||||
if (fs_ctx->skip_hash_layer_crypto && fs_ctx->hash_type == NcaHashType_None)
|
if (fs_ctx->skip_hash_layer_crypto && fs_ctx->hash_type == NcaHashType_None)
|
||||||
|
@ -927,7 +924,6 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
NcaAesCtrUpperIv sparse_upper_iv = {0};
|
NcaAesCtrUpperIv sparse_upper_iv = {0};
|
||||||
memcpy(sparse_upper_iv.value, fs_ctx->header.aes_ctr_upper_iv.value, sizeof(sparse_upper_iv.value));
|
memcpy(sparse_upper_iv.value, fs_ctx->header.aes_ctr_upper_iv.value, sizeof(sparse_upper_iv.value));
|
||||||
sparse_upper_iv.generation = ((u32)(sparse_info->generation) << 16);
|
sparse_upper_iv.generation = ((u32)(sparse_info->generation) << 16);
|
||||||
|
|
||||||
aes128CtrInitializePartialCtr(fs_ctx->sparse_ctr, sparse_upper_iv.value, fs_ctx->sparse_table_offset);
|
aes128CtrInitializePartialCtr(fs_ctx->sparse_ctr, sparse_upper_iv.value, fs_ctx->sparse_table_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,7 +983,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
|
||||||
{
|
{
|
||||||
/* Validate all hash regions boundaries. Skip the last one if a sparse layer is used. */
|
/* Validate all hash regions boundaries. Skip the last one if a sparse layer is used. */
|
||||||
NcaRegion *hash_region = &(hash_data->hash_region[i]);
|
NcaRegion *hash_region = &(hash_data->hash_region[i]);
|
||||||
if (hash_region->offset != accum || !hash_region->size || \
|
if (hash_region->offset < accum || !hash_region->size || \
|
||||||
((i < (hash_data->hash_region_count - 1) || !ctx->has_sparse_layer) && (hash_region->offset + hash_region->size) > ctx->section_size))
|
((i < (hash_data->hash_region_count - 1) || !ctx->has_sparse_layer) && (hash_region->offset + hash_region->size) > ctx->section_size))
|
||||||
{
|
{
|
||||||
LOG_MSG("HierarchicalSha256 region #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
|
LOG_MSG("HierarchicalSha256 region #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
|
||||||
|
@ -996,7 +992,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
accum += hash_region->size;
|
accum = (hash_region->offset + hash_region->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
success = valid;
|
success = valid;
|
||||||
|
@ -1017,10 +1013,10 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
|
||||||
|
|
||||||
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
||||||
{
|
{
|
||||||
/* Validate all level informations boundaries. Skip the last one if a sparse layer is used. */
|
/* Validate all level informations boundaries. Skip the last one if we're dealing with a Patch RomFS, or if a sparse layer is used. */
|
||||||
NcaHierarchicalIntegrityVerificationLevelInformation *level_information = &(hash_data->info_level_hash.level_information[i]);
|
NcaHierarchicalIntegrityVerificationLevelInformation *lvl_info = &(hash_data->info_level_hash.level_information[i]);
|
||||||
if (level_information->offset != accum || !level_information->size || !level_information->block_order || \
|
if (lvl_info->offset < accum || !lvl_info->size || !lvl_info->block_order || ((i < (NCA_IVFC_LEVEL_COUNT - 1) || \
|
||||||
((i < (NCA_IVFC_LEVEL_COUNT - 1) || !ctx->has_sparse_layer) && (level_information->offset + level_information->size) > ctx->section_size))
|
(!ctx->has_sparse_layer && ctx->section_type != NcaFsSectionType_PatchRomFs)) && (lvl_info->offset + lvl_info->size) > ctx->section_size))
|
||||||
{
|
{
|
||||||
LOG_MSG("HierarchicalIntegrity level #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
|
LOG_MSG("HierarchicalIntegrity level #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
|
||||||
i, ctx->section_idx, nca_ctx->content_id_str);
|
i, ctx->section_idx, nca_ctx->content_id_str);
|
||||||
|
@ -1028,7 +1024,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
accum += level_information->size;
|
accum = (lvl_info->offset + lvl_info->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
success = valid;
|
success = valid;
|
||||||
|
|
Loading…
Reference in a new issue