Editing PFS
Jump to navigation
Jump to search
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 1: | Line 1: | ||
'''PFS''' ( | '''PFS''' (Playstation File System) is the file system used by (at least) downloadable content and games on the PS4. It is loosely based on the [[wikipedia:UFS|UFS]] (Unix File System) used in FreeBSD. PFS typically uses a 64kB block size, although the block size is configurable and specified in the file system header. The minimum block size is 4kB, and the maximum seems to be 16MiB; the block size must be a power of 2. | ||
= Structure = | == Structure == | ||
There are four main sections in | There are four main sections in a Playstation File System: | ||
* Header (superblock) | * Header (superblock) | ||
* Inode blocks | * Inode blocks | ||
Line 9: | Line 9: | ||
* Data blocks | * Data blocks | ||
== Header/Superblock == | === Header/Superblock === | ||
{| class="wikitable" | {| class="wikitable" | ||
! Offset !! Value !! Size !! Notes | ! Offset !! Value !! Size !! Notes | ||
|- | |- | ||
| 0x00 || version || 0x8 || | | 0x00 || version || 0x8 || Should be 1 | ||
|- | |- | ||
| 0x08 || | | 0x08 || magic || 0x8 || Should be 20130315. Used like a version number for the PFS. | ||
|- | |- | ||
| 0x10 || id || 0x8 || | | 0x10 || id || 0x8 || | ||
Line 32: | Line 31: | ||
| 0x1E || unknown || 0x2 || | | 0x1E || unknown || 0x2 || | ||
|- | |- | ||
| 0x20 || blocksz || 0x4 || The size of each block in the filesystem | | 0x20 || blocksz || 0x4 || The size of each block in the filesystem. | ||
|- | |- | ||
| 0x24 || nbackup || 0x4 || Seems to always be 0 | | 0x24 || nbackup || 0x4 || Seems to always be 0 | ||
Line 46: | Line 45: | ||
| 0x48 || superroot_ino || 0x8 || | | 0x48 || superroot_ino || 0x8 || | ||
|} | |} | ||
<source lang="c"> | <source lang="c"> | ||
typedef struct { | typedef struct { | ||
Line 68: | Line 66: | ||
</source> | </source> | ||
== Inodes == | === Inodes === | ||
Inode table starts at the second block and continues for <tt>header.ndinodeblock</tt> blocks. | Inode table starts at the second block and continues for <tt>header.ndinodeblock</tt> blocks. | ||
There are only <tt>header.blocksz / sizeof(di_d32)</tt> inodes per block. (inodes will never cross a block boundary) | There are only <tt>header.blocksz / sizeof(di_d32)</tt> inodes per block. (inodes will never cross a block boundary) | ||
Line 104: | Line 101: | ||
| 0x94 || ib || 0x14 || Indirect blocks | | 0x94 || ib || 0x14 || Indirect blocks | ||
|} | |} | ||
<source lang="c"> | <source lang="c"> | ||
typedef struct { | typedef struct { | ||
Line 145: | Line 141: | ||
</source> | </source> | ||
== Dirents == | === Dirents === | ||
Each inode with a the <tt>mode.dir</tt> bit set (<tt>mode |= 0x4000</tt>) points to block(s) of dirents. Dirents contain the name and type of files, directories, symlinks, etc. Each directory will have an associated dirent block containing at least the '.' and '..' special files, along with all other files and sub-directories in that directory. Dirents are 8-byte aligned. The <tt>entsize</tt> value will say the total length of this dirent. There is typically padding after <tt>name</tt> which can just be skipped. | Each inode with a the <tt>mode.dir</tt> bit set (<tt>mode |= 0x4000</tt>) points to block(s) of dirents. Dirents contain the name and type of files, directories, symlinks, etc. Each directory will have an associated dirent block containing at least the '.' and '..' special files, along with all other files and sub-directories in that directory. Dirents are 8-byte aligned. The <tt>entsize</tt> value will say the total length of this dirent. There is typically padding after <tt>name</tt> which can just be skipped. | ||
Line 167: | Line 162: | ||
| 0x11 + namelen || padding || variable || Padding so this structure is exactly <tt>entsize</tt> bytes. | | 0x11 + namelen || padding || variable || Padding so this structure is exactly <tt>entsize</tt> bytes. | ||
|} | |} | ||
<source lang="c"> | <source lang="c"> | ||
typedef struct { | typedef struct { | ||
Line 178: | Line 172: | ||
</source> | </source> | ||
== Finding the root == | === Finding the root === | ||
The filesystem tree starts with the superroot, a sort of meta-directory that contains the root directory within it, along with something called a "flat_path_table". The superroot's inode is typically the first (zeroeth) inode entry in the table, but you should check <tt>PFS_HDR.superroot_ino</tt> for the actual index. The true root of the filesystem is the "uroot" directory within the super root. | The filesystem tree starts with the superroot, a sort of meta-directory that contains the root directory within it, along with something called a "flat_path_table". The superroot's inode is typically the first (zeroeth) inode entry in the table, but you should check <tt>PFS_HDR.superroot_ino</tt> for the actual index. The true root of the filesystem is the "uroot" directory within the super root. | ||
== flat_path_table == | === flat_path_table === | ||
The flat_path_table is a file located above the root directory on every PFS image. It is simply a mapping of filename hashes to inode number, to increase the lookup speed for files: | The flat_path_table is a file located above the root directory on every PFS image. It is simply a mapping of filename hashes to inode number, to increase the lookup speed for files: | ||
<source lang="c"> | <source lang="c"> | ||
typedef struct { | typedef struct { | ||
Line 194: | Line 185: | ||
The hashes are sorted in ascending order in the file. The hash function is given below: | The hashes are sorted in ascending order in the file. The hash function is given below: | ||
<source lang="c"> | <source lang="c"> | ||
uint32_t fptbl_hash(const char* filename){ | uint32_t fptbl_hash(const char* filename){ | ||
Line 210: | Line 200: | ||
[https://www.google.com/patents/EP2878348A1?cl=en Patent explaining the flat path table] | [https://www.google.com/patents/EP2878348A1?cl=en Patent explaining the flat path table] | ||
= Tools = | == Tools == | ||
* [https://github.com/maxton/GameArchives/releases/latest GameArchives/ArchiveExplorer | * [https://github.com/maxton/GameArchives/releases/latest GameArchives/ArchiveExplorer] C# library/tool that supports opening and extracting from PFS images | ||
* [https://github.com/maxton/MakePFS MakePFS | * [https://github.com/maxton/MakePFS MakePFS] C# tool for creating PFS images |