ScriptParseTree Asset: Difference between revisions
Created page with "__NOTOC__ Category:Assets Category:BO2 The ScriptParseTree asset is a new asset in Black Ops 2 and this is the only Call of Duty game it has appeared in so far. This i..." |
No edit summary |
||
| Line 265: | Line 265: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
to continue reading. When dumping the function name should be printed after "::". | to continue reading. When dumping the function name should be printed after "::". | ||
==== OP_CreateLocalVariable ==== | |||
This opcode is used at the beginning of any function with arguments or local variables to define them. After the opcode is a single byte that is the number to define. Each variable argument is found by getting the reference for it in the argument section. To get the offset of each reference | This opcode is used at the beginning of any function with arguments or local variables to define them. After the opcode is a single byte that is the number to define. Each variable argument is found by getting the reference for it in the argument section. To get the offset of each reference | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
Revision as of 20:41, 11 January 2014
The ScriptParseTree asset is a new asset in Black Ops 2 and this is the only Call of Duty game it has appeared in so far. This is Treyarch's answer to GSCs (Keep in mind that on Black Ops 1 GSCs were still rawfiles). Compared to IW's equivalent scriptfile asset, the ScriptParseTree has proven to give slightly more information and thus are easier to reverse. The basic structure is identical to the rawfiles
struct ScriptParseTree
{
const char *name;
int len;
const char *buffer;
};Simply an uncompressed buffer holding the GSC data. The binary data is then passed to the GSC Virtual Machine (VM) to be ran. Now for the header of the binary data
#define GSCMagic = 0x804753430D0A0006
struct GSCHeader
{
__int64 magic;
int unknown1;
unsigned int includeSectionOffset;
unsigned int usingAnimtreeSectionOffset;
unsigned int executionDataSectionOffset;
unsigned int argumentSectionOffset;
unsigned int functionDeclSectionOffset;
unsigned int callFunctionSectionOffset;
char unknown2[0xE];
unsigned short totalArgumentCount;
unsigned short functionDeclCount;
unsigned short callFunctionCount;
int unknown3;
byte includeSectionCount;
byte usingAnimtreeCount;
};Following the header is a section of null terminated strings (undetermined size/count). This section is also padded at the end to a 4-byte buffer. The following sections will reference back to this section when a string is used. Keep in mind that the offsets are simply just an offset in the buffer.
Include Section
The first known section after the string table is the include section, and this should be the first section dumped as well. This section is used to replace the following in a raw gsc
#include maps\mp\_utility;
#include common_scripts\utility;The include section is probably the easiest section to reverse. Each include exists as a single integer which holds the offset of the include's string in the buffer.
Using Animtree Section
Although this section doesn't follow the include section, it is what needs to be dumped second. When a GSC wants to directly access and animtree, then the following code is added (for animtree "mp_vehicles")
#using_animtree( "mp_vehicles" );Then animations can be accessed from the tree as follows (Using the %)
destructible_anims[ "car" ] = %veh_car_destroy;Knowing this, the binary data section can be reversed fairly easily. The structure for their definitions in this section is
struct Reference
{
unsigned short name;
unsigned short xrefOffset;
};
struct AnimTree
{
unsigned short nameOffset;
unsigned short pathOffset; //Paths do not exist for animtrees (so far)
unsigned short referenceCount;
Reference references[referenceCount]; //This isn't valid C++
};XRefs are explained in better detail in the code section. Each animtree thats included will have an AnimTree struct, and every time an animation is used it will be defined in the references.
Argument Section
The argument section not only defines any function arguments that are defined with function declarations, but any local variable a function might create. Although they are defined the same, the difference between the two is highlighted in the code section. Each argument will be defined as follows
struct Argument
{
unsigned short nameOffset;
unsigned byte referenceCount;
byte unknown;
unsigned int references[referenceCount]; //This isn't valid C++
};The references will explained in more detail in the code section.
Function Call Section
If a function is called by a function in this GSC, it must be defined here in the function call section, even if that function is located inside this GSC. First the struct
struct FunctionCall
{
unsigned short nameOffset;
unsigned short pathOffset;
unsigned short referenceCount;
unsigned int references[referenceCount]; //This isn't valid C++
};NameOffset obviously holds the name of the called function. References are explained in the code section. If the function is not located in this GSC or in a "using" GSC, then the function must have a path with the name of the GSC that it is defined in, and this is where the pathOffset points to. eg
thread maps\mp\gametypes\_spawning::init();Function Declaration Section
This is the section that will be cycled through when dumping the GSC. Every function that is defined in this GSC is here.
struct FunctionDeclaration
{
int unknown1;
unsigned int executionDataOffset;
unsigned short nameOffset;
byte unknown2;
byte argumentCount;
};The executionDataOffset is an offset in the code section defining where this function begins.
Executable Data
This section is by far the trickiest section. This is where the actual code the functions are compiled into is stored. The first thing that will be needed is a list of operation codes (OPcodes). Keep in mind that these were reversed from the Xbox 360 version of Black Ops 2.
enum OPCodes : char
{
OP_Return = 0x1,
OP_GetUndefined = 0x2,
OP_GetZero = 0x3,
OP_GetByte = 0x4,
OP_GetNegByte = 0x5,
OP_GetUnsignedShort = 0x6,
OP_GetNegUnsignedShort = 0x7,
OP_GetInteger = 0x8,
OP_GetFloat = 0x9,
OP_GetString = 0xA,
OP_GetIString = 0xB,
OP_GetLevelObject = 0xD,
OP_GetSelf = 0xF,
OP_GetLevel = 0x10,
OP_GetGame = 0x11,
OP_GetAnimation = 0x13,
OP_GetGameRef = 0x14,
OP_GetFunction = 0x15,
OP_CreateLocalVariable = 0x17,
OP_EvalLocalVariableCached1 = 0x19,
OP_EvalArray = 0x1A,
OP_EvalArrayRef = 0x1C,
OP_ClearArray = 0x1D,
OP_EmptyArray = 0x1E,
OP_GetSelfObject = 0x1F,
OP_20 = 0x20,
OP_21 = 0x21,
OP_ClearFieldVariable = 0x22,
OP_25 = 0x25,
OP_checkclearparams = 0x26,
OP_EvalLocalVariableRefCached = 0x27,
OP_SetVariableField = 0x28,
OP_CallBuiltin = 0x29,
OP_ScriptMethodCall = 0x2A,
OP_wait = 0x2B,
OP_waittillFrameEnd = 0x2C,
OP_PreScriptCall = 0x2D,
OP_ScriptFunctionCall = 0x2E,
OP_ScriptFunctionCallPointer = 0x2F,
OP_ScriptMethodCall2 = 0x30,
OP_ScriptMethodCallPointer = 0x31,
OP_ScriptThreadCall = 0x32,
OP_ScriptThreadCallPointer = 0x33,
OP_ScriptMethodThreadCall = 0x34,
OP_ScriptMethodThreadCallPointer = 0x35,
OP_clearparams = 0x36,
OP_37 = 0x37,
OP_BoolNot = 0x39,
OP_JumpOnFalse = 0x3B,
OP_JumpOnTrue = 0x3C,
OP_JumpOnFalseExpr = 0x3D,
OP_JumpOnTrueExpr = 0x3E,
OP_jump = 0x3F,
OP_inc = 0x41,
OP_dec = 0x42,
OP_bit_or = 0x43,
OP_bit_and = 0x45,
OP_equality = 0x46,
OP_inequality = 0x47,
OP_less = 0x48,
OP_greater = 0x49,
OP_less_equal = 0x4A,
OP_greater_equal = 0x4B,
OP_plus = 0x4E,
OP_minus = 0x4F,
OP_multiply = 0x50,
OP_divide = 0x51,
OP_mod = 0x52,
OP_size = 0x53,
OP_waittillmatch = 0x54,
OP_waittill = 0x55,
OP_notify = 0x56,
OP_endon = 0x57,
OP_voidCodepos = 0x58,
OP_switch = 0x59,
OP_endswitch = 0x5A,
OP_vector = 0x5B,
OP_5C = 0x5C,
OP_5E = 0x5E,
OP_5F = 0x5F,
OP_60 = 0x60,
OP_61 = 0x61,
OP_62 = 0x62,
OP_63 = 0x63,
OP_64 = 0x64,
OP_65 = 0x65,
OP_66 = 0x66,
OP_67 = 0x67,
OP_68 = 0x68,
OP_69 = 0x69,
OP_6A = 0x6A,
OP_70 = 0x70,
OP_71 = 0x71,
OP_7B = 0x7B,
OP_count = 0x7C,
OP_NOP = 0x7F
};Notice that 0x7B is the highest opcode, but 0x7F is still valid. In truth anything over 0x7B will register as NOP. The executable does some odd jumping around for each OP code, so a recap of each OP code will try to be added. In the following functions "currentFunctionDataOffset" is defined as an integer that points to the current OP code being read. Keep in mind that an OP code is read, the data offset is increased by 1, and then the following code is ran. OP codes are parsed in the order they are read, but if a dump is being done then parts are going to be dumped in reverse order. Here is an example of a series of OP codes...
OP_PreScriptCall
OP_GetString "1350"
OP_GetString "scr_veh_health_tank"
OP_CallBuiltin 2, "setdvar"
OP_clearparamsThis would be the "asm" of the GSC, and would look like this...
setdvar( "scr_veh_health_tank", "1350" );OP_Return
The return OP is exactly as expected. Any OP codes in the stack go after a "return". There is no data skipped, it is simply a 1 byte opcode. Keep in mind that if the game reaches a return, it leaves the function however for a complete dump, data must continue to be read and sometimes this data is unnecessary.
OP_GetUndefined & OP_GetZero
This simply represents the "undefined"/0 element in GSCs. It is a simple 1 byte opcode, and once read it is put right onto the stack. When dumping from the stack, simply print "undefined"/0 and continue.
OP_GetByte & OP_GetNegByte
These are used to hold any numbers that are small enough to be held in 1 byte. These are 2 byte opcode in that the first byte is the opcode and the second byte is an unsigned value. The opcode is added to the stack once read. When dumping from the stack the value is simply printed and then continued. Keep in mind that OP_GetNegByte should get a negative symbol before the value is printed.
OP_GetUnsignedShort & OP_GetNegUnsignedShort
These are used to hold any numbers that are small enough to be held in 2 byte. After reading the opcode, the following is used to determine the offset of the unsigned short value
(++currentFunctionDataOffset) &= 0xFFFFFFFE;Then "currentFunctionDataOffset" will be the offset of the unsigned short value to use. Then skip another 2 bytes for that value to continue reading. The opcode is added to the stack once read. When dumping from the stack the value is simply printed and then continued. Keep in mind that OP_GetNegUnsignedShort should get a negative symbol before the value is printed.
OP_GetInteger & OP_GetFloat
OP_GetInteger is used for any numbers that are large enough to warrant a signed integer, while OP_GetFloat is used for bigger numbers and decimals. After reading the opcode, the following is used to determine the offset of the value to get
currentFunctionDataOffset += 3;
currentFunctionDataOffset &= 0xFFFFFFFC;At this point "currentFunctionDataOffset" will be the offset of the value to get, then skip another 4 bytes past the value to continue reading. The opcode is then added to the stack. When dumping the value is simply printed and then continued;
OP_GetString & OP_GetIString
These are very clearly used to reference strings in code. Strings are found by their references. To get the offset of the reference use the following
(++currentFunctionDataOffset) &= 0xFFFFFFFE;In the argument section should be an argument with a reference that is equal to "currentFunctionDataOffset" at this point. The name of the argument is the offset of the string to use in the string pool from the beginning of the GSC. Add another 2 to currentFunctionDataOffset to skip past the reference to continue reading. Keep in mind that OP_GetIString is used to get localized strings, and a & should be a added before the quotes.
OP_GetLevelObject & OP_GetLevel & OP_GetSelf & OP_GetGame & OP_GetGameRef
All of these are simple 1 byte opcodes that are added to the stack once they are read. They represent (and should be printed as) the "game", "level" and "self" objects in GSC code.
OP_GetAnimation
These are used to reference animations in an animtree that was included to this GSC. The name of the animation is found by a reference. To get the offset of the reference
currentFunctionDataOffset += 3;
currentFunctionDataOffset &= 0xFFFFFFFC;There should be an animtree in the animtree include section that has an animation with a reference equal to "currentFunctionDataOffset" at this point. Add another 4 bytes to skip past the reference to continue reading opcodes. After this is read it is added to the stack. When dumping the reference animation name should be found and dumped (without quotes) preceeded by a % sign.
OP_GetFunction
This is used to reference a function without calling it. This allows you to set a variable to a function, and then call the variable later. There should be a function call with a reference equal to "currentFunctionDataOffset", and this is the function to use. This is then added to the stack. After that the following code is performed
currentFunctionDataOffset += 3;
currentFunctionDataOffset &= 0xFFFFFFFC;
currentFunctionDataOffset += 4;to continue reading. When dumping the function name should be printed after "::".
OP_CreateLocalVariable
This opcode is used at the beginning of any function with arguments or local variables to define them. After the opcode is a single byte that is the number to define. Each variable argument is found by getting the reference for it in the argument section. To get the offset of each reference
(++currentFunctionDataOffset) &= 0xFFFFFFFE;That gets ran before to find a reference offset. There should be a argument with a reference equal to the "currentFunctionDataOffset". Then 2 is added to skip past the current reference before the above is started again. The function declaration states the number of arguments, any extra are local variables. Keep in mind the stack should be clear after this is ran.
OP_EvalLocalVariableCached1
This is a simple 2 byte opcode used to reference an argument or local variable. The second byte is the index of the local variable to use, defined by OP_CreateLocalVariable. This is then added to the stack to be dumped. To dump the local variable must be looked up, the argument found and the name referenced.
Source Format
The source format for GSCs is very well known. Simply a code file (text) with a .gsc extension.