From 5f1e8b27de0b21c429bff35c46c0c87177d9d11c Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 22:53:21 +0800 Subject: [PATCH] Port PfsProcess to libtoolchain --- deps/libnintendo-hac | 2 +- src/PfsProcess.cpp | 130 +++++++++++++++++++++---------------------- src/PfsProcess.h | 5 +- src/main.cpp | 11 ++-- src/util.cpp | 18 +++++- src/util.h | 4 ++ 6 files changed, 91 insertions(+), 79 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 02c6703..17c5c71 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 02c6703c9720d98197b3bb697fdab12008a9829e +Subproject commit 17c5c7177d8a899d01826091b3637e133b1f1c8e diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 425df18..bf26009 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -1,20 +1,16 @@ #include "PfsProcess.h" - -#include -#include - -#include -#include +#include "util.h" #include +#include nstool::PfsProcess::PfsProcess() : + mModuleName("nstool::PfsProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), mExtractPath(), - mExtract(false), mMountName(), mListFs(false), mPfs() @@ -33,7 +29,7 @@ void nstool::PfsProcess::process() } if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify) validateHfs(); - if (mExtract) + if (mExtractPath.isSet()) extractFs(); } @@ -57,9 +53,8 @@ void nstool::PfsProcess::setMountPointName(const std::string& mount_name) mMountName = mount_name; } -void nstool::PfsProcess::setExtractPath(const std::string& path) +void nstool::PfsProcess::setExtractPath(const tc::io::Path& path) { - mExtract = true; mExtractPath = path; } @@ -75,125 +70,126 @@ const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const void nstool::PfsProcess::importHeader() { + if (mFile == nullptr) + { + throw tc::Exception(mModuleName, "No file reader set."); + } + tc::ByteData scratch; - if (*mFile == nullptr) + // read base header to determine complete header size + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sPfsHeader))) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small"); } - - // open minimum header to get full header size - scratch.alloc(sizeof(nn::hac::sPfsHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); + + scratch = tc::ByteData(sizeof(nn::hac::sPfsHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false) { - throw tc::Exception(kModuleName, "Corrupt Header"); + throw tc::Exception(mModuleName, "Corrupt PartitionFs: Header had incorrect struct magic."); } + + // read complete size header size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data())); - - // open minimum header to get full header size - scratch.alloc(pfsHeaderSize); - (*mFile)->read(scratch.data(), 0, scratch.size()); + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(pfsHeaderSize)) + { + throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small"); + } + + scratch = tc::ByteData(pfsHeaderSize); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // process PFS mPfs.fromBytes(scratch.data(), scratch.size()); } void nstool::PfsProcess::displayHeader() { - std::cout << "[PartitionFS]" << std::endl; - std::cout << " Type: " << nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()) << std::endl; - std::cout << " FileNum: " << std::dec << mPfs.getFileList().size() << std::endl; + fmt::print("[PartitionFS]\n"); + fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())); + fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size()); if (mMountName.empty() == false) { - std::cout << " MountPoint: " << mMountName; + fmt::print(" MountPoint: {:s}"); if (mMountName.at(mMountName.length()-1) != '/') - std::cout << "/"; - std::cout << std::endl; + fmt::print("/"); + fmt::print("\n"); } } void nstool::PfsProcess::displayFs() { - for (size_t i = 0; i < mPfs.getFileList().size(); i++) + auto file_list = mPfs.getFileList(); + for (auto itr = file_list.begin(); itr != file_list.end(); itr++) { - const nn::hac::PartitionFsHeader::sFile& file = mPfs.getFileList()[i]; - std::cout << " " << file.name; + fmt::print(" {:s}", itr->name); if (mCliOutputMode.show_layout) { switch (mPfs.getFsType()) { case (nn::hac::PartitionFsHeader::TYPE_PFS0): - std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")"; + fmt::print(" (offset=0x{:x}, size=0x{:x})", itr->offset, itr->size); break; case (nn::hac::PartitionFsHeader::TYPE_HFS0): - std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ", hash_protected_size=0x" << file.hash_protected_size << ")"; + fmt::print(" (offset=0x{:x}, size=0x{:x}, hash_protected_size=0x{:x})", itr->offset, itr->size, itr->hash_protected_size); break; } } - std::cout << std::endl; + fmt::print("\n"); } } size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) { size_t fileEntrySize = 0; - if (hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic) + if (hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic) fileEntrySize = sizeof(nn::hac::sPfsFile); else fileEntrySize = sizeof(nn::hac::sHashedPfsFile); - return sizeof(nn::hac::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get(); + return sizeof(nn::hac::sPfsHeader) + hdr->file_num.unwrap() * fileEntrySize + hdr->name_table_size.unwrap(); } bool nstool::PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr) { - return hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.get() == nn::hac::pfs::kHashedPfsStructMagic; + return hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.unwrap() == nn::hac::pfs::kHashedPfsStructMagic; } void nstool::PfsProcess::validateHfs() { - fnd::sha::sSha256Hash hash; - const std::vector& file = mPfs.getFileList(); - for (size_t i = 0; i < file.size(); i++) + nn::hac::detail::sha256_hash_t hash; + auto file_list = mPfs.getFileList(); + for (auto itr = file_list.begin(); itr != file_list.end(); itr++) { - mCache.alloc(file[i].hash_protected_size); - (*mFile)->read(mCache.data(), file[i].offset, file[i].hash_protected_size); - fnd::sha::Sha256(mCache.data(), file[i].hash_protected_size, hash.bytes); - if (hash != file[i].hash) + tc::ByteData cache = tc::ByteData(tc::io::IOUtil::castInt64ToSize(itr->hash_protected_size)); + mFile->seek(itr->offset, tc::io::SeekOrigin::Begin); + mFile->read(cache.data(), cache.size()); + tc::crypto::GenerateSha256Hash(hash.data(), cache.data(), cache.size()); + if (hash != itr->hash) { - printf("[WARNING] HFS0 %s%s%s: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", (!mMountName.empty() && mMountName.at(mMountName.length()-1) != '/' )? "/" : "", file[i].name.c_str()); + fmt::print("[WARNING] HFS0 {:s}{:s}{:s}: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", (!mMountName.empty() && mMountName.at(mMountName.length()-1) != '/' )? "/" : "", itr->name); } } } void nstool::PfsProcess::extractFs() { - // allocate only when extractDir is invoked - mCache.alloc(kCacheSize); + // create extract directory + tc::io::LocalStorage fs; + fs.createDirectory(mExtractPath.get()); - // make extract dir - fnd::io::makeDirectory(mExtractPath); + // extract files + tc::ByteData cache_for_extract = tc::ByteData(kCacheSize); - fnd::SimpleFile outFile; - const std::vector& file = mPfs.getFileList(); - - std::string file_path; - for (size_t i = 0; i < file.size(); i++) + auto file_list = mPfs.getFileList(); + for (auto itr = file_list.begin(); itr != file_list.end(); itr++) { - file_path.clear(); - fnd::io::appendToPath(file_path, mExtractPath); - fnd::io::appendToPath(file_path, file[i].name); + tc::io::Path extract_path = mExtractPath.get() + itr->name; - if (mCliOutputMode.show_basic_info) - printf("extract=[%s]\n", file_path.c_str()); - - outFile.open(file_path, outFile.Create); - (*mFile)->seek(file[i].offset); - for (size_t j = 0; j < ((file[i].size / kCacheSize) + ((file[i].size % kCacheSize) != 0)); j++) - { - (*mFile)->read(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize)); - outFile.write(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize)); - } - outFile.close(); + writeSubStreamToFile(mFile, itr->offset, itr->size, extract_path, cache_for_extract); } } \ No newline at end of file diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 50a482b..cef266f 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -25,9 +25,10 @@ public: const nn::hac::PartitionFsHeader& getPfsHeader() const; private: - const std::string kModuleName = "PfsProcess"; static const size_t kCacheSize = 0x10000; + std::string mModuleName; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; @@ -36,8 +37,6 @@ private: std::string mMountName; bool mListFs; - tc::ByteData mCache; - nn::hac::PartitionFsHeader mPfs; void importHeader(); diff --git a/src/main.cpp b/src/main.cpp index 15f0d70..a1e811e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,7 @@ //#include "GameCardProcess.h" -//#include "PfsProcess.h" +#include "PfsProcess.h" //#include "RomfsProcess.h" //#include "NcaProcess.h" //#include "MetaProcess.h" @@ -50,9 +50,7 @@ int umain(const std::vector& args, const std::vector& 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; @@ -66,6 +64,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS) { nstool::RomfsProcess obj; @@ -113,8 +112,8 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - - else*/ if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) + */ + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) { nstool::CnmtProcess obj; diff --git a/src/util.cpp b/src/util.cpp index 435fc2d..0147dce 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -66,21 +66,29 @@ void nstool::processResFile(const std::shared_ptr& file, std::m } +void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache) +{ + writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache); +} + void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size) { writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); } +void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, tc::ByteData& cache) +{ + writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache); +} void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size) { writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); } -void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size) +void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, tc::ByteData& cache) { // iterate thru child files - tc::ByteData cache = tc::ByteData(cache_size); size_t cache_read_len; in_stream->seek(0, tc::io::SeekOrigin::Begin); @@ -99,6 +107,12 @@ void nstool::writeStreamToStream(const std::shared_ptr& in_stre } } +void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size) +{ + tc::ByteData cache = tc::ByteData(cache_size); + writeStreamToStream(in_stream, out_stream, cache); +} + std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len) { if (data == nullptr) { return fmt::format(""); } diff --git a/src/util.h b/src/util.h index aa1d962..d54275f 100644 --- a/src/util.h +++ b/src/util.h @@ -6,10 +6,14 @@ namespace nstool void processResFile(const std::shared_ptr& file, std::map& dict); +void writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache); void writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size = 0x10000); +void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, tc::ByteData& cache); void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000); +void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, tc::ByteData& cache); void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size = 0x10000); + std::string getTruncatedBytesString(const byte_t* data, size_t len); std::string getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate);