Skip to content

About

Create and Manage Unsigned PFS (Playstation File System) image files with file compression and checksum support.

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

MkPFS.C#

CI License: GPL-3.0

Build, check, and repair PS4/PS5 PFS game images (.ffpfs, .ffpfsc, .exfat) from the command line or a desktop app.

MkPFS.C# is a .NET 10 port of the Python MkPFS by PSBrew. It ships as a native executable (no Python needed), writes the same images as the original, and adds an offline PFSC block repair ported from PS5 Game Compressor.

Features

  • Pack: a game folder, an exFAT image, or any single file into a compressed .ffpfsc (PFSC, zlib), or a folder into a plain PFS image (--raw, with signed, encrypted, 64-bit inode, and PS4 options).
  • exFAT: build exFAT images from game folders (deterministic, 64 KiB clusters by default).
  • Batch: pack every game folder and image in a folder in one run.
  • Check: verify, inspect, tree, and unpack for PFS, PFSC, and exFAT images, including encrypted ones (--ekpfs-key).
  • Repair: find and fix compressed blocks the PS5 may decode wrongly (images made with ISA-L).
  • APR Emu: ampr_emu.index is created or refreshed automatically for games that use it.
  • GUI: mkpfs-gui with a page per command, cover and metadata preview, batch queue, and a PFSC block map; English, Português (BR), and Español.

Download

Get the archive for your system from the releases page and unpack it anywhere:

System Command line Desktop app
Windows x64 mkpfs-<version>-win-x64.zip mkpfs-gui-<version>-win-x64.zip
Linux x64 mkpfs-<version>-linux-x64.tar.gz mkpfs-gui-<version>-linux-x64.tar.gz
macOS Apple silicon mkpfs-<version>-osx-arm64.tar.gz mkpfs-gui-<version>-osx-arm64.tar.gz
  • Keep each program in its folder with the libraries next to it (mkpfs_zlib, plus Skia and HarfBuzz for the desktop app).
  • macOS: the desktop app is MkPFS.C#.app. It is not notarized, so open it the first time with right-click > Open.
  • Linux: the desktop app needs X11 and fontconfig, which desktop distributions include.
  • SHA256SUMS.txt on the release page lists the archive checksums.
  • Check the command line with mkpfs selftest.

Usage

All paths may be absolute or relative to the current directory. Replace values in angle brackets with your own paths; square brackets indicate an optional argument. Run mkpfs <command> --help for the parser's built-in help.

Commands at a glance

Command Arguments Default result Purpose
pack folder <source_dir> <image_file> game/homebrew folder, output path exFAT wrapped in a compressed .ffpfsc Package a game folder.
pack file <source_file> <image_file> input file, output path compressed .ffpfsc Package one file in a PFS container.
pack exfat <source_dir> [output] game/homebrew folder, optional output path <titleId>.exfat beside the source Build an uncompressed exFAT image.
batch <source_dir> <output_dir> directory of folders/images, destination directory one .ffpfsc per discovered item Package many inputs; existing outputs are skipped.
verify <image_file> image path — Validate an image, optionally against its source.
inspect <image_file> image path text report Show image metadata and integrity information.
tree <image_file> folder or image path outer tree List files and directories.
unpack <image_file> <output_dir> image path, destination directory — Extract an image.
repair <image_file> single-file .ffpfsc path repairs risky blocks Repair PFSC blocks that a PS5 may decode incorrectly.

Common examples

Pack a game folder into a .ffpfsc (wrapped in exFAT and compressed in one pass):

mkpfs pack folder PPSA12345-app PPSA12345.ffpfsc

Compress an existing exFAT image:

mkpfs pack file PPSA12345.exfat PPSA12345.ffpfsc

Pack a folder directly as PFS (--signed, --encrypted, --inode-bits 64, and --version PS4 apply here):

mkpfs pack folder PPSA12345-app PPSA12345.ffpfs --raw

Build an exFAT image from a game folder:

mkpfs pack exfat PPSA12345-app PPSA12345.exfat

Pack every game folder and image file in a folder (existing outputs are skipped):

mkpfs batch ./games ./output

Verify an image against its source:

mkpfs verify PPSA12345.ffpfsc --source-file PPSA12345.exfat

Show image details (--format json for scripts):

mkpfs inspect PPSA12345.ffpfsc

List the files, including inside a wrapped exFAT:

mkpfs tree PPSA12345.ffpfsc --deep

Extract the game files:

mkpfs unpack PPSA12345.ffpfsc out --deep

Repair blocks the PS5 may decode wrongly in a single-file .ffpfsc:

mkpfs repair PPSA12345.ffpfsc

pack folder and pack file

Both commands require a source path and an output image path. By default, the output extension is changed to .ffpfsc when necessary. The default build is PS5, 32-bit inodes, case-insensitive, zlib level 7, 64 KiB blocks, and compression enabled. Direct-PFS folder builds and single-file builds also run a quick structure verification by default. The default exFAT-wrapped folder flow runs a post-pack check only when --verify is supplied.

For the default exFAT-wrapped pack folder flow, compression remains enabled and the PFS block size remains 64 KiB. Use --raw to make options that control direct PFS layout or compression (--no-compress, --block-size, --inode-bits, --max-compressed-ratio, --min-compress-size, and --skip-executable-compression) take effect.

Option Default Meaning
--adjust-output-file-extension on Change the requested output extension to match the selected pack mode.
--no-adjust-output-file-extension off Keep the output filename exactly as supplied. Cannot be combined with --adjust-output-file-extension.
--compress / --no-compress compression on Enable or disable PFSC block compression. The two flags are mutually exclusive.
--threshold-gain <0-100> 0 Keep a compressed block only when it saves at least this percentage.
--block-size <bytes|auto|auto-fit> auto (65536) PFS block size; it must be a power of two from 4096 through 2097152. auto-fit is supported by folder packing and the spool path to reduce file-data padding.
--temp-folder <dir> system temporary directory Where staged pack artifacts are written.
--version <PS4|PS5> PS5 PFS profile version.
--inode-bits <32|64> 32 PFS inode-width mode.
--case-sensitive / --case-insensitive case-insensitive Select the PFS name-comparison mode. The two flags are mutually exclusive.
--cpu-count <n> 0 (automatic) PFSC compression workers. Automatic mode uses up to 16 workers and leaves one logical CPU free; a nonzero value is clamped to at least one.
--compression-level <0-9> 7 zlib compression level.
--compression-backend <auto|zlib-ng|zlib|isal> auto Compatibility option. This port always uses zlib 1.3.1; unsupported values produce a warning.
--max-compressed-ratio <0-100> 100 Do not use PFSC when its stored size exceeds this percentage of the raw file size.
--min-compress-size <bytes> resolved block size Store smaller files raw without attempting PFSC compression.
--skip-executable-compression off Do not compress important executable files.
--signed off Build a signed PFS using a zero EKPFS/seed. It is not supported by the default exFAT-wrapped folder mode.
--encrypted off Encrypt filesystem blocks with AES-XTS.
--ekpfs-key <64-hex> all-zero key EKPFS key for an encrypted image; it requires --encrypted.
--verbose off Print per-file decisions.
--dry-run off Scan and report the planned layout without writing an image.
--verify off Run full post-pack verification instead of the default structure-only check.
--verify-structure / --no-verify-structure structure check on Explicitly enable or disable the default quick post-pack check. The two flags are mutually exclusive.
--skip-verification off Skip all post-pack verification. It cannot be combined with --verify.

pack folder also accepts the following options:

Option Default Meaning
--raw off Package the source directly as a PFS .ffpfs, rather than making the default exFAT-wrapped .ffpfsc. Use this mode for --signed, --inode-bits 64, and other direct-PFS settings.
--require-game-files off Refuse to pack unless sce_sys/param.json and eboot.bin are present.
--no-ampr-index off Do not create ampr_emu.index when fakelib/libSceAmpr.sprx is present.
--ampr-skip-regen-if-exists off When AMPR generation applies, retain a valid existing index.
--ampr-force-regen off Regenerate an existing AMPR index.

pack file also accepts:

Option Default Meaning
--use-spool off Force the legacy staged/spool builder instead of direct-to-image streaming.
--rename-inner-image / --no-rename-inner-image rename on Normalize the filename stored inside the image (the first flag explicitly selects the default), or preserve the source filename.

pack exfat

mkpfs pack exfat <source_dir> [output] creates an uncompressed exFAT image. If output is omitted, the program derives <titleId>.exfat beside the source directory; an output directory is also accepted and receives that derived filename.

Option Default Meaning
--cluster-size <bytes|auto> auto (65536) exFAT cluster size. The automatic 64 KiB value is optimized for SMP/LVD.
--overwrite off Replace an existing output image.
--verbose off Print detailed packing output.
--no-progress off Hide the progress bar written to standard error.

batch

mkpfs batch <source_dir> <output_dir> discovers packable folders and image files in source_dir and writes .ffpfsc images into output_dir. It skips existing outputs by default. Its compression, PFS-profile, naming, and encryption options have the same meanings and defaults as the corresponding pack options: --compress/--no-compress, --threshold-gain, --block-size (auto = 65536; auto-fit is not accepted), --version, --inode-bits, --case-sensitive/--case-insensitive, --cpu-count, --compression-level, --compression-backend, --max-compressed-ratio, --min-compress-size, --skip-executable-compression, --encrypted, --ekpfs-key, and --verbose.

Option Default Meaning
--overwrite off Replace images that already exist in the output directory.
--dry-run off Report the conversions without writing images.
--verify off Run full verification for each successful image.
--compress / --no-compress compression on Enable or disable compression; these flags are mutually exclusive.

Reading and extracting images

Encrypted read commands accept --ekpfs-key <64-hex> (default: all-zero key) and --new-crypt (default: off) to select the alternate EKPFS derivation. verify, tree, and unpack also take --format <auto|pfs|exfat>; the default auto detects the format from the input.

Command Options Default Meaning
inspect <image_file> --format <text|json> text Select a human-readable or JSON metadata report.
tree <image_file> --deep off For a PFS that wraps one exFAT image, list the files inside that exFAT.
unpack <image_file> <output_dir> --overwrite off Replace an existing output path.
--deep off Extract files from an inner exFAT image instead of only the outer PFS contents.
--only <inner-path> none; repeatable With --deep, extract only the named inner exFAT file or directory.
--no-progress off Hide extraction progress on standard error.
verify <image_file> --source-dir <dir> none Compare hierarchy and payloads against a source folder. Cannot be combined with --source-file.
--source-file <file> none Compare a single-file image to the source file; not supported for exFAT input.
--expect-crc32 <hex> none Require this cumulative payload CRC32.
--expect-manifest-sha256 <64-hex> none Require this manifest SHA256 digest.
--require-game-files off Warn when sce_sys/param.json, eboot.bin, or pfs-version.dat is missing.

repair

repair operates on an unsigned, unencrypted, single-file .ffpfsc. By default it scans for risky compressed blocks, stores replacements raw, verifies their decoded content, and cleans unused bytes in the outer PFS wrapper.

Option Default Meaning
--scan off Report only; do not modify the image. Returns exit code 3 if blocks need repair.
--bad-blocks <file> none Repair the block numbers in a PS5 Game Compressor bad_blocks.tsv instead of the scan's risky-block selection.
--recompress off Re-encode repaired blocks with zlib level 7 instead of storing them raw.
--mode <auto|in-place|copy> auto auto copy-replaces when free space is at least 1.2× the image, otherwise rewrites in place. in-place can leave an interrupted image corrupt; copy always uses a replacement copy.
--report-dir <dir> none Write summary.json and bad_blocks.tsv to this directory.
--no-slack-cleanup off Leave unused bytes in the outer PFS wrapper unchanged.
--cpu-count <n> 0 (all cores) Worker count for scanning and repair.
--no-progress off Hide progress output.

Exit code 0 means success, 1 means an operation failed, 2 means invalid command-line usage, and repair --scan uses 3 when it finds blocks that need repair.

GUI

mkpfs-gui runs the same commands from a window and shows their output and progress. From a source checkout:

dotnet run --project src/MkPFS.Gui -c Release
  • Pick a game folder or image to see its cover, title, IDs, version, region, and APR Emu marker.
  • The Batch page lists every item it will pack before you run it.
  • Pack File, Pack Folder, and Batch have compression presets (Fast, Balanced, Max, Low RAM) and settings for the zlib level, CPU cores, block size, and when to keep blocks uncompressed.
  • The Repair page scans an image and draws a block map (zlib, raw, risky); click a cell for its offset, stored size, and largest back-reference distance.

Differences from Python MkPFS

  • Same inputs give the same images as Python MkPFS 1.0.0 run with its zlib backend. Set SOURCE_DATE_EPOCH for reproducible timestamps.
  • Compression always uses zlib 1.3.1 at level 7. --compression-backend is accepted but ignored: ISA-L output uses back-references the PS5 decodes wrongly.
  • repair is new.
  • Bugs found in the Python version while porting are listed in tools/oracle/README.md; some are fixed here.
  • Switching from Python MkPFS: see MIGRATION.md.

Build from source

Requirements:

  • .NET SDK 10.0.401 or a newer 10.0.4xx (see global.json).
  • Windows: Visual Studio 2026 with "Desktop development with C++" (native zlib and Native AOT).
  • Linux and macOS: CMake and a C compiler; Linux Native AOT also needs clang and zlib1g-dev.

Build and test:

dotnet build MkPFS.slnx
dotnet test --solution MkPFS.slnx

The first build compiles the bundled zlib in native/. Parity tests skip when the oracle corpus (tests/fixtures/generated) is missing.

Publish native executables:

dotnet publish src/MkPFS.Cli -r win-x64 -c Release
dotnet publish src/MkPFS.Gui -r win-x64 -c Release

Use linux-x64 or osx-arm64 on those systems. On Windows, Native AOT linking needs the Visual Studio Installer folder (C:\Program Files (x86)\Microsoft Visual Studio\Installer) on PATH.

Project layout

Path Content
src/MkPFS.Core Formats and codecs: PFS, PFSC, exFAT, AMPR, crypto, readers, validators
src/MkPFS.Build Image builders, compression planner, batch
src/MkPFS.Repair PFSC repair (Game Compressor port)
src/MkPFS.Cli mkpfs command line
src/MkPFS.Gui mkpfs-gui desktop app (Avalonia)
native/ zlib 1.3.1 and the mkpfs_zlib shim
tests/MkPFS.Tests Unit tests
tests/MkPFS.Parity Byte-for-byte tests against the Python oracle corpus
tests/MkPFS.Gui.Tests GUI view model and headless UI tests
tools/oracle Python oracle scripts (README)

Oracle corpus

The parity tests compare against images and logs made by Python MkPFS. With Python MkPFS checked out at ../MkPFS and uv installed:

uv run --project ../MkPFS python tools/oracle/build_goldens.py --check

Releases

Push a version tag to publish a release. The workflow reruns CI, then uploads the archives, SHA256SUMS.txt, and release notes, and marks the release as latest. With a VT_API_KEY repository secret (a VirusTotal API key), it also scans every archive and links the reports in the notes.

git tag v2.0.0
git push origin v2.0.0

Credits

  • MkPFS by PSBrew: the Python original this port follows.
  • PS5 Game Compressor by Juma Sayeh: the PFSC repair logic.
  • Drakmor's APR Emu: the ampr_emu.index format.

Third-party components:

License

GPL-3.0-only, same as MkPFS. See LICENSE.md.

This project is not affiliated with Sony Interactive Entertainment.

About

Create and Manage Unsigned PFS (Playstation File System) image files with file compression and checksum support.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages