Version 3.2.2: in-memory replacements fill their slots exactly (padded ones played as silence)
This commit is contained in:
@@ -65,8 +65,11 @@ tables): high risk for little gain, not done.
|
||||
- **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.
|
||||
- **In place:** each copy keeps its size; DIDX/HIRC offsets and the package layout never change, so nothing
|
||||
else in the engine data has to be rewritten. Unlike WAD speech, an in-memory copy is never zero-padded:
|
||||
the replacement fills its slot exactly with a label list chunk (the game plays a padded in-memory WEM as
|
||||
silence; `docs/formats/banks.md`). Shrinking the declared sizes (DIDX and every Sound object's in-memory
|
||||
size) would also work, but needs every HIRC source of the media in every bank copy located and 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.
|
||||
|
||||
@@ -111,10 +111,16 @@ and the game ties the recordings together itself:
|
||||
- Catalog archive `BANK` (one row per media ID) + `data/catalog/BANK_locations.csv.gz` (every copy, and the
|
||||
heads and whole copies of WAD speech recordings); 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 or whole copies rewrites them (the first bytes of the new,
|
||||
zero-padded line; for a whole copy, all of it).
|
||||
- A replacement is written **in place into every copy** (no larger than the smallest copy), 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.
|
||||
- **Each copy is filled exactly**, never zero-padded: every in-memory WEM the game ships ends exactly at its
|
||||
slot's end, and the game plays a replacement followed by zero padding there as **silence** (findings 24:
|
||||
footsteps replaced that way were silent in game; filled, they play). A smaller replacement is grown to the
|
||||
slot size with a label list chunk before `data` (`LIST{adtl, labl}`, the form the game's own WEMs use for
|
||||
markers; `ackit/wem.py: fill`), so the encoder leaves at least 26 spare bytes, an even number.
|
||||
- Replacing a speech line that has prefetch heads or whole copies rewrites them: a head gets the first bytes
|
||||
of the new, zero-padded line (the start of the streamed WEM as the WAD stores it); a whole copy is filled
|
||||
like an in-memory sound.
|
||||
- Design and revert rules: `docs/decisions.md` (4b). The same WEM rules apply (`wem.md`).
|
||||
|
||||
@@ -125,6 +125,15 @@ Conclusion: approach A works end-to-end in-game. Use >= 30 s probes for the intr
|
||||
catalog had the same `<Bank>_<n>` name as an SFX WAD sound of the same bank (`CHR_RasBossFight_1`,
|
||||
`SEQ_DemonTrials_1`); they now take `<Bank>_<MEDIA>`, and a line's own name wins over another recording's
|
||||
position alias (`CHR_RadioScanner#1` in FRA). Every name of every catalog resolves to its own sound.
|
||||
24. (2026-10-10, gameplay test by the user) **Padded in-memory replacements play as silence.** All 568 Batman
|
||||
footsteps were replaced; in game the original steps were heard and the replaced cape-open sound was silent.
|
||||
Memory scan while walking: no original footstep WEM loaded, 137 replacements loaded; debugger: the
|
||||
replacements' Vorbis setups are created and parsed OK (ret 0). Every untouched in-memory WEM fills its slot
|
||||
exactly (RIFF size = DIDX size, 500 / 500 sampled), while the replacements were zero-padded. Filled with a
|
||||
`LIST{adtl, labl}` chunk before `data` (55 in-memory originals carry cue+LIST chunks), every step plays the
|
||||
replacement. ackit now fills every in-memory copy and every whole bank copy of a WAD recording
|
||||
(`wem.fill`); WAD entries and prefetch heads keep zero padding (streamed playback is unaffected). The
|
||||
remaining sound after each step is Batman's gear and cape foley (`CHR_BatmanFoleyBelt`, `..Cape`).
|
||||
|
||||
## Old path -> new path
|
||||
`scripts/harness.py` -> `ackit verify --ingame` (ackit/verify/ingame.py); `scripts/validate_builder.py` ->
|
||||
|
||||
Reference in New Issue
Block a user