The UESPWiki – Your source for The Elder Scrolls since 1995
Outdated Tech: While these pages still provide the best public documentation of the Oblivion file formats, all mod makers should also be using
Tes4View, which currently provides the most complete and correct understanding of the mod file format in a readily accessible manner.
[edit] Overview
Mod files for CS4 (TES4 Construction Set) are essentially collections of records, which are further divided into subrecords. Records generally correspond to objects in the construction set (e.g., a creature, a GMST 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 subrecords of the record. Records themselves are organized into groups by GRUP records. At the highest grouping level, the TES4 file is simply:
- A single TES4 record
- A collection of top groups.
While CS4 seems to have some flexibility in the ordering and structure of records and groups in the files it reads, it also clearly likes to write files in a more specific ordering, which is described below. If your application is writing a mod file, it is suggested that you follow this preferred format if possible.
For a comparison with Morrowind (TES3) file format, see: Mod File Format/Vs Morrowind.
[edit] Groups
GRUPs are new (compared to Tes3), and seem to have been introduced largely to improve scanning of files, since they make it easier to skip over blocks of 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.)
| Name |
Type/Size |
Info |
| type |
char[4] |
Always "GRUP" |
| groupSize |
ulong |
Size of the entire group, including the group header (20 bytes).
- This is in contrast to records and subrecords, whose sizes does not include their header sizes.
|
| label |
ubyte[4] |
Format depends on group type (see next field).
- In the CS4 Details view, you can mark a group as ignored, but CS4 ignores this setting and reads the group anyway. If you subsequently save, the group will be written without the ignore markings. The ignore flag interferes with what should ordinarily be in the label field. E.g., "HAIR" becomes "HQIR". This mislabeling has no effect on record loading. In short, the label field of a group is not reliable.
|
| groupType |
long |
Group type...
| Type |
Info |
Label |
Label |
| 0 |
Top (Type) |
char[4] |
Record type |
| 1 |
World Children |
formid |
Parent |
| 2 |
Interior Cell Block |
long |
Block number |
| 3 |
Interior Cell Sub-Block |
long |
Sub-block number |
| 4 |
Exterior Cell Block |
ushort[2] |
Grid Y, X (Note the reverse order) |
| 5 |
Exterior Cell Sub-Block |
ushort[2] |
Grid Y, X (Note the reverse order) |
| 6 |
Cell Children |
formid |
Parent |
| 7 |
Topic Children |
formid |
Parent |
| 8 |
Cell Persistent Childen |
formid |
Parent |
| 9 |
Cell Temporary Children |
formid |
Parent |
| 10 |
Cell Visible Distant Children |
formid |
Parent |
|
| stamp |
ulong? |
Unknown. ?Date stamp?
- For a given esp, the stamp is the same for all groups, and seems to increase with date (and possibly time).
- Oblivion.esm stamps vary. Possibly due to a merging process?
|
[edit] Top Groups
In Oblivion.esm, the top, or highest level groups are stored in the following order:
- GMST, GLOB, CLAS, FACT, HAIR, EYES, RACE, SOUN, SKIL, MGEF, SCPT, LTEX, ENCH, SPEL, BSGN, ACTI, APPA, ARMO, BOOK, CLOT, CONT, DOOR, INGR, LIGH, MISC, STAT, GRAS, TREE, FLOR, FURN, WEAP, AMMO, NPC_, CREA, LVLC, SLGM, KEYM, ALCH, SBSP, SGST, LVLI, WTHR, CLMT, REGN, CELL, WRLD, DIAL, QUST, IDLE, PACK, CSTY, LSCR, LVSP, ANIO, WATR, EFSH.
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. Structure and ordering of those are as follows...
[edit] Hierarchical Top Groups
| DIAL Top Group |
|
|
| CELL Top Group |
- Interior Cell Block
- Interior Cell Sub-Block
- CELL
- Cell Childen
- Persistent children
- Visible distant children
- Temp Children
|
| WRLD Top Group |
- WRLD
- World Children
- ROAD
- CELL
- Cell Children
- Persistent Children
- Visible Distant Children
- Temp Children
- Exterior World Block
- Exterior World Sub-block
- CELL
- Cell Childen
- Persistent Children
- Visible Distant Children
- Temp Children
- LAND
- PGRD
- REFR, ACHR, ACRE
|
[edit] Records
| Name |
Type/Size |
Info |
| type |
char[4] |
Record type |
| dataSize |
ulong |
Size of data field. |
| flags1 |
ulong |
Flags...
| Flag |
Meaning |
| 0x00000001 |
ESM file. (TES4.HEDR record only.) |
| 0x00000020 |
Deleted |
| 0x00000200 |
Casts shadows |
| 0x00000400 |
Quest item / Persistent reference |
| 0x00000800 |
Initially disabled |
| 0x00001000 |
Ignored |
| 0x00008000 |
Visible when distant |
| 0x00020000 |
Dangerous / Off limits (Interior cell) |
| 0x00040000 |
Data is compressed |
| 0x00080000 |
Can't wait |
|
| formid |
formid |
Record identifier.
- Tes4 does not have a FormId.
- Some GMST records do not have a FormID.
|
| flags2 |
ulong |
Unknown. Seem to be flags. |
| data |
ubyte[dataSize] |
Data
- For uncompressed records, this is a sequence of subrecords.
- Compressed data is the same, except that the subrecords are compressed using ZLIB level 6, and stored into the data field like so...
| Name |
Type/Size |
Info |
| decompSize |
uint |
Size of decompressed data. |
| compData |
ubyte[dataSize-4] |
Compressed collection of subrecords. |
|
[edit] Subrecords
| Name |
Type/Size |
Info |
| subType |
char[4] |
Subrecord type. |
| dataSize |
ushort |
Size of data field.
- If the size of the subrecord data is too large to fit in a ushort, then this value is set to zero and the actual size is stored as the data [uint] of an immediately preceding subrecord of type 'XXXX'. This happens once in Oblivion.esm.
|
| data |
ubyte[dataSize] |
Data.
- Format depends on record and subrecord type.
|
There are a number of subrecord formats that are appear in several places.
- Variable Length String (terminated)
- Holds a variable length string including the nul (0x00) terminator. This means that an empty string would have a subrecord size of 1. Most variable string subrecords use this type.
- Variable Length String (not terminated)
- Holds a variable length string but does not include the nul (0x00) terminator. Only seen in SCTX subrecords in scripts so far.
- byte/word/dword/int64/float
- Basic data types which are used in a variety of places. The subrecord size is the same size of the native type.