1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 12:12:02 +00:00

Unified XML generator + finished NACP parsing.

Big thanks to 0Liam.
This commit is contained in:
Pablo Curiel 2020-10-06 11:41:26 -04:00
parent 2859f26cb3
commit e943e84380
10 changed files with 275 additions and 394 deletions

View file

@ -12,9 +12,9 @@
{
fprintf(title_infos_txt, "Storage ID: 0x%02X\r\n", g_titleInfo[i].storage_id);
fprintf(title_infos_txt, "Title ID: %016lX\r\n", g_titleInfo[i].meta_key.id);
fprintf(title_infos_txt, "Version: %u (%u.%u.%u-%u.%u)\r\n", g_titleInfo[i].meta_key.version, g_titleInfo[i].dot_version.TitleVersion_Major, \
g_titleInfo[i].dot_version.TitleVersion_Minor, g_titleInfo[i].dot_version.TitleVersion_Micro, g_titleInfo[i].dot_version.TitleVersion_MajorRelstep, \
g_titleInfo[i].dot_version.TitleVersion_MinorRelstep);
fprintf(title_infos_txt, "Version: %u (%u.%u.%u-%u.%u)\r\n", g_titleInfo[i].meta_key.version, g_titleInfo[i].version.TitleVersion_Major, \
g_titleInfo[i].version.TitleVersion_Minor, g_titleInfo[i].version.TitleVersion_Micro, g_titleInfo[i].version.TitleVersion_MajorRelstep, \
g_titleInfo[i].version.TitleVersion_MinorRelstep);
fprintf(title_infos_txt, "Type: 0x%02X\r\n", g_titleInfo[i].meta_key.type);
fprintf(title_infos_txt, "Install Type: 0x%02X\r\n", g_titleInfo[i].meta_key.install_type);
fprintf(title_infos_txt, "Title Size: %s (0x%lX)\r\n", g_titleInfo[i].title_size_str, g_titleInfo[i].title_size);

View file

@ -1,266 +0,0 @@
/*
* main.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 "gamecard.h"
#include "title.h"
#include "nacp.h"
static void consolePrint(const char *text, ...)
{
va_list v;
va_start(v, text);
vfprintf(stdout, text, v);
va_end(v);
consoleUpdate(NULL);
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
int ret = 0;
LOGFILE(APP_TITLE " starting.");
consoleInit(NULL);
consolePrint("initializing...\n");
if (!utilsInitializeResources())
{
ret = -1;
goto out;
}
u32 app_count = 0;
TitleApplicationMetadata **app_metadata = NULL;
TitleUserApplicationData user_app_data = {0};
u32 selected_idx = 0, page_size = 30, scroll = 0;
bool exit_prompt = true;
NcaContext *nca_ctx = NULL;
Ticket tik = {0};
NacpContext nacp_ctx = {0};
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
if (!app_metadata || !app_count)
{
consolePrint("app metadata failed\n");
goto out2;
}
consolePrint("app metadata succeeded\n");
utilsSleep(1);
while(true)
{
consoleClear();
printf("select an user application to generate a nacp xml for.\npress b to exit.\n\n");
printf("title: %u / %u\n\n", selected_idx + 1, app_count);
for(u32 i = scroll; i < app_count; i++)
{
if (i >= (scroll + page_size)) break;
printf("%s%016lX - %s\n", i == selected_idx ? " -> " : " ", app_metadata[i]->title_id, app_metadata[i]->lang_entry.name);
}
printf("\n");
consoleUpdate(NULL);
u64 btn_down = 0, btn_held = 0;
while(true)
{
hidScanInput();
btn_down = utilsHidKeysAllDown();
btn_held = utilsHidKeysAllHeld();
if (btn_down || btn_held) break;
if (titleIsGameCardInfoUpdated())
{
free(app_metadata);
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
if (!app_metadata)
{
consolePrint("\napp metadata failed\n");
goto out2;
}
selected_idx = scroll = 0;
break;
}
}
if (btn_down & KEY_A)
{
if (!titleGetUserApplicationData(app_metadata[selected_idx]->title_id, &user_app_data) || !user_app_data.app_info)
{
consolePrint("\nthe selected title doesn't have available base content.\n");
utilsSleep(3);
continue;
}
break;
} else
if ((btn_down & KEY_DDOWN) || (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN)))
{
selected_idx++;
if (selected_idx >= app_count)
{
if (btn_down & KEY_DDOWN)
{
selected_idx = scroll = 0;
} else {
selected_idx = (app_count - 1);
}
} else
if (selected_idx >= (scroll + (page_size / 2)) && app_count > (scroll + page_size))
{
scroll++;
}
} else
if ((btn_down & KEY_DUP) || (btn_held & (KEY_LSTICK_UP | KEY_RSTICK_UP)))
{
selected_idx--;
if (selected_idx == UINT32_MAX)
{
if (btn_down & KEY_DUP)
{
selected_idx = (app_count - 1);
scroll = (app_count >= page_size ? (app_count - page_size) : 0);
} else {
selected_idx = 0;
}
} else
if (selected_idx < (scroll + (page_size / 2)) && scroll > 0)
{
scroll--;
}
} else
if (btn_down & KEY_B)
{
exit_prompt = false;
goto out2;
}
if (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN | KEY_LSTICK_UP | KEY_RSTICK_UP)) svcSleepThread(50000000); // 50 ms
}
consoleClear();
consolePrint("selected title:\n%s (%016lX)\n\n", app_metadata[selected_idx]->lang_entry.name, app_metadata[selected_idx]->title_id);
nca_ctx = calloc(1, sizeof(NcaContext));
if (!nca_ctx)
{
consolePrint("nca ctx calloc failed\n");
goto out2;
}
consolePrint("nca ctx calloc succeeded\n");
if (!ncaInitializeContext(nca_ctx, user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? GameCardHashFileSystemPartitionType_Secure : 0), \
titleGetContentInfoByTypeAndIdOffset(user_app_data.app_info, NcmContentType_Control, 0), &tik))
{
consolePrint("Control nca initialize ctx failed\n");
goto out2;
}
consolePrint("Control nca initialize ctx succeeded\n");
if (!nacpInitializeContext(&nacp_ctx, nca_ctx))
{
consolePrint("nacp initialize ctx failed\n");
goto out2;
}
consolePrint("nacp initialize ctx succeeded\n");
if (nacpGenerateAuthoringToolXml(&nacp_ctx))
{
consolePrint("nacp xml succeeded\n");
FILE *xml_fd = NULL;
char path[FS_MAX_PATH] = {0};
sprintf(path, "sdmc:/%s.nacp", nca_ctx->content_id_str);
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(nacp_ctx.data, 1, sizeof(_NacpStruct), xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
sprintf(path, "sdmc:/%s.nacp.xml", nca_ctx->content_id_str);
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(nacp_ctx.authoring_tool_xml, 1, nacp_ctx.authoring_tool_xml_size, xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
for(u8 i = 0; i < nacp_ctx.icon_count; i++)
{
NacpIconContext *icon_ctx = &(nacp_ctx.icon_ctx[i]);
sprintf(path, "sdmc:/%s.nx.%s.jpg", nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language));
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(icon_ctx->icon_data, 1, icon_ctx->icon_size, xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
}
} else {
consolePrint("nacp xml failed\n");
}
out2:
if (exit_prompt)
{
consolePrint("press any button to exit\n");
utilsWaitForButtonPress(KEY_NONE);
}
nacpFreeContext(&nacp_ctx);
if (nca_ctx) free(nca_ctx);
if (app_metadata) free(app_metadata);
out:
utilsCloseResources();
consoleExit(NULL);
return ret;
}

View file

@ -22,6 +22,7 @@
#include "gamecard.h"
#include "title.h"
#include "cnmt.h"
#include "nacp.h"
static void consolePrint(const char *text, ...)
{
@ -60,7 +61,12 @@ int main(int argc, char *argv[])
NcaContext *nca_ctx = NULL;
Ticket tik = {0};
ContentMetaContext cnmt_ctx = {0};
NacpContext nacp_ctx = {0};
FILE *xml_fd = NULL;
char path[FS_MAX_PATH] = {0};
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
if (!app_metadata || !app_count)
@ -182,6 +188,8 @@ int main(int argc, char *argv[])
consolePrint("nca ctx calloc succeeded\n");
u32 meta_idx = (user_app_data.app_info->content_count - 1), control_idx = 0;
for(u32 i = 0, j = 0; i < user_app_data.app_info->content_count; i++)
{
if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_Meta) continue;
@ -193,12 +201,12 @@ int main(int argc, char *argv[])
goto out2;
}
if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_Control && user_app_data.app_info->content_infos[i].id_offset == 0) control_idx = j;
consolePrint("%s nca initialize ctx succeeded\n", titleGetNcmContentTypeName(user_app_data.app_info->content_infos[i].content_type));
j++;
}
u32 meta_idx = (user_app_data.app_info->content_count - 1);
if (!ncaInitializeContext(&(nca_ctx[meta_idx]), user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? GameCardHashFileSystemPartitionType_Secure : 0), \
titleGetContentInfoByTypeAndIdOffset(user_app_data.app_info, NcmContentType_Meta, 0), &tik))
{
@ -208,6 +216,9 @@ int main(int argc, char *argv[])
consolePrint("Meta nca initialize ctx succeeded\n");
sprintf(path, "sdmc:/%016lX_xml", app_metadata[selected_idx]->title_id);
mkdir(path, 0777);
if (!cnmtInitializeContext(&cnmt_ctx, &(nca_ctx[meta_idx])))
{
consolePrint("cnmt initialize ctx failed\n");
@ -220,21 +231,79 @@ int main(int argc, char *argv[])
{
consolePrint("cnmt xml succeeded\n");
FILE *xml_fd = NULL;
char path[FS_MAX_PATH] = {0};
sprintf(path, "sdmc:/%016lX_xml/%s.cnmt", app_metadata[selected_idx]->title_id, nca_ctx[meta_idx].content_id_str);
sprintf(path, "sdmc:/%s.cnmt.xml", nca_ctx[meta_idx].content_id_str);
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(cnmt_ctx.raw_data, 1, cnmt_ctx.raw_data_size, xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
sprintf(path, "sdmc:/%016lX_xml/%s.cnmt.xml", app_metadata[selected_idx]->title_id, nca_ctx[meta_idx].content_id_str);
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(cnmt_ctx.authoring_tool_xml, 1, cnmt_ctx.authoring_tool_xml_size, xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
} else {
consolePrint("cnmt xml failed\n");
}
if (!nacpInitializeContext(&nacp_ctx, &(nca_ctx[control_idx])))
{
consolePrint("nacp initialize ctx failed\n");
goto out2;
}
consolePrint("nacp initialize ctx succeeded\n");
if (nacpGenerateAuthoringToolXml(&nacp_ctx, titleGetVersionInteger(&(user_app_data.app_info->version)), cnmtGetRequiredTitleVersion(&cnmt_ctx)))
{
consolePrint("nacp xml succeeded\n");
sprintf(path, "sdmc:/%016lX_xml/%s.nacp", app_metadata[selected_idx]->title_id, nca_ctx[control_idx].content_id_str);
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(nacp_ctx.data, 1, sizeof(_NacpStruct), xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
sprintf(path, "sdmc:/%016lX_xml/%s.nacp.xml", app_metadata[selected_idx]->title_id, nca_ctx[control_idx].content_id_str);
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(nacp_ctx.authoring_tool_xml, 1, nacp_ctx.authoring_tool_xml_size, xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
for(u8 i = 0; i < nacp_ctx.icon_count; i++)
{
NacpIconContext *icon_ctx = &(nacp_ctx.icon_ctx[i]);
sprintf(path, "sdmc:/%016lX_xml/%s.nx.%s.jpg", app_metadata[selected_idx]->title_id, nca_ctx[control_idx].content_id_str, nacpGetLanguageString(icon_ctx->language));
xml_fd = fopen(path, "wb");
if (xml_fd)
{
fwrite(icon_ctx->icon_data, 1, icon_ctx->icon_size, xml_fd);
fclose(xml_fd);
xml_fd = NULL;
}
}
} else {
consolePrint("nacp xml failed\n");
}
out2:
if (exit_prompt)
{

View file

@ -315,10 +315,10 @@ bool cnmtGenerateAuthoringToolXml(ContentMetaContext *cnmt_ctx, NcaContext *nca_
if (cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Application || cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Patch || \
cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_AddOnContent)
{
u32 required_title_version = cnmtGetVersionInteger((ContentMetaVersion*)(cnmt_ctx->extended_header + sizeof(u64)));
u32 required_title_version = cnmtGetRequiredTitleVersion(cnmt_ctx);
const char *required_title_version_str = cnmtGetRequiredTitleVersionString(cnmt_ctx->packaged_header->content_meta_type);
u64 required_title_id = *((u64*)cnmt_ctx->extended_header);
u64 required_title_id = cnmtGetRequiredTitleId(cnmt_ctx);
const char *required_title_type_str = cnmtGetRequiredTitleTypeString(cnmt_ctx->packaged_header->content_meta_type);
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \

View file

@ -290,4 +290,18 @@ NX_INLINE u32 cnmtGetVersionInteger(ContentMetaVersion *version)
return (version ? *((u32*)version) : 0);
}
NX_INLINE u64 cnmtGetRequiredTitleId(ContentMetaContext *cnmt_ctx)
{
return ((cnmtIsValidContext(cnmt_ctx) && (cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Application || \
cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Patch || cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_AddOnContent)) ? \
*((u64*)cnmt_ctx->extended_header) : 0);
}
NX_INLINE u32 cnmtGetRequiredTitleVersion(ContentMetaContext *cnmt_ctx)
{
return ((cnmtIsValidContext(cnmt_ctx) && (cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Application || \
cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Patch || cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_AddOnContent)) ? \
cnmtGetVersionInteger((ContentMetaVersion*)(cnmt_ctx->extended_header + sizeof(u64))) : 0);
}
#endif /* __CNMT_H__ */

View file

@ -150,6 +150,12 @@ static const char *g_nacpStartupUserAccountOptionStrings[] = {
[NacpStartupUserAccountOption_IsOptional] = "IsOptional"
};
static const char *g_nacpPlayLogQueryCapabilityStrings[] = {
[NacpPlayLogQueryCapability_None] = "None",
[NacpPlayLogQueryCapability_WhiteList] = "WhiteList",
[NacpPlayLogQueryCapability_All] = "All"
};
static const char *g_nacpRepairStrings[] = {
[NacpRepair_SuppressGameCardAccess] = "SuppressGameCardAccess"
};
@ -185,8 +191,9 @@ NX_INLINE bool nacpCheckBitflagField(const void *flag, u8 flag_bitcount, u8 idx)
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 flag_width, u8 max_flag_idx, NacpStringFunction str_func);
static bool nacpAddU64FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u64 value);
static bool nacpAddU16FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u16 value, bool hex);
static bool nacpAddU32FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u32 value);
static bool nacpAddU64FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u64 value);
bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx)
{
@ -319,7 +326,7 @@ end:
return success;
}
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version)
{
if (!nacpIsValidContext(nacp_ctx))
{
@ -341,7 +348,6 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
bool ndcc_sgc_available = false, ndcc_rgc_available = false;
NacpNeighborDetectionClientConfiguration *ndcc = &(nacp->neighbor_detection_client_configuration);
bool raocsbd_available = false;
NacpRequiredAddOnContentsSetBinaryDescriptor *raocsbd = &(nacp->required_add_on_contents_set_binary_descriptor);
bool success = false;
@ -381,46 +387,37 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
/* StartupUserAccount. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "StartupUserAccount", nacp->startup_user_account, &nacpGetStartupUserAccountString)) goto end;
/* StartupUserAccountOption. */
if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "StartupUserAccountOption", &(nacp->startup_user_account_option_flag), sizeof(nacp->startup_user_account_option_flag), \
NacpStartupUserAccountOption_Count, &nacpGetStartupUserAccountOptionString)) 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. */
/* Attribute. */
if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Attribute", &(nacp->attribute_flag), sizeof(nacp->attribute_flag), NacpAttribute_Count, &nacpGetAttributeString)) goto end;
/* SupportedLanguage. */
/* 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), sizeof(nacp->supported_language_flag) * 8, i)) 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;
/* ParentalControl. */
if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ParentalControl", &(nacp->parental_control_flag), sizeof(nacp->parental_control_flag), NacpParentalControl_Count, \
&nacpGetParentalControlString)) goto end;
/* SupportedLanguage. */
if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SupportedLanguage", &(nacp->supported_language_flag), sizeof(nacp->supported_language_flag), NacpLanguage_Count, \
&nacpGetLanguageString)) 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. */
/* DisplayVersion. */
if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DisplayVersion", nacp->display_version)) goto end;
/* Rating. */
for(i = 0, count = 0; i < NacpRatingAgeOrganization_Count; i++)
{
u8 age = *((u8*)&(nacp->rating_age) + i);
@ -439,11 +436,11 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
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;
/* DataLossConfirmation. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "DataLossConfirmation", nacp->data_loss_confirmation, &nacpGetDataLossConfirmationString)) goto end;
/* AddOnContentBaseId. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentBaseId", nacp->add_on_content_base_id)) goto end;
/* PlayLogPolicy. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "PlayLogPolicy", nacp->play_log_policy, &nacpGetPlayLogPolicyString)) goto end;
/* SaveDataOwnerId. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SaveDataOwnerId", nacp->save_data_owner_id)) goto end;
@ -466,6 +463,23 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
/* ApplicationErrorCodeCategory. */
if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ApplicationErrorCodeCategory", nacp->application_error_code_category)) goto end;
/* AddOnContentBaseId. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentBaseId", nacp->add_on_content_base_id)) goto end;
/* Version. */
if (!nacpAddU32FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Version", version)) goto end;
/* ReleaseVersion and PrivateVersion. Unused but kept anyway. */
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <ReleaseVersion />\n" \
" <PrivateVersion />\n")) goto end;
/* LogoType. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "LogoType", nacp->logo_type, &nacpGetLogoTypeString)) goto end;
/* RequiredSystemVersion. */
if (!nacpAddU32FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "RequiredSystemVersion", required_system_version)) goto end;
/* LocalCommunicationId. */
for(i = 0, count = 0; i < 0x8; i++)
{
@ -476,23 +490,42 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <LocalCommunicationId />\n")) goto end;
/* LogoType. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "LogoType", nacp->logo_type, &nacpGetLogoTypeString)) goto end;
/* LogoHandling. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "LogoHandling", nacp->logo_handling, &nacpGetLogoHandlingString)) goto end;
/* RuntimeAddOnContentInstall. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "RuntimeAddOnContentInstall", nacp->runtime_add_on_content_install, &nacpGetRuntimeAddOnContentInstallString)) goto end;
/* Icon. */
for(i = 0, count = 0; i < nacp_ctx->icon_count; i++)
{
NacpIconContext *icon_ctx = &(nacp_ctx->icon_ctx[i]);
/* Calculate icon hash. */
sha256CalculateHash(icon_hash, icon_ctx->icon_data, icon_ctx->icon_size);
/* Generate icon hash string. Only the first half from the hash is used. */
utilsGenerateHexStringFromData(icon_hash_str, SHA256_HASH_SIZE + 1, icon_hash, SHA256_HASH_SIZE / 2);
/* Add XML element. */
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <Icon>\n" \
" <Language>%s</Language>\n" \
" <IconPath />\n" \
" <NxIconPath />\n" \
" <RawIconHash />\n" \
" <NxIconHash>%s</NxIconHash>\n" \
" </Icon>\n", \
nacpGetLanguageString(icon_ctx->language), \
icon_hash_str)) goto end;
count++;
}
/* RuntimeParameterDelivery. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "RuntimeParameterDelivery", nacp->runtime_parameter_delivery, &nacpGetRuntimeParameterDeliveryString)) goto end;
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <Icon />\n")) goto end;
/* CrashReport. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CrashReport", nacp->crash_report, &nacpGetCrashReportString)) goto end;
/* Hdcp. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Hdcp", nacp->hdcp, &nacpGetHdcpString)) goto end;
/* HtmlDocumentPath, LegalInformationFilePath and AccessibleUrlsFilePath. Unused but kept anyway. */
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <HtmlDocumentPath />\n" \
" <LegalInformationFilePath />\n" \
" <AccessibleUrlsFilePath />\n")) goto end;
/* SeedForPseudoDeviceId. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SeedForPseudoDeviceId", nacp->seed_for_pseudo_device_id)) goto end;
@ -500,9 +533,8 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
/* BcatPassphrase. */
if (!nacpAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "BcatPassphrase", nacp->bcat_passphrase)) goto end;
/* StartupUserAccountOption. */
if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "StartupUserAccountOption", &(nacp->startup_user_account_option_flag), sizeof(nacp->startup_user_account_option_flag), \
NacpStartupUserAccountOption_Count, &nacpGetStartupUserAccountOptionString)) goto end;
/* AddOnContentRegistrationType. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "AddOnContentRegistrationType", nacp->add_on_content_registration_type, &nacpGetAddOnContentRegistrationTypeString)) goto end;
/* UserAccountSaveDataSizeMax. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "UserAccountSaveDataSizeMax", nacp->user_account_save_data_size_max)) goto end;
@ -531,6 +563,15 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
/* CacheStorageIndexMax. */
if (!nacpAddU16FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CacheStorageIndexMax", nacp->cache_storage_index_max, true)) goto end;
/* Hdcp. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Hdcp", nacp->hdcp, &nacpGetHdcpString)) goto end;
/* CrashReport. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CrashReport", nacp->crash_report, &nacpGetCrashReportString)) goto end;
/* RuntimeAddOnContentInstall. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "RuntimeAddOnContentInstall", nacp->runtime_add_on_content_install, &nacpGetRuntimeAddOnContentInstallString)) goto end;
/* PlayLogQueryableApplicationId. */
for(i = 0, count = 0; i < 0x10; i++)
{
@ -541,6 +582,9 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <PlayLogQueryableApplicationId />\n")) goto end;
/* PlayLogQueryCapability. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "PlayLogQueryCapability", nacp->play_log_query_capability, &nacpGetPlayLogQueryCapabilityString)) goto end;
/* Repair. */
if (!nacpAddBitflagFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "Repair", &(nacp->repair_flag), sizeof(nacp->repair_flag), NacpRepair_Count, &nacpGetRepairString)) goto end;
@ -564,27 +608,27 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
{
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <NeighborDetectionClientConfiguration>\n")) goto end;
/* SendDataConfiguration. */
/* SendGroupConfiguration. */
utilsGenerateHexStringFromData(key_str, sizeof(key_str), ndcc->send_group_configuration.key, sizeof(ndcc->send_group_configuration.key));
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <SendDataConfiguration>\n" \
" <DataId>0x%016lx</DataId>\n" \
" <SendGroupConfiguration>\n" \
" <GroupId>0x%016lx</GroupId>\n" \
" <Key>%s</Key>\n" \
" </SendDataConfiguration>\n", \
" </SendGroupConfiguration>\n", \
ndcc->send_group_configuration.group_id,
key_str)) goto end;
/* ReceivableDataConfiguration. */
/* ReceivableGroupConfiguration. */
for(i = 0; i < 0x10; i++)
{
utilsGenerateHexStringFromData(key_str, sizeof(key_str), ndcc->receivable_group_configurations[i].key, sizeof(ndcc->receivable_group_configurations[i].key));
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <ReceivableDataConfiguration>\n" \
" <DataId>0x%016lx</DataId>\n" \
" <ReceivableGroupConfiguration>\n" \
" <GroupId>0x%016lx</GroupId>\n" \
" <Key>%s</Key>\n" \
" </ReceivableDataConfiguration>\n", \
" </ReceivableGroupConfiguration>\n", \
ndcc->receivable_group_configurations[i].group_id,
key_str)) goto end;
}
@ -603,32 +647,21 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
nacpGetJitConfigurationFlagString(nacp->jit_configuration.jit_configuration_flag),
nacp->jit_configuration.memory_size)) goto end;
/* RequiredAddOnContentsSetBinaryDescriptor. */
for(i = 0; i < 0x20; i++)
/* RequiredAddOnContentsSet. */
for(i = 0, count = 0; i < 0x20; i++)
{
if (!raocsbd->descriptors[i].NacpDescriptors_ContinueSet) continue;
if ((raocsbd_available = (raocsbd->descriptors[i].NacpDescriptors_Index != 0))) break;
if (!raocsbd->descriptors[i].NacpDescriptors_Index || !raocsbd->descriptors[i].NacpDescriptors_ContinueSet) continue;
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <RequiredAddOnContentsSet>\n" \
" <Index>%u</Index>\n" \
" </RequiredAddOnContentsSet>\n",
raocsbd->descriptors[i].NacpDescriptors_Index)) goto end;
count++;
}
if (raocsbd_available)
{
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <RequiredAddOnContentsSetBinaryDescriptor>\n")) goto end;
for(i = 0; i < 0x20; i++)
{
if (!raocsbd->descriptors[i].NacpDescriptors_Index || !raocsbd->descriptors[i].NacpDescriptors_ContinueSet) continue;
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <Descriptor>\n" \
" <Index>%u</Index>\n"
" </Descriptor>\n",
raocsbd->descriptors[i].NacpDescriptors_Index)) goto end;
}
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " </RequiredAddOnContentsSetBinaryDescriptor>\n")) goto end;
} else {
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <RequiredAddOnContentsSetBinaryDescriptor />\n")) goto end;
}
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <RequiredAddOnContentsSet />\n")) goto end;
/* PlayReportPermission. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "PlayReportPermission", nacp->play_report_permission, &nacpGetPlayReportPermissionString)) goto end;
@ -639,30 +672,33 @@ bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx)
/* CrashScreenshotForDev. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "CrashScreenshotForDev", nacp->crash_screenshot_for_dev, &nacpGetCrashScreenshotForDevString)) goto end;
/* Icon. */
for(i = 0, count = 0; i < nacp_ctx->icon_count; i++)
/* AccessibleLaunchRequiredVersion. */
for(i = 0, count = 0; i < 0x8; i++)
{
NacpIconContext *icon_ctx = &(nacp_ctx->icon_ctx[i]);
if (!nacp->accessible_launch_required_version.application_id[i]) continue;
/* Calculate icon hash. */
sha256CalculateHash(icon_hash, icon_ctx->icon_data, icon_ctx->icon_size);
/* Generate icon hash string. Only the first half from the hash is used. */
utilsGenerateHexStringFromData(icon_hash_str, SHA256_HASH_SIZE + 1, icon_hash, SHA256_HASH_SIZE / 2);
/* Add XML element. */
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
" <Icon>\n" \
" <Language>%s</Language>\n" \
" <NxIconHash>%s</NxIconHash>\n" \
" </Icon>\n", \
nacpGetLanguageString(icon_ctx->language), \
icon_hash_str)) goto end;
" <AccessibleLaunchRequiredVersion>\n" \
" <ApplicationId>0x%016lx</ApplicationId>\n" \
" </AccessibleLaunchRequiredVersion>\n",
nacp->accessible_launch_required_version.application_id[i])) goto end;
count++;
}
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <Icon />\n")) goto end;
if (!count && !utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <AccessibleLaunchRequiredVersion />\n")) goto end;
/* History. Unused but kept anyway. */
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <History />\n")) goto end;
/* RuntimeParameterDelivery. */
if (!nacpAddEnumFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "RuntimeParameterDelivery", nacp->runtime_parameter_delivery, &nacpGetRuntimeParameterDeliveryString)) goto end;
/* ApplicationId. */
if (!nacpAddU64FieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "ApplicationId", nacp_ctx->nca_ctx->header.program_id)) goto end;
/* FilterDescriptionFilePath. Unused but kept anyway. */
if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, " <FilterDescriptionFilePath />\n")) goto end;
if (!(success = utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, "</Application>"))) goto end;
@ -770,6 +806,11 @@ const char *nacpGetStartupUserAccountOptionString(u8 startup_user_account_option
return (startup_user_account_option < NacpStartupUserAccountOption_Count ? g_nacpStartupUserAccountOptionStrings[startup_user_account_option] : g_unknownString);
}
const char *nacpGetPlayLogQueryCapabilityString(u8 play_log_query_capability)
{
return (play_log_query_capability < NacpPlayLogQueryCapability_Count ? g_nacpPlayLogQueryCapabilityStrings[play_log_query_capability] : g_unknownString);
}
const char *nacpGetRepairString(u8 repair)
{
return (repair < NacpRepair_Count ? g_nacpRepairStrings[repair] : g_unknownString);
@ -846,8 +887,6 @@ static bool nacpAddBitflagFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_s
return false;
}
if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>", tag_name)) goto end;
for(i = 0; i < flag_width; i++)
{
if (flag_u8[i])
@ -862,33 +901,22 @@ static bool nacpAddBitflagFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_s
for(i = 0; i < max_flag_idx; i++)
{
if (!nacpCheckBitflagField(flag, flag_bitcount, i)) continue;
if (count && !utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, ",")) goto end;
if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, "%s", str_func(i))) goto end;
if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>%s</%s>\n", tag_name, str_func(i), tag_name)) 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;
/* Edge case for new, unsupported flags. */
if (!count) empty_flag = true;
}
success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, "</%s>\n", tag_name);
if (empty_flag && !utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s />\n", tag_name)) goto end;
success = true;
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))
{
LOGFILE("Invalid parameters!");
return false;
}
return utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>0x%016lx</%s>\n", tag_name, value, tag_name);
}
static bool nacpAddU16FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u16 value, bool hex)
{
if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name))
@ -900,3 +928,25 @@ static bool nacpAddU16FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size,
return (hex ? utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>0x%04x</%s>\n", tag_name, value, tag_name) : \
utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>%u</%s>\n", tag_name, value, tag_name));
}
static bool nacpAddU32FieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, u32 value)
{
if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name))
{
LOGFILE("Invalid parameters!");
return false;
}
return utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>%u</%s>\n", tag_name, value, tag_name);
}
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))
{
LOGFILE("Invalid parameters!");
return false;
}
return utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>0x%016lx</%s>\n", tag_name, value, tag_name);
}

View file

@ -277,7 +277,7 @@ typedef struct {
typedef struct {
u16 NacpDescriptors_Index : 15;
u16 NacpDescriptors_ContinueSet : 1; ///< Called "flag" by Nintendo.
u16 NacpDescriptors_ContinueSet : 1; ///< Called "flag" by Nintendo, which isn't really great...
} NacpDescriptors;
typedef struct {
@ -302,6 +302,10 @@ typedef enum {
NacpCrashScreenshotForDev_Count = 2 ///< Not a real value.
} NacpCrashScreenshotForDev;
typedef struct {
u64 application_id[8];
} NacpAccessibleLaunchRequiredVersion;
typedef struct {
NacpTitle title[0x10];
char isbn[0x25];
@ -360,7 +364,9 @@ typedef struct {
u8 play_report_permission; ///< NacpPlayReportPermission.
u8 crash_screenshot_for_prod; ///< NacpCrashScreenshotForProd.
u8 crash_screenshot_for_dev; ///< NacpCrashScreenshotForDev.
u8 reserved_5[0xBFD];
u8 reserved_5[0x5];
NacpAccessibleLaunchRequiredVersion accessible_launch_required_version;
u8 reserved_6[0xBB8];
} _NacpStruct;
typedef struct {
@ -388,9 +394,9 @@ typedef struct {
/// 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.
/// 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.
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx);
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version);
/// These functions return pointers to string representations of the input value.
/// If the provided value is invalid, "Unknown" is returned.
@ -412,6 +418,7 @@ const char *nacpGetRuntimeParameterDeliveryString(u8 runtime_parameter_delivery)
const char *nacpGetCrashReportString(u8 crash_report);
const char *nacpGetHdcpString(u8 hdcp);
const char *nacpGetStartupUserAccountOptionString(u8 startup_user_account_option);
const char *nacpGetPlayLogQueryCapabilityString(u8 play_log_query_capability);
const char *nacpGetRepairString(u8 repair);
const char *nacpGetRequiredNetworkServiceLicenseOnLaunchString(u8 required_network_service_license_on_launch);
const char *nacpGetJitConfigurationFlagString(u64 jig_configuration_flag);

View file

@ -357,7 +357,13 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
}
/* Retrieve file entry. */
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name for \"%s\"! (\"%s\").", filename, path);
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename)))
{
/* Only log error if we're not dealing with NACP icons. */
int res = strncmp("/icon_", path, 6);
if (res != 0 || (res == 0 && ((NcaContext*)ctx->nca_fs_ctx->nca_ctx)->content_type != NcmContentType_Control))
LOGFILE("Failed to retrieve file entry by name for \"%s\"! (\"%s\").", filename, path);
}
end:
if (path_dup) free(path_dup);

View file

@ -1357,8 +1357,9 @@ static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id)
/* Fill information. */
cur_title_info->storage_id = storage_id;
memcpy(&(cur_title_info->dot_version), &(meta_keys[i].version), sizeof(u32));
memcpy(&(cur_title_info->meta_key), &(meta_keys[i]), sizeof(NcmContentMetaKey));
memcpy(&(cur_title_info->version), &(meta_keys[i].version), sizeof(u32));
if (cur_title_info->meta_key.type <= NcmContentMetaType_Application) cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(meta_keys[i].id);
/* Retrieve content infos. */

View file

@ -52,8 +52,8 @@ typedef struct {
/// Retrieved using ncm databases.
typedef struct _TitleInfo {
u8 storage_id; ///< NcmStorageId.
TitleVersion dot_version; ///< Holds the same value from meta_key.version.
NcmContentMetaKey meta_key; ///< Used with ncm calls.
TitleVersion version; ///< Holds the same value from meta_key.version.
u32 content_count; ///< Content info count.
NcmContentInfo *content_infos; ///< Content info entries from this title.
u64 title_size; ///< Total title size.