Files
benilla-octo/crates
gromandClaude Opus 5 8866e151bc An encrypted or single-unit MPQ entry reads instead of being refused
The reader refused both as "outside the 1.12.1 envelope". The 1.12.1 part was
right and the envelope was wrong: this realm's chain carries 98 files of both
kinds, and every one of them failed.

Measured over `Data/` (the block tables, read independently of this reader):

  * `(listfile)` is `COMPRESS|ENCRYPTED|FIX_KEY` in patch-1, -2, -4, -5 and -A,
    and `COMPRESS|ENCRYPTED` in patch-N, which also holds two encrypted
    `textures\UnitSelectTextur*.blp`. The listfile union is what
    `Chain::list_under` walks, so the macro-icon catalog
    (`benilla_formats::load_macro_icon_catalog`, and the ClassicAPI
    `GetMacroIcons` built on it) and the archive pass of the addon loader each
    lost five archives' worth of names to one `Unsupported`.
  * `Patch-Q.mpq` stores all 93 of its files as single units, 92 of them
    `ITEM\TextureComponents\` armour art — Tauren battleground sets, the Raid
    Shaman robe, the tm_* cloth/leather/plate sets, the alchemy coat, PvP Horde
    plate, the Outland belts. Q mounts above every other patch, and
    `Chain::read` answers from the winning archive and does not fall through on
    an error, so those are the copies the armour composite asks for and the
    read failed outright.

Encryption: the key is the name's last component under the FILE_KEY hash,
offset by the block position and XOR'd with the size for a `FIX_KEY` entry. The
offset table decrypts under `key - 1` and sector i under `key + i`, over the
stored (compressed) bytes. A single unit is sector 0, so its key does not step.
`decrypt_bytes` leaves a tail shorter than four bytes as stored, as Storm's
`DecryptMpqBlock` leaves it. The plain path still reads straight out of the one
bulk sector buffer; only an encrypted file pays a per-sector copy.

Single unit: `compressed_size` bytes at `file_pos`, no offset table, a
codec-mask byte first only where the unit actually shrank. `BlockEntry` now
keeps `compressed_size`, which a sectored file still never reads.

Tests: a byte-wise crypto round trip that pins the short tail; an encrypted
stored entry and an encrypted `FIX_KEY` compressed entry, both read by name; a
stored and a deflated single unit, the deflated one longer than a test sector
so nothing can sector it. Three more are corpus-backed and gated on `$WOW_DATA`:
every `(listfile)` in the install reads (asserting at least one of them was
encrypted, so an install without any cannot pass while proving nothing), and
every single-unit file an archive names reads to the size its block entry
promises (92 of them here).

Verified against the live chain: `ITEM\TEXTURECOMPONENTS\HandTexture\
tm_cloth_01_Glove_HA_U.blp` now reads 175,938 bytes from `Patch-Q.mpq` and
decodes as a 512x128 BLP2.

The encryption half is a port of benilla-bourbon's fix (tripleslash/
benilla-bourbon, "MPQ files encrypted under their name read by name"), adapted
to this fork's single-function bulk read — upstream's streaming `open_file`/
`ArchiveFile` is not in this tree. Its nameless-read API (`entries`,
`read_block`, `name_key`) is left out: it exists for that fork's BENPACK
container builder, which this tree has no counterpart to. The single-unit half
is this tree's own; bourbon still refuses those.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
2026-10-26 07:11:58 -06:00
..