Version 3.2.0: fast lists with loading spinners, names from the game files, INT docs and screenshots, cleanup

This commit is contained in:
2026-10-10 14:58:17 +02:00
parent fbde809370
commit 1586abb8e9
40 changed files with 449 additions and 19279 deletions

View File

@@ -62,10 +62,10 @@ Two separate fields, because Wwise names describe a *scene*, not the speaker of
alternates speakers between `NAME_1`, `NAME_2`, ...):
- **character** - the verified speaker, only from the curated lists in `data/catalog/source/<lang>/*.txt`
(one file per character, listening-based). The lists name ITA sounds by their position in the ITA WAD
(wemscan names); positions differ between languages, so each listed recording is found at its ITA position
and the label goes to its line (`characters.csv`, keyed by the line's name), i.e. to the same recording in
every language. A label applies to exactly the listed recordings (not to other versions of the same event).
(one file per character, listening-based; the lists so far were made on the Italian voices, `source/ita/`).
A list names sounds by their position in its language's WAD (wemscan names); positions differ between
languages, so each listed recording is found at its position in that language and the label goes to its line
(`characters.csv`, keyed by the line's name), i.e. to the same recording in every language. A label applies to exactly the listed recordings (not to other versions of the same event).
Checked by `ackit verify --catalog` against the speaker field of the game's dialogue objects: 2,766 labelled
lines agree (spelling aside: Mister Freeze, Bruce Wayne, TwoFace, Mysterious_stranger = Azrael), 25 listed
as Unknown are named there, 19 differ (to listen to: `Batman_sword_raised_01-04` game: Ra's al Ghul,

View File

@@ -77,7 +77,7 @@ tables): high risk for little gain, not done.
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
- **Names:** no list names these media. Names come from Wwise events whose names are in the
packages' name and export tables (FNV-1 of the name = event ID), following event -> action -> containers ->
sounds (6,456 sounds); the rest are numbered per bank. Categories use the bank name. A localized speech line
has the same name in every language (its line, section 5).
@@ -111,6 +111,9 @@ copy per voice language) is filtered the same way, so the sound list never mixes
## 5. Catalog
- Shipped CSVs per archive (names, categories, characters, original size + checksum) make loading instant and
enable change detection; any other build is scanned at runtime (header reads only, cached).
- Names come from the game files only: a WAD entry or stream pack is named after its soundbank in the game's
`CookedBanks.txt` (entry ID = FNV-1 of the bank name; this gives exactly the names of the community hash list
the toolkit used to ship, which was removed), speech lines after their events (below).
- Verified speakers come only from the user's lists; name-based character inference is kept in a separate field.
- Voice lines are identified by the game's own data, not by names or positions (`docs/formats/banks.md`,
*Lines*): the recordings that are sources of the same Wwise Sound objects are one line, with exactly one
@@ -159,8 +162,13 @@ copy per voice language) is filtered the same way, so the sound list never mixes
- Background: the wallpaper sits only behind the filter panel, scaled to the panel height so its left part fills
the panel, under a light veil. Settings -> Background toggles it and sets its strength (default 10 %).
- Loading feedback stays where the work is, never modal: a spinner inside the waveform being loaded (sound or
replacement file, decoded off the GUI thread), next to the Apply/Revert buttons while encoding/writing, and in
the status bar for game loading and bulk changes.
replacement file, decoded off the GUI thread), next to the Apply/Revert buttons while encoding/writing, over
a list while it loads (the sound list at start and on a language change, the subtitles / menu text of a
language, read off the GUI thread), and in the status bar for game loading and bulk changes.
- Lists filter and sort in Python, not with Qt's QSortFilterProxyModel (`app/models.py: RowView`): the view
computes the visible rows in one pass and one sort over cached keys (~10-20 ms for 21,000 sounds; the Qt
proxy called back into Python for every comparison: up to 1.5 s per sort or widened filter). Rows a filter
shows again keep their sorted place; selection and the current row survive every change.
All filter selectors (including Character) are plain, non-editable drop-downs so they look and behave alike.
## 8. Verification

View File

@@ -28,7 +28,7 @@ All WEMs seen are Vorbis (format tag 0xFFFF), the same codec as the WADs: the fo
the embedded ones mono or stereo (~22 % stereo in a 2,533-WEM sample).
## Names
- The community hash list names none of these media IDs; only the bank names are known.
- No list names these media IDs (the community hash list names only WAD entries and banks).
- `CookedPCConsole\CookedBanks.txt` lists all 3,345 cooked bank names. Bank ID = FNV-1 32 of the lowercased
name (the same hash Wwise uses for every name). 3,338 of them are embedded in packages, 4 are loose `.bnk`
files (`Init`, `Global`, `Music`, `Music_PDLC`); `AMB_OW_A6`, `AMB_OW_C1`, `AMB_OW_E1`,

View File

@@ -9,7 +9,7 @@ per language (INT, ITA, FRA, DEU, ESN, ESM, POL, POR, RUS, JPN, KOR), ~37 files
- UTF-16LE with a BOM (`FF FE`), CRLF line ends; the first line is empty. (`INT\Version.int` and
`VersionData.int` are plain ASCII build IDs, not UI text: ackit ignores them.)
- `[Section]` lines, `Key=Value` lines, `;` comments. A few values are in double quotes (`Quit="Quit game"`).
- File extension = language code (`FRONTEND.int`, `FRONTEND.ITA`); case varies, lookups are case-insensitive.
- File extension = language code (`FRONTEND.int`, `FRONTEND.FRA`); case varies, lookups are case-insensitive.
- Markers inside values (e.g. `\n`, button tags) are the game's own; ackit keeps them as plain text.
## Keys and writing

View File

@@ -21,11 +21,13 @@ Rules:
- 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 media/file ID (names: `hashlist.txt`, e.g. `CHR_RadioScanner`, `AMB_Steel_B3`, `Music`).
- `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 (ITA: 10816 WEMs in
2464 entries; e.g. `CHR_RadioScanner` holds 91). Each WEM starts at the next 0x800 boundary after
- **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

View File

@@ -1,7 +1,9 @@
# WEM format accepted by Arkham City (Wwise 2011.1, Vorbis)
Derived from the game's WEMs; `ackit verify` / `tests/test_realgame.py` rebuild them byte-for-byte from these rules
alone (all 10816 ITA WEMs; in SFX only the 4 looping music tracks with loop points differ).
alone. Checked on every WEM of the 5 speech WADs: 54,079 of 54,080 are identical; the exception (INT
`FE9963E8:74`) differs only in the informational avg-bytes/s field (one lower than the formula below). In SFX
only the 4 looping music tracks with loop points differ.
## RIFF layout
`RIFF` size `WAVE`, then `fmt ` (0x42 bytes), optional `cue `/`LIST` (37 files), `data`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 191 KiB

View File

@@ -80,7 +80,7 @@ Conclusion: approach A works end-to-end in-game. Use >= 30 s probes for the intr
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
none is a probe. Tried: Enter/arrow keys posted to the game window (WM_KEYDOWN/WM_KEYUP; that helper was removed later) 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

View File

@@ -28,7 +28,7 @@ Changing it reloads the lists; the next start is again in the game's language.
## Names
Every voice line has one name in every language, taken from the game itself (e.g. `Speech_TAPE_JOKER_1_04`, the
same as in the Subtitles tab): the line `Speech_X` in Italian is the same line `Speech_X` in English, French,
same as in the Subtitles tab): the line `Speech_X` in English is the same line `Speech_X` in French, Italian,
German and Spanish. Inside a WAD entry the game stores the variations of a line in a different order in each
language, so the position-based name is kept only as a second name (`Speech_TAPE_JOKER_1#4` = the 4th WEM of
that entry in this language; search finds both). When the game plays one of two versions of a line, the
@@ -118,7 +118,7 @@ Click the **★** column to mark favorites. These are stored in your user data f
data plus the audio, so it also works for another language whose original has a different size).
- **Import mod…**: shows which sounds match this game, then installs them. You can revert them like any change.
- Changed subtitles and menu texts are exported and imported with the sounds (listed as *Text*); a text stays
in its language (an Italian subtitle edit changes the Italian subtitle).
in its language (a French subtitle edit changes the French subtitle).
- In another language, voice lines are matched by the game's own identity of the line, so the same line is
replaced even where its position differs. Voice lines from packages made with versions before 3.1 carry no
such identity: the import lists them instead of guessing (export the mod again with this version).