NACP changes.

Fixed types for some NACP struct entries (thanks @0Liam !) + added functions to generate and write NACP patches.
This commit is contained in:
Pablo Curiel 2020-10-28 18:48:46 -04:00
parent 2b8e80c283
commit 0229124173
11 changed files with 185 additions and 101 deletions

View file

@ -47,8 +47,11 @@ typedef struct {
static options_t options[] = { static options_t options[] = {
{ "set download distribution type", false }, { "set download distribution type", false },
{ "remove console specific data", false }, { "remove console specific data", false },
{ "remove titlekey crypto (implies previous option)", false }, { "remove titlekey crypto (overrides previous option)", false },
{ "change acid rsa key/sig", false } { "change acid rsa key/sig", false },
{ "disable linked account requirement", false },
{ "enable screenshots", false },
{ "enable video capture", false }
}; };
static const u32 options_count = MAX_ELEMENTS(options); static const u32 options_count = MAX_ELEMENTS(options);
@ -85,7 +88,14 @@ static void nspDump(TitleInfo *title_info)
for(u32 i = 0; i < options_count; i++) printf("%s: %s\n", options[i].str, options[i].val ? "yes" : "no"); for(u32 i = 0; i < options_count; i++) printf("%s: %s\n", options[i].str, options[i].val ? "yes" : "no");
printf("______________________________\n\n"); printf("______________________________\n\n");
bool set_download_type = options[0].val, remove_console_data = options[1].val, remove_titlekey_crypto = options[2].val, change_acid_rsa = options[3].val, success = false; bool set_download_type = options[0].val;
bool remove_console_data = options[1].val;
bool remove_titlekey_crypto = options[2].val;
bool change_acid_rsa = options[3].val;
bool patch_sua = options[4].val;
bool patch_screenshot = options[5].val;
bool patch_video_capture = options[6].val;
bool success = false;
u8 *buf = NULL; u8 *buf = NULL;
char *dump_name = NULL, *path = NULL; char *dump_name = NULL, *path = NULL;
@ -263,7 +273,11 @@ static void nspDump(TitleInfo *title_info)
goto end; goto end;
} }
// add nacp mods here if (!nacpGenerateNcaPatch(cur_nacp_ctx, patch_sua, patch_screenshot, patch_video_capture))
{
consolePrint("nacp nca patch failed (%s)\n", cur_nca_ctx->content_id_str);
goto end;
}
if (!nacpGenerateAuthoringToolXml(cur_nacp_ctx, title_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx))) if (!nacpGenerateAuthoringToolXml(cur_nacp_ctx, title_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
{ {
@ -505,18 +519,13 @@ static void nspDump(TitleInfo *title_info)
switch(cur_nca_ctx->content_type) switch(cur_nca_ctx->content_type)
{ {
case NcmContentType_Meta: case NcmContentType_Meta:
{
cnmtWriteNcaPatch(&cnmt_ctx, buf, blksize, offset); cnmtWriteNcaPatch(&cnmt_ctx, buf, blksize, offset);
break; break;
}
case NcmContentType_Program: case NcmContentType_Program:
{ programInfoWriteNcaPatch((ProgramInfoContext*)cur_nca_ctx->content_type_ctx, buf, blksize, offset);
ProgramInfoContext *cur_program_info_ctx = (ProgramInfoContext*)cur_nca_ctx->content_type_ctx;
programInfoWriteNcaPatch(cur_program_info_ctx, buf, blksize, offset);
break; break;
}
case NcmContentType_Control: case NcmContentType_Control:
// write nacp patches here nacpWriteNcaPatch((NacpContext*)cur_nca_ctx->content_type_ctx, buf, blksize, offset);
break; break;
default: default:
break; break;

View file

@ -46,8 +46,11 @@ typedef struct {
static options_t options[] = { static options_t options[] = {
{ "set download distribution type", false }, { "set download distribution type", false },
{ "remove console specific data", false }, { "remove console specific data", false },
{ "remove titlekey crypto (implies previous option)", false }, { "remove titlekey crypto (overrides previous option)", false },
{ "change acid rsa key/sig", false } { "change acid rsa key/sig", false },
{ "disable linked account requirement", false },
{ "enable screenshots", false },
{ "enable video capture", false }
}; };
static const u32 options_count = MAX_ELEMENTS(options); static const u32 options_count = MAX_ELEMENTS(options);
@ -84,7 +87,13 @@ static void nspDump(TitleInfo *title_info)
for(u32 i = 0; i < options_count; i++) printf("%s: %s\n", options[i].str, options[i].val ? "yes" : "no"); for(u32 i = 0; i < options_count; i++) printf("%s: %s\n", options[i].str, options[i].val ? "yes" : "no");
printf("______________________________\n\n"); printf("______________________________\n\n");
bool set_download_type = options[0].val, remove_console_data = options[1].val, remove_titlekey_crypto = options[2].val, change_acid_rsa = options[3].val; bool set_download_type = options[0].val;
bool remove_console_data = options[1].val;
bool remove_titlekey_crypto = options[2].val;
bool change_acid_rsa = options[3].val;
bool patch_sua = options[4].val;
bool patch_screenshot = options[5].val;
bool patch_video_capture = options[6].val;
u8 *buf = NULL; u8 *buf = NULL;
char *dump_name = NULL, *path = NULL; char *dump_name = NULL, *path = NULL;
@ -262,7 +271,11 @@ static void nspDump(TitleInfo *title_info)
goto end; goto end;
} }
// add nacp mods here if (!nacpGenerateNcaPatch(cur_nacp_ctx, patch_sua, patch_screenshot, patch_video_capture))
{
consolePrint("nacp nca patch failed (%s)\n", cur_nca_ctx->content_id_str);
goto end;
}
if (!nacpGenerateAuthoringToolXml(cur_nacp_ctx, title_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx))) if (!nacpGenerateAuthoringToolXml(cur_nacp_ctx, title_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
{ {
@ -523,18 +536,13 @@ static void nspDump(TitleInfo *title_info)
switch(cur_nca_ctx->content_type) switch(cur_nca_ctx->content_type)
{ {
case NcmContentType_Meta: case NcmContentType_Meta:
{
cnmtWriteNcaPatch(&cnmt_ctx, buf, blksize, offset); cnmtWriteNcaPatch(&cnmt_ctx, buf, blksize, offset);
break; break;
}
case NcmContentType_Program: case NcmContentType_Program:
{ programInfoWriteNcaPatch((ProgramInfoContext*)cur_nca_ctx->content_type_ctx, buf, blksize, offset);
ProgramInfoContext *cur_program_info_ctx = (ProgramInfoContext*)cur_nca_ctx->content_type_ctx;
programInfoWriteNcaPatch(cur_program_info_ctx, buf, blksize, offset);
break; break;
}
case NcmContentType_Control: case NcmContentType_Control:
// write nacp patches here nacpWriteNcaPatch((NacpContext*)cur_nca_ctx->content_type_ctx, buf, blksize, offset);
break; break;
default: default:
break; break;

View file

@ -320,14 +320,14 @@ void cnmtWriteNcaPatch(ContentMetaContext *cnmt_ctx, void *buf, u64 buf_size, u6
/* Using cnmtIsValidContext() here would probably take up precious CPU cycles. */ /* Using cnmtIsValidContext() here would probably take up precious CPU cycles. */
if (!cnmt_ctx || !cnmt_ctx->nca_ctx || cnmt_ctx->nca_ctx->content_type != NcmContentType_Meta || !cnmt_ctx->nca_ctx->content_type_ctx_patch || cnmt_ctx->nca_patch.written) return; if (!cnmt_ctx || !cnmt_ctx->nca_ctx || cnmt_ctx->nca_ctx->content_type != NcmContentType_Meta || !cnmt_ctx->nca_ctx->content_type_ctx_patch || cnmt_ctx->nca_patch.written) return;
/* Attempt to write Partition FS entry. */ /* Attempt to write Partition FS entry patch. */
pfsWriteEntryPatchToMemoryBuffer(&(cnmt_ctx->pfs_ctx), &(cnmt_ctx->nca_patch), buf, buf_size, buf_offset); pfsWriteEntryPatchToMemoryBuffer(&(cnmt_ctx->pfs_ctx), &(cnmt_ctx->nca_patch), buf, buf_size, buf_offset);
/* Check if we need to update the NCA content type context patch status. */ /* Check if we need to update the NCA content type context patch status. */
if (cnmt_ctx->nca_patch.written) if (cnmt_ctx->nca_patch.written)
{ {
cnmt_ctx->nca_ctx->content_type_ctx_patch = false; cnmt_ctx->nca_ctx->content_type_ctx_patch = false;
LOGFILE("CNMT Partition FS file entry patch successfully written to NCA \"%s\"!", cnmt_ctx->nca_ctx->content_id_str); LOGFILE("CNMT Partition FS entry patch successfully written to NCA \"%s\"!", cnmt_ctx->nca_ctx->content_id_str);
} }
} }

View file

@ -247,7 +247,7 @@ bool cnmtUpdateContentInfo(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx);
/// Generates a Partition FS entry patch for the NcaContext pointed to by the input ContentMetaContext, using its raw CNMT data. /// Generates a Partition FS entry patch for the NcaContext pointed to by the input ContentMetaContext, using its raw CNMT data.
bool cnmtGenerateNcaPatch(ContentMetaContext *cnmt_ctx); bool cnmtGenerateNcaPatch(ContentMetaContext *cnmt_ctx);
/// Writes data from the Partition FS patch in the input ContentMetaContext to the provided buffer. /// Writes data from the Partition FS entry patch in the input ContentMetaContext to the provided buffer.
void cnmtWriteNcaPatch(ContentMetaContext *cnmt_ctx, void *buf, u64 buf_size, u64 buf_offset); void cnmtWriteNcaPatch(ContentMetaContext *cnmt_ctx, void *buf, u64 buf_size, u64 buf_offset);
/// Generates an AuthoringTool-like XML using information from a previously initialized ContentMetaContext, as well as a pointer to 'nca_ctx_count' NcaContext with content information. /// Generates an AuthoringTool-like XML using information from a previously initialized ContentMetaContext, as well as a pointer to 'nca_ctx_count' NcaContext with content information.

View file

@ -329,6 +329,71 @@ end:
return success; return success;
} }
bool nacpGenerateNcaPatch(NacpContext *nacp_ctx, bool patch_sua, bool patch_screenshot, bool patch_video_capture)
{
if (!nacpIsValidContext(nacp_ctx))
{
LOGFILE("Invalid parameters!");
return false;
}
_NacpStruct *data = nacp_ctx->data;
u8 nacp_hash[SHA256_HASH_SIZE] = {0};
/* Check if we're not patching anything. */
if (!patch_sua && !patch_screenshot && !patch_video_capture) return true;
/* Patch StartupUserAccount, StartupUserAccountOption and UserAccountSwitchLock. */
if (patch_sua)
{
data->startup_user_account = NacpStartupUserAccount_None;
data->startup_user_account_option &= ~NacpStartupUserAccountOption_IsOptional;
data->user_account_switch_lock = NacpUserAccountSwitchLock_Disable;
}
/* Patch Screenshot. */
if (patch_screenshot) data->screenshot = NacpScreenshot_Allow;
/* Patch VideoCapture. */
if (patch_video_capture) data->video_capture = NacpVideoCapture_Enable;
/* Check if we really need to generate this patch. */
sha256CalculateHash(nacp_hash, data, sizeof(_NacpStruct));
if (!memcmp(nacp_hash, nacp_ctx->data_hash, sizeof(nacp_hash)))
{
LOGFILE("Skipping NACP patching - no flags have changed.");
return true;
}
/* Generate RomFS file entry patch. */
if (!romfsGenerateFileEntryPatch(&(nacp_ctx->romfs_ctx), nacp_ctx->romfs_file_entry, data, sizeof(_NacpStruct), 0, &(nacp_ctx->nca_patch)))
{
LOGFILE("Failed to generate RomFS file entry patch!");
return false;
}
/* Update NCA content type context patch status. */
nacp_ctx->nca_ctx->content_type_ctx_patch = true;
return true;
}
void nacpWriteNcaPatch(NacpContext *nacp_ctx, void *buf, u64 buf_size, u64 buf_offset)
{
/* Using nacpIsValidContext() here would probably take up precious CPU cycles. */
if (!nacp_ctx || !nacp_ctx->nca_ctx || nacp_ctx->nca_ctx->content_type != NcmContentType_Control || !nacp_ctx->nca_ctx->content_type_ctx_patch || nacp_ctx->nca_patch.written) return;
/* Attempt to write RomFS file entry patch. */
romfsWriteFileEntryPatchToMemoryBuffer(&(nacp_ctx->romfs_ctx), &(nacp_ctx->nca_patch), buf, buf_size, buf_offset);
/* Check if we need to update the NCA content type context patch status. */
if (nacp_ctx->nca_patch.written)
{
nacp_ctx->nca_ctx->content_type_ctx_patch = false;
LOGFILE("NACP RomFS file entry patch successfully written to NCA \"%s\"!", nacp_ctx->nca_ctx->content_id_str);
}
}
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version) bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version)
{ {
if (!nacpIsValidContext(nacp_ctx)) if (!nacpIsValidContext(nacp_ctx))
@ -424,13 +489,13 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 requir
/* Rating. */ /* Rating. */
for(i = 0, count = 0; i < NacpRatingAgeOrganization_Count; i++) for(i = 0, count = 0; i < NacpRatingAgeOrganization_Count; i++)
{ {
u8 age = *((u8*)&(nacp->rating_age) + i); s8 age = *(((s8*)&(nacp->rating_age)) + i);
if (age == 0xFF) continue; if (age < 0) continue;
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <Rating>\n" \ " <Rating>\n" \
" <Organization>%s</Organization>\n" \ " <Organization>%s</Organization>\n" \
" <Age>%u</Age>\n" \ " <Age>%d</Age>\n" \
" </Rating>\n", \ " </Rating>\n", \
nacpGetRatingAgeOrganizationString(i), nacpGetRatingAgeOrganizationString(i),
age)) goto end; age)) goto end;
@ -450,19 +515,19 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 requir
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SaveDataOwnerId", nacp->save_data_owner_id)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SaveDataOwnerId", nacp->save_data_owner_id)) goto end;
/* UserAccountSaveDataSize. */ /* UserAccountSaveDataSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataSize", nacp->user_account_save_data_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataSize", (u64)nacp->user_account_save_data_size)) goto end;
/* UserAccountSaveDataJournalSize. */ /* UserAccountSaveDataJournalSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataJournalSize", nacp->user_account_save_data_journal_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataJournalSize", (u64)nacp->user_account_save_data_journal_size)) goto end;
/* DeviceSaveDataSize. */ /* DeviceSaveDataSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataSize", nacp->device_save_data_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataSize", (u64)nacp->device_save_data_size)) goto end;
/* DeviceSaveDataJournalSize. */ /* DeviceSaveDataJournalSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataJournalSize", nacp->device_save_data_journal_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataJournalSize", (u64)nacp->device_save_data_journal_size)) goto end;
/* BcatDeliveryCacheStorageSize. */ /* BcatDeliveryCacheStorageSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "BcatDeliveryCacheStorageSize", nacp->bcat_delivery_cache_storage_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "BcatDeliveryCacheStorageSize", (u64)nacp->bcat_delivery_cache_storage_size)) goto end;
/* ApplicationErrorCodeCategory. */ /* ApplicationErrorCodeCategory. */
if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ApplicationErrorCodeCategory", nacp->application_error_code_category)) goto end; if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ApplicationErrorCodeCategory", nacp->application_error_code_category)) goto end;
@ -541,28 +606,28 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 requir
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentRegistrationType", nacp->add_on_content_registration_type, &nacpGetAddOnContentRegistrationTypeString)) goto end; if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentRegistrationType", nacp->add_on_content_registration_type, &nacpGetAddOnContentRegistrationTypeString)) goto end;
/* UserAccountSaveDataSizeMax. */ /* UserAccountSaveDataSizeMax. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataSizeMax", nacp->user_account_save_data_size_max)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataSizeMax", (u64)nacp->user_account_save_data_size_max)) goto end;
/* UserAccountSaveDataJournalSizeMax. */ /* UserAccountSaveDataJournalSizeMax. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataJournalSizeMax", nacp->user_account_save_data_journal_size_max)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataJournalSizeMax", (u64)nacp->user_account_save_data_journal_size_max)) goto end;
/* DeviceSaveDataSizeMax. */ /* DeviceSaveDataSizeMax. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataSizeMax", nacp->device_save_data_size_max)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataSizeMax", (u64)nacp->device_save_data_size_max)) goto end;
/* DeviceSaveDataJournalSizeMax. */ /* DeviceSaveDataJournalSizeMax. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataJournalSizeMax", nacp->device_save_data_journal_size_max)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataJournalSizeMax", (u64)nacp->device_save_data_journal_size_max)) goto end;
/* TemporaryStorageSize. */ /* TemporaryStorageSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "TemporaryStorageSize", nacp->temporary_storage_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "TemporaryStorageSize", (u64)nacp->temporary_storage_size)) goto end;
/* CacheStorageSize. */ /* CacheStorageSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageSize", nacp->cache_storage_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageSize", (u64)nacp->cache_storage_size)) goto end;
/* CacheStorageJournalSize. */ /* CacheStorageJournalSize. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageJournalSize", nacp->cache_storage_journal_size)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageJournalSize", (u64)nacp->cache_storage_journal_size)) goto end;
/* CacheStorageDataAndJournalSizeMax. */ /* CacheStorageDataAndJournalSizeMax. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageDataAndJournalSizeMax", nacp->cache_storage_data_and_journal_size_max)) goto end; if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageDataAndJournalSizeMax", (u64)nacp->cache_storage_data_and_journal_size_max)) goto end;
/* CacheStorageIndexMax. */ /* CacheStorageIndexMax. */
if (!nacpAddU16FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageIndexMax", nacp->cache_storage_index_max, true)) goto end; if (!nacpAddU16FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageIndexMax", nacp->cache_storage_index_max, true)) goto end;

View file

@ -75,7 +75,11 @@ typedef enum {
NacpLanguage_TraditionalChinese = 13, NacpLanguage_TraditionalChinese = 13,
NacpLanguage_SimplifiedChinese = 14, NacpLanguage_SimplifiedChinese = 14,
NacpLanguage_BrazilianPortuguese = 15, NacpLanguage_BrazilianPortuguese = 15,
NacpLanguage_Count = 16 ///< Total values supported by this enum. NacpLanguage_Count = 16, ///< Total values supported by this enum.
/// Old.
NacpLanguage_Taiwanese = NacpLanguage_TraditionalChinese,
NacpLanguage_Chinese = NacpLanguage_SimplifiedChinese
} NacpLanguage; } NacpLanguage;
typedef enum { typedef enum {
@ -156,20 +160,20 @@ typedef enum {
} NacpRatingAgeOrganization; } NacpRatingAgeOrganization;
typedef struct { typedef struct {
u8 cero; s8 cero;
u8 gracgcrb; s8 grac_gcrb;
u8 gsrmr; s8 gsrmr;
u8 esrb; s8 esrb;
u8 class_ind; s8 class_ind;
u8 usk; s8 usk;
u8 pegi; s8 pegi;
u8 pegi_portugal; s8 pegi_portugal;
u8 pegibbfc; s8 pegi_bbfc;
u8 russian; s8 russian;
u8 acb; s8 acb;
u8 oflc; s8 oflc;
u8 iarc_generic; s8 iarc_generic;
u8 reserved[0x13]; s8 reserved[0x13];
} NacpRatingAge; } NacpRatingAge;
typedef enum { typedef enum {
@ -293,11 +297,11 @@ typedef struct {
char display_version[0x10]; char display_version[0x10];
u64 add_on_content_base_id; u64 add_on_content_base_id;
u64 save_data_owner_id; u64 save_data_owner_id;
u64 user_account_save_data_size; s64 user_account_save_data_size;
u64 user_account_save_data_journal_size; s64 user_account_save_data_journal_size;
u64 device_save_data_size; s64 device_save_data_size;
u64 device_save_data_journal_size; s64 device_save_data_journal_size;
u64 bcat_delivery_cache_storage_size; s64 bcat_delivery_cache_storage_size;
char application_error_code_category[0x8]; char application_error_code_category[0x8];
u64 local_communication_id[8]; u64 local_communication_id[8];
u8 logo_type; ///< NacpLogoType. u8 logo_type; ///< NacpLogoType.
@ -311,14 +315,14 @@ typedef struct {
char bcat_passphrase[0x41]; char bcat_passphrase[0x41];
u8 startup_user_account_option; ///< NacpStartupUserAccountOption. u8 startup_user_account_option; ///< NacpStartupUserAccountOption.
u8 reserved_2[0x6]; u8 reserved_2[0x6];
u64 user_account_save_data_size_max; s64 user_account_save_data_size_max;
u64 user_account_save_data_journal_size_max; s64 user_account_save_data_journal_size_max;
u64 device_save_data_size_max; s64 device_save_data_size_max;
u64 device_save_data_journal_size_max; s64 device_save_data_journal_size_max;
u64 temporary_storage_size; s64 temporary_storage_size;
u64 cache_storage_size; s64 cache_storage_size;
u64 cache_storage_journal_size; s64 cache_storage_journal_size;
u64 cache_storage_data_and_journal_size_max; s64 cache_storage_data_and_journal_size_max;
u16 cache_storage_index_max; u16 cache_storage_index_max;
u8 reserved_3[0x6]; u8 reserved_3[0x6];
u64 play_log_queryable_application_id[0x10]; u64 play_log_queryable_application_id[0x10];
@ -363,6 +367,15 @@ typedef struct {
/// Initializes a NacpContext using a previously initialized NcaContext (which must belong to a Control NCA). /// Initializes a NacpContext using a previously initialized NcaContext (which must belong to a Control NCA).
bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx); bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx);
/// Changes flags in the NACP from the input NacpContext and generates a RomFS file entry patch if needed.
/// If 'patch_sua' is true, StartupUserAccount is set to None, the IsOptional bit in StartupUserAccountOption is cleared and UserAccountSwitchLock is set to Disable.
/// If 'patch_screenshot' is true, Screenshot is set to Allow.
/// If 'patch_video_capture' is true, VideoCapture is set to Enable.
bool nacpGenerateNcaPatch(NacpContext *nacp_ctx, bool patch_sua, bool patch_screenshot, bool patch_video_capture);
/// Writes data from the RomFS file entry patch in the input NacpContext to the provided buffer.
void nacpWriteNcaPatch(NacpContext *nacp_ctx, void *buf, u64 buf_size, u64 buf_offset);
/// Generates an AuthoringTool-like XML using information from a previously initialized NacpContext, as well as the Application/Patch version and the required system version. /// Generates an AuthoringTool-like XML using information from a previously initialized NacpContext, as well as the Application/Patch version and the required system version.
/// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the NacpContext. /// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the NacpContext.
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version); bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version);
@ -438,17 +451,4 @@ NX_INLINE bool nacpIsValidContext(NacpContext *nacp_ctx)
return true; return true;
} }
NX_INLINE bool nacpIsNcaPatchRequired(NacpContext *nacp_ctx)
{
if (!nacpIsValidContext(nacp_ctx)) return false;
u8 tmp_hash[SHA256_HASH_SIZE] = {0};
sha256CalculateHash(tmp_hash, nacp_ctx->data, sizeof(_NacpStruct));
return (memcmp(tmp_hash, nacp_ctx->data_hash, SHA256_HASH_SIZE) != 0);
}
NX_INLINE bool nacpGenerateNcaPatch(NacpContext *nacp_ctx)
{
return (nacpIsValidContext(nacp_ctx) && romfsGenerateFileEntryPatch(&(nacp_ctx->romfs_ctx), nacp_ctx->romfs_file_entry, nacp_ctx->data, sizeof(_NacpStruct), 0, &(nacp_ctx->nca_patch)));
}
#endif /* __NACP_H__ */ #endif /* __NACP_H__ */

View file

@ -306,13 +306,13 @@ void npdmWriteNcaPatch(NpdmContext *npdm_ctx, void *buf, u64 buf_size, u64 buf_o
if (!npdm_ctx || !npdm_ctx->pfs_ctx || !npdm_ctx->pfs_ctx->nca_fs_ctx || !(nca_ctx = (NcaContext*)npdm_ctx->pfs_ctx->nca_fs_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program || \ if (!npdm_ctx || !npdm_ctx->pfs_ctx || !npdm_ctx->pfs_ctx->nca_fs_ctx || !(nca_ctx = (NcaContext*)npdm_ctx->pfs_ctx->nca_fs_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program || \
!nca_ctx->content_type_ctx_patch || npdm_ctx->nca_patch.written) return; !nca_ctx->content_type_ctx_patch || npdm_ctx->nca_patch.written) return;
/* Attempt to write Partition FS entry. */ /* Attempt to write Partition FS entry patch. */
pfsWriteEntryPatchToMemoryBuffer(npdm_ctx->pfs_ctx, &(npdm_ctx->nca_patch), buf, buf_size, buf_offset); pfsWriteEntryPatchToMemoryBuffer(npdm_ctx->pfs_ctx, &(npdm_ctx->nca_patch), buf, buf_size, buf_offset);
/* Check if we need to update the NCA content type context patch status. */ /* Check if we need to update the NCA content type context patch status. */
if (npdm_ctx->nca_patch.written) if (npdm_ctx->nca_patch.written)
{ {
nca_ctx->content_type_ctx_patch = false; nca_ctx->content_type_ctx_patch = false;
LOGFILE("NPDM Partition FS file entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str); LOGFILE("NPDM Partition FS entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str);
} }
} }

View file

@ -557,7 +557,7 @@ bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx
/// Changes the ACID public key from the NPDM in the input NpdmContext, updates the ACID signature from the NCA header in the underlying NCA context and generates a Partition FS entry patch. /// Changes the ACID public key from the NPDM in the input NpdmContext, updates the ACID signature from the NCA header in the underlying NCA context and generates a Partition FS entry patch.
bool npdmGenerateNcaPatch(NpdmContext *npdm_ctx); bool npdmGenerateNcaPatch(NpdmContext *npdm_ctx);
/// Writes data from the Partition FS patch in the input NpdmContext to the provided buffer. /// Writes data from the Partition FS entry patch in the input NpdmContext to the provided buffer.
void npdmWriteNcaPatch(NpdmContext *npdm_ctx, void *buf, u64 buf_size, u64 buf_offset); void npdmWriteNcaPatch(NpdmContext *npdm_ctx, void *buf, u64 buf_size, u64 buf_offset);
/// Helper inline functions. /// Helper inline functions.

View file

@ -524,6 +524,8 @@ bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEnt
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch)); success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
} }
out->written = false;
if (!success) LOGFILE("Failed to generate 0x%lX bytes Hierarchical%s patch at offset 0x%lX for RomFS file entry!", data_size, \ if (!success) LOGFILE("Failed to generate 0x%lX bytes Hierarchical%s patch at offset 0x%lX for RomFS file entry!", data_size, \
ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? "Sha256" : "Integrity", fs_offset); ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? "Sha256" : "Integrity", fs_offset);

View file

@ -106,6 +106,7 @@ typedef struct {
typedef struct { typedef struct {
bool use_old_format_patch; ///< Old format patch flag. bool use_old_format_patch; ///< Old format patch flag.
bool written; ///< Set to true if the patch has been completely written.
NcaHierarchicalSha256Patch old_format_patch; ///< Used with NCA0 RomFS sections. NcaHierarchicalSha256Patch old_format_patch; ///< Used with NCA0 RomFS sections.
NcaHierarchicalIntegrityPatch cur_format_patch; ///< Used with NCA2/NCA3 RomFS sections. NcaHierarchicalIntegrityPatch cur_format_patch; ///< Used with NCA2/NCA3 RomFS sections.
} RomFileSystemFileEntryPatch; } RomFileSystemFileEntryPatch;
@ -183,17 +184,19 @@ NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx,
if (patch->use_old_format_patch) if (patch->use_old_format_patch)
{ {
ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->old_format_patch), buf, buf_size, buf_offset); ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->old_format_patch), buf, buf_size, buf_offset);
patch->written = patch->old_format_patch.written;
} else { } else {
ncaWriteHierarchicalIntegrityPatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->cur_format_patch), buf, buf_size, buf_offset); ncaWriteHierarchicalIntegrityPatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->cur_format_patch), buf, buf_size, buf_offset);
patch->written = patch->cur_format_patch.written;
} }
} }
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch) NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
{ {
if (!patch) return; if (!patch) return;
patch->use_old_format_patch = false;
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch)); ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch)); ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
memset(patch, 0, sizeof(RomFileSystemFileEntryPatch));
} }
#endif /* __ROMFS_H__ */ #endif /* __ROMFS_H__ */

View file

@ -7,31 +7,28 @@ list of top level functions designed to alter nca data in order of (possible) us
* ncaRemoveTitlekeyCrypto (can be used with digital titles + game updates in gamecards) * ncaRemoveTitlekeyCrypto (can be used with digital titles + game updates in gamecards)
* cnmtGenerateNcaPatch (Meta)
* calls pfsGenerateEntryPatch
* calls ncaGenerateHierarchicalSha256Patch
* programInfoGenerateNcaPatch (Program) * programInfoGenerateNcaPatch (Program)
* calls npdmChangeAcidPublicKeyAndNcaSignature * calls npdmGenerateNcaPatch
* calls pfsGenerateEntryPatch * calls pfsGenerateEntryPatch
* calls ncaGenerateHierarchicalSha256Patch * calls ncaGenerateHierarchicalSha256Patch
* nacpGenerateNcaPatch (Control) * nacpGenerateNcaPatch (Control)
* calls romfsGenerateFileEntryPatch * calls romfsGenerateFileEntryPatch
* calls ncaGenerateHierarchicalSha256Patch / ncaGenerateHierarchicalIntegrityPatch * calls ncaGenerateHierarchicalSha256Patch / ncaGenerateHierarchicalIntegrityPatch
* nacpIsNcaPatchRequired is used to check if a nacp patch was applied
* missing wrapper for romfsWriteFileEntryPatchToMemoryBuffer !!!
* missing functions for nacp mods !!!
* ncaEncryptHeader (doesn't modify anything per se, but it's used to generate new encrypted header data if needed) * ncaEncryptHeader (doesn't modify anything per se, but it's used to generate new encrypted header data if needed)
inside dump loop: inside dump loop:
* cnmtGenerateNcaPatch (Meta)
* calls pfsGenerateEntryPatch
* calls ncaGenerateHierarchicalSha256Patch
* returns true if cnmt needs no patching
* demands an immediate ncaEncryptHeader call
* ncaIsHeaderDirty (doesn't modify anything per se, but it's used to check if any of the functions above has been used, basically - and by extension, if the functions below need to be used) * ncaIsHeaderDirty (doesn't modify anything per se, but it's used to check if any of the functions above has been used, basically - and by extension, if the functions below need to be used)
* ncaWriteEncryptedHeaderDataToMemoryBuffer (write encrypted nca header data) * ncaWriteEncryptedHeaderDataToMemoryBuffer (write encrypted nca header data)
* cnmtUpdateContentInfo (used to update content entry info in the raw cnmt copy after dumping each one)
* cnmtWriteNcaPatch (writes cnmt patch) * cnmtWriteNcaPatch (writes cnmt patch)
* calls pfsWriteEntryPatchToMemoryBuffer * calls pfsWriteEntryPatchToMemoryBuffer
* calls ncaWriteHierarchicalSha256PatchToMemoryBuffer * calls ncaWriteHierarchicalSha256PatchToMemoryBuffer
@ -41,16 +38,16 @@ list of top level functions designed to alter nca data in order of (possible) us
* calls pfsWriteEntryPatchToMemoryBuffer * calls pfsWriteEntryPatchToMemoryBuffer
* calls ncaWriteHierarchicalSha256PatchToMemoryBuffer * calls ncaWriteHierarchicalSha256PatchToMemoryBuffer
* pfsWriteEntryPatchToMemoryBuffer * nacpWriteNcaPatch (writes nacp patch)
* calls ncaWriteHierarchicalSha256PatchToMemoryBuffer * calls romfsWriteFileEntryPatchToMemoryBuffer
* romfsWriteFileEntryPatchToMemoryBuffer
* calls ncaWriteHierarchicalSha256PatchToMemoryBuffer / ncaWriteHierarchicalIntegrityPatchToMemoryBuffer * calls ncaWriteHierarchicalSha256PatchToMemoryBuffer / ncaWriteHierarchicalIntegrityPatchToMemoryBuffer
* missing nacp wrapper
* cnmtUpdateContentInfo (used to update content entry info in the raw cnmt copy after dumping each one - ignores the current content if its a meta nca)
minor steps to take into account: minor steps to take into account:
* check if rights_id_available == true and titlekey_retrieved == false (preload handling) * check if rights_id_available == true and titlekey_retrieved == false (preload handling)
* actually, just inform the user about it - this is being handled