A cross-platform CLI and GUI tool to pack and build PS5 homebrew images in the .ffpfsc container format for ShadowMountPlus (SMP).
Rather than using complex, platform-dependent exFAT loopback mounts, this tool creates a standard uncompressed PFS image (pfs_image.dat) and packs it inside a compressed PFS container (.ffpfsc), which is the recommended layout for nested images in ShadowMountPlus.
- Pure Python & Cross-Platform: Runs natively on macOS, Linux, and Windows with zero OS-specific dependencies.
- No Admin/Root Privileges Required: Since it doesn't need to mount loopback disks or format drives, it runs completely in userland.
- Smart Folder Discovery: You don't need to pass the exact game root. The tool recursively scans the provided directory for the actual game folder (containing
eboot.binandsce_sys/param.json). - Title ID Auto-Naming: Automatically parses
sce_sys/param.jsonto extract the game's actual Title ID (e.g.,PPSA01411) and names output files accordingly. - Archive Support: Directly process ZIP and RAR archives (including password-protected) without manual extraction. Uses a built-in, self-contained UnRAR C++ extension — no external
unraror7-Zipbinaries required. - Batch Processing: Process an entire directory of multiple games, archives, and
.exfatfiles into separate images automatically using the--batchflag. - GUI Mode: Launch a graphical user interface with
--guifor a point-and-click workflow. - Auto-Cleanup: Automatically cleans up intermediate nested PFS files, keeping only the final compressed
.ffpfscfile.
Prebuilt standalone binaries are available on the Releases page. No Python installation required.
| Platform | Download |
|---|---|
| Windows (x64) | PS5-FFPFS-CLI-Windows-x64.exe |
| macOS (universal) | PS5-FFPFS-CLI-macOS-universal.zip |
| Linux (x64) | PS5-FFPFS-CLI-Linux-x64.AppImage |
- Python 3.8+
- A C++ compiler (only needed to build the built-in
unrarextension from source)
git clone https://github.com/bizkut/ps5-ffpfs-cli.git
cd ps5-ffpfs-cli
pip install -e ./unrarThe
unrardirectory contains a self-contained C++ extension for RAR extraction. It will be built automatically duringpip install.
python3 cli.py [game_folder_or_archive_or_exfat] [output] [options]| Flag | Description |
|---|---|
--keep-pfs |
Keep the intermediate nested PFS image (<title_id>_nested_pfs.dat) |
--batch |
Process multiple inputs into multiple outputs. output is treated as a directory. |
--gui |
Launch the graphical user interface |
-f, --force, --overwrite |
Overwrite existing files without prompting |
--password PASSWORD |
Password for ZIP/RAR archives |
--temp-dir DIR |
Custom directory for temporary PFS images (defaults to system temp) |
Standard Process (Game Folder → .ffpfsc):
python3 cli.py /path/to/GameFolderProcess a Password-Protected RAR Archive:
python3 cli.py /path/to/Game.rar --password DLPSGAME.COMProcess a ZIP Archive:
python3 cli.py /path/to/Game.zipConvert Existing exFAT (exFAT → .ffpfsc):
python3 cli.py /path/to/GameImage.exfatBatch Processing:
python3 cli.py /path/to/AllGames /path/to/OutputFolder --batchKeep Intermediate Files:
python3 cli.py /path/to/GameFolder --keep-pfsCustom Temp Directory (useful for large games on SSDs):
python3 cli.py /path/to/GameFolder --temp-dir /fast/ssd/tmpLaunch GUI:
python3 cli.py --guiLaunch the GUI either by running the standalone executable (no terminal needed) or from the command line:
python3 gui.pyThe GUI supports:
- Selecting source folders, archives, or
.exfatfiles via a file picker - Password input for protected archives
- Batch folder selection
- Output directory selection
- Temp directory selection for intermediate PFS images (useful when processing large games on SSDs)
- Progress feedback during packing
pip install pyinstaller
pyinstaller --name PS5-FFPFS-CLI --onefile --windowed \
--hidden-import tkinter --hidden-import mkpfs.cli --hidden-import multiprocessing \
--hidden-import unrar --hidden-import unrar.rarfile --hidden-import unrar._unrar \
--collect-data tkinter --collect-binaries tkinter \
--collect-data customtkinter --collect-data mkpfs --collect-binaries mkpfs \
gui.pyNote for RAR support in frozen builds: The
unrarC extension must be placed in theunrar/package directory within the bundle soimport unrar._unrarcan locate it. If PyInstaller fails to collect it automatically, copy_unrar*.so(or_unrar*.pydon Windows) into theunrar/source directory before building.
| Issue | Cause | Solution |
|---|---|---|
| "No module named '_unrar'" in frozen build | PyInstaller places the C extension at the bundle root instead of inside unrar/ |
Ensure the .so/.pyd is in unrar/ before building, or use the .spec file provided in the repo |
| GUI spawns multiple instances during compression (macOS/Windows) | multiprocessing.Pool workers re-run the entry point |
Fixed in v1.1.5+ — multiprocessing.freeze_support() is called at module level |
| Console windows flicker on Windows during compression | Subprocess spawns visible console | Fixed in v1.1.5+ — subprocesses use CREATE_NO_WINDOW flag in frozen mode |
| Cancel button does nothing in frozen build | Direct-call path bypassed subprocess termination | Fixed in v1.1.5+ — restored subprocess path with headless env so terminate() works |
MIT License. The built-in UnRAR extension is based on the RARLAB UnRAR source (freeware license for extraction-only use).