Note: LucasForums Archive Project
The content here was reconstructed by scraping the Wayback Machine in an effort to restore some of what was lost when LF went down. The LucasForums Archive Project claims no ownership over the content or assets that were archived on archive.org.

This project is meant for research purposes only.

How do install a mod without TSL patcher?

Page: 1 of 1
 munchbacca
07-23-2007, 9:39 PM
#1
hello. i'm playing KOTOR on my mac and i want to install a mod (super skip taris) to use with the game. the only problem is that it uses a tsl patcher to install it.

i tried just dumping all the files into the override folder and that doesn't work, how do i manually install a mod that uses tsl patcher?
 RedHawke
07-23-2007, 9:54 PM
#2
Moving to the T3-M4 A.D.C. Tools forum...

Edit: And I do have a likely silly question, but why don't you just run the installer?
 munchbacca
07-23-2007, 10:32 PM
#3
i would but it is a .exe... which means it no likey my mac.

edit: oh yeah and thanks for moving this for me... i'm obviously new to the boards and don't know where everything goes.
 stoffe
07-24-2007, 6:48 AM
#4
hello. i'm playing KOTOR on my mac and i want to install a mod (super skip taris) to use with the game. the only problem is that it uses a tsl patcher to install it.

i tried just dumping all the files into the override folder and that doesn't work, how do i manually install a mod that uses tsl patcher?

If you don't have the ability to run Win32 executables you'll need to duplicate the work that the patcher does by hand instead. That would require that there are some modding tools available that can be run on a mac (2DA editor, TLK editor, GFF editor and script compiler in particular, depending on what mods you try to install). I don't know if there are any since I haven't owned a mac in 8 years. :) The actual work you need to do depends on what mod it is and how complex the installer instructions for it are.

If you have the necessary tools look inside the tslpatchdata folder for a file named changes.ini. This is a plain text file that contains work instructions for the TSLPatcher.

There are usually five main sections in this file containing key/value pairs (and usually a fair number of sub-sections referenced by them): [TLKList], [2DAList], [GFFList], [InstallList], [CompileList]. Sometimes there is a [HACKList] section as well.

The [TLKList] contains a list of tokens with their corresponding index value in the append.tlk file (that comes with the mod and should be in the tslpatchdata folder as well, if used). The patcher inserts these entries into the game's dialog.tlk file and then substitutes the StrRef# token with the dialog.tlk StrRef index the entry was inserted as, whenever the token is encountered in a 2DA, GFF or Script modifier below.

The [2DAList] contains a list of table modifiers, specifying the name of 2DA file which needs to be changed by the mod. It looks for 2DA files in the override folder first, and if it already exists there it is modified. If it doesn't exist it's copied from the tslpatchdata folder to the override folder and then modified.

Each value in the 2DAList (for example spells.2da) has a corresponding section (i.e. [spells.2da]) further down the changes.ini file. In this file-specific section is a list of modifier-types as key with a section name as value. There are three types of keys, AddRow#, ChangeRow# and CopyRow#, followed by a unique incrementing numerical index. They behave as could be expected, AddRow-modifiers add a new line at the end of the 2DA file, ChangeRow modifies a cell value of an existing line and CopyRow makes a copy of an existing row and adds it as a new line at the end of the file (usually modifying some of the cell values as well).

The values of these keys is the name of another section further down the changes.ini file that describes what changes should be made. Most of the keys in these sections are just column heading labels in the 2DA file, with the value being what should be set in the cell for that column on the row being manipulated/added. There are a few exceptions though:

For ChangeRow/CopyRow modifiers, the first line contains either a RowIndex, RowLabel or LabelIndex key to inform the patcher of which line to modify (or to make a copy of, in the latter case).
RowIndex specifies the line number in the file, starting at 0 at the top and counting down.
RowLabel specifies the Row Label value of the line, which is a separate column in the 2DA file
LabelIndex looks for a label column and looks for a row with a value matching what's set in that column


For AddRow/CopyRow modifiers the first row may optionally have an ExclusiveColumn key, which is set to the label of a column in which the value assigned to that column below in the section must not already exist for a line in the 2DA file. If it does the existing line will be modified rather than a new being inserted.

In either of these cases the keys can also be one of the 2DAMEMORY# tokens, which are essentially memory slots where the patcher temporarily holds a value that might be assigned somewhere else later on. Those are usually used to keep track of the line number of a new row that has been created, so that it might be inserted into another 2DA, a GFF file or a script later on.

There is also a special high() value that might be assigned, which will set the value in that column to the highest existing numerical value of all rows + 1.


The [2DAList] contains a name of GFF files that should be modified, and each filename value here has a corresponding section below in the changes.ini file as well. This file-specific section they has a key that specifies a path/name of a field to set the value of, and the value to set to that key. For example, a key like PropertiesList\0\Subtype would modify the Subtype field inside the first struct inside the PropertiesList list field in the file.

The [CompileList] holds a list of NSS script source files that the patcher should compile. It may also contain an optional first key, !DefaultDestination, which changes where the compiled scripts are installed. If left out they will be put in the override folder. The NSS scripts usually contains either a StrRef# or a 2DAMEMORY# token, which the patcher would substitute with the corresponding value before compiling. Thus you will need to replace them manually in the NSS file with the proper value before you compile the scripts.

The [InstallList] contains a list of folder path/names within the game folder where files will just be copied unmodified. Each key in this list has an identically named section in changes.ini, which lists the files that should be copied to that folder, and how it should behave if the file already exists at the destination (overwrite/skip).

The [HACKList] section, if present, contains a list of NCS compiled script filenames, which a correspondingly named section below. The section contains a list of byte offsets in the file, and a value to write to that offset.
 munchbacca
07-24-2007, 12:31 PM
#5
wow thanks for that super detailed response! i don't know but i might be too stupid to know what exactly to do with all that information you just gave me though...

so this is the changes.ini file:

[Settings]
FileExists=1
WindowCaption=Install super skip Taris & Endar Spire
ConfirmMessage=N/A
LogLevel=4
InstallerMode=1
BackupFiles=1
PlaintextLog=0
LookupGameFolder=1
LookupGameNumber=1
SaveProcessedScripts=0


[TLKList]


[InstallList]
install_folder0=Override


[2DAList]


[GFFList]
File0=end_trask01.dlg


[CompileList]


[SSFList]

; ================================================== =================
[end_trask01.dlg]
AddField0=gff_end_trask01_RepliesList_2_0
AddField1=gff_end_trask01_RepliesList_3_0
AddField2=gff_end_trask01_RepliesList_4_0
AddField3=gff_end_trask01_ReplyList_93_0
AddField4=gff_end_trask01_ReplyList_94_0
AddField5=gff_end_trask01_ReplyList_95_0
AddField6=gff_end_trask01_EditorInfo_0
[gff_end_trask01_RepliesList_2_0]
FieldType=Struct
Path=EntryList\55\RepliesList
Label=
TypeId=2
AddField0=gff_end_trask01_Index_0
AddField1=gff_end_trask01_Active_0
AddField2=gff_end_trask01_IsChild_0
[gff_end_trask01_Index_0]
FieldType=DWORD
Label=Index
Value=93
[gff_end_trask01_Active_0]
FieldType=ResRef
Label=Active
Value=
[gff_end_trask01_IsChild_0]
FieldType=Byte
Label=IsChild
Value=0
[gff_end_trask01_RepliesList_3_0]
FieldType=Struct
Path=EntryList\55\RepliesList
Label=
TypeId=3
AddField0=gff_end_trask01_Index_1
AddField1=gff_end_trask01_Active_1
AddField2=gff_end_trask01_IsChild_1
[gff_end_trask01_Index_1]
FieldType=DWORD
Label=Index
Value=94
[gff_end_trask01_Active_1]
FieldType=ResRef
Label=Active
Value=
[gff_end_trask01_IsChild_1]
FieldType=Byte
Label=IsChild
Value=0
[gff_end_trask01_RepliesList_4_0]
FieldType=Struct
Path=EntryList\55\RepliesList
Label=
TypeId=4
AddField0=gff_end_trask01_Index_2
AddField1=gff_end_trask01_Active_2
AddField2=gff_end_trask01_IsChild_2
[gff_end_trask01_Index_2]
FieldType=DWORD
Label=Index
Value=95
[gff_end_trask01_Active_2]
FieldType=ResRef
Label=Active
Value=
[gff_end_trask01_IsChild_2]
FieldType=Byte
Label=IsChild
Value=0
[gff_end_trask01_ReplyList_93_0]
FieldType=Struct
Path=ReplyList
Label=
TypeId=93
AddField0=gff_end_trask01_Listener_0
AddField1=gff_end_trask01_AnimList_0
AddField2=gff_end_trask01_Text_0
AddField3=gff_end_trask01_VO_ResRef_0
AddField4=gff_end_trask01_Script_0
AddField5=gff_end_trask01_Delay_0
AddField6=gff_end_trask01_Comment_0
AddField7=gff_end_trask01_Sound_0
AddField8=gff_end_trask01_Quest_0
AddField9=gff_end_trask01_PlotIndex_0
AddField10=gff_end_trask01_PlotXPPercentage_0
AddField11=gff_end_trask01_WaitFlags_0
AddField12=gff_end_trask01_CameraAngle_0
AddField13=gff_end_trask01_FadeType_0
AddField14=gff_end_trask01_EntriesList_0
AddField15=gff_end_trask01_SoundExists_0
AddField16=gff_end_trask01_CameraID_0
[gff_end_trask01_Listener_0]
FieldType=ExoString
Label=Listener
Value=
[gff_end_trask01_AnimList_0]
FieldType=List
Label=AnimList
[gff_end_trask01_Text_0]
FieldType=ExoLocString
Label=Text
StrRef=-1
lang0=[Skip Endar Spire]
[gff_end_trask01_VO_ResRef_0]
FieldType=ResRef
Label=VO_ResRef
Value=
[gff_end_trask01_Script_0]
FieldType=ResRef
Label=Script
Value=skip_spire
[gff_end_trask01_Delay_0]
FieldType=DWORD
Label=Delay
Value=4294967295
[gff_end_trask01_Comment_0]
FieldType=ExoString
Label=Comment
Value=
[gff_end_trask01_Sound_0]
FieldType=ResRef
Label=Sound
Value=
[gff_end_trask01_Quest_0]
FieldType=ExoString
Label=Quest
Value=
[gff_end_trask01_PlotIndex_0]
FieldType=Int
Label=PlotIndex
Value=-1
[gff_end_trask01_PlotXPPercentage_0]
FieldType=Float
Label=PlotXPPercentage
Value=1
[gff_end_trask01_WaitFlags_0]
FieldType=DWORD
Label=WaitFlags
Value=0
[gff_end_trask01_CameraAngle_0]
FieldType=DWORD
Label=CameraAngle
Value=0
[gff_end_trask01_FadeType_0]
FieldType=Byte
Label=FadeType
Value=0
[gff_end_trask01_EntriesList_0]
FieldType=List
Label=EntriesList
[gff_end_trask01_SoundExists_0]
FieldType=Byte
Label=SoundExists
Value=0
[gff_end_trask01_CameraID_0]
FieldType=Int
Label=CameraID
Value=-1
[gff_end_trask01_ReplyList_94_0]
FieldType=Struct
Path=ReplyList
Label=
TypeId=94
AddField0=gff_end_trask01_Listener_1
AddField1=gff_end_trask01_AnimList_1
AddField2=gff_end_trask01_Text_1
AddField3=gff_end_trask01_VO_ResRef_1
AddField4=gff_end_trask01_Script_1
AddField5=gff_end_trask01_Delay_1
AddField6=gff_end_trask01_Comment_1
AddField7=gff_end_trask01_Sound_1
AddField8=gff_end_trask01_Quest_1
AddField9=gff_end_trask01_PlotIndex_1
AddField10=gff_end_trask01_PlotXPPercentage_1
AddField11=gff_end_trask01_WaitFlags_1
AddField12=gff_end_trask01_CameraAngle_1
AddField13=gff_end_trask01_FadeType_1
AddField14=gff_end_trask01_EntriesList_1
AddField15=gff_end_trask01_SoundExists_1
AddField16=gff_end_trask01_CameraID_1
[gff_end_trask01_Listener_1]
FieldType=ExoString
Label=Listener
Value=
[gff_end_trask01_AnimList_1]
FieldType=List
Label=AnimList
[gff_end_trask01_Text_1]
FieldType=ExoLocString
Label=Text
StrRef=-1
lang0=[Light Side skip Taris]
[gff_end_trask01_VO_ResRef_1]
FieldType=ResRef
Label=VO_ResRef
Value=
[gff_end_trask01_Script_1]
FieldType=ResRef
Label=Script
Value=ls_skip_taris
[gff_end_trask01_Delay_1]
FieldType=DWORD
Label=Delay
Value=4294967295
[gff_end_trask01_Comment_1]
FieldType=ExoString
Label=Comment
Value=
[gff_end_trask01_Sound_1]
FieldType=ResRef
Label=Sound
Value=
[gff_end_trask01_Quest_1]
FieldType=ExoString
Label=Quest
Value=
[gff_end_trask01_PlotIndex_1]
FieldType=Int
Label=PlotIndex
Value=-1
[gff_end_trask01_PlotXPPercentage_1]
FieldType=Float
Label=PlotXPPercentage
Value=1
[gff_end_trask01_WaitFlags_1]
FieldType=DWORD
Label=WaitFlags
Value=0
[gff_end_trask01_CameraAngle_1]
FieldType=DWORD
Label=CameraAngle
Value=0
[gff_end_trask01_FadeType_1]
FieldType=Byte
Label=FadeType
Value=0
[gff_end_trask01_EntriesList_1]
FieldType=List
Label=EntriesList
[gff_end_trask01_SoundExists_1]
FieldType=Byte
Label=SoundExists
Value=0
[gff_end_trask01_CameraID_1]
FieldType=Int
Label=CameraID
Value=-1
[gff_end_trask01_ReplyList_95_0]
FieldType=Struct
Path=ReplyList
Label=
TypeId=95
AddField0=gff_end_trask01_Listener_2
AddField1=gff_end_trask01_AnimList_2
AddField2=gff_end_trask01_Text_2
AddField3=gff_end_trask01_VO_ResRef_2
AddField4=gff_end_trask01_Script_2
AddField5=gff_end_trask01_Delay_2
AddField6=gff_end_trask01_Comment_2
AddField7=gff_end_trask01_Sound_2
AddField8=gff_end_trask01_Quest_2
AddField9=gff_end_trask01_PlotIndex_2
AddField10=gff_end_trask01_PlotXPPercentage_2
AddField11=gff_end_trask01_WaitFlags_2
AddField12=gff_end_trask01_CameraAngle_2
AddField13=gff_end_trask01_FadeType_2
AddField14=gff_end_trask01_EntriesList_2
AddField15=gff_end_trask01_SoundExists_2
AddField16=gff_end_trask01_CameraID_2
[gff_end_trask01_Listener_2]
FieldType=ExoString
Label=Listener
Value=
[gff_end_trask01_AnimList_2]
FieldType=List
Label=AnimList
[gff_end_trask01_Text_2]
FieldType=ExoLocString
Label=Text
StrRef=-1
lang0=[Dark Side skip Taris]
[gff_end_trask01_VO_ResRef_2]
FieldType=ResRef
Label=VO_ResRef
Value=
[gff_end_trask01_Script_2]
FieldType=ResRef
Label=Script
Value=ds_skip_taris
[gff_end_trask01_Delay_2]
FieldType=DWORD
Label=Delay
Value=4294967295
[gff_end_trask01_Comment_2]
FieldType=ExoString
Label=Comment
Value=
[gff_end_trask01_Sound_2]
FieldType=ResRef
Label=Sound
Value=
[gff_end_trask01_Quest_2]
FieldType=ExoString
Label=Quest
Value=
[gff_end_trask01_PlotIndex_2]
FieldType=Int
Label=PlotIndex
Value=-1
[gff_end_trask01_PlotXPPercentage_2]
FieldType=Float
Label=PlotXPPercentage
Value=1
[gff_end_trask01_WaitFlags_2]
FieldType=DWORD
Label=WaitFlags
Value=0
[gff_end_trask01_CameraAngle_2]
FieldType=DWORD
Label=CameraAngle
Value=0
[gff_end_trask01_FadeType_2]
FieldType=Byte
Label=FadeType
Value=0
[gff_end_trask01_EntriesList_2]
FieldType=List
Label=EntriesList
[gff_end_trask01_SoundExists_2]
FieldType=Byte
Label=SoundExists
Value=0
[gff_end_trask01_CameraID_2]
FieldType=Int
Label=CameraID
Value=-1
[gff_end_trask01_EditorInfo_0]
FieldType=ExoString
Path=
Label=EditorInfo
Value=v2.2.8 May 11, 2006 LastEdit: 04-Sep-06 17:42:47
[install_folder0]
File0=ds_skip_taris.ncs
File1=ls_skip_taris.ncs
File2=skip_spire.ncs


so what am is supposed to do with this information?!
 stoffe
08-05-2007, 3:58 PM
#6
wow thanks for that super detailed response! i don't know but i might be too stupid to know what exactly to do with all that information you just gave me though...

so this is the changes.ini file:

so what am is supposed to do with this information?!

Sorry I didn't reply earlier, I must have overlooked this post. Anyway:

That configuration is set up to make the patcher modify the end_trask01.dlg dialog file. AddField# is a special key word that causes the patcher to add a new field to the file, and it is followed by a section identifier which holds the data of the new field to be added.

Looking from the start, the [GFFList] section is a list of the names of all GFF files to modify, where the file name also corresponds to a section name with instructions on what to to with the file, in this case [end_trask01.dlg].

This section contains 7 AddField directives, the value of each key is the name of a section, as said above. So, for the first you have the [gff_end_trask01_RepliesList_2_0] section containing the specifics for that field. Looking at this section you have the following keys
FieldType - a mandatory key whose value is set to the GFF data type this field should be of (as listed in GFF editor utilities)
Path - an optional key which specifies where in the GFF file tree hierarchy the field should be added. If this key is left out the new field will be added below the field whose section contain the AddField key.
Label - mandatory key holding the name label of the field to add in the GFF file. All fields have a label except Struct fields below a List field. Since this is the case here the value is blank.
TypeId - an optional key that is used for Struct fields, which sets the type-id value of the struct field.


Aside from those you have another set of AddField keys, which like before the value points to the name of a section containing the specifics of the field to add. If you look inside the first, [gff_end_trask01_Index_0], you'll see that it only contains three keys:
FieldType - as mentioned before, this key must always be present, telling the data type of the field to add. In this case it's a numeric DWORD field.
Label - as above, specifies the name label of the new field to add, "Index" in this case. It's a text string that can be no more than 16 characters in length.
Value - most fields that are data carriers (strings, numbers etc) rather than containers (structs, lists) have a value key that sets the value this particular field should be set to in the GFF file.


As seen the "Path" key is missing, meaning that this new field will be added to the struct field created in the previous section, i.e. EntryList\55\RepliesList\?\Index (the ? is the index number of the new struct added to the RepliesList list field, which is the next free number in sequence and thus dynamic).

The other sections are used similarily, to add a branch to a tree structure within a DLG file. For this to make any sense you need some basic idea of how DLG files are structured. There should be documentation for this in the tutorial section in Holowan or on bioware's NWN site's developer section. The very short version is:

Two main LIST fields are used, EntryList and ReplyList. Unsurprisingly the EntryList holds all the Entry nodes representing what NPCs say, and the ReplyList holds all the Reply nodes holding the player response choices. These two are then interlinked to form a tree structure.

The EntryList contains a number of Entry structs, one for each Entry node in the dialog file. Among other fields, this struct has a RepliesList field, which contains one struct for every player reponse available after this NPC line. These structs hold an Index field that points to the TypeId of a struct in the ReplyList that holds the data for that particular reply node.

The ReplyList, in turn, contains one Reply struct for each player response node in the dialog file. Among its other fields it has an EntriesList field, which contains one struct for every NPC line this response directly leads to (usually just one, but can be several if conditional scripts are used to trigger different NPC responses under different circumstances). These structs also contain an Index field, which contains the TypeId of a struct in the EntryList of the entries it leads to.

So, with this in mind, the config instructions would insert new fields into the GFF file's tree structure like:


Root/top node
List: EntryList

Struct: 55

List: RepliesList

Struct: ? (typeid=2)

Dword: Index = 93
ResRef: Active = ''
Byte: IsChild = 0

Struct: ? (typeid=3)

Dword: Index = 94
ResRef: Active = ''
Byte: IsChild = 0

Struct: ? (typeid=4)

Dword: Index = 95
ResRef: Active = ''
Byte: IsChild = 0




List: ReplyList

Struct: ? (typeid=93)

ExoString: Listener = ''
List: AnimList (empty)
ExoLocString: Text (strref=-1)

Text English/Male: '[Skip Endar Spire]'

ResRef: VO_ResRef = ''
ResRef: Script = 'skip_spire'
Dword: Delay = 4294967295 (which is 0xFFFFFFFF in hex, meaning -1)
ExoString: Comment = ''
ResRef: Sound = ''
ExoString: Quest = ''
Int: PlotIndex = -1
Float: PlotXPPercentage = 1.0
Dword: WaitFlags = 0
Dword: CameraAngle = 0
Byte: FadeType = 0
List: EntriesList (empty)
Byte: SoundExists = 0
Int: CameraID = -1

Struct: ? (typeid=94)

ExoString: Listener = ''
List: AnimList (empty)
ExoLocString: Text (strref=-1)

Text English/Male: '[Light Side skip Taris]'

ResRef: VO_ResRef = ''
ResRef: Script = 'ls_skip_taris'
Dword: Delay = 4294967295 (which is 0xFFFFFFFF in hex, meaning -1)
ExoString: Comment = ''
ResRef: Sound = ''
ExoString: Quest = ''
Int: PlotIndex = -1
Float: PlotXPPercentage = 1.0
Dword: WaitFlags = 0
Dword: CameraAngle = 0
Byte: FadeType = 0
List: EntriesList (empty)
Byte: SoundExists = 0
Int: CameraID = -1

Struct: ? (typeid=95)

ExoString: Listener = ''
List: AnimList (empty)
ExoLocString: Text (strref=-1)

Text English/Male: '[Dark Side skip Taris]'

ResRef: VO_ResRef = ''
ResRef: Script = 'ds_skip_taris'
Dword: Delay = 4294967295 (which is 0xFFFFFFFF in hex, meaning -1)
ExoString: Comment = ''
ResRef: Sound = ''
ExoString: Quest = ''
Int: PlotIndex = -1
Float: PlotXPPercentage = 1.0
Dword: WaitFlags = 0
Dword: CameraAngle = 0
Byte: FadeType = 0
List: EntriesList (empty)
Byte: SoundExists = 0
Int: CameraID = -1


ExoString: EditorInfo = 'v2.2.8 May 11, 2006 LastEdit: 04-Sep-06 17:42:47'


(? is the list index of the new struct, i.e. the next number in sequence in the list.)

As an aside I noticed that the config file contains a few potential errors which will cause problems if the dlg file in question does not look like the mod author expected. The TypeID values for the structs (and the Index fields that point to them) should not have their values set directly, like was done, but rather be set to the same value as the List index the struct is added as (i.e. it's position within the List field, starting at 0 and counting down).
Page: 1 of 1