TSLPatcher InstallList Syntax - NickHugi/PyKotor GitHub Wiki
The [InstallList] section in TSLPatcher's changes.ini file enables you to copy files from your mod's tslpatchdata folder to their proper location in the game installation. This includes installing files to folders (such as Override, Modules, StreamVoice, etc.) or directly into ERF/RIM/MOD archive files. Unlike other patch lists, InstallList is designed for copying files that haven't been modified by other sections.
Important: Do not add any files that have been modified by any of the other sections (GFFList, CompileList, 2DAList, etc.) to the InstallList, or the modified files might be overwritten! The other sections already handle saving files to their proper locations. The only exception to this is ERF files which have had files added to them by those sections. They must still be added to the InstallList to be put in their proper places.
- Basic Structure
- Processing Order
- Folder-Level Configuration
- File-Level Configuration
- File Replacement Behavior
- Installing to Folders
- Installing to Archives
- Renaming Files
- Source Folder Configuration
- Override Type Handling
- Examples
- Special Cases and Edge Cases
- Troubleshooting
The InstallList uses a two-level hierarchical structure:
[InstallList]
Folder0=Override
Folder1=Modules
Folder2=StreamVoice\AVO\_HuttHap
[Override]
File0=my_texture.tpc
File1=my_script.ncs
Replace0=existing_file.tpc
[Modules]
!SourceFolder=modules
File0=new_module.mod
[StreamVoice\AVO\_HuttHap]
File0=sound1.wav
File1=sound2.wav-
[InstallList]section: Contains keys that map to folder destination names. Each key (likeFolder0,Folder1, etc.) should reference a section with the same name as the value (the destination folder). -
Folder sections (e.g.,
[Override],[Modules]): Contain the list of files to install to that folder, along with optional folder-level configuration. -
File sections (optional): Individual files can have their own sections for per-file configuration options.
In HoloPatcher, the InstallList runs first in the patch execution order:
- [InstallList] - Files are installed first
- [TLKList] - TLK modifications
- [2DAList] - 2DA file modifications
- [GFFList] - GFF file modifications
- [CompileList] - Script compilation
- [HACKList] - Binary hacking
- [SSFList] - Sound set modifications
Note: In original TSLPatcher, InstallList executes after TLKList, but HoloPatcher changed this order to allow installing a whole dialog.tlk file before TLK modifications are applied. This priority change should not affect the output of mods.
Each folder section (e.g., [Override]) supports the following configuration keys:
| Key | Type | Default | Description |
|---|---|---|---|
!SourceFolder |
string |
. (tslpatchdata folder) |
Relative path from mod_path (typically the tslpatchdata folder, the parent directory of changes.ini or namespaces.ini) where files should be sourced from. The default value . refers to the tslpatchdata folder itself, not its parent directory. Path resolution: mod_path / !SourceFolder / filename. HoloPatcher extension - allows subfolder organization within tslpatchdata. |
The folder section contains the list of files to install. Each file entry uses one of two syntaxes:
| Key Format | Replace Behavior | Description |
|---|---|---|
File#=filename.ext |
No replacement | Install the file only if it doesn't already exist at the destination. If the file exists, it will be skipped (warning logged). |
Replace#=filename.ext |
Replacement enabled | Install the file and overwrite any existing file at the destination. |
Syntax Notes:
-
#is a sequential number starting from 0 (File0, File1, File2, ..., Replace0, Replace1, etc.) - Numbers can be sequential, but gaps are allowed (File0, File2, File5 is valid)
- Case-insensitive matching is used for the prefix (file, replace, File, Replace all work)
- The filename can include subdirectories if using
!SourceFolder
Examples:
[Override]
File0=texture1.tpc
File1=texture2.tpc
Replace0=existing.tpc
Replace1=another_existing.tpc
File2=subfolder\texture3.tpcEach file can optionally have its own section (e.g., [my_texture.tpc]) for per-file configuration:
| Key | Type | Default | Description |
|---|---|---|---|
!SourceFile |
string | Same as filename in File#/Replace# entry | Alternative source filename to load from tslpatchdata. The file will be installed with the name specified in the File#/Replace# entry (or !SaveAs/!Filename if specified). |
!SaveAs |
string | Same as !SourceFile
|
The final filename to save the file as at the destination. Allows renaming during installation. |
!Filename |
string | Same as !SaveAs
|
Alias for !SaveAs. Both keys are equivalent. |
!Destination |
string | Inherited from folder section name | Override the destination folder for this specific file. Can specify a different folder or archive path. |
!ReplaceFile |
0/1 | Determined by File#/Replace# prefix | Whether to replace existing files. Takes priority over the File#/Replace# prefix syntax. 1 = replace, 0 = don't replace. |
!SourceFolder |
string | Inherited from folder section !SourceFolder
|
Override the source folder for this specific file. Relative path within tslpatchdata. |
!OverrideType |
ignore/warn/rename
|
warn (HoloPatcher) / ignore (TSLPatcher) |
How to handle conflicts when installing to archives. See Override Type Handling section. |
[InstallList]
Folder0=Override
[Override]
File0=source_texture.tpc
File1=renamed_script.ncs
[source_texture.tpc]
!SourceFile=original_name.tpc
!SaveAs=final_texture.tpc
!Destination=textures
[renamed_script.ncs]
!Filename=custom_name.ncs
!ReplaceFile=1InstallList has special behavior regarding file replacement that differs from other patch lists:
InstallList (and CompileList) use skip_if_not_replace=True, which means:
-
If
!ReplaceFile=0(or usingFile#=syntax) and the file already exists at the destination:- The file will be skipped (not installed)
- A note is logged:
'filename.ext' already exists in the 'destination' folder. Skipping file... - No error is raised - this is expected behavior
-
If
!ReplaceFile=1(or usingReplace#=syntax) and the file already exists:- The file will be replaced (overwritten)
- A note is logged:
Copying 'filename.ext' and replacing existing file in the 'destination' folder
-
If the file does not exist:
- The file will be installed normally
- A note is logged:
Copying 'filename.ext' and saving to the 'destination' folder
-
!ReplaceFilekey (if present) takes highest priority -
Replace#=prefix syntax (if!ReplaceFilenot specified) -
File#=prefix syntax (default, no replacement)
Example:
[Override]
Replace0=example.tpc
[example.tpc]
!ReplaceFile=0In this case, even though Replace0= was used, !ReplaceFile=0 takes priority, so the file will NOT replace existing files.
The most common use case is installing files to standard game folders:
[InstallList]
Folder0=Override
Folder1=Modules
Folder2=StreamVoice
Folder3=StreamMusic
Folder4=StreamWaves
[Override]
File0=my_texture.tpc
File1=my_item.uti
[Modules]
File0=custom_module.mod
[StreamVoice]
File0=voice_line.wav
[StreamMusic]
File0=background_music.mp3
[StreamWaves]
File0=sound_effect.wavYou can install files into subdirectories by specifying the relative path with backslashes:
[InstallList]
Folder0=StreamVoice\AVO\_HuttHap
[StreamVoice\AVO\_HuttHap]
File0=conversation1.wav
File1=conversation2.wavImportant Notes:
- Use backslashes (
\) for path separators (TSLPatcher convention) - HoloPatcher/PyKotor will normalize both forward slashes (
/) and backslashes (\) - If the specified folder path does not exist, it will be automatically created
- Folder creation happens recursively (parent folders are created as needed)
To install files directly into the game root folder, use .\ as the folder name:
[InstallList]
Folder0=.\
[.\]
File0=readme.txt
File1=config.iniNote: In logs, .\ is reported as the "Game" folder for clarity.
You can set a default destination for all files in InstallList using !DefaultDestination:
[InstallList]
!DefaultDestination=Override
Folder0=Override
Folder1=Modules
[Override]
File0=file1.tpc
[Modules]
File0=file2.modNote: !DefaultDestination is highly undocumented in TSLPatcher. In PyKotor/HoloPatcher, it is believed to take priority over folder section destinations, except when !Destination is explicitly set in a file section.
InstallList supports installing files directly into ERF/MOD/RIM archive files. This is done by specifying the archive file path (relative to the game folder) as the destination.
[InstallList]
Folder0=Modules\901myn.mod
Folder1=Modules\custom_module.rim
[Modules\901myn.mod]
File0=new_resource.uti
File1=new_texture.tpc
Replace0=existing_resource.uti
[Modules\custom_module.rim]
File0=another_resource.2da-
If the archive does not exist at the specified path:
- An error is logged:
The capsule 'Modules\901myn.mod' did not exist when attempting to copy 'filename.ext'. Skipping file... - The patch is skipped (no error is raised, execution continues)
- An error is logged:
-
If the archive exists:
- The file is added to the archive
- If a resource with the same name already exists in the archive:
- If
!ReplaceFile=1orReplace#=: The existing resource is overwritten - If
!ReplaceFile=0orFile#=: The file is skipped (see File Replacement Behavior)
- If
-
Archive Types Supported:
-
.mod(MOD/ERF format) -
.erf(ERF format) -
.rim(RIM format) -
.sav(Save game ERF format)
-
If you've modified an archive using GFFList or CompileList (e.g., added resources to it), you must include that archive in InstallList to save it to its proper location:
[GFFList]
File0=Modules\901myn.mod
[Modules\901myn.mod]
; ... GFF modifications ...
[InstallList]
Folder0=Modules
[Modules]
Replace0=901myn.mod ; Must include to save the modified archiveYou can rename files during installation using !SaveAs or !Filename:
[InstallList]
Folder0=Override
[Override]
File0=source_name.tpc
[source_name.tpc]
!SourceFile=original_filename.tpc
!SaveAs=final_filename.tpcThis will:
- Load
original_filename.tpcfrom tslpatchdata - Install it as
final_filename.tpcto the Override folder
Notes:
-
!SaveAsand!Filenameare equivalent - use either one - If
!SourceFileis not specified, the filename from the File#/Replace# entry is used as the source - The source file must exist in the tslpatchdata folder (or
!SourceFolderif specified)
You can specify a source folder for all files in a folder section:
[InstallList]
Folder0=Override
[Override]
!SourceFolder=textures
File0=texture1.tpc
File1=texture2.tpcThis will look for files in tslpatchdata\textures\ instead of tslpatchdata\.
You can override the source folder for individual files:
[InstallList]
Folder0=Override
[Override]
!SourceFolder=default_folder
File0=file1.tpc
File1=file2.tpc
[file1.tpc]
!SourceFolder=custom_folderIn this example:
-
file1.tpcis loaded fromtslpatchdata\custom_folder\ -
file2.tpcis loaded fromtslpatchdata\default_folder\
-
!SourceFolderis a HoloPatcher extension - original TSLPatcher may not support this feature - Paths are relative to the
tslpatchdatafolder - Use
.(period) to reference the root tslpatchdata folder explicitly - Supports subdirectory paths:
!SourceFolder=subfolder\deeper\folder - Backslashes and forward slashes are both normalized
When installing files to archives (ERF/MOD/RIM), there's a potential conflict: a file might already exist in the Override folder with the same name. The !OverrideType setting controls how this conflict is handled:
| Value | Behavior | Description |
|---|---|---|
ignore |
No action | Do nothing - don't even check for conflicts. This is the TSLPatcher default. |
warn |
Log warning | Check for conflicts and log a warning if found, but continue with installation. This is the HoloPatcher default. |
rename |
Rename override file | If a conflicting file exists in Override, rename it with an old_ prefix (e.g., old_filename.ext) and log a warning. |
Example:
[Modules\901myn.mod]
File0=resource.uti
!OverrideType=warnWhy This Matters:
The game's resource loading system checks folders in this order:
- Override folder (highest priority)
- Module archives (.mod files)
- RIM files
- Other archives
If a file exists in both Override and an archive, the Override version takes precedence. The !OverrideType setting helps manage this shadowing behavior.
[InstallList]
Folder0=Override
[Override]
File0=my_texture.tpc
File1=my_item.uti
File2=my_script.ncs
Replace0=existing_file.tpc[InstallList]
Folder0=Override
Folder1=StreamVoice\AVO\_HuttHap
Folder2=Modules
[Override]
!SourceFolder=override_files
File0=texture1.tpc
File1=texture2.tpc
[StreamVoice\AVO\_HuttHap]
!SourceFolder=voice_files
File0=conv1.wav
File1=conv2.wav
[Modules]
!SourceFolder=modules
File0=custom.mod[InstallList]
Folder0=Override
[Override]
File0=renamed_texture.tpc
File1=renamed_item.uti
[renamed_texture.tpc]
!SourceFile=original_texture.tpc
!SaveAs=final_texture_name.tpc
[renamed_item.uti]
!SourceFile=source_item.uti
!Filename=custom_item_name.uti[InstallList]
Folder0=Modules\901myn.mod
Folder1=Modules\custom.rim
[Modules\901myn.mod]
File0=new_creature.utc
File1=new_dialog.dlg
Replace0=existing_item.uti
!OverrideType=warn
[Modules\custom.rim]
File0=custom_2da.2da
!ReplaceFile=1[InstallList]
!DefaultDestination=Override
Folder0=Override
Folder1=Modules
Folder2=Modules\901myn.mod
Folder3=StreamVoice\AVO\_HuttHap
[Override]
!SourceFolder=textures
File0=texture1.tpc
File1=texture2.tpc
Replace0=existing_texture.tpc
[texture1.tpc]
!SourceFile=original_name.tpc
!SaveAs=final_texture.tpc
!Destination=textures
!OverrideType=rename
[Modules]
!SourceFolder=modules
Replace0=modified_module.mod
[Modules\901myn.mod]
File0=new_resource.uti
File1=new_texture.tpc
Replace0=modified_resource.utc
!SourceFolder=archive_resources
[new_resource.uti]
!Filename=custom_name.uti
!ReplaceFile=1
[StreamVoice\AVO\_HuttHap]
!SourceFolder=voice
File0=line1.wav
File1=line2.wavAn empty [InstallList] section is valid and will be skipped:
[InstallList]No files will be installed, and a note will be logged: [InstallList] section missing from ini. (if the section doesn't exist) or no error if the section exists but is empty.
If a folder key in [InstallList] references a section that doesn't exist, a KeyError is raised:
[InstallList]
Folder0=NonExistentFolder
; Error: Section [NonExistentFolder] not foundIf a source file specified in a File#/Replace# entry doesn't exist in tslpatchdata (or the specified !SourceFolder), an error is logged:
Could not locate resource to copy: 'missing_file.tpc'
The patcher will continue with the next file.
Folders are automatically created if they don't exist:
[InstallList]
Folder0=NewFolder\SubFolder\DeepFolder
[NewFolder\SubFolder\DeepFolder]
File0=file.tpcAll parent folders (NewFolder, SubFolder, DeepFolder) will be created automatically.
- Archive doesn't exist: Error logged, patch skipped
- Archive exists but is read-only: Permission error logged, patch skipped
- Archive exists, file already in archive: See File Replacement Behavior
- Archive exists, file doesn't exist in archive: File is added normally
- Folder and file keys are case-insensitive:
File0,file0,FILE0all work -
Replace#prefix detection is case-insensitive:Replace0,replace0,REPLACE0all work - File paths on Windows are case-insensitive, but PyKotor uses
CaseAwarePathto preserve case when possible
- TSLPatcher convention: Use backslashes (
\) for Windows paths - PyKotor/HoloPatcher: Normalizes both backslashes (
\) and forward slashes (/) - Archive paths: Use backslashes:
Modules\901myn.mod
If the mod contains nwscript.nss in the tslpatchdata folder and there are scripts to compile ([CompileList]), HoloPatcher will automatically append an InstallFile entry to install nwscript.nss to the Override folder. This is required for some versions of nwnnsscomp.exe that expect nwscript.nss to be in Override rather than tslpatchdata.
This happens during the _prepare_compilelist phase before the main patch loop runs.
Problem: File listed in InstallList but not being installed.
Possible Causes:
-
File already exists and
Replace#=or!ReplaceFile=1not set- Solution: Check logs for "already exists... Skipping file" message
-
Fix: Use
Replace#=or set!ReplaceFile=1
-
Source file doesn't exist in tslpatchdata
- Solution: Check logs for "Could not locate resource" error
-
Fix: Ensure file exists in tslpatchdata (or specified
!SourceFolder)
-
Archive doesn't exist
- Solution: Check logs for "capsule did not exist" error
- Fix: Create the archive first or ensure the path is correct
-
Permission errors
- Solution: Check logs for permission/access denied errors
- Fix: Run with appropriate permissions, check file/folder permissions
Problem: File installing to wrong location.
Possible Causes:
-
!Destinationoverride in file section -
!DefaultDestinationset incorrectly - Folder section name typo
Solution: Check file section for !Destination, verify folder section names match destination paths.
Problem: File not appearing in archive after installation.
Possible Causes:
- Archive doesn't exist (error logged)
- File already exists and replacement not enabled
- Archive is read-only or locked
Solution: Check logs for errors, ensure Replace#= or !ReplaceFile=1 is set, verify archive permissions.
Problem: Files that should install are being skipped.
Possible Causes:
-
File#=syntax used with existing files (expected behavior - useReplace#=) -
!ReplaceFile=0explicitly set - File already exists in archive without replacement enabled
Solution: Review File Replacement Behavior section, use Replace#= or !ReplaceFile=1 to enable replacement.
[InstallList]
!DefaultDestination=<folder_path> ; Optional: default destination for all files
Folder#=<destination_path> ; Required: map to folder section[<destination_path>]
!SourceFolder=<relative_path> ; Optional: source folder within tslpatchdata
File#=<filename.ext> ; Install file (skip if exists)
Replace#=<filename.ext> ; Install file (replace if exists)[<filename.ext>]
!SourceFile=<source_filename.ext> ; Optional: alternative source file
!SaveAs=<final_filename.ext> ; Optional: rename during installation
!Filename=<final_filename.ext> ; Optional: alias for !SaveAs
!Destination=<destination_path> ; Optional: override folder destination
!ReplaceFile=<0|1> ; Optional: override replacement behavior
!SourceFolder=<relative_path> ; Optional: override source folder
!OverrideType=<ignore|warn|rename> ; Optional: conflict resolution mode- All paths in TSLPatcher use backslashes (
\) by convention, but HoloPatcher/PyKotor normalizes both slashes - Folder paths are created automatically if they don't exist
- Archive paths must exist before files can be installed to them
- InstallList runs before other patch lists in HoloPatcher (but after TLKList in original TSLPatcher)
- Files are backed up before installation (if they exist)
- Uninstall scripts are generated automatically in the backup folder
- TSLPatcher's Official Readme - Original TSLPatcher documentation
- Explanations on HoloPatcher Internal Logic - Internal implementation details
- TSLPatcher GFFList Syntax - Documentation for GFF modifications
- Mod Creation Best Practices - Best practices for mod development