Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Latest research roundsNeueste Forschungsrunden

Adding a New Wwise Sound Event (TNC)

Partly solvedTeilweise gelöst Original page in the GitHub wikiOriginalseite im GitHub-Wiki

In plain wordsIn einfachen Worten

Building on sound replacement, this report asks how to add a completely new sound event in Wolfenstein II, for example a new gunshot sound. A sound event's internal number is a hash of its lowercased name, and weapons name their shot sound in a plain text field. A full new chain (event, play action, sound, audio data) was added to a copy of a real sound bank without touching anything existing and was found and decoded correctly. The game was never launched, so it is unknown whether the new sound is actually heard.

  • An event's ID is the FNV-1 hash of its lowercased name; this matches real weapon sound names.
  • 47 projectile definitions have a fireSoundEvent field that a mod can point at a new event.
  • In 91.5% of 38,236 retail cases a play action points straight at a sound, so the simple chain is representative.
  • The new sound was routed to the verified 'Weapons' mix bus; all existing sounds in the bank still decoded.
  • Not tested in game; the real pistol sound uses an extra switch layer that was not decoded.

Aufbauend auf dem Sound-Ersetzen fragt dieser Bericht, wie man in Wolfenstein II ein ganz neues Sound-Event hinzufügt, etwa einen neuen Schussklang. Die interne Nummer eines Sound-Events ist ein Hash seines kleingeschriebenen Namens, und Waffen nennen ihren Schussklang in einem einfachen Textfeld. Eine komplette neue Kette (Event, Abspiel-Aktion, Sound, Audiodaten) wurde in eine Kopie einer echten Soundbank eingefügt, ohne Vorhandenes anzufassen, und wurde korrekt gefunden und dekodiert. Das Spiel wurde nie gestartet, ob der neue Sound wirklich zu hören ist, ist also unbekannt.

  • Die ID eines Events ist der FNV-1-Hash des kleingeschriebenen Namens; das passt zu echten Waffen-Soundnamen.
  • 47 Projektil-Definitionen haben ein fireSoundEvent-Feld, das ein Mod auf ein neues Event umleiten kann.
  • In 91,5 % von 38.236 Originalfällen zeigt eine Abspiel-Aktion direkt auf einen Sound, die einfache Kette ist also typisch.
  • Der neue Sound wurde auf den bestätigten Mischkanal „Weapons“ gelegt; alle vorhandenen Sounds der Bank ließen sich weiter dekodieren.
  • Nicht im Spiel getestet; der echte Pistolensound nutzt eine zusätzliche Umschalt-Ebene, die nicht entschlüsselt wurde.

Technical detailsTechnische Details

Der technische Teil ist auf Englisch – so, wie er im Wiki steht. Unterstrichene Begriffe haben eine Erklärung auf Deutsch.

Builds on the project's existing proof that a Wwise sound replacement works, and asks the next question: how to ADD a genuinely new event (not overwrite an existing one), and how weapon/kiscule decls name the event they trigger. Found and structurally proven: a sound event's engine-side id is a hash of its lowercased name, the same mechanism already known from cutscene videos turns out to be identical for weapon fire events, and a complete new Event→Action→Sound chain (plus new audio data) can be appended to a copy of a real sound bank without touching any existing object, then be found again, correctly routed, and correctly decoded by the project's own unmodified readers. Everything here is a structural round trip on file copies — the game was never launched, so whether a posted event actually plays audible sound in-game is not yet known.

The naming mechanism

An event's HIRC object id is FNV-1 (not FNV-1a) of the lowercased name: digest = 0x811C9DC5; for each byte: digest = (digest * 0x01000193) XOR byte. Already known for play_<video> cutscene names; this pass confirms the same formula resolves real weapon event names pulled live from decls:

play_p61_pl_single          -> real Event object   (projectile/player/pistol's fireSoundEvent)
play_mp61_pl_fire_single     -> real Event object   (projectile/player/smg)
play_this_event_does_not_exist_zz9z -> not found    (negative control)

Case does not matter — fnv1("Play_x") == fnv1("play_x") — which matters because decls spell event names with mixed case. 47 projectile decls carry a fireSoundEvent field; this is the field a mod edits to point a weapon at a different, or a brand-new, event — a plain text-decl edit, the same already-proven write path as any other decl value edit.

HIRC object formats (confirmed by building, not just reading)

  • Event (type 4): u8 count, u32[count] action_ids.
  • Action (type 3), Play (0x0403): u16 type, u32 target, u8 isBus, <props block>, then undecoded trailing bytes whose exact meaning is not settled — handled safely by cloning a real Action's bytes and patching only the known fields, never composing new ones.
  • Sound (type 2): u32 plugin, u8 stream_type, u32 source (media id), u32 size (must equal the DIDX row's size), u8 bits, then NodeBaseParams at offset 14 including a u32 bus field — same clone-and-patch-3-fields discipline.

A Play action's target points at a Sound object directly in 91.5% of all 38,236 surveyed retail cases (the rest go through Random/Switch/Blend containers or music objects) — so building a new event as the simple Event → Action(Play) → Sound chain, skipping any container, is the representative case, not a toy simplification.

Bus routing

81 real Audio Bus (type 8) objects exist corpus-wide. Hashing plausible Wwise bus names and checking against the real id set confirms 6 real buses by direct id match, not by reading strings: Master Audio Bus (the root, parent id 0), Music, Ambience, Explosions, VO, and Weapons (0x577F56AC) — the bus this round's new Sound object was routed to, independently verified rather than guessed. TNC's entire mix-bus graph lives inside init.bnk's HIRC data; it has no soundmixing-decl equivalent (that mechanism is TNO-only).

The round trip (structural, PROVEN)

Three new HIRC objects + one new media slot were appended to a copy of a real bank, with zero existing objects touched, then re-read with the project's own unmodified readers:

  • Sound cloned from a real object, 3 fields patched (source, size, bus).
  • Action cloned from a real Play action, 1 field patched (target).
  • Event built fresh, id = FNV1(new_name), verified absent from the 85,719-id retail+DLC corpus beforehand.
  • A synthetic 16-bit PCM tone appended as new media, indexed by a new DIDX row; HIRC's object count bumped by 3.

Result: the new event resolves by its hashed name, walks to the right action, to the right Sound, to the right byte-exact media, on the correct bus; every pre-existing sound in the same bank still decodes (regression control); a 1-character-different name does not collide; and — the sharpest negative control — leaving the HIRC object-count field un-bumped makes the new Event genuinely invisible to the same parser that otherwise finds it, confirming the count field is load-bearing.

Honest limitation: the real pistol event is structurally deeper than what this build uses — it goes through a type-6 Switch Container (for soundStateSwitchName variant selection), which was not decoded this round. Not needed for the deliverable, since a direct-to-Sound chain is the dominant retail shape anyway, but it means the switch-based variant mechanism itself remains unexplored.

Recipe to ship a new event in a mod

  1. Find the bank certainly loaded when the trigger posts the event — for a weapon, resolve its existing fireSoundEvent name and read off which bank it lives in (the pistol's is sb_pl_p61.bnk, overridden by a patch-level pack — same name-precedence rule this project already documents for every other asset type).
  2. Clone a real Sound and a real Action object from that same bank, patch only source/size/bus and target.
  3. Append the media, add its DIDX row, append the 3 new HIRC objects, bump the object count — all through the project's existing archive write/finalize path.
  4. Point a weapon at it by editing the projectile decl's fireSoundEvent text field.
  5. Re-verify offline exactly as this round did before touching the real game.

INFERRED (not runtime-proven)

  • Whether a newly-added event is resolvable only from the specific bank it was added to, or from any bank currently loaded in memory — the safe assumption is "same bank as the weapon's own real events," never verified against a running game.
  • That the undecoded trailing bytes on Action/Sound objects are safely ignorable when cloned verbatim from a real object in the same bank.
  • That posting a custom event through a real decl edit produces audible sound in game, as opposed to silence or a validation rejection.

In-game test (not yet run)

Patch two things together: the target bank gets the new Event/Action/Sound/media quartet (use a distinctive 2–3 second tone, not silence, so a "no audible difference" result is unambiguous), and the pistol's fireSoundEvent is redirected to the new event name. Fire the (non-silenced) pistol once. Three distinguishable outcomes: new tone plays (mechanism works end to end); normal pistol shot plays (event not found — the bank-scope assumption above is wrong, or something in the construction is off); no sound or a crash (the undecoded trailing bytes or the Switch Container layer are not safely ignorable after all). Revert and re-fire to confirm the change was scoped to only this bank/decl.

Related: Audio, Streamed Audio Containers, TNC Modding Surface.