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.
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] |
|
Sections[edit]
String Table[edit]
Name |
Type/Size |
Info |
count |
uint16 |
|
strings |
wstring[count] |
|
Debug Info[edit]
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 Function[edit]
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 Flag[edit]
Name |
Type/Size |
Info |
nameIndex |
uint16 |
Index(base 0) into string table. |
flagIndex |
uint8 |
Bit index |
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 Data[edit]
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] |
|
Variable[edit]
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 Data[edit]
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 |
Property[edit]
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 |
Name |
Type/Size |
Info |
name |
uint16 |
Index(base 0) into string table, empty string for default state |
numFunctions |
uint16 |
|
functions |
Named Function[numFunctions] |
|
Named Function[edit]
Name |
Type/Size |
Info |
functionName |
uint16 |
Index(base 0) into string table |
function |
Function |
|
Function[edit]
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 |
- bit 0 = global function
- bit 1 = native function (i.e., no code)
|
numParams |
uint16 |
|
params |
Variable Type[numParams] |
|
numLocals |
uint16 |
|
locals |
Variable Type[numLocals] |
|
numInstructions |
uint16 |
|
instructions |
Instruction[numInstructions] |
|
Variable Type[edit]
Name |
Type/Size |
Info |
name |
uint16 |
Index(base 0) into string table |
type |
uint16 |
Index(base 0) into string table |
Instruction[edit]
Opcodes[edit]
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].