2020-02-26 09:05:51 +00:00
|
|
|
#include "PfsProcess.h"
|
|
|
|
|
2018-08-13 17:14:21 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
2020-02-26 09:05:51 +00:00
|
|
|
|
2018-04-24 07:51:29 +00:00
|
|
|
#include <fnd/SimpleFile.h>
|
2018-04-24 05:24:20 +00:00
|
|
|
#include <fnd/io.h>
|
2020-02-26 09:05:51 +00:00
|
|
|
|
|
|
|
#include <nn/hac/PartitionFsUtil.h>
|
|
|
|
|
2018-04-24 05:24:20 +00:00
|
|
|
|
|
|
|
PfsProcess::PfsProcess() :
|
2018-09-23 03:29:22 +00:00
|
|
|
mFile(),
|
2018-06-18 15:30:19 +00:00
|
|
|
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
2018-04-24 05:24:20 +00:00
|
|
|
mVerify(false),
|
|
|
|
mExtractPath(),
|
|
|
|
mExtract(false),
|
|
|
|
mMountName(),
|
|
|
|
mListFs(false),
|
|
|
|
mPfs()
|
|
|
|
{
|
2018-05-12 15:02:53 +00:00
|
|
|
}
|
2018-04-24 05:24:20 +00:00
|
|
|
|
|
|
|
void PfsProcess::process()
|
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
importHeader();
|
2018-04-24 05:24:20 +00:00
|
|
|
|
2018-06-18 15:30:19 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
|
|
|
|
{
|
2018-04-24 05:24:20 +00:00
|
|
|
displayHeader();
|
2018-06-18 15:30:19 +00:00
|
|
|
if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
|
|
|
|
displayFs();
|
|
|
|
}
|
2018-04-24 05:24:20 +00:00
|
|
|
if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify)
|
|
|
|
validateHfs();
|
|
|
|
if (mExtract)
|
|
|
|
extractFs();
|
|
|
|
}
|
|
|
|
|
2018-09-23 03:29:22 +00:00
|
|
|
void PfsProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
|
2018-04-24 05:24:20 +00:00
|
|
|
{
|
2018-06-03 08:48:12 +00:00
|
|
|
mFile = file;
|
2018-04-24 05:24:20 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 15:30:19 +00:00
|
|
|
void PfsProcess::setCliOutputMode(CliOutputMode type)
|
2018-04-24 05:24:20 +00:00
|
|
|
{
|
2018-06-18 15:30:19 +00:00
|
|
|
mCliOutputMode = type;
|
2018-04-24 05:24:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setVerifyMode(bool verify)
|
|
|
|
{
|
|
|
|
mVerify = verify;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setMountPointName(const std::string& mount_name)
|
|
|
|
{
|
|
|
|
mMountName = mount_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setExtractPath(const std::string& path)
|
|
|
|
{
|
|
|
|
mExtract = true;
|
|
|
|
mExtractPath = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setListFs(bool list_fs)
|
|
|
|
{
|
|
|
|
mListFs = list_fs;
|
|
|
|
}
|
|
|
|
|
2018-10-27 05:43:57 +00:00
|
|
|
const nn::hac::PartitionFsHeader& PfsProcess::getPfsHeader() const
|
2018-04-24 05:24:20 +00:00
|
|
|
{
|
|
|
|
return mPfs;
|
|
|
|
}
|
2018-05-26 13:13:21 +00:00
|
|
|
|
2018-08-13 17:14:21 +00:00
|
|
|
void PfsProcess::importHeader()
|
|
|
|
{
|
|
|
|
fnd::Vec<byte_t> scratch;
|
|
|
|
|
2018-09-23 03:29:22 +00:00
|
|
|
if (*mFile == nullptr)
|
2018-08-13 17:14:21 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// open minimum header to get full header size
|
|
|
|
scratch.alloc(sizeof(nn::hac::sPfsHeader));
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(scratch.data(), 0, scratch.size());
|
2018-08-13 17:14:21 +00:00
|
|
|
if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
|
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "Corrupt Header");
|
|
|
|
}
|
|
|
|
size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data()));
|
|
|
|
|
|
|
|
// open minimum header to get full header size
|
|
|
|
scratch.alloc(pfsHeaderSize);
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(scratch.data(), 0, scratch.size());
|
2018-08-13 17:14:21 +00:00
|
|
|
mPfs.fromBytes(scratch.data(), scratch.size());
|
|
|
|
}
|
|
|
|
|
2018-05-26 13:13:21 +00:00
|
|
|
void PfsProcess::displayHeader()
|
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << "[PartitionFS]" << std::endl;
|
2020-02-26 09:05:51 +00:00
|
|
|
std::cout << " Type: " << nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()) << std::endl;
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << " FileNum: " << std::dec << mPfs.getFileList().size() << std::endl;
|
|
|
|
if (mMountName.empty() == false)
|
|
|
|
{
|
|
|
|
std::cout << " MountPoint: " << mMountName;
|
|
|
|
if (mMountName.at(mMountName.length()-1) != '/')
|
|
|
|
std::cout << "/";
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::displayFs()
|
|
|
|
{
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < mPfs.getFileList().size(); i++)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-10-27 05:43:57 +00:00
|
|
|
const nn::hac::PartitionFsHeader::sFile& file = mPfs.getFileList()[i];
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << " " << file.name;
|
2018-06-18 15:30:19 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
switch (mPfs.getFsType())
|
|
|
|
{
|
2018-10-27 05:43:57 +00:00
|
|
|
case (nn::hac::PartitionFsHeader::TYPE_PFS0):
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")";
|
|
|
|
break;
|
2018-10-27 05:43:57 +00:00
|
|
|
case (nn::hac::PartitionFsHeader::TYPE_HFS0):
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ", hash_protected_size=0x" << file.hash_protected_size << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << std::endl;
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
size_t PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
|
|
|
size_t fileEntrySize = 0;
|
2018-08-07 07:17:51 +00:00
|
|
|
if (hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic)
|
|
|
|
fileEntrySize = sizeof(nn::hac::sPfsFile);
|
2018-05-26 13:13:21 +00:00
|
|
|
else
|
2018-08-07 07:17:51 +00:00
|
|
|
fileEntrySize = sizeof(nn::hac::sHashedPfsFile);
|
2018-05-26 13:13:21 +00:00
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
return sizeof(nn::hac::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get();
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
bool PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-08-07 07:17:51 +00:00
|
|
|
return hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.get() == nn::hac::pfs::kHashedPfsStructMagic;
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::validateHfs()
|
|
|
|
{
|
2018-08-07 08:35:03 +00:00
|
|
|
fnd::sha::sSha256Hash hash;
|
2018-10-27 05:43:57 +00:00
|
|
|
const fnd::List<nn::hac::PartitionFsHeader::sFile>& file = mPfs.getFileList();
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < file.size(); i++)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-05-27 10:17:34 +00:00
|
|
|
mCache.alloc(file[i].hash_protected_size);
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(mCache.data(), file[i].offset, file[i].hash_protected_size);
|
2018-08-07 08:35:03 +00:00
|
|
|
fnd::sha::Sha256(mCache.data(), file[i].hash_protected_size, hash.bytes);
|
2018-05-26 13:13:21 +00:00
|
|
|
if (hash != file[i].hash)
|
|
|
|
{
|
2018-06-18 15:30:19 +00:00
|
|
|
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());
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::extractFs()
|
|
|
|
{
|
|
|
|
// allocate only when extractDir is invoked
|
2018-05-27 10:17:34 +00:00
|
|
|
mCache.alloc(kCacheSize);
|
2018-05-26 13:13:21 +00:00
|
|
|
|
|
|
|
// make extract dir
|
|
|
|
fnd::io::makeDirectory(mExtractPath);
|
|
|
|
|
|
|
|
fnd::SimpleFile outFile;
|
2018-10-27 05:43:57 +00:00
|
|
|
const fnd::List<nn::hac::PartitionFsHeader::sFile>& file = mPfs.getFileList();
|
2018-05-26 13:13:21 +00:00
|
|
|
|
|
|
|
std::string file_path;
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < file.size(); i++)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
|
|
|
file_path.clear();
|
|
|
|
fnd::io::appendToPath(file_path, mExtractPath);
|
|
|
|
fnd::io::appendToPath(file_path, file[i].name);
|
|
|
|
|
2018-06-18 15:30:19 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
|
2018-05-26 13:13:21 +00:00
|
|
|
printf("extract=[%s]\n", file_path.c_str());
|
|
|
|
|
|
|
|
outFile.open(file_path, outFile.Create);
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->seek(file[i].offset);
|
2018-05-27 10:17:34 +00:00
|
|
|
for (size_t j = 0; j < ((file[i].size / kCacheSize) + ((file[i].size % kCacheSize) != 0)); j++)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize));
|
2018-06-24 15:01:16 +00:00
|
|
|
outFile.write(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize));
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
outFile.close();
|
|
|
|
}
|
2018-08-13 17:14:21 +00:00
|
|
|
}
|