TSLPatcher SSFList Syntax - OpenKotOR/PyKotor GitHub Wiki
TSLPatcher SSFList Syntax Documentation
This guide explains how to modify SSF files using TSLPatcher syntax. For the complete SSF file format specification, see SSF File Format. For general TSLPatcher information, see TSLPatcher's Official Readme. For HoloPatcher-specific information, see HoloPatcher README for Mod Developers.
Overview
The [SSFList] section in TSLPatcher's changes.ini file enables you to modify SSF (sound set files) files that define sound string references for characters. SSF files contain 28 predefined sound slots that map to specific character audio cues such as battle cries, select sounds, attack grunts, pain reactions, and various action-based sound effects.
Table of Contents
- Basic Structure
- File-Level Configuration
- Sound Entry Modifiers
- Memory Token System
- Available Sound Entries
- Examples
Basic structure
[SSFList]
!DefaultDestination=Override
!DefaultSourceFolder=. ; Note: `.` refers to the tslpatchdata folder (where changes.ini is located)
File0=example.ssf
Replace0=different_soundset.ssf
[example.ssf]
!Destination=Override
!SourceFolder=.
!SourceFile=source.ssf
!ReplaceFile=0
; Modify sound entries
Battlecry 1=12345
Battlecry 2=2DAMEMORY5
Attack 1=StrRef10
Death=100
The [SSFList] section declares SSF files to patch. Each entry references another section with the same name as the filename.
file-Level Configuration
Top-Level Keys in [SSFList]
| Key | Type | Default | Description |
|---|---|---|---|
!DefaultDestination |
string | Override |
Default destination for all SSF files in this section |
!DefaultSourceFolder |
string | . |
Default source folder for SSF files. This is a relative path from mod_path, which is typically the tslpatchdata folder (the parent directory of the changes.ini file). The default value . refers to the tslpatchdata folder itself. Path resolution: mod_path / !DefaultSourceFolder / filename |
file Section Configuration
Each SSF file requires its own section (e.g., [example.ssf]).
| Key | Type | Default | Description |
|---|---|---|---|
!Destination |
string | Inherited from !DefaultDestination |
Where to save the modified file (Override or path\to\file.mod) |
!SourceFolder |
string | Inherited from !DefaultSourceFolder |
Source folder for the SSF file. Relative path from mod_path (typically the tslpatchdata folder). When ., refers to the tslpatchdata folder itself. |
!SourceFile |
string | Same as section name | Alternative source filename |
!ReplaceFile |
0/1 | 0 | Overwrite existing file before modifications |
Destination values:
Overrideor empty: Save to the Override folderModules\module.mod: Insert into a MOD, ERF, or RIM container- Use backslashes for path separators
Replace file Behavior:
- If
FileN=filename.ssfis used: The SSF file will be checked if it exists in the target destination. If it exists, it will be modified in place. If it doesn't exist, a copy from tslpatchdata will be made and modified. - If
ReplaceN=filename.ssfis used: The SSF file will be copied from tslpatchdata to the target destination (overwriting any existing file with the same name) and then modified. - If
!ReplaceFile=1is set in the file section: Same behavior asReplaceN, taking priority over theFileNvsReplaceNsyntax.
Sound Entry Modifiers
Each line in the SSF file section defines a sound entry to modify. The format is:
[SoundName]=Value
Where:
SoundNameis one of the 28 predefined sound entry names (see Available Sound Entries)Valuecan be:
value Syntax Examples
[example.ssf]
; Set sound to a constant stringref number
Battlecry 1=12345
; Set sound to a TLK memory token
Selected 1=StrRef5
; Set sound to a 2DA memory token
Attack 1=2DAMEMORY10
; Negative values are supported (represents "no sound")
Pain 1=-1
Important Notes:
- Sound entry names are case-insensitive
- Sound entry names must match exactly (including spaces) from the allowed list
- Negative values (including -1) are valid and typically represent "no sound" or unused sound slots
- Stringref values must be numeric strings
Memory Token System
SSFList supports memory tokens for dynamic stringref assignment from:
StrRef Tokens
[StrRef](Audio-and-Localization-Formats#string-references-strref)# tokens reference TLK (Talk) memory tokens that have been set elsewhere in changes.ini (typically from TLKList entries). When the SSF file is patched, the stored StrRef value from memory will be used.
[example.ssf]
; Use StrRef5 from TLK memory
Battlecry 1=StrRef5
; Use StrRef12 from TLK memory
Attack 1=StrRef12
Syntax: StrRef followed immediately by a numeric token ID
When to use: When you want the sound entry to reference a dialog entry you've added to the game's TLK file.
2DAMEMORY Tokens
2DAMEMORY# tokens reference 2DA memory tokens that have been set elsewhere in changes.ini (typically from 2DAList entries). The stored value is converted to an integer stringref.
[example.ssf]
; Use 2DAMEMORY5 from 2DA memory
Selected 1=2DAMEMORY5
; Use 2DAMEMORY10 from 2DA memory
Pain 1=2DAMEMORY10
Syntax: 2DAMEMORY followed immediately by a numeric token ID
When to use: When you want the sound entry to reference a value stored in 2DA memory (typically row indices or other numeric identifiers).
Token Resolution
Both token types are resolved during patch application:
- TLK Memory (
[StrRef](Audio-and-Localization-Formats#string-references-strref)#): Returns the stored integer stringref value - 2DA Memory (
2DAMEMORY#): Returns the stored integer value (converted from string) - Constant values: Used directly as integer stringref values
If a token references an uninitialized memory slot, the behavior is undefined and may cause errors.
Available Sound Entries
SSF files contain exactly 28 sound slots. The following table lists all available sound entry names:
| Sound Entry Name | ID | Enum value | Description |
|---|---|---|---|
Battlecry 1 |
0 | BATTLE_CRY_1 | First battle cry sound |
Battlecry 2 |
1 | BATTLE_CRY_2 | Second battle cry sound |
Battlecry 3 |
2 | BATTLE_CRY_3 | Third battle cry sound |
Battlecry 4 |
3 | BATTLE_CRY_4 | Fourth battle cry sound |
Battlecry 5 |
4 | BATTLE_CRY_5 | Fifth battle cry sound |
Battlecry 6 |
5 | BATTLE_CRY_6 | Sixth battle cry sound |
Selected 1 |
6 | SELECT_1 | First selection sound |
Selected 2 |
7 | SELECT_2 | Second selection sound |
Selected 3 |
8 | SELECT_3 | Third selection sound |
Attack 1 |
9 | ATTACK_GRUNT_1 | First attack grunt sound |
Attack 2 |
10 | ATTACK_GRUNT_2 | Second attack grunt sound |
Attack 3 |
11 | ATTACK_GRUNT_3 | Third attack grunt sound |
Pain 1 |
12 | PAIN_GRUNT_1 | First pain grunt sound |
Pain 2 |
13 | PAIN_GRUNT_2 | Second pain grunt sound |
Low health |
14 | LOW_HEALTH | Low health warning sound |
Death |
15 | DEAD | Death sound effect |
Critical hit |
16 | CRITICAL_HIT | Critical hit sound |
Target immune |
17 | TARGET_IMMUNE | Target immune to attack sound |
Place mine |
18 | LAY_MINE | Mine placement sound |
Disarm mine |
19 | DISARM_MINE | Mine disarming sound |
Stealth on |
20 | BEGIN_STEALTH | Stealth activation sound |
Search |
21 | BEGIN_SEARCH | Search action sound |
Pick lock start |
22 | BEGIN_UNLOCK | Lockpicking start sound |
Pick lock fail |
23 | UNLOCK_FAILED | Lockpicking failure sound |
Pick lock done |
24 | UNLOCK_SUCCESS | Lockpicking success sound |
Leave party |
25 | SEPARATED_FROM_PARTY | Party separation sound |
Rejoin party |
26 | REJOINED_PARTY | Party rejoin sound |
Poisoned |
27 | POISONED | Poison effect sound |
Note: All sound entry names are case-insensitive when used in changes.ini. The spaces in entry names must match exactly (e.g., "Battlecry 1" not "Battlecry1").
Examples
Example 1: Basic Sound Set Modification
This example modifies an existing sound set by changing a few sound entries to constant values:
[SSFList]
File0=custom_voice.ssf
[custom_voice.ssf]
Battlecry 1=50001
Battlecry 2=50002
Selected 1=50003
Attack 1=50004
Death=50005
Example 2: Using TLK Memory Tokens
This example uses StrRef tokens to dynamically reference dialog entries added to the TLK file:
[TLKList]
StrRef1000=Hello, warrior!
StrRef1001=For the Republic!
StrRef1002=AAAGH!
[SSFList]
File0=jedi_soundset.ssf
[jedi_soundset.ssf]
Selected 1=StrRef1000
Battlecry 1=StrRef1001
Pain 1=StrRef1002
Example 3: Using 2DA Memory Tokens
This example uses 2DAMEMORY tokens to reference row indices from a custom appearance.2da table:
[2DAList]
2DAMEMORY100=appearance_sounds.2da
2DAMEMORY101=sound_battlecry_row
2DAMEMORY102=sound_selected_row
[appearance_sounds.2da]
; Column is soundset_strref
row_label|soundset_strref
JediVoice|50000
SithVoice|50100
[sound_battlecry_row]
; References the appearance_sounds.2da row index
RowIndex=2DAMEMORY100
[sound_selected_row]
RowIndex=2DAMEMORY101
[SSFList]
File0=dynamic_voice.ssf
[dynamic_voice.ssf]
Battlecry 1=2DAMEMORY102
Selected 1=2DAMEMORY103
Note: The 2DAMEMORY values in this example would need to extract the actual stringref values from the 2DA cells, which requires additional configuration in the 2DA section.
Example 4: Replace file Behavior
This example demonstrates the difference between FileN and ReplaceN syntax:
[SSFList]
!DefaultDestination=Override
File0=modify_existing.ssf
Replace0=always_fresh.ssf
[modify_existing.ssf]
; This will be loaded from Override if it exists,
; otherwise copied from tslpatchdata and then modified
Battlecry 1=60001
Selected 1=60002
[always_fresh.ssf]
; This will always copy fresh from tslpatchdata,
; overwriting any existing file, then be modified
Battlecry 1=70001
Selected 1=70002
Example 5: Complex Multi-file Sound Set
This example patches multiple sound sets with different configurations:
[SSFList]
!DefaultDestination=Override
!DefaultSourceFolder=voices
File0=jedi_male.ssf
File1=jedi_female.ssf
Replace0=sith_dark.ssf
[jedi_male.ssf]
!SourceFolder=voices/jedi
Battlecry 1=40001
Battlecry 2=40002
Selected 1=40003
Attack 1=40004
Death=40005
Critical hit=40006
[jedi_female.ssf]
!SourceFolder=voices/jedi
Battlecry 1=40051
Battlecry 2=40052
Selected 1=40053
Attack 1=40054
Death=40055
Critical hit=40056
[sith_dark.ssf]
!Destination=Override
!ReplaceFile=1
Battlecry 1=40101
Battlecry 2=40102
Selected 1=40103
Attack 1=40104
Death=40105
Critical hit=40106
Target immune=40107
Example 6: Comprehensive Sound Set with All Entries
This example demonstrates patching all 28 sound entries:
[SSFList]
File0=comprehensive_voice.ssf
[comprehensive_voice.ssf]
Battlecry 1=50001
Battlecry 2=50002
Battlecry 3=50003
Battlecry 4=50004
Battlecry 5=50005
Battlecry 6=50006
Selected 1=50010
Selected 2=50011
Selected 3=50012
Attack 1=50020
Attack 2=50021
Attack 3=50022
Pain 1=50030
Pain 2=50031
Low health=50040
Death=50050
Critical hit=50060
Target immune=50070
Place mine=50080
Disarm mine=50081
Stealth on=50090
Search=50091
Pick lock start=50100
Pick lock fail=50101
Pick lock done=50102
Leave party=50110
Rejoin party=50111
Poisoned=50120
Example 7: Modifying SSF files in Containers
This example shows how to patch SSF files that are stored in MOD, ERF, or RIM container files:
[SSFList]
File0=npc_voice.ssf
[npc_voice.ssf]
!Destination=Modules\my_module.mod
Battlecry 1=60001
Selected 1=60002
Attack 1=60003
Death=60004
Important: When patching files in containers, ensure that:
- The container file exists in the game directory
- The SSF file already exists in that container (or use ReplaceN to force overwrite)
- Use backslashes for path separators in
!Destination
Example 8: Clearing Sound Entries
This example demonstrates setting sound entries to -1 (no sound):
[SSFList]
File0=quiet_character.ssf
[quiet_character.ssf]
; Remove all battle cries
Battlecry 1=-1
Battlecry 2=-1
Battlecry 3=-1
Battlecry 4=-1
Battlecry 5=-1
Battlecry 6=-1
; Keep only selection sounds
Selected 1=12345
Selected 2=12346
Selected 3=12347
Note: The value -1 (or any negative value) typically represents "no sound" in the game engine. Setting all entries to -1 would create a silent character soundset.
Advanced Usage
Combining Multiple Token types
You can mix different value types within the same SSF file section:
[SSFList]
File0=mixed_tokens.ssf
[mixed_tokens.ssf]
Battlecry 1=12345
Battlecry 2=StrRef100
Battlecry 3=2DAMEMORY50
Battlecry 4=-1
Selected 1=StrRef101
Attack 1=54321
Pain 1=2DAMEMORY51
Inheritance of Configuration
Configuration options cascade from the [SSFList] section to individual file sections:
[SSFList]
!DefaultDestination=Override
!DefaultSourceFolder=voices
File0=voice1.ssf
File1=voice2.ssf
[voice1.ssf]
; Inherits !DefaultDestination=Override
; Inherits !DefaultSourceFolder=voices
Battlecry 1=10001
[voice2.ssf]
; Inherits !DefaultDestination=Override
; Inherits !DefaultSourceFolder=voices
!SourceFolder=voices/alternate
Battlecry 1=20001
Compatibility Notes
- SSF files are binary format files with version "V1.1"
- All stringref values are stored as 32-bit unsigned integers
- The game engine interprets negative values (-1) as "no sound"
- SSF files have a fixed structure with exactly 28 sound slots
- Empty or unset sound slots default to -1 when a new SSF is created
- PyKotor/TSLPatcher loads existing SSF files from the override folder or specified container if they exist
Troubleshooting
Common Issues
Problem: Sound entries not applying
- Solution: Verify that the sound entry name matches exactly (including spaces) from the Available Sound Entries table
- Solution: Ensure the SSF file exists in the specified source location (tslpatchdata folder)
Problem: Token values not resolving
- Solution: Ensure TLK memory tokens (StrRef#) are set before SSFList section runs
- Solution: Ensure 2DA memory tokens (2DAMEMORY#) are set before SSFList section runs
- Solution: Verify token IDs match between where they're set and where they're used
Problem: file not found in destination
- Solution: When using FileN syntax, the file must either exist in destination or in tslpatchdata
- Solution: Use ReplaceN syntax to always copy from tslpatchdata
- Solution: Set
!ReplaceFile=1in the file section to force replacement
Problem: Incorrect file being loaded
- Solution: Check
!SourceFolderpath is correct relative to tslpatchdata root - Solution: Verify
!SourceFilematches the actual filename if different from section name - Solution: Ensure !DefaultSourceFolder is set correctly at SSFList level
Problem: Container insertion failing
- Solution: Verify the container file exists in the game directory
- Solution: Use backslashes (not forward slashes) in !Destination paths
- Solution: Ensure the SSF file already exists in the container if not using ReplaceN syntax
Installation Order
TSLPatcher processes sections in a specific order. SSFList is processed after:
- TLK Appending (TLKList)
- Install List
- 2DA changes (2DAList)
- GFF Changes (GFFList)
- Script compilation
This means that memory tokens set in TLKList and 2DAList will be available when SSFList runs. SSFList runs before final file installation operations.
Reference
Implementation Details
Binary format:
- header: "SSF " (4 bytes)
- Version: "V1.1" (4 bytes)
- offset to sound data: 4 bytes (UInt32)
- Sound entries: 28 x 4 bytes (UInt32 each) = 112 bytes
- Padding: 12 x 4 bytes (0xFFFFFFFF) = 48 bytes
- Total size: ~160 bytes
Default values:
- All sound entries default to -1 in a new SSF file
- Replacement behavior defaults to modifying existing files when possible
- Destination defaults to Override folder
- Source folder defaults to tslpatchdata root (
.)
Supported Operations:
-
Read existing SSF files from Override or containers
-
Write modified SSF files to Override or containers
-
Modify any of the 28 sound entry stringrefs
-
Memory token resolution for:
-
Replace file or modify existing file modes
See also
- Audio-and-Localization-Formats#ssf -- Sound set structure
- TSLPatcher TLKList Syntax -- StrRef tokens
- TSLPatcher 2DAList Syntax -- Token usage
- TSLPatcher's Official Readme
- HoloPatcher README for Mod Developers
- Community sources and archives -- DeadlyStream, LucasForums for soundset modding
Last Updated: Based on PyKotor implementation and TSLPatcher v1.2.10b compatibility