Files
UnheardCity/docs/formats/wad.md

77 lines
4.1 KiB
Markdown

# Arkham City WAD / BKPK container format
All values little-endian. Verified by byte-identical round trips of every shipped WAD
(INT/ITA/FRA/DEU/ESN LOC WADs and `SFX\17705D3E.wad`) and every BKPK pack.
## WWAD (`*.wad`)
| off | type | meaning |
|------|------|---------|
| 0x00 | 4s | `WWAD` |
| 0x04 | u32 | version `0x01020000` |
| 0x08 | u32 | entry count N |
| 0x0C | u32 | table offset (always `0x1C`) |
| 0x10 | u32 | table size (`12*N`) |
| 0x14 | u32 | data start (header area size; `0x8000` LOC, `0x1000` SFX) |
| 0x18 | u32 | data size (`file size - data start`) |
| 0x1C | N x {u32 hash, u32 size, u32 offset} | |
Rules:
- Table sorted by `hash` interpreted as **signed** int32. Payloads stored in table order.
- Each payload starts at the next **0x1000-aligned** offset after the previous one.
- `size` = payload length rounded up to **0x800**; padding is zero.
- File ends 0x1000-aligned; header area after the table is zero.
- `hash` = Wwise ID of the soundbank whose streamed media the entry holds: FNV-1 of a bank name listed in
`CookedPCConsole\CookedBanks.txt` (e.g. `CHR_RadioScanner`, `AMB_Steel_B3`, `Music`); this names every
entry the game uses (2,544 of 2,547; ackit takes the names from there).
Payload kinds:
- **LOC WADs**: each entry is a group of WEMs, the variations of one line (every language: 10,816 WEMs in
2,464 entries; e.g. `CHR_RadioScanner` holds 91). Each WEM starts at the next 0x800 boundary after
the previous one (zero padding); 3 entries pack two WEMs back to back. Addressed `HASH:i`. The WEMs of an
entry are stored in media-ID order and the languages have different media IDs, so variation `i` is a
different line in each language (75 % of the lines). Which recording is which line comes from the banks
(`banks.md`, *Lines*), not from positions.
- **SFX WAD**: 79 grouped ambience/effect entries (260 WEMs) plus 4 **stream packs** (below).
Where the game gets the inner offsets: the entry IDs appear next to `BKPK` headers inside the
LZO-compressed UE3 packages (e.g. `BmGame.upk`, `OW.upk`, `Church_B1_*.upk`). A speech entry has one table
per language, inside a localized `AkBank` export (its k-th table belongs to the k-th language the export lists;
the SFX WAD's 79 tables are outside such exports). Those embedded BKPK tables give each WEM's offset **and
size**; in every language each WEM has exactly one row (`banks.md`, *Lines*). ackit never changes them,
so these WEMs are replaced **in place, no larger than the original** (zero-filled to the original size), and
the WAD layout never changes. The user's earlier quickbms-reimport mod did the same and was loaded by the game.
Only the 4 SFX stream packs, whose BKPK index is a loose `.bnk`, can change size.
## BKPK stream packs (`SFX\<ID>.bnk` + SFX WAD entry `<ID>`)
`EDF036D6` (Music, 314 WEMs, incl. the intro/front-end music `393BC53A`), `E8F980BE` (Music_PDLC, 75),
`57572DAC` (Global, 1), `FCA4F778` (1). The `.bnk` file is an index + an embedded soundbank; the WEM
data lives in the SFX WAD entry with the same ID.
| off | meaning |
|------|---------|
| 0x00 | `BKPK` |
| 0x04 | version `0x01020000` |
| 0x08 | pack id (== WAD entry hash) |
| 0x10 | media count N |
| 0x18 | table size `12*N` |
| 0x1C | table offset `0x48` |
| 0x20 | total stream size (== WAD entry size) |
| 0x2C | end of table (`0x48 + 12*N`) |
| 0x38 | embedded bank size |
| 0x3C | embedded bank offset (0x800 or 0x1000; a normal BKHD/HIRC/STID bank) |
| 0x48 | N x {u32 media id, u32 size, u32 offset in the WAD entry} |
Rules: table sorted by media id; each media starts at the next **0x800-aligned** offset; total =
end rounded up to 0x800. Padding is mostly zero (a few non-zero bytes in `EDF036D6`; the tool keeps
original bytes where nothing moved).
Replacing a pack media (`ackit` target `EDF036D6/393BC53A`) rewrites the pack table (`.bnk`), the
WAD entry payload, and the WAD table/offsets. Verified in-game (findings E2, E14).
## Regular soundbanks
The other `SFX\*.bnk` files and the banks embedded in `.upk` packages hold their WEMs in memory (not in a
WAD); ackit lists and writes them as the catalog archive `BANK` (`banks.md`).