NACP parsing (part 1).

God, I'm so tired. I'll go play some good old MGS3 and continue this stuff at a later time. It builds, though.
This commit is contained in:
Pablo Curiel 2020-10-03 13:09:29 -04:00
parent fa1b9c70a5
commit d385d40862
10 changed files with 1070 additions and 21 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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.

565
source/nacp.c Normal file
View File

@ -0,0 +1,565 @@
/*
* nacp.c
*
* 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/>.
*/
#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, \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" \
"<Application>\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, \
" <Title>\n" \
" <Language>%s</Language>\n" \
" <Name>%s</Name>\n" \
" <Publisher>%s</Publisher>\n" \
" </Title>\n", \
nacpGetLanguageString(i),
nacp->title[i].name,
nacp->title[i].publisher)) goto end;
count++;
}
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <Title />\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);
}

488
source/nacp.h Normal file
View File

@ -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__ */

View File

@ -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;

View File

@ -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)

View File

@ -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.