mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 10:45:28 +00:00
Support for nca patches
This commit is contained in:
parent
b0a07c061a
commit
b5e3fb56b1
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/AesKeygen.h>
|
||||||
#include <pietendo/hac/HierarchicalSha256Stream.h>
|
#include <pietendo/hac/HierarchicalSha256Stream.h>
|
||||||
#include <pietendo/hac/HierarchicalIntegrityStream.h>
|
#include <pietendo/hac/HierarchicalIntegrityStream.h>
|
||||||
|
#include <pietendo/hac/BKTREncryptedStream.h>
|
||||||
#include <pietendo/hac/PartitionFsSnapshotGenerator.h>
|
#include <pietendo/hac/PartitionFsSnapshotGenerator.h>
|
||||||
#include <pietendo/hac/RomFsSnapshotGenerator.h>
|
#include <pietendo/hac/RomFsSnapshotGenerator.h>
|
||||||
#include <pietendo/hac/CombinedFsSnapshotGenerator.h>
|
#include <pietendo/hac/CombinedFsSnapshotGenerator.h>
|
||||||
|
@ -48,6 +49,11 @@ void nstool::NcaProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& fi
|
||||||
mFile = file;
|
mFile = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nstool::NcaProcess::setBaseNCAPath(const tc::Optional<tc::io::Path>& baseNCA)
|
||||||
|
{
|
||||||
|
baseNcaPath = baseNCA;
|
||||||
|
}
|
||||||
|
|
||||||
void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg)
|
void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg)
|
||||||
{
|
{
|
||||||
mKeyCfg = 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()
|
void nstool::NcaProcess::generatePartitionConfiguration()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
|
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
|
||||||
|
@ -266,13 +292,13 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// create raw partition
|
// 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
|
// handle encryption if required reader based on encryption type
|
||||||
if (info.enc_type == pie::hac::nca::EncryptionType_None)
|
if (info.enc_type == pie::hac::nca::EncryptionType_None)
|
||||||
{
|
{
|
||||||
// no encryption so do nothing
|
// 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)
|
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);
|
tc::crypto::IncrementCounterAes128Ctr(partition_ctr.data(), info.offset>>4);
|
||||||
|
|
||||||
// create decryption stream
|
// 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)));
|
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):
|
case (pie::hac::nca::HashType_None):
|
||||||
break;
|
break;
|
||||||
case (pie::hac::nca::HashType_HierarchicalSha256):
|
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;
|
break;
|
||||||
case (pie::hac::nca::HashType_HierarchicalIntegrity):
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw tc::Exception(mModuleName, fmt::format("HashType({:s}): UNKNOWN", pie::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type)));
|
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 setKeyCfg(const KeyBag& keycfg);
|
||||||
void setCliOutputMode(CliOutputMode type);
|
void setCliOutputMode(CliOutputMode type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
void setBaseNCAPath(const tc::Optional<tc::io::Path>& keycfg);
|
||||||
|
|
||||||
|
|
||||||
// fs specific
|
// fs specific
|
||||||
void setShowFsTree(bool show_fs_tree);
|
void setShowFsTree(bool show_fs_tree);
|
||||||
|
@ -39,6 +41,7 @@ private:
|
||||||
KeyBag mKeyCfg;
|
KeyBag mKeyCfg;
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
tc::Optional<tc::io::Path> baseNcaPath;
|
||||||
|
|
||||||
// fs processing
|
// fs processing
|
||||||
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
|
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
|
||||||
|
@ -93,6 +96,8 @@ private:
|
||||||
// raw partition data
|
// raw partition data
|
||||||
struct sPartitionInfo
|
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;
|
std::shared_ptr<tc::io::IStream> reader;
|
||||||
tc::io::VirtualFileSystem::FileSystemSnapshot fs_snapshot;
|
tc::io::VirtualFileSystem::FileSystemSnapshot fs_snapshot;
|
||||||
std::shared_ptr<tc::io::IFileSystem> fs_reader;
|
std::shared_ptr<tc::io::IFileSystem> fs_reader;
|
||||||
|
@ -126,6 +131,8 @@ private:
|
||||||
void displayHeader();
|
void displayHeader();
|
||||||
void processPartitions();
|
void processPartitions();
|
||||||
|
|
||||||
|
NcaProcess readBaseNCA();
|
||||||
|
|
||||||
std::string getContentTypeForMountStr(pie::hac::nca::ContentType cont_type) const;
|
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, { "--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<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
|
// kip options
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" })));
|
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(" --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(" --secure Extract \"secure\" partition to directory. (Alias for \"-x /secure <out path>\")\n");
|
||||||
fmt::print("\n NCA (Nintendo Content Archive)\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(" --fstree Print filesystem tree.\n");
|
||||||
fmt::print(" -x, --extract Extract a file or directory to local filesystem.\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");
|
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(" --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(" --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(" --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("\n NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object)\n");
|
||||||
fmt::print(" {:s} [--listapi --listsym] [--insttype <inst. type>] <file>\n", BIN_NAME);
|
fmt::print(" {:s} [--listapi --listsym] [--insttype <inst. type>] <file>\n", BIN_NAME);
|
||||||
fmt::print(" --listapi Print SDK API List.\n");
|
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> part1_extract_path;
|
||||||
tc::Optional<tc::io::Path> part2_extract_path;
|
tc::Optional<tc::io::Path> part2_extract_path;
|
||||||
tc::Optional<tc::io::Path> part3_extract_path;
|
tc::Optional<tc::io::Path> part3_extract_path;
|
||||||
|
tc::Optional<tc::io::Path> base_nca_path;
|
||||||
} nca;
|
} nca;
|
||||||
|
|
||||||
// KIP options
|
// KIP options
|
||||||
|
@ -110,6 +111,8 @@ struct Settings
|
||||||
|
|
||||||
kip.extract_path = tc::Optional<tc::io::Path>();
|
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.icon_extract_path = tc::Optional<tc::io::Path>();
|
||||||
aset.nacp_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;
|
nstool::NcaProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
|
obj.setBaseNCAPath(set.nca.base_nca_path);
|
||||||
obj.setKeyCfg(set.opt.keybag);
|
obj.setKeyCfg(set.opt.keybag);
|
||||||
obj.setCliOutputMode(set.opt.cli_output_mode);
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
Loading…
Reference in a new issue