Tes4Mod:Mod File Format/SCPT

A SCPT record holds information on a single script.

Subrecords known to occur in the SCPT record include (confirmed):
:*EDID (required): Editor ID (variable length string)
:*SCHR (required): Script data (20 bytes)
:*SCDA (required): Compiled script data (variable length)
:*SCTX (required): Script text (variable length string, not always nul terminated)
:*SLSD (optional, mult): Variable data (24 bytes, confirmed)
:*SCVR (optional, mult): Variable name, follows a SLSD (variable length string)
:*SCRO (optional, mult): Global variable reference (formid, confirmed)
:*SCRV (optional, mult): Ref variable data (one for each ref declared) (dword)


SCHR Subrecord
Appears to hold 20 bytes (confirmed) of basic script data .



SCDA Subrecord
Holds the compiled script data. Note that an empty script (with just a "scriptname ...") has a 4-byte SCDA with a value of 0x00000001D.

The compiled script consists of a list of 2-byte opcodes, each followed by the 2-byte length of its arguments, optionally followed by the 2-byte number of arguments and the arguments itself. Arguments with a variable length (strings, RPN-encoded expressions and so on) have an additional 2-byte length prepending them. The only notable exception to this rule is the SetCurrentReference pseudo-instruction (represented as a single dot in the script editor), which consists only of its opcode (0x001C) and the 2-byte index of the SCRO subrecord to be used.

SCRO subrecords in expressions are referenced using 0x72, followed by the 2-byte index of the corresponding subrecord. SLSD/SLVR subrecords use 0x73, followed by the index. The first subrecord has the index 1.

Examples:

''Note: The name of the script doesn't get used in the compiled script.




SCDA Output Codes
The following is a work in progress and likely contains errors. Text in the format [Name(N)] indicates variable data (N) bytes in size.



SCDA Expression Codes
Expression codes, used in set and if statements, use a somewhat different format that the rest of the SCDA data. Expressions are parsed using a simple stack and infix notation.



SLSD/SCVR Subrecords
SLSD and SCVR records together represent the local variables of the script. SLSD provides general info, while SCVR gives the name of the variable. 

SLSD appears to hold 24 bytes (confirmed) of data related to a local variable definition.



Variable Indexing
SLSD/SCVR pairs appear in the order in which they're defined in the script at compile time. When compiling a script, the compiler will use index already assigned to the variable if it already exists in the SLSC/SCVR pair listing. If the variable is not in the list, then the next available index is assigned -- where next available index is numVars (from SCHR) plus 1. 

This means:
Changing the type of an existing variable will not affect its index number.
Reordering variable definition will not affect their indices, though it will affect the ordering of the SLSD/SCVR pairs.
Renaming a variable will result in a new index number being assigned, which means that savegames will effectively have the value reset. (Since savegames remember variable value by index.)
Deleting a variable, compiling and then reading the variable will cause the redefined variable to have a new index number. This is because when the variable is deleted, it is removed from the SLSD/SCVR pair list, and so the compiler is not able to find its old index number when the variable is redefined.
Renaming a variable will likely cause other scripts attempting to access it to lose it -- until they too are modified to use the new name and recompiled.

Note: It appears that the savegames store all numbers (despite their short, long, float designation) as doubles (64 bit floats). Refs are stored differently, but in the same space(?)

It seems that problems can arise if their is a conflict over what a given index means. This can happen if: 
# a master defines a script with 4 variables
# a mod overrides the script and adds 2 more variables
# the master then is updated to add another variable
# player starts playing with master version 2, then loads mod (compiled on version 1 of master script), since there is now a conflict over what variable index 5 refers to.
This might in particular be a problem when a float is reinterpreted as a ref value. It is suspected that this conflict was the source of some CTDs when version 1.3 of OOO (built on top of unpatched Oblivion) was used with savegames using patched Oblivion.

SCRV
There will be one SCRV subrecord for each local ref variable used in the script. They seem to follow at the end of the script data after all the SLSD/SCVR pairs. The SCRV data is a 4 byte dword (confirmed) and seems to hold the ref variable index as stored in the variable's SLSD subrecord.

SCRO
There will be one 4-byte SCRO subrecord for each object reference used in the script. The value of the SCRO is the formid of the object. The one exception is an SCRO value of 0x00000014 which actually refers to the player (whose true formid is 0x00000007).