List and edit in-memory sounds in soundbanks

This commit is contained in:
2026-10-09 20:12:10 +02:00
parent 61bbf26f3b
commit 5d6c2447f7
36 changed files with 71568 additions and 54823 deletions

View File

@@ -25,19 +25,31 @@ list; unnamed IDs show as hex). First matching rule wins:
| `Speech_CHAR_` | Speech | Character lines |
| `Speech_CH_`, `Speech_SEQUENCE_`, `Speech_STEEL_`, `_MUSEUM_`, `_CHURCH_`, `_COURT_`, `_UNDER_`, `_GCPD_`, `_ZP1_` | Speech | Story missions |
| `Speech_GAME_`, `Speech_MAP_`, `Speech_AUGMENTED_`, `Speech_FORENSICS_`, `Speech_TEMP_` | Speech | Gameplay & hints |
| `NPC_` | Ambience | Crowds |
| `NPC_...Crowd...` | Ambience | Crowds |
| other `Speech_` | Speech | Other speech |
| media of the `Music` / `Music_PDLC` stream packs, `Music*` | Music | Music / Music (DLC) |
| media of the `Music` / `Music_PDLC` stream packs, `Music*`, `MUS_` | Music | Music / Music (DLC) |
| `AMB_`, `PSA_` | Ambience | Ambience |
| `PSO_` (flies, drips, fans, lights) | Ambience | Ambient emitters |
| `WND_` | Ambience | Wind |
| `WTR_` | Ambience | Weather |
| `VOL_`, `PVOL_` | Ambience | Room tone |
| `SEQ_` | SFX | Scripted sequences |
| anything containing `Footstep` | SFX | Footsteps |
| `CHR_`/`NPC_` containing `Combat`, `BoneBreak`, `Bodyfall` | SFX | Combat |
| `SEQ_`, `MTN_` | SFX | Scripted sequences |
| `OBJ_` | SFX | Objects |
| `PX_`, `PHY_` | SFX | Physics |
| `WPN_` | SFX | Weapons |
| `CHR_` (in SFX) | SFX | Character foley |
| `DVC_` | SFX | Gadgets |
| `GUI_`, `SBJ_` | SFX | Interface |
| `VFX_` | SFX | Visual effects |
| other `NPC_` | SFX | Enemies & NPCs |
| `CHR_`, `GEN_` | SFX | Character foley |
| `Global` | SFX | Global |
| anything else (e.g. unnamed hex IDs) | Unknown | Unknown |
BANK sounds (in-memory, `docs/formats/banks.md`) are classified by the name of their **soundbank** (e.g.
`CHR_CatwomanFoleySuit`, `Speech_EMOTE_Batman`), which is more systematic than their event names.
Counts in the ITA catalog: Story missions 1993, Predator chatter 1796, Character lines 1537, Open-world chatter
1295, Side missions 808, Combat barks 801, Efforts & emotes 616, Interview tapes 514, Riddler 360, Radio & TV 226,
Gameplay & hints 199, DLC 167, Boss fights 150, Cinematics 150, Reactions 108, Interrogations 95. SFX archive:

View File

@@ -55,8 +55,29 @@ loopback); it is a developer tool and not part of the editor.
Speech/ambience variations are addressed by offset+size from BKPK tables inside compressed `.upk` packages,
which ackit does not rewrite. Replacements are written in place and must not exceed the original WEM's size;
the encoder lowers quality, then sample rate, to fit, and refuses otherwise. Music stream packs have a loose
index (`SFX\*.bnk`) that ackit rewrites, so music can be any length. (Rewriting `.upk` packages would lift the
limit but means LZO recompression of large engine packages: out of scope, high risk.)
index (`SFX\*.bnk`) that ackit rewrites, so music can be any length. In-memory sounds follow the same rule
(below). Lifting the limit would mean re-laying out the uncompressed packages (export offsets, BKPK/DIDX
tables): high risk for little gain, not done.
## 4b. In-memory sounds (soundbanks in loose `.bnk` files and `.upk` packages)
- **Model:** one catalog row per Wwise media ID (archive `BANK`, target = media ID), not per bank: the same
media is stored in up to dozens of bank copies (banks are cooked into every package that uses them), and the
game may load any of them. A replacement is written into **every** copy (`BANK_locations.csv.gz`).
- **In place:** each copy keeps its size (zero-padded, like WAD speech); DIDX/HIRC offsets and the package
layout never change, so nothing else in the engine data has to be rewritten.
- **Packages:** ackit reads the LZO packages itself (pure Python, ~47 MB/s, block-level access) and writes
only the changed 128 KiB blocks, stored as a single LZO literal run (valid LZO1X without matches), then
fixes the chunk table. No compressor and no external tool are needed; untouched blocks stay byte-identical.
- **Revert:** before the first change to a bank file a verified full copy goes to the store (mandatory, not
optional like the WAD copies: the file is rebuilt from it). The file content is always "original copy +
replacements applied now", so reverting the last one restores the original bytes exactly, and an interrupted
operation is repaired by the next one. Conflicts: the whole-file SHA-1 must be the original's or the last one
ackit wrote.
- **Prefetch heads:** 396 speech lines (about 79 per language) have their first bytes copied into banks; a
replacement of such a line rewrites those heads too, otherwise the start would play the old line.
- **Names:** the hash list names none of these media. Names come from Wwise events whose names are in the
packages' name tables (FNV-1 of the name = event ID), following event -> action -> containers -> sounds
(6,416 sounds); the rest are numbered per bank. Categories use the bank name.
## 5. Catalog
- Shipped CSVs per archive (names, categories, characters, original size + checksum) make loading instant and

View File

@@ -2,8 +2,8 @@
Most short sound effects (footsteps, foley, combat, weapons, objects, UI, grunts) are **not in the WADs**: they
are WEMs stored inside Wwise soundbanks, either loose in `SFX\*.bnk` or embedded in the UE3 packages (`.upk`).
The catalog and the editor only cover the WADs (`wad.md`), so none of these sounds are listed or editable yet.
Survey of the installed game (buildid 3718300) on 2026-10-09, read-only.
ackit lists them as the catalog archive `BANK` and writes them (see the end of this page).
Survey of the installed game (buildid 3718300), 2026-10-09.
## Inventory
@@ -60,11 +60,12 @@ WAD sounds have such heads (2,101 copies); replacing one of them must rewrite th
- Largest: `CHR_RobinFoleyCape` 300 WEMs, `CHR_BatmanFoleyCape` 282, `CHR_Clayface` 263,
`CHR_NightwingFoley` 242, `CHR_CatwomanFoleySuit` 225, `CHR_MrFreeze` 176, `CHR_Combat` 166.
## Consequences for editing (not implemented)
- **Loose banks**: a replacement means rebuilding `DATA`/`DIDX` and the in-memory offsets/sizes in the HIRC
Sound objects of that bank (and in any other bank whose sources use its media). Whether the game accepts a
bank that changed size is untested.
- **Embedded banks**: the package must be rewritten (decompress, patch every blob of the bank, recompress
LZO, fix the package's chunk table and export offsets/sizes). A sound must be replaced in **every** package
that carries a copy, otherwise the game may load the original from another package.
- Either way: unverified in game so far; the same WEM rules apply (`wem.md`).
## How ackit edits them
- Catalog archive `BANK` (one row per media ID) + `data/catalog/BANK_locations.csv.gz` (every copy, and the
prefetch heads of speech lines); built by `tools/build_catalog.py`, checked by `ackit verify --catalog`.
- A replacement is written **in place into every copy** (no larger than the smallest copy, zero-padded), so
DIDX/HIRC offsets and sizes and the package layout never change. Loose banks are patched directly; in
packages only the touched blocks are re-encoded (`upk.md`). A sound left in one package only would let the
game load the original from another one.
- Replacing a speech line that has prefetch heads rewrites the heads (first bytes of the new, zero-padded line).
- Design and revert rules: `docs/decisions.md` (4b). The same WEM rules apply (`wem.md`).

View File

@@ -72,6 +72,19 @@ Conclusion: approach A works end-to-end in-game. Use >= 30 s probes for the intr
UI, grunts) are in-memory WEMs inside soundbanks: 65 loose `SFX\*.bnk` (1,117) and 3,340 banks embedded in
the `.upk` packages (15,641; many banks copied into several packages). None are in the catalog; the WAD
side is complete. Inventory, layout and editing consequences: `docs/formats/banks.md`.
17. (2026-10-09) Speech lines have **per-language media IDs**: every package carries one BKPK table per language
for the same WAD entry. 396 lines (78-80 per language) have a prefetch head (first N bytes) in banks; each
head equals the start of exactly one language's WEM.
18. (2026-10-09) In-game test of in-memory sounds (`ackit verify --ingame --banks`): 77 probes written into the
78 sounds stored in the Frontend*/Startup packages (54 files: patched LZO packages + loose banks). The game
starts and runs, all 68 probe byte patterns are found in its memory (it loaded the patched packages), revert
byte-identical: PASS. Not shown: a probe being *heard*. Only 7 Vorbis streams start on the title screen
(setups 4f708975, 6ded1c65, 92f94a3f, d93b5944, d4b2e824: shared by hundreds of sounds, music included) and
none is a probe. Tried: Enter/arrow keys posted to the game window (`debugger.post_key`, WM_KEYDOWN) to reach
the main menu: no UI sound followed (the game probably reads input another way; global SendInput is not
used on purpose); a breakpoint on the setup parser entry: no extra events. Hypotheses: no in-memory sound
plays without input; the decode path is the same as for WAD sounds (same WEM rules, 300/300 in-memory WEMs
rebuild byte-identically), so playback is expected to work like E4/E10.
## Old path -> new path
`scripts/harness.py` -> `ackit verify --ingame` (ackit/verify/ingame.py); `scripts/validate_builder.py` ->

View File

@@ -20,14 +20,16 @@ back with one click.
- Type in the **search box** (Ctrl+F): words can be parts of the name, a category, a character, your own
notes or tags, or an ID like `b49cdf48`. All words must match.
- Narrow down with **Group** (Speech, Music, Ambience, SFX), **Category** (e.g. *Interview tapes*,
*Combat barks*, *Cinematics*), **Character**, **Archive** (speech language or SFX), **Tag**, **Modified
*Combat barks*, *Cinematics*), **Character**, **Archive** (speech language, SFX or BANK), **Tag**, **Modified
only** and **Favorites only**. Combine them freely; results update instantly.
- **Character** is the verified speaker from the curated catalog. Grey `~` names are only *mentioned* in the
sound's name (useful hints for the many unlabelled lines); tick *Include characters mentioned in names*
to filter on them too.
- Click a column header to sort (length, size, status, ...). Ctrl/Shift-click selects several rows.
- Most short effects (footsteps, foley, weapons, menu sounds) are stored inside soundbanks and are not listed
yet; the list covers voice lines, music, ambience and the effects of the SFX archive.
- Archive **BANK** holds the ~15,400 short effects stored inside soundbanks (footsteps, foley, combat,
weapons, menu sounds, grunts). Many are named after the game event that plays them (`Play_...`), the
rest after their bank (`CHR_Bane_3`). The editor shows the bank and how many copies the game keeps of
the sound; a replacement is written into every copy.
- **Language** (top left) switches the speech language. Speech is read from the language the game is set to.
![Browsing by category](img/browse.png)