mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
Move some files around to test each process as it's ported.
This commit is contained in:
parent
258173915c
commit
09e6b8a857
30 changed files with 258 additions and 248 deletions
|
@ -35,7 +35,7 @@ void nstool::AssetProcess::setVerifyMode(bool verify)
|
||||||
|
|
||||||
void nstool::AssetProcess::setListFs(bool list)
|
void nstool::AssetProcess::setListFs(bool list)
|
||||||
{
|
{
|
||||||
mRomfs.setListFs(list);
|
//mRomfs.setListFs(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path)
|
void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path)
|
||||||
|
@ -50,7 +50,7 @@ void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path)
|
||||||
|
|
||||||
void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path)
|
void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path)
|
||||||
{
|
{
|
||||||
mRomfs.setExtractPath(path);
|
//mRomfs.setExtractPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,11 +95,11 @@ void nstool::AssetProcess::processSections()
|
||||||
writeSubStreamToFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size, mNacpExtractPath.get());
|
writeSubStreamToFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size, mNacpExtractPath.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
mNacp.setInputFile(std::make_shared<tc::io::SubStream>(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size));
|
//mNacp.setInputFile(std::make_shared<tc::io::SubStream>(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size));
|
||||||
mNacp.setCliOutputMode(mCliOutputMode);
|
//mNacp.setCliOutputMode(mCliOutputMode);
|
||||||
mNacp.setVerifyMode(mVerify);
|
//mNacp.setVerifyMode(mVerify);
|
||||||
|
|
||||||
mNacp.process();
|
//mNacp.process();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mHdr.getRomfsInfo().size > 0)
|
if (mHdr.getRomfsInfo().size > 0)
|
||||||
|
@ -107,11 +107,11 @@ void nstool::AssetProcess::processSections()
|
||||||
if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size)
|
if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size)
|
||||||
throw tc::Exception(mModuleName, "ASET geometry for romfs beyond file size");
|
throw tc::Exception(mModuleName, "ASET geometry for romfs beyond file size");
|
||||||
|
|
||||||
mRomfs.setInputFile(std::make_shared<tc::io::SubStream>(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size));
|
//mRomfs.setInputFile(std::make_shared<tc::io::SubStream>(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size));
|
||||||
mRomfs.setCliOutputMode(mCliOutputMode);
|
//mRomfs.setCliOutputMode(mCliOutputMode);
|
||||||
mRomfs.setVerifyMode(mVerify);
|
//mRomfs.setVerifyMode(mVerify);
|
||||||
|
|
||||||
mRomfs.process();
|
//mRomfs.process();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,8 @@ private:
|
||||||
tc::Optional<tc::io::Path> mNacpExtractPath;
|
tc::Optional<tc::io::Path> mNacpExtractPath;
|
||||||
|
|
||||||
nn::hac::AssetHeader mHdr;
|
nn::hac::AssetHeader mHdr;
|
||||||
NacpProcess mNacp;
|
//NacpProcess mNacp;
|
||||||
RomfsProcess mRomfs;
|
//RomfsProcess mRomfs;
|
||||||
|
|
||||||
void importHeader();
|
void importHeader();
|
||||||
void processSections();
|
void processSections();
|
||||||
|
|
|
@ -45,7 +45,6 @@ void nstool::CnmtProcess::importCnmt()
|
||||||
throw tc::Exception(mModuleName, "No file reader set.");
|
throw tc::Exception(mModuleName, "No file reader set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check if file_size is greater than 20MB, don't import.
|
// check if file_size is greater than 20MB, don't import.
|
||||||
size_t cnmt_file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
|
size_t cnmt_file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
|
||||||
if (cnmt_file_size > (0x100000 * 20))
|
if (cnmt_file_size > (0x100000 * 20))
|
||||||
|
@ -68,7 +67,7 @@ void nstool::CnmtProcess::displayCnmt()
|
||||||
fmt::print("[ContentMeta]\n");
|
fmt::print("[ContentMeta]\n");
|
||||||
fmt::print(" TitleId: 0x{:016x}\n", mCnmt.getTitleId());
|
fmt::print(" TitleId: 0x{:016x}\n", mCnmt.getTitleId());
|
||||||
fmt::print(" Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()), mCnmt.getTitleVersion());
|
fmt::print(" Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()), mCnmt.getTitleVersion());
|
||||||
fmt::print(" Type: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()), (uint32_t)mCnmt.getContentMetaType());
|
fmt::print(" Type: {:s} ({:d})\n", nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()), (uint32_t)mCnmt.getContentMetaType());
|
||||||
fmt::print(" Attributes: 0x{:x}", *((byte_t*)&cnmt_hdr->attributes));
|
fmt::print(" Attributes: 0x{:x}", *((byte_t*)&cnmt_hdr->attributes));
|
||||||
if (mCnmt.getAttribute().size())
|
if (mCnmt.getAttribute().size())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <fnd/SimpleTextOutput.h>
|
|
||||||
#include <fnd/OffsetAdjustedIFile.h>
|
|
||||||
#include <nn/pki/SignUtils.h>
|
|
||||||
#include "EsTikProcess.h"
|
#include "EsTikProcess.h"
|
||||||
#include "PkiValidator.h"
|
#include "PkiValidator.h"
|
||||||
|
|
||||||
|
#include <nn/pki/SignUtils.h>
|
||||||
|
|
||||||
nstool::EsTikProcess::EsTikProcess() :
|
nstool::EsTikProcess::EsTikProcess() :
|
||||||
|
mModuleName("nstool::EsTikProcess"),
|
||||||
mFile(),
|
mFile(),
|
||||||
mCliOutputMode(true, false, false, false),
|
mCliOutputMode(true, false, false, false),
|
||||||
mVerify(false)
|
mVerify(false)
|
||||||
|
@ -53,16 +49,23 @@ void nstool::EsTikProcess::setVerifyMode(bool verify)
|
||||||
|
|
||||||
void nstool::EsTikProcess::importTicket()
|
void nstool::EsTikProcess::importTicket()
|
||||||
{
|
{
|
||||||
tc::ByteData scratch;
|
if (mFile == nullptr)
|
||||||
|
|
||||||
|
|
||||||
if (*mFile == nullptr)
|
|
||||||
{
|
{
|
||||||
throw tc::Exception(kModuleName, "No file reader set.");
|
throw tc::Exception(mModuleName, "No file reader set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch.alloc((*mFile)->size());
|
// check if file_size is greater than 20MB, don't import.
|
||||||
(*mFile)->read(scratch.data(), 0, scratch.size());
|
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
|
||||||
|
if (file_size > (0x100000 * 20))
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "File too large.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read cnmt
|
||||||
|
tc::ByteData scratch = tc::ByteData(file_size);
|
||||||
|
mFile->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
mTik.fromBytes(scratch.data(), scratch.size());
|
mTik.fromBytes(scratch.data(), scratch.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,12 +77,12 @@ void nstool::EsTikProcess::verifyTicket()
|
||||||
switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType()))
|
switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType()))
|
||||||
{
|
{
|
||||||
case (nn::pki::sign::HASH_ALGO_SHA1):
|
case (nn::pki::sign::HASH_ALGO_SHA1):
|
||||||
tik_hash.alloc(fnd::sha::kSha1HashLen);
|
tik_hash = tc::ByteData(tc::crypto::Sha1Generator::kHashSize);
|
||||||
fnd::sha::Sha1(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data());
|
tc::crypto::GenerateSha1Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
|
||||||
break;
|
break;
|
||||||
case (nn::pki::sign::HASH_ALGO_SHA256):
|
case (nn::pki::sign::HASH_ALGO_SHA256):
|
||||||
tik_hash.alloc(fnd::sha::kSha256HashLen);
|
tik_hash = tc::ByteData(tc::crypto::Sha256Generator::kHashSize);
|
||||||
fnd::sha::Sha256(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data());
|
tc::crypto::GenerateSha256Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,85 +94,73 @@ void nstool::EsTikProcess::verifyTicket()
|
||||||
}
|
}
|
||||||
catch (const tc::Exception& e)
|
catch (const tc::Exception& e)
|
||||||
{
|
{
|
||||||
std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl;
|
fmt::print("[WARNING] Ticket signature could not be validated ({:s})\n", e.error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::EsTikProcess::displayTicket()
|
void nstool::EsTikProcess::displayTicket()
|
||||||
{
|
{
|
||||||
#define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf)
|
|
||||||
|
|
||||||
const nn::es::TicketBody_V2& body = mTik.getBody();
|
const nn::es::TicketBody_V2& body = mTik.getBody();
|
||||||
|
|
||||||
std::cout << "[ES Ticket]" << std::endl;
|
fmt::print("[ES Ticket]\n");
|
||||||
|
fmt::print(" SignType: {:s}", getSignTypeStr(mTik.getSignature().getSignType()));
|
||||||
std::cout << " SignType: " << getSignTypeStr(mTik.getSignature().getSignType());
|
|
||||||
if (mCliOutputMode.show_extended_info)
|
if (mCliOutputMode.show_extended_info)
|
||||||
std::cout << " (0x" << std::hex << mTik.getSignature().getSignType() << ")";
|
fmt::print(" (0x{:x})", mTik.getSignature().getSignType());
|
||||||
std::cout << std::endl;
|
fmt::print("\n");
|
||||||
|
|
||||||
std::cout << " Issuer: " << body.getIssuer() << std::endl;
|
fmt::print(" Issuer: {:s}\n", body.getIssuer());
|
||||||
std::cout << " Title Key:" << std::endl;
|
fmt::print(" Title Key:\n");
|
||||||
std::cout << " EncMode: " << getTitleKeyPersonalisationStr(body.getTitleKeyEncType()) << std::endl;
|
fmt::print(" EncMode: {:s}\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType()));
|
||||||
std::cout << " KeyGeneration: " << std::dec << (uint32_t)body.getCommonKeyId() << std::endl;
|
fmt::print(" KeyGeneration: {:d}\n", (uint32_t)body.getCommonKeyId());
|
||||||
if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048)
|
if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048)
|
||||||
{
|
{
|
||||||
std::cout << " Data:" << std::endl;
|
fmt::print(" Data:\n");
|
||||||
for (size_t i = 0; i < 0x10; i++)
|
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, ":", 0x10, 6, false));
|
||||||
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey() + 0x10*i, 0x10, true, ":") << std::endl;
|
|
||||||
}
|
}
|
||||||
else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
|
else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
|
||||||
{
|
{
|
||||||
std::cout << " Data:" << std::endl;
|
fmt::print(" Data:\n");
|
||||||
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey(), 0x10, true, ":") << std::endl;
|
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getEncTitleKey(), 0x10, true, ":"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << " Data: <cannot display>" << std::endl;
|
fmt::print(" Data: <cannot display>\n");
|
||||||
}
|
}
|
||||||
|
fmt::print(" Version: {:s} (v{:d})\n", getTitleVersionStr(body.getTicketVersion()), body.getTicketVersion());
|
||||||
std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion());
|
fmt::print(" License Type: {:s}\n", getLicenseTypeStr(body.getLicenseType()));
|
||||||
if (mCliOutputMode.show_extended_info)
|
|
||||||
std::cout << " (" << (uint32_t)body.getTicketVersion() << ")";
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl;
|
|
||||||
|
|
||||||
if (body.getPropertyFlags().size() > 0)
|
if (body.getPropertyFlags().size() > 0)
|
||||||
{
|
{
|
||||||
std::cout << " Flags:" << std::endl;
|
nn::es::sTicketBody_v2* raw_body = (nn::es::sTicketBody_v2*)body.getBytes().data();
|
||||||
|
fmt::print(" PropertyMask: 0x{:04x}\n", ((tc::bn::le16<uint16_t>*)&raw_body->property_mask)->unwrap());
|
||||||
for (size_t i = 0; i < body.getPropertyFlags().size(); i++)
|
for (size_t i = 0; i < body.getPropertyFlags().size(); i++)
|
||||||
{
|
{
|
||||||
std::cout << " " << getPropertyFlagStr(body.getPropertyFlags()[i]) << std::endl;
|
fmt::print(" {:s}\n", getPropertyFlagStr(body.getPropertyFlags()[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCliOutputMode.show_extended_info)
|
if (mCliOutputMode.show_extended_info)
|
||||||
{
|
{
|
||||||
std::cout << " Reserved Region:" << std::endl;
|
fmt::print(" Reserved Region:\n");
|
||||||
fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4);
|
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getReservedRegion(), 8, true, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.getTicketId() != 0 || mCliOutputMode.show_extended_info)
|
if (body.getTicketId() != 0 || mCliOutputMode.show_extended_info)
|
||||||
std::cout << " TicketId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getTicketId() << std::endl;
|
fmt::print(" TicketId: 0x{:016x}\n", body.getTicketId());
|
||||||
|
|
||||||
if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info)
|
if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info)
|
||||||
std::cout << " DeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getDeviceId() << std::endl;
|
fmt::print(" DeviceId: 0x{:016x}\n", body.getDeviceId());
|
||||||
|
|
||||||
std::cout << " RightsId: " << std::endl << " ";
|
fmt::print(" RightsId: \n");
|
||||||
fnd::SimpleTextOutput::hexDump(body.getRightsId(), 16);
|
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getRightsId(), 16, true, ""));
|
||||||
|
|
||||||
std::cout << " SectionTotalSize: 0x" << std::hex << body.getSectionTotalSize() << std::endl;
|
fmt::print(" SectionTotalSize: 0x{:x}\n", body.getSectionTotalSize());
|
||||||
std::cout << " SectionHeaderOffset: 0x" << std::hex << body.getSectionHeaderOffset() << std::endl;
|
fmt::print(" SectionHeaderOffset: 0x{:x}\n", body.getSectionHeaderOffset());
|
||||||
std::cout << " SectionNum: 0x" << std::hex << body.getSectionNum() << std::endl;
|
fmt::print(" SectionNum: 0x{:x}\n", body.getSectionNum());
|
||||||
std::cout << " SectionEntrySize: 0x" << std::hex << body.getSectionEntrySize() << std::endl;
|
fmt::print(" SectionEntrySize: 0x{:x}\n", body.getSectionEntrySize());
|
||||||
|
|
||||||
#undef _SPLIT_VER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* nstool::EsTikProcess::getSignTypeStr(uint32_t type) const
|
std::string nstool::EsTikProcess::getSignTypeStr(uint32_t type) const
|
||||||
{
|
{
|
||||||
const char* str = nullptr;
|
std::string str;
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case (nn::pki::sign::SIGN_ID_RSA4096_SHA1):
|
case (nn::pki::sign::SIGN_ID_RSA4096_SHA1):
|
||||||
|
@ -197,9 +188,9 @@ const char* nstool::EsTikProcess::getSignTypeStr(uint32_t type) const
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
|
std::string nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
|
||||||
{
|
{
|
||||||
const char* str = nullptr;
|
std::string str;
|
||||||
switch(flag)
|
switch(flag)
|
||||||
{
|
{
|
||||||
case (nn::es::ticket::AES128_CBC):
|
case (nn::es::ticket::AES128_CBC):
|
||||||
|
@ -209,15 +200,15 @@ const char* nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) con
|
||||||
str = "Personalised (RSA2048)";
|
str = "Personalised (RSA2048)";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
str = "Unknown";
|
str = fmt::format("Unknown ({:d})", flag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const
|
std::string nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const
|
||||||
{
|
{
|
||||||
const char* str = nullptr;
|
std::string str;
|
||||||
switch(flag)
|
switch(flag)
|
||||||
{
|
{
|
||||||
case (nn::es::ticket::LICENSE_PERMANENT):
|
case (nn::es::ticket::LICENSE_PERMANENT):
|
||||||
|
@ -239,15 +230,15 @@ const char* nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const
|
||||||
str = "Service";
|
str = "Service";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
str = "Unknown";
|
str = fmt::format("Unknown ({:d})", flag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const
|
std::string nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const
|
||||||
{
|
{
|
||||||
const char* str = nullptr;
|
std::string str;
|
||||||
switch(flag)
|
switch(flag)
|
||||||
{
|
{
|
||||||
case (nn::es::ticket::FLAG_PRE_INSTALL):
|
case (nn::es::ticket::FLAG_PRE_INSTALL):
|
||||||
|
@ -259,9 +250,23 @@ const char* nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const
|
||||||
case (nn::es::ticket::FLAG_ALLOW_ALL_CONTENT):
|
case (nn::es::ticket::FLAG_ALLOW_ALL_CONTENT):
|
||||||
str = "AllContent";
|
str = "AllContent";
|
||||||
break;
|
break;
|
||||||
|
case (nn::es::ticket::FLAG_DEVICE_LINK_INDEPENDENT):
|
||||||
|
str = "DeviceLinkIndependent";
|
||||||
|
break;
|
||||||
|
case (nn::es::ticket::FLAG_VOLATILE):
|
||||||
|
str = "Volatile";
|
||||||
|
break;
|
||||||
|
case (nn::es::ticket::FLAG_ELICENSE_REQUIRED):
|
||||||
|
str = "ELicenseRequired";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
str = "Unknown";
|
str = fmt::format("Unknown ({:d})", flag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string nstool::EsTikProcess::getTitleVersionStr(uint16_t version) const
|
||||||
|
{
|
||||||
|
return fmt::format("{:d}.{:d}.{:d}", ((version>>10) & 0x3f), ((version>>4) & 0x3f), ((version>>0) & 0xf));
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ public:
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "EsTikProcess";
|
std::string mModuleName;
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStream> mFile;
|
std::shared_ptr<tc::io::IStream> mFile;
|
||||||
KeyBag mKeyCfg;
|
KeyBag mKeyCfg;
|
||||||
|
@ -36,10 +36,11 @@ private:
|
||||||
void importTicket();
|
void importTicket();
|
||||||
void verifyTicket();
|
void verifyTicket();
|
||||||
void displayTicket();
|
void displayTicket();
|
||||||
const char* getSignTypeStr(uint32_t type) const;
|
std::string getSignTypeStr(uint32_t type) const;
|
||||||
const char* getTitleKeyPersonalisationStr(byte_t flag) const;
|
std::string getTitleKeyPersonalisationStr(byte_t flag) const;
|
||||||
const char* getLicenseTypeStr(byte_t flag) const;
|
std::string getLicenseTypeStr(byte_t flag) const;
|
||||||
const char* getPropertyFlagStr(byte_t flag) const;
|
std::string getPropertyFlagStr(byte_t flag) const;
|
||||||
|
std::string getTitleVersionStr(uint16_t version) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@ public:
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
// xci specific
|
// xci specific
|
||||||
void setPartitionForExtract(const std::string& partition_name, const std::string& extract_path);
|
void setPartitionForExtract(const std::string& partition_name, const tc::io::Path& extract_path);
|
||||||
void setListFs(bool list_fs);
|
void setListFs(bool list_fs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -37,7 +37,7 @@ private:
|
||||||
struct sExtractInfo
|
struct sExtractInfo
|
||||||
{
|
{
|
||||||
std::string partition_name;
|
std::string partition_name;
|
||||||
std::string extract_path;
|
tc::io::Path extract_path;
|
||||||
|
|
||||||
void operator=(const sExtractInfo& other)
|
void operator=(const sExtractInfo& other)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,7 @@ private:
|
||||||
extract_path = other.extract_path;
|
extract_path = other.extract_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const std::string& name) const
|
bool operator==(const tc::io::Path& name) const
|
||||||
{
|
{
|
||||||
return name == partition_name;
|
return name == partition_name;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ private:
|
||||||
bool mIsSdkXciEncrypted;
|
bool mIsSdkXciEncrypted;
|
||||||
size_t mGcHeaderOffset;
|
size_t mGcHeaderOffset;
|
||||||
bool mProccessExtendedHeader;
|
bool mProccessExtendedHeader;
|
||||||
byte_t mHdrSignature[fnd::rsa::kRsa2048Size];
|
nn::hac::detail::rsa2048_signature_t mHdrSignature;
|
||||||
fnd::sha::sSha256Hash mHdrHash;
|
fnd::sha::sSha256Hash mHdrHash;
|
||||||
nn::hac::GameCardHeader mHdr;
|
nn::hac::GameCardHeader mHdr;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ public:
|
||||||
void setCliOutputMode(CliOutputMode type);
|
void setCliOutputMode(CliOutputMode type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
void setKipExtractPath(const std::string& path);
|
void setKipExtractPath(const tc::io::Path& path);
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "IniProcess";
|
const std::string kModuleName = "IniProcess";
|
||||||
const std::string kKipExtention = ".kip";
|
const std::string kKipExtention = ".kip";
|
||||||
|
@ -27,8 +27,7 @@ private:
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
|
||||||
bool mDoExtractKip;
|
tc::Optional<tc::io::Path> mKipExtractPath;
|
||||||
std::string mKipExtractPath;
|
|
||||||
|
|
||||||
nn::hac::IniHeader mHdr;
|
nn::hac::IniHeader mHdr;
|
||||||
std::vector<std::shared_ptr<tc::io::IStream>> mKipList;
|
std::vector<std::shared_ptr<tc::io::IStream>> mKipList;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <tc/Optional.h>
|
#include <tc/Optional.h>
|
||||||
#include <tc/io.h>
|
#include <tc/io.h>
|
||||||
|
#include <nn/pki/SignUtils.h>
|
||||||
#include <nn/hac/define/types.h>
|
#include <nn/hac/define/types.h>
|
||||||
#include <nn/hac/define/nca.h>
|
#include <nn/hac/define/nca.h>
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ public:
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
// nca specfic
|
// nca specfic
|
||||||
void setPartition0ExtractPath(const std::string& path);
|
void setPartition0ExtractPath(const tc::io::Path& path);
|
||||||
void setPartition1ExtractPath(const std::string& path);
|
void setPartition1ExtractPath(const tc::io::Path& path);
|
||||||
void setPartition2ExtractPath(const std::string& path);
|
void setPartition2ExtractPath(const tc::io::Path& path);
|
||||||
void setPartition3ExtractPath(const std::string& path);
|
void setPartition3ExtractPath(const tc::io::Path& path);
|
||||||
void setListFs(bool list_fs);
|
void setListFs(bool list_fs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -36,11 +36,7 @@ private:
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
|
||||||
struct sExtract
|
std::array<tc::Optional<tc::io::Path>, nn::hac::nca::kPartitionNum> mPartitionPath;
|
||||||
{
|
|
||||||
std::string path;
|
|
||||||
bool doExtract;
|
|
||||||
} mPartitionPath[nn::hac::nca::kPartitionNum];
|
|
||||||
|
|
||||||
bool mListFs;
|
bool mListFs;
|
||||||
|
|
||||||
|
@ -82,23 +78,25 @@ private:
|
||||||
};
|
};
|
||||||
std::vector<sKeyAreaKey> kak_list;
|
std::vector<sKeyAreaKey> kak_list;
|
||||||
|
|
||||||
tc::Optional<KeyBag::aes128_key_t> aes_ctr;
|
tc::Optional<nn::hac::detail::aes128_key_t> aes_ctr;
|
||||||
} mContentKey;
|
} mContentKey;
|
||||||
|
|
||||||
struct sPartitionInfo
|
struct sPartitionInfo
|
||||||
{
|
{
|
||||||
std::shared_ptr<tc::io::IStream> reader;
|
std::shared_ptr<tc::io::IStream> reader;
|
||||||
std::string fail_reason;
|
std::string fail_reason;
|
||||||
size_t offset;
|
int64_t offset;
|
||||||
size_t size;
|
int64_t size;
|
||||||
|
|
||||||
// meta data
|
// meta data
|
||||||
nn::hac::nca::FormatType format_type;
|
nn::hac::nca::FormatType format_type;
|
||||||
nn::hac::nca::HashType hash_type;
|
nn::hac::nca::HashType hash_type;
|
||||||
nn::hac::nca::EncryptionType enc_type;
|
nn::hac::nca::EncryptionType enc_type;
|
||||||
fnd::LayeredIntegrityMetadata layered_intergrity_metadata;
|
//fnd::LayeredIntegrityMetadata layered_intergrity_metadata;
|
||||||
fnd::aes::sAesIvCtr aes_ctr;
|
nn::hac::detail::aes_iv_t aes_ctr;
|
||||||
} mPartitions[nn::hac::nca::kPartitionNum];
|
}
|
||||||
|
|
||||||
|
std::array<sPartitionInfo, nn::hac::nca::kPartitionNum> mPartitions;
|
||||||
|
|
||||||
void importHeader();
|
void importHeader();
|
||||||
void generateNcaBodyEncryptionKeys();
|
void generateNcaBodyEncryptionKeys();
|
||||||
|
|
|
@ -25,9 +25,9 @@ public:
|
||||||
|
|
||||||
// for homebrew NROs with Asset blobs appended
|
// for homebrew NROs with Asset blobs appended
|
||||||
void setAssetListFs(bool list);
|
void setAssetListFs(bool list);
|
||||||
void setAssetIconExtractPath(const std::string& path);
|
void setAssetIconExtractPath(const tc::io::Path& path);
|
||||||
void setAssetNacpExtractPath(const std::string& path);
|
void setAssetNacpExtractPath(const tc::io::Path& path);
|
||||||
void setAssetRomfsExtractPath(const std::string& path);
|
void setAssetRomfsExtractPath(const tc::io::Path& path);
|
||||||
|
|
||||||
const RoMetadataProcess& getRoMetadataProcess() const;
|
const RoMetadataProcess& getRoMetadataProcess() const;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
|
|
||||||
// pfs specific
|
// pfs specific
|
||||||
void setMountPointName(const std::string& mount_name);
|
void setMountPointName(const std::string& mount_name);
|
||||||
void setExtractPath(const std::string& path);
|
void setExtractPath(const tc::io::Path& path);
|
||||||
void setListFs(bool list_fs);
|
void setListFs(bool list_fs);
|
||||||
|
|
||||||
const nn::hac::PartitionFsHeader& getPfsHeader() const;
|
const nn::hac::PartitionFsHeader& getPfsHeader() const;
|
||||||
|
@ -32,8 +32,7 @@ private:
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
|
||||||
std::string mExtractPath;
|
tc::Optional<tc::io::Path> mExtractPath;
|
||||||
bool mExtract;
|
|
||||||
std::string mMountName;
|
std::string mMountName;
|
||||||
bool mListFs;
|
bool mListFs;
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ void nstool::PkiValidator::addCertificate(const nn::pki::SignedData<nn::pki::Cer
|
||||||
}
|
}
|
||||||
catch (const tc::Exception& e)
|
catch (const tc::Exception& e)
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, fmt::format("Failed to add certificate {:s} ({:s})", cert_ident, e.error());
|
throw tc::Exception(mModuleName, fmt::format("Failed to add certificate {:s} ({:s})", cert_ident, e.error()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +89,6 @@ void nstool::PkiValidator::clearCertificates()
|
||||||
void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const
|
void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const
|
||||||
{
|
{
|
||||||
nn::pki::sign::SignatureAlgo sign_algo = nn::pki::sign::getSignatureAlgo(signature_id);
|
nn::pki::sign::SignatureAlgo sign_algo = nn::pki::sign::getSignatureAlgo(signature_id);
|
||||||
nn::pki::sign::HashAlgo hash_algo = nn::pki::sign::getHashAlgo(signature_id);
|
|
||||||
|
|
||||||
|
|
||||||
// validate signature
|
// validate signature
|
||||||
bool sig_valid = false;
|
bool sig_valid = false;
|
||||||
|
@ -106,12 +104,12 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki:
|
||||||
|
|
||||||
if (itr == mKeyCfg.broadon_signer.end())
|
if (itr == mKeyCfg.broadon_signer.end())
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, fmt::print("Public key for issuer \"{:s}\" does not exist.", issuer);
|
throw tc::Exception(mModuleName, fmt::format("Public key for issuer \"{:s}\" does not exist.", issuer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign_algo != itr->second.key_type)
|
if (sign_algo != itr->second.key_type)
|
||||||
{
|
{
|
||||||
throw tc::Exception(mModuleName, fmt::print("Public key for issuer \"{:s}\" cannot verify this signature.", issuer);
|
throw tc::Exception(mModuleName, fmt::format("Public key for issuer \"{:s}\" cannot verify this signature.", issuer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
|
if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
|
||||||
|
@ -129,7 +127,7 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki:
|
||||||
|
|
||||||
if (issuer_pubk_type == nn::pki::cert::RSA4096 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
|
if (issuer_pubk_type == nn::pki::cert::RSA4096 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
|
||||||
{
|
{
|
||||||
rsa_key = issuer_cert.getRsa4098PublicKey();
|
rsa_key = issuer_cert.getRsa4096PublicKey();
|
||||||
}
|
}
|
||||||
else if (issuer_pubk_type == nn::pki::cert::RSA2048 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
|
else if (issuer_pubk_type == nn::pki::cert::RSA2048 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
|
||||||
{
|
{
|
||||||
|
@ -148,42 +146,25 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki:
|
||||||
|
|
||||||
// verify signature
|
// verify signature
|
||||||
switch (signature_id) {
|
switch (signature_id) {
|
||||||
case (SIGN_ID_RSA4096_SHA1):
|
case (nn::pki::sign::SIGN_ID_RSA4096_SHA1):
|
||||||
sig_validate_res = tc::crypto::VerifyRsa4096Pkcs1Sha1(signature.data(), hash.data(), rsa_key);
|
sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha1(signature.data(), hash.data(), rsa_key);
|
||||||
break;
|
break;
|
||||||
case (SIGN_ID_RSA2048_SHA1):
|
case (nn::pki::sign::SIGN_ID_RSA2048_SHA1):
|
||||||
sig_validate_res = tc::crypto::VerifyRsa2048Pkcs1Sha1(signature.data(), hash.data(), rsa_key);
|
sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha1(signature.data(), hash.data(), rsa_key);
|
||||||
break;
|
break;
|
||||||
case (SIGN_ID_ECDSA240_SHA1):
|
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1):
|
||||||
sig_validate_res = false;
|
sig_valid = false;
|
||||||
break;
|
break;
|
||||||
case (SIGN_ID_RSA4096_SHA256):
|
case (nn::pki::sign::SIGN_ID_RSA4096_SHA256):
|
||||||
sig_validate_res = tc::crypto::VerifyRsa4096Pkcs1Sha256(signature.data(), hash.data(), rsa_key);
|
sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha256(signature.data(), hash.data(), rsa_key);
|
||||||
break;
|
break;
|
||||||
case (SIGN_ID_RSA2048_SHA256):
|
case (nn::pki::sign::SIGN_ID_RSA2048_SHA256):
|
||||||
sig_validate_res = tc::crypto::VerifyRsa2048Pkcs1Sha256(signature.data(), hash.data(), rsa_key);
|
sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha256(signature.data(), hash.data(), rsa_key);
|
||||||
break;
|
break;
|
||||||
case (SIGN_ID_ECDSA240_SHA256):
|
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256):
|
||||||
sig_validate_res = false;
|
sig_valid = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
|
|
||||||
{
|
|
||||||
sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
|
|
||||||
}
|
|
||||||
else if (sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
|
|
||||||
{
|
|
||||||
sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
|
|
||||||
}
|
|
||||||
else if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
|
|
||||||
{
|
|
||||||
throw tc::Exception(mModuleName, "ECDSA signatures are not supported");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw tc::Exception(mModuleName, "Mismatch between issuer public key and signature type");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (sig_valid == false)
|
if (sig_valid == false)
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,8 +45,8 @@ public:
|
||||||
struct sFile
|
struct sFile
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
uint64_t offset;
|
int64_t offset;
|
||||||
uint64_t size;
|
int64_t size;
|
||||||
|
|
||||||
void operator=(const sFile& other)
|
void operator=(const sFile& other)
|
||||||
{
|
{
|
||||||
|
@ -84,7 +84,7 @@ public:
|
||||||
|
|
||||||
// romfs specific
|
// romfs specific
|
||||||
void setMountPointName(const std::string& mount_name);
|
void setMountPointName(const std::string& mount_name);
|
||||||
void setExtractPath(const std::string& path);
|
void setExtractPath(const tc::io::Path& path);
|
||||||
void setListFs(bool list_fs);
|
void setListFs(bool list_fs);
|
||||||
|
|
||||||
const sDirectory& getRootDir() const;
|
const sDirectory& getRootDir() const;
|
||||||
|
@ -101,8 +101,7 @@ private:
|
||||||
bool mVerbose;
|
bool mVerbose;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
|
||||||
std::string mExtractPath;
|
tc::Optional<tc::io::Path> mExtractPath;
|
||||||
bool mExtract;
|
|
||||||
std::string mMountName;
|
std::string mMountName;
|
||||||
bool mListFs;
|
bool mListFs;
|
||||||
|
|
||||||
|
|
|
@ -349,6 +349,9 @@ private:
|
||||||
nstool::SettingsInitializer::SettingsInitializer(const std::vector<std::string>& args) :
|
nstool::SettingsInitializer::SettingsInitializer(const std::vector<std::string>& args) :
|
||||||
Settings(),
|
Settings(),
|
||||||
mModuleLabel("nstool::SettingsInitializer"),
|
mModuleLabel("nstool::SettingsInitializer"),
|
||||||
|
mShowLayout(false),
|
||||||
|
mShowKeydata(false),
|
||||||
|
mVerbose(false),
|
||||||
mTitleKey(),
|
mTitleKey(),
|
||||||
mBodyKey(),
|
mBodyKey(),
|
||||||
mTikPath(),
|
mTikPath(),
|
||||||
|
@ -359,6 +362,23 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector<std::string>&
|
||||||
if (infile.path.isNull())
|
if (infile.path.isNull())
|
||||||
throw tc::ArgumentException(mModuleLabel, "No input file was specified.");
|
throw tc::ArgumentException(mModuleLabel, "No input file was specified.");
|
||||||
|
|
||||||
|
// determine CLI output mode
|
||||||
|
opt.cli_output_mode.show_basic_info = true;
|
||||||
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
opt.cli_output_mode.show_extended_info = true;
|
||||||
|
opt.cli_output_mode.show_layout = true;
|
||||||
|
opt.cli_output_mode.show_keydata = true;
|
||||||
|
}
|
||||||
|
if (mShowKeydata)
|
||||||
|
{
|
||||||
|
opt.cli_output_mode.show_keydata = true;
|
||||||
|
}
|
||||||
|
if (mShowLayout)
|
||||||
|
{
|
||||||
|
opt.cli_output_mode.show_layout = true;
|
||||||
|
}
|
||||||
|
|
||||||
// locate key file, if not specfied
|
// locate key file, if not specfied
|
||||||
if (mKeysetPath.isNull())
|
if (mKeysetPath.isNull())
|
||||||
{
|
{
|
||||||
|
@ -431,9 +451,9 @@ void nstool::SettingsInitializer::parse_args(const std::vector<std::string>& arg
|
||||||
// none just yet
|
// none just yet
|
||||||
|
|
||||||
// get option flags
|
// get option flags
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.show_layout, {"--showlayout"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(mShowLayout, {"--showlayout"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.show_keydata, { "--showkeys" })));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(mShowKeydata, { "--showkeys" })));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verbose, {"-v", "--verbose"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(mVerbose, {"-v", "--verbose"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verify, {"-y", "--verify"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verify, {"-y", "--verify"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.is_dev, {"-d", "--dev"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.is_dev, {"-d", "--dev"})));
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,7 @@ private:
|
||||||
|
|
||||||
bool mShowLayout;
|
bool mShowLayout;
|
||||||
bool mShowKeydata;
|
bool mShowKeydata;
|
||||||
|
bool mVerbose;
|
||||||
|
|
||||||
tc::Optional<tc::io::Path> mKeysetPath;
|
tc::Optional<tc::io::Path> mKeysetPath;
|
||||||
tc::Optional<KeyBag::aes128_key_t> mTitleKey;
|
tc::Optional<KeyBag::aes128_key_t> mTitleKey;
|
||||||
|
|
176
src/main.cpp
176
src/main.cpp
|
@ -2,22 +2,22 @@
|
||||||
#include <tc/os/UnicodeMain.h>
|
#include <tc/os/UnicodeMain.h>
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
/*
|
|
||||||
#include "GameCardProcess.h"
|
//#include "GameCardProcess.h"
|
||||||
#include "PfsProcess.h"
|
//#include "PfsProcess.h"
|
||||||
#include "RomfsProcess.h"
|
//#include "RomfsProcess.h"
|
||||||
#include "NcaProcess.h"
|
//#include "NcaProcess.h"
|
||||||
#include "MetaProcess.h"
|
//#include "MetaProcess.h"
|
||||||
#include "CnmtProcess.h"
|
#include "CnmtProcess.h"
|
||||||
#include "NsoProcess.h"
|
//#include "NsoProcess.h"
|
||||||
#include "NroProcess.h"
|
//#include "NroProcess.h"
|
||||||
#include "NacpProcess.h"
|
//#include "NacpProcess.h"
|
||||||
#include "IniProcess.h"
|
//#include "IniProcess.h"
|
||||||
#include "KipProcess.h"
|
//#include "KipProcess.h"
|
||||||
#include "PkiCertProcess.h"
|
//#include "PkiCertProcess.h"
|
||||||
#include "EsTikProcess.h"
|
#include "EsTikProcess.h"
|
||||||
#include "AssetProcess.h"
|
#include "AssetProcess.h"
|
||||||
*/
|
|
||||||
|
|
||||||
int umain(const std::vector<std::string>& args, const std::vector<std::string>& env)
|
int umain(const std::vector<std::string>& args, const std::vector<std::string>& env)
|
||||||
{
|
{
|
||||||
|
@ -27,208 +27,214 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStream> infile_stream = std::make_shared<tc::io::FileStream>(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read));
|
std::shared_ptr<tc::io::IStream> infile_stream = std::make_shared<tc::io::FileStream>(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read));
|
||||||
|
|
||||||
|
/*
|
||||||
if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD)
|
if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD)
|
||||||
{
|
{
|
||||||
GameCardProcess obj;
|
nstool::GameCardProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
|
|
||||||
obj.setKeyCfg(set.opt.keybag);
|
obj.setKeyCfg(set.opt.keybag);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (set.xci.update_extract_path.isSet())
|
if (set.xci.update_extract_path.isSet())
|
||||||
obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get());
|
obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get());
|
||||||
if (set.xci.logo_extract_path.isSet())
|
if (set.xci.logo_extract_path.isSet())
|
||||||
obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, set.xci.logo_extract_path.get());
|
obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, set.xci.logo_extract_path.get());
|
||||||
if (user_set.getXciNormalPath().isSet())
|
if (set.xci.normal_extract_path.isSet())
|
||||||
obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, user_set.getXciNormalPath().get());
|
obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, set.xci.normal_extract_path.get());
|
||||||
if (user_set.getXciSecurePath().isSet())
|
if (set.xci.secure_extract_path.isSet())
|
||||||
obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, user_set.getXciSecurePath().get());
|
obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, set.xci.secure_extract_path.get());
|
||||||
obj.setListFs(user_set.isListFs());
|
obj.setListFs(set.fs.show_fs_tree);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP)
|
||||||
{
|
{
|
||||||
PfsProcess obj;
|
nstool::PfsProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (user_set.getFsPath().isSet())
|
if (set.fs.extract_path.isSet())
|
||||||
obj.setExtractPath(user_set.getFsPath().get());
|
obj.setExtractPath(set.fs.extract_path.get());
|
||||||
obj.setListFs(user_set.isListFs());
|
obj.setListFs(set.fs.show_fs_tree);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS)
|
||||||
{
|
{
|
||||||
RomfsProcess obj;
|
nstool::RomfsProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (user_set.getFsPath().isSet())
|
if (set.fs.extract_path.isSet())
|
||||||
obj.setExtractPath(user_set.getFsPath().get());
|
obj.setExtractPath(set.fs.extract_path.get());
|
||||||
obj.setListFs(user_set.isListFs());
|
obj.setListFs(set.fs.show_fs_tree);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA)
|
||||||
{
|
{
|
||||||
NcaProcess obj;
|
nstool::NcaProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setKeyCfg(set.opt.keybag);
|
obj.setKeyCfg(set.opt.keybag);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
|
|
||||||
if (user_set.getNcaPart0Path().isSet())
|
if (set.nca.part0_extract_path.isSet())
|
||||||
obj.setPartition0ExtractPath(user_set.getNcaPart0Path().get());
|
obj.setPartition0ExtractPath(set.nca.part0_extract_path.get());
|
||||||
if (user_set.getNcaPart1Path().isSet())
|
if (set.nca.part1_extract_path.isSet())
|
||||||
obj.setPartition1ExtractPath(user_set.getNcaPart1Path().get());
|
obj.setPartition1ExtractPath(set.nca.part1_extract_path.get());
|
||||||
if (user_set.getNcaPart2Path().isSet())
|
if (set.nca.part2_extract_path.isSet())
|
||||||
obj.setPartition2ExtractPath(user_set.getNcaPart2Path().get());
|
obj.setPartition2ExtractPath(set.nca.part2_extract_path.get());
|
||||||
if (user_set.getNcaPart3Path().isSet())
|
if (set.nca.part3_extract_path.isSet())
|
||||||
obj.setPartition3ExtractPath(user_set.getNcaPart3Path().get());
|
obj.setPartition3ExtractPath(set.nca.part3_extract_path.get());
|
||||||
obj.setListFs(user_set.isListFs());
|
obj.setListFs(set.fs.show_fs_tree);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META)
|
||||||
{
|
{
|
||||||
MetaProcess obj;
|
nstool::MetaProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setKeyCfg(set.opt.keybag);
|
obj.setKeyCfg(set.opt.keybag);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT)
|
|
||||||
|
else*/ if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT)
|
||||||
{
|
{
|
||||||
CnmtProcess obj;
|
nstool::CnmtProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO)
|
||||||
{
|
{
|
||||||
NsoProcess obj;
|
nstool::NsoProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.setIs64BitInstruction(user_set.getIs64BitInstruction());
|
obj.setIs64BitInstruction(set.code.is_64bit_instruction);
|
||||||
obj.setListApi(user_set.isListApi());
|
obj.setListApi(set.code.list_api);
|
||||||
obj.setListSymbols(user_set.isListSymbols());
|
obj.setListSymbols(set.code.list_symbols);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NRO)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NRO)
|
||||||
{
|
{
|
||||||
NroProcess obj;
|
nstool::NroProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.setIs64BitInstruction(user_set.getIs64BitInstruction());
|
obj.setIs64BitInstruction(set.code.is_64bit_instruction);
|
||||||
obj.setListApi(user_set.isListApi());
|
obj.setListApi(set.code.list_api);
|
||||||
obj.setListSymbols(user_set.isListSymbols());
|
obj.setListSymbols(set.code.list_symbols);
|
||||||
|
|
||||||
if (user_set.getAssetIconPath().isSet())
|
if (set.aset.icon_extract_path.isSet())
|
||||||
obj.setAssetIconExtractPath(user_set.getAssetIconPath().get());
|
obj.setAssetIconExtractPath(set.aset.icon_extract_path.get());
|
||||||
if (user_set.getAssetNacpPath().isSet())
|
if (set.aset.nacp_extract_path.isSet())
|
||||||
obj.setAssetNacpExtractPath(user_set.getAssetNacpPath().get());
|
obj.setAssetNacpExtractPath(set.aset.nacp_extract_path.get());
|
||||||
|
|
||||||
if (user_set.getFsPath().isSet())
|
if (set.fs.extract_path.isSet())
|
||||||
obj.setAssetRomfsExtractPath(user_set.getFsPath().get());
|
obj.setAssetRomfsExtractPath(set.fs.extract_path.get());
|
||||||
obj.setAssetListFs(user_set.isListFs());
|
obj.setAssetListFs(set.fs.show_fs_tree);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP)
|
||||||
{
|
{
|
||||||
NacpProcess obj;
|
nstool::NacpProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI)
|
||||||
{
|
{
|
||||||
IniProcess obj;
|
nstool::IniProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (user_set.getKipExtractPath().isSet())
|
if (set.kip.extract_path.isSet())
|
||||||
obj.setKipExtractPath(user_set.getKipExtractPath().get());
|
obj.setKipExtractPath(set.kip.extract_path.get());
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_KIP)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_KIP)
|
||||||
{
|
{
|
||||||
KipProcess obj;
|
nstool::KipProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT)
|
||||||
{
|
{
|
||||||
PkiCertProcess obj;
|
nstool::PkiCertProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setKeyCfg(set.opt.keybag);
|
obj.setKeyCfg(set.opt.keybag);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_TIK)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_TIK)
|
||||||
{
|
{
|
||||||
EsTikProcess obj;
|
nstool::EsTikProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setKeyCfg(set.opt.keybag);
|
obj.setKeyCfg(set.opt.keybag);
|
||||||
obj.setCertificateChain(user_set.getCertificateChain());
|
//obj.setCertificateChain(user_set.getCertificateChain());
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_HB_ASSET)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_HB_ASSET)
|
||||||
{
|
{
|
||||||
AssetProcess obj;
|
nstool::AssetProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
obj.setCliOutputMode(user_set.getCliOutputMode());
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (user_set.getAssetIconPath().isSet())
|
if (set.aset.icon_extract_path.isSet())
|
||||||
obj.setIconExtractPath(user_set.getAssetIconPath().get());
|
obj.setIconExtractPath(set.aset.icon_extract_path.get());
|
||||||
if (user_set.getAssetNacpPath().isSet())
|
if (set.aset.nacp_extract_path.isSet())
|
||||||
obj.setNacpExtractPath(user_set.getAssetNacpPath().get());
|
obj.setNacpExtractPath(set.aset.nacp_extract_path.get());
|
||||||
|
|
||||||
if (user_set.getFsPath().isSet())
|
if (set.fs.extract_path.isSet())
|
||||||
obj.setRomfsExtractPath(user_set.getFsPath().get());
|
obj.setRomfsExtractPath(set.fs.extract_path.get());
|
||||||
obj.setListFs(user_set.isListFs());
|
obj.setListFs(set.fs.show_fs_tree);
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <tc/types.h>
|
#include <tc/types.h>
|
||||||
|
#include <tc/Exception.h>
|
||||||
#include <tc/Optional.h>
|
#include <tc/Optional.h>
|
||||||
#include <tc/io.h>
|
#include <tc/io.h>
|
||||||
#include <tc/io/IOUtil.h>
|
#include <tc/io/IOUtil.h>
|
||||||
|
|
|
@ -68,13 +68,13 @@ void nstool::processResFile(const std::shared_ptr<tc::io::IStream>& file, std::m
|
||||||
|
|
||||||
void nstool::writeSubStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size)
|
void nstool::writeSubStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size)
|
||||||
{
|
{
|
||||||
writeStreamToStream(std::make_shared<tc::io::SubStream>(tc::io::SubStream(in_stream, offset, length)), std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileAccess::OpenOrCreate, tc::io::FileMode::Write)), cache_size);
|
writeStreamToStream(std::make_shared<tc::io::SubStream>(tc::io::SubStream(in_stream, offset, length)), std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void nstool::writeStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, const tc::io::Path& out_path, size_t cache_size)
|
void nstool::writeStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, const tc::io::Path& out_path, size_t cache_size)
|
||||||
{
|
{
|
||||||
writeStreamToStream(in_stream, std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileAccess::OpenOrCreate, tc::io::FileMode::Write)), cache_size);
|
writeStreamToStream(in_stream, std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stream, const std::shared_ptr<tc::io::IStream>& out_stream, size_t cache_size)
|
void nstool::writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stream, const std::shared_ptr<tc::io::IStream>& out_stream, size_t cache_size)
|
||||||
|
@ -90,7 +90,7 @@ void nstool::writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stre
|
||||||
cache_read_len = in_stream->read(cache.data(), cache.size());
|
cache_read_len = in_stream->read(cache.data(), cache.size());
|
||||||
if (cache_read_len == 0)
|
if (cache_read_len == 0)
|
||||||
{
|
{
|
||||||
throw tc::io::IOException(mModuleLabel, "Failed to read from RomFs file.");
|
throw tc::io::IOException("nstool::writeStreamToStream()", "Failed to read from source streeam.");
|
||||||
}
|
}
|
||||||
|
|
||||||
out_stream->write(cache.data(), cache_read_len);
|
out_stream->write(cache.data(), cache_read_len);
|
||||||
|
|
Loading…
Reference in a new issue