mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
Merge pull request #89 from jakcron/v1.7-development
Update NSTool to v1.7.0
This commit is contained in:
commit
d87c0fcf1c
18 changed files with 140 additions and 55 deletions
10
README.md
10
README.md
|
@ -143,6 +143,16 @@ nstool -x /path/to/a/file.bin ./extract_dir/different_name.bin some_file.bin
|
||||||
* NSP
|
* NSP
|
||||||
* XCI
|
* XCI
|
||||||
|
|
||||||
|
## NCA Patches
|
||||||
|
Nintendo distributes game patches/updates in the style of a diff to keep file sizes down. This means extracting game patches requires the base version of the game to be able to process patch data. Typically this is only done for the Program NCA.
|
||||||
|
|
||||||
|
If `basegame_v0.nca` is the base Program NCA, and `gamepatch_v13219.nca` is the patch Program NCA, simply specify the base NCA using the base NCA option `--basenca` when processing the patch NCA.
|
||||||
|
|
||||||
|
```
|
||||||
|
nstool --basenca ./basegame_v0.nca -x ./patchdata gamepatch_v13219.nca
|
||||||
|
```
|
||||||
|
In the above example the patch NCA is being extracted to `./patchdata`
|
||||||
|
|
||||||
## Encrypted Files
|
## Encrypted Files
|
||||||
Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them.
|
Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them.
|
||||||
|
|
||||||
|
|
2
deps/libfmt
vendored
2
deps/libfmt
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit ae6df0aead2fdaae8a0b117524a6eb036c8fa075
|
Subproject commit 22d3ddc718021809d8499913867bb9d93dfce7d0
|
2
deps/liblz4
vendored
2
deps/liblz4
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit b40b46406e87a753328abfda3b53dfabd2408da2
|
Subproject commit ea157aaacb8da70a9619c7c7f140b3983e06c988
|
2
deps/libmbedtls
vendored
2
deps/libmbedtls
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit e9e56bb773111f831af8dd36ad74de73b0c31aa2
|
Subproject commit d6d2803b77f6baa9a64bf3b2a787f0e93db7c48f
|
2
deps/libpietendo
vendored
2
deps/libpietendo
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 994e70282691fb8cdb9276906500fd873ac51270
|
Subproject commit 49c73ab54c661c752fadcae0b34a4474e54e84d9
|
2
deps/libtoolchain
vendored
2
deps/libtoolchain
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 05a38e1f0b7e28d8763937f503d4ac34dc8d823d
|
Subproject commit 6fcb2677cf3cac0a4900002f216b7550a4f16b7a
|
27
makefile
27
makefile
|
@ -1,6 +1,6 @@
|
||||||
# C++/C Recursive Project Makefile
|
# C++/C Recursive Project Makefile
|
||||||
# (c) Jack
|
# (c) Jack
|
||||||
# Version 6 (20211110)
|
# Version 8 (20220420)
|
||||||
|
|
||||||
# Project Name
|
# Project Name
|
||||||
PROJECT_NAME = nstool
|
PROJECT_NAME = nstool
|
||||||
|
@ -23,13 +23,6 @@ ifeq ($(ROOT_PROJECT_NAME),)
|
||||||
export ROOT_PROJECT_DEPENDENCY_PATH = $(ROOT_PROJECT_PATH)/deps
|
export ROOT_PROJECT_DEPENDENCY_PATH = $(ROOT_PROJECT_PATH)/deps
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Shared Library Definitions
|
|
||||||
PROJECT_SO_VER_MAJOR = 0
|
|
||||||
PROJECT_SO_VER_MINOR = 1
|
|
||||||
PROJECT_SO_VER_PATCH = 0
|
|
||||||
PROJECT_SONAME = $(PROJECT_NAME).so.$(PROJECT_SO_VER_MAJOR)
|
|
||||||
PROJECT_SO_FILENAME = $(PROJECT_SONAME).$(PROJECT_SO_VER_MINOR).$(PROJECT_SO_VER_PATCH)
|
|
||||||
|
|
||||||
# Project Dependencies
|
# Project Dependencies
|
||||||
PROJECT_DEPEND = pietendo toolchain fmt lz4 mbedtls
|
PROJECT_DEPEND = pietendo toolchain fmt lz4 mbedtls
|
||||||
PROJECT_DEPEND_LOCAL_DIR = libpietendo libtoolchain libfmt liblz4 libmbedtls
|
PROJECT_DEPEND_LOCAL_DIR = libpietendo libtoolchain libfmt liblz4 libmbedtls
|
||||||
|
@ -82,6 +75,7 @@ ifeq ($(PROJECT_PLATFORM), WIN32)
|
||||||
# Windows Flags/Libs
|
# Windows Flags/Libs
|
||||||
CC = x86_64-w64-mingw32-gcc
|
CC = x86_64-w64-mingw32-gcc
|
||||||
CXX = x86_64-w64-mingw32-g++
|
CXX = x86_64-w64-mingw32-g++
|
||||||
|
DEFINEFLAGS =
|
||||||
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable
|
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable
|
||||||
ARCHFLAGS =
|
ARCHFLAGS =
|
||||||
INC +=
|
INC +=
|
||||||
|
@ -91,6 +85,7 @@ else ifeq ($(PROJECT_PLATFORM), GNU)
|
||||||
# GNU/Linux Flags/Libs
|
# GNU/Linux Flags/Libs
|
||||||
#CC =
|
#CC =
|
||||||
#CXX =
|
#CXX =
|
||||||
|
DEFINEFLAGS =
|
||||||
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable
|
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable
|
||||||
ARCHFLAGS =
|
ARCHFLAGS =
|
||||||
INC +=
|
INC +=
|
||||||
|
@ -100,6 +95,7 @@ else ifeq ($(PROJECT_PLATFORM), MACOS)
|
||||||
# MacOS Flags/Libs
|
# MacOS Flags/Libs
|
||||||
#CC =
|
#CC =
|
||||||
#CXX =
|
#CXX =
|
||||||
|
DEFINEFLAGS =
|
||||||
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-private-field
|
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-private-field
|
||||||
ARCHFLAGS = -arch $(PROJECT_PLATFORM_ARCH)
|
ARCHFLAGS = -arch $(PROJECT_PLATFORM_ARCH)
|
||||||
INC +=
|
INC +=
|
||||||
|
@ -108,19 +104,18 @@ else ifeq ($(PROJECT_PLATFORM), MACOS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Compiler Flags
|
# Compiler Flags
|
||||||
CXXFLAGS = -std=c++11 $(INC) $(WARNFLAGS) $(ARCHFLAGS) -fPIC
|
CXXFLAGS = -std=c++11 $(INC) $(DEFINEFLAGS) $(WARNFLAGS) $(ARCHFLAGS) -fPIC
|
||||||
CFLAGS = -std=c11 $(INC) $(WARNFLAGS) $(ARCHFLAGS) -fPIC
|
CFLAGS = -std=c11 $(INC) $(DEFINEFLAGS) $(WARNFLAGS) $(ARCHFLAGS) -fPIC
|
||||||
|
|
||||||
# Object Files
|
# Object Files
|
||||||
SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
||||||
TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
||||||
|
|
||||||
# all is the default, user should specify what the default should do
|
# all is the default, user should specify what the default should do
|
||||||
# - 'static_lib' for building static library
|
# - 'static_lib' for building source as a static library
|
||||||
# - 'shared_lib' for building shared library
|
# - 'program' for building source as executable program
|
||||||
# - 'program' for building the program
|
|
||||||
# - 'test_program' for building the test program
|
# - 'test_program' for building the test program
|
||||||
# These can typically be used together however *_lib and program should not be used together
|
# test_program can be used with program or static_lib, but program and static_lib cannot be used together
|
||||||
all: program
|
all: program
|
||||||
|
|
||||||
clean: clean_object_files remove_binary_dir
|
clean: clean_object_files remove_binary_dir
|
||||||
|
@ -158,10 +153,6 @@ static_lib: $(SRC_OBJ) create_binary_dir
|
||||||
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME).a
|
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME).a
|
||||||
@ar $(ARFLAGS) "$(PROJECT_BIN_PATH)/$(PROJECT_NAME).a" $(SRC_OBJ)
|
@ar $(ARFLAGS) "$(PROJECT_BIN_PATH)/$(PROJECT_NAME).a" $(SRC_OBJ)
|
||||||
|
|
||||||
shared_lib: $(SRC_OBJ) create_binary_dir
|
|
||||||
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_SO_FILENAME)
|
|
||||||
@gcc -shared -Wl,-soname,$(PROJECT_SONAME) -o "$(PROJECT_BIN_PATH)/$(PROJECT_SO_FILENAME)" $(SRC_OBJ)
|
|
||||||
|
|
||||||
# Build Program
|
# Build Program
|
||||||
program: $(SRC_OBJ) create_binary_dir
|
program: $(SRC_OBJ) create_binary_dir
|
||||||
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME)
|
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME)
|
||||||
|
|
|
@ -104,14 +104,14 @@ void nstool::EsCertProcess::displayCert(const pie::hac::es::SignedData<pie::hac:
|
||||||
|
|
||||||
fmt::print(" SignType {:s}", getSignTypeStr(cert.getSignature().getSignType()));
|
fmt::print(" SignType {:s}", getSignTypeStr(cert.getSignature().getSignType()));
|
||||||
if (mCliOutputMode.show_extended_info)
|
if (mCliOutputMode.show_extended_info)
|
||||||
fmt::print(" (0x{:x}) ({:s})", cert.getSignature().getSignType(), getEndiannessStr(cert.getSignature().isLittleEndian()));
|
fmt::print(" (0x{:x}) ({:s})", (uint32_t)cert.getSignature().getSignType(), getEndiannessStr(cert.getSignature().isLittleEndian()));
|
||||||
fmt::print("\n");
|
fmt::print("\n");
|
||||||
|
|
||||||
fmt::print(" Issuer: {:s}\n", cert.getBody().getIssuer());
|
fmt::print(" Issuer: {:s}\n", cert.getBody().getIssuer());
|
||||||
fmt::print(" Subject: {:s}\n", cert.getBody().getSubject());
|
fmt::print(" Subject: {:s}\n", cert.getBody().getSubject());
|
||||||
fmt::print(" PublicKeyType: {:s}", getPublicKeyTypeStr(cert.getBody().getPublicKeyType()));
|
fmt::print(" PublicKeyType: {:s}", getPublicKeyTypeStr(cert.getBody().getPublicKeyType()));
|
||||||
if (mCliOutputMode.show_extended_info)
|
if (mCliOutputMode.show_extended_info)
|
||||||
fmt::print(" ({:d})", cert.getBody().getPublicKeyType());
|
fmt::print(" ({:d})", (uint32_t)cert.getBody().getPublicKeyType());
|
||||||
fmt::print("\n");
|
fmt::print("\n");
|
||||||
fmt::print(" CertID: 0x{:x}\n", cert.getBody().getCertId());
|
fmt::print(" CertID: 0x{:x}\n", cert.getBody().getCertId());
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,8 @@ void nstool::EsTikProcess::verifyTicket()
|
||||||
tc::crypto::GenerateSha1Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
|
tc::crypto::GenerateSha1Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
|
||||||
break;
|
break;
|
||||||
case (pie::hac::es::sign::HASH_ALGO_SHA256):
|
case (pie::hac::es::sign::HASH_ALGO_SHA256):
|
||||||
tik_hash = tc::ByteData(tc::crypto::Sha256Generator::kHashSize);
|
tik_hash = tc::ByteData(tc::crypto::Sha2256Generator::kHashSize);
|
||||||
tc::crypto::GenerateSha256Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
|
tc::crypto::GenerateSha2256Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ void nstool::EsTikProcess::displayTicket()
|
||||||
fmt::print("[ES Ticket]\n");
|
fmt::print("[ES Ticket]\n");
|
||||||
fmt::print(" SignType: {:s}", getSignTypeStr(mTik.getSignature().getSignType()));
|
fmt::print(" SignType: {:s}", getSignTypeStr(mTik.getSignature().getSignType()));
|
||||||
if (mCliOutputMode.show_extended_info)
|
if (mCliOutputMode.show_extended_info)
|
||||||
fmt::print(" (0x{:x})", mTik.getSignature().getSignType());
|
fmt::print(" (0x{:x})", (uint32_t)mTik.getSignature().getSignType());
|
||||||
fmt::print("\n");
|
fmt::print("\n");
|
||||||
|
|
||||||
fmt::print(" Issuer: {:s}\n", body.getIssuer());
|
fmt::print(" Issuer: {:s}\n", body.getIssuer());
|
||||||
|
|
|
@ -114,7 +114,7 @@ void nstool::GameCardProcess::importHeader()
|
||||||
pie::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (pie::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
|
pie::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (pie::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
|
||||||
|
|
||||||
// generate hash of raw header
|
// generate hash of raw header
|
||||||
tc::crypto::GenerateSha256Hash(mHdrHash.data(), (byte_t*)&hdr_ptr->header, sizeof(pie::hac::sGcHeader));
|
tc::crypto::GenerateSha2256Hash(mHdrHash.data(), (byte_t*)&hdr_ptr->header, sizeof(pie::hac::sGcHeader));
|
||||||
|
|
||||||
// save the signature
|
// save the signature
|
||||||
memcpy(mHdrSignature.data(), hdr_ptr->signature.data(), mHdrSignature.size());
|
memcpy(mHdrSignature.data(), hdr_ptr->signature.data(), mHdrSignature.size());
|
||||||
|
@ -225,7 +225,7 @@ bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len,
|
||||||
mFile->read(scratch.data(), scratch.size());
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
// update hash
|
// update hash
|
||||||
tc::crypto::Sha256Generator sha256_gen;
|
tc::crypto::Sha2256Generator sha256_gen;
|
||||||
sha256_gen.initialize();
|
sha256_gen.initialize();
|
||||||
sha256_gen.update(scratch.data(), scratch.size());
|
sha256_gen.update(scratch.data(), scratch.size());
|
||||||
if (use_salt)
|
if (use_salt)
|
||||||
|
@ -247,7 +247,7 @@ void nstool::GameCardProcess::validateXciSignature()
|
||||||
{
|
{
|
||||||
if (mKeyCfg.xci_header_sign_key.isSet())
|
if (mKeyCfg.xci_header_sign_key.isSet())
|
||||||
{
|
{
|
||||||
if (tc::crypto::VerifyRsa2048Pkcs1Sha256(mHdrSignature.data(), mHdrHash.data(), mKeyCfg.xci_header_sign_key.get()) == false)
|
if (tc::crypto::VerifyRsa2048Pkcs1Sha2256(mHdrSignature.data(), mHdrHash.data(), mKeyCfg.xci_header_sign_key.get()) == false)
|
||||||
{
|
{
|
||||||
fmt::print("[WARNING] GameCard Header Signature: FAIL\n");
|
fmt::print("[WARNING] GameCard Header Signature: FAIL\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>& nca_path)
|
||||||
|
{
|
||||||
|
mBaseNcaPath = nca_path;
|
||||||
|
}
|
||||||
|
|
||||||
void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg)
|
void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg)
|
||||||
{
|
{
|
||||||
mKeyCfg = keycfg;
|
mKeyCfg = keycfg;
|
||||||
|
@ -110,7 +116,7 @@ void nstool::NcaProcess::importHeader()
|
||||||
pie::hac::ContentArchiveUtil::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyCfg.nca_header_key.get());
|
pie::hac::ContentArchiveUtil::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyCfg.nca_header_key.get());
|
||||||
|
|
||||||
// generate header hash
|
// generate header hash
|
||||||
tc::crypto::GenerateSha256Hash(mHdrHash.data(), (byte_t*)&mHdrBlock.header, sizeof(pie::hac::sContentArchiveHeader));
|
tc::crypto::GenerateSha2256Hash(mHdrHash.data(), (byte_t*)&mHdrBlock.header, sizeof(pie::hac::sContentArchiveHeader));
|
||||||
|
|
||||||
// proccess main header
|
// proccess main header
|
||||||
mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(pie::hac::sContentArchiveHeader));
|
mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(pie::hac::sContentArchiveHeader));
|
||||||
|
@ -211,6 +217,32 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nstool::NcaProcess nstool::NcaProcess::readBaseNCA()
|
||||||
|
{
|
||||||
|
// open base nca stream
|
||||||
|
if (mBaseNcaPath.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(mBaseNcaPath.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read));
|
||||||
|
|
||||||
|
// process base nca with output suppressed
|
||||||
|
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 processed base nca
|
||||||
|
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++)
|
||||||
|
@ -224,7 +256,7 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
||||||
|
|
||||||
// validate header hash
|
// validate header hash
|
||||||
pie::hac::detail::sha256_hash_t fs_header_hash;
|
pie::hac::detail::sha256_hash_t fs_header_hash;
|
||||||
tc::crypto::GenerateSha256Hash(fs_header_hash.data(), (const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(pie::hac::sContentArchiveFsHeader));
|
tc::crypto::GenerateSha2256Hash(fs_header_hash.data(), (const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(pie::hac::sContentArchiveFsHeader));
|
||||||
if (fs_header_hash != partition.fs_header_hash)
|
if (fs_header_hash != partition.fs_header_hash)
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Hash: FAIL", partition.header_index));
|
throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Hash: FAIL", partition.header_index));
|
||||||
|
@ -266,13 +298,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)
|
||||||
{
|
{
|
||||||
|
@ -284,13 +316,49 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
||||||
|
|
||||||
// get partition counter
|
// get partition counter
|
||||||
pie::hac::detail::aes_iv_t partition_ctr = info.aes_ctr;
|
pie::hac::detail::aes_iv_t partition_ctr = info.aes_ctr;
|
||||||
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);
|
||||||
|
|
||||||
|
// TODO see if AesCtrEx encryption can just be for creating the transparent decryption, with IndirectStorage IStream construction being done after decryption but before hash layer processing
|
||||||
|
// this might be relevant when processing compressed or sparse storage
|
||||||
|
|
||||||
|
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)));
|
||||||
}
|
}
|
||||||
|
@ -304,12 +372,14 @@ void nstool::NcaProcess::generatePartitionConfiguration()
|
||||||
switch (info.hash_type)
|
switch (info.hash_type)
|
||||||
{
|
{
|
||||||
case (pie::hac::nca::HashType_None):
|
case (pie::hac::nca::HashType_None):
|
||||||
|
// no hash layer, do nothing
|
||||||
|
info.reader = info.decrypt_reader;
|
||||||
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)));
|
||||||
|
@ -342,7 +412,7 @@ void nstool::NcaProcess::validateNcaSignatures()
|
||||||
// validate signature[0]
|
// validate signature[0]
|
||||||
if (mKeyCfg.nca_header_sign0_key.find(mHdr.getSignatureKeyGeneration()) != mKeyCfg.nca_header_sign0_key.end())
|
if (mKeyCfg.nca_header_sign0_key.find(mHdr.getSignatureKeyGeneration()) != mKeyCfg.nca_header_sign0_key.end())
|
||||||
{
|
{
|
||||||
if (tc::crypto::VerifyRsa2048PssSha256(mHdrBlock.signature_main.data(), mHdrHash.data(), mKeyCfg.nca_header_sign0_key[mHdr.getSignatureKeyGeneration()]) == false)
|
if (tc::crypto::VerifyRsa2048PssSha2256(mHdrBlock.signature_main.data(), mHdrHash.data(), mKeyCfg.nca_header_sign0_key[mHdr.getSignatureKeyGeneration()]) == false)
|
||||||
{
|
{
|
||||||
fmt::print("[WARNING] NCA Header Main Signature: FAIL\n");
|
fmt::print("[WARNING] NCA Header Main Signature: FAIL\n");
|
||||||
}
|
}
|
||||||
|
@ -376,7 +446,7 @@ void nstool::NcaProcess::validateNcaSignatures()
|
||||||
npdm.setCliOutputMode(CliOutputMode(false, false, false, false));
|
npdm.setCliOutputMode(CliOutputMode(false, false, false, false));
|
||||||
npdm.process();
|
npdm.process();
|
||||||
|
|
||||||
if (tc::crypto::VerifyRsa2048PssSha256(mHdrBlock.signature_acid.data(), mHdrHash.data(), npdm.getMeta().getAccessControlInfoDesc().getContentArchiveHeaderSignature2Key()) == false)
|
if (tc::crypto::VerifyRsa2048PssSha2256(mHdrBlock.signature_acid.data(), mHdrHash.data(), npdm.getMeta().getAccessControlInfoDesc().getContentArchiveHeaderSignature2Key()) == false)
|
||||||
{
|
{
|
||||||
throw tc::Exception("Bad signature");
|
throw tc::Exception("Bad signature");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>& nca_path);
|
||||||
|
|
||||||
|
|
||||||
// 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> mBaseNcaPath;
|
||||||
|
|
||||||
// fs processing
|
// fs processing
|
||||||
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
|
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
|
||||||
|
@ -93,7 +96,9 @@ private:
|
||||||
// raw partition data
|
// raw partition data
|
||||||
struct sPartitionInfo
|
struct sPartitionInfo
|
||||||
{
|
{
|
||||||
std::shared_ptr<tc::io::IStream> reader;
|
std::shared_ptr<tc::io::IStream> raw_reader; // raw unprocessed partition stream
|
||||||
|
std::shared_ptr<tc::io::IStream> decrypt_reader; // partition stream with transparent decryption
|
||||||
|
std::shared_ptr<tc::io::IStream> reader; // partition stream with transparent decryption & hash layer processing
|
||||||
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;
|
||||||
std::string fail_reason;
|
std::string fail_reason;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ void nstool::NsoProcess::importCodeSegments()
|
||||||
}
|
}
|
||||||
if (mHdr.getTextSegmentInfo().is_hashed)
|
if (mHdr.getTextSegmentInfo().is_hashed)
|
||||||
{
|
{
|
||||||
tc::crypto::GenerateSha256Hash(calc_hash.data(), mTextBlob.data(), mTextBlob.size());
|
tc::crypto::GenerateSha2256Hash(calc_hash.data(), mTextBlob.data(), mTextBlob.size());
|
||||||
if (calc_hash != mHdr.getTextSegmentInfo().hash)
|
if (calc_hash != mHdr.getTextSegmentInfo().hash)
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, "NSO text segment failed SHA256 verification");
|
throw tc::Exception(mModuleName, "NSO text segment failed SHA256 verification");
|
||||||
|
@ -149,7 +149,7 @@ void nstool::NsoProcess::importCodeSegments()
|
||||||
}
|
}
|
||||||
if (mHdr.getRoSegmentInfo().is_hashed)
|
if (mHdr.getRoSegmentInfo().is_hashed)
|
||||||
{
|
{
|
||||||
tc::crypto::GenerateSha256Hash(calc_hash.data(), mRoBlob.data(), mRoBlob.size());
|
tc::crypto::GenerateSha2256Hash(calc_hash.data(), mRoBlob.data(), mRoBlob.size());
|
||||||
if (calc_hash != mHdr.getRoSegmentInfo().hash)
|
if (calc_hash != mHdr.getRoSegmentInfo().hash)
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, "NSO ro segment failed SHA256 verification");
|
throw tc::Exception(mModuleName, "NSO ro segment failed SHA256 verification");
|
||||||
|
@ -182,7 +182,7 @@ void nstool::NsoProcess::importCodeSegments()
|
||||||
}
|
}
|
||||||
if (mHdr.getDataSegmentInfo().is_hashed)
|
if (mHdr.getDataSegmentInfo().is_hashed)
|
||||||
{
|
{
|
||||||
tc::crypto::GenerateSha256Hash(calc_hash.data(), mDataBlob.data(), mDataBlob.size());
|
tc::crypto::GenerateSha2256Hash(calc_hash.data(), mDataBlob.data(), mDataBlob.size());
|
||||||
if (calc_hash != mHdr.getDataSegmentInfo().hash)
|
if (calc_hash != mHdr.getDataSegmentInfo().hash)
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, "NSO data segment failed SHA256 verification");
|
throw tc::Exception(mModuleName, "NSO data segment failed SHA256 verification");
|
||||||
|
|
|
@ -62,8 +62,8 @@ void nstool::PkiValidator::addCertificate(const pie::hac::es::SignedData<pie::ha
|
||||||
tc::crypto::GenerateSha1Hash(cert_hash.data(), cert.getBody().getBytes().data(), cert.getBody().getBytes().size());
|
tc::crypto::GenerateSha1Hash(cert_hash.data(), cert.getBody().getBytes().data(), cert.getBody().getBytes().size());
|
||||||
break;
|
break;
|
||||||
case (pie::hac::es::sign::HASH_ALGO_SHA256):
|
case (pie::hac::es::sign::HASH_ALGO_SHA256):
|
||||||
cert_hash = tc::ByteData(tc::crypto::Sha256Generator::kHashSize);
|
cert_hash = tc::ByteData(tc::crypto::Sha2256Generator::kHashSize);
|
||||||
tc::crypto::GenerateSha256Hash(cert_hash.data(), cert.getBody().getBytes().data(), cert.getBody().getBytes().size());
|
tc::crypto::GenerateSha2256Hash(cert_hash.data(), cert.getBody().getBytes().data(), cert.getBody().getBytes().size());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw tc::Exception(mModuleName, "Unrecognised hash type");
|
throw tc::Exception(mModuleName, "Unrecognised hash type");
|
||||||
|
@ -154,10 +154,10 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, pie::hac
|
||||||
sig_valid = false;
|
sig_valid = false;
|
||||||
break;
|
break;
|
||||||
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA256):
|
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA256):
|
||||||
sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha256(signature.data(), hash.data(), rsa_key);
|
sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha2256(signature.data(), hash.data(), rsa_key);
|
||||||
break;
|
break;
|
||||||
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA256):
|
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA256):
|
||||||
sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha256(signature.data(), hash.data(), rsa_key);
|
sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha2256(signature.data(), hash.data(), rsa_key);
|
||||||
break;
|
break;
|
||||||
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA256):
|
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA256):
|
||||||
sig_valid = false;
|
sig_valid = false;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
#define APP_NAME "NSTool"
|
#define APP_NAME "NSTool"
|
||||||
#define BIN_NAME "nstool"
|
#define BIN_NAME "nstool"
|
||||||
#define VER_MAJOR 1
|
#define VER_MAJOR 1
|
||||||
#define VER_MINOR 6
|
#define VER_MINOR 7
|
||||||
#define VER_PATCH 6
|
#define VER_PATCH 0
|
||||||
#define AUTHORS "jakcron"
|
#define AUTHORS "jakcron"
|
Loading…
Reference in a new issue