Skip to main content
The BNKFile class handles Wwise soundbank (.bnk) files, which are containers for multiple WEM audio files. BNK files are embedded within PCK archives and can be modified to replace individual WEMs.

BNKFile

Constructor

from src.bnk_handler import BNKFile

# Load from file
bnk = BNKFile(bnk_path="soundbank.bnk")

# Or load from bytes
bnk = BNKFile(bnk_bytes=bnk_data)
bnk_path
str | Path
default:"None"
Path to BNK file to load.
bnk_bytes
bytes
default:"None"
BNK file data as bytes (useful when extracting from PCK).
Note: Provide either bnk_path or bnk_bytes, not both.

Methods

list_wems

Returns list of WEM IDs contained in the BNK.
wem_ids = bnk.list_wems()
wem_ids
list[int]
List of WEM file IDs in the soundbank.
Returns: Empty list if BNK has no DATA section.

extract_wem

Extracts a single WEM file from the BNK.
wem_data = bnk.extract_wem(wem_id, output_path=None)
wem_id
int
required
WEM ID to extract.
output_path
str | Path
default:"None"
Optional path to save the WEM file. If None, returns bytes without saving.
wem_data
bytes
The WEM file data.
Raises:
  • ValueError: If BNK has no DATA section
  • KeyError: If WEM ID not found in BNK

replace_wem

Replaces a WEM file inside the BNK.
bnk.replace_wem(wem_id, wem_path=None, wem_bytes=None)
wem_id
int
required
WEM ID to replace.
wem_path
str | Path
default:"None"
Path to replacement WEM file.
wem_bytes
bytes
default:"None"
Replacement WEM data as bytes.
Raises:
  • ValueError: If BNK has no DATA section or neither wem_path nor wem_bytes provided
  • KeyError: If WEM ID not found in BNK
Behavior:
  • Updates WEM data in memory
  • Automatically corrects DIDX offsets and sizes
  • Prints confirmation with new file size
  • Call save() or get_bytes() to persist changes

save

Saves the modified BNK to a file.
bnk.save(output_path)
output_path
str | Path
required
Where to save the modified BNK file.

get_bytes

Returns the entire BNK as bytes (useful for embedding back into PCK).
bnk_bytes = bnk.get_bytes()
bnk_bytes
bytes
Complete BNK file as byte string.

BNK File Structure

BNK files contain several chunks:

BKHD (Bank Header)

  • Contains soundbank metadata
  • Always the first chunk

DIDX (Data Index)

  • Index of WEM files in the DATA section
  • Maps WEM ID → (offset, size)

DATA (Audio Data)

  • Contains raw WEM file data
  • Files are 16-byte aligned

HIRC (Hierarchy)

  • Optional chunk containing sound hierarchy
  • Preserved when modifying WEMs

Helper Classes

WEM

Represents a single WEM file.
wem = WEM(data)
wem_data = wem.getdata()
wem_size = len(wem)

DIDX

Manages the WEM index.
didx.wem_offsets[wem_id]  # Get offset
didx.wem_sizes[wem_id]     # Get size

DATA

Manages WEM data storage.
data.wem_data[wem_id]  # Get WEM object

Functions

extract_bnk_wems

Convenience function to extract all WEMs from a BNK.
from src.bnk_handler import extract_bnk_wems

extract_bnk_wems(bnk_path, output_dir)
bnk_path
str | Path
required
Path to BNK file.
output_dir
str | Path
required
Directory to extract WEMs to.
Behavior:
  • Extracts all WEMs as {wem_id}.wem
  • Prints progress for each file

Example Usage

Extract WEMs from BNK

from src.bnk_handler import BNKFile
from pathlib import Path

# Load BNK
bnk = BNKFile(bnk_path="2471637648.bnk")

# List contents
wem_ids = bnk.list_wems()
print(f"Found {len(wem_ids)} WEM files")
print(f"WEM IDs: {wem_ids}")

# Extract specific WEM
output_dir = Path("extracted_wems")
output_dir.mkdir(exist_ok=True)

for wem_id in wem_ids:
    output_file = output_dir / f"{wem_id}.wem"
    bnk.extract_wem(wem_id, output_file)
    print(f"Extracted: {wem_id}.wem")

Modify BNK Soundbank

from src.bnk_handler import BNKFile

# Load original BNK
bnk = BNKFile(bnk_path="original.bnk")

# Replace multiple WEMs
replacements = {
    100000: "replacement_1.wem",
    100001: "replacement_2.wem",
    100002: "replacement_3.wem"
}

for wem_id, wem_file in replacements.items():
    try:
        bnk.replace_wem(wem_id, wem_path=wem_file)
        print(f"✓ Replaced WEM {wem_id}")
    except KeyError:
        print(f"✗ WEM {wem_id} not found in BNK")

# Save modified BNK
bnk.save("modified.bnk")
print("Modified BNK saved!")

Use with PCK Files

from src.pck_extractor import PCKExtractor
from src.bnk_handler import BNKFile
from io import BytesIO

# Extract BNK from PCK
extractor = PCKExtractor("SoundBank_SFX_1.pck")

with open("SoundBank_SFX_1.pck", 'rb') as f:
    extractor.file_handle = f
    extractor.parse_header()
    
    # Find BNK by ID
    bnk_id = 2471637648
    bnk_info = next(
        b for b in extractor.banks_info 
        if b['id'] == bnk_id
    )
    
    # Extract BNK data
    f.seek(bnk_info['offset'])
    bnk_data = f.read(bnk_info['size'])

# Modify BNK
bnk = BNKFile(bnk_bytes=bnk_data)
bnk.replace_wem(100000, wem_path="new_sound.wem")

# Get modified bytes for re-packing
modified_bnk = bnk.get_bytes()

Command-Line Usage

List WEMs in BNK

python -m src.bnk_handler list soundbank.bnk

Extract all WEMs

python -m src.bnk_handler extract soundbank.bnk ./output_dir

Replace WEM in BNK

python -m src.bnk_handler replace soundbank.bnk 100000 new_sound.wem output.bnk

Technical Details

Alignment

WEM files are 16-byte aligned within the DATA section:
def align16(x):
    return (16 - (x % 16)) % 16

Offset Calculation

DIDX stores block-based offsets:
if blocksize != 0:
    actual_offset = offset_block * blocksize
else:
    actual_offset = offset_block

Section Order

BNK chunks must appear in this order:
  1. BKHD (Bank Header)
  2. DIDX (Data Index)
  3. DATA (Audio Data)
  4. HIRC (Hierarchy, optional)

Error Handling

from src.bnk_handler import BNKFile

try:
    bnk = BNKFile(bnk_path="soundbank.bnk")
    bnk.replace_wem(12345, wem_path="new.wem")
    bnk.save("modified.bnk")
except ValueError as e:
    print(f"Invalid BNK file: {e}")
except KeyError as e:
    print(f"WEM not found: {e}")
except FileNotFoundError as e:
    print(f"File not found: {e}")

Notes

  • BNK files must start with BKHD magic bytes
  • WEM IDs are 32-bit unsigned integers
  • Modifying WEMs preserves the HIRC hierarchy
  • Always keep backups of original BNK files
  • Use get_bytes() when re-embedding modified BNKs into PCKs
  • The DIDX and DATA sections are automatically synchronized when replacing WEMs

Build docs developers (and LLMs) love