SCO
Материал из GTAModding.ru
Версия от 10:33, 31 мая 2009; Chipsman (обсуждение | вклад)
Эта статья требует полного или частичного перевода. Часть этой статьи написана на иностранном языке. Если вы знаете его, пожалуйста, помогите с ее переводом на русский язык. |
Файлы формата SCO (с расширением *.sco) содержат скрипты игры GTA 4. Это новый формат, пришедший на смену соответствующих файлов *.scm в предыдущих версиях.
Содержание |
Формат файла
Файл SCO разделен на 4 сегмента. Первый является заголовком и содержит информацию о файле. Второй слой это сегмент кода, он состоит из последовательности опкодов, которые определяют как скрипт будет интерпретирован игрой. Третий сегмент содержит достаточно места для размещения глобальных переменных скрипта. И четвертый сегмент используется для размещения публичных переменных.
Заголовок
Существуют кодированные и не кодированные SCO файлы. Вне зависимости от этого файл содержит не кодированную заголовочную структуру, что позволяет всегда определить какой вид SCO файла перед вами. Размер заголовка - 24 байта, которые состоят из 6 частей каждый по 4 байта:
4b - CHAR[4]/UINT32 - SCO идентификатор 4b - UINT32 – Размер кода – количество байт, занятых сегментом кода 4b - UINT32 – Количество глобальных переменных 4b - UINT32 – Количество публичных переменных 4b - UINT32 – Скриптовые флаги 4b - UINT32 – Сигнатура UINT32 – это беззнаковое целое 32 бит (= 4 байта), оно может принимать значения от 0 до (2^32 – 1) CHAR[4] – строка из 4 символов (4*8 = 32 бит = 4 байта)
SCO идентификатор может быть типа "SCR\r" (или 0xD524353) в не кодированной версии, и "scr"+0xE (или 0xE726373) в кодированной. Для декодирования любого кодированного файла вы должны декодировать каждый сегмент (кроме не кодированного заголовка) используя специальный метод GTA IV AES криптографии. Сегмент глобальных переменных начинается с байта номер (24 + Размер кода + 1), и содержит (4 * Количество глобальных переменных) байт, т.к. каждая глобальная переменная должна быть описана в 4 байта. Аналогично сегмент публичных переменных начинается с байта (24 + Размер кода + 4 * Количество глобальных переменных + 1) и содержит (4 * Количество публичных переменных) байт. Скриптовые флаги это 32 булевых бита характеризующие данный скрипт. Сигнатура отличается только в navgen_main, но может использоваться для установки приоритета скрипту.
Сегмент кода
Сегмент кода состоит из последовательности опкодов, которые определяют как скрипт будет интерпретирован игрой.
Опкоды
Разные опкоды могут занимать разное количество байт, но все опкоды идентифицируются в первом байте. Всего есть 79 опкодов которые могут встретится, а все опкоды после 96 включительно это Push (помещаемые) опкоды которые помещаются в стек. Для примера опкод 100 будет помещён 4 в стек. Опкоды 76, 77, 78 даются с поддержкой XLive буфера и доступны только на PC платформе. Неопределенные опкоды будучи использованными приведут к вылету скрипта.
ID | Имя | Определение | Длина |
---|---|---|---|
0 | nop | Нет оператора | 1 byte |
1 | iadd | Складывает 2 верхних элемента в стеке | 1 байт |
2 | isub | Вычитает 2 верхних элемента в стеке | 1 байт |
3 | imul | Умножает 2 верхних элемента в стеке | 1 байт |
4 | idiv | Делит нацело 2 верхних элемента в стеке | 1 байт |
5 | imod | Дает остаток от деления 2 верхних элементов в стеке | 1 байт |
6 | iszero | Проверяет первый элемент стека на равенство 0 | 1 байт |
7 | ineg | Меняет знак верхнего элемента в стеке | 1 байт |
8 | icmpeq | Сравнивает два целых сверху стека на равенство | 1 байт |
9 | icmpne | Сравнивает два целых сверху стека на не равенство | 1 байт |
10 | icmpgt | Сравнивает два целых сверху стека на то что первый больше второго | 1 байт |
11 | icmpge | Сравнивает два целых сверху стека на то что первый больше или равен второму | 1 байт |
12 | icmplt | Сравнивает два целых сверху стека на то что первый меньше второго | 1 байт |
13 | icmple | Сравнивает два целых сверху стека на то что первый меньше или равен второму | 1 байт |
14 | fadd | Складывает 2 верхних дробных элемента в стеке | 1 байт |
15 | fsub | Вычитает 2 верхних дробных элемента в стеке | 1 байт |
16 | fmul | Умножает 2 верхних дробных элемента в стеке | 1 байт |
17 | fdiv | Делит 2 верхних дробных элемента в стеке | 1 байт |
18 | fmod | Дает остаток от деления 2 верхних дробных элементов в стеке | 1 байт |
19 | fneg | Меняет знак верхнего дробных элемента в стеке | 1 байт |
20 | fcmpeq | Сравнивает два дробных сверху стека на равенство | 1 байт |
21 | fcmpne | Сравнивает два дробных сверху стека на не равенство | 1 байт |
22 | fcmpgt | Сравнивает два дробных сверху стека на то что первый больше второго | 1 байт |
23 | fcmpge | Сравнивает два дробных сверху стека на то что первый больше или равен второму | 1 байт |
24 | fcmplt | Сравнивает два дробных сверху стека на то что первый меньше второго | 1 байт |
25 | fcmple | Сравнивает два дробных сверху стека на то что первый меньше или равен второму | 1 байт |
26 | vadd | Складывает 2 верхних вектора[1] в стеке | 1 байт |
27 | vsub | Вычитает 2 верхних вектора[1] в стеке | 1 байт |
28 | vmul | Умножает 2 верхних вектора[1] в стеке | 1 байт |
29 | vdiv | Делит 2 верхних вектора[1] в стеке | 1 байт |
30 | vneg | Меняет знак верхнего вектора[1] в стеке | 1 байт |
31 | iand | Оператор And для 2 первых целых в стеке | 1 байт |
32 | ior | Оператор Or для 2 первых целых в стеке | 1 байт |
33 | ixor | Оператор Xor для 2 первых целых в стеке | 1 байт |
34 | jmp | Перейти по адресу в коде, использует следующие после опкода 4 байта как адрес | 5 байт |
35 | jmpf | Перейти по адресу в коде, если сверху стека 0, использует следующие после опкода 4 байта как адрес | 5 байт |
36 | jmpt | Перейти по адресу в коде, если сверху стека 1, использует следующие после опкода 4 байта как адрес | 5 байт |
37 | itof | Переводит верхнее целое в стеке в дробное и кладет это дробное в стек | 1 байт |
38 | ftoi | Переводит верхнее дробное в стеке в целое и кладет это целое в стек | 1 байт |
39 | ftov | Переводит верхнее дробное в стеке в вектор[1] (содержащий три числа, равных этому дробному) и кладет указатель на вектор[1] в стек | 1 байт |
40 | ipush2 | Кладет короткое целое в стек, которое определятся следующими за опкодом 2 байтами. | 3 байт |
41 | ipush | Кладет целое в стек, которое определятся следующими за опкодом 4 байтами | 5 байт |
42 | fpush | Кладет дробное в стек, которое определятся следующими за опкодом 4 байтами | 5 байт |
43 | dup | Копирует первый элемент стека, и помещает его обратно в стек | 1 байт |
44 | pop | Извлекает верхний элемент из стека | 1 байт |
45 | native | Вызов native функции. Количество аргументов определяется 2 байтом. Количество значение (либо 0 либо 1) определяется 3 байтом. Последние 4 байта из 7 содержат идентификатор имени функции. | 7 байт |
46 | call | Вызов функции в скрипте. Помещает возращенный адрес на стек. Местоположение функции определяется следующими 4 байтами. | 5 байт |
47 | enter | Индикатор начала функции, определяемой в скрипте. Байт после опкода определяет количество аргументов функции, которые будут взяты из стека. Следующие 2 байта определяют количество переменных функции, которые будут сгенерированы в стеке. | 4 байт |
48 | ret | Индикатор конца функции, определяемой в скрипте. Байт после опкода определяет количество аргументов, которые будут извлечены из стека. 3 байт определяет номер в стеке с возвращенным адресом. | 3 байт |
49 | pget | Извлекает указатель из стека и помещает в стек значение по адресу указателя. | 1 байт |
50 | pset | Извлекает 2 элемента из стека и записывает второй элемент по адресу обозначенному в первом элементе (должен быть указателем). | 1 байт |
51 | ppeekset | Извлекает первый элемент из стека и линкует его во втором элементе. Теперь на содержание первого элемента указывает указатель во втором. | 1 байт |
52 | explode | Извлекает 2 элемента из стека, один становится началом массива в памяти, а другой – концом. Разница между ними деленная на 4 дает количество элементов в массиве, после чего элементы массива помещается в стек один за одним с 4 байтным сегментом. | 1 байт |
53 | implode | Извлекает первый элемент из стека для получения адреса массива для записи и помещает стек в массив. | 1 байт |
54 | flvar0 | Помещает указатель на 1 локальную переменную функции в стек. | 1 байт |
55 | flvar1 | Помещает указатель на 2 локальную переменную функции в стек. | 1 байт |
56 | flvar2 | Помещает указатель на 3 локальную переменную функции в стек. | 1 байт |
57 | flvar3 | Помещает указатель на 4 локальную переменную функции в стек. | 1 байт |
58 | flvar4 | Помещает указатель на 5 локальную переменную функции в стек. | 1 байт |
59 | flvar5 | Помещает указатель на 6 локальную переменную функции в стек. | 1 байт |
60 | flvar6 | Помещает указатель на 7 локальную переменную функции в стек. | 1 байт |
61 | flvar7 | Помещает указатель на 8 локальную переменную функции в стек. | 1 байт |
62 | flvar | Помещает указатель на локальную переменную функции в стек, индекс меньше или равен 8. | 1 байт |
63 | global | Извлекает индекс глобальной переменной из стека, помещает указатель на глобальную переменную скрипта в стек. | 1 байт |
64 | public | Извлекает индекс публичной переменной из стека, помещает указатель на публичную переменную в стек. | 1 байт |
65 | array | Извлекает местоположение массива, размер элемента и индекс из стека. Помещает указатель на индекс массива в стек | 1 байт |
66 | switch | Pops the item to compare off the stack, and then jumps to location corresponding to that item. After the opcode byte it contains a byte defining the number of possible entries, and after that the number of possible entries times 8 are taken up with repeating instances of 4 bytes of the index identifier, and 4 bytes of the location to jump to if that index is correct | (Байт после опкода * 8) + 2 |
67 | spush | Помещает строку в стек. Байт после опкода обозначает длину строки. Дальше следуют символы строки. | (Байт после опкода)+2 |
68 | null | Помещает указатель на пустую память в стек. | 1 байт |
69 | scpy | Извлекает 2 указателя из стека и копирует второй элемент в первый. | 1 байт |
70 | itos | Извлекает целое из стека и помещает массив-строку, соответствующую числу, в стек. | 1 байт |
71 | sadd | Извлекает 2 указателя из стека, и прибавляет второй элемент к первому. | 1 байт |
72 | saddi | Извлекает 2 элемента из стека, переводит второй в из целого в строку (IntToStr) и добавляет эту строку к первому элементу. | 1 байт |
73 | catch | Устанавливает верх области сохранения, содержащей состояния перехваченных ошибок. | 1 байт |
74 | throw | Индикатор области хендлов скриптовых ошибок относительно перехваченных скриптов. | 1 байт |
75 | memcpy | Извлекает 3 указателя из стека, копирует содержание по 3 адресу в первый элемент (по адресу) с повторением, определенным во втором элементе. Затем добавляет null в содержание по первому адресу. Получается строка в (1) = строке (3) дублированной (2) раз и завершенная нулевым символом. | 1 байт |
76 | getxprotect | Извлекает адресс памяти из стека и добавляет в стек не поддерживаемое XLive значение (только для PC) | 1 байт |
77 | setxprotect | Извлекает расположение памяти из стека извлекает другое значение из стека. Преобразует/поддерживает втрое значение используя XLive и записывает новое значение в память по адресу. (только для PC) | 1 байт |
78 | refxprotect | Извлекает адресс памяти из стека, извлекает другое значение для определения флагов преобразования, и наконец извлекает следующее значение, которое содержит количество элементов для обработки. Если первый бит в флаге установлен все элементы в памяти по адресу будут преобразованы в не поддерживаемое XLive состояние. Если 2 бит установлен они будут XLive поддержаны. (только для PC) | 1 байт |
79 | exit | Терминирует скрипт возвращая ошибку | 1 байт |
80 -> 255 | ipush1 | Помещает любое целое в стек. Целое определяется числом 96. Для примера: опкод 95 поместит -1 в стек, а опкод 97 поместит 1 | 1 байт |
^ Вектор на стеке это указатель по адресу, который содержит полный адрес. Т.е. при передачи через стек (например при вызове функций) передается не весь вектор/массив, а только указатель (как это делается в С). Вектор это синоним следующей структуре:
4b - FLOAT32 - X 4b - FLOAT32 - Y 4b - FLOAT32 - Z
Глобальные переменные
Содержит глобальные переменные скрипта. Каждая глобальная переменная занимает 4 байта, и может содержать информацию о самом скрипте.
Публичные переменные
Определяют что делает секция.
Высокоуровневое представление
При переводе ассамблера SCO файлов в высокоуровневое представление возникает несколько моментов. Массивы и структуры могут быть определены только на основе следующих типов: целых int, дробных float, строк string или уже определенных структур. Интересно заметить что опкоды содержат типы целых и объединения в строки, это свойство роднит язык SCO с java где строки определяются также (равно как и в С строки – массивы целых типа char). Еще кажется что SCO файлы не имеют низкоуровневой поддержки классов, однако это не значит что они не могут быть введены в SCO также как они были введены в C++.