feat(assets): decode research tables and calculate candidate costs
This commit is contained in:
5 files changed
+1981
-23
No files matched your search
@@ -177,7 +177,11 @@ token и типизированную `float`/`DWORD` ячейку. Неизве
|
||||
всей цепочки кампании. Обычная `Single.01` доходит до следующего blocker-а:
|
||||
`Handler(43)` в clan 1, `Init` instruction 10. Rust не перескакивает его;
|
||||
preview оставляет varset этого клана на defaults, а полный load завершает ошибкой
|
||||
с rollback. Это ещё не загрузка/запуск игры и не общий dispatcher.
|
||||
с rollback. Для самого расчёта уже есть отдельный bounded reader research-таблицы
|
||||
и pure ore/power core в `fparkan-assets`. Они принимают один Unit DAT и не
|
||||
исполняют script instruction: runtime по-прежнему не создаёт владельца живого
|
||||
контекста, не перечисляет AI candidates и не выбирает результат Handler(43).
|
||||
Это ещё не загрузка/запуск игры и не общий dispatcher.
|
||||
|
||||
Для GOG-сборки также найден статический host call path в `Init`. В
|
||||
`iron3d.dll!FUN_1005ff40` (RVA `0x5ff40`) одна ветвь сначала проходит записи
|
||||
@@ -752,15 +756,36 @@ command/event captures для меню, HUD, briefing и диалогов.
|
||||
|
||||
## Research, economy and properties
|
||||
|
||||
Экспорты `LoadResearch`, `CalcFullResearchCost`, TRF/preload resources и TMA
|
||||
properties доказывают отдельный слой исследований, стоимости, добычи и
|
||||
производственных параметров. Формулы стоимости, dependency graph технологий,
|
||||
inventory/economy transitions и точная типизация всех 16-byte property values
|
||||
не закрыты.
|
||||
Для GOG `ArealMap.dll` SHA-256
|
||||
`a25f9254ebb339d70e2e07c8121950191e9584ca0858d8e9c2e2b6c671edf6ea`
|
||||
`CalcFullResearchCost` по RVA `0x16280` имеет подтверждённый ABI
|
||||
stdcall `[context, candidate, out1, out2]`, `RET 0x10`, и восстановленный
|
||||
алгоритм для одного candidate tree: zero-kind subtree pruning, direct preorder,
|
||||
общая дедупликация, ordered DFS по `TRF2`/`TRF3`, raw gates из `TRF1` и
|
||||
последовательное `f32` сложение. Принятый набор из 23 synthetic native cases
|
||||
включал Calc и два AI predicate; для контролируемых сравнений x87 word был
|
||||
установлен в `0x007f`. У набора synthetic context, поэтому он не измеряет
|
||||
реальную миссионную стоимость.
|
||||
|
||||
Закрывающий эксперимент: сопоставить research functions с ресурсами и UI,
|
||||
снять изменения state на контролируемых покупках/исследованиях и построить
|
||||
typed schema свойств по consumers, а не по одному имени.
|
||||
`fparkan-assets` декодирует используемые NRes chunks, сохраняя сырые TRF0 words
|
||||
и непрозрачные данные. Native cost outputs сопоставлены только с двумя raw
|
||||
words: out1 складывает TRF0 word 1 и подписан как ore, out2 складывает word 0
|
||||
и подписан как power. Смыслы остальных полей полного 32-byte callback record
|
||||
не установлены. Non-ASCII case folding research keys также остаётся открытым;
|
||||
Rust явно возвращает unsupported для таких сравнений.
|
||||
|
||||
Открыты host binding полного Handler(43), factory/ownership/lifetime реального
|
||||
исследовательского context при запуске миссии, AI candidate enumeration и tie
|
||||
selection, live x87 policy, inventory/economy transitions и точная типизация
|
||||
всех 16-byte TMA property values. Контролируемый тест с настоящими
|
||||
`scream.trf` и `12hel1.dat` проверяет разбор и API одного candidate, но не
|
||||
сравнивает результат с native вызовом реального миссионного context.
|
||||
|
||||
Закрывающий эксперимент для оставшихся границ: связать startup owner, research
|
||||
context и AI candidate selection в реальном `Init`; затем сравнить контекст,
|
||||
порядок кандидатов и cost outputs оригинала с Rust на одной миссии. Покупки,
|
||||
inventory transitions и typed property schema требуют отдельных контролируемых
|
||||
исследований.
|
||||
|
||||
## Rare branches
|
||||
|
||||
|
||||
@@ -34,6 +34,60 @@ VM state;
|
||||
`.trf` — NRes tables, чей framing подтверждён, а field semantics местами лишь
|
||||
consumer-inferred.
|
||||
|
||||
### Исследовательский профиль TRF и стоимость кандидата
|
||||
|
||||
Для одной ветви `Handler(43)` уже восстановлен путь вычисления стоимости.
|
||||
Результат привязан к GOG `ArealMap.dll`, SHA-256
|
||||
`a25f9254ebb339d70e2e07c8121950191e9584ca0858d8e9c2e2b6c671edf6ea`,
|
||||
`CalcFullResearchCost` по RVA `0x16280`. Реальный ABI — четыре аргумента
|
||||
stdcall на стеке: `[context, candidate, out1, out2]`; функция снимает с него
|
||||
`0x10` байт и не принимает `candidate` через `ECX`.
|
||||
|
||||
Источник этих таблиц привязан к GOG `MisLoad.dll`, SHA-256
|
||||
`c607762bb67db14ca530927c8e1347339b62b0a163ac90c708ee83142292e2bf`.
|
||||
`FUN_10002fe0` (RVA `0x2fe0`) загружает required TRF chunks. Внутренний
|
||||
research context разрешает ключ через `FUN_10002a40` (RVA `0x2a40`), выдаёт
|
||||
32-byte row record через `FUN_10002aa0` (RVA `0x2aa0`) и возвращает ordered
|
||||
dependency array через `FUN_10002cf0` (RVA `0x2cf0`). Эти методы соответствуют
|
||||
виртуальным слотам context `+0x08`, `+0x0c` и `+0x24`; native Calc получает их
|
||||
через таблицу вызовов, а не напрямую из файлов.
|
||||
|
||||
`ResearchTrfDocument` в `fparkan-assets` повторно использует NRes reader и
|
||||
сохраняет исходный архив. `TRF0.attr1` задаёт число строк по 40 байт; native
|
||||
загрузчик требует `TRF0.attr2 == 3`. `TRF1` задаёт один флаговый byte на строку:
|
||||
Rust воспроизводит native zero-fill при коротком payload и отклоняет более
|
||||
длинный. `TRFB.attr1` задаёт пары little-endian `(u16 name_offset, u16 row)`;
|
||||
имя берётся из NUL-terminated pool `TRF6`. Поиск идёт по исходному порядку и
|
||||
берёт первую ASCII-case-insensitive пару. Связь строки с парой использует
|
||||
только младшие 16 бит сырого слова `TRF0` по смещению `+0x20`.
|
||||
|
||||
Две таблицы зависимостей имеют собственные размеры: `TRF2` считает группы,
|
||||
`TRF3` содержит их плоский упорядоченный поток индексов; `TRF4`/`TRF5` образуют
|
||||
отдельную пару. Только `TRF2`/`TRF3` участвуют в `CalcFullResearchCost`.
|
||||
Отсутствующий `TRF3` или `TRF5` означает пустой поток лишь при нулевой сумме
|
||||
счётчиков соответствующей таблицы. Required `TRF7`–`TRFA` остаются
|
||||
непрозрачными. Reader проверяет фактически читаемые срезы, строки, ссылки и
|
||||
размеры выделений для памяти Rust; это дополнительная безопасность, а не
|
||||
утверждение, что native loader отвергает те же плохие файлы.
|
||||
|
||||
Из флагового byte `TRF1` native provider строит слова record `+0`, `+4` и `+8`
|
||||
из битов `(1, 0, 2)`. `CalcFullResearchCost` использует первые два как raw gate,
|
||||
не присваивая им общего игрового имени: `+0` определяет, добавлять ли строку,
|
||||
`+4` — раскрывать ли её зависимости. Нулевой `kind` компонента Unit DAT
|
||||
отсекает весь узел с потомками. Иначе дерево сканируется в preorder; найденные
|
||||
строки добавляются один раз, затем для каждой прямой строки выполняется
|
||||
упорядоченный DFS по `TRF2`/`TRF3`. Общая таблица посещений завершает циклы и
|
||||
удаляет повторы.
|
||||
|
||||
По debug-строкам `out1` — ore и суммирует `TRF0` word 1, а `out2` — power и
|
||||
суммирует word 0. Native код хранит каждый шаг суммы как `f32`; один cost-вектор
|
||||
`1, 2^24, -2^24` даёт первый результат `0`, а в другом порядке
|
||||
`2^24, -2^24, 1` даёт `1`. Rust core сохраняет тот же порядок накопления. В
|
||||
принятом наборе из 23 синтетических вызовов оригинальных DLL были случаи
|
||||
`CalcFullResearchCost` и два AI-предиката; x87 control word был установлен в
|
||||
`0x007f` для контролируемых сравнений. Набор проверяет выбранные ветви на
|
||||
синтетическом контексте, но не использует реальный контекст миссии.
|
||||
|
||||
## Безопасная модель исполнения
|
||||
|
||||
Целевая VM должна разделять immutable package (bytecode, symbols, events,
|
||||
@@ -285,9 +339,12 @@ TMA properties остаются four raw `u32` words плюс имя, пока c
|
||||
сохраняются `NOT USED`; corpus подтверждает `Invulnerability`, life state,
|
||||
`ClanID`, ore, speed и free-time properties.
|
||||
|
||||
Research/economy работают в simulation: `LoadResearch` и
|
||||
`CalcFullResearchCost` доказывают данные и вычислимую стоимость, но не полный
|
||||
layout prerequisites/modifiers/unlocks. General formula evaluator требует
|
||||
Для одного кандидата и одной research-таблицы порядок prerequisite-closure,
|
||||
дедупликация и две выходные стоимости `CalcFullResearchCost` восстановлены и
|
||||
проверены отдельно, как описано в
|
||||
[исследовательском профиле TRF](#исследовательский-профиль-trf-и-стоимость-кандидата).
|
||||
Это не закрывает production-modifier/unlock data, экономические переходы или
|
||||
полное исполнение Handler(43). Общий evaluator формул всё ещё требует
|
||||
versioned grammar, typed operands, deterministic numeric policy, bounded stack
|
||||
и явных errors; x87-compatible rounding нужен там, где оно выбирает ветку.
|
||||
Описанный ниже literal-путь первого `Init` не закрывает общий evaluator.
|
||||
@@ -448,8 +505,11 @@ Rust сохраняет всю таблицу строк, но вычисляе
|
||||
полную загрузку кампании Mission.01 после этих присваиваний; у обычной
|
||||
`Single.01` clan 1 после формул остаётся `Handler(43)` в instruction 10, который
|
||||
не поддержан и не обходится. Preview оставляет значения этого клана без
|
||||
частичных записей, а полный load возвращает ошибку транзакционно. Это не
|
||||
подтверждение запуска самой игры или полной VM.
|
||||
частичных записей, а полный load возвращает ошибку транзакционно. В
|
||||
`fparkan-assets` теперь отдельно доступны bounded TRF reader и pure cost core
|
||||
для одного Unit DAT, но script/runtime пока не создают нужный research context,
|
||||
не перечисляют AI candidates и не выполняют этот handler. Это не подтверждение
|
||||
запуска самой игры или полной VM.
|
||||
|
||||
### Первый вызов Behavior и сценарного `Init`
|
||||
|
||||
|
||||
Reference in new issue
Block a user