Jump to content

Home

How do install a mod without TSL patcher?


Recommended Posts

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?

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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?!

Link to comment
Share on other sites

  • 2 weeks later...
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).

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...