2 Commits
Author SHA1 Message Date
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
JujuforceandClaude Opus 4.8 d79ded6ee5 test: add synthetic, committable e2e fixtures (base/delta app + opt)
Forge three small fixtures with no proprietary content so the e2e test runs by
default and covers the full decrypt+extract path, including the delta/base VHD merge:

- TEST_T001_..._0.opt              encrypted exFAT (OPTION key)
- TEST_1.00.00_..._0.app           base APP: outer NTFS -> internal_0.vhd (fixed) -> inner NTFS
- TEST_1.01.00_..._1_1.00.00.app   delta APP: differencing internal_1.vhd, auto-merged with base by GUID

The APP fixtures use the synthetic game id TEST, decrypted via the committed
TEST.bin external key (also exercising that key-file fallback). e2e.rs copies the
fixtures + key into a temp dir and runs from there so the delta finds its base and
the key. .gitignore whitelists only these synthetic files so real containers
dropped in fixtures/ stay private. The generator is kept local, not committed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 13:37:37 +02:00