mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
[nx|pfstool] Add support for HFS0. Take out PFS consts and structs to a separate file.
This commit is contained in:
parent
fd9261b789
commit
109b64be52
4 changed files with 201 additions and 48 deletions
|
@ -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);
|
||||||
|
|
41
lib/libnx/include/nx/pfs.h
Normal file
41
lib/libnx/include/nx/pfs.h
Normal 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)
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue