Divine Skins
Divine Skins Wiki

Advanced - Reading and editing events.bnk

Advanced guide on how to interpret wwise banks

Required Tool

wwiser

Key to reading events.bnk.


The Two Approaches at a Glance

Wwise ReplicationBinary Patching
How it worksRebuild the target hierarchy in Wwise, export a new bankEdit raw byte values in the .bnk file directly
SpeedSlower to set upFast once you know the offsets
ControlHigh: full Wwise toolset availableLimited to the values you're patching
Game update stabilityStable: your project adaptsFragile: offsets shift when the bank is rebuilt by Riot
Best forComplex, long-term mods; anything that needs to be maintainedQuick tweaks; one-off adjustments; prototyping

Use whichever fits your goal. They're not mutually exclusive. You might use an edited events.bnk for some events while replicating other events in Wwise.


Step 1: Generate a Hierarchy Dump

First, dump the hierarchy of the events bank you want to work with:

wwiser.py -d txt {input_events_bnk_file} -dn output.txt

This produces a human-readable text file showing the full Wwise object hierarchy inside that bank: events, containers, switches, sequences, busses, everything.

wwiser hierarchy dump output

This dump is your map. Ctrl+F is your friend.


Technique 1: Replicating in Wwise

Once you have the dump, you can read the hierarchy and reconstruct the relevant parts of it in your own Wwise project.

Working with IDs:

Many elements in the hierarchy use Wwise's FNV-based name hash as their ID. You can convert any event name to its numeric ID using this function:

def wwise_hash(name: str) -> int:
    FNV32_OFFSET = 0x811C9DC5
    FNV32_PRIME  = 0x01000193
    h = FNV32_OFFSET
    for b in name.lower().encode("utf-8"):
        h = (h * FNV32_PRIME) & 0xFFFFFFFF
        h ^= b
    return h

Example:

Play_vo_Yone_Attack2DGeneral  →  681920763

For hierarchy elements that don't use name-based IDs (random containers, sequence containers, etc.), you'll need to pull the raw ID values from the wwiser dump and manually set them in your .wwu project files. This is the fiddly part, but it's what allows your bank to properly override elements from the original.

Once your project is set up, export your custom bank from Wwise and slot it into your mod using the loading order technique from the Intermediate section.

Stability note: Because you own the Wwise project, when Riot updates the game bank, you can re-dump the new version, compare the hierarchy, adjust your project, and re-export. It's extra work per update, but nothing starts from zero.


Technique 2: Binary Patching

Instead of rebuilding anything, binary patching lets you reach directly into the .bnk file and change specific values at known byte offsets.

The wwiser dump includes byte offsets for every value in the file. Find the value you want to change, note its offset and data type, and write a patch.

Here's a ready-to-use patching script:

import struct

FILE_PATH = "mus_map11_seasonal_25s2_bloom_events.bnk"

patches = [
    {"offset": 0x000028ee, "type": "d64", "value": 1}
]

def patch_file(path):
    with open(path, "rb") as f:
        data = bytearray(f.read())

    for patch in patches:
        offset = patch["offset"]
        if patch["type"] == "u8":
            data[offset] = patch["value"]
        elif patch["type"] == "u16":
            data[offset:offset+2] = struct.pack("<H", patch["value"])
        elif patch["type"] == "float":
            data[offset:offset+4] = struct.pack("<f", patch["value"])
        elif patch["type"] == "u32":
            data[offset:offset+4] = struct.pack("<I", patch["value"])
        elif patch["type"] == "d64":
            data[offset:offset+8] = struct.pack("<d", patch["value"])
        elif patch["type"] == "set_bit":
            current = data[offset]
            current |= (1 << patch["bit"])
            data[offset] = current

    output_path = path.replace(".bnk", "_patched.bnk")
    with open(output_path, "wb") as f:
        f.write(data)

    print(f"Patched file saved as: {output_path}")

if __name__ == "__main__":
    patch_file(FILE_PATH)

Supported patch types:

TypeSizeUse case
u81 byteFlags, small integers
u162 bytesShort integers
u324 bytesMost integer values
float4 bytesVolume, pitch, probability
d648 bytesDouble-precision floats
set_bit1 byteToggle a single bit flag

To add more patches, just add entries to the patches list:

patches = [
    {"offset": 0x000028ee, "type": "d64",   "value": 1},
    {"offset": 0x00001a30, "type": "float",  "value": 0.75},
    {"offset": 0x00000c12, "type": "set_bit", "bit": 3},
]

Fragility note: When Riot rebuilds the events bank (patches, new seasons, content updates), every byte offset shifts. Your patch list becomes invalid and needs to be rebuilt from a fresh wwiser dump of the updated bank. Fast to write initially, but requires maintenance after game updates.


Choosing Between the Two

  • Building a music or VO mod you want to maintain long-term? Set up the Wwise project. The upfront cost pays off.
  • Making a quick experimental tweak, or testing whether a value change has the effect you expect? Binary patch it first, confirm it works, then decide if it's worth formalizing.
  • Working on something event-driven and complex (randomized containers, switch containers, state machines)? You'll likely need the Wwise project. Binary patching becomes unwieldy fast.