Converts PS4 .pkg files into shadPS4 .zar archives, and optionally merges a base game with its
update into one archive.
Status: converts and merges real pkgs end to end, verified against the reference ZArchive
reader. Single pkg → .zar, base + update → one .zar with the update winning conflicts.
Split piece sets (prefix_0.pkg, prefix_1.pkg, ...) are accepted.
Requires Zig 0.17.0. The UI uses dvui with the SDL3 backend; SDL is built as a Zig dependency, so there is nothing else to install.
zig build
./zig-out/bin/ps4-pkg2zarOne command builds the core, the tools and the UI. The build is Zig only.
zig build # debug binaries in zig-out/bin
zig build run # start the app
zig build test # unit tests
zig build check # formatting check on the Zig sources
zig build --release=safe # binaries for distributionZig must be 0.17.0. If another version comes first on PATH, put 0.17 first.
GUI: add one pkg (convert) or two (merge, roles detected automatically), choose Archive or
Extract, pick the output, press Convert. "Add folder" takes an already extracted game tree
(for example an emulator's CUSA00190 directory) and archives it as it is. Two folders merge like
a base and its update: APP_VER from sce_sys/param.sfo decides which one is the update, the
update wins on a shared path, and the name gets - merged (or - base / - update with
"Each separately"). A mismatched title id or an equal version is refused with the reason. The output name is built from param.sfo and the
header: Terraria (CUSA00740) v01.00.zar, plus - merged, - base or - update where it
matters, so names are stable regardless of the source file names. Cancel leaves nothing behind. Extract writes into
<out>.part and replaces the destination only after every file is size-checked.
Headless, same code path as the GUI button:
zig-out/bin/export out.zar base.pkg [update.pkg]
zig-out/bin/export out.zar extracted-folder/ # archive a loose tree, no pkg needed
zig-out/bin/export out.zar base-folder/ update-folder/ # merge two trees by APP_VER
zig-out/bin/extract out-dir base.pkg [update.pkg]
zig-out/bin/plan base.pkg [update.pkg] # preflight and merge plan, writes nothing
zig-out/bin/listfiles base.pkg [piece...] # file tree of a pkg or piece set
zig-out/bin/pkginfo base.pkg # header fields
zig-out/bin/zar list out.zar # tree inside a written archive
zig-out/bin/zar extract dir out.zar # write it back out, refuses to overwriteMerge detects roles by content, not by list order: listing the update first swaps the two and logs a note.
core/src/— Zig core, built as a static library (core/src/root.zig); the UI calls it throughcore/src/bridge.zigui/main.zig— dvui front end (SDL3 backend); all work runs throughcore/src/bridge.zigtools/— pkginfo, listfiles, plan, export, extractdocs/debt.md— running debt ledger
- PlayGo state is not modelled. A finished
.zaris a fully installed game, sosce_sys/playgo-chunk.datis copied as an ordinary file. - Delta updates, retail PSN passcodes, and DLC install layout are refused, not handled.
- Folder input archives regular files and directories only; a symlink or special file inside the tree is refused with its path, never followed or skipped.
- Test fixtures are not committed. The unit tests build their own pkg (header, key blobs, PFSC
tree, param.sfo) and convert it end to end, so CI covers the real path. Point
P2Z_TEST_ROMat a directory of.pkgfiles to run the fixture tests as well.
GPL-3.0-or-later. See NOTICE for the origin of ported code.