Skip to content

AMPR emulation automated index and OS-metadata exclusion for folder packing - #63

Merged
RenanGBarreto merged 5 commits into
PSBrew:mainfrom
rdmrocha:enh/ampr_emu_and_content_sanitize
Jun 23, 2026
Merged

RenanGBarreto merged 5 commits into
PSBrew:mainfrom
rdmrocha:enh/ampr_emu_and_content_sanitize

Conversation

@rdmrocha

Copy link
Copy Markdown
Contributor

Two related source-prep changes for folder packing.

AMPR index. When a source folder contains fakelib/libSceAmpr.sprx (an emulation build), pack folder now generates an ampr_emu.index (AMPRIDX3: FNV-1a-64 path hash table + per-file size/mtime records) into the folder before packing, so it ships inside the image. It's regenerated on every pack to stay in sync with the current files — the rebuild is metadata-only (directory walk + stat, no payload reads), so it's inexpensive. --no-ampr-index skips it and preserves any existing index.

OS-metadata exclusion. A centralized is_ignored_name helper now drops OS-generated junk during scanning and AMPR indexing — macOS (.DS_Store, ._*, __MACOSX, Spotlight-V100, ...) and Windows (Thumbs.db, desktop.ini, $RECYCLE.BIN, ...) — including whole junk directories. This keeps such files out of every image and the index.

Note — behavior change: files like .DS_Store/Thumbs.db that were packed before are now excluded (always on; they're never wanted in a PS5 image).

On-disk PFS/PFSC format is unchanged. Adds unit + integration tests for the index format/gating and the exclusion across both code paths.

@RenanGBarreto RenanGBarreto self-assigned this Jun 21, 2026
@RenanGBarreto RenanGBarreto changed the title feat: AMPR emulation index + OS-metadata exclusion for folder packing AMPR emulation automated index and OS-metadata exclusion for folder packing Jun 23, 2026
@RenanGBarreto
RenanGBarreto merged commit 3fd1f1f into PSBrew:main Jun 23, 2026
3 checks passed
RenanGBarreto added a commit that referenced this pull request Jun 27, 2026
…folder, deep read (#65)

## Why

Producing an exFAT-wrapped game image today depends on the host OS and
elevated privileges: it shells out to `newfs_exfat`/`mkfs.exfat`, mounts
a loopback/disk image (`losetup`/`hdiutil`/OSFMount), copies files in as
root/admin, and unmounts.

That means three divergent code paths, external tools that must be
installed, and admin rights — and it cannot run unprivileged or on a
machine without the right filesystem tooling. It also forces a two-step
workflow (build a full `.exfat`, then compress it), so peak disk is
roughly double the game size while both the intermediate image and the
output exist.

This PR removes all of that. exFAT is built and read entirely in pure
Python — no mount, no root, no external binaries — identically on macOS,
Linux, and Windows, and the folder→`.ffpfsc` path runs in a single
streaming pass with no temporary `.exfat` on disk.
`mkpfs pack folder ./game ./game.ffpfsc` now wraps the folder in an
exFAT and compresses it straight into the `.ffpfsc` in one pass.

## New commands and flags

- `pack folder` (default): folder → exFAT → `.ffpfsc` in one pass, no
temp image.
- `pack folder --raw`: pack the folder directly as PFS (the previous
default).
- `pack exfat <dir> [out]`: build a raw `.exfat` image (auto-named
`<titleId>.exfat`).
- `unpack --deep`: extract the files *inside* a wrapped exFAT, not the
inner `.exfat`.
- `tree --deep`: list the files inside a wrapped exFAT. 

## How it works

- **Forward-only exFAT serializer**: computes the full layout up front
(sizes are known after scanning) and emits the image in strict offset
order, so the byte stream feeds straight into the compressor — no
intermediate file, flat memory.
- **Read-only exFAT parser** over any seekable source, plus a
PFSC-backed view so a `.ffpfsc` can be peeled to its inner contents
without writing a temp `.exfat`.
- **Multicore compression** on the fused path (bounded look-ahead thread
pool; zlib-ng releases the GIL), deterministic output.
- Inner image is named `<titleId>.exfat` from `sce_sys/param.json`.
- OS-generated metadata (`.DS_Store`, `._*`, `__MACOSX`, `Thumbs.db`, …)
is excluded consistently across packing, the AMPR index, and verify.
  
## Disk and speed

- Peak disk drops to `source + output` (no inner `.exfat`, no doubling).
- Fused compression scales across cores (≈6x vs single-threaded in local
tests), byte-identical regardless of worker count.
  
## Validation

- The generated exFAT is accepted by a real PS5 (boot-tested via
ShadowMountPlus), is `fsck_exfat`-clean, and mounts/reads correctly
under the macOS kernel driver.
- Round-trip tested through an independent in-tree exFAT reader;
committed tests use a tiny gzipped fixture (no mount, portable). Full
suite green.
  
## Notes

- The fused path is unsigned only; use `--raw` for signed PFS images.

## Dependencies

This branch is stacked on two PRs and should be merged after both:

- #62
- #63
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants