Daggerfall Mod:MAPS.BSA

The UESPWiki – Your source for The Elder Scrolls since 1995
Jump to: navigation, search

MAPS.BSA is a Name Record (0x0100) BSA file containing the overland data for 62 regions of the game. The file contains 4 different record types. Each described region has one of each record type associated with it, for a total of 248 distinct records within the file. Within each of the region's associated records, all navigable settlements, dungeons, shrines, etc are listed and described.

General File Layout[edit]

Each described region has 4 records associated with it. Records are all named in 7-bit ASCII string DOS8.3 format. The 3 character record extension is the ASCII representation of the region's decimal number, with leading 0's. E.g., Daggerfall (0x11) is identified by records ending with "017", such as "MAPPITEM.017". The leading 8 characters indicate the specific type of record:

MapNames
These records simply lists the names of all navigable locations within the region, stored as an array of 7-bit ASCII strings.
MapTable
These records describe the coarse GPS data, and type of the navigable locations within the region.
MapPItem
These records describe the exterior of each navigable location within the region.
MapDItem
These records describe the dungeon interiors of each navigable location within the region.

E.g., "MAPNAMES.005" is the list of the names of all navigable places within Dwynnen.

Location Name Record (MapNames)[edit]

This type of record contains the count of all navigable locations within the record's associated region, immediately followed by the names of all navigable locations within the record's associated region.

The first four bytes are the count of name elements as an UInt32, referred to as LocationCount. This record is variably sized, but must always be 4 + ( 32 * LocationCount ) bytes long.

Immediately following this count are the actual names of all the navigable locations for the record's associated region, each 32 characters wide, stored as a null-terminated, 7-bit ASCII string. There are exactly LocationCount elements.

Location GPS Record (MapTable)[edit]

These records report the GPS location data, as well as the type of the described location, for each navigable location within the record's associated region. Each record consists of a contiguous list of MapTable structures. Each MapTable structure is 17 bytes long in size.

There are exactly MapNames.LocationCount elements in the contained list, and each indexed record is the same location as in the map names.

MapTable Element structure
Offset Type Name Description
0-3 Int32 MapId This numeric ID is used to associate values MapTable elements with LocationRecordElement elements. Each LocationExterior value must be described by a MapTable value. The first 20 bits of this field matches the first 20 bits of a LocationExterior.ExteriorData.MapId value; Simply match ( LocationExterior.ExteriorData.MapId & 0x000fffff ) with ( MapTable.MapId & 0x000fffff ). Those values must be stored in increasing order.

The upper bits contain the location index. It must correspond to a position of MapTable element in the array.

4-7 UInt32 LatitudeType This field is actually the combination of the location's Latitude and location type. Decoding instructions are below.
8-11 UInt32 LongitudeType The location's Longitude (see below).
12 UInt8 Flavor If this location is one of four kinds of dungeons, this field controls its discovery message (add 500 to obtain the string index). Otherwise, unknown.
13-16 UInt32 Services A bitfield representing which guilds, temples, and shops found in the location.
Decoding the LatitudeType field
bits Field Code
0-24 Latitude LatitudeType & 0x1ffffff
25-29 Type (LatitudeType >> 25) & 0x1f
30 Discovered LatitudeType & 0x40000000
31 Hidden LatitudeType & 0x80000000
Decoding the LongitudeType field
bits Field Code
0-23 Longitude LongitudeType & 0xffffff
24-27 Width (LongitudeType >> 24) & 0xf
28-31 Height LongitudeType >> 28

Width and height are given in 4096 units.

Location Item Records[edit]

Basic Structure[edit]

The remaining two types of records (MapPItem and MapDItem) each use a common base format for their root data, described here. Each record consists of a contiguous list of the data for all the navigable locations for that record's associated region. Since not all locations have dungeons, Location Exterior Data Records may contain more elements than Dungeon Interior Data Records.

The base format, LocationRecordElement, is variably-sized. It consists of a DoorHeader, immediately followed by a contiguous list of zero or more Door elements, immediately followed by a LocationRecordElementHeader with its ObjectHeader.

LocationRecordElement coarse structure
DoorHeader
*Door
ObjectHeader
LocationRecordElementHeader

DoorHeader[edit]

DoorHeader structure
Offset Type Name Description
0-3 UInt32 DoorCount The count of Door elements which immediately follow this header as a contiguous list.

Door[edit]

Immediately following the DoorHeader is a contiguous list of Door elements, DoorCount in count, each 6 bytes long.

Door structure
Offset Type Name Description
0-1 UInt16 BuildingDataIndex This is a valid index to an element within the LocationExterior.BuildingData array, or 0xffff which indicates this Door is not associated with an element of the BuildingData array. This places a maximum count of BuildingData elements at 0x7fff.
2 UInt8 NullValue This value is always 0x00.
3 UInt8 Mask This value is a mask of some type. The specific purpose is presently unknown.
4 UInt8 Unknown1 Unknown Purpose
5 UInt8 Unknown2 Unknown Purpose

ObjectHeader[edit]

The ObjectHeader immediately follows the contiguous list of Door elements. The ObjectHeader structure is 71 bytes long, it is the standard Daggerfall object header.

ObjectHeader structure
Offset Type Name Description
0 UInt8 AlwaysOne1 This value is always 1 for all records.
1-6 UInt16[ 3 ] NullValue1 These values are always 0x0000 for all records.
7-10 Int32 Latitude This is the Longitude (east-west axis) for the location in game world units. It's 256 times the longitude plus 0-255.
11-14 UInt32 NullValue2 This value is always 0x00000000 for all records.
15-18 Int32 Longitude This is the Latitude (north-south axis) for the location in game world units. It's 256 times the latitude plus 0-255.
19-20 UInt16 IsExterior This value is an enumeration. Since there are only two valid values (described below), this may be treated as a Bool16.
21-22 UInt16 NullValue3 This value is always 0x0000 for all records.
23-24 UInt16 DungeonType For MapPItems which have dungeons this is the type of dungeon. Otherwise 0.
25-26 UInt16 UsuallyHalfDoorCount For MapDItems which have doors this is half the DoorCount. For MapDItems without doors, this is 1. For MapPItems this is 0.
27-30 UInt32 Index Index of the MapDItem or MapPItem in it's list for the given region. Due to the separate lists for MapDItem and MapPItem, a matching MapDItem and MapPItem pair's index will usually not match.
31-32 UInt16 AlwaysOne2 This value is always 1 for all records.
33-34 UInt16 LocationNum Appears to be order locations were created in. For non-unique locations this increases left to right, top to bottom. For unique locations this is a small amount over 50000, except for your ship(s), which are 990 & 992.
35-38 UInt32 NullValue4 Always 0x00000000 for all records.
39-40 UInt16 HasParent For MapDItem records: always 1. For MapPItem records: always 0.
41-42 UInt16 ParentLocationNum For MapDItem records: this is the LocationNum of the parent. For MapPItem records: always 0.
43-70 UInt32[ 7 ] Pointers Reserved, set to 0.

IsExterior[edit]

IsExterior field enumeration
Value Description
0x0000 The LocationRecordElementHeader describes dungeon interior data.
0x8000 The LocationRecordElementHeader describes location exterior data.

LocationRecordElementHeader[edit]

The LocationRecordElementHeader immediately follows the ObjectHeader. Each LocationRecordElementHeader structure is 48 bytes long.

LocationRecordElementHeader structure
Offset Type Name Description
0-31 7-bit ASCII string LocationName Reports the NULL-terminated name of the location, but may not always exactly match the values in the MapNames record for the region. This value is used when displaying the name of a location when entering it. The fast travel map uses the MapNames record for display.
32 UInt8 Width The width of the location, in blocks
33 UInt8 Height The height of the location, in blocks.
34 UInt8 LocationType The same location type value as in MapTable.LongitudeType
35-36 UInt16 NullValue1 Always 0x00000000 for all records.
37-38 UInt16 LastID For PItems this is the BuildingID that the building after the last building would have, if it existed.

For DItems this is unknown, but seems to be correlated with dungeon size.

39 UInt8 SpecialDoorCount The number of doors with a mask value of 16 or 64.
40 UInt16 Constant Always 250.
41-42 UInt16 BuildingCount This is the count of BuildingData structures which immediately follow this header, as a contiguous list. This field must be less-than-or-equal-to 0x7fff.

In dungeon records, this is always 0.

43-46 UInt32 Interiors Reserved.
47 UInt8 NullValue2 Always 0x00000000 for all records.

After the LocationRecordElementHeader, data specific to the record type (MapPItem or MapDItem) follows.

Location Exterior Data Record (MapPItem)[edit]

Each MapPItem record begins with a list of UInt32 values with MapNames.LocationCount elements. This is a list of offsets to the LocationExterior structures for each location. These values are relative to the end of the list, so adding (LocationCount * 4) to the offset locates the actual record. One offset may be greater than a subsequent one; no data order is presumed.

At each offset indicated, a LocationExterior structure is present, which includes all fields from the LocationRecordElement and some data of its own.

LocationExterior coarse structure
LocationRecordElement
*BuildingData
ExteriorData

BuildingData[edit]

Immediately following the LocationRecordElement, is a contiguous list of BuildingData structures. Each BuildingData is 26 bytes long. BuildingData structures are associated with buildings within a settlement, such as a tavern.

BuildingData structure
Offset Type Name Description
0-1 UInt16 NameSeed This field is used as a seed value for generating the building's name.
2-9 UInt64 NullValue1 Should always be 0x0000000000000000.
10-17 UInt64 NullValue2 Should always be 0x0000000000000000.
18-19 UInt16 FactionId This is the FactionId which should be associated with this building, or 0x0000 if no faction should be associated with this building. Valid values for this field are defined by the FACTION.TXT file.
20-21 UInt16 LocationNum LocationNum of the parent location.
22-23 UInt16 BuildingID An ID of the building. As you go through a location's building list, these increment. In ideal scenarios, e.g. 1 block locations, these increment at: 2 + doorCount + personCount + monsterMarkerCount + randomTreasureMarkerCount. With multi block locations this is not usually the case.
24 UInt8 BuildingType This indicates the type of the building. Valid values are defined in the Building Type Code enumeration.
25 UInt8 Quality This specifies the quality of the building, always non-zero and ranges from 1 to 0x14 (20). Divide by 4 to rate on the "rusty-relics…" through "incense burning…" scale.

ExteriorData[edit]

ExteriorData structure
Offset Type Name Description
0-31 7-bit ASCII string UsuallyName For most locations this is the name for the location. The exception is Taverns, for which this is a number (as ASCII text) which matches the NameSeed of the first Tavern building in the location. Changing the value has no visible effect on the game.
32-35 Int32 MapId Since each LocationExterior value must be described by a MapTable value, this field must correspond to a MapTable.LocationId value. This field matches the first 20 bits of a MapTable.MapId value; Simply match this with ( MapTable.MapId & 0x000fffff ).

This is equal to mapX + mapY * mapWidth.

mapx = floor(x / 2^15), where x is longitude in Daggerfall units.

mapY = floor(mapHeight - y / 2^15), where y is latitude in Daggerfall units, and mapHeight is the height of WOODS.WLD, POLITIC.PAK, and CLIMATE.PAK.

mapWidth = width of WOODS.WLD, width of POLITIC.PAK - 1, and width of CLIMATE.PAK - 1.

36-39 UInt32 HandcraftedLocationNum Usually 0. All exceptions to this appear to be handcrafted locations.

The following locations have a non zero value, which matches the LocationRecordElement.LocationNum value: The Alcaire Coven, The Beldama Coven, Castle Llugwych, The Coven in the Marsh, The Coven of Dust, The Coven of the Peaks, The Coven of the Tide, The Coven on the Bluff, Daggerfall, The Daggerfall Coven, The Devilrock Coven, Direnni Tower, The Glenmorial Coven, The Kykos Coven, Lysandus' Tomb, Mantellan Crux, Orsinium, Privateer's Hold, Scourg Barrow, Sentinel, Shedungent, The Skeffington Coven, The Tamarilyn Coven, Wayrest, Woodborne Hall, The Wroth Coven, Your Ship(s)

40 UInt8 Width The width of the exterior data. This value may not be 0, and must be less-than-or-equal-to 8.
41 UInt8 Height The height of the exterior data. This value may not be 0, and must be less-than-or-equal-to 8.
42 UInt8 WidthProduct This value is usually equal to: 64 - Width * 8. Unknown Purpose. Theory: Related to a location's position within its map pixel.
43 UInt8 HeightProduct This value is usually equal to: 63 + Height * 8. Unknown Purpose. Theory: Related to a location's position within its map pixel.
44 UInt8 HasDungeon 1 if the location has a MapDItem associated with it, 0 if it doesn't.
45 UInt8 RegionIndex The index of the region the location is in.
46 char Letter A letter used in calculation of RMB block names.
47 UInt8 Port Non zero if it is a port town that sells ships.
48 UInt8 LocationType The same location type value as in MapTable.LongitudeType.
49-112 UInt8[ 64 ] BlockIndex Only the first Width * Height elements will have any meaning. See decoding instructions below.
113-176 UInt8[ 64 ] BlockNumber Only the first Width * Height elements will have any meaning. See decoding instructions below.
177-240 UInt8[ 64 ] BlockCharacter Only the first Width * Height elements will have any meaning. See decoding instructions below.
241-272 char[ 32 ] DungeonName The name of the dungeon associated with this location. All the following dungeon fields are zeroed if there is no dungeon.
273 UInt8 Encounters An index into the encounter table for this dungeon.
274 UInt8 BlockCount The number of blocks in the dungeon.
275-283 UInt8[ 9 ] NullValue1 Always 0. The only exception is Warborne in the Betony Demo for which all bytes are 0xFF.
284-412 DungeonBlock[ 32 ] Blocks Blocks of the dungeon associated with this location. Only BlockCount first elements are valid.
412-415 UInt32 Services The same value as MapTable.Services.

The following exceptions have WidthProduct and HeightProduct values of 72: Coven in the Marsh, Devilrock Coven, Scourg Barrow, Coven of the Peaks, Direnni Tower, Kykos Coven, Shedungent, Wroth Coven, Privateer's Hold, Coven on the Bluff, Woodborne Hall, Orsinium, Mantellan Crux, Your Ship(s), Tamarilyn Coven, Lysandus' Tomb, Alcaire Coven, Skeffington Coven, Castle Llugwych, Beldama Coven, Daggerfall Coven, Coven of the Tide, Coven of Dust, Glenmorial Coven

Interpreting the Block Data arrays[edit]

The BlockIndex, BlockNumber, and BlockCharacter arrays all have a fixed size of 64 elements, but only the first Width * Height elements are used; the remainder are ignored. All 64 elements must be read just the same. These values are used to load the appropriate records from the BLOCKS.BSA file, which contains the automap data and are in turn used to load the appropriate meshes and models from ARCH3D.BSA to actually display the scene.

When accessing the block arrays, they should be synchronized. This is to say BlockIndex[ 5 ] should refer to the same Block record as BlockCharacter[ 5 ] and BlockNumber[ 5 ]. If one adds, removes, or reorders any array then one must reorder both of the other arrays to keep them synchronized.

The arrays are stored in west-to-east, south-to-north order. This means index 0 refers to the southwest corner of the location, and index ( Width * Height ) refers to the northeast corner of the location. Locations are not required to be squares (Width is not required to equal Height).

The details of the logic are described in Block Record Indexes.

Dungeon Interior Data Record (MapDItem)[edit]

Each MapDItem record begins with an UInt32 count (DungeonCount) of DungeonInterior elements contained within the record.

Dungeon Offset Section[edit]

Immediately following the DungeonCount field is a contiguous list of DungeonOffset values. Each value is 8 bytes long. The elements are not required to be in any particular order; That is to say the first element's offset could be higher than the second element's offset.

DungeonOffset structure
Offset Type Name Description
0-3 UInt32 Offset These offsets refer to the DungeonInterior elements of the record, and all offsets are relative to the end of the offset list.
4-7 UInt32 DungeonObjId This value is calculated as (LocationExterior.LocationRecordElement.LocationId << 16) + 1.

Dungeon Record Data[edit]

Each DungeonOffset.Offset refers to a DungeonInterior element. This is also a composite structure, similar to the LocationExterior structure.

DungeonInterior coarse structure
LocationRecordElement
DungeonHeader
+DungeonBlock
?Padding
DungeonHeader[edit]

Immediately following the LocationRecordElement structure is a DungeonHeader structure.

DungeonHeader structure
Offset Type Name Description
0-2 UInt8[3] Unknown2 Unknown Purpose.
3-4 UInt16 BlockCount This is the count of the DungeonBlock elements in the contiguous list which follows. This value must be greater than 0 and less-than-or-equal-to 32.
5-9 UInt8[ 5 ] Unknown3 Unknown Purpose.

Immediately following the DungeonHeader is a contiguous list of DungeonBlock structures, each 4 bytes long. There are BlockCount elements in this list.

DungeonBlock Element[edit]

Each DungeonInterior structure contains a contiguous list of DungeonBlock structures. There are exactly DungeonHeader.BlockCount elements in this list.

DungeonBlock structure
Offset Type Name Description
0 UInt8 X The X coordinate of the block.
1 UInt8 Z The Z coordinate of the block.
2-3 UInt16 BlockNumberStartIndex This is a concatenation of the BlockIndex and BlockNumber values. Decoding instructions follow.

The X and Z fields indicate how the blocks should be arranged in a two-dimensional grid. Dungeons are not required to be uniform such as squares or rectangles. They can contain empty spaces, or have spurs.

E.g., Privateer's Hold
           B0000012
 B0000009 @S0000999  B0000003
           B0000006
E.g., Mantellan Crux
 S0000005  S0000006
           S0000004  S0000003  S0000002  S0000001 @S0000000
Decoding BlockNumberStartIndex
bits Type Name Description
0-9 UInt16 BlockNumber This value is used for decoding the string name of the RDB record from BLOCKS.BSA. As of the current patch, bits 8-9 are only necessary for Privateer's Hold (999); all other blocks require only bits 0-7.
10 Bool IsStartingBlock This bit is set (1) if this block is the "entrance" block. This is the block where the player starts when entering a dungeon. This bit is reset (0) otherwise. All DungeonInterior records must have one and only one DungeonBlock element with this bit set (1).
11-15 UInt8 BlockIndex This value is used for decoding the string name of the RDB record from BLOCKS.BSA. Valid values are described in Block Record Indexes.
Padding[edit]

Immediately following the last DungeonBlock element are ( 128 - ( BlockCount * 4 ) ) bytes of padding. These values are simply padding and can be ignored. This gives each Dungeon a maximum of 32 DungeonBlock elements.