Skyrim Mod:ChangeFlags
Contents
Change Flags[edit]
- ChangeFlags are part of the ChangeForm structure in a savefile.
- It's the uint32 that follows the changeFormId for every changeForm in a savefile.
- The changeFormId may influence the amount of data stored per ChangeFlag.
- ChangeFlags have different meanings per ChangeForm type.
- Sizes may also be different for combinations of ChangeFlags
Bit | Value | Constant Name | Pretty Name (In code) |
---|---|---|---|
All records | |||
0 | 0x00000001 | CHANGE_FORM_FLAGS | Flags |
CLAS | |||
1 | 0x00000002 | CHANGE_CLASS_TAG_SKILLS | Tag Skills |
FACT | |||
1 | 0x00000002 | CHANGE_FACTION_FLAGS | Faction Flags |
2 | 0x00000004 | CHANGE_FACTION_REACTIONS | Faction Reactions |
31 | 0x80000000 | CHANGE_FACTION_CRIME_COUNTS | Faction Crime Counts |
ACTI | |||
23 | 0x00800000 | CHANGE_TALKING_ACTIVATOR_SPEAKER | Speaker |
BOOK | |||
5 | 0x00000020 | CHANGE_BOOK_TEACHES | Teaches Skill |
6 | 0x00000040 | CHANGE_BOOK_READ | Read |
DOOR | |||
! | The instructions for this flag appear to be unreachable in SkyrimSE 1.5.97 | ||
17 | 0x00020000 | CHANGE_DOOR_EXTRA_TELEPORT | Teleport Extra |
INGR | |||
31 | 0x80000000 | CHANGE_INGREDIENT_USE | Ingredient Use |
NPC_ | |||
1 | 0x00000002 | CHANGE_ACTOR_BASE_DATA | Base Data |
2 | 0x00000004 | CHANGE_ACTOR_BASE_ATTRIBUTES | Attributes |
3 | 0x00000008 | CHANGE_ACTOR_BASE_AIDATA | AI Data |
4 | 0x00000010 | CHANGE_ACTOR_BASE_SPELLLIST | Spell List |
5 | 0x00000020 | CHANGE_ACTOR_BASE_FULLNAME | Full Name |
6 | 0x00000040 | CHANGE_ACTOR_BASE_FACTIONS | Factions |
9 | 0x00000200 | CHANGE_NPC_SKILLS | NPC Skills |
10 | 0x00000400 | CHANGE_NPC_CLASS | Class |
11 | 0x00000800 | CHANGE_NPC_FACE | Face |
12 | 0x00001000 | CHANGE_NPC_DEFAULT_OUTFIT | Default Outfit |
13 | 0x00002000 | CHANGE_NPC_SLEEP_OUTFIT | Sleep Outfit |
24 | 0x01000000 | CHANGE_NPC_GENDER | Gender |
25 | 0x02000000 | CHANGE_NPC_RACE | Race |
LVLN, LVLI, LVSP | |||
31 | 0x80000000 | CHANGE_LEVELED_LIST_ADDED_OBJECT | Added Object |
NOTE | |||
31 | 0x80000000 | CHANGE_NOTE_READ | Note Read |
CELL | |||
1 | 0x00000002 | CHANGE_CELL_FLAGS | Flags |
2 | 0x00000004 | CHANGE_CELL_FULLNAME | Full name |
3 | 0x00000008 | CHANGE_CELL_OWNERSHIP | Ownership |
28 | 0x10000000 | CHANGE_CELL_EXTERIOR_SHORT | Exterior Short |
29 | 0x20000000 | CHANGE_CELL_EXTERIOR_CHAR | Exterior Char |
30 | 0x40000000 | CHANGE_CELL_DETACHTIME | Detach Time |
31 | 0x80000000 | CHANGE_CELL_SEENDATA | Seen Data |
REFR, ACHR, PMIS, PARW, PGRE, PBEA, PFLA, PCON, PBAR, PHZD | |||
1 | 0x00000002 | CHANGE_REFR_MOVE | Moved |
2 | 0x00000004 | CHANGE_REFR_HAVOK_MOVE | Havok Moved |
3 | 0x00000008 | CHANGE_REFR_CELL_CHANGED | Cell Changed |
4 | 0x00000010 | CHANGE_REFR_SCALE | Scale |
5 | 0x00000020 | CHANGE_REFR_INVENTORY | Inventory |
6 | 0x00000040 | CHANGE_REFR_EXTRA_OWNERSHIP | Ownership Extra |
7 | 0x00000080 | CHANGE_REFR_BASEOBJECT | BaseObject |
25 | 0x02000000 | CHANGE_REFR_PROMOTED | Promoted |
26 | 0x04000000 | CHANGE_REFR_EXTRA_ACTIVATING_CHILDREN | Activating Children |
27 | 0x08000000 | CHANGE_REFR_LEVELED_INVENTORY | Leveled Inventory |
28 | 0x10000000 | CHANGE_REFR_ANIMATION | Animation |
29 | 0x20000000 | CHANGE_REFR_EXTRA_ENCOUNTER_ZONE | Enc Zone Extra |
30 | 0x40000000 | CHANGE_REFR_EXTRA_CREATED_ONLY | Created Only Extra |
31 | 0x80000000 | CHANGE_REFR_EXTRA_GAME_ONLY | Game Only Extra |
! | ACHR only | ||
10 | 0x00000400 | CHANGE_ACTOR_LIFESTATE | Life State |
11 | 0x00000800 | CHANGE_ACTOR_EXTRA_PACKAGE_DATA | Package Data Extra |
12 | 0x00001000 | CHANGE_ACTOR_EXTRA_MERCHANT_CONTAINER | Merchant Container |
17 | 0x00020000 | CHANGE_ACTOR_EXTRA_DISMEMBERED_LIMBS | Dismembered Limbs |
18 | 0x00040000 | CHANGE_ACTOR_LEVELED_ACTOR | Leveled Actor |
19 | 0x00080000 | CHANGE_ACTOR_DISPOSITION_MODIFIERS | Disp Modifiers |
20 | 0x00100000 | CHANGE_ACTOR_TEMP_MODIFIERS | Temp Modifiers |
21 | 0x00200000 | CHANGE_ACTOR_DAMAGE_MODIFIERS | Damage Modifiers |
22 | 0x00400000 | CHANGE_ACTOR_OVERRIDE_MODIFIERS | Override Modifiers |
23 | 0x00800000 | CHANGE_ACTOR_PERMANENT_MODIFIERS | Permanent Modifiers |
! | All except ACHR | ||
10 | 0x00000400 | CHANGE_OBJECT_EXTRA_ITEM_DATA | Item Data Extra |
11 | 0x00000800 | CHANGE_OBJECT_EXTRA_AMMO | Ammo Extra |
12 | 0x00001000 | CHANGE_OBJECT_EXTRA_LOCK | Lock Extra |
21 | 0x00200000 | CHANGE_OBJECT_EMPTY | Empty |
22 | 0x00400000 | CHANGE_OBJECT_OPEN_DEFAULT_STATE | Open Default State |
23 | 0x00800000 | CHANGE_OBJECT_OPEN_STATE | Open Dtate |
INFO | |||
31 | 0x80000000 | CHANGE_TOPIC_SAIDONCE | Said Once |
QUST | |||
1 | 0x00000002 | CHANGE_QUEST_FLAGS | Quest Flags |
2 | 0x00000004 | CHANGE_QUEST_SCRIPT_DELAY | Quest Script Delay |
26 | 0x04000000 | CHANGE_QUEST_ALREADY_RUN | Quest Already Run |
27 | 0x08000000 | CHANGE_QUEST_INSTANCES | Quest Instance Data |
28 | 0x10000000 | CHANGE_QUEST_RUNDATA | Quest Runtime Data |
29 | 0x20000000 | CHANGE_QUEST_OBJECTIVES | Quest Objectives |
30 | 0x40000000 | CHANGE_QUEST_SCRIPT | Quest Script |
31 | 0x80000000 | CHANGE_QUEST_STAGES | Quest Stages |
PACK | |||
30 | 0x40000000 | CHANGE_PACKAGE_WAITING | Waiting Flag |
31 | 0x80000000 | CHANGE_PACKAGE_NEVER_RUN | Never Run Flag |
FLST | |||
31 | 0x80000000 | CHANGE_FORM_LIST_ADDED_FORM | Added Form |
ECZN | |||
1 | 0x00000002 | CHANGE_ENCOUNTER_ZONE_FLAGS | Zone Flags |
31 | 0x80000000 | CHANGE_ENCOUNTER_ZONE_GAME_DATA | Game Data |
LCTN | |||
30 | 0x40000000 | CHANGE_LOCATION_KEYWORDDATA | KeywordData |
31 | 0x80000000 | CHANGE_LOCATION_CLEARED | Cleared |
SMQN | |||
31 | 0x80000000 | CHANGE_QUEST_NODE_TIME_RUN | Time Last Run |
RELA | |||
1 | 0x00000002 | CHANGE_RELATIONSHIP_DATA | Relationship Data |
SCEN | |||
31 | 0x80000000 | CHANGE_SCENE_ACTIVE | Active |
All records not yet named | |||
1 | 0x00000002 | CHANGE_BASE_OBJECT_VALUE | Object Value |
2 | 0x00000004 | CHANGE_BASE_OBJECT_FULLNAME | Object Full Name |
Initial type[edit]
- Certain changeforms first have an initial data type and data.
- These are applied in order.
If change form type is CELL:
- If flag CHANGE_CELL_DETACHTIME is not set, initialType = 0.
- Else if flag CHANGE_CELL_EXTERIOR_CHAR is set, initialType = 1.
- Else if flag CHANGE_CELL_EXTERIOR_SHORT is set initialType = 2.
- Else if flag CHANGE_CELL_DETACHTIME is set initialType = 3.
- Else initialType = 0.
If change form type is one of these (REFR, ACHR, PMIS, PGRE, PBEA, PFLA, PHZD, PBAR, PCON, PARW):
- If change form is created one (FormId >= FF000000) intialType = 5.
- Else if flag CHANGE_REFR_PROMOTED or CHANGE_REFR_CELL_CHANGED is set, initialType is 6.
- Else if flag CHANGE_REFR_HAVOK_MOVE or CHANGE_REFR_MOVE is set, initialType = 4.
- Else initialType = 0.
Initial type | Data Size | Data |
---|---|---|
0 | 0 | none |
1 | 8 | uint16 unk uint8 Cell_X, Cell_Y uint32 unk2 |
2 | 10 | uint16 unk sint16 unk1, unk2 uint32 unk3 |
3 | 4 | uint32 unk |
4 | 27 | RefId Cell / world float pos[3], rot[3] |
5 | 31 | RefId Cell / world float pos[3], rot[3] uint8 unk RefId base object |
6 | 34 | RefId Cell / world float pos[3], rot[3] RefId starting cell / world? sint16 unk1, unk2 |
Change form flags[edit]
Name | Type | Description |
---|---|---|
flag | uint32 | |
unknown | uint16 |
CELL change form flags[edit]
Bit | Value | Name | Description |
---|---|---|---|
0 | 0000 0001 | CHANGE_FORM_FLAGS | |
1 | 0000 0002 | CHANGE_CELL_FLAGS | |
2 | 0000 0004 | CHANGE_CELL_FULLNAME | |
3 | 0000 0008 | CHANGE_CELL_OWNERSHIP | |
28 | 1000 0000 | CHANGE_CELL_EXTERIOR_SHORT | |
29 | 2000 0000 | CHANGE_CELL_EXTERIOR_CHAR | |
30 | 4000 0000 | CHANGE_CELL_DETACHTIME | |
31 | 8000 0000 | CHANGE_CELL_SEENDATA |
QUST change form[edit]
Bit | Value | Data Size | Name | Description |
---|---|---|---|---|
1 | 0000 0002 | 2 | CHANGE_QUEST_FLAGS | |
26 | 0400 0000 | 1 | CHANGE_QUEST_ALREADY_RUN | |
27 | 0800 0000 | CHANGE_QUEST_INSTANCES | ||
28 | 1000 0000 | CHANGE_QUEST_RUNDATA | ||
29 | 2000 0000 | CHANGE_QUEST_OBJECTIVES | ||
31 | 8000 0000 | CHANGE_QUEST_STAGES |
REFR change form[edit]
Data Elements[edit]
These are the elements I have so far seen in a REFR change form.
Field | Size | Contents | Notes |
---|---|---|---|
cellId | 3 | refId | Contains a reference to the cell where this object currently is |
location | 24 | float x float y float z float rx float ry float rz |
Contains the location and orientation of the object |
unknown1 | 1 | byte | Always (and only) present in created objects (formId starts with 0xff) |
refIdx | 3 | refId | Only seen on objects with the CHANGE_REFR_PROMOTED flag set and always seems to contain the same refId as the cellId. |
unkown2 | 4 | int16 a int16 b |
A tuple containing (moderately) positive or negative numbers |
havokData | var | byte[1-3] size byte[size] data |
The two lowest bits of the first byte indicate how many bytes are used to store the size. The other bits indicate the size (bit 3 of the first byte thus indicates a size of one byte, the lowest bit of byte 2 equals 64 bytes). |
flags | 6 | byte[6] flags | Six bytes of flags |
scale | 4 | float | New scale to appplied to the object (not 100% certain about the order of scale and baseId) |
baseId | 3 | refId | A reference to a new base object for this object |
gameOnly | var | byte[1-3?] itemCount per item: - byte itemType - [opt] byte itemRepeat - byte[var] itemData |
itemCount should be shifted by two bits to get the count, the other bit probably indicate additional count bytes. The itemType influences what is next, in some cases another count that has to be shifted as well, in some cases a fixed number of bytes. If a count is present, it seems to indicate how many repeats of a certain fixed size are following. |
inventory | var | byte[1-3?] itemCount per item: - refId item - uint32 count - complex |
The refId and count are followed by some complex nested structure that's similar to the "gameOnly" field above. I.e. each item can have extra properties from the table below. |
animation | var | complex |
Flags[edit]
This table lists which fields will be present for which flags. It is incomplete at the moment.
changeFormId = 0x00...... | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Bit | Value | Name | cellId | location | unknown1 | refIdX | unknown2 | havokData | flags | scale | baseId | gameOnly | inventory |
0 | 0000 0001 | CHANGE_FORM_FLAGS | x | ||||||||||
1 | 0000 0002 | CHANGE_REFR_MOVE | x | x | |||||||||
2 | 0000 0004 | CHANGE_REFR_HAVOK_MOVE | x | x | x | ||||||||
3 | 0000 0008 | CHANGE_REFR_CELL_CHANGED | x | x | x | x | |||||||
4 | 0000 0010 | CHANGE_REFR_SCALE | |||||||||||
5 | 0000 0020 | CHANGE_REFR_INVENTORY | x | ||||||||||
6 | 0000 0040 | CHANGE_REFR_EXTRA_OWNERSHIP | |||||||||||
7 | 0000 0080 | CHANGE_REFR_BASEOBJECT | x | ||||||||||
10 | 0000 0400 | CHANGE_OBJECT_EXTRA_ITEM_DATA | x | ||||||||||
12 | 0000 1000 | CHANGE_OBJECT_EXTRA_LOCK | x | ||||||||||
21 | 0020 0000 | CHANGE_OBJECT_EMPTY | no extra data, just a flag | ||||||||||
23 | 0080 0000 | CHANGE_OBJECT_OPEN_STATE | no extra data, just a flag | ||||||||||
25 | 0200 0000 | CHANGE_REFR_PROMOTED | x | x | x | x | x | ||||||
26 | 0400 0000 | CHANGE_REFR_EXTRA_ACTIVATING_CHILDREN | |||||||||||
27 | 0800 0000 | CHANGE_REFR_LEVELED_INVENTORY | x | ||||||||||
28 | 1000 0000 | CHANGE_REFR_ANIMATION | |||||||||||
29 | 2000 0000 | CHANGE_REFR_EXTRA_ENCOUNTER_ZONE | x | ||||||||||
31 | 8000 0000 | CHANGE_REFR_EXTRA_GAME_ONLY | x | ||||||||||
changeFormId = 0xff...... | |||||||||||||
Bit | Value | Name | cellId | location | unknown1 | refIdX | unknown2 | havokData | flags | scale | baseId | gameOnly | inventory |
0 | 0000 0001 | CHANGE_FORM_FLAGS | x | x | x | x | x | ||||||
1 | 0000 0002 | CHANGE_REFR_MOVE | x | x | x | x | |||||||
3 | 0000 0008 | CHANGE_REFR_CELL_CHANGED | x | x | x | x | |||||||
4 | 0000 0010 | CHANGE_REFR_SCALE | x | ||||||||||
10 | 0000 0400 | CHANGE_OBJECT_EXTRA_ITEM_DATA | x | x | x | x | x | ||||||
25 | 0200 0000 | CHANGE_REFR_PROMOTED | x | x | x | x | x | ||||||
31 | 8000 0000 | CHANGE_REFR_EXTRA_GAME_ONLY | x | x | x | x | x |
The 0xff000000 type changeFormIds have at least 31 bytes of data in structure called 'Base', containing at least a RefId and the location/rotation fields. In this case the additional data added by the other flags may be different, for example CHANGE_REFR_MOVE and CHANGE_REFR_CELL_CHANGED will not add any data, while CHANGE_REFR_PROMOTED will only add 6 bytes instead of 40. The CHANGE_REFR_MOVE flag appears to be set on every changeForm of this type.
extraData[edit]
The extra data starts with the number of items contained in the field. Bit 0 and 1 of the first byte probably indicate how many bytes are used to store the size: 00 = just this byte, 01 = 1 extra byte, 10 = ?, 11 = ?. The count is obtained by reading the bytes and shifting them two bits to the right. 0x04 thus becomes 1, 0x08 becomes 2, ...
The next bytes tell something about the data that follows.
An incomplete list of possible data in the extraData field:
fieldType | hasRepeatCount | bytesToFollow | comment |
---|---|---|---|
0x1c | 3 | ||
0x21 | 3 | Seen when Extra Item Data flag set, could be owner | |
0x24 | 2 | ||
0x25 | 4 | Appears to be a float | |
0x28 | 4 | ||
0x2a | 13 | Seen when Lock Extra flag is set. Bytes 0 is the lock level (like XLOC in REFR), byte 1 is unknown, bytes 2-4 are the KEYM RefId to open the lock, bytes 5-12 always zero? | |
0x2c | 1 | ||
0x2e | 5 | ||
0x2f | 4 | ||
0x31 | 29 | ||
0x32 | 4 | ||
0x38 | 3 | ||
0x48 | 3 | ||
0x49 | 5 | ||
0x55 | 4 | ||
0x58 | 7 | ||
0x5d | 4 | ||
0x70 | 3 | This is present when the Extra Encounter Zone flag is set. Probably a refId to the zone then | |
0x78 | x | 8 | |
0x85 | 3 | ||
0x88 | x | 7 | A QUST RefId, followed by a uint32 equal to one of the ALST fields in that QUST. It probably means this reference is assigned to that alias in that quest. |
0x8c | x | 3 | Present on promoted objects, usually with one item that looks like a refId: 0x40028A |
0x8e | 3 | A OTFT RefId, probably indicating that this inventory item is part of that outfit. | |
0x92 | 3 | ||
0x95 | 7 | ||
0x98 | 14(may vary) | ExtraData_AttachedArrows3D - Appears to always be 14 bytes long, but the first 2 bytes are always 0c 00. If read as a UInt16, this would be 12, which is also the number of bytes that follow. It's unknown what type of data is contained in the remaining bytes, as the only values seen so far are 00 and 02. | |
0x99 | 10+var | When this is in an inventory object and the last 4 bytes are 0xfffffffe, a string follows (2 bytes for the length, then the characters). This is used to store user modified names during enchanting | |
0x9b | 5 | ||
0x9f | 6 | ||
0xa0 | 4 | ||
0xa1 | 88 |
The fields with a repeat should be interpreted like this: after the fieldType there is a count (again bit 0 and 1 of the first count byte indicate how many extra bytes are used for the count). After the count, count * bytesToFollow will follow. So 0x8c 0x08 .... indicates that 2 (0x08 shifted by 2) records of type 0x8c follow, which are 3 bytes per record, so 6 bytes in total. Frequently 0x88 0x00 is observed, meaning 0 * 7 bytes follow. As another example, 0x88 0x81 0x01 means count is (81>>2 + 1<<6) = 96, so 96 * 7 bytes follow.
NPC_ change form[edit]
Bit | Value | Name | Data Size | Data |
---|---|---|---|---|
0 | 0000 0001 | CHANGE_FORM_FLAGS | 6 | Change form flags |
1 | 0000 0002 | CHANGE_ACTOR_BASE_DATA | 24 | See ACBS field of NPC_ record |
6 | 0000 0040 | CHANGE_ACTOR_BASE_FACTIONS | var | vsval count
struct {refID factionID; uint8 rank} [count] |
4 | 0000 0010 | CHANGE_ACTOR_BASE_SPELLLIST | var | vsval spellCount
refID spells[spellCount] vsval leveledspellCount refID leveledspells[leveledspellCount] vsval shoutCount refID shouts[shoutCount] |
3 | 0000 0008 | CHANGE_ACTOR_BASE_AIDATA | 20 | See AIDT field of NPC_ record |
5 | 0000 0020 | CHANGE_ACTOR_BASE_FULLNAME | var | wstring name |
9 | 0000 0200 | CHANGE_NPC_SKILLS | 52 | See DNAM field of NPC_ record |
10 | 0000 0400 | CHANGE_NPC_CLASS | 3 | refID class |
25 | 0200 0000 | CHANGE_NPC_RACE | 6 | refID race
refID race2 (?) |
11 | 0000 0800 | CHANGE_NPC_FACE | var | ? |
24 | 0100 0000 | CHANGE_NPC_GENDER | 1 | uint8 gender (0 – male, 1 - female) |
12 | 0000 1000 | CHANGE_NPC_DEFAULT_OUTFIT | 3 | refID defOutfit |
12 | 0000 1000 | CHANGE_NPC_SLEEP_OUTFIT | 3 | refID sleepOutfit |
The order of data in changeForm is the same as in the table
Skills/Health, etc are stored in ACHR records (as floats) associated with the NPC (for the player this is formid 0x00000014, refid=0x40 0x00 0x14 and is zlib compressed)
- if any of the data is stored in the NPC_ record it seems to be ignored by the game.