mirror of
https://github.com/jakcron/nstool
synced 2024-12-25 14:11:14 +00:00
Merge pull request #88 from sagumamugas/nca-patches
Support for nca patches
This commit is contained in:
commit
21e1e33e55
6 changed files with 78 additions and 8 deletions
2
deps/libpietendo
vendored
2
deps/libpietendo
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 994e70282691fb8cdb9276906500fd873ac51270
|
||||
Subproject commit 40d9332a2d5ffc96dda91dc41b73cd979ffd8d7e
|
|
@ -6,6 +6,7 @@
|
|||
#include <pietendo/hac/AesKeygen.h>
|
||||
#include <pietendo/hac/HierarchicalSha256Stream.h>
|
||||
#include <pietendo/hac/HierarchicalIntegrityStream.h>
|
||||
#include <pietendo/hac/BKTREncryptedStream.h>
|
||||
#include <pietendo/hac/PartitionFsSnapshotGenerator.h>
|
||||
#include <pietendo/hac/RomFsSnapshotGenerator.h>
|
||||
#include <pietendo/hac/CombinedFsSnapshotGenerator.h>
|
||||
|
@ -48,6 +49,11 @@ void nstool::NcaProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& fi
|
|||
mFile = file;
|
||||
}
|
||||
|
||||
void nstool::NcaProcess::setBaseNCAPath(const tc::Optional<tc::io::Path>& baseNCA)
|
||||
{
|
||||
baseNcaPath = baseNCA;
|
||||
}
|
||||
|
||||
void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg)
|
||||
{
|
||||
mKeyCfg = keycfg;
|
||||
|
@ -211,6 +217,26 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys()
|
|||
}
|
||||
}
|
||||
|
||||
nstool::NcaProcess nstool::NcaProcess::readBaseNCA() {
|
||||
if (baseNcaPath.isNull()) {
|
||||
throw tc::Exception(mModuleName, "Base NCA not supplied. Necessary for update NCA.");
|
||||
}
|
||||
std::shared_ptr<tc::io::IStream> base_stream = std::make_shared<tc::io::FileStream>(tc::io::FileStream(baseNcaPath.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read));
|
||||
|
||||
NcaProcess obj;
|
||||
nstool::CliOutputMode cliOutput;
|
||||
cliOutput.show_basic_info = false;
|
||||
cliOutput.show_extended_info = false;
|
||||
cliOutput.show_keydata = false;
|
||||
cliOutput.show_layout = false;
|
||||
obj.setCliOutputMode(cliOutput);
|
||||
obj.setVerifyMode(true);
|
||||
obj.setKeyCfg(mKeyCfg);
|
||||
obj.setInputFile(base_stream);
|
||||
obj.process();
|
||||
return obj;
|
||||
}
|
||||
|
||||
void nstool::NcaProcess::generatePartitionConfiguration()
|
||||
{
|
||||
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
|
||||
|
@ -266,13 +292,13 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
|||
else
|
||||
{
|
||||
// create raw partition
|
||||
info.reader = std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, info.offset, info.size));
|
||||
info.raw_reader = std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, info.offset, info.size));
|
||||
|
||||
// handle encryption if required reader based on encryption type
|
||||
if (info.enc_type == pie::hac::nca::EncryptionType_None)
|
||||
{
|
||||
// no encryption so do nothing
|
||||
//info.reader = info.reader;
|
||||
info.decrypt_reader = info.raw_reader;
|
||||
}
|
||||
else if (info.enc_type == pie::hac::nca::EncryptionType_AesCtr)
|
||||
{
|
||||
|
@ -287,10 +313,40 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
|||
tc::crypto::IncrementCounterAes128Ctr(partition_ctr.data(), info.offset>>4);
|
||||
|
||||
// create decryption stream
|
||||
info.reader = std::make_shared<tc::crypto::Aes128CtrEncryptedStream>(tc::crypto::Aes128CtrEncryptedStream(info.reader, partition_key, partition_ctr));
|
||||
info.decrypt_reader = std::make_shared<tc::crypto::Aes128CtrEncryptedStream>(tc::crypto::Aes128CtrEncryptedStream(info.raw_reader, partition_key, partition_ctr));
|
||||
|
||||
}
|
||||
else if (info.enc_type == pie::hac::nca::EncryptionType_AesXts || info.enc_type == pie::hac::nca::EncryptionType_AesCtrEx)
|
||||
else if (info.enc_type == pie::hac::nca::EncryptionType_AesCtrEx)
|
||||
{
|
||||
if (mContentKey.aes_ctr.isNull())
|
||||
throw tc::Exception(mModuleName, "AES-CTR Key was not determined");
|
||||
|
||||
// get partition key
|
||||
pie::hac::detail::aes128_key_t partition_key = mContentKey.aes_ctr.get();
|
||||
|
||||
// get partition counter
|
||||
pie::hac::detail::aes_iv_t partition_ctr = info.aes_ctr;
|
||||
tc::crypto::IncrementCounterAes128Ctr(partition_ctr.data(), info.offset >> 4);
|
||||
|
||||
NcaProcess nca_base = readBaseNCA();
|
||||
if (nca_base.mHdr.getProgramId() != mHdr.getProgramId()) {
|
||||
throw tc::Exception(mModuleName, "Invalid base nca. ProgramID diferent.");
|
||||
}
|
||||
std::shared_ptr<tc::io::IStream> base_reader;
|
||||
for (auto& partition_base : nca_base.mPartitions) {
|
||||
if (partition_base.format_type == pie::hac::nca::FormatType::FormatType_RomFs && partition_base.raw_reader != nullptr)
|
||||
{
|
||||
base_reader = partition_base.decrypt_reader;
|
||||
}
|
||||
}
|
||||
if (base_reader == nullptr) {
|
||||
throw tc::Exception(mModuleName, "Cannot determine RomFs from base nca.");
|
||||
}
|
||||
|
||||
// create decryption stream
|
||||
info.decrypt_reader = std::make_shared<pie::hac::BKTREncryptedStream>(pie::hac::BKTREncryptedStream(info.raw_reader, partition_key, partition_ctr, fs_header.patch_info, base_reader));
|
||||
}
|
||||
else if (info.enc_type == pie::hac::nca::EncryptionType_AesXts)
|
||||
{
|
||||
throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNSUPPORTED", pie::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type)));
|
||||
}
|
||||
|
@ -306,10 +362,10 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
|||
case (pie::hac::nca::HashType_None):
|
||||
break;
|
||||
case (pie::hac::nca::HashType_HierarchicalSha256):
|
||||
info.reader = std::make_shared<pie::hac::HierarchicalSha256Stream>(pie::hac::HierarchicalSha256Stream(info.reader, info.hierarchicalsha256_hdr));
|
||||
info.reader = std::make_shared<pie::hac::HierarchicalSha256Stream>(pie::hac::HierarchicalSha256Stream(info.decrypt_reader, info.hierarchicalsha256_hdr));
|
||||
break;
|
||||
case (pie::hac::nca::HashType_HierarchicalIntegrity):
|
||||
info.reader = std::make_shared<pie::hac::HierarchicalIntegrityStream>(pie::hac::HierarchicalIntegrityStream(info.reader, info.hierarchicalintegrity_hdr));
|
||||
info.reader = std::make_shared<pie::hac::HierarchicalIntegrityStream>(pie::hac::HierarchicalIntegrityStream(info.decrypt_reader, info.hierarchicalintegrity_hdr));
|
||||
break;
|
||||
default:
|
||||
throw tc::Exception(mModuleName, fmt::format("HashType({:s}): UNKNOWN", pie::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type)));
|
||||
|
|
|
@ -21,6 +21,8 @@ public:
|
|||
void setKeyCfg(const KeyBag& keycfg);
|
||||
void setCliOutputMode(CliOutputMode type);
|
||||
void setVerifyMode(bool verify);
|
||||
void setBaseNCAPath(const tc::Optional<tc::io::Path>& keycfg);
|
||||
|
||||
|
||||
// fs specific
|
||||
void setShowFsTree(bool show_fs_tree);
|
||||
|
@ -39,6 +41,7 @@ private:
|
|||
KeyBag mKeyCfg;
|
||||
CliOutputMode mCliOutputMode;
|
||||
bool mVerify;
|
||||
tc::Optional<tc::io::Path> baseNcaPath;
|
||||
|
||||
// fs processing
|
||||
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
|
||||
|
@ -93,6 +96,8 @@ private:
|
|||
// raw partition data
|
||||
struct sPartitionInfo
|
||||
{
|
||||
std::shared_ptr<tc::io::IStream> raw_reader;
|
||||
std::shared_ptr<tc::io::IStream> decrypt_reader;
|
||||
std::shared_ptr<tc::io::IStream> reader;
|
||||
tc::io::VirtualFileSystem::FileSystemSnapshot fs_snapshot;
|
||||
std::shared_ptr<tc::io::IFileSystem> fs_reader;
|
||||
|
@ -126,6 +131,8 @@ private:
|
|||
void displayHeader();
|
||||
void processPartitions();
|
||||
|
||||
NcaProcess readBaseNCA();
|
||||
|
||||
std::string getContentTypeForMountStr(pie::hac::nca::ContentType cont_type) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -645,6 +645,8 @@ void nstool::SettingsInitializer::parse_args(const std::vector<std::string>& arg
|
|||
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part2" }, tc::io::Path("/2/"))));
|
||||
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part3" }, tc::io::Path("/3/"))));
|
||||
|
||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(nca.base_nca_path, { "--basenca" })));
|
||||
|
||||
// kip options
|
||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" })));
|
||||
|
||||
|
@ -796,7 +798,7 @@ void nstool::SettingsInitializer::usage_text() const
|
|||
fmt::print(" --normal Extract \"normal\" partition to directory. (Alias for \"-x /normal <out path>\")\n");
|
||||
fmt::print(" --secure Extract \"secure\" partition to directory. (Alias for \"-x /secure <out path>\")\n");
|
||||
fmt::print("\n NCA (Nintendo Content Archive)\n");
|
||||
fmt::print(" {:s} [--fstree] [-x [<virtual path>] <out path>] [--bodykey <key> --titlekey <key> -tik <tik path>] <.nca file>\n", BIN_NAME);
|
||||
fmt::print(" {:s} [--fstree] [-x [<virtual path>] <out path>] [--bodykey <key> --titlekey <key> -tik <tik path> --basenca <.nca file>] <.nca file>\n", BIN_NAME);
|
||||
fmt::print(" --fstree Print filesystem tree.\n");
|
||||
fmt::print(" -x, --extract Extract a file or directory to local filesystem.\n");
|
||||
fmt::print(" --titlekey Specify (encrypted) title key extracted from ticket.\n");
|
||||
|
@ -807,6 +809,7 @@ void nstool::SettingsInitializer::usage_text() const
|
|||
fmt::print(" --part1 Extract partition \"1\" to directory. (Alias for \"-x /1 <out path>\")\n");
|
||||
fmt::print(" --part2 Extract partition \"2\" to directory. (Alias for \"-x /2 <out path>\")\n");
|
||||
fmt::print(" --part3 Extract partition \"3\" to directory. (Alias for \"-x /3 <out path>\")\n");
|
||||
fmt::print(" --basenca Specify base NCA file for update NCA files.\n");
|
||||
fmt::print("\n NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object)\n");
|
||||
fmt::print(" {:s} [--listapi --listsym] [--insttype <inst. type>] <file>\n", BIN_NAME);
|
||||
fmt::print(" --listapi Print SDK API List.\n");
|
||||
|
|
|
@ -76,6 +76,7 @@ struct Settings
|
|||
tc::Optional<tc::io::Path> part1_extract_path;
|
||||
tc::Optional<tc::io::Path> part2_extract_path;
|
||||
tc::Optional<tc::io::Path> part3_extract_path;
|
||||
tc::Optional<tc::io::Path> base_nca_path;
|
||||
} nca;
|
||||
|
||||
// KIP options
|
||||
|
@ -110,6 +111,8 @@ struct Settings
|
|||
|
||||
kip.extract_path = tc::Optional<tc::io::Path>();
|
||||
|
||||
nca.base_nca_path = tc::Optional<tc::io::Path>();
|
||||
|
||||
aset.icon_extract_path = tc::Optional<tc::io::Path>();
|
||||
aset.nacp_extract_path = tc::Optional<tc::io::Path>();
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
|||
nstool::NcaProcess obj;
|
||||
|
||||
obj.setInputFile(infile_stream);
|
||||
obj.setBaseNCAPath(set.nca.base_nca_path);
|
||||
obj.setKeyCfg(set.opt.keybag);
|
||||
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||
obj.setVerifyMode(set.opt.verify);
|
||||
|
|
Loading…
Reference in a new issue