Daggerfall Mod:SaveTree.Dat
Contents
Fundamental Data Types[edit]
DateTime[edit]
A Daggerfall DateTime structure is a UInt32 value, indicating the count of minutes since the epoch's origin. In Daggerfall, the epoch begins at 00:00, 1st of Morning Star, 3E 400. Since the game starts 0x07fd0a minutes into the epoch, all DateTime values should be greater than this number. Given there are 60 minutes per hour, 24 hours per day, 30 days per month, and 12 months per year, computing the absolute date-time from a Daggerfall DateTime is trivial. Ex: 0x00080488 computes to 364.21:28:00 from the epoch origin, or 21:28, 5th of Morning Star, 3E 401.
RecordType enumeration[edit]
The RecordType enumeration is by no means complete. Only those values which are thoroughly understood are described. The RecordType value associated with each Record instance determines how the associated data is to be interpretted, assumedly via a factory design pattern.
Value | Name | Description |
---|---|---|
0x01 | CharacterPostion | The character's position, expressed as a three-dimensional coordinate. |
0x02 | Item | The character's inventory is described, item-for-item, via these types of records. |
0x03 | Character | This fully describes the character's attributes and stats, including modifications from spells, diseases, potions, etc., et al. |
0x04 | CharacterParent | This record is a parent record for the character record. The purpose is not fully understood. |
0x07 | DungeonInformation | This record seems to describe the interior of the current dungeon the character occupies. This record is absent when outside dungeons. When interpreting DungeonInformation records, the recordLength field MUST be multiplied by 39 (0x27) to properly process the record and remaining file. |
0x09 | Spell | These records describe the spells the character can cast from his spell book, or invoke from an enchanted item. |
0x0a | GuildMembership | These records indicate the character's standing and status with the various guilds and orders found within the game. |
0x0e | QuestBinary | Active quests are stored in these records. In this way a given quest template could be active multiple times without ill effect, since they each quest's state data would be separate and private from the other quests. |
0x17 | ControlSetting | The detail, volume, etc, values configurable by the player via the options screen. Includes sight-distance fogging. |
0x19 | BankAccount | The various bank accounts the character has open/outstanding are described in these records. |
0x22 | Mobile1 | The various monsters and mobile town citizens are described in these records. |
0x2c | Mobile2 | The various monsters and mobile town citizens are described in these records. |
0x34 | Container | The containment hierarchy of how/where all the Item records are stored are described via these records. |
0x36 | Repair | Items left at a shop for repair are described via these records. |
RecordRoot structure[edit]
Not all fields are known, but the following appear to be uniform and consistent.
Offset | Type | Name | Description |
---|---|---|---|
6 | Position | Position | The position (if applicable) of the record, as it exists within the game world as a 3d point. |
30 | UInt32 | RecordId | A 32-bit, unsigned integer, serving as a unique identifier for this record. |
37 | UInt8 | QuestId | Identifies to which quest this record should be associated, or 0x00 if the record should not be associated with any quest. |
38 | UInt32 | ParentRecordId | The RecordId of the record to which ought to contain this record, or to which this record is to be considered subordinate. |
66 | Int32 | ParentRecordType | This field indicates the type of the records parent. The values of this field must conform to the values of the RecordType enumeration, and are stored as a 32-bit integer. |
Position structure[edit]
Offset | Type | Name | Description |
---|---|---|---|
0-3 | Int32 | X | The longitude (X-axis) of the object in the game world. |
4-5 | UInt16 | YOffset | The altitude (Y-axis) offset of the object in the game world, from some reference altitude. |
6-7 | UInt16 | YBase | This value appears to always be 0xFF, and behaves as the reference altitude from which the YOffset field should be interpretted. |
8-11 | Int32 | Z | The object's longitude (Z-axis) within the game world. |
The object's true altitude within the game world appears to be computed via YBase - YOffset
, plus the baseAltitude value of the current Woods.Wld map pixel.
SaveTree.Dat coarse structure[edit]
Header |
---|
LocationDetail |
RecordElement+ |
Header structure[edit]
See File Header.
LocationDetail structure[edit]
Immediately following the Header structure, is a variably-sized LocationDetail strcuture. If the character is within a town or dungeon, this structure will contain one or more records specific to that town or dungeon. If the character is overland, there will typically either be no records, or only empty (zero-length) records.
Offset | Type | Name | Description |
---|---|---|---|
0 | Int8 | LocationDetailCount | The count of RecordElement structures which immediately follow. |
1… | RecordElement[ locationDetailCount ] | Record | The records which describe the dungeon or town immediately occupied by the character. |
RecordElement structure[edit]
Offset | Type | Name | Description |
---|---|---|---|
0-3 | Int32 | RecordLength | The length of the following Record structure. If the recordLength field is 0, then there is no following Record structure. |
4… | Byte[ recordLength ] | Data | The actual data of the Record. This field is not present if the recordLength field reports 0. |
RecordBase structure[edit]
Most records within the SaveTree.Dat file inherit from the RecordBase template. The first byte of the RecordElement.Data field is a RecordType value which indicates how the following data should be interpreted and parsed. There are very few exceptions to this format. Notably the records found within the LocationDetail do not follow this pattern, and DungeonInformation records (RecordType 0x07) misreport their size; one must multiply the recordSize field by 39 to determine the true size of a DungeonInformation record.
Offset | Type | Name | Description |
---|---|---|---|
0 | RecordType | RecordType | Describes how the following data should be interpreted. |
1-70 | RecordRoot | RecordRoot | Basic, fundamental data common to almost all records. |
71-(recordLength - 71) | Byte[ recordLength - 71 ] | RecordData | The data payload for this specific record. The recordType field implies how this binary data is to be interpretted. |
Record structures[edit]
The following records document how the RecordData field is to be interpreted, based on the RecordType field. Offsets are given from the beginning of the RecordData field, which immediately follows the RecordRoot structure.
BankAccount[edit]
The character's banking records are stored within a BankAccount record, one per character. This single record contains 62 entries, one AccountInformation structure for each of the 62 regions within the Iliac Bay.
AccountInformation[edit]
Offset | Type | Name | Description |
---|---|---|---|
0 | Int32 | Balance | The character's balance in that region's bank. |
4 | Int32 | Debt | The character's currently outstanding debt with that region's bank; the amount of any loan plus any interest. |
8 | DateTime | DatePayable | The loan schedule, if the Debt field is not zero. |
12 | UInt8 | Default | Whether the player defaulted on their loan. |
BankAccount structure[edit]
Offset | Type | Name | Description |
---|---|---|---|
0 | AccountInformation[ 62 ] | AccountInformation | The character's banking information is stored as a contiguous list of AccountInformation structures, indexed by region number. Ex: Ykalon banking information is the 41st element in the list. |
Working with SaveTree.Dat[edit]
After reading all the records, one must then reconstruct the hierarchy of association and/or containment. Root records are identified as having no parent (ParentRecordId is 0). Subordinate records must then matched up with their superior records, such as the character's inventory must be grouped with the various container records, which are then grouped with the character's character record, which is itself subordinate to another record.