diff --git a/source/hactool_options.cpp b/source/hactool_options.cpp index 22bb12d..8b47d41 100644 --- a/source/hactool_options.cpp +++ b/source/hactool_options.cpp @@ -163,6 +163,11 @@ namespace ams::hactool { MakeOptionHandler("normaldir", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.normal_partition_out_dir), arg); }), MakeOptionHandler("updatedir", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.update_partition_out_dir), arg); }), MakeOptionHandler("logodir", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.logo_partition_out_dir), arg); }), + MakeOptionHandler("basenca", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.base_nca_path), arg); }), + MakeOptionHandler("basexci", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.base_xci_path), arg); }), + MakeOptionHandler("basepfs", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.base_pfs_path), arg); }), + MakeOptionHandler("basensp", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.base_pfs_path), arg); }), + MakeOptionHandler("baseappfs", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.base_appfs_path), arg); }), MakeOptionHandler("listromfs", [] (Options &options) { options.list_romfs = true; }), MakeOptionHandler("listupdate", [] (Options &options) { options.list_update = true; }), MakeOptionHandler("appindex", [] (Options &options, const char *arg) { return ParseIntegerArgument(std::addressof(options.preferred_app_index), arg); }), diff --git a/source/hactool_options.hpp b/source/hactool_options.hpp index 7c83f1b..1781220 100644 --- a/source/hactool_options.hpp +++ b/source/hactool_options.hpp @@ -36,6 +36,10 @@ namespace ams::hactool { struct Options { const char *in_file_path = nullptr; FileType file_type = FileType::Nca; + const char *base_nca_path = nullptr; + const char *base_xci_path = nullptr; + const char *base_pfs_path = nullptr; + const char *base_appfs_path = nullptr; bool valid = false; bool raw = false; bool verify = false; diff --git a/source/hactool_processor.hpp b/source/hactool_processor.hpp index d95acc7..13b70b0 100644 --- a/source/hactool_processor.hpp +++ b/source/hactool_processor.hpp @@ -140,6 +140,15 @@ namespace ams::hactool { fssrv::impl::ExternalKeyManager m_external_nca_key_manager; std::shared_ptr m_local_fs; + bool m_has_base_nca; + bool m_has_base_xci; + bool m_has_base_pfs; + bool m_has_base_appfs; + ProcessAsNcaContext m_base_nca_ctx; + ProcessAsXciContext m_base_xci_ctx; + ProcessAsPfsContext m_base_pfs_ctx; + ProcessAsApplicationFileSystemContext m_base_appfs_ctx; + os::SdkMutex m_print_lock; char m_indent_buffer[1_KB]; public: diff --git a/source/hactool_processor.main.cpp b/source/hactool_processor.main.cpp index cd26a06..4392b1a 100644 --- a/source/hactool_processor.main.cpp +++ b/source/hactool_processor.main.cpp @@ -19,7 +19,13 @@ namespace ams::hactool { - Processor::Processor(const Options &options) : m_options(options) { + Processor::Processor(const Options &options) : m_options(options), m_base_nca_ctx{}, m_base_xci_ctx{}, m_base_pfs_ctx{}, m_base_appfs_ctx{} { + /* Default to no bases. */ + m_has_base_nca = false; + m_has_base_xci = false; + m_has_base_pfs = false; + m_has_base_appfs = false; + /* Create local file system for host root. */ fssrv::fscreator::LocalFileSystemCreator local_fs_creator(true); fs::Path normalized_path; @@ -33,6 +39,61 @@ namespace ams::hactool { /* Setup our internal keys. */ this->PresetInternalKeys(); + /* Open any bases we've been provided. */ + { + if (m_options.base_nca_path != nullptr) { + std::shared_ptr storage = nullptr; + if (const auto open_res = OpenFileStorage(std::addressof(storage), m_local_fs, m_options.base_nca_path); R_SUCCEEDED(open_res)) { + if (const auto proc_res = this->ProcessAsNca(std::move(storage), std::addressof(m_base_nca_ctx)); R_SUCCEEDED(proc_res)) { + m_has_base_nca = true; + } else { + fprintf(stderr, "Failed to process base nca (%s): 2%03d-%04d\n", m_options.base_nca_path, proc_res.GetModule(), proc_res.GetDescription()); + } + } else { + fprintf(stderr, "Failed to open base nca (%s): 2%03d-%04d\n", m_options.base_nca_path, open_res.GetModule(), open_res.GetDescription()); + } + } + + if (m_options.base_xci_path != nullptr) { + std::shared_ptr storage = nullptr; + if (const auto open_res = OpenFileStorage(std::addressof(storage), m_local_fs, m_options.base_xci_path); R_SUCCEEDED(open_res)) { + if (const auto proc_res = this->ProcessAsXci(std::move(storage), std::addressof(m_base_xci_ctx)); R_SUCCEEDED(proc_res)) { + m_has_base_xci = true; + } else { + fprintf(stderr, "Failed to process base xci (%s): 2%03d-%04d\n", m_options.base_xci_path, proc_res.GetModule(), proc_res.GetDescription()); + } + } else { + fprintf(stderr, "Failed to open base xci (%s): 2%03d-%04d\n", m_options.base_xci_path, open_res.GetModule(), open_res.GetDescription()); + } + } + + if (m_options.base_pfs_path != nullptr) { + std::shared_ptr storage = nullptr; + if (const auto open_res = OpenFileStorage(std::addressof(storage), m_local_fs, m_options.base_pfs_path); R_SUCCEEDED(open_res)) { + if (const auto proc_res = this->ProcessAsPfs(std::move(storage), std::addressof(m_base_pfs_ctx)); R_SUCCEEDED(proc_res)) { + m_has_base_pfs = true; + } else { + fprintf(stderr, "Failed to process base pfs (%s): 2%03d-%04d\n", m_options.base_pfs_path, proc_res.GetModule(), proc_res.GetDescription()); + } + } else { + fprintf(stderr, "Failed to open base pfs (%s): 2%03d-%04d\n", m_options.base_pfs_path, open_res.GetModule(), open_res.GetDescription()); + } + } + + if (m_options.base_appfs_path != nullptr) { + std::shared_ptr fs = nullptr; + if (const auto open_res = OpenSubDirectoryFileSystem(std::addressof(fs), m_local_fs, m_options.base_appfs_path); R_SUCCEEDED(open_res)) { + if (const auto proc_res = this->ProcessAsApplicationFileSystem(std::move(fs), std::addressof(m_base_appfs_ctx)); R_SUCCEEDED(proc_res)) { + m_has_base_appfs = true; + } else { + fprintf(stderr, "Failed to process base app fs (%s): 2%03d-%04d\n", m_options.base_appfs_path, proc_res.GetModule(), proc_res.GetDescription()); + } + } else { + fprintf(stderr, "Failed to open base app fs (%s): 2%03d-%04d\n", m_options.base_appfs_path, open_res.GetModule(), open_res.GetDescription()); + } + } + } + if (m_options.file_type == FileType::AppFs) { /* Open the filesystem. */ std::shared_ptr input = nullptr; diff --git a/source/hactool_processor.nca.cpp b/source/hactool_processor.nca.cpp index 7dc5cf0..7f13905 100644 --- a/source/hactool_processor.nca.cpp +++ b/source/hactool_processor.nca.cpp @@ -127,6 +127,47 @@ namespace ams::hactool { /* Create an NCA reader for the input file. */ R_TRY(ParseNca(std::addressof(ctx->reader), ctx->storage, m_external_nca_key_manager)); + /* Decide on a base, if one isn't already set. */ + { + /* First see if we explicitly have a viable base nca. */ + if (ctx->base_reader == nullptr && m_has_base_nca && m_base_nca_ctx.reader != nullptr && m_base_nca_ctx.reader->GetProgramId() == ctx->reader->GetProgramId()) { + ctx->base_reader = m_base_nca_ctx.reader; + } + + /* Next, we'll try looking for a match in the appfs of a base xci, pfs, or appfs. */ + auto GetBaseFromAppFs = [&](ProcessAsApplicationFileSystemContext &app_ctx, const char *src) { + if (ctx->base_reader == nullptr) { + if (auto app_prog = app_ctx.apps.Find(ncm::ApplicationId{ctx->reader->GetProgramId() & ~static_cast(0xFF)}, 0, ctx->reader->GetProgramId() & 0xFF, ncm::ContentType::Program, ncm::ContentMetaType::Application); app_prog != app_ctx.apps.end()) { + ProcessAsNcaContext tmp_ctx{}; + if (const auto process_res = this->ProcessAsNca(app_prog->GetData().storage, std::addressof(tmp_ctx)); R_SUCCEEDED(process_res)) { + if (ctx->reader->GetProgramId() ) + ctx->base_reader = tmp_ctx.reader; + } else { + fprintf(stderr, "[Warning]: Failed to process base program nca from %s: 2%03d-%04d\n", src, process_res.GetModule(), process_res.GetDescription()); + } + } + } + }; + + if (m_has_base_xci && m_base_xci_ctx.secure_partition.fs != nullptr) { + m_has_base_xci = false; + GetBaseFromAppFs(m_base_xci_ctx.app_ctx, "basexci"); + m_has_base_xci = true; + } + + if (m_has_base_pfs && !m_base_pfs_ctx.is_exefs) { + m_has_base_xci = false; + GetBaseFromAppFs(m_base_pfs_ctx.app_ctx, "basepfs"); + m_has_base_pfs = true; + } + + if (m_has_base_appfs) { + m_has_base_appfs = false; + GetBaseFromAppFs(m_base_appfs_ctx, "baseappfs"); + m_has_base_appfs = true; + } + } + /* Open storages for each section. */ std::shared_ptr npdm_storage; @@ -279,6 +320,8 @@ namespace ams::hactool { addon.v32 = util::ConvertToBigEndian(ctx.reader->GetSdkAddonVersion()); this->PrintFormat("SDK Version", "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, addon.v8[0], addon.v8[1], addon.v8[2], addon.v8[3]); + this->PrintId64("Program Id", ctx.reader->GetProgramId()); + this->PrintString("Distribution Type", fs::impl::IdString().ToString(ctx.reader->GetDistributionType())); this->PrintString("Content Type", fs::impl::IdString().ToString(ctx.reader->GetContentType()));