We often like to know what a character's attributes and powers are and these are often not available in the game. In addition, we want to know what levels the powers are at, how many attributes the character has activated (purchased) and so on. We've already gotten the plain FFEdit character information from Campaign_ReadCharacters() - here we note the functions that fill out that data and also read data from saved games and hero files.
These functions are pretty much the heart of the data reading utility. When combined with GetCharacterData() in the chardata module, we have all we need to determine a built-in (created with FFEdit or EZHero using "Send to DAT") character's information during a campaign. If we combine that with the unique complex attributes automatically set for us when we scan a given mod folder with DrMike2000's FFX Control Centre, we can get the same information for characters in Danger Room skirmishes.
The module file datfiles.py can go in the Scripts directory. Any python file that uses these functions should import datfiles or from datfiles import *. For example, we can say
import datfiles
myhero = datfiles.Campaign_ReadCharacterData('hero_1')
or we can say
from datfiles import *
myhero = Campaign_ReadCharacterData('hero_1')
For calls to any of these function made during the game, not specifying FileName will cause the function to try and read the appropriate files for the current mod, when it can figure them out. Generally, this means most function can be called without any Filename arguments. However, hero files must still be specified, although the function will try looking in the hero directory if only the hero file name itself (and not the full path) is specified for the FileName.
None of these functions in the datfiles module use the ff, js, cshelper or other Freedom Force-specific modules, so they can be run from a python shell outside the game to summarize or explore DAT file entries, if desired. In that case, FileName must be specified.
A "filled-in" character is one for which the data dictionary for that character is filled-in with information such as the full power dictionary for each of the character's powers, the levels at which the powers are bought, and the number of active attributes the character has purchased. This information is all already in a hero file (although it can change if the hero is recruited into a campaign and spends CP on new powers or attributes or raising the level of existing powers). The information is generally in even an built-in character's entry in a saved game file, but the power names are just names, not full power dictionaries. Finally, barest of all, are unrecruited built-in heroes, who need powers filled in as well as power levels set to their default starting levels.
There are three principle functions for reading filled-in characters, Campaign_ReadCharacterData(), Campaign_ReadCharactersFromSavedGame(), and Campaign_ReadHeroFile().
This function reads in the characters.dat, objects.dat, and powers.dat for a mod and returns a dictionary of filled-in dictionaries, one each character for all of the built-in characters in the mod. The outer dictionary's keys are the characters' template names. For each template, its value is a diction with almost all of the fields from the character's Characters tab and Objects tab FFEdit entries as well as his powers from the FFEdit Powers tab. So, if we call chars = Campaign_ReadCharacterData() then chars['mentor'] is the whole filled-in dictionary for Mentor, chars['mentor']['speed'] is his speed, chars['mentor']['powers'] is a dictionary where each entry is one of his powers, chars['mentor']['mass'] is his mass (this is from the template data, so it does not account for material multipliers), and so on.
This function and Campaign_ReadHeroFile() (below) are used to read data for characters in skirmish mode. The characters attributes and powers are assumed to be at their starting levels.
ModPath is the path of the mod where the characters.dat, objects.dat, and powers.dat file are. It can usually be left blank when used during a running mission.
ForceRead forces the DAT files to be re-read, even if they have been read before.
verbose prints some info as each object is read from the DAT file. Prints a warning for each character entry that lacks an object template entry and for every character listed as having a power that is not in the powers.dat database.
Reads and returns the character data out of a saved game file. This is the best way to get information about the recruited heroes during a campaign mission. There are no saved games for skirmish matches.
In addition to the information about characters returned by Campaign_ReadCharacterData(), Campaign_ReadCharactersFromSavedGame() also returns entries for the teams prestige ('PP'), the text name of the saved game that shows up in the save and load dialogs ('saveName'), and the mission DAT file from that was running when the game was saved ('missionDATFile').
FileName is the full path of the saved game file. It can usually be left blank when used during a running mission. When it is blank (the default empty string), it will try and determine the most recently saved game that was saved from a mission that comes no later in the campaign than the current mission.
ModPath is the path of the mod where the objects.dat and powers.dat file are. They need to be read to fill in the built-in characters' powers and object attributes from the saved game file. It can usually be left blank when used during a running mission.
ForceRead forces the DAT files to be re-read, even if they have been read before.
verbose prints some info as each object is read from the DAT file. Prints a warning for each character entry that lacks an object template entry and for every character listed as having a power that is not in the powers.dat database.
Reads and returns the character data out of a single hero file.
FileName a hero file. This needs to be specified, although it does not have to be a complete path. If the string given isn't a file, the function assumes it is just the basename of the hero file (e.g. "Mister SPiff.hero") and tries to find that file in the hero files directory for the current mod. (*** Don't know about FFvsT3R under Win9X systems yet.)
This function and Campaign_ReadCharacterData() (above) are used to read data for characters in skirmish mode.
ForceRead forces the hero file to be re-read, even if it has been read before.
verbose prints some info as the data is read from the hero file.
Each of these functions returns a dictionary. See the Python Tutorial for a quick summary of Python dictionaries and the Python Library entry for a more complete description, with more of the methods available for using dictionary data, keeping in mind that Freedom Force uses Python 1.5.
Most of the keys for each character dictionary are pretty self-explanatory and most are named similarly to their corresponding FFEdit entries. Below are three examples.
To start off, here we read and print Liberty Lad's data from a game saved during the Robots on the Rampage missions.
>>> import datfiles >>> sgd = datfiles.Campaign_ReadCharactersFromSavedGame() >>> print sgd['liberty_lad']
This will return something like
print sgd['liberty_lad']
{'powers': {'liberty Backflip': {'PowerName': 'liberty Backflip', 'Stun': 0, 'RangeMax': 5, 'SpecialType': 0, 'Speed': 3, 'AttackFlags': 128, 'EPCost': 3, 'RangeMin': 1, 'Radius': 0, 'Magnitude': 3, 'SubType': 0, 'notForCustom': 0, 'animation': 'melee_2', 'DamageType': 0, 'FX': '', 'MaxInstances': 0, 'Knockback': 3, 'Accuracy': 0, 'PowerType': 1}, 'liberty Tumble': {'PowerName': 'liberty Tumble', 'Stun': 0, 'RangeMax': 3, 'SpecialType': 7, 'Speed': 3, 'AttackFlags': 0, 'EPCost': 4, 'RangeMin': 1, 'Radius': 0, 'Magnitude': 0, 'SubType': 0, 'notForCustom': 0, 'animation': 'special_2', 'DamageType': 1, 'FX': 'libertylad_tumble', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 0, 'PowerType': 7}, 'liberty Exploding Spitball': {'PowerName': 'liberty Exploding Spitball', 'Stun': 0, 'RangeMax': 1, 'SpecialType': 0, 'Speed': 1, 'AttackFlags': 2, 'EPCost': 4, 'RangeMin': 0, 'Radius': 2, 'Magnitude': 4, 'SubType': 3, 'notForCustom': 0, 'animation': 'ranged_2', 'DamageType': 12, 'FX': 'libertylad_grenade', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 3, 'PowerType': 2}, 'liberty Proximity Grenade': {'PowerName': 'liberty Proximity Grenade', 'Stun': 1, 'RangeMax': 1, 'SpecialType': 0, 'Speed': 1, 'AttackFlags': 514, 'EPCost': 5, 'RangeMin': 0, 'Radius': 2, 'Magnitude': 3, 'SubType': 3, 'notForCustom': 0, 'animation': 'ranged', 'DamageType': 3, 'FX': 'libertylad_proximitygrenade', 'MaxInstances': 0, 'Knockback': 1, 'Accuracy': 3, 'PowerType': 2}, 'liberty One Two': {'PowerName': 'liberty One Two', 'Stun': 1, 'RangeMax': 5, 'SpecialType': 0, 'Speed': 3, 'AttackFlags': 0, 'EPCost': 0, 'RangeMin': 1, 'Radius': 0, 'Magnitude': 2, 'SubType': 0, 'notForCustom': 0, 'animation': 'melee', 'DamageType': 0, 'FX': 'libertylad_onetwo', 'MaxInstances': 0, 'Knockback': 1, 'Accuracy': 0, 'PowerType': 1}, 'liberty Energy Grenade': {'PowerName': 'liberty Energy Grenade', 'Stun': 0, 'RangeMax': 1, 'SpecialType': 0, 'Speed': 1, 'AttackFlags': 2, 'EPCost': 4, 'RangeMin': 0, 'Radius': 1, 'Magnitude': 3, 'SubType': 3, 'notForCustom': 0, 'animation': 'ranged_2', 'DamageType': 2, 'FX': 'libertylad_grenade', 'MaxInstances': 0, 'Knockback': 2, 'Accuracy': 3, 'PowerType': 2}, 'liberty Molecular Excitation': {'PowerName': 'liberty Molecular Excitation', 'Stun': 0, 'RangeMax': 1, 'SpecialType': 6, 'Speed': 2, 'AttackFlags': 0, 'EPCost': 3, 'RangeMin': 0, 'Radius': 0, 'Magnitude': 3, 'SubType': 0, 'notForCustom': 0, 'animation': 'special', 'DamageType': 1, 'FX': 'libertylad_molecularexcite', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 0, 'PowerType': 4}, 'liberty Stun Grenade': {'PowerName': 'liberty Stun Grenade', 'Stun': 5, 'RangeMax': 2, 'SpecialType': 0, 'Speed': 1, 'AttackFlags': 2, 'EPCost': 5, 'RangeMin': 0, 'Radius': 2, 'Magnitude': 0, 'SubType': 3, 'notForCustom': 0, 'animation': 'ranged', 'DamageType': 0, 'FX': 'libertylad_stungrenade', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 3, 'PowerType': 2}, 'liberty Taunt': {'PowerName': 'liberty Taunt', 'Stun': 0, 'RangeMax': 1, 'SpecialType': 0, 'Speed': 3, 'AttackFlags': 0, 'EPCost': 4, 'RangeMin': 0, 'Radius': 3, 'Magnitude': 4, 'SubType': 0, 'notForCustom': 0, 'animation': 'area', 'DamageType': 12, 'FX': 'libertylad_taunt', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 0, 'PowerType': 3}}, 'camp_only': 1, 'charName': 'liberty_lad', 'mass': 55.0, 'complex': 230.0, 'movementRadius': 1.0, 'NIF': 'library\\characters\\liberty_lad\\character.nif', 'endurance': 2, 'powerLevels': {'liberty Backflip': 0, 'liberty Tumble': 0, 'liberty Exploding Spitball': 0, 'liberty Proximity Grenade': 0, 'liberty One Two': 3, 'liberty Energy Grenade': 2, 'liberty Molecular Excitation': 0, 'liberty Stun Grenade': 3, 'liberty Taunt': 3}, 'templateName': 'liberty_lad', 'VID': 'LL', 'activeAttributes': 3, 'strength': 3, 'class': 'GAME_OBJ_HERO', 'speed': 5, 'CP': 6, 'XP': 2000, 'isCustom': 0, 'CSBase': '', 'characterAttributes': ['nimble', 'jumper', 'danger sense'], 'objectAttributes': ['complex', 'class', 'NIF', 'material', 'templateName', 'mass', 'pickupDistance', 'elasticity'], 'elasticity': 0.0, 'material': 0.0, 'energy': 3, 'AI': 'CGenericHero', 'alterEgo': '', 'tier_a': ['liberty One Two', 'liberty Taunt', 'liberty Backflip', 'liberty Tumble', 'liberty Exploding Spitball'], 'tier_b': ['liberty Stun Grenade', 'liberty Energy Grenade', 'liberty Proximity Grenade', 'liberty Molecular Excitation'], 'pickupDistance': 2.5, 'agility': 6}
Note that the keys of a python dictionary are not usually sorted.
And, for testing, the ShowHero() function is useful. Note that the second ShowHero() parameter (by default 1) can be set to 0 to turn off display of the full power entries. (As can be seen above, Liberty Lad has many powers.)
>>> sgd=datfiles.Campaign_ReadCharactersFromSavedGame() >>> datfiles.ShowHero(sgd['liberty_lad'],0) charName : liberty_lad isCustom : 0 strength : 3 speed : 5 agility : 6 endurance : 2 energy : 3 VID : LL AI : CGenericHero NIF : library\characters\liberty_lad\character.nif material : 0.0 mass : 55.0 alterEgo : activeAttributes : 3 characterAttributes : ['nimble', 'jumper', 'danger sense'] objectAttributes : ['complex', 'class', 'NIF', 'material', 'templateName', 'mass', 'pickupDistance', 'elasticity'] movementRadius : 1.0 class : GAME_OBJ_HERO CSBase : XP : 2000 CP : 6 tier_a : ['liberty One Two', 'liberty Taunt', 'liberty Backflip', 'liberty Tumble', 'liberty Exploding Spitball'] tier_b : ['liberty Stun Grenade', 'liberty Energy Grenade', 'liberty Proximity Grenade', 'liberty Molecular Excitation'] powerLevels : {'liberty Backflip': 0, 'liberty Tumble': 0, 'liberty Exploding Spitball': 0, 'liberty Proximity Grenade': 0, 'liberty One Two': 3, 'liberty Energy Grenade': 2, 'liberty Molecular Excitation': 0, 'liberty Stun Grenade': 3, 'liberty Taunt': 3} complex : 230.0 elasticity : 0.0 pickupDistance : 2.5 powers.keys() : ['liberty Backflip', 'liberty Tumble', 'liberty Exploding Spitball', 'liberty Proximity Grenade', 'liberty One Two', 'liberty Energy Grenade', 'liberty Molecular Excitation', 'liberty Stun Grenade', 'liberty Taunt']
Similarly, we can get a built-in character's starting data using Campaign_ReadCharacterData().
>>> import datfiles
>>> startchars=datfiles.Campaign_ReadCharacterData()
>>> print startchars['el_tanko']
{'powers': {'el_tanko old one-two-three': {'PowerName': 'el_tanko old one-two-three', 'Stun': 1, 'RangeMax': 5, 'SpecialType': 0, 'Speed': 3, 'AttackFlags': 0, 'EPCost': 0, 'RangeMin': 1, 'Radius': 0, 'Magnitude': 3, 'SubType': 0, 'notForCustom': 0, 'animation': 'melee_4', 'DamageType': 0, 'FX': 'rainbow_fist', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 0, 'PowerType': 1}, 'el_tanko fall down': {'PowerName': 'el_tanko fall down', 'Stun': 0, 'RangeMax': 2, 'SpecialType': 32, 'Speed': 0, 'AttackFlags': 0, 'EPCost': 3, 'RangeMin': 0, 'Radius': 0, 'Magnitude': 4, 'SubType': 0, 'notForCustom': 0, 'animation': 'ranged_3', 'DamageType': 0, 'FX': 'generic_direct01', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 2, 'PowerType': 4}, 'el_tanko weakest link': {'PowerName': 'el_tanko weakest link', 'Stun': 2, 'RangeMax': 5, 'SpecialType': 24, 'Speed': 4, 'AttackFlags': 0, 'EPCost': 4, 'RangeMin': 1, 'Radius': 0, 'Magnitude': 4, 'SubType': 0, 'notForCustom': 0, 'animation': 'melee_3', 'DamageType': 4, 'FX': 'krunk', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 0, 'PowerType': 1}, 'el_tanko heal': {'PowerName': 'el_tanko heal', 'Stun': 0, 'RangeMax': 2, 'SpecialType': 18, 'Speed': 1, 'AttackFlags': 0, 'EPCost': 4, 'RangeMin': 0, 'Radius': 0, 'Magnitude': 4, 'SubType': 1, 'notForCustom': 0, 'animation': 'area', 'DamageType': 4, 'FX': 'orcolajet', 'MaxInstances': 0, 'Knockback': 0, 'Accuracy': 3, 'PowerType': 2}, 'el_tanko free will and motion': {'PowerName': 'el_tanko free will and motion', 'AttackModesBlocked': 152, 'DamageTypesBlocked': 1536, 'notForCustom': 0, 'BlockType': 1, 'DefenceFlags': 1, 'Success': 4, 'PowerType': 6}}, 'camp_only': 1, 'charName': 'el_tanko', 'mass': 250.0, 'complex': 155.0, 'movementRadius': 1.0, 'skin': 'standard', 'NIF': 'custom_characters\\male_heavy\\character.nif', 'endurance': 5, 'powerLevels': {'el_tanko old one-two-three': 1, 'el_tanko fall down': 1, 'el_tanko weakest link': 1, 'el_tanko heal': 1, 'el_tanko free will and motion': 1}, 'templateName': 'el_tanko', 'VID': 'OR', 'activeAttributes': 2, 'strength': 5, 'class': 'GAME_OBJ_HERO', 'speed': 6, 'isCustom': 0, 'CSBase': '', 'characterAttributes': ['density control', 'strange visitor', 'superhealer', 'blitzkrieg'], 'tier_b_start': 1, 'tier_a_start': 1, 'objectAttributes': ['class', 'NIF', 'material', 'templateName', 'mass', 'complex', 'skin'], 'attrib_start': 2, 'material': 1.0, 'energy': 4, 'AI': 'CGenericHero', 'alterEgo': '', 'tier_a': ['el_tanko old one-two-three', 'el_tanko fall down'], 'tier_b': ['el_tanko free will and motion', 'el_tanko weakest link', 'el_tanko heal'], 'agility': 7}
And, again in the more human-friendly ShowHero() form, this time showing the powers as well.
>>> datfiles.ShowHero(startchars['el_tanko']) charName : el_tanko isCustom : 0 strength : 5 speed : 6 agility : 7 endurance : 5 energy : 4 VID : OR AI : CGenericHero NIF : custom_characters\male_heavy\character.nif material : 1.0 mass : 250.0 skin : standard alterEgo : attrib_start : 2 activeAttributes : 2 characterAttributes : ['density control', 'strange visitor', 'superhealer', 'blitzkrieg'] objectAttributes : ['class', 'NIF', 'material', 'templateName', 'mass', 'complex', 'skin'] movementRadius : 1.0 class : GAME_OBJ_HERO CSBase : tier_a_start : 1 tier_a : ['el_tanko old one-two-three', 'el_tanko fall down'] tier_b_start : 1 tier_b : ['el_tanko free will and motion', 'el_tanko weakest link', 'el_tanko heal'] powerLevels : {'el_tanko old one-two-three': 1, 'el_tanko fall down': 1, 'el_tanko weakest link': 1, 'el_tanko heal': 1, 'el_tanko free will and motion': 1} complex : 155.0 powers.keys() : ['el_tanko old one-two-three', 'el_tanko fall down', 'el_tanko weakest link', 'el_tanko heal', 'el_tanko free will and motion'] el_tanko old one-two-three: PowerName = el_tanko old one-two-three PowerType = PT_MELEE SubType = PT_ATTACK_SUBTYPE_NONE EPCost = none animation = melee_4 FX = rainbow_fist Magnitude = medium DamageType = PT_DAMAGE_CRUSH Speed = fast Stun = low Knockback = none RangeMin = medium RangeMax = 5 Accuracy = very low Radius = none SpecialType = PT_SPECIAL_NONE MaxInstances = 0 AttackFlags = notForCustom = 0 el_tanko fall down: PowerName = el_tanko fall down PowerType = PT_DIRECT SubType = PT_ATTACK_SUBTYPE_NONE EPCost = very low animation = ranged_3 FX = generic_direct01 Magnitude = high DamageType = PT_DAMAGE_CRUSH Speed = very slow Stun = none Knockback = none RangeMin = short RangeMax = long Accuracy = medium Radius = none SpecialType = PT_SPECIAL_DENSITY_MAXIMIZE MaxInstances = 0 AttackFlags = notForCustom = 0 el_tanko weakest link: PowerName = el_tanko weakest link PowerType = PT_MELEE SubType = PT_ATTACK_SUBTYPE_NONE EPCost = low animation = melee_3 FX = krunk Magnitude = high DamageType = PT_DAMAGE_RADIATION Speed = very fast Stun = medium Knockback = none RangeMin = medium RangeMax = 5 Accuracy = very low Radius = none SpecialType = PT_SPECIAL_ALTER_GENETIC_STRUCTURE MaxInstances = 0 AttackFlags = notForCustom = 0 el_tanko heal: PowerName = el_tanko heal PowerType = PT_RANGED SubType = PT_ATTACK_SUBTYPE_BEAM EPCost = low animation = area FX = orcolajet Magnitude = high DamageType = PT_DAMAGE_RADIATION Speed = slow Stun = none Knockback = none RangeMin = short RangeMax = long Accuracy = high Radius = none SpecialType = PT_SPECIAL_LIFE_GIFT MaxInstances = 0 AttackFlags = notForCustom = 0 el_tanko free will and motion: PowerName = el_tanko free will and motion PowerType = PT_PASSIVE_DEFENCE BlockType = PT_BLOCK_TYPE_NORMAL DamageTypesBlocked = PT_DAMAGE_BLOCKED_MENTAL PT_DAMAGE_BLOCKED_MYSTICAL AttackModesBlocked = PT_AREA_BLOCKED PT_SPECIAL_BLOCKED PT_DIRECT_BLOCKED DefenceFlags = PT_DEFENCE_FLAG_INACTIVE Success = PT_BLOCK_SUCCESS_ALWAYS notForCustom = 0
As is clear, the entries are very similar, which is the idea.
Finally, a custom hero read in with Campaign_ReadHeroFile():
>>> wildfire=datfiles.Campaign_ReadHeroFile('wildfire .hero') >>> datfiles.ShowHero(wildfire) name : wildfire charName : isCustom : 1 BaseTemplateName : generic_hero strength : 4 speed : 5 agility : 3 endurance : 5 energy : 5 VID : MB AI : CGenericHero NIF : custom_characters\male_basic_wildfire\character.nif material : 5.0 mass : 80.0 skin : wildfire alterEgo : activeAttributes : 5 characterAttributes : ['dispersed', 'flier', 'invulnerable', 'light speed', 'grim resolve'] objectAttributes : ['mass', 'material', 'complex'] movementRadius : 1.4573504029e-043 class : GAME_OBJ_HERO CSBase : XP : 0 CP : 0 powerLevels : {'annihilation ': 1, 'energy blast': 1, 'punch': 1} complex : 1099.0 powers.keys() : ['annihilation ', 'energy blast', 'punch'] annihilation : PowerName = annihilation PowerType = PT_AREA SubType = PT_ATTACK_SUBTYPE_NONE EPCost = medium animation = area FX = evilmale_annhilate Magnitude = extreme DamageType = PT_DAMAGE_ENERGY Speed = normal Stun = none Knockback = high RangeMin = short RangeMax = medium Accuracy = very low Radius = large SpecialType = PT_SPECIAL_NONE MaxInstances = 0 AttackFlags = notForCustom = 0 energy blast: PowerName = energy blast PowerType = PT_RANGED SubType = PT_ATTACK_SUBTYPE_BEAM EPCost = very low animation = ranged_4 FX = pulse_bolts_beam2 Magnitude = high DamageType = PT_DAMAGE_ENERGY Speed = fast Stun = medium Knockback = none RangeMin = short RangeMax = long Accuracy = high Radius = none SpecialType = PT_SPECIAL_NONE MaxInstances = 0 AttackFlags = notForCustom = 0 punch: PowerName = punch PowerType = PT_MELEE SubType = PT_ATTACK_SUBTYPE_NONE EPCost = trace animation = melee FX = default_punch Magnitude = low DamageType = PT_DAMAGE_CRUSH Speed = fast Stun = low Knockback = none RangeMin = medium RangeMax = long Accuracy = very low Radius = none SpecialType = PT_SPECIAL_NONE MaxInstances = 0 AttackFlags = notForCustom = 0
First, for these functions to work, there must actually be an objects.dat, characters.dat, powers.dat, and so on files unzipped in the right spots, either in the mod folder for whatever mission is running or where specified by FileName. There is a facility in FFvsT3R to unzip these files from a python script, but it is unavailable in the first game.
I wrote this module with an eye on keeping delays minimal. Because of that, each of these functions tries to avoid re-reading the DAT, hero, or saved game files every time it is called and that should keep disk I/O pretty low. The first read, however, isn't necessarily quick. On my laptop machine, a full Campaign_ReadCharacterData() read of the three principle DAT files from the Strangers campaign (which has pretty big DAT files), a saved game file and a hero file all took about 1.25 seconds, en toto. Subsequent calls are much faster (under a millisecond). Because of this initial delay, if these functions are to be called during a mission, it is a good idea to put a call to Campaign_ReadCharacterData() and Campaign_ReadCharactersFromSavedGame() in onPostInit() right after the cut-scene, so that the file is read in the background.
Finally, I didn't fully decode every byte of the DAT files. There was certain information that I wanted to extract and I tried to ignore the rest. However, that means that there may be parts of a DAT file that confuse the reader functions, though I have tested each of these functions at least on the main campaign DATs. If you have a working DAT file (everything looks fine in FFEdit) with which these functions do not work, PM me and I will check into it.