A course on SFO - chapter 1.2

Getting started

Back to contents

This chapter explains how to set up SFO, and the basics of its modding environment.

1.2.1 Setting up SFO

Installing SFO is simple. Just follow these steps:

  1. Make sure your mod has the line 'AUTO_EVAL_STRINGS' in its preamble. SFO requires AUTO_EVAL_STRINGS to function.
  2. Include the 'sfo' folder in your mod. You can get an up-to-date copy from my mod 'Talents of Faerun', available at gibberlings3.net. Do not use the 'sfo' folder from my mod 'Sword Coast Stratagems' (SCS), which is an earlier version. (You can use the 'sfo2e' folder from SCS, which is the new version of SFO, but you should rename it 'sfo' when you put it into your mod.)
  3. Include the line
    INCLUDE "sfo/sfo_init.tpc"
    
    in your ALWAYS block (probably at the top).

Once installed, SFO loads the SFO and SFO-LUA function libraries, so that you can use SFO functions. It also does the following things:

  1. Automatically loads an ini file, if you have one, and makes its contents available.
  2. Automatically handles your tra files to manage interoperability between oBG2 and the Enhanced Edition.
  3. Loads a bunch of variables which help simplify describing your particular modding environment.
  4. Creates the weidu_external folder and a bunch of subfolders, and sets more variables pointing to them.
  5. Loads more variables which preload various often-used bits of game data.
  6. Loads even more variables which help manage compatibility between the various versions of BG (TUTU, BGT, BGEE, EET).
  7. Fixes a few small errors and omissions in the unmodded game which interfere with modding.

If you don't want any of these features and just want to load SFO's function library, set the variable sfo_lite=1 before installing:

OUTER_SET sfo_lite=1
INCLUDE "sfo/sfo_init.tpc"

There are a number of other variables that can be set to customize an SFO install more finely; again, this needs to be done in the ALWAYS block before INCLUDEing sfo_init. I describe various of them below.

1.2.2 Loading an ini file

Ini files are used by some mods to fine-tune installation options, beyond what the normal WEIDU subcomponent framework can handle. An ini file is a text file consisting of a series of 'key=value' pairs. Here key should be some string without spaces, and value should either have no spaces or be wrapped in quotes (" or ~ should both be fine). Optionally, your ini file can be separated into sections. A section starts with a string wrapped in "[]".

For instance, here's a fragment of the ini file for Talents of Faerun:

[alignment]
jaheira_is_ng=1
faldorn_is_ne=1

[class]
lay_on_hands_level=5
lay_on_hands_interval=4
smite_evil_level=7
smite_evil_interval=6
number_of_required_specialist_spells=1
list_specialist_spells=1

[default_start]
start_iwd=iwd
start_bg=bg
start_bg2=bg2
start_eet=bg
You should not repeat section titles; you can repeat a key, but only in different sections.

SFO assumes that your ini file, if you have one, is sitting at %MOD_FOLDER%/%MOD_FOLDER%.ini, and if it finds a file there, it will load it. As far as SFO is concerned your ini file is case-insensitive, so it loads everything lowercased. (If you prefer to keep your ini file somewhere else, you can tell SFO about it; see below.)

You can access your ini file via the (dimorphic) function check_ini, like this:

LAF check_ini STR_VAR ini=smite_evil_level RET value END
Used on the above ini, this would return value=7. Optionally, you can specify a section too, e.g.
LAF check_ini STR_VAR ini=smite_evil_level section=class RET value END
(If you don't specify a section, but you have a key that occurs in more than one section, the last value in the ini will be returned.)

By default, check_ini will throw a WARNING if you check an ini entry that does not occur in the ini file. This is a debugging feature that guards against typos and against forgetting to set up the ini. If you want to check an ini without assuming it is present, use the INT_VAR silent=1, as in

LAF check_ini INT_VAR silent=1 STR_VAR ini=smite_evil_level RET value END
In any case, if you check an ini that is not present, check_ini returns zero.

If you want to keep your ini function somewhere else, you do it like this:

OUTER_SPRINT sfo_ini_file "my_awesome_ini.ini" 	// the filename of your ini file
OUTER_SPRINT sfo_ini_file_location "%MOD_FOLDER%/lib" 	// set to the full path (relative to your game directory) 
							//of the folder where your ini file lives.

INCLUDE "sfo/sfo_init.tpc"

1.2.3 Automatic handling of tra files

SFO automatically handles your tra files (using the 'charset_wrapper' function, which is called automatically when you run it). If:

  • You keep your tra files in the 'tra', 'lang', or 'language' file
  • You encode your tra files in UTF-8 (the encoding used by the Enhanced Edition)
  • You keep the strings used by your TP2 itself (like component names and incompatibility messages) in 'setup.tra'
  • You keep the 'iconv' folder, used for conversions to or from UTF-8, in the same directory as your tra files
  • You don't want to load any tra files up front and will use LOAD_TRA/WITH_TRA to load your tra files as and when you need them
  • The language in which all your strings are guaranteed to be present is English
then you don't need to do anything: all your tra files will be moved to weidu_external/lang/%MOD_FOLDER%, and if necessary (on non-Enhanced Edition installs) converted from UTF-8. The variable 'sfo_tra_path' will be set to the location of your tra files, so you can just do
LOAD_TRA "%sfo_tra_loc%/english/my_tra.tra" "%sfo_tra_loc%/%LANGUAGE%/my_tra.tra
(or the equivalent WITH_TRA syntax) to use a specific tra file. You can do AUTO_TRA similarly.

If one or more of these isn't correct, you can change the defaults by setting various variables before INCLUDEing sfo_init.tpc. Here's the full syntax:

OUTER_SPRINT sfo_tra_path "tra" 	// the location of your tra files, relative to MOD_FOLDER
OUTER_SET sfo_from_utf8=1 		// set to 0 if your tra files are encoded in oBG2 style 
					//and need converting to UTF-8 on enhanced-edition installs
OUTER_SPRINT sfo_setup_tra "setup" 	// set to the name (without .tra) of the file in which your tp2 component names, 
					//component-compatibility messages, component group names, etc live
OUTER_SPRINT sfo_iconv_path "tra/iconv" // set to the location (relative to MOD_FOLDER) of iconv.exe
OUTER_SPRINT sfo_tra_load ""		// set to a space-separated list of names (.tra is optional) of 
					//tra files that you want loaded after conversion/copyover
OUTER_SPRINT sfo_default_language "english" // set to the language in which you are developing the mod
OUTER_SPRINT sfo_extra_tra_folders ""	// set to a space-separated list of additional folders that also contain 
					//tra files (you still need to avoid namespace conflict)

INCLUDE "sfo/sfo_init.tpc"

1.2.4 SFO's preset environment variables.

SFO sets a group of variables which give convenient access to details of the environment in which you are installing. Here's the full list (each is equal to 0 by default). It saves a bit of typing to define them, and also has some runtime advantages over native checks like GAME_IS.

  • is_bg (=1 if you are installing on Baldur's Gate or any of its conversions (TUTU, BGT, BGEE, EET))
  • is_bg1 (synonym for is_bg)
  • is_bg2 (=1 if you are installing on any version of Baldur's Gate II (including the Trilogy mods))
  • is_sod (=1 if Siege of Dragonspear is present)
  • is_iwd (=1 if you are installing on either original Icewind Dale or on IWDEE)
  • is_tutu (=1 if you are installing on Baldur's Gate 1 TUTU or EasyTUTU)
  • enhanced_edition (=1 if you are installing on an EE game)
  • is_eet (=1 if you are installing on Enhanced Edition Trilogy)
  • is_bgt (=1 if you are installing on the original Baldur's Gate Trilogy mod)
  • is_tobex (=1 if the ToBeX file extender for original BG2 is present)
  • demivrgvs (=1 if the main component of the Spell Revisions mod is present)
  • lefreut_ui (=1 if the 0 component of any of the mods LEUI-BG1EE, LEUI-BG2EE, or LEUI-SOD are installed)

1.2.5 The weidu_external folder, and labels

weidu_external is a folder in your mod directory, used by SFO (and other) mods to store files. SFO will set up the directory and a number of subdirectories, and defines variables that point to them. A partial list is:

  • 'workspace' is set to weidu_external/workspace. Put anything you like in here, and don't make any assumptions about other mods not messing with it - workspace is for things you use once and then discard.
  • 'data_loc' is set to weidu_external/data/%MOD_FOLDER%. This is reserved specifically for your mod, so you can put anything you like into it.
  • 'data_loc_shared' is set to weidu_external/data/dw_shared. This is for data intended to be shared between different mods. Please use your modder prefix if you put anything into it. (I confess that my own mods don't always do this; a perk of setting up the system!)
  • (Others are more specialized and/or used only by SFO's internal functions.)

    In addition, SFO sets up a system of 'labels' that can be created to tell your mod, or other people's mods, that some task has been carried out or some object is present. You create labels using the make_label (dimorphic) function:

    LAF make_label STR_VAR label=dw#iwd_spells_are_installed END
    
    and check them with the check_label (dimorphic) function
    LAF check_label STR_VAR label=dw#iwd_spells_are_installed RET value END
    
    which returns 1 if the label has previously been set and 0 otherwise. Labels exist in a shared namespace and so you should use a modder prefix with them. (Under the hood, labels are small files placed in weidu_external/markers).

    1.2.6 Preloaded data

    SFO's 'lib_data' library contains a number of macros designed to load data into memory. Two of those macros, data_spell_resrefs and data_joinable_dvs, run automatically when SFO is INCLUDEd, and contain, respectively, all the resrefs of the spells in spell.ids and all the death variables of party-joinable NPCs. I'll give the details below.

    data_spell_resrefs

    The main thing this does is go through spell.ids and, for each entry, set a variable whose name is the entry and whose value is the (uppercased) resref of the associated spell. For instance, WIZARD_FIREBALL is set to SPWI304. This makes for hopefully-more-readable code: you can do things like

    COPY_EXISTING "%WIZARD_FIREBALL%.spl" override
    	[patch]
    
    In addition, the variable WIZARD_FIREBALL_LEVEL gets set to the spell's level (in this case 3) and the variable WIZARD_FIREBALL_TYPE gets set to 'wizard' (other possible values are 'priest' and 'innate'). The data is also created in array form: the arrays sfo_spell_resrefs, sfo_spell_levels, and sfo_spell_types are in each case associative arrays with IDS entries as keys and the appropriate values. (So $sfo_spell_resrefs(WIZARD_FIREBALL) is set to SPWI304, etc.)

    There are a few details worth knowing:

    1. A few spells are hardcoded into data_spell_resrefs (basically the spells used by the Dark Moon, Dwarven Defender, Cleric of Tempus, Cleric of Tyr, and Avenger kits). See sfo/data/extra_spell_ids.txt.
    2. The files dw_ext_spell.ids and dw_feat_data.2da (both in weidu_external/data/dw_shared) are read if they exist, and treated as additional lists of spell names, overriding spell.ids if there is a namespace conflict. (They are used by SFO to store, respectively, extra spells added outside the usual namespace and high-level abilities.)
    3. SFO uses resrefs of the form DWWPabc, DWPWabc, DWPIabc, and DWWIabc to store, respectively, wizard spells that have been converted to priest spells, priest spells that have been converted to wizard spells, and priest and wizard spells that have been converted to innate. data_spell_resrefs also processes these, e.g.
      • INNATE_CLERIC_CURE_LIGHT_WOUNDS will be set to DWPI103
      • INNATE_WIZARD_FIREBALL will be set to DWWI304
      • WIZARD_CURE_LIGHT_WOUNDS will be set to DWPW103
      • CLERIC_FIREBALL will be set to DWWP304
      In each case this happens only if the relevant resource actually exists.

    data_joinable_dvs

    This simply outputs an associative array, 'sfo_joinable_dvs', whose keys are the lowercased death variables (i.e. script name) of any NPC who can join the party (the values are blank). Joinable NPCs are defined as any that occur in pdialog.2da or in any file in the PDIALOG column of campaign.2da. In addition, the joinable NPCS in Candlekeep in BG (arkanis, canderous, deder, mordaine, osprey) are hardcoded to be added.

    1.2.7 Automatic crossplatform variables

    A major nuisance of modding BG specifically is that there are no fewer than five versions - original BG, the TUTU conversion, the Baldur's Gate Trilogy combination of oBG with original BG2, the Enhanced Edition, and the Enhanced Edition Trilogy (EET) combination of BGEE and BG2EE - and all have different naming conventions. This also causes a few issues when modding BG2 - mostly the Trilogy combinations leave the BG2 filenames invariant, but EET in particular changes the chapter-number conventions. The community standard for handling this is to define a number of 'crossplatform' variables that evaluate differently on different installs.

    SFO automates this, automatically loading the right crossplatform variables for your install. The full details of crossplatform modding are a bit out of scope for this course so here I'll just summarise what is defined.

    Areas

    SFO sets a variable for every area in BG, whose name is that area's human-readable name in the usual conventions used in crossplatform modding and whose value is the resref of that area on your current install. For instance, 'NWBaldursGate' resolves to AR0100 on oBG or BGEE, FW0100 on TUTU, AR7200 on BGT, and BG0100 on EET. A full list can be found in sfo/data/bg1_area_list.2da.

    Area scripts

    SFO also sets a variable for the script of each BG area, whose name is the area name followed by '_BCS' and whose value is the area script. For example, 'NWBaldursGate_BCS' would resolve as AR0100 on oBG or BGEE, _AR0100 on TUTU, BG0100 on EET, and AR7200 on BGT.

    Prefix/suffix variables

    SFO sets variables 'tutu_var' and 'tutuscripta' through 'tutuscriptz'. On TUTU, all of these are set to '_'. On any other install, tutu_var is set to "" and the others are set to the appropriate letter of the alphabet, e.g. tutuscriptk=k.

    SFO also sets 'eet_var', which equals '_' on EET and is blank otherwise.

    Chapter variables

    SFO sets variables 'bg1_chapter_1' through 'bg1_chapter_7' and 'bg2_chapter_1' through 'bg2_chapter_10', which resolve as the chapter numbers used in the respective installs.

    Special cases

    There are a few specific variables which aren't conveniently captured by a systematic scheme and need to be set by hand. These can be found in sfo/data/bg1_file_names.2da

    1.2.8 Automatic fixes

    SFO is not a fixpack, but there are a few bugs on the various IE games that actually get in its way, and so SFO quietly fixes them when you first install. Here's the list.

    Enable dir.ids

    Not all versions of the IE use dir.ids, which allows directions to be given on a compass (N,NNW, etc) and not just as integers in the 0-15 range. SFO enables it.

    Add missing gtimes.ids entries

    The IE games are very inconsistent as to which times are present in gtimes.ids. SFO insists that at least a large group are present.

    Add missing spell.ids entries

    Certain spell.ids entries are not present in some versions of the game even when the spell itself is present. SFO standardizes spell.ids a bit by adding a bunch of entries if they are not already there. (This only applies on games with a spell system based on BG2, i.e. BG2 itself, BGEE, and IWDEE.)

    Make sure FarthestObject and variants are present

    The Enhanced Edition introduces the FarthestObject object (and variants) but they are not always present in object.ids. SFO puts them there if they're not there already.

    Remove broken oBG2 items

    The non-Enhanced version of BG2 has a number of broken itm and are files which disrupt regexp patching. SFO removes them if they haven't been removed already (e.g., by the BG2 fixpack).

    Remove + signs from missile.ids

    Version 2.6 of the EE introduces some entries in missile.ids that contain a + sign. That's legal in the IE but WEIDU can't parse them, so SFO swaps them for the string 'plus_'.

    Make rows in weapprof.2da unique

    weapprof.2da reuses two row titles, 'SPEAR' and 'AXE'. SFO leans heavily on the paradigm of unique lookup of 2da entries by row/column, so it would find this confusing. It swaps the first appearance of 'SPEAR' for 'SPEAR_BG1' and does similarly for the first appearance of 'AXE'.

    Fix minor bugs in the UI

    The EE game's UI ships with a couple of minor bugs: they're harmless in-game but make it a pain to debug UI edits. SFO fixes them. (This fix doesn't happen if Lefreut's UI is present.)

    1.2.9 SFO customization options

    If you want to, you can tweak quite a few features of the SFO environment by setting these variables before INCLUDEing. Mostly I recommend not doing so, but I thought it would be good to have them documented.) The variables are:
    • sfo_library_path (default value 'sfo'). Set to the folder where sfo's files live (relative to %MOD_FOLDER%). SCS sets this to 'sfo2e', for instance, to allow old and new versions of sfo to coexist.)
    • external_loc (default value 'weidu_external'). Set to the external folder used to store data.
    • sfo_lite (default value 0). If set to 1, sfo_init only loads functions, and skips all the other environmental steps.
    • sfo_charsets (default value 1). If set to 0, SFO doesn't automatically try to handle tra file conversion (see section 1.3).
    • sfo_handle_ini (default value 1). If set to 0, SFO doesn't automatically load the ini file (see section 1.2).
    • sfo_handle_crossplatform (default value 1). If set to 0, SFO doesn't automatically load the crossplatform variables (see section 1.7).
    • sfo_handle_fixes (default value 1). If set to 0, SFO doesn't automatically fix various broken files (see section 1.8).
    • sfo_load structs (default value 1). If set to 0, SFO doesn't automatically load the data requires for the 'lib_struct' function library (discussed later).
    • sfo_load_data (default value 'data_spell_resrefs data_joinable_dvs'). A space-separated list of action macros that SFO will automatically run on loading.
    • sfo_tra_path (default value 'lang', 'tra', or 'languages', depending on which folder exists in your mod folder. (If more than one does, this list is in decreasing order of priority)). Where SFO looks (relative to your mod folder) for tra files.
    • sfo_setup_tra (default value 'setup'). The tra file where you keep the component names and other strings used in the WEIDU installation process.
    • sfo_tra_load (default value ''). A space-separated list of tra files to be loaded by SFO.
    • sfo_extra_tra_folders (default value ''). A space-separated list of additional places SFO should check for tra files.
    • sfo_iconv_path (default value '%sfo_tra_path%/iconv'). Where SFO looks (relative to your mod folder) for the 'iconv.exe' executable.
    • sfo_default_language (default value 'english'). The language in which your mod is written and in which all tra files are guaranteed to be complete.
    • ext_lang_loc (default value '%external_loc%/lang/%MOD_FOLDER%'). Where SFO should store possibly-converted tra files.
    • ssl_loc (default value '%MOD_FOLDER%/bin'). Where SFO should look for the 'SSL' executable, if you are using my Stratagems Scripting Language.
    • sfo_from_utf8 (default value 1). If set to 0, SFO assumes your tra files are encoded for oBG2, and attempts to convert them if you are installing on an Enhanced Edition game, rather than vice versa.
    • sfo_ini_file (default value '%MOD_FOLDER%.ini'). The name of your INI file, if any.
    • sfo_ini_file_location (default value '%MOD_FOLDER%'). The location of your INI file, if any.