HeDeDe
Участник форума
- Регистрация
- 17 Авг 2009
- Сообщения
- 203
- Благодарности
- 79
- Баллы
- 180
- Первое сообщение
- #1
Приветстую.
У меня вдруг зачесались руки написать компилятор готических скриптов с нуля. В связи с этим у меня появился вопрос вопрос: есть ли кто-нибудь, кто знает структуру Gothic.dat?
Попытался расковырять Gothic.dat, но осталось несколько непонятных моментов.
Что я выяснил 3 часа ковыряния Gothic.dat:
Для чего массив в начале файла — не ясно. Сначала думал, что он связан с таблицей символов, но никакой корреляции найти не удалось.
EDIT: полностью разобрал формат
У меня вдруг зачесались руки написать компилятор готических скриптов с нуля. В связи с этим у меня появился вопрос вопрос: есть ли кто-нибудь, кто знает структуру Gothic.dat?
Попытался расковырять Gothic.dat, но осталось несколько непонятных моментов.
Что я выяснил 3 часа ковыряния Gothic.dat:
Для чего массив в начале файла — не ясно. Сначала думал, что он связан с таблицей символов, но никакой корреляции найти не удалось.
EDIT: полностью разобрал формат
Код:
Первый байт — tree_version. Во второй Готике это 0x32.
Gothic.dat состоит из двух секций — таблицы символов и байткода.
Таблица символов состоит из двух массивов: первый массив — это номера символов, отсортированные по именам символов (используется std::string::compare), и начинается с числа символов в таблицы. Все значения имеют размер 4 байта.
Записи во втором массиве имеют следующий формат:
struct Symbol {
/*
* 1, если имя символа присутствует,
* 0, если оно пустое
*/
int has_name;
/*
* Имя символа (Например: "C_NPC", "B_GiveXP", "KDF_404_XARDAS")
* Заканчивается переносом строки (\n).
*/
string name;
/*
* Для переменных класса: Смещение внутри класса
* Для instance: указатель на объект
* Для функций: тип возвращаемого значения
*/
int offset;
/*
* Тип символа, размер массива, флаги
* (описаны ниже)
*/
uint16 type;
uint16 flags;
// номер файла в .src
int file;
int line;
int line_count;
int pos;
// длина строки в исходниках
int size;
};
Далее записывается значение, это может быть:
- int
- float
- char[] — строковое значение, заканчивающееся переносом строки ('\n')
- указатель на функцию или переменную движка
- количество байт от начала секции кода — для func и instance
После этого значения записывается номер символа класса или функции (если это член класса, инстанция или локальная переменная, в противном случае записывается -1).
flags имеет седующую структуру:
1 00 1001 0 101 0000'0000'0000
a ?? emrc ? ttt ssssssssssss
a — как-то связан с выделением памяти
e — external — функция движка
m — член класса
r — return
c — const
t — тип, известны следующие типы:
enum type {
void = 0;
float = 1;
int = 2;
string = 3;
class = 4;
func = 5;
prototype = 6;
instance = 7;
}
s — количество аргументов/размер массива/количество членов класса
Обязательно присутствует символ "\xffINSTANCE_HELP".
В самом конце — секция кода. Готика использует стековую виртуальную машину. В начале секции — её длина, далее идут опкоды (каждый — 1 байт, но некоторые могут иметь 4-байтовый аргумент, исключение — PUSH_ARRAY_VAR — имеет 2 аргумента, 4-байтовый адрес и 1-байтовый индекс массива).
Существуют следующие опкоды:
Арифметические операции:
Берутся два значения со стека (A, B), в стек записывается A @ B
00 + ADD
01 - SUB
02 * MUL
03 / DIV
04 % MOD
05 | OR
06 & AND
0D << LSHIFT
0E >> RSHIFT
Операции сравнения:
Аналогичны арифметическим. В стек записывается 1 если верно, 0 если нет.
07 < LESS
08 > GREATER
0F <= LEQ
10 == EQUAL
11 != NEQ
12 => GEQ
Логические операции:
0B || OR_BOOL
0C && AND_BOOL
Унарные операции:
1E + // берет значение со стека и кладет обратно
1F -
20 !
21 ~
Работа со стеком:
«Кладут» значение своего аргумента (следующего за инструкцией) в стек.
40 PUSHINT [VALUE]
41 PUSHADR [ADDR]
43 PUSHINSTANCE [ADDR]
F5 PUSH_ARRAY_VAR [ADDR][INDEX] // индекс всего 1 байт
Операции присвоения:
Берет 2 значения со стека (A, B) и присваивает A = B
09 SET_INT
46 SET_STRING
47 SET_STRING_REF
48 SET_FUNC
49 SET_FLOAT
4A SET_INSTANCE
Арифметические операции с присвоением
13 +=
14 -=
15 *=
16 /=
Функции
3C RET
3D CALL [ADDR]
3E CALL_BUILTIN [ADDR]
Ветвление:
4B JMP_FALSE // берет значение со стека, и переходит по адресу, если оно равно 0
4C JMP
50 SET_CURRENT_INSTANCE
Последнее редактирование: