Script Container
Contents |
YSC files contain GTA V's game scripts. The new format is an adaptation of the script container format used in Red Dead Redemption.
File Format
The basic format of YSC starts with a header that's 0x80 bytes in size, there are still some unknowns in the header but for basic dissassembling you have enough information.
struct YSCHeader
{
QWORD Magic;// 0x00 - Seems to always hold the value D09E5A4001000000
QWORD UnkPointer;// 0x08 -Points to a section in script thats always null
QWORD CodeBlocksBasePointer;// 0x10 Points to an array of code block offsets
DWORD GlobalsVersion;// 0x18 - Unsure what exactly this means
DWORD CodeSize;// 0x1C - The size of all the code tables
DWORD ParameterCount;// 0x20 - These are for starting a script with args. The args appear at the start of the script static variables
DWORD StaticCount;// 0x24 - The number of static variables in the script
DWORD GlobalCount;// 0x28 - This is used for scripts that seem to initialise global variable tables
DWORD NativesCount;// 0x2C - The total amount of natives in the native table
QWORD StaticsPointer;// 0x30 - The Offset in file where static variables are initialised
QWORD GlobalsPointer;// 0x38 - The Offset in file where global variales are initilaised(only used for registration scripts)
QWORD NativesPointer;// 0x40 - The Offset in file where the natives table is stored
QWORD Null1;//0x48
QWORD Null2;//0x50;
DWORD ScriptNameHash;//0x58 - A Jenkins hash of the scripts name
DWORD UnkUsually1;//0x5C
QWORD ScriptNamePointer;//0x60 - Points to an offset in the file that has the name of the script
QWORD StringBlocksBasePointer;//0x68 - Points to an array of string block offsets
DWORD StringSize;//0x70 - The Size of all the string tables
DWORD Null3;//0x74
DWORD Null4;//0x78
DWORD Null4;//0x7C
};
All Pointer items are stored in the file as 0xXXXXXX50 and must therefore be masked with 0xFFFFFF to get their actual file offset.
Pages
The Code and Strings used by the script are stored in pages, each one up to a maximum size of 0x4000 bytes. The number of pages used can be calculated based of the CodeSize/StringSize using this:
int pagegCount = (totalSize + 0x3FFF) >> 14;
The size of each of the pages can be calculated using this function
int pageSize(int pageIndex, int totalSize)
{
if (pageIndex >= (totalSize >> 14)){
return totalSize & 0x3FFF;
}
return 0x4000;
}
Code
The Poistion of each of the code pages can be found by reading the positions stored in the array pointer to from CodeBlocksBasePointer, then you can read up to pageSize in that page. The code itself is a set of 127 opcodes that is ran on a stack machine.
Opcode
ID | Name | Description | Length | |
---|---|---|---|---|
0 | nop | No operation | 1 byte | |
1 | iadd | Adds the top 2 items on the stack | 1 byte | |
2 | isub | Subtracts the top 2 items on the stack | 1 byte | |
3 | imul | Multiplies the top 2 items on the stack | 1 byte | |
4 | idiv | Divides the top 2 items on the stack | 1 byte | |
5 | imod | Mods the top 2 items on the stack | 1 byte | |
6 | iszero | Checks the first item on the stack to see if it equals 0 | 1 byte | |
7 | ineg | Reverses the sign on the item on the top of the stack | 1 byte | |
8 | icmpeq | Compares the top 2 integers on the stack to see if they are equal | 1 byte | |
9 | icmpne | Compares the top 2 integers on the stack to see if they are not equal | 1 byte | |
10 | icmpgt | Compares the top 2 integers on the stack to see if the first one is greater than the second one | 1 byte | |
11 | icmpge | Compares the top 2 integers on the stack to see if the first one is greater than or equal to the second one | 1 byte | |
12 | icmplt | Compares the top 2 integers on the stack to see if the first one is less than the second one | 1 byte | |
13 | icmple | Compares the top 2 integers on the stack to see if the first one is less than or equal to the second one | 1 byte | |
14 | fadd | Adds the top 2 floats on the stack | 1 byte | |
15 | fsub | Subtracts the top 2 floats on the stack | 1 byte | |
16 | fmul | Multiplies the top 2 floats on the stack | 1 byte | |
17 | fdiv | Divides the top 2 floats on the stack | 1 byte | |
18 | fmod | Mods the top 2 floats on the stack | 1 byte | |
19 | fneg | Reverses the sign on the first float on the stack | 1 byte | |
20 | fcmpeq | Compares the top 2 floats on the stack to see if they are equal | 1 byte | |
21 | fcmpne | Compares the top 2 floats on the stack to see if they are not equal | 1 byte | |
22 | fcmpgt | Compares the top 2 floats on the stack to see if the first one is greater than the second one | 1 byte | |
23 | fcmpge | Compares the top 2 floats on the stack to see if the first one is greater than or equal to the second one | 1 byte | |
24 | fcmplt | Compares the top 2 floats on the stack to see if the first one is less than the second one | 1 byte | |
25 | fcmple | Compares the top 2 floats on the stack to see if the first one is less than or equal to the second one | 1 byte | |
26 | vadd | Adds the top 2 Vectors[1] on the stack | 1 byte | |
27 | vsub | Subtracts the top 2 Vectors[1] on the stack | 1 byte | |
28 | vmul | Multiplies the top 2 Vectors[1] on the stack | 1 byte | |
29 | vdiv | Divides the top 2 Vectors[1] on the stack | 1 byte | |
30 | vneg | Reverses the sign on the first vector[1] on the stack | 1 byte | |
31 | iand | Performs an And operation to the first 2 integers on the stack | 1 byte | |
32 | ior | Performs an Or operation to the first 2 integers on the stack | 1 byte | |
33 | ixor | Performs a Xor operation to the first 2 integers on the stack | 1 byte | |
34 | itof | Converts the top integer on the stack to a float, and puts that float on the stack | 1 byte | |
35 | ftoi | Converts the top float on the stack to an integer, and puts that integer on the stack | 1 byte | |
36 | ftov | Converts the top float into a Vector containing 3 instances of the same float, and pushes the pointer to that Vector onto the top of the stack | 1 byte | |
37 | pushb | Pushes a byte onto the stack, the byte is defined as the next byte after the opcode | 2 bytes | |
38 | pushb2 | Pushes 2 bytes onto the stack, the bytes are the next 2 bytes after the opcode | 3 bytes | |
39 | pushb3 | Pushes 3 bytes onto the stack, the bytes are the next 3 bytes after the opcode | 4 bytes | |
40 | push | Pushes an int onto the stack, the integer is defined in the next 4 bytes after the opcode | 5 bytes | |
41 | pushf | Pushes a float onto the stack, the float is defined in the next 4 bytes after the opcode | 5 bytes | |
42 | dup | Duplicates the first item on the stack, and pushes it back onto the stack | 1 byte | |
43 | pop | Pops the top item off the stack | 1 byte | |
44 | native | Calls a native function. The number of arguments for the native to take is defined in the first 6 bits of the next byte after the opcode(0-63). The number of returns is stored in the following 2 bits(0-3). The hash of the native functions is stored in the native table at the index specified by the following 2 bytes(expressed as an unsigned short) | 4 bytes | |
45 | enter | Indicates the beginning of an internal function. The byte after the opcode indicates the amount of arguments the function takes off the stack, and the next 2 bytes after that indicate the number of variables the function will have to generate on the stack. The following byte indicates how many bytes to skip past after the opcode(usually so a name can be appended to the function definition) | 5 bytes + value of 5th byte | |
46 | ret | Indicates the end of an internal function. The byte after the opcode indicates the amount of arguments that will have to be popped off the stack, and the next byte after that indicates the stack number of the return address | 3 bytes | |
47 | pget | Pops a pointer off the stack and pushes the value stored in that pointer back onto the stack | 1 byte | |
48 | pset | Pops 2 items off the stack and stores the second item at the location of the first item (the first item being a pointer) | 1 byte | |
49 | peekset | Pops the first item off the stack and peeks at the second item on the stack, then stores the first item at the location pointed to by the second item on the stack | 1 byte | |
50 | tostack | Pops 2 items off the stack, the first being the number of items, second being the memory address. It then pushes that many items to the stack from the memory address | 1 byte | |
51 | fromstack | Pops 2 items off the stack, the first being the number of items, second being the memory address. It then pops that many items from the stack to the memory address | 1 byte | |
52 | getarrayp1 | Pops 2 items off the stack, the first being an array index, second being the pointer to an array. It then pushes the pointer to theindex in the array to the top of the stack. The size each item in the array is determined by the byte following the opcode | 2 byte | |
53 | getarray1 | Pops 2 items off the stack, the first being an array index, second being the pointer to an array. It then pushes the item at the array index to the top of the stack. The size each item in the array is determined by the byte following the opcode | 2 byte | |
54 | setarray1 | Pops 3 items off the stack, the first being the value to set, second being an array index and last being the pointer to an array. It the sets the value at the index in the array to first value. The size each item in the array is determined by the byte following the opcode | 2 byte |