55 lines
3.9 KiB
Markdown
55 lines
3.9 KiB
Markdown
# Subtitles (`RDialogueEvent` objects in the packages)
|
|
|
|
There are no subtitle files: every speech line has a dialogue object (class `RDialogueEvent`, Rocksteady) inside
|
|
the UE3 packages (`upk.md`), holding the subtitle text of all 11 languages. ackit reads them with
|
|
`ackit/subtitles.py` and writes them through `Workspace.texts` (`ackit/texts.py`).
|
|
|
|
## Inventory (buildid 3718300)
|
|
- 14,743 dialogue objects in the packages for 9,016 speech lines (a line's object is copied into every package
|
|
that uses it; all copies are identical).
|
|
- 11 languages: INT, FRA, ITA, DEU, ESN, ESM, POL, POR, RUS, JPN, KOR (the speech WADs exist only for INT, FRA,
|
|
ITA, DEU, ESN; the other languages play the INT voices with their own subtitles).
|
|
- ~9,400 subtitle strings per language: 94 % of the subtitles have one line, the rest up to 5 lines.
|
|
- Strings are stored as cp1252 (positive FString length) or UTF-16 (negative length): RUS, POL, JPN, KOR
|
|
and some INT/ESN/FRA/DEU strings are UTF-16.
|
|
|
|
## Scripted subtitles (`RSubtitle`)
|
|
41 more subtitles (radio and encryption-card lines) are `RSubtitle` objects in `Startup.upk`: the same layout,
|
|
with the per-language array named `ArraySubtitles` and a float (duration) at `0x2C` instead of a speech
|
|
reference. `RSubtitleLookup` (`Startup.upk`, `Lookups`) is an array of `{int key @0, object @4}`, key =
|
|
FNV-1 of the speech line name without `Speech_` (e.g. `CH_ZERO_CH0_ENCRYPTION_CARD2_01`), which links each
|
|
`RSubtitle` exactly to its line. ackit reads and writes both kinds the same way.
|
|
|
|
## Object layout (compact tagged properties)
|
|
The export data is a list of properties `u16 type, u16 field offset, value`, ended by `u16 0`. Types seen:
|
|
`0x10` object reference (i32 export/import index), `0x0D` FString, `0x09` struct/array (name i32+i32 = struct
|
|
or property name from the name table, i32 byte size, i32 array index, then `size` bytes), `0x04` 4-byte value,
|
|
`0x03` bool, `0x01` enum. Relevant properties:
|
|
|
|
| field | content |
|
|
|-------|---------|
|
|
| `0x10` @ `0x2C` | the speech event this line plays, e.g. export `Speech_CHURCH_B1_Ch2_POST_Explosion_BM_Cash_04` |
|
|
| `0x0D` @ `0x34` | speaker, e.g. `Batman` |
|
|
| `0x09 LocalizedSubtitles` | u32 count, then per language: `0D 00 00 00` FString language code, `09 00 0C 00 Subtitles` struct, `00 00` |
|
|
| `Subtitles` | u32 line count, then per line: `0D 00 00 00` FString text, `04 00 0C 00` 4-byte value, `00 00` |
|
|
|
|
The sizes add up exactly in all 14,743 objects (checked by the parser: `SubtitleError` otherwise).
|
|
|
|
## Link to the speech sound
|
|
The speech event name is hashed (FNV-1) to the Wwise event ID, followed through the soundbanks (event ->
|
|
action -> containers -> Sound) to the streamed media, and that media ID is looked up in the per-language
|
|
BKPK tables (`banks.md`) to find the WAD entry and variation of each language. This is exact; the `_NN`
|
|
suffix of the event name does **not** match the variation order inside the WAD entry (WEMs are stored in
|
|
media-ID order). Lines kept in memory link to the localized BANK sound of their language. Linked: ~99 % of
|
|
the strings (e.g. INT 9,265 / 9,379); the rest have no event that reaches a sound (unused or placeholder
|
|
lines such as `..._TEMP_29`) and stay editable without a sound link.
|
|
Event names with a number (`Speech_X_04`) exist only as export names, not in the name table.
|
|
|
|
## How ackit writes them
|
|
- In place, into **every copy** (`data/catalog/SUB_locations.csv.gz`): the new text replaces the FString's
|
|
bytes and is padded with NUL characters, keeping the length field. The game stops at the first NUL, so no
|
|
size field, export or package layout changes. Limit: the new text must fit the original string's bytes
|
|
(cp1252: one byte per character; UTF-16: two) - shown as "room" in the CLI and the editor.
|
|
- A cp1252 string can only hold cp1252 characters; UTF-16 strings can hold any character.
|
|
- Packages are rebuilt from the store's original copy + all applied sound and text edits (`docs/decisions.md`).
|