Tes5Mod:Mod File Format

Mod files (Plugin files) are collections of records, which are further divided into fields.

Records generally correspond to objects (e.g., a creature, a game setting, a dialog entry), with the fine details of the object (e.g., health of a creature, a dialog entry test) being handled by the fields of the record. Records themselves are organized into groups.

At the highest grouping level, a plugin file is generally:
A single TES4 record (plugin information).
A collection of top groups.

 
Record Types

|style="width:25%" |

|style="width:25%" |

|style="width:25%" |

|}

Groups
GRUPs are collections of records, and are used to improve scanning of files to make it easier to skip over records that the reading program is not interested in. In addition to this, subgroups for WRLD and CELLS provide some useful structural information (e.g., the division of cell data into persistent and non-persistent references).

File Format

|-
| uint16 || Timestamp
Skyrim: The low byte is the day of the month and the high byte is a combined value representing the month number and last digit of the year times 12. That value is offset, however, so the range is nominally 13-132, representing dates from January 20x4 through December 20x3. Lower values can be seen in Skyrim.esm, likely corresponding to older records held over from Oblivion where values of 1-12 represented 2003 (see the Oblivion version of this page for specifics).   To derive the correct values, use the following formulae, where Y is the single-digit year, M is the month number, and HB is the high byte of the value:
: 

Skyrim SE: Bits are used to represent each part, with a two-digit year: 0bYYYYYYYMMMMDDDDD. Thus, January 25, 2021 would be (spaces added for clarity): 0b 0010101 0001 11001 or 0x2A39.
|-
| uint16 || Version Control Info
The low byte is the user id that last had the form checked out.
The high byte is the user id (if any) that currently has the form checked out.
|-
| uint32 || Unknown. The values stored here are significantly different than those used in records and appear to be a 32-bit value rather than two 16-bit values. Values break out by group type:
All top-level groups have a 0 here, with the exception of CELL, which can have a 1 in some add-ons.
Topic children have single-byte values or 0xCCCCCCCC.
Interior cells have a value of either 0 or 0xCCCCCCCC. Some add-ons have a value of 1.
Other cell/world-related groups have a wide array of small to large values that are not Form IDs. Again, 0xCCCCCCCC is a possible value.
|-
| uint8[groupSize-24]
| Records and subgroups.
|}

Top Groups
In Skyrim.esm, the top, or highest level groups are stored in the following order:

:GMST, KYWD, LCRT, AACT, TXST, GLOB, CLAS, FACT, HDPT, HAIR, EYES, RACE, SOUN, ASPC, MGEF, SCPT, LTEX, ENCH, SPEL, SCRL, ACTI, TACT, ARMO, BOOK, CONT, DOOR, INGR, LIGH, MISC, APPA, STAT, SCOL, MSTT, PWAT, GRAS, TREE, CLDC, FLOR, FURN, WEAP, AMMO, NPC_, LVLN, KEYM, ALCH, IDLM, COBJ, PROJ, HAZD, SLGM, LVLI, WTHR, CLMT, SPGD, RFCT, REGN, NAVI, CELL, WRLD, DIAL, QUST, IDLE, PACK, CSTY, LSCR, LVSP, ANIO, WATR, EFSH, EXPL, DEBR, IMGS, IMAD, FLST, PERK, BPTD, ADDN, AVIF, CAMS, CPTH, VTYP, MATT, IPCT, IPDS, ARMA, ECZN, LCTN, MESG, RGDL, DOBJ, LGTM, MUSC, FSTP, FSTS, SMBN, SMQN, SMEN, DLBR, MUST, DLVW, WOOP, SHOU, EQUP, RELA, SCEN, ASTP, OTFT, ARTO, MATO, MOVT, HAZD, SNDR, DUAL, SNCT, SOPM, COLL, CLFM, REVB

Whether the game engine expects this order is unknown, but it's probably safer to use this order than not.

All top groups contain records matching their label (e.g., the GMST top group contains GMST records). For most top groups, only the matching record types are present. However, in the CELL, WRLD and DIAL top groups, each main record can be followed by one or more child groups which contain additional records of a different type. Unlike Oblivion, WRLD groups in Skyrim no longer contain ROAD records.

Notes:
6 record types have a GRUP but no records: CLDC, HAIR, RGDL, SCPT, SCOL, PWAT. 
There are 2 HAZD groups, the second one has no records.

Dawnguard
The following had field order changes and updates. Also pre-existing or new records were added.
LCTN, INFO, NPC_, RACE, NAVI, QUST, REFR, WRLD

New Fields:
LCTN added: ACID, ACSR, ACUN, ACEC, ACEP, RCPR
NAVI added: NVSI
REFR added: XATR

Subgroups
The following groups are only found in subgroups of other records:
:REFR, ACHR, NAVM, PGRE, PHZD, LAND, INFO

Records


|-
| uint32 || Record (form) identifier
|-
| uint16 || Timestamp (see the same field in Groups)
|-
| uint16 || Version Control Info (see the same field in Groups)
|-
| uint16 || The internal version of the record. This can be used to distinguish certain records that have different field layouts or sizes.
|-
| uint16|| Unknown. Values range between 0-15.
|-
| uint8[dataSize] || Data
For uncompressed records, this is a sequence of fields. 
Compressed data is the same, except that the fields are compressed using ZLIB, and stored into the data field like so...

|}

Fields

 If the preceding field has the type XXXX, then dataSize will be 0 and the size of the data is in fact the 32 bit quantity stored in the XXXX field. This feature is commonly used to store large navmesh fields in Skyrim.esm.

Commonly used fields and their definitions can be found here. Many records allow for object bounds (OBND) and scripting (VMAD) fields, but ignore them. It's unclear why. Only those records to which they actually apply will have these fields listed.

See Also
Raw Data: a dump of all records/fields in Skyrim.esm and their associated counts/sizes.
Raw Function Data: a dump of all script/console functions and events in TESV.exe.