[nx|pfstool] Add support for HFS0. Take out PFS consts and structs to a separate file.

This commit is contained in:
jakcron 2018-04-07 16:03:01 +08:00
parent fd9261b789
commit 109b64be52
4 changed files with 201 additions and 48 deletions

View file

@ -4,6 +4,7 @@
#include <fnd/MemoryBlob.h> #include <fnd/MemoryBlob.h>
#include <fnd/List.h> #include <fnd/List.h>
#include <fnd/ISerialiseableBinary.h> #include <fnd/ISerialiseableBinary.h>
#include <nx/pfs.h>
namespace nx namespace nx
@ -12,17 +13,27 @@ namespace nx
public fnd::ISerialiseableBinary public fnd::ISerialiseableBinary
{ {
public: public:
enum FsType
{
TYPE_PFS0,
TYPE_HFS0
};
struct sFile struct sFile
{ {
std::string name; std::string name;
size_t offset; size_t offset;
size_t size; size_t size;
size_t hash_protected_size;
crypto::sha::sSha256Hash hash;
sFile& operator=(const sFile& other) sFile& operator=(const sFile& other)
{ {
name = other.name; name = other.name;
offset = other.offset; offset = other.offset;
size = other.size; size = other.size;
hash_protected_size = other.hash_protected_size;
hash = other.hash;
return *this; return *this;
} }
@ -30,7 +41,9 @@ namespace nx
{ {
return (name == other.name) \ return (name == other.name) \
&& (offset == other.offset) \ && (offset == other.offset) \
&& (size == other.size); && (size == other.size) \
&& (hash_protected_size == other.hash_protected_size) \
&& (hash == other.hash);
} }
bool operator!=(const sFile& other) const bool operator!=(const sFile& other) const
@ -68,36 +81,24 @@ namespace nx
// variables // variables
void clear(); void clear();
FsType getFsType() const;
void setFsType(FsType type);
const fnd::List<sFile>& getFileList() const; const fnd::List<sFile>& getFileList() const;
void addFile(const std::string& name, size_t size); void addFile(const std::string& name, size_t size);
void addFile(const std::string& name, size_t size, size_t hash_protected_size, const crypto::sha::sSha256Hash& hash);
private: private:
const std::string kModuleName = "PFS_HEADER"; const std::string kModuleName = "PFS_HEADER";
const std::string kPfsStructSig = "PFS0";
static const size_t kPfsAlign = 0x40;
#pragma pack (push, 1)
struct sPfsFile
{
le_uint64_t data_offset;
le_uint64_t size;
le_uint64_t name_offset;
};
struct sPfsHeader
{
char signature[4];
le_uint32_t file_num;
le_uint64_t name_table_size;
};
#pragma pack (pop)
// binary blob // binary blob
fnd::MemoryBlob mBinaryBlob; fnd::MemoryBlob mBinaryBlob;
// variables // variables
FsType mFsType;
fnd::List<sFile> mFileList; fnd::List<sFile> mFileList;
size_t getFileEntrySize(FsType fs_type);
void calculateOffsets(size_t data_offset); void calculateOffsets(size_t data_offset);
bool isEqual(const PfsHeader& other) const; bool isEqual(const PfsHeader& other) const;
void copyFrom(const PfsHeader& other); void copyFrom(const PfsHeader& other);

View file

@ -0,0 +1,41 @@
#include <string>
#include <fnd/types.h>
#include <crypto/sha.h>
#include <fnd/ISerialiseableBinary.h>
namespace nx
{
namespace pfs
{
const std::string kPfsSig = "PFS0";
const std::string kHashedPfsSig = "HFS0";
static const size_t kAlignLen = 64;
}
#pragma pack(push,1)
struct sPfsHeader
{
char signature[4];
le_uint32_t file_num;
le_uint64_t name_table_size;
};
struct sPfsFile
{
le_uint64_t data_offset;
le_uint64_t size;
le_uint32_t name_offset;
byte_t padding[4];
}; // sizeof(0x18)
struct sHashedPfsFile
{
le_uint64_t data_offset;
le_uint64_t size;
le_uint32_t name_offset;
le_uint32_t hash_protected_size;
byte_t padding[8];
crypto::sha::sSha256Hash hash;
}; // sizeof(0x40)
#pragma pack(pop)
}

View file

@ -24,35 +24,67 @@ void nx::PfsHeader::exportBinary()
name_table_size += mFileList[i].name.length() + 1; name_table_size += mFileList[i].name.length() + 1;
} }
size_t pfs_header_size = align(sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize() + name_table_size, kPfsAlign); size_t pfs_header_size = align(sizeof(sPfsHeader) + getFileEntrySize(mFsType) * mFileList.getSize() + name_table_size, pfs::kAlignLen);
// align name_table_size // align name_table_size
name_table_size = pfs_header_size - (sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize()); name_table_size = pfs_header_size - (sizeof(sPfsHeader) + getFileEntrySize(mFsType) * mFileList.getSize());
// allocate pfs header binary // allocate pfs header binary
mBinaryBlob.alloc(pfs_header_size); mBinaryBlob.alloc(pfs_header_size);
sPfsHeader* hdr = (sPfsHeader*)mBinaryBlob.getBytes(); sPfsHeader* hdr = (sPfsHeader*)mBinaryBlob.getBytes();
// set header fields // set header fields
strncpy(hdr->signature, kPfsStructSig.c_str(), 4); switch (mFsType)
{
case (TYPE_PFS0):
strncpy(hdr->signature, pfs::kPfsSig.c_str(), 4);
break;
case (TYPE_HFS0):
strncpy(hdr->signature, pfs::kHashedPfsSig.c_str(), 4);
break;
}
hdr->file_num = mFileList.getSize(); hdr->file_num = mFileList.getSize();
hdr->name_table_size = name_table_size; hdr->name_table_size = name_table_size;
// set file entries // set file entries
sPfsFile* raw_files = (sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader)); if (mFsType == TYPE_PFS0)
char* raw_name_table = (char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize());
size_t raw_name_table_pos = 0;
calculateOffsets(pfs_header_size);
for (size_t i = 0; i < mFileList.getSize(); i++)
{ {
raw_files[i].data_offset = (mFileList[i].offset - pfs_header_size); sPfsFile* raw_files = (sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader));
raw_files[i].size = (mFileList[i].size); char* raw_name_table = (char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize());
raw_files[i].name_offset = (raw_name_table_pos); size_t raw_name_table_pos = 0;
strcpy(raw_name_table + raw_name_table_pos, mFileList[i].name.c_str()); calculateOffsets(pfs_header_size);
raw_name_table_pos += mFileList[i].name.length() + 1; for (size_t i = 0; i < mFileList.getSize(); i++)
{
raw_files[i].data_offset = (mFileList[i].offset - pfs_header_size);
raw_files[i].size = mFileList[i].size;
raw_files[i].name_offset = raw_name_table_pos;
strcpy(raw_name_table + raw_name_table_pos, mFileList[i].name.c_str());
raw_name_table_pos += mFileList[i].name.length() + 1;
}
} }
else if (mFsType == TYPE_HFS0)
{
sHashedPfsFile* raw_files = (sHashedPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader));
char* raw_name_table = (char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sHashedPfsFile) * mFileList.getSize());
size_t raw_name_table_pos = 0;
calculateOffsets(pfs_header_size);
for (size_t i = 0; i < mFileList.getSize(); i++)
{
raw_files[i].data_offset = (mFileList[i].offset - pfs_header_size);
raw_files[i].size = mFileList[i].size;
raw_files[i].name_offset = raw_name_table_pos;
raw_files[i].hash_protected_size = mFileList[i].hash_protected_size;
raw_files[i].hash = mFileList[i].hash;
strcpy(raw_name_table + raw_name_table_pos, mFileList[i].name.c_str());
raw_name_table_pos += mFileList[i].name.length() + 1;
}
}
} }
void nx::PfsHeader::importBinary(const byte_t * bytes, size_t len) void nx::PfsHeader::importBinary(const byte_t * bytes, size_t len)
@ -69,13 +101,16 @@ void nx::PfsHeader::importBinary(const byte_t * bytes, size_t len)
const sPfsHeader* hdr = (const sPfsHeader*)mBinaryBlob.getBytes(); const sPfsHeader* hdr = (const sPfsHeader*)mBinaryBlob.getBytes();
// check struct signature // check struct signature
if (memcmp(hdr->signature, kPfsStructSig.c_str(), 4) != 0) FsType fs_type;
{ if (memcmp(hdr->signature, pfs::kPfsSig.c_str(), 4) == 0)
fs_type = TYPE_PFS0;
else if (memcmp(hdr->signature, pfs::kHashedPfsSig.c_str(), 4) == 0)
fs_type = TYPE_HFS0;
else
throw fnd::Exception(kModuleName, "PFS header corrupt"); throw fnd::Exception(kModuleName, "PFS header corrupt");
}
// determine complete header size // determine complete header size
size_t pfs_full_header_size = sizeof(sPfsHeader) + sizeof(sPfsFile) * hdr->file_num.get() + hdr->name_table_size.get(); size_t pfs_full_header_size = sizeof(sPfsHeader) + getFileEntrySize(fs_type) * hdr->file_num.get() + hdr->name_table_size.get();
// check input length meets complete size // check input length meets complete size
if (len < pfs_full_header_size) if (len < pfs_full_header_size)
@ -91,23 +126,61 @@ void nx::PfsHeader::importBinary(const byte_t * bytes, size_t len)
// clear variables // clear variables
clear(); clear();
// get pointers to raw data mFsType = fs_type;
const sPfsFile* raw_files = (const sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader)); if (mFsType == TYPE_PFS0)
const char* raw_name_table = (const char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * hdr->file_num.get());
// process file entries
for (size_t i = 0; i < hdr->file_num.get(); i++)
{ {
mFileList.addElement({ std::string(raw_name_table + raw_files[i].name_offset.get()), raw_files[i].data_offset.get() + pfs_full_header_size, raw_files[i].size.get() }); // get pointers to raw data
const sPfsFile* raw_files = (const sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader));
const char* raw_name_table = (const char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * hdr->file_num.get());
// process file entries
for (size_t i = 0; i < hdr->file_num.get(); i++)
{
mFileList.addElement({
std::string(raw_name_table + raw_files[i].name_offset.get()),
raw_files[i].data_offset.get() + pfs_full_header_size,
raw_files[i].size.get()
});
}
} }
else if (mFsType == TYPE_HFS0)
{
// get pointers to raw data
const sHashedPfsFile* raw_files = (const sHashedPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader));
const char* raw_name_table = (const char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sHashedPfsFile) * hdr->file_num.get());
// process file entries
for (size_t i = 0; i < hdr->file_num.get(); i++)
{
mFileList.addElement({
std::string(raw_name_table + raw_files[i].name_offset.get()),
raw_files[i].data_offset.get() + pfs_full_header_size,
raw_files[i].size.get(),
raw_files[i].hash_protected_size.get(),
raw_files[i].hash
});
}
}
} }
void nx::PfsHeader::clear() void nx::PfsHeader::clear()
{ {
mBinaryBlob.clear(); mBinaryBlob.clear();
mFsType = TYPE_PFS0;
mFileList.clear(); mFileList.clear();
} }
nx::PfsHeader::FsType nx::PfsHeader::getFsType() const
{
return mFsType;
}
void nx::PfsHeader::setFsType(FsType type)
{
mFsType = type;
}
const fnd::List<nx::PfsHeader::sFile>& nx::PfsHeader::getFileList() const const fnd::List<nx::PfsHeader::sFile>& nx::PfsHeader::getFileList() const
{ {
return mFileList; return mFileList;
@ -115,7 +188,29 @@ const fnd::List<nx::PfsHeader::sFile>& nx::PfsHeader::getFileList() const
void nx::PfsHeader::addFile(const std::string & name, size_t size) void nx::PfsHeader::addFile(const std::string & name, size_t size)
{ {
mFileList.addElement({ name, 0, size }); mFileList.addElement({ name, 0, size, 0 });
}
void nx::PfsHeader::addFile(const std::string & name, size_t size, size_t hash_protected_size, const crypto::sha::sSha256Hash& hash)
{
mFileList.addElement({ name, 0, size, hash_protected_size, hash });
}
size_t nx::PfsHeader::getFileEntrySize(FsType fs_type)
{
size_t size = 0;
switch(fs_type)
{
case (TYPE_PFS0):
size = sizeof(sPfsFile);
break;
case (TYPE_HFS0):
size = sizeof(sHashedPfsFile);
break;
default:
throw fnd::Exception(kModuleName, "Unknown PFS type");
}
return size;
} }
void nx::PfsHeader::calculateOffsets(size_t data_offset) void nx::PfsHeader::calculateOffsets(size_t data_offset)
@ -128,7 +223,7 @@ void nx::PfsHeader::calculateOffsets(size_t data_offset)
bool nx::PfsHeader::isEqual(const PfsHeader & other) const bool nx::PfsHeader::isEqual(const PfsHeader & other) const
{ {
return mFileList == other.mFileList; return (mFsType == other.mFsType) && (mFileList == other.mFileList);
} }
void nx::PfsHeader::copyFrom(const PfsHeader & other) void nx::PfsHeader::copyFrom(const PfsHeader & other)
@ -140,6 +235,7 @@ void nx::PfsHeader::copyFrom(const PfsHeader & other)
else else
{ {
clear(); clear();
mFsType = other.mFsType;
mFileList = other.mFileList; mFileList = other.mFileList;
} }
} }

View file

@ -11,6 +11,13 @@
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
std::string kFsTypeStr[]
{
"PFS0",
"HFS0"
};
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
if (argc < 2) if (argc < 2)
@ -37,10 +44,18 @@ int main(int argc, char** argv)
#endif #endif
} }
printf("[PFS]\n"); printf("[PartitionFS]\n");
printf(" Type: %s\n", kFsTypeStr[pfs.getFsType()].c_str());
printf(" FileSystem: (%d files)\n", pfs.getFileList().getSize());
for (size_t i = 0; i < pfs.getFileList().getSize(); i++) for (size_t i = 0; i < pfs.getFileList().getSize(); i++)
{ {
printf(" %s (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", pfs.getFileList()[i].name.c_str(), pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
printf(" %s", pfs.getFileList()[i].name.c_str());
if (pfs.getFsType() == pfs.TYPE_PFS0)
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
else
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ", hash_protected_size=0x%" PRIx64 ")\n", pfs.getFileList()[i].offset, pfs.getFileList()[i].size, pfs.getFileList()[i].hash_protected_size);
if (argc == 3) if (argc == 3)
{ {
#ifdef _WIN32 #ifdef _WIN32