Skip to content
TinyBacklogPublic

About

A Textual TUI over mkpfs: build, inspect, verify, tree and unpack PlayStation PFS (.ffpfs/.ffpfsc) images.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

mkpfs-tui

CI Python License: GPL-3.0-or-later

A Textual terminal UI for mkpfs — pack, inspect, verify, tree, unpack, build raw dumps to exFAT, and deploy to a jailbroken PS5 over FTP from a single sidebar-driven app. Every operation has its own view with file/directory pickers, live progress, and a result panel that surfaces warnings and errors without ever leaving the terminal.

mkpfs-tui is a third-party frontend. It pins mkpfs as a dependency and never modifies it.

Contents: Screenshots · Operations · Requirements · Install · Usage · Build exFAT · Deploy to PS5 · Contributing · License


Operations

Operation What it does
Pack Build a PFS image from a source folder or file (compression, signing, encryption, dry-run).
Inspect Show an image's header, inode/dir/file counts, sizes, and checksums in a table.
Verify Validate an image's structure and checksums — optionally against a source tree or expected CRC32 / manifest.
Tree Browse the file tree stored inside an image.
Unpack Extract an image to a target directory, with progress and a files/dirs/bytes summary.
Build exFAT Turn a PS5 dump folder into a .exfat image for ShadowMountPlus — adaptive cluster, param.json-derived name/label.
Deploy Push a built image (or any file) to a jailbroken PS5 over FTP — live progress, remote listing, overwrite confirm.

Screenshots

About — the welcome screen:

mkpfs-tui — the About / welcome screen

Pack — build an image from a folder or file:

mkpfs-tui — the Pack view

Inspect — header, counts, sizes, and checksums:

mkpfs-tui — the Inspect view

Verify — structure / checksum checks with a PASS/FAIL banner:

mkpfs-tui — the Verify view

Tree — browse the file tree stored inside an image:

mkpfs-tui — the Tree view


Requirements

  • Python 3.11 or newer (only needed for the uvx/pipx install; the Linux binary bundles its own).
  • A terminal that renders modern TUIs. Most do; on Windows use Windows Terminal (the default on Windows 11), not the legacy console.
  • mkpfs is installed automatically as a dependency — you do not install it yourself.
  • Build exFAT only: exfatprogs and rsync must be installed on the host system, and the build step requires sudo access for the loop-mount copy (sudo mount). These are not needed for any other operation.

Install

Prefer a self-contained download? Grab a build from the Releases page.

The cross-platform way is via Python, using either uv (recommended — uvx runs it without installing) or pipx (installs the mkpfs-tui command).

Linux

# Prerequisite — uv (one line, no root):
curl -LsSf https://astral.sh/uv/install.sh | sh

# Run it (downloads + runs in an isolated env, nothing to clean up):
uvx mkpfs-tui

Or with pipx — sudo apt install pipx (Debian/Ubuntu) / sudo pacman -S python-pipx (Arch), then:

pipx ensurepath        # once, then restart the shell
pipx install mkpfs-tui
mkpfs-tui

No Python? Download mkpfs-tui-linux-x86_64.tar.gz from the Releases page, then:

tar -xzf mkpfs-tui-linux-x86_64.tar.gz
./mkpfs-tui/mkpfs-tui

macOS

# Prerequisite — uv (via Homebrew or the install script):
brew install uv          # or: curl -LsSf https://astral.sh/uv/install.sh | sh

uvx mkpfs-tui

Or with pipx: brew install pipx && pipx ensurepath, then pipx install mkpfs-tui. (No prebuilt macOS binary yet — use the Python install above.)

Windows

Use Windows Terminal for a TUI. In PowerShell:

# Prerequisite — uv:
winget install --id=astral-sh.uv  # or: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

uvx mkpfs-tui

Or with pipx: py -m pip install --user pipx, then py -m pipx ensurepath (restart the terminal) and pipx install mkpfs-tui. (No prebuilt Windows binary yet — use the Python install above.)


Usage

Launch the app (uvx mkpfs-tui, or mkpfs-tui if installed). You land on the Pack view.

The interface

  • Left sidebar — the seven operations. Move with ↑/↓; the right pane switches as you go.
  • Right pane — the selected operation's form, run button, progress, and a result panel (errors in red, warnings in amber, success in green).
  • Browse… buttons open a file/directory picker — navigate with the arrows, Choose to accept, Cancel/Escape to dismiss. You can also just type a path into the field.

Keyboard

Key Action
↑ / ↓ Move in the sidebar / lists / tree
Tab / Shift+Tab Move between form fields
Enter / Space Activate the focused button / switch
Ctrl+P Command palette — includes theme switching
Escape Close a modal (picker / overwrite prompt)
Ctrl+Q Quit

By operation

  • Pack — choose Folder or File mode, pick the Source and the Output image path (the image is written exactly to the path you type — include the extension you want), then set options: PS4/PS5 version, inode width (folder mode only), compression level, CPU count, block size, and the switches (Compress, Case-insensitive, Signed, Encrypted, Dry run, Verify after). Press Pack to watch a live progress bar; Cancel stops the build. If the output already exists you'll be asked to confirm before it's overwritten.
  • Inspect — point at an Image and press Inspect; the table fills with version, sizes, counts, and checksums.
  • Verify — give an Image and, optionally, a Source directory and/or an expected CRC32 / manifest SHA-256, then press Verify for a PASS/FAIL banner plus any errors.
  • Tree — pick an Image and press Build tree to browse its contents.
  • Unpack — pick an Image and an Output directory and press Unpack. Turn on Overwrite to clear a non-empty output directory first (you'll be asked to confirm the deletion).
  • Build exFAT — pick a Dump folder (a PS5 game dump directory) and an Output path, then press Build. The app reads param.json inside the dump to suggest the output filename and the exFAT volume label; you can override both. Use a Preset (PPSA / +Title / +Version) and the Lowercase toggle to control the suggested filename. Choose a cluster size or leave it on Auto (adaptive). Tick Verify after to run fsck.exfat on the finished image. Tick Deploy after to push the finished image to the PS5 immediately (requires FTP host set in the Deploy view or config). The pipeline is: truncate → mkfs.exfat → sudo mount (loop) → rsync → umount → optional fsck.exfat. ShadowMountPlus keys off the .exfat filename, and all game files are placed at the image root. Requires exfatprogs + rsync on the host; the mount step needs sudo.
  • Deploy — fill in the Host (and optionally Port, Path, User, Remote name) then press List to browse the remote directory or Deploy to upload. Settings persist to ~/.config/mkpfs-tui/config.toml (password is never stored). FTP is plain (no TLS) — intended for LAN use with a jailbreak payload running on the console; the console mounts game images itself via ShadowMountPlus.

Encrypted images

Every view has an EKPFS key field (64 hex characters) and a newCrypt switch for encrypted images; leave them blank/off for unencrypted ones. In Pack, the key is only applied when Encrypted is on.


Build exFAT

The build-exfat subcommand exposes the same pipeline as the TUI view for scripting:

mkpfs-tui build-exfat <dump> [-o out.exfat] [--cluster auto|32K|64K|128K|256K|512K|1M] \
    [--label LABEL] [--preset ppsa|title|version] [--lower] [--no-verify] [--deploy]
  • <dump> — path to the PS5 dump folder (must contain sce_sys/param.json).
  • -o / --output — path for the .exfat image to create (auto-derived from param.json if omitted).
  • --cluster — exFAT cluster size; default auto chooses adaptively based on the dump size.
  • --label — override the volume label (auto-derived from param.json if omitted).
  • --preset — filename preset: ppsa (PPSA code only), title (title only), or version (title + version, default).
  • --lower — lowercase the suggested filename.
  • --no-verify — skip the fsck.exfat check after building.
  • --deploy — push the finished image to the PS5 over FTP immediately after building (uses the FTP config from ~/.config/mkpfs-tui/config.toml; requires --host if no config is saved).

Runtime requirements: exfatprogs (provides mkfs.exfat + fsck.exfat) and rsync must be installed. The copy step mounts the image via a loop device — sudo is required for that step. The output .exfat filename is what ShadowMountPlus uses to identify the title; all game files are placed at the image root.


Deploy to PS5

mkpfs-tui can push a file (typically the .exfat image you just built) to a jailbroken PS5 running an FTP server (such as the etaHEN payload) over a plain LAN FTP connection. FTP is not encrypted — this is intended for local network use only. The console mounts game images itself via ShadowMountPlus; mkpfs-tui only handles the upload side.

Standalone Deploy view

Open the Deploy view from the sidebar, fill in:

  • Host — IP address of your PS5.
  • Port — FTP port (default 2121, the etaHEN default).
  • Path — remote directory to upload into (default /data/etaHEN/games/).
  • User — FTP username (default anonymous).
  • File — local file to upload.
  • Remote name — filename on the console (defaults to the local filename).

Press List to browse the remote directory. Press Deploy to upload with a live progress bar. If a file with the same name already exists on the console you'll be asked to confirm before overwriting. Settings (host / port / path / user) are saved to ~/.config/mkpfs-tui/config.toml — the password is never stored.

deploy CLI

mkpfs-tui deploy <file> --host <PS5-IP> [--port 2121] [--path /data/etaHEN/games/] \
    [--user anonymous] [--name remote-filename.exfat]
  • <file> — local file to push.
  • --host — PS5 IP address (required if no host is saved in the config).
  • --port — FTP port (default 2121).
  • --path — remote directory (default /data/etaHEN/games/).
  • --user — FTP username (default anonymous); enter the password interactively when prompted.
  • --name — override the remote filename (defaults to the local filename).

Inline deploy after build

In the Build exFAT view, tick Deploy after to push the finished image to the PS5 immediately after the build completes. The same FTP settings apply (loaded from config, or entered in the Deploy view first). The build-exfat --deploy CLI flag does the same for scripted workflows.


Contributing

Issues and PRs are welcome. The project uses uv for everything.

Set up

git clone https://github.com/TinyBacklog/mkpfs-tui
cd mkpfs-tui
uv sync                                              # creates .venv with deps + dev tools
uv run textual run --dev mkpfs_tui.app:MkpfsTuiApp   # run the app (or: uv run python -m mkpfs_tui)

Tip: run uv run textual console in a second pane to see logs/tracebacks from the --dev app.

Test & lint

./run-tests.sh        # ruff format + ruff check --fix + pytest (what to run before committing)
uv run pytest         # tests only

Tests use Textual's App.run_test() Pilot harness with pytest-asyncio in auto mode. CI runs lint + tests (check-mode) on every push and PR.

Conventions

  • uv only — uv sync, uv run …; never pip or a global Python.
  • Type hints everywhere, Google-style docstrings, from __future__ import annotations at the top of every module, prefer X | None over Optional. ruff is law (config in pyproject.toml).
  • The mkpfs boundary (most important rule): all imports of and calls to mkpfs.* live in the single module mkpfs_tui/mkpfs_runner.py, which exposes the app's own value types. No other module imports mkpfs. When you depend on new mkpfs surface, add an assertion to tests/test_mkpfs_contract.py so an upstream change fails loudly.
  • Updating the pinned mkpfs: uv lock --upgrade-package mkpfs, run the tests (including the contract test), then commit.

See CHANGELOG.md for release history. The architecture and the per-milestone build plans are kept outside the repo (in the author's planning notes).


License

GPL-3.0-or-later. mkpfs-tui imports mkpfs, which is GPLv3, so the combined work is GPL.

About

A Textual TUI over mkpfs: build, inspect, verify, tree and unpack PlayStation PFS (.ffpfs/.ffpfsc) images.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages