Repository navigation
v3.0
KytyPlus v3.0
Tag: v3.0 (cef954c)
License: GPL-2.0
Platforms: Windows, macOS, Linux
This is a correctness release. The PKG/PFS layer was parsing formats that matched our own synthetic test packages but not the real on-disk layouts: every file offset in the PKG entry table was zero, the PFS dirent field order was wrong, and a relative PKG path extracted to the drive root. All of it is fixed, verified against the reference PFS implementation, and round-tripped end to end with the compiled binary. If a .pkg install failed on v2.9, retry on this build. No new game dumps were used to validate — the PKG path is verified by format-accurate round-trips; the first real-PKG install is still pending.
PKG parser — entry table (major)
The entry table is now actually parsed. Previously only file names were read, via a hard-coded 0x2B30 offset, and every entry's offset and size were left as zero:
- Real per-entry records: table_offset + table_entries × 32 bytes, bounds-checked against file size.
- Descriptor record (code 0) locates the name table; table size bounded to 64 KB and to the file.
- File records (code ≥ 0x200) populate real offset and size pointing at file data in the body.
- Names resolved through the name table by code − 0x200, each offset bounds-checked.
- Big-endian field decoding via memcpy reads — no type punning, UBSan clean.
- Every failure path logs a diagnostic instead of continuing with zeros.
PFS parser — dirent format (major)
The on-disk dirent layout was corrected to match the reference implementation (MkPFS): {u32 inode, i32 type, i32 name_len, i32 ent_size} followed by the name and zero padding, striding by ent_size. The previous parser read {type, inode, name_size, padding} with a NUL-terminated name — a layout that agreed with the synthetic generator but corrupted names and lost entries on real packages.
PFS parser — block lists
Unused direct and indirect block slots are now treated as empty. Block 0 is the superblock and never file data; previously a zero slot walked the parser into the superblock, reading the PFS magic as directory contents.
Extraction — path bug
A relative PKG path extracted to the drive root (C:\pkg_out) instead of the PKG's own directory. The output directory now resolves relative to the package location, built with std::filesystem path division instead of string concatenation.
Hardening — untrusted input (several)
Extraction and parsing now assume hostile or corrupt input rather than well-formed files:
- Path traversal guard: extracted names are validated before use — ".." components, absolute paths, and drive letters are rejected. A crafted package can no longer write outside the install directory (Zip Slip).
- Recursion guards: the PFS walk carries a visited-inode set and a depth cap. A cyclic or runaway directory graph logs and skips instead of overflowing the stack inside the launcher.
- Stream state: a failed read no longer poisons every subsequent seek — the stream state is cleared before each operation.
- Diagnostics: out-of-range blocks, short reads, unreadable indirect blocks, oversized dirent names, and failed inode re-reads all log with the inode number. "Extraction failed" reports are now actionable.
Graphics — ray tracing fallback
Missing driver ray tracing functions no longer terminate the process (previously: exit 321, no message, at startup, before a game was even loaded). RT now disables itself with a log line on drivers that report the extensions but fail to load the functions — a failure mode common on iGPU drivers, which is this fork's actual target audience.
Known limitations
PFSC-compressed images are rejected at parse rather than extracted incorrectly. Encrypted PFS / EKPFS paths are not supported — pre-decrypted binaries only, as before.
Legal
Clean HLE emulator. Does not ship Sony code or keys, does not decrypt or circumvent DRM, requires already-decrypted content supplied by the user, and does not provide instructions or tooling for producing it. GPL-2.0.
Tag v3.0, GPL-2.0.