2018-04-24 05:24:20 +00:00
|
|
|
#include "PfsProcess.h"
|
2018-04-24 07:51:29 +00:00
|
|
|
#include <fnd/SimpleFile.h>
|
2018-04-24 05:24:20 +00:00
|
|
|
#include <fnd/io.h>
|
|
|
|
|
|
|
|
void PfsProcess::displayHeader()
|
|
|
|
{
|
|
|
|
printf("[PartitionFS]\n");
|
|
|
|
printf(" Type: %s\n", mPfs.getFsType() == mPfs.TYPE_PFS0? "PFS0" : "HFS0");
|
|
|
|
printf(" FileNum: %u\n", mPfs.getFileList().getSize());
|
|
|
|
if (mMountName.empty() == false)
|
|
|
|
printf(" MountPoint: %s%s\n", mMountName.c_str(), mMountName.at(mMountName.length()-1) != '/' ? "/" : "");
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::displayFs()
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < mPfs.getFileList().getSize(); i++)
|
|
|
|
{
|
|
|
|
printf(" %s", mPfs.getFileList()[i].name.c_str());
|
|
|
|
if (mCliOutputType >= OUTPUT_VERBOSE)
|
|
|
|
{
|
|
|
|
if (mPfs.getFsType() == mPfs.TYPE_PFS0)
|
|
|
|
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", mPfs.getFileList()[i].offset, mPfs.getFileList()[i].size);
|
|
|
|
else
|
|
|
|
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ", hash_protected_size=0x%" PRIx64 ")\n", mPfs.getFileList()[i].offset, mPfs.getFileList()[i].size, mPfs.getFileList()[i].hash_protected_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t PfsProcess::determineHeaderSize(const nx::sPfsHeader* hdr)
|
|
|
|
{
|
|
|
|
size_t fileEntrySize = 0;
|
|
|
|
if (std::string(hdr->signature, 4) == nx::pfs::kPfsSig)
|
|
|
|
fileEntrySize = sizeof(nx::sPfsFile);
|
|
|
|
else
|
|
|
|
fileEntrySize = sizeof(nx::sHashedPfsFile);
|
|
|
|
|
|
|
|
return sizeof(nx::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::validateHfs()
|
|
|
|
{
|
|
|
|
fnd::MemoryBlob scratch;
|
|
|
|
crypto::sha::sSha256Hash hash;
|
|
|
|
const fnd::List<nx::PfsHeader::sFile>& file = mPfs.getFileList();
|
|
|
|
for (size_t i = 0; i < file.getSize(); i++)
|
|
|
|
{
|
|
|
|
scratch.alloc(file[i].hash_protected_size);
|
|
|
|
mReader->read(scratch.getBytes(), mOffset + file[i].offset, file[i].hash_protected_size);
|
|
|
|
crypto::sha::Sha256(scratch.getBytes(), scratch.getSize(), hash.bytes);
|
|
|
|
if (hash != file[i].hash)
|
|
|
|
{
|
|
|
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
|
|
|
printf("[WARNING] HFS0 %s%s%s: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", !mMountName.empty()? "/" : "", file[i].name.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::extractFs()
|
|
|
|
{
|
|
|
|
// allocate scratch memory
|
|
|
|
fnd::MemoryBlob scratch;
|
|
|
|
scratch.alloc(kFileExportBlockSize);
|
|
|
|
|
|
|
|
// make extract dir
|
|
|
|
fnd::io::makeDirectory(mExtractPath);
|
|
|
|
|
|
|
|
fnd::SimpleFile outFile;
|
|
|
|
const fnd::List<nx::PfsHeader::sFile>& file = mPfs.getFileList();
|
|
|
|
|
2018-04-25 15:21:05 +00:00
|
|
|
std::string file_path;
|
2018-04-24 05:24:20 +00:00
|
|
|
for (size_t i = 0; i < file.getSize(); i++)
|
|
|
|
{
|
2018-04-25 15:21:05 +00:00
|
|
|
file_path.clear();
|
|
|
|
fnd::io::appendToPath(file_path, mExtractPath);
|
|
|
|
fnd::io::appendToPath(file_path, file[i].name);
|
|
|
|
outFile.open(file_path, outFile.Create);
|
2018-04-24 05:24:20 +00:00
|
|
|
mReader->seek(mOffset + file[i].offset);
|
|
|
|
for (size_t j = 0; j < (file[i].size / kFileExportBlockSize); j++)
|
|
|
|
{
|
|
|
|
mReader->read(scratch.getBytes(), kFileExportBlockSize);
|
|
|
|
outFile.write(scratch.getBytes(), kFileExportBlockSize);
|
|
|
|
}
|
|
|
|
if (file[i].size % kFileExportBlockSize)
|
|
|
|
{
|
|
|
|
mReader->read(scratch.getBytes(), file[i].size % kFileExportBlockSize);
|
|
|
|
outFile.write(scratch.getBytes(), file[i].size % kFileExportBlockSize);
|
|
|
|
}
|
|
|
|
outFile.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PfsProcess::PfsProcess() :
|
|
|
|
mReader(nullptr),
|
|
|
|
mOffset(0),
|
|
|
|
mKeyset(nullptr),
|
|
|
|
mCliOutputType(OUTPUT_NORMAL),
|
|
|
|
mVerify(false),
|
|
|
|
mExtractPath(),
|
|
|
|
mExtract(false),
|
|
|
|
mMountName(),
|
|
|
|
mListFs(false),
|
|
|
|
mPfs()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::process()
|
|
|
|
{
|
|
|
|
fnd::MemoryBlob scratch;
|
2018-04-25 11:36:58 +00:00
|
|
|
|
|
|
|
if (mReader == nullptr)
|
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
|
|
|
}
|
2018-04-24 05:24:20 +00:00
|
|
|
|
|
|
|
// open minimum header to get full header size
|
|
|
|
scratch.alloc(sizeof(nx::sPfsHeader));
|
|
|
|
mReader->read(scratch.getBytes(), mOffset, scratch.getSize());
|
|
|
|
size_t pfsHeaderSize = determineHeaderSize(((nx::sPfsHeader*)scratch.getBytes()));
|
|
|
|
|
|
|
|
// open minimum header to get full header size
|
|
|
|
scratch.alloc(pfsHeaderSize);
|
|
|
|
mReader->read(scratch.getBytes(), mOffset, scratch.getSize());
|
|
|
|
mPfs.importBinary(scratch.getBytes(), scratch.getSize());
|
|
|
|
|
|
|
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
|
|
|
displayHeader();
|
|
|
|
if (mListFs || mCliOutputType >= OUTPUT_VERBOSE)
|
|
|
|
displayFs();
|
|
|
|
if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify)
|
|
|
|
validateHfs();
|
|
|
|
if (mExtract)
|
|
|
|
extractFs();
|
|
|
|
}
|
|
|
|
|
2018-05-11 13:20:28 +00:00
|
|
|
void PfsProcess::setInputFile(fnd::IFile* reader)
|
2018-04-24 05:24:20 +00:00
|
|
|
{
|
2018-05-11 13:20:28 +00:00
|
|
|
mReader = reader;
|
2018-04-24 05:24:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setInputFileOffset(size_t offset)
|
|
|
|
{
|
|
|
|
mOffset = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setKeyset(const sKeyset* keyset)
|
|
|
|
{
|
|
|
|
mKeyset = keyset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PfsProcess::setCliOutputMode(CliOutputType type)
|
|
|
|
{
|
|
|
|
mCliOutputType = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
const nx::PfsHeader& PfsProcess::getPfsHeader() const
|
|
|
|
{
|
|
|
|
return mPfs;
|
|
|
|
}
|