mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 04:02:11 +00:00
Add FsAccessControlData to ProgramInfo XML.
Also: * Fixed support for NSOs with invalid module_name, api_info, dynstr and/or dynsym offsets/sizes. * Updated the XML generator example to generate XML data for all available NCAs, regardless of their ID offset values.
This commit is contained in:
parent
4ab8f44003
commit
ebd97d7c82
7 changed files with 236 additions and 150 deletions
|
@ -35,6 +35,17 @@ static void consolePrint(const char *text, ...)
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void writeFile(void *buf, size_t buf_size, const char *path)
|
||||||
|
{
|
||||||
|
FILE *fd = fopen(path, "wb");
|
||||||
|
if (fd)
|
||||||
|
{
|
||||||
|
fwrite(buf, 1, buf_size, fd);
|
||||||
|
fclose(fd);
|
||||||
|
utilsCommitSdCardFileSystemChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
(void)argc;
|
(void)argc;
|
||||||
|
@ -65,12 +76,18 @@ int main(int argc, char *argv[])
|
||||||
NcaContext *nca_ctx = NULL;
|
NcaContext *nca_ctx = NULL;
|
||||||
Ticket tik = {0};
|
Ticket tik = {0};
|
||||||
|
|
||||||
|
u32 meta_idx = 0;
|
||||||
ContentMetaContext cnmt_ctx = {0};
|
ContentMetaContext cnmt_ctx = {0};
|
||||||
ProgramInfoContext program_info_ctx = {0};
|
|
||||||
NacpContext nacp_ctx = {0};
|
|
||||||
LegalInfoContext legal_info_ctx = {0};
|
|
||||||
|
|
||||||
FILE *xml_fd = NULL;
|
u32 program_count = 0, program_idx = 0;
|
||||||
|
ProgramInfoContext *program_info_ctx = NULL;
|
||||||
|
|
||||||
|
u32 control_count = 0, control_idx = 0;
|
||||||
|
NacpContext *nacp_ctx = NULL;
|
||||||
|
|
||||||
|
u32 legal_info_count = 0, legal_info_idx = 0;
|
||||||
|
LegalInfoContext *legal_info_ctx = NULL;
|
||||||
|
|
||||||
char path[FS_MAX_PATH] = {0};
|
char path[FS_MAX_PATH] = {0};
|
||||||
|
|
||||||
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
|
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
|
||||||
|
@ -194,26 +211,80 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
consolePrint("nca ctx calloc succeeded\n");
|
consolePrint("nca ctx calloc succeeded\n");
|
||||||
|
|
||||||
u32 meta_idx = (user_app_data.app_info->content_count - 1), program_idx = 0, control_idx = 0, legal_idx = 0;
|
meta_idx = (user_app_data.app_info->content_count - 1);
|
||||||
|
|
||||||
for(u32 i = 0, j = 0; i < user_app_data.app_info->content_count; i++)
|
program_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_Program);
|
||||||
|
if (program_count && !(program_info_ctx = calloc(program_count, sizeof(ProgramInfoContext))))
|
||||||
{
|
{
|
||||||
if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_Meta) continue;
|
consolePrint("program info ctx calloc failed\n");
|
||||||
|
|
||||||
if (!ncaInitializeContext(&(nca_ctx[j]), user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? GameCardHashFileSystemPartitionType_Secure : 0), \
|
|
||||||
&(user_app_data.app_info->content_infos[i]), &tik))
|
|
||||||
{
|
|
||||||
consolePrint("%s nca initialize ctx failed\n", titleGetNcmContentTypeName(user_app_data.app_info->content_infos[i].content_type));
|
|
||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_Program && user_app_data.app_info->content_infos[i].id_offset == 0) control_idx = j;
|
control_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_Control);
|
||||||
|
if (control_count && !(nacp_ctx = calloc(control_count, sizeof(NacpContext))))
|
||||||
|
{
|
||||||
|
consolePrint("nacp ctx calloc failed\n");
|
||||||
|
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;
|
legal_info_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_LegalInformation);
|
||||||
|
if (legal_info_count && !(legal_info_ctx = calloc(legal_info_count, sizeof(LegalInfoContext))))
|
||||||
|
{
|
||||||
|
consolePrint("legal info ctx calloc failed\n");
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_LegalInformation && user_app_data.app_info->content_infos[i].id_offset == 0) legal_idx = j;
|
for(u32 i = 0, j = 0; i < user_app_data.app_info->content_count; i++)
|
||||||
|
{
|
||||||
|
// set meta nca as the last nca
|
||||||
|
NcmContentInfo *content_info = &(user_app_data.app_info->content_infos[i]);
|
||||||
|
if (content_info->content_type == NcmContentType_Meta) continue;
|
||||||
|
|
||||||
|
if (!ncaInitializeContext(&(nca_ctx[j]), user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? GameCardHashFileSystemPartitionType_Secure : 0), \
|
||||||
|
content_info, &tik))
|
||||||
|
{
|
||||||
|
consolePrint("%s #%u initialize nca ctx failed\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
|
||||||
|
|
||||||
|
switch(content_info->content_type)
|
||||||
|
{
|
||||||
|
case NcmContentType_Program:
|
||||||
|
if (!programInfoInitializeContext(&(program_info_ctx[program_idx]), &(nca_ctx[j])))
|
||||||
|
{
|
||||||
|
consolePrint("initialize program info ctx failed (%s)\n", nca_ctx[j].content_id_str);
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
nca_ctx[j].content_type_ctx = &(program_info_ctx[program_idx++]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NcmContentType_Control:
|
||||||
|
if (!nacpInitializeContext(&(nacp_ctx[control_idx]), &(nca_ctx[j])))
|
||||||
|
{
|
||||||
|
consolePrint("initialize nacp ctx failed (%s)\n", nca_ctx[j].content_id_str);
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
nca_ctx[j].content_type_ctx = &(nacp_ctx[control_idx++]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NcmContentType_LegalInformation:
|
||||||
|
if (!legalInfoInitializeContext(&(legal_info_ctx[legal_info_idx]), &(nca_ctx[j])))
|
||||||
|
{
|
||||||
|
consolePrint("initialize legal info ctx failed (%s)\n", nca_ctx[j].content_id_str);
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
nca_ctx[j].content_type_ctx = &(legal_info_ctx[legal_info_idx++]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
consolePrint("%s nca initialize ctx succeeded\n", titleGetNcmContentTypeName(user_app_data.app_info->content_infos[i].content_type));
|
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,11 +297,6 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
consolePrint("Meta nca initialize ctx succeeded\n");
|
consolePrint("Meta nca initialize ctx succeeded\n");
|
||||||
|
|
||||||
mkdir("sdmc:/at_xml", 0777);
|
|
||||||
|
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX", app_metadata[selected_idx]->title_id);
|
|
||||||
mkdir(path, 0777);
|
|
||||||
|
|
||||||
if (!cnmtInitializeContext(&cnmt_ctx, &(nca_ctx[meta_idx])))
|
if (!cnmtInitializeContext(&cnmt_ctx, &(nca_ctx[meta_idx])))
|
||||||
{
|
{
|
||||||
consolePrint("cnmt initialize ctx failed\n");
|
consolePrint("cnmt initialize ctx failed\n");
|
||||||
|
@ -239,124 +305,86 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
consolePrint("cnmt initialize ctx succeeded\n");
|
consolePrint("cnmt initialize ctx succeeded\n");
|
||||||
|
|
||||||
|
sprintf(path, "sdmc:/at_xml/%016lX", app_metadata[selected_idx]->title_id);
|
||||||
|
utilsCreateDirectoryTree(path, true);
|
||||||
|
|
||||||
if (cnmtGenerateAuthoringToolXml(&cnmt_ctx, nca_ctx, user_app_data.app_info->content_count))
|
if (cnmtGenerateAuthoringToolXml(&cnmt_ctx, nca_ctx, user_app_data.app_info->content_count))
|
||||||
{
|
{
|
||||||
consolePrint("cnmt xml succeeded\n");
|
consolePrint("cnmt xml succeeded\n");
|
||||||
|
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
//sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
||||||
|
//writeFile(cnmt_ctx.raw_data, cnmt_ctx.raw_data_size, path);
|
||||||
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:/at_xml/%016lX/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
|
||||||
|
writeFile(cnmt_ctx.authoring_tool_xml, cnmt_ctx.authoring_tool_xml_size, path);
|
||||||
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 {
|
} else {
|
||||||
consolePrint("cnmt xml failed\n");
|
consolePrint("cnmt xml failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!programInfoInitializeContext(&program_info_ctx, &(nca_ctx[program_idx])))
|
for(u32 i = 0; i < user_app_data.app_info->content_count; i++)
|
||||||
{
|
{
|
||||||
consolePrint("program info initialize ctx failed\n");
|
NcaContext *cur_nca_ctx = &(nca_ctx[i]);
|
||||||
|
|
||||||
|
if (!cur_nca_ctx->content_type_ctx || cur_nca_ctx->content_type == NcmContentType_Meta) continue;
|
||||||
|
|
||||||
|
switch(cur_nca_ctx->content_type)
|
||||||
|
{
|
||||||
|
case NcmContentType_Program:
|
||||||
|
{
|
||||||
|
ProgramInfoContext *cur_program_info_ctx = (ProgramInfoContext*)cur_nca_ctx->content_type_ctx;
|
||||||
|
if (!programInfoGenerateAuthoringToolXml(cur_program_info_ctx))
|
||||||
|
{
|
||||||
|
consolePrint("program info xml failed (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
|
||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
consolePrint("program info initialize ctx succeeded\n");
|
consolePrint("program info xml succeeded (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
|
||||||
|
|
||||||
if (programInfoGenerateAuthoringToolXml(&program_info_ctx))
|
sprintf(path, "sdmc:/at_xml/%016lX/%s.programinfo.xml", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
|
||||||
{
|
writeFile(cur_program_info_ctx->authoring_tool_xml, cur_program_info_ctx->authoring_tool_xml_size, path);
|
||||||
consolePrint("program info xml succeeded\n");
|
|
||||||
|
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.programinfo.xml", app_metadata[selected_idx]->title_id, program_info_ctx.nca_ctx->content_id_str);
|
break;
|
||||||
|
|
||||||
xml_fd = fopen(path, "wb");
|
|
||||||
if (xml_fd)
|
|
||||||
{
|
|
||||||
fwrite(program_info_ctx.authoring_tool_xml, 1, program_info_ctx.authoring_tool_xml_size, xml_fd);
|
|
||||||
fclose(xml_fd);
|
|
||||||
xml_fd = NULL;
|
|
||||||
}
|
}
|
||||||
} else {
|
case NcmContentType_Control:
|
||||||
consolePrint("program info xml failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nacpInitializeContext(&nacp_ctx, &(nca_ctx[control_idx])))
|
|
||||||
{
|
{
|
||||||
consolePrint("nacp initialize ctx failed\n");
|
NacpContext *cur_nacp_ctx = (NacpContext*)cur_nca_ctx->content_type_ctx;
|
||||||
|
if (!nacpGenerateAuthoringToolXml(cur_nacp_ctx, user_app_data.app_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
|
||||||
|
{
|
||||||
|
consolePrint("nacp xml failed (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
|
||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
consolePrint("nacp initialize ctx succeeded\n");
|
consolePrint("nacp xml succeeded (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
|
||||||
|
|
||||||
if (nacpGenerateAuthoringToolXml(&nacp_ctx, user_app_data.app_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
|
//sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
|
||||||
|
//writeFile(cur_nacp_ctx->data, sizeof(_NacpStruct), path);
|
||||||
|
|
||||||
|
sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp.xml", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
|
||||||
|
writeFile(cur_nacp_ctx->authoring_tool_xml, cur_nacp_ctx->authoring_tool_xml_size, path);
|
||||||
|
|
||||||
|
for(u8 j = 0; j < cur_nacp_ctx->icon_count; j++)
|
||||||
{
|
{
|
||||||
consolePrint("nacp xml succeeded\n");
|
NacpIconContext *icon_ctx = &(cur_nacp_ctx->icon_ctx[j]);
|
||||||
|
sprintf(path, "sdmc:/at_xml/%016lX/%s.nx.%s.jpg", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language));
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str);
|
writeFile(icon_ctx->icon_data, icon_ctx->icon_size, path);
|
||||||
|
|
||||||
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:/at_xml/%016lX/%s.nacp.xml", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str);
|
break;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
case NcmContentType_LegalInformation:
|
||||||
for(u8 i = 0; i < nacp_ctx.icon_count; i++)
|
|
||||||
{
|
{
|
||||||
NacpIconContext *icon_ctx = &(nacp_ctx.icon_ctx[i]);
|
LegalInfoContext *cur_legal_info_ctx = (LegalInfoContext*)cur_nca_ctx->content_type_ctx;
|
||||||
|
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.nx.%s.jpg", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language));
|
sprintf(path, "sdmc:/at_xml/%016lX/%s.legalinfo.xml", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
|
||||||
|
writeFile(cur_legal_info_ctx->authoring_tool_xml, cur_legal_info_ctx->authoring_tool_xml_size, path);
|
||||||
|
|
||||||
xml_fd = fopen(path, "wb");
|
consolePrint("legal info xml succeeded (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
|
||||||
if (xml_fd)
|
|
||||||
{
|
break;
|
||||||
fwrite(icon_ctx->icon_data, 1, icon_ctx->icon_size, xml_fd);
|
|
||||||
fclose(xml_fd);
|
|
||||||
xml_fd = NULL;
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
consolePrint("nacp xml failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!legalInfoInitializeContext(&legal_info_ctx, &(nca_ctx[legal_idx])))
|
|
||||||
{
|
|
||||||
consolePrint("legalinfo initialize ctx failed\n");
|
|
||||||
goto out2;
|
|
||||||
}
|
|
||||||
|
|
||||||
consolePrint("legalinfo initialize ctx succeeded\n");
|
|
||||||
|
|
||||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.legalinfo.xml", app_metadata[selected_idx]->title_id, legal_info_ctx.nca_ctx->content_id_str);
|
|
||||||
|
|
||||||
xml_fd = fopen(path, "wb");
|
|
||||||
if (xml_fd)
|
|
||||||
{
|
|
||||||
fwrite(legal_info_ctx.authoring_tool_xml, 1, legal_info_ctx.authoring_tool_xml_size, xml_fd);
|
|
||||||
fclose(xml_fd);
|
|
||||||
xml_fd = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out2:
|
out2:
|
||||||
|
@ -366,11 +394,23 @@ out2:
|
||||||
utilsWaitForButtonPress(KEY_NONE);
|
utilsWaitForButtonPress(KEY_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
legalInfoFreeContext(&legal_info_ctx);
|
if (legal_info_ctx)
|
||||||
|
{
|
||||||
|
for(u32 i = 0; i < legal_info_count; i++) legalInfoFreeContext(&(legal_info_ctx[i]));
|
||||||
|
free(legal_info_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
nacpFreeContext(&nacp_ctx);
|
if (nacp_ctx)
|
||||||
|
{
|
||||||
|
for(u32 i = 0; i < control_count; i++) nacpFreeContext(&(nacp_ctx[i]));
|
||||||
|
free(nacp_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
programInfoFreeContext(&program_info_ctx);
|
if (program_info_ctx)
|
||||||
|
{
|
||||||
|
for(u32 i = 0; i < program_count; i++) programInfoFreeContext(&(program_info_ctx[i]));
|
||||||
|
free(program_info_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
cnmtFreeContext(&cnmt_ctx);
|
cnmtFreeContext(&cnmt_ctx);
|
||||||
|
|
||||||
|
|
|
@ -318,6 +318,7 @@ typedef struct {
|
||||||
u8 header_hash[SHA256_HASH_SIZE]; ///< NCA header hash. Used to determine if it's necessary to replace the NCA header while dumping this NCA.
|
u8 header_hash[SHA256_HASH_SIZE]; ///< NCA header hash. Used to determine if it's necessary to replace the NCA header while dumping this NCA.
|
||||||
NcaFsSectionContext fs_contexts[NCA_FS_HEADER_COUNT];
|
NcaFsSectionContext fs_contexts[NCA_FS_HEADER_COUNT];
|
||||||
NcaDecryptedKeyArea decrypted_key_area;
|
NcaDecryptedKeyArea decrypted_key_area;
|
||||||
|
void *content_type_ctx; ///< Pointer to a content type context (e.g. ContentMetaContext, ProgramInfoContext, NacpContext, LegalInfoContext). Set to NULL if unused.
|
||||||
} NcaContext;
|
} NcaContext;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -195,9 +195,9 @@ typedef struct {
|
||||||
u8 version;
|
u8 version;
|
||||||
u8 reserved_1[0x3];
|
u8 reserved_1[0x3];
|
||||||
u64 flags;
|
u64 flags;
|
||||||
u32 content_owner_info_offset;
|
u32 content_owner_info_offset; ///< Relative to the start of this block. Only valid if 'content_owner_info_size' is greater than 0.
|
||||||
u32 content_owner_info_size;
|
u32 content_owner_info_size;
|
||||||
u32 save_data_owner_info_offset;
|
u32 save_data_owner_info_offset; ///< Relative to the start of this block. Only valid if 'save_data_owner_info_size' is greater than 0.
|
||||||
u32 save_data_owner_info_size;
|
u32 save_data_owner_info_size;
|
||||||
} NpdmAciFsAccessControlDescriptor;
|
} NpdmAciFsAccessControlDescriptor;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
|
@ -105,25 +105,21 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx,
|
||||||
if (out->nso_header.module_name_offset < sizeof(NsoHeader) || !out->nso_header.module_name_size || (out->nso_header.module_name_offset + out->nso_header.module_name_size) > pfs_entry->size)
|
if (out->nso_header.module_name_offset < sizeof(NsoHeader) || !out->nso_header.module_name_size || (out->nso_header.module_name_offset + out->nso_header.module_name_size) > pfs_entry->size)
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid module name offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.module_name_offset, out->nso_header.module_name_size);
|
LOGFILE("Invalid module name offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.module_name_offset, out->nso_header.module_name_size);
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out->nso_header.api_info_section_header.size && (out->nso_header.api_info_section_header.offset + out->nso_header.api_info_section_header.size) > out->nso_header.rodata_segment_header.size)
|
if (out->nso_header.api_info_section_header.size && (out->nso_header.api_info_section_header.offset + out->nso_header.api_info_section_header.size) > out->nso_header.rodata_segment_header.size)
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid .api_info section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.api_info_section_header.offset, out->nso_header.api_info_section_header.size);
|
LOGFILE("Invalid .api_info section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.api_info_section_header.offset, out->nso_header.api_info_section_header.size);
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!out->nso_header.dynstr_section_header.size || (out->nso_header.dynstr_section_header.offset + out->nso_header.dynstr_section_header.size) > out->nso_header.rodata_segment_header.size)
|
if (!out->nso_header.dynstr_section_header.size || (out->nso_header.dynstr_section_header.offset + out->nso_header.dynstr_section_header.size) > out->nso_header.rodata_segment_header.size)
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid .dynstr section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynstr_section_header.offset, out->nso_header.dynstr_section_header.size);
|
LOGFILE("Invalid .dynstr section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynstr_section_header.offset, out->nso_header.dynstr_section_header.size);
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!out->nso_header.dynsym_section_header.size || (out->nso_header.dynsym_section_header.offset + out->nso_header.dynsym_section_header.size) > out->nso_header.rodata_segment_header.size)
|
if (!out->nso_header.dynsym_section_header.size || (out->nso_header.dynsym_section_header.offset + out->nso_header.dynsym_section_header.size) > out->nso_header.rodata_segment_header.size)
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid .dynsym section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynsym_section_header.offset, out->nso_header.dynsym_section_header.size);
|
LOGFILE("Invalid .dynsym section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynsym_section_header.offset, out->nso_header.dynsym_section_header.size);
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get module name. */
|
/* Get module name. */
|
||||||
|
@ -159,7 +155,7 @@ end:
|
||||||
|
|
||||||
static bool nsoGetModuleName(NsoContext *nso_ctx)
|
static bool nsoGetModuleName(NsoContext *nso_ctx)
|
||||||
{
|
{
|
||||||
if (nso_ctx->nso_header.module_name_size <= 1) return true;
|
if (nso_ctx->nso_header.module_name_offset < sizeof(NsoHeader) || nso_ctx->nso_header.module_name_size <= 1) return true;
|
||||||
|
|
||||||
NsoModuleName module_name = {0};
|
NsoModuleName module_name = {0};
|
||||||
|
|
||||||
|
@ -281,7 +277,7 @@ static bool nsoGetModuleInfoName(NsoContext *nso_ctx, u8 *rodata_buf)
|
||||||
|
|
||||||
static bool nsoGetSectionFromRodataSegment(NsoContext *nso_ctx, u8 *rodata_buf, u8 **section_ptr, u64 section_offset, u64 section_size)
|
static bool nsoGetSectionFromRodataSegment(NsoContext *nso_ctx, u8 *rodata_buf, u8 **section_ptr, u64 section_offset, u64 section_size)
|
||||||
{
|
{
|
||||||
if (!section_size) return true;
|
if (!section_size || (section_offset + section_size) > nso_ctx->nso_header.rodata_segment_header.size) return true;
|
||||||
|
|
||||||
/* Allocate memory for the desired .rodata section. */
|
/* Allocate memory for the desired .rodata section. */
|
||||||
if (!(*section_ptr = malloc(section_size)))
|
if (!(*section_ptr = malloc(section_size)))
|
||||||
|
|
|
@ -31,6 +31,13 @@ static const char *g_trueString = "True", *g_falseString = "False";
|
||||||
static const char g_nnSdkString[] = "NintendoSdk_nnSdk";
|
static const char g_nnSdkString[] = "NintendoSdk_nnSdk";
|
||||||
static const size_t g_nnSdkStringLength = (MAX_ELEMENTS(g_nnSdkString) - 1);
|
static const size_t g_nnSdkStringLength = (MAX_ELEMENTS(g_nnSdkString) - 1);
|
||||||
|
|
||||||
|
static const char *g_facAccessibilityStrings[] = {
|
||||||
|
"None",
|
||||||
|
"Read",
|
||||||
|
"Write",
|
||||||
|
"ReadWrite"
|
||||||
|
};
|
||||||
|
|
||||||
/* Function prototypes. */
|
/* Function prototypes. */
|
||||||
|
|
||||||
static bool programInfoGetSdkVersionAndBuildTypeFromSdkNso(ProgramInfoContext *program_info_ctx, char **sdk_version, char **build_type);
|
static bool programInfoGetSdkVersionAndBuildTypeFromSdkNso(ProgramInfoContext *program_info_ctx, char **sdk_version, char **build_type);
|
||||||
|
@ -43,6 +50,8 @@ static bool programInfoAddStringFieldToAuthoringToolXml(char **xml_buf, u64 *xml
|
||||||
static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx);
|
static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx);
|
||||||
static bool programInfoIsElfSymbolValid(u8 *dynsym_ptr, char *dynstr_base_ptr, u64 dynstr_size, bool is_64bit, char **symbol_str);
|
static bool programInfoIsElfSymbolValid(u8 *dynsym_ptr, char *dynstr_base_ptr, u64 dynstr_size, bool is_64bit, char **symbol_str);
|
||||||
|
|
||||||
|
static bool programInfoAddFsAccessControlDataToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx);
|
||||||
|
|
||||||
bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx)
|
bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx)
|
||||||
{
|
{
|
||||||
if (!out || !nca_ctx || !strlen(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Program || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \
|
if (!out || !nca_ctx || !strlen(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Program || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \
|
||||||
|
@ -226,31 +235,19 @@ bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx)
|
||||||
/* PrivateApiList. */
|
/* PrivateApiList. */
|
||||||
if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "PrivateApi", "Api", "SDK Private")) goto end;
|
if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "PrivateApi", "Api", "SDK Private")) goto end;
|
||||||
|
|
||||||
/* UnresolvedApiList. Add symbols from "main" NSO. */
|
/* UnresolvedApiList. */
|
||||||
if (!programInfoAddNsoSymbolsToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx)) goto end;
|
if (!programInfoAddNsoSymbolsToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx)) goto end;
|
||||||
|
|
||||||
/* GuidelineList. */
|
/* GuidelineList. */
|
||||||
if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "GuidelineApi", "Api", "SDK Guideline")) goto end;
|
if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "GuidelineApi", "Api", "SDK Guideline")) goto end;
|
||||||
|
|
||||||
|
/* FsAccessControlData. */
|
||||||
|
if (!programInfoAddFsAccessControlDataToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx)) goto end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!(success = utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
|
if (!(success = utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \
|
||||||
" <FsAccessControlData />\n" \
|
|
||||||
" <History />\n" /* Impossible to get. */ \
|
" <History />\n" /* Impossible to get. */ \
|
||||||
"</ProgramInfo>"))) goto end;
|
"</ProgramInfo>"))) goto end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Update ProgramInfo context. */
|
/* Update ProgramInfo context. */
|
||||||
program_info_ctx->authoring_tool_xml = xml_buf;
|
program_info_ctx->authoring_tool_xml = xml_buf;
|
||||||
program_info_ctx->authoring_tool_xml_size = strlen(xml_buf);
|
program_info_ctx->authoring_tool_xml_size = strlen(xml_buf);
|
||||||
|
@ -552,3 +549,55 @@ static bool programInfoIsElfSymbolValid(u8 *dynsym_ptr, char *dynstr_base_ptr, u
|
||||||
|
|
||||||
return is_valid;
|
return is_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool programInfoAddFsAccessControlDataToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx)
|
||||||
|
{
|
||||||
|
NpdmAciFsAccessControlDescriptor *aci_fac_descriptor = NULL;
|
||||||
|
NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock *save_data_owner_block = NULL;
|
||||||
|
u64 *save_data_owner_ids = NULL;
|
||||||
|
bool success = false, fac_data_available = false;
|
||||||
|
|
||||||
|
if (!xml_buf || !xml_buf_size || !program_info_ctx || !(aci_fac_descriptor = program_info_ctx->npdm_ctx.aci_fac_descriptor))
|
||||||
|
{
|
||||||
|
LOGFILE("Invalid parameters!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if there's save data owner data available in the FS access control data descriptor from the ACI0 section in the NPDM. */
|
||||||
|
fac_data_available = (aci_fac_descriptor->save_data_owner_info_offset >= sizeof(NpdmAciFsAccessControlDescriptor) && aci_fac_descriptor->save_data_owner_info_size);
|
||||||
|
if (!fac_data_available) goto end;
|
||||||
|
|
||||||
|
/* Get save data owner block and check the ID count. */
|
||||||
|
save_data_owner_block = (NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock*)((u8*)aci_fac_descriptor + aci_fac_descriptor->save_data_owner_info_offset);
|
||||||
|
if (!save_data_owner_block->save_data_owner_id_count)
|
||||||
|
{
|
||||||
|
fac_data_available = false;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get save data owner IDs. */
|
||||||
|
/* Padding to a 0x4-byte boundary is needed. Each accessibility field takes up a single byte, so we can get away with it by aligning the ID count. */
|
||||||
|
save_data_owner_ids = (u64*)((u8*)save_data_owner_block + sizeof(NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock) + ALIGN_UP(save_data_owner_block->save_data_owner_id_count, 0x4));
|
||||||
|
|
||||||
|
if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <FsAccessControlData>\n")) goto end;
|
||||||
|
|
||||||
|
/* Append save data owner IDs. */
|
||||||
|
for(u32 i = 0; i < save_data_owner_block->save_data_owner_id_count; i++)
|
||||||
|
{
|
||||||
|
if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, \
|
||||||
|
" <SaveDataOwnerIds>\n" \
|
||||||
|
" <Accessibility>%s</Accessibility>\n" \
|
||||||
|
" <Id>0x%016lx</Id>\n" \
|
||||||
|
" </SaveDataOwnerIds>\n", \
|
||||||
|
g_facAccessibilityStrings[save_data_owner_block->accessibility[i] & 0x3], \
|
||||||
|
save_data_owner_ids[i])) goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " </FsAccessControlData>\n");
|
||||||
|
|
||||||
|
end:
|
||||||
|
/* Append an empty XML element if no FS access control data exists. */
|
||||||
|
if (!success && !fac_data_available) success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <FsAccessControlData />\n");
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
|
@ -383,9 +383,8 @@ void utilsWriteMessageToLogFile(const char *func_name, const char *fmt, ...)
|
||||||
mutexLock(&g_logfileMutex);
|
mutexLock(&g_logfileMutex);
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
FILE *logfile = NULL;
|
|
||||||
|
|
||||||
logfile = fopen(LOGFILE_PATH, "a+");
|
FILE *logfile = fopen(LOGFILE_PATH, "a+");
|
||||||
if (!logfile) goto end;
|
if (!logfile) goto end;
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
|
|
7
todo.txt
7
todo.txt
|
@ -8,7 +8,7 @@ todo:
|
||||||
tik: automatically dump tickets to the SD card?
|
tik: automatically dump tickets to the SD card?
|
||||||
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
||||||
|
|
||||||
gamecard: hfs0 functions to display filelist
|
gamecard: functions to display filelist
|
||||||
|
|
||||||
pfs0: functions to display filelist
|
pfs0: functions to display filelist
|
||||||
pfs0: full header aligned to 0x20 (nsp)
|
pfs0: full header aligned to 0x20 (nsp)
|
||||||
|
@ -17,7 +17,8 @@ todo:
|
||||||
|
|
||||||
bktr: functions to display filelist (wrappers for romfs functions tbh)
|
bktr: functions to display filelist (wrappers for romfs functions tbh)
|
||||||
|
|
||||||
title: more functions for title lookup (filters, patches / aoc, etc.)
|
title: more functions for title lookup? (filters, patches / aoc, etc.)
|
||||||
title: more functions for content lookup (based on id?)
|
title: more functions for content lookup? (based on id)
|
||||||
|
title: parse the update partition from gamecards (if available) to generate ncmcontentinfo data for all update titles
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue