mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
Fix support for NSOs without a valid .api_info section + ProgramInfo context.
ProgramInfo XML generation is still missing. Getting real close to reimplementing NSP dumping.
This commit is contained in:
parent
f45d1a21b5
commit
495e331306
5 changed files with 242 additions and 11 deletions
|
@ -22,6 +22,7 @@
|
|||
#include "gamecard.h"
|
||||
#include "title.h"
|
||||
#include "cnmt.h"
|
||||
#include "program_info.h"
|
||||
#include "nacp.h"
|
||||
#include "legal_info.h"
|
||||
|
||||
|
@ -65,6 +66,7 @@ int main(int argc, char *argv[])
|
|||
Ticket tik = {0};
|
||||
|
||||
ContentMetaContext cnmt_ctx = {0};
|
||||
ProgramInfoContext program_info_ctx = {0};
|
||||
NacpContext nacp_ctx = {0};
|
||||
LegalInfoContext legal_info_ctx = {0};
|
||||
|
||||
|
@ -192,7 +194,7 @@ 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, legal_idx = 0;
|
||||
u32 meta_idx = (user_app_data.app_info->content_count - 1), program_idx = 0, control_idx = 0, legal_idx = 0;
|
||||
|
||||
for(u32 i = 0, j = 0; i < user_app_data.app_info->content_count; i++)
|
||||
{
|
||||
|
@ -205,6 +207,8 @@ int main(int argc, char *argv[])
|
|||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
@ -222,7 +226,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("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])))
|
||||
|
@ -237,7 +243,7 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
consolePrint("cnmt xml succeeded\n");
|
||||
|
||||
sprintf(path, "sdmc:/%016lX_xml/%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);
|
||||
|
||||
xml_fd = fopen(path, "wb");
|
||||
if (xml_fd)
|
||||
|
@ -247,7 +253,7 @@ int main(int argc, char *argv[])
|
|||
xml_fd = NULL;
|
||||
}
|
||||
|
||||
sprintf(path, "sdmc:/%016lX_xml/%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);
|
||||
|
||||
xml_fd = fopen(path, "wb");
|
||||
if (xml_fd)
|
||||
|
@ -260,6 +266,27 @@ int main(int argc, char *argv[])
|
|||
consolePrint("cnmt xml failed\n");
|
||||
}
|
||||
|
||||
if (!programInfoInitializeContext(&program_info_ctx, &(nca_ctx[program_idx])))
|
||||
{
|
||||
consolePrint("program info initialize ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("program info initialize ctx succeeded\n");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!nacpInitializeContext(&nacp_ctx, &(nca_ctx[control_idx])))
|
||||
{
|
||||
consolePrint("nacp initialize ctx failed\n");
|
||||
|
@ -272,7 +299,7 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
consolePrint("nacp xml succeeded\n");
|
||||
|
||||
sprintf(path, "sdmc:/%016lX_xml/%s.nacp", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str);
|
||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str);
|
||||
|
||||
xml_fd = fopen(path, "wb");
|
||||
if (xml_fd)
|
||||
|
@ -282,7 +309,7 @@ int main(int argc, char *argv[])
|
|||
xml_fd = NULL;
|
||||
}
|
||||
|
||||
sprintf(path, "sdmc:/%016lX_xml/%s.nacp.xml", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str);
|
||||
sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp.xml", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str);
|
||||
|
||||
xml_fd = fopen(path, "wb");
|
||||
if (xml_fd)
|
||||
|
@ -296,7 +323,7 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
NacpIconContext *icon_ctx = &(nacp_ctx.icon_ctx[i]);
|
||||
|
||||
sprintf(path, "sdmc:/%016lX_xml/%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.nx.%s.jpg", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language));
|
||||
|
||||
xml_fd = fopen(path, "wb");
|
||||
if (xml_fd)
|
||||
|
@ -318,7 +345,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
consolePrint("legalinfo initialize ctx succeeded\n");
|
||||
|
||||
sprintf(path, "sdmc:/%016lX_xml/%s.legalinfo.xml", app_metadata[selected_idx]->title_id, legal_info_ctx.nca_ctx->content_id_str);
|
||||
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)
|
||||
|
@ -339,6 +366,8 @@ out2:
|
|||
|
||||
nacpFreeContext(&nacp_ctx);
|
||||
|
||||
programInfoFreeContext(&program_info_ctx);
|
||||
|
||||
cnmtFreeContext(&cnmt_ctx);
|
||||
|
||||
if (nca_ctx) free(nca_ctx);
|
||||
|
|
|
@ -108,7 +108,7 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx,
|
|||
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);
|
||||
goto end;
|
||||
|
@ -279,6 +279,8 @@ 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)
|
||||
{
|
||||
if (!section_size) return true;
|
||||
|
||||
/* Allocate memory for the desired .rodata section. */
|
||||
if (!(*section_ptr = malloc(section_size)))
|
||||
{
|
||||
|
|
|
@ -116,8 +116,9 @@ typedef struct {
|
|||
NsoHeader nso_header; ///< NSO header.
|
||||
char *module_name; ///< Pointer to a dynamically allocated buffer that holds the NSO module name, if available. Otherwise, this is set to NULL.
|
||||
char *module_info_name; ///< Pointer to a dynamically allocated buffer that holds the .rodata module info module name, if available. Otherwise, this is set to NULL.
|
||||
char *rodata_api_info_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata API info section data. Middleware/GuidelineApi data is retrieved from this section.
|
||||
u64 rodata_api_info_section_size; ///< .rodata API info section size. Kept here for convenience - this is part of 'nso_header'.
|
||||
char *rodata_api_info_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata API info section data, if available. Otherwise, this is set to NULL.
|
||||
///< Middleware and GuidelineApi entries are retrieved from this section.
|
||||
u64 rodata_api_info_section_size; ///< .rodata API info section size, if available. Otherwise, this is set to 0. Kept here for convenience - this is part of 'nso_header'.
|
||||
char *rodata_dynstr_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata dynamic string section data. UnresolvedApi data is retrieved from this section.
|
||||
u64 rodata_dynstr_section_size; ///< .rodata dynamic string section size. Kept here for convenience - this is part of 'nso_header'.
|
||||
u8 *rodata_dynsym_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata dynamic symbol section data. Used to retrieve pointers to symbol strings within dynstr.
|
||||
|
|
111
source/program_info.c
Normal file
111
source/program_info.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* program_info.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 <mbedtls/base64.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "program_info.h"
|
||||
#include "elf_symbol.h"
|
||||
|
||||
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 || \
|
||||
(nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
nca_ctx->header.content_type != NcaContentType_Program || !out)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 i = 0, pfs_entry_count = 0, magic = 0;
|
||||
NsoContext *tmp_nso_ctx = NULL;
|
||||
|
||||
bool success = false;
|
||||
|
||||
/* Free output context beforehand. */
|
||||
programInfoFreeContext(out);
|
||||
|
||||
/* Initialize Partition FS context. */
|
||||
if (!pfsInitializeContext(&(out->pfs_ctx), &(nca_ctx->fs_contexts[0])))
|
||||
{
|
||||
LOGFILE("Failed to initialize Partition FS context!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check if we're indeed dealing with an ExeFS. */
|
||||
if (!out->pfs_ctx.is_exefs)
|
||||
{
|
||||
LOGFILE("Initialized Partition FS is not an ExeFS!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Get ExeFS entry count. Edge case, we should never trigger this. */
|
||||
if (!(pfs_entry_count = pfsGetEntryCount(&(out->pfs_ctx))))
|
||||
{
|
||||
LOGFILE("ExeFS has no file entries!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Initialize NPDM context. */
|
||||
if (!npdmInitializeContext(&(out->npdm_ctx), &(out->pfs_ctx)))
|
||||
{
|
||||
LOGFILE("Failed to initialize NPDM context!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Initialize NSO contexts. */
|
||||
for(i = 0; i < pfs_entry_count; i++)
|
||||
{
|
||||
/* Skip the main.npdm entry, as well as any other entries without a NSO header. */
|
||||
PartitionFileSystemEntry *pfs_entry = pfsGetEntryByIndex(&(out->pfs_ctx), i);
|
||||
char *pfs_entry_name = pfsGetEntryName(&(out->pfs_ctx), pfs_entry);
|
||||
if (!pfs_entry || !pfs_entry_name || !strncmp(pfs_entry_name, "main.npdm", 9) || !pfsReadEntryData(&(out->pfs_ctx), pfs_entry, &magic, sizeof(u32), 0) || \
|
||||
__builtin_bswap32(magic) != NSO_HEADER_MAGIC) continue;
|
||||
|
||||
/* Reallocate NSO context buffer. */
|
||||
if (!(tmp_nso_ctx = realloc(out->nso_ctx, (out->nso_count + 1) * sizeof(NsoContext))))
|
||||
{
|
||||
LOGFILE("Failed to reallocate NSO context buffer for NSO \"%s\"! (entry #%u).", pfs_entry_name, i);
|
||||
goto end;
|
||||
}
|
||||
|
||||
out->nso_ctx = tmp_nso_ctx;
|
||||
tmp_nso_ctx = NULL;
|
||||
|
||||
memset(&(out->nso_ctx[out->nso_count]), 0, sizeof(NsoContext));
|
||||
|
||||
/* Initialize NSO context. */
|
||||
if (!nsoInitializeContext(&(out->nso_ctx[out->nso_count]), &(out->pfs_ctx), pfs_entry))
|
||||
{
|
||||
LOGFILE("Failed to initialize context for NSO \"%s\"! (entry #%u).", pfs_entry_name, i);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update NSO count. */
|
||||
out->nso_count++;
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (!success) programInfoFreeContext(out);
|
||||
|
||||
return success;
|
||||
}
|
88
source/program_info.h
Normal file
88
source/program_info.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* program_info.h
|
||||
*
|
||||
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __PROGRAM_INFO_H__
|
||||
#define __PROGRAM_INFO_H__
|
||||
|
||||
#include "npdm.h"
|
||||
#include "nso.h"
|
||||
|
||||
typedef struct {
|
||||
NcaContext *nca_ctx; ///< Pointer to the NCA context for the Program NCA from which program data (NPDM / NSO) is retrieved.
|
||||
PartitionFileSystemContext pfs_ctx; ///< PartitionFileSystemContext for the Program NCA ExeFS, which is where program data (NPDM / NSO) is stored.
|
||||
NpdmContext npdm_ctx; ///< NpdmContext for the NPDM stored in Program NCA ExeFS. Holds its own NcaHierarchicalSha256Patch that may be applied to the Program NCA if needed.
|
||||
u32 nso_count; ///< Number of NSOs stored in Program NCA FS section #0.
|
||||
NsoContext *nso_ctx; ///< Pointer to a dynamically allocated buffer that holds 'nso_count' NSO contexts.
|
||||
char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data.
|
||||
///< This is always NULL unless programInfoGenerateAuthoringToolXml() is used on this ProgramInfoContext.
|
||||
u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'.
|
||||
///< This is always 0 unless programInfoGenerateAuthoringToolXml() is used on this ProgramInfoContext.
|
||||
} ProgramInfoContext;
|
||||
|
||||
/// Initializes a ProgramInfoContext using a previously initialized NcaContext (which must belong to a Program NCA).
|
||||
bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx);
|
||||
|
||||
/// Generates an AuthoringTool-like XML using information from a previously initialized ProgramInfoContext.
|
||||
/// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the ProgramInfoContext.
|
||||
bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx);
|
||||
|
||||
/// Helper inline functions.
|
||||
|
||||
NX_INLINE void programInfoFreeContext(ProgramInfoContext *program_info_ctx)
|
||||
{
|
||||
if (!program_info_ctx) return;
|
||||
|
||||
pfsFreeContext(&(program_info_ctx->pfs_ctx));
|
||||
npdmFreeContext(&(program_info_ctx->npdm_ctx));
|
||||
|
||||
if (program_info_ctx->nso_ctx)
|
||||
{
|
||||
for(u32 i = 0; i < program_info_ctx->nso_count; i++) nsoFreeContext(&(program_info_ctx->nso_ctx[i]));
|
||||
free(program_info_ctx->nso_ctx);
|
||||
}
|
||||
|
||||
if (program_info_ctx->authoring_tool_xml) free(program_info_ctx->authoring_tool_xml);
|
||||
memset(program_info_ctx, 0, sizeof(ProgramInfoContext));
|
||||
}
|
||||
|
||||
NX_INLINE bool programInfoIsValidContext(ProgramInfoContext *program_info_ctx)
|
||||
{
|
||||
return (program_info_ctx && program_info_ctx->nca_ctx && npdmIsValidContext(&(program_info_ctx->npdm_ctx)) && program_info_ctx->npdm_ctx.pfs_ctx == &(program_info_ctx->pfs_ctx) && \
|
||||
program_info_ctx->nso_count && program_info_ctx->nso_ctx);
|
||||
}
|
||||
|
||||
NX_INLINE bool programInfoChangeAcidPublicKeyAndNcaSignature(ProgramInfoContext *program_info_ctx)
|
||||
{
|
||||
return (programInfoIsValidContext(program_info_ctx) && npdmChangeAcidPublicKeyAndNcaSignature(&(program_info_ctx->npdm_ctx)));
|
||||
}
|
||||
|
||||
NX_INLINE bool programInfoIsNcaPatchRequired(ProgramInfoContext *program_info_ctx)
|
||||
{
|
||||
return (programInfoIsValidContext(program_info_ctx) && npdmIsNcaPatchRequired(&(program_info_ctx->npdm_ctx)));
|
||||
}
|
||||
|
||||
NX_INLINE bool programInfoGenerateNcaPatch(ProgramInfoContext *program_info_ctx)
|
||||
{
|
||||
return (programInfoIsValidContext(program_info_ctx) && npdmGenerateNcaPatch(&(program_info_ctx->npdm_ctx)));
|
||||
}
|
||||
|
||||
#endif /* __PROGRAM_INFO_H__ */
|
Loading…
Reference in a new issue