Files
JujuforceandClaude Opus 4.8 622aa537ee fix: extract exFAT OPTION containers that contain empty files
exfat-fs 0.1.3's StreamExtensionEntry::valid() rejects any entry with
FirstCluster < 2, but a zero-length file (or empty dir) legitimately has
FirstCluster = 0 / DataLength = 0 per the exFAT spec. The parser returned
InvalidStreamExtension and aborted the *entire* directory tree the moment it
hit one empty file, so real OPTION (.opt) containers failed to extract with:

    WARNING: Failed to extract exfat contents: InvalidFileEntry(InvalidStreamExtension)

even though unsega/7-Zip read them fine.

0.1.3 is the latest published release and its `main` has an incompatible
public API, so override the dependency via [patch.crates-io] with a fork
branch that is the 0.1.3 release plus only the one-function fix (gate the
cluster check on DataLength: 0 -> FirstCluster must be 0, else >= 2).

Regenerate the synthetic OPTION fixture with a zero-byte file so tests/e2e.rs
reproduces the bug: without the patch it fails with InvalidStreamExtension;
with it, extraction succeeds. Verified against a real 191 MB OPTION container
(1602 files / 1217 dirs, empty file written as 0 bytes) and the full suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 22:06:38 +02:00
..

Test fixtures

These are synthetic SEGA fscrypt containers used by tests/e2e.rs. They contain only dummy files (a few text/XML/stub binaries), so they are safe to commit — there is no proprietary game content inside.

File Type What it exercises
TEST_T001_20240101120000_0.opt OPTION exFAT decrypt + extract (built-in OPTION key)
TEST_1.00.00_20240101120000_0.app APP (base) outer NTFS → internal_0.vhd (fixed VHD) → inner NTFS extract
TEST_1.01.00_20240102120000_1_1.00.00.app APP (delta) differencing internal_1.vhd, auto-merged against the base by VHD GUID
TEST.bin key external key (16-byte key + 16-byte IV) for game id TEST

The delta is a real differencing VHD linked to the base; decrypting it (with the base alongside) merges the two and yields the base files plus the delta's added data/patch_notes.txt and a modified readme.txt.

The .app fixtures use the synthetic game id TEST, which is not in the built-in key table — so they are decrypted via the external-key-file fallback using the committed TEST.bin (this also exercises that fallback path). tests/e2e.rs copies TEST.bin next to the containers and runs from there so the binary finds it. The OPTION fixture uses the built-in OPTION key. Everything here — keys and filesystem data alike — is synthetic.

You can also drop your own real .app/.opt files in this folder to test against them; everything here except these committed fixtures is git-ignored.

Regenerating

These are generated by a local, uncommitted script set (NTFS/exFAT image creation in Docker + a small SEGA fscrypt packer adapted from beerpsi/x). The byte output is not reproducible (NTFS/exFAT embed creation timestamps), but regenerated fixtures are functionally equivalent.