• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!

Gothic.dat

HeDeDe

Участник форума
Регистрация
17 Авг 2009
Сообщения
203
Благодарности
79
Баллы
180
  • Первое сообщение
  • #1
Приветстую.
У меня вдруг зачесались руки написать компилятор готических скриптов с нуля. В связи с этим у меня появился вопрос вопрос: есть ли кто-нибудь, кто знает структуру 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
 
Последнее редактирование:

Lorddemonik

★★★★★
Редактор раздела
Регистрация
17 Дек 2011
Сообщения
1.114
Благодарности
581
Баллы
350
зачем тебе всё собирать? необходимое отредактируй , положи в папку авторан, а потом через консоль скомпилируй новый датник.
Знаешь, даже если переводить таким образом, прокликивать каждый файл то ещё развлечение.
Да и скормить можно в таком виде не только Юне, Но и ninja тоже способен заменять скрипты
Да и цель была совсем другая, А дальше "работает - не трожь"
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
2.003
Благодарности
973
Баллы
295
Lorddemonik, "цель другая". у кого? гражданин задал вопрос. я предложил ответ. то что ты начал тестить этот вариант и давать ему оценки и твоё право и твоя личная инициатива.

можно ли таким образом декомпилировать скрипты? можно. можно ли по маске что то найти и разложить по папкам?
можно можно ли внести изменения например через инъекции? можно. можно ли скомпилировать новый датник с инъекциями? можно.

про ниндзя ничего не знаю. ориентируюсь на инструменты union от Gratt

***

этой командой все D файлы сольются в один dialogs.d, и после этого все остальные файлы можно грохнуть))
лучше сначала разложить по папкам:) простенький батник копирующий по папкам диалоги, нпс, монстров и прочего в скрепке.

список файлов которые надо скопировать можно составить через поиск по маске в тоталкоммандере.

1661860866795.png
1661860947286.png
а уже все файлы предметов можно слить в общий файл.

Код:
@echo off
for /r %%i in (*.d) do @call :ConcatFolder "%%i"


:ConcatFolder
::pause
if "" == "%1" goto :EOF
setlocal ENABLEEXTENSIONS
set FLDR=%~dp1
type "%1" >> "%FLDR%aggregate.txt"
endlocal & goto :EOF
 

Вложения

  • Sistematizaciya.zip
    425 байт · Просмотры: 8
Последнее редактирование:

Lorddemonik

★★★★★
Редактор раздела
Регистрация
17 Дек 2011
Сообщения
1.114
Благодарности
581
Баллы
350
MW 7, Этот гражданин который задал вопрос, явно скрипты ниразу не видел, т.к умений нет даже чтобы найти "имя" монстра. Тесты я начал просто чтобы выяснить насколько далеко зашёл прогресс. Очевидно, что собрать из такого количества файлов проект можно. А поднятие темы привлечет к себе внимание практикующих с советами. Ты вон уже поделился методом сортировки. Благодарочка
 

N1kX


Модостроитель
Регистрация
13 Ноя 2009
Сообщения
6.156
Благодарности
5.656
Баллы
910
Да зачем эта возня, если мне осталось немного докопировать перевод мода?
Тем более русский перевод мода сделан на старой версии мода и там строк для перевода нехило так подскочил.
 

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
547
Баллы
245
Дайте мне файлы для декомпиляции.
 

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
547
Баллы
245
Вот что получилось. Приложен файл с возможными ошибками в русификаторе. Не декомпилируется из-за использования оп-кода 04 MODULO.
 

Вложения

  • _Velaya.7z
    585,5 KB · Просмотры: 17
Последнее редактирование:

Lorddemonik

★★★★★
Редактор раздела
Регистрация
17 Дек 2011
Сообщения
1.114
Благодарности
581
Баллы
350
Вот что получилось. Приложен файл с возможными ошибками в русификаторе. Не декомпилируется из-за использования оп-кода 04 MODULO.
Коллега, нас не интересует исход. Лишь процесс представляет интерес. Чем как и почему вы сохранили читаемость. Чем подробнее тем лучше
 
Сверху Снизу