Skyrim supports version 3.0 to 3.1 compiled script files.
The format appears to be stored in big-endian regardless of platform (Windows, PS3, and 360 observed as big-endian).
Consult the list of Conventions for a list of primitive types and how to parse them.
HeaderEdit
Name | Type/Size | Info |
---|---|---|
magic | uint32 | 0xFA57C0DE (FASTCODE?) |
majorVersion | uint8 | 3 |
minorVersion | uint8 | 1 (Dawnguard, Hearthfire and Dragonborn scripts are 2) |
gameId | uint16 | 1 = Skyrim? |
compilationTime | uint64 | the compilation time |
sourceFileName | wstring | Name of the source file this file was compiled from (.psc extension). |
username | wstring | Username used to compile the script |
machinename | wstring | Machine name used to compile the script |
stringTable | String Table | StringTable to look up member names and other stuff from |
debugInfo | Debug Info | |
userFlagCount | uint16 | |
userFlags | User Flag[userFlagCount] | |
objectCount | uint16 | |
objects | Object[objectCount] |
SectionsEdit
String TableEdit
Name | Type/Size | Info |
---|---|---|
count | uint16 | |
strings | wstring[count] |
Debug InfoEdit
Name | Type/Size | Info |
---|---|---|
hasDebugInfo | uint8 | Flag, if zero then no debug info is present and the rest of the record is skipped |
modificationTime | uint64 | time_t |
functionCount | uint16 | |
functions | Debug Function[functionCount] |
Debug FunctionEdit
Name | Type/Size | Info |
---|---|---|
objectNameIndex | uint16 | Index(base 0) into string table. |
stateNameIndex | uint16 | Index(base 0) into string table. |
functionNameIndex | uint16 | Index(base 0) into string table. |
functionType | uint8 | valid values 0-3 |
instructionCount | uint16 | |
lineNumbers | uint16[instructionCount] | Maps instructions to their original lines in the source. |
User FlagEdit
Name | Type/Size | Info |
---|---|---|
nameIndex | uint16 | Index(base 0) into string table. |
flagIndex | uint8 | Bit index |
ObjectEdit
Name | Type/Size | Info |
---|---|---|
nameIndex | uint16 | Index(base 0) into string table. |
size | uint32 | |
data | Object Data [size-4] | size includes itself for some reason, hence size-4 |
Object DataEdit
Name | Type/Size | Info |
---|---|---|
parentClassName | uint16 | Index(base 0) into string table. |
docstring | uint16 | Index(base 0) into string table. |
userFlags | uint32 | |
autoStateName | uint16 | Index(base 0) into string table. |
numVariables | uint16 | |
variables | Variable[numVariables] | |
numProperties | uint16 | |
properties | Property[numProperties] | |
numStates | uint16 | |
states | State[numStates] |
VariableEdit
Name | Type/Size | Info |
---|---|---|
name | uint16 | Index(base 0) into string table. |
typeName | uint16 | Index(base 0) into string table. |
userFlags | uint32 | |
data | Variable Data | Default value |
Variable DataEdit
Name | Type/Size | Info |
---|---|---|
type | uint8 | 0 = null, 1 = identifier, 2 = string, 3 = integer, 4 = float, 5 = bool |
data | uint16 | Index(base 0) into string table, present for identifier and string types only |
data | int32 | present for integer types only |
data | float32 | present for float types only |
data | uint8 | present for bool types only |
PropertyEdit
Name | Type/Size | Info |
---|---|---|
name | uint16 | Index(base 0) into string table |
type | uint16 | Index(base 0) into string table |
docstring | uint16 | Index(base 0) into string table |
userFlags | uint32 | |
flags | uint8 | bitfield: 1(bit 1) = read, 2(bit 2) = write, 4(bit 3) = autovar. For example, Property in a source script contains only get() or is defined AutoReadOnly then the flags is 0x1, contains get() and set() then the flags is 0x3. |
autoVarName | uint16 | Index(base 0) into string table, present if (flags & 4) != 0 |
readHandler | Function | present if (flags & 5) == 1 |
writeHandler | Function | present if (flags & 6) == 2 |
StateEdit
Name | Type/Size | Info |
---|---|---|
name | uint16 | Index(base 0) into string table, empty string for default state |
numFunctions | uint16 | |
functions | Named Function[numFunctions] |
Named FunctionEdit
Name | Type/Size | Info |
---|---|---|
functionName | uint16 | Index(base 0) into string table |
function | Function |
FunctionEdit
Function is nameless. Its name will be defined in Named Function at State.
Name | Type/Size | Info |
---|---|---|
returnType | uint16 | Index(base 0) into string table |
docstring | uint16 | Index(base 0) into string table |
userFlags | uint32 | |
flags | uint8 |
|
numParams | uint16 | |
params | Variable Type[numParams] | |
numLocals | uint16 | |
locals | Variable Type[numLocals] | |
numInstructions | uint16 | |
instructions | Instruction[numInstructions] |
Variable TypeEdit
Name | Type/Size | Info |
---|---|---|
name | uint16 | Index(base 0) into string table |
type | uint16 | Index(base 0) into string table |
InstructionEdit
Name | Type/Size | Info |
---|---|---|
op | uint8 | see Opcodes |
arguments | Variable Data[changes depending on opcode] | Length is dependent on opcode, also varargs |
OpcodesEdit
Opcode | Name | Arguments | Description |
---|---|---|---|
00 | nop | none | do nothing |
01 | iadd | SII | add two integers |
02 | fadd | SFF | add two floats |
03 | isub | SII | subtract two integers |
04 | fsub | SFF | subtract two floats |
05 | imul | SII | multiply two integers |
06 | fmul | SFF | multiply two floats |
07 | idiv | SII | divide two integers |
08 | fdiv | SFF | divide two floats |
09 | imod | SII | remainder of two integers |
0A | not | SA | flip a bool, type conversion may occur? |
0B | ineg | SI | negate an integer |
0C | fneg | SF | negate a float |
0D | assign | SA | store a variable |
0E | cast | SA | type conversion? |
0F | cmp_eq | SAA | set a bool to true if a == b |
10 | cmp_lt | SAA | set a bool to true if a < b |
11 | cmp_le | SAA | set a bool to true if a <= b |
12 | cmp_gt | SAA | set a bool to true if a > b |
13 | cmp_ge | SAA | set a bool to true if a >= b |
14 | jmp | L | relative unconditional branch |
15 | jmpt | AL | relative conditional branch if a bool is true |
16 | jmpf | AL | relative conditional branch if a bool is false |
17 | callmethod | NSS* | |
18 | callparent | NS* | |
19 | callstatic | NNS* | |
1A | return | A | |
1B | strcat | SQQ | concatenate two strings |
1C | propget | NSS | retrieve an instance property |
1D | propset | NSA | set an instance property |
1E | array_create | Su | create an array of the specified size |
1F | array_length | SS | get an array's length |
20 | array_getelement | SSI | get an element from an array |
21 | array_setelement | SIA | set an element to an array |
22 | array_findelement | SSII | find an element in an array. The 4th arg is the startIndex, default = 0 |
23 | array_rfindelement | SSII | find an element in an array, starting from the end. The 4th arg is the startIndex, default = -1 |
Each character in the "arguments" string specifies an argument type. They're all read as Variable Data.
- I = integer, i.e. a Variable Data with type = 0x03 and data consisting of a signed int32.
- F = float, i.e. a Variable Data with type = 0x04 and data consisting of a float32.
- A = boolean, i.e. a Variable Data with type = 0x05 and data consisting of a uint8.
- S = string, i.e. a Variable Data with type = 0x01 or 0x02 and data consisting of a uint16 index to the String Table.
- N = identifier, i.e. a Variable Data with type = 0x01 and data consisting of a uint16 index to the String Table.
- * = variable-length arguments. These consist of a Variable Data integer which specifies the number of additional arguments, followed by that number of Variable Data groups.
- u = unsigned integer? Presumably a 0x03 Variable Data object with the data interpreted as a uint32.
- Q = S? It's not clear why Q is used here.
- L = a code label of some sort, probably a program counter absolute value or offset?
A tool for dumping compiled scripts w/ source may be found at [1].