aboutsummaryrefslogtreecommitdiff
path: root/docs/tomes/06-behavior.md
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-20 00:45:58 +0300
committerValentin Popov <valentin@popov.link>2026-10-20 00:45:58 +0300
commit37db376dd3b259cfefdb7081770ca82bdc7542dd (patch)
tree033c50733cee522e7105abd2f2df0b893a14e762 /docs/tomes/06-behavior.md
parentbcedda6e93d228593be7bef1552369aa5b70b623 (diff)
downloadfparkan-37db376dd3b259cfefdb7081770ca82bdc7542dd.tar.xz
fparkan-37db376dd3b259cfefdb7081770ca82bdc7542dd.zip
docs(script): describe native condition evaluation
Diffstat (limited to 'docs/tomes/06-behavior.md')
-rw-r--r--docs/tomes/06-behavior.md66
1 files changed, 47 insertions, 19 deletions
diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md
index d1af7ba..cd61533 100644
--- a/docs/tomes/06-behavior.md
+++ b/docs/tomes/06-behavior.md
@@ -181,25 +181,53 @@ IDs и отправляет команды через игровые interfaces,
открытым направлением. До появления decompiler-а `.scr` binary body сохраняется
lossless, а доказанные symbol/event tables документируются отдельно.
-### Подтверждённый evaluator выражений
-
-Ghidra 12.1.2 decompile GOG `ai.dll` фиксирует отдельный evaluator по VA
-`0x10005180` (не dispatcher инструкций `.scr`). Он получает индекс записи,
-ищет её в контейнере `this + 0x34`, переключается по `u32 tag` в offset `+0`
-и при успешном результате пишет completion byte в `+0x0c`. Из кода доказаны
-ровно пять ветвей `tag = 1..5`; `tag = 1..3` требуют `u32` subtype в
-`+0x04 == 0`, а `tag = 4..5` — `+0x04 == 1`. Поле payload находится по
-`+0x08`.
-
-Ветки 1--3 делают lookup через object/interface, достижимый от
-`this + 0x60 + 0x35c`, и используют его virtual slot `+0x1c`. Ветка 2
-дополнительно читает virtual slots `+0x10`/`+0x18` возвращённого объекта и
-разрешает result tags `1`, `0x12`, `0x13`. Ветка 3 сравнивает virtual slot
-`+0x44` с текущим object value. Ветки 4--5 используют `payload` как index в
-контейнере `this + 0x4c`, обходят его child indices и делают тот же lookup;
-их разные success-guards пока не именуются семантически. Это доказывает
-typed condition/evaluation layer, но **не** формат `.scr`, размеры инструкций
-или связь чисел tag с языковыми операторами.
+### Подтверждённый evaluator записей условий
+
+У сценарного слоя есть не только последовательность инструкций, но и отдельные
+короткие проверки состояния. В GOG `ai.dll` версии `1.00` evaluator таких
+проверок расположен по RVA `0x5180` (VA `0x10005180` при image base
+`0x10000000`; SHA-256 сборки —
+`c19cd6cd31cd2896f8c4ba0f0e700dd3408aec8182daaf4863d98a7d233a2abc`). Он
+получает индекс 16-byte записи и смотрит на её tag, subtype и payload. Для tags
+1–3 допустим subtype `0`, для tags 4–5 — subtype `1`; другие сочетания не
+завершают условие. Байт по смещению `+0x0c` — флаг завершения: evaluator ставит
+`1`, когда проверка проходит, и оставляет запись как есть, когда не проходит.
+Вызывающая функция позже пропускает уже завершённые условия.
+
+Tag выбирает конкретное правило. Для tag 1 запись завершается, когда поиск
+объекта по payload ничего не нашёл. Для tag 2 объект, наоборот, должен
+существовать: затем игра проверяет одно из его свойств и, в более сложной
+ветви, принимает только три числовых кода результата — `1`, `0x12` и `0x13`.
+Для tag 3 найденное значение сравнивается с первым числом текущего контекста.
+Мы не знаем из одного этого кода, каким игровым словам соответствуют эти поля,
+поэтому не называем их владельцем, жизнью или приказом.
+
+Теги 4 и 5 обходят дочерние ссылки выбранного списка. Значение `-1` означает
+«элемента нет» и пропускается. Тег 4 завершается, только если ни один дочерний
+ключ не нашёл объект. Тег 5 тоже пропускает отсутствующий объект, но для
+найденного смотрит результат другого виртуального вызова: ноль разрешает
+продолжить, а первый ненулевой результат прекращает обход и оставляет условие
+незавершённым. Так различаются два алгоритма проверки набора объектов без
+предположений о том, почему сценарист выбрал один из них.
+
+У списков обнаружена нетипичная граница. Программа сначала сравнивает индекс
+списка с его длиной как unsigned число и отбрасывает только значения строго
+больше длины. Равный длине индекс проходит первую проверку, но следующий
+проверенный доступ обнаруживает выход за границу и возбуждает C++ exception.
+Поэтому в будущем движке это нельзя незаметно заменить на «все неверные
+индексы просто дают ложь».
+
+Это статическая реконструкция ветвей, а не доказательство исполнения условия
+в работающем оригинале. Сам вызов RVA `0x5180` и внешний object interface не
+проверялись динамически; неизвестен и код, который создаёт эти 16-byte записи.
+Считыватель `.scr` хранит описатели событий и вложенных записей во внутренних
+контейнерах с шагом `0x14` и `0x20` соответственно. На диске же `.scr` использует
+переменные поля: имя события задаётся длиной, а вложенная запись содержит
+заголовок и список ссылок. Связь этих структур с evaluator-ом не доказана.
+Подробные смещения,
+виртуальные слоты и границы анализа приведены в
+[`приложении о сценарной VM`](../appendices/script-vm.md#evaluator-записей-условий-ai-dll-rva-0x5180)
+и [`границах знаний`](../appendices/knowledge-boundaries.md#script-vm).
### TRF и preload-данные