From 109b64be5252adb80ea81a1cfc28ef50ee4354a3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 7 Apr 2018 16:03:01 +0800 Subject: [PATCH] [nx|pfstool] Add support for HFS0. Take out PFS consts and structs to a separate file. --- lib/libnx/include/nx/PfsHeader.h | 39 ++++---- lib/libnx/include/nx/pfs.h | 41 +++++++++ lib/libnx/source/PfsHeader.cpp | 150 +++++++++++++++++++++++++------ programs/pfstool/source/main.cpp | 19 +++- 4 files changed, 201 insertions(+), 48 deletions(-) create mode 100644 lib/libnx/include/nx/pfs.h diff --git a/lib/libnx/include/nx/PfsHeader.h b/lib/libnx/include/nx/PfsHeader.h index f2f109f..c3a76a1 100644 --- a/lib/libnx/include/nx/PfsHeader.h +++ b/lib/libnx/include/nx/PfsHeader.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace nx @@ -12,17 +13,27 @@ namespace nx public fnd::ISerialiseableBinary { public: + enum FsType + { + TYPE_PFS0, + TYPE_HFS0 + }; + struct sFile { std::string name; size_t offset; size_t size; + size_t hash_protected_size; + crypto::sha::sSha256Hash hash; sFile& operator=(const sFile& other) { name = other.name; offset = other.offset; size = other.size; + hash_protected_size = other.hash_protected_size; + hash = other.hash; return *this; } @@ -30,7 +41,9 @@ namespace nx { return (name == other.name) \ && (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 @@ -68,36 +81,24 @@ namespace nx // variables void clear(); + + FsType getFsType() const; + void setFsType(FsType type); const fnd::List& getFileList() const; 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: 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 fnd::MemoryBlob mBinaryBlob; // variables + FsType mFsType; fnd::List mFileList; + size_t getFileEntrySize(FsType fs_type); void calculateOffsets(size_t data_offset); bool isEqual(const PfsHeader& other) const; void copyFrom(const PfsHeader& other); diff --git a/lib/libnx/include/nx/pfs.h b/lib/libnx/include/nx/pfs.h new file mode 100644 index 0000000..4666488 --- /dev/null +++ b/lib/libnx/include/nx/pfs.h @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +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) +} diff --git a/lib/libnx/source/PfsHeader.cpp b/lib/libnx/source/PfsHeader.cpp index adade27..1b5755e 100644 --- a/lib/libnx/source/PfsHeader.cpp +++ b/lib/libnx/source/PfsHeader.cpp @@ -24,35 +24,67 @@ void nx::PfsHeader::exportBinary() 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 - 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 mBinaryBlob.alloc(pfs_header_size); sPfsHeader* hdr = (sPfsHeader*)mBinaryBlob.getBytes(); // 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->name_table_size = name_table_size; // set file entries - sPfsFile* raw_files = (sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader)); - 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++) + if (mFsType == TYPE_PFS0) { - 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); + sPfsFile* raw_files = (sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader)); + char* raw_name_table = (char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize()); + size_t raw_name_table_pos = 0; - strcpy(raw_name_table + raw_name_table_pos, mFileList[i].name.c_str()); - raw_name_table_pos += mFileList[i].name.length() + 1; + 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; + + 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) @@ -69,13 +101,16 @@ void nx::PfsHeader::importBinary(const byte_t * bytes, size_t len) const sPfsHeader* hdr = (const sPfsHeader*)mBinaryBlob.getBytes(); // 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"); - } // 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 if (len < pfs_full_header_size) @@ -91,23 +126,61 @@ void nx::PfsHeader::importBinary(const byte_t * bytes, size_t len) // clear variables clear(); - // 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++) + mFsType = fs_type; + if (mFsType == TYPE_PFS0) { - 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() { mBinaryBlob.clear(); + mFsType = TYPE_PFS0; mFileList.clear(); } +nx::PfsHeader::FsType nx::PfsHeader::getFsType() const +{ + return mFsType; +} + +void nx::PfsHeader::setFsType(FsType type) +{ + mFsType = type; +} + const fnd::List& nx::PfsHeader::getFileList() const { return mFileList; @@ -115,7 +188,29 @@ const fnd::List& nx::PfsHeader::getFileList() const 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) @@ -128,7 +223,7 @@ void nx::PfsHeader::calculateOffsets(size_t data_offset) 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) @@ -140,6 +235,7 @@ void nx::PfsHeader::copyFrom(const PfsHeader & other) else { clear(); + mFsType = other.mFsType; mFileList = other.mFileList; } } diff --git a/programs/pfstool/source/main.cpp b/programs/pfstool/source/main.cpp index d86507a..8e0e4a8 100644 --- a/programs/pfstool/source/main.cpp +++ b/programs/pfstool/source/main.cpp @@ -11,6 +11,13 @@ #include #endif + +std::string kFsTypeStr[] +{ + "PFS0", + "HFS0" +}; + int main(int argc, char** argv) { if (argc < 2) @@ -37,10 +44,18 @@ int main(int argc, char** argv) #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++) { - 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) { #ifdef _WIN32