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

@@ -2,9 +2,10 @@
| file | content |
|------|---------|
| `<ARCHIVE>.csv` | every sound of one audio archive: `ITA`, `INT`, `FRA`, `DEU`, `ESN` (speech, one per language) and `SFX` (music, ambience, effects of the SFX WAD; sounds stored inside soundbanks are not listed, see `docs/formats/banks.md`) |
| `<ARCHIVE>.csv` | every sound of one audio archive: `ITA`, `INT`, `FRA`, `DEU`, `ESN` (speech WADs, one per language), `SFX` (music, ambience and effects streamed from the SFX WAD) and `BANK` (in-memory sounds stored inside soundbanks: footsteps, foley, combat, weapons, UI, efforts; `docs/formats/banks.md`) |
| `BANK_locations.csv.gz` | every stored copy of the `BANK` sounds, and the prefetch heads of speech lines (below) |
| `characters.csv` | `name,character`: verified speaker per sound name (language independent) |
| `index.json` | fingerprint of the game WAD each CSV was built from (`ackit/game.py: wad_fingerprint`) and counts |
| `index.json` | per archive: fingerprint of the game files the CSV was built from (`ackit/game.py: wad_fingerprint`, `ackit/banks.py: game_fingerprint`) and counts |
| `source/<lang>/*.txt` | **the curated original lists** (one file per character, one wemscan-style file name per line, e.g. `Speech_TAPE_JOKER_1_4.wem`). `Files.txt` = all files, `Stats.txt` = counts, `Checkpoint.txt` = progress marker. Never edited by tools. |
All CSVs are UTF-8 with a header row. They contain only names and metadata (no game audio).
@@ -12,26 +13,42 @@ All CSVs are UTF-8 with a header row. They contain only names and metadata (no g
## `<ARCHIVE>.csv` columns
| column | meaning |
|--------|---------|
| `archive` | `ITA`, `INT`, ... or `SFX` |
| `target` | location inside the archive: `HASH` (entry with one WEM), `HASH:i` (i-th variation, 0-based), `PACK/MEDIA` (music stream pack) |
| `name` | display name (`NAME`, `NAME_1..n` for variations, see `ackit/catalog.py`) |
| `aliases` | other accepted names, `\|`-separated (`NAME_1` for single entries, `NAME#i` for variations) |
| `archive` | `ITA`, `INT`, ..., `SFX` or `BANK` |
| `target` | location: `HASH` (WAD entry with one WEM), `HASH:i` (i-th variation, 0-based), `PACK/MEDIA` (music stream pack), `MEDIA` (BANK: Wwise media ID) |
| `name` | display name: `NAME`, `NAME_1..n` for variations (`NAME#i` when `NAME_i` is already another entry's own name); BANK: the Wwise event that plays it (`Play_..._i`) or `<Bank>_<n>` |
| `aliases` | other accepted names, `\|`-separated (`NAME_1` for single entries, `NAME#i` for variations, `<Bank>/<MEDIA>` for BANK) |
| `group` | Speech, Music, Ambience, SFX, Unknown (`docs/categories.md`) |
| `category` | finer category (`docs/categories.md`) |
| `bank` | BANK: the soundbank holding the sound (first by name if several do); empty for WAD sounds |
| `character` | verified speaker(s) from `characters.csv`, `; `-separated, empty if unknown |
| `characters_inferred` | characters/roles mentioned by the name (hint only) |
| `characters_inferred` | characters/roles mentioned by the name (and bank) (hint only) |
| `seconds`, `rate`, `channels` | of the original sound |
| `size` | size of the original WEM in bytes = maximum size of a replacement (except music packs: any size) |
| `sha1` | first 16 hex digits of the original WEM's SHA-1 (detects files changed outside the tool) |
| `sha1` | first 16 hex digits of the original WEM's SHA-1 (detects files changed outside the tool; empty when the copies of a BANK sound differ) |
| `notes` | free text (empty; for future curation, e.g. transcripts) |
The editor uses these files directly when the installed WAD matches `index.json`; for another game build it
scans the WAD itself and applies the same rules (no checksums then).
## `BANK_locations.csv.gz` columns (gzip-compressed CSV)
| column | meaning |
|--------|---------|
| `media` | Wwise media ID (hex) |
| `file` | file under `BmGame/CookedPCConsole`, `/`-separated (`SFX/xxxxxxxx.bnk` or `Name.upk`) |
| `offset` | position of the WEM in the *uncompressed* file (hex; `docs/formats/upk.md`) |
| `size` | bytes reserved for it there |
| `kind` | `m` = copy of a BANK sound, `p` = prefetch head of a speech line |
| `sound` | `p` rows: key of that speech line (e.g. `ITA:2B14E472:3`); each language has its own lines and heads |
A replacement is written into every `m` copy of a BANK sound; replacing a speech line also rewrites its `p`
heads (`docs/formats/banks.md`).
The editor uses these files directly when the installed game matches `index.json`; for another game build it
scans the game itself and applies the same rules (no checksums then; the bank scan takes a few minutes).
`ackit verify --catalog` cross-checks the catalog in use against the game files (`--quick` samples packages).
## Rebuilding / adding a language
1. Make sure the game files are original (`ackit status`; `ackit revert --all`).
2. Optional: add listening-based lists for another language in `source/<lang>/<Character>.txt` (same format;
names are the same in every language, so the Italian lists already label all languages).
3. Run `python tools/build_catalog.py` - it scans every archive of the installed game (all languages present)
and rewrites the CSVs, `characters.csv` and `index.json`.
3. Run `python tools/build_catalog.py` - it scans every archive and all soundbanks of the installed game (all
languages present; ~5 minutes) and rewrites the CSVs, the location index, `characters.csv` and `index.json`.
4. Run `ackit verify --catalog` (no issues expected).
New character labels need a display name in `ackit/classify.py: USER_LABELS` (otherwise the file name is used).