diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index ef6ea40..7111f5c 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -8,15 +8,7 @@ nstool::AssetProcess::AssetProcess() : mCliOutputMode(true, false, false, false), mVerify(false) { -} - -void nstool::AssetProcess::process() -{ - importHeader(); - if (mCliOutputMode.show_basic_info) - displayHeader(); - processSections(); -} +} void nstool::AssetProcess::setInputFile(const std::shared_ptr& file) { @@ -33,11 +25,6 @@ void nstool::AssetProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::AssetProcess::setListFs(bool list) -{ - mRomfs.setListFs(list); -} - void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path) { mIconExtractPath = path; @@ -48,11 +35,23 @@ void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path) mNacpExtractPath = path; } -void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path) +void nstool::AssetProcess::setRomfsShowFsTree(bool show_fs_tree) { - mRomfs.setExtractPath(path); + mRomfs.setShowFsTree(show_fs_tree); } +void nstool::AssetProcess::setRomfsExtractJobs(const std::vector& extract_jobs) +{ + mRomfs.setExtractJobs(extract_jobs); +} + +void nstool::AssetProcess::process() +{ + importHeader(); + if (mCliOutputMode.show_basic_info) + displayHeader(); + processSections(); +} void nstool::AssetProcess::importHeader() { diff --git a/src/AssetProcess.h b/src/AssetProcess.h index 5d1ea5b..96f40ec 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -12,18 +12,17 @@ class AssetProcess public: AssetProcess(); - void process(); - void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - void setListFs(bool list); - void setIconExtractPath(const tc::io::Path& path); void setNacpExtractPath(const tc::io::Path& path); - void setRomfsExtractPath(const tc::io::Path& path); + + void setRomfsShowFsTree(bool show_fs_tree); + void setRomfsExtractJobs(const std::vector& extract_jobs); + void process(); private: std::string mModuleName; diff --git a/src/FsProcess.cpp b/src/FsProcess.cpp index 3a7270d..78e2aee 100644 --- a/src/FsProcess.cpp +++ b/src/FsProcess.cpp @@ -1,11 +1,20 @@ #include "FsProcess.h" -#include +#include "util.h" + +#include +#include +#include nstool::FsProcess::FsProcess() : mModuleLabel("nstool::FsProcess"), mInputFs(), - mShowFs(false), - mExtractPath() + mFsFormatName(), + mShowFsInfo(false), + mProperties(), + mShowFsTree(false), + mFsRootLabel(), + mExtractJobs(), + mDataCache(0x10000) { } @@ -15,19 +24,34 @@ void nstool::FsProcess::setInputFileSystem(const std::shared_ptr& properties) { - mExtractPath = extract_path; + mProperties = properties; +} + +void nstool::FsProcess::setShowFsTree(bool show_fs_tree) +{ + mShowFsTree = show_fs_tree; +} + +void nstool::FsProcess::setFsRootLabel(const std::string& root_label) +{ + mFsRootLabel = root_label; +} + +void nstool::FsProcess::setExtractJobs(const std::vector& extract_jobs) +{ + mExtractJobs = extract_jobs; } void nstool::FsProcess::process() @@ -37,23 +61,147 @@ void nstool::FsProcess::process() throw tc::InvalidOperationException(mModuleLabel, "No input filesystem"); } - if (mShowFs) - printFs(); + if (mShowFsInfo) + { + fmt::print("[{:s}]\n", mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem/Info"); + for (auto itr = mProperties.begin(); itr != mProperties.end(); itr++) + { + fmt::print(" {:s}\n", *itr); + } + } - if (mExtractPath.isSet()) + if (mShowFsTree) + { + printFs(); + } + + + if (mExtractJobs.empty() == false) + { extractFs(); + } } void nstool::FsProcess::printFs() { - fmt::print("[{:s}FsTree]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : "")); + fmt::print("[{:s}/Tree]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem")); visitDir(tc::io::Path("/"), tc::io::Path("/"), false, true); } void nstool::FsProcess::extractFs() { - fmt::print("[{:s}FsExtract]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : "")); - visitDir(tc::io::Path("/"), mExtractPath.get(), true, false); + fmt::print("[{:s}/Extract]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem")); + + for (auto itr = mExtractJobs.begin(); itr != mExtractJobs.end(); itr++) + { + std::string path_str; + tc::io::PathUtil::pathToUnixUTF8(itr->virtual_path, path_str); + + // check if root path (legacy case) + if (itr->virtual_path == tc::io::Path("/")) + { + visitDir(tc::io::Path("/"), itr->extract_path, true, false); + + //fmt::print("Root Dir Virtual Path: \"{:s}\"\n", path_str); + + // root directory extract successful, continue to next job + continue; + } + + // otherwise determine if this is a file or subdirectory + try { + std::shared_ptr file_stream; + mInputFs->openFile(itr->virtual_path, tc::io::FileMode::Open, tc::io::FileAccess::Read, file_stream); + + //fmt::print("Valid File Path: \"{:s}\"\n", path_str); + + // the output path for this file will depend on the user specified extract path + std::shared_ptr local_fs = std::make_shared(tc::io::LocalStorage()); + + // case: the extract_path is a valid path to an existing directory + // behaviour: extract the file, preserving the original filename, to the specified directory + // method: try getDirectoryListing(itr->extract_path), if this is does not throw, then we can be sure this is a valid path to a directory, file_extract_path = itr->extract_path + itr->virtual_path.back() + + try { + tc::io::sDirectoryListing dir_listing; + local_fs->getDirectoryListing(itr->extract_path, dir_listing); + + tc::io::Path file_extract_path = itr->extract_path + itr->virtual_path.back(); + + std::string file_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(file_extract_path, file_extract_path_str); + + fmt::print("Saving {:s}...\n", file_extract_path_str); + + writeStreamToFile(file_stream, itr->extract_path + itr->virtual_path.back(), mDataCache); + + continue; + + } catch (tc::io::DirectoryNotFoundException& e) { + // acceptable exception, just means directory didn't exist + } + + // case: the extract_path up until the last element is a valid path to an existing directory, but the full path specifies neither a directory or a file + // behaviour: treat extract_path as the intended location to write the extracted file (the original filename is not preserved, instead specified by the user in the final element of the extract path) + // method: since this checks n-1 elements, it implies a path with more than one element, so that must be accounted for, as relative paths are valid and single element paths aren't always root + + try { + std::string test_path_str; + + // get path to parent directory + tc::io::Path parent_dir_path = itr->extract_path; + + // replace final path element with the current directory alias + parent_dir_path.pop_back(); // remove filename + parent_dir_path.push_back("."); // replace with the current dir name alias + tc::io::PathUtil::pathToUnixUTF8(parent_dir_path, test_path_str); + + // test parent directory exists + tc::io::sDirectoryListing dir_listing; + local_fs->getDirectoryListing(parent_dir_path, dir_listing); + + std::string file_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(itr->extract_path, file_extract_path_str); + + fmt::print("Saving {:s} as {:s}...\n", path_str, file_extract_path_str); + + writeStreamToFile(file_stream, itr->extract_path, mDataCache); + + continue; + } catch (tc::io::DirectoryNotFoundException& e) { + // acceptable exception, just means the parent directory didn't exist + } + + + // extract path could not be determined, inform the user and skip this job + std::string literal_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(itr->extract_path, literal_extract_path_str); + fmt::print("[WARNING] Extract path was invalid, and was skipped: {:s}\n", literal_extract_path_str); + continue; + } catch (tc::io::FileNotFoundException& e) { + // acceptable exception, just means file didn't exist + } + + // not a file, attempt to process this as a directory + try { + tc::io::sDirectoryListing dir_listing; + mInputFs->getDirectoryListing(itr->virtual_path, dir_listing); + + + visitDir(itr->virtual_path, itr->extract_path, true, false); + + //fmt::print("Valid Directory Path: \"{:s}\"\n", path_str); + + // directory extract successful, continue to next job + continue; + + } catch (tc::io::DirectoryNotFoundException& e) { + // acceptable exception, just means directory didn't exist + } + + fmt::print("[WARNING] Failed to extract virtual path: \"{:s}\"\n", path_str); + } + } void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& l_path, bool extract_fs, bool print_fs) @@ -67,9 +215,9 @@ void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& if (print_fs) { for (size_t i = 0; i < v_path.size(); i++) - fmt::print(" ");; + fmt::print(" "); - fmt::print("{:s}/\n", ((v_path.size() == 1) ? "Root:" : v_path.back())); + fmt::print("{:s}/\n", ((v_path.size() == 1) ? (mFsRootLabel.isSet() ? (mFsRootLabel.get() + ":") : "Root:") : v_path.back())); } if (extract_fs) { @@ -78,7 +226,6 @@ void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& } // iterate thru child files - tc::ByteData cache = tc::ByteData(0x10000); size_t cache_read_len; tc::io::Path out_path; std::string out_path_str; @@ -108,13 +255,13 @@ void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& out_stream->seek(0, tc::io::SeekOrigin::Begin); for (int64_t remaining_data = in_stream->length(); remaining_data > 0;) { - cache_read_len = in_stream->read(cache.data(), cache.size()); + cache_read_len = in_stream->read(mDataCache.data(), mDataCache.size()); if (cache_read_len == 0) { - throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from {:s}file.", (mFsLabel.isSet() ? (mFsLabel.get() + " ") : ""))); + throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from {:s}file.", (mFsFormatName.isSet() ? (mFsFormatName.get() + " ") : ""))); } - out_stream->write(cache.data(), cache_read_len); + out_stream->write(mDataCache.data(), cache_read_len); remaining_data -= int64_t(cache_read_len); } diff --git a/src/FsProcess.h b/src/FsProcess.h index b40dbb6..d9b5e3b 100644 --- a/src/FsProcess.h +++ b/src/FsProcess.h @@ -13,18 +13,33 @@ public: FsProcess(); void setInputFileSystem(const std::shared_ptr& input_fs); - void setFsLabel(const std::string& fs_label); - void setCliOutputMode(bool show_fs); - void setExtractPath(const tc::io::Path& extract_path); + void setFsFormatName(const std::string& fs_format_name); + void setFsProperties(const std::vector& properties); + void setShowFsInfo(bool show_fs_info); + void setShowFsTree(bool show_fs_tree); + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); void process(); private: std::string mModuleLabel; std::shared_ptr mInputFs; - tc::Optional mFsLabel; - bool mShowFs; - tc::Optional mExtractPath; + + // fs info + tc::Optional mFsFormatName; + bool mShowFsInfo; + std::vector mProperties; + + // fs tree + bool mShowFsTree; + tc::Optional mFsRootLabel; + + // extract jobs + std::vector mExtractJobs; + + // cache for file extract + tc::ByteData mDataCache; void printFs(); void extractFs(); diff --git a/src/GameCardProcess.cpp b/src/GameCardProcess.cpp index d06a6ce..d16a85b 100644 --- a/src/GameCardProcess.cpp +++ b/src/GameCardProcess.cpp @@ -1,20 +1,25 @@ -#include -#include -#include -#include +#include "GameCardProcess.h" + +#include +#include + #include #include #include -#include "GameCardProcess.h" + +#include +#include "FsProcess.h" + nstool::GameCardProcess::GameCardProcess() : + mModuleName("nstool::GameCardProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), mListFs(false), mProccessExtendedHeader(false), mRootPfs(), - mExtractInfo() + mExtractJobs() { } @@ -30,11 +35,8 @@ void nstool::GameCardProcess::process() if (mCliOutputMode.show_basic_info) displayHeader(); - // process root partition + // process nested HFS0 processRootPfs(); - - // process partitions - processPartitionPfs(); } void nstool::GameCardProcess::setInputFile(const std::shared_ptr& file) @@ -57,60 +59,69 @@ void nstool::GameCardProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path) +void nstool::GameCardProcess::setExtractJobs(const std::vector extract_jobs) { - mExtractInfo.push_back({partition_name, extract_path}); + mExtractJobs = extract_jobs; } -void nstool::GameCardProcess::setListFs(bool list_fs) +void nstool::GameCardProcess::setShowFsTree(bool show_fs_tree) { - mListFs = list_fs; + mListFs = show_fs_tree; } void nstool::GameCardProcess::importHeader() { - 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."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); } + // check stream is large enough for header + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sSdkGcHeader))) + { + throw tc::Exception(mModuleName, "Corrupt GameCard Image: File too small."); + } + // allocate memory for header - scratch.alloc(sizeof(nn::hac::sSdkGcHeader)); + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sSdkGcHeader)); // read header region - (*mFile)->read((byte_t*)scratch.data(), 0, sizeof(nn::hac::sSdkGcHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); // determine if this is a SDK XCI or a "Community" XCI - if (((nn::hac::sSdkGcHeader*)scratch.data())->signed_header.header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic) + if (((nn::hac::sSdkGcHeader*)scratch.data())->signed_header.header.st_magic.unwrap() == nn::hac::gc::kGcHeaderStructMagic) { mIsTrueSdkXci = true; mGcHeaderOffset = sizeof(nn::hac::sGcKeyDataRegion); } - else if (((nn::hac::sGcHeader_Rsa2048Signed*)scratch.data())->header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic) + else if (((nn::hac::sGcHeader_Rsa2048Signed*)scratch.data())->header.st_magic.unwrap() == nn::hac::gc::kGcHeaderStructMagic) { mIsTrueSdkXci = false; mGcHeaderOffset = 0; } else { - throw tc::Exception(kModuleName, "GameCard image did not have expected magic bytes"); + throw tc::Exception(mModuleName, "Corrupt GameCard Image: Unexpected magic bytes."); } nn::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (nn::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset); // generate hash of raw header - fnd::sha::Sha256((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader), mHdrHash.bytes); + tc::crypto::GenerateSha256Hash(mHdrHash.data(), (byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader)); // save the signature - memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size); + memcpy(mHdrSignature.data(), hdr_ptr->signature.data(), mHdrSignature.size()); // decrypt extended header - KeyBag::aes128_key_t header_key; - if (mKeyCfg.getXciHeaderKey(header_key)) + byte_t xci_header_key_index = hdr_ptr->header.key_flag & 7; + if (mKeyCfg.xci_header_key.find(xci_header_key_index) != mKeyCfg.xci_header_key.end()) { - nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, header_key.key); + nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, mKeyCfg.xci_header_key[xci_header_key_index].data()); mProccessExtendedHeader = true; } @@ -120,164 +131,156 @@ void nstool::GameCardProcess::importHeader() void nstool::GameCardProcess::displayHeader() { - std::cout << "[GameCard Header]" << std::endl; - std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl; - std::cout << " RomSize: " << nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType()); + const nn::hac::sGcHeader* raw_hdr = (const nn::hac::sGcHeader*)mHdr.getBytes().data(); + + fmt::print("[GameCard/Header]\n"); + fmt::print(" CardHeaderVersion: {:d}\n", mHdr.getCardHeaderVersion()); + fmt::print(" RomSize: {:s}", nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType())); if (mCliOutputMode.show_extended_info) - std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")"; - std::cout << std::endl; - std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl; - std::cout << " Flags: 0x" << std::dec << (uint32_t)mHdr.getFlags() << std::endl; - if (mHdr.getFlags() != 0) + fmt::print(" (0x{:x})", mHdr.getRomSizeType()); + fmt::print("\n"); + fmt::print(" PackageId: 0x{:016x}\n", mHdr.getPackageId()); + fmt::print(" Flags: 0x{:02x}\n", *((byte_t*)&raw_hdr->flags)); + for (auto itr = mHdr.getFlags().begin(); itr != mHdr.getFlags().end(); itr++) { - for (uint32_t i = 0; i < 8; i++) - { - if (_HAS_BIT(mHdr.getFlags(), i)) - { - std::cout << " " << nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)i) << std::endl; - } - } + fmt::print(" {:s}\n", nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)*itr)); + } + + + if (mCliOutputMode.show_extended_info) + { + fmt::print(" KekIndex: {:s} ({:d})\n", nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()), mHdr.getKekIndex()); + fmt::print(" TitleKeyDecIndex: {:d}\n", mHdr.getTitleKeyDecIndex()); + fmt::print(" InitialData:\n"); + fmt::print(" Hash:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getInitialDataHash().data(), mHdr.getInitialDataHash().size(), true, ":", 0x10, 6, false)); } if (mCliOutputMode.show_extended_info) { - std::cout << " KekIndex: " << nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()) << " (" << std::dec << (uint32_t)mHdr.getKekIndex() << ")" << std::endl; - std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl; - std::cout << " InitialData:" << std::endl; - std::cout << " Hash:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes+0x10, 0x10, true, ":") << std::endl; + fmt::print(" Extended Header AesCbc IV:\n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getAesCbcIv().data(), mHdr.getAesCbcIv().size(), true, ":")); } - if (mCliOutputMode.show_extended_info) - { - std::cout << " Extended Header AesCbc IV:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), true, ":") << std::endl; - } - std::cout << " SelSec: 0x" << std::hex << mHdr.getSelSec() << std::endl; - std::cout << " SelT1Key: 0x" << std::hex << mHdr.getSelT1Key() << std::endl; - std::cout << " SelKey: 0x" << std::hex << mHdr.getSelKey() << std::endl; + fmt::print(" SelSec: 0x{:x}\n", mHdr.getSelSec()); + fmt::print(" SelT1Key: 0x{:x}\n", mHdr.getSelT1Key()); + fmt::print(" SelKey: 0x{:x}\n", mHdr.getSelKey()); if (mCliOutputMode.show_layout) { - std::cout << " RomAreaStartPage: 0x" << std::hex << mHdr.getRomAreaStartPage(); + fmt::print(" RomAreaStartPage: 0x{:x}", mHdr.getRomAreaStartPage()); if (mHdr.getRomAreaStartPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage())); + fmt::print("\n"); - std::cout << " BackupAreaStartPage: 0x" << std::hex << mHdr.getBackupAreaStartPage(); + fmt::print(" BackupAreaStartPage: 0x{:x}", mHdr.getBackupAreaStartPage()); if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage())); + fmt::print("\n"); - std::cout << " ValidDataEndPage: 0x" << std::hex << mHdr.getValidDataEndPage(); + fmt::print(" ValidDataEndPage: 0x{:x}", mHdr.getValidDataEndPage()); if (mHdr.getValidDataEndPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage())); + fmt::print("\n"); - std::cout << " LimArea: 0x" << std::hex << mHdr.getLimAreaPage(); + fmt::print(" LimArea: 0x{:x}", mHdr.getLimAreaPage()); if (mHdr.getLimAreaPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage())); + fmt::print("\n"); - std::cout << " PartitionFs Header:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl; + fmt::print(" PartitionFs Header:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getPartitionFsAddress()); + fmt::print(" Size: 0x{:x}\n", mHdr.getPartitionFsSize()); if (mCliOutputMode.show_extended_info) { - std::cout << " Hash:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes+0x10, 0x10, true, ":") << std::endl; + fmt::print(" Hash:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getPartitionFsHash().data(), mHdr.getPartitionFsHash().size(), true, ":", 0x10, 6, false)); } } if (mProccessExtendedHeader) { - std::cout << "[GameCard Extended Header]" << std::endl; - std::cout << " FwVersion: v" << std::dec << mHdr.getFwVersion() << " (" << nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion()) << ")" << std::endl; - std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl; - std::cout << " CardClockRate: " << nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1()) << std::endl; - std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl; - std::cout << " Wait2TimeRead: 0x" << std::hex << mHdr.getWait2TimeRead() << std::endl; - std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl; - std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl; - std::cout << " SdkAddon Version: " << nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()) << " (v" << std::dec << mHdr.getFwMode() << ")" << std::endl; - std::cout << " CompatibilityType: " << nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()) << " (" << std::dec << (uint32_t) mHdr.getCompatibilityType() << ")" << std::endl; - std::cout << " Update Partition Info:" << std::endl; - std::cout << " CUP Version: " << nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()) << " (v" << std::dec << mHdr.getUppVersion() << ")" << std::endl; - std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl; - std::cout << " CUP Digest: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl; + fmt::print("[GameCard/ExtendedHeader]\n"); + fmt::print(" FwVersion: v{:d} ({:s})\n", mHdr.getFwVersion(), nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion())); + fmt::print(" AccCtrl1: 0x{:x}\n", mHdr.getAccCtrl1()); + fmt::print(" CardClockRate: {:s}\n", nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1())); + fmt::print(" Wait1TimeRead: 0x{:x}\n", mHdr.getWait1TimeRead()); + fmt::print(" Wait2TimeRead: 0x{:x}\n", mHdr.getWait2TimeRead()); + fmt::print(" Wait1TimeWrite: 0x{:x}\n", mHdr.getWait1TimeWrite()); + fmt::print(" Wait2TimeWrite: 0x{:x}\n", mHdr.getWait2TimeWrite()); + fmt::print(" SdkAddon Version: {:s} (v{:d})\n", nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()), mHdr.getFwMode()); + fmt::print(" CompatibilityType: {:s} ({:d})\n", nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()), mHdr.getCompatibilityType()); + fmt::print(" Update Partition Info:\n"); + fmt::print(" CUP Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()), mHdr.getUppVersion()); + fmt::print(" CUP TitleId: 0x{:016x}\n", mHdr.getUppId()); + fmt::print(" CUP Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getUppHash().data(), mHdr.getUppHash().size(), true, ":")); } } -bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt) +bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash, bool use_salt, byte_t salt) { - tc::ByteData scratch; - fnd::sha::sSha256Hash calc_hash; + // read region into memory + tc::ByteData scratch = tc::ByteData(tc::io::IOUtil::castInt64ToSize(len)); + mFile->seek(offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // update hash + tc::crypto::Sha256Generator sha256_gen; + sha256_gen.initialize(); + sha256_gen.update(scratch.data(), scratch.size()); if (use_salt) - { - scratch.alloc(len + 1); - scratch.data()[len] = salt; - } - else - { - scratch.alloc(len); - } + sha256_gen.update(&salt, sizeof(salt)); - (*mFile)->read(scratch.data(), offset, len); - fnd::sha::Sha256(scratch.data(), scratch.size(), calc_hash.bytes); + // calculate hash + nn::hac::detail::sha256_hash_t calc_hash; + sha256_gen.getHash(calc_hash.data()); - return calc_hash.compare(test_hash); + return memcmp(calc_hash.data(), test_hash, calc_hash.size()) == 0; } -bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash) +bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash) { return validateRegionOfFile(offset, len, test_hash, false, 0); } void nstool::GameCardProcess::validateXciSignature() { - fnd::rsa::sRsa2048Key header_sign_key; - - mKeyCfg.getXciHeaderSignKey(header_sign_key); - if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrSignature) != 0) + if (mKeyCfg.xci_header_sign_key.isSet()) { - std::cout << "[WARNING] GameCard Header Signature: FAIL" << std::endl; + if (tc::crypto::VerifyRsa2048Pkcs1Sha256(mHdrSignature.data(), mHdrHash.data(), mKeyCfg.xci_header_sign_key.get()) == false) + { + fmt::print("[WARNING] GameCard Header Signature: FAIL\n"); + } + } + else + { + fmt::print("[WARNING] GameCard Header Signature: FAIL (Failed to load rsa public key.)\n"); } } void nstool::GameCardProcess::processRootPfs() { - if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes, mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false) + if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().data(), mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false) { - std::cout << "[WARNING] GameCard Root HFS0: FAIL (bad hash)" << std::endl; + fmt::print("[WARNING] GameCard Root HFS0: FAIL (bad hash)\n"); } - mRootPfs.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize())); - mRootPfs.setListFs(mListFs); - mRootPfs.setVerifyMode(false); - mRootPfs.setCliOutputMode(mCliOutputMode); - mRootPfs.setMountPointName(kXciMountPointName); - mRootPfs.process(); -} -void nstool::GameCardProcess::processPartitionPfs() -{ - const std::vector& rootPartitions = mRootPfs.getPfsHeader().getFileList(); - for (size_t i = 0; i < rootPartitions.size(); i++) - { - // this must be validated here because only the size of the root partiton header is known at verification time - if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].hash_protected_size, rootPartitions[i].hash.bytes) == false) - { - std::cout << "[WARNING] GameCard " << rootPartitions[i].name << " Partition HFS0: FAIL (bad hash)" << std::endl; - } + std::shared_ptr gc_fs_raw = std::make_shared(tc::io::SubStream(mFile, mHdr.getPartitionFsAddress(), nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()+1) - mHdr.getPartitionFsAddress())); - PfsProcess tmp; - tmp.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].size)); - tmp.setListFs(mListFs); - tmp.setVerifyMode(mVerify); - tmp.setCliOutputMode(mCliOutputMode); - tmp.setMountPointName(kXciMountPointName + rootPartitions[i].name); - if (mExtractInfo.hasElement(rootPartitions[i].name)) - tmp.setExtractPath(mExtractInfo.getElement(rootPartitions[i].name).extract_path); - - tmp.process(); - } + auto gc_vfs_meta = nn::hac::GameCardFsMetaGenerator(gc_fs_raw, mHdr.getPartitionFsSize(), mVerify ? nn::hac::GameCardFsMetaGenerator::ValidationMode_Warn : nn::hac::GameCardFsMetaGenerator::ValidationMode_None); + std::shared_ptr gc_vfs = std::make_shared(tc::io::VirtualFileSystem(gc_vfs_meta) ); + + FsProcess fs_proc; + + fs_proc.setInputFileSystem(gc_vfs); + fs_proc.setFsFormatName("PartitionFS"); + fs_proc.setFsProperties({ + fmt::format("Type: Nested HFS0"), + fmt::format("DirNum: {:d}", gc_vfs_meta.dir_entries.empty() ? 0 : gc_vfs_meta.dir_entries.size() - 1), // -1 to not include root directory + fmt::format("FileNum: {:d}", gc_vfs_meta.file_entries.size()) + }); + fs_proc.setShowFsInfo(mCliOutputMode.show_basic_info); + fs_proc.setShowFsTree(mListFs); + fs_proc.setFsRootLabel(kXciMountPointName); + fs_proc.setExtractJobs(mExtractJobs); + + fs_proc.process(); } \ No newline at end of file diff --git a/src/GameCardProcess.h b/src/GameCardProcess.h index 3f294a8..0be04a8 100644 --- a/src/GameCardProcess.h +++ b/src/GameCardProcess.h @@ -12,65 +12,46 @@ class GameCardProcess public: GameCardProcess(); - void process(); - // generic void setInputFile(const std::shared_ptr& file); void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // xci specific - void setPartitionForExtract(const std::string& partition_name, const tc::io::Path& extract_path); - void setListFs(bool list_fs); + // fs specific + void setShowFsTree(bool show_fs_tree); + void setExtractJobs(const std::vector extract_jobs); + + void process(); private: - const std::string kModuleName = "GameCardProcess"; - const std::string kXciMountPointName = "gamecard:/"; + const std::string kXciMountPointName = "gamecard"; + + std::string mModuleName; std::shared_ptr mFile; KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; bool mListFs; - - struct sExtractInfo - { - std::string partition_name; - tc::io::Path extract_path; - - void operator=(const sExtractInfo& other) - { - partition_name = other.partition_name; - extract_path = other.extract_path; - } - - bool operator==(const tc::io::Path& name) const - { - return name == partition_name; - } - }; - - bool mIsTrueSdkXci; bool mIsSdkXciEncrypted; size_t mGcHeaderOffset; bool mProccessExtendedHeader; nn::hac::detail::rsa2048_signature_t mHdrSignature; - fnd::sha::sSha256Hash mHdrHash; + nn::hac::detail::sha256_hash_t mHdrHash; nn::hac::GameCardHeader mHdr; PfsProcess mRootPfs; - std::vector mExtractInfo; + std::vector mExtractJobs; void importHeader(); void displayHeader(); - bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt); - bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash); + bool validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash, bool use_salt, byte_t salt); + bool validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash); void validateXciSignature(); void processRootPfs(); - void processPartitionPfs(); }; } \ No newline at end of file diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 4dd59e9..b32606a 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -17,19 +17,7 @@ nstool::PfsProcess::PfsProcess() : mFileSystem(), mFsProcess() { - mFsProcess.setFsLabel("PartitionFS"); -} - -void nstool::PfsProcess::process() -{ - importHeader(); - - if (mCliOutputMode.show_basic_info) - { - displayHeader(); - } - - mFsProcess.process(); + mFsProcess.setFsFormatName("PartitionFS"); } void nstool::PfsProcess::setInputFile(const std::shared_ptr& file) @@ -40,6 +28,7 @@ void nstool::PfsProcess::setInputFile(const std::shared_ptr& fi void nstool::PfsProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; + mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); } void nstool::PfsProcess::setVerifyMode(bool verify) @@ -47,32 +36,22 @@ void nstool::PfsProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::PfsProcess::setMountPointName(const std::string& mount_name) +void nstool::PfsProcess::setShowFsTree(bool show_fs_tree) { - mFsProcess.setFsLabel(mount_name); + mFsProcess.setShowFsTree(show_fs_tree); } -void nstool::PfsProcess::setExtractPath(const tc::io::Path& path) +void nstool::PfsProcess::setFsRootLabel(const std::string& root_label) { - mFsProcess.setExtractPath(path); + mFsProcess.setFsRootLabel(root_label); } -void nstool::PfsProcess::setListFs(bool list_fs) +void nstool::PfsProcess::setExtractJobs(const std::vector& extract_jobs) { - mFsProcess.setCliOutputMode(list_fs); + mFsProcess.setExtractJobs(extract_jobs); } -const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const -{ - return mPfs; -} - -const std::shared_ptr& nstool::PfsProcess::getFileSystem() const -{ - return mFileSystem; -} - -void nstool::PfsProcess::importHeader() +void nstool::PfsProcess::process() { if (mFile == nullptr) { @@ -116,13 +95,24 @@ void nstool::PfsProcess::importHeader() // create virtual filesystem mFileSystem = std::make_shared(tc::io::VirtualFileSystem(nn::hac::PartitionFsMetaGenerator(mFile, mVerify ? nn::hac::PartitionFsMetaGenerator::ValidationMode_Warn : nn::hac::PartitionFsMetaGenerator::ValidationMode_None))); mFsProcess.setInputFileSystem(mFileSystem); + + // set properties for FsProcess + mFsProcess.setFsProperties({ + fmt::format("Type: {:s}", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())), + fmt::format("FileNum: {:d}", mPfs.getFileList().size()) + }); + + mFsProcess.process(); } -void nstool::PfsProcess::displayHeader() +const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const { - fmt::print("[PartitionFS]\n"); - fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())); - fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size()); + return mPfs; +} + +const std::shared_ptr& nstool::PfsProcess::getFileSystem() const +{ + return mFileSystem; } size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 9c13efd..00b205c 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -11,18 +11,19 @@ class PfsProcess public: PfsProcess(); - void process(); - // generic void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // pfs specific - void setMountPointName(const std::string& mount_name); - void setExtractPath(const tc::io::Path& path); - void setListFs(bool list_fs); + // fs specific + void setShowFsTree(bool show_fs_tree); + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); + void process(); + + // post process() get PFS/FS out const nn::hac::PartitionFsHeader& getPfsHeader() const; const std::shared_ptr& getFileSystem() const; @@ -39,9 +40,7 @@ private: std::shared_ptr mFileSystem; FsProcess mFsProcess; - - void importHeader(); - void displayHeader(); + size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr); bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr); }; diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index ca1bf6c..23e737f 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -15,19 +15,7 @@ nstool::RomfsProcess::RomfsProcess() : mFileSystem(), mFsProcess() { - mFsProcess.setFsLabel("RomFS"); -} - -void nstool::RomfsProcess::process() -{ - importHeader(); - - if (mCliOutputMode.show_basic_info) - { - displayHeader(); - } - - mFsProcess.process(); + mFsProcess.setFsFormatName("RomFS"); } void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) @@ -38,6 +26,7 @@ void nstool::RomfsProcess::setInputFile(const std::shared_ptr& void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; + mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); } void nstool::RomfsProcess::setVerifyMode(bool verify) @@ -45,27 +34,22 @@ void nstool::RomfsProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::RomfsProcess::setMountPointName(const std::string& mount_name) +void nstool::RomfsProcess::setFsRootLabel(const std::string& root_label) { - mFsProcess.setFsLabel(mount_name); + mFsProcess.setFsRootLabel(root_label); } -void nstool::RomfsProcess::setExtractPath(const tc::io::Path& path) +void nstool::RomfsProcess::setExtractJobs(const std::vector& extract_jobs) { - mFsProcess.setExtractPath(path); + mFsProcess.setExtractJobs(extract_jobs); } -void nstool::RomfsProcess::setListFs(bool list_fs) +void nstool::RomfsProcess::setShowFsTree(bool list_fs) { - mFsProcess.setCliOutputMode(list_fs); + mFsProcess.setShowFsTree(list_fs); } -const std::shared_ptr& nstool::RomfsProcess::getFileSystem() const -{ - return mFileSystem; -} - -void nstool::RomfsProcess::importHeader() +void nstool::RomfsProcess::process() { if (mFile == nullptr) { @@ -132,11 +116,13 @@ void nstool::RomfsProcess::importHeader() // create virtual filesystem mFileSystem = std::make_shared(tc::io::VirtualFileSystem(nn::hac::RomFsMetaGenerator(mFile))); mFsProcess.setInputFileSystem(mFileSystem); -} -void nstool::RomfsProcess::displayHeader() -{ - fmt::print("[RomFS]\n"); - fmt::print(" DirNum: {:d}\n", mDirNum); - fmt::print(" FileNum: {:d}\n", mFileNum); + // set properties for FsProcess + mFsProcess.setFsProperties({ + fmt::format("DirNum: {:d}", mDirNum), + fmt::format("FileNum: {:d}", mFileNum) + }); + + // process filesystem + mFsProcess.process(); } \ No newline at end of file diff --git a/src/RomfsProcess.h b/src/RomfsProcess.h index c7609a7..6f2304c 100644 --- a/src/RomfsProcess.h +++ b/src/RomfsProcess.h @@ -11,20 +11,17 @@ class RomfsProcess public: RomfsProcess(); - void process(); - // generic void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // pfs specific - void setMountPointName(const std::string& mount_name); - void setExtractPath(const tc::io::Path& path); - void setListFs(bool list_fs); - - const std::shared_ptr& getFileSystem() const; + // fs specific + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); + void setShowFsTree(bool show_fs_tree); + void process(); private: static const size_t kCacheSize = 0x10000; @@ -40,9 +37,6 @@ private: std::shared_ptr mFileSystem; FsProcess mFsProcess; - - void importHeader(); - void displayHeader(); }; } \ No newline at end of file diff --git a/src/Settings.cpp b/src/Settings.cpp index 51dd188..8e47291 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -350,6 +350,68 @@ private: std::vector mOptStrings; }; +class ExtractDataPathOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + ExtractDataPathOptionHandler(std::vector& jobs, const std::vector& opts) : + mJobs(jobs), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() == 1) + { + mJobs.push_back({tc::io::Path("/"), tc::io::Path(params[0])}); + } + else if (params.size() == 2) + { + mJobs.push_back({tc::io::Path(params[0]), tc::io::Path(params[1])}); + } + else + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires parameters in the format \"[] \".", option)); + } + } +private: + std::vector& mJobs; + std::vector mOptStrings; +}; + +class CustomExtractDataPathOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + CustomExtractDataPathOptionHandler(std::vector& jobs, const std::vector& opts, const tc::io::Path& custom_path) : + mJobs(jobs), + mOptStrings(opts), + mCustomPath(custom_path) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + mJobs.push_back({mCustomPath, tc::io::Path(params[0])}); + } +private: + std::vector& mJobs; + std::vector mOptStrings; + tc::io::Path mCustomPath; +}; + nstool::SettingsInitializer::SettingsInitializer(const std::vector& args) : Settings(), mModuleLabel("nstool::SettingsInitializer"), @@ -484,19 +546,19 @@ void nstool::SettingsInitializer::parse_args(const std::vector& arg // fs options opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(fs.show_fs_tree, { "--listfs" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(fs.extract_path, { "--fsdir" }))); + opts.registerOptionHandler(std::shared_ptr(new ExtractDataPathOptionHandler(fs.extract_jobs, { "--fsdir", "-x", "--extract" }))); // xci options - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.update_extract_path, { "--update" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.normal_extract_path, { "--normal" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.secure_extract_path, { "--secure" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.logo_extract_path, { "--logo" }))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--update" }, tc::io::Path("/update/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--normal" }, tc::io::Path("/normal/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--secure" }, tc::io::Path("/secure/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--logo" }, tc::io::Path("/logo/")))); // nca options - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part0_extract_path, { "--part0" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part1_extract_path, { "--part1" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part2_extract_path, { "--part2" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part3_extract_path, { "--part3" }))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part0" }, tc::io::Path("/0/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part1" }, tc::io::Path("/1/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part2" }, tc::io::Path("/2/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part3" }, tc::io::Path("/3/")))); // kip options opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" }))); @@ -552,7 +614,7 @@ void nstool::SettingsInitializer::determine_filetype() // detect ROMFS else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sRomfsHeader)) && _TYPE_PTR(nn::hac::sRomfsHeader)->header_size.unwrap() == sizeof(nn::hac::sRomfsHeader) - && _TYPE_PTR(nn::hac::sRomfsHeader)->sections[1].offset.unwrap() == (_TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].offset.unwrap() + _TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].size.unwrap())) + && _TYPE_PTR(nn::hac::sRomfsHeader)->dir_entry.offset.unwrap() == (_TYPE_PTR(nn::hac::sRomfsHeader)->dir_hash_bucket.offset.unwrap() + _TYPE_PTR(nn::hac::sRomfsHeader)->dir_hash_bucket.size.unwrap())) { infile.filetype = FILE_TYPE_ROMFS; } diff --git a/src/Settings.h b/src/Settings.h index c0a1a27..0be0384 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -57,7 +57,7 @@ struct Settings struct FsOptions { bool show_fs_tree; - tc::Optional extract_path; + std::vector extract_jobs; } fs; // XCI options @@ -105,18 +105,8 @@ struct Settings code.list_symbols = false; code.is_64bit_instruction = true; - xci.update_extract_path = tc::Optional(); - xci.logo_extract_path = tc::Optional(); - xci.normal_extract_path = tc::Optional(); - xci.secure_extract_path = tc::Optional(); - fs.show_fs_tree = false; - fs.extract_path = tc::Optional(); - - nca.part0_extract_path = tc::Optional(); - nca.part1_extract_path = tc::Optional(); - nca.part2_extract_path = tc::Optional(); - nca.part3_extract_path = tc::Optional(); + fs.extract_jobs = std::vector(); kip.extract_path = tc::Optional(); diff --git a/src/main.cpp b/src/main.cpp index b697ad8..8036edd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include "Settings.h" -//#include "GameCardProcess.h" +#include "GameCardProcess.h" #include "PfsProcess.h" #include "RomfsProcess.h" //#include "NcaProcess.h" @@ -27,7 +27,6 @@ int umain(const std::vector& args, const std::vector& std::shared_ptr infile_stream = std::make_shared(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read)); - /* if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD) { nstool::GameCardProcess obj; @@ -38,29 +37,22 @@ int umain(const std::vector& args, const std::vector& obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (set.xci.update_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get()); - if (set.xci.logo_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, set.xci.logo_extract_path.get()); - if (set.xci.normal_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, set.xci.normal_extract_path.get()); - if (set.xci.secure_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, set.xci.secure_extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); - + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); + 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) { nstool::PfsProcess obj; obj.setInputFile(infile_stream); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (set.fs.extract_path.isSet()) - obj.setExtractPath(set.fs.extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); obj.process(); } @@ -73,9 +65,8 @@ int umain(const std::vector& args, const std::vector& obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (set.fs.extract_path.isSet()) - obj.setExtractPath(set.fs.extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); obj.process(); } @@ -234,9 +225,8 @@ int umain(const std::vector& args, const std::vector& if (set.aset.nacp_extract_path.isSet()) obj.setNacpExtractPath(set.aset.nacp_extract_path.get()); - if (set.fs.extract_path.isSet()) - obj.setRomfsExtractPath(set.fs.extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); + obj.setRomfsShowFsTree(set.fs.show_fs_tree); + obj.setRomfsExtractJobs(set.fs.extract_jobs); obj.process(); }