diff --git a/build.sh b/build.sh index 7c273d1..8a3ac09 100644 --- a/build.sh +++ b/build.sh @@ -3,6 +3,8 @@ cd "$(dirname "${BASH_SOURCE[0]}")" tar_filename="nxdumptool-rewrite_poc_$(shell git rev-parse --short HEAD).tar.bz2" +rm -f $tar_filename + rm -rf ./code_templates/tmp mkdir ./code_templates/tmp diff --git a/source/cnmt.c b/source/cnmt.c index 8eafb4c..a737cc5 100644 --- a/source/cnmt.c +++ b/source/cnmt.c @@ -241,7 +241,7 @@ bool cnmtGenerateAuthoringToolXml(ContentMetaContext *cnmt_ctx, NcaContext *nca_ return false; } - u16 i, j; + u32 i, j; char *xml_buf = NULL; u64 xml_buf_size = 0; char digest_str[0x41] = {0}; @@ -437,8 +437,6 @@ static const char *cnmtGetRequiredTitleTypeString(u8 content_meta_type) str = "PatchId"; break; case NcmContentMetaType_Patch: - str = "OriginalId"; - break; case NcmContentMetaType_AddOnContent: str = "ApplicationId"; break; diff --git a/source/cnmt.h b/source/cnmt.h index 5600716..383fd17 100644 --- a/source/cnmt.h +++ b/source/cnmt.h @@ -274,7 +274,7 @@ NX_INLINE bool cnmtIsValidContext(ContentMetaContext *cnmt_ctx) NX_INLINE bool cnmtIsNcaPatchRequired(ContentMetaContext *cnmt_ctx) { - if (!cnmtIsValidContext(cnmt_ctx) || cnmt_ctx->nca_patch.hash_region_count) return false; + if (!cnmtIsValidContext(cnmt_ctx)) return false; u8 tmp_hash[SHA256_HASH_SIZE] = {0}; sha256CalculateHash(tmp_hash, cnmt_ctx->raw_data, cnmt_ctx->raw_data_size); return (memcmp(tmp_hash, cnmt_ctx->raw_data_hash, SHA256_HASH_SIZE) != 0); @@ -282,7 +282,7 @@ NX_INLINE bool cnmtIsNcaPatchRequired(ContentMetaContext *cnmt_ctx) NX_INLINE bool cnmtGenerateNcaPatch(ContentMetaContext *cnmt_ctx) { - return (cnmtIsValidContext(cnmt_ctx) ? pfsGenerateEntryPatch(&(cnmt_ctx->pfs_ctx), cnmt_ctx->pfs_entry, cnmt_ctx->raw_data, cnmt_ctx->raw_data_size, 0, &(cnmt_ctx->nca_patch)) : false); + return (cnmtIsValidContext(cnmt_ctx) && pfsGenerateEntryPatch(&(cnmt_ctx->pfs_ctx), cnmt_ctx->pfs_entry, cnmt_ctx->raw_data, cnmt_ctx->raw_data_size, 0, &(cnmt_ctx->nca_patch))); } NX_INLINE u32 cnmtGetVersionInteger(ContentMetaVersion *version) diff --git a/source/gamecard.c b/source/gamecard.c index 7582e05..a03e79d 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -121,8 +121,7 @@ static const char *g_gameCardHfsPartitionNames[] = { [GameCardHashFileSystemPartitionType_Logo] = "logo", [GameCardHashFileSystemPartitionType_Normal] = "normal", [GameCardHashFileSystemPartitionType_Secure] = "secure", - [GameCardHashFileSystemPartitionType_Boot] = "boot", - [GameCardHashFileSystemPartitionType_Boot + 1] = "unknown" + [GameCardHashFileSystemPartitionType_Boot] = "boot" }; /* Function prototypes. */ @@ -372,8 +371,7 @@ bool gamecardGetBundledFirmwareUpdateVersion(u32 *out) const char *gamecardGetHashFileSystemPartitionName(u8 hfs_partition_type) { - u8 idx = (hfs_partition_type > GameCardHashFileSystemPartitionType_Boot ? (GameCardHashFileSystemPartitionType_Boot + 1) : hfs_partition_type); - return g_gameCardHfsPartitionNames[idx]; + return (hfs_partition_type < GameCardHashFileSystemPartitionType_Count ? g_gameCardHfsPartitionNames[hfs_partition_type] : NULL); } bool gamecardGetEntryCountFromHashFileSystemPartition(u8 hfs_partition_type, u32 *out_count) diff --git a/source/gamecard.h b/source/gamecard.h index 1fb607f..2570347 100644 --- a/source/gamecard.h +++ b/source/gamecard.h @@ -187,7 +187,8 @@ typedef enum { GameCardHashFileSystemPartitionType_Logo = 2, ///< Only available in GameCardFwVersion_Since400NUP gamecards. GameCardHashFileSystemPartitionType_Normal = 3, GameCardHashFileSystemPartitionType_Secure = 4, - GameCardHashFileSystemPartitionType_Boot = 5 ///< Only available in Terra (Tencent) gamecards. + GameCardHashFileSystemPartitionType_Boot = 5, ///< Only available in Terra (Tencent) gamecards. + GameCardHashFileSystemPartitionType_Count = 6 ///< Not a real value. } GameCardHashFileSystemPartitionType; /// Initializes data needed to access raw gamecard storage areas. @@ -220,7 +221,7 @@ bool gamecardGetTrimmedSize(u64 *out); bool gamecardGetRomCapacity(u64 *out); ///< Not the same as gamecardGetTotalSize(). bool gamecardGetBundledFirmwareUpdateVersion(u32 *out); -/// Returns a pointer to a string holding the name of the provided hash file system partition type. +/// Returns a pointer to a string holding the name of the provided hash file system partition type. Returns NULL if the provided value is invalid. const char *gamecardGetHashFileSystemPartitionName(u8 hfs_partition_type); /// Retrieves the entry count from a hash FS partition. diff --git a/source/nacp.c b/source/nacp.c new file mode 100644 index 0000000..8856149 --- /dev/null +++ b/source/nacp.c @@ -0,0 +1,565 @@ +/* + * nacp.c + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "utils.h" +#include "nacp.h" +#include "title.h" + +/* Type definitions. */ + +typedef const char *(*NacpStringFunction)(u8 value); /* Used while adding fields to the AuthoringTool-like XML. */ + +/* Global variables. */ + +static const char *g_unknownString = "Unknown"; + +static const char *g_nacpLanguageStrings[] = { + [NacpLanguage_AmericanEnglish] = "AmericanEnglish", + [NacpLanguage_BritishEnglish] = "BritishEnglish", + [NacpLanguage_Japanese] = "Japanese", + [NacpLanguage_French] = "French", + [NacpLanguage_German] = "German", + [NacpLanguage_LatinAmericanSpanish] = "LatinAmericanSpanish", + [NacpLanguage_Spanish] = "Spanish", + [NacpLanguage_Italian] = "Italian", + [NacpLanguage_Dutch] = "Dutch", + [NacpLanguage_CanadianFrench] = "CanadianFrench", + [NacpLanguage_Portuguese] = "Portuguese", + [NacpLanguage_Russian] = "Russian", + [NacpLanguage_Korean] = "Korean", + [NacpLanguage_TraditionalChinese] = "TraditionalChinese", + [NacpLanguage_SimplifiedChinese] = "SimplifiedChinese", + [NacpLanguage_BrazilianPortuguese] = "BrazilianPortuguese" +}; + +static const char *g_nacpStartupUserAccountStrings[] = { + [NacpStartupUserAccount_None] = "None", + [NacpStartupUserAccount_Required] = "Required", + [NacpStartupUserAccount_RequiredWithNetworkServiceAccountAvailable] = "RequiredWithNetworkServiceAccountAvailable" +}; + +static const char *g_nacpUserAccountSwitchLockStrings[] = { + [NacpUserAccountSwitchLock_Disable] = "Disable", + [NacpUserAccountSwitchLock_Enable] = "Enable" +}; + +static const char *g_nacpAddOnContentRegistrationTypeStrings[] = { + [NacpAddOnContentRegistrationType_AllOnLaunch] = "AllOnLaunch", + [NacpAddOnContentRegistrationType_OnDemand] = "OnDemand" +}; + +static const char *g_nacpAttributeStrings[] = { + [NacpAttribute_Demo] = "Demo", + [NacpAttribute_RetailInteractiveDisplay] = "RetailInteractiveDisplay" +}; + +static const char *g_nacpParentalControlStrings[] = { + [NacpParentalControl_FreeCommunication] = "FreeCommunication" +}; + +static const char *g_nacpScreenshotStrings[] = { + [NacpScreenshot_Allow] = "Allow", + [NacpScreenshot_Deny] = "Deny" +}; + +static const char *g_nacpVideoCaptureStrings[] = { + [NacpVideoCapture_Disable] = "Disable", + [NacpVideoCapture_Manual] = "Manual", + [NacpVideoCapture_Enable] = "Enable" +}; + +static const char *g_nacpDataLossConfirmationStrings[] = { + [NacpDataLossConfirmation_None] = "None", + [NacpDataLossConfirmation_Required] = "Required" +}; + +static const char *g_nacpPlayLogPolicyStrings[] = { + [NacpPlayLogPolicy_Open] = "Open", + [NacpPlayLogPolicy_LogOnly] = "LogOnly", + [NacpPlayLogPolicy_None] = "None", + [NacpPlayLogPolicy_Closed] = "Closed" +}; + +static const char *g_nacpRatingAgeOrganizationStrings[] = { + [NacpRatingAgeOrganization_CERO] = "CERO", + [NacpRatingAgeOrganization_GRACGCRB] = "GRACGCRB", + [NacpRatingAgeOrganization_GSRMR] = "GSRMR", + [NacpRatingAgeOrganization_ESRB] = "ESRB", + [NacpRatingAgeOrganization_ClassInd] = "ClassInd", + [NacpRatingAgeOrganization_USK] = "USK", + [NacpRatingAgeOrganization_PEGI] = "PEGI", + [NacpRatingAgeOrganization_PEGIPortugal] = "PEGIPortugal", + [NacpRatingAgeOrganization_PEGIBBFC] = "PEGIBBFC", + [NacpRatingAgeOrganization_Russian] = "Russian", + [NacpRatingAgeOrganization_ACB] = "ACB", + [NacpRatingAgeOrganization_OFLC] = "OFLC", + [NacpRatingAgeOrganization_IARCGeneric] = "IARCGeneric" +}; + + + + + + + + + + + + + +/* Function prototypes. */ + +static bool nacpAddStringFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, const char *value); +static bool nacpAddEnumFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u8 value, NacpStringFunction str_func); +static bool nacpAddBitflagFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, const void *flag, u8 max_flag_idx, NacpStringFunction str_func); +static bool nacpAddU64FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u64 value); + + + + + + + + + + + +bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx) +{ + if (!out || !nca_ctx || !strlen(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Control || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \ + (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \ + nca_ctx->header.content_type != NcaContentType_Control || !out) + { + LOGFILE("Invalid parameters!"); + return false; + } + + const char *language_str = NULL; + char icon_path[0x80] = {0}; + RomFileSystemFileEntry *icon_entry = NULL; + NacpIconContext *tmp_icon_ctx = NULL; + + bool success = false; + + /* Free output context beforehand. */ + nacpFreeContext(out); + + /* Initialize RomFS context. */ + if (!romfsInitializeContext(&(out->romfs_ctx), &(nca_ctx->fs_contexts[0]))) + { + LOGFILE("Failed to initialize RomFS context!"); + goto end; + } + + /* Retrieve RomFS file entry for 'control.nacp'. */ + if (!(out->romfs_file_entry = romfsGetFileEntryByPath(&(out->romfs_ctx), "/control.nacp"))) + { + LOGFILE("Failed to retrieve file entry for \"control.nacp\" from RomFS!"); + goto end; + } + + //LOGFILE("Found 'control.nacp' entry in Control NCA \"%s\".", nca_ctx->content_id_str); + + /* Verify NACP size. */ + if (out->romfs_file_entry->size != sizeof(_NacpStruct)) + { + LOGFILE("Invalid NACP size!"); + goto end; + } + + /* Allocate memory for the NACP data. */ + out->data = malloc(sizeof(_NacpStruct)); + if (!out->data) + { + LOGFILE("Failed to allocate memory for the NACP data!"); + goto end; + } + + /* Read NACP data into memory buffer. */ + if (!romfsReadFileEntryData(&(out->romfs_ctx), out->romfs_file_entry, out->data, sizeof(_NacpStruct), 0)) + { + LOGFILE("Failed to read NACP data!"); + goto end; + } + + /* Calculate SHA-256 checksum for the whole NACP. */ + sha256CalculateHash(out->data_hash, out->data, sizeof(_NacpStruct)); + + /* Save pointer to NCA context to the output NACP context. */ + out->nca_ctx = nca_ctx; + + /* Retrieve NACP icon data. */ + for(u8 i = 0; i < NacpLanguage_Count; i++) + { + NacpIconContext *icon_ctx = NULL; + + /* Check if the current language is supported. */ + if (!nacpCheckBitflagField(&(out->data->supported_language_flag), i, NacpLanguage_Count)) continue; + + /* Get language string. */ + language_str = nacpGetLanguageString(i); + + /* Generate icon path. */ + sprintf(icon_path, "/icon_%s.dat", language_str); + + /* Retrieve RomFS file entry for this icon. */ + if (!(icon_entry = romfsGetFileEntryByPath(&(out->romfs_ctx), icon_path))) continue; + + /* Check icon size. */ + if (!icon_entry->size || icon_entry->size > NACP_MAX_ICON_SIZE) + { + LOGFILE("Invalid NACP icon size!"); + goto end; + } + + /* Reallocate icon context buffer. */ + if (!(tmp_icon_ctx = realloc(out->icon_ctx, (out->icon_count + 1) * sizeof(NacpIconContext)))) + { + LOGFILE("Failed to reallocate NACP icon context buffer!"); + goto end; + } + + out->icon_ctx = tmp_icon_ctx; + tmp_icon_ctx = NULL; + + icon_ctx = &(out->icon_ctx[out->icon_count]); + memset(icon_ctx, 0, sizeof(NacpIconContext)); + + /* Allocate memory for this icon data. */ + if (!(icon_ctx->icon_data = malloc(icon_entry->size))) + { + LOGFILE("Failed to allocate memory for NACP icon data!"); + goto end; + } + + /* Read icon data. */ + if (!romfsReadFileEntryData(&(out->romfs_ctx), icon_entry, icon_ctx->icon_data, icon_entry->size, 0)) + { + LOGFILE("Failed to read NACP icon data!"); + goto end; + } + + /* Fill icon context. */ + icon_ctx->language = i; + icon_ctx->icon_size = icon_entry->size; + + /* Update icon count. */ + out->icon_count++; + } + + success = true; + +end: + if (!success) nacpFreeContext(out); + + return success; +} + +bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx) +{ + if (!nacpIsValidContext(nacp_ctx)) + { + LOGFILE("Invalid parameters!"); + return false; + } + + _NacpStruct *nacp = nacp_ctx->data; + u8 i = 0, count = 0; + char *xml_buf = NULL; + u64 xml_buf_size = 0; + bool success = false; + + /* Free AuthoringTool-like XML data if needed. */ + if (nacp_ctx->authoring_tool_xml) free(nacp_ctx->authoring_tool_xml); + nacp_ctx->authoring_tool_xml = NULL; + nacp_ctx->authoring_tool_xml_size = 0; + + if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ + "\n" \ + "\n")) goto end; + + /* Title. */ + for(i = 0, count = 0; i < NacpLanguage_Count; i++) + { + if (!strlen(nacp->title[i].name) || !strlen(nacp->title[i].publisher)) continue; + + if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ + " \n" \ + " <Language>%s</Language>\n" \ + " <Name>%s</Name>\n" \ + " <Publisher>%s</Publisher>\n" \ + " \n", \ + nacpGetLanguageString(i), + nacp->title[i].name, + nacp->title[i].publisher)) goto end; + + count++; + } + + if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " \n")) goto end; + + /* Isbn. */ + if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Isbn", nacp->isbn)) goto end; + + /* StartupUserAccount. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "StartupUserAccount", nacp->startup_user_account, &nacpGetStartupUserAccountString)) goto end; + + /* UserAccountSwitchLock. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSwitchLock", nacp->user_account_switch_lock, &nacpGetUserAccountSwitchLockString)) goto end; + + /* AddOnContentRegistrationType. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentRegistrationType", nacp->add_on_content_registration_type, &nacpGetAddOnContentRegistrationTypeString)) goto end; + + /* AttributeFlag. */ + if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Attribute", &(nacp->attribute_flag), NacpAttribute_Count, &nacpGetAttributeString)) goto end; + + /* SupportedLanguageFlag. */ + /* Even though this is a bitflag field, it doesn't follow the same format as the rest. */ + for(i = 0, count = 0; i < NacpLanguage_Count; i++) + { + if (!nacpCheckBitflagField(&(nacp->supported_language_flag), i, NacpLanguage_Count)) continue; + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SupportedLanguage", i, &nacpGetLanguageString)) goto end; + count++; + } + + if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <SupportedLanguage />\n")) goto end; + + /* ParentalControlFlag. */ + if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ParentalControl", &(nacp->parental_control_flag), NacpParentalControl_Count, &nacpGetParentalControlString)) goto end; + + /* Screenshot. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Screenshot", nacp->screenshot, &nacpGetScreenshotString)) goto end; + + /* VideoCapture. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "VideoCapture", nacp->video_capture, &nacpGetVideoCaptureString)) goto end; + + /* DataLossConfirmation. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DataLossConfirmation", nacp->data_loss_confirmation, &nacpGetDataLossConfirmationString)) goto end; + + /* PlayLogPolicy. */ + if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "PlayLogPolicy", nacp->play_log_policy, &nacpGetPlayLogPolicyString)) goto end; + + /* PresenceGroupId. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "PresenceGroupId", nacp->presence_group_id)) goto end; + + /* RatingAge. */ + for(i = 0, count = 0; i < NacpRatingAgeOrganization_Count; i++) + { + u8 age = *((u8*)&(nacp->rating_age) + i); + if (age == 0xFF) continue; + + if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ + " <Rating>\n" \ + " <Organization>%s</Organization>\n" \ + " <Age>%u</Age>\n" \ + " </Rating>\n", \ + nacpGetRatingAgeOrganizationString(i), + age)) goto end; + + count++; + } + + if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <Rating />\n")) goto end; + + /* DisplayVersion. */ + if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DisplayVersion", nacp->display_version)) goto end; + + /* AddOnContentBaseId. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentBaseId", nacp->add_on_content_base_id)) goto end; + + /* SaveDataOwnerId. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SaveDataOwnerId", nacp->save_data_owner_id)) goto end; + + /* UserAccountSaveDataSize. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataSize", nacp->user_account_save_data_size)) goto end; + + /* UserAccountSaveDataJournalSize. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataJournalSize", nacp->user_account_save_data_journal_size)) goto end; + + /* DeviceSaveDataSize. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataSize", nacp->device_save_data_size)) goto end; + + /* DeviceSaveDataJournalSize. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DeviceSaveDataJournalSize", nacp->device_save_data_journal_size)) goto end; + + /* BcatDeliveryCacheStorageSize. */ + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "BcatDeliveryCacheStorageSize", nacp->bcat_delivery_cache_storage_size)) goto end; + + /* ApplicationErrorCodeCategory. */ + if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ApplicationErrorCodeCategory", nacp->application_error_code_category)) goto end; + + /* LocalCommunicationId. */ + for(i = 0, count = 0; i < 0x8; i++) + { + if (!nacp->local_communication_id[i]) continue; + if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "LocalCommunicationId", nacp->local_communication_id[i])) goto end; + count++; + } + + if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <LocalCommunicationId />\n")) goto end; + + + + + + + + + + + if (!(success = utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, "</Application>"))) goto end; + + /* Update NACP context. */ + nacp_ctx->authoring_tool_xml = xml_buf; + nacp_ctx->authoring_tool_xml_size = strlen(xml_buf); + +end: + if (!success) + { + if (xml_buf) free(xml_buf); + LOGFILE("Failed to generate NACP AuthoringTool XML!"); + } + + return success; +} + +const char *nacpGetLanguageString(u8 language) +{ + return (language < NacpLanguage_Count ? g_nacpLanguageStrings[language] : g_unknownString); +} + +const char *nacpGetStartupUserAccountString(u8 startup_user_account) +{ + return (startup_user_account < NacpStartupUserAccount_Count ? g_nacpStartupUserAccountStrings[startup_user_account] : g_unknownString); +} + +const char *nacpGetUserAccountSwitchLockString(u8 user_account_switch_lock) +{ + return (user_account_switch_lock < NacpUserAccountSwitchLock_Count ? g_nacpUserAccountSwitchLockStrings[user_account_switch_lock] : g_unknownString); +} + +const char *nacpGetAddOnContentRegistrationTypeString(u8 add_on_content_registration_type) +{ + return (add_on_content_registration_type < NacpAddOnContentRegistrationType_Count ? g_nacpAddOnContentRegistrationTypeStrings[add_on_content_registration_type] : g_unknownString); +} + +const char *nacpGetAttributeString(u8 attribute) +{ + return (attribute < NacpAttribute_Count ? g_nacpAttributeStrings[attribute] : g_unknownString); +} + +const char *nacpGetParentalControlString(u8 parental_control) +{ + return (parental_control < NacpParentalControl_Count ? g_nacpParentalControlStrings[parental_control] : g_unknownString); +} + +const char *nacpGetScreenshotString(u8 screenshot) +{ + return (screenshot < NacpScreenshot_Count ? g_nacpScreenshotStrings[screenshot] : g_unknownString); +} + +const char *nacpGetVideoCaptureString(u8 video_capture) +{ + return (video_capture < NacpVideoCapture_Count ? g_nacpVideoCaptureStrings[video_capture] : g_unknownString); +} + +const char *nacpGetDataLossConfirmationString(u8 data_loss_confirmation) +{ + return (data_loss_confirmation < NacpDataLossConfirmation_Count ? g_nacpDataLossConfirmationStrings[data_loss_confirmation] : g_unknownString); +} + +const char *nacpGetPlayLogPolicyString(u8 play_log_policy) +{ + return (play_log_policy < NacpPlayLogPolicy_Count ? g_nacpPlayLogPolicyStrings[play_log_policy] : g_unknownString); +} + +const char *nacpGetRatingAgeOrganizationString(u8 rating_age_organization) +{ + return (rating_age_organization < NacpRatingAgeOrganization_Count ? g_nacpRatingAgeOrganizationStrings[rating_age_organization] : g_unknownString); +} + + + + + + + + + + + + + + + + + + + + +static bool nacpAddStringFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, const char *value) +{ + if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name) || !value) return false; + return (strlen(value) ? utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>%s</%s>\n", tag_name, value, tag_name) : \ + utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s />\n", tag_name)); +} + +static bool nacpAddEnumFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u8 value, NacpStringFunction str_func) +{ + if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name) || !str_func) return false; + return utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>%s</%s>\n", tag_name, str_func(value), tag_name); +} + +static bool nacpAddBitflagFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, const void *flag, u8 max_flag_idx, NacpStringFunction str_func) +{ + if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name) || !flag || max_flag_idx >= 0x20 || !str_func) return false; + + u8 i = 0, count = 0; + bool success = false; + + if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>", tag_name)) goto end; + + if (*((u32*)flag)) + { + for(i = 0; i < max_flag_idx; i++) + { + if (!nacpCheckBitflagField(flag, i, max_flag_idx)) continue; + if (count && !utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, ",")) goto end; + if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, "%s", str_func(i))) goto end; + count++; + } + + if (!count && !utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, "%s", g_unknownString)) goto end; + } else { + if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, "None")) goto end; + } + + success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, "</%s>\n", tag_name); + +end: + return success; +} + +static bool nacpAddU64FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u64 value) +{ + if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name)) return false; + return utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>0x%016lx</%s>\n", tag_name, value, tag_name); +} diff --git a/source/nacp.h b/source/nacp.h new file mode 100644 index 0000000..c934afc --- /dev/null +++ b/source/nacp.h @@ -0,0 +1,488 @@ +/* + * nacp.h + * + * Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>. + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#ifndef __NACP_H__ +#define __NACP_H__ + +#include "romfs.h" + +#define NACP_MAX_ICON_SIZE 0x20000 /* 128 KiB. */ + +typedef struct { + char name[0x200]; + char publisher[0x100]; +} NacpTitle; + +typedef enum { + NacpStartupUserAccount_None = 0, + NacpStartupUserAccount_Required = 1, + NacpStartupUserAccount_RequiredWithNetworkServiceAccountAvailable = 2, + NacpStartupUserAccount_Count = 3 ///< Not a real value. +} NacpStartupUserAccount; + +typedef enum { + NacpUserAccountSwitchLock_Disable = 0, + NacpUserAccountSwitchLock_Enable = 1, + NacpUserAccountSwitchLock_Count = 2 ///< Not a real value. +} NacpUserAccountSwitchLock; + +typedef enum { + NacpAddOnContentRegistrationType_AllOnLaunch = 0, + NacpAddOnContentRegistrationType_OnDemand = 1, + NacpAddOnContentRegistrationType_Count = 2 ///< Not a real value. +} NacpAddOnContentRegistrationType; + +/// Indexes used to access NACP attribute info. +typedef enum { + NacpAttribute_Demo = 0, + NacpAttribute_RetailInteractiveDisplay = 1, + NacpAttribute_Count = 2 ///< Not a real value. +} NacpAttribute; + +typedef struct { + u32 NacpAttributeFlag_Demo : 1; + u32 NacpAttributeFlag_RetailInteractiveDisplay : 1; + u32 NacpAttributeFlag_Reserved : 30; +} NacpAttributeFlag; + +/// Indexes used to access NACP language info. +typedef enum { + NacpLanguage_AmericanEnglish = 0, + NacpLanguage_BritishEnglish = 1, + NacpLanguage_Japanese = 2, + NacpLanguage_French = 3, + NacpLanguage_German = 4, + NacpLanguage_LatinAmericanSpanish = 5, + NacpLanguage_Spanish = 6, + NacpLanguage_Italian = 7, + NacpLanguage_Dutch = 8, + NacpLanguage_CanadianFrench = 9, + NacpLanguage_Portuguese = 10, + NacpLanguage_Russian = 11, + NacpLanguage_Korean = 12, + NacpLanguage_TraditionalChinese = 13, + NacpLanguage_SimplifiedChinese = 14, + NacpLanguage_BrazilianPortuguese = 15, + NacpLanguage_Count = 16 ///< Not a real value. +} NacpLanguage; + +typedef struct { + u32 NacpSupportedLanguageFlag_AmericanEnglish : 1; + u32 NacpSupportedLanguageFlag_BritishEnglish : 1; + u32 NacpSupportedLanguageFlag_Japanese : 1; + u32 NacpSupportedLanguageFlag_French : 1; + u32 NacpSupportedLanguageFlag_German : 1; + u32 NacpSupportedLanguageFlag_LatinAmericanSpanish : 1; + u32 NacpSupportedLanguageFlag_Spanish : 1; + u32 NacpSupportedLanguageFlag_Italian : 1; + u32 NacpSupportedLanguageFlag_Dutch : 1; + u32 NacpSupportedLanguageFlag_CanadianFrench : 1; + u32 NacpSupportedLanguageFlag_Portuguese : 1; + u32 NacpSupportedLanguageFlag_Russian : 1; + u32 NacpSupportedLanguageFlag_Korean : 1; + u32 NacpSupportedLanguageFlag_TraditionalChinese : 1; ///< Old: NacpSupportedLanguageFlag_Taiwanese. + u32 NacpSupportedLanguageFlag_SimplifiedChinese : 1; ///< Old: NacpSupportedLanguageFlag_Chinese. + u32 NacpSupportedLanguageFlag_BrazilianPortuguese : 1; + u32 NacpSupportedLanguageFlag_Reserved : 16; +} NacpSupportedLanguageFlag; + +/// Indexes used to access NACP parental control info. +typedef enum { + NacpParentalControl_FreeCommunication = 0, + NacpParentalControl_Count = 1 ///< Not a real value. +} NacpParentalControl; + +typedef struct { + u32 NacpParentalControlFlag_FreeCommunication : 1; + u32 NacpParentalControlFlag_Reserved : 31; +} NacpParentalControlFlag; + +typedef enum { + NacpScreenshot_Allow = 0, + NacpScreenshot_Deny = 1, + NacpScreenshot_Count = 2 ///< Not a real value. +} NacpScreenshot; + +typedef enum { + NacpVideoCapture_Disable = 0, + NacpVideoCapture_Manual = 1, + NacpVideoCapture_Enable = 2, + NacpVideoCapture_Count = 3, ///< Not a real value. + + /// Old. + NacpVideoCapture_Deny = NacpVideoCapture_Disable, + NacpVideoCapture_Allow = NacpVideoCapture_Manual +} NacpVideoCapture; + +typedef enum { + NacpDataLossConfirmation_None = 0, + NacpDataLossConfirmation_Required = 1, + NacpDataLossConfirmation_Count = 2 ///< Not a real value. +} NacpDataLossConfirmation; + +typedef enum { + NacpPlayLogPolicy_Open = 0, + NacpPlayLogPolicy_LogOnly = 1, + NacpPlayLogPolicy_None = 2, + NacpPlayLogPolicy_Closed = 3, + NacpPlayLogPolicy_Count = 4, ///< Not a real value. + + /// Old. + NacpPlayLogPolicy_All = NacpPlayLogPolicy_Open +} NacpPlayLogPolicy; + +/// Indexes used to access NACP rating age info. +typedef enum { + NacpRatingAgeOrganization_CERO = 0, + NacpRatingAgeOrganization_GRACGCRB = 1, + NacpRatingAgeOrganization_GSRMR = 2, + NacpRatingAgeOrganization_ESRB = 3, + NacpRatingAgeOrganization_ClassInd = 4, + NacpRatingAgeOrganization_USK = 5, + NacpRatingAgeOrganization_PEGI = 6, + NacpRatingAgeOrganization_PEGIPortugal = 7, + NacpRatingAgeOrganization_PEGIBBFC = 8, + NacpRatingAgeOrganization_Russian = 9, + NacpRatingAgeOrganization_ACB = 10, + NacpRatingAgeOrganization_OFLC = 11, + NacpRatingAgeOrganization_IARCGeneric = 12, + NacpRatingAgeOrganization_Count = 13 +} NacpRatingAgeOrganization; + +typedef struct { + u8 cero; + u8 gracgcrb; + u8 gsrmr; + u8 esrb; + u8 class_ind; + u8 usk; + u8 pegi; + u8 pegi_portugal; + u8 pegibbfc; + u8 russian; + u8 acb; + u8 oflc; + u8 iarc_generic; + u8 reserved[0x13]; +} NacpRatingAge; + +typedef enum { + NacpLogoType_LicensedByNintendo = 0, + NacpLogoType_DistributedByNintendo = 1, ///< Removed. + NacpLogoType_Nintendo = 2 +} NacpLogoType; + +typedef enum { + NacpLogoHandling_Auto = 0, + NacpLogoHandling_Manual = 1 +} NacpLogoHandling; + +typedef enum { + NacpRuntimeAddOnContentInstall_Deny = 0, + NacpRuntimeAddOnContentInstall_AllowAppend = 1, + NacpRuntimeAddOnContentInstall_AllowAppendButDontDownloadWhenUsingNetwork = 2 +} NacpRuntimeAddOnContentInstall; + +typedef enum { + NacpRuntimeParameterDelivery_Always = 0, + NacpRuntimeParameterDelivery_AlwaysIfUserStateMatched = 1, + NacpRuntimeParameterDelivery_OnRestart = 2 +} NacpRuntimeParameterDelivery; + +typedef enum { + NacpCrashReport_Deny = 0, + NacpCrashReport_Allow = 1 +} NacpCrashReport; + +typedef enum { + NacpHdcp_None = 0, + NacpHdcp_Required = 1 +} NacpHdcp; + +typedef struct { + u8 NacpStartupUserAccountOption_IsOptional : 1; + u8 NacpStartupUserAccountOption_Reserved : 7; +} NacpStartupUserAccountOption; + +typedef enum { + NacpPlayLogQueryCapability_None = 0, + NacpPlayLogQueryCapability_WhiteList = 1, + NacpPlayLogQueryCapability_All = 2 +} NacpPlayLogQueryCapability; + +typedef struct { + u8 NacpRepairFlag_SuppressGameCardAccess : 1; + u8 NacpRepairFlag_Reserved : 7; +} NacpRepairFlag; + +typedef struct { + u8 NacpRequiredNetworkServiceLicenseOnLaunchFlag_Common : 1; + u8 NacpRequiredNetworkServiceLicenseOnLaunchFlag_Reserved : 7; +} NacpRequiredNetworkServiceLicenseOnLaunchFlag; + +typedef enum { + NacpJitConfigurationFlag_None = 0, + NacpJitConfigurationFlag_Enabled = 1 +} NacpJitConfigurationFlag; + +typedef struct { + u64 jit_configuration_flag; ///< NacpJitConfigurationFlag. + u64 memory_size; +} NacpJitConfiguration; + +typedef struct { + u16 NacpDescriptors_Index : 15; + u16 NacpDescriptors_ContinueSet : 1; ///< Not given a name by Nintendo. +} NacpDescriptors; + +typedef struct { + NacpDescriptors descriptors[0x20]; +} NacpRequiredAddOnContentsSetBinaryDescriptor; + +typedef enum { + NacpPlayReportPermission_None = 0, + NacpPlayReportPermission_TargetMarketing = 1 +} NacpPlayReportPermission; + +typedef enum { + NacpCrashScreenshotForProd_Deny = 0, + NacpCrashScreenshotForProd_Allow = 1 +} NacpCrashScreenshotForProd; + +typedef enum { + NacpCrashScreenshotForDev_Deny = 0, + NacpCrashScreenshotForDev_Allow = 1 +} NacpCrashScreenshotForDev; + +typedef struct { + NacpTitle title[0x10]; + char isbn[0x25]; + u8 startup_user_account; ///< NacpStartupUserAccount. + u8 user_account_switch_lock; ///< NacpUserAccountSwitchLock. Old: touch_screen_usage (None, Supported, Required). + u8 add_on_content_registration_type; ///< NacpAddOnContentRegistrationType. + NacpAttributeFlag attribute_flag; + NacpSupportedLanguageFlag supported_language_flag; + NacpParentalControlFlag parental_control_flag; + u8 screenshot; ///< NacpScreenshot. + u8 video_capture; ///< NacpVideoCapture. + u8 data_loss_confirmation; ///< NacpDataLossConfirmation. + u8 play_log_policy; ///< NacpPlayLogPolicy. + u64 presence_group_id; + NacpRatingAge rating_age; + char display_version[0x10]; + u64 add_on_content_base_id; + u64 save_data_owner_id; + u64 user_account_save_data_size; + u64 user_account_save_data_journal_size; + u64 device_save_data_size; + u64 device_save_data_journal_size; + u64 bcat_delivery_cache_storage_size; + char application_error_code_category[0x8]; + u64 local_communication_id[8]; + u8 logo_type; ///< NacpLogoType. + u8 logo_handling; ///< NacpLogoHandling. + u8 runtime_add_on_content_install; ///< NacpRuntimeAddOnContentInstall. + u8 runtime_parameter_delivery; ///< NacpRuntimeParameterDelivery. + u8 reserved_1[0x2]; + u8 crash_report; ///< NacpCrashReport. + u8 hdcp; ///< NacpHdcp. + u64 seed_for_pseudo_device_id; + char bcat_passphrase[0x41]; + NacpStartupUserAccountOption startup_user_account_option; + u8 reserved_2[0x6]; + u64 user_account_save_data_size_max; + u64 user_account_save_data_journal_size_max; + u64 device_save_data_size_max; + u64 device_save_data_journal_size_max; + u64 temporary_storage_size; + u64 cache_storage_size; + u64 cache_storage_journal_size; + u64 cache_storage_data_and_journal_size_max; + u16 cache_storage_index_max; + u8 reserved_3[0x6]; + u64 play_log_queryable_application_id[0x10]; + u8 play_log_query_capability; ///< NacpPlayLogQueryCapability. + NacpRepairFlag repair_flag; + u8 program_index; + NacpRequiredNetworkServiceLicenseOnLaunchFlag required_network_service_license_on_launch_flag; + u8 reserved_4[0x4]; + NacpNeighborDetectionClientConfiguration neighbor_detection_client_configuration; + NacpJitConfiguration jit_configuration; + NacpRequiredAddOnContentsSetBinaryDescriptor required_add_on_contents_set_binary_descriptor; + u8 play_report_permission; ///< NacpPlayReportPermission. + u8 crash_screenshot_for_prod; ///< NacpCrashScreenshotForProd. + u8 crash_screenshot_for_dev; ///< NacpCrashScreenshotForDev. + u8 reserved_5[0xBFD]; +} _NacpStruct; + +typedef struct { + u8 language; ///< NacpLanguage. + u64 icon_size; ///< JPG icon size. Must not exceed NACP_MAX_ICON_SIZE. + u8 *icon_data; ///< Pointer to a dynamically allocated buffer that holds the JPG icon data. +} NacpIconContext; + +typedef struct { + NcaContext *nca_ctx; ///< Pointer to the NCA context for the Control NCA from which NACP data is retrieved. + RomFileSystemContext romfs_ctx; ///< RomFileSystemContext for the Control NCA FS section #0, which is where the NACP is stored. + RomFileSystemFileEntry *romfs_file_entry; ///< RomFileSystemFileEntry for the NACP in the Control NCA FS section #0. Used to generate a RomFileSystemFileEntryPatch if needed. + RomFileSystemFileEntryPatch nca_patch; ///< RomFileSystemFileEntryPatch generated if NACP modifications are needed. Used to seamlessly replace Control NCA data while writing it. + ///< Bear in mind that generating a patch modifies the NCA context. + _NacpStruct *data; ///< Pointer to a dynamically allocated buffer that holds the full NACP. + u8 data_hash[SHA256_HASH_SIZE]; ///< SHA-256 checksum calculated over the whole NACP. Used to determine if NcaHierarchicalSha256Patch generation is truly needed. + u32 icon_count; ///< NACP icon count. May be zero if no icons are available. + NacpIconContext *icon_ctx; ///< Pointer to a dynamically allocated buffer that holds 'icon_count' NACP icon contexts. May be NULL if no icons are available. + char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data. + ///< This is always NULL unless nacpGenerateAuthoringToolXml() is used on this NacpContext. + u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'. + ///< This is always 0 unless nacpGenerateAuthoringToolXml() is used on this NacpContext. +} NacpContext; + +/// Initializes a NacpContext using a previously initialized NcaContext (which must belong to a Control NCA). +bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx); + +/// Generates an AuthoringTool-like XML using information from a previously initialized 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); + + + + + + + + + + + + + + +/// Returns a pointer to a string holding the name of the provided NacpLanguage value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetLanguageString(u8 language); + +/// Returns a pointer to a string holding the name of the provided NacpStartupUserAccount value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetStartupUserAccountString(u8 startup_user_account); + +/// Returns a pointer to a string holding the name of the provided NacpUserAccountSwitchLock value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetUserAccountSwitchLockString(u8 user_account_switch_lock); + +/// Returns a pointer to a string holding the name of the provided NacpAddOnContentRegistrationType value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetAddOnContentRegistrationTypeString(u8 add_on_content_registration_type); + +/// Returns a pointer to a string holding the name of the provided NacpAttribute value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetAttributeString(u8 attribute); + +/// Returns a pointer to a string holding the name of the provided NacpParentalControl value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetParentalControlString(u8 parental_control); + +/// Returns a pointer to a string holding the name of the provided NacpScreenshot value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetScreenshotString(u8 screenshot); + +/// Returns a pointer to a string holding the name of the provided NacpVideoCapture value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetVideoCaptureString(u8 video_capture); + +/// Returns a pointer to a string holding the name of the provided NacpDataLossConfirmation value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetDataLossConfirmationString(u8 data_loss_confirmation); + +/// Returns a pointer to a string holding the name of the provided NacpPlayLogPolicy value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetPlayLogPolicyString(u8 play_log_policy); + +/// Returns a pointer to a string holding the name of the provided NacpRatingAgeOrganization value. Returns "Unknown" if the provided value is invalid. +const char *nacpGetRatingAgeOrganizationString(u8 rating_age_organization); + + + + + + + + + + + + + + + + + + +/// Helper inline functions. + +NX_INLINE void nacpFreeContext(NacpContext *nacp_ctx) +{ + if (!nacp_ctx) return; + + romfsFreeContext(&(nacp_ctx->romfs_ctx)); + romfsFreeFileEntryPatch(&(nacp_ctx->nca_patch)); + if (nacp_ctx->data) free(nacp_ctx->data); + + if (nacp_ctx->icon_ctx) + { + for(u32 i = 0; i < nacp_ctx->icon_count; i++) + { + if (nacp_ctx->icon_ctx[i].icon_data) free(nacp_ctx->icon_ctx[i].icon_data); + } + + free(nacp_ctx->icon_ctx); + } + + if (nacp_ctx->authoring_tool_xml) free(nacp_ctx->authoring_tool_xml); + memset(nacp_ctx, 0, sizeof(NacpContext)); +} + +NX_INLINE bool nacpIsValidIconContext(NacpIconContext *icon_ctx) +{ + return (icon_ctx && icon_ctx->language < NacpLanguage_Count && icon_ctx->icon_size && icon_ctx->icon_data); +} + +NX_INLINE bool nacpIsValidContext(NacpContext *nacp_ctx) +{ + if (!nacp_ctx || !nacp_ctx->nca_ctx || !nacp_ctx->romfs_file_entry || !nacp_ctx->data || (!nacp_ctx->icon_count && nacp_ctx->icon_ctx) || (nacp_ctx->icon_count && !nacp_ctx->icon_ctx)) return false; + + for(u32 i = 0; i < nacp_ctx->icon_count; i++) + { + if (!nacpIsValidIconContext(&(nacp_ctx->icon_ctx[i]))) return false; + } + + 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))); +} + +NX_INLINE bool nacpCheckBitflagField(const void *flag, u8 idx, u8 max_flag_idx) +{ + return (flag && idx < 0x20 && idx < max_flag_idx && ((*((const u32*)flag) >> idx) & 0x1)); +} + +#endif /* __NACP_H__ */ diff --git a/source/nca.c b/source/nca.c index 7cb82ac..cedcffc 100644 --- a/source/nca.c +++ b/source/nca.c @@ -88,7 +88,7 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, NcmContentStorage *ncm_storage = NULL; if (!out || (storage_id != NcmStorageId_GameCard && !(ncm_storage = titleGetNcmStorageByStorageId(storage_id))) || \ - (storage_id == NcmStorageId_GameCard && hfs_partition_type > GameCardHashFileSystemPartitionType_Boot) || !content_info || content_info->content_type > NcmContentType_DeltaFragment || !tik) + (storage_id == NcmStorageId_GameCard && hfs_partition_type >= GameCardHashFileSystemPartitionType_Count) || !content_info || content_info->content_type > NcmContentType_DeltaFragment || !tik) { LOGFILE("Invalid parameters!"); return false; diff --git a/source/title.c b/source/title.c index 0289077..789a8d5 100644 --- a/source/title.c +++ b/source/title.c @@ -57,8 +57,7 @@ static const char *g_titleNcmContentTypeNames[] = { [NcmContentType_Control] = "Control", [NcmContentType_HtmlDocument] = "HtmlDocument", [NcmContentType_LegalInformation] = "LegalInformation", - [NcmContentType_DeltaFragment] = "DeltaFragment", - [NcmContentType_DeltaFragment + 1] = "Unknown" + [NcmContentType_DeltaFragment] = "DeltaFragment" }; static const char *g_titleNcmContentMetaTypeNames[] = { @@ -862,15 +861,13 @@ end: const char *titleGetNcmContentTypeName(u8 content_type) { - u8 idx = (content_type > NcmContentType_DeltaFragment ? (NcmContentType_DeltaFragment + 1) : content_type); - return g_titleNcmContentTypeNames[idx]; + return (content_type <= NcmContentType_DeltaFragment ? g_titleNcmContentTypeNames[content_type] : NULL); } const char *titleGetNcmContentMetaTypeName(u8 content_meta_type) { - u8 idx = (content_meta_type <= NcmContentMetaType_BootImagePackageSafe ? content_meta_type : \ - ((content_meta_type < NcmContentMetaType_Application || content_meta_type > NcmContentMetaType_Delta) ? 0 : (content_meta_type - 0x7A))); - return g_titleNcmContentMetaTypeNames[idx]; + return ((content_meta_type <= NcmContentMetaType_BootImagePackageSafe || (content_meta_type >= NcmContentMetaType_Application && content_meta_type <= NcmContentMetaType_Delta)) ? \ + g_titleNcmContentMetaTypeNames[content_meta_type] : NULL); } NX_INLINE void titleFreeApplicationMetadata(void) diff --git a/source/title.h b/source/title.h index ee9112e..61fb48e 100644 --- a/source/title.h +++ b/source/title.h @@ -130,10 +130,10 @@ char *titleGenerateFileName(const TitleInfo *title_info, u8 name_convention, u8 /// Returns NULL if an error occurs. char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_type); -/// Returns a pointer to a string holding the name of the provided NcmContentType value. +/// Returns a pointer to a string holding the name of the provided NcmContentType value. Returns NULL if the provided value is invalid. const char *titleGetNcmContentTypeName(u8 content_type); -/// Returns a pointer to a string holding the name of the provided NcmContentMetaType value. +/// Returns a pointer to a string holding the name of the provided NcmContentMetaType value. Returns NULL if the provided value is invalid. const char *titleGetNcmContentMetaTypeName(u8 content_meta_type); /// Miscellaneous functions.