Safety - Writes are serialized (Workspace.lock): two background jobs rebuilding one package (a sound and its subtitle applied side by side) lost an edit. - A store copies a bank file or package as "original" only if it matches the shipped catalog (Workspace.unoriginal_files: in-memory sounds, bank copies of voice lines, subtitles); the editor refuses to switch stores while changes are applied. A second store could otherwise resurrect a reverted sound. - Mod import in the editor asks before overwriting files changed outside the tool (it forced silently). - Revert all in the editor reverts texts too; the uninstaller counts text edits before offering to delete the store. - ackit restore-backup: the full-backup restore decisions.md described; it keeps the sounds of WADs without a full copy recorded. - build_catalog also stops when a subtitle differs from the catalog. Languages and texts - Game.language() returns languages without voices (JPN, RUS, ...) and Game.voice_archive() their INT voices; ackit text --lang JPN works. - Menu lookups parse one cached file (find & replace: minutes -> ms). - Subtitles decode cp1252 as Windows does: the UTF-8-in-cp1252 line Speech_TAPE_J_Answer_1_00 (KOR/POL/POR/RUS) is written back byte-for-byte; SUB_KOR/SUB_RUS rebuilt from the original game (only those rows changed). - Mods exported while another language is loaded keep name and line. Other fixes - Down-mix folds extra channels in at -3 dB (a 5.1 centre voice was dropped). - e2e_editor no longer overwrites the user's settings.json; kill_game waits for the game to exit before reverts. - Recorder handles Int32/UInt8 input and split frames; package block cache locked; conflict messages, CLI help examples, stale docstrings and doc references fixed; tests no longer read this PC's game language. Docs (decisions, user guide, README, subtitles.md, banks.md, catalog README, REPORT, NOTES, CLAUDE.md) updated; check_docs: 102 claims match. 148 tests; e2e_features 9/9, e2e_editor and verify PASS; game original afterwards.
9.5 KiB
9.5 KiB
Report — full-game audio, subtitles and menu text (mission 3)
Brief: docs/history/mission3_audio_text_prompt.md. Earlier missions: the WAD/WEM toolkit (mission 1) and the
editor Unheard City (mission 2).
What works (verified)
- Every sound of the game is cataloged and editable: the speech WADs (5 languages x 10,816), the SFX WAD (651)
and, new, the 15,355 in-memory sounds stored inside soundbanks (archive
BANK: footsteps, foley, combat, weapons, UI, efforts; 8,092 shared by all languages, ~1,450 localized speech lines per voice language, 6 shared by several) in 65 loose.bnkfiles and 975.upkpackages. ackit reads and patches the LZO-compressed UE3 packages itself (ackit/upk.py,lzo.py; no external tools). A replacement goes into every copy (93,826 copies). 445 WAD voice recordings (89 lines) also have bank copies (396 prefetch heads, 49 whole copies; 2,185 copies), which are rewritten with them. Names: 13,501 BANK sounds are named after the Wwise event that plays them (Play_...,Speech_...), the other 1,854 after their bank. - Catalog check:
ackit verify --catalogcompares the catalog with a fresh scan of the game (missing, extra, duplicate, size/checksum, category, character, language, bank copies, subtitle copies/texts) and re-derives every relation from the game files (lines, names, subtitle links, prefetch heads, copies): catalog OK on the full game. - Subtitles: found in
RDialogueEventobjects inside the packages (9,032 speech lines x 11 languages) and in 41 scriptedRSubtitleobjects (radio, encryption cards, Riddler; linked throughRSubtitleLookup), parsed, cataloged (SUB_<LANG>.csv) and linked to their voice recordings through the soundbanks: 103,304 of 103,658 strings in 11 languages (INT 9,373 of 9,404), each on the same line in every language; an event that plays two versions of a line links both. Edited in place in every copy (168,688 copies). - One recording per language, from the game data alone (
docs/formats/banks.md, Lines): every voice recording is tied to its line by the game's Wwise Sound objects (same IDs in every language), and each WAD recording to its media by its own language's BKPK table (10,816 / 10,816 in each language). Result: 12,434 lines (WAD + in-memory) with exactly one recording in each of the 5 voice languages, no gap and no conflict; recordings stored twice (5 per language) are written together. Independent checks: every WAD recording is a source in a bank of its own language (54,055 / 54,055), every prefetch head or whole bank copy is the start of its recording (2,185 / 2,185). Each line has one name in every language and carries its identity (line): speaker labels (3,366 in every language), subtitles and mod packages (another language: the same line) follow it. Speaker labels vs the game's dialogue speaker field: 2,766 agree, 19 differ (categories.md). - Menu text: the localization files (
BmGame\Localization\<LANG>\*.int, ~7,000 strings per language), edited value by value; everything else in the files stays byte-identical. - Editor: one Language setting (default: the game's language) for voices, subtitles and menu text (languages without voices use the INT ones, like the game); tabs Sounds / Subtitles / Menu text. The sound editor shows Listen / Replace / Subtitle side by side (resizable columns, no scrolling at usual window sizes); the subtitle list links back to the sound. Search, filters (character, category, file, modified), room counter, apply/revert/revert all, find & replace on the filtered rows, mod export/import with texts, changed-outside banner for sounds and texts.
- Safety: every changed file (WAD, bank, package, localization file) is rebuilt from a verified original copy
in the store + the edits applied now; reverting the last edit restores it byte-for-byte; files changed by
another tool or Steam are detected (whole-file SHA-1) and not overwritten without confirmation (also when
installing a mod in the editor). The original copy is taken only from a file that matches the catalog
(another tool's or another originals store's change is a conflict; checked on all 1,447 files with sounds or
subtitles: no false alarm), and the editor does not switch stores while changes are applied. Writes are
serialized, so a sound and its subtitle applied at the same time both reach the package. Revert all
(editor and
ackit revert --all) reverts sounds and texts;ackit restore-backupis the last resort for a damaged store. - Tests: 148 automated tests (unit, synthetic game with packages/banks/dialogues/localization, a
two-language game for the lines (incl. a recording also stored whole in a bank), the shipped catalog's cross-references, real-game read-only (every
name of every language's catalog resolves to its own sound), offscreen GUI incl. list order, selection and
loading overlays) pass.
ackit verifyPASS (incl. 300/300 in-memory WEMs rebuilt byte-identically). - Real game (2026-10-10), all reverting and checking byte-identity themselves:
tools/e2e_features.py --lang all89/89 PASS in all 11 languages (every copy of an in-memory sound, each language's localized line, prefetch heads, a line also stored whole in banks, a multi-copy subtitle, a scripted subtitle, a menu text, sound + subtitle in one package, a mod moved to another voice language and matched to the same line at another position);ackit verify --ingamePASS (music heard, ncc 1.0);--banksPASS (77 in-memory sounds of the front-end packages replaced in 54 files: the game loads them, 71 probes found in its memory, no crash);--textsPASS (12/12 menu texts and 23/23 subtitles of Startup.upk found in the game's memory, no crash);tools/e2e_editor.pyPASS. Game files original at the end (ackit status). The catalog builder refuses to run on a modified game, so the catalog always describes the original files. - Package:
dist/UnheardCity-3.2.3-windows-amd64.zip(86 MB, catalogs included). WithPATHreduced to the Windows folders the packaged exe passes its smoke test (in the game's language - Italian on the test PC: 21,015 sounds loaded, encode + inject, 9,377 subtitles and 7,020 menu texts loaded, an in-memory sound decoded from a package) and the GUI starts and quits cleanly. The installer is unchanged since mission 2 (install/upgrade/uninstall verified then); the uninstaller now also counts text edits before offering to delete the user data (2026-10-13; that count was tested, the full uninstall cycle was not run again).
Unresolved / limitations
- Checked by a person in game (2026-10-10), not automatically (nothing of this plays or shows on the title screen without input, findings 18): replaced voice lines and edited subtitles are heard and shown; replaced footsteps play on every step. That test found the one defect the automatic checks could not: in-memory replacements followed by zero padding in their slot played as silence (fixed in 3.2.2, findings 24).
- Size limits: in-memory sounds and speech must fit their original size; subtitles must fit the original text's length ("room"); cp1252 subtitles accept cp1252 characters only. Lifting these limits means moving objects inside the packages (new export offsets/sizes, added chunks): designed but not done, because it could not be verified automatically in the game (decisions 4c).
- Unlinked subtitles: 24 speech names (24-36 strings per language, INT 31: Riddler trophy texts, Arkham City Stories, radio reports and tapes) have no event in any bank; they are listed and editable but not linked.
- Subtitle coverage is uneven in the game itself (INT voice lines with a subtitle): none for efforts/grunts, few for combat barks (9 %), cinematics and boss fights (23 %; most cutscene lines are not played through speech events and have no dialogue object), 89-97 % for story, predator, character and open-world lines. No other subtitle storage was found in the packages (all classes with subtitle data checked).
- 637 voice lines are played by no event whose name the game stores; they are named after their English recording's position (the same name in every language). Mod packages from versions before 3.1 carry no line identity: their voice lines are not moved to another language (listed instead of guessed).
- Speaker labels: 2 names of the curated lists match two recordings each (wemscan name collision), both are labelled; 19 labels differ from the game's dialogue speaker field (to be checked by listening).
- Unnamed sounds: 1,854 BANK sounds are reached by no event whose name the game stores and are named
<Bank>_<n>. - 25 SFX originals (music seek tables and sample counts, looping ambience) are not reproduced byte-for-byte by
the WEM rules (
docs/formats/wem.md); writing is not affected (replaced music plays in game). - Two copies of media
1C898731differ; its checksum is left empty. Channel order for 4-6 channel sounds is unverified. M4A/AAC/WMA input needs an ffmpeg on the system. - Another game build is scanned at first start (WADs: seconds; banks, lines and subtitles: a few minutes, cached); it gets the same line identities, but no original checksums.
Next steps
- Optional: lift the subtitle length limit by relocating dialogue objects (checked in game by a person).
- Build and smoke-test the Linux package once on a Linux machine.
- Listen to the 19 lines whose speaker label differs from the game's dialogue speaker field
(
docs/categories.md) and fix the curated lists if needed; extend them (data/catalog/source/<lang>/*.txt) and rebuild the catalog.