1
0
Fork 0
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:
Pablo Curiel 2020-10-12 16:35:47 -04:00
parent 4ab8f44003
commit ebd97d7c82
7 changed files with 236 additions and 150 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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