mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 18:55:29 +00:00
Port GameCardProcess & use new ExtractJob system. User can now specify custom extract jobs with specific virtual paths.
This commit is contained in:
parent
9d41a1c913
commit
e7dfa8ad44
|
@ -10,14 +10,6 @@ nstool::AssetProcess::AssetProcess() :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::AssetProcess::process()
|
|
||||||
{
|
|
||||||
importHeader();
|
|
||||||
if (mCliOutputMode.show_basic_info)
|
|
||||||
displayHeader();
|
|
||||||
processSections();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::AssetProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
void nstool::AssetProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
||||||
{
|
{
|
||||||
mFile = file;
|
mFile = file;
|
||||||
|
@ -33,11 +25,6 @@ void nstool::AssetProcess::setVerifyMode(bool verify)
|
||||||
mVerify = verify;
|
mVerify = verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::AssetProcess::setListFs(bool list)
|
|
||||||
{
|
|
||||||
mRomfs.setListFs(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path)
|
void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path)
|
||||||
{
|
{
|
||||||
mIconExtractPath = path;
|
mIconExtractPath = path;
|
||||||
|
@ -48,11 +35,23 @@ void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path)
|
||||||
mNacpExtractPath = 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<nstool::ExtractJob>& extract_jobs)
|
||||||
|
{
|
||||||
|
mRomfs.setExtractJobs(extract_jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::AssetProcess::process()
|
||||||
|
{
|
||||||
|
importHeader();
|
||||||
|
if (mCliOutputMode.show_basic_info)
|
||||||
|
displayHeader();
|
||||||
|
processSections();
|
||||||
|
}
|
||||||
|
|
||||||
void nstool::AssetProcess::importHeader()
|
void nstool::AssetProcess::importHeader()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,18 +12,17 @@ class AssetProcess
|
||||||
public:
|
public:
|
||||||
AssetProcess();
|
AssetProcess();
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
||||||
void setCliOutputMode(CliOutputMode type);
|
void setCliOutputMode(CliOutputMode type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
void setListFs(bool list);
|
|
||||||
|
|
||||||
void setIconExtractPath(const tc::io::Path& path);
|
void setIconExtractPath(const tc::io::Path& path);
|
||||||
void setNacpExtractPath(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<nstool::ExtractJob>& extract_jobs);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mModuleName;
|
std::string mModuleName;
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
#include "FsProcess.h"
|
#include "FsProcess.h"
|
||||||
#include <iostream>
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <tc/io/FileNotFoundException.h>
|
||||||
|
#include <tc/io/DirectoryNotFoundException.h>
|
||||||
|
|
||||||
nstool::FsProcess::FsProcess() :
|
nstool::FsProcess::FsProcess() :
|
||||||
mModuleLabel("nstool::FsProcess"),
|
mModuleLabel("nstool::FsProcess"),
|
||||||
mInputFs(),
|
mInputFs(),
|
||||||
mShowFs(false),
|
mFsFormatName(),
|
||||||
mExtractPath()
|
mShowFsInfo(false),
|
||||||
|
mProperties(),
|
||||||
|
mShowFsTree(false),
|
||||||
|
mFsRootLabel(),
|
||||||
|
mExtractJobs(),
|
||||||
|
mDataCache(0x10000)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,19 +24,34 @@ void nstool::FsProcess::setInputFileSystem(const std::shared_ptr<tc::io::IStorag
|
||||||
mInputFs = input_fs;
|
mInputFs = input_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::FsProcess::setFsLabel(const std::string& fs_label)
|
void nstool::FsProcess::setFsFormatName(const std::string& fs_format_name)
|
||||||
{
|
{
|
||||||
mFsLabel = fs_label;
|
mFsFormatName = fs_format_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::FsProcess::setCliOutputMode(bool show_fs)
|
void nstool::FsProcess::setShowFsInfo(bool show_fs_info)
|
||||||
{
|
{
|
||||||
mShowFs = show_fs;
|
mShowFsInfo = show_fs_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::FsProcess::setExtractPath(const tc::io::Path& extract_path)
|
void nstool::FsProcess::setFsProperties(const std::vector<std::string>& 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<nstool::ExtractJob>& extract_jobs)
|
||||||
|
{
|
||||||
|
mExtractJobs = extract_jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::FsProcess::process()
|
void nstool::FsProcess::process()
|
||||||
|
@ -37,23 +61,147 @@ void nstool::FsProcess::process()
|
||||||
throw tc::InvalidOperationException(mModuleLabel, "No input filesystem");
|
throw tc::InvalidOperationException(mModuleLabel, "No input filesystem");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mShowFs)
|
if (mShowFsInfo)
|
||||||
printFs();
|
{
|
||||||
|
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();
|
extractFs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::FsProcess::printFs()
|
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);
|
visitDir(tc::io::Path("/"), tc::io::Path("/"), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::FsProcess::extractFs()
|
void nstool::FsProcess::extractFs()
|
||||||
{
|
{
|
||||||
fmt::print("[{:s}FsExtract]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : ""));
|
fmt::print("[{:s}/Extract]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem"));
|
||||||
visitDir(tc::io::Path("/"), mExtractPath.get(), true, false);
|
|
||||||
|
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<tc::io::IStream> 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<tc::io::IStorage> local_fs = std::make_shared<tc::io::LocalStorage>(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)
|
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)
|
if (print_fs)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < v_path.size(); i++)
|
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)
|
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
|
// iterate thru child files
|
||||||
tc::ByteData cache = tc::ByteData(0x10000);
|
|
||||||
size_t cache_read_len;
|
size_t cache_read_len;
|
||||||
tc::io::Path out_path;
|
tc::io::Path out_path;
|
||||||
std::string out_path_str;
|
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);
|
out_stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
for (int64_t remaining_data = in_stream->length(); remaining_data > 0;)
|
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)
|
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);
|
remaining_data -= int64_t(cache_read_len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,33 @@ public:
|
||||||
FsProcess();
|
FsProcess();
|
||||||
|
|
||||||
void setInputFileSystem(const std::shared_ptr<tc::io::IStorage>& input_fs);
|
void setInputFileSystem(const std::shared_ptr<tc::io::IStorage>& input_fs);
|
||||||
void setFsLabel(const std::string& fs_label);
|
void setFsFormatName(const std::string& fs_format_name);
|
||||||
void setCliOutputMode(bool show_fs);
|
void setFsProperties(const std::vector<std::string>& properties);
|
||||||
void setExtractPath(const tc::io::Path& extract_path);
|
void setShowFsInfo(bool show_fs_info);
|
||||||
|
void setShowFsTree(bool show_fs_tree);
|
||||||
|
void setFsRootLabel(const std::string& root_label);
|
||||||
|
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
private:
|
private:
|
||||||
std::string mModuleLabel;
|
std::string mModuleLabel;
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStorage> mInputFs;
|
std::shared_ptr<tc::io::IStorage> mInputFs;
|
||||||
tc::Optional<std::string> mFsLabel;
|
|
||||||
bool mShowFs;
|
// fs info
|
||||||
tc::Optional<tc::io::Path> mExtractPath;
|
tc::Optional<std::string> mFsFormatName;
|
||||||
|
bool mShowFsInfo;
|
||||||
|
std::vector<std::string> mProperties;
|
||||||
|
|
||||||
|
// fs tree
|
||||||
|
bool mShowFsTree;
|
||||||
|
tc::Optional<std::string> mFsRootLabel;
|
||||||
|
|
||||||
|
// extract jobs
|
||||||
|
std::vector<nstool::ExtractJob> mExtractJobs;
|
||||||
|
|
||||||
|
// cache for file extract
|
||||||
|
tc::ByteData mDataCache;
|
||||||
|
|
||||||
void printFs();
|
void printFs();
|
||||||
void extractFs();
|
void extractFs();
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
#include <iostream>
|
#include "GameCardProcess.h"
|
||||||
#include <iomanip>
|
|
||||||
#include <fnd/SimpleTextOutput.h>
|
#include <tc/crypto.h>
|
||||||
#include <fnd/OffsetAdjustedIFile.h>
|
#include <tc/io/IOUtil.h>
|
||||||
|
|
||||||
#include <nn/hac/GameCardUtil.h>
|
#include <nn/hac/GameCardUtil.h>
|
||||||
#include <nn/hac/ContentMetaUtil.h>
|
#include <nn/hac/ContentMetaUtil.h>
|
||||||
#include <nn/hac/ContentArchiveUtil.h>
|
#include <nn/hac/ContentArchiveUtil.h>
|
||||||
#include "GameCardProcess.h"
|
|
||||||
|
#include <nn/hac/GameCardFsMetaGenerator.h>
|
||||||
|
#include "FsProcess.h"
|
||||||
|
|
||||||
|
|
||||||
nstool::GameCardProcess::GameCardProcess() :
|
nstool::GameCardProcess::GameCardProcess() :
|
||||||
|
mModuleName("nstool::GameCardProcess"),
|
||||||
mFile(),
|
mFile(),
|
||||||
mCliOutputMode(true, false, false, false),
|
mCliOutputMode(true, false, false, false),
|
||||||
mVerify(false),
|
mVerify(false),
|
||||||
mListFs(false),
|
mListFs(false),
|
||||||
mProccessExtendedHeader(false),
|
mProccessExtendedHeader(false),
|
||||||
mRootPfs(),
|
mRootPfs(),
|
||||||
mExtractInfo()
|
mExtractJobs()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +35,8 @@ void nstool::GameCardProcess::process()
|
||||||
if (mCliOutputMode.show_basic_info)
|
if (mCliOutputMode.show_basic_info)
|
||||||
displayHeader();
|
displayHeader();
|
||||||
|
|
||||||
// process root partition
|
// process nested HFS0
|
||||||
processRootPfs();
|
processRootPfs();
|
||||||
|
|
||||||
// process partitions
|
|
||||||
processPartitionPfs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::GameCardProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
void nstool::GameCardProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
||||||
|
@ -57,60 +59,69 @@ void nstool::GameCardProcess::setVerifyMode(bool verify)
|
||||||
mVerify = verify;
|
mVerify = verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path)
|
void nstool::GameCardProcess::setExtractJobs(const std::vector<nstool::ExtractJob> 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()
|
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
|
// allocate memory for header
|
||||||
scratch.alloc(sizeof(nn::hac::sSdkGcHeader));
|
tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sSdkGcHeader));
|
||||||
|
|
||||||
// read header region
|
// 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
|
// 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;
|
mIsTrueSdkXci = true;
|
||||||
mGcHeaderOffset = sizeof(nn::hac::sGcKeyDataRegion);
|
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;
|
mIsTrueSdkXci = false;
|
||||||
mGcHeaderOffset = 0;
|
mGcHeaderOffset = 0;
|
||||||
}
|
}
|
||||||
else
|
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);
|
nn::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (nn::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
|
||||||
|
|
||||||
// generate hash of raw header
|
// 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
|
// save the signature
|
||||||
memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size);
|
memcpy(mHdrSignature.data(), hdr_ptr->signature.data(), mHdrSignature.size());
|
||||||
|
|
||||||
// decrypt extended header
|
// decrypt extended header
|
||||||
KeyBag::aes128_key_t header_key;
|
byte_t xci_header_key_index = hdr_ptr->header.key_flag & 7;
|
||||||
if (mKeyCfg.getXciHeaderKey(header_key))
|
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;
|
mProccessExtendedHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,164 +131,156 @@ void nstool::GameCardProcess::importHeader()
|
||||||
|
|
||||||
void nstool::GameCardProcess::displayHeader()
|
void nstool::GameCardProcess::displayHeader()
|
||||||
{
|
{
|
||||||
std::cout << "[GameCard Header]" << std::endl;
|
const nn::hac::sGcHeader* raw_hdr = (const nn::hac::sGcHeader*)mHdr.getBytes().data();
|
||||||
std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl;
|
|
||||||
std::cout << " RomSize: " << nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType());
|
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)
|
if (mCliOutputMode.show_extended_info)
|
||||||
std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")";
|
fmt::print(" (0x{:x})", mHdr.getRomSizeType());
|
||||||
std::cout << std::endl;
|
fmt::print("\n");
|
||||||
std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl;
|
fmt::print(" PackageId: 0x{:016x}\n", mHdr.getPackageId());
|
||||||
std::cout << " Flags: 0x" << std::dec << (uint32_t)mHdr.getFlags() << std::endl;
|
fmt::print(" Flags: 0x{:02x}\n", *((byte_t*)&raw_hdr->flags));
|
||||||
if (mHdr.getFlags() != 0)
|
for (auto itr = mHdr.getFlags().begin(); itr != mHdr.getFlags().end(); itr++)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < 8; i++)
|
fmt::print(" {:s}\n", nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)*itr));
|
||||||
{
|
|
||||||
if (_HAS_BIT(mHdr.getFlags(), i))
|
|
||||||
{
|
|
||||||
std::cout << " " << nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)i) << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
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;
|
fmt::print(" Extended Header AesCbc IV:\n");
|
||||||
std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl;
|
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getAesCbcIv().data(), mHdr.getAesCbcIv().size(), true, ":"));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
if (mCliOutputMode.show_extended_info)
|
fmt::print(" SelSec: 0x{:x}\n", mHdr.getSelSec());
|
||||||
{
|
fmt::print(" SelT1Key: 0x{:x}\n", mHdr.getSelT1Key());
|
||||||
std::cout << " Extended Header AesCbc IV:" << std::endl;
|
fmt::print(" SelKey: 0x{:x}\n", mHdr.getSelKey());
|
||||||
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;
|
|
||||||
if (mCliOutputMode.show_layout)
|
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))
|
if (mHdr.getRomAreaStartPage() != (uint32_t)(-1))
|
||||||
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage()) << ")";
|
fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage()));
|
||||||
std::cout << std::endl;
|
fmt::print("\n");
|
||||||
|
|
||||||
std::cout << " BackupAreaStartPage: 0x" << std::hex << mHdr.getBackupAreaStartPage();
|
fmt::print(" BackupAreaStartPage: 0x{:x}", mHdr.getBackupAreaStartPage());
|
||||||
if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1))
|
if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1))
|
||||||
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage()) << ")";
|
fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage()));
|
||||||
std::cout << std::endl;
|
fmt::print("\n");
|
||||||
|
|
||||||
std::cout << " ValidDataEndPage: 0x" << std::hex << mHdr.getValidDataEndPage();
|
fmt::print(" ValidDataEndPage: 0x{:x}", mHdr.getValidDataEndPage());
|
||||||
if (mHdr.getValidDataEndPage() != (uint32_t)(-1))
|
if (mHdr.getValidDataEndPage() != (uint32_t)(-1))
|
||||||
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()) << ")";
|
fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()));
|
||||||
std::cout << std::endl;
|
fmt::print("\n");
|
||||||
|
|
||||||
std::cout << " LimArea: 0x" << std::hex << mHdr.getLimAreaPage();
|
fmt::print(" LimArea: 0x{:x}", mHdr.getLimAreaPage());
|
||||||
if (mHdr.getLimAreaPage() != (uint32_t)(-1))
|
if (mHdr.getLimAreaPage() != (uint32_t)(-1))
|
||||||
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage()) << ")";
|
fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage()));
|
||||||
std::cout << std::endl;
|
fmt::print("\n");
|
||||||
|
|
||||||
std::cout << " PartitionFs Header:" << std::endl;
|
fmt::print(" PartitionFs Header:\n");
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl;
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getPartitionFsAddress());
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl;
|
fmt::print(" Size: 0x{:x}\n", mHdr.getPartitionFsSize());
|
||||||
if (mCliOutputMode.show_extended_info)
|
if (mCliOutputMode.show_extended_info)
|
||||||
{
|
{
|
||||||
std::cout << " Hash:" << std::endl;
|
fmt::print(" Hash:\n");
|
||||||
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl;
|
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getPartitionFsHash().data(), mHdr.getPartitionFsHash().size(), true, ":", 0x10, 6, false));
|
||||||
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes+0x10, 0x10, true, ":") << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (mProccessExtendedHeader)
|
if (mProccessExtendedHeader)
|
||||||
{
|
{
|
||||||
std::cout << "[GameCard Extended Header]" << std::endl;
|
fmt::print("[GameCard/ExtendedHeader]\n");
|
||||||
std::cout << " FwVersion: v" << std::dec << mHdr.getFwVersion() << " (" << nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion()) << ")" << std::endl;
|
fmt::print(" FwVersion: v{:d} ({:s})\n", mHdr.getFwVersion(), nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion()));
|
||||||
std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl;
|
fmt::print(" AccCtrl1: 0x{:x}\n", mHdr.getAccCtrl1());
|
||||||
std::cout << " CardClockRate: " << nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1()) << std::endl;
|
fmt::print(" CardClockRate: {:s}\n", nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1()));
|
||||||
std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl;
|
fmt::print(" Wait1TimeRead: 0x{:x}\n", mHdr.getWait1TimeRead());
|
||||||
std::cout << " Wait2TimeRead: 0x" << std::hex << mHdr.getWait2TimeRead() << std::endl;
|
fmt::print(" Wait2TimeRead: 0x{:x}\n", mHdr.getWait2TimeRead());
|
||||||
std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl;
|
fmt::print(" Wait1TimeWrite: 0x{:x}\n", mHdr.getWait1TimeWrite());
|
||||||
std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl;
|
fmt::print(" Wait2TimeWrite: 0x{:x}\n", mHdr.getWait2TimeWrite());
|
||||||
std::cout << " SdkAddon Version: " << nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()) << " (v" << std::dec << mHdr.getFwMode() << ")" << std::endl;
|
fmt::print(" SdkAddon Version: {:s} (v{:d})\n", nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()), mHdr.getFwMode());
|
||||||
std::cout << " CompatibilityType: " << nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()) << " (" << std::dec << (uint32_t) mHdr.getCompatibilityType() << ")" << std::endl;
|
fmt::print(" CompatibilityType: {:s} ({:d})\n", nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()), mHdr.getCompatibilityType());
|
||||||
std::cout << " Update Partition Info:" << std::endl;
|
fmt::print(" Update Partition Info:\n");
|
||||||
std::cout << " CUP Version: " << nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()) << " (v" << std::dec << mHdr.getUppVersion() << ")" << std::endl;
|
fmt::print(" CUP Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()), mHdr.getUppVersion());
|
||||||
std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl;
|
fmt::print(" CUP TitleId: 0x{:016x}\n", mHdr.getUppId());
|
||||||
std::cout << " CUP Digest: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl;
|
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;
|
// read region into memory
|
||||||
fnd::sha::sSha256Hash calc_hash;
|
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)
|
if (use_salt)
|
||||||
{
|
sha256_gen.update(&salt, sizeof(salt));
|
||||||
scratch.alloc(len + 1);
|
|
||||||
scratch.data()[len] = salt;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
scratch.alloc(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*mFile)->read(scratch.data(), offset, len);
|
// calculate hash
|
||||||
fnd::sha::Sha256(scratch.data(), scratch.size(), calc_hash.bytes);
|
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);
|
return validateRegionOfFile(offset, len, test_hash, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::GameCardProcess::validateXciSignature()
|
void nstool::GameCardProcess::validateXciSignature()
|
||||||
{
|
{
|
||||||
fnd::rsa::sRsa2048Key header_sign_key;
|
if (mKeyCfg.xci_header_sign_key.isSet())
|
||||||
|
|
||||||
mKeyCfg.getXciHeaderSignKey(header_sign_key);
|
|
||||||
if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrSignature) != 0)
|
|
||||||
{
|
{
|
||||||
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()
|
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<nn::hac::PartitionFsHeader::sFile>& 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<std::string>(rootPartitions[i].name))
|
|
||||||
tmp.setExtractPath(mExtractInfo.getElement<std::string>(rootPartitions[i].name).extract_path);
|
|
||||||
|
|
||||||
tmp.process();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<tc::io::IStream> gc_fs_raw = std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, mHdr.getPartitionFsAddress(), nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()+1) - mHdr.getPartitionFsAddress()));
|
||||||
|
|
||||||
|
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<tc::io::IStorage> gc_vfs = std::make_shared<tc::io::VirtualFileSystem>(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();
|
||||||
}
|
}
|
|
@ -12,21 +12,22 @@ class GameCardProcess
|
||||||
public:
|
public:
|
||||||
GameCardProcess();
|
GameCardProcess();
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
// generic
|
// generic
|
||||||
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
||||||
void setKeyCfg(const KeyBag& keycfg);
|
void setKeyCfg(const KeyBag& keycfg);
|
||||||
void setCliOutputMode(CliOutputMode type);
|
void setCliOutputMode(CliOutputMode type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
// xci specific
|
// fs specific
|
||||||
void setPartitionForExtract(const std::string& partition_name, const tc::io::Path& extract_path);
|
void setShowFsTree(bool show_fs_tree);
|
||||||
void setListFs(bool list_fs);
|
void setExtractJobs(const std::vector<nstool::ExtractJob> extract_jobs);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "GameCardProcess";
|
const std::string kXciMountPointName = "gamecard";
|
||||||
const std::string kXciMountPointName = "gamecard:/";
|
|
||||||
|
std::string mModuleName;
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStream> mFile;
|
std::shared_ptr<tc::io::IStream> mFile;
|
||||||
KeyBag mKeyCfg;
|
KeyBag mKeyCfg;
|
||||||
|
@ -34,43 +35,23 @@ private:
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
bool mListFs;
|
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 mIsTrueSdkXci;
|
||||||
bool mIsSdkXciEncrypted;
|
bool mIsSdkXciEncrypted;
|
||||||
size_t mGcHeaderOffset;
|
size_t mGcHeaderOffset;
|
||||||
bool mProccessExtendedHeader;
|
bool mProccessExtendedHeader;
|
||||||
nn::hac::detail::rsa2048_signature_t mHdrSignature;
|
nn::hac::detail::rsa2048_signature_t mHdrSignature;
|
||||||
fnd::sha::sSha256Hash mHdrHash;
|
nn::hac::detail::sha256_hash_t mHdrHash;
|
||||||
nn::hac::GameCardHeader mHdr;
|
nn::hac::GameCardHeader mHdr;
|
||||||
|
|
||||||
PfsProcess mRootPfs;
|
PfsProcess mRootPfs;
|
||||||
std::vector<sExtractInfo> mExtractInfo;
|
std::vector<nstool::ExtractJob> mExtractJobs;
|
||||||
|
|
||||||
void importHeader();
|
void importHeader();
|
||||||
void displayHeader();
|
void displayHeader();
|
||||||
bool validateRegionOfFile(size_t offset, size_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, 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);
|
||||||
void validateXciSignature();
|
void validateXciSignature();
|
||||||
void processRootPfs();
|
void processRootPfs();
|
||||||
void processPartitionPfs();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -17,19 +17,7 @@ nstool::PfsProcess::PfsProcess() :
|
||||||
mFileSystem(),
|
mFileSystem(),
|
||||||
mFsProcess()
|
mFsProcess()
|
||||||
{
|
{
|
||||||
mFsProcess.setFsLabel("PartitionFS");
|
mFsProcess.setFsFormatName("PartitionFS");
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::PfsProcess::process()
|
|
||||||
{
|
|
||||||
importHeader();
|
|
||||||
|
|
||||||
if (mCliOutputMode.show_basic_info)
|
|
||||||
{
|
|
||||||
displayHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
mFsProcess.process();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::PfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
void nstool::PfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
||||||
|
@ -40,6 +28,7 @@ void nstool::PfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& fi
|
||||||
void nstool::PfsProcess::setCliOutputMode(CliOutputMode type)
|
void nstool::PfsProcess::setCliOutputMode(CliOutputMode type)
|
||||||
{
|
{
|
||||||
mCliOutputMode = type;
|
mCliOutputMode = type;
|
||||||
|
mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::PfsProcess::setVerifyMode(bool verify)
|
void nstool::PfsProcess::setVerifyMode(bool verify)
|
||||||
|
@ -47,32 +36,22 @@ void nstool::PfsProcess::setVerifyMode(bool verify)
|
||||||
mVerify = 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<nstool::ExtractJob>& extract_jobs)
|
||||||
{
|
{
|
||||||
mFsProcess.setCliOutputMode(list_fs);
|
mFsProcess.setExtractJobs(extract_jobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const
|
void nstool::PfsProcess::process()
|
||||||
{
|
|
||||||
return mPfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<tc::io::IStorage>& nstool::PfsProcess::getFileSystem() const
|
|
||||||
{
|
|
||||||
return mFileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::PfsProcess::importHeader()
|
|
||||||
{
|
{
|
||||||
if (mFile == nullptr)
|
if (mFile == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -116,13 +95,24 @@ void nstool::PfsProcess::importHeader()
|
||||||
// create virtual filesystem
|
// create virtual filesystem
|
||||||
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(nn::hac::PartitionFsMetaGenerator(mFile, mVerify ? nn::hac::PartitionFsMetaGenerator::ValidationMode_Warn : nn::hac::PartitionFsMetaGenerator::ValidationMode_None)));
|
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(nn::hac::PartitionFsMetaGenerator(mFile, mVerify ? nn::hac::PartitionFsMetaGenerator::ValidationMode_Warn : nn::hac::PartitionFsMetaGenerator::ValidationMode_None)));
|
||||||
mFsProcess.setInputFileSystem(mFileSystem);
|
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");
|
return mPfs;
|
||||||
fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()));
|
}
|
||||||
fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size());
|
|
||||||
|
const std::shared_ptr<tc::io::IStorage>& nstool::PfsProcess::getFileSystem() const
|
||||||
|
{
|
||||||
|
return mFileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr)
|
size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr)
|
||||||
|
|
|
@ -11,18 +11,19 @@ class PfsProcess
|
||||||
public:
|
public:
|
||||||
PfsProcess();
|
PfsProcess();
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
// generic
|
// generic
|
||||||
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
||||||
void setCliOutputMode(CliOutputMode type);
|
void setCliOutputMode(CliOutputMode type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
// pfs specific
|
// fs specific
|
||||||
void setMountPointName(const std::string& mount_name);
|
void setShowFsTree(bool show_fs_tree);
|
||||||
void setExtractPath(const tc::io::Path& path);
|
void setFsRootLabel(const std::string& root_label);
|
||||||
void setListFs(bool list_fs);
|
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
// post process() get PFS/FS out
|
||||||
const nn::hac::PartitionFsHeader& getPfsHeader() const;
|
const nn::hac::PartitionFsHeader& getPfsHeader() const;
|
||||||
const std::shared_ptr<tc::io::IStorage>& getFileSystem() const;
|
const std::shared_ptr<tc::io::IStorage>& getFileSystem() const;
|
||||||
|
|
||||||
|
@ -40,8 +41,6 @@ private:
|
||||||
std::shared_ptr<tc::io::IStorage> mFileSystem;
|
std::shared_ptr<tc::io::IStorage> mFileSystem;
|
||||||
FsProcess mFsProcess;
|
FsProcess mFsProcess;
|
||||||
|
|
||||||
void importHeader();
|
|
||||||
void displayHeader();
|
|
||||||
size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr);
|
size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr);
|
||||||
bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr);
|
bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr);
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,19 +15,7 @@ nstool::RomfsProcess::RomfsProcess() :
|
||||||
mFileSystem(),
|
mFileSystem(),
|
||||||
mFsProcess()
|
mFsProcess()
|
||||||
{
|
{
|
||||||
mFsProcess.setFsLabel("RomFS");
|
mFsProcess.setFsFormatName("RomFS");
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::RomfsProcess::process()
|
|
||||||
{
|
|
||||||
importHeader();
|
|
||||||
|
|
||||||
if (mCliOutputMode.show_basic_info)
|
|
||||||
{
|
|
||||||
displayHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
mFsProcess.process();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::RomfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
void nstool::RomfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
||||||
|
@ -38,6 +26,7 @@ void nstool::RomfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>&
|
||||||
void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type)
|
void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type)
|
||||||
{
|
{
|
||||||
mCliOutputMode = type;
|
mCliOutputMode = type;
|
||||||
|
mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nstool::RomfsProcess::setVerifyMode(bool verify)
|
void nstool::RomfsProcess::setVerifyMode(bool verify)
|
||||||
|
@ -45,27 +34,22 @@ void nstool::RomfsProcess::setVerifyMode(bool verify)
|
||||||
mVerify = 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<nstool::ExtractJob>& 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<tc::io::IStorage>& nstool::RomfsProcess::getFileSystem() const
|
void nstool::RomfsProcess::process()
|
||||||
{
|
|
||||||
return mFileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::RomfsProcess::importHeader()
|
|
||||||
{
|
{
|
||||||
if (mFile == nullptr)
|
if (mFile == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -132,11 +116,13 @@ void nstool::RomfsProcess::importHeader()
|
||||||
// create virtual filesystem
|
// create virtual filesystem
|
||||||
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(nn::hac::RomFsMetaGenerator(mFile)));
|
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(nn::hac::RomFsMetaGenerator(mFile)));
|
||||||
mFsProcess.setInputFileSystem(mFileSystem);
|
mFsProcess.setInputFileSystem(mFileSystem);
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::RomfsProcess::displayHeader()
|
// set properties for FsProcess
|
||||||
{
|
mFsProcess.setFsProperties({
|
||||||
fmt::print("[RomFS]\n");
|
fmt::format("DirNum: {:d}", mDirNum),
|
||||||
fmt::print(" DirNum: {:d}\n", mDirNum);
|
fmt::format("FileNum: {:d}", mFileNum)
|
||||||
fmt::print(" FileNum: {:d}\n", mFileNum);
|
});
|
||||||
|
|
||||||
|
// process filesystem
|
||||||
|
mFsProcess.process();
|
||||||
}
|
}
|
|
@ -11,20 +11,17 @@ class RomfsProcess
|
||||||
public:
|
public:
|
||||||
RomfsProcess();
|
RomfsProcess();
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
// generic
|
// generic
|
||||||
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
|
||||||
void setCliOutputMode(CliOutputMode type);
|
void setCliOutputMode(CliOutputMode type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
// pfs specific
|
// fs specific
|
||||||
void setMountPointName(const std::string& mount_name);
|
void setFsRootLabel(const std::string& root_label);
|
||||||
void setExtractPath(const tc::io::Path& path);
|
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
|
||||||
void setListFs(bool list_fs);
|
void setShowFsTree(bool show_fs_tree);
|
||||||
|
|
||||||
const std::shared_ptr<tc::io::IStorage>& getFileSystem() const;
|
|
||||||
|
|
||||||
|
void process();
|
||||||
private:
|
private:
|
||||||
static const size_t kCacheSize = 0x10000;
|
static const size_t kCacheSize = 0x10000;
|
||||||
|
|
||||||
|
@ -40,9 +37,6 @@ private:
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStorage> mFileSystem;
|
std::shared_ptr<tc::io::IStorage> mFileSystem;
|
||||||
FsProcess mFsProcess;
|
FsProcess mFsProcess;
|
||||||
|
|
||||||
void importHeader();
|
|
||||||
void displayHeader();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -350,6 +350,68 @@ private:
|
||||||
std::vector<std::string> mOptStrings;
|
std::vector<std::string> mOptStrings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ExtractDataPathOptionHandler : public tc::cli::OptionParser::IOptionHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExtractDataPathOptionHandler(std::vector<nstool::ExtractJob>& jobs, const std::vector<std::string>& opts) :
|
||||||
|
mJobs(jobs),
|
||||||
|
mOptStrings(opts)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::vector<std::string>& getOptionStrings() const
|
||||||
|
{
|
||||||
|
return mOptStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processOption(const std::string& option, const std::vector<std::string>& 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 \"[<internal path>] <extract path>\".", option));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<nstool::ExtractJob>& mJobs;
|
||||||
|
std::vector<std::string> mOptStrings;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CustomExtractDataPathOptionHandler : public tc::cli::OptionParser::IOptionHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CustomExtractDataPathOptionHandler(std::vector<nstool::ExtractJob>& jobs, const std::vector<std::string>& opts, const tc::io::Path& custom_path) :
|
||||||
|
mJobs(jobs),
|
||||||
|
mOptStrings(opts),
|
||||||
|
mCustomPath(custom_path)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::vector<std::string>& getOptionStrings() const
|
||||||
|
{
|
||||||
|
return mOptStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processOption(const std::string& option, const std::vector<std::string>& 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<nstool::ExtractJob>& mJobs;
|
||||||
|
std::vector<std::string> mOptStrings;
|
||||||
|
tc::io::Path mCustomPath;
|
||||||
|
};
|
||||||
|
|
||||||
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"),
|
||||||
|
@ -484,19 +546,19 @@ void nstool::SettingsInitializer::parse_args(const std::vector<std::string>& arg
|
||||||
|
|
||||||
// fs options
|
// fs options
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(fs.show_fs_tree, { "--listfs" })));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(fs.show_fs_tree, { "--listfs" })));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(fs.extract_path, { "--fsdir" })));
|
opts.registerOptionHandler(std::shared_ptr<ExtractDataPathOptionHandler>(new ExtractDataPathOptionHandler(fs.extract_jobs, { "--fsdir", "-x", "--extract" })));
|
||||||
|
|
||||||
// xci options
|
// xci options
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(xci.update_extract_path, { "--update" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--update" }, tc::io::Path("/update/"))));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(xci.normal_extract_path, { "--normal" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--normal" }, tc::io::Path("/normal/"))));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(xci.secure_extract_path, { "--secure" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--secure" }, tc::io::Path("/secure/"))));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(xci.logo_extract_path, { "--logo" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--logo" }, tc::io::Path("/logo/"))));
|
||||||
|
|
||||||
// nca options
|
// nca options
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(nca.part0_extract_path, { "--part0" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part0" }, tc::io::Path("/0/"))));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(nca.part1_extract_path, { "--part1" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part1" }, tc::io::Path("/1/"))));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(nca.part2_extract_path, { "--part2" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part2" }, tc::io::Path("/2/"))));
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(nca.part3_extract_path, { "--part3" })));
|
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part3" }, tc::io::Path("/3/"))));
|
||||||
|
|
||||||
// kip options
|
// kip options
|
||||||
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" })));
|
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" })));
|
||||||
|
@ -552,7 +614,7 @@ void nstool::SettingsInitializer::determine_filetype()
|
||||||
// detect ROMFS
|
// detect ROMFS
|
||||||
else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sRomfsHeader))
|
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)->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;
|
infile.filetype = FILE_TYPE_ROMFS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ struct Settings
|
||||||
struct FsOptions
|
struct FsOptions
|
||||||
{
|
{
|
||||||
bool show_fs_tree;
|
bool show_fs_tree;
|
||||||
tc::Optional<tc::io::Path> extract_path;
|
std::vector<ExtractJob> extract_jobs;
|
||||||
} fs;
|
} fs;
|
||||||
|
|
||||||
// XCI options
|
// XCI options
|
||||||
|
@ -105,18 +105,8 @@ struct Settings
|
||||||
code.list_symbols = false;
|
code.list_symbols = false;
|
||||||
code.is_64bit_instruction = true;
|
code.is_64bit_instruction = true;
|
||||||
|
|
||||||
xci.update_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
xci.logo_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
xci.normal_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
xci.secure_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
|
|
||||||
fs.show_fs_tree = false;
|
fs.show_fs_tree = false;
|
||||||
fs.extract_path = tc::Optional<tc::io::Path>();
|
fs.extract_jobs = std::vector<ExtractJob>();
|
||||||
|
|
||||||
nca.part0_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
nca.part1_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
nca.part2_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
nca.part3_extract_path = tc::Optional<tc::io::Path>();
|
|
||||||
|
|
||||||
kip.extract_path = tc::Optional<tc::io::Path>();
|
kip.extract_path = tc::Optional<tc::io::Path>();
|
||||||
|
|
||||||
|
|
32
src/main.cpp
32
src/main.cpp
|
@ -3,7 +3,7 @@
|
||||||
#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"
|
||||||
|
@ -27,7 +27,6 @@ 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)
|
||||||
{
|
{
|
||||||
nstool::GameCardProcess obj;
|
nstool::GameCardProcess obj;
|
||||||
|
@ -38,29 +37,22 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
||||||
obj.setCliOutputMode(set.opt.cli_output_mode);
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (set.xci.update_extract_path.isSet())
|
obj.setShowFsTree(set.fs.show_fs_tree);
|
||||||
obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get());
|
obj.setExtractJobs(set.fs.extract_jobs);
|
||||||
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.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)
|
||||||
{
|
{
|
||||||
nstool::PfsProcess obj;
|
nstool::PfsProcess obj;
|
||||||
|
|
||||||
obj.setInputFile(infile_stream);
|
obj.setInputFile(infile_stream);
|
||||||
|
|
||||||
obj.setCliOutputMode(set.opt.cli_output_mode);
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (set.fs.extract_path.isSet())
|
obj.setShowFsTree(set.fs.show_fs_tree);
|
||||||
obj.setExtractPath(set.fs.extract_path.get());
|
obj.setExtractJobs(set.fs.extract_jobs);
|
||||||
obj.setListFs(set.fs.show_fs_tree);
|
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
@ -73,9 +65,8 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
||||||
obj.setCliOutputMode(set.opt.cli_output_mode);
|
obj.setCliOutputMode(set.opt.cli_output_mode);
|
||||||
obj.setVerifyMode(set.opt.verify);
|
obj.setVerifyMode(set.opt.verify);
|
||||||
|
|
||||||
if (set.fs.extract_path.isSet())
|
obj.setShowFsTree(set.fs.show_fs_tree);
|
||||||
obj.setExtractPath(set.fs.extract_path.get());
|
obj.setExtractJobs(set.fs.extract_jobs);
|
||||||
obj.setListFs(set.fs.show_fs_tree);
|
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
@ -234,9 +225,8 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
||||||
if (set.aset.nacp_extract_path.isSet())
|
if (set.aset.nacp_extract_path.isSet())
|
||||||
obj.setNacpExtractPath(set.aset.nacp_extract_path.get());
|
obj.setNacpExtractPath(set.aset.nacp_extract_path.get());
|
||||||
|
|
||||||
if (set.fs.extract_path.isSet())
|
obj.setRomfsShowFsTree(set.fs.show_fs_tree);
|
||||||
obj.setRomfsExtractPath(set.fs.extract_path.get());
|
obj.setRomfsExtractJobs(set.fs.extract_jobs);
|
||||||
obj.setListFs(set.fs.show_fs_tree);
|
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue