nstool/src/PfsProcess.cpp

195 lines
5.1 KiB
C++
Raw Normal View History

#include "PfsProcess.h"
2021-10-02 14:53:21 +00:00
#include "util.h"
#include <nn/hac/PartitionFsUtil.h>
2021-10-02 14:53:21 +00:00
#include <tc/io/LocalStorage.h>
nstool::PfsProcess::PfsProcess() :
2021-10-02 14:53:21 +00:00
mModuleName("nstool::PfsProcess"),
mFile(),
mCliOutputMode(true, false, false, false),
mVerify(false),
mExtractPath(),
mMountName(),
mListFs(false),
mPfs()
{
}
void nstool::PfsProcess::process()
{
2018-08-13 17:14:21 +00:00
importHeader();
if (mCliOutputMode.show_basic_info)
{
displayHeader();
if (mListFs || mCliOutputMode.show_extended_info)
displayFs();
}
if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify)
validateHfs();
2021-10-02 14:53:21 +00:00
if (mExtractPath.isSet())
extractFs();
}
void nstool::PfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void nstool::PfsProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void nstool::PfsProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void nstool::PfsProcess::setMountPointName(const std::string& mount_name)
{
mMountName = mount_name;
}
2021-10-02 14:53:21 +00:00
void nstool::PfsProcess::setExtractPath(const tc::io::Path& path)
{
mExtractPath = path;
}
void nstool::PfsProcess::setListFs(bool list_fs)
{
mListFs = list_fs;
}
const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const
{
return mPfs;
}
void nstool::PfsProcess::importHeader()
2018-08-13 17:14:21 +00:00
{
2021-10-02 14:53:21 +00:00
if (mFile == nullptr)
{
throw tc::Exception(mModuleName, "No file reader set.");
}
tc::ByteData scratch;
2018-08-13 17:14:21 +00:00
2021-10-02 14:53:21 +00:00
// read base header to determine complete header size
if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sPfsHeader)))
2018-08-13 17:14:21 +00:00
{
2021-10-02 14:53:21 +00:00
throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small");
2018-08-13 17:14:21 +00:00
}
2021-10-02 14:53:21 +00:00
scratch = tc::ByteData(sizeof(nn::hac::sPfsHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
2018-08-13 17:14:21 +00:00
if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
{
2021-10-02 14:53:21 +00:00
throw tc::Exception(mModuleName, "Corrupt PartitionFs: Header had incorrect struct magic.");
2018-08-13 17:14:21 +00:00
}
2021-10-02 14:53:21 +00:00
// read complete size header
2018-08-13 17:14:21 +00:00
size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data()));
2021-10-02 14:53:21 +00:00
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
2018-08-13 17:14:21 +00:00
mPfs.fromBytes(scratch.data(), scratch.size());
}
void nstool::PfsProcess::displayHeader()
{
2021-10-02 14:53:21 +00:00
fmt::print("[PartitionFS]\n");
fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()));
fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size());
2018-08-13 17:14:21 +00:00
if (mMountName.empty() == false)
{
2021-10-02 14:53:21 +00:00
fmt::print(" MountPoint: {:s}");
2018-08-13 17:14:21 +00:00
if (mMountName.at(mMountName.length()-1) != '/')
2021-10-02 14:53:21 +00:00
fmt::print("/");
fmt::print("\n");
2018-08-13 17:14:21 +00:00
}
}
void nstool::PfsProcess::displayFs()
{
2021-10-02 14:53:21 +00:00
auto file_list = mPfs.getFileList();
for (auto itr = file_list.begin(); itr != file_list.end(); itr++)
{
2021-10-02 14:53:21 +00:00
fmt::print(" {:s}", itr->name);
if (mCliOutputMode.show_layout)
{
2018-08-13 17:14:21 +00:00
switch (mPfs.getFsType())
{
case (nn::hac::PartitionFsHeader::TYPE_PFS0):
2021-10-02 14:53:21 +00:00
fmt::print(" (offset=0x{:x}, size=0x{:x})", itr->offset, itr->size);
2018-08-13 17:14:21 +00:00
break;
case (nn::hac::PartitionFsHeader::TYPE_HFS0):
2021-10-02 14:53:21 +00:00
fmt::print(" (offset=0x{:x}, size=0x{:x}, hash_protected_size=0x{:x})", itr->offset, itr->size, itr->hash_protected_size);
2018-08-13 17:14:21 +00:00
break;
}
}
2021-10-02 14:53:21 +00:00
fmt::print("\n");
}
}
size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr)
{
size_t fileEntrySize = 0;
2021-10-02 14:53:21 +00:00
if (hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic)
2018-08-07 07:17:51 +00:00
fileEntrySize = sizeof(nn::hac::sPfsFile);
else
2018-08-07 07:17:51 +00:00
fileEntrySize = sizeof(nn::hac::sHashedPfsFile);
2021-10-02 14:53:21 +00:00
return sizeof(nn::hac::sPfsHeader) + hdr->file_num.unwrap() * fileEntrySize + hdr->name_table_size.unwrap();
}
bool nstool::PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr)
{
2021-10-02 14:53:21 +00:00
return hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.unwrap() == nn::hac::pfs::kHashedPfsStructMagic;
}
void nstool::PfsProcess::validateHfs()
{
2021-10-02 14:53:21 +00:00
nn::hac::detail::sha256_hash_t hash;
auto file_list = mPfs.getFileList();
for (auto itr = file_list.begin(); itr != file_list.end(); itr++)
{
2021-10-02 14:53:21 +00:00
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)
{
2021-10-02 14:53:21 +00:00
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()
{
2021-10-02 14:53:21 +00:00
// create extract directory
tc::io::LocalStorage fs;
fs.createDirectory(mExtractPath.get());
2021-10-02 14:53:21 +00:00
// extract files
tc::ByteData cache_for_extract = tc::ByteData(kCacheSize);
2021-10-02 14:53:21 +00:00
auto file_list = mPfs.getFileList();
for (auto itr = file_list.begin(); itr != file_list.end(); itr++)
{
2021-10-02 14:53:21 +00:00
tc::io::Path extract_path = mExtractPath.get() + itr->name;
2021-10-02 14:53:21 +00:00
writeSubStreamToFile(mFile, itr->offset, itr->size, extract_path, cache_for_extract);
}
2018-08-13 17:14:21 +00:00
}